// ─── Crafting Application System ──────────────────────────────────────────── // Application system functions import type { EquipmentInstance, AppliedEnchantment, EnchantmentDesign, ApplicationProgress } from './types'; import { calculateApplicationTime, calculateApplicationManaPerHour } from './crafting-utils'; import { HOURS_PER_TICK } from './constants'; import { hasSpecial, SPECIAL_EFFECTS } from './effects/special-effects'; import type { ComputedEffects } from './effects/upgrade-effects.types'; import type { AttunementState } from './types'; import { calculateEnchantingXP, getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } from './data/attunements'; import { ENCHANTMENT_EFFECTS } from './data/enchantment-effects'; // ─── Application Validation ───────────────────────────────────────────────── export function canApplyEnchantment( instance: EquipmentInstance | undefined, design: EnchantmentDesign | undefined, currentAction: string ): { canApply: boolean; reason?: string } { if (!instance) return { canApply: false, reason: 'Equipment instance not found' }; if (!design) return { canApply: false, reason: 'Enchantment design not found' }; if (currentAction !== 'meditate') return { canApply: false, reason: 'Must be in meditate state' }; if (!instance.tags?.includes('Ready for Enchantment')) return { canApply: false, reason: 'Equipment must be prepared for enchanting' }; if (instance.usedCapacity + design.totalCapacityUsed > instance.totalCapacity) return { canApply: false, reason: 'Not enough capacity on equipment' }; return { canApply: true }; } // ─── Application Resource Calculation ──────────────────────────────────────── export interface ApplicationCosts { time: number; manaPerHour: number; manaPerTick: number; } export function calculateApplicationCosts(design: EnchantmentDesign): ApplicationCosts { const time = calculateApplicationTime(design); const manaPerHour = calculateApplicationManaPerHour(design); const manaPerTick = manaPerHour * HOURS_PER_TICK; return { time, manaPerHour, manaPerTick }; } // ─── Application Progress ─────────────────────────────────────────────────── export function initializeApplicationProgress( equipmentInstanceId: string, designId: string, design: EnchantmentDesign ): ApplicationProgress { const costs = calculateApplicationCosts(design); return { equipmentInstanceId, designId, progress: 0, required: costs.time, manaPerHour: costs.manaPerHour, paused: false, manaSpent: 0, }; } // Free enchant chance per special effect const FREE_ENCHANT_CHANCES: Record = { [SPECIAL_EFFECTS.ENCHANT_PRESERVATION]: 0.25, [SPECIAL_EFFECTS.THRIFTY_ENCHANTER]: 0.10, [SPECIAL_EFFECTS.OPTIMIZED_ENCHANTING]: 0.25, }; export interface ApplicationTickResult { progress: number; manaSpent: number; manaConsumed: number; isComplete: boolean; triggeredFreeEnchant: boolean; } export function calculateApplicationTick( currentProgress: number, required: number, currentManaSpent: number, manaPerTick: number, computedEffects: ComputedEffects ): ApplicationTickResult { let progress = currentProgress + HOURS_PER_TICK; let manaSpent = currentManaSpent + manaPerTick; let manaConsumed = manaPerTick; let triggeredFreeEnchant = false; let freeEnchantChance = 0; for (const [special, chance] of Object.entries(FREE_ENCHANT_CHANCES)) { if (hasSpecial(computedEffects, special)) freeEnchantChance += chance; } if (freeEnchantChance > 0 && Math.random() < freeEnchantChance) { progress = required; manaConsumed = 0; manaSpent = currentManaSpent; triggeredFreeEnchant = true; } return { progress, manaSpent, manaConsumed, isComplete: progress >= required, triggeredFreeEnchant }; } // ─── Enchantment Application ──────────────────────────────────────────────── const PURE_ESSENCE_STACK_BONUS = 1.25; const PURE_ESSENCE_COST_CAP = 100; export function applyEnchantments( instance: EquipmentInstance, design: EnchantmentDesign, computedEffects: ComputedEffects ): { updatedInstance: EquipmentInstance; xpGained: number; logMessage: string } { const isPureEssenceActive = hasSpecial(computedEffects, SPECIAL_EFFECTS.PURE_ESSENCE); const newEnchantments: AppliedEnchantment[] = design.effects.map(eff => { const effectDef = ENCHANTMENT_EFFECTS[eff.effectId]; const bonusStacks = isPureEssenceActive && effectDef && effectDef.baseCapacityCost < PURE_ESSENCE_COST_CAP; return { effectId: eff.effectId, stacks: bonusStacks ? Math.ceil(eff.stacks * PURE_ESSENCE_STACK_BONUS) : eff.stacks, actualCost: eff.capacityCost, }; }); const xpGained = calculateEnchantingXP(design.totalCapacityUsed); const updatedInstance: EquipmentInstance = { ...instance, enchantments: [...instance.enchantments, ...newEnchantments], usedCapacity: instance.usedCapacity + design.totalCapacityUsed, }; return { updatedInstance, xpGained, logMessage: `✨ Enchantment "${design.name}" applied to ${instance.name}! (+${xpGained} Enchanter XP)`, }; } // ─── Attunement XP Updates ────────────────────────────────────────────────── export function updateEnchanterAttunement( attunements: Record, xpGained: number ): Record { if (!attunements?.enchanter?.active || xpGained <= 0) return attunements; const enchanterState = attunements.enchanter; let newXP = enchanterState.experience + xpGained; let newLevel = enchanterState.level; while (newLevel < MAX_ATTUNEMENT_LEVEL) { const xpNeeded = getAttunementXPForLevel(newLevel + 1); if (newXP >= xpNeeded) { newXP -= xpNeeded; newLevel++; } else { break; } } return { ...attunements, enchanter: { ...enchanterState, level: newLevel, experience: newXP }, }; } // ─── Application Cancellation ──────────────────────────────────────────────── export function cancelApplication() { return { logMessage: 'Enchantment application cancelled.' }; } export function pauseApplication() { return { logMessage: 'Enchantment application paused.' }; } export function resumeApplication() { return { logMessage: 'Enchantment application resumed.' }; } // ─── Progress Calculations ────────────────────────────────────────────────── export function getApplicationManaCostForTick(manaPerHour: number): number { return manaPerHour * HOURS_PER_TICK; } export function getApplicationRemainingTime(currentProgress: number, required: number): number { return Math.max(0, required - currentProgress); } export function getApplicationCompletionPercent(currentProgress: number, required: number): number { return Math.min(100, Math.floor((currentProgress / required) * 100)); }