'use client'; import React, { useState, useCallback, useMemo } from 'react'; import { useShallow } from 'zustand/react/shallow'; import { useCombatStore } from '@/lib/game/stores/combatStore'; import { useAttunementStore } from '@/lib/game/stores/attunementStore'; import { useManaStore } from '@/lib/game/stores/manaStore'; import { GOLEMS_DEF, isGolemUnlocked, canAffordGolemSummon, getGolemSlots } from '@/lib/game/data/golems'; import type { GolemDef } from '@/lib/game/data/golems'; import { ELEMENTS } from '@/lib/game/constants/elements'; import { DebugName } from '@/components/game/debug/debug-context'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import clsx from 'clsx'; // ─── Tier configuration ────────────────────────────────────────────────────── interface TierConfig { key: string; label: string; tier: number; } const TIERS: TierConfig[] = [ { key: 'base', label: 'Base', tier: 1 }, { key: 'elemental', label: 'Elemental', tier: 2 }, { key: 'hybrid', label: 'Hybrid', tier: 3 }, ]; function getTierLabel(tier: number): string { if (tier <= 1) return 'Base'; if (tier <= 2) return 'Elemental'; return 'Hybrid'; } function getTierColor(tier: number): string { if (tier <= 1) return 'bg-gray-600'; if (tier <= 2) return 'bg-blue-600'; if (tier <= 3) return 'bg-purple-600'; return 'bg-amber-500'; } // ─── Helpers ───────────────────────────────────────────────────────────────── function formatCost(cost: GolemDef['summonCost'][number]): string { if (cost.type === 'raw') return `${cost.amount} raw`; const elem = cost.element ? ELEMENTS[cost.element] : null; return `${cost.amount} ${elem?.sym ?? ''} ${cost.element ?? ''}`.trim(); } function formatUnlockCondition(golem: GolemDef): string { const cond = golem.unlockCondition; switch (cond.type) { case 'attunement_level': return `${cond.attunement} level ${cond.level}`; case 'mana_unlocked': { const elem = cond.manaType ? ELEMENTS[cond.manaType] : null; return `Unlock ${elem?.sym ?? ''} ${cond.manaType ?? ''}`.trim(); } case 'dual_attunement': return `${cond.attunements?.join(' + ')} level ${cond.levels?.join('/')}`; default: return 'Unknown'; } } // ─── Golem Card ────────────────────────────────────────────────────────────── interface GolemCardProps { golem: GolemDef; unlocked: boolean; enabled: boolean; summoned: boolean; canAfford: boolean; onToggle: (id: string) => void; } const GolemCard: React.FC = React.memo(({ golem, unlocked, enabled, summoned, canAfford, onToggle, }) => { const elemColor = ELEMENTS[golem.baseManaType]?.color ?? '#888'; const elemSym = ELEMENTS[golem.baseManaType]?.sym ?? ''; return (
{/* Header */}

{golem.name}

{golem.description}

{elemSym} T{golem.tier}
{/* Stats grid */}
DMG: {golem.damage}
SPD: {golem.attackSpeed}/h
HP: {golem.hp}
AP: {Math.round(golem.armorPierce * 100)}%
{golem.isAoe && (
AoE: {golem.aoeTargets} targets
)}
{/* Costs */}
Summon:{' '} {golem.summonCost.map((c, i) => ( {formatCost(c)}{i < golem.summonCost.length - 1 ? ' + ' : ''} ))}
Upkeep:{' '} {golem.maintenanceCost.map((c, i) => ( {formatCost(c)}{i < golem.maintenanceCost.length - 1 ? ' + ' : ''} ))}/tick
{/* Unlock requirement */} {!unlocked && (
🔒 Requires: {formatUnlockCondition(golem)}
)} {/* Status + toggle */}
{summoned ? ( ● Summoned ) : enabled ? ( ○ Queued ) : ( — Idle )}
); }); GolemCard.displayName = 'GolemCard'; // ─── Main Tab ──────────────────────────────────────────────────────────────── export const GolemancyTab: React.FC = () => { const [activeTier, setActiveTier] = useState('base'); const { golemancy, toggleGolem } = useCombatStore(useShallow(s => ({ golemancy: s.golemancy, toggleGolem: s.toggleGolem, }))); const attunements = useAttunementStore(s => s.attunements); const { rawMana, elements } = useManaStore(useShallow(s => ({ rawMana: s.rawMana, elements: s.elements, }))); // Build attunement lookup for isGolemUnlocked const attunementLookup = useMemo(() => { const lookup: Record = {}; for (const [id, att] of Object.entries(attunements)) { lookup[id] = { active: att.active, level: att.level }; } return lookup; }, [attunements]); const unlockedElements = useMemo( () => Object.entries(elements).filter(([, e]) => e.unlocked).map(([k]) => k), [elements], ); // Group golems by tier const golemsByTier = useMemo(() => { const groups: Record = { base: [], elemental: [], hybrid: [] }; for (const golem of Object.values(GOLEMS_DEF)) { const label = getTierLabel(golem.tier); const key = label.toLowerCase(); if (groups[key]) { groups[key].push(golem); } else { // tier 4 golems go into hybrid groups.hybrid.push(golem); } } return groups; }, []); const handleToggle = useCallback((id: string) => { toggleGolem(id); }, [toggleGolem]); // Golem slot info const fabricatorLevel = attunements.fabricator?.level ?? 0; const golemSlots = getGolemSlots(fabricatorLevel); const enabledCount = golemancy.enabledGolems.length; const activeTierGolems = golemsByTier[activeTier] ?? []; return (
{/* Header info */}

Configure your golem loadout. Enabled golems are automatically summoned when entering the spire if you can afford the cost.

Slots: {enabledCount}/{golemSlots > 0 ? golemSlots : '—'} Summoned: {golemancy.summonedGolems.length}
{/* Tier tabs */}
{TIERS.map((tier) => { const count = golemsByTier[tier.key]?.length ?? 0; return ( ); })}
{/* Golem cards */} {activeTierGolems.length === 0 ? (
No golems in this tier.
) : (
{activeTierGolems.map((golem) => { const unlocked = isGolemUnlocked(golem.id, attunementLookup, unlockedElements); const enabled = golemancy.enabledGolems.includes(golem.id); const summoned = golemancy.summonedGolems.some(g => g.golemId === golem.id); const canAfford = canAffordGolemSummon(golem.id, rawMana, elements); return ( ); })}
)}
); }; GolemancyTab.displayName = 'GolemancyTab';