refactor: cleanup codebase — remove hydration guards, extract constants, fix bugs
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m20s

This commit is contained in:
2026-05-26 11:20:36 +02:00
parent 5c64bb00fa
commit b402b8f56e
23 changed files with 579 additions and 979 deletions
+31 -132
View File
@@ -86,7 +86,6 @@ export const useManaStore = create<ManaStore>()(
spendRawMana: (amount: number) => {
const state = get();
if (state.rawMana < amount) return false;
set({ rawMana: state.rawMana - amount });
return true;
},
@@ -98,71 +97,35 @@ export const useManaStore = create<ManaStore>()(
}));
},
setMeditateTicks: (ticks: number) => {
set({ meditateTicks: ticks });
},
incrementMeditateTicks: () => {
set((state) => ({ meditateTicks: state.meditateTicks + 1 }));
},
resetMeditateTicks: () => {
set({ meditateTicks: 0 });
},
setMeditateTicks: (ticks: number) => set({ meditateTicks: ticks }),
incrementMeditateTicks: () => set((s) => ({ meditateTicks: s.meditateTicks + 1 })),
resetMeditateTicks: () => set({ meditateTicks: 0 }),
convertMana: (element: string, amount: number) => {
const state = get();
const elem = state.elements[element];
if (!elem?.unlocked) {
return fail(ErrorCode.ELEMENT_NOT_UNLOCKED, `Element ${element} is not unlocked`);
}
if (!elem?.unlocked) return fail(ErrorCode.ELEMENT_NOT_UNLOCKED, `Element ${element} is not unlocked`);
const cost = MANA_PER_ELEMENT * amount;
if (state.rawMana < cost) {
return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${cost} raw mana, have ${state.rawMana}`);
}
if (elem.current >= elem.max) {
return fail(ErrorCode.ELEMENT_MAX_CAPACITY, `Element ${element} is at max capacity`);
}
if (state.rawMana < cost) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${cost} raw mana, have ${state.rawMana}`);
if (elem.current >= elem.max) return fail(ErrorCode.ELEMENT_MAX_CAPACITY, `Element ${element} is at max capacity`);
const canConvert = Math.min(
amount,
Math.floor(state.rawMana / MANA_PER_ELEMENT),
elem.max - elem.current
);
if (canConvert <= 0) {
return fail(ErrorCode.INVALID_INPUT, 'Cannot convert 0 or negative amount');
}
const canConvert = Math.min(amount, Math.floor(state.rawMana / MANA_PER_ELEMENT), elem.max - elem.current);
if (canConvert <= 0) return fail(ErrorCode.INVALID_INPUT, 'Cannot convert 0 or negative amount');
set({
rawMana: state.rawMana - canConvert * MANA_PER_ELEMENT,
elements: {
...state.elements,
[element]: { ...elem, current: elem.current + canConvert },
},
elements: { ...state.elements, [element]: { ...elem, current: elem.current + canConvert } },
});
return ok({ converted: canConvert });
},
unlockElement: (element: string, cost: number) => {
const state = get();
if (state.elements[element]?.unlocked) {
return fail(ErrorCode.INVALID_INPUT, `Element ${element} is already unlocked`);
}
if (state.rawMana < cost) {
return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${cost} raw mana, have ${state.rawMana}`);
}
set({
rawMana: state.rawMana - cost,
elements: {
...state.elements,
[element]: { ...state.elements[element], unlocked: true },
},
});
if (state.elements[element]?.unlocked) return fail(ErrorCode.INVALID_INPUT, `Element ${element} is already unlocked`);
if (state.rawMana < cost) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${cost} raw mana, have ${state.rawMana}`);
set({ rawMana: state.rawMana - cost, elements: { ...state.elements, [element]: { ...state.elements[element], unlocked: true } } });
return okVoid();
},
@@ -170,15 +133,8 @@ export const useManaStore = create<ManaStore>()(
set((state) => {
const elem = state.elements[element];
if (!elem) return state;
return {
elements: {
...state.elements,
[element]: {
...elem,
current: Math.min(elem.current + amount, max),
},
},
elements: { ...state.elements, [element]: { ...elem, current: Math.min(elem.current + amount, max) } },
};
});
},
@@ -186,64 +142,35 @@ export const useManaStore = create<ManaStore>()(
spendElementMana: (element: string, amount: number) => {
const state = get();
const elem = state.elements[element];
if (!elem) {
return fail(ErrorCode.INVALID_ELEMENT, `Element ${element} does not exist`);
}
if (elem.current < amount) {
return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${amount} ${element} mana, have ${elem.current}`);
}
set({
elements: {
...state.elements,
[element]: { ...elem, current: elem.current - amount },
},
});
if (!elem) return fail(ErrorCode.INVALID_ELEMENT, `Element ${element} does not exist`);
if (elem.current < amount) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${amount} ${element} mana, have ${elem.current}`);
set({ elements: { ...state.elements, [element]: { ...elem, current: elem.current - amount } } });
return okVoid();
},
setElementMax: (max: number) => {
set((state) => ({
elements: Object.fromEntries(
Object.entries(state.elements).map(([k, v]) => [k, { ...v, max }])
) as Record<string, ElementState>,
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;
});
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 fail(ErrorCode.INSUFFICIENT_MANA, `Need ${amt} ${r} mana, have ${state.elements[r]?.current || 0}`);
}
if ((state.elements[r]?.current || 0) < amt) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${amt} ${r} mana, have ${state.elements[r]?.current || 0}`);
}
// Deduct ingredients
const newElems = { ...state.elements };
for (const [r, amt] of Object.entries(costs)) {
newElems[r] = {
...newElems[r],
current: newElems[r].current - amt,
};
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,
};
newElems[target] = { ...(targetElem || { current: 0, max: 10, unlocked: false }), current: (targetElem?.current || 0) + 1, unlocked: true };
set({ elements: newElems });
return okVoid();
},
@@ -252,27 +179,16 @@ export const useManaStore = create<ManaStore>()(
const state = get();
const elements = { ...state.elements };
const unlockedElements = Object.entries(elements)
.filter(([, e]) => e.unlocked && e.current < e.max);
if (unlockedElements.length === 0 || rawMana < 100) return null;
const unlockedElements = Object.entries(elements).filter(([, e]) => e.unlocked && e.current < e.max);
if (unlockedElements.length === 0 || rawMana < MANA_PER_ELEMENT) return null;
unlockedElements.sort((a, b) => (b[1].max - b[1].current) - (a[1].max - a[1].current));
const [targetId, targetState] = unlockedElements[0];
const canConvert = Math.min(
Math.floor(rawMana / 100),
targetState.max - targetState.current
);
const canConvert = Math.min(Math.floor(rawMana / MANA_PER_ELEMENT), targetState.max - targetState.current);
if (canConvert <= 0) return null;
rawMana -= canConvert * 100;
const updatedElements = {
...elements,
[targetId]: { ...targetState, current: targetState.current + canConvert }
};
return { rawMana, elements: updatedElements };
rawMana -= canConvert * MANA_PER_ELEMENT;
return { rawMana, elements: { ...elements, [targetId]: { ...targetState, current: targetState.current + canConvert } } };
},
resetMana: (
@@ -283,24 +199,13 @@ export const useManaStore = create<ManaStore>()(
) => {
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,
});
set({ rawMana: startingMana, meditateTicks: 0, totalManaGathered: 0, elements: makeInitialElements(elementMax, prestigeUpgrades) });
},
}),
{
storage: createSafeStorage(),
name: 'mana-loop-mana',
partialize: (state) => ({
rawMana: state.rawMana,
totalManaGathered: state.totalManaGathered,
elements: state.elements,
}),
partialize: (state) => ({ rawMana: state.rawMana, totalManaGathered: state.totalManaGathered, elements: state.elements }),
}
)
);
@@ -311,16 +216,10 @@ export function makeInitialElements(
prestigeUpgrades: Record<string, number> = {}
): Record<string, ElementState> {
const elemStart = (prestigeUpgrades.elemStart || 0) * 5;
const elements: Record<string, ElementState> = {};
Object.keys(ELEMENTS).forEach(k => {
for (const k of Object.keys(ELEMENTS)) {
const isUnlocked = BASE_UNLOCKED_ELEMENTS.includes(k);
elements[k] = {
current: isUnlocked ? elemStart : 0,
max: elementMax,
unlocked: isUnlocked,
};
});
elements[k] = { current: isUnlocked ? elemStart : 0, max: elementMax, unlocked: isUnlocked };
}
return elements;
}