Skip to content

API Reference

← Back to Home

Documentation Files

File Contents
Automation System How the automation engine works: trigger lifecycle, filters, callbacks, activation modes, sockets, cancel/modify, flow injection, registration, caches
Combat API Combat & execution flows, weapon/item details
Spatial API Distance & grid math, coordinate helpers, faction/disposition, cell data, debug overlays
Effects API Status effect management, global/constant bonuses, immunities, flow state injection
Interactive API Token picker, zones, knockback, choice/vote cards, deployables, thrown weapons, hard cover
Items API Item tags, resource management, activated items, auto-consume config
Flags API Item/token/actor flags, once-per-round/turn gates, flow flags
HUD API Extra actions, action locks, and combat overlays in the Token Action HUD
Movement API Movement tracking, history, movement cap
Token Display API Extra token stat bars
API How-To Registration, user helpers, how-tos, Grid-Aware Auras wrapper
Macros The shipped L.A - macro compendium

Accessing the API

const api = game.modules.get('lancer-automations').api;

Or via hook:

Hooks.on('lancer-automations.ready', (api) => {
});


Fundamentals

Shared Types

Source: scripts/typing/types.d.ts.

CancelFunction → Promise<void>


(reasonText?, title?, allowConfirm?, userIdControl?, preConfirm?, postChoice?, opts?) => Promise<void>
.wait(): Promise<void>

Used by cancelAttack, cancelTechAttack, cancelCheck, cancelAction, cancelChange, cancelStructure, cancelStress, cancelStructureOutcome, cancelStressOutcome. Aborts synchronously, so call it before any await in your activationCode.

Param Type Default Description
reasonText string per-trigger Card description
title string per-trigger Card header
allowConfirm boolean true false cancels with no card
userIdControl string \| string[] \| null null Who sees the card. null = active GM
preConfirm (() => Promise<boolean>) \| null null Asked first. Truthy lets the confirmation card proceed. The ignore path runs only when every registered preConfirm returns falsy
postChoice ((chose: boolean) => Promise<void>) \| null null chose is true if it stayed cancelled
opts { item?, originToken?, relatedToken? } {} Documents shown on the card

The ignore path re-runs the original action. Reactors already in _cancelledBy are skipped on the redo, so a gate is not asked twice. .wait() resolves after the card and any redo.

cancelStructureOutcome and cancelStressOutcome are the exception. They render no Ignore choice, and their ignore path does nothing.

CancelMoveFunction
ChangeMoveFunction → Promise<void>


cancelTriggeredMove(reasonText?, allowConfirm?, userIdControl?, preConfirm?, postChoice?, opts?)
changeTriggeredMove(position: {x: number, y: number}, extraData?, reasonText?, allowConfirm?, userIdControl?, preConfirm?, postChoice?, opts?)

CancelFunction params minus title. A rerouted move is a new move, so reactors may evaluate it again.

ModifyValueFunction
RerollFunction
ChangeRollFunction → Promise<void>


modifyHpChange(newValue: number, reasonText?, allowConfirm?, userIdControl?, preConfirm?, postChoice?, opts?)
modifyHeatChange(newValue: number, ...same)
modifyRoll(newTotal: number) => void
reroll(reasonText?, subtype?, title?, allowConfirm?, userIdControl?, opts?)
changeRoll(newTotal: number, reasonText?, title?, allowConfirm?, userIdControl?, preConfirm?, postChoice?, opts?)

modifyHpChange and modifyHeatChange both expose .wait(). modifyRoll is structure/stress only and shows no card. subtype defaults to 'retry'.

Trailing params behave as on CancelFunction for modifyHpChange, modifyHeatChange and changeRoll. reroll has no positional preConfirm / postChoice slots, pass them inside opts.

ActivationCallback - shape of evaluate and activationCode


(triggerType: TriggerType, triggerData: TriggerData, reactorToken: Token,
 item: Item | null, activationName: string, api: LancerAutomationsAPI) => any

item is null for general activations.

actionData
flowState - the recurring payload objects


actionData field Type
type string ("action" / "attack" / "tech")
title string
action { name: string, activation: string } on "action", { name: string } on "attack" / "tech"
detail string
tags Array<{ lid: string, val?: string }>
attack_type string - "attack" only
deployable { actor: Actor, lid: string } \| null - "action" only
flowState FlowState

