# Hooks

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](/docs/conditions), and a
[body](/docs/rule-bodies).

## 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](/docs/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.

```js
// 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`](/docs/steps) inside it stops the
action and explains why.

```js
// 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:

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

**`onTurnEnd`** — a high fever hurts, once per turn:

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

**`onBeforeAction`** — too sick to fight:

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

## Next

[Conditions](/docs/conditions) — gating when a hook is allowed to run.
