Files
Mana-Loop/src/lib/game/store/pactSlice.ts
T
2026-04-03 17:23:15 +00:00

181 lines
5.3 KiB
TypeScript
Executable File

// ─── Pact Slice ───────────────────────────────────────────────────────────────
// Manages guardian pacts, signing, and mana unlocking
import type { StateCreator } from 'zustand';
import type { GameState } from '../types';
import { GUARDIANS, ELEMENTS } from '../constants';
import { computePactMultiplier, computePactInsightMultiplier } from './computed';
export interface PactSlice {
// State
signedPacts: number[];
pendingPactOffer: number | null;
maxPacts: number;
pactSigningProgress: {
floor: number;
progress: number;
required: number;
manaCost: number;
} | null;
signedPactDetails: Record<number, {
floor: number;
guardianId: string;
signedAt: { day: number; hour: number };
skillLevels: Record<string, number>;
}>;
pactInterferenceMitigation: number;
pactSynergyUnlocked: boolean;
// Actions
acceptPact: (floor: number) => void;
declinePact: (floor: number) => void;
// Computed getters
getPactMultiplier: () => number;
getPactInsightMultiplier: () => number;
}
export const createPactSlice = (
set: StateCreator<GameState>['set'],
get: () => GameState
): PactSlice => ({
signedPacts: [],
pendingPactOffer: null,
maxPacts: 1,
pactSigningProgress: null,
signedPactDetails: {},
pactInterferenceMitigation: 0,
pactSynergyUnlocked: false,
acceptPact: (floor: number) => {
const state = get();
const guardian = GUARDIANS[floor];
if (!guardian || state.signedPacts.includes(floor)) return;
const maxPacts = 1 + (state.prestigeUpgrades.pactCapacity || 0);
if (state.signedPacts.length >= maxPacts) {
set({
log: [`⚠️ Cannot sign more pacts! Maximum: ${maxPacts}.`, ...state.log.slice(0, 49)],
});
return;
}
const baseCost = guardian.signingCost.mana;
const discount = Math.min((state.prestigeUpgrades.pactDiscount || 0) * 0.1, 0.5);
const manaCost = Math.floor(baseCost * (1 - discount));
if (state.rawMana < manaCost) {
set({
log: [`⚠️ Need ${manaCost} mana to sign pact with ${guardian.name}!`, ...state.log.slice(0, 49)],
});
return;
}
const baseTime = guardian.signingCost.time;
const haste = Math.min((state.prestigeUpgrades.pactHaste || 0) * 0.1, 0.5);
const signingTime = Math.max(1, baseTime * (1 - haste));
set({
rawMana: state.rawMana - manaCost,
pactSigningProgress: {
floor,
progress: 0,
required: signingTime,
manaCost,
},
pendingPactOffer: null,
currentAction: 'study',
log: [`📜 Beginning pact signing with ${guardian.name}... (${signingTime}h, ${manaCost} mana)`, ...state.log.slice(0, 49)],
});
},
declinePact: (floor: number) => {
const state = get();
const guardian = GUARDIANS[floor];
if (!guardian) return;
set({
pendingPactOffer: null,
log: [`🚫 Declined pact with ${guardian.name}.`, ...state.log.slice(0, 49)],
});
},
getPactMultiplier: () => computePactMultiplier(get()),
getPactInsightMultiplier: () => computePactInsightMultiplier(get()),
});
// Process pact signing progress (called during tick)
export function processPactSigning(state: GameState, deltaHours: number): Partial<GameState> {
if (!state.pactSigningProgress) return {};
const progress = state.pactSigningProgress.progress + deltaHours;
const log = [...state.log];
if (progress >= state.pactSigningProgress.required) {
const floor = state.pactSigningProgress.floor;
const guardian = GUARDIANS[floor];
if (!guardian || state.signedPacts.includes(floor)) {
return { pactSigningProgress: null };
}
const signedPacts = [...state.signedPacts, floor];
const signedPactDetails = {
...state.signedPactDetails,
[floor]: {
floor,
guardianId: guardian.element,
signedAt: { day: state.day, hour: state.hour },
skillLevels: {},
},
};
// Unlock mana types
let elements = { ...state.elements };
for (const elemId of guardian.unlocksMana) {
if (elements[elemId]) {
elements = {
...elements,
[elemId]: { ...elements[elemId], unlocked: true },
};
}
}
// Check for compound element unlocks
const unlockedSet = new Set(
Object.entries(elements)
.filter(([, e]) => e.unlocked)
.map(([id]) => id)
);
for (const [elemId, elemDef] of Object.entries(ELEMENTS)) {
if (elemDef.recipe && !elements[elemId]?.unlocked) {
const canUnlock = elemDef.recipe.every(comp => unlockedSet.has(comp));
if (canUnlock) {
elements = {
...elements,
[elemId]: { ...elements[elemId], unlocked: true },
};
log.unshift(`🔮 ${elemDef.name} mana unlocked through component synergy!`);
}
}
}
log.unshift(`📜 Pact with ${guardian.name} signed! ${guardian.unlocksMana.map(e => ELEMENTS[e]?.name || e).join(', ')} mana unlocked!`);
return {
signedPacts,
signedPactDetails,
elements,
pactSigningProgress: null,
log,
};
}
return {
pactSigningProgress: {
...state.pactSigningProgress,
progress,
},
};
}