2026 10 18 classic dungeon temples design - xopherdeep/do-it-for-the-xp GitHub Wiki
Date: 2026-10-18. Status: design approved in chat by the owner, section by section; this spec awaits review.
Builds on: docs/Quest-Rooms-Spec.md (combat rooms hold quests, sections 5, 10 and 11),
2026-10-13-temple-layout-hardening-design.md (real wall/bombable sides, roomGrants.ts, the solver and audit) and
the Wind Temple the owner laid out by hand after The Legend of Zelda's Level 1 (commit 3199f6b5).
The owner wants the main temples to be a homage to the original Zelda dungeon maps: a mapped cell block filled with our own RPG quests. The Wind Temple already follows Level 1 (the Eagle) in outline. The other eight are generated from seeds today and look like nothing in particular. Each temple is a themed chore base (Water is the bathroom, Shadow the kitchen, and so on), and a full dungeon of beasts should feel like a challenge even though a hero only has to beat the rooms that block the way.
- Wind keeps the Eagle; the other eight become A Link to the Past (LTTP) dungeons. LTTP fits our engine: its Big Key is our Boss Key, its small keys stay in their dungeon, it has real floors with stairs and holes (our stairs and pits), and its Big Chest holds the dungeon item (our relic). The Zelda 1 lock clashes (no Boss Key, keys carried between dungeons, Bombs on the main path) mostly disappear.
-
Every enemy room holds a themed chore; only the must-clear ones block the way. A must-clear room (a shutter
room, an enemy key drop, a miniboss, the boss) seals on entry and holds a chore of its own. A side room with enemies
holds a chore too, but the hero can walk past it. (In a monster room Fight is already a choice; only
lockOnEnterseals.) - Side rooms reuse the theme's beasts the way Zelda reuses Stalfos. Must-clear rooms each get a different chore.
- Zelda bends to our rules, never the reverse. Every door into the boss room is a Boss door (the engine's rule); keys stay inside a temple; a temple must be finishable without Bombs from outside.
-
Bombs: temple shops already sell them by default (
roomInteractions/shop.ts). A Bomb chest sits before any cracked wall on the only way forward. Crafting Bombs waits for the Workbench (board #1438). - Fight rooms can carry loot. When a fight is won, the beast drops what the room holds (a key, the Boss Key, gold, items) through the chest reveal. Random RPG drop tables are a follow-up with crafting (#1438).
- One cell is one LTTP screen. A 2x2 hall becomes a 2x2 block of open cells, like LTTP's own pause map.
- Small nudges are welcome. The owner's 4 extra Eagle rooms stay; the shop, save point, camp and Master Room are ours. The maps are a homage, not a tracing exercise, and can change later.
- Chores wake floor by floor (owner, 2026-10-19). Wake on arrival (#1435) turns a shipped temple's monster rooms into real quests the first time a hero walks in. For the big LTTP temples that would wake 15 or more chores at once, each with a schedule and ambushes. Instead, walking in wakes the entrance floor; every other floor wakes the first time the hero reaches it. Side rooms reuse the beasts of the must-clear rooms on their own floor, so a floor wakes only its must-clear chores.
- No "new layout shipped" notice. The owner did not edit the shipped temples; unedited copies refresh on their own.
- Level 3 of Zelda 1 (shaped like 卍) was discussed for Water and dropped with the move to LTTP.
| Source | Commit | What we read |
|---|---|---|
aerinon/ALttPDoorRandomizer (branch DoorDev) |
6f390947159b6d2162b52a60aebfd969ccf9b922 |
Doors.py (every door: supertile, wall, slot, kind), Regions.py (areas, their doors and chest locations), source/dungeon/EnemyList.py (vanilla enemies per supertile with tile x/y and area) |
TheVGLC/TheVGLC, The Legend of Zelda/
|
0e86a8f31f20ecad4eaa5741ff061af88767f7fb |
Graph Processed/LoZ_1.dot (Level 1 rooms tagged enemy/key/item/puzzle/boss/triforce, doors tagged locked/bombable/shutter), Original/tloz1_1.png (the Eagle map) |
The three randomizer files and the one VGLC graph are copied under tools/temples/classic/sources/ with both MIT
licence texts and the commit ids. The app never reads them; only the converter does.
| Temple (id unchanged) | Region | Theme | Source | Size (rough) |
|---|---|---|---|---|
Wind (wind-temple) |
plains | quick easy chores | Zelda 1 Level 1, the Eagle | 17 Eagle rooms + the owner's 4 |
Earth (earth-temple) |
forest | plants, yard, pets | LTTP Skull Woods | ~30 areas |
Water (water-temple) |
islands | bathroom | LTTP Swamp Palace | ~50 |
Shadow (shadow-temple) |
swamps | kitchen | LTTP Palace of Darkness | ~54 |
Fire (fire-temple) |
mountains | cooking, big jobs | LTTP Turtle Rock | ~57 |
Light (light-temple) |
desert | school, homework | LTTP Desert Palace | ~31 |
Ice (ice-temple) |
tundra | laundry, fridge | LTTP Ice Palace | ~51 |
Storm (storm-temple) |
clouds | bedroom, clutter | LTTP Tower of Hera | ~27 |
Moon (moon-temple) |
moon | bedtime, self-care | LTTP Ganon's Tower | ~92 |
"Areas" are the randomizer's regions (ledges and sub-rooms), counted roughly; the converter reports the real cell
counts. Ganon's Tower is the biggest by far; if it plays too long, Misery Mire is the fallback for Moon. Temple ids,
names, regions and themes do not change, so routes, the world map and docs/Quest-Rooms-Spec.md section 10 still hold.
tools/temples/classic/convert.py reads the pinned sources and writes one file per dungeon to
tools/temples/classic/dungeons/<temple-id>.json. The JSON files are committed; nothing at build time or run time
needs Python.
Cells. A supertile is 2x2 screens. Each screen is one cell. A supertile's position comes from its id (row
id >> 4, column id & 0xf), which is how LTTP itself finds a neighbour (walking north from 0xc9 lands in 0xb9).
Screens that belong to one room are joined by open sides; screens of different rooms inside one supertile are split by
walls. An enemy belongs to the screen its tile x/y falls in (the split is at 0x20).
Floors. Rooms joined by plain doors share a floor. Stairs (Up/Dn doors) move one floor up or down; holes
(regions' drop-down exits) move down. The converter numbers floors from that graph (B2, B1, 1F, 2F...) and lays each
floor out on its own grid, trimmed to the cells in use.
Doors. Each side between two cells is one of open, small-key, big-key, bomb, shutter, item (a gap
LTTP gates with an item: hookshot, boots, torches, flippers) or wall, both ends agreeing.
Contents per cell. enemies (count), chest (map, compass, big-key, big-chest, small-key, other),
keyDrop (small or big, carried by an enemy), potKey, boss, puzzle (switch, torch, peg), stairs, hole,
entrance.
Where the tables cannot place something (an area that spans screens in an odd way, a chest with no screen), the
converter stops with a message naming it, and a hand-written tools/temples/classic/overrides/<temple-id>.json settles
it. The converter prints, per dungeon: cells per floor, must-clear rooms, side rooms, keys against locked doors.
Wind is converted the same way from LoZ_1.dot: its rooms are matched onto the Eagle's 17 cells and the bow
basement, keeping every graph edge an orthogonal neighbour or the stairway (a node that fits no cell is reported, never
guessed). The match is checked into
dungeons/wind-temple.json; the owner's 4 east rooms are added as an override, unchanged from today's
wind-temple.ts.
tools/temples/classic/dress.ts (run with esbuild the way tools/temples/regenerate.ts is) turns each JSON into
src/lib/engine/temples/<temple-id>.ts, with a header saying it is generated and from what. regenerate.ts stops
writing the eight temples (the Temple Creator's Generate keeps using the generator).
Rooms
| Converted cell | Our room |
|---|---|
entrance |
entrance |
shutter doors + enemies, or an enemy keyDrop
|
must-clear monster, lockOnEnter, its own chore; a key drop becomes the room's drops
|
| a shutter room guarding the Big Key chest or the Big Chest |
miniboss, lockOnEnter
|
| any other cell with enemies |
side monster, no seal, a reused theme beast |
boss |
boss, lockOnEnter, the theme's boss chore; the master room (Pegasus chest) in a free cell behind it |
big-key chest |
loot { chest: 'dungeon', dungeon: 'master-key' }
|
big-chest |
loot holding the temple's relic (today's relic per temple, e.g. Gaia's Hammer) |
map / compass chest |
loot map / compass |
small-key chest |
loot key |
potKey |
key room (a key lying in the open) |
other chest |
loot gold or consumables; a Bomb chest where a cracked wall needs one |
puzzle |
puzzle (our glyph riddle); the door it opened in LTTP is a plain door |
| empty cells | our extras: one shop nearest the entrance, one save, one camp per floor; the rest empty
|
stairs |
stairs-up / stairs-down
|
hole |
pit when it drops one floor; a teleport when it drops further |
One cell holds one room type. A fight that holds a key is one cell with drops, never a key moved next door.
Doors. open is a door; small-key is locked; big-key is masterlocked; every side into the boss room is
masterlocked whatever LTTP has; bomb is bombable; shutter is a plain door whose room seals (lockOnEnter);
item is a plain door; wall is a wall.
Cracked walls on the way. When the solver (which treats bombable as a wall) cannot finish a temple, each cracked
wall on the only way forward gets a Bomb chest in a reachable cell before it. The shop already sells Bombs.
Chores. The quest plan per temple moves from regenerate.ts into dress.ts and grows to cover every must-clear
room with a different beast of the theme. Difficulty: must-clear monsters 1-2, minibosses 3-5, the boss 8-13; the
easiest go nearest the entrance (path distance, stable sort), as in docs/Quest-Rooms-Spec.md section 11. Side rooms
cycle through the beasts of the must-clear monster rooms on their own floor (a floor with none borrows the nearest
floor's), so they never add a chore of their own. Where a theme has fewer beasts than must-clear rooms, the room repeats
one (marked in the plan) until the art lands.
src/lib/services/temples/wakeOnEntry.ts today wakes every monster room of a shipped temple, once per hero per temple,
remembered in questEffects.templesWokenAt[templeId]. It changes to once per hero per floor:
- Walking in wakes the monster rooms of the entrance floor. Arriving on another floor (stairs, a pit, a teleport) wakes that floor's, the first time.
- The memory key becomes
${templeId}@${floor}. A legacy keytempleIdcounts as the entrance floor woken. - Everything else stays as #1435 built it: one
tq-quest per beast, a beast another hero woke takes this hero in as an assignee, the family's own quests are never joined, a Game Master's profile wakes nothing, minibosses and bosses never wake by themselves, and the Temple Quests tab can still wake any template by hand. - A floor that wakes beasts says so with an in-world reveal ("Three beasts stir on 2F"), never an Ionic alert, in the temple's own accent (the in-world UI rules). A floor with nothing new to wake says nothing.
- A one-floor temple (Wind) wakes everything on entry, exactly as today.
-
content.dropson amonster,minibossorbossroom has the shape of a chest's content (the fieldschestLootinsrc/lib/services/temples/ChestLoot.tsreads): a dungeon item (key,master-key,map,compass, a relic),items,gold. - On a win the beast drops it: the hero takes it through the chest reveal on returning to the room.
- When the room has no fight for this hero (cleared, done at home, pending, away, not theirs) the drop lies in the room and the sheet offers "Take it". A key is never stuck behind someone else's chore.
-
Once per run, filed in
TempleState.openedChestsunder its own key (${level}:${row},${col}:drop) so it never collides with "Search the room". A restored daily chore does not drop a second key; a new run (blood moon) restocks. -
src/lib/services/temples/roomGrants.tscounts drops (smallKeysGranted,grantsBossKey,chestDungeonItem), so chest setup, the audit, the solver, previews and counts agree that a temple is finishable. Its header note that fight rooms never hand out keys changes. - The Temple Creator gets a Drops picker on fight rooms, reusing the chest's loot picker (
RoomLootConfig.vue).
planSeedSync (src/lib/engine/core/dungeons/importAllTemples.ts) already refreshes a stored copy the Game Master
never edited and keeps an edited one. Switching a temple's layout clears every hero's saved run in that temple: they
start at the entrance, chests restock, small keys and the Boss Key are gone, Pegasi already earned stay (as the Old
House rebuild does).
The converter's report gives, per theme, the must-clear rooms against the beasts the theme has. The gap becomes one art
task for tools/art/beast_portraits.py. Art never blocks a temple: a must-clear room repeats a beast until its own is
painted. Original beasts 001-093 are family art and are never overwritten.
-
Fight-room drops: engine,
roomGrants.ts, solver, audit, the room sheet, the Temple Creator picker. Stands alone. -
Converter and sources:
convert.py, the pinned sources with licences, overrides, the eight LTTP JSON files and the report. Stands alone (runs beside 1). - Dress step and the eight temples, one temple at a time, smallest first: Tower of Hera (Storm), Skull Woods (Earth), Desert Palace (Light), then Swamp, Palace of Darkness, Ice, Turtle Rock, Ganon's Tower. Needs 1 and 2.
-
Wind's 1:1 pass: the Eagle's contents and doors from
LoZ_1.dot, the owner's 4 rooms kept. Needs 1 and the converter's Wind match. - Beast art list from the converter's report.
-
Floor-by-floor waking:
wakeOnEntry.ts, the arrival hook in the temple screen, the reveal. Stands alone; it must land before the first LTTP temple ships (step 3).
- Drops: jest specs for the win drop, the drop lying there when there is no fight for this hero, once per run (and
restocked by a new run), the separate spent key, and
roomGrantscounting drops. - Each converted temple: every token has a room, sides agree at both ends, the entrance is a real cell, every beast is
a library beast of the temple's theme, every door into the boss room is a Boss door, and
tests/unit/ShippedTemplesAudit.spec.tssolves it without Bombs from outside.tests/unit/WindTemple.spec.tsis the model. - Converter: the JSON files are checked by a jest spec of their own shape (cells, floors, both-ends doors).
- Locally: one spec at a time with
chemx test <spec>; the full suite runs on the PR (CI). - Waking: jest specs for the entrance floor waking on entry, another floor waking on first arrival (stairs and pit), no second wake on a floor, the legacy key read as the entrance floor, and a one-floor temple waking whole.
- One Cypress run through Tower of Hera (the smallest), entrance to Master Room.
- Random beast drop tables and Workbench crafting (#1438).
- Zelda 1's Second Quest.
- A "new layout shipped" notice for edited copies.
- LTTP's item-gated paths (they open as plain doors) and its puzzles' mechanics (they become glyph riddles).