'use client'; import { createContext, useContext, useMemo, type ReactNode } from 'react'; import { useSkillStore } from '@/lib/game/stores/skillStore'; import { useManaStore } from '@/lib/game/stores/manaStore'; import { usePrestigeStore } from '@/lib/game/stores/prestigeStore'; import { useUIStore } from '@/lib/game/stores/uiStore'; import { useCombatStore } from '@/lib/game/stores/combatStore'; import { useGameStore, useGameLoop } from '@/lib/game/stores/gameStore'; import { computeEffects, hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/upgrade-effects'; import { getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants'; import { getTierMultiplier } from '@/lib/game/skill-evolution'; import { computeMaxMana, computeRegen, computeClickMana, getMeditationBonus, canAffordSpellCost, calcDamage, getFloorElement, getBoonBonuses, getIncursionStrength, } from '@/lib/game/utils'; import { ELEMENTS, GUARDIANS, SPELLS_DEF, HOURS_PER_TICK, TICK_MS, } from '@/lib/game/constants'; import type { ElementDef, GuardianDef, SpellDef, GameAction } from '@/lib/game/types'; // Define a unified store type that combines all stores interface UnifiedStore { // From gameStore (coordinator) day: number; hour: number; incursionStrength: number; containmentWards: number; initialized: boolean; tick: () => void; resetGame: () => void; gatherMana: () => void; startNewLoop: () => void; // From manaStore rawMana: number; meditateTicks: number; totalManaGathered: number; elements: Record; setRawMana: (amount: number) => void; addRawMana: (amount: number, max: number) => void; spendRawMana: (amount: number) => boolean; convertMana: (element: string, amount: number) => boolean; unlockElement: (element: string, cost: number) => boolean; craftComposite: (target: string, recipe: string[]) => boolean; // From skillStore skills: Record; skillProgress: Record; skillUpgrades: Record; skillTiers: Record; paidStudySkills: Record; currentStudyTarget: { type: 'skill' | 'spell' | 'blueprint'; id: string; progress: number; required: number } | null; parallelStudyTarget: { type: 'skill' | 'spell' | 'blueprint'; id: string; progress: number; required: number } | null; setSkillLevel: (skillId: string, level: number) => void; startStudyingSkill: (skillId: string, rawMana: number) => { started: boolean; cost: number }; startStudyingSpell: (spellId: string, rawMana: number, studyTime: number) => { started: boolean; cost: number }; cancelStudy: (retentionBonus: number) => void; selectSkillUpgrade: (skillId: string, upgradeId: string) => void; deselectSkillUpgrade: (skillId: string, upgradeId: string) => void; commitSkillUpgrades: (skillId: string, upgradeIds: string[]) => void; tierUpSkill: (skillId: string) => void; getSkillUpgradeChoices: (skillId: string, milestone: 5 | 10) => { available: Array<{ id: string; name: string; desc: string; milestone: 5 | 10; effect: { type: string; stat?: string; value?: number; specialId?: string } }>; selected: string[] }; // From prestigeStore loopCount: number; insight: number; totalInsight: number; loopInsight: number; prestigeUpgrades: Record; memorySlots: number; pactSlots: number; memories: Array<{ skillId: string; level: number; tier: number; upgrades: string[] }>; defeatedGuardians: number[]; signedPacts: number[]; pactRitualFloor: number | null; pactRitualProgress: number; doPrestige: (id: string) => void; addMemory: (memory: { skillId: string; level: number; tier: number; upgrades: string[] }) => void; removeMemory: (skillId: string) => void; clearMemories: () => void; startPactRitual: (floor: number, rawMana: number) => boolean; cancelPactRitual: () => void; removePact: (floor: number) => void; defeatGuardian: (floor: number) => void; // From combatStore currentFloor: number; floorHP: number; floorMaxHP: number; maxFloorReached: number; activeSpell: string; currentAction: GameAction; castProgress: number; spells: Record; setAction: (action: GameAction) => void; setSpell: (spellId: string) => void; learnSpell: (spellId: string) => void; advanceFloor: () => void; // From uiStore log: string[]; paused: boolean; gameOver: boolean; victory: boolean; addLog: (message: string) => void; togglePause: () => void; setPaused: (paused: boolean) => void; setGameOver: (gameOver: boolean, victory?: boolean) => void; } interface GameContextValue { // Unified store for backward compatibility store: UnifiedStore; // Individual stores for direct access if needed skillStore: ReturnType; manaStore: ReturnType; prestigeStore: ReturnType; uiStore: ReturnType; combatStore: ReturnType; // Computed effects from upgrades upgradeEffects: ReturnType; // Derived stats maxMana: number; baseRegen: number; clickMana: number; floorElem: string; floorElemDef: ElementDef | undefined; isGuardianFloor: boolean; currentGuardian: GuardianDef | undefined; activeSpellDef: SpellDef | undefined; meditationMultiplier: number; incursionStrength: number; studySpeedMult: number; studyCostMult: number; // Effective regen calculations effectiveRegenWithSpecials: number; manaCascadeBonus: number; effectiveRegen: number; // DPS calculation dps: number; // Boons activeBoons: ReturnType; // Helpers canCastSpell: (spellId: string) => boolean; hasSpecial: (effects: ReturnType, specialId: string) => boolean; SPECIAL_EFFECTS: typeof SPECIAL_EFFECTS; } const GameContext = createContext(null); export function GameProvider({ children }: { children: ReactNode }) { // Get all individual stores const gameStore = useGameStore(); const skillState = useSkillStore(); const manaState = useManaStore(); const prestigeState = usePrestigeStore(); const uiState = useUIStore(); const combatState = useCombatStore(); // Create unified store object for backward compatibility const unifiedStore = useMemo(() => ({ // From gameStore day: gameStore.day, hour: gameStore.hour, incursionStrength: gameStore.incursionStrength, containmentWards: gameStore.containmentWards, initialized: gameStore.initialized, tick: gameStore.tick, resetGame: gameStore.resetGame, gatherMana: gameStore.gatherMana, startNewLoop: gameStore.startNewLoop, // From manaStore rawMana: manaState.rawMana, meditateTicks: manaState.meditateTicks, totalManaGathered: manaState.totalManaGathered, elements: manaState.elements, setRawMana: manaState.setRawMana, addRawMana: manaState.addRawMana, spendRawMana: manaState.spendRawMana, convertMana: manaState.convertMana, unlockElement: manaState.unlockElement, craftComposite: manaState.craftComposite, // From skillStore skills: skillState.skills, skillProgress: skillState.skillProgress, skillUpgrades: skillState.skillUpgrades, skillTiers: skillState.skillTiers, paidStudySkills: skillState.paidStudySkills, currentStudyTarget: skillState.currentStudyTarget, parallelStudyTarget: skillState.parallelStudyTarget, setSkillLevel: skillState.setSkillLevel, startStudyingSkill: skillState.startStudyingSkill, startStudyingSpell: skillState.startStudyingSpell, cancelStudy: skillState.cancelStudy, selectSkillUpgrade: skillState.selectSkillUpgrade, deselectSkillUpgrade: skillState.deselectSkillUpgrade, commitSkillUpgrades: skillState.commitSkillUpgrades, tierUpSkill: skillState.tierUpSkill, getSkillUpgradeChoices: skillState.getSkillUpgradeChoices, // From prestigeStore loopCount: prestigeState.loopCount, insight: prestigeState.insight, totalInsight: prestigeState.totalInsight, loopInsight: prestigeState.loopInsight, prestigeUpgrades: prestigeState.prestigeUpgrades, memorySlots: prestigeState.memorySlots, pactSlots: prestigeState.pactSlots, memories: prestigeState.memories, defeatedGuardians: prestigeState.defeatedGuardians, signedPacts: prestigeState.signedPacts, pactRitualFloor: prestigeState.pactRitualFloor, pactRitualProgress: prestigeState.pactRitualProgress, doPrestige: prestigeState.doPrestige, addMemory: prestigeState.addMemory, removeMemory: prestigeState.removeMemory, clearMemories: prestigeState.clearMemories, startPactRitual: prestigeState.startPactRitual, cancelPactRitual: prestigeState.cancelPactRitual, removePact: prestigeState.removePact, defeatGuardian: prestigeState.defeatGuardian, // From combatStore currentFloor: combatState.currentFloor, floorHP: combatState.floorHP, floorMaxHP: combatState.floorMaxHP, maxFloorReached: combatState.maxFloorReached, activeSpell: combatState.activeSpell, currentAction: combatState.currentAction, castProgress: combatState.castProgress, spells: combatState.spells, setAction: combatState.setAction, setSpell: combatState.setSpell, learnSpell: combatState.learnSpell, advanceFloor: combatState.advanceFloor, // From uiStore log: uiState.logs, paused: uiState.paused, gameOver: uiState.gameOver, victory: uiState.victory, addLog: uiState.addLog, togglePause: uiState.togglePause, setPaused: uiState.setPaused, setGameOver: uiState.setGameOver, }), [gameStore, skillState, manaState, prestigeState, uiState, combatState]); // Computed effects from upgrades const upgradeEffects = useMemo( () => computeEffects(skillState.skillUpgrades || {}, skillState.skillTiers || {}), [skillState.skillUpgrades, skillState.skillTiers] ); // Create a minimal state object for compute functions const stateForCompute = useMemo(() => ({ skills: skillState.skills, prestigeUpgrades: prestigeState.prestigeUpgrades, skillUpgrades: skillState.skillUpgrades, skillTiers: skillState.skillTiers, signedPacts: prestigeState.signedPacts, rawMana: manaState.rawMana, meditateTicks: manaState.meditateTicks, incursionStrength: gameStore.incursionStrength, }), [skillState, prestigeState, manaState, gameStore.incursionStrength]); // Derived stats const maxMana = useMemo( () => computeMaxMana(stateForCompute, upgradeEffects), [stateForCompute, upgradeEffects] ); const baseRegen = useMemo( () => computeRegen(stateForCompute, upgradeEffects), [stateForCompute, upgradeEffects] ); const clickMana = useMemo(() => computeClickMana(stateForCompute), [stateForCompute]); // Floor element from combat store const floorElem = useMemo(() => getFloorElement(combatState.currentFloor), [combatState.currentFloor]); const floorElemDef = ELEMENTS[floorElem]; const isGuardianFloor = !!GUARDIANS[combatState.currentFloor]; const currentGuardian = GUARDIANS[combatState.currentFloor]; const activeSpellDef = SPELLS_DEF[combatState.activeSpell]; const meditationMultiplier = useMemo( () => getMeditationBonus(manaState.meditateTicks, skillState.skills, upgradeEffects.meditationEfficiency), [manaState.meditateTicks, skillState.skills, upgradeEffects.meditationEfficiency] ); const incursionStrength = useMemo( () => getIncursionStrength(gameStore.day, gameStore.hour), [gameStore.day, gameStore.hour] ); const studySpeedMult = useMemo( () => getStudySpeedMultiplier(skillState.skills), [skillState.skills] ); const studyCostMult = useMemo( () => getStudyCostMultiplier(skillState.skills), [skillState.skills] ); // Effective regen calculations const effectiveRegenWithSpecials = baseRegen * (1 - incursionStrength); const manaCascadeBonus = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_CASCADE) ? Math.floor(maxMana / 100) * 0.1 : 0; const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus) * meditationMultiplier; // Active boons const activeBoons = useMemo( () => getBoonBonuses(prestigeState.signedPacts), [prestigeState.signedPacts] ); // DPS calculation - based on active spell, attack speed, and damage const dps = useMemo(() => { if (!activeSpellDef) return 0; const baseDmg = calcDamage( { skills: skillState.skills, signedPacts: prestigeState.signedPacts }, combatState.activeSpell, floorElem ); const dmgWithEffects = baseDmg * upgradeEffects.baseDamageMultiplier + upgradeEffects.baseDamageBonus; const attackSpeed = (1 + (skillState.skills.quickCast || 0) * 0.05) * upgradeEffects.attackSpeedMultiplier; const castSpeed = activeSpellDef.castSpeed || 1; return dmgWithEffects * attackSpeed * castSpeed; }, [activeSpellDef, skillState.skills, prestigeState.signedPacts, floorElem, upgradeEffects, combatState.activeSpell]); // Helper functions const canCastSpell = (spellId: string): boolean => { const spell = SPELLS_DEF[spellId]; if (!spell) return false; return canAffordSpellCost(spell.cost, manaState.rawMana, manaState.elements); }; const value: GameContextValue = { store: unifiedStore, skillStore: skillState, manaStore: manaState, prestigeStore: prestigeState, uiStore: uiState, combatStore: combatState, upgradeEffects, maxMana, baseRegen, clickMana, floorElem, floorElemDef, isGuardianFloor, currentGuardian, activeSpellDef, meditationMultiplier, incursionStrength, studySpeedMult, studyCostMult, effectiveRegenWithSpecials, manaCascadeBonus, effectiveRegen, dps, activeBoons, canCastSpell, hasSpecial, SPECIAL_EFFECTS, }; return {children}; } export function useGameContext() { const context = useContext(GameContext); if (!context) { throw new Error('useGameContext must be used within a GameProvider'); } return context; } // Re-export useGameLoop for convenience export { useGameLoop };