chore: commit investigation state
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m25s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m25s
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Circular Dependencies
|
||||
Generated: 2026-06-09T16:48:20.172Z
|
||||
Generated: 2026-06-09T17:09:05.689Z
|
||||
Found: 2 circular chain(s) — these MUST be fixed before modifying involved files.
|
||||
|
||||
1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"_meta": {
|
||||
"generated": "2026-06-09T16:48:18.218Z",
|
||||
"generated": "2026-06-09T17:09:03.568Z",
|
||||
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
||||
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
||||
},
|
||||
|
||||
@@ -215,6 +215,7 @@ Mana-Loop/
|
||||
│ │ │ │ ├── discipline-math.test.ts
|
||||
│ │ │ │ ├── discipline-prerequisites.test.ts
|
||||
│ │ │ │ ├── discipline-reactivate-bug.test.ts
|
||||
│ │ │ │ ├── earth-desync.test.ts
|
||||
│ │ │ │ ├── enemy-barrier-utils.test.ts
|
||||
│ │ │ │ ├── enemy-defenses.test.ts
|
||||
│ │ │ │ ├── enemy-generator.test.ts
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useManaStore } from '../stores/manaStore';
|
||||
import { ELEMENTS, BASE_UNLOCKED_ELEMENTS } from '../constants/elements';
|
||||
|
||||
// ─── Regression test: Earth element desync after Unlock All ─────────────────
|
||||
// https://gitea.tailf367e3.ts.net/Anexim/Mana-Loop/issues/338
|
||||
// https://gitea.tailf367e3.ts.net/Anexim/Mana-Loop/issues/339
|
||||
|
||||
function resetManaStore() {
|
||||
useManaStore.setState({
|
||||
rawMana: 10,
|
||||
meditateTicks: 0,
|
||||
totalManaGathered: 0,
|
||||
elements: Object.fromEntries(
|
||||
Object.keys(ELEMENTS).map(k => [
|
||||
k,
|
||||
{
|
||||
current: BASE_UNLOCKED_ELEMENTS.includes(k) ? 0 : 0,
|
||||
max: 10,
|
||||
baseMax: 10,
|
||||
unlocked: BASE_UNLOCKED_ELEMENTS.includes(k),
|
||||
},
|
||||
])
|
||||
) as Record<string, { current: number; max: number; baseMax: number; unlocked: boolean }>,
|
||||
elementRegen: {},
|
||||
});
|
||||
}
|
||||
|
||||
describe('Earth element desync after Unlock All', () => {
|
||||
beforeEach(() => {
|
||||
resetManaStore();
|
||||
});
|
||||
|
||||
it('earth element exists in initial state', () => {
|
||||
const elements = useManaStore.getState().elements;
|
||||
expect(elements.earth).toBeDefined();
|
||||
expect(elements.earth.unlocked).toBe(false);
|
||||
});
|
||||
|
||||
it('unlockElement unlocks earth correctly', () => {
|
||||
const result = useManaStore.getState().unlockElement('earth', 0);
|
||||
expect(result.success).toBe(true);
|
||||
const earth = useManaStore.getState().elements.earth;
|
||||
expect(earth).toBeDefined();
|
||||
expect(earth.unlocked).toBe(true);
|
||||
expect(earth.current).toBe(0);
|
||||
expect(earth.max).toBe(10);
|
||||
expect(earth.baseMax).toBe(10);
|
||||
});
|
||||
|
||||
it('unlockElement preserves earth state fields', () => {
|
||||
// First give earth some mana
|
||||
useManaStore.setState((s) => ({
|
||||
elements: {
|
||||
...s.elements,
|
||||
earth: { ...s.elements.earth, current: 5, max: 20, baseMax: 20 },
|
||||
},
|
||||
}));
|
||||
|
||||
const result = useManaStore.getState().unlockElement('earth', 0);
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
const earth = useManaStore.getState().elements.earth;
|
||||
expect(earth.unlocked).toBe(true);
|
||||
expect(earth.current).toBe(5);
|
||||
expect(earth.max).toBe(20);
|
||||
expect(earth.baseMax).toBe(20);
|
||||
});
|
||||
|
||||
it('unlock all elements via unlockElement preserves all fields', () => {
|
||||
const elements = useManaStore.getState().elements;
|
||||
for (const id of Object.keys(elements)) {
|
||||
if (!elements[id].unlocked) {
|
||||
useManaStore.getState().unlockElement(id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const updatedElements = useManaStore.getState().elements;
|
||||
const earth = updatedElements.earth;
|
||||
|
||||
expect(earth).toBeDefined();
|
||||
expect(earth.unlocked).toBe(true);
|
||||
expect(earth.current).toBe(0);
|
||||
expect(earth.max).toBe(10);
|
||||
expect(earth.baseMax).toBe(10);
|
||||
});
|
||||
|
||||
it('all 22 elements are unlocked after unlock all', () => {
|
||||
const elements = useManaStore.getState().elements;
|
||||
for (const id of Object.keys(elements)) {
|
||||
if (!elements[id].unlocked) {
|
||||
useManaStore.getState().unlockElement(id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const updatedElements = useManaStore.getState().elements;
|
||||
const unlockedCount = Object.values(updatedElements).filter(e => e.unlocked).length;
|
||||
expect(unlockedCount).toBe(Object.keys(ELEMENTS).length);
|
||||
});
|
||||
|
||||
it('earth shows in unlocked elements list after unlock all', () => {
|
||||
const elements = useManaStore.getState().elements;
|
||||
for (const id of Object.keys(elements)) {
|
||||
if (!elements[id].unlocked) {
|
||||
useManaStore.getState().unlockElement(id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const updatedElements = useManaStore.getState().elements;
|
||||
const unlockedIds = Object.entries(updatedElements)
|
||||
.filter(([, state]) => state.unlocked)
|
||||
.map(([id]) => id);
|
||||
|
||||
expect(unlockedIds).toContain('earth');
|
||||
});
|
||||
|
||||
it('earth element has valid state after unlock (not missing fields)', () => {
|
||||
useManaStore.getState().unlockElement('earth', 0);
|
||||
const earth = useManaStore.getState().elements.earth;
|
||||
|
||||
// These would be undefined if the element state was corrupted
|
||||
expect(earth.current).toBeDefined();
|
||||
expect(earth.max).toBeDefined();
|
||||
expect(earth.baseMax).toBeDefined();
|
||||
expect(earth.unlocked).toBe(true);
|
||||
});
|
||||
|
||||
it('unlockElement with cost 0 does not deduct raw mana', () => {
|
||||
const before = useManaStore.getState().rawMana;
|
||||
useManaStore.getState().unlockElement('earth', 0);
|
||||
const after = useManaStore.getState().rawMana;
|
||||
expect(after).toBe(before);
|
||||
});
|
||||
|
||||
it('unlockElement twice for earth is a no-op', () => {
|
||||
const result1 = useManaStore.getState().unlockElement('earth', 0);
|
||||
expect(result1.success).toBe(true);
|
||||
|
||||
const result2 = useManaStore.getState().unlockElement('earth', 0);
|
||||
expect(result2.success).toBe(false); // Already unlocked
|
||||
|
||||
const earth = useManaStore.getState().elements.earth;
|
||||
expect(earth.unlocked).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -105,7 +105,13 @@ export const useManaStore = create<ManaStore>()(
|
||||
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 the element doesn't exist in the store (e.g. from an old save), create it
|
||||
const existing = state.elements[element];
|
||||
const newElement = existing
|
||||
? { ...existing, unlocked: true }
|
||||
: { current: 0, max: 10, baseMax: 10, unlocked: true };
|
||||
|
||||
set({ rawMana: state.rawMana - cost, elements: { ...state.elements, [element]: newElement } });
|
||||
return okVoid();
|
||||
},
|
||||
|
||||
@@ -174,6 +180,18 @@ export const useManaStore = create<ManaStore>()(
|
||||
persistedState.elements[k].baseMax = persistedState.elements[k].max ?? 10;
|
||||
}
|
||||
}
|
||||
// Add any missing elements that exist in ELEMENTS but not in the save
|
||||
for (const k of Object.keys(ELEMENTS)) {
|
||||
if (!persistedState.elements[k]) {
|
||||
const isUnlocked = BASE_UNLOCKED_ELEMENTS.includes(k);
|
||||
persistedState.elements[k] = {
|
||||
current: isUnlocked ? 0 : 0,
|
||||
max: 10,
|
||||
baseMax: 10,
|
||||
unlocked: isUnlocked,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return persistedState;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user