ce084a61a3
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.
275 lines
10 KiB
TypeScript
275 lines
10 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Separator } from '@/components/ui/separator';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import { Label } from '@/components/ui/label';
|
|
import {
|
|
RotateCcw, AlertTriangle, Zap, Clock, Eye,
|
|
} from 'lucide-react';
|
|
import { useDebug } from '@/components/game/debug/debug-context';
|
|
import { useGameStore, useManaStore, useUIStore, useCombatStore } from '@/lib/game/stores';
|
|
import { computeMaxMana } from '@/lib/game/stores';
|
|
|
|
// ─── Display Options ─────────────────────────────────────────────────────────
|
|
|
|
function DisplayOptions() {
|
|
const { showComponentNames, toggleComponentNames } = useDebug();
|
|
|
|
return (
|
|
<Card className="bg-gray-900/80 border-gray-700">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-cyan-400 text-sm flex items-center gap-2">
|
|
<Eye className="w-4 h-4" />
|
|
Display Options
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label htmlFor="show-component-names" className="text-sm">Show Component Names</Label>
|
|
<p className="text-xs text-gray-400">
|
|
Display component names at the top of each component for debugging
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
id="show-component-names"
|
|
checked={showComponentNames}
|
|
onCheckedChange={toggleComponentNames}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
// ─── Game Reset Section ──────────────────────────────────────────────────────
|
|
|
|
function GameResetSection({ confirmReset, onReset }: { confirmReset: boolean; onReset: () => void }) {
|
|
return (
|
|
<Card className="bg-gray-900/80 border-gray-700">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-red-400 text-sm flex items-center gap-2">
|
|
<RotateCcw className="w-4 h-4" />
|
|
Game Reset
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<p className="text-xs text-gray-400">
|
|
Reset all game progress and start fresh. This cannot be undone.
|
|
</p>
|
|
<Button
|
|
className={`w-full ${confirmReset ? 'bg-red-600 hover:bg-red-700' : 'bg-gray-700 hover:bg-gray-600'}`}
|
|
onClick={onReset}
|
|
>
|
|
{confirmReset ? (
|
|
<>
|
|
<AlertTriangle className="w-4 h-4 mr-2" />
|
|
Click Again to Confirm Reset
|
|
</>
|
|
) : (
|
|
<>
|
|
<RotateCcw className="w-4 h-4 mr-2" />
|
|
Reset Game
|
|
</>
|
|
)}
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
// ─── Mana Debug Section ──────────────────────────────────────────────────────
|
|
|
|
function ManaDebugSection({ rawMana, onAddMana, onFillMana }: {
|
|
rawMana: number;
|
|
onAddMana: (amount: number) => void;
|
|
onFillMana: () => void;
|
|
}) {
|
|
const maxMana = computeMaxMana(
|
|
{ skills: {}, prestigeUpgrades: {}, skillUpgrades: {}, skillTiers: {} }
|
|
);
|
|
|
|
return (
|
|
<Card className="bg-gray-900/80 border-gray-700">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-blue-400 text-sm flex items-center gap-2">
|
|
<Zap className="w-4 h-4" />
|
|
Mana Debug
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<div className="text-xs text-gray-400 mb-2">
|
|
Current: {rawMana} / {maxMana || '?'}
|
|
</div>
|
|
<div className="flex gap-2 flex-wrap">
|
|
<Button size="sm" variant="outline" onClick={() => onAddMana(10)}>
|
|
<Zap className="w-3 h-3 mr-1" /> +10
|
|
</Button>
|
|
<Button size="sm" variant="outline" onClick={() => onAddMana(100)}>
|
|
<Zap className="w-3 h-3 mr-1" /> +100
|
|
</Button>
|
|
<Button size="sm" variant="outline" onClick={() => onAddMana(1000)}>
|
|
<Zap className="w-3 h-3 mr-1" /> +1K
|
|
</Button>
|
|
<Button size="sm" variant="outline" onClick={() => onAddMana(10000)}>
|
|
<Zap className="w-3 h-3 mr-1" /> +10K
|
|
</Button>
|
|
</div>
|
|
<Separator className="bg-gray-700" />
|
|
<div className="text-xs text-gray-400 mb-2">Fill to max:</div>
|
|
<Button size="sm" className="w-full bg-blue-600 hover:bg-blue-700" onClick={onFillMana}>
|
|
Fill Mana
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
// ─── Time Control Section ────────────────────────────────────────────────────
|
|
|
|
function TimeControlSection({ day, hour, paused, onSetDay, onTogglePause }: {
|
|
day: number;
|
|
hour: number;
|
|
paused: boolean;
|
|
onSetDay: (day: number) => void;
|
|
onTogglePause: () => void;
|
|
}) {
|
|
return (
|
|
<Card className="bg-gray-900/80 border-gray-700">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-amber-400 text-sm flex items-center gap-2">
|
|
<Clock className="w-4 h-4" />
|
|
Time Control
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
<div className="text-xs text-gray-400">
|
|
Current: Day {day}, Hour {hour}
|
|
</div>
|
|
<div className="flex gap-2 flex-wrap">
|
|
<Button size="sm" variant="outline" onClick={() => onSetDay(1)}>Day 1</Button>
|
|
<Button size="sm" variant="outline" onClick={() => onSetDay(10)}>Day 10</Button>
|
|
<Button size="sm" variant="outline" onClick={() => onSetDay(20)}>Day 20</Button>
|
|
<Button size="sm" variant="outline" onClick={() => onSetDay(30)}>Day 30</Button>
|
|
</div>
|
|
<Separator className="bg-gray-700" />
|
|
<div className="flex gap-2">
|
|
<Button size="sm" variant="outline" onClick={onTogglePause}>
|
|
{paused ? '▶ Resume' : '⏸ Pause'}
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
// ─── Quick Actions Section ───────────────────────────────────────────────────
|
|
|
|
function QuickActionsSection({ elements, onUnlockBase, onSkipToFloor, onResetFloorHP }: {
|
|
elements: Record<string, { unlocked?: boolean }>;
|
|
onUnlockBase: () => void;
|
|
onSkipToFloor: () => void;
|
|
onResetFloorHP: () => void;
|
|
}) {
|
|
return (
|
|
<Card className="bg-gray-900/80 border-gray-700">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-cyan-400 text-sm flex items-center gap-2">
|
|
<Zap className="w-4 h-4" />
|
|
Quick Actions
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex gap-2 flex-wrap">
|
|
<Button size="sm" variant="outline" onClick={onUnlockBase}>
|
|
Unlock All Base Elements
|
|
</Button>
|
|
<Button size="sm" variant="outline" onClick={onSkipToFloor}>
|
|
Skip to Floor 100
|
|
</Button>
|
|
<Button size="sm" variant="outline" onClick={onResetFloorHP}>
|
|
Reset Floor HP
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
// ─── Main Component ───────────────────────────────────────────────────────────
|
|
|
|
export function GameStateDebugSection() {
|
|
const [confirmReset, setConfirmReset] = useState(false);
|
|
const { showComponentNames, toggleComponentNames } = useDebug();
|
|
|
|
const rawMana = useManaStore((s) => s.rawMana);
|
|
const day = useGameStore((s) => s.day);
|
|
const hour = useGameStore((s) => s.hour);
|
|
const paused = useUIStore((s) => s.paused);
|
|
const togglePause = useUIStore((s) => s.togglePause);
|
|
const resetGame = useGameStore((s) => s.resetGame);
|
|
const gatherMana = useGameStore((s) => s.gatherMana);
|
|
const debugSetFloor = useCombatStore((s) => s.debugSetFloor);
|
|
const resetFloorHP = useCombatStore((s) => s.resetFloorHP);
|
|
const elements = useManaStore((s) => s.elements);
|
|
const unlockElement = useManaStore((s) => s.unlockElement);
|
|
|
|
const handleReset = () => {
|
|
if (confirmReset) {
|
|
resetGame();
|
|
setConfirmReset(false);
|
|
} else {
|
|
setConfirmReset(true);
|
|
setTimeout(() => setConfirmReset(false), 3000);
|
|
}
|
|
};
|
|
|
|
const handleAddMana = (amount: number) => {
|
|
for (let i = 0; i < amount; i++) {
|
|
gatherMana();
|
|
}
|
|
};
|
|
|
|
const handleFillMana = () => {
|
|
const maxMana = computeMaxMana(
|
|
{ skills: {}, prestigeUpgrades: {}, skillUpgrades: {}, skillTiers: {} }
|
|
) || 100;
|
|
useManaStore.setState((s) => ({ rawMana: Math.max(s.rawMana, maxMana) }));
|
|
};
|
|
|
|
const handleSetDay = (d: number) => {
|
|
useGameStore.setState({ day: d, hour: 0 });
|
|
};
|
|
|
|
const handleUnlockBase = () => {
|
|
['fire', 'water', 'air', 'earth', 'light', 'dark', 'death'].forEach(e => {
|
|
if (!elements[e]?.unlocked) {
|
|
unlockElement(e, 0);
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<DisplayOptions />
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<GameResetSection confirmReset={confirmReset} onReset={handleReset} />
|
|
<ManaDebugSection rawMana={rawMana} onAddMana={handleAddMana} onFillMana={handleFillMana} />
|
|
<TimeControlSection day={day} hour={hour} paused={paused} onSetDay={handleSetDay} onTogglePause={togglePause} />
|
|
<QuickActionsSection
|
|
elements={elements}
|
|
onUnlockBase={handleUnlockBase}
|
|
onSkipToFloor={() => debugSetFloor?.(100)}
|
|
onResetFloorHP={() => resetFloorHP?.()}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
GameStateDebugSection.displayName = 'GameStateDebugSection';
|