/** * RecipeSystem.js * =============== * Manages crafting recipes, blueprints, and progression unlocks * * Features: * - Recipe database with material requirements * - Blueprint unlock mechanics (find, level, quest, buy) * - Crafting validation and execution * - Material consumption * - Crafting time delays * - UI integration * * @author NovaFarma Team * @date 2025-12-22 */ export default class RecipeSystem { constructor(scene) { this.scene = scene; // Recipe storage this.recipes = new Map(); this.unlockedRecipes = new Set(); this.blueprintLocations = new Map(); // World spawn locations // Crafting state this.currentlyCrafting = null; this.craftQueue = []; // Categories this.categories = { BUILDING: 'building', EQUIPMENT: 'equipment', FURNITURE: 'furniture', MAGIC: 'magic', TRANSPORT: 'transport', UPGRADE: 'upgrade' }; // Initialize recipe database this.initializeRecipes(); // Load saved unlocks this.loadUnlockedRecipes(); console.log('🔨 RecipeSystem initialized with', this.recipes.size, 'recipes'); } initializeRecipes() { // ===== BUILDING RECIPES ===== this.addRecipe('wooden_fence', { name: 'Wooden Fence', category: this.categories.BUILDING, description: 'Basic fence to keep animals in', materials: [ { item: 'wood', quantity: 5, displayName: 'Wood' }, { item: 'nails', quantity: 2, displayName: 'Nails' } ], craftTime: 2000, // 2 seconds unlockLevel: 1, blueprint: null, // Available from start output: { item: 'fence_wooden', quantity: 1 }, sprite: 'fence_horizontal' }); this.addRecipe('stone_fence', { name: 'Stone Fence', category: this.categories.BUILDING, description: 'Durable stone fence', materials: [ { item: 'stone', quantity: 10, displayName: 'Stone' }, { item: 'mortar', quantity: 3, displayName: 'Mortar' } ], craftTime: 4000, unlockLevel: 5, blueprint: 'blueprint_stone_fence', output: { item: 'fence_stone', quantity: 1 }, sprite: 'fence_full' }); this.addRecipe('house_upgrade_2', { name: 'House Upgrade Level 2', category: this.categories.UPGRADE, description: 'Expand your house - adds bedroom', materials: [ { item: 'wood', quantity: 100, displayName: 'Wood' }, { item: 'stone', quantity: 50, displayName: 'Stone' }, { item: 'gold', quantity: 500, displayName: 'Gold' } ], craftTime: 10000, // 10 seconds unlockLevel: 3, blueprint: 'blueprint_house_2', output: { item: 'house_level_2', quantity: 1 }, sprite: 'house_sprite', prerequisites: ['house_level_1'] }); this.addRecipe('barn_upgrade_2', { name: 'Barn Upgrade Level 2', category: this.categories.UPGRADE, description: 'Expand barn capacity to 8 animals', materials: [ { item: 'wood', quantity: 75, displayName: 'Wood' }, { item: 'stone', quantity: 30, displayName: 'Stone' }, { item: 'gold', quantity: 400, displayName: 'Gold' } ], craftTime: 8000, unlockLevel: 4, blueprint: 'blueprint_barn_2', output: { item: 'barn_level_2', quantity: 1 }, sprite: 'barn_isometric' }); // ===== EQUIPMENT RECIPES ===== this.addRecipe('iron_axe', { name: 'Iron Axe', category: this.categories.EQUIPMENT, description: 'Chops trees faster than basic axe', materials: [ { item: 'iron_ingot', quantity: 3, displayName: 'Iron Ingot' }, { item: 'wood', quantity: 2, displayName: 'Wood' } ], craftTime: 3000, unlockLevel: 2, blueprint: null, output: { item: 'axe_iron', quantity: 1 }, sprite: 'tools' }); this.addRecipe('steel_pickaxe', { name: 'Steel Pickaxe', category: this.categories.EQUIPMENT, description: 'Mine rare ores', materials: [ { item: 'steel_ingot', quantity: 5, displayName: 'Steel Ingot' }, { item: 'wood', quantity: 3, displayName: 'Wood' } ], craftTime: 5000, unlockLevel: 6, blueprint: 'blueprint_steel_pickaxe', output: { item: 'pickaxe_steel', quantity: 1 }, sprite: 'tools' }); // ===== FURNITURE RECIPES ===== this.addRecipe('wooden_bed', { name: 'Wooden Bed', category: this.categories.FURNITURE, description: 'Comfortable place to sleep', materials: [ { item: 'wood', quantity: 30, displayName: 'Wood' }, { item: 'fabric', quantity: 10, displayName: 'Fabric' } ], craftTime: 4000, unlockLevel: 2, blueprint: null, output: { item: 'bed_wooden', quantity: 1 }, sprite: 'house_sprite' }); // ===== MAGIC RECIPES ===== this.addRecipe('fire_staff', { name: 'Fire Staff', category: this.categories.MAGIC, description: 'Cast fire spells', materials: [ { item: 'magic_crystal', quantity: 5, displayName: 'Magic Crystal' }, { item: 'wood', quantity: 10, displayName: 'Enchanted Wood' }, { item: 'ruby', quantity: 1, displayName: 'Ruby' } ], craftTime: 8000, unlockLevel: 10, blueprint: 'blueprint_fire_staff', output: { item: 'staff_fire', quantity: 1 }, sprite: 'magic_staffs_6_types' }); // ===== TRANSPORT RECIPES ===== this.addRecipe('wooden_cart', { name: 'Wooden Cart', category: this.categories.TRANSPORT, description: 'Haul resources faster', materials: [ { item: 'wood', quantity: 50, displayName: 'Wood' }, { item: 'iron_ingot', quantity: 10, displayName: 'Iron' }, { item: 'rope', quantity: 5, displayName: 'Rope' } ], craftTime: 6000, unlockLevel: 5, blueprint: 'blueprint_cart', output: { item: 'cart_wooden', quantity: 1 }, sprite: 'cart_wagon_system' }); console.log('📜 Loaded', this.recipes.size, 'recipes'); } addRecipe(id, data) { this.recipes.set(id, { id, ...data, timescrafted: 0 }); } canCraft(recipeId) { const recipe = this.recipes.get(recipeId); if (!recipe) { console.warn('Recipe not found:', recipeId); return { canCraft: false, reason: 'Recipe not found' }; } // Check if unlocked if (recipe.blueprint && !this.unlockedRecipes.has(recipeId)) { return { canCraft: false, reason: 'Blueprint not unlocked' }; } // Check level requirement const playerLevel = this.scene.player?.stats?.level || 1; if (playerLevel < recipe.unlockLevel) { return { canCraft: false, reason: `Requires level ${recipe.unlockLevel}` }; } // Check prerequisites if (recipe.prerequisites) { for (const prereq of recipe.prerequisites) { if (!this.hasItem(prereq)) { return { canCraft: false, reason: `Requires ${prereq}` }; } } } // Check materials const missingMaterials = []; for (const material of recipe.materials) { const hasQuantity = this.getItemQuantity(material.item); if (hasQuantity < material.quantity) { missingMaterials.push({ item: material.displayName, need: material.quantity, have: hasQuantity }); } } if (missingMaterials.length > 0) { return { canCraft: false, reason: 'Missing materials', missing: missingMaterials }; } return { canCraft: true }; } craft(recipeId) { const check = this.canCraft(recipeId); if (!check.canCraft) { this.scene.uiSystem?.showError(check.reason); return false; } const recipe = this.recipes.get(recipeId); // Consume materials this.consumeMaterials(recipe); // Show crafting UI this.scene.uiSystem?.showCraftingProgress(recipe); // Set crafting state this.currentlyCrafting = { recipeId, startTime: Date.now(), endTime: Date.now() + recipe.craftTime }; // Wait for craft time this.scene.time.delayedCall(recipe.craftTime, () => { this.completeCraft(recipe); }); // Emit event this.scene.events.emit('craftingStarted', { recipeId, recipe }); return true; } consumeMaterials(recipe) { for (const material of recipe.materials) { this.removeItem(material.item, material.quantity); } console.log('💰 Consumed materials for:', recipe.name); } completeCraft(recipe) { // Add item to inventory this.addItem(recipe.output.item, recipe.output.quantity); // Update stats recipe.timescrafted++; // Clear crafting state this.currentlyCrafting = null; // Show notification this.scene.uiSystem?.showNotification({ title: 'Crafting Complete!', message: `Created ${recipe.name}`, icon: recipe.sprite, duration: 3000 }); // Emit event this.scene.events.emit('craftingComplete', { recipeId: recipe.id, recipe, output: recipe.output }); // Process queue if any if (this.craftQueue.length > 0) { const nextRecipe = this.craftQueue.shift(); this.craft(nextRecipe); } console.log('✅ Crafted:', recipe.name); } // ===== BLUEPRINT UNLOCK SYSTEM ===== unlockBlueprint(recipeId, method = 'find') { if (this.unlockedRecipes.has(recipeId)) { console.warn('Blueprint already unlocked:', recipeId); return false; } const recipe = this.recipes.get(recipeId); if (!recipe) { console.warn('Recipe not found:', recipeId); return false; } // Add to unlocked this.unlockedRecipes.add(recipeId); // Show notification this.scene.uiSystem?.showNotification({ title: '📜 Blueprint Unlocked!', message: `You can now craft: ${recipe.name}`, icon: 'blueprint', duration: 5000, color: '#FFD700' }); // Save unlocks this.saveUnlockedRecipes(); // Emit event this.scene.events.emit('blueprintUnlocked', { recipeId, recipe, method }); console.log('📜 Blueprint unlocked:', recipe.name, `(${method})`); return true; } findBlueprintInWorld(x, y) { // Check if player is near a blueprint object const nearbyBlueprints = this.scene.blueprintObjects?.filter(bp => { const distance = Phaser.Math.Distance.Between(x, y, bp.x, bp.y); return distance < 50; // Within 50 pixels }); if (nearbyBlueprints && nearbyBlueprints.length > 0) { const blueprint = nearbyBlueprints[0]; this.unlockBlueprint(blueprint.recipeId, 'find'); blueprint.destroy(); // Remove from world return true; } return false; } unlockBlueprintByLevel(level) { // Auto-unlock recipes at certain levels const levelUnlocks = { 2: ['iron_axe', 'wooden_bed'], 5: ['stone_fence', 'wooden_cart'], 10: ['fire_staff'] }; if (levelUnlocks[level]) { for (const recipeId of levelUnlocks[level]) { this.unlockBlueprint(recipeId, 'level'); } } } // ===== INVENTORY INTEGRATION ===== hasItem(itemId) { // Integrate with InventorySystem if (this.scene.inventorySystem) { return this.scene.inventorySystem.hasItem(itemId); } // Fallback: check player stats return this.scene.player?.inventory?.[itemId] > 0; } getItemQuantity(itemId) { // Integrate with InventorySystem if (this.scene.inventorySystem) { return this.scene.inventorySystem.getQuantity(itemId); } // Fallback return this.scene.player?.inventory?.[itemId] || 0; } addItem(itemId, quantity) { // Integrate with InventorySystem if (this.scene.inventorySystem) { this.scene.inventorySystem.addItem(itemId, quantity); } else { // Fallback if (!this.scene.player.inventory) { this.scene.player.inventory = {}; } this.scene.player.inventory[itemId] = (this.scene.player.inventory[itemId] || 0) + quantity; } } removeItem(itemId, quantity) { // Integrate with InventorySystem if (this.scene.inventorySystem) { this.scene.inventorySystem.removeItem(itemId, quantity); } else { // Fallback this.scene.player.inventory[itemId] -= quantity; } } // ===== DATA PERSISTENCE ===== saveUnlockedRecipes() { const unlocked = Array.from(this.unlockedRecipes); localStorage.setItem('unlockedRecipes', JSON.stringify(unlocked)); } loadUnlockedRecipes() { const saved = localStorage.getItem('unlockedRecipes'); if (saved) { const unlocked = JSON.parse(saved); this.unlockedRecipes = new Set(unlocked); console.log('📜 Loaded', this.unlockedRecipes.size, 'unlocked recipes'); } } // ===== GETTERS ===== getRecipe(recipeId) { return this.recipes.get(recipeId); } getAllRecipes() { return Array.from(this.recipes.values()); } getRecipesByCategory(category) { return this.getAllRecipes().filter(r => r.category === category); } getUnlockedRecipes() { return this.getAllRecipes().filter(r => !r.blueprint || this.unlockedRecipes.has(r.id) ); } getLockedRecipes() { return this.getAllRecipes().filter(r => r.blueprint && !this.unlockedRecipes.has(r.id) ); } getCraftableRecipes() { return this.getUnlockedRecipes().filter(r => this.canCraft(r.id).canCraft ); } getCraftingProgress() { if (!this.currentlyCrafting) return null; const elapsed = Date.now() - this.currentlyCrafting.startTime; const total = this.currentlyCrafting.endTime - this.currentlyCrafting.startTime; const progress = Math.min(1, elapsed / total); return { recipeId: this.currentlyCrafting.recipeId, progress, timeRemaining: Math.max(0, this.currentlyCrafting.endTime - Date.now()) }; } // ===== UPDATE ===== update(time, delta) { // Update crafting progress UI if (this.currentlyCrafting) { const progress = this.getCraftingProgress(); this.scene.events.emit('craftingProgress', progress); } } // ===== CLEANUP ===== destroy() { this.saveUnlockedRecipes(); this.recipes.clear(); this.unlockedRecipes.clear(); this.blueprintLocations.clear(); this.craftQueue = []; this.currentlyCrafting = null; } }