# Animator

> Talos 0.7 · Since 0.6; changed in 0.7

Present on `ctx.animator` when the entity has an animator component.

Reached in a behaviour as `ctx.animator`.

## Signature

```ts
export interface Animator {
  /**
   * Switch to a state defined in the component, over the blend of the transition that reaches
   * it. A name the animator does not have changes nothing and is said once as an
   * `animator-unknown` warning, counted on `GET /feel` as `animator_misses`. Setting the state
   * playing now does nothing (`play` restarts one).
   */
  set(state: string): void
  readonly state: string | null
  /**
   * Writes a blend-tree parameter — `ctx.animator.param("speed", 3.2)`.
   *
   * Every parameter a blend tree reads is declared in the component's `parameters`, so the
   * names that work are written down in the scene. A parameter the component does not
   * declare is refused: it throws an `Error` whose `name` is `AnimatorParamError` and whose
   * message opens with `animator-param-unknown`. A typo would otherwise leave the blend
   * frozen with nothing anywhere saying why.
   *
   * A parameter with a `source` is written by the engine every tick from the character
   * controller, so writing it here is overwritten on the next tick. Leave `source` off the
   * ones a behavior drives.
   *
   * A state carrying `rootMotion` must not read a parameter bound to `speed`, `strafeX` or
   * `strafeY`: root motion writes the velocity those report, so the tree would be reading
   * its own output and the character would never leave its first clip. Bind it to
   * `inputSpeed`, `inputStrafeX` or `inputStrafeY` — what the controller was asked for — or
   * write it here. The contract reports the combination as `animator-root-motion-source`.
   */
  param(name: string, value: number): void
  /**
   * How far root motion moved this entity, in world space and in metres.
   *
   * "On the last tick" inside `update`, where the animator has not run yet; this tick's
   * inside an `onEvent` handler, because animation events are delivered after the animator
   * pass that raised them. One tick apart, never wrong, only differently old.
   *
   * Zero unless the playing state sets `rootMotion: true`. A character with a `controller`
   * component has already been moved by it — the engine hands the delta to the controller,
   * which resolves it against the world — so reading it there is for inspection. An entity
   * with no controller has been moved by it too, on its transform. A behavior that wants to
   * own the movement takes this and applies it itself.
   */
  rootDelta(): Vec3
  /**
   * Whether an animation-event window called `name` is open: a window is an `events` entry
   * with `until`, open while its clip is the animator's event source and the playhead is in
   * `[at, until)`. The same state the `begin` and `end` events report, as of the last
   * animator pass — this tick's inside an `onEvent` handler, the previous tick's inside
   * `update`. `false` for a name no window has. Every animator layer is an event source of
   * its own, so this is true while the window is open on the base or on any layer; a layer's
   * events carry `layer` in their data.
   */
  window(name: string): boolean
  /**
   * A bone's world position and rotation as of the last animator pass (inside `update`, the
   * previous tick's), or `null` for a bone the rig does not have. A retargeted rig also
   * answers to the canonical humanoid's names.
   *
   * `pose` "final" (the default) is the pose drawn: after look-at, the pelvis drop and IK.
   * "animated" is the clips' and layers' pose before those constraints ran, which is what an
   * IK target must be measured from: a foot target taken from the final pose of the same foot
   * chases its own result.
   */
  bone(name: string, pose?: "final" | "animated"): BonePose | null
  /**
   * Points an IK chain's end bone at a world-space point, or releases it with `null`.
   *
   * The chain is declared in the component's `ik`, so the names that work are written down
   * in the scene; one it does not declare throws an `Error` whose `name` is
   * `AnimatorIkError`. A released chain returns to the pose the clips produced.
   *
   * The engine owns the solve and the game owns the target: where a foot should go is a game
   * question, because a stair tread and a puddle are the same collision geometry. Call it
   * every tick a foot is planted — the target is remembered, so a character standing still
   * costs one call and no bookkeeping.
   *
   * `weight` is the chain's blend, 0..1, applied whether or not the target moved. **Nothing
   * ramps it for you**: 0 to 1 in one tick snaps the foot, and the engine cannot tell a
   * footfall from a stance change, so the caller ramps it over a handful of ticks.
   *
   * The target may be an entity handle as well as a point, in which case its position is
   * used.
   */
  ik(chain: string, target: Vec3 | EntityHandle | null, weight?: number): void
  /**
   * Aims the look-at chain at a world-space point, or releases it with `null`.
   *
   * Throws `AnimatorLookAtError` when the component declares no `lookAt`. Releasing eases the
   * head back to whatever the clip is doing over the constraint's own `smoothing`, rather
   * than dropping the aim in one frame.
   */
  lookAt(target: Vec3 | EntityHandle | null): void
  /**
   * Plays a state, or a clip of the animator's asset that no state plays, **once** — and then
   * hands over by itself: to `opts.then`, or back to the state that was playing before.
   *
   * `ctx.animator.play("reload", { interrupt: ["die"] })` is a reload that nothing but death
   * cuts short; `ctx.animator.play("Wave")` is an emote straight from a clip name, with no
   * state authored for it; a door's `play("open", { then: "opened" })` swings once and rests.
   * A state already declared with `once` in the scene keeps its declared behaviour and the
   * options override it field by field.
   *
   * The hand-over starts `blendOut` seconds before the clip's last frame, on the tick the clip
   * gets there — deterministic, a function of the clip and the tick rate only. When it ends —
   * run out or cut short — the bus event `opts.event` (default `"animation-end"`) is raised
   * with {@link AnimationEndEvent} as its data. Calling `play` again with the one-shot that is
   * playing starts it over, which a `set` of the playing state never does.
   *
   * Returns `false`, changing nothing, when the one-shot playing now does not let this one cut
   * in (its `interrupt` list leaves it out). Throws an `Error` whose `name` is
   * `AnimatorPlayError` for a name that is neither a state nor a clip, a `then` that is not a
   * state (message opening `animator-play-unknown`), or a negative blend time
   * (`animator-play-invalid`).
   */
  play(name: string, opts?: AnimatorPlayOptions): boolean
  /**
   * Motion warping: from now until clip time `opts.by` (seconds into the clip as authored) the
   * playing state's ground root-motion travel is re-aimed and scaled so the character's root
   * arrives at `opts.to` (x and z) when the clip reaches `by`. The scale is clamped to
   * `[0, opts.maxScale]` (default 2.5); vertical and yaw root motion, and all travel after `by`,
   * are the clip's own, and the controller's collision still applies. A state change clears it,
   * and so does `warp(null)`.
   *
   * Returns `false`, changing nothing, when the playing state has no root motion to warp (no
   * `rootMotion`, a root that does not travel, a blend tree) or the clip is already past `by`.
   * Throws `TypeError` for a malformed `opts`.
   */
  warp(opts: AnimatorWarpOptions | null): boolean
  /**
   * Reads or sets the weight of an animator layer declared in the component's `layers`:
   * `layerWeight("upper")` is the weight it has, `layerWeight("upper", 0.5)` sets it (0..1,
   * clamped) and returns it. The layer's own fade in and out and a `weightParam` multiply on top.
   * Throws an `Error` whose `name` is `AnimatorLayerError` (message opening
   * `animator-layer-unknown`) for a layer the animator does not declare.
   */
  layerWeight(layer: string, weight?: number): number
  /**
   * Holds the model's morph channel `name` (one of its asset's `morphs`: a face shape such as
   * `jawOpen`, or any morph target) at `weight` over whatever the animator's clips and layers give
   * it, or releases it with `null`, after which it follows the animation again at once. Returns
   * the weight it holds (for a release, the one drawn last frame). 0 is the neutral shape and 1 the
   * full one. Throws an `Error` whose `name` is `AnimatorMorphError` (message opening
   * `animator-morph-unknown`, listing the model's channels) for a channel the model does not have,
   * or a weight that is not a number.
   */
  setMorph(name: string, weight: number | null): number
  /**
   * The weight the model's morph channel `name` was drawn at last frame. Throws
   * `AnimatorMorphError` as {@link setMorph} does.
   */
  morph(name: string): number
  /**
   * Releases an animator layer: what plays on it keeps playing while the layer fades out over its
   * `blendOut`, then the layer is empty. Throws `AnimatorLayerError` for an undeclared layer.
   */
  stopLayer(layer: string): void
  /**
   * The animator-wide playback multiplier: 1 (the default) plays every state at its own rate,
   * 2 doubles it, 0 freezes the pose where it is, a negative one plays backwards (a forward walk
   * clip as a back-step). It multiplies each state's `speed` and
   * `speedParam` from the scene, so it is the knob for one character's slow motion or a
   * hit-stop, not for pacing a state — declare `speed` on the state for that. Throws
   * `RangeError` when not finite. The effective rate, every multiplier in, is
   * `anim.speed` on `GET /entities`.
   */
  speed: number
}
```

