Triggers & actions
Behavior in ScryMaster is data, not code. A rule reads: when something happens, if a condition holds, then run an ordered list of actions. That’s the whole system — and it’s why community content is safe to run.
Closed vocabulary, default-deny. Actions come from one fixed list. Anything not on it is rejected by the validator and can never execute — no
eval, no arbitrary code, no RCE vector in shared content. Every table below is generated from the same source the engine and the AI grounding endpoint use, so it’s always current. The machine-readable version lives at
/api/v1/actions.
Triggers (the “when”)
Rules fire on a trigger. You attach rules to a node, or once at the adventure level. Triggers marked reserved are declared for upcoming passes and don’t fire yet.
| Trigger | Fires? | When |
|---|
onEnterNode | ✓ live | When a node is entered. |
onExitNode | reserved | When a node is left (reserved). |
onChoice | reserved | When a choice is taken (reserved). |
onCheckTier | reserved | When a check resolves to a tier (reserved). |
onCombatEnd | reserved | When combat ends (reserved). |
onFlagSet | reserved | When a flag is set (reserved). |
onVarChange | reserved | When a variable changes (reserved). |
onZoneEnter | ✓ live | When the hero moves into a scene zone (gate with inZone). |
onAttacked | reserved | In-combat reaction window — reserved/deferred (task #52). |
Conditions (the “if”)
A condition is a small predicate over the run’s state. Combine and nest them with all (AND), any (OR), and not — e.g. (A and B) and (here or there). The same predicates power choice gating (requires) and gate-node branching.
| Condition | Shape | What it tests |
|---|
all | all: Condition[] | All sub-conditions are true (AND). |
any | any: Condition[] | Any sub-condition is true (OR). |
not | not: Condition | The sub-condition is false (NOT). |
flag | flag, is | A flag equals a boolean. |
hasFlag | hasFlag | A flag is truthy (shorthand). |
var | var, op, value | A variable compares (==,!=,>,>=,<,<=) to a value. |
clock | clock, gte?, lte? | A clock is within gte/lte bounds. |
hasClue | hasClue | The hero has learned a clue. |
objective | objective, is | An objective has a given status. |
relationship | relationship, gte?, lte? | An NPC relationship is within gte/lte bounds. |
hasItem | hasItem, qty? | The hero holds an item (optional qty). |
affords | affords: {cp?,sp?,ep?,gp?,pp?} | The purse can afford a coin cost. |
visited | visited: NodeId | A node has been entered at least once. |
visitCount | visitCount, gte?, lte? | A node’s visit count is within gte/lte bounds. |
inZone | inZone: zoneId | The hero is in a given scene zone. |
objectState | objectState: {id, key?, is} | A scene object’s state key matches (default: revealed). |
stat | stat, op, value | Compare a character stat (level / xp / hp / maxHp). |
Actions (the “then”)
A then is an ordered list of actions — they run top to bottom, so you chain as many as you like. Grouped by what they touch:
State
| Action | Parameters | What it does |
|---|
setFlag | flag | Set a boolean flag true. |
clearFlag | flag | Set a boolean flag false. |
incVar | var, by | Add to a numeric variable (counters/clocks). |
setVar | var, value | Set a variable to a number or string. |
tickClock | clock, by | Advance a declared clock by N. |
addClue | clue | Add a clue to the investigation board. |
setObjective | objective, to | Set a quest objective’s status. |
adjustRelationship | npc, by | Shift an NPC relationship score. |
grantAdvantageNext | scope? | Advantage on the next check (or initiative). |
grantDisadvantageNext | scope? | Disadvantage on the next check (or initiative). |
Combat & dice
| Action | Parameters | What it does |
|---|
applyDamage | target, dice?|amount?, damageType? | Deal damage (dice or amount) to a target. |
applyHeal | target, dice?|amount? | Heal a target (dice or amount). |
applyCondition | target, condition | Apply a 5e condition to a target. |
clearCondition | target, condition | Remove a condition from a target. |
requestCheck | ability?|skill?, dc, adv? | Deprecated — author an ability/skill check as a skillCheck NODE (with outcome tiers), not a then-list action. |
resolveAbility | elementId, targets? | Deprecated — not wired; apply effects via applyDamage/applyHeal/applyCondition (or an item), not this verb. |
startCombat | encounter | Deprecated — begin an encounter via a combat NODE (with onVictory/onDefeat/onFlee), not a then-list action. |
Rewards & currency
| Action | Parameters | What it does |
|---|
grantItem | itemId, qty?, note? | Give an item to the hero’s bag. |
grantGold | gp, note? | Give gold (folds into the purse + ledger). |
grantCoins | coins, note? | Give mixed coins (cp/sp/ep/gp/pp). |
spendCoins | amount, note? | Deduct coins (gate with an affords condition). |
grantLevel | — | Level the hero up (milestone). |
awardXp | xp | Award XP toward the next level. |
craftItem | recipeId | Craft from a recipe — consume the ingredients, grant the output (re-validated against the bag). |
grantInspiration | amount? | Grant real Heroic Inspiration — spendable at the combat roll windows (✨). |
Random
| Action | Parameters | What it does |
|---|
rollTable | table[], note? | Roll a seeded weighted table and apply the picked entry. |
Flow & sub-scenes
| Action | Parameters | What it does |
|---|
callScene | node | Jump to a node and push a return address (sub-scene). |
returnScene | — | Pop back to the caller of a sub-scene. |
ifThen | if, then, else? | Run `then` (else `else`) by an inline condition — conditional effects inside a then-list. |
goto | node | Jump to another node. |
Sheet actions
| Action | Parameters | What it does |
|---|
consumeItem | itemId, qty? | Consume a single-use item the hero holds. |
useItem | itemId | Use an owned item — a PLAYER command from the bag/inventory (not an author then-list action). |
spendResource | key, amount? | Spend a resource/slot the hero actually has. |
Scene state
| Action | Parameters | What it does |
|---|
revealObject | objectId | Reveal a hidden scene object. |
setObjectState | objectId, key, value | Set a scene object’s state key (drives layer swaps). |
moveActor | actorId, zoneId | Move an actor to a zone. |
setActorVisibility | actorId, visibility | Set an actor visible/hidden/concealed. |
Display & I/O
| Action | Parameters | What it does |
|---|
emitEvent | name, props? | Emit a domain event (analytics / AI / voice hook seam). |
showScene | sceneRef | Show a scene on the shared display. |
playAudio | trackId | Play a soundboard clip. |
showToast | text, ms? | Float a short message to the player ("You found 10 silver"). |
Node kinds (the beats you string together)
An adventure is a graph of nodes. These are the kinds you can place:
| Node kind | What it is |
|---|
scene | Narrative beat: prose + optional interactables / passive onEnterCheck → next. |
choice | A branch: a list of choices (each with optional requires / then / next). |
dialogue | A conversation: conditional lines + response choices in one node. |
skillCheck | An ability/skill check with 5 outcome tiers + a failure model. |
challenge | A skill challenge: reach N successes before M failures. |
combat | An encounter → onVictory / onDefeat / onFlee. |
loot | Grant rewards (then actions) → next. |
transition | A conditional jump to another node. |
gate | First matching branch wins; else a fallback node. |
consequence | Apply effects (then actions) → next. |
rest | A short/long rest (spend Hit Dice / refresh resources) → next. |
hub | A revisitable hub with conditional exits. |
ending | A terminal node (endingId + outcome + epilogue variants). |
Authoring with AI
Because the vocabulary is small, closed, and published as JSON, an LLM can author whole adventures grounded in the real verbs — then self-check with validate + simulate before you publish. Build in the Studio by hand, or bring your own AI (Claude, Gemini, ChatGPT) pointed at the grounding endpoint and OpenAPI spec.
Next: Publishing & saves →