2026 10 13 moving house temple design - xopherdeep/do-it-for-the-xp GitHub Wiki

The Old House, built like a house: a smarter Moving Day temple

Date: 2026-10-13. Status: implemented 2026-10-13 (moving temple commits through the Old House name plate, 8618f25e); see the plan docs/superpowers/plans/2026-10-13-moving-house-temple.md.

Why

The owner: "the create pack for the move out pack doesnt build very good temples. can you make it loads smarter?"

What the builder does today (src/lib/services/core/packs/movingTemple.ts, run 2026-10-13 for a grown-up, a 12-year-old and a 6-year-old in the default three-bedroom home):

      .     .     .     .     KEY   .     .     .     .
      mb    mb    mb    mon   mb    mb    mb    .     .
DOOR  map   cmp   hall  hall  hall  camp  VAN   egg
      .     mb    mb    mb    mb    mb    mb    .     .
  1. A comb. One straight hall, 12 dead-end rooms off it, one key. Nothing like a house: no loops, no floors.
  2. A wall of minibosses. 10 of the 12 side rooms are sealed minibosses (lockOnEnter); size 95 where 21 is a full temple; the only camp is at the very end.
  3. The kids barely play. The grown-up holds 8 rooms; the 12-year-old and the 6-year-old get one fight each (their bedrooms) and a sleeping beast everywhere else. A house room takes only the first quest the pack made for it, so 15 of the 17 kid quests (mugs, game shelf, books, frames, garage gear, dust bunny hunt, pillow parade...) never enter the temple.
  4. The order is wrong. The Final Walkthrough is a side room fought before the van is loaded.
  5. Generated temples have no floor art (a black starfield). Out of scope here (section 9).

Owner decisions (2026-10-13)

  • One room, each hero's chore. A room of the house is one temple room; every hero meets the chore of theirs that is due there (the grown-up meets the Crockery Colossus in the kitchen, the 12-year-old the Mug Thug). This needs the engine to let a room hold several quests.
  • Floors come from the setup. The Moving House dialog asks for 1 or 2 floors, and a basement when storage is ticked. Bedrooms go upstairs on a two-floor house; storage goes to the basement.
  • The whole design below, including: every old-house quest and the pack's dailies inside; only the kitchen and the van seal; the van, then the walkthrough, then the egg; rebuilding resets the heroes' walk through this one temple.

1. Rooms that hold several quests (engine)

RoomContent gains questIds?: string[]: the quests the room holds, in the Game Master's order. A room with a single questId reads as [questId], so every existing temple behaves exactly as before.

Resolution for a hero (resolveRoomQuest, docs/Quest-Rooms-Spec.md 5.2, extended):

  1. Take the room's quests (questIds, else [questId]) that are live (exist, not a draft).
  2. Among those visible to the hero (on their board, or an Open Bounty, or assigned to them): pick by the existing preference, lurking (open) before pending before cleared before away; ties go to list order. Result { kind: "quest", source: "questId" }.
  3. None visible but some live: notYours with the first live one (its beast sleeps here).
  4. None live: the template (content.quest) as today, else emptyLair.

A single-quest room passes through these steps and gets the same answer resolvePick gives now; the existing questRooms specs must pass unchanged.

Consequences, all through the one resolver: the run sync (templeSystem/questRooms.ts) seals or opens the room from the resolved status; the battle loader fights the resolved quest; clearing the room completes that quest. When a hero beats one beast and another of their chores in the room is open, the room lurks again: the next beast is there the next time they walk in (after a win the hero is not thrown into a second fight on the spot; the plan verifies how the return from battle behaves and keeps it that way).

A sealed room holds one quest. A lockOnEnter room that turns lurking again under a hero who just won is sealed again on the way back from the battle (heroRuns reseals an uncleared fight room), which would throw them into a second fight. So the guard and the van each hold a single quest; lists live only in open rooms.

