476 lines
13 KiB
JavaScript
476 lines
13 KiB
JavaScript
/**
|
|
* COOKING & RECIPE SYSTEM
|
|
* Complete cooking system with recipes, food buffs, spoilage, and skill progression
|
|
*/
|
|
class CookingSystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
this.enabled = true;
|
|
|
|
// Recipes
|
|
this.recipes = new Map();
|
|
this.knownRecipes = new Set();
|
|
|
|
// Cooking stations
|
|
this.cookingStations = new Map();
|
|
|
|
// Active cooking
|
|
this.activeCooking = [];
|
|
|
|
// Food items
|
|
this.foodItems = new Map();
|
|
|
|
// Cooking skill
|
|
this.cookingLevel = 1;
|
|
this.cookingXP = 0;
|
|
|
|
// Settings
|
|
this.settings = {
|
|
spoilageRate: 0.01, // 1% per minute
|
|
xpPerRecipe: 10,
|
|
xpScaling: 1.5
|
|
};
|
|
|
|
this.loadProgress();
|
|
this.init();
|
|
|
|
console.log('✅ Cooking System initialized');
|
|
}
|
|
|
|
init() {
|
|
this.defineRecipes();
|
|
this.defineCookingStations();
|
|
console.log('🍳 Cooking system ready');
|
|
}
|
|
|
|
// ========== RECIPES ==========
|
|
|
|
defineRecipes() {
|
|
// Basic recipes
|
|
this.defineRecipe('wheat_bread', {
|
|
name: 'Wheat Bread',
|
|
ingredients: { wheat: 3, water: 1 },
|
|
cookTime: 30, // seconds
|
|
cookingStation: 'oven',
|
|
buffs: { hunger: 50, health: 10 },
|
|
buffDuration: 0, // permanent (hunger/health)
|
|
spoilTime: 300, // 5 minutes
|
|
xp: 10,
|
|
requiredLevel: 1
|
|
});
|
|
|
|
this.defineRecipe('vegetable_soup', {
|
|
name: 'Vegetable Soup',
|
|
ingredients: { carrot: 2, potato: 2, water: 1 },
|
|
cookTime: 45,
|
|
cookingStation: 'stove',
|
|
buffs: { hunger: 60, health: 15, stamina: 20 },
|
|
buffDuration: 180, // 3 minutes
|
|
spoilTime: 240,
|
|
xp: 15,
|
|
requiredLevel: 2
|
|
});
|
|
|
|
this.defineRecipe('grilled_meat', {
|
|
name: 'Grilled Meat',
|
|
ingredients: { raw_meat: 1 },
|
|
cookTime: 20,
|
|
cookingStation: 'grill',
|
|
buffs: { hunger: 70, health: 20, strength: 15 },
|
|
buffDuration: 300, // 5 minutes
|
|
spoilTime: 180,
|
|
xp: 20,
|
|
requiredLevel: 3
|
|
});
|
|
|
|
this.defineRecipe('mutant_stew', {
|
|
name: 'Mutant Stew',
|
|
ingredients: { mutant_meat: 2, carrot: 3, potato: 2, water: 1 },
|
|
cookTime: 60,
|
|
cookingStation: 'cauldron',
|
|
buffs: { hunger: 80, health: 30, strength: 25, speed: 15 },
|
|
buffDuration: 600, // 10 minutes
|
|
spoilTime: 360,
|
|
xp: 50,
|
|
requiredLevel: 5
|
|
});
|
|
|
|
this.defineRecipe('golden_apple_pie', {
|
|
name: 'Golden Apple Pie',
|
|
ingredients: { golden_apple: 1, wheat: 5, sugar: 3 },
|
|
cookTime: 90,
|
|
cookingStation: 'oven',
|
|
buffs: { hunger: 100, health: 50, all_stats: 20 },
|
|
buffDuration: 900, // 15 minutes
|
|
spoilTime: 600,
|
|
xp: 100,
|
|
requiredLevel: 10
|
|
});
|
|
}
|
|
|
|
defineRecipe(id, data) {
|
|
this.recipes.set(id, {
|
|
id,
|
|
...data,
|
|
discovered: false
|
|
});
|
|
}
|
|
|
|
// ========== COOKING STATIONS ==========
|
|
|
|
defineCookingStations() {
|
|
this.cookingStations.set('stove', {
|
|
name: 'Stove',
|
|
cost: { iron: 10, wood: 5 },
|
|
cookingSpeed: 1.0
|
|
});
|
|
|
|
this.cookingStations.set('oven', {
|
|
name: 'Oven',
|
|
cost: { iron: 15, stone: 20 },
|
|
cookingSpeed: 1.2
|
|
});
|
|
|
|
this.cookingStations.set('grill', {
|
|
name: 'Grill',
|
|
cost: { iron: 8, wood: 10 },
|
|
cookingSpeed: 0.8
|
|
});
|
|
|
|
this.cookingStations.set('cauldron', {
|
|
name: 'Cauldron',
|
|
cost: { iron: 20, magic_crystal: 1 },
|
|
cookingSpeed: 1.5
|
|
});
|
|
}
|
|
|
|
// ========== COOKING ==========
|
|
|
|
canCook(recipeId) {
|
|
const recipe = this.recipes.get(recipeId);
|
|
if (!recipe) return false;
|
|
|
|
// Check level
|
|
if (this.cookingLevel < recipe.requiredLevel) {
|
|
console.log(`❌ Requires cooking level ${recipe.requiredLevel}`);
|
|
return false;
|
|
}
|
|
|
|
// Check ingredients
|
|
if (!this.scene.inventorySystem) return false;
|
|
|
|
for (const [ingredient, amount] of Object.entries(recipe.ingredients)) {
|
|
const has = this.scene.inventorySystem.getItemCount(ingredient);
|
|
if (has < amount) {
|
|
console.log(`❌ Missing ${ingredient}: ${has}/${amount}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
startCooking(recipeId, stationType) {
|
|
if (!this.canCook(recipeId)) return false;
|
|
|
|
const recipe = this.recipes.get(recipeId);
|
|
const station = this.cookingStations.get(stationType);
|
|
|
|
if (!station) {
|
|
console.log('❌ Invalid cooking station');
|
|
return false;
|
|
}
|
|
|
|
if (recipe.cookingStation !== stationType) {
|
|
console.log(`❌ Requires ${recipe.cookingStation}`);
|
|
return false;
|
|
}
|
|
|
|
// Consume ingredients
|
|
for (const [ingredient, amount] of Object.entries(recipe.ingredients)) {
|
|
this.scene.inventorySystem.removeItem(ingredient, amount);
|
|
}
|
|
|
|
// Start cooking
|
|
const cookTime = recipe.cookTime / station.cookingSpeed;
|
|
const cooking = {
|
|
recipeId,
|
|
startTime: Date.now(),
|
|
cookTime: cookTime * 1000, // convert to ms
|
|
station: stationType
|
|
};
|
|
|
|
this.activeCooking.push(cooking);
|
|
|
|
// Discover recipe
|
|
if (!recipe.discovered) {
|
|
this.discoverRecipe(recipeId);
|
|
}
|
|
|
|
console.log(`🍳 Started cooking ${recipe.name} (${cookTime}s)`);
|
|
return true;
|
|
}
|
|
|
|
updateCooking(delta) {
|
|
const now = Date.now();
|
|
|
|
for (let i = this.activeCooking.length - 1; i >= 0; i--) {
|
|
const cooking = this.activeCooking[i];
|
|
const elapsed = now - cooking.startTime;
|
|
|
|
if (elapsed >= cooking.cookTime) {
|
|
// Cooking complete!
|
|
this.completeCooking(cooking);
|
|
this.activeCooking.splice(i, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
completeCooking(cooking) {
|
|
const recipe = this.recipes.get(cooking.recipeId);
|
|
|
|
// Create food item
|
|
const food = this.createFoodItem(recipe);
|
|
|
|
// Add to inventory
|
|
if (this.scene.inventorySystem) {
|
|
this.scene.inventorySystem.addItem(cooking.recipeId, 1);
|
|
}
|
|
|
|
// Grant XP
|
|
this.addCookingXP(recipe.xp);
|
|
|
|
// Visual effect
|
|
if (this.scene.visualEnhancements) {
|
|
const player = this.scene.player;
|
|
if (player) {
|
|
const pos = player.getPosition();
|
|
this.scene.visualEnhancements.createSparkleEffect(pos.x, pos.y);
|
|
}
|
|
}
|
|
|
|
console.log(`✅ Cooked ${recipe.name}!`);
|
|
}
|
|
|
|
createFoodItem(recipe) {
|
|
const food = {
|
|
id: `food_${Date.now()}`,
|
|
recipeId: recipe.id,
|
|
name: recipe.name,
|
|
buffs: recipe.buffs,
|
|
buffDuration: recipe.buffDuration,
|
|
createdTime: Date.now(),
|
|
spoilTime: recipe.spoilTime * 1000,
|
|
spoiled: false
|
|
};
|
|
|
|
this.foodItems.set(food.id, food);
|
|
return food;
|
|
}
|
|
|
|
// ========== EATING ==========
|
|
|
|
eatFood(foodId) {
|
|
const food = this.foodItems.get(foodId);
|
|
if (!food) return false;
|
|
|
|
// Check if spoiled
|
|
if (this.isSpoiled(food)) {
|
|
console.log('🤢 Food is spoiled!');
|
|
// Negative effects
|
|
if (this.scene.statsSystem) {
|
|
this.scene.statsSystem.health -= 10;
|
|
}
|
|
this.foodItems.delete(foodId);
|
|
return false;
|
|
}
|
|
|
|
// Apply buffs
|
|
this.applyFoodBuffs(food);
|
|
|
|
// Remove food
|
|
this.foodItems.delete(foodId);
|
|
|
|
console.log(`😋 Ate ${food.name}!`);
|
|
return true;
|
|
}
|
|
|
|
applyFoodBuffs(food) {
|
|
const buffs = food.buffs;
|
|
|
|
// Permanent buffs (hunger, health)
|
|
if (this.scene.statsSystem) {
|
|
if (buffs.hunger) {
|
|
this.scene.statsSystem.hunger = Math.min(100, this.scene.statsSystem.hunger + buffs.hunger);
|
|
}
|
|
if (buffs.health) {
|
|
this.scene.statsSystem.health = Math.min(this.scene.statsSystem.maxHealth, this.scene.statsSystem.health + buffs.health);
|
|
}
|
|
}
|
|
|
|
// Temporary buffs
|
|
if (food.buffDuration > 0) {
|
|
this.applyTemporaryBuffs(buffs, food.buffDuration);
|
|
}
|
|
}
|
|
|
|
applyTemporaryBuffs(buffs, duration) {
|
|
// Apply buffs for duration
|
|
const buffData = {
|
|
buffs,
|
|
startTime: Date.now(),
|
|
duration: duration * 1000
|
|
};
|
|
|
|
// Store active buffs (would integrate with player stats)
|
|
console.log(`✨ Buffs active for ${duration}s:`, buffs);
|
|
|
|
// Schedule buff removal
|
|
setTimeout(() => {
|
|
console.log('⏰ Buffs expired');
|
|
}, duration * 1000);
|
|
}
|
|
|
|
// ========== SPOILAGE ==========
|
|
|
|
updateSpoilage(delta) {
|
|
const now = Date.now();
|
|
|
|
for (const food of this.foodItems.values()) {
|
|
const age = now - food.createdTime;
|
|
|
|
if (age > food.spoilTime) {
|
|
food.spoiled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
isSpoiled(food) {
|
|
const age = Date.now() - food.createdTime;
|
|
return age > food.spoilTime;
|
|
}
|
|
|
|
// ========== RECIPE DISCOVERY ==========
|
|
|
|
discoverRecipe(recipeId) {
|
|
const recipe = this.recipes.get(recipeId);
|
|
if (!recipe) return;
|
|
|
|
recipe.discovered = true;
|
|
this.knownRecipes.add(recipeId);
|
|
|
|
console.log(`📖 Discovered recipe: ${recipe.name}!`);
|
|
|
|
// Achievement
|
|
if (this.scene.uiGraphics && this.knownRecipes.size >= 10) {
|
|
this.scene.uiGraphics.unlockAchievement('master_chef');
|
|
}
|
|
|
|
this.saveProgress();
|
|
}
|
|
|
|
tryDiscoverRecipe(ingredients) {
|
|
// Try to match ingredients to unknown recipes
|
|
for (const [recipeId, recipe] of this.recipes.entries()) {
|
|
if (recipe.discovered) continue;
|
|
|
|
// Check if ingredients match
|
|
const matches = this.matchIngredients(ingredients, recipe.ingredients);
|
|
if (matches) {
|
|
this.discoverRecipe(recipeId);
|
|
return recipe;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
matchIngredients(provided, required) {
|
|
const providedKeys = Object.keys(provided).sort();
|
|
const requiredKeys = Object.keys(required).sort();
|
|
|
|
if (providedKeys.length !== requiredKeys.length) return false;
|
|
|
|
for (let i = 0; i < providedKeys.length; i++) {
|
|
if (providedKeys[i] !== requiredKeys[i]) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ========== SKILL PROGRESSION ==========
|
|
|
|
addCookingXP(amount) {
|
|
this.cookingXP += amount;
|
|
|
|
// Level up check
|
|
const xpNeeded = this.getCookingXPForLevel(this.cookingLevel + 1);
|
|
if (this.cookingXP >= xpNeeded) {
|
|
this.levelUpCooking();
|
|
}
|
|
|
|
this.saveProgress();
|
|
}
|
|
|
|
getCookingXPForLevel(level) {
|
|
return Math.floor(100 * Math.pow(this.settings.xpScaling, level - 1));
|
|
}
|
|
|
|
levelUpCooking() {
|
|
this.cookingLevel++;
|
|
this.cookingXP = 0;
|
|
|
|
console.log(`🎉 Cooking level up! Now level ${this.cookingLevel}`);
|
|
|
|
// Visual effect
|
|
if (this.scene.visualEnhancements) {
|
|
const player = this.scene.player;
|
|
if (player) {
|
|
const pos = player.getPosition();
|
|
this.scene.visualEnhancements.createSparkleEffect(pos.x, pos.y);
|
|
}
|
|
}
|
|
|
|
this.saveProgress();
|
|
}
|
|
|
|
// ========== UPDATE ==========
|
|
|
|
update(delta) {
|
|
this.updateCooking(delta);
|
|
this.updateSpoilage(delta);
|
|
}
|
|
|
|
// ========== PERSISTENCE ==========
|
|
|
|
saveProgress() {
|
|
const data = {
|
|
cookingLevel: this.cookingLevel,
|
|
cookingXP: this.cookingXP,
|
|
knownRecipes: Array.from(this.knownRecipes)
|
|
};
|
|
|
|
localStorage.setItem('novafarma_cooking', JSON.stringify(data));
|
|
}
|
|
|
|
loadProgress() {
|
|
const saved = localStorage.getItem('novafarma_cooking');
|
|
if (saved) {
|
|
try {
|
|
const data = JSON.parse(saved);
|
|
this.cookingLevel = data.cookingLevel || 1;
|
|
this.cookingXP = data.cookingXP || 0;
|
|
this.knownRecipes = new Set(data.knownRecipes || []);
|
|
console.log('✅ Cooking progress loaded');
|
|
} catch (error) {
|
|
console.error('Failed to load cooking progress:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
this.saveProgress();
|
|
console.log('🍳 Cooking System destroyed');
|
|
}
|
|
}
|