Log inGet started
◇
component · drop-in viewer
asset⌬ componentcomponentprimary: init.luau·originates fromworld e339ab22-c…

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 chang…

by◐aiden·posted 1d ago
What it does

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.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

game/ui/UiDirector.component — the interface, and what its buttons mean. A screen belongs to the component whose own code registered it, and every callback the user triggers lands in THIS file's `onCallback`. So the screens live in `game.ui.Screens` as pure trees and the meaning of a press lives here, next to the game state it changes. NOTHING HERE IS DECORATIVE. Every entry does something real: Play starts the run, Resume unpauses the engine, Restart rebuilds the player's state and puts them back at the spawn, 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. IT RUNS WHILE THE GAME IS PAUSED. `editorUpdate` is the hook that fires while the clock is held, so the tree is refreshed from there too — a menu that stops updating the moment it pauses itself is a menu whose buttons go dead.

playerValues( ) → void

Reading the game. The interface owns none of this state; it reads the components that do, so what is on the glass cannot drift from what is being simulated.

grabValues( ) → void

Whether a creature is holding the player, and how close they are to breaking free. Read from the camera, which owns the hold, so the prompt on screen is the state of the thing itself rather than a copy of it.

endlessValues( ) → void

setShown(name: string, wanted: boolean) → void

NOTE: the interface's memory of the last key press is kept in the component's own FIELDS, not in locals here. Module-level locals in this component have twice read as nil inside `update` while the file plainly declared them, and a nil there throws on the comparison, which errors the update and gets the whole component disabled — the screens stop updating and every key silently stops working.

argtypedescription
namestring
wantedboolean

refresh(values: any?) → void

argtypedescription
valuesany?

playerBody( ) → void

Doing. What a press means.

resume( ) → void

startStory( ) → void

startEndless( ) → void

stopEndless( ) → void

toMenu( ) → void

awake( ) → void

tick(dt: number) → void

argtypedescription
dtnumber

update(dt: ?) → void

argtypedescription
dt?

editorUpdate(dt: ?) → void

The clock is held while the pause screen is up, and this is the hook that still fires — so the menu keeps refreshing and its buttons keep working.

argtypedescription
dt?

onCallback(id: string, data: any) → void

argtypedescription
idstring
dataany

onDestroy( ) → void

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

Showing up to 3 levels deep. Open a part to see what it holds further down.

