fix: combat room progression - replace legacy room-utils with spire-utils, align UI with store state
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m1s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m1s
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useMemo, useRef } from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useCombatStore, useManaStore, usePrestigeStore, useGameStore, fmt, computeMaxMana, computeRegen } from '@/lib/game/stores';
|
||||
import { computeDisciplineEffects } from '@/lib/game/effects/discipline-effects';
|
||||
import { getUnifiedEffects } from '@/lib/game/effects';
|
||||
import { useCraftingStore } from '@/lib/game/stores/craftingStore';
|
||||
import { getGuardianForFloor, isGuardianFloor } from '@/lib/game/data/guardian-encounters';
|
||||
import { getRoomsForFloor, generateSpireFloorState } from '@/lib/game/utils/spire-utils';
|
||||
import { SpireHeader } from './SpireHeader';
|
||||
import { RoomDisplay } from './RoomDisplay';
|
||||
import { SpireCombatControls } from './SpireCombatControls';
|
||||
@@ -54,8 +51,6 @@ function useSpireStats(prestigeUpgrades: Record<string, number>, equippedInstanc
|
||||
// ─── Main Component ───────────────────────────────────────────────────────────
|
||||
|
||||
export function SpireCombatPage() {
|
||||
const [roomsCleared, setRoomsCleared] = useState(0);
|
||||
|
||||
// ─── Spec: read room-aware state from combat store ───────────────────────
|
||||
const {
|
||||
currentFloor,
|
||||
@@ -70,10 +65,6 @@ export function SpireCombatPage() {
|
||||
roomsPerFloor,
|
||||
isDescentComplete,
|
||||
startFloor,
|
||||
setCurrentRoom,
|
||||
setFloorHP,
|
||||
setClearedFloor,
|
||||
climbDownFloor,
|
||||
exitSpireMode,
|
||||
startClimbUp,
|
||||
startClimbDown,
|
||||
@@ -92,10 +83,6 @@ export function SpireCombatPage() {
|
||||
roomsPerFloor: s.roomsPerFloor,
|
||||
isDescentComplete: s.isDescentComplete,
|
||||
startFloor: s.startFloor,
|
||||
setCurrentRoom: s.setCurrentRoom,
|
||||
setFloorHP: s.setFloorHP,
|
||||
setClearedFloor: s.setClearedFloor,
|
||||
climbDownFloor: s.climbDownFloor,
|
||||
exitSpireMode: s.exitSpireMode,
|
||||
startClimbUp: s.startClimbUp,
|
||||
startClimbDown: s.startClimbDown,
|
||||
@@ -124,79 +111,6 @@ export function SpireCombatPage() {
|
||||
|
||||
const { maxMana, baseRegen } = useSpireStats(prestigeUpgrades, equippedInstances, equipmentInstances);
|
||||
|
||||
// Use a deterministic seed based on floor to avoid Math.random() causing
|
||||
// referential instability and infinite re-render loops.
|
||||
const seededRandom = useMemo(() => {
|
||||
let seed = currentFloor * 12345;
|
||||
return () => {
|
||||
seed = (seed * 16807 + 0) % 2147483647;
|
||||
return (seed - 1) / 2147483646;
|
||||
};
|
||||
}, [currentFloor]);
|
||||
const totalRooms = useMemo(() => {
|
||||
if (isGuardianFloor(currentFloor)) return 1;
|
||||
const base = 5;
|
||||
const range = 10;
|
||||
const floorBonus = Math.min(range, Math.floor(currentFloor / 20));
|
||||
const randomVariation = Math.floor(seededRandom() * 3);
|
||||
return base + floorBonus + randomVariation;
|
||||
}, [currentFloor, seededRandom]);
|
||||
|
||||
// Generate initial room when floor or room count changes.
|
||||
// Uses a ref guard to prevent infinite re-render loops.
|
||||
const lastGeneratedRef = useRef<string | null>(null);
|
||||
useEffect(() => {
|
||||
const key = `${currentFloor}:${totalRooms}`;
|
||||
if (lastGeneratedRef.current === key) return;
|
||||
lastGeneratedRef.current = key;
|
||||
setRoomsCleared(0);
|
||||
const newRoom = generateSpireFloorState(currentFloor, 0, totalRooms);
|
||||
setCurrentRoom(newRoom);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentFloor, totalRooms]);
|
||||
|
||||
// Reset the generation guard when the component mounts
|
||||
// (e.g. when re-entering spire mode after exit)
|
||||
useEffect(() => {
|
||||
lastGeneratedRef.current = null;
|
||||
}, []);
|
||||
|
||||
const _handleRoomCleared = () => {
|
||||
const nextRoomIndex = roomsCleared + 1;
|
||||
|
||||
if (nextRoomIndex >= totalRooms) {
|
||||
const wasGuardian = isGuardianFloor(currentFloor);
|
||||
setClearedFloor(currentFloor, true);
|
||||
|
||||
if (wasGuardian) {
|
||||
const guardian = getGuardianForFloor(currentFloor);
|
||||
if (guardian) {
|
||||
addActivityLog('enemy_defeated', `⚔️ ${guardian.name} defeated!`, {
|
||||
enemyName: guardian.name,
|
||||
floor: currentFloor,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
addActivityLog('floor_cleared', `🏰 Floor ${currentFloor} cleared!`, {
|
||||
floor: currentFloor,
|
||||
});
|
||||
|
||||
const newFloor = currentFloor + 1;
|
||||
const newTotalRooms = getRoomsForFloor(newFloor);
|
||||
const newRoom = generateSpireFloorState(newFloor, 0, newTotalRooms);
|
||||
|
||||
setCurrentRoom(newRoom);
|
||||
setFloorHP(floorMaxHP);
|
||||
setClearedFloor(currentFloor, true);
|
||||
setRoomsCleared(0);
|
||||
} else {
|
||||
const newRoom = generateSpireFloorState(currentFloor, nextRoomIndex, totalRooms);
|
||||
setCurrentRoom(newRoom);
|
||||
setRoomsCleared(nextRoomIndex);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClimbUp = () => {
|
||||
startClimbUp();
|
||||
addActivityLog('floor_transition', `⬆️ Climbing to floor ${currentFloor + 1}...`);
|
||||
@@ -205,8 +119,6 @@ export function SpireCombatPage() {
|
||||
const handleClimbDown = () => {
|
||||
if (currentFloor <= 1) return;
|
||||
startClimbDown();
|
||||
climbDownFloor();
|
||||
setRoomsCleared(0);
|
||||
addActivityLog('floor_transition', `⬇️ Descending to floor ${currentFloor - 1}...`);
|
||||
};
|
||||
|
||||
@@ -234,8 +146,8 @@ export function SpireCombatPage() {
|
||||
currentFloor={currentFloor}
|
||||
floorHP={floorHP}
|
||||
floorMaxHP={floorMaxHP}
|
||||
roomsCleared={roomsCleared}
|
||||
totalRooms={totalRooms}
|
||||
roomsCleared={currentRoomIndex}
|
||||
totalRooms={roomsPerFloor}
|
||||
onClimbUp={handleClimbUp}
|
||||
onClimbDown={handleClimbDown}
|
||||
onExitSpire={handleExitSpire}
|
||||
|
||||
Reference in New Issue
Block a user