/** * CRAFTING TABLES SYSTEM - Recipe Management & Item Creation * Part of: Home Upgrade & Customization System * Created: January 4, 2026 * * Features: * - Small crafting table (50 basic recipes) * - Large planning table (100+ advanced recipes) * - Recipe unlocking system * - Ingredient checking * - Batch crafting * - Gronk's expedition integration */ class CraftingTablesSystem { constructor(game) { this.game = game; this.player = game.player; // Table types this.tables = { SMALL: { id: 'small_table', name: 'Small Crafting Table', cost: 0, unlocked: true, maxRecipes: 50, craftingSpeed: 1.0, sprite: 'interior_table_small.png', description: 'A basic workbench for simple crafting.', categories: ['tools_wooden', 'repairs', 'food_basic', 'potions_beginner'] }, LARGE: { id: 'large_table', name: 'Large Planning Table', cost: 5000, requirements: { metGronk: true, questCompleted: 'builders_vision' }, unlocked: false, maxRecipes: 100, craftingSpeed: 1.5, sprite: 'interior_table_large.png', description: 'An advanced table for complex projects and expeditions.', categories: ['tools_wooden', 'repairs', 'food_basic', 'potions_beginner', 'expedition_planning', 'mechanisms', 'weapons_advanced', 'gronk_special'] } }; // Crafting recipes database this.recipes = this.initializeRecipes(); // Player's current table this.currentTable = { ...this.tables.SMALL }; // Unlocked recipes this.unlockedRecipes = []; // Crafting state this.isCrafting = false; this.craftingQueue = []; this.currentCraft = null; } /** * Initialize all crafting recipes */ initializeRecipes() { return { // TOOLS - WOODEN TIER wooden_hoe: { id: 'wooden_hoe', name: 'Wooden Hoe', category: 'tools_wooden', table: 'SMALL', ingredients: { wood: 10, rope: 2 }, output: { item: 'wooden_hoe', quantity: 1 }, craftTime: 5, // seconds unlocked: true, description: 'Basic farming tool for tilling soil.' }, wooden_pickaxe: { id: 'wooden_pickaxe', name: 'Wooden Pickaxe', category: 'tools_wooden', table: 'SMALL', ingredients: { wood: 15, stone: 5 }, output: { item: 'wooden_pickaxe', quantity: 1 }, craftTime: 8, unlocked: true, description: 'Basic mining tool for breaking rocks.' }, // REPAIRS locket_cleaning: { id: 'locket_cleaning', name: 'Clean Ana\'s Locket', category: 'repairs', table: 'SMALL', ingredients: { cloth: 1, water: 1 }, output: { item: 'locket_clean', quantity: 1 }, craftTime: 3, unlocked: false, questRequired: 'memory_lane', description: 'Restore Ana\'s precious locket to its former shine.' }, // FOOD - BASIC sandwich: { id: 'sandwich', name: 'Sandwich', category: 'food_basic', table: 'SMALL', ingredients: { bread: 2, cheese: 1 }, output: { item: 'sandwich', quantity: 1 }, craftTime: 2, energy_restore: 20, unlocked: true, description: 'A simple but filling meal.' }, salad: { id: 'salad', name: 'Garden Salad', category: 'food_basic', table: 'SMALL', ingredients: { lettuce: 3, tomato: 2, cucumber: 1 }, output: { item: 'salad', quantity: 1 }, craftTime: 3, energy_restore: 25, unlocked: true, description: 'Fresh vegetables in a healthy mix.' }, // POTIONS - BEGINNER healing_potion_basic: { id: 'healing_potion_basic', name: 'Basic Healing Potion', category: 'potions_beginner', table: 'SMALL', ingredients: { red_herb: 2, water: 1, glass_bottle: 1 }, output: { item: 'healing_potion_basic', quantity: 1 }, craftTime: 10, health_restore: 50, unlocked: false, skillRequired: { alchemy: 1 }, description: 'Restores 50 HP.' }, // EXPEDITION PLANNING (Large Table Only) expedition_map: { id: 'expedition_map', name: 'Expedition Map', category: 'expedition_planning', table: 'LARGE', ingredients: { paper: 5, ink: 2, compass: 1 }, output: { item: 'expedition_map', quantity: 1 }, craftTime: 20, unlocked: false, npcRequired: 'gronk', description: 'Required for planning expeditions with Gronk.' }, expedition_supplies: { id: 'expedition_supplies', name: 'Expedition Supplies', category: 'expedition_planning', table: 'LARGE', ingredients: { rope: 10, torch: 5, food_rations: 20, water_bottle: 10 }, output: { item: 'expedition_supplies', quantity: 1 }, craftTime: 30, unlocked: false, npcRequired: 'gronk', description: 'Everything needed for a successful expedition.' }, // COMPLEX MECHANISMS (Large Table Only) automated_sprinkler: { id: 'automated_sprinkler', name: 'Automated Sprinkler', category: 'mechanisms', table: 'LARGE', ingredients: { iron_pipe: 10, gears: 5, water_pump: 1 }, output: { item: 'automated_sprinkler', quantity: 1 }, craftTime: 60, unlocked: false, skillRequired: { engineering: 3 }, description: 'Waters crops automatically in a 3x3 area.' }, // ADVANCED WEAPONS (Large Table Only) steel_sword: { id: 'steel_sword', name: 'Steel Sword', category: 'weapons_advanced', table: 'LARGE', ingredients: { steel_ingot: 5, leather: 2, ruby: 1 }, output: { item: 'steel_sword', quantity: 1 }, craftTime: 45, damage: 50, unlocked: false, skillRequired: { combat: 5 }, description: 'A powerful blade forged from steel.' }, // GRONK SPECIAL (Large Table Only) gronk_explosive: { id: 'gronk_explosive', name: 'Gronk\'s Explosive', category: 'gronk_special', table: 'LARGE', ingredients: { gunpowder: 5, wire: 3, metal_casing: 1, gronks_secret_ingredient: 1 }, output: { item: 'gronk_explosive', quantity: 3 }, craftTime: 90, unlocked: false, npcRequired: 'gronk', relationshipRequired: { gronk: 7 }, description: 'BOOM! Gronk\'s special explosive for clearing obstacles.' } }; } /** * Purchase Large Planning Table */ purchaseLargeTable() { const largeTable = this.tables.LARGE; // Check if already purchased if (largeTable.unlocked) { return { success: false, message: 'You already own the Large Table!' }; } // Check requirements if (!this.player.hasMetNPC('gronk')) { return { success: false, message: 'You need to meet Gronk first!' }; } if (!this.player.hasCompletedQuest('builders_vision')) { return { success: false, message: 'Complete "Builder\'s Vision" quest first!' }; } // Check cost if (this.player.money < largeTable.cost) { return { success: false, message: `Not enough money! Need ${largeTable.cost}g` }; } // Purchase this.player.money -= largeTable.cost; largeTable.unlocked = true; this.currentTable = { ...largeTable }; // Unlock advanced recipes this.unlockAdvancedRecipes(); return { success: true, message: `Purchased ${largeTable.name} for ${largeTable.cost}g!` }; } /** * Unlock advanced recipes when Large Table is purchased */ unlockAdvancedRecipes() { Object.values(this.recipes).forEach(recipe => { if (recipe.table === 'LARGE' && !recipe.npcRequired && !recipe.skillRequired) { recipe.unlocked = true; } }); } /** * Check if player can craft a recipe */ canCraft(recipeId) { const recipe = this.recipes[recipeId]; if (!recipe) { return { canCraft: false, reason: 'Recipe not found' }; } // Check if recipe is unlocked if (!recipe.unlocked) { return { canCraft: false, reason: 'Recipe locked' }; } // Check table requirement if (recipe.table === 'LARGE' && !this.tables.LARGE.unlocked) { return { canCraft: false, reason: 'Requires Large Planning Table' }; } // Check NPC requirement if (recipe.npcRequired && !this.player.hasMetNPC(recipe.npcRequired)) { return { canCraft: false, reason: `Requires meeting ${recipe.npcRequired}` }; } // Check relationship requirement if (recipe.relationshipRequired) { for (const [npc, level] of Object.entries(recipe.relationshipRequired)) { if (this.player.getRelationshipLevel(npc) < level) { return { canCraft: false, reason: `Requires ${level}+ hearts with ${npc}` }; } } } // Check skill requirement if (recipe.skillRequired) { for (const [skill, level] of Object.entries(recipe.skillRequired)) { if (this.player.getSkillLevel(skill) < level) { return { canCraft: false, reason: `Requires ${skill} level ${level}` }; } } } // Check ingredients const missingIngredients = []; for (const [ingredient, amount] of Object.entries(recipe.ingredients)) { const playerAmount = this.player.inventory.getItemCount(ingredient); if (playerAmount < amount) { missingIngredients.push({ item: ingredient, have: playerAmount, need: amount }); } } if (missingIngredients.length > 0) { return { canCraft: false, reason: 'Missing ingredients', missingIngredients: missingIngredients }; } return { canCraft: true }; } /** * Start crafting a recipe */ craft(recipeId, quantity = 1) { const canCraftCheck = this.canCraft(recipeId); if (!canCraftCheck.canCraft) { return { success: false, message: canCraftCheck.reason, missingIngredients: canCraftCheck.missingIngredients }; } const recipe = this.recipes[recipeId]; // Remove ingredients for (const [ingredient, amount] of Object.entries(recipe.ingredients)) { this.player.inventory.removeItem(ingredient, amount * quantity); } // Calculate craft time (with table speed bonus) const tableSpeed = this.currentTable.craftingSpeed; const totalTime = (recipe.craftTime / tableSpeed) * quantity; // Add to crafting queue const craftJob = { recipe: recipe, quantity: quantity, timeRemaining: totalTime, totalTime: totalTime }; this.craftingQueue.push(craftJob); // Start crafting if not already crafting if (!this.isCrafting) { this.startNextCraft(); } return { success: true, message: `Crafting ${quantity}x ${recipe.name}... (${Math.floor(totalTime)}s)`, craftJob: craftJob }; } /** * Start next craft in queue */ startNextCraft() { if (this.craftingQueue.length === 0) { this.isCrafting = false; this.currentCraft = null; return; } this.isCrafting = true; this.currentCraft = this.craftingQueue[0]; // Emit crafting started event this.game.emit('craftingStarted', { recipe: this.currentCraft.recipe, quantity: this.currentCraft.quantity, time: this.currentCraft.totalTime }); } /** * Update crafting progress */ update(deltaTime) { if (!this.isCrafting || !this.currentCraft) return; // Decrease time remaining this.currentCraft.timeRemaining -= deltaTime; // Check if completed if (this.currentCraft.timeRemaining <= 0) { this.completeCraft(); } } /** * Complete current craft */ completeCraft() { if (!this.currentCraft) return; const recipe = this.currentCraft.recipe; const quantity = this.currentCraft.quantity; // Add crafted item to inventory this.player.inventory.addItem( recipe.output.item, recipe.output.quantity * quantity ); // Show completion message this.game.showMessage( `✅ Crafted ${quantity}x ${recipe.name}!` ); // Emit crafting completed event this.game.emit('craftingCompleted', { recipe: recipe, quantity: quantity }); // Remove from queue this.craftingQueue.shift(); // Start next craft this.startNextCraft(); } /** * Get all available recipes for current table */ getAvailableRecipes() { return Object.values(this.recipes).filter(recipe => { // Filter by table type if (recipe.table === 'LARGE' && !this.tables.LARGE.unlocked) { return false; } return recipe.unlocked; }); } /** * Get crafting UI data */ getCraftingUIData() { return { currentTable: this.currentTable, isCrafting: this.isCrafting, currentCraft: this.currentCraft, queue: this.craftingQueue, availableRecipes: this.getAvailableRecipes(), largeTableUnlocked: this.tables.LARGE.unlocked }; } }