feat: recreate Achievements tab with category sections, progress tracking, and hidden achievement logic
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s

This commit is contained in:
2026-05-19 14:44:27 +02:00
parent 50a9a62060
commit 639d396f80
15 changed files with 1396 additions and 4 deletions
@@ -0,0 +1,235 @@
'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';
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 getMaxMana = () => {
return computeMaxMana(
{ skills: {}, prestigeUpgrades: {}, skillUpgrades: {}, skillTiers: {} }
);
};
return (
<div className="space-y-4">
{/* Display Options */}
<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>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Game Reset */}
<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={handleReset}
>
{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 */}
<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} / {getMaxMana() || '?'}
</div>
<div className="flex gap-2 flex-wrap">
<Button size="sm" variant="outline" onClick={() => handleAddMana(10)}>
<Zap className="w-3 h-3 mr-1" /> +10
</Button>
<Button size="sm" variant="outline" onClick={() => handleAddMana(100)}>
<Zap className="w-3 h-3 mr-1" /> +100
</Button>
<Button size="sm" variant="outline" onClick={() => handleAddMana(1000)}>
<Zap className="w-3 h-3 mr-1" /> +1K
</Button>
<Button size="sm" variant="outline" onClick={() => handleAddMana(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={() => {
const max = getMaxMana() || 100;
useManaStore.setState((s) => ({ rawMana: Math.max(s.rawMana, max) }));
}}
>
Fill Mana
</Button>
</CardContent>
</Card>
{/* Time Control */}
<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={() => useGameStore.setState({ day: 1, hour: 0 })}>
Day 1
</Button>
<Button size="sm" variant="outline" onClick={() => useGameStore.setState({ day: 10, hour: 0 })}>
Day 10
</Button>
<Button size="sm" variant="outline" onClick={() => useGameStore.setState({ day: 20, hour: 0 })}>
Day 20
</Button>
<Button size="sm" variant="outline" onClick={() => useGameStore.setState({ day: 30, hour: 0 })}>
Day 30
</Button>
</div>
<Separator className="bg-gray-700" />
<div className="flex gap-2">
<Button size="sm" variant="outline" onClick={togglePause}>
{paused ? '▶ Resume' : '⏸ Pause'}
</Button>
</div>
</CardContent>
</Card>
{/* Quick Actions */}
<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={() => {
['fire', 'water', 'air', 'earth', 'light', 'dark', 'death'].forEach(e => {
if (!elements[e]?.unlocked) {
unlockElement(e, 0);
}
});
}}
>
Unlock All Base Elements
</Button>
<Button
size="sm"
variant="outline"
onClick={() => debugSetFloor?.(100)}
>
Skip to Floor 100
</Button>
<Button
size="sm"
variant="outline"
onClick={() => resetFloorHP?.()}
>
Reset Floor HP
</Button>
</div>
</CardContent>
</Card>
</div>
</div>
);
}
GameStateDebugSection.displayName = "GameStateDebugSection";