4103423b95
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m23s
- GameStateDebugSection.tsx: use real prestigeUpgrades, equipment, and unified effects instead of empty objects always returning base 100 - GameStateDebug.tsx (legacy): same fix - Both now compute max mana identically to LeftPanel.tsx Fixes #242 (closes incorrect #237 - mana wasn't exceeding cap)
302 lines
11 KiB
TypeScript
302 lines
11 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, Settings, Eye,
|
|
} from 'lucide-react';
|
|
import { DebugName, useDebug } from '@/components/game/debug/debug-context';
|
|
import { useGameStore, useManaStore, useUIStore, usePrestigeStore, useCraftingStore } from '@/lib/game/stores';
|
|
import { computeTotalMaxMana } from '@/lib/game/effects';
|
|
import { getUnifiedEffects } from '@/lib/game/effects';
|
|
|
|
// ─── Warning Banner ──────────────────────────────────────────────────────────
|
|
|
|
function WarningBanner() {
|
|
return (
|
|
<Card className="bg-amber-900/20 border-amber-600/50">
|
|
<CardContent className="pt-4">
|
|
<div className="flex items-center gap-2 text-amber-400">
|
|
<AlertTriangle className="w-5 h-5" />
|
|
<span className="font-semibold">Debug Mode</span>
|
|
</div>
|
|
<p className="text-sm text-amber-300/70 mt-1">
|
|
These tools are for development and testing. Using them may break game balance or save data.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
// ─── 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, maxMana, onAddMana, onFillMana }: {
|
|
rawMana: number;
|
|
maxMana: number;
|
|
onAddMana: (amount: number) => void;
|
|
onFillMana: () => void;
|
|
}) {
|
|
|
|
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 {Number.isFinite(hour) ? hour.toFixed(2) : '0.00'}
|
|
</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({ onUnlockBase, onUnlockUtility }: {
|
|
onUnlockBase: () => void;
|
|
onUnlockUtility: () => void;
|
|
}) {
|
|
return (
|
|
<Card className="bg-gray-900/80 border-gray-700 md:col-span-2">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-cyan-400 text-sm flex items-center gap-2">
|
|
<Settings 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={onUnlockUtility}>
|
|
Unlock Utility Elements
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
// ─── Main Component ───────────────────────────────────────────────────────────
|
|
|
|
export function GameStateDebug() {
|
|
const [confirmReset, setConfirmReset] = useState(false);
|
|
const { showComponentNames, toggleComponentNames } = useDebug();
|
|
|
|
const rawMana = useManaStore((s) => s.rawMana);
|
|
const elements = useManaStore((s) => s.elements);
|
|
const unlockElement = useManaStore((s) => s.unlockElement);
|
|
const gatherMana = useGameStore((s) => s.gatherMana);
|
|
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 prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
|
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
|
|
|
|
|
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 upgradeEffects = getUnifiedEffects({ skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances });
|
|
const computedMaxMana = computeTotalMaxMana(
|
|
{ skills: {}, prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances },
|
|
upgradeEffects
|
|
);
|
|
|
|
const handleFillMana = () => {
|
|
useManaStore.setState((s) => ({ rawMana: Math.max(s.rawMana, computedMaxMana) }));
|
|
};
|
|
|
|
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, 500);
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleUnlockUtility = () => {
|
|
['transference'].forEach(e => {
|
|
if (!elements[e]?.unlocked) {
|
|
unlockElement(e, 500);
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
<DebugName name="GameStateDebug">
|
|
<div className="space-y-4">
|
|
<WarningBanner />
|
|
<DisplayOptions />
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<GameResetSection confirmReset={confirmReset} onReset={handleReset} />
|
|
<ManaDebugSection rawMana={rawMana} maxMana={computedMaxMana} onAddMana={handleAddMana} onFillMana={handleFillMana} />
|
|
<TimeControlSection day={day} hour={hour} paused={paused} onSetDay={handleSetDay} onTogglePause={togglePause} />
|
|
<QuickActionsSection
|
|
onUnlockBase={handleUnlockBase}
|
|
onUnlockUtility={handleUnlockUtility}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</DebugName>
|
|
);
|
|
}
|
|
|
|
GameStateDebug.displayName = 'GameStateDebug';
|