fix: bugs #238,#240,#244,#246 + docs #248 update
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s

- #238: Fix spire tab inconsistent state (Max Floor 1 but Floors Cleared 0) by not inflating maxFloorReached on enterSpireMode and preserving it on exitSpireMode
- #240: Fix guardian armor display stray text by extracting stat formatters in SpireSummaryTab
- #244: Improve discipline auto-pause UX with log messages and visual feedback on DisciplineCard
- #246: Fix raw mana exceeding max cap by recomputing maxMana after discipline XP gains
- #248: Update AGENTS.md (remove gitea_get_project_boards, add gitea_start_session, 22 mana types, 8 stores, updated guardian tiers)
- #248: Update README.md (remove Prisma/SQLite refs, update mana types/guardian tiers/discipline counts)
- #248: Update GAME_BRIEFING.md (8 stores, 22 mana types, 64 disciplines, 8-tier guardians, correct code architecture)
This commit is contained in:
2026-06-01 13:54:28 +02:00
parent 7dd9ad5b92
commit fa78c7a93a
15 changed files with 448 additions and 407 deletions
+5 -4
View File
@@ -153,7 +153,7 @@ export const useCombatStore = create<CombatStore>()(
},
exitSpireMode: () => {
set({
set((s) => ({
spireMode: false,
currentAction: 'meditate',
climbDirection: null,
@@ -164,8 +164,9 @@ export const useCombatStore = create<CombatStore>()(
currentRoom: generateFloorState(1),
castProgress: 0,
clearedFloors: {},
maxFloorReached: 0,
});
// Preserve maxFloorReached — don't reset to 0 on spire exit (fix #238)
maxFloorReached: Math.max(s.maxFloorReached, 1),
}));
},
startClimbUp: () => set({ climbDirection: 'up', currentAction: 'climb' }),
@@ -219,7 +220,7 @@ export const useCombatStore = create<CombatStore>()(
climbDirection: null,
isDescending: false,
clearedFloors: {},
maxFloorReached: Math.max(s.maxFloorReached, s.currentFloor),
// Don't inflate maxFloorReached — it should reflect actual progress (fix #238)
}));
},
+8 -4
View File
@@ -55,6 +55,7 @@ export interface DisciplineStoreActions {
elements: Record<string, ElementState>;
unlockedEffects: string[];
unlockedRecipes: string[];
autoPausedNames: string[];
};
setPracticingCallbacks(callbacks: { onStartPracticing: () => void; onStopPracticing: () => void }): void;
resetDisciplines: () => void;
@@ -83,10 +84,10 @@ export const useDisciplineStore = create<DisciplineStore>()(
// Allow re-activation if discipline exists but is paused
const existing = s.disciplines[id];
if (activeIds.includes(id)) {
// If already active and paused, un-pause it
// If already active and paused (manually or auto), un-pause it
if (existing?.paused) {
return {
disciplines: { ...s.disciplines, [id]: { ...existing, paused: false } },
disciplines: { ...s.disciplines, [id]: { ...existing, paused: false, autoPaused: false } },
};
}
return s;
@@ -166,10 +167,12 @@ export const useDisciplineStore = create<DisciplineStore>()(
const newProcessedPerks = [...(s.processedPerks ?? [])];
const drainedIds: string[] = [];
const drainedNames: string[] = [];
for (const id of s.activeIds ?? []) {
const disc = newDisciplines[id];
if (!disc) continue;
if (disc.paused) continue;
if (disc.autoPaused) continue; // already auto-paused, don't re-process
const def = DISCIPLINE_MAP[id];
if (!def) continue;
@@ -179,8 +182,9 @@ export const useDisciplineStore = create<DisciplineStore>()(
const available = def.manaType === 'raw' ? rawMana : element?.current;
if (!available || available < drain) {
newDisciplines[id] = { ...disc, paused: true };
newDisciplines[id] = { ...disc, paused: true, autoPaused: true };
drainedIds.push(id);
drainedNames.push(def.name);
continue;
}
@@ -261,7 +265,7 @@ export const useDisciplineStore = create<DisciplineStore>()(
processedPerks: newProcessedPerks,
});
return { rawMana, elements, unlockedEffects: newUnlockedEffects, unlockedRecipes: newUnlockedRecipes };
return { rawMana, elements, unlockedEffects: newUnlockedEffects, unlockedRecipes: newUnlockedRecipes, autoPausedNames: drainedNames };
},
}),
{ storage: createSafeStorage(), name: 'mana-loop-discipline-store', version: 1, partialize: (state) => ({ disciplines: state.disciplines, activeIds: state.activeIds, concurrentLimit: state.concurrentLimit, totalXP: state.totalXP, processedPerks: state.processedPerks }) }
+15
View File
@@ -218,6 +218,21 @@ export const useGameStore = create<GameCoordinatorStore>()(
rawMana = disciplineResult.rawMana;
elements = disciplineResult.elements;
// Log auto-paused disciplines for better UX feedback (fix #244)
if (disciplineResult.autoPausedNames.length > 0) {
const names = disciplineResult.autoPausedNames.join(', ');
addLog('⏸️ Auto-paused (insufficient mana): ' + names);
}
// Recompute maxMana after discipline XP gains so clamping uses updated value (fix #246)
const updatedDisciplineEffects = computeDisciplineEffects();
const updatedMaxMana = computeMaxMana(
{ prestigeUpgrades: ctx.prestige.prestigeUpgrades },
undefined,
updatedDisciplineEffects,
);
rawMana = Math.min(rawMana, updatedMaxMana);
for (const [targetElem, conv] of Object.entries(disciplineEffects.conversions)) {
const conversionAmount = conv.rate * HOURS_PER_TICK;
let canConvert = true;