Files
Mana-Loop/src/lib/game/stores/craftingStore.ts
T
n8n-gitea 58aa74486e
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m9s
fix: BUG 4 - Add starting equipment to craftingStore initial state
2026-05-07 14:07:23 +02:00

373 lines
13 KiB
TypeScript

// ─── Crafting Store ─────────────────────────────────────────────────────
// Handles equipment crafting, enchantment design, and crafting progress
// This is a modular store that manages all crafting-related state
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { DesignProgress, PreparationProgress, ApplicationProgress, EquipmentCraftingProgress, EnchantmentDesign, EquipmentInstance, DesignEffect } from '../types';
// Import crafting modules for action logic
import * as CraftingUtils from '../crafting-utils';
import * as CraftingDesign from '../crafting-design';
import { computeEffects } from '../upgrade-effects';
import { hasSpecial, SPECIAL_EFFECTS } from '../special-effects';
// Import other stores to access required state
import { useSkillStore } from './skillStore';
import { useGameStore } from './gameStore';
import { useManaStore } from './manaStore';
import { useCombatStore } from './combatStore';
import { createStartingEquipment } from '../store/crafting-modules/starting-equipment';
import { useUIStore } from './uiStore';
// Import action modules
import * as ApplicationActions from '../crafting-actions/application-actions';
import * as CraftingApply from '../crafting-apply';
import * as PreparationActions from '../crafting-actions/preparation-actions';
import * as CraftingEquipment from '../crafting-equipment';
export interface CraftingState {
// Crafting progress
designProgress: DesignProgress | null;
designProgress2: DesignProgress | null; // For ENCHANT_MASTERY (2 concurrent designs)
preparationProgress: PreparationProgress | null;
applicationProgress: ApplicationProgress | null;
equipmentCraftingProgress: EquipmentCraftingProgress | null;
// Enchantment designs
enchantmentDesigns: EnchantmentDesign[];
// Unlocked enchantment effects
unlockedEffects: string[];
// Equipment instances (instanceId -> instance)
equipmentInstances: Record<string, EquipmentInstance>;
// Equipped instances (slot -> instanceId or null)
equippedInstances: Record<string, string | null>;
// Loot inventory
lootInventory: {
materials: Record<string, number>;
blueprints: string[];
};
}
export interface CraftingActions {
// Actions for design progress
setDesignProgress: (progress: DesignProgress | null) => void;
setDesignProgress2: (progress: DesignProgress | null) => void;
// Actions for preparation progress
setPreparationProgress: (progress: PreparationProgress | null) => void;
// Actions for application progress
setApplicationProgress: (progress: ApplicationProgress | null) => void;
// Actions for equipment crafting progress
setEquipmentCraftingProgress: (progress: EquipmentCraftingProgress | null) => void;
// Enchantment design actions
startDesigningEnchantment: (name: string, equipmentTypeId: string, effects: DesignEffect[]) => boolean;
cancelDesign: () => void;
saveDesign: (design: EnchantmentDesign) => void;
deleteDesign: (designId: string) => void;
// Enchantment application actions
startApplying: (equipmentInstanceId: string, designId: string) => boolean;
pauseApplication: () => void;
resumeApplication: () => void;
cancelApplication: () => void;
// Enchantment preparation actions
startPreparing: (equipmentInstanceId: string) => boolean;
cancelPreparation: () => void;
// Loot inventory actions
deleteMaterial: (materialId: string, amount: number) => void;
deleteEquipmentInstance: (instanceId: string) => void;
// Equipment crafting actions
startCraftingEquipment: (blueprintId: string) => boolean;
cancelEquipmentCrafting: () => void;
}
export type CraftingStore = CraftingState & CraftingActions;
export const useCraftingStore = create<CraftingStore>()(
persist(
(set, get) => {
const startingEquipment = createStartingEquipment();
return {
// Initial state
designProgress: null,
designProgress2: null,
preparationProgress: null,
applicationProgress: null,
equipmentCraftingProgress: null,
enchantmentDesigns: [],
unlockedEffects: [],
...startingEquipment,
lootInventory: {
materials: {},
blueprints: [],
},
// Actions
setDesignProgress: (progress) => set({ designProgress: progress }),
setDesignProgress2: (progress) => set({ designProgress2: progress }),
setPreparationProgress: (progress) => set({ preparationProgress: progress }),
setApplicationProgress: (progress) => set({ applicationProgress: progress }),
setEquipmentCraftingProgress: (progress) => set({ equipmentCraftingProgress: progress }),
// Enchantment design actions
startDesigningEnchantment: (name, equipmentTypeId, effects) => {
// Get state from other stores
const skillState = useSkillStore.getState();
const state = get(); // crafting state
const enchantingLevel = skillState.skills?.enchanting || 0;
const validation = CraftingDesign.validateDesignEffects(effects, equipmentTypeId, enchantingLevel);
if (!validation.valid) return false;
const equipType = CraftingUtils.getEquipmentType(equipmentTypeId);
if (!equipType) return false;
const efficiencyBonus = (skillState.skillUpgrades?.['efficientEnchant'] || []).length * 0.05 || 0;
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, efficiencyBonus);
if (totalCapacityCost > equipType.baseCapacity) return false;
const computedEffects = computeEffects(skillState.skillUpgrades || {}, skillState.skillTiers || {});
const hasEnchantMastery = hasSpecial(computedEffects, SPECIAL_EFFECTS.ENCHANT_MASTERY);
let updates: Partial<CraftingState> = {};
if (!state.designProgress) {
updates = {
designProgress: {
designId: CraftingUtils.generateDesignId(),
progress: 0,
required: CraftingDesign.calculateDesignTime(effects),
name,
equipmentType: equipmentTypeId,
effects,
},
};
// Update currentAction in combatStore
useCombatStore.setState({ currentAction: 'design' });
} else if (hasEnchantMastery && !state.designProgress2) {
updates = {
designProgress2: {
designId: CraftingUtils.generateDesignId(),
progress: 0,
required: CraftingDesign.calculateDesignTime(effects),
name,
equipmentType: equipmentTypeId,
effects,
},
};
} else {
return false;
}
set(updates);
return true;
},
cancelDesign: () => {
const state = get();
if (state.designProgress2 && !state.designProgress) {
set({ designProgress2: null });
} else {
set({ designProgress: null });
useCombatStore.setState({ currentAction: 'meditate' });
}
},
deleteDesign: (designId) => {
set((state) => ({
enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== designId),
}));
},
// Enchantment design save
saveDesign: (design) => {
const state = get();
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' });
}
},
// Enchantment application actions
startApplying: (equipmentInstanceId, designId) => {
return ApplicationActions.startApplying(
equipmentInstanceId,
designId,
get,
set
);
},
pauseApplication: () => {
ApplicationActions.pauseApplication(get, set);
},
resumeApplication: () => {
ApplicationActions.resumeApplication(get, set);
},
cancelApplication: () => {
ApplicationActions.cancelApplication(set);
useCombatStore.setState({ currentAction: 'meditate' });
},
// Preparation actions
startPreparing: (equipmentInstanceId) => {
// Get rawMana from manaStore
const rawMana = useManaStore.getState().rawMana;
// Temporary state to pass to preparation action
const tempState = { ...get(), rawMana } as any;
return PreparationActions.startPreparing(
equipmentInstanceId,
() => tempState,
set
);
},
cancelPreparation: () => {
PreparationActions.cancelPreparation(set);
useCombatStore.setState({ currentAction: 'meditate' });
},
// Equipment crafting actions
startCraftingEquipment: (blueprintId: string) => {
const state = get();
const rawMana = useManaStore.getState().rawMana;
const currentAction = useCombatStore.getState().currentAction;
// Check if we can start crafting
const check = CraftingEquipment.canStartEquipmentCrafting(
bluePrintId,
state.lootInventory.blueprints.includes(blueprintId),
state.lootInventory.materials,
rawMana,
currentAction
);
if (!check.canCraft) return false;
// Initialize crafting
const result = CraftingEquipment.initializeEquipmentCrafting(
bluePrintId,
state.lootInventory.materials,
rawMana
);
// Update crafting store state
set((s) => ({
lootInventory: {
...s.lootInventory,
materials: result.newMaterials,
},
equipmentCraftingProgress: result.progress,
}));
// Update mana store (deduct mana)
useManaStore.setState((s) => ({ rawMana: s.rawMana - result.manaCost }));
// Update combat store (set current action)
useCombatStore.setState({ currentAction: 'craft' });
return true;
},
cancelEquipmentCrafting: () => {
const state = get();
const progress = state.equipmentCraftingProgress;
if (!progress) return;
// Get cancel result (mana refund)
const cancelResult = CraftingEquipment.cancelEquipmentCrafting(
progress.blueprintId,
progress.manaSpent
);
// Update crafting store state
set({ equipmentCraftingProgress: null });
// Refund mana to mana store
useManaStore.setState((s) => ({ rawMana: s.rawMana + cancelResult.manaRefund }));
// Update combat store (reset action)
useCombatStore.setState({ currentAction: 'meditate' });
// Add log message to UI store
useUIStore.getState().addLog(cancelResult.logMessage);
},
// Loot inventory actions
deleteMaterial: (materialId: string, amount: number) => {
set((state) => {
const newMaterials = { ...state.lootInventory.materials };
const currentAmount = newMaterials[materialId] || 0;
const newAmount = Math.max(0, currentAmount - amount);
if (newAmount <= 0) {
delete newMaterials[materialId];
} else {
newMaterials[materialId] = newAmount;
}
return {
lootInventory: {
...state.lootInventory,
materials: newMaterials,
},
};
});
},
deleteEquipmentInstance: (instanceId: string) => {
set((state) => {
let newEquipped = { ...state.equippedInstances };
for (const [slot, id] of Object.entries(newEquipped)) {
if (id === instanceId) {
newEquipped[slot as any] = null;
}
}
const newInstances = { ...state.equipmentInstances };
delete newInstances[instanceId];
return {
equippedInstances: newEquipped,
equipmentInstances: newInstances,
};
});
},
};
},
{
name: 'mana-loop-crafting',
partialize: (state) => ({
designProgress: state.designProgress,
designProgress2: state.designProgress2,
preparationProgress: state.preparationProgress,
applicationProgress: state.applicationProgress,
equipmentCraftingProgress: state.equipmentCraftingProgress,
enchantmentDesigns: state.enchantmentDesigns,
unlockedEffects: state.unlockedEffects,
equipmentInstances: state.equipmentInstances,
equippedInstances: state.equippedInstances,
lootInventory: state.lootInventory,
}),
}
)
);