# EnvironmentInfo

> Talos next · Since 0.6; changed in next

The sky the scene is lit by, as it is drawn, and a behaviour's override of it.

The scene file (and `POST /environment`, and the editor) author the sky. A behaviour that brings weather (a storm rolling in, mist thickening at dusk) draws an override over it with {@link EnvironmentInfo.override}. The override is never written to the scene, and after a hot reload it is drawn again over the document's new sky, so the two never fight. Reacting to the sky (dimming a torch at dusk) only needs the reads. See docs/SKY.md, "A behaviour's override".

## Signature

```ts
export interface EnvironmentInfo {
  readonly kind: SkyKind
  /** The preset name for a procedural or atmosphere sky; null for the other kinds. */
  readonly preset: string | null
  /** Travel direction of the sun (sun -> scene), `[x, y, z]`. All zero when the sky has no sun. */
  readonly sunDirection: Vec3
  /** The sun's irradiance. 0 below the horizon, so `sunIntensity > 0` is "it is daytime". */
  readonly sunIntensity: number
  /** Scalar on the whole environment: background, diffuse and specular alike. */
  readonly intensity: number
  /** Yaw applied to the sky, radians. */
  readonly rotation: number
  /**
   * Draws `patch` over the scene's sky, fog, ambient and exposure at the end of this tick, merged
   * into what earlier calls asked (objects field by field); a field set to null is the scene's own
   * again. The override is above the scene: a `preset` it writes (a sky's, its `clouds`', a fog's)
   * sets aside the scene's fields that preset fills. The scene's own rules check it when it is
   * drawn: an override the scene file could not have is refused with one
   * `environment.override-refused` warning, and the last good one stays. Fog, ambient, exposure and
   * the sky's `intensity` and `rotation` are cheap to set every tick (a storm's ramp); a `preset`,
   * `kind` or sun change re-bakes the sky's light, once per change. What reflects the sky (in HD,
   * the sea) reflects it through the fog. The reads above say what is drawn, from the tick after. A
   * run with no renderer (headless) draws nothing, so its reads stay the scene's. Throws
   * `environment.override-invalid` (a `TypeError`, or a `RangeError` for a number that is not
   * finite) for a patch that is not one.
   *
   * ```ts
   * update(ctx) {
   *   const storm = Math.min(1, ctx.time.now / 60)
   *   ctx.environment.override({ fog: { visibilityM: 8000 - 7000 * storm }, sky: { intensity: 1 - 0.6 * storm }, exposureMode: "manual", exposure: 1 - 0.3 * storm })
   *   if (storm === 1) ctx.environment.override({ sky: { clouds: { preset: "storm" } } })
   * }
   * ```
   */
  override(patch: EnvironmentOverride): void
  /** Ends the override: the scene's own sky, fog, ambient and exposure again, from the next tick. */
  clearOverride(): void
}
```

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

## Members

| Member | Signature | Description |
|---|---|---|
| `kind` | `readonly kind: SkyKind` |  |
| `preset` | `readonly preset: string \| null` | The preset name for a procedural or atmosphere sky; null for the other kinds. |
| `sunDirection` | `readonly sunDirection: Vec3` | Travel direction of the sun (sun -> scene), `[x, y, z]`. All zero when the sky has no sun. |
| `sunIntensity` | `readonly sunIntensity: number` | The sun's irradiance. 0 below the horizon, so `sunIntensity > 0` is "it is daytime". |
| `intensity` | `readonly intensity: number` | Scalar on the whole environment: background, diffuse and specular alike. |
| `rotation` | `readonly rotation: number` | Yaw applied to the sky, radians. |
| `override` | `override(patch: EnvironmentOverride): void` | Draws `patch` over the scene's sky, fog, ambient and exposure at the end of this tick, merged into what earlier calls asked (objects field by field); a field set to null is the scene's own again. The override is above the scene: a `preset` it writes (a sky's, its `clouds`', a fog's) sets aside the scene's fields that preset fills. The scene's own rules check it when it is drawn: an override the scene file could not have is refused with one `environment.override-refused` warning, and the last good one stays. Fog, ambient, exposure and the sky's `intensity` and `rotation` are cheap to set every tick (a storm's ramp); a `preset`, `kind` or sun change re-bakes the sky's light, once per change. What reflects the sky (in HD, the sea) reflects it through the fog. The reads above say what is drawn, from the tick after. A run with no renderer (headless) draws nothing, so its reads stay the scene's. Throws `environment.override-invalid` (a `TypeError`, or a `RangeError` for a number that is not finite) for a patch that is not one. `ts update(ctx) {   const storm = Math.min(1, ctx.time.now / 60)   ctx.environment.override({ fog: { visibilityM: 8000 - 7000 * storm }, sky: { intensity: 1 - 0.6 * storm }, exposureMode: "manual", exposure: 1 - 0.3 * storm })   if (storm === 1) ctx.environment.override({ sky: { clouds: { preset: "storm" } } }) } ``` |
| `clearOverride` | `clearOverride(): void` | Ends the override: the scene's own sky, fog, ambient and exposure again, from the next tick. |

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