# A_cold_dread_stinger__a_sub_bass_drop_wi
Managed audio asset. Edit the `.metadata` `settings` block to change compression.
# game.ui.UiDirector
The interface, and what its buttons mean. Screens are registered here, so every callback the user
triggers lands in this component's `onCallback` — which is why the trees live in `game.ui.Screens`
as pure functions and the meaning of a press lives here, next to the state it changes.
Attaches to one entity (`ui_director`, placed by `game.ui.sceneContent`). Nothing it does is
decorative: Play starts a run, Resume unpauses the engine, Restart resets the player, Endless flips
the generator on and moves the player into the arena, and the sliders write the engine's own volume
channels and the camera's look sensitivity.
## Screens
`menu`, `hud`, `journal`, `pause`, `ending`, `endless`, `blackout`, `gameover` — shown one at a time
except for the journal, which overlays the HUD by design.
## Two things learned the hard way
**`ui.updateScreen` SHOWS the screen it updates**, so hiding a screen before updating it re-shows it.
Visibility is applied last.
**The interface's key memory lives in component FIELDS, not module locals.** A module local read as
nil inside `update`, the throw disabled the component, and the journal and pause keys were silently
dead — while the screens kept rendering from an older compile and looked fine.
# SoundClip
Managed audio asset. `data.zaud` is the engine-native ZAUD payload the runtime decodes and plays; compression settings live in `settings.yaml` (`clipRef:setSettings({ ... })` to change one).
# game.audio.breath
The player's breathing: a laboured, exhausted breath, close and unmusical. The only sound in this
game that was generated rather than reused — nothing published fitted, and it is the whole point of
the breathing mechanic.
It plays as ONE looping spatial source that follows the player, with **volume and pitch carrying the
intensity** (quiet at rest, loud and faster when sprinting or out of breath). One source is what
makes it smooth: there is no clip to restart and nothing to overlap.
Driven by `game.audio.AudioDirector` from the player's `breath` field. It is silenced when the run is
over or not live, so it cannot outlive a capture, a Retry or a return to the menu.
# game.systems.Player
Everything the player is and does. Attached to the player's own avatar (the
`body` entity inside the player prototype, so every peer gets one).
It owns the torch and its battery, sprint and crouch with stamina, health,
what is carried, which clues have been read, how far along the objective chain
the player is, and what happens when they press E at something. It publishes
all of it as fields so the interface can draw it and the creatures can hear it.
## What it does not own
The house owns the doors and the hiding places; this reads an `Interactable`
marker on them and decides whether the player may pass. The run — objectives,
clues, the ending — lives in `game.systems.PlayerState` and in the shared
store, so a reload cannot lose it.
## How it touches the engine
Two patterns worth keeping, both learned the hard way here:
- **Finding what the player can use** goes through the `scene.find` *tool*,
never `queryEntitiesFlex`: that function ignores a component filter and
answers with every entity in the scene, so "nearest thing in front" becomes
a wall.
- **Changing a field** is a write onto the component object
(`comp.brightness = v`). There is no `component.set`.
- **Reach is three-dimensional.** The house is stacked — the living room sits
under the attic — so a distance test on x/z alone lets a player take the
attic key from the room below.
## Fields
`active`, `difficulty`, `health`, `stamina`, `battery`, `torchOn`, `hiding`,
`objective`, `prompt`, `carrying`, `clues`, `ending` — all read-only mirrors
of the state, for the interface.
# SoundClip
Managed audio asset. `data.zaud` is the engine-native ZAUD payload the runtime decodes and plays; compression settings live in `settings.yaml` (`clipRef:setSettings({ ... })` to change one).
# game.Bus
The one table the game's parts talk over. **Every slice announces what happened
and listens for what it cares about; no slice requires another**, and this is
the table in the middle they share.
```lua
local Bus = require("game.Bus")
Bus.emit(Bus.E.noise, { id = "sprint", position = p, source = "player" })
local handle = Bus.on(Bus.E.playerCaught, function(payload)
print(payload.monster) -- "resident"
end)
Bus.off(Bus.E.playerCaught, handle)
```
`Bus.E` is the event-name list, spelled once so no slice can invent a string
nobody else listens for. It is the same set as `GameRules.EVENTS`, which also
documents the payload each event carries; the scene entrypoint checks the two
agree on every load and warns if they have drifted.
`on`, `once`, `off`, `emit`, `count`, `clear`, plus `error()` and `clearError()`
for the last handler failure.
## Two rules, because getting them wrong desynchronises a co-op run
1. **The bus is local.** It is a module, and a module is one instance *per
peer*. An emit reaches the parts of the game running on this machine and
nowhere else. Anything another player must agree about — a door, a key, the
objective list, the monster — is written to the **shared store**
(`runtime_data.world`, under the keys in `GameRules.DATA`) and read back by
every peer. Emit the local *response* to a thing; store the *fact* of it.
A part that emits `doorOpened` and never writes the store has a door that
opens for one player and stays shut for everyone else.
2. **An emit is synchronous.** Handlers run before `emit` returns, in
subscription order, on the frame the thing happened. The payload table is
handed out, not copied — read it, do not keep it, do not mutate it. A handler
that throws is caught and reported, so one broken listener cannot silence
every other listener on the same event; `Bus.error()` reads the last one back.
Who calls it: every slice. The house emits doors and pickups, the monsters emit
their state and a sighting, the survival systems emit noise, damage and clues,
the interface listens to most of it, and the audio director listens to nearly
all of it.
# an_old_museum_building_settling_at_night
Managed audio asset. Edit the `.metadata` `settings` block to change compression.
# Old_wet_timber_planks_creaking_and_flexi
Managed audio asset. Edit the `.metadata` `settings` block to change compression.
# rh_shriek
Procedurally synthesised cue for Railhead: Dust Sea. 0.95 s, 32000 Hz mono, one-shot.
Regenerate with `RhAudio.bake(true)`.