Skip to content

task 7a brief

Xopher edited this page Oct 22, 2026 · 1 revision

Task 7a: Locked climbs (board #1484)

LTTP has 8 key-locked stairways (Tower of Hera's Lobby Key Stairs gate the whole tower), and some bosses are reached only by stairs, so the Boss Key must be able to gate a climb. The converter already records them as ClassicLink.lock ("small-key" | "big-key") on the stairs link of the cell that holds the locked door; this task teaches the engine, the room sheet and the solver a locked climb. The dress step (Task 7, next) writes the field.

Files:

  • Modify: src/lib/engine/core/dungeons/types.ts (RoomContent), src/lib/services/temples/roomInteractions/travel.ts (climb, ~line 110) and the stairs cases in src/lib/services/temples/RoomInteractions.ts, src/lib/engine/core/dungeons/templeSystem/doors.ts (+ TempleSystem.ts wrappers) for the per-run unlock state, src/lib/engine/dungeons/generator/templeGraph.ts (the stairs links, ~line 244-260), src/app/Admin/XpCompendium/views/XpTemples/hooks/templeData/dungeonCounts.ts (small locks counted)
  • Test: tests/unit/TempleSolver.spec.ts, tests/unit/RoomInteractions.spec.ts (and the spec covering dungeonCounts, found with chemx q smallKeysGranted)

Interfaces:

  • Produces:

    • RoomContent.locked?: 'small' | 'master' on a stairs-up / stairs-down room: the climb FROM this room is locked. Its landing on the other floor climbs back freely.
    • isClimbUnlocked(runs, templeId, level, position): boolean and unlockClimb(runs, templeId, level, position, kind: 'small' | 'master'): boolean in doors.ts (public TempleSystem wrappers without runs). State lives in TempleState.openedDoors under climbKey(level, position) = ${roomPositionKey(level, position)}:climb, so a blood moon / new run locks it again and a saved run keeps it.
    • unlockClimb with 'small' spends one small key (refuses with no key); with 'master' it needs the Boss Key and does not spend it.
    • In templeGraph, the stairs door leaving a locked stairs room carries lock: 'small' or 'master'; the solver already knows those locks.
  • Step 1: Write failing tests.

    • TempleSolver.spec.ts: a two-floor temple whose 1F stairs-up room has locked: 'small' and whose only small key lies on 1F is solvable; with the key removed it is not (an R2 reason). A locked: 'master' climb to the boss floor needs the Boss Key: solvable only when the Boss Key is reachable before it.
    • RoomInteractions.spec.ts: a locked: 'small' stairs room with no key: the sheet says the stairs are locked and offers no Climb; with a key: it offers "Unlock and climb", which spends one key and climbs; climbing there again in the same run does not spend another key; a locked: 'master' stairs room needs the Boss Key and does not spend it.
    • dungeonCounts: a locked stairs room counts as one small lock (so the Temple Creator's keys-vs-locks tally stays honest).
  • Step 2: Run the specs. Expected: FAIL.

  • Step 3: Implement the interfaces. The sheet lines follow the existing raw-string idiom in travel.ts / RoomInteractions.ts ("Stairs lead up. A small lock bars the way." / "Stairs lead up. The Boss Door's lock bars the way.").

  • Step 4: Run the specs. Expected: PASS.

  • Step 5: Commit feat(temples): a flight of stairs can be locked: a small key or the Boss Key opens the climb.

Global Constraints (part 2)

  • You work in your own isolated git worktree branched from origin/master; run pnpm install --frozen-lockfile --prefer-offline first. Commit on your branch only; the lead merges it into feat/classic-temples-2 and ships one PR. Never touch /home/xopher/www/x/do-it-for-the-xp itself (other sessions share it). Never git stash.
  • Use chemx only. Run only the spec(s) your brief names, one at a time: chemx test tests/unit/<Name>.spec.ts. Never raw pnpm test|typecheck|lint|jest or npx jest, never a bare chemx test, never chemx verify, no typecheck/lint/build (the lead runs those once per wave; CI runs the suite on the PR).
  • Keep every file you create or grow under 500 lines (the repo's audit ratchet counts files over 500 as LINE_BUDGET_FILE). Name inline conditions in ifs (const isX = ...; if (isX)), and give object-literal parameter types a named interface (TYPE_COLOCATION) — the ratchet blocks the PR otherwise.
  • Temple ids, names, regions and themes don't change (wind-temple, earth-temple, …). Theme = id.replace("-temple", "") against BEAST_LIBRARY[].theme.
  • Game rules go in pure modules under src/lib/services/** (or src/lib/engine/**) with jest specs; screens stay thin. New components use <script setup lang="ts">; pages keep defineComponent with a sibling .ts/.scss.
  • In-world text comes from the copy catalogue (src/lib/services/copy/catalog/, three voices) through useCopy(); in-world prompts use useRevealPrompt(), never Ionic alerts. Sounds use play$fx(<key of src/assets/fx/index.ts>).
  • Original beasts 001–093 are family art: never overwrite or re-point them.
  • Quest difficulty is Fibonacci: must-clear monsters 1–2, minibosses 3–5, boss 8–13. Schedules follow SCHEDULES (daily 19:00, am 08:00, bed 20:30, school weekdays, weekly 18:00, monthly custom).
  • Every door into a boss room is a Boss door (isBossRoom in authoredDoors.ts). Bombs never open locked doors.
  • Keep comment density and naming like the surrounding code (long, plain-English doc comments).
  • Do not dispatch subagents of any kind, and never a reviewer.

Table of Contents


Home - See Pages

Developer Docs

Start Screen

User Management

My Portal

My Home

The City (Hometown)

The World Map

The Dungeon

Game Master

Settings

Support & Glossary

About XP

Clone this wiki locally