refactor: extract sub-components from monster functions (issue #99)
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
- GuardianPactsTab: extracted GuardianCard, PactHeaderSummary, TierFilter + 5 helper components into guardian-pacts-components.tsx - SpireSummaryTab: extracted TopStatsRow, NextGuardianCard, GuardianRoster, GuardianRosterItem, FloorLegend - PrestigeTab: extracted InsightSummary, MemoriesCard, PactsCard, ResetLoopSection - GameStateDebug: extracted WarningBanner, DisplayOptions, GameResetSection, ManaDebugSection, TimeControlSection, QuickActionsSection - EquipmentCrafter: extracted CraftingProgress, BlueprintCard, BlueprintList, MaterialCard, MaterialsInventory - PactDebug: extracted GuardianPactRow, GuardianPactList - GameStateDebugSection: extracted DisplayOptions, GameResetSection, ManaDebugSection, TimeControlSection, QuickActionsSection - PactDebugSection: extracted GuardianPactRow - SpireCombatPage: extracted useSpireStats hook - page.tsx: extracted GrimoireTab to separate file, useGameDerivedStats hook, TabTriggers, LazyTab wrapper All files now under 400 lines. Build passes. All 639 tests pass.
This commit is contained in:
@@ -5,82 +5,21 @@ import { useShallow } from 'zustand/react/shallow';
|
||||
import { useCombatStore, useManaStore, usePrestigeStore, fmt, computeMaxMana, computeRegen } from '@/lib/game/stores';
|
||||
import { computeDisciplineEffects } from '@/lib/game/effects/discipline-effects';
|
||||
import { getUnifiedEffects } from '@/lib/game/effects';
|
||||
import { useDisciplineStore } from '@/lib/game/stores/discipline-slice';
|
||||
import { useCraftingStore } from '@/lib/game/stores/craftingStore';
|
||||
import { GUARDIANS } from '@/lib/game/constants';
|
||||
import { getExtendedGuardian, isGuardianFloor } from '@/lib/game/data/guardian-encounters';
|
||||
import { getRoomsForFloor, generateSpireFloorState, calcInsight } from '@/lib/game/utils/spire-utils';
|
||||
import { getRoomsForFloor, generateSpireFloorState } from '@/lib/game/utils/spire-utils';
|
||||
import { SpireHeader } from './SpireHeader';
|
||||
import { RoomDisplay } from './RoomDisplay';
|
||||
import { SpireCombatControls } from './SpireCombatControls';
|
||||
import { SpireActivityLog } from './SpireActivityLog';
|
||||
import { SpireManaDisplay } from './SpireManaDisplay';
|
||||
|
||||
export function SpireCombatPage() {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [roomsCleared, setRoomsCleared] = useState(0);
|
||||
// ─── Derived Stats Hook ──────────────────────────────────────────────────────
|
||||
|
||||
// Combat store
|
||||
const {
|
||||
currentFloor,
|
||||
floorHP,
|
||||
floorMaxHP,
|
||||
castProgress,
|
||||
clearedFloors,
|
||||
isDescending,
|
||||
currentRoom,
|
||||
activityLog,
|
||||
setCurrentRoom,
|
||||
setFloorHP,
|
||||
setClearedFloor,
|
||||
climbDownFloor,
|
||||
exitSpireMode,
|
||||
startClimbUp,
|
||||
startClimbDown,
|
||||
addActivityLog,
|
||||
processCombatTick,
|
||||
setAction,
|
||||
} = useCombatStore(useShallow((s) => ({
|
||||
currentFloor: s.currentFloor,
|
||||
floorHP: s.floorHP,
|
||||
floorMaxHP: s.floorMaxHP,
|
||||
castProgress: s.castProgress,
|
||||
clearedFloors: s.clearedFloors,
|
||||
isDescending: s.isDescending,
|
||||
currentRoom: s.currentRoom,
|
||||
activityLog: s.activityLog,
|
||||
setCurrentRoom: s.setCurrentRoom,
|
||||
setFloorHP: s.setFloorHP,
|
||||
setClearedFloor: s.setClearedFloor,
|
||||
climbDownFloor: s.climbDownFloor,
|
||||
exitSpireMode: s.exitSpireMode,
|
||||
startClimbUp: s.startClimbUp,
|
||||
startClimbDown: s.startClimbDown,
|
||||
addActivityLog: s.addActivityLog,
|
||||
processCombatTick: s.processCombatTick,
|
||||
setAction: s.setAction,
|
||||
})));
|
||||
|
||||
// Mana store
|
||||
const { rawMana, elements } = useManaStore(useShallow((s) => ({
|
||||
rawMana: s.rawMana,
|
||||
elements: s.elements,
|
||||
})));
|
||||
|
||||
// Prestige store
|
||||
const { prestigeUpgrades, insight } = usePrestigeStore(useShallow((s) => ({
|
||||
prestigeUpgrades: s.prestigeUpgrades,
|
||||
insight: s.insight,
|
||||
})));
|
||||
|
||||
// Crafting store for equipment effects
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
|
||||
// Discipline effects
|
||||
function useSpireStats(prestigeUpgrades: Record<string, number>, equippedInstances: unknown[], equipmentInstances: unknown[]) {
|
||||
const disciplineEffects = computeDisciplineEffects();
|
||||
|
||||
// Compute derived stats
|
||||
const upgradeEffects = getUnifiedEffects({
|
||||
skillUpgrades: {},
|
||||
skillTiers: {},
|
||||
@@ -103,10 +42,70 @@ export function SpireCombatPage() {
|
||||
attunements: {},
|
||||
}, upgradeEffects, disciplineEffects);
|
||||
|
||||
// Total rooms for current floor
|
||||
return { maxMana, baseRegen };
|
||||
}
|
||||
|
||||
// ─── Main Component ───────────────────────────────────────────────────────────
|
||||
|
||||
export function SpireCombatPage() {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [roomsCleared, setRoomsCleared] = useState(0);
|
||||
|
||||
const {
|
||||
currentFloor,
|
||||
floorHP,
|
||||
floorMaxHP,
|
||||
castProgress,
|
||||
clearedFloors,
|
||||
isDescending,
|
||||
currentRoom,
|
||||
activityLog,
|
||||
setCurrentRoom,
|
||||
setFloorHP,
|
||||
setClearedFloor,
|
||||
climbDownFloor,
|
||||
exitSpireMode,
|
||||
startClimbUp,
|
||||
startClimbDown,
|
||||
addActivityLog,
|
||||
setAction,
|
||||
} = useCombatStore(useShallow((s) => ({
|
||||
currentFloor: s.currentFloor,
|
||||
floorHP: s.floorHP,
|
||||
floorMaxHP: s.floorMaxHP,
|
||||
castProgress: s.castProgress,
|
||||
clearedFloors: s.clearedFloors,
|
||||
isDescending: s.isDescending,
|
||||
currentRoom: s.currentRoom,
|
||||
activityLog: s.activityLog,
|
||||
setCurrentRoom: s.setCurrentRoom,
|
||||
setFloorHP: s.setFloorHP,
|
||||
setClearedFloor: s.setClearedFloor,
|
||||
climbDownFloor: s.climbDownFloor,
|
||||
exitSpireMode: s.exitSpireMode,
|
||||
startClimbUp: s.startClimbUp,
|
||||
startClimbDown: s.startClimbDown,
|
||||
addActivityLog: s.addActivityLog,
|
||||
setAction: s.setAction,
|
||||
})));
|
||||
|
||||
const { rawMana, elements } = useManaStore(useShallow((s) => ({
|
||||
rawMana: s.rawMana,
|
||||
elements: s.elements,
|
||||
})));
|
||||
|
||||
const { prestigeUpgrades, insight } = usePrestigeStore(useShallow((s) => ({
|
||||
prestigeUpgrades: s.prestigeUpgrades,
|
||||
insight: s.insight,
|
||||
})));
|
||||
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
|
||||
const { maxMana, baseRegen } = useSpireStats(prestigeUpgrades, equippedInstances, equipmentInstances);
|
||||
|
||||
const totalRooms = useMemo(() => getRoomsForFloor(currentFloor), [currentFloor]);
|
||||
|
||||
// Initialize room on floor change
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
setRoomsCleared(0);
|
||||
@@ -115,12 +114,10 @@ export function SpireCombatPage() {
|
||||
setAction('climb');
|
||||
}, [currentFloor, totalRooms, setCurrentRoom, setAction]);
|
||||
|
||||
// Handle room/floor transitions
|
||||
const handleRoomCleared = () => {
|
||||
const nextRoomIndex = roomsCleared + 1;
|
||||
|
||||
if (nextRoomIndex >= totalRooms) {
|
||||
// Floor cleared
|
||||
const wasGuardian = isGuardianFloor(currentFloor);
|
||||
setClearedFloor(currentFloor, true);
|
||||
|
||||
@@ -138,30 +135,26 @@ export function SpireCombatPage() {
|
||||
floor: currentFloor,
|
||||
});
|
||||
|
||||
// Auto-advance to next floor
|
||||
const newFloor = currentFloor + 1;
|
||||
const newTotalRooms = getRoomsForFloor(newFloor);
|
||||
const newRoom = generateSpireFloorState(newFloor, 0, newTotalRooms);
|
||||
|
||||
setCurrentRoom(newRoom);
|
||||
setFloorHP(floorMaxHP); // Reset HP for new floor
|
||||
setFloorHP(floorMaxHP);
|
||||
setClearedFloor(currentFloor, true);
|
||||
setRoomsCleared(0);
|
||||
} else {
|
||||
// Next room on same floor
|
||||
const newRoom = generateSpireFloorState(currentFloor, nextRoomIndex, totalRooms);
|
||||
setCurrentRoom(newRoom);
|
||||
setRoomsCleared(nextRoomIndex);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle climb up
|
||||
const handleClimbUp = () => {
|
||||
startClimbUp();
|
||||
addActivityLog('floor_transition', `⬆️ Climbing to floor ${currentFloor + 1}...`);
|
||||
};
|
||||
|
||||
// Handle climb down
|
||||
const handleClimbDown = () => {
|
||||
if (currentFloor <= 1) return;
|
||||
startClimbDown();
|
||||
@@ -170,7 +163,6 @@ export function SpireCombatPage() {
|
||||
addActivityLog('floor_transition', `⬇️ Descending to floor ${currentFloor - 1}...`);
|
||||
};
|
||||
|
||||
// Handle exit spire
|
||||
const handleExitSpire = () => {
|
||||
exitSpireMode();
|
||||
addActivityLog('floor_transition', '🚪 Exited the Spire.');
|
||||
@@ -186,7 +178,6 @@ export function SpireCombatPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-950 flex flex-col">
|
||||
{/* Compact header */}
|
||||
<header className="sticky top-0 z-50 bg-gray-900/95 border-b border-gray-800 px-4 py-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-lg font-bold text-amber-400 tracking-wider">🏔️ SPIRE</h1>
|
||||
@@ -196,9 +187,7 @@ export function SpireCombatPage() {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main content */}
|
||||
<main className="flex-1 p-4 space-y-4 max-w-7xl mx-auto w-full">
|
||||
{/* Top section: Header + Mana */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<div className="lg:col-span-2">
|
||||
<SpireHeader
|
||||
@@ -218,7 +207,6 @@ export function SpireCombatPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Middle section: Room + Controls */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<div className="lg:col-span-2">
|
||||
<RoomDisplay floorState={currentRoom} floor={currentFloor} />
|
||||
@@ -228,7 +216,6 @@ export function SpireCombatPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom: Activity Log */}
|
||||
<SpireActivityLog activityLog={activityLog} maxEntries={20} />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user