39+items
◇
component · born here
❒asset
# game.house.EndlessDirector Endless Nightmare's engine: it builds the procedurally generated house the player is standing in, a floor at a time, and never stops. Attach it to one entity — the scene build puts it on `endless_director` inside `game.house.sceneContent`. It needs no sibling component; the rooms it makes are ordinary scene entities, and the story house is untouched. ## Fields | Field | Default | What it does | |---|---|---| | `active` | `false` | Flip it on to start a run. Inert until then, so the story house is never disturbed. | | `seed` | `20261012` | Together with the floor, decides every room. Two peers with the same pair build the same house. | | `floor` | `1` | The floor the player is on. It climbs and never caps. | | `spread` | `2` | How many rooms either side of the player stay loaded. | | `roomsExplored` | `0` | Rooms built this floor; resets each floor. | ## What it does - Builds the rooms within `spread` of the player and **unloads the ones behind them**, so an hour-long run does not cost an hour of memory. - Steps the floor when the player reaches the room marked `stairs`, replacing the floor rather than adding to it. - Announces each floor on the bus (`modeChanged`) and writes the run to the shared store under `GameRules.DATA.endless`, so every peer sees the same floor number and the same statistic. - Emits a `power_on` noise when a floor is generated — a new floor is loud, and the thing hunting the player is allowed to hear it. Generation runs at most four times a second: a player crosses a room in about two seconds and building one is not free.
▲ 0↑ born
·
inputmap · born here
❒asset
▲ 0↑ born
◇
component · born here
❒asset
# 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.
▲ 0↑ born
▣
module · born here
❒asset
# 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.
▲ 0↑ born
▣
module · born here
❒asset
# game.GameRules The constants and rules of **The House That Knows You** — the one module every part of the game reads, so two systems written in different sessions agree on what a room is called, how loud a sprint is, how long a torch battery lasts and which ending the player earned. It holds **no behaviour and no entities**: pure data plus a handful of pure functions. Require it and read it; never write to it. ```lua local RULES = require("game.GameRules") ``` ## What is in it - **`ART_DIRECTION`** — the one sentence every generation prompt reuses word for word, and **`REFERENCES`** — the pictures the game is judged against. - **`PALETTE`** — that sentence as numbers, for the interface and for materials. - **`HOUSE`** — the wall height, the wall thickness, the doorway size, the room module, the storey height and the player's own dimensions. Every prop in the game is sized against these. - **`AREAS`** / **`AREA_BY_ID`** — the twelve areas, each with its floor, its nominal centre on the ground plan and the item that unlocks it. - **`OBJECTIVES`** — the twelve-step chain, in order, each naming the area it happens in and the item it needs. - **`ITEMS`**, **`REQUIRED_CLUES`**, **`CLUE_COUNT_FOR_TRUTH`** — what can be picked up, and what the ending is decided on. - **`ENDINGS`** and **`selectEnding(cluesFound, secretRoomOpened)`** — the three endings and the rule that picks one, stated once so the interface and the systems cannot disagree. - **`SURVIVAL`** — speeds, stamina, health, torch battery, interaction ranges. - **`DIFFICULTY`** — easy, normal, nightmare. These change the monster's persistence, reach and the supply density **only**; all three run the same monster logic. - **`MONSTERS`** and **`MONSTER_STATES`** — THE RESIDENT and THE BROODMOTHER, and the states both run: patrol, investigate, chase, search, attack, recover. - **`NOISE`** — the interface between what a player *does* and what a monster *hears*. A system emits a noise id; the monster AI turns it into a reach. Nothing else may write a raw radius. - **`SPIDERS`** — the baby-spider event: five presses, six seconds, the slow-motion tutorial, the prompts, the damage. - **`ENDLESS`** / **`endlessDifficultyFor(floor)`** — Endless Nightmare, and a floor number that never stops climbing. - **`MODES`**, **`MAX_PLAYERS`**, **`DATA`** — the game modes and the keys shared state lives under. ## Who calls it Every slice: the house builder, both monsters, the survival systems, the interface and the audio director. Every component that runs in play reads its numbers from here rather than carrying its own copy.
▲ 0↑ born
▣
module · born here
❒asset
# game.house.Endless The house of **Endless Nightmare**, as data. No geometry, no entities: `plan(floor, seed)` answers what is on a floor and `EndlessDirector` builds it. ```lua local Endless = require("game.house.Endless") local plan = Endless.plan(3, seed) plan.cells -- every room on the floor, by key plan.stairs -- the room that leads deeper plan.difficulty -- from GameRules.endlessDifficultyFor(floor) ``` - **`plan(floor, seed)`** — rooms, the doors between them, what each room is, where the supplies are, and which room is the way down. A **pure function of (seed, floor)**, so two peers build the same house from two numbers and nobody sends a layout across the wire, and so a run can be repeated exactly. - **`cellRect(gx, gz)`** — a room's interior rectangle in world space. - **`cellAt(x, z)`** — which grid cell a world position is in. - **`openingsFor(cell, rect)`** — the doorway each side of a cell needs, in the form the wall builder takes. - **`ORIGIN`** — where the arena sits (2000 m out), well clear of the story house, so a run of one never stands inside the other. The systems slice moves the player here when a run starts. **Connectivity is by construction:** a room is only ever added beside a room that already exists, and a loop is occasionally closed so the house can be circled rather than only retraced. There is no sealed pocket, no door that opens onto a wall, and no supply in an unreachable room — they cannot be generated. Rooms are **reusable shapes**, not new geometry: a cell is a rectangle with a doorway per neighbour, exactly the shape the twelve hand-built areas use, so the generator and the story house cannot drift apart. There is no final floor; `plan` answers for any number.
▲ 0↑ born
◇
component · born here
❒asset
# game.camera.CameraPanic How the view moves when you are frightened. It rides the player's **CameraRig**, the same entity the engine's first-person controller drives, and runs after it (`executionOrder = 60`) so what it adds sits on top of the eye rather than being overwritten by it. `game.camera.sceneContent` attaches it. ## Sway A small natural walk, more when running, more again when a creature is hunting within 24 m (it reads the creatures' live state), unsteady when stamina is low, settling when the player stops. The `reduceShake` setting scales all of it to a third without turning it off. ## The capture When the creature reaches the player the view drops to the floor and rolls onto its side, then `Bus.E.captured` raises the blackout, and at 3.9 seconds `Bus.E.runEnded` hands the run to the interface's GAME OVER card. It fires once per capture and cannot fire again until a new run starts. Its timing uses its own clock: it originally read the *sway* clock, which only advances in the branch the capture skips, so the sequence sat on its first frame for ever.
▲ 0↑ born
▣
module · born here
❒asset
# game.ui.Screens Every screen the game draws, as trees. **No logic here**: each function takes the current state and returns the tree the engine renders, and `game.ui.UiDirector` decides when one is shown and what a press means. That split keeps a screen readable and makes the interface testable by handing the builder a table. The palette is `GameRules.PALETTE` — the same colours the materials and the art direction use — so the interface belongs to this house rather than to a generic overlay. - `menu` — title, subtitle, Story / Endless / Settings / Resume / Return, and the settings panel - `hud` — objective, clue count, crosshair, stamina and torch meters, the interaction prompt, and the Broodmother's swarm prompt with its E keycap - `journal` — the objective chain with the current step marked, and the clues found - `pause`, `ending`, `endless`, `blackout`, `gameover`
▲ 0↑ born
·
inputbinding · born here
❒asset
▲ 0↑ born
·
inputbinding · born here
❒asset
▲ 0↑ born
·
inputbinding · born here
❒asset
▲ 0↑ born
·
inputbinding · born here
❒asset
▲ 0↑ born
·
inputbinding · born here
❒asset
▲ 0↑ born
backing path · game/ui/UiDirector.component

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported on this asset or on the parts this page shows. Parts it leaves out are not counted here.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.