129 lines
5.4 KiB
TypeScript
129 lines
5.4 KiB
TypeScript
'use client';
|
||
|
||
import { canAffordSpellCost } from '@/lib/game/stores';
|
||
import { useCombatStore, useManaStore } from '@/lib/game/stores';
|
||
import { ELEMENTS, SPELLS_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';
|
||
|
||
// Format spell cost for display
|
||
function formatSpellCost(cost: { type: 'raw' | 'element'; element?: string; amount: number }): string {
|
||
if (cost.type === 'raw') {
|
||
return `${cost.amount} raw`;
|
||
}
|
||
const elemDef = ELEMENTS[cost.element || ''];
|
||
return `${cost.amount} ${elemDef?.sym || '?'}`;
|
||
}
|
||
|
||
// Get cost color
|
||
function getSpellCostColor(cost: { type: 'raw' | 'element'; element?: string; amount: number }): string {
|
||
if (cost.type === 'raw') {
|
||
return '#60A5FA';
|
||
}
|
||
return ELEMENTS[cost.element || '']?.color || '#9CA3AF';
|
||
}
|
||
|
||
export function SpellsTab() {
|
||
const spells = useCombatStore((s) => s.spells);
|
||
const activeSpell = useCombatStore((s) => s.activeSpell);
|
||
const setSpell = useCombatStore((s) => s.setSpell);
|
||
const rawMana = useManaStore((s) => s.rawMana);
|
||
const elements = useManaStore((s) => s.elements);
|
||
|
||
const spellTiers = [0, 1, 2, 3, 4];
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
{spellTiers.map(tier => {
|
||
const spellsInTier = Object.entries(SPELLS_DEF).filter(([, def]) => def.tier === tier);
|
||
if (spellsInTier.length === 0) return null;
|
||
|
||
const tierNames = ['Basic Spells (Raw Mana)', 'Tier 1 - Elemental', 'Tier 2 - Advanced', 'Tier 3 - Master', 'Tier 4 - Legendary'];
|
||
const tierColors = ['text-gray-400', 'text-green-400', 'text-blue-400', 'text-purple-400', 'text-amber-400'];
|
||
|
||
return (
|
||
<div key={tier}>
|
||
<h3 className={`text-lg font-semibold mb-3 ${tierColors[tier]}`}>{tierNames[tier]}</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
{spellsInTier.map(([id, def]) => {
|
||
const state = spells?.[id];
|
||
const learned = state?.learned;
|
||
const elemDef = def.elem === 'raw' ? null : ELEMENTS[def.elem];
|
||
const isActive = activeSpell === id;
|
||
const canCast = learned && canAffordSpellCost(def.cost, rawMana, elements);
|
||
|
||
return (
|
||
<Card
|
||
key={id}
|
||
className={`bg-gray-900/80 border-gray-700 ${learned ? '' : 'opacity-75'} ${canCast ? 'ring-1 ring-green-500/30' : ''}`}
|
||
>
|
||
<CardHeader className="pb-2">
|
||
<div className="flex items-center justify-between">
|
||
<CardTitle className="text-sm game-panel-title" style={{ color: def.elem === 'raw' ? '#60A5FA' : elemDef?.color }}>
|
||
{def.name}
|
||
</CardTitle>
|
||
{def.tier > 0 && (
|
||
<Badge variant="outline" className="text-xs">
|
||
T{def.tier}
|
||
</Badge>
|
||
)}
|
||
</div>
|
||
</CardHeader>
|
||
<CardContent className="space-y-2">
|
||
<div className="text-xs text-gray-400">
|
||
{def.elem !== 'raw' && <span className="mr-2">{elemDef?.sym} {elemDef?.name}</span>}
|
||
<span className="mr-2">⚔️ {def.dmg} dmg</span>
|
||
</div>
|
||
|
||
{/* Cost display */}
|
||
<div className="text-xs game-mono" style={{ color: getSpellCostColor(def.cost) }}>
|
||
Cost: {formatSpellCost(def.cost)}
|
||
</div>
|
||
|
||
{def.desc && (
|
||
<div className="text-xs text-gray-500 italic">{def.desc}</div>
|
||
)}
|
||
|
||
{def.effects && Array.isArray(def.effects) && def.effects.length > 0 && (
|
||
<div className="flex gap-1 flex-wrap">
|
||
{def.effects.map((eff, i) => (
|
||
<Badge key={i} variant="outline" className="text-xs">
|
||
{eff.type === 'burn' && `🔥 Burn`}
|
||
{eff.type === 'stun' && `⚡ Stun`}
|
||
{eff.type === 'pierce' && `🎯 Pierce`}
|
||
{eff.type === 'multicast' && `✨ Multicast`}
|
||
</Badge>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{learned ? (
|
||
<div className="flex gap-2">
|
||
<Badge className="bg-green-900/50 text-green-300">Learned</Badge>
|
||
{isActive && <Badge className="bg-amber-900/50 text-amber-300">Active</Badge>}
|
||
{!isActive && (
|
||
<Button size="sm" variant="outline" onClick={() => setSpell(id)}>
|
||
Set Active
|
||
</Button>
|
||
)}
|
||
</div>
|
||
) : (
|
||
<div className="text-xs text-gray-500">
|
||
Not yet learned
|
||
</div>
|
||
)}
|
||
</CardContent>
|
||
</Card>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
SpellsTab.displayName = "SpellsTab";
|