194 lines
8.5 KiB
TypeScript
Executable File
194 lines
8.5 KiB
TypeScript
Executable File
'use client';
|
||
|
||
import { useGameStore, fmt, fmtDec, computePactMultiplier } from '@/lib/game/store';
|
||
import { ELEMENTS, GUARDIANS, PRESTIGE_DEF } from '@/lib/game/constants';
|
||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||
import { Badge } from '@/components/ui/badge';
|
||
import { Button } from '@/components/ui/button';
|
||
import { RotateCcw } from 'lucide-react';
|
||
|
||
export function GrimoireTab() {
|
||
const store = useGameStore();
|
||
|
||
return (
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||
{/* Current Status */}
|
||
<Card className="bg-gray-900/80 border-gray-700">
|
||
<CardHeader className="pb-2">
|
||
<CardTitle className="text-amber-400 game-panel-title text-xs">Loop Status</CardTitle>
|
||
</CardHeader>
|
||
<CardContent className="space-y-3">
|
||
<div className="grid grid-cols-2 gap-3">
|
||
<div className="p-3 bg-gray-800/50 rounded">
|
||
<div className="text-2xl font-bold text-amber-400 game-mono">{store.loopCount}</div>
|
||
<div className="text-xs text-gray-400">Loops Completed</div>
|
||
</div>
|
||
<div className="p-3 bg-gray-800/50 rounded">
|
||
<div className="text-2xl font-bold text-purple-400 game-mono">{fmt(store.insight)}</div>
|
||
<div className="text-xs text-gray-400">Current Insight</div>
|
||
</div>
|
||
<div className="p-3 bg-gray-800/50 rounded">
|
||
<div className="text-2xl font-bold text-blue-400 game-mono">{fmt(store.totalInsight)}</div>
|
||
<div className="text-xs text-gray-400">Total Insight</div>
|
||
</div>
|
||
<div className="p-3 bg-gray-800/50 rounded">
|
||
<div className="text-2xl font-bold text-green-400 game-mono">{store.memorySlots}</div>
|
||
<div className="text-xs text-gray-400">Memory Slots</div>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Signed Pacts */}
|
||
<Card className="bg-gray-900/80 border-gray-700 lg:col-span-2">
|
||
<CardHeader className="pb-2">
|
||
<div className="flex items-center justify-between">
|
||
<CardTitle className="text-amber-400 game-panel-title text-xs">Signed Pacts ({store.signedPacts.length}/{1 + (store.prestigeUpgrades.pactCapacity || 0)})</CardTitle>
|
||
{store.signedPacts.length > 1 && (
|
||
<div className="text-xs text-gray-400">
|
||
Combined: ×{fmtDec(computePactMultiplier(store), 2)} damage
|
||
</div>
|
||
)}
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent>
|
||
{store.signedPacts.length === 0 ? (
|
||
<div className="text-gray-500 text-sm">No pacts signed yet. Defeat guardians to earn pacts.</div>
|
||
) : (
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||
{store.signedPacts.map((floor) => {
|
||
const guardian = GUARDIANS[floor];
|
||
if (!guardian) return null;
|
||
return (
|
||
<div
|
||
key={floor}
|
||
className="p-3 rounded border"
|
||
style={{ borderColor: guardian.color, backgroundColor: `${guardian.color}10` }}
|
||
>
|
||
<div className="flex items-center justify-between mb-2">
|
||
<div>
|
||
<div className="font-semibold text-sm" style={{ color: guardian.color }}>
|
||
{guardian.name}
|
||
</div>
|
||
<div className="text-xs text-gray-400">{guardian.theme} • Floor {floor}</div>
|
||
</div>
|
||
<Badge style={{ backgroundColor: `${guardian.color}30`, color: guardian.color }}>
|
||
{guardian.damageMultiplier}x dmg / {guardian.insightMultiplier}x insight
|
||
</Badge>
|
||
</div>
|
||
|
||
{/* Unique Boon */}
|
||
{guardian.uniqueBoon && (
|
||
<div className="mb-2 p-2 bg-cyan-900/20 rounded border border-cyan-800/30">
|
||
<div className="text-xs font-semibold text-cyan-300">✨ {guardian.uniqueBoon.name}</div>
|
||
<div className="text-xs text-cyan-200/70">{guardian.uniqueBoon.desc}</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Perks & Costs */}
|
||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||
{guardian.perks.length > 0 && (
|
||
<div>
|
||
<div className="text-green-400 font-semibold mb-1">Perks</div>
|
||
{guardian.perks.map(perk => (
|
||
<div key={perk.id} className="text-green-300/70">• {perk.desc}</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
{guardian.costs.length > 0 && (
|
||
<div>
|
||
<div className="text-red-400 font-semibold mb-1">Costs</div>
|
||
{guardian.costs.map(cost => (
|
||
<div key={cost.id} className="text-red-300/70">• {cost.desc}</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Unlocked Mana */}
|
||
{guardian.unlocksMana.length > 0 && (
|
||
<div className="mt-2 flex flex-wrap gap-1">
|
||
{guardian.unlocksMana.map(elemId => {
|
||
const elem = ELEMENTS[elemId];
|
||
return (
|
||
<Badge key={elemId} variant="outline" className="text-xs" style={{ borderColor: elem?.color, color: elem?.color }}>
|
||
{elem?.sym}
|
||
</Badge>
|
||
);
|
||
})}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
)}
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Prestige Upgrades */}
|
||
<Card className="bg-gray-900/80 border-gray-700 lg:col-span-2">
|
||
<CardHeader className="pb-2">
|
||
<CardTitle className="text-amber-400 game-panel-title text-xs">Insight Upgrades (Permanent)</CardTitle>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||
{Object.entries(PRESTIGE_DEF).map(([id, def]) => {
|
||
const level = store.prestigeUpgrades[id] || 0;
|
||
const maxed = level >= def.max;
|
||
const canBuy = !maxed && store.insight >= def.cost;
|
||
|
||
return (
|
||
<div
|
||
key={id}
|
||
className="p-3 rounded border border-gray-700 bg-gray-800/50"
|
||
>
|
||
<div className="flex items-center justify-between mb-2">
|
||
<div className="font-semibold text-amber-400 text-sm">{def.name}</div>
|
||
<Badge variant="outline" className="text-xs">
|
||
{level}/{def.max}
|
||
</Badge>
|
||
</div>
|
||
<div className="text-xs text-gray-400 italic mb-2">{def.desc}</div>
|
||
<Button
|
||
size="sm"
|
||
variant={canBuy ? 'default' : 'outline'}
|
||
className="w-full"
|
||
disabled={!canBuy}
|
||
onClick={() => store.doPrestige(id)}
|
||
>
|
||
{maxed ? 'Maxed' : `Upgrade (${fmt(def.cost)} insight)`}
|
||
</Button>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* Reset Game Button */}
|
||
<div className="mt-4 pt-4 border-t border-gray-700">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<div className="text-sm text-gray-400">Reset All Progress</div>
|
||
<div className="text-xs text-gray-500">Clear all data and start fresh</div>
|
||
</div>
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
className="border-red-600/50 text-red-400 hover:bg-red-900/20"
|
||
onClick={() => {
|
||
if (confirm('Are you sure you want to reset ALL progress? This cannot be undone!')) {
|
||
store.resetGame();
|
||
}
|
||
}}
|
||
>
|
||
<RotateCcw className="w-4 h-4 mr-1" />
|
||
Reset
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
);
|
||
}
|