feat: implement non-combat room gameplay (Library, Recovery, Treasure, Puzzle)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m4s

This commit is contained in:
2026-06-04 19:28:25 +02:00
parent 40a50d34f4
commit ee24227d62
12 changed files with 539 additions and 124 deletions
+13 -50
View File
@@ -6,9 +6,14 @@ import type { CombatState, CombatStore } from './combat-state.types';
import { generateSpireFloorState, getRoomsForFloor } from '../utils/spire-utils';
import { getGuardianForFloor } from '../data/guardian-encounters';
import { usePrestigeStore } from './prestigeStore';
import { useDisciplineStore } from './discipline-slice';
import { useManaStore } from './manaStore';
import { summonGolemsOnRoomEntry } from './golem-combat-actions';
import {
onEnterLibraryRoom,
onEnterRecoveryRoom,
onEnterTreasureRoom,
onEnterPuzzleRoom,
} from './non-combat-room-actions';
type GetFn = () => CombatStore;
@@ -31,7 +36,6 @@ export function enterDescentMode(get: GetFn, set: SetFn): void {
});
get().addActivityLog('floor_transition',
`Beginning descent from Floor ${s.currentFloor}, Room ${s.currentRoomIndex + 1}`);
// Start descending from the current room
onEnterRoomDescend(get, set);
}
@@ -41,7 +45,6 @@ export function advanceRoomOrFloor(get: GetFn, set: SetFn): void {
const s = get();
if (s.climbDirection === 'down') {
// ── Descending (spec §4.6) ────────────────────────────────────────────
get().addActivityLog('floor_transition', `Room ${s.currentRoomIndex + 1} passed`);
if (s.currentFloor <= s.exitFloor && s.currentRoomIndex <= 0) {
@@ -80,12 +83,9 @@ export function advanceRoomOrFloor(get: GetFn, set: SetFn): void {
});
}
// ── Golem summoning on room entry (spec §9.3) ─────────────────────
summonGolemsForRoom(get, set);
onEnterRoomDescend(get, set);
} else {
// ── Ascending (spec §4.4) ─────────────────────────────────────────────
const roomKey = `${s.currentFloor}:${s.currentRoomIndex}`;
set((prev) => ({
clearedRooms: { ...prev.clearedRooms, [roomKey]: true },
@@ -121,15 +121,18 @@ export function advanceRoomOrFloor(get: GetFn, set: SetFn): void {
});
}
// ── Golem summoning on room entry (spec §9.3) ─────────────────────
summonGolemsForRoom(get, set);
// Handle non-combat rooms on ascent
// Handle non-combat rooms on ascent — initialize progress, do NOT auto-advance
const room = get().currentRoom;
if (room.roomType === 'library') {
onEnterLibraryRoom(get, set);
} else if (room.roomType === 'recovery' || room.roomType === 'treasure' || room.roomType === 'puzzle') {
advanceRoomOrFloor(get, set);
} else if (room.roomType === 'recovery') {
onEnterRecoveryRoom(get, set);
} else if (room.roomType === 'treasure') {
onEnterTreasureRoom(get, set);
} else if (room.roomType === 'puzzle') {
onEnterPuzzleRoom(get, set);
}
}
}
@@ -155,12 +158,10 @@ function summonGolemsForRoom(get: GetFn, set: SetFn): void {
summonResult.logMessages.forEach((msg) => get().addActivityLog('special_effect', msg));
}
// Write summoned golems back to combat store
set({
golemancy: { ...s.golemancy, activeGolems: summonResult.activeGolems },
});
// Deduct summon costs from mana store
useManaStore.setState({
rawMana: summonResult.rawMana,
elements: summonResult.elements,
@@ -213,44 +214,6 @@ export function onEnterRoomDescend(get: GetFn, set: SetFn): void {
}
}
// ─── onEnterLibraryRoom (climbing spec §4.8) ──────────────────────────────────
export function onEnterLibraryRoom(get: GetFn, set: SetFn): void {
const s = get();
const BASE_LIBRARY_XP = 50;
const xpGrant = Math.floor(BASE_LIBRARY_XP * (1 + s.currentFloor / 10));
const disciplineStore = useDisciplineStore.getState();
const activeIds = disciplineStore.activeIds || [];
const allDisciplines = disciplineStore.disciplines;
const activeEntries = activeIds
.map((id: string) => [id, allDisciplines[id]] as const)
.filter(([, ds]) => ds != null);
const targetPool = activeEntries.length > 0
? activeEntries
: Object.entries(allDisciplines);
if (targetPool.length > 0) {
const [targetId, targetDs] = targetPool[Math.floor(Math.random() * targetPool.length)];
useDisciplineStore.setState((prev) => ({
disciplines: {
...prev.disciplines,
[targetId]: { ...targetDs, xp: (targetDs?.xp || 0) + xpGrant },
},
totalXP: prev.totalXP + xpGrant,
}));
const discName = targetDs?.id || targetId;
get().addActivityLog('special_effect',
`${discName} gained ${xpGrant} XP from ancient tome`);
}
get().addActivityLog('floor_transition',
`Entered library room on Floor ${s.currentFloor}`);
advanceRoomOrFloor(get, set);
}
// ─── enterSpireMode (climbing spec §4.1) ──────────────────────────────────────
export function createEnterSpireMode(get: GetFn, set: SetFn) {