'use client'; import { useEffect, useState, lazy, Suspense } from 'react'; import { useGameStore, useGameLoop, fmt, getFloorElement, computeMaxMana, computeRegen, computeClickMana, getMeditationBonus, getIncursionStrength, canAffordSpellCost } from '@/lib/game/store'; import { ActivityLogEntry } from '@/lib/game/types'; import { getActiveEquipmentSpells, getTotalDPS } from '@/lib/game/computed-stats'; import { ELEMENTS, GUARDIANS, SPELLS_DEF, PRESTIGE_DEF, getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants'; import { getUnifiedEffects, hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects'; import { Button } from '@/components/ui/button'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; import { RotateCcw, Mountain, ChevronDown } from 'lucide-react'; import { TooltipProvider } from '@/components/ui/tooltip'; import { DebugName } from '@/lib/game/debug-context'; // Non-tab component imports import { ActionButtons, CalendarDisplay, ManaDisplay, TimeDisplay } from '@/components/game'; // Loot and Achievements moved to separate tabs // Lazy load tab components const SpireTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SpireTab }))); const SkillsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SkillsTab }))); const SpellsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SpellsTab }))); const LabTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.LabTab }))); const StatsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.StatsTab }))); const EquipmentTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.EquipmentTab }))); const AttunementsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.AttunementsTab }))); const DebugTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.DebugTab }))); const LootTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.LootTab }))); const AchievementsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.AchievementsTab }))); const GolemancyTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.GolemancyTab }))); const CraftingTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.CraftingTab }))); // Loading fallback component const TabLoadingFallback = () =>
Loading...
; export default function ManaLoopGame() { const [activeTab, setActiveTab] = useState('spire'); const [isGathering, setIsGathering] = useState(false); // Game store const store = useGameStore(); const gameLoop = useGameLoop(); // Computed effects from upgrades and equipment const upgradeEffects = getUnifiedEffects(store); // Derived stats const maxMana = computeMaxMana(store, upgradeEffects); const baseRegen = computeRegen(store, upgradeEffects); const clickMana = computeClickMana(store); const floorElem = getFloorElement(store.currentFloor); const floorElemDef = ELEMENTS[floorElem]; const isGuardianFloor = !!GUARDIANS[store.currentFloor]; const currentGuardian = GUARDIANS[store.currentFloor]; const meditationMultiplier = getMeditationBonus(store.meditateTicks, store.skills, upgradeEffects.meditationEfficiency); const incursionStrength = getIncursionStrength(store.day, store.hour); const studySpeedMult = getStudySpeedMultiplier(store.skills); const studyCostMult = getStudyCostMultiplier(store.skills); // Effective regen with incursion penalty const effectiveRegenWithSpecials = baseRegen * (1 - incursionStrength); // Mana Cascade bonus const manaCascadeBonus = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_CASCADE) ? Math.floor(maxMana / 100) * 0.1 : 0; // Effective regen const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus) * meditationMultiplier; // Get all active spells from equipment const activeEquipmentSpells = getActiveEquipmentSpells(store.equippedInstances, store.equipmentInstances); // Compute total DPS const totalDPS = getTotalDPS(store, upgradeEffects, floorElem); // Auto-gather while holding useEffect(() => { if (!isGathering) return; let lastGatherTime = 0; const minGatherInterval = 100; let animationFrameId: number; const gatherLoop = (timestamp: number) => { if (timestamp - lastGatherTime >= minGatherInterval) { store.gatherMana(); lastGatherTime = timestamp; } animationFrameId = requestAnimationFrame(gatherLoop); }; animationFrameId = requestAnimationFrame(gatherLoop); return () => cancelAnimationFrame(animationFrameId); }, [isGathering, store]); // Handle gather button events const handleGatherStart = () => { setIsGathering(true); store.gatherMana(); }; const handleGatherEnd = () => { setIsGathering(false); }; // Start game loop useEffect(() => { const cleanup = gameLoop.start(); return cleanup; }, [gameLoop]); // Check if spell can be cast const canCastSpell = (spellId: string): boolean => { const spell = SPELLS_DEF[spellId]; if (!spell) return false; return canAffordSpellCost(spell.cost, store.rawMana, store.elements); }; // Game Over Screen if (store.gameOver) { return (
{store.victory ? 'VICTORY!' : 'LOOP ENDS'}

{store.victory ? 'The Awakened One falls! Your power echoes through eternity.' : 'The time loop resets... but you remember.'}

{fmt(store.loopInsight)}
Insight Gained
{store.maxFloorReached}
Best Floor
{store.signedPacts.length}
Pacts Signed
{store.loopCount + 1}
Total Loops
); } return (
{/* Header */}

MANA LOOP

{/* Main Content */}
{/* Left Panel - Mana & Actions */}
{/* Mana Display */} {/* Climb the Spire Button - only show when not in Spire Mode */} {!store.spireMode && ( )} {/* Action Buttons - only show when not in Spire Mode */} {!store.spireMode && ( )} {/* Calendar */} {/* Loot and Achievements moved to tabs */}
{/* Right Panel - Conditional rendering based on Spire Mode */} {store.spireMode ? ( /* Spire Mode - Simplified UI */

๐Ÿ”๏ธ Spire Mode - Floor {store.currentFloor}

{/* Show Climbing indicator when actively climbing */} {store.currentAction === 'climb' && !store.isDescending && ( Climbing )}
}> {/* Activity Log for Spire Mode */} Activity Log
{(store.activityLog || []).slice(0, 50).map((entry: ActivityLogEntry, i) => { // Style based on event type const getEventStyle = (eventType: string) => { switch (eventType) { case 'enemy_defeated': case 'floor_cleared': return 'text-green-400'; case 'damage_dealt': return 'text-red-400'; case 'dodge': return 'text-yellow-400'; case 'armor_proc': return 'text-blue-400'; case 'special_effect': return 'text-purple-400'; case 'floor_transition': return 'text-cyan-400'; case 'spell_cast': return 'text-amber-400'; case 'golem_attack': return 'text-orange-400'; case 'puzzle_solved': return 'text-pink-400'; default: return 'text-gray-300'; } }; return (
{entry.message}
); })} {(store.activityLog || []).length === 0 && (
No activity yet...
)}
) : ( /* Normal Mode - Tabs */
โš”๏ธ Spire โœจ Attune ๐Ÿ—ฟ Golems ๐Ÿ“š Skills ๐Ÿ”ฎ Spells ๐Ÿ›ก๏ธ Gear ๐Ÿ”ง Craft ๐Ÿ’Ž Loot ๐Ÿ† Achieve ๐Ÿ”ฌ Lab ๐Ÿ“Š Stats ๐Ÿ”ง Debug ๐Ÿ“– Grimoire }> }> }> }> }> }> }> }> }> }> }> {renderGrimoireTab()} }>
)}
); // Grimoire Tab (Prestige) function renderGrimoireTab() { return (
{/* Current Status */} Loop Status
{store.loopCount}
Loops Completed
{fmt(store.insight)}
Current Insight
{fmt(store.totalInsight)}
Total Insight
{store.memorySlots}
Memory Slots
{/* Signed Pacts */} Signed Pacts {store.signedPacts.length === 0 ? (
No pacts signed yet. Defeat guardians to earn pacts.
) : (
{store.signedPacts.map((floor) => { const guardian = GUARDIANS[floor]; if (!guardian) return null; return (
{guardian.name}
Floor {floor}
{guardian.pact}x multiplier
); })}
)}
{/* 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
); } }