// ─── Crafting Loot Inventory System ──────────────────────────────────────────── // Loot inventory functions import type { LootInventory } from './types'; import type { CraftingRecipe } from './data/crafting-recipes'; // ─── Inventory Queries ────────────────────────────────────────────────────── // Get material count export function getMaterialCount(inventory: LootInventory, materialId: string): number { return inventory.materials[materialId] || 0; } // Check if has blueprint export function hasBlueprint(inventory: LootInventory, blueprintId: string): boolean { return inventory.blueprints.includes(blueprintId); } // Check if has any materials export function hasMaterials(inventory: LootInventory): boolean { return Object.keys(inventory.materials).length > 0; } // Get total number of unique materials export function getUniqueMaterialCount(inventory: LootInventory): number { return Object.keys(inventory.materials).length; } // Get total material stacks (sum of all quantities) export function getTotalMaterialStacks(inventory: LootInventory): number { return Object.values(inventory.materials || {}).reduce((sum, qty) => sum + qty, 0); } // ─── Inventory Modifications ──────────────────────────────────────────────── // Add materials to inventory export function addMaterial(inventory: LootInventory, materialId: string, amount: number): LootInventory { const currentAmount = inventory.materials[materialId] || 0; return { ...inventory, materials: { ...inventory.materials, [materialId]: currentAmount + amount, }, }; } // Add multiple materials at once export function addMaterials( inventory: LootInventory, materials: Record ): LootInventory { const newMaterials = { ...inventory.materials }; for (const [matId, amount] of Object.entries(materials)) { newMaterials[matId] = (newMaterials[matId] || 0) + amount; } return { ...inventory, materials: newMaterials, }; } // Remove materials from inventory export function removeMaterial(inventory: LootInventory, materialId: string, amount: number): { inventory: LootInventory; removed: number; } { const currentAmount = inventory.materials[materialId] || 0; const removed = Math.min(amount, currentAmount); const remaining = Math.max(0, currentAmount - amount); const newMaterials = { ...inventory.materials }; if (remaining <= 0) { delete newMaterials[materialId]; } else { newMaterials[materialId] = remaining; } return { inventory: { ...inventory, materials: newMaterials, }, removed, }; } // Delete materials completely (admin/debug operation) export function deleteMaterial(inventory: LootInventory, materialId: string): LootInventory { const newMaterials = { ...inventory.materials }; delete newMaterials[materialId]; return { ...inventory, materials: newMaterials, }; } // Clear all materials export function clearMaterials(inventory: LootInventory): LootInventory { return { ...inventory, materials: {}, }; } // ─── Blueprint Management ────────────────────────────────────────────────── // Add a blueprint export function addBlueprint(inventory: LootInventory, blueprintId: string): LootInventory { if (inventory.blueprints.includes(blueprintId)) { return inventory; } return { ...inventory, blueprints: [...inventory.blueprints, blueprintId], }; } // Remove a blueprint export function removeBlueprint(inventory: LootInventory, blueprintId: string): LootInventory { return { ...inventory, blueprints: inventory.blueprints.filter(id => id !== blueprintId), }; } // Check and remove consumed blueprint (if single-use) export function consumeBlueprint(inventory: LootInventory, _blueprintId: string): LootInventory { // Blueprints are persistent for now, but this function provides future flexibility return inventory; } // ─── Recipe Material Checks ───────────────────────────────────────────────── // Check if specific materials are available export function checkMaterialsAvailable( inventory: LootInventory, required: Record ): { hasAll: boolean; missing: Record } { const missing: Record = {}; let hasAll = true; for (const [matId, amount] of Object.entries(required)) { const available = inventory.materials[matId] || 0; if (available < amount) { missing[matId] = amount - available; hasAll = false; } } return { hasAll, missing }; } // Deduct recipe materials export function deductRecipeMaterials( inventory: LootInventory, recipeMaterials: Record ): LootInventory { const newMaterials = { ...inventory.materials }; for (const [matId, amount] of Object.entries(recipeMaterials)) { const newAmount = (newMaterials[matId] || 0) - amount; if (newAmount <= 0) { delete newMaterials[matId]; } else { newMaterials[matId] = newAmount; } } return { ...inventory, materials: newMaterials, }; } // ─── Inventory Filters & Sorting ──────────────────────────────────────────── export interface MaterialWithInfo { id: string; quantity: number; name: string; rarity: string; } // Get all materials with their info export function getAllMaterials(inventory: LootInventory): MaterialWithInfo[] { return Object.entries(inventory.materials).map(([id, quantity]) => ({ id, quantity, name: id, // Could be replaced with lookup from LOOT_DROPS rarity: 'common', // Could be replaced with lookup })); } // Filter materials by minimum quantity export function filterMaterialsByQuantity( inventory: LootInventory, minQuantity: number ): Record { const filtered: Record = {}; for (const [id, qty] of Object.entries(inventory.materials)) { if (qty >= minQuantity) { filtered[id] = qty; } } return filtered; } // ─── Crafting Availability ───────────────────────────────────────────────── // Check if can craft a recipe export function canCraftFromInventory( inventory: LootInventory, recipe: CraftingRecipe, currentMana: number ): { canCraft: boolean; reason?: string; missingMaterials?: Record; missingMana?: number } { const { hasAll, missing } = checkMaterialsAvailable(inventory, recipe.materials); if (!hasAll) { return { canCraft: false, missingMaterials: missing }; } if (currentMana < recipe.manaCost) { return { canCraft: false, missingMana: recipe.manaCost - currentMana, reason: 'Insufficient mana', }; } if (!inventory.blueprints.includes(recipe.id)) { return { canCraft: false, reason: 'Blueprint not acquired' }; } return { canCraft: true }; } // Get all craftable recipes from inventory export function getCraftableRecipes( inventory: LootInventory, recipes: Record, currentMana: number ): CraftingRecipe[] { const craftable: CraftingRecipe[] = []; for (const blueprintId of inventory.blueprints) { const recipe = recipes[blueprintId]; if (!recipe) continue; const { canCraft } = canCraftFromInventory(inventory, recipe, currentMana); if (canCraft) { craftable.push(recipe); } } return craftable; } // ─── Inventory Statistics ────────────────────────────────────────────────── export interface InventoryStats { totalUniqueMaterials: number; totalMaterialStacks: number; totalBlueprints: number; craftableItems: string[]; } export function getInventoryStats(inventory: LootInventory): InventoryStats { const totalUniqueMaterials = Object.keys(inventory.materials).length; const totalMaterialStacks = Object.values(inventory.materials || {}).reduce((sum, qty) => sum + qty, 0); const totalBlueprints = inventory.blueprints.length; return { totalUniqueMaterials, totalMaterialStacks, totalBlueprints, craftableItems: [], // Would need recipes to compute }; }