Import: `import type { Animator } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `set` | `set(state: string): void` | Switch to a state defined in the component, over the blend of the transition that reaches it. A name the animator does not have changes nothing and is said once as an `animator-unknown` warning, counted on `GET /feel` as `animator_misses`. Setting the state playing now does nothing (`play` restarts one). |
| `state` | `readonly state: string \| null` |  |
| `param` | `param(name: string, value: number): void` | Writes a blend-tree parameter — `ctx.animator.param("speed", 3.2)`. Every parameter a blend tree reads is declared in the component's `parameters`, so the names that work are written down in the scene. A parameter the component does not declare is refused: it throws an `Error` whose `name` is `AnimatorParamError` and whose message opens with `animator-param-unknown`. A typo would otherwise leave the blend frozen with nothing anywhere saying why. A parameter with a `source` is written by the engine every tick from the character controller, so writing it here is overwritten on the next tick. Leave `source` off the ones a behavior drives. A state carrying `rootMotion` must not read a parameter bound to `speed`, `strafeX` or `strafeY`: root motion writes the velocity those report, so the tree would be reading its own output and the character would never leave its first clip. Bind it to `inputSpeed`, `inputStrafeX` or `inputStrafeY` — what the controller was asked for — or write it here. The contract reports the combination as `animator-root-motion-source`. |
| `rootDelta` | `rootDelta(): Vec3` | How far root motion moved this entity, in world space and in metres. "On the last tick" inside `update`, where the animator has not run yet; this tick's inside an `onEvent` handler, because animation events are delivered after the animator pass that raised them. One tick apart, never wrong, only differently old. Zero unless the playing state sets `rootMotion: true`. A character with a `controller` component has already been moved by it — the engine hands the delta to the controller, which resolves it against the world — so reading it there is for inspection. An entity with no controller has been moved by it too, on its transform. A behavior that wants to own the movement takes this and applies it itself. |
| `window` | `window(name: string): boolean` | Whether an animation-event window called `name` is open: a window is an `events` entry with `until`, open while its clip is the animator's event source and the playhead is in `[at, until)`. The same state the `begin` and `end` events report, as of the last animator pass — this tick's inside an `onEvent` handler, the previous tick's inside `update`. `false` for a name no window has. Every animator layer is an event source of its own, so this is true while the window is open on the base or on any layer; a layer's events carry `layer` in their data. |
| `bone` | `bone(name: string, pose?: "final" \| "animated"): BonePose \| null` | A bone's world position and rotation as of the last animator pass (inside `update`, the previous tick's), or `null` for a bone the rig does not have. A retargeted rig also answers to the canonical humanoid's names. `pose` "final" (the default) is the pose drawn: after look-at, the pelvis drop and IK. "animated" is the clips' and layers' pose before those constraints ran, which is what an IK target must be measured from: a foot target taken from the final pose of the same foot chases its own result. |
| `ik` | `ik(chain: string, target: Vec3 \| EntityHandle \| null, weight?: number): void` | Points an IK chain's end bone at a world-space point, or releases it with `null`. The chain is declared in the component's `ik`, so the names that work are written down in the scene; one it does not declare throws an `Error` whose `name` is `AnimatorIkError`. A released chain returns to the pose the clips produced. The engine owns the solve and the game owns the target: where a foot should go is a game question, because a stair tread and a puddle are the same collision geometry. Call it every tick a foot is planted — the target is remembered, so a character standing still costs one call and no bookkeeping. `weight` is the chain's blend, 0..1, applied whether or not the target moved. **Nothing ramps it for you**: 0 to 1 in one tick snaps the foot, and the engine cannot tell a footfall from a stance change, so the caller ramps it over a handful of ticks. The target may be an entity handle as well as a point, in which case its position is used. |
| `lookAt` | `lookAt(target: Vec3 \| EntityHandle \| null): void` | Aims the look-at chain at a world-space point, or releases it with `null`. Throws `AnimatorLookAtError` when the component declares no `lookAt`. Releasing eases the head back to whatever the clip is doing over the constraint's own `smoothing`, rather than dropping the aim in one frame. |
| `play` | `play(name: string, opts?: AnimatorPlayOptions): boolean` | Plays a state, or a clip of the animator's asset that no state plays, **once** — and then hands over by itself: to `opts.then`, or back to the state that was playing before. `ctx.animator.play("reload", { interrupt: ["die"] })` is a reload that nothing but death cuts short; `ctx.animator.play("Wave")` is an emote straight from a clip name, with no state authored for it; a door's `play("open", { then: "opened" })` swings once and rests. A state already declared with `once` in the scene keeps its declared behaviour and the options override it field by field. The hand-over starts `blendOut` seconds before the clip's last frame, on the tick the clip gets there — deterministic, a function of the clip and the tick rate only. When it ends — run out or cut short — the bus event `opts.event` (default `"animation-end"`) is raised with {@link AnimationEndEvent} as its data. Calling `play` again with the one-shot that is playing starts it over, which a `set` of the playing state never does. Returns `false`, changing nothing, when the one-shot playing now does not let this one cut in (its `interrupt` list leaves it out). Throws an `Error` whose `name` is `AnimatorPlayError` for a name that is neither a state nor a clip, a `then` that is not a state (message opening `animator-play-unknown`), or a negative blend time (`animator-play-invalid`). |
| `warp` | `warp(opts: AnimatorWarpOptions \| null): boolean` | Motion warping: from now until clip time `opts.by` (seconds into the clip as authored) the playing state's ground root-motion travel is re-aimed and scaled so the character's root arrives at `opts.to` (x and z) when the clip reaches `by`. The scale is clamped to `[0, opts.maxScale]` (default 2.5); vertical and yaw root motion, and all travel after `by`, are the clip's own, and the controller's collision still applies. A state change clears it, and so does `warp(null)`. Returns `false`, changing nothing, when the playing state has no root motion to warp (no `rootMotion`, a root that does not travel, a blend tree) or the clip is already past `by`. Throws `TypeError` for a malformed `opts`. |
| `layerWeight` | `layerWeight(layer: string, weight?: number): number` | Reads or sets the weight of an animator layer declared in the component's `layers`: `layerWeight("upper")` is the weight it has, `layerWeight("upper", 0.5)` sets it (0..1, clamped) and returns it. The layer's own fade in and out and a `weightParam` multiply on top. Throws an `Error` whose `name` is `AnimatorLayerError` (message opening `animator-layer-unknown`) for a layer the animator does not declare. |
| `setMorph` | `setMorph(name: string, weight: number \| null): number` | Holds the model's morph channel `name` (one of its asset's `morphs`: a face shape such as `jawOpen`, or any morph target) at `weight` over whatever the animator's clips and layers give it, or releases it with `null`, after which it follows the animation again at once. Returns the weight it holds (for a release, the one drawn last frame). 0 is the neutral shape and 1 the full one. Throws an `Error` whose `name` is `AnimatorMorphError` (message opening `animator-morph-unknown`, listing the model's channels) for a channel the model does not have, or a weight that is not a number. |
| `morph` | `morph(name: string): number` | The weight the model's morph channel `name` was drawn at last frame. Throws `AnimatorMorphError` as {@link setMorph} does. |
| `stopLayer` | `stopLayer(layer: string): void` | Releases an animator layer: what plays on it keeps playing while the layer fades out over its `blendOut`, then the layer is empty. Throws `AnimatorLayerError` for an undeclared layer. |
| `speed` | `speed: number` | The animator-wide playback multiplier: 1 (the default) plays every state at its own rate, 2 doubles it, 0 freezes the pose where it is, a negative one plays backwards (a forward walk clip as a back-step). It multiplies each state's `speed` and `speedParam` from the scene, so it is the knob for one character's slow motion or a hit-stop, not for pacing a state — declare `speed` on the state for that. Throws `RangeError` when not finite. The effective rate, every multiplier in, is `anim.speed` on `GET /entities`. |

## Example

The tested fixture `fixtures/app/agent.ts`.

```ts
import type { Behavior } from "@argus/api"

// Sends a nav agent to `props.target` once, then reports arrival through ctx.state so the
// test can read it without polling the crowd directly.
export default {
  init(ctx) {
    ctx.state.arrived = false
    ctx.nav?.goTo(ctx.props.target as [number, number, number])
  },
  update(ctx) {
    if (ctx.nav?.arrived) ctx.state.arrived = true
  },
} satisfies Behavior
```
