# TimeInfo

> Talos next · Since 0.6; changed in 0.7, next

Simulation clock.

Reached in a behaviour as `ctx.time`.

## Signature

```ts
export interface TimeInfo {
  /** Simulated seconds since load (scaled). */
  readonly now: number
  readonly frame: number
  /** Fixed tick length, 1/60 s. */
  readonly dt: number
  /**
   * Always 1. A tool's or the editor's time scale paces ticks against the wall clock and changes
   * nothing measured in ticks, and a game must not be able to tell a fast-forwarded playtest from play.
   */
  readonly scale: number
  /**
   * Raise the bus event `event` from this behaviour `seconds` of sim time from now, as `ctx.events.emit`
   * would then: heard by `onEvent` and `ctx.events.on(event, …)` with `by` this entity's id, delivered at the
   * end of the tick it falls due on. A delayed call, without hidden state: the timer is kept in this behaviour's `ctx.state` (under `$after`), so a snapshot, a
   * restore and a hot reload carry it and a restart drops it. It waits while the world is paused. `seconds`
   * must be finite and not negative, and it is called from a hook or a listener; otherwise
   * `time.after-invalid`.
   *
   * ```ts
   * onTriggerEnter(ctx) { ctx.time.after(3, "door-close") }
   * init(ctx) { ctx.events.on("door-close", (ev) => { if (ev.by === ctx.entity.id) ctx.state.open = false }) }
   * ```
   */
  after(seconds: number, event: string, data?: unknown, opts?: AfterOptions): void
}
```

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

## Members

| Member | Signature | Description |
|---|---|---|
| `now` | `readonly now: number` | Simulated seconds since load (scaled). |
| `frame` | `readonly frame: number` |  |
| `dt` | `readonly dt: number` | Fixed tick length, 1/60 s. |
| `scale` | `readonly scale: number` | Always 1. A tool's or the editor's time scale paces ticks against the wall clock and changes nothing measured in ticks, and a game must not be able to tell a fast-forwarded playtest from play. |
| `after` | `after(seconds: number, event: string, data?: unknown, opts?: AfterOptions): void` | Raise the bus event `event` from this behaviour `seconds` of sim time from now, as `ctx.events.emit` would then: heard by `onEvent` and `ctx.events.on(event, …)` with `by` this entity's id, delivered at the end of the tick it falls due on. A delayed call, without hidden state: the timer is kept in this behaviour's `ctx.state` (under `$after`), so a snapshot, a restore and a hot reload carry it and a restart drops it. It waits while the world is paused. `seconds` must be finite and not negative, and it is called from a hook or a listener; otherwise `time.after-invalid`. `ts onTriggerEnter(ctx) { ctx.time.after(3, "door-close") } init(ctx) { ctx.events.on("door-close", (ev) => { if (ev.by === ctx.entity.id) ctx.state.open = false }) } ``` |

## Example

The tested fixture `fixtures/recipes/jump-pad-and-elevator/scenes/behaviors/camera-zone.ts`.

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

/**
 * A trigger volume that retunes the camera while the player is inside it.
 *
 * The engine has no camera-zone concept and needs none: a volume, a `trigger` component and this.
 * Here the player's behaviour owns the camera (it re-sends its params when the view is turned), so
 * the zone tells it what to change rather than calling `ctx.camera.set` itself. In a scene where
 * nothing else writes the camera, the zone can call `ctx.camera.set(kind, params)` directly.
 */
interface Props {
  /** `ctx.camera.set` params to merge in on the way in, and on the way out. */
  inside?: Record<string, unknown>
  outside?: Record<string, unknown>
}

export default {
  onTrigger(ctx, trigger) {
    const params = trigger.entering ? ctx.props.inside : ctx.props.outside
    if (params) ctx.events.emit("camera-zone", { target: trigger.other.id, params })
  },
} satisfies Behavior<Record<string, never>, Props>
```