Other readers read a list room by its headline quest: its biggest live quest, ties to list order (headlineQuest in questRooms.ts).

  • isQuestRoom (questRooms.ts and templeQuestRows.ts): true for a non-empty questIds too.
  • gmResolution (templeQuestRows.ts, the Game Master's temple quest list): the headline quest. The row's existing moreQuests count is the list's other live quests, and the tier mismatch flag is not raised for a room written with questIds (a pack types its rooms by layout, not by quest).
  • roomSizeShare (questRoomPicker.ts, the editor's size meter) and the tab's size: a list room counts at its headline quest (one fight at a time). sizedRoomOf already works from the resolved quest.
  • Temple editor (roomQuest.ts): describeRoomQuest shows a list room as picked with its headline quest; applyQuestPick replaces the list with the single pick (deletes questIds); releaseQuest deletes questIds too. No list editing UI in this round.

A room name. RoomContent gains name?: string ("Kitchen", "Twelve's bedroom"). The temple screen shows it as a small cream plate in the header (StatusPlz), so a hero knows where in the house they stand. A room without a name shows nothing: the nine shipped temples do not change.

2. What goes in which room

Every quest of the pack's old-house phases (1 Declutter to 5 Moving Day) and the four dailies land in exactly one room. The unpack phase (6) stays out: it belongs to the new house (section 9). A room is built only when the family has it and at least one of its quests exists; a room whose quests all went missing is left out.

Room Built when Quests (pack slugs), in list order
Front porch (entrance) always none
Front hall always open-first, donation-run, light-boxes, pillow-parade; paperwork when there is no office; the packing station's quests when there is no dining room
Dining room = packing station dining ticked supplies, box-labels, frames
Living room living ticked living-dining, game-shelf, daily tidy
Kitchen (miniboss, sealed) kitchen ticked its first quest that exists: dishes, else pantry-fridge, else mugs, else daily dishes
Pantry (open, beside the kitchen) the kitchen has quests left the kitchen's other quests, in that order
Laundry room laundry ticked laundry
Bathroom bathroom >= 1 bathrooms, daily teeth
Garage garage ticked garage, garage-gear
Yard (out the back door) yard ticked yard
Pet corner pets ticked pets, daily feed-pets
Home office office ticked office, paperwork
Guest room guest >= 1 guest-room
Storage (basement when ticked, else ground floor) storage ticked storage
Main bedroom bedroom >= 1 and a grown-up plays each grown-up's closet-grownup, beds-apart
A bedroom per kid or little one bedroom >= 1 that hero's keep-donate-trash, books-kid, dresser-kid, backpack-kid, clear-old-room (kid) or toy-triage, books-little, toys-little, backpack-little, dust-bunny-hunt (little)
The moving van (boss) always load-van
Last look always walkthrough, deep-clean
Keys to the New House (Master Room) always the Pegasus chest

* per-hero quests: every hero of the band has one, and all of them go in the list.

  • Bedrooms: the grown-ups share the main bedroom ("Dad's bedroom" for one grown-up, "Main bedroom" for more); the kids and little ones get max(1, bedrooms - 1) rooms when a grown-up plays (bedrooms when none does). With fewer rooms than kids, the kids share: they are dealt round robin (in the dialog's hero order) into the rooms there are, and a shared room is named for its kids ("Max and Pip's room"). No bedroom ticked: the bedroom quests go to the front hall.
  • One bathroom room holds the bathroom quests whatever the count; a second bathroom is the upstairs heal room (section 3), never a quest room.
  • A family with no grown-up has no main bedroom; quests a family does not have are skipped (the pack decides who gets which quest; the temple only places them).
  • The room's layout type: miniboss (sealed) for the key guard, boss for the van, monster (open) for every other quest room. The fight follows each resolved quest's own tier, as today.
  • The key guard is a sealed room of one quest: the kitchen; else the garage (its first quest, with a "Workbench" open room beside it for the rest); else a "Packing pile" off the front hall holding open-first.

3. The house plan

Floors

Setup 1F 2F B1
1 floor every room none none
2 floors porch, hall, living, dining, kitchen, laundry, garage, yard, pets, the bathroom, stairs up landing (stairs down, save point), upstairs hall, the bedrooms, guest, office, one heal room ("Medicine cabinet" when the home has 2+ bathrooms, else "Linen closet") none
+ basement (storage ticked) stairs down (the small-key door) as above landing (stairs up), storage, a "Forgotten box" chest

The floor list is B1, 1F, 2F (only the ones built), so the entrance is [index of 1F, row, col]. Stairs come in twins on the same cell of both floors (R8) and carry targetLevel and targetCoords so the screen never guesses.

The planner

A pure, seeded function grows each floor from its hub (1F: the front hall north of the porch; 2F and B1: the landing). Each room has a parent and a list of preferred directions from it, the way homes are built:

  • 1F: hall -> living (west), dining (north of living), kitchen (north of hall, or east of dining), laundry and garage (off the kitchen), yard (north of the kitchen: the back door), bathroom and office/guest (off the hall), stairs (off the hall).
  • 2F: landing -> upstairs hall -> bedrooms, bathroom, guest, office.

A room takes the first preferred free cell next to its parent, else any free cell next to it, else the nearest free cell next to any placed room of its floor. The seed is a hash of the answers that shape the house (rooms, floors, heroes, bands) and never the move date: the same answers give the same house. Each floor is trimmed to its bounding box and must fit 9x9 (R10); the largest home the setup allows must fit, and a test proves it.

Rooms that touch are open to each other (the engine has no inner walls), so a compact plan gets real loops, as a house has. Three cells must touch only what they are meant to, and the planner reserves walls around them:

  • The junk drawer (a chest with the Boss Key) touches only the key guard (R4).
  • The small-key room (section 3, below) touches only the room whose door it is locked from.
  • The van touches only the porch (its one door, locked with the master key, R6) and Last look.
  • Last look touches only the van and the Master Room; the Master Room touches only Last look (R7).

So the driveway runs off the porch: porch -> van -> Last look -> Keys to the New House. A hero comes in the front door, crosses the house to the kitchen, takes the van keys from the junk drawer, walks back out to the van and loads it, takes the last look, and hands over the keys.

Rooms that are not fights

  • Map chest: in the front hall's first free neighbour. Compass chest: next to the living room or dining room (else the hall).
  • One small-key door on the way into the quiet wing: the basement stairs, else the garage, else the yard. Its key is in a chest "Under the couch cushions" next to the living room (else next to the hall). With no quiet wing there is no small-key door.
  • Camp "Sleeping bags" next to the porch, before the van. Heal room upstairs as listed. Save point: the upstairs hall. At most one save room per floor (R10).

The record

The same id (gen-move-house), name ("The Old House"), theme and element as today, generated: true, a dungeonLayout of floors of tokens and a rooms table (unique tokens per room, the generator's naming). The description names the key guard: "...the key to the van is in the junk drawer behind the kitchen."

4. The setup dialog (MovingHouseModal.vue)

Under "Rooms in the home", one more row: Floors: 1 / 2 (radio buttons in the bands' style), and a Basement tick box shown only while storage is ticked. MovingAnswers gains floors?: 1 | 2 (default 1) and basement?: boolean (default false); the pack ignores them, only the temple reads them. The preview line reads "...and a 2-floor house temple with 16 rooms". The summary after the build lists the rooms floor by floor in one short line each.

5. Rebuilding

The temple keeps its id, so loading the pack again replaces the record and nothing else. Before the merge load the dialog clears every hero's saved progress in this one temple (TempleProgressDb.clearProgress(heroId, MOVING_TEMPLE_ID) for each hero of the family): the rooms moved, so their opened chests, keys and visited rooms would land on the wrong cells. Quests, their done or not done state, and every other temple's progress stay. The summary already asks for a reload, which drops any run still in memory.

6. Error handling

  • Nobody plays, or load-van is missing: no temple (null), as today; the pack still loads.
  • A 2-floor house with nothing upstairs (no bedroom, guest or office) is built as 1 floor. Small floors are not padded: R10's six rooms a floor is not checked by the solver or the audit, and empty filler rooms make a dull house.
  • The planner never throws on a shipped setup; a plan that fails the solver is a bug the test matrix catches.

7. Testing

  • tests/unit/questRooms*.spec.ts: list rooms resolve per hero (open beats pending beats done beats away, list order breaks ties), notYours when none is the hero's, template fallback when none is live, a single questId unchanged; gmResolution rows and roomSizeShare for list rooms; the editor helpers.
  • tests/unit/movingTemple.spec.ts rewritten: a matrix of setups (1 and 2 floors, basement on and off, each room ticked and not, bedrooms 0 to 8, 1 to 6 heroes in mixed bands): the solver solves it with no reasons, the audit passes, every old-house quest and daily the pack made sits in exactly one room, no unpack quest is in, every hero who plays has a quest in at least one room (at least three rooms each in the default home), only the guard and the van seal, the van leads to Last look and then the Master Room, each floor fits 9x9, same answers give the same record and the move date does not change it.
  • tests/e2e/specs/moving-temple.spec.js updated: build with 2 floors, enter on the porch ("Front porch" in the header), step north into "Front hall". The floor plan itself is proved by the unit matrix, not by a walk.
  • Gate: chemx verify (with --build for the .vue changes).

8. Files

  • Engine: src/lib/engine/core/dungeons/types.ts, src/lib/services/temples/questRooms.ts, src/lib/services/temples/templeQuestRows.ts, src/lib/services/temples/questRoomPicker.ts, src/app/Admin/XpCompendium/views/XpTemples/components/XpTempleCreator/hooks/grid/roomQuest.ts (and the quest card that reads it).
  • Room name: src/app/Console/TempleGrounds/TempleGrounds.vue (+ its .ts/.scss).
  • Builder: src/lib/services/core/packs/movingTemple.ts, split so no file grows past a few hundred lines (for example movingTemple/rooms.ts for section 2, movingTemple/planner.ts for section 3).
  • Setup: src/lib/services/core/packs/movingPack.ts (the two answers), src/app/SideMenu/XpSettings/components/MovingHouseModal.vue.

9. Not in this round

  • The New House temple, built from the unpack phase.
  • Floor art for generated temples (wood, tile, carpet, grass per room kind instead of the starfield).
  • Editing a room's quest list in the temple editor.
⚠️ **GitHub.com Fallback** ⚠️