# Fields

A **field** is a value your plugin puts on entities: `satiety`, `infection`, `mana`,
`weariness`. Declaring one is usually the first thing a plugin does.

## A meter is more than a number

Declaring `infection` on living entities gives you:

- a **range** (min/max) the engine clamps every write to;
- **tiers** — what the narrator says at each band, and what the HUD badge reads;
- **visibility** — a sheet row by default, a HUD bar if you escalate;
- optionally a **creation question**, asked when a player builds their character.

> **Image: `docs/field-editor.png`**
> The field editor with a meter configured: key, min/max, the tier list with prose for each
> band, and the visibility control.

## Tiers are how the narrator knows

The narrator **never sees the number**. It sees the tier prose. That is the whole mechanism
by which a mechanical value becomes writing:

| Band | Narrator reads |
|---|---|
| 0–3 | *nothing — silence is a tier* |
| 4–9 | "a fever rides him" |
| 10–14 | "veins darkening at the wrist" |
| 15+ | "barely lucid, burning to the touch" |

Write these as things a narrator could say in scene. `"infection: 12"` is not a tier; it is
a leak.

**Silence is a legitimate tier.** A field at rest should usually say nothing, or every turn
carries a status report nobody asked for.

## Absolute bands vs ratio bands

A field **without** a declared max bands by **absolute value**.
A field **with** a max bands by **ratio** of the ceiling.

The engine decides which from the declaration, so an unbounded counter cannot silently put
everything in the top tier. If your tiers look wrong, check whether you meant to declare a
max.

## Reading a field

```js
$.field('infection')             // on the subject
$.field('infection', 'target')   // on the aimed-at entity
```

A field nobody has reads as `null`, not `0` — see [Missing values](/docs/missing-values).

## Derived fields

A field can carry a `valueExpr` rather than a stored value — computed on read from other
state. Useful for a value that is genuinely a function of other things (a carry limit from
Might, say) rather than something you write to.

A derived field has no stored entry, so `$.hasField()` answers `false` for it inside your
own plugin's hooks. If a hook needs to branch on one, branch on the inputs instead.

## Next

[Cooperation](/docs/plugin-cooperation) — reading fields another plugin declared.
