refactor: split bloated state types into State + Actions interfaces (issue #102)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s

- CombatState: split into CombatState (data) + CombatActions + CombatStore
- PrestigeState: split into PrestigeState (data) + PrestigeActions + PrestigeStore
- ManaState: split into ManaState (data) + ManaActions + ManaStore
- GameState: deprecated, removed from barrel exports
- crafting-actions: updated to use CraftingState instead of GameState
- combat-utils/mana-utils: replaced Pick<GameState,...> with focused interfaces
- DisciplineCardProps: split into Definition + Runtime + Callbacks
- stores/index.ts: now exports both State and Actions types
This commit is contained in:
2026-05-20 21:05:22 +02:00
parent ee893e8973
commit 8a7ddaae27
24 changed files with 411 additions and 321 deletions
+59 -53
View File
@@ -6,23 +6,25 @@ import { persist } from 'zustand/middleware';
import type { Memory } from '../types';
import { GUARDIANS, PRESTIGE_DEF } from '../constants';
// ─── Prestige State (data only) ──────────────────────────────────────────────
export interface PrestigeState {
// Loop counter
loopCount: number;
// Insight
insight: number;
totalInsight: number;
loopInsight: number; // Insight earned at end of current loop
loopInsight: number;
// Prestige upgrades
prestigeUpgrades: Record<string, number>;
memorySlots: number;
pactSlots: number;
// Memories (skills preserved across loops)
memories: Memory[];
// Guardian pacts
defeatedGuardians: number[];
signedPacts: number[];
@@ -34,8 +36,11 @@ export interface PrestigeState {
}>;
pactRitualFloor: number | null;
pactRitualProgress: number;
// Actions
}
// ─── Prestige Actions ────────────────────────────────────────────────────────
export interface PrestigeActions {
doPrestige: (id: string) => boolean;
addMemory: (memory: Memory) => void;
removeMemory: (skillId: string) => void;
@@ -46,7 +51,7 @@ export interface PrestigeState {
updatePactRitualProgress: (hours: number) => void;
removePact: (floor: number) => void;
defeatGuardian: (floor: number) => void;
// Methods called by gameStore
addSignedPact: (floor: number) => void;
removeDefeatedGuardian: (floor: number) => void;
@@ -59,14 +64,14 @@ export interface PrestigeState {
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<number, {
@@ -77,40 +82,41 @@ export interface PrestigeState {
}>) => void;
}
const initialState = {
// ─── Combined Prestige Store Type ────────────────────────────────────────────
export type PrestigeStore = PrestigeState & PrestigeActions;
// ─── Initial State ───────────────────────────────────────────────────────────
const initialState: PrestigeState = {
loopCount: 0,
insight: 0,
totalInsight: 0,
loopInsight: 0,
prestigeUpgrades: {} as Record<string, number>,
prestigeUpgrades: {},
memorySlots: 3,
pactSlots: 1,
memories: [] as Memory[],
defeatedGuardians: [] as number[],
signedPacts: [] as number[],
signedPactDetails: {} as Record<number, {
floor: number;
guardianId: string;
signedAt: { day: number; hour: number };
skillLevels: Record<string, number>;
}>,
pactRitualFloor: null as number | null,
memories: [],
defeatedGuardians: [],
signedPacts: [],
signedPactDetails: {},
pactRitualFloor: null,
pactRitualProgress: 0,
};
export const usePrestigeStore = create<PrestigeState>()(
export const usePrestigeStore = create<PrestigeStore>()(
persist(
(set, get) => ({
...initialState,
doPrestige: (id: string) => {
const state = get();
const pd = PRESTIGE_DEF[id];
if (!pd) return false;
const lvl = state.prestigeUpgrades[id] || 0;
if (lvl >= pd.max || state.insight < pd.cost) return false;
const newPU = { ...state.prestigeUpgrades, [id]: lvl + 1 };
set({
insight: state.insight - pd.cost,
@@ -120,114 +126,114 @@ export const usePrestigeStore = create<PrestigeState>()(
});
return true;
},
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 false;
if (!state.defeatedGuardians.includes(floor)) return false;
if (state.signedPacts.includes(floor)) return false;
if (state.signedPacts.length >= state.pactSlots) return false;
if (rawMana < guardian.pactCost) return false;
if (state.pactRitualFloor !== null) return false;
set({
pactRitualFloor: floor,
pactRitualProgress: 0,
});
return true;
},
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<string, number>,
@@ -247,7 +253,7 @@ export const usePrestigeStore = create<PrestigeState>()(
loopInsight: 0,
});
},
startNewLoop: (insightGained: number) => {
const state = get();
set({
@@ -262,15 +268,15 @@ export const usePrestigeStore = create<PrestigeState>()(
pactRitualProgress: 0,
});
},
setLoopInsight: (insight: number) => {
set({ loopInsight: insight });
},
resetPrestige: () => {
set(initialState);
},
// Debug helpers
debugSetSignedPacts: (pacts: number[]) => {
set({ signedPacts: pacts });