# 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.
# 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.
# game.house.HouseDoor
A door leaf that swings on its own hinge. It is what stands in the doorway of
every room in the house, so a doorway reads as shut rather than as a hole.
`HouseBuilder` attaches it to each `door_<id>` leaf, with the door's lock.
## Who opens it
Not this component. It owns the **swing** and the **lock data**; the systems
slice owns the **interaction** — whether the player holds the key, what the
prompt says, and the message when a door refuses. The systems slice sets
`open = true` when the player may pass. So the house never has to know about
inventory, and the inventory never has to know about hinges.
## Fields
| Field | What it is |
|---|---|
| `open` | Whether the leaf is swung aside. **Sync** — a door one player opens is open for everyone. |
| `lockedBy` | The `GameRules.ITEMS` id that opens it, or `""`. Empty means it is never locked. |
| `swing` | How far the leaf turns, in degrees. |
| `hinge` | Which edge it turns about, `1` or `-1`. |
| `width`, `axis` | The leaf's size and which way its width runs, so the hinge can be found from them. |
| `speed` | Degrees a second while it swings. |
## The hinge is the whole problem
A door is a box, and a box turns about its own **centre** — which swings the leaf
straight through both walls. So the leaf's position is carried around its hinge:
the hinge sits at one edge, and opening is that point staying still while the rest
of the leaf travels around it. `awake` finds the hinge from the leaf's position,
width and axis; every frame of the swing re-derives the position from the closed
one, so a door that is interrupted halfway still ends exactly shut or exactly open.
# game.house.HouseBuilder
The twelve areas of **The House That Knows You**, as data and one builder.
`HouseBuilder.build()` is called from `game.house.sceneContent`, which the scene
build calls, so what it makes is baked into `scenes/house.scene/scene.json` and
is there the moment the world opens.
## The plan is data
- **`ROOMS`** — each area's interior rectangle: `x0..x1`, `z0..z1`, `y0..y1`.
The entrance hall is `HALL`, and is built by the scene skeleton, not here.
- **`DOORS`** — a doorway is declared by `(a, side, at, other)`: room `a` cuts an
opening on the wall named by `side`, centred at `at` measured along it. It is
declared by room-and-side rather than by a coordinate because two rooms never
share a wall: interiors are spaced so neighbouring walls **abut** and never
coincide, and each room cuts its own. Two coplanar walls look perfect in a
still frame and shimmer the moment the camera moves.
- **`STAIRS`** — the three places the house changes level, each with the room it
starts in, the room it arrives in, and the item that locks it. The hole a stair
needs is derived from the same entry, so a stair and its hole cannot disagree.
- **`PROPS`** — what each room is furnished with; `hide = true` marks a place the
player can get inside.
- **`UNLIT_ROOMS`** — the basement, secret room, attic and storage room carry no
light on purpose. They are torch territory.
## What it exposes
- `build()` — build it all. Called from the scene build.
- `area(id)` — the centre of an area, for any slice that needs to place something
in a room. `roomOf(id)` gives the rectangle.
- `doorInfo(id)` — a doorway's lock, for the systems slice.
- `hidingPlaces()` — every `hide_<room>_<n>` entity, by name.
## What it names, so other slices can find it
`room_<area>` is each room's root; `light_<area>` its bulb; `door_<id>` a door
leaf; `door_stair_<id>` a locked stair door; `hide_<area>_<n>` a hiding place;
`prop_<area>_<n>` furniture; `spawn_resident_<area>` and `patrol_<area>` the two
anchors the monsters slice uses.
The furniture is **blocked out**: named, in the right place, at the right size,
in a plain material, so the game runs and the polish pass can put the real prop
exactly there.
# 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.