// ─── Crafting Helper Utilities ───────────────────────────────────────────────── // Instance/ID generation and helper functions extracted from crafting-slice.ts import type { EquipmentInstance, EnchantmentDesign, DesignEffect } from './types'; import { EQUIPMENT_TYPES, type EquipmentCategory, type EquipmentSlot } from './data/equipment'; import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects'; import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes'; // ─── Instance/ID Generation ────────────────────────────────────────────────── export function generateInstanceId(): string { return `equip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } export function generateDesignId(): string { return `design_${Date.now()}`; } // ─── Equipment Category & Type Helpers ─────────────────────────────────────── export function getEquipmentCategory(typeId: string): EquipmentCategory | null { const type = EQUIPMENT_TYPES[typeId]; return type?.category || null; } export function getEquipmentType(typeId: string) { return EQUIPMENT_TYPES[typeId] || null; } // ─── Capacity Calculation Helpers ──────────────────────────────────────────── export function calculateDesignCapacityCost(effects: DesignEffect[], efficiencyBonus: number = 0): number { return effects.reduce((total, eff) => total + calculateEffectCapacityCost(eff.effectId, eff.stacks, efficiencyBonus), 0); } export function getAvailableCapacity(instance: EquipmentInstance): number { return instance.totalCapacity - instance.usedCapacity; } export function designFitsInEquipment(design: EnchantmentDesign, instance: EquipmentInstance): boolean { return instance.usedCapacity + design.totalCapacityUsed <= instance.totalCapacity; } // ─── Time Calculation Helpers ──────────────────────────────────────────────── export function calculateDesignTime(effects: DesignEffect[]): number { let time = 1; for (const eff of effects) { const effectDef = ENCHANTMENT_EFFECTS[eff.effectId]; if (effectDef) { time += 0.5 * eff.stacks; } } return time; } export function calculatePrepTime(equipmentCapacity: number): number { return 2 + Math.floor(equipmentCapacity / 50); } export function calculateApplicationTime(design: EnchantmentDesign): number { return 2 + design.effects.reduce((total, eff) => total + eff.stacks, 0); } // ─── Mana Cost Calculation Helpers ─────────────────────────────────────────── export function calculatePrepManaCost(equipmentCapacity: number): number { return equipmentCapacity * 10; } export function calculateApplicationManaPerHour(design: EnchantmentDesign): number { return 20 + design.effects.reduce((total, eff) => total + eff.stacks * 5, 0); } export function calculateManaPerHourForPrep(equipmentCapacity: number, prepTime: number): number { return calculatePrepManaCost(equipmentCapacity) / prepTime; } // ─── Recipe & Crafting Helpers ─────────────────────────────────────────────── export function checkRecipeMaterials( recipe: CraftingRecipe, materials: Record ): { canCraft: boolean; missingMaterials: Record } { const missingMaterials: Record = {}; let canCraft = true; for (const [matId, amount] of Object.entries(recipe.materials)) { const have = materials[matId] || 0; if (have < amount) { missingMaterials[matId] = amount - have; canCraft = false; } } return { canCraft, missingMaterials }; } export function deductRecipeMaterials( recipe: CraftingRecipe, materials: Record ): Record { const newMaterials = { ...materials }; for (const [matId, amount] of Object.entries(recipe.materials)) { newMaterials[matId] = (newMaterials[matId] || 0) - amount; if (newMaterials[matId] <= 0) { delete newMaterials[matId]; } } return newMaterials; } export function refundCraftMaterials(recipe: CraftingRecipe, refundRate: number = 0.5): Record { const refunds: Record = {}; for (const [matId, amount] of Object.entries(recipe.materials)) { refunds[matId] = Math.floor(amount * refundRate); } return refunds; } // ─── Validation Helpers ────────────────────────────────────────────────────── export function canEquipInSlot( instance: EquipmentInstance, slot: EquipmentSlot, currentlyEquipped: Record ): boolean { const type = EQUIPMENT_TYPES[instance.typeId]; if (!type) return false; const validSlots = type.category === 'accessory' ? ['accessory1', 'accessory2'] : [type.slot]; if (!validSlots.includes(slot)) return false; if (currentlyEquipped[slot] === instance.instanceId) return true; const isTwoHanded = type.twoHanded === true; if (isTwoHanded) { if (currentlyEquipped.mainHand || currentlyEquipped.offHand) { return false; } if (slot !== 'mainHand') return false; } if (slot === 'offHand' && currentlyEquipped.mainHand) { const mainHandType = EQUIPMENT_TYPES[currentlyEquipped.mainHand]; if (mainHandType?.twoHanded) { return false; } } return true; } export function isTwoHanded(typeId: string): boolean { return EQUIPMENT_TYPES[typeId]?.twoHanded === true; }