fix: resolve critical bugs - disciplines, debug reset, floating point, spire loop
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
Fixes:
- Issue 193: Remove unnecessary useEffect that set activeTab when spireMode is true, and redundant setAction('climb') in SpireCombatPage
- Issue 194: Fix signed_pact prerequisite check in checkDisciplinePrerequisites by accepting signedPacts param; add 'At Limit' feedback on discipline button when concurrent limit reached
- Issue 195: Add resetDisciplines(), resetAttunements(), resetCrafting() calls to createResetGame; add resetCrafting action to crafting store
- Issue 196: Fix floating point display in ElementStatsSection (mana pools) and GameStateDebug (time); fix duplicate 'Base Regen' label in ManaStatsSection
All 917 tests pass. Files stay under 400-line limit.
This commit is contained in:
@@ -3,6 +3,38 @@
|
||||
|
||||
import * as CraftingUtils from '../crafting-utils';
|
||||
import type { EquipmentInstance } from '../types';
|
||||
import type { CraftingState } from './craftingStore.types';
|
||||
|
||||
/**
|
||||
* Create the full default state for the crafting store.
|
||||
* Used by both initial store creation and resetCrafting().
|
||||
*/
|
||||
export function createDefaultCraftingState(): CraftingState {
|
||||
const initial = createInitialEquipmentInstances();
|
||||
return {
|
||||
designProgress: null,
|
||||
designProgress2: null,
|
||||
preparationProgress: null,
|
||||
applicationProgress: null,
|
||||
equipmentCraftingProgress: null,
|
||||
enchantmentDesigns: [],
|
||||
unlockedEffects: [],
|
||||
equippedInstances: initial.equippedInstances,
|
||||
equipmentInstances: initial.instances,
|
||||
lootInventory: {
|
||||
materials: {},
|
||||
blueprints: [],
|
||||
},
|
||||
enchantmentSelection: {
|
||||
selectedEquipmentType: null,
|
||||
selectedEffects: [],
|
||||
designName: '',
|
||||
selectedDesign: null,
|
||||
selectedEquipmentInstance: null,
|
||||
},
|
||||
lastError: null,
|
||||
};
|
||||
}
|
||||
|
||||
export function createInitialEquipmentInstances() {
|
||||
const staffId = CraftingUtils.generateInstanceId();
|
||||
|
||||
@@ -15,7 +15,7 @@ import * as CraftingEquipment from '../crafting-equipment';
|
||||
import { equipItem as equipItemAction, unequipItem as unequipItemAction } from '../crafting-actions/equipment-actions';
|
||||
import { ErrorCode } from '../utils/result';
|
||||
import { createSafeStorage } from '../utils/safe-persist';
|
||||
import { createInitialEquipmentInstances } from './crafting-initial-state';
|
||||
import { createDefaultCraftingState } from './crafting-initial-state';
|
||||
import {
|
||||
getFabricatorRecipe,
|
||||
deductFabricatorMana,
|
||||
@@ -28,30 +28,9 @@ import { processEquipmentCraftingTick } from './crafting-equipment-tick';
|
||||
export const useCraftingStore = create<CraftingStore>()(
|
||||
persist(
|
||||
(set, get) => {
|
||||
const initial = createInitialEquipmentInstances();
|
||||
const defaultState = createDefaultCraftingState();
|
||||
return {
|
||||
// Initial state
|
||||
designProgress: null,
|
||||
designProgress2: null,
|
||||
preparationProgress: null,
|
||||
applicationProgress: null,
|
||||
equipmentCraftingProgress: null,
|
||||
enchantmentDesigns: [],
|
||||
unlockedEffects: [],
|
||||
equippedInstances: initial.equippedInstances,
|
||||
equipmentInstances: initial.instances,
|
||||
lootInventory: {
|
||||
materials: {},
|
||||
blueprints: [],
|
||||
},
|
||||
enchantmentSelection: {
|
||||
selectedEquipmentType: null,
|
||||
selectedEffects: [],
|
||||
designName: '',
|
||||
selectedDesign: null,
|
||||
selectedEquipmentInstance: null,
|
||||
},
|
||||
lastError: null,
|
||||
...defaultState,
|
||||
|
||||
// Actions
|
||||
setDesignProgress: (progress) => set({ designProgress: progress }),
|
||||
@@ -369,6 +348,10 @@ export const useCraftingStore = create<CraftingStore>()(
|
||||
const state = get();
|
||||
return processEquipmentCraftingTick(state, set as unknown as (partial: Partial<CraftingState>) => void);
|
||||
},
|
||||
|
||||
resetCrafting: () => {
|
||||
set(createDefaultCraftingState());
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
|
||||
@@ -73,6 +73,7 @@ export interface CraftingActions {
|
||||
clearLastError: () => void;
|
||||
unlockEffects: (effectIds: string[]) => void;
|
||||
processEquipmentCraftingTick: () => { completed: boolean; logMessage?: string };
|
||||
resetCrafting: () => void;
|
||||
}
|
||||
|
||||
export type CraftingStore = CraftingState & CraftingActions;
|
||||
|
||||
@@ -95,14 +95,8 @@ export const useDisciplineStore = create<DisciplineStore>()(
|
||||
if (nonPaused >= s.concurrentLimit) return s;
|
||||
if (!canProceedDiscipline(def, existing, gameState)) return s;
|
||||
|
||||
// Invoker disciplines require at least one signed guardian pact
|
||||
if (def.attunement === 'invoker') {
|
||||
const signedPacts = gameState?.signedPacts || [];
|
||||
if (signedPacts.length === 0) return s;
|
||||
}
|
||||
|
||||
// Check discipline prerequisites (requires field → discipline XP or mana type unlock)
|
||||
const prereqCheck = checkDisciplinePrerequisites(def, s.disciplines, ALL_DISCIPLINES, gameState?.elements);
|
||||
const prereqCheck = checkDisciplinePrerequisites(def, s.disciplines, ALL_DISCIPLINES, gameState?.elements, gameState?.signedPacts);
|
||||
if (!prereqCheck.canProceed) return s;
|
||||
|
||||
// For conversion disciplines: gate on having all source mana types unlocked
|
||||
|
||||
@@ -4,6 +4,9 @@ import { useUIStore } from './uiStore';
|
||||
import { usePrestigeStore } from './prestigeStore';
|
||||
import { useManaStore } from './manaStore';
|
||||
import { useCombatStore } from './combatStore';
|
||||
import { useDisciplineStore } from './discipline-slice';
|
||||
import { useAttunementStore } from './attunementStore';
|
||||
import { useCraftingStore } from './craftingStore';
|
||||
import { computeDisciplineEffects } from '../effects/discipline-effects';
|
||||
|
||||
// Exact localStorage keys matching each store's persist config `name`
|
||||
@@ -32,6 +35,9 @@ export const createResetGame = (set: (state: Partial<GameCoordinatorState>) => v
|
||||
usePrestigeStore.getState().resetPrestige();
|
||||
useManaStore.getState().resetMana({});
|
||||
useCombatStore.getState().resetCombat(startFloor);
|
||||
useDisciplineStore.getState().resetDisciplines();
|
||||
useAttunementStore.getState().resetAttunements();
|
||||
useCraftingStore.getState().resetCrafting();
|
||||
|
||||
set({
|
||||
...initialState,
|
||||
|
||||
@@ -117,6 +117,7 @@ export function checkDisciplinePrerequisites(
|
||||
allDisciplines: Record<string, DisciplineState>,
|
||||
allDefinitions: DisciplineDefinition[],
|
||||
elements?: Record<string, { unlocked: boolean }>,
|
||||
signedPacts?: number[],
|
||||
): { canProceed: boolean; missingPrereqs: string[] } {
|
||||
if (!discipline.requires || discipline.requires.length === 0) {
|
||||
return { canProceed: true, missingPrereqs: [] };
|
||||
@@ -127,7 +128,9 @@ export function checkDisciplinePrerequisites(
|
||||
for (const reqId of discipline.requires) {
|
||||
// Special case: 'signed_pact' requires at least one guardian pact
|
||||
if (reqId === 'signed_pact') {
|
||||
missingPrereqs.push('Signed guardian pact');
|
||||
if (!signedPacts || signedPacts.length === 0) {
|
||||
missingPrereqs.push('Signed guardian pact');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user