'use client'; import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; import { TooltipProvider } from '@/components/ui/tooltip'; import { Swords, BookOpen, ChevronUp, ChevronDown, RotateCcw, X } from 'lucide-react'; import type { GameStore } from '@/lib/game/types'; import { ELEMENTS, GUARDIANS, SPELLS_DEF, SKILLS_DEF, GOLEM_DEFS, GOLEM_VARIANTS } from '@/lib/game/constants'; import { fmt, fmtDec, getFloorElement, canAffordSpellCost } from '@/lib/game/store'; import { getActiveEquipmentSpells, getTotalDPS } from '@/lib/game/computed-stats'; import { formatSpellCost, getSpellCostColor, formatStudyTime } from '@/lib/game/formatting'; import { CraftingProgress, StudyProgress } from '@/components/game'; import { getUnifiedEffects } from '@/lib/game/effects'; interface SpireTabProps { store: GameStore; } export function SpireTab({ store }: SpireTabProps) { const floorElem = getFloorElement(store.currentFloor); const floorElemDef = ELEMENTS[floorElem]; const isGuardianFloor = !!GUARDIANS[store.currentFloor]; const currentGuardian = GUARDIANS[store.currentFloor]; const climbDirection = store.climbDirection || 'up'; const clearedFloors = store.clearedFloors || {}; // Check if current floor is cleared (for respawn indicator) const isFloorCleared = clearedFloors[store.currentFloor]; // Get active equipment spells const activeEquipmentSpells = getActiveEquipmentSpells(store.equippedInstances, store.equipmentInstances); // Get upgrade effects and DPS const upgradeEffects = getUnifiedEffects(store); const spellDPS = getTotalDPS(store, upgradeEffects, floorElem); const golemDPS = store.getActiveGolemDPS(); const totalDPS = spellDPS + golemDPS; const studySpeedMult = 1; // Base study speed const canCastSpell = (spellId: string): boolean => { const spell = SPELLS_DEF[spellId]; if (!spell) return false; return canAffordSpellCost(spell.cost, store.rawMana, store.elements); }; // Helper to get golem element color const getGolemElementColor = (element: string): string => { return ELEMENTS[element]?.color || '#F4A261'; // Default to earth color }; // Helper to get golem element symbol const getGolemElementSymbol = (element: string): string => { return ELEMENTS[element]?.sym || '⛰️'; }; // Get active golems on current floor const activeGolemsOnFloor = store.activeGolems.filter(g => g.currentFloor === store.currentFloor); return (
{/* Current Floor Card */} Current Floor
{store.currentFloor} / 100 {floorElemDef?.sym} {floorElemDef?.name} {isGuardianFloor && ( GUARDIAN )}
{isGuardianFloor && currentGuardian && (
⚔️ {currentGuardian.name}
)} {/* HP Bar */}
{fmt(store.floorHP)} / {fmt(store.floorMaxHP)} HP {store.currentAction === 'climb' && (activeEquipmentSpells.length > 0 || activeGolemsOnFloor.length > 0) ? ( DPS: {fmtDec(totalDPS)} {activeGolemsOnFloor.length > 0 && ( (Spell: {fmtDec(spellDPS)} | Golem: {fmtDec(golemDPS)}) )} ) : '—'}
{/* Floor Navigation - Direction indicator only */}
Direction
Up Down
{isFloorCleared && (
Floor cleared! Advancing...
)}
Best: Floor {store.maxFloorReached} • Pacts: {store.signedPacts.length}
{/* Active Golems Card */} 0 ? '#F4A26150' : undefined }}> 🗿 Active Golems {activeGolemsOnFloor.length > 0 && ( {activeGolemsOnFloor.length} )} {activeGolemsOnFloor.length > 0 ? ( <>
{activeGolemsOnFloor.map((golem, index) => { const golemDef = GOLEM_DEFS[golem.golemId]; const variantDef = golem.variant ? GOLEM_VARIANTS[golem.variant] : null; const elementColor = getGolemElementColor(golemDef?.element || 'earth'); const elementSymbol = getGolemElementSymbol(golemDef?.element || 'earth'); // Calculate golem DPS let golemSingleDPS = golemDef?.baseDamage || 0; if (variantDef) { golemSingleDPS *= variantDef.damageMultiplier; } if (store.skills.golemancyMaster === 1) { golemSingleDPS *= 1.5; } if (store.skills.pactBondedGolems === 1) { golemSingleDPS *= 1 + (store.signedPacts.length * 0.1); } if (store.skills.guardianInfusion === 1 && GUARDIANS[store.currentFloor]) { golemSingleDPS *= 1.25; } golemSingleDPS *= golemDef?.attackSpeed || 1; return (
{elementSymbol} {variantDef?.name || golemDef?.name || 'Unknown Golem'} {golem.variant && !variantDef && ( ({golem.variant}) )}
{fmtDec(golemSingleDPS)} DPS
{/* HP Bar */}
{fmt(golem.currentHP)} / {fmt(golem.maxHP)} HP
{/* Remaining Floors */}
{golem.remainingFloors} floor{golem.remainingFloors !== 1 ? 's' : ''} remaining {fmt(golem.damageDealt)} total dmg
); })}
{/* Total Golem DPS Summary */}
Total Golem DPS {fmtDec(golemDPS)}
) : (

No golems summoned.

Visit the Crafting tab to summon golems.

)} {/* Active Spells Card - Shows all spells from equipped weapons */} Active Spells ({activeEquipmentSpells.length}) {activeEquipmentSpells.length > 0 ? (
{activeEquipmentSpells.map(({ spellId, equipmentId }) => { const spellDef = SPELLS_DEF[spellId]; if (!spellDef) return null; const spellState = store.equipmentSpellStates?.find( s => s.spellId === spellId && s.sourceEquipment === equipmentId ); const progress = spellState?.castProgress || 0; const canCast = canAffordSpellCost(spellDef.cost, store.rawMana, store.elements); return (
{spellDef.name} {spellDef.tier === 0 && Basic} {spellDef.tier >= 4 && Legendary}
{canCast ? '✓' : '✗'}
⚔️ {fmt(spellDPS)} DPS • {' '}{formatSpellCost(spellDef.cost)} {' '}• ⚡ {(spellDef.castSpeed || 1).toFixed(1)}/hr
{/* Cast progress bar when climbing */} {store.currentAction === 'climb' && (
Cast {(progress * 100).toFixed(0)}%
)} {spellDef.effects && spellDef.effects.length > 0 && (
{spellDef.effects.map((eff, i) => ( {eff.type === 'lifesteal' && `🩸 ${Math.round(eff.value * 100)}%`} {eff.type === 'burn' && `🔥 Burn`} {eff.type === 'freeze' && `❄️ Freeze`} ))}
)}
); })}
) : (
No spells on equipped weapons. Enchant a staff with spell effects in the Crafting tab.
)}
{/* Current Study (if any) */} {store.currentStudyTarget && ( {/* Parallel Study Progress */} {store.parallelStudyTarget && (
Parallel: {SKILLS_DEF[store.parallelStudyTarget.id]?.name} {store.parallelStudyTarget.type === 'skill' && ` Lv.${(store.skills[store.parallelStudyTarget.id] || 0) + 1}`}
{formatStudyTime(store.parallelStudyTarget.progress)} / {formatStudyTime(store.parallelStudyTarget.required)} 50% speed (Parallel Study)
)}
)} {/* Crafting Progress (if any) */} {(store.designProgress || store.preparationProgress || store.applicationProgress) && ( )} {/* Activity Log */} Activity Log
{store.log.slice(0, 20).map((entry, i) => (
{entry}
))}
); }