flowState is the Lancer flow state: state.data (e.g. data.damage, data.bonus_damage), state.la_extraData, state.injectFlowExtraData(obj), state.getFlowExtraData(), state.actor, state.item.


Trigger Types & Data

Every trigger passes a data object. All objects receive distanceToTrigger and canTriggerReaction (reactor to triggering token), plus _cancelledBy on cancellable triggers. Attack-carrying triggers also get isRangedAttack(): item-less basic attacks report Melee, so past 1 hex from every target they count as ranged.

Tag is { lid: string, val?: string }. ActionData and FlowState are defined above.


Attack Triggers

onInitAttack - attack initiated, before Attack HUD
{
    triggeringToken: Token,
    weapon: Item,
    targets: Array<Token>,
    actionName: string,
    tags: Array<Tag>,
    actionData: ActionData,
    cancelAttack: CancelFunction,
    flowState: FlowState
}
onAttack - attack roll made
{
    triggeringToken: Token,
    weapon: Item,
    targets: Array<Token>,
    attackType: string,
    actionName: string,
    tags: Array<Tag>,
    actionData: ActionData,
    flowState: FlowState
}
onHit - attack hit
{
    triggeringToken: Token,
    weapon: Item,
    targets: Array<{ target: Token, roll: Roll, crit: boolean }>,
    attackType: string,
    actionName: string,
    tags: Array<Tag>,
    actionData: ActionData,
    flowState: FlowState
}
onMiss - attack missed
{
    triggeringToken: Token,
    weapon: Item,
    targets: Array<{ target: Token, roll: Roll }>,
    attackType: string,
    actionName: string,
    tags: Array<Tag>,
    actionData: ActionData,
    flowState: FlowState
}
onPreDamage - once per damage roll, before the damage HUD builds

Mutate triggerData.flowState.data.damage or .bonus_damage to alter base damage types/values before the player rolls.

{
    triggeringToken: Token,
    weapon: Item,
    targets: Array<Token>,
    hitTokens: Array<Token>,
    attackType: string,
    actionName: string,
    tags: Array<Tag>,
    actionData: ActionData,
    cancelDamage: CancelFunction,
    flowState: FlowState
}

cancelDamage aborts the whole damage roll, so no damage card is printed. It is flow-wide, not per target: to spare one target of several, set that entry's hit to false in flowState.data.hit_results instead.

onDamage - damage applied
{
    triggeringToken: Token,
    weapon: Item,
    target: Token,
    damages: Array<number>,
    types: Array<string>,
    isCrit: boolean,
    isHit: boolean,
    attackType: string,
    actionName: string,
    tags: Array<Tag>,
    actionData: ActionData,
    flowState: FlowState
}

Tech Triggers

onInitTechAttack - before Tech HUD
{
    triggeringToken: Token,
    techItem: Item,
    targets: Array<Token>,
    actionName: string,
    isInvade: boolean,
    tags: Array<Tag>,
    actionData: ActionData,
    cancelTechAttack: CancelFunction,
    flowState: FlowState
}
onTechAttack - tech roll made
{
    triggeringToken: Token,
    techItem: Item,
    targets: Array<Token>,
    actionName: string,
    isInvade: boolean,
    tags: Array<Tag>,
    actionData: ActionData,
    flowState: FlowState
}
onTechHit - tech attack hit
{
    triggeringToken: Token,
    techItem: Item,
    targets: Array<{ target: Token, roll: Roll, crit: boolean }>,
    actionName: string,
    isInvade: boolean,
    tags: Array<Tag>,
    actionData: ActionData,
    flowState: FlowState
}
onTechMiss - tech attack missed
{
    triggeringToken: Token,
    techItem: Item,
    targets: Array<{ target: Token, roll: Roll }>,
    actionName: string,
    isInvade: boolean,
    tags: Array<Tag>,
    actionData: ActionData,
    flowState: FlowState
}

Movement Triggers

onPreMove - before movement is finalized
{
    triggeringToken: Token,
    distanceToMove: number,
    elevationToMove: number,
    startPos: { x, y },
    endPos: { x, y },
    isDrag: boolean,
    moveInfo: {
        isInvoluntary: boolean,
        isTeleport: boolean,
        isUndo: boolean,
        isModified: boolean,
        pathHexes: Array<{ x, y, cx, cy, isHistory, hexes }>
    },
    cancel: () => void,
    cancelTriggeredMove: CancelMoveFunction,
    changeTriggeredMove: ChangeMoveFunction
}
onMove - movement completed
{
    triggeringToken: Token,
    distanceMoved: number,
    elevationMoved: number,
    startPos: { x, y },
    endPos: { x, y },
    isDrag: boolean,
    moveInfo: {
        isInvoluntary: boolean,
        isTeleport: boolean,
        pathHexes: Array<Object>,
        isFreeMovement: boolean,
        movementCost: number,
        isModified: boolean,
        extraData: Record<string, any>
    },
    moveLeg: {
        start: string | null,
        end: string | null,
        boosted: boolean,
        granted: boolean,
        spentBefore: number,
        spentAfter: number
    } | null
}

moveLeg names the movement band the leg started and ended in (standard, boost, over-boost), with the movement spent either side. It is null out of combat and on free movement.

onInvoluntaryMove - before each involuntary per-token move, cancellable
{
    triggeringToken: Token,
    token: Token,
    distance: number,
    actionName: string,
    item: Item,
    destination: { x: number, y: number },
    cancel: (reason?: string) => void
}
  • cancel(reason?) synchronously skips this specific token's move. Other tokens in the batch still proceed.
  • Does not fire when knockBackToken() is called with { asVoluntary: true } - in that mode the move goes through onPreMove/onMove like a regular drag.
  • actionName and item are passed from the caller (e.g. "Grapple"), used by onlyOnSourceMatch.

Deployment & Placement Triggers

onDeploy - deployable or weapon token placed on the map
{
    triggeringToken: Token,
    item: Item,
    deployedTokens: Array<TokenDocument | Token>,
    deployType: "deployable" | "throw",
    distanceToTrigger: number,
    canTriggerReaction?: boolean
}

deployedTokens holds TokenDocuments from the throw and deploy flows, but the manual drop path passes canvas Tokens. Read through .document ?? entry if you need one shape.


Turn Events

onTurnStart / onTurnEnd
{ triggeringToken: Token }
onRoundStart - once at the start of every round, including round 1
{ combat: Combat, round: number }
onEnterCombat / onExitCombat - token added to / removed from the combat tracker
{ triggeringToken: Token }

Status Effect Triggers

onPreStatusApplied - before a status is applied (non-async evaluate only)
{
    triggeringToken: Token,
    statusId: string,
    effect: ActiveEffect,
    cancelChange: CancelFunction
}
onPreStatusRemoved - before a status is removed (non-async evaluate only)
{
    triggeringToken: Token,
    statusId: string,
    effect: ActiveEffect,
    cancelChange: CancelFunction
}
onStatusApplied / onStatusRemoved
{
    triggeringToken: Token,
    statusId: string,
    effect: ActiveEffect
}

Structure & Stress Triggers

onPreStructure - before the structure roll, can cancel the flow
{
    triggeringToken: Token,
    remainingStructure: number,
    cancelStructure: CancelFunction,
    flowState: FlowState
}
onStructure - after the structure roll
{
    triggeringToken: Token,
    remainingStructure: number,
    rollResult: number,
    rollDice: number[],
    cancelStructureOutcome: CancelFunction,
    modifyRoll: (newTotal: number) => void,
    flowState: FlowState
}
onPreStress - before the overheat roll, can cancel the flow
{
    triggeringToken: Token,
    remainingStress: number,
    cancelStress: CancelFunction,
    flowState: FlowState
}
onStress - after the overheat roll
{
    triggeringToken: Token,
    remainingStress: number,
    rollResult: number,
    rollDice: number[],
    cancelStressOutcome: CancelFunction,
    modifyRoll: (newTotal: number) => void,
    flowState: FlowState
}
onRoll - between a roll resolving and its chat card printing

Fires for attackRoll, techAttackRoll, damageRoll, skillRoll, structureRoll, stressRoll.

{
    triggeringToken: Token,
    rollType: string,
    roll: Roll,
    total: number,
    success: boolean,
    targets: Array<Object> | undefined,
    item: Item,
    isReroll: boolean,
    rerollCount: number,
    reroll: RerollFunction,
    changeRoll: ChangeRollFunction,
    flowState: FlowState
}
  • reroll() re-runs the Lancer flow step that produced the roll. changeRoll(newTotal) sets the total (and recomputes hit/crit for attack flows). Both cascade: after either call, onRoll re-fires so later reactions see the new state.
  • No engine-level reroll cap. Reactions that reroll should gate themselves via api.setFlowFlag(triggerData, '_myReactionRerolled') + api.getFlowFlag in evaluate.
  • success rule: attack/tech = any hit, skill = total >= 10, damage/structure/stress = undefined.
  • targets is undefined (not an empty array) on skillRoll, structureRoll and stressRoll.
  • changeRoll on structure/stress only updates roll._total (title/desc stay stale, prefer reroll()).
onDestroyed - token delete when structure.value <= 0 || stress.value <= 0

triggeringToken may be a fallback { document, id, name, actor } object if the canvas token is already gone.

{ triggeringToken: Token }
onTokenCreated - any token placed on the canvas (100ms delay, same timing as onInit)
{
    triggeringToken: Token,
    distanceToTrigger: number,
    canTriggerReaction: boolean
}
onTokenRemoved - any token deletion (unconditional, unlike onDestroyed)

Fires before the token leaves canvas.tokens, and not at all if it is already gone.

{
    triggeringToken: Token,
    distanceToTrigger: number,
    canTriggerReaction: boolean
}
onTokenVisibility - token hidden flag toggled (GM eye icon)
{
    triggeringToken: Token,
    isHidden: boolean,
    distanceToTrigger: number,
    canTriggerReaction: boolean
}

HP & Heat Triggers

onPreHpChange - before HP changes, can cancel or modify the value
{
    triggeringToken: Token,
    previousHP: number,
    newHP: number,
    delta: number,
    cancelHpChange: CancelFunction,
    modifyHpChange: ModifyValueFunction
}
onHpGain - after HP increases
{
    triggeringToken: Token,
    hpChange: number,
    currentHP: number,
    maxHP: number
}
onHpLoss - after HP decreases
{
    triggeringToken: Token,
    hpLost: number,
    currentHP: number
}
onPreHeatChange - before heat changes, can cancel or modify the value
{
    triggeringToken: Token,
    previousHeat: number,
    newHeat: number,
    delta: number,
    cancelHeatChange: CancelFunction,
    modifyHeatChange: ModifyValueFunction
}
onHeatGain - after heat increases
{
    triggeringToken: Token,
    heatChange: number,
    currentHeat: number,
    inDangerZone: boolean
}
onHeatLoss - after heat decreases
{
    triggeringToken: Token,
    heatCleared: number,
    currentHeat: number
}

Stat & Activation Triggers

onInitCheck - before the check roll
{
    triggeringToken: Token,
    statName: string,
    checkAgainstToken: Token,
    targetVal: number,
    item: Item | null,
    actionName: string | null,
    cancelCheck: CancelFunction,
    flowState: FlowState
}

item / actionName are null unless the caller stamped sourceItemUuid / sourceAction.

onCheck - check result
{
    triggeringToken: Token,
    statName: string,
    roll: Roll,
    total: number,
    success: boolean,
    checkAgainstToken: Token,
    targetVal: number,
    item: Item | null,
    actionName: string | null,
    flowState: FlowState
}
onInitActivation - before item/action activates, before resource use (non-async evaluate only)
{
    triggeringToken: Token,
    actionType: string,
    actionName: string,
    item: Item,
    actionData: ActionData,
    deployable: { actor: Actor, lid: string } | null,
    endActivation: false,
    cancelAction: CancelFunction,
    flowState: FlowState
}
onActivation - item/action fired

extraData carries anything injected via startRelatedFlowToReactor / flow-state injection. Profile switches fire it with the profile name. Mod activations carry extraData.hostWeapon.

Ending an activation fires onEndActivation instead, never this trigger.

reactionJustConsumed is true when this activation is what spent the actor's Reaction, which is how a self-reactor with checkReaction on still passes its own gate here.

{
    triggeringToken: Token,
    actionType: string,
    actionName: string,
    item: Item,
    actionData: ActionData,
    deployable: { actor: Actor, lid: string } | null,
    reactionJustConsumed: boolean,
    endActivation: false,
    extraData: Record<string, any>,
    flowState: FlowState
}
onInitEndActivation - before an activation is ended (non-async evaluate only)

Fires when the end action from setItemAsActivated runs, in place of onInitActivation. cancelAction only suppresses the end card, the item is already marked inactive by then.

{
    triggeringToken: Token,
    actionType: string,
    actionName: string,
    item: Item,
    actionData: ActionData,
    deployable: { actor: Actor, lid: string } | null,
    endActivation: true,
    cancelAction: CancelFunction,
    flowState: FlowState
}
onEndActivation - an activation was ended

Fires when the end action from setItemAsActivated runs, in place of onActivation. This is where teardown belongs.

{
    triggeringToken: Token,
    actionType: string,
    actionName: string,
    item: Item,
    actionData: ActionData,
    deployable: { actor: Actor, lid: string } | null,
    reactionJustConsumed: boolean,
    endActivation: true,
    extraData: Record<string, any>,
    flowState: FlowState
}
onPostActivation - after the onActivation sweep

Fires once every onActivation reaction has run, with the same payload plus results: whatever each awaited reaction returned from activationCode, keyed by reaction name. A reaction that returns nothing is absent. Two reactors firing the same name give an array.

Not in results: popup activations, which resolve later, and reactions with awaitActivationCompletion: false. This trigger only reads what happened, it cannot cancel or change the activation.

{
    triggeringToken: Token,
    actionType: string,
    actionName: string,
    item: Item,
    actionData: ActionData,
    deployable: { actor: Actor, lid: string } | null,
    reactionJustConsumed: boolean,
    endActivation: false,
    extraData: Record<string, any>,
    flowState: FlowState,
    results: Record<string, any>
}
onPostEndActivation - after the onEndActivation sweep

Same as onPostActivation for the end action, with endActivation: true.

onUpdate - token document update, after it settles (movement animation done, final ruler segment only)
{
    triggeringToken: Token,
    document: TokenDocument,
    change: Record<string, any>,
    options: Record<string, any>,
    distanceToTrigger: number | null
}

Fires for any token update, so gate on change. For movement that means x, y or elevation present. The hook waits for movementAnimationPromise and skips non-final ruler segments, so distanceToTrigger is the settled end-of-move distance. Engagement runs on this trigger.


Custom Triggers

dispatchCustomTrigger → Promise<void> - fire your own trigger name
api.dispatchCustomTrigger(name, data)
Param Type Description
name string Not a built-in name, not onInit*
data object Becomes triggerData. Add triggeringToken for the self/other, disposition and distance filters

Listen via the editor's Custom field or triggers: ["myTrigger"]. Always fires, in or out of combat. Not cancellable, no consumption.


Callback Signatures

Shared params: triggerType: TriggerType, triggerData: TriggerData, reactorToken: Token, item: Item | null (null for general activations), activationName: string, api: LancerAutomationsAPI.

Callback Signature Returns
evaluate (triggerType, triggerData, reactorToken, item, activationName, api) boolean - must be synchronous on cancellable triggers
activationCode (triggerType, triggerData, reactorToken, item, activationName, api) Promise<void>
onInit (token: Token, item: Item, api: LancerAutomationsAPI) Promise<void> - runs when a token carrying the item is created
onMessage (triggerType, data: any, reactorToken, item, activationName, api) Promise<void> - runs on the client targeted by sendMessageToReactor
debugActivation → Object


triggerData.debugActivation(label?)
api.debugActivation(triggerType, triggerData, reactorToken, item, activationName, label?)

Console-logs everything the current callback received, including the helper functions the trigger provides, and returns the same summary object. Available in evaluate and activationCode.

Param Type Default Description
label string activation name Heading on the console group

Debug mode and breakpoints: Automation Engine - Debugging an automation.


Concepts

Consumption

Charge-consumption config attached to an effect. Pass it as extraOptions.consumption on applyEffectsToTokens, or options.consumption on addGlobalBonus.

The charge count is not part of the config, it sits beside it: extraOptions.stack on applyEffectsToTokens, bonusData.uses on addGlobalBonus.

consumeEffectCharge(effect) decrements the effect's statuscounter on each matching trigger and deletes the effect at 0. grouped / groupId make several effects share one counter (deleted together).

Field Type Default Description
trigger TriggerType \| TriggerType[] required Trigger(s) that consume a charge
originId string bearer token id Only consume if this token is involved
role "source" \| "target" either How the origin must be involved: caused the trigger, or was one of its targets
grouped boolean false Share one counter across all effects in this call (auto-fills groupId)
groupId string auto Shared counter id across calls
evaluate (triggerType: TriggerType, data: TriggerData, token: Token, effect: ActiveEffect) => boolean null Extra gate
itemLid string - Only consume for this item source. Comma-separated for several LIDs
itemId string - Only consume for this exact item document id
actionName string - Only consume for this action name, e.g. "Boost"
minDistance number - Only consume if distanceMoved is at least this. Movement triggers only
checkType string - Only consume for this roll title, e.g. "AGI Check" or "AGI Save (>= 12)". Matched whole against the card title, not a stat name
checkAbove number - Only consume if the roll total is at or above this
checkBelow number - Only consume if the roll total is at or below this
statusId string - Status triggers only. Only consume for this status id. Comma-separated for several

Resistance that lasts 3 hits (Dispersal Shield). One counter shared by all three resistance effects, so they vanish together on the third hit:

await api.applyEffectsToTokens({
    tokens: [target],
    effectNames: ["resistance_kinetic", "resistance_energy", "resistance_explosive"],
    note: "Dispersal Shield"
}, {
    stack: 3,
    consumption: { trigger: "onDamage", originId: target.id, grouped: true }
});

+1 accuracy on the next attack only, spent when it hits:

await api.addGlobalBonus(target.actor, { name: "Squad Leader", val: 1, type: "accuracy", rollTypes: ["attack"] },
    { duration: "1 Round", origin: reactorToken, consumption: { trigger: "onHit" } });

Only spend on a failed AGI check, using the gate:

consumption: {
    trigger: "onCheck",
    checkType: "AGI Check",
    evaluate: (triggerType, data, token, effect) => data.success === false
}

One die, several possible uses (Leadership die). Bonuses added in separate calls share one groupId: whichever consumes first removes them all.

const groupId = foundry.utils.randomID();

await api.addGlobalBonus(actor, { name: "Leadership (Accuracy)", type: "accuracy", val: 1, rollTypes: ["attack"] },
    { consumption: { trigger: "onAttack", groupId } });

await api.addGlobalBonus(actor, { name: "Leadership (Damage)", type: "damage", damage: [{ val: "1d6", type: "Kinetic" }] },
    { consumption: { trigger: "onDamage", groupId } });

addGlobalBonus copies only the trigger filters. It drops originId, role, grouped and statusId, silently. Without originId the engine falls back to the bearer token at trigger time, so each bearer consumes on its own. When the bearer can also be a target of the trigger, gate with evaluate, e.g. (t, data, bearer) => data.triggeringToken?.id === bearer.id.

Reaction economy

Two separate keys, both opt-in, at different scopes: - checkReaction (reaction config, default off) - the availability gate, opt-in per activation. When set, the reaction is skipped if the reactor has no reaction left this round. - consumeReaction (world setting, default off) - what spends a reaction, opt-in per world: when a Reaction-type action fires, it decrements system.action_tracker.reaction by 1.

A reactor that just spent its own reaction still passes its own checkReaction gate on that same trigger, so an activation can react to the action that consumed it.

Effect flags

Every effect this module creates stores its metadata under flags['lancer-automations']. Any extra keys you pass in extraOptions (beyond reserved meta keys like stack / consumption / changes) are copied there as-is: extraFlags on removeEffectsByName* deletes an effect only if ALL supplied keys equal the stored values.

Two keys the module manages itself: - linkedBonusId - ties an effect to a bonus so removing one removes the other. - statDirect - stat-reversal metadata { key, value, preBonusValue } used to restore a current-resource stat by its delta when the effect ends.

extraData / la_extraData

Ad-hoc state that round-trips through a flow. Pass it in (startRelatedFlowToReactor(userId, extraData), or flowState.injectFlowExtraData(extraData) mid-flow). It is merged onto state.la_extraData and resurfaces as triggerData.extraData on the downstream onActivation. Read it back inside a flow with flowState.getFlowExtraData().

Immunity subtypes

Immunity bonuses (type: "immunity") carry exactly one subtype. The engine only recognises these values. All resolve through getImmunityBonuses(actor, subtype):

Subtype Checked by Extra fields
effect checkEffectImmunities effects: [names]
damage applyDamageImmunities damageTypes: [types], "all" for every type
resistance checkDamageResistances (halves) damageTypes: [types], "all" for every type
crit hasCritImmunity -
hit hasHitImmunity -
miss hasMissImmunity -
elevation isClimbingImmune (movement) -
terrain isTerrainImmune (terrain / zones) -
obstacle isPhasing (move through other characters) -
provoke engagement + reaction gate -

Duration labels

Accepted duration.label values: - start / end / round - tick down at turn start, turn end, or round change. Only these expire by time, and only in combat. - indefinite - never expires by time. unlimited is a retired alias, still accepted and normalized to indefinite. - permanent - never expires by time and survives a Full Repair. - constant - bonus only: passive and invisible, no token icon or counter (same as addConstantBonus).

Stat codes

HASE-plus-grit keys used by stat rolls and checks: HULL, AGI, SYS, ENG, GRIT.


Activation Object Structure

One entry in an activation group's reactions array. Interface: ReactionConfig in types.d.ts.

Field Type Default Description
triggers (TriggerType \| string)[] required Trigger names this entry listens to. Any non built-in name is a custom trigger
name string "" Display name in the manager, and the key that matches an entry to its saved user settings
enabled boolean true Master toggle
awaitActivationCompletion boolean true Required to intercept onPreMove, onInitActivation, onInitEndActivation, onInitAttack, onInitTechAttack, onInitCheck. The engine tests !== false, so a code-registered entry awaits unless you opt out. The manager's checkbox writes an explicit value and starts unchecked
triggerDescription string "" Header text on the activation card
effectDescription string "" Body text on the activation card
comments string "" Author notes. Never shown in play
actionType "Automation" \| "Reaction" \| "Free Action" \| "Quick Action" \| "Full Action" \| "Protocol" \| "Other" "Automation" Lancer action type. "Reaction" is what spends a reaction
frequency string "" Display-only text
triggerSelf boolean false React to own actions
triggerOther boolean true React to others' actions (includes targets)
triggerTarget boolean false React when the reactor is one of the event's targets, even with triggerOther off. Target-capable triggers only
checkReaction boolean false Skip if the reactor has no Reaction left this round. Plain truthy check, so omitting it leaves the gate off
requireCanProvoke boolean false Skip unless the trigger source can provoke the reactor (engagement, provoke immunity). Used by Overwatch
checkUsage boolean false Item entries only. Skip when the item is unloaded, uncharged, out of uses, or past its tg_turn / tg_round limit
isReaction boolean false Marks the entry as a reaction in the manager UI
outOfCombat boolean false Also fire outside combat. Bypassed for onEnterCombat, onExitCombat, onTurnStart, onTurnEnd, onRoundStart and custom triggers, which always fire
onlyOnSourceMatch boolean false Match by name (general) or by possession (item)
dispositionFilter Array<"hostile" \| "friendly" \| "neutral" \| "secret"> [] Restrict by disposition toward the trigger
reactionPath string "" Action path, e.g. extraActions.Print. Also gates availability: ranks[N] needs the talent at rank N+1, profiles[N] needs that weapon profile selected
evaluate ActivationCallback \| string - Gate. Must be synchronous on cancellable triggers
activationType "code" \| "macro" \| "flow" \| "none" "flow" What runs
activationMode "instead" \| "after" item: "instead", general: "after" after also fires the reaction's own flow/card. Macro/code only
sceneReactor "off" \| "add" \| "only" "off" General only. Evaluate once as the active scene on the GM client. add keeps the per-token passes, only replaces them
sceneId string "" General only. Limit the activation to this scene id, empty for every scene
activationCode ActivationCallback \| string - The body, for activationType: "code"
activationMacro string "" Macro name, for activationType: "macro"
autoActivate boolean false Skip the popup and run immediately
onInit ((token, item, api) => Promise<void>) \| string - Runs on token creation. Scene reactors: also on scene load, token is the scene stand-in
onMessage ((triggerType, data, reactorToken, item, activationName, api) => Promise<void>) \| string - Runs on the client targeted by sendMessageToReactor

The group wrapper (ReactionGroup) is { category?: string, itemType?: string, enabled?: boolean, reactions: ReactionConfig[] }.

A whole group, as registered by an item LID:

api.registerDefaultItemReactions({
    "npcf_suppress_archer": {
        category: "NPC",
        itemType: "npc_feature",
        reactions: [{
            triggers: ["onActivation"],
            onlyOnSourceMatch: true,
            triggerSelf: true,
            autoActivate: true,
            outOfCombat: true,
            actionType: "Quick Action",
            activationType: "code",
            activationMode: "instead",
            activationCode: async function (triggerType, triggerData, reactorToken, item, activationName, api) {
                const targets = await api.chooseToken(reactorToken, { range: 10, count: 1 });
                if (targets?.length)
                    await api.applyMark(reactorToken, targets, { effect: "impaired" });
            }
        }]
    }
});

A setup-only entry (no trigger, runs once per token) uses triggers: [] with activationType: "none" and an onInit.