237 lines
10 KiB
TypeScript
237 lines
10 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useState, lazy, Suspense } from 'react';
|
||
import { useShallow } from 'zustand/react/shallow';
|
||
|
||
import {
|
||
useGameStore,
|
||
useUIStore,
|
||
useManaStore,
|
||
useCombatStore,
|
||
usePrestigeStore,
|
||
useCraftingStore,
|
||
computeMaxMana,
|
||
computeRegen,
|
||
computeClickMana,
|
||
getMeditationBonus,
|
||
getIncursionStrength
|
||
} from '@/lib/game/stores';
|
||
import { computeDisciplineEffects } from '@/lib/game/effects/discipline-effects';
|
||
import { useGameLoop } from '@/lib/game/stores/gameHooks';
|
||
import { getUnifiedEffects } from '@/lib/game/effects';
|
||
import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects';
|
||
import { TimeDisplay } from '@/components/game';
|
||
import { DebugName } from '@/components/game/debug/debug-context';
|
||
|
||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||
import { TooltipProvider } from '@/components/ui/tooltip';
|
||
import { ErrorBoundary } from '@/components/ErrorBoundary';
|
||
|
||
import { GameOverScreen } from './components/GameOverScreen';
|
||
import { LeftPanel } from './components/LeftPanel';
|
||
import { GrimoireTab } from './components/GrimoireTab';
|
||
|
||
// Lazy load tab components
|
||
const DisciplinesTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.DisciplinesTab })));
|
||
const SpellsTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.SpellsTab })));
|
||
const StatsTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.StatsTab })));
|
||
const DebugTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.DebugTab })));
|
||
const AchievementsTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.AchievementsTab })));
|
||
const AttunementsTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.AttunementsTab })));
|
||
const PrestigeTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.PrestigeTab })));
|
||
const EquipmentTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.EquipmentTab })));
|
||
const GolemancyTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.GolemancyTab })));
|
||
const GuardianPactsTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.GuardianPactsTab })));
|
||
const SpireSummaryTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.SpireSummaryTab })));
|
||
const CraftingTab = lazy(() => import('@/components/game/tabs').then(m => ({ default: m.CraftingTab })));
|
||
const SpireCombatPage = lazy(() => import('@/components/game/tabs/SpireCombatPage').then(m => ({ default: m.SpireCombatPage })));
|
||
|
||
const TabFallback = () => <div className="p-4 text-center text-gray-400">Loading...</div>;
|
||
|
||
function TabErrorFallback({ name }: { name: string }) {
|
||
return <div className="p-4 text-red-400">{name} tab failed to load.</div>;
|
||
}
|
||
|
||
// ─── Derived Stats Hook ──────────────────────────────────────────────────────
|
||
|
||
function useGameDerivedStats() {
|
||
const { prestigeUpgrades } = usePrestigeStore(useShallow(s => ({
|
||
prestigeUpgrades: s.prestigeUpgrades,
|
||
})));
|
||
const { meditateTicks } = useManaStore(useShallow(s => ({
|
||
meditateTicks: s.meditateTicks,
|
||
})));
|
||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||
const day = useGameStore((s) => s.day);
|
||
const hour = useGameStore((s) => s.hour);
|
||
|
||
const upgradeEffects = getUnifiedEffects({
|
||
skillUpgrades: {},
|
||
skillTiers: {},
|
||
equippedInstances,
|
||
equipmentInstances,
|
||
});
|
||
|
||
const disciplineEffects = computeDisciplineEffects();
|
||
|
||
const maxMana = computeMaxMana({
|
||
skills: {},
|
||
prestigeUpgrades,
|
||
skillUpgrades: {},
|
||
skillTiers: {},
|
||
}, upgradeEffects, disciplineEffects);
|
||
|
||
const baseRegen = computeRegen({
|
||
skills: {},
|
||
prestigeUpgrades,
|
||
skillUpgrades: {},
|
||
skillTiers: {},
|
||
attunements: {},
|
||
}, upgradeEffects, disciplineEffects);
|
||
|
||
const clickMana = computeClickMana({}, disciplineEffects);
|
||
const meditationMultiplier = getMeditationBonus(meditateTicks, upgradeEffects.meditationEfficiency, disciplineEffects.meditationCapBonus);
|
||
const incursionStrength = getIncursionStrength(day, hour);
|
||
|
||
const effectiveRegenWithSpecials = baseRegen * (1 - incursionStrength);
|
||
|
||
const manaCascadeBonus = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_CASCADE)
|
||
? Math.floor(maxMana / 100) * 0.1
|
||
: 0;
|
||
|
||
const manaWaterfallBonus = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_WATERFALL)
|
||
? Math.floor(maxMana / 100) * 0.25
|
||
: 0;
|
||
|
||
const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus + manaWaterfallBonus) * meditationMultiplier;
|
||
|
||
return { maxMana, effectiveRegen, clickMana, meditationMultiplier };
|
||
}
|
||
|
||
// ─── Tab Triggers ────────────────────────────────────────────────────────────
|
||
|
||
function TabTriggers() {
|
||
return (
|
||
<TabsList className="flex flex-wrap gap-1 w-full mb-4 h-auto">
|
||
<TabsTrigger value="spells" className="text-xs px-2 py-1">🔮 Spells</TabsTrigger>
|
||
<TabsTrigger value="stats" className="text-xs px-2 py-1">📊 Stats</TabsTrigger>
|
||
<TabsTrigger value="disciplines" className="text-xs px-2 py-1">📚 Disciplines</TabsTrigger>
|
||
<TabsTrigger value="grimoire" className="text-xs px-2 py-1">📖 Grimoire</TabsTrigger>
|
||
<TabsTrigger value="debug" className="text-xs px-2 py-1">🐛 Debug</TabsTrigger>
|
||
<TabsTrigger value="attunements" className="text-xs px-2 py-1">⚗️ Attunements</TabsTrigger>
|
||
<TabsTrigger value="achievements" className="text-xs px-2 py-1">🏆 Achievements</TabsTrigger>
|
||
<TabsTrigger value="prestige" className="text-xs px-2 py-1">✨ Prestige</TabsTrigger>
|
||
<TabsTrigger value="equipment" className="text-xs px-2 py-1">⚔️ Equipment</TabsTrigger>
|
||
<TabsTrigger value="golemancy" className="text-xs px-2 py-1">🗿 Golemancy</TabsTrigger>
|
||
<TabsTrigger value="pacts" className="text-xs px-2 py-1">📜 Pacts</TabsTrigger>
|
||
<TabsTrigger value="spire" className="text-xs px-2 py-1">🏔️ Spire</TabsTrigger>
|
||
<TabsTrigger value="crafting" className="text-xs px-2 py-1">⚒️ Crafting</TabsTrigger>
|
||
</TabsList>
|
||
);
|
||
}
|
||
|
||
// ─── Lazy Tab Content ────────────────────────────────────────────────────────
|
||
|
||
function LazyTab({ name, children }: { name: string; children: React.ReactNode }) {
|
||
return (
|
||
<ErrorBoundary fallback={<TabErrorFallback name={name} />}>
|
||
<Suspense fallback={<TabFallback />}>
|
||
{children}
|
||
</Suspense>
|
||
</ErrorBoundary>
|
||
);
|
||
}
|
||
|
||
// ─── Main Game Component ─────────────────────────────────────────────────────
|
||
|
||
export default function ManaLoopGame() {
|
||
const [activeTab, setActiveTab] = useState('spells');
|
||
|
||
useGameLoop();
|
||
|
||
const { day, hour, initGame } = useGameStore(useShallow(s => ({
|
||
day: s.day,
|
||
hour: s.hour,
|
||
initGame: s.initGame,
|
||
})));
|
||
const { insight, loopInsight } = usePrestigeStore(useShallow(s => ({
|
||
insight: s.insight,
|
||
loopInsight: s.loopInsight,
|
||
})));
|
||
const spireMode = useCombatStore((s) => s.spireMode);
|
||
const gameOver = useUIStore((s) => s.gameOver);
|
||
|
||
useGameDerivedStats();
|
||
|
||
useEffect(() => {
|
||
initGame();
|
||
}, [initGame]);
|
||
|
||
const [mounted, setMounted] = useState(false);
|
||
useEffect(() => { setMounted(true); }, []); // eslint-disable-line react-hooks/set-state-in-effect
|
||
|
||
if (gameOver) {
|
||
return <GameOverScreen day={day} hour={hour} insightGained={loopInsight} totalInsight={insight} />;
|
||
}
|
||
|
||
if (!mounted) return <div className="p-4 text-center text-gray-400">Loading...</div>;
|
||
|
||
if (spireMode) {
|
||
return (
|
||
<ErrorBoundary
|
||
onReset={() => {
|
||
useCombatStore.getState().exitSpireMode();
|
||
}}
|
||
>
|
||
<Suspense fallback={<div className="p-4 text-center text-gray-400">Loading spire...</div>}>
|
||
<SpireCombatPage />
|
||
</Suspense>
|
||
</ErrorBoundary>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<DebugName name="HomePage">
|
||
<ErrorBoundary>
|
||
<TooltipProvider>
|
||
<div className="game-root min-h-screen flex flex-col">
|
||
<header className="sticky top-0 z-50 bg-gradient-to-b from-gray-900 to-gray-900/80 border-b border-gray-700 px-4 py-2">
|
||
<div className="flex items-center justify-between">
|
||
<h1 className="text-xl font-bold game-title tracking-wider">MANA LOOP</h1>
|
||
<div className="flex items-center gap-4">
|
||
<TimeDisplay day={day} hour={hour} insight={insight} />
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<main className="flex-1 flex flex-col md:flex-row gap-4 p-4">
|
||
<LeftPanel />
|
||
|
||
<div className="flex-1 min-w-0">
|
||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||
<TabTriggers />
|
||
|
||
<TabsContent value="spells"><LazyTab name="spells"><SpellsTab /></LazyTab></TabsContent>
|
||
<TabsContent value="stats"><LazyTab name="stats"><StatsTab /></LazyTab></TabsContent>
|
||
<TabsContent value="disciplines"><LazyTab name="disciplines"><DisciplinesTab /></LazyTab></TabsContent>
|
||
<TabsContent value="grimoire"><GrimoireTab /></TabsContent>
|
||
<TabsContent value="debug"><LazyTab name="debug"><DebugTab /></LazyTab></TabsContent>
|
||
<TabsContent value="attunements"><LazyTab name="attunements"><AttunementsTab /></LazyTab></TabsContent>
|
||
<TabsContent value="achievements"><LazyTab name="achievements"><AchievementsTab /></LazyTab></TabsContent>
|
||
<TabsContent value="prestige"><LazyTab name="prestige"><PrestigeTab /></LazyTab></TabsContent>
|
||
<TabsContent value="equipment"><LazyTab name="equipment"><EquipmentTab /></LazyTab></TabsContent>
|
||
<TabsContent value="golemancy"><LazyTab name="golemancy"><GolemancyTab /></LazyTab></TabsContent>
|
||
<TabsContent value="pacts"><LazyTab name="pacts"><GuardianPactsTab /></LazyTab></TabsContent>
|
||
<TabsContent value="spire"><LazyTab name="spire"><SpireSummaryTab /></LazyTab></TabsContent>
|
||
<TabsContent value="crafting"><LazyTab name="crafting"><CraftingTab /></LazyTab></TabsContent>
|
||
</Tabs>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
</TooltipProvider>
|
||
</ErrorBoundary>
|
||
</DebugName>
|
||
);
|
||
}
|