// Crafting System - Handles recipe management and item crafting class CraftingSystem { constructor(scene) { this.scene = scene; this.recipes = {}; this.categories = []; this.unlockedRecipes = new Set(); // Crafting queue this.craftingQueue = []; this.isCrafting = false; this.currentCraft = null; this.craftProgress = 0; console.log('🛠️ CraftingSystem initialized'); } async loadRecipes() { try { // Load recipes from JSON file const response = await fetch('data/recipes.json'); const data = await response.json(); this.recipes = data.recipes; this.categories = data.categories; // Initialize unlocked recipes Object.keys(this.recipes).forEach(recipeId => { const recipe = this.recipes[recipeId]; if (recipe.unlocked) { this.unlockedRecipes.add(recipeId); } }); console.log(`✅ Loaded ${Object.keys(this.recipes).length} recipes`); console.log(`🔓 ${this.unlockedRecipes.size} unlocked recipes`); return true; } catch (error) { console.error('❌ Failed to load recipes:', error); return false; } } // Get all recipes (optionally filtered by category) getRecipes(category = 'all') { const recipeList = Object.values(this.recipes); if (category === 'all') { return recipeList; } return recipeList.filter(recipe => recipe.category === category); } // Get only unlocked recipes getUnlockedRecipes(category = 'all') { return this.getRecipes(category).filter(recipe => this.unlockedRecipes.has(recipe.id) ); } // Check if recipe is unlocked isUnlocked(recipeId) { return this.unlockedRecipes.has(recipeId); } // Unlock a recipe unlockRecipe(recipeId) { if (this.recipes[recipeId]) { this.unlockedRecipes.add(recipeId); console.log(`🔓 Unlocked recipe: ${this.recipes[recipeId].name}`); // Notify UI this.scene.events.emit('recipe-unlocked', recipeId); return true; } return false; } // Check if player has required ingredients canCraft(recipeId) { const recipe = this.recipes[recipeId]; if (!recipe) return false; // Check if unlocked if (!this.isUnlocked(recipeId)) { return { canCraft: false, reason: 'locked' }; } // Check ingredients const inventory = this.scene.inventorySystem; if (!inventory) { return { canCraft: false, reason: 'no_inventory' }; } const missing = []; for (const [itemId, requiredAmount] of Object.entries(recipe.ingredients)) { const hasAmount = inventory.getItemCount(itemId); if (hasAmount < requiredAmount) { missing.push({ item: itemId, required: requiredAmount, has: hasAmount, need: requiredAmount - hasAmount }); } } if (missing.length > 0) { return { canCraft: false, reason: 'missing_ingredients', missing }; } return { canCraft: true }; } // Start crafting an item craftItem(recipeId) { const recipe = this.recipes[recipeId]; if (!recipe) { console.warn(`⚠️ Recipe not found: ${recipeId}`); return false; } // Check if can craft const check = this.canCraft(recipeId); if (!check.canCraft) { console.warn(`⚠️ Cannot craft ${recipe.name}: ${check.reason}`); return false; } // Consume ingredients const inventory = this.scene.inventorySystem; for (const [itemId, amount] of Object.entries(recipe.ingredients)) { inventory.removeItem(itemId, amount); } // Add to crafting queue this.craftingQueue.push({ recipeId: recipeId, recipe: recipe, startTime: Date.now(), duration: recipe.craftTime || 1000 }); console.log(`🔨 Started crafting: ${recipe.name}`); // Start crafting if not already crafting if (!this.isCrafting) { this.startNextCraft(); } // Emit event this.scene.events.emit('craft-started', recipeId); // Play sound if (this.scene.soundManager && this.scene.soundManager.playCraftSound) { this.scene.soundManager.playCraftSound(); } return true; } // Start next item in queue startNextCraft() { if (this.craftingQueue.length === 0) { this.isCrafting = false; this.currentCraft = null; this.craftProgress = 0; return; } this.isCrafting = true; this.currentCraft = this.craftingQueue[0]; this.craftProgress = 0; console.log(`⏳ Processing: ${this.currentCraft.recipe.name}`); } // Update crafting progress update(delta) { if (!this.isCrafting || !this.currentCraft) return; const elapsed = Date.now() - this.currentCraft.startTime; this.craftProgress = Math.min(1.0, elapsed / this.currentCraft.duration); // Check if finished if (this.craftProgress >= 1.0) { this.completeCraft(); } // Emit progress event for UI this.scene.events.emit('craft-progress', { recipe: this.currentCraft.recipe, progress: this.craftProgress }); } // Complete current craft completeCraft() { if (!this.currentCraft) return; const recipe = this.currentCraft.recipe; // Add result to inventory const inventory = this.scene.inventorySystem; inventory.addItem(recipe.result.item, recipe.result.quantity); console.log(`✅ Crafted: ${recipe.result.quantity}x ${recipe.name}`); // Emit event this.scene.events.emit('craft-complete', { recipeId: recipe.id, item: recipe.result.item, quantity: recipe.result.quantity }); // Play sound if (this.scene.soundManager && this.scene.soundManager.playSuccessSound) { this.scene.soundManager.playSuccessSound(); } // Show floating text if (this.scene.player && this.scene.player.sprite) { const text = `+${recipe.result.quantity} ${recipe.name}`; this.scene.events.emit('floating-text', { x: this.scene.player.sprite.x, y: this.scene.player.sprite.y - 50, text: text, color: '#00ff00' }); // ✨ PARTICLE EFFECT - Craft sparkles if (this.scene.particleEnhancements) { this.scene.particleEnhancements.craftSparkles( this.scene.player.sprite.x, this.scene.player.sprite.y ); } } // Remove from queue this.craftingQueue.shift(); // Start next craft this.startNextCraft(); } // Cancel current craft cancelCraft() { if (!this.currentCraft) return false; const recipe = this.currentCraft.recipe; // Refund ingredients (partial refund based on progress) const refundPercent = 1.0 - this.craftProgress; const inventory = this.scene.inventorySystem; for (const [itemId, amount] of Object.entries(recipe.ingredients)) { const refundAmount = Math.floor(amount * refundPercent); if (refundAmount > 0) { inventory.addItem(itemId, refundAmount); } } console.log(`❌ Cancelled crafting: ${recipe.name} (${Math.floor(refundPercent * 100)}% refund)`); // Remove from queue this.craftingQueue.shift(); // Emit event this.scene.events.emit('craft-cancelled', recipe.id); // Start next this.startNextCraft(); return true; } // Get current crafting info getCurrentCraft() { if (!this.currentCraft) return null; return { recipe: this.currentCraft.recipe, progress: this.craftProgress, timeRemaining: this.currentCraft.duration * (1 - this.craftProgress) }; } // Get queue length getQueueLength() { return this.craftingQueue.length; } // Clear entire queue clearQueue() { // Refund all queued items this.craftingQueue.forEach(craft => { const inventory = this.scene.inventorySystem; for (const [itemId, amount] of Object.entries(craft.recipe.ingredients)) { inventory.addItem(itemId, amount); } }); this.craftingQueue = []; this.isCrafting = false; this.currentCraft = null; this.craftProgress = 0; console.log('🗑️ Cleared crafting queue'); } // Save crafting state getSaveData() { return { unlockedRecipes: Array.from(this.unlockedRecipes), craftingQueue: this.craftingQueue.map(craft => ({ recipeId: craft.recipeId, startTime: craft.startTime, duration: craft.duration })), currentProgress: this.craftProgress }; } // Load crafting state loadSaveData(data) { if (!data) return; // Restore unlocked recipes if (data.unlockedRecipes) { this.unlockedRecipes = new Set(data.unlockedRecipes); } // Restore crafting queue if (data.craftingQueue && data.craftingQueue.length > 0) { this.craftingQueue = data.craftingQueue.map(saved => ({ recipeId: saved.recipeId, recipe: this.recipes[saved.recipeId], startTime: saved.startTime, duration: saved.duration })); this.startNextCraft(); } console.log('💾 Loaded crafting state'); } }