Builder's Manual · Chapter 5

Floors and Runs

A floor file is two things at once: a description of a place, and an authored layout of nine rooms. Both are yours, and neither is regenerated at runtime.

The fields

Durable, authored by you:

  • name, description — what this place is
  • depth — how deep, which gates which monsters can appear through their minDepth
  • seed — a label recording which idea the layout came from; nothing reads it during play any more
  • rooms — the nine placed rooms, with their templates, contents and door directions
  • entranceRoomId, and generated: true

Reset at the start of each run by the GM, and only these:

  • every room's discovered — false, except the entrance — and cleared
  • currentRoomId back to entranceRoomId

Why layouts are authored, not generated

The GM used to build a floor from a seed at the top of every run. It no longer does, and it is worth knowing why before putting it back.

Generating whole floors was already the second design. Building them room by room as the player walked produced cell collisions and layouts that contradicted narration from three rooms ago; generating the entire floor in one write fixed that. But it still cost a long, error-prone turn before the player had typed anything, it produced layouts that were merely legal rather than good, and it meant the map had to be derived from something that did not exist until the session started.

Authoring moves that work to where it can be checked. A floor you wrote can be read, walked, balanced, and given a correct map — once, permanently. What you give up is novelty in the layout itself, and that is bought back somewhere much cheaper.

Where variety comes from now

@Missing file carries a pool of finished floors. A run plays three of them, in an order, chosen arithmetically from a seed word the player supplies — [ID: RUN].

Eight floors taken three at a time, in order, is a few hundred distinct descents, and every one of them is a descent you actually built. Adding one floor to the pool multiplies that, which makes a new floor the highest-leverage thing you can add to this project.

The seed still does real work; it decides which floors rather than what is in them. Keep that property if you extend the selection — derive from the seed, never choose by taste. A language model asked to pick three floors freely will pick the ones with the best names, every time.

Authoring a floor

The grid is four columns by three rows, and it cannot change. _tokenX and _tokenY divide by 4 and 3 to place the delver's token, so a floor of any other shape puts the token in the wrong room.

Lay out a single non-branching path of nine rooms. Each room carries the cell it occupies and the direction of its exit; the last room has no doorDirection and holds the toughest thing on the floor. Give three or four rooms an event and space them so the fountain falls before the last fight rather than after it. Leave one or two rooms holding nothing at all — a floor with something in every room has no rhythm.

Walk the path before you push it. A room whose exit opens onto an empty cell, or a second room with no exit at all, silently strands part of the floor: those rooms exist, hold monsters, and can never be reached. That is not hypothetical — @Missing file shipped with two orphaned rooms and two Rusted Sentinels nobody could ever meet. .tmp/art/check-floors.ps1 walks the door graph from the entrance and reports exactly this.

Then derive each room's tile key — its own exit, plus the wall opposite its neighbour's exit — and confirm that key exists on the template you assigned. A room whose key is missing from its template has no art, and you will not find out until the map is projected.

Finally, add the file to the dungeon's floors array. A floor that is not in the pool is never played.

The run reset

Starting a run resets the chosen delver as well as the floors: stats back to base, gold to zero, kit back to that character's own startingKit, and hit points last of all — because maximum HP follows from stats and equipment, and both just changed.

startingKit is a definition and equipment is mutable state; the two are deliberately separate fields. Before they were, the starting loadout lived in a sentence inside [ID: RUN], which was fine with one playable character and actively wrong with three — it would have handed a poleaxe to an elf. A rule that needs the GM to remember a value belongs in a field.

A fountain's toll does not carry between runs, and neither does a cursed relic.

If you add persistent progression later, this is the single instruction to change — but be aware you would be moving the game away from what it currently is, which is a crawl where every run starts equal and the only thing that improves is the player.

Manual updated Aug 17, 2026.