// ─── Equipment Crafting System ────────────────────────────────────────────── // Equipment crafting functions import type { EquipmentInstance, EquipmentCraftingProgress } from './types'; import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes'; import { EQUIPMENT_TYPES } from './data/equipment'; import { ok, fail, ErrorCode } from './utils/result'; import type { Result } from './utils/result'; import { HOURS_PER_TICK } from './constants'; const MANA_REFUND_RATE = 0.5; // ─── Equipment Crafting Validation ────────────────────────────────────────── export function canStartEquipmentCrafting( blueprintId: string, hasBlueprint: boolean, materials: Record, currentMana: number, currentAction: string ): { canCraft: boolean; reason?: string; recipe?: CraftingRecipe; missingMaterials?: Record; missingMana?: number } { if (currentAction !== 'meditate') return { canCraft: false, reason: 'Must be in meditate state' }; const recipe = CRAFTING_RECIPES[blueprintId]; if (!recipe) return { canCraft: false, reason: 'Invalid blueprint' }; if (!hasBlueprint) return { canCraft: false, reason: 'Blueprint not acquired' }; const { canCraft, missingMaterials } = canCraftRecipe(recipe, materials, currentMana); if (canCraft) return { canCraft: true, recipe }; const missingManaAmount = Math.max(0, recipe.manaCost - currentMana); return { canCraft: false, reason: missingManaAmount > 0 ? 'Insufficient mana' : 'Missing materials', recipe, missingMaterials, missingMana: missingManaAmount > 0 ? missingManaAmount : undefined, }; } // ─── Equipment Crafting Execution ─────────────────────────────────────────── export interface CraftingInitResult { recipe: CraftingRecipe; newMaterials: Record; manaCost: number; progress: EquipmentCraftingProgress; } export function initializeEquipmentCrafting( blueprintId: string, materials: Record, _currentMana: number ): CraftingInitResult { const recipe = CRAFTING_RECIPES[blueprintId]; 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 { recipe, newMaterials, manaCost: recipe.manaCost, progress: { blueprintId, equipmentTypeId: recipe.equipmentTypeId, progress: 0, required: recipe.craftTime, manaSpent: recipe.manaCost, }, }; } // ─── Crafting Progress ────────────────────────────────────────────────────── export interface CraftingTickResult { progress: number; isComplete: boolean; } export function calculateCraftingTick(currentProgress: number, required: number): CraftingTickResult { const progress = currentProgress + HOURS_PER_TICK; return { progress, isComplete: progress >= required }; } // ─── Crafting Completion ─────────────────────────────────────────────────── const BASE_EQUIPMENT_QUALITY = 100; export function completeEquipmentCrafting( blueprintId: string, recipe: CraftingRecipe ): Result<{ instanceId: string; instance: EquipmentInstance; logMessage: string }> { const equipType = EQUIPMENT_TYPES[recipe.equipmentTypeId]; if (!equipType) return fail(ErrorCode.INVALID_EQUIPMENT_TYPE, `Invalid equipment type: ${recipe.equipmentTypeId}`); const instanceId = `equip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; return ok({ instanceId, instance: { instanceId, typeId: recipe.equipmentTypeId, name: recipe.name, enchantments: [], usedCapacity: 0, totalCapacity: equipType.baseCapacity, rarity: recipe.rarity, quality: BASE_EQUIPMENT_QUALITY, tags: [], }, logMessage: `🔨 Crafted ${recipe.name}!`, }); } // ─── Crafting Cancellation ────────────────────────────────────────────────── export interface CraftingCancelResult { manaRefund: number; logMessage: string; } export function cancelEquipmentCrafting(blueprintId: string, manaSpent: number): CraftingCancelResult { const recipe = CRAFTING_RECIPES[blueprintId]; if (!recipe) return { manaRefund: 0, logMessage: 'Invalid crafting recipe.' }; const manaRefund = Math.floor(manaSpent * MANA_REFUND_RATE); return { manaRefund, logMessage: `🚫 Equipment crafting cancelled. Refunded ${manaRefund} mana.` }; } // ─── Recipe Information ───────────────────────────────────────────────────── export function getRecipe(blueprintId: string): CraftingRecipe | null { return CRAFTING_RECIPES[blueprintId] || null; } export function getCraftableRecipes( blueprints: string[], materials: Record, currentMana: number ): CraftingRecipe[] { const craftable: CraftingRecipe[] = []; for (const blueprintId of blueprints) { const recipe = CRAFTING_RECIPES[blueprintId]; if (!recipe) continue; if (canCraftRecipe(recipe, materials, currentMana).canCraft) craftable.push(recipe); } return craftable; } // ─── Material Management ──────────────────────────────────────────────────── export function deleteMaterials(materialId: string, amount: number, materials: Record): { newMaterials: Record; deleted: number; } { const currentAmount = materials[materialId] || 0; const deleted = Math.min(amount, currentAmount); const remaining = Math.max(0, currentAmount - amount); const newMaterials = { ...materials }; if (remaining <= 0) { delete newMaterials[materialId]; } else { newMaterials[materialId] = remaining; } return { newMaterials, deleted }; } export function getMaterialCount(materials: Record, materialId: string): number { return materials[materialId] || 0; } export function addMaterials(materials: Record, materialId: string, amount: number): Record { const newMaterials = { ...materials }; newMaterials[materialId] = (newMaterials[materialId] || 0) + amount; return newMaterials; }