fix: discipline reset on mana depletion and re-activation after stop
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m35s

- #143: processTick now removes drained disciplines from activeIds and calls onStopPracticing so currentAction resets to 'meditate'
- #144: Removed paused guard from canProceedDiscipline so stopped disciplines can be re-activated
- Updated test to match new expected behavior for paused disciplines
This commit is contained in:
2026-05-27 11:13:08 +02:00
parent 64b472572b
commit 7962a4fdaa
5 changed files with 18 additions and 5 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# Circular Dependencies # Circular Dependencies
Generated: 2026-05-27T08:45:44.894Z Generated: 2026-05-27T09:06:29.331Z
No circular dependencies found. ✅ No circular dependencies found. ✅
+6 -1
View File
@@ -1,6 +1,6 @@
{ {
"_meta": { "_meta": {
"generated": "2026-05-27T08:45:43.143Z", "generated": "2026-05-27T09:06:27.471Z",
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.", "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." "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."
}, },
@@ -151,6 +151,10 @@
"types.ts", "types.ts",
"utils/result.ts" "utils/result.ts"
], ],
"crafting-fabricator.ts": [
"data/fabricator-recipes.ts",
"types.ts"
],
"crafting-loot.ts": [ "crafting-loot.ts": [
"data/crafting-recipes.ts", "data/crafting-recipes.ts",
"types.ts" "types.ts"
@@ -464,6 +468,7 @@
"crafting-actions/preparation-actions.ts", "crafting-actions/preparation-actions.ts",
"crafting-design.ts", "crafting-design.ts",
"crafting-equipment.ts", "crafting-equipment.ts",
"crafting-fabricator.ts",
"crafting-utils.ts", "crafting-utils.ts",
"stores/combatStore.ts", "stores/combatStore.ts",
"stores/crafting-initial-state.ts", "stores/crafting-initial-state.ts",
@@ -230,12 +230,12 @@ describe('canProceedDiscipline', () => {
expect(result).toBe(true); expect(result).toBe(true);
}); });
it('should return false when discipline is paused', () => { it('should return true when discipline is paused (paused state is handled by activate, not canProceedDiscipline)', () => {
const state: DisciplineState = { id: 'raw-mastery', xp: 0, paused: true }; const state: DisciplineState = { id: 'raw-mastery', xp: 0, paused: true };
const result = canProceedDiscipline(rawMastery, state, { const result = canProceedDiscipline(rawMastery, state, {
rawMana: 1000, rawMana: 1000,
}); });
expect(result).toBe(false); expect(result).toBe(true);
}); });
it('should return true when raw mana is sufficient', () => { it('should return true when raw mana is sufficient', () => {
+9
View File
@@ -154,6 +154,7 @@ export const useDisciplineStore = create<DisciplineStore>()(
const newUnlockedEffects: string[] = []; const newUnlockedEffects: string[] = [];
const newProcessedPerks = [...s.processedPerks]; const newProcessedPerks = [...s.processedPerks];
const drainedIds: string[] = [];
for (const id of s.activeIds) { for (const id of s.activeIds) {
const disc = newDisciplines[id]; const disc = newDisciplines[id];
if (!disc) continue; if (!disc) continue;
@@ -168,6 +169,7 @@ export const useDisciplineStore = create<DisciplineStore>()(
if (!available || available < drain) { if (!available || available < drain) {
newDisciplines[id] = { ...disc, paused: true }; newDisciplines[id] = { ...disc, paused: true };
drainedIds.push(id);
continue; continue;
} }
@@ -206,8 +208,15 @@ export const useDisciplineStore = create<DisciplineStore>()(
MAX_CONCURRENT_DISCIPLINES + 3 MAX_CONCURRENT_DISCIPLINES + 3
); );
// Remove mana-drained disciplines from activeIds so onStopPracticing fires
const newActiveIds = s.activeIds.filter((aid) => !drainedIds.includes(aid));
if (newActiveIds.length === 0 && s.activeIds.length > 0) {
get().practicingCallbacks?.onStopPracticing?.();
}
set({ set({
disciplines: newDisciplines, disciplines: newDisciplines,
activeIds: newActiveIds,
totalXP: newXP, totalXP: newXP,
concurrentLimit: Math.max(s.concurrentLimit, newLimit), concurrentLimit: Math.max(s.concurrentLimit, newLimit),
processedPerks: newProcessedPerks, processedPerks: newProcessedPerks,
-1
View File
@@ -68,7 +68,6 @@ export function canProceedDiscipline(
gameState?: { elements?: Record<string, any>; rawMana?: number } gameState?: { elements?: Record<string, any>; rawMana?: number }
): boolean { ): boolean {
if (!disciplineState) return true; if (!disciplineState) return true;
if (disciplineState.paused) return false;
// If no game state provided, allow activation (optimistic) // If no game state provided, allow activation (optimistic)
if (!gameState) return true; if (!gameState) return true;