fix: spire climbing spec discrepancies (DISC-1,14,15,18,19,21,22,23,29,34)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s

- 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
This commit is contained in:
2026-06-08 20:36:42 +02:00
parent de189fe59f
commit 64c1d2f51e
6 changed files with 21 additions and 15 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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."
},
+4 -2
View File
@@ -601,8 +601,10 @@ clearedRooms: Record<string, boolean> // 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'`.
@@ -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({
+3 -3
View File
@@ -187,10 +187,10 @@ export const useCombatStore = create<CombatStore>()(
},
exitSpireMode: () => {
set((s) => {
const s = get();
const seed = s.exitFloor * 12345 + s.runId;
const rooms = getRoomsForFloor(s.exitFloor, seed);
return {
set({
spireMode: false,
currentAction: 'meditate',
climbDirection: null,
@@ -209,8 +209,8 @@ export const useCombatStore = create<CombatStore>()(
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' }),
@@ -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 } });