'use client'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { ScrollArea } from '@/components/ui/scroll-area'; import { RotateCcw, Save, Trash2, Star, Flame, Clock, AlertCircle } from 'lucide-react'; import { useGameContext } from '../GameContext'; import { GUARDIANS, PRESTIGE_DEF, SKILLS_DEF, ELEMENTS } from '@/lib/game/constants'; import { getTierMultiplier, getBaseSkillId } from '@/lib/game/skill-evolution'; import { fmt, fmtDec, getBoonBonuses } from '@/lib/game/stores'; import type { Memory } from '@/lib/game/types'; import { useMemo, useState } from 'react'; export function GrimoireTab() { const { store } = useGameContext(); const [showMemoryPicker, setShowMemoryPicker] = useState(false); // Get all skills that can be saved to memory const saveableSkills = useMemo(() => { const skills: { skillId: string; level: number; tier: number; upgrades: string[]; name: string }[] = []; for (const [skillId, level] of Object.entries(store.skills)) { if (level && level > 0) { const baseSkillId = getBaseSkillId(skillId); const tier = store.skillTiers?.[baseSkillId] || 1; const tieredSkillId = tier > 1 ? `${baseSkillId}_t${tier}` : baseSkillId; const upgrades = store.skillUpgrades?.[tieredSkillId] || []; const skillDef = SKILLS_DEF[baseSkillId]; if (skillId === baseSkillId || skillId.includes('_t')) { const actualLevel = store.skills[tieredSkillId] || store.skills[baseSkillId] || 0; if (actualLevel > 0) { skills.push({ skillId: baseSkillId, level: actualLevel, tier, upgrades, name: skillDef?.name || baseSkillId, }); } } } } const uniqueSkills = new Map(); for (const skill of skills) { const existing = uniqueSkills.get(skill.skillId); if (!existing || skill.tier > existing.tier || (skill.tier === existing.tier && skill.level > existing.level)) { uniqueSkills.set(skill.skillId, skill); } } return Array.from(uniqueSkills.values()).sort((a, b) => { if (a.tier !== b.tier) return b.tier - a.tier; if (a.level !== b.level) return b.level - a.level; return a.name.localeCompare(b.name); }); }, [store.skills, store.skillTiers, store.skillUpgrades]); const isSkillInMemory = (skillId: string) => store.memories.some(m => m.skillId === skillId); const canAddMore = store.memories.length < store.memorySlots; const addToMemory = (skill: typeof saveableSkills[0]) => { const memory: Memory = { skillId: skill.skillId, level: skill.level, tier: skill.tier, upgrades: skill.upgrades, }; store.addMemory(memory); }; // Calculate total boons from active pacts const activeBoons = useMemo(() => { return getBoonBonuses(store.signedPacts); }, [store.signedPacts]); // Check if player can sign a pact const canSignPact = (floor: number) => { const guardian = GUARDIANS[floor]; if (!guardian) return false; if (!store.defeatedGuardians.includes(floor)) return false; if (store.signedPacts.includes(floor)) return false; if (store.signedPacts.length >= store.pactSlots) return false; if (store.rawMana < guardian.pactCost) return false; if (store.pactRitualFloor !== null) return false; return true; }; // Get pact affinity bonus for display const pactAffinityBonus = (store.prestigeUpgrades.pactAffinity || 0) * 10; return (
{/* Current Status */} Loop Status
{store.loopCount}
Loops Completed
{fmt(store.insight)}
Current Insight
{fmt(store.totalInsight)}
Total Insight
{store.memorySlots}
Memory Slots
{/* Pact Slots & Active Ritual */} Pact Slots ({store.signedPacts.length}/{store.pactSlots}) {/* Active Ritual Progress */} {store.pactRitualFloor !== null && (
{(() => { const guardian = GUARDIANS[store.pactRitualFloor]; if (!guardian) return null; const requiredTime = guardian.pactTime * (1 - (store.prestigeUpgrades.pactAffinity || 0) * 0.1); const progress = Math.min(100, (store.pactRitualProgress / requiredTime) * 100); return ( <>
Signing Pact with {guardian.name} {fmtDec(progress, 1)}%
{fmtDec(store.pactRitualProgress, 1)}h / {fmtDec(requiredTime, 1)}h
); })()}
)} {/* Active Pacts */} {store.signedPacts.length > 0 ? (
{store.signedPacts.map((floor) => { const guardian = GUARDIANS[floor]; if (!guardian) return null; return (
{guardian.name}
Floor {floor} Guardian
"{guardian.uniquePerk}"
{guardian.boons.map((boon, idx) => ( {boon.desc} ))}
); })}
) : (
No active pacts. Defeat guardians and sign pacts to gain boons.
)} {/* Available Guardians for Pacts */} Available Guardians ({store.defeatedGuardians.length}) {store.defeatedGuardians.length === 0 ? (
Defeat guardians in the Spire to make them available for pacts.
) : (
{store.defeatedGuardians .sort((a, b) => a - b) .map((floor) => { const guardian = GUARDIANS[floor]; if (!guardian) return null; const canSign = canSignPact(floor); const notEnoughMana = store.rawMana < guardian.pactCost; const atCapacity = store.signedPacts.length >= store.pactSlots; return (
{guardian.name}
Floor {floor} • {ELEMENTS[guardian.element]?.name || guardian.element}
{fmt(guardian.pactCost)} mana
{guardian.pactTime}h ritual
"{guardian.uniquePerk}"
{guardian.boons.map((boon, idx) => ( {boon.desc} ))}
); })}
)}
{/* Memory Slots */} Memory Slots ({store.memories.length}/{store.memorySlots})

Skills saved to memory will retain their level, tier, and upgrades when you start a new loop.

{/* Saved Memories */} {store.memories.length > 0 ? (
{store.memories.map((memory) => { const skillDef = SKILLS_DEF[memory.skillId]; const tierMult = getTierMultiplier(memory.tier > 1 ? `${memory.skillId}_t${memory.tier}` : memory.skillId); return (
{skillDef?.name || memory.skillId} {memory.tier > 1 && ( T{memory.tier} ({tierMult}x) )} Lv.{memory.level} {memory.upgrades.length > 0 && ( {memory.upgrades.length} )}
); })}
) : (
No memories saved. Add skills below.
)} {/* Add Memory Button */} {canAddMore && ( )} {/* Skill Picker */} {showMemoryPicker && canAddMore && (
{saveableSkills.length === 0 ? (
No skills with progress to save
) : ( saveableSkills.map((skill) => { const isInMemory = isSkillInMemory(skill.skillId); const tierMult = getTierMultiplier(skill.tier > 1 ? `${skill.skillId}_t${skill.tier}` : skill.skillId); return (
!isInMemory && addToMemory(skill)} >
{skill.name} {skill.tier > 1 && ( T{skill.tier} ({tierMult}x) )}
Lv.{skill.level} {skill.upgrades.length > 0 && ( {skill.upgrades.length} )}
); }) )}
)}
{/* Active Boons Summary */} {store.signedPacts.length > 0 && ( Active Boons Summary
{activeBoons.maxMana > 0 && (
Max Mana: +{activeBoons.maxMana}
)} {activeBoons.manaRegen > 0 && (
Mana Regen: +{activeBoons.manaRegen}/h
)} {activeBoons.castingSpeed > 0 && (
Cast Speed: +{activeBoons.castingSpeed}%
)} {activeBoons.elementalDamage > 0 && (
Elem. Damage: +{activeBoons.elementalDamage}%
)} {activeBoons.rawDamage > 0 && (
Raw Damage: +{activeBoons.rawDamage}%
)} {activeBoons.critChance > 0 && (
Crit Chance: +{activeBoons.critChance}%
)} {activeBoons.critDamage > 0 && (
Crit Damage: +{activeBoons.critDamage}%
)} {activeBoons.spellEfficiency > 0 && (
Spell Cost: -{activeBoons.spellEfficiency}%
)} {activeBoons.manaGain > 0 && (
Mana Gain: +{activeBoons.manaGain}%
)} {activeBoons.insightGain > 0 && (
Insight Gain: +{activeBoons.insightGain}%
)} {activeBoons.studySpeed > 0 && (
Study Speed: +{activeBoons.studySpeed}%
)} {activeBoons.prestigeInsight > 0 && (
Prestige Insight: +{activeBoons.prestigeInsight}/loop
)}
)} {/* Prestige Upgrades */} Insight Upgrades (Permanent)
{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 (
{def.name}
{level}/{def.max}
{def.desc}
); })}
{/* Reset Game Button */}
Reset All Progress
Clear all data and start fresh
); }