refactor: wire design-actions.ts into craftingStore.ts, remove dead code
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m23s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m23s
- Delegates startDesigningEnchantment, cancelDesign, saveDesign, deleteDesign to design-actions.ts - Passes enchanter level from attunement store to startDesigningEnchantment (fixes hardcoded 0) - Removes orphaned disenchant-actions.ts (preparation phase handles recovery inline) - Removes unused imports (CraftingUtils, CraftingDesign, useUIStore) - Updates regression test to check design-actions.ts instead of craftingStore.ts
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# Circular Dependencies
|
# Circular Dependencies
|
||||||
Generated: 2026-06-14T21:49:16.203Z
|
Generated: 2026-06-15T08:59:04.077Z
|
||||||
Found: 7 circular chain(s) — these MUST be fixed before modifying involved files.
|
Found: 7 circular chain(s) — these MUST be fixed before modifying involved files.
|
||||||
|
|
||||||
1. 1) data/guardian-encounters.ts > data/guardian-procedural.ts
|
1. 1) data/guardian-encounters.ts > data/guardian-procedural.ts
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"generated": "2026-06-14T21:49:13.932Z",
|
"generated": "2026-06-15T08:59:01.387Z",
|
||||||
"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."
|
||||||
},
|
},
|
||||||
@@ -542,6 +542,7 @@
|
|||||||
"stores/attunementStore.ts",
|
"stores/attunementStore.ts",
|
||||||
"stores/combatStore.ts",
|
"stores/combatStore.ts",
|
||||||
"stores/craftingStore.ts",
|
"stores/craftingStore.ts",
|
||||||
|
"stores/discipline-slice.ts",
|
||||||
"stores/gameStore.ts",
|
"stores/gameStore.ts",
|
||||||
"stores/manaStore.ts",
|
"stores/manaStore.ts",
|
||||||
"stores/prestigeStore.ts",
|
"stores/prestigeStore.ts",
|
||||||
|
|||||||
@@ -293,7 +293,6 @@ Mana-Loop/
|
|||||||
│ │ │ │ ├── crafting-equipment-actions.ts
|
│ │ │ │ ├── crafting-equipment-actions.ts
|
||||||
│ │ │ │ ├── crafting-material-actions.ts
|
│ │ │ │ ├── crafting-material-actions.ts
|
||||||
│ │ │ │ ├── design-actions.ts
|
│ │ │ │ ├── design-actions.ts
|
||||||
│ │ │ │ ├── disenchant-actions.ts
|
|
||||||
│ │ │ │ ├── equipment-actions.ts
|
│ │ │ │ ├── equipment-actions.ts
|
||||||
│ │ │ │ ├── index.ts
|
│ │ │ │ ├── index.ts
|
||||||
│ │ │ │ └── preparation-actions.ts
|
│ │ │ │ └── preparation-actions.ts
|
||||||
|
|||||||
@@ -292,14 +292,15 @@ describe('Issue 79 — startDesigningEnchantment slot 2', () => {
|
|||||||
expect(useCraftingStore.getState().designProgress2).not.toBeNull();
|
expect(useCraftingStore.getState().designProgress2).not.toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('store code has else-if branch for designProgress2', () => {
|
it('design-actions has else-if branch for designProgress2', () => {
|
||||||
// Verify the source code contains the fix for issue 79
|
// Verify the source code contains the fix for issue 79
|
||||||
|
// (design logic was moved to design-actions.ts during refactor)
|
||||||
const source = readFileSync(
|
const source = readFileSync(
|
||||||
'/home/user/repos/Mana-Loop/src/lib/game/stores/craftingStore.ts',
|
'/home/user/repos/Mana-Loop/src/lib/game/crafting-actions/design-actions.ts',
|
||||||
'utf-8'
|
'utf-8'
|
||||||
);
|
);
|
||||||
// The fix adds an else-if branch for designProgress2
|
// The fix adds an else-if branch for designProgress2
|
||||||
expect(source).toContain('else if (!state.designProgress2)');
|
expect(source).toContain('else if (hasEnchantMastery && !state.designProgress2)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false when both slots are occupied', async () => {
|
it('should return false when both slots are occupied', async () => {
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
// ─── Disenchanting Actions ─────────────────────────────────────────────────
|
|
||||||
|
|
||||||
import type { CraftingState } from '../stores/craftingStore.types';
|
|
||||||
import { useManaStore } from '../stores/manaStore';
|
|
||||||
|
|
||||||
export function disenchantEquipment(
|
|
||||||
instanceId: string,
|
|
||||||
get: () => CraftingState,
|
|
||||||
set: (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
|
||||||
) {
|
|
||||||
const state = get();
|
|
||||||
const instance = state.equipmentInstances[instanceId];
|
|
||||||
if (!instance || instance.enchantments.length === 0) return;
|
|
||||||
|
|
||||||
const disenchantLevel = 0;
|
|
||||||
const recoveryRate = 0.1 + disenchantLevel * 0.2;
|
|
||||||
|
|
||||||
let totalRecovered = 0;
|
|
||||||
for (const ench of instance.enchantments) {
|
|
||||||
totalRecovered += Math.floor(ench.actualCost * recoveryRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit recovered mana to raw mana pool
|
|
||||||
if (totalRecovered > 0) {
|
|
||||||
useManaStore.setState((s) => ({ rawMana: s.rawMana + totalRecovered }));
|
|
||||||
}
|
|
||||||
|
|
||||||
set((s) => ({
|
|
||||||
equipmentInstances: {
|
|
||||||
...s.equipmentInstances,
|
|
||||||
[instanceId]: {
|
|
||||||
...instance,
|
|
||||||
enchantments: [],
|
|
||||||
usedCapacity: 0,
|
|
||||||
rarity: 'common',
|
|
||||||
tags: [...(instance.tags || []), 'Ready for Enchantment'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
log: [`✨ Disenchanted ${instance.name}, recovered ${totalRecovered} mana.`],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,5 @@ export { createEquipmentInstance, equipItem, unequipItem, deleteEquipmentInstanc
|
|||||||
export { startDesigningEnchantment, cancelDesign, saveDesign, deleteDesign } from './design-actions';
|
export { startDesigningEnchantment, cancelDesign, saveDesign, deleteDesign } from './design-actions';
|
||||||
export { startPreparing, cancelPreparation } from './preparation-actions';
|
export { startPreparing, cancelPreparation } from './preparation-actions';
|
||||||
export { startApplying, pauseApplication, resumeApplication, cancelApplication } from './application-actions';
|
export { startApplying, pauseApplication, resumeApplication, cancelApplication } from './application-actions';
|
||||||
export { disenchantEquipment } from './disenchant-actions';
|
|
||||||
export { startCraftingEquipment, cancelEquipmentCrafting, deleteMaterial } from './crafting-equipment-actions';
|
export { startCraftingEquipment, cancelEquipmentCrafting, deleteMaterial } from './crafting-equipment-actions';
|
||||||
export { getEquipmentSpells, getEquipmentEffects, getAvailableCapacity } from './computed-getters';
|
export { getEquipmentSpells, getEquipmentEffects, getAvailableCapacity } from './computed-getters';
|
||||||
|
|||||||
@@ -3,24 +3,21 @@ import { create } from 'zustand';
|
|||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import type { EquipmentSlot } from '../types/equipmentSlot';
|
import type { EquipmentSlot } from '../types/equipmentSlot';
|
||||||
import type { CraftingStore, CraftingState } from './craftingStore.types';
|
import type { CraftingStore, CraftingState } from './craftingStore.types';
|
||||||
import * as CraftingUtils from '../crafting-utils';
|
|
||||||
import * as CraftingDesign from '../crafting-design';
|
|
||||||
import { useManaStore } from './manaStore';
|
import { useManaStore } from './manaStore';
|
||||||
import { useCombatStore } from './combatStore';
|
import { useCombatStore } from './combatStore';
|
||||||
|
|
||||||
import { useUIStore } from './uiStore';
|
|
||||||
import { getEnchantingEfficiencyBonus } from '../effects/discipline-effects';
|
|
||||||
import { hasSpecial, SPECIAL_EFFECTS } from '../effects/special-effects';
|
|
||||||
import * as ApplicationActions from '../crafting-actions/application-actions';
|
import * as ApplicationActions from '../crafting-actions/application-actions';
|
||||||
import * as PreparationActions from '../crafting-actions/preparation-actions';
|
import * as PreparationActions from '../crafting-actions/preparation-actions';
|
||||||
import { equipItem as equipItemAction, unequipItem as unequipItemAction } from '../crafting-actions/equipment-actions';
|
import { equipItem as equipItemAction, unequipItem as unequipItemAction } from '../crafting-actions/equipment-actions';
|
||||||
|
import { startDesigningEnchantment as startDesignAction, cancelDesign as cancelDesignAction, saveDesign as saveDesignAction, deleteDesign as deleteDesignAction } from '../crafting-actions/design-actions';
|
||||||
|
import { useAttunementStore } from './attunementStore';
|
||||||
import { ErrorCode } from '../utils/result';
|
import { ErrorCode } from '../utils/result';
|
||||||
import { createSafeStorage } from '../utils/safe-persist';
|
import { createSafeStorage } from '../utils/safe-persist';
|
||||||
import { createDefaultCraftingState } from './crafting-initial-state';
|
import { createDefaultCraftingState } from './crafting-initial-state';
|
||||||
import { craftMaterial as craftMaterialAction } from '../crafting-actions/crafting-material-actions';
|
import { craftMaterial as craftMaterialAction } from '../crafting-actions/crafting-material-actions';
|
||||||
import { processEquipmentCraftingTick } from './crafting-equipment-tick';
|
import { processEquipmentCraftingTick } from './crafting-equipment-tick';
|
||||||
import { startCraftingEquipment, cancelEquipmentCrafting, startFabricatorCrafting } from './pipelines/equipment-crafting';
|
import { startCraftingEquipment, cancelEquipmentCrafting, startFabricatorCrafting } from './pipelines/equipment-crafting';
|
||||||
import { computeEffects } from '../effects/upgrade-effects';
|
|
||||||
|
|
||||||
export const useCraftingStore = create<CraftingStore>()(
|
export const useCraftingStore = create<CraftingStore>()(
|
||||||
persist(
|
persist(
|
||||||
@@ -36,100 +33,53 @@ export const useCraftingStore = create<CraftingStore>()(
|
|||||||
setApplicationProgress: (progress) => set({ applicationProgress: progress }),
|
setApplicationProgress: (progress) => set({ applicationProgress: progress }),
|
||||||
setEquipmentCraftingProgress: (progress) => set({ equipmentCraftingProgress: progress }),
|
setEquipmentCraftingProgress: (progress) => set({ equipmentCraftingProgress: progress }),
|
||||||
|
|
||||||
// Enchantment design actions
|
// Enchantment design actions — delegated to design-actions.ts
|
||||||
startDesigningEnchantment: (name, equipmentTypeId, effects) => {
|
startDesigningEnchantment: (name, equipmentTypeId, effects) => {
|
||||||
const state = get(); // crafting state
|
const attunements = useAttunementStore.getState();
|
||||||
const efficiencyBonus = getEnchantingEfficiencyBonus();
|
const enchanterLevel = attunements.attunements?.enchanter?.level ?? 0;
|
||||||
|
const success = startDesignAction(
|
||||||
const validation = CraftingDesign.validateDesignEffects(effects, equipmentTypeId, 0, state.unlockedEffects);
|
|
||||||
if (!validation.valid) return false;
|
|
||||||
|
|
||||||
const equipType = CraftingUtils.getEquipmentType(equipmentTypeId);
|
|
||||||
if (!equipType) return false;
|
|
||||||
|
|
||||||
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, efficiencyBonus);
|
|
||||||
|
|
||||||
if (totalCapacityCost > equipType.baseCapacity) return false;
|
|
||||||
|
|
||||||
let updates: Partial<CraftingState> = {};
|
|
||||||
|
|
||||||
if (!state.designProgress) {
|
|
||||||
updates = {
|
|
||||||
designProgress: {
|
|
||||||
designId: CraftingUtils.generateDesignId(),
|
|
||||||
progress: 0,
|
|
||||||
required: CraftingDesign.calculateDesignTime(effects),
|
|
||||||
name,
|
name,
|
||||||
equipmentType: equipmentTypeId,
|
equipmentTypeId,
|
||||||
effects,
|
effects,
|
||||||
},
|
{ skills: { enchanting: enchanterLevel } },
|
||||||
};
|
get,
|
||||||
// Update currentAction in combatStore
|
set as unknown as (fn: (state: CraftingState) => Partial<CraftingState>) => void
|
||||||
|
);
|
||||||
|
if (success) {
|
||||||
useCombatStore.setState({ currentAction: 'design' });
|
useCombatStore.setState({ currentAction: 'design' });
|
||||||
} else if (!state.designProgress2) {
|
|
||||||
// Check for Enchant Mastery before allowing second design slot
|
|
||||||
const computedEffects = computeEffects();
|
|
||||||
const hasEnchantMastery = hasSpecial(computedEffects, SPECIAL_EFFECTS.ENCHANT_MASTERY);
|
|
||||||
if (!hasEnchantMastery) return false;
|
|
||||||
updates = {
|
|
||||||
designProgress2: {
|
|
||||||
designId: CraftingUtils.generateDesignId(),
|
|
||||||
progress: 0,
|
|
||||||
required: CraftingDesign.calculateDesignTime(effects),
|
|
||||||
name,
|
|
||||||
equipmentType: equipmentTypeId,
|
|
||||||
effects,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
useCombatStore.setState({ currentAction: 'design' });
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return success;
|
||||||
set(updates);
|
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelDesign: (slot?: 1 | 2) => {
|
cancelDesign: (slot?: 1 | 2) => {
|
||||||
const state = get();
|
|
||||||
if (slot === 2) {
|
if (slot === 2) {
|
||||||
|
const state = get();
|
||||||
if (state.designProgress2) {
|
if (state.designProgress2) {
|
||||||
set({ designProgress2: null });
|
set({ designProgress2: null });
|
||||||
}
|
}
|
||||||
} else if (slot === 1) {
|
} else if (slot === 1) {
|
||||||
|
const state = get();
|
||||||
if (state.designProgress) {
|
if (state.designProgress) {
|
||||||
set({ designProgress: null });
|
set({ designProgress: null });
|
||||||
useCombatStore.setState({ currentAction: 'meditate' });
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
}
|
}
|
||||||
} else if (state.designProgress) {
|
} else {
|
||||||
set({ designProgress: null });
|
cancelDesignAction(get, set as unknown as (fn: (state: CraftingState) => Partial<CraftingState>) => void);
|
||||||
|
const state = get();
|
||||||
|
// If both slots are now null, reset to meditate
|
||||||
|
if (!state.designProgress && !state.designProgress2) {
|
||||||
useCombatStore.setState({ currentAction: 'meditate' });
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
} else if (state.designProgress2) {
|
}
|
||||||
set({ designProgress2: null });
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteDesign: (designId) => {
|
deleteDesign: (designId) => {
|
||||||
set((state) => ({
|
deleteDesignAction(designId, set as unknown as (fn: (state: CraftingState) => Partial<CraftingState>) => void);
|
||||||
enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== designId),
|
|
||||||
}));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Enchantment design save
|
|
||||||
saveDesign: (design) => {
|
saveDesign: (design) => {
|
||||||
const state = get();
|
saveDesignAction(design, get, set as unknown as (fn: (state: CraftingState) => Partial<CraftingState>) => void);
|
||||||
if (state.designProgress2 && state.designProgress2.designId === design.id) {
|
|
||||||
set((s) => ({
|
|
||||||
enchantmentDesigns: [...s.enchantmentDesigns, design],
|
|
||||||
designProgress2: null,
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
set((s) => ({
|
|
||||||
enchantmentDesigns: [...s.enchantmentDesigns, design],
|
|
||||||
designProgress: null,
|
|
||||||
}));
|
|
||||||
useCombatStore.setState({ currentAction: 'meditate' });
|
useCombatStore.setState({ currentAction: 'meditate' });
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Enchantment application actions
|
// Enchantment application actions
|
||||||
|
|||||||
Reference in New Issue
Block a user