// ─── Prestige Store ─────────────────────────────────────────────────────────── // Handles insight, prestige upgrades, memories, loops, pacts import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { createSafeStorage } from '../utils/safe-persist'; import type { Memory } from '../types'; import { GUARDIANS, PRESTIGE_DEF } from '../constants'; import { ok, okVoid, fail, ErrorCode } from '../utils/result'; import type { Result } from '../utils/result'; // ─── Prestige State (data only) ────────────────────────────────────────────── export interface PrestigeState { // Loop counter loopCount: number; // Insight insight: number; totalInsight: number; loopInsight: number; // Prestige upgrades prestigeUpgrades: Record; memorySlots: number; pactSlots: number; // Memories (skills preserved across loops) memories: Memory[]; // Guardian pacts defeatedGuardians: number[]; signedPacts: number[]; signedPactDetails: Record; }>; pactRitualFloor: number | null; pactRitualProgress: number; } // ─── Prestige Actions ──────────────────────────────────────────────────────── export interface PrestigeActions { doPrestige: (id: string) => Result; addMemory: (memory: Memory) => void; removeMemory: (skillId: string) => void; clearMemories: () => void; startPactRitual: (floor: number, rawMana: number) => Result; cancelPactRitual: () => void; completePactRitual: (addLog: (msg: string) => void) => void; updatePactRitualProgress: (hours: number) => void; removePact: (floor: number) => void; defeatGuardian: (floor: number) => void; // Methods called by gameStore addSignedPact: (floor: number) => void; removeDefeatedGuardian: (floor: number) => void; setPactRitualFloor: (floor: number | null) => void; addDefeatedGuardian: (floor: number) => void; incrementLoopCount: () => void; resetPrestigeForNewLoop: ( totalInsight: number, prestigeUpgrades: Record, memories: Memory[], memorySlots: number ) => void; // Loop management startNewLoop: (insightGained: number) => void; setLoopInsight: (insight: number) => void; // Reset resetPrestige: () => void; // Debug helpers debugSetSignedPacts: (pacts: number[]) => void; debugSetPactDetails: (details: Record; }>) => void; } // ─── Combined Prestige Store Type ──────────────────────────────────────────── export type PrestigeStore = PrestigeState & PrestigeActions; // ─── Initial State ─────────────────────────────────────────────────────────── const initialState: PrestigeState = { loopCount: 0, insight: 0, totalInsight: 0, loopInsight: 0, prestigeUpgrades: {}, memorySlots: 3, pactSlots: 1, memories: [], defeatedGuardians: [], signedPacts: [], signedPactDetails: {}, pactRitualFloor: null, pactRitualProgress: 0, }; export const usePrestigeStore = create()( persist( (set, get) => ({ ...initialState, doPrestige: (id: string) => { const state = get(); const pd = PRESTIGE_DEF[id]; if (!pd) return fail(ErrorCode.INVALID_PRESTIGE_ID, `Unknown prestige upgrade: ${id}`); const lvl = state.prestigeUpgrades[id] || 0; if (lvl >= pd.max) return fail(ErrorCode.PRESTIGE_MAX_LEVEL, `Upgrade ${id} is already at max level (${pd.max})`); if (state.insight < pd.cost) return fail(ErrorCode.INSUFFICIENT_INSIGHT, `Need ${pd.cost} insight, have ${state.insight}`); const newPU = { ...state.prestigeUpgrades, [id]: lvl + 1 }; set({ insight: state.insight - pd.cost, prestigeUpgrades: newPU, memorySlots: id === 'deepMemory' ? state.memorySlots + 1 : state.memorySlots, pactSlots: id === 'pactBinding' ? state.pactSlots + 1 : state.pactSlots, }); return okVoid(); }, addMemory: (memory: Memory) => { const state = get(); if (state.memories.length >= state.memorySlots) return; if (state.memories.some(m => m.skillId === memory.skillId)) return; set({ memories: [...state.memories, memory] }); }, removeMemory: (skillId: string) => { set((state) => ({ memories: state.memories.filter(m => m.skillId !== skillId), })); }, clearMemories: () => { set({ memories: [] }); }, startPactRitual: (floor: number, rawMana: number) => { const state = get(); const guardian = GUARDIANS[floor]; if (!guardian) return fail(ErrorCode.INVALID_INPUT, `No guardian at floor ${floor}`); if (!state.defeatedGuardians.includes(floor)) return fail(ErrorCode.GUARDIAN_NOT_DEFEATED, `Guardian at floor ${floor} has not been defeated`); if (state.signedPacts.includes(floor)) return fail(ErrorCode.PACT_ALREADY_SIGNED, `Pact with ${guardian.name} is already signed`); if (state.signedPacts.length >= state.pactSlots) return fail(ErrorCode.PACT_SLOTS_FULL, `All pact slots are full (${state.pactSlots})`); if (rawMana < guardian.pactCost) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${guardian.pactCost} raw mana, have ${rawMana}`); if (state.pactRitualFloor !== null) return fail(ErrorCode.RITUAL_IN_PROGRESS, `A pact ritual is already in progress for floor ${state.pactRitualFloor}`); set({ pactRitualFloor: floor, pactRitualProgress: 0, }); return okVoid(); }, cancelPactRitual: () => { set({ pactRitualFloor: null, pactRitualProgress: 0, }); }, completePactRitual: (addLog: (msg: string) => void) => { const state = get(); if (state.pactRitualFloor === null) return; const guardian = GUARDIANS[state.pactRitualFloor]; if (!guardian) return; set({ signedPacts: [...state.signedPacts, state.pactRitualFloor], defeatedGuardians: state.defeatedGuardians.filter(f => f !== state.pactRitualFloor), pactRitualFloor: null, pactRitualProgress: 0, }); addLog(`📜 Pact signed with ${guardian.name}! You have gained their boons.`); }, updatePactRitualProgress: (hours: number) => { set((state) => ({ pactRitualProgress: state.pactRitualProgress + hours, })); }, removePact: (floor: number) => { set((state) => ({ signedPacts: state.signedPacts.filter(f => f !== floor), })); }, defeatGuardian: (floor: number) => { const state = get(); if (state.defeatedGuardians.includes(floor) || state.signedPacts.includes(floor)) return; set({ defeatedGuardians: [...state.defeatedGuardians, floor], }); }, addSignedPact: (floor: number) => { const state = get(); if (state.signedPacts.includes(floor)) return; set({ signedPacts: [...state.signedPacts, floor] }); }, removeDefeatedGuardian: (floor: number) => { set((state) => ({ defeatedGuardians: state.defeatedGuardians.filter(f => f !== floor), })); }, setPactRitualFloor: (floor: number | null) => { set({ pactRitualFloor: floor, pactRitualProgress: 0 }); }, addDefeatedGuardian: (floor: number) => { const state = get(); if (state.defeatedGuardians.includes(floor) || state.signedPacts.includes(floor)) return; set({ defeatedGuardians: [...state.defeatedGuardians, floor] }); }, incrementLoopCount: () => { set((state) => ({ loopCount: state.loopCount + 1 })); }, resetPrestigeForNewLoop: ( totalInsight: number, prestigeUpgrades: Record, memories: Memory[], memorySlots: number ) => { set({ insight: totalInsight, prestigeUpgrades, memories, memorySlots, // Reset loop-specific state defeatedGuardians: [], signedPacts: [], pactRitualFloor: null, pactRitualProgress: 0, loopInsight: 0, }); }, startNewLoop: (insightGained: number) => { const state = get(); set({ loopCount: state.loopCount + 1, insight: state.insight + insightGained, totalInsight: state.totalInsight + insightGained, loopInsight: 0, // Reset loop-specific state defeatedGuardians: [], signedPacts: [], pactRitualFloor: null, pactRitualProgress: 0, }); }, setLoopInsight: (insight: number) => { set({ loopInsight: insight }); }, resetPrestige: () => { set(initialState); }, // Debug helpers debugSetSignedPacts: (pacts: number[]) => { set({ signedPacts: pacts }); }, debugSetPactDetails: (details: Record; }>) => { set({ signedPactDetails: details }); }, }), { storage: createSafeStorage(), name: 'mana-loop-prestige', partialize: (state) => ({ loopCount: state.loopCount, insight: state.insight, totalInsight: state.totalInsight, loopInsight: state.loopInsight, prestigeUpgrades: state.prestigeUpgrades, memorySlots: state.memorySlots, pactSlots: state.pactSlots, memories: state.memories, defeatedGuardians: state.defeatedGuardians, signedPacts: state.signedPacts, signedPactDetails: state.signedPactDetails, pactRitualFloor: state.pactRitualFloor, pactRitualProgress: state.pactRitualProgress, }), } ) );