Initial commit

This commit is contained in:
Z User
2026-04-03 17:23:15 +00:00
commit 4f474dbcf3
192 changed files with 47527 additions and 0 deletions
+264
View File
@@ -0,0 +1,264 @@
// ─── Mana Store ───────────────────────────────────────────────────────────────
// Handles raw mana, elements, meditation, and mana regeneration
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { ELEMENTS, MANA_PER_ELEMENT, BASE_UNLOCKED_ELEMENTS } from '../constants';
import type { ElementState } from '../types';
export interface ManaState {
rawMana: number;
meditateTicks: number;
totalManaGathered: number;
elements: Record<string, ElementState>;
// Actions
setRawMana: (amount: number) => void;
addRawMana: (amount: number, maxMana: number) => void;
spendRawMana: (amount: number) => boolean;
gatherMana: (amount: number, maxMana: number) => void;
// Meditation
setMeditateTicks: (ticks: number) => void;
incrementMeditateTicks: () => void;
resetMeditateTicks: () => void;
// Elements
convertMana: (element: string, amount: number) => boolean;
unlockElement: (element: string, cost: number) => boolean;
addElementMana: (element: string, amount: number, max: number) => void;
spendElementMana: (element: string, amount: number) => boolean;
setElementMax: (max: number) => void;
craftComposite: (target: string, recipe: string[]) => boolean;
// Reset
resetMana: (
prestigeUpgrades: Record<string, number>,
skills?: Record<string, number>,
skillUpgrades?: Record<string, string[]>,
skillTiers?: Record<string, number>
) => void;
}
export const useManaStore = create<ManaState>()(
persist(
(set, get) => ({
rawMana: 10,
meditateTicks: 0,
totalManaGathered: 0,
elements: Object.fromEntries(
Object.keys(ELEMENTS).map(k => [
k,
{
current: 0,
max: 10,
unlocked: BASE_UNLOCKED_ELEMENTS.includes(k),
}
])
) as Record<string, ElementState>,
setRawMana: (amount: number) => {
set({ rawMana: Math.max(0, amount) });
},
addRawMana: (amount: number, maxMana: number) => {
set((state) => ({
rawMana: Math.min(state.rawMana + amount, maxMana),
totalManaGathered: state.totalManaGathered + amount,
}));
},
spendRawMana: (amount: number) => {
const state = get();
if (state.rawMana < amount) return false;
set({ rawMana: state.rawMana - amount });
return true;
},
gatherMana: (amount: number, maxMana: number) => {
set((state) => ({
rawMana: Math.min(state.rawMana + amount, maxMana),
totalManaGathered: state.totalManaGathered + amount,
}));
},
setMeditateTicks: (ticks: number) => {
set({ meditateTicks: ticks });
},
incrementMeditateTicks: () => {
set((state) => ({ meditateTicks: state.meditateTicks + 1 }));
},
resetMeditateTicks: () => {
set({ meditateTicks: 0 });
},
convertMana: (element: string, amount: number) => {
const state = get();
const elem = state.elements[element];
if (!elem?.unlocked) return false;
const cost = MANA_PER_ELEMENT * amount;
if (state.rawMana < cost) return false;
if (elem.current >= elem.max) return false;
const canConvert = Math.min(
amount,
Math.floor(state.rawMana / MANA_PER_ELEMENT),
elem.max - elem.current
);
if (canConvert <= 0) return false;
set({
rawMana: state.rawMana - canConvert * MANA_PER_ELEMENT,
elements: {
...state.elements,
[element]: { ...elem, current: elem.current + canConvert },
},
});
return true;
},
unlockElement: (element: string, cost: number) => {
const state = get();
if (state.elements[element]?.unlocked) return false;
if (state.rawMana < cost) return false;
set({
rawMana: state.rawMana - cost,
elements: {
...state.elements,
[element]: { ...state.elements[element], unlocked: true },
},
});
return true;
},
addElementMana: (element: string, amount: number, max: number) => {
set((state) => {
const elem = state.elements[element];
if (!elem) return state;
return {
elements: {
...state.elements,
[element]: {
...elem,
current: Math.min(elem.current + amount, max),
},
},
};
});
},
spendElementMana: (element: string, amount: number) => {
const state = get();
const elem = state.elements[element];
if (!elem || elem.current < amount) return false;
set({
elements: {
...state.elements,
[element]: { ...elem, current: elem.current - amount },
},
});
return true;
},
setElementMax: (max: number) => {
set((state) => ({
elements: Object.fromEntries(
Object.entries(state.elements).map(([k, v]) => [k, { ...v, max }])
) as Record<string, ElementState>,
}));
},
craftComposite: (target: string, recipe: string[]) => {
const state = get();
// Count required ingredients
const costs: Record<string, number> = {};
recipe.forEach(r => {
costs[r] = (costs[r] || 0) + 1;
});
// Check if we have all ingredients
for (const [r, amt] of Object.entries(costs)) {
if ((state.elements[r]?.current || 0) < amt) return false;
}
// Deduct ingredients
const newElems = { ...state.elements };
for (const [r, amt] of Object.entries(costs)) {
newElems[r] = {
...newElems[r],
current: newElems[r].current - amt,
};
}
// Add crafted element
const targetElem = newElems[target];
newElems[target] = {
...(targetElem || { current: 0, max: 10, unlocked: false }),
current: (targetElem?.current || 0) + 1,
unlocked: true,
};
set({ elements: newElems });
return true;
},
resetMana: (
prestigeUpgrades: Record<string, number>,
skills: Record<string, number> = {},
skillUpgrades: Record<string, string[]> = {},
skillTiers: Record<string, number> = {}
) => {
const elementMax = 10 + (prestigeUpgrades.elemMax || 0) * 5;
const startingMana = 10 + (prestigeUpgrades.manaStart || 0) * 10;
const elements = makeInitialElements(elementMax, prestigeUpgrades);
set({
rawMana: startingMana,
meditateTicks: 0,
totalManaGathered: 0,
elements,
});
},
}),
{
name: 'mana-loop-mana',
partialize: (state) => ({
rawMana: state.rawMana,
totalManaGathered: state.totalManaGathered,
elements: state.elements,
}),
}
)
);
// Helper function to create initial elements
export function makeInitialElements(
elementMax: number,
prestigeUpgrades: Record<string, number> = {}
): Record<string, ElementState> {
const elemStart = (prestigeUpgrades.elemStart || 0) * 5;
const elements: Record<string, ElementState> = {};
Object.keys(ELEMENTS).forEach(k => {
const isUnlocked = BASE_UNLOCKED_ELEMENTS.includes(k);
elements[k] = {
current: isUnlocked ? elemStart : 0,
max: elementMax,
unlocked: isUnlocked,
};
});
return elements;
}