SC - Conditional AE
SC - Conditional AE
Overview
SC - Conditional AE extends the D&D 5e Active Effect workflow in three practical ways:
- a dedicated Condition tab on Active Effect sheets
- an optional Formula column for activation-time rolls
- a macro execution change that can run when an effect turns on or off
In practice, this lets you build effects that:
- only apply while a JavaScript condition returns
true - roll a fresh number when the effect activates and write that result into the normal change value
- trigger a world macro when the effect is enabled, disabled, removed, or becomes available again after suppression
If a native condition evaluates to false, the effect is suppressed and its
changes do not apply.
| Feature | What it does | Where you use it |
|---|---|---|
| Condition tab | Runs JavaScript to decide whether the Active Effect is currently available. | Active Effect sheet |
| Formula column | Rolls a formula at activation time and writes the rolled total into the standard change value. | Active Effect changes table |
| Macro execution change | Executes a world macro when the effect turns on or off. | Active Effect changes table |
Compatibility and Installation
This module is free and released.
Install it through Foundry with this manifest URL:
https://github.com/Shattered-Codex/sc-conditional-ae/releases/latest/download/module.jsonRequirements:
- Foundry VTT
v13andv14 - system:
dnd5e dnd5esystem version4.0.0+- recommended compatibility helper:
libWrapper
Optional adjacent integrations:
- DAE compatibility expressions and legacy DAE condition flags
- Aura Effects fallback typing when Aura Effects is not active
After installing:
- Enable SC - Conditional AE in your world.
- Open Configure Settings > Module Settings.
- Find SC - Conditional AE and click Open settings.
- Turn on the features you want before editing your Active Effects.
Module Settings
The module registers a dedicated settings window under Configure Settings > Module Settings > SC - Conditional AE > Open settings.
| Setting | Scope | Default | What it does |
|---|---|---|---|
| Enable formula column | World | On | Adds the Formula column to Active Effect changes and enables activation-time rolls. Requires reload. |
| Post formula roll chat card | World | Off | Posts a chat card with a roll button instead of rolling immediately when a formula-backed effect activates or a suppressed conditional effect becomes available again. |
| Show condition tab | World | On | Adds the Condition tab to Active Effect configuration sheets. Requires reload. |
| Enable debug logging | Client | Off | Logs condition evaluation, suppression refreshes, and activation transitions to the browser console. |
| Documentation | Menu | - | Opens this wiki page from Foundry. |
| Support the developer | Menu | - | Opens the Patreon support page. |
If DAE is active, Foundry may show a libWrapper warning in the browser
console. The module settings text and implementation both treat that as a
warning, not as a confirmed functional break.
Condition Tab
When Show condition tab is enabled, Active Effect sheets gain a dedicated Condition tab.
The tab does more than store code. It also exposes a current-evaluation panel, an inactive-state badge label, and a target-application behavior selector.
| Control | What it does | Why it matters |
|---|---|---|
| Current evaluation | Shows whether the condition is currently true, false, empty, or throwing an error, plus the returned value, evaluation context, and effect state. | Lets you debug the rule against the current actor or item before you close the sheet. |
| Condition badge label | Stores a short label shown on inactive effects. | Useful when a suppressed effect should explain why it is not active without opening the effect. |
| When applied to a target | Controls whether a target application reapplies the current Active Effect, stacks a duplicate, or follows DAE stacking rules when DAE is active. | Important when the same source effect can be applied to a target more than once. |
| Condition editor | Stores the native JavaScript condition or the adapted DAE compatibility expression. | This is the rule that decides whether the effect is available. |
| Open wiki | Links from the sheet directly to this documentation section. | Keeps the authoring workflow close to the explanation. |
Writing Conditions
You can write a full script:
return (actor?.system?.attributes?.hp?.value ?? 0) > 0;You can also write a single expression:
(actor?.system?.attributes?.hp?.value ?? 0) > 0Expression shorthand only works when the whole condition is one expression. If
you use statements such as const, let, or if, finish with return.
This works:
const hp = actor?.system?.attributes?.hp?.value ?? 0;
return hp > 0;This does not work:
const hp = actor?.system?.attributes?.hp?.value ?? 0;
hp > 0;Native SC - Conditional AE conditions are synchronous. If the condition returns a Promise or tries to rely on async work, the module treats it as an error and suppresses the effect.
Conditions receive these native context variables:
| Variable | What it gives you | Notes |
|---|---|---|
| `effect` | The current Active Effect document. | Useful for reading effect flags or metadata. |
| `actor` | The actor affected by the effect. | This is the main actor-side data source for most conditions. |
| `targetActor` | The actor affected by the effect. | In native JavaScript mode this currently resolves to the affected actor, not to a live selected target. |
| `item` | The owning or origin item when one can be resolved. | Useful for transferred item effects and item-state checks such as equipped or attuned. |
| `origin` | The resolved effect origin document when one exists. | Can be an actor, item, or Active Effect depending on the source. |
| `originActor` | The actor behind the resolved origin, when one exists. | Useful when the origin is an item or Active Effect owned by another actor. |
| `user` | The current Foundry user. | Available, but target-selection logic is not a reliable long-lived Active Effect driver. |
| `rollData` | The affected actor's roll data. | Built lazily from the affected actor so ordinary conditions do not do extra prep work. |
| `source` | A cloned snapshot of the current effect data. | Useful when you need raw effect data without mutating the document. |
| `getProperty` | Foundry's property helper. | Useful for deep flag or system reads. |
| `hasProperty` | Foundry's property-existence helper. | Useful when you want a presence check without reading the value first. |
| `deepClone` | Foundry's deep clone helper. | Mostly useful for complex compatibility or debugging logic. |
| `game` | The global Foundry `game` object. | Useful for combat, module checks, user state, or world-level data. |
There is no dedicated native target variable for current target selection.
Target-sensitive rules are usually a better fit for
SC - Conditional Activities than for a long-lived Active Effect.
When Conditions Are Evaluated
Condition timing is the part that most often causes confusion.
| Trigger | What the module does | What it means for your rule |
|---|---|---|
| While editing the effect | The Current evaluation panel runs the condition against the current sheet context. | You can preview whether the rule is true, false, empty, or erroring before saving. |
| During Active Effect application | If the effect is enabled but the condition is false, the module skips applying that effect's changes. | A false condition suppresses the effect instead of partially applying it. |
| When the actor, owned item, or effect data changes | The module schedules an actor refresh, reevaluates conditioned effects, and rerenders effect surfaces. | Actor HP, item state, flags, and similar data-driven rules can turn an effect on or off as data changes. |
| When a previously suppressed effect becomes available | If the effect is still enabled, the module can trigger formula activation and `on` macro execution. | Conditional reactivation is a real activation event for formulas and macro hooks. |
| When current target selection changes | There is no dedicated refresh hook for live target selection changes. | Rules based on `user.targets` can go stale until some actor, item, or effect update forces reevaluation. |
An Active Effect that depends on a live selected target is usually the wrong surface.
Why:
- the native condition context does not build a dedicated target-selection pipeline
targetActorcurrently resolves to the actor affected by the effect- the module refreshes on actor, item, and effect changes, not on every target selection change
That is why an Active Effect usually should not own a rule such as "this bonus works only against my current selected target." That kind of rule belongs to an activity-time workflow instead.
Use this decision table:
| Need | Best tool | Why |
|---|---|---|
| The effect depends on actor HP, combat state, flags, equipped state, or other actor or item data. | SC - Conditional AE | Those values participate naturally in the module's refresh and suppression flow. |
| The rule depends on the currently selected target, chosen usage data, or activity-use context. | SC - Conditional Activities | Activity-side conditions can inspect current use context at the moment of use instead of relying on a long-lived Active Effect refresh. |
| The effect needs a fresh random numeric value when it becomes active. | SC - Conditional AE formula column | Formula-backed changes roll at activation time and store the total in the normal Value field. |
| The effect should trigger follow-up automation when it turns on or off. | SC - Conditional AE macro execution change | Macro changes receive structured on/off context without needing a second automation layer. |
Searchable Condition Examples
Condition Example Finder
Search only within this example set. Use it to filter by actor, item, flag, combat, socket, or GM terms.
Actor Must Be Alive
Allow the effect only while the affected actor has more than 0 HP.
return (actor?.system?.attributes?.hp?.value ?? 0) > 0;Actor Must Be Bloodied
Enable the effect only while the actor is at or below half HP.
const hp = actor?.system?.attributes?.hp?.value ?? 0;
const maxHp = actor?.system?.attributes?.hp?.max ?? 0;
return maxHp > 0 && hp <= maxHp / 2;Actor Must Be Below Half HP
A stricter version that requires the actor to be below half HP, not exactly at half.
const hp = actor?.system?.attributes?.hp?.value ?? 0;
const maxHp = actor?.system?.attributes?.hp?.max ?? 0;
return maxHp > 0 && hp < maxHp / 2;Item Must Be Equipped
Use this when the effect belongs to an item and should only work while that item is equipped.
return item?.system?.equipped === true;Item Must Be Attuned
Gate the effect behind the item's attunement state.
return item?.system?.attunement === 2;Actor Must Have A Minimum Strength Score
Require a minimum Strength score before the effect applies.
return (actor?.system?.abilities?.str?.value ?? 0) >= 16;Actor Must Have A Specific Item
Allow the effect only if the actor owns an item that matches the required name.
const items = actor?.items?.contents ?? [];
return items.some((ownedItem) => ownedItem.name === "Essence Core");Actor Must Have A Specific Active Effect
Require an existing Active Effect with a matching name on the actor.
const effects = actor?.effects?.contents ?? [];
return effects.some((existingEffect) => existingEffect.name === "Rage");Actor Must Have A Specific Flag
Read a custom actor flag through Foundry's property helper.
return getProperty(actor, "flags.world.canUseAncientPower") === true;Only The GM Can Benefit From The Effect
Restrict the effect to the current GM user.
return user?.isGM === true;Effect Must Only Apply During Combat
Allow the effect only while combat exists in the current world state.
return Boolean(game?.combat);At Least One Occupied Socket
Simple Sockets example. Require at least one occupied socket on the owning item.
const sockets = item?.getFlag?.("sc-simple-sockets", "sockets") ?? [];
return sockets.some((slot) => Boolean(slot?.gem || slot?._gemData));At Least Two Occupied Sockets
Simple Sockets example. Require two or more filled sockets on the owning item.
const sockets = item?.getFlag?.("sc-simple-sockets", "sockets") ?? [];
const occupied = sockets.filter((slot) => Boolean(slot?.gem || slot?._gemData));
return occupied.length >= 2;Any Socketed Gem Name Contains Ruby
Simple Sockets example. Match a gem name without relying on async API helpers.
const sockets = item?.getFlag?.("sc-simple-sockets", "sockets") ?? [];
return sockets.some((slot) =>
(slot?.gem?.name ?? slot?._gemData?.name ?? "").toLowerCase().includes("ruby"),
);The Simple Sockets examples use direct flag reads because the native condition engine is synchronous.
Rule Patterns
Rule Patterns and Timing
These examples show not just what to write, but when the rule is evaluated and where the pattern does or does not fit.
Gate The Effect On Actor Or Item State
This is the classic Conditional AE use case: check HP, equipped state, attunement, combat state, flags, or another piece of actor or item data that belongs to the effect context itself.
Owning actor and item data such as HP, equipped state, attunement, flags, and combat-state checks.
Reevaluated during actor refreshes caused by actor, item, or effect document updates.
If the condition is false, the effect stays enabled as a document but its changes are suppressed.
Do not use this pattern when the rule depends on a live selected target.
return item?.system?.equipped === true && (actor?.system?.attributes?.hp?.value ?? 0) > 0;
Roll A Formula When The Effect Comes Online
Use a formula-backed change when the number should be decided at activation time instead of being stored as a fixed value ahead of time.
Variable bonuses or penalties that should roll when the effect activates or becomes available again.
Triggered when the effect activates, re-enables, or becomes available after being condition-suppressed.
The rolled total is written into the standard Value field for that change.
If the effect is turned off and back on later, it can roll again.
Attribute Key: system.attributes.hp.tempmax Mode: Add Value: 0 Formula: 1d4 + 1
Do Not Depend On Current Selected Targets
A rule that keys off whoever is targeted right now is unstable on a long-lived Active Effect because target selection changes do not continuously trigger reevaluation.
Very rare cases. Most target-dependent rules belong on the activity side instead.
This only uses target state from the moment the effect happens to be evaluated.
The effect can appear to follow target selection once, then stay stale until some actor, item, or effect update forces a refresh.
If current target choice matters, move the rule to SC - Conditional Activities.
return user?.targets?.size === 1;
Formula-Backed Changes
When Enable formula column is enabled, eligible non-custom Active Effect changes can store an extra formula alongside the normal value.
Use this when you want the effect to roll at activation time instead of hard-coding the final number.
Example setup:
Attribute Key: system.attributes.hp.tempmax
Mode: Add
Value: 0
Formula: -2d6Typical runtime flow:
| Step | What happens | Notes |
|---|---|---|
| 1. Effect activates | The module checks whether the effect is active and not suppressed. | This also applies when a previously suppressed conditional effect becomes available. |
| 2. Responsible user handles the roll | An active non-GM owner is preferred first; otherwise the active GM handles it. | The module only prompts the responsible user. |
| 3. Formula is confirmed or rolled from chat | The user can roll immediately or from chat if **Post formula roll chat card** is enabled. | The module can show a Foundry roll dialog or a chat-card request flow. |
| 4. Result is written into Value | The rolled total replaces the standard change value for that activation. | The formula remains stored separately as the rule source. |
| 5. Later reactivation can reroll | Disabling and re-enabling the effect, or regaining availability after suppression, can trigger a fresh roll. | Use this only when rerolling later is the intended behavior. |
Macro Execution Changes
To execute a world macro from an Active Effect, add a change with:
- Key:
cae.macro.execute - Mode:
Custom - Value: a macro name or UUID, followed by optional arguments
Example:
Apply Rage "fire" 2That tries to execute the world macro Apply Rage and passes fire and 2 as
arguments.
Macro execution scope:
| Field | What it contains |
|---|---|
| `action` | `on` when the effect activates, `off` when the effect is disabled or deleted. |
| `actor` | The actor that owns the active effect at runtime. |
| `token` | The actor's token object when one can be resolved. |
| `effect` | The current Active Effect document. |
| `item` | The owning or origin item when one can be resolved. |
| `origin` | The resolved origin document when one can be resolved. |
| `change` | The specific Active Effect change row that triggered the macro. |
| `args` | The full macro argument array, starting with `action` and ending with `lastArg`. |
| `macroArgs` | Only the parsed arguments that came after the macro name or UUID. |
| `lastArg` | A DAE-style summary object with actor, token, item, origin, and effect references. |
| `speaker` | The chat speaker object for the actor or token. |
| `user` | The current Foundry user. |
Important runtime notes:
sc-conditional-ae.macro.executeis also supported as a legacy keymacro.executeis supported when DAE is not active- if DAE is active, DAE remains responsible for
macro.execute - if an enabled effect was suppressed and later becomes available again, the
module can trigger the
onmacro from the conditional activation flow - a condition becoming false does not by itself emit an
offmacro unless the effect is also disabled or deleted
Compatibility Notes
| Integration | Behavior |
|---|---|
| DAE condition flags | Reads legacy `flags.dae.enableCondition` and `flags.dae.disableCondition` values and surfaces them through the native Condition tab. |
| DAE expression syntax | Supports compatibility expressions that use `@` roll-data references plus DAE helpers such as `dae.eval(...)` and `dae.roll(...)`. |
| DAE stacking behavior | The **When applied to a target** selector can expose **Same as DAE** when DAE is active. |
| DAE macro changes | Adds `cae.macro.execute` to DAE specials and avoids taking over `macro.execute` when DAE is active. |
| DAE warning text | The module settings UI explicitly documents the possible `libWrapper` console warning as a warning, not a confirmed break. |
| Aura Effects | Registers a fallback Active Effect type for Aura Effects data when that module is not active. |
Troubleshooting
The effect never applies
Check these first:
- Confirm the Condition tab currently resolves to
true. - Verify the code is valid JavaScript.
- Remove any async logic from the native condition.
- If the effect came from DAE, verify the compatibility expression still makes sense for the actor's current data.
The Formula column is missing
- Make sure Enable formula column is on.
- Reload the world after changing that setting.
The macro does not run
- Confirm the change key is
cae.macro.execute. - Confirm the change mode is Custom.
- Make sure the referenced macro exists in world macros.
- Check whether the effect is being suppressed by its condition.
The effect seems stuck to an old target state
That is expected for target-selection-driven rules on long-lived Active Effects.
Check these points:
- Native SC - Conditional AE conditions do not continuously refresh when
user.targetschanges. targetActorcurrently resolves to the affected actor, not to the current selected target.- If the rule depends on the live selected target, move it to SC - Conditional Activities instead.
I see a DAE or libWrapper warning in the browser console
If behavior is otherwise correct, treat that as a warning first. The module explicitly documents this case in its settings UI because both modules touch the Active Effect pipeline when DAE is active.
Notes and Limits
- native JavaScript conditions are synchronous
- empty conditions allow the effect
- thrown errors suppress the effect
- actor, owned item, and effect document updates participate in reevaluation; live target selection changes do not
- formula-backed values are activation-time values, not continuously live formulas
- target- or use-context-driven rules belong on an activity-time condition surface instead of a long-lived Active Effect