Initial commit
This commit is contained in:
Executable
+193
@@ -0,0 +1,193 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user