No active sessions yet.

No games yet.

No conversations yet.

No characters yet.

Hooks

The fifteen moments a rule can attach to, and what each one carries.

A hook is a rule that fires when the engine does something, anywhere in the world — as opposed to an item rule, which fires when a player uses one specific object.

A hook has a trigger, an optional condition, and a body.

The triggers

Trigger Fires when Carries
onSpawnCreature a creature enters the world, before anyone acts on it
onDamage hit points actually move — any source .amount
onHeal the mirror of onDamage .amount
onDeath once per entity, after loot and XP .killed
onCombatStart a fight begins (subject is the location)
onEffectApplied a status effect lands on someone
onAttack every swing, including a miss
onLocationEnter the party arrives (subject is the new location)
onItemUse an item rule runs, whether or not it did anything
onRoll a skill check resolves — observe-only .passed .total .dc .margin .crit .botch
onTurnStart once per actor per turn
onTurnEnd once per actor per turn .inCombat
onTimePasses in-game time advances — turn, travel or sleep .minutes .resting
onTravel the party covers ground .distance
onBeforeAction a player attempts an action, before it happens .attack .weapon .ranged

Picking the right one

onAttack vs onDamage. onAttack fires on every swing including misses; onDamage fires only when hit points actually move. A rule about trying uses the first; a rule about hurting uses the second.

onTurnEnd vs onTimePasses. This is the one that matters. onTurnEnd fires once per actor per turn, in every game. onTimePasses fires when the clock moves, and carries minutes that may be the narrator's estimate — see Minutes are not time.

For anything that ticks per character per turn — a resource regenerating, a cooldown — use onTurnEnd.

onRoll is observe-only

It may write fields and nothing else. A hook that could rewrite a roll would make the roll card unable to explain its own number.

This is what lets a number go up by being used: gate on the roll you care about, and add to a declared field. There is no built-in proficiency system — if you want one, this plus a field with a roll term is how you build it.

// condition: $.event.passed && $.event.margin > 5
$.adjustField({ field: "lockpicking", by: 1 })

$.event.margin is total minus DC — negative on a failure. crit and botch read the natural face, which is a different question from passed: a natural 20 passes a DC it could not otherwise reach, so a rule firing on a fumble cannot find one from passed alone.

onBeforeAction is the one that says no

It is the only trigger whose job is refusal. A refuse inside it stops the action and explains why.

// condition: $.event.attack && $.field('stamina') < 5
$.refuse("their arms are too heavy to lift the blade again")

It carries which action was attempted, so a rule gates on $.event.attack rather than parsing a string.

Worked example: a fever that worsens outdoors

Three hooks, one system.

onTimePasses — the fever climbs with time:

if ($.field('fever') > 0) {
  $.adjustField({ field: "fever", by: $.timed
    ? ($.event.minutes / 60) * $.dial('feverPerHour')
    : $.dial('feverPerTurn') })
}

onTurnEnd — a high fever hurts, once per turn:

if ($.field('fever') >= 10) {
  $.damage({ amount: 1 })
}

onBeforeAction — too sick to fight:

if ($.event.attack && $.field('fever') >= 15) {
  $.refuse("the fever has taken their balance; the blade will not answer")
}

Next

Conditions — gating when a hook is allowed to run.