# Behavior

> Talos 0.6 · Since 0.6

What a behavior module exports as `default`.

Every hook is optional. A throw disables this instance and emits `script-error`; the others keep running. One exception: a stale handle — a `DespawnedEntityError`, from a handle kept past its entity — thrown from `update`, `pausedUpdate`, `onCollision`, `onTrigger`, `onEvent` or a `ctx.events.on` listener ends that call only; the instance runs again next tick and the error is reported (at most once a second) with `disabled: false`. In `init` it disables like any throw. Each hook has a wall-clock budget (2 ms by default); ten consecutive overruns are reported as a `script-error` (phase `budget`, `disabled: false`, at most once every 600 ticks after the first) and the instance keeps running. A runaway loop is still stopped and disables the instance.

## Signature

```ts
export interface Behavior<S = Record<string, any>, P = Record<string, any>> {
  /** Once after attach, and again after each hot reload (with `state` preserved). */
  init?(ctx: Ctx<S, P>): void
  /** Every fixed tick; `dt` is 1/60 s. Not while the world is held (`ctx.session.pause()`, a `pausesWorld` screen). */
  update?(ctx: Ctx<S, P>, dt: number): void
  /**
   * Every fixed tick while the world is held, in place of `update`: where a pause menu reads its
   * own key, since `update` is not running. `ctx.time.now` stands still; `dt` is still 1/60 s.
   */
  pausedUpdate?(ctx: Ctx<S, P>, dt: number): void
  /**
   * Before detach and before a hot reload swaps the module. `reason` says which: `"detach"` (the entity,
   * or the scene, is going) or `"reload"` (`init` follows, with `state` kept), so an effect that belongs
   * to the entity going away is not run by an edit.
   */
  dispose?(ctx: Ctx<S, P>, reason?: "detach" | "reload"): void
  onCollision?(ctx: Ctx<S, P>, collision: Collision): void
  onTrigger?(ctx: Ctx<S, P>, trigger: TriggerEvent): void
  /**
   * The usual pair, with its usual meaning: `other` is the other participant from this side — the volume
   * hears who entered, and the entrant hears the volume. (In {@link onTrigger}'s payload `other` is always
   * the entrant.) Each runs after `onTrigger` for the same contact.
   */
  onTriggerEnter?(ctx: Ctx<S, P>, other: EntityHandle): void
  /** The other participant left: see {@link onTriggerEnter}. */
  onTriggerExit?(ctx: Ctx<S, P>, other: EntityHandle): void
  /** Any `emit`, from any behavior or from the app. */
  onEvent?(ctx: Ctx<S, P>, event: GameEvent): void
}
```

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

## Members

| Member | Signature | Description |
|---|---|---|
| `init` | `init?(ctx: Ctx<S, P>): void` | Once after attach, and again after each hot reload (with `state` preserved). |
| `update` | `update?(ctx: Ctx<S, P>, dt: number): void` | Every fixed tick; `dt` is 1/60 s. Not while the world is held (`ctx.session.pause()`, a `pausesWorld` screen). |
| `pausedUpdate` | `pausedUpdate?(ctx: Ctx<S, P>, dt: number): void` | Every fixed tick while the world is held, in place of `update`: where a pause menu reads its own key, since `update` is not running. `ctx.time.now` stands still; `dt` is still 1/60 s. |
| `dispose` | `dispose?(ctx: Ctx<S, P>, reason?: "detach" \| "reload"): void` | Before detach and before a hot reload swaps the module. `reason` says which: `"detach"` (the entity, or the scene, is going) or `"reload"` (`init` follows, with `state` kept), so an effect that belongs to the entity going away is not run by an edit. |
| `onCollision` | `onCollision?(ctx: Ctx<S, P>, collision: Collision): void` |  |
| `onTrigger` | `onTrigger?(ctx: Ctx<S, P>, trigger: TriggerEvent): void` |  |
| `onTriggerEnter` | `onTriggerEnter?(ctx: Ctx<S, P>, other: EntityHandle): void` | The usual pair, with its usual meaning: `other` is the other participant from this side — the volume hears who entered, and the entrant hears the volume. (In {@link onTrigger}'s payload `other` is always the entrant.) Each runs after `onTrigger` for the same contact. |
| `onTriggerExit` | `onTriggerExit?(ctx: Ctx<S, P>, other: EntityHandle): void` | The other participant left: see {@link onTriggerEnter}. |
| `onEvent` | `onEvent?(ctx: Ctx<S, P>, event: GameEvent): void` | Any `emit`, from any behavior or from the app. |

## 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
```
