import { EQUIPMENT_TYPES } from '@/lib/game/data/equipment'; import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from '@/lib/game/data/enchantment-effects'; import type { DesignEffect, EquipmentInstance, EquipmentCategory } from '@/lib/game/types'; import { calculateDesignCapacityCost as calcCapacityCost, calculateDesignTime as calcDesignTime } from '@/lib/game/crafting-design'; /** * Get available effects for selected equipment type (only unlocked ones) * Requirement (task3 bug #7): Show incompatible enchantments in greyed-out "Unavailable" section */ export function getAvailableEffects( selectedEquipmentType: string | null, unlockedEffects: string[] ) { if (!selectedEquipmentType) return []; const type = EQUIPMENT_TYPES[selectedEquipmentType]; if (!type) return []; return Object.values(ENCHANTMENT_EFFECTS).filter( effect => effect.allowedEquipmentCategories.includes(type.category) && (unlockedEffects.length === 0 || unlockedEffects.includes(effect.id)) ); } /** * Get incompatible effects (unlocked but not for this equipment type) */ export function getIncompatibleEffects( selectedEquipmentType: string | null, unlockedEffects: string[] ) { if (!selectedEquipmentType) return []; const type = EQUIPMENT_TYPES[selectedEquipmentType]; if (!type) return []; return Object.values(ENCHANTMENT_EFFECTS).filter( effect => !effect.allowedEquipmentCategories.includes(type.category) && unlockedEffects.includes(effect.id) ); } /** * Get equipment types that the player actually owns (has instances of) * This ensures enchantment compatibility is based on owned items, not just blueprints */ export function getOwnedEquipmentTypes(equipmentInstances: Record) { // Get all unique equipment type IDs from owned instances const ownedEquipmentTypeIds = new Set(); // Check all equipment instances the player owns for (const instance of Object.values(equipmentInstances || {})) { ownedEquipmentTypeIds.add(instance.typeId); } // Filter EQUIPMENT_TYPES to only include types the player owns return Object.values(EQUIPMENT_TYPES).filter(type => ownedEquipmentTypeIds.has(type.id)); } /** * Get the reason why an effect is incompatible */ export function getIncompatibilityReason( effect: { id: string; name: string; description: string; allowedEquipmentCategories: EquipmentCategory[] }, selectedEquipmentType: string | null ): string { if (!selectedEquipmentType) return 'No equipment selected'; const type = EQUIPMENT_TYPES[selectedEquipmentType]; if (!type) return 'Unknown equipment type'; // Check what categories this effect is allowed for const allowedCategories = effect.allowedEquipmentCategories; const equipmentCategory = type.category; if (allowedCategories.includes(equipmentCategory)) { return 'Compatible'; } // Provide specific reasons if (allowedCategories.includes('weapon' as EquipmentCategory) && equipmentCategory !== 'sword' && equipmentCategory !== 'caster' && equipmentCategory !== 'catalyst') { return `Requires a weapon (${allowedCategories.filter(c => ['sword', 'caster', 'catalyst'].includes(c)).join(', ')})`; } return `Requires ${allowedCategories.join(' or ')} equipment`; } /** * Calculate total capacity cost for current design * Delegates to canonical calculateDesignCapacityCost from crafting-design */ export function calculateDesignCapacityCost( selectedEffects: DesignEffect[], efficiencyBonus: number ): number { return calcCapacityCost(selectedEffects, efficiencyBonus); } /** * Get capacity limit for selected equipment type */ export function getEquipmentCapacity(selectedEquipmentType: string | null): number { return selectedEquipmentType ? EQUIPMENT_TYPES[selectedEquipmentType]?.baseCapacity || 0 : 0; } /** * Calculate design time * Delegates to canonical calculateDesignTime from crafting-design */ export function calculateDesignTime(selectedEffects: DesignEffect[]): number { return calcDesignTime(selectedEffects); } /** * Add effect to design */ export function addEffectToDesign( effectId: string, selectedEffects: DesignEffect[], efficiencyBonus: number, setSelectedEffects: (effects: DesignEffect[]) => void ) { const existing = selectedEffects.find(e => e.effectId === effectId); const effectDef = ENCHANTMENT_EFFECTS[effectId]; if (!effectDef) return; if (existing) { if (existing.stacks < effectDef.maxStacks) { setSelectedEffects(selectedEffects.map(e => e.effectId === effectId ? { ...e, stacks: e.stacks + 1 } : e )); } } else { setSelectedEffects([...selectedEffects, { effectId, stacks: 1, capacityCost: calculateEffectCapacityCost(effectId, 1, efficiencyBonus), }]); } } /** * Remove effect from design */ export function removeEffectFromDesign( effectId: string, selectedEffects: DesignEffect[], setSelectedEffects: (effects: DesignEffect[]) => void ) { const existing = selectedEffects.find(e => e.effectId === effectId); if (!existing) return; if (existing.stacks > 1) { setSelectedEffects(selectedEffects.map(e => e.effectId === effectId ? { ...e, stacks: e.stacks - 1 } : e )); } else { setSelectedEffects(selectedEffects.filter(e => e.effectId !== effectId)); } }