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
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:
@@ -1,5 +1,5 @@
|
|||||||
# Circular Dependencies
|
# 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.
|
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) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_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.",
|
"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."
|
"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."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -601,8 +601,10 @@ clearedRooms: Record<string, boolean> // key = "floor:roomIndex"
|
|||||||
isDescentComplete: boolean
|
isDescentComplete: boolean
|
||||||
|
|
||||||
// Non-combat room tracking (climbing spec §4.8–§4.12)
|
// 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
|
// Note: libraryStayed and recoveryStayed live on the currentRoom object, not as
|
||||||
recoveryStayed: boolean // true if player already used "Stay 1 Hour More" in current recovery room
|
// 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'`.
|
> `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) {
|
if (s.currentRoomIndex + 1 >= s.roomsPerFloor) {
|
||||||
const newFloor = Math.min(s.currentFloor + 1, 100);
|
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 newRoom = generateSpireFloorState(newFloor, 0, newRoomsPerFloor, s.runId);
|
||||||
const newFloorHP = calcRoomHP(newRoom);
|
const newFloorHP = calcRoomHP(newRoom);
|
||||||
set({
|
set({
|
||||||
|
|||||||
@@ -187,10 +187,10 @@ export const useCombatStore = create<CombatStore>()(
|
|||||||
},
|
},
|
||||||
|
|
||||||
exitSpireMode: () => {
|
exitSpireMode: () => {
|
||||||
set((s) => {
|
const s = get();
|
||||||
const seed = s.exitFloor * 12345 + s.runId;
|
const seed = s.exitFloor * 12345 + s.runId;
|
||||||
const rooms = getRoomsForFloor(s.exitFloor, seed);
|
const rooms = getRoomsForFloor(s.exitFloor, seed);
|
||||||
return {
|
set({
|
||||||
spireMode: false,
|
spireMode: false,
|
||||||
currentAction: 'meditate',
|
currentAction: 'meditate',
|
||||||
climbDirection: null,
|
climbDirection: null,
|
||||||
@@ -209,8 +209,8 @@ export const useCombatStore = create<CombatStore>()(
|
|||||||
roomsPerFloor: 1,
|
roomsPerFloor: 1,
|
||||||
maxFloorReached: Math.max(s.maxFloorReached, 1),
|
maxFloorReached: Math.max(s.maxFloorReached, 1),
|
||||||
golemancy: { ...s.golemancy, activeGolems: [] as RuntimeActiveGolem[] },
|
golemancy: { ...s.golemancy, activeGolems: [] as RuntimeActiveGolem[] },
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
get().addActivityLog('floor_transition', 'Exited the Spire');
|
||||||
},
|
},
|
||||||
|
|
||||||
startClimbUp: () => set({ climbDirection: 'up', currentAction: 'climb' }),
|
startClimbUp: () => set({ climbDirection: 'up', currentAction: 'climb' }),
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { CombatState, CombatStore } from './combat-state.types';
|
|||||||
import { useDisciplineStore } from './discipline-slice';
|
import { useDisciplineStore } from './discipline-slice';
|
||||||
import { useManaStore } from './manaStore';
|
import { useManaStore } from './manaStore';
|
||||||
import { useAttunementStore } from './attunementStore';
|
import { useAttunementStore } from './attunementStore';
|
||||||
|
import { MAX_ATTUNEMENT_LEVEL } from '../data/attunements';
|
||||||
import { PUZZLE_ROOMS } from '../constants';
|
import { PUZZLE_ROOMS } from '../constants';
|
||||||
|
|
||||||
type GetFn = () => CombatStore;
|
type GetFn = () => CombatStore;
|
||||||
@@ -81,8 +82,7 @@ export function onEnterPuzzleRoom(get: GetFn, set: SetFn): void {
|
|||||||
for (const attId of attunements) {
|
for (const attId of attunements) {
|
||||||
const attState = attunementStore.attunements[attId];
|
const attState = attunementStore.attunements[attId];
|
||||||
if (attState?.active) {
|
if (attState?.active) {
|
||||||
const MAX_LEVEL = 10;
|
const levelFraction = Math.min((attState.level || 1) / MAX_ATTUNEMENT_LEVEL, 1);
|
||||||
const levelFraction = Math.min((attState.level || 1) / MAX_LEVEL, 1);
|
|
||||||
totalReduction += perAttunementShare * levelFraction;
|
totalReduction += perAttunementShare * levelFraction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ export function onEnterPuzzleRoom(get: GetFn, set: SetFn): void {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
get().addActivityLog('floor_transition',
|
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 ────────────────────────────────────────────────────────────
|
// ─── Tick Handlers ────────────────────────────────────────────────────────────
|
||||||
@@ -132,7 +132,7 @@ function tickLibraryRoom(get: GetFn, set: SetFn, hours: number, advance: Advance
|
|||||||
if (xpGrant > 0) {
|
if (xpGrant > 0) {
|
||||||
const disciplineStore = useDisciplineStore.getState();
|
const disciplineStore = useDisciplineStore.getState();
|
||||||
const allDisciplines = disciplineStore.disciplines;
|
const allDisciplines = disciplineStore.disciplines;
|
||||||
const entries = Object.entries(allDisciplines);
|
const entries = Object.entries(allDisciplines).filter(([, ds]) => ds && !ds.paused);
|
||||||
|
|
||||||
if (entries.length > 0) {
|
if (entries.length > 0) {
|
||||||
const [targetId, targetDs] = entries[Math.floor(Math.random() * entries.length)];
|
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) {
|
if (progress >= required) {
|
||||||
set({ currentRoom: { ...room, libraryProgress: progress } });
|
set({ currentRoom: { ...room, libraryProgress: progress } });
|
||||||
|
get().addActivityLog('special_effect', 'Library study complete');
|
||||||
advance(get, set);
|
advance(get, set);
|
||||||
} else {
|
} else {
|
||||||
set({ currentRoom: { ...room, libraryProgress: progress } });
|
set({ currentRoom: { ...room, libraryProgress: progress } });
|
||||||
@@ -164,6 +165,7 @@ function tickRecoveryRoom(get: GetFn, set: SetFn, hours: number, advance: Advanc
|
|||||||
|
|
||||||
if (progress >= required) {
|
if (progress >= required) {
|
||||||
set({ currentRoom: { ...room, recoveryProgress: progress } });
|
set({ currentRoom: { ...room, recoveryProgress: progress } });
|
||||||
|
get().addActivityLog('special_effect', 'Recovery complete — mana regen and conversion boosted');
|
||||||
advance(get, set);
|
advance(get, set);
|
||||||
} else {
|
} else {
|
||||||
set({ currentRoom: { ...room, recoveryProgress: progress } });
|
set({ currentRoom: { ...room, recoveryProgress: progress } });
|
||||||
@@ -227,7 +229,9 @@ function tickTreasureRoom(get: GetFn, set: SetFn, hours: number, advance: Advanc
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (progress >= required) {
|
if (progress >= required) {
|
||||||
|
const claimedCount = claimedSet.size;
|
||||||
set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: Array.from(claimedSet) } });
|
set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: Array.from(claimedSet) } });
|
||||||
|
get().addActivityLog('special_effect', `Treasure room looted — ${claimedCount} items recovered`);
|
||||||
advance(get, set);
|
advance(get, set);
|
||||||
} else {
|
} else {
|
||||||
set({ currentRoom: { ...room, treasureProgress: progress, treasureLootClaimed: Array.from(claimedSet) } });
|
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) {
|
if (progress >= required) {
|
||||||
set({ currentRoom: { ...room, puzzleProgress: progress } });
|
set({ currentRoom: { ...room, puzzleProgress: progress } });
|
||||||
get().addActivityLog('puzzle_solved', `Puzzle solved on Floor ${s.currentFloor}!`);
|
get().addActivityLog('puzzle_solved', 'Puzzle solved!');
|
||||||
advance(get, set);
|
advance(get, set);
|
||||||
} else {
|
} else {
|
||||||
set({ currentRoom: { ...room, puzzleProgress: progress } });
|
set({ currentRoom: { ...room, puzzleProgress: progress } });
|
||||||
|
|||||||
Reference in New Issue
Block a user