// ─── Crafting Slice Logic ───────────────────────────────────────────────── import type { StateCreator } from 'zustand'; import type { CraftingStore } from './types'; import type { DesignEffect, EnchantmentDesign, EquipmentInstance } from '../../types'; import type { EquipmentSlot } from '../../data/equipment'; import { initialCraftingState } from './initial-state'; import { generateInstanceId, generateDesignId, createEquipmentInstance, calculateDesignTime, calculatePreparationTime, calculatePreparationManaCost, calculateApplicationTime, calculateApplicationManaPerHour } from './utils'; import { EQUIPMENT_SLOTS, getEquipmentType } from '../../data/equipment'; import { createSelectors } from './selectors'; import { processDesignTick, processPreparationTick, processApplicationTick } from './tick-processors'; // ─── Cached Skills Workaround ────────────────────────────────────────────── // We need to access skills from the main store - this is a workaround // The store will pass skills when calling these methods let cachedSkills: Record = {}; export function setCachedSkills(skills: Record): void { cachedSkills = skills; } // ─── Slice Creator ───────────────────────────────────────────────────────── export const createCraftingSlice: StateCreator = (set, get) => { const selectors = createSelectors(get); return { ...initialCraftingState, // Equipment management createEquipment: (typeId: string, slot?: EquipmentSlot) => { const instance = createEquipmentInstance(typeId); set((state) => ({ equipmentInstances: { ...state.equipmentInstances, [instance.instanceId]: instance, }, })); // Auto-equip if slot provided if (slot) { get().equipInstance(instance.instanceId, slot); } return instance; }, equipInstance: (instanceId: string, slot: EquipmentSlot) => { const instance = get().equipmentInstances[instanceId]; if (!instance) return; const typeDef = getEquipmentType(instance.typeId); if (!typeDef) return; // Check if equipment can go in this slot if (typeDef.slot !== slot) { // For accessories, both accessory1 and accessory2 are valid if (typeDef.category !== 'accessory' || (slot !== 'accessory1' && slot !== 'accessory2')) { return; } } set((state) => ({ equippedInstances: { ...state.equippedInstances, [slot]: instanceId, }, })); }, unequipSlot: (slot: EquipmentSlot) => { set((state) => ({ equippedInstances: { ...state.equippedInstances, [slot]: null, }, })); }, deleteInstance: (instanceId: string) => { set((state) => { const newInstanceMap = { ...state.equipmentInstances }; delete newInstanceMap[instanceId]; // Remove from equipped slots const newEquipped = { ...state.equippedInstances }; for (const slot of EQUIPMENT_SLOTS) { if (newEquipped[slot] === instanceId) { newEquipped[slot] = null; } } return { equipmentInstances: newInstanceMap, equippedInstances: newEquipped, }; }); }, // Enchantment design startDesign: (name: string, equipmentType: string, effects: DesignEffect[]) => { const totalCapacity = effects.reduce((sum, e) => sum + e.capacityCost, 0); const designTime = calculateDesignTime(effects); const design: EnchantmentDesign = { id: generateDesignId(), name, equipmentType, effects, totalCapacityUsed: totalCapacity, designTime, created: Date.now(), }; set((state) => ({ enchantmentDesigns: [...state.enchantmentDesigns, design], designProgress: { designId: design.id, progress: 0, required: designTime, name, equipmentType, effects, }, })); }, cancelDesign: () => { const progress = get().designProgress; if (!progress) return; set((state) => ({ designProgress: null, enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== progress.designId), })); }, deleteDesign: (designId: string) => { set((state) => ({ enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== designId), })); }, // Equipment preparation startPreparation: (instanceId: string) => { const instance = get().equipmentInstances[instanceId]; if (!instance) return; const prepTime = calculatePreparationTime(instance.typeId); const manaCost = calculatePreparationManaCost(instance.typeId); set({ preparationProgress: { equipmentInstanceId: instanceId, progress: 0, required: prepTime, manaCostPaid: 0, }, }); }, cancelPreparation: () => { set({ preparationProgress: null }); }, // Enchantment application startApplication: (instanceId: string, designId: string) => { const instance = get().equipmentInstances[instanceId]; const design = get().enchantmentDesigns.find(d => d.id === designId); if (!instance || !design) return; const appTime = calculateApplicationTime(design.effects, cachedSkills); const manaPerHour = calculateApplicationManaPerHour(design.effects); set({ applicationProgress: { equipmentInstanceId: instanceId, designId, progress: 0, required: appTime, manaPerHour, paused: false, manaSpent: 0, }, }); }, pauseApplication: () => { const progress = get().applicationProgress; if (!progress) return; set({ applicationProgress: { ...progress, paused: true }, }); }, resumeApplication: () => { const progress = get().applicationProgress; if (!progress) return; set({ applicationProgress: { ...progress, paused: false }, }); }, cancelApplication: () => { set({ applicationProgress: null }); }, // Tick processing - delegated to tick-processors module processDesignTick: (hours: number) => { return processDesignTick(get(), set, hours); }, processPreparationTick: (hours: number, manaAvailable: number) => { return processPreparationTick(get(), set, hours, manaAvailable); }, processApplicationTick: (hours: number, manaAvailable: number) => { return processApplicationTick(get(), set, get, hours, manaAvailable, cachedSkills); }, // Selectors - delegated to selectors module getEquippedInstance: selectors.getEquippedInstance, getAllEquipped: selectors.getAllEquipped, getAvailableSpells: selectors.getAvailableSpells, getEquipmentEffects: selectors.getEquipmentEffects, }; };