API - Effects & Bonuses¶
Apply & Remove¶
applyEffectsToTokens async → Array<Token>
options Object:
| Param | Type | Default | Description |
|---|---|---|---|
inside options |
|||
| tokens | Array<Token> |
required | Targets |
| effectNames | string\|{ name?: string; icon?: string; isCustom?: boolean }\|Array |
required | "prone" or { name, icon, isCustom }. isCustom: true marks a Temporary-Custom-Statuses effect, not a built-in status |
| note | string |
"" |
Flavor note |
| duration | Object |
{} |
{ label, turns, rounds, overrideTurnOriginId } - label is a duration label. When overrideTurnOriginId is set, duration ticks down from that token's turn instead of the target's |
| checkEffectCallback | (token: Token, effectData: object) => boolean |
null |
Dup-check predicate (token, effectData) => boolean. Returning true blocks the apply with a warning |
| notify | Object\|boolean |
true |
Notification config { prefixText, source, whisper } (or true) |
| refresh | boolean |
false |
If the effect already exists, reset its duration instead of blocking. Stack untouched |
extraOptions Object:
{ stack?: number, linkedBonusId?: string, consumption?: object, statDirect?: object, changes?: Array, ...customFlags }
consumption→ Concepts: Consumption.linkedBonusId,statDirect, and any extra...customFlags→ Concepts: Effect flags. Extra keys (e.g.suppressSourceId) are stored as-is inflags['lancer-automations']on each created effect and become removal filters viaextraFlagsinremoveEffectsByNameFromTokens.
Those extra keys are also the effect's identity on apply. An existing effect of the same name only stacks when every one of them matches. A mismatch is treated as a distinct effect and a new one is created.
applyMark async → Promise<Token[]>
findMarkedTokens → Token[]
clearMarks async → Promise<Token[]>
await api.applyMark(sourceToken, targets, { effect, note, duration, flagKey, extraOptions })
api.findMarkedTokens(sourceToken, effectName, { flagKey }) // → Token[]
await api.clearMarks(sourceToken, effectName, { flagKey }) // → Token[] cleared
Params: targets Token[] · effectName string the mark's effect name
Source-stamped effect lifecycle: applyMark applies the effect with { [flagKey]: sourceToken.id } stamped on it, findMarkedTokens scans the scene for tokens carrying it, clearMarks sweeps them all. The Suppress / Engineer's Mark / Sniper's Mark pattern.
See findEffectFrom and findEffectsOnToken for the lookup side.
| Param | Type | Default | Description |
|---|---|---|---|
inside options |
|||
| effect | string\|{ name, icon, isCustom } |
required | What to apply |
| note / duration | string / Object |
"" / indefinite |
Forwarded to applyEffectsToTokens |
| flagKey | string |
'markSourceId' |
Stamp key. Pass a feature-specific one to keep marks independent |
| extraOptions | Object |
{} |
Extra flags stamped alongside |
removeEffectsByName async → void
Single-token version of removeEffectsByNameFromTokens.
| Param | Type | Default | Description |
|---|---|---|---|
| targetID | string |
required | The token ID to remove effects from |
| effectName | string\|{ name: string } |
required | Effect name to match and remove |
| originID | string |
null |
Only remove effects whose stored originID flag matches |
| extraFlags | Object |
null |
Key/value pairs that must ALL match the effect's flags['lancer-automations'] data |
removeEffectsByNameFromTokens async → Array<Token|TokenDocument>
Use deleteEffect instead for one specific effect by ID.
| Param | Type | Default | Description |
|---|---|---|---|
inside options |
|||
| tokens | Array<Token\|TokenDocument> |
required | Tokens to remove from |
| effectNames | string\|{ name?: string; icon?: string; isCustom?: boolean }\|Array |
required | Effect name(s) to match and remove |
| originId | string |
null |
Only remove effects whose stored originID flag matches this value |
| extraFlags | Object |
null |
Key/value pairs that must ALL match the effect's flags['lancer-automations'] data |
| notify | Object\|boolean |
true |
Notification config |
originId and extraFlags are independent filters, both applied when provided.
Example:
deleteEffect async → Promise<void>
Deletes one active effect by object or ID, no name matching. Routes through the GM socket automatically for non-GM users.
| Param | Type | Description |
|---|---|---|
| token | Token\|TokenDocument\|string |
The token (or its ID) that owns the effect |
| effect | ActiveEffect\|string |
The effect (or its ID) to delete |
Example:
deleteAllEffects async → Promise<void>
executeEffectManager async → Promise<void>
await api.deleteAllEffects(tokens) // Removes ALL active effects from the provided tokens
await api.executeEffectManager(options) // Opens the Effect Manager UI
| Param | Type | Description |
|---|---|---|
| tokens | Array<Token\|TokenDocument> |
Tokens to clear (deleteAllEffects) |
executeEffectManager(options) - options: { item?, actor?, forcePrototype?, initialTab? }. The first three pre-select the target (an item's prototype, an actor's active token, or the actor prototype when forcePrototype). initialTab opens on a named tab: 'standard', 'custom', 'bonus', 'manage'.
untilEndOfTurn
untilStartOfTurn → object
currentTurnKey → string|null
api.untilEndOfTurn(token, turns = 1) // → duration object
api.untilStartOfTurn(token, turns = 1)
api.currentTurnKey() // → "round:turn", null out of combat
untilEndOfTurn / untilStartOfTurn build the "until the end of their next turn" duration: the token becomes the turn origin, and the count is bumped by one when it is already that token's turn. Use these instead of hand-writing { label: 'end', turns: 1, rounds: 0 }, which is off by one if applied on the target's own turn.
currentTurnKey stamps "the turn this happened on", so a later turn-end handler can tell whether it is looking at the same turn.
Find & Query¶
findEffectOnToken → ActiveEffect | undefined
First match by name or predicate. The string form uses the house name rules (exact name, custom-status originalName, effect flags, loose includes, status id) and delegates to findEffectsOnToken, which is the one to use for flag filters or all matches.
| Param | Type | Description |
|---|---|---|
| token | Token\|TokenDocument |
The token to search |
| identifier | string\|((e: ActiveEffect) => boolean) |
Effect name (string) or predicate (effect) => boolean |
Example:
findEffectsOnToken → any[]
Every matching effect on one token, same loose name rules as findEffectOnToken. Use it for flag filters or all matches rather than the first.
| Param | Type | Default | Description |
|---|---|---|---|
| token | Token\|TokenDocument |
required | The token to search |
| effectName | string |
required | Name or status id |
inside options |
|||
| extraFlags | Object |
{} |
Module flags that must match exactly |
| hasFlags | string[] |
[] |
Flag keys that must be present, any value |
| excludeId | string |
null |
Effect id to skip, for onStatusRemoved "is any other left?" checks |
findEffectFrom → ActiveEffect | undefined
The originID variant: the effect on token that sourceToken applied, matched on the origin stamp left by addGlobalBonus / applyEffectsToTokens. Use findEffectsOnToken with extraFlags instead when the source was stamped by applyMark's flagKey.
The name match here is exact. The loose house name rules do not apply.
| Param | Type | Description |
|---|---|---|
| token | Token |
The token carrying the effect |
| effectName | string |
Exact effect name |
| sourceToken | Token |
The token that applied it |
hasStatus → boolean
True when any of the given status ids is active.
| Param | Type | Description |
|---|---|---|
| tokenOrActor | Token\|TokenDocument\|Actor |
Whose statuses to read |
| statusIds | ...(string\|string[]) |
Status ids, or arrays of them. Matches if any is present |
Takes status ids ('cover_hard'), not display names. For effects by name, or for flag filters, use findEffectOnToken.
inDangerZone → boolean
Params: tokenOrActor Token|Actor
True when heat is at or above half the heat cap. Same rule the onHeatGain trigger reports as inDangerZone.
getAllEffects → Array<ActiveEffect>
Returns all active effects on the target, including unflagged player-added ones.
| Param | Type | Description |
|---|---|---|
| target | Token\|TokenDocument\|Actor |
The target to inspect |
Charges & Immunity¶
An effect's charges live in a single counter, flags.statuscounter.value. The stack extra option, a bonus's uses, and "charges" here all write and read that one number.
consumeEffectCharge async → boolean
Decrements the counter by 1. If it reaches 0, the effect is deleted. Grouped effects (via consumption.groupId) share a counter and are all deleted together.
| Param | Type | Description |
|---|---|---|
| effect | ActiveEffect |
The effect to consume a charge from |
Returns true if consumed, false for a null or unparented effect and for an effect with no consumption data. A non-GM who does not own the actor gets true as soon as the GM socket request is sent, before the GM has acted on it.
triggerEffectImmunity async → void
Removes the named effects from the token and announces immunity in chat.
| Param | Type | Default | Description |
|---|---|---|---|
| token | Token\|TokenDocument |
required | The immune token |
| effectNames | string\|Array<string> |
required | Effect name(s) to remove |
| source | Item\|string |
"" |
Source of immunity (item or text) |
| notify | boolean |
true |
Post chat notification |
checkEffectImmunities → Array<string>
api.checkEffectImmunities(actor, effectIdOrName, effect, state, tokens)
api.getEffectImmunityBonuses(actor, effectIdOrName, effect, state, tokens)
Returns an array of source names (e.g. ["Immunity Bonus", "Armor Plating"]), empty when the actor is not immune. Test .length: the empty array is truthy. getEffectImmunityBonuses returns the bonus objects instead.
| Param | Type | Default | Description |
|---|---|---|---|
| actor | Actor |
required | The actor to check |
| effectIdOrName | string |
required | Effect ID or name to check immunity for |
| effect | ActiveEffect |
null |
Optional effect object for additional context |
| state | Object |
null |
Optional flow state |
| tokens | Object |
{} |
{ ownerTokenId, otherToken }, the bearer and whoever applied the status |
Called bare it returns the unfiltered list. Most status applications have no applier, a token HUD click included, so applyToCondition is often skipped here.
Global & Constant Bonuses¶
addGlobalBonus async → string (Bonus ID)
Returns undefined if actor is falsy.
options Object:
| Param | Type | Default | Description |
|---|---|---|---|
| duration | string |
'end' in combat |
A duration label. A string here, unlike the effect APIs where duration is an object |
| durationTurns | number |
1 |
Origin turns until the effect ends. See below |
| origin | Token\|TokenDocument\|string |
the actor's token | Whose turns durationTurns counts |
| consumption | ConsumptionConfig |
null |
A Consumption config |
| refresh | boolean |
false |
Re-add an existing bonus id in place: values replaced, linked effect's duration reset instead of blocked |
| icon | string |
per bonus type | Image path for the linked effect, overriding the type's default icon |
| forcePrototype | boolean |
false |
Ignore the scene token and write the effect on the actor itself, so spawned tokens inherit it |
durationTurns counts origin turns until the effect ends:
- 0: next matching trigger. If applied during origin's own turn with duration: "end", ends at end of that same turn. With duration: "start", it ends at start of the next turn. Off the origin's turn, 0 clamps to 1.
- 1: one full origin turn (default). With duration: "end" applied during origin's own turn, ends at end of origin's next turn.
- n ≥ 2: n origin turns.
None of that applies out of combat, or with duration: 'indefinite'. There is no clamping: the effect is created indefinite, with no turn count. The prototype write (no scene token, or forcePrototype) is indefinite too.
bonusData Object:
Core fields (all bonus types)
| Property | Type | Description |
|---|---|---|
| id | string |
Optional custom ID |
| name | string |
Display name |
| type | string |
"accuracy", "difficulty", "damage", "stat", "immunity", "tag", "range", "multi", "target_modifier", "reroll", "movement_extra" |
| val | number\|string |
Value for stat, accuracy, difficulty, tag, or range bonuses |
| uses | number |
Charges. Written to the linked effect's counter, the same one consumeEffectCharge reads |
| consumeOnUsage | boolean |
Burn 1 charge only when the bonus actually applies (still checked at roll time / immunity blocked / reroll accepted). Supported: accuracy, difficulty, damage, target_modifier, reroll, immunity (effect/crit/hit/miss/damage/resistance/provoke/terrain). An immunity charge burns where that immunity is consulted, never on the bearer's own roll, so resistance burns at damage-apply time. Default true, except immunity which defaults false. The Auto-consume on: triggers burn regardless and take precedence. |
| frequency | "round" \| "turn" \| "combat" |
Applies once per round / turn / combat, then stops until the window passes. Gated with the gate API under the key bonus:<id>, burned where the bonus is actually consulted. Out of combat gates never block, so the bonus always applies |
| rollTypes | Array |
["attack"], ["check"], etc. |
| condition | string\|fn |
(state, actor, data, context) => boolean. Per-bonus gate - if false, the whole bonus is skipped. On an immunity, actor is the other party and the owner is context.ownerTokenId. |
| applyToCondition | string\|fn |
(target, state, reactorToken, entry) => boolean. Per-target gate for accuracy, difficulty, target_modifier and every filtered immunity. reactorToken is the owner, target the Token rolled against. On an immunity target is the other party instead, and entry is null. Skipped, not failed, when there is no other party. Must be synchronous. Serialized via @@fn: - survives reloads. |
| itemLids | Array |
LID filters |
| applyTo | Array |
Token ID filters. Static - set at bonus creation. For a dynamic per-target gate, see applyToCondition. |
| applyToTargetter | boolean |
Reverse direction: the bonus sits on the defender and applies to anyone rolling against it, never to the owner's own rolls. condition then runs in the attacker's flow (state.actor is the attacker, the owner is context.ownerTokenId) and applyTo filters attacker token IDs instead. Not for stat or range. How Brace grants its +1 difficulty. |
| tier | 1\|2\|3 |
Gate to an NPC owner tier. Unset = any. Non-NPC owners ignore it |
Immunity fields (type: "immunity")
| Property | Type | Description |
|---|---|---|
| subtype | string |
One of the immunity subtypes |
| effects | Array |
Only for subtype: "effect". List of effect/status names (e.g. ["Prone", "Immobilized"]) |
| damageTypes | Array |
Only for subtype: "damage" or "resistance". List of damage types (e.g. ["Energy", "Kinetic"]). "all" covers every type, and "variable" is treated the same way. Infection is included only while its integration setting is on |
"provoke" acts like permanent DISENGAGE. No extra fields required.
Filters honoured per subtype:
| Subtype | Honoured |
|---|---|
damage, resistance, crit, hit, miss |
rollTypes, itemLids, condition, applyToCondition |
effect, provoke |
condition, applyToCondition |
terrain, obstacle, elevation |
none |
itemLids filters the other party's weapon. applyTo and applyToTargetter are ignored. A filtered resistance is not written to system.resistances, so it no longer shows as a checked resistance on the sheet.
Movement extra fields (type: "movement_extra")
| Property | Type | Description |
|---|---|---|
| subtype | string |
"boost" (default) lengthens every Boost, "standard" lengthens the Standard Move |
| val | number |
Spaces added to each matching move |
Feeds both the ruler bands and the movement cap through
getMovementBands. Use it for standing effects ("your boosts are longer").
For a one-shot on a single move use recordMovementExtra instead. The legacy type id speed_boost_extra is
read as movement_extra with subtype: "boost".
Target modifier fields (type: "target_modifier")
| Property | Type | Description |
|---|---|---|
| subtype | string |
Attack: "invisible", "no_invisible", "no_cover", "soft_cover", "hard_cover". Damage: "ap", "half_damage", "paracausal", "crit", "hit", "miss" |
| applyToCondition | string\|fn |
Per-target gate, see the core fields. Also runs on the damage card and on each toggle. |
"no_invisible" forces plugins.invisibility.data = 0 on the target, bypassing "invisible".
Example - ignore invisibility only within range 3:
await api.addConstantBonus(actor, {
id: 'lesser-sight',
name: 'Lesser Sight',
type: 'target_modifier',
subtype: 'no_invisible',
applyToCondition: (target, state, reactorToken) => {
const api = game.modules.get('lancer-automations')?.api;
return api?.getTokenDistance(reactorToken, target) <= 3
&& target?.actor?.effects?.some(e => e.statuses?.has('invisible'));
}
});
Tag fields (type: "tag")
| Property | Type | Description |
|---|---|---|
| tagName | string |
Name of the custom tag (e.g. "Inaccurate") |
| tagMode | string |
"add" or "override" |
| removeTag | boolean |
If true, negates the tag instead of adding it |
Range fields (type: "range")
| Property | Type | Description |
|---|---|---|
| rangeType | string |
"Range", "Threat", "Line", "Blast", "Burst", "Cone" |
| rangeMode | string |
"add" (default, accepts negative val), "override" (set existing or create), or "change" (replace all ranges with a single entry) |
Reroll fields (type: "reroll")
| Property | Type | Description |
|---|---|---|
| subtype | string |
"retry" (default), "highest", "lowest", or "choose". See resolution table below. |
| rollTypes | Array<string> |
"attackRoll", "techAttackRoll", "damageRoll", "skillRoll", "structureRoll", "stressRoll". Empty = all. |
Offered via a choice card before onRoll fires. Consumed only on Use (Keep leaves the charge).
| Subtype | Resolution after the alt roll runs |
|---|---|
"retry" |
Replace original with the alt (current default behavior). |
"highest" |
Auto-keep max(originalTotal, altTotal). Stacking gives best-of-N+1. |
"lowest" |
Auto-keep min(originalTotal, altTotal). Stacking gives worst-of-N+1. |
"choose" |
Second card prompts Original (X) / Alt (Y) and the user picks. |
Stacking: when an actor has multiple reroll bonuses matching a roll, they fire sequentially, each operating on the current total. Candidates are sorted by subtype priority (retry → highest/lowest → choose). Damage rolls deep-snapshot damage_results/reliable_results/targets, so "keep original" restores the full breakdown.
Multi / Damage fields
| Property | Type | Description |
|---|---|---|
| bonuses | Array |
Only for type: "multi". Array of sub-bonus objects. |
| damage | Array |
Damage bonus. Shape depends on damageMode: [{ type, val }] for add/add_base/replace, [{ from, to }] for change_type (use from: "all" as a fallback catch-all, and specific from types win over "all"). |
| damageMode | string |
"add" (default, adds bonus damage rows in the Bonus Damage section), "add_base" (appends extra damage rows to the weapon's Base Damage), "replace" (weapon's base damage is fully replaced), "change_type" (weapon's damage values kept, types remapped). All modes except add are actor-wide only. applyTo is stripped on save for those. |
| stat | string |
Property path (e.g. system.hp.max) |
| statMode | string |
"add" (default, adds val to the current stat) or "replace" (sets the stat to val). Reversal preserves any damage / healing taken during the effect (delta-based, see statDirect). |
removeGlobalBonus async → boolean
Removes one or more global bonuses from an actor. Also deletes linked active effects unless skipEffectRemoval is true.
| Param | Type | Default | Description |
|---|---|---|---|
| actor | Actor |
required | The actor to modify |
| bonusIdOrPredicate | string\|((bonus) => boolean) |
required | Bonus ID string, or predicate (bonus) => boolean to match multiple |
| skipEffectRemoval | boolean |
false |
If true, keeps the linked active effects |
Example:
getGlobalBonuses → any[]
getGlobalBonus → object|null
const all = api.getGlobalBonuses(actor) // → Array<BonusData> (empty if falsy)
const single = api.getGlobalBonus(actor, bonusId) // → BonusData | null
| Param | Type | Description |
|---|---|---|
| actor | Actor |
The actor to inspect |
| bonusId | string |
The bonus ID (for getGlobalBonus only) |
getGlobalBonuses drops bonuses gated to a tier the NPC owner is not. getGlobalBonus does not: a by-id lookup returns the bonus whatever its tier gate.
addConstantBonus async → Promise<void>
getConstantBonuses → any[]
removeConstantBonus async → Promise<void>
await api.addConstantBonus(actor, bonusData) // same bonusData shape as addGlobalBonus
const bonuses = api.getConstantBonuses(actor) // → Array<BonusData> (empty if falsy)
await api.removeConstantBonus(actor, bonusIdOrPredicate) // string ID or predicate
Constant bonuses are permanent (stored in flags, not linked to an active effect). Auto-generates an id if not provided. getConstantBonuses drops bonuses gated to a tier the NPC owner is not.
| Param | Type | Description |
|---|---|---|
| target / actor | Actor |
The actor to modify/inspect |
| bonusData | Object |
Same shape as addGlobalBonus |
| bonusIdOrPredicate | string\|((bonus) => boolean) |
Bonus ID or predicate (bonus) => boolean |
When the target is an item or a prototype actor, use
linkBonusToItem/linkBonusToActorinstead.
Attach to items and prototype actors¶
Statuses and bonuses can be attached directly to an item or a prototype actor instead of a token. The entry lives on the source doc, and applies to the token's actor on item-add, token-spawn, or re-enable. It's cleaned up on remove / destroy / disable. Charges persist across the cycle.
linkEffectToItem
linkEffectToActor
ensureLinkedEffect async → Array
await api.linkEffectToItem({ items, effectNames, note, duration }, extraOptions)
await api.linkEffectToActor({ actors, effectNames, note, duration }, extraOptions)
await api.ensureLinkedEffect({ items, effectNames, note, duration }, extraOptions) // idempotent
Attaches a status to each source doc. Fires immediately on any active tokens.
| Param | Type | Default | Description |
|---|---|---|---|
inside options |
|||
| items / actors | Array |
required | Source docs |
| effectNames | string\|Object\|Array |
required | Same shape as applyEffectsToTokens |
| note | string |
"" |
Flavor note |
| duration | Object |
{} (permanent) |
{ label, turns?, rounds? } |
extraOptions keys are stored on the source and copied to every effect that comes from it. extraOptions.tier (1-3) gates materialization to NPC owners of that tier.
ensureLinkedEffect is linkEffectToItem that skips effects the item already carries as a template (match = effect name + every extraOptions flag). The onInit way to link: no hand-written guard needed.
unlinkEffectFromItem
unlinkEffectFromActor async → Array
await api.unlinkEffectFromItem({ items, effectName, extraFlags })
await api.unlinkEffectFromActor({ actors, effectName, extraFlags })
Removes the entry from the source doc. Every effect that came from it is removed from active tokens too.
| Param | Type | Default | Description |
|---|---|---|---|
inside options |
|||
| items / actors | Item[] / Actor[] |
[] |
Source docs to unlink from |
| effectName | string |
"" |
Name of the linked effect |
| extraFlags | Object |
null |
Must match the linked entry's flags |
linkBonusToItem
linkBonusToActor
ensureLinkedBonus async → Array
await api.linkBonusToItem({ items, bonusData, addOptions }, extraOptions)
await api.linkBonusToActor({ actors, bonusData, addOptions }, extraOptions)
await api.ensureLinkedBonus({ items, bonusData, addOptions }, extraOptions) // idempotent, needs bonusData.id
Attaches a bonus to each source doc. Applies immediately on active tokens. Each call returns one { item, templateId } (or { actor, templateId }) pair per source doc. Keep the templateId, it is what unlinkBonusFromItem / unlinkBonusFromActor take.
The duration in addOptions decides how it shows up:
'constant': passive, invisible, no token icon (same asaddConstantBonus).- anything else (
'permanent','indefinite','end','start','round'): full bonus with icon, uses / consumption, and turn tracking (same asaddGlobalBonus).
bonusData and addOptions shapes match addGlobalBonus.
ensureLinkedBonus is linkBonusToItem that skips items already carrying a template with the same bonusData.id. The onInit way to link a bonus.
unlinkBonusFromItem
unlinkBonusFromActor async → Array
await api.unlinkBonusFromItem({ items, templateId })
await api.unlinkBonusFromActor({ actors, templateId })
Removes the entry from the source doc. Every bonus that came from it is removed from active tokens too. Charge counts are saved back to the source first, so a re-link picks them back up.
| Param | Type | Default | Description |
|---|---|---|---|
inside options |
|||
| items / actors | Item[] / Actor[] |
[] |
Source docs to unlink from |
| templateId | string |
required | The id of the link itself, from the link* call's return value |
templateId is generated per link, not taken from bonusData.id. Passing bonusData.id matches nothing and the call is a silent no-op. When you no longer hold the return value, read it off the source with getLinkedBonuses.
const [{ templateId }] = await api.linkBonusToItem({ items: [item], bonusData: { id: 'smart-rounds', type: 'accuracy', val: 1 } });
await api.unlinkBonusFromItem({ items: [item], templateId });
const template = api.getLinkedBonuses(item).find(t => t.bonusData?.id === 'smart-rounds');
await api.unlinkBonusFromItem({ items: [item], templateId: template.id });
getLinkedEffects → any[]
getLinkedBonuses → any[]
api.getLinkedEffects(source) // → ActiveEffect[] status templates on Item or Actor
api.getLinkedBonuses(source) // → Object[] bonus templates on Item or Actor
Params: source Item|Actor
Returns the LINKED entries only, not merged with runtime state on the actor. A bonus entry is { id, bonusData, addOptions }, where id is the link's templateId and the bonus you passed in is under bonusData.
Manual apply / cleanup helpers all async
await api.applyItemTemplatesToTokens(item, tokens) // apply the item's statuses to given tokens
await api.applyActorTemplatesToTokens(actor, tokens) // apply the actor's statuses to given tokens
await api.applyItemBonusTemplatesToTokens(item, tokens) // apply the item's bonuses
await api.applyActorBonusTemplatesToTokens(actor, tokens) // apply the actor's bonuses
await api.cleanupItemBonusesFromActor(item, actor) // remove bonuses that came from this item
await api.cleanupActorBonusesFromTokens(actor) // remove bonuses that came from this actor
The lifecycle hooks call these for you. Only reach for them if you need to force a pass from custom code. All safe to call repeatedly - they skip anything already applied.
Args: item/actor source doc. The apply* helpers also take tokens (Array<Token|TokenDocument>). cleanupItemBonusesFromActor(item, actor) and cleanupActorBonusesFromTokens(actor).
Flow State Data Injection¶
During an active flow (attack, check, etc.), triggerData contains a flowState object. Inject ephemeral bonuses or share variables across triggers for the flow's lifespan.
flowState.injectBonus
flowState.injectFlowExtraData
flowState.getFlowExtraData
injectBonusToFlowState async → Promise<void>
The standalone form of flowState.injectBonus, for when you hold the flow state rather than a triggerData. Pushes the bonus onto state.la_extraData.flow_bonus, generating an id if you didn't supply one. Does nothing if state is missing.
| Param | Type | Description |
|---|---|---|
| state | Object |
The flow state, e.g. triggerData.flowState |
| bonus | Object |
Same shape as addGlobalBonus. Gets an id if absent |
Immunity Queries¶
getImmunityBonuses → any[]
checkDamageResistances → string[]
applyDamageImmunities → Array<{ type: string; val: any }>
api.getImmunityBonuses(actor, subtype?, state) // → Array<object>
api.checkDamageResistances(actor, damageType, allowedIds) // → Array<string>
api.applyDamageImmunities(actor, damages, state, bonuses) // → Array<object>
Params: actor Actor | Token · subtype string immunity subtype, omit for all · damageType string · damages Array<{type, val}>
| Function | Description |
|---|---|
getImmunityBonuses |
Returns the actor's immunity bonuses of the given subtype, or every immunity bonus when subtype is omitted. A Token resolves to its actor. Unknown subtypes log a console warning. No filters are applied, so this is the raw list. |
checkDamageResistances |
Source names of the "resistance" subtype bonuses matching the damage type, not the bonus objects. Empty when there is no resistance. allowedIds (string[], default null) keeps only the filtered bonuses whose id is listed, which is how a decision made during the damage flow reaches apply time. null filters nothing. |
applyDamageImmunities |
Takes an array of damage objects {type, val} and returns a copy with the immune types zeroed. Both val and amount are zeroed where present. With no immunities it returns the array it was given, not a copy. bonuses (object[], default null) supplies a prefiltered list instead of re-reading the flags. |
getImmunityBonuses takes state only to fold in flow-scoped bonuses. To apply a bonus's own filters, use one of the three helpers below.
getApplicableImmunityBonuses
getAttackImmunityBonuses
getGateImmunityBonuses → any[]
api.getApplicableImmunityBonuses(actor, subtype, state, { flowType, ownerTokenId, otherToken })
api.getAttackImmunityBonuses(actor, subtype, attackerActor, state, { defenderToken, attackerToken })
api.getGateImmunityBonuses(actor, subtype, { ownerToken, otherToken, state })
getImmunityBonuses minus the bonuses their own filters reject.
| Function | Use |
|---|---|
getApplicableImmunityBonuses |
Inside a flow. Runs every filter. flowType picks the tag set (default "damage"). |
getAttackImmunityBonuses |
crit, hit, miss. Adds flowType: "attack" and rebinds state.actor to the attacker. |
getGateImmunityBonuses |
Outside a flow, for effect and provoke. Runs the two code fields only. |
ownerToken is the bearer and becomes reactorToken. otherToken becomes applyToCondition's target.
Missing context skips a gate rather than failing it. With no context at all, the raw list comes back unfiltered.
hasCritImmunity
hasHitImmunity
hasMissImmunity async → boolean
await api.hasCritImmunity(actor, attackerActor, state, tokens)
await api.hasHitImmunity(actor, attackerActor, state, tokens)
await api.hasMissImmunity(actor, attackerActor, state, tokens)
Returns true if the actor has any applicable immunity bonus of the corresponding subtype. Thin wrappers over getAttackImmunityBonuses, which returns the bonuses themselves when you need to burn the right charge.
| Param | Type | Default | Description |
|---|---|---|---|
| actor | Actor |
required | The actor to check |
| attackerActor | Actor |
null |
The attacker. Without it nothing is filtered and any bonus counts |
| state | Object |
null |
Optional flow state |
| tokens | Object |
{} |
{ defenderToken, attackerToken }. Needed for condition to reach reactorToken and for applyToCondition to run at all |