API - Token Action HUD¶
Back to API Reference · Feature guide: Token Action HUD
Extra Actions¶
Everything here shows up in the TAH action menu.
getItemActions → object[]
getActorActions → object[]
getLinkedActions → any[]
api.getItemActions(item, opts?) // → Object[] (system + profile actions + extras)
api.getActorActions(target) // → Object[] (extras only)
api.getLinkedActions(target) // → Object[] (same function as getActorActions)
| Param | Type | Description |
|---|---|---|
| target | Item\|Token\|Actor |
Item reads itself. Token/Actor reads the actor |
| opts.extraOnly | boolean |
getItemActions only. Return just the extras, skipping system and profile actions |
getItemActions merges system.actions, the weapon's active_profile.actions and the item's extras, folds action overlays into the first two, and drops exact duplicates matched on name + activation + detail. getActorActions and getLinkedActions only read the extraActions flag, and are the same function under two names.
addExtraActions async → Promise<any>
removeExtraActions async → Promise<void>
await api.addExtraActions(target, actions, opts?) // add to Item, Token, or Actor
await api.removeExtraActions(target, filter?) // string name, predicate, or null (clear all)
| Param | Type | Description |
|---|---|---|
| target | Item\|Token\|Actor |
Item stores on itself. Token/Actor stores on the actor |
| actions | ExtraAction\|ExtraAction[] |
One action or an array |
| opts.grant | ActionGrant |
Stamped as _grant on every entry that carries none of its own. See Granted actions |
| filter | Function\|string\|string[]\|null |
Predicate, name, array of names, or null (clear all) |
addExtraActions skips any entry whose name and grantor already exist on the target, so an onInit that adds the same action every time is idempotent and stored charge state survives. Two different grantors of the same action name stay separate entries, each revocable on its own.
ExtraAction shape (LancerAction + extras):
| Field | Type | Notes |
|---|---|---|
name |
string |
Required |
activation |
string |
Required. "Quick" / "Full" / "Protocol" / "Reaction" / "Free" / "Quick Tech" / "Full Tech" / "Invade" |
detail |
string |
HTML effect text |
lid, cost, heat_cost, frequency, init, trigger, terse |
various | Standard LancerAction fields |
tech_attack |
boolean |
Routes click through beginTechAttackFlow |
damage, range |
Array<{val,type}> |
Same shape as system actions. Consumed in combat mode |
tags |
Array<{lid,val}> |
Standard Lancer tags. Weapon tags (tg_smart etc.) coexist with consumable tags |
icon |
string |
TAH icon override (path or FontAwesome class) |
ignoresLineOfSight |
boolean |
Drops the sight check from the range pulse. Every range is sight bound without it, Sensor included |
condition |
(actor, action, item) => string\|boolean |
Runtime gate, see Conditional actions |
_grant |
ActionGrant |
Grantor stamp, see Granted actions. Set it through opts.grant, not by hand |
recharge, charged |
number, boolean |
Charge state for tg_recharge actions |
loaded |
boolean |
Charge state for tg_loading actions |
uses |
{value,max} |
Charge state for tg_limited actions |
usesPerTurn |
{value,max} |
Charge state for tg_turn actions. Resets at the owner's turn start |
usesPerRound |
{value,max} |
Charge state for tg_round actions. Resets at round start |
tier |
1\|2\|3 |
Gate to an NPC owner tier. Unset = any tier. Non-NPC owners ignore it |
laCombat |
'attack'\|'damage' |
Turn the action into an attack or damage roll (see below). Absent = plain card |
accuracy, difficulty, attack_bonus |
number |
Combat attack: flat accuracy/difficulty dice + flat to-hit bonus |
attack_type |
'Melee'\|'Ranged' |
Combat attack: melee vs ranged |
Item-held actions appear under their item in the TAH menu, actor-held actions in the actor's action list. No refresh needed.
laCombat mode: stays in its activation column. Clicking prints the card then fires executeExtraActionCombat. 'attack' rolls a to-hit (weapon tags apply, tg_smart = E-DEF, Invade/Quick Tech/Full Tech = tech attack at Sensors). 'damage' rolls damage with no to-hit.
Auto-behaviors when target is an Item:
- _sourceItemId is stamped onto every added action so onlyOnSourceMatch reactions can resolve the parent item.
- If the action carries a consumable tag (tg_loading / tg_recharge / tg_limited) that's already on the parent item, that tag is stripped from the action along with its state field (loaded / charged+recharge / uses). A warning is shown. Item-level state stays authoritative.
Example:
await api.addExtraActions(myItem, { name: "Suppressive Fire", activation: "Quick", detail: "..." });
await api.removeExtraActions(myToken, "Custom Strike");
await api.addExtraActions(actor, { name: "Plasma Lance", activation: "Quick", laCombat: "attack",
tags: [{ lid: "tg_smart" }], damage: [{ val: "2d6", type: "Energy" }], range: [{ type: "Range", val: 10 }] });
Conditional actions - the condition gate
| Param | Type | Description |
|---|---|---|
| actor | Actor\|null |
The holder |
| action | any |
The stored entry, _grant included |
| item | Item\|null |
Holding item, null when actor-held |
| Return | Row |
|---|---|
'hidden' / false |
not shown |
'locked' |
grey, same as lockActorAction |
'disabled' |
yellow, same as disableActorAction |
'visible' / anything else |
normal |
Runs once per row each time the HUD is built, and must be synchronous. If it throws, the error is logged and the row shows as normal.
Saved as @@fn: text and rebuilt when read, so it cannot use variables from outside itself. Read what it needs from actor, action and item.
resolveGrant → { token, actor, item } | null
findGrantedAction → { action, token, actor, item } | null
isGrantStale → boolean
sweepStaleGrants async → Promise<void>
api.resolveGrant(actionOrGrant)
api.findGrantedAction(holder, name)
api.isGrantStale(action)
await api.sweepStaleGrants({ tokenId, itemId })
| Param | Type | Description |
|---|---|---|
| actionOrGrant | any |
An action carrying _grant, or a grant on its own |
| holder | Item\|Token\|Actor |
Whose sheet carries the action |
| name | string |
The action's name |
| action | any |
The stored entry, for isGrantStale |
| tokenId / itemId | string\|null |
What sweepStaleGrants removes grants for |
opts.grant on addExtraActions marks an action with who gave it.
ActionGrant
| Field | Type | Description |
|---|---|---|
| tokenId | string |
Token that gave the action. Used to take it back |
| actorUuid | string |
Actor that gave it |
| itemId | string |
Item that gave it |
| sceneId | string\|null |
Set from the current scene when absent |
| label | string |
Text for display |
resolveGrant takes an action carrying _grant, or a grant on its own. findGrantedAction finds the action on the holder by name first.
isGrantStale is checked when a row is read. sweepStaleGrants deletes the entries, and runs on the GM client on deleteToken and deleteItem. Use condition for range, adjacency and resource checks.
consumeExtraAction async → Promise<boolean>
reloadExtraAction async → Promise<void>
rechargeExtraActionsForActor async → Promise<void>
resetPerRoundExtraActionsForActor async → Promise<void>
await api.consumeExtraAction(target, actionName)
await api.reloadExtraAction(target, actionName)
await api.rechargeExtraActionsForActor(actor)
await api.resetPerRoundExtraActionsForActor(actor)
Charge plumbing for extras with tg_loading / tg_recharge / tg_limited / tg_turn / tg_round tags. consume decrements / spends every state the entry's tags call for, returns false if any of them is depleted. reload resets them all. recharge rolls 1d6 vs entry.recharge per uncharged entry and refills usesPerTurn, and fires on turn start. resetPerRound refills usesPerRound, and fires at round start. Both sweeps cover the actor's own extras and its items'.
| Param | Type | Description |
|---|---|---|
| target | Item\|Actor |
Holder of extraActions flag |
| actionName | string |
Matches action.name |
| actor | Actor |
Sweep target |
lockActorAction async → Promise<any>
unlockActorAction async → Promise<any>
isActionLocked → boolean
getLockedActions → string[]
await api.lockActorAction(item, actionName, { reason? }) // lock held BY the item
await api.lockActorAction(actor, actionName, sourceId, { reason? }) // manual actor lock
await api.unlockActorAction(target, actionName, sourceId?) // sourceId only for actor locks
api.isActionLocked(actor, actionName) // → boolean (manual + item locks, disabled included)
api.getLockedActions(actor) // → string[]
Both readers count disable* entries alongside locks, and neither sees activation-type locks.
| Param | Type | Description |
|---|---|---|
| target | Item\|Actor\|Token |
Item: lock lives on the item - off while destroyed/disabled, gone when removed, back on repair. Actor: source-tracked manual lock. |
| actionName | string |
Standard action display name ("Boost", "Grapple", ...), or a weapon's name to grey that weapon's rows |
| sourceId | string |
Actor locks only. Stays locked until every source is removed. |
inside opts |
||
| reason | string |
Optional. Shown in the popup's "Locked by:" line (item locks default to the item name). |
The third argument takes the sourceId string or an options object (sourceIdOrOpts). The trailing kind is an internal lock category used by the status system - leave it unset.
Locked actions are grayed in TAH. The action popup names the locker (status, item, or reason). Locking a weapon (item target, actionName = the weapon's name) grays the weapon row and its FIGHT / SKIRMISH / BARRAGE / ATTACK entries, with the reason in the weapon popup.
lockActorActionTypes async → Promise<any>
unlockActorActionTypes async → Promise<any>
await api.lockActorActionTypes(target, activationTypes, sourceIdOrOpts?, opts?)
await api.unlockActorActionTypes(target, activationTypes?, sourceId?)
Locks by activation type rather than by name, so one call greys every Quick or Full action at once. Same target rules as lockActorAction: an item target holds the lock itself, an actor target is source-tracked.
| Param | Type | Default | Description |
|---|---|---|---|
| target | Item\|Actor\|Token |
required | Item holds the lock, actor is source-tracked |
| activationTypes | string\|string[] |
required | Types to lock, e.g. "Quick" / "Full". "*" locks every type |
| sourceIdOrOpts | string\|Object |
null |
Source id for later removal, or the options object itself |
| opts.reason | string |
null |
Shown in the popup's "Locked by:" line |
| opts.except | string[] |
[] |
Action names the lock skips |
On unlock of an actor target, both activationTypes and the same sourceId are required. Omitting either errors and unlocks nothing. Only an Item target may omit them, which drops every type lock the item holds.
disableActorAction async → Promise<any>
enableActorAction async → Promise<any>
disableActorActionTypes async → Promise<any>
enableActorActionTypes async → Promise<any>
await api.disableActorAction(target, actionName, sourceIdOrOpts?, opts?)
await api.enableActorAction(target, actionName, sourceId?)
await api.disableActorActionTypes(target, activationTypes, sourceIdOrOpts?, opts?)
await api.enableActorActionTypes(target, activationTypes?, sourceId?)
Same arguments as lockActorAction / lockActorActionTypes, but the rows show yellow (like status-disabled actions) instead of grey - use disable for temporary states, lock for lasting ones. Disabled entries are tracked separately: unlock* never removes them, enable* only removes them.
| Param | Type | Default | Description |
|---|---|---|---|
| target | Item\|Actor\|Token |
required | Same target rules as lockActorAction. Item targets work here too |
| actionName | string |
required | Action to disable. *Types variants take activationTypes instead |
| activationTypes | string\|string[] |
required | Activation types to disable, e.g. "Quick" / "Full" |
| sourceIdOrOpts | string\|Object |
null |
Source id for later removal, or the options object itself |
| opts | Object |
null |
Options when sourceIdOrOpts held the source id |
| opts.reason | string |
null |
Shown on the disabled row |
enable* takes sourceId in place of the last two. On an actor target pass the same id used to disable, omitting it errors and enables nothing. An Item target ignores sourceId and drops its own disabled entries, and enableActorActionTypes on an Item may also omit activationTypes to drop all of them.
setActionOverlay async → Promise<any>
getActionOverlay → object | null
getActionOverlays → Record<string, object>
removeActionOverlay async → Promise<any>
await api.setActionOverlay(target, actionName, overlay) // attach / patch; null removes
api.getActionOverlay(target, actionName) // → overlay | null
api.getActionOverlays(target) // → { [actionName]: overlay }
await api.removeActionOverlay(target, actionName)
Combat data on an item's native actions (system.actions), stored in a flag so re-imports don't wipe it. Name / activation / detail are never touched. Activating the action anywhere (TAH, sheet, macro) prints the normal card, then rolls via executeExtraActionCombat.
| Param | Type | Description |
|---|---|---|
| target | Item\|Token\|Actor |
Item that owns the action. Token/Actor for a deployable's own actions |
| actionName | string |
Name as it appears in system.actions. Dots are safe |
| overlay | Object\|null |
Combat fields below. Patch-merge. An empty value clears a field, null removes the overlay |
Overlay fields: laCombat ('attack'|'damage'), attack_bonus, accuracy, difficulty, attack_type, tags, damage, range - same semantics as the ExtraAction combat fields above. laCombat is optional: a range-only overlay just grants range.
Example:
await api.setActionOverlay(deployableActor, "Turret Attack (Auto)", {
laCombat: "attack", attack_bonus: 2, attack_type: "Ranged",
damage: [{ val: "5", type: "Kinetic" }] });
await api.setActionOverlay(item, "Lock On", { range: [{ type: "Range", val: 10 }] });
Managed from the UI via Add Extra > Action Combat.
applyActionOverlays → object[]
resolveGrantedActionRange → number | null
api.applyActionOverlays(target, actions) // → actions with overlays folded in
api.resolveGrantedActionRange(actor, actionName, base?) // → number | null
resolveGrantedActionRange takes the owning actor. applyActionOverlays takes an Item or Actor, a Token is not resolved and comes back with its actions unchanged.
Range grants: resolveGrantedActionRange folds the overlay range entries for that action from every item that is neither destroyed nor disabled onto base. Per-entry mode works like AE changes: upgrade (default, greater wins), add (sums, negatives allowed), override (replaces base and any upgrade, highest override wins). add still sums on top of an override, so it is not a hard final value. Consumed by the Lock On automation (base = Sensors) and the TAH hover range pulse.
openExtrasDialog → void
Dialog for managing an owner's extras: extra actions, extra deployment actors, deployable LIDs, and extra token stat bars. Only lists entries created here. Warns when the stat-bar setting is off, the data is still saved. Also reachable via TAH > Utility > Misc > Add Extra.
| Param | Type | Description |
|---|---|---|
| target | Actor\|Item |
Owner. A Token is not resolved and throws |