575 lines
17 KiB
JavaScript
575 lines
17 KiB
JavaScript
/**
|
|
* 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
|
|
};
|
|
}
|
|
}
|
|
|
|
|