From 64c1d2f51e2a067d9c722354e66c583f4636b888 Mon Sep 17 00:00:00 2001 From: n8n-gitea Date: Mon, 8 Jun 2026 20:36:42 +0200 Subject: [PATCH] fix: spire climbing spec discrepancies (DISC-1,14,15,18,19,21,22,23,29,34) - DISC-1: Fix ascent seed missing +runId in combat-descent-actions.ts - DISC-14: Recovery room 10x regen/conversion already in gameStore.ts - DISC-15/18/21: Add missing completion logs for recovery, library, treasure rooms - DISC-19: Filter library discipline selection to non-paused disciplines - DISC-22: Fix puzzle room log format to match spec - DISC-23: Replace hardcoded MAX_LEVEL with MAX_ATTUNEMENT_LEVEL - DISC-29: Update spec to document libraryStayed/recoveryStayed on currentRoom - DISC-34: Add 'Exited the Spire' activity log in exitSpireMode --- docs/circular-deps.txt | 2 +- docs/dependency-graph.json | 2 +- docs/specs/spire-climbing-spec.md | 6 ++++-- src/lib/game/stores/combat-descent-actions.ts | 2 +- src/lib/game/stores/combatStore.ts | 10 +++++----- src/lib/game/stores/non-combat-room-actions.ts | 14 +++++++++----- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index 9fc8017..95fcaaf 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,5 +1,5 @@ # Circular Dependencies -Generated: 2026-06-08T14:03:03.873Z +Generated: 2026-06-08T18:24:27.320Z Found: 1 circular chain(s) — these MUST be fixed before modifying involved files. 1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index ca777e7..8d39a3f 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-06-08T14:03:01.818Z", + "generated": "2026-06-08T18:24:25.222Z", "description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.", "usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry." }, diff --git a/docs/specs/spire-climbing-spec.md b/docs/specs/spire-climbing-spec.md index a9a371a..07b1f5d 100644 --- a/docs/specs/spire-climbing-spec.md +++ b/docs/specs/spire-climbing-spec.md @@ -601,8 +601,10 @@ clearedRooms: Record // key = "floor:roomIndex" isDescentComplete: boolean // Non-combat room tracking (climbing spec §4.8–§4.12) -libraryStayed: boolean // true if player already used "Stay 1 Hour More" in current library room -recoveryStayed: boolean // true if player already used "Stay 1 Hour More" in current recovery room +// Note: libraryStayed and recoveryStayed live on the currentRoom object, not as +// top-level state fields. This keeps per-room transient state co-located. +libraryStayed: boolean // on currentRoom; true if player already used "Stay 1 Hour More" in current library room +recoveryStayed: boolean // on currentRoom; true if player already used "Stay 1 Hour More" in current recovery room ``` > `isDescending: boolean` (legacy alias) can be removed in favour of `climbDirection === 'down'`. diff --git a/src/lib/game/stores/combat-descent-actions.ts b/src/lib/game/stores/combat-descent-actions.ts index 2fc0061..fc4eab8 100644 --- a/src/lib/game/stores/combat-descent-actions.ts +++ b/src/lib/game/stores/combat-descent-actions.ts @@ -101,7 +101,7 @@ export function advanceRoomOrFloor(get: GetFn, set: SetFn): void { if (s.currentRoomIndex + 1 >= s.roomsPerFloor) { const newFloor = Math.min(s.currentFloor + 1, 100); - const newRoomsPerFloor = getRoomsForFloor(newFloor, newFloor * 12345); + const newRoomsPerFloor = getRoomsForFloor(newFloor, newFloor * 12345 + s.runId); const newRoom = generateSpireFloorState(newFloor, 0, newRoomsPerFloor, s.runId); const newFloorHP = calcRoomHP(newRoom); set({ diff --git a/src/lib/game/stores/combatStore.ts b/src/lib/game/stores/combatStore.ts index 6ade775..fa5746d 100644 --- a/src/lib/game/stores/combatStore.ts +++ b/src/lib/game/stores/combatStore.ts @@ -187,10 +187,10 @@ export const useCombatStore = create()( }, exitSpireMode: () => { - set((s) => { - const seed = s.exitFloor * 12345 + s.runId; - const rooms = getRoomsForFloor(s.exitFloor, seed); - return { + const s = get(); + const seed = s.exitFloor * 12345 + s.runId; + const rooms = getRoomsForFloor(s.exitFloor, seed); + set({ spireMode: false, currentAction: 'meditate', climbDirection: null, @@ -209,8 +209,8 @@ export const useCombatStore = create()( roomsPerFloor: 1, maxFloorReached: Math.max(s.maxFloorReached, 1), golemancy: { ...s.golemancy, activeGolems: [] as RuntimeActiveGolem[] }, - }; }); + get().addActivityLog('floor_transition', 'Exited the Spire'); }, startClimbUp: () => set({ climbDirection: 'up', currentAction: 'climb' }), diff --git a/src/lib/game/stores/non-combat-room-actions.ts b/src/lib/game/stores/non-combat-room-actions.ts index ceab270..51b7ca7 100644 --- a/src/lib/game/stores/non-combat-room-actions.ts +++ b/src/lib/game/stores/non-combat-room-actions.ts @@ -6,6 +6,7 @@ import type { CombatState, CombatStore } from './combat-state.types'; import { useDisciplineStore } from './discipline-slice'; import { useManaStore } from './manaStore'; import { useAttunementStore } from './attunementStore'; +import { MAX_ATTUNEMENT_LEVEL } from '../data/attunements'; import { PUZZLE_ROOMS } from '../constants'; type GetFn = () => CombatStore; @@ -81,8 +82,7 @@ export function onEnterPuzzleRoom(get: GetFn, set: SetFn): void { for (const attId of attunements) { const attState = attunementStore.attunements[attId]; if (attState?.active) { - const MAX_LEVEL = 10; - const levelFraction = Math.min((attState.level || 1) / MAX_LEVEL, 1); + const levelFraction = Math.min((attState.level || 1) / MAX_ATTUNEMENT_LEVEL, 1); totalReduction += perAttunementShare * levelFraction; } } @@ -100,7 +100,7 @@ export function onEnterPuzzleRoom(get: GetFn, set: SetFn): void { }, }); get().addActivityLog('floor_transition', - `Entered puzzle room on Floor ${s.currentFloor} (${puzzle?.name || puzzleId})`); + `Entered puzzle room on Floor ${s.currentFloor} — ${puzzle?.name || puzzleId}`); } // ─── Tick Handlers ──────────────────────────────────────────────────────────── @@ -132,7 +132,7 @@ function tickLibraryRoom(get: GetFn, set: SetFn, hours: number, advance: Advance if (xpGrant > 0) { const disciplineStore = useDisciplineStore.getState(); const allDisciplines = disciplineStore.disciplines; - const entries = Object.entries(allDisciplines); + const entries = Object.entries(allDisciplines).filter(([, ds]) => ds && !ds.paused); if (entries.length > 0) { const [targetId, targetDs] = entries[Math.floor(Math.random() * entries.length)]; @@ -150,6 +150,7 @@ function tickLibraryRoom(get: GetFn, set: SetFn, hours: number, advance: Advance if (progress >= required) { set({ currentRoom: { ...room, libraryProgress: progress } }); + get().addActivityLog('special_effect', 'Library study complete'); advance(get, set); } else { set({ currentRoom: { ...room, libraryProgress: progress } }); @@ -164,6 +165,7 @@ function tickRecoveryRoom(get: GetFn, set: SetFn, hours: number, advance: Advanc if (progress >= required) { set({ currentRoom: { ...room, recoveryProgress: progress } }); + get().addActivityLog('special_effect', 'Recovery complete — mana regen and conversion boosted'); advance(get, set); } else { set({ currentRoom: { ...room, recoveryProgress: progress } }); @@ -227,7 +229,9 @@ function tickTreasureRoom(get: GetFn, set: SetFn, hours: number, advance: Advanc } if (progress >= required) { + const claimedCount = claimedSet.size; set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: Array.from(claimedSet) } }); + get().addActivityLog('special_effect', `Treasure room looted — ${claimedCount} items recovered`); advance(get, set); } else { set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: Array.from(claimedSet) } }); @@ -242,7 +246,7 @@ function tickPuzzleRoom(get: GetFn, set: SetFn, hours: number, advance: AdvanceF if (progress >= required) { set({ currentRoom: { ...room, puzzleProgress: progress } }); - get().addActivityLog('puzzle_solved', `Puzzle solved on Floor ${s.currentFloor}!`); + get().addActivityLog('puzzle_solved', 'Puzzle solved!'); advance(get, set); } else { set({ currentRoom: { ...room, puzzleProgress: progress } });