💻 GAME SYSTEMS CODE - Sleep, Crafting, Bakery
Implemented 3 major game systems in JavaScript
This commit is contained in:
574
src/systems/CraftingTablesSystem.js
Normal file
574
src/systems/CraftingTablesSystem.js
Normal file
@@ -0,0 +1,574 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export 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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default CraftingTablesSystem;
|
||||
Reference in New Issue
Block a user