'use client'; import { useState, useEffect, useMemo } from 'react'; import { Button } from '@/components/ui/button'; import { Mountain } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; import { ManaDisplay } from '@/components/game'; import { ActionButtons } from '@/components/game'; import { ActivityLogPanel } from '@/components/game/ActivityLogPanel'; import { DebugName } from '@/components/game/debug/debug-context'; import { useGameStore, useManaStore, useCombatStore, useCraftingStore, usePrestigeStore, useAttunementStore } from '@/lib/game/stores'; import { getUnifiedEffects } from '@/lib/game/effects'; import { getMeditationBonus, getIncursionStrength } from '@/lib/game/stores'; import { computeTotalMaxMana, computeTotalRegen, computeTotalClickMana } from '@/lib/game/effects'; import { computeDisciplineEffects } from '@/lib/game/effects/discipline-effects'; import { computeConversionRates } from '@/lib/game/utils/conversion-rates'; import { getGuardianForFloor } from '@/lib/game/data/guardian-encounters'; import { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements'; import type { ElementRegenBreakdown } from '@/components/game/ManaDisplay'; export function LeftPanel() { const [isGathering, setIsGathering] = useState(false); const rawMana = useManaStore((s) => s.rawMana); const elements = useManaStore((s) => s.elements); const meditateTicks = useManaStore((s) => s.meditateTicks); const elementRegen = useManaStore((s) => s.elementRegen); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); const signedPacts = usePrestigeStore((s) => s.signedPacts); const attunements = useAttunementStore((s) => s.attunements); const equippedInstances = useCraftingStore((s) => s.equippedInstances); const equipmentInstances = useCraftingStore((s) => s.equipmentInstances); const gatherMana = useGameStore((s) => s.gatherMana); const spireMode = useCombatStore((s) => s.spireMode); const enterSpireMode = useCombatStore((s) => s.enterSpireMode); const currentAction = useCombatStore((s) => s.currentAction); const designProgress = useCraftingStore((s) => s.designProgress); const designProgress2 = useCraftingStore((s) => s.designProgress2); const cancelDesign = useCraftingStore((s) => s.cancelDesign); const preparationProgress = useCraftingStore((s) => s.preparationProgress); const applicationProgress = useCraftingStore((s) => s.applicationProgress); const equipmentCraftingProgress = useCraftingStore((s) => s.equipmentCraftingProgress); const handleGatherStart = () => { setIsGathering(true); gatherMana(); }; const handleGatherEnd = () => { setIsGathering(false); }; useEffect(() => { if (!isGathering) return; let lastGatherTime = 0; const minGatherInterval = 100; let animationFrameId: number; const gatherLoop = (timestamp: number) => { if (timestamp - lastGatherTime >= minGatherInterval) { gatherMana(); lastGatherTime = timestamp; } animationFrameId = requestAnimationFrame(gatherLoop); }; animationFrameId = requestAnimationFrame(gatherLoop); return () => cancelAnimationFrame(animationFrameId); }, [isGathering, gatherMana]); const upgradeEffects = getUnifiedEffects({ skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances }); const disciplineEffects = computeDisciplineEffects(); const maxMana = computeTotalMaxMana({ skills: {}, prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances }, upgradeEffects); const baseRegen = computeTotalRegen({ skills: {}, prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances }, upgradeEffects); const clickMana = computeTotalClickMana({ skills: {}, skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances }, upgradeEffects); const meditationMultiplier = getMeditationBonus(meditateTicks, upgradeEffects.meditationEfficiency, disciplineEffects.meditationCapBonus); const incursionStrength = getIncursionStrength(useGameStore((s) => s.day), useGameStore((s) => s.hour)); const effectiveRegen = baseRegen * (1 - incursionStrength) * meditationMultiplier; // Compute per-element regen breakdown for ManaDisplay (DISC-8) const elementRegenBreakdown = useMemo((): Record | undefined => { const pactElementMap: Record = {}; for (const floor of signedPacts) { const g = getGuardianForFloor(floor); if (g?.element?.length) pactElementMap[floor] = g.element[0]; } const grossRegen: Record = {}; for (const [id, state] of Object.entries(attunements)) { if (!state.active) continue; const def = ATTUNEMENTS_DEF[id]; if (def?.primaryManaType) { grossRegen[def.primaryManaType] = (grossRegen[def.primaryManaType] || 0) + (def.conversionRate || 0); } } const invokerLevel = attunements.invoker?.active ? (attunements.invoker.level || 1) : 0; const conversionResult = computeConversionRates({ disciplineEffects, attunements, signedPacts, pactElementMap, invokerLevel, meditationMultiplier, grossRegen, rawGrossRegen: baseRegen, }); const breakdown: Record = {}; for (const [elem, entry] of Object.entries(conversionResult.rates)) { if (entry.paused) continue; const drains: Record = {}; // This element is drained when it's a component of a higher conversion for (const [destElem, destEntry] of Object.entries(conversionResult.rates)) { if (destEntry.paused) continue; if (destEntry.componentCosts[elem]) { drains[destElem] = (drains[destElem] || 0) + destEntry.finalRate * destEntry.componentCosts[elem]; } } if (entry.finalRate > 0 || Object.keys(drains).length > 0) { breakdown[elem] = { produced: entry.finalRate, drains }; } } return Object.keys(breakdown).length > 0 ? breakdown : undefined; }, [disciplineEffects, attunements, signedPacts, meditationMultiplier, baseRegen]); return (
{/* 1. Mana Display */} {/* 2. Spire Entry */} {!spireMode && ( )} {/* 3. Current Action */} {!spireMode && ( )} {/* 4. Activity Log */}
); }