diff --git a/DEBUG_TOTAL_RECOVERY/BuildingVisualsSystem.js b/DEBUG_TOTAL_RECOVERY/BuildingVisualsSystem.js
new file mode 100644
index 000000000..7dd5cae2a
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/BuildingVisualsSystem.js
@@ -0,0 +1,634 @@
+/**
+ * BUILDING VISUALS & GENETICS SYSTEM
+ * Advanced animations for farm automation buildings and genetics lab
+ */
+class BuildingVisualsSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Building animations
+ this.buildings = new Map();
+ this.conveyorBelts = [];
+ this.windmills = [];
+ this.silos = [];
+
+ // Genetics lab
+ this.geneticsLab = null;
+ this.dnaHelixAnimation = null;
+ this.mutationVats = [];
+
+ // Settings
+ this.settings = {
+ buildingAnimations: true,
+ particleEffects: true,
+ geneticsUI: true,
+ animationSpeed: 1.0
+ };
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Building Visuals System initialized');
+ }
+
+ init() {
+ console.log('๐ญ Building animations ready');
+ }
+
+ // ========== AUTO-PLANTER ==========
+
+ createAutoPlanter(x, y) {
+ if (!this.settings.buildingAnimations) return null;
+
+ const planter = {
+ x, y,
+ arm: null,
+ seed: null,
+ isPlanting: false
+ };
+
+ // Create mechanical arm
+ const arm = this.scene.add.graphics();
+ arm.lineStyle(4, 0x888888, 1);
+ arm.lineBetween(0, 0, 0, 30); // Vertical arm
+ arm.lineBetween(0, 30, 20, 30); // Horizontal extension
+ arm.setPosition(x, y);
+ arm.setDepth(100);
+ planter.arm = arm;
+
+ this.buildings.set(`planter_${x}_${y}`, planter);
+ return planter;
+ }
+
+ animatePlanting(planter, targetX, targetY) {
+ if (!planter || planter.isPlanting) return;
+
+ planter.isPlanting = true;
+
+ // Arm extends down
+ this.scene.tweens.add({
+ targets: planter.arm,
+ y: planter.y + 20,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ // Drop seed
+ this.createSeedParticle(targetX, targetY);
+
+ // Arm retracts
+ this.scene.tweens.add({
+ targets: planter.arm,
+ y: planter.y,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ planter.isPlanting = false;
+ }
+ });
+ }
+ });
+ }
+
+ createSeedParticle(x, y) {
+ const seed = this.scene.add.circle(x, y - 20, 3, 0x8B4513);
+ seed.setDepth(99);
+
+ this.scene.tweens.add({
+ targets: seed,
+ y: y,
+ duration: 300,
+ ease: 'Bounce.easeOut',
+ onComplete: () => {
+ // Puff of dirt
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(x, y);
+ }
+ seed.destroy();
+ }
+ });
+ }
+
+ // ========== AUTO-HARVESTER ==========
+
+ createAutoHarvester(x, y) {
+ if (!this.settings.buildingAnimations) return null;
+
+ const harvester = {
+ x, y,
+ blades: [],
+ isHarvesting: false,
+ rotation: 0
+ };
+
+ // Create spinning blades
+ for (let i = 0; i < 4; i++) {
+ const blade = this.scene.add.graphics();
+ blade.lineStyle(3, 0xC0C0C0, 1);
+ blade.lineBetween(0, 0, 15, 0);
+ blade.setPosition(x, y);
+ blade.setRotation((Math.PI / 2) * i);
+ blade.setDepth(100);
+ harvester.blades.push(blade);
+ }
+
+ this.buildings.set(`harvester_${x}_${y}`, harvester);
+ return harvester;
+ }
+
+ animateHarvesting(harvester, cropX, cropY) {
+ if (!harvester || harvester.isHarvesting) return;
+
+ harvester.isHarvesting = true;
+
+ // Spin blades
+ for (const blade of harvester.blades) {
+ this.scene.tweens.add({
+ targets: blade,
+ rotation: blade.rotation + Math.PI * 4,
+ duration: 1000,
+ ease: 'Linear'
+ });
+ }
+
+ // Move to crop
+ this.scene.tweens.add({
+ targets: harvester.blades,
+ x: cropX,
+ y: cropY,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ // Harvest effect
+ this.createHarvestEffect(cropX, cropY);
+
+ // Return to base
+ this.scene.tweens.add({
+ targets: harvester.blades,
+ x: harvester.x,
+ y: harvester.y,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ harvester.isHarvesting = false;
+ }
+ });
+ }
+ });
+ }
+
+ createHarvestEffect(x, y) {
+ // Crop particles
+ const emitter = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 30, max: 60 },
+ scale: { start: 0.5, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 800,
+ quantity: 10,
+ tint: 0xFFD700
+ });
+
+ this.scene.time.delayedCall(800, () => emitter.destroy());
+ }
+
+ // ========== CONVEYOR BELT ==========
+
+ createConveyorBelt(x, y, width, direction = 'right') {
+ if (!this.settings.buildingAnimations) return null;
+
+ const belt = {
+ x, y, width, direction,
+ items: [],
+ speed: 50 // pixels per second
+ };
+
+ // Create belt graphics
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0x444444, 1);
+ graphics.fillRect(x, y, width, 20);
+
+ // Moving lines to show direction
+ for (let i = 0; i < width; i += 20) {
+ const line = this.scene.add.rectangle(
+ x + i,
+ y + 10,
+ 10,
+ 2,
+ 0x888888
+ );
+ line.setDepth(99);
+
+ // Animate line movement
+ const targetX = direction === 'right' ? x + width : x;
+ this.scene.tweens.add({
+ targets: line,
+ x: targetX,
+ duration: (width / belt.speed) * 1000,
+ repeat: -1,
+ ease: 'Linear'
+ });
+ }
+
+ belt.graphics = graphics;
+ this.conveyorBelts.push(belt);
+ return belt;
+ }
+
+ addItemToBelt(belt, itemSprite) {
+ if (!belt) return;
+
+ belt.items.push(itemSprite);
+ itemSprite.setPosition(belt.x, belt.y + 10);
+ itemSprite.setDepth(100);
+
+ // Move item along belt
+ const targetX = belt.direction === 'right'
+ ? belt.x + belt.width
+ : belt.x;
+
+ this.scene.tweens.add({
+ targets: itemSprite,
+ x: targetX,
+ duration: (belt.width / belt.speed) * 1000,
+ ease: 'Linear',
+ onComplete: () => {
+ // Remove from belt
+ const index = belt.items.indexOf(itemSprite);
+ if (index > -1) {
+ belt.items.splice(index, 1);
+ }
+ }
+ });
+ }
+
+ // ========== WINDMILL ==========
+
+ createWindmill(x, y) {
+ if (!this.settings.buildingAnimations) return null;
+
+ const windmill = {
+ x, y,
+ blades: null,
+ powerGlow: null,
+ isPowered: false,
+ rotation: 0
+ };
+
+ // Create blades
+ const blades = this.scene.add.graphics();
+ blades.lineStyle(4, 0x8B4513, 1);
+
+ // Draw 4 blades
+ for (let i = 0; i < 4; i++) {
+ const angle = (Math.PI / 2) * i;
+ const endX = Math.cos(angle) * 30;
+ const endY = Math.sin(angle) * 30;
+ blades.lineBetween(0, 0, endX, endY);
+ }
+
+ blades.setPosition(x, y);
+ blades.setDepth(100);
+ windmill.blades = blades;
+
+ // Power glow
+ const glow = this.scene.add.circle(x, y, 40, 0x00ffff, 0);
+ glow.setBlendMode(Phaser.BlendModes.ADD);
+ glow.setDepth(99);
+ windmill.powerGlow = glow;
+
+ // Rotate blades
+ this.scene.tweens.add({
+ targets: blades,
+ rotation: Math.PI * 2,
+ duration: 3000 / this.settings.animationSpeed,
+ repeat: -1,
+ ease: 'Linear'
+ });
+
+ this.windmills.push(windmill);
+ return windmill;
+ }
+
+ setPowerState(windmill, powered) {
+ if (!windmill) return;
+
+ windmill.isPowered = powered;
+
+ if (powered) {
+ // Glow on
+ this.scene.tweens.add({
+ targets: windmill.powerGlow,
+ alpha: 0.4,
+ duration: 500
+ });
+
+ // Particle trail
+ if (this.settings.particleEffects) {
+ const emitter = this.scene.add.particles(
+ windmill.x,
+ windmill.y,
+ 'particle_white',
+ {
+ speed: 20,
+ scale: { start: 0.3, end: 0 },
+ alpha: { start: 0.6, end: 0 },
+ lifespan: 1000,
+ frequency: 200,
+ quantity: 1,
+ tint: 0x00ffff
+ }
+ );
+ windmill.particleEmitter = emitter;
+ }
+ } else {
+ // Glow off
+ this.scene.tweens.add({
+ targets: windmill.powerGlow,
+ alpha: 0,
+ duration: 500
+ });
+
+ // Stop particles
+ if (windmill.particleEmitter) {
+ windmill.particleEmitter.destroy();
+ windmill.particleEmitter = null;
+ }
+ }
+ }
+
+ // ========== STORAGE SILO ==========
+
+ createStorageSilo(x, y, capacity = 1000) {
+ if (!this.settings.buildingAnimations) return null;
+
+ const silo = {
+ x, y,
+ capacity,
+ currentAmount: 0,
+ fillIndicator: null,
+ fillBar: null
+ };
+
+ // Silo structure
+ const structure = this.scene.add.graphics();
+ structure.fillStyle(0x666666, 1);
+ structure.fillRect(x - 20, y - 60, 40, 60);
+ structure.fillCircle(x, y - 60, 20);
+ structure.setDepth(100);
+ silo.structure = structure;
+
+ // Fill indicator (inside silo)
+ const fillBar = this.scene.add.rectangle(
+ x,
+ y,
+ 36,
+ 0,
+ 0xFFD700,
+ 0.8
+ );
+ fillBar.setOrigin(0.5, 1);
+ fillBar.setDepth(101);
+ silo.fillBar = fillBar;
+
+ // Percentage text
+ const text = this.scene.add.text(x, y - 70, '0%', {
+ fontSize: '12px',
+ color: '#ffffff',
+ fontStyle: 'bold'
+ });
+ text.setOrigin(0.5);
+ text.setDepth(102);
+ silo.text = text;
+
+ this.silos.push(silo);
+ return silo;
+ }
+
+ updateSiloFill(silo, amount) {
+ if (!silo) return;
+
+ silo.currentAmount = Phaser.Math.Clamp(amount, 0, silo.capacity);
+ const percentage = (silo.currentAmount / silo.capacity) * 100;
+ const fillHeight = (percentage / 100) * 56; // Max height
+
+ // Animate fill
+ this.scene.tweens.add({
+ targets: silo.fillBar,
+ height: fillHeight,
+ duration: 500,
+ ease: 'Power2'
+ });
+
+ // Update text
+ silo.text.setText(`${Math.round(percentage)}%`);
+ }
+
+ // ========== GENETICS LAB ==========
+
+ createGeneticsLab(x, y) {
+ if (!this.settings.geneticsUI) return null;
+
+ const lab = {
+ x, y,
+ dnaHelix: null,
+ vats: [],
+ isActive: false
+ };
+
+ // Lab structure
+ const structure = this.scene.add.rectangle(x, y, 80, 60, 0x333333);
+ structure.setStrokeStyle(2, 0x00ff00);
+ structure.setDepth(100);
+ lab.structure = structure;
+
+ this.geneticsLab = lab;
+ return lab;
+ }
+
+ showDNAHelixAnimation(x, y) {
+ if (!this.settings.geneticsUI) return null;
+
+ // Create DNA helix
+ const helix = this.scene.add.container(x, y);
+ helix.setDepth(200);
+
+ // Two strands
+ const strand1 = [];
+ const strand2 = [];
+
+ for (let i = 0; i < 20; i++) {
+ const yPos = i * 10 - 100;
+ const angle = (i / 20) * Math.PI * 4;
+
+ const x1 = Math.cos(angle) * 15;
+ const x2 = Math.cos(angle + Math.PI) * 15;
+
+ const dot1 = this.scene.add.circle(x1, yPos, 3, 0x00ff00);
+ const dot2 = this.scene.add.circle(x2, yPos, 3, 0x00ffff);
+
+ helix.add(dot1);
+ helix.add(dot2);
+
+ strand1.push(dot1);
+ strand2.push(dot2);
+
+ // Connecting lines
+ if (i % 3 === 0) {
+ const line = this.scene.add.graphics();
+ line.lineStyle(1, 0xffffff, 0.5);
+ line.lineBetween(x1, yPos, x2, yPos);
+ helix.add(line);
+ }
+ }
+
+ // Rotate helix
+ this.scene.tweens.add({
+ targets: helix,
+ rotation: Math.PI * 2,
+ duration: 4000,
+ repeat: -1,
+ ease: 'Linear'
+ });
+
+ // Pulse effect
+ this.scene.tweens.add({
+ targets: helix,
+ scale: { from: 1, to: 1.1 },
+ duration: 2000,
+ yoyo: true,
+ repeat: -1
+ });
+
+ this.dnaHelixAnimation = helix;
+ return helix;
+ }
+
+ hideDNAHelixAnimation() {
+ if (this.dnaHelixAnimation) {
+ this.dnaHelixAnimation.destroy();
+ this.dnaHelixAnimation = null;
+ }
+ }
+
+ createMutationVat(x, y) {
+ if (!this.settings.geneticsUI) return null;
+
+ const vat = {
+ x, y,
+ container: null,
+ liquid: null,
+ bubbles: null,
+ isActive: false
+ };
+
+ // Vat container
+ const container = this.scene.add.graphics();
+ container.fillStyle(0x444444, 1);
+ container.fillRect(x - 20, y - 40, 40, 40);
+ container.lineStyle(2, 0x666666);
+ container.strokeRect(x - 20, y - 40, 40, 40);
+ container.setDepth(100);
+ vat.container = container;
+
+ // Liquid
+ const liquid = this.scene.add.rectangle(
+ x,
+ y - 20,
+ 36,
+ 36,
+ 0x00ff00,
+ 0.6
+ );
+ liquid.setDepth(101);
+ vat.liquid = liquid;
+
+ this.mutationVats.push(vat);
+ return vat;
+ }
+
+ activateMutationVat(vat) {
+ if (!vat || vat.isActive) return;
+
+ vat.isActive = true;
+
+ // Bubbling effect
+ const emitter = this.scene.add.particles(
+ vat.x,
+ vat.y - 40,
+ 'particle_white',
+ {
+ speedY: { min: -30, max: -50 },
+ speedX: { min: -5, max: 5 },
+ scale: { start: 0.2, end: 0 },
+ alpha: { start: 0.8, end: 0 },
+ lifespan: 1000,
+ frequency: 200,
+ quantity: 2,
+ tint: 0x00ff00
+ }
+ );
+ vat.bubbles = emitter;
+
+ // Lightning effect
+ if (this.settings.particleEffects) {
+ this.createLightningEffect(vat.x, vat.y - 60);
+ }
+
+ // Liquid glow pulse
+ this.scene.tweens.add({
+ targets: vat.liquid,
+ alpha: { from: 0.6, to: 0.9 },
+ duration: 800,
+ yoyo: true,
+ repeat: -1
+ });
+ }
+
+ createLightningEffect(x, y) {
+ const lightning = this.scene.add.graphics();
+ lightning.lineStyle(2, 0xffff00, 1);
+
+ // Zigzag lightning
+ let currentX = x;
+ let currentY = y;
+
+ for (let i = 0; i < 5; i++) {
+ const nextX = currentX + (Math.random() - 0.5) * 20;
+ const nextY = currentY + 10;
+ lightning.lineBetween(currentX, currentY, nextX, nextY);
+ currentX = nextX;
+ currentY = nextY;
+ }
+
+ lightning.setDepth(200);
+
+ // Flash and fade
+ this.scene.tweens.add({
+ targets: lightning,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => lightning.destroy()
+ });
+ }
+
+ // ========== SETTINGS ==========
+
+ saveSettings() {
+ localStorage.setItem('novafarma_building_visuals', JSON.stringify(this.settings));
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_building_visuals');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ destroy() {
+ if (this.dnaHelixAnimation) this.dnaHelixAnimation.destroy();
+ for (const vat of this.mutationVats) {
+ if (vat.bubbles) vat.bubbles.destroy();
+ }
+ console.log('๐ญ Building Visuals System destroyed');
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/IntroScene.js b/DEBUG_TOTAL_RECOVERY/IntroScene.js
new file mode 100644
index 000000000..cc383a3b9
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/IntroScene.js
@@ -0,0 +1,313 @@
+// ๐ฌ INTRO SEQUENCE - 60-SECOND EPIC CINEMATIC
+// "From Colors to Darkness" - Complete Story
+// Created: January 10, 2026
+// Style: Style 32 Dark-Chibi Noir + Polaroid + VHS
+// Voices: Kai (Rok) + Ana (Petra) + Gronk (Rok deep)
+
+class IntroScene extends Phaser.Scene {
+ constructor() {
+ super({ key: 'IntroScene' });
+ this.skipEnabled = false;
+ this.currentPhase = 0;
+ this.skipPrompt = null;
+ this.currentPolaroid = null;
+ this.currentText = null;
+ this.vhsNoise = null;
+ this.scanlines = null;
+ this.ambientAudio = null;
+ this.currentVoice = null;
+ }
+
+ preload() {
+ console.log('๐ฌ IntroScene: Loading EPIC 60s assets...');
+
+ // Base path for intro shots
+ const introPath = 'assets/references/intro_shots/';
+
+ // ALL 20 INTRO SHOTS
+ // DREAMY INTRO ASSETS (Generated)
+ const dreamyPath = 'assets/images/intro_sequence/';
+
+ this.load.image('intro_family_portrait', dreamyPath + 'family_portrait_complete_dreamy.png');
+ this.load.image('intro_otac_longboard', dreamyPath + 'otac_longboard_pier_dreamy.png');
+ this.load.image('intro_kai_dreads', dreamyPath + 'kai_first_dreads_family_dreamy.png');
+ this.load.image('intro_ana_barbershop', dreamyPath + 'ana_barbershop_dreads_dreamy.png');
+ this.load.image('intro_birthday_cake', dreamyPath + 'birthday_cake_rd_dreamy.png');
+ this.load.image('intro_virus', introPath + 'virus_xnoir_microscope.png');
+ this.load.image('intro_chaos', dreamyPath + 'chaos_streets_apocalypse_dreamy.png');
+ this.load.image('intro_zombies', dreamyPath + 'zombie_silhouettes_panic_dreamy.png');
+ this.load.image('intro_parents_ghosts', dreamyPath + 'parents_transparent_ghosts_dreamy.png');
+ this.load.image('intro_ana_taken', introPath + 'ana_taken_military.png');
+ this.load.image('intro_kai_alone', introPath + 'kai_alone_basement.png');
+ this.load.image('intro_kai_young', introPath + 'kai_young_timelapse.png');
+ this.load.image('intro_kai_adult', introPath + 'kai_adult_35_timelapse.png');
+ this.load.image('intro_kai_elder', introPath + 'kai_elder_50_timelapse.png');
+ this.load.image('intro_ana_memory', introPath + 'ana_memory_flash_purple.png');
+ this.load.image('intro_bedroom', introPath + 'kai_bedroom_wakeup.png');
+ this.load.image('intro_gronk', introPath + 'gronk_doorway_silhouette.png');
+ this.load.image('intro_twins_childhood', introPath + 'kai_ana_twins_childhood.png');
+
+ // ๐ต AMBIENT MUSIC
+ this.loadAudioSafe('noir_ambience', 'assets/audio/ambient/noir_ambience.mp3');
+
+ // ๐ค KAI VOICES (12 total - ENGLISH)
+ this.loadAudioSafe('kai_01', 'assets/audio/voiceover/kai_en_01.mp3');
+ this.loadAudioSafe('kai_02', 'assets/audio/voiceover/kai_en_02.mp3');
+ this.loadAudioSafe('kai_03', 'assets/audio/voiceover/kai_en_03.mp3');
+ this.loadAudioSafe('kai_04', 'assets/audio/voiceover/kai_en_04.mp3');
+ this.loadAudioSafe('kai_05', 'assets/audio/voiceover/kai_en_05.mp3');
+ this.loadAudioSafe('kai_06', 'assets/audio/voiceover/kai_en_06.mp3');
+ this.loadAudioSafe('kai_07', 'assets/audio/voiceover/kai_en_07.mp3');
+ this.loadAudioSafe('kai_08', 'assets/audio/voiceover/kai_en_08.mp3');
+ this.loadAudioSafe('kai_09', 'assets/audio/voiceover/kai_en_09.mp3');
+ this.loadAudioSafe('kai_10', 'assets/audio/voiceover/kai_en_10.mp3');
+ this.loadAudioSafe('kai_11', 'assets/audio/voiceover/kai_en_11.mp3');
+ this.loadAudioSafe('kai_12', 'assets/audio/voiceover/kai_en_12.mp3');
+
+ // ๐ค ANA VOICES (8 total - ENGLISH)
+ this.loadAudioSafe('ana_01', 'assets/audio/voiceover/ana_en_01.mp3');
+ this.loadAudioSafe('ana_02', 'assets/audio/voiceover/ana_en_02.mp3');
+ this.loadAudioSafe('ana_03', 'assets/audio/voiceover/ana_en_03.mp3');
+ this.loadAudioSafe('ana_04', 'assets/audio/voiceover/ana_en_04.mp3');
+ this.loadAudioSafe('ana_05', 'assets/audio/voiceover/ana_en_05.mp3');
+ this.loadAudioSafe('ana_06', 'assets/audio/voiceover/ana_en_06.mp3');
+ this.loadAudioSafe('ana_07', 'assets/audio/voiceover/ana_en_07.mp3');
+ this.loadAudioSafe('ana_08', 'assets/audio/voiceover/ana_en_08.mp3');
+
+ // ๐ค GRONK VOICE (ENGLISH - Deep UK)
+ this.loadAudioSafe('gronk_01', 'assets/audio/voiceover/gronk_en_01.mp3');
+ }
+
+ loadAudioSafe(key, path) {
+ try {
+ this.load.audio(key, path);
+ } catch (error) {
+ console.warn(`โ ๏ธ Audio skipped: ${key}`);
+ }
+ }
+
+ create() {
+ console.log('๐ฌ IntroScene: Amnesia Start Sequence (Fixed Timing)');
+ this.cameras.main.setBackgroundColor('#000000');
+
+ // 1. SHADER INTEGRATION: Gaussian Blur
+ this.blurFX = null;
+ if (this.cameras.main.postFX) {
+ try {
+ this.blurFX = this.cameras.main.postFX.addBlur(0, 0, 0);
+ } catch (e) {
+ console.warn('โ ๏ธ PostFX not supported');
+ }
+ }
+
+ // Initialize Blur at 20
+ if (this.blurFX) {
+ this.blurFX.strength = 20; // High blur
+
+ // Tween blur to 0 over 6 seconds (Standard Time)
+ this.tweens.add({
+ targets: this.blurFX,
+ strength: 0,
+ duration: 6000,
+ ease: 'Power2.easeOut',
+ onComplete: () => {
+ this.triggerWakeUp();
+ }
+ });
+ } else {
+ // Fallback if shaders not supported
+ this.cameras.main.alpha = 0;
+ this.tweens.add({
+ targets: this.cameras.main,
+ alpha: 1,
+ duration: 6000,
+ onComplete: () => this.triggerWakeUp()
+ });
+ }
+
+ // 2. FLASHBACK ENGINE (Safe Mode)
+ // Images: Birthday (0s), Longboard (1.5s), Dreads (3.0s)
+ this.flashbackSequence([
+ 'intro_birthday_cake',
+ 'intro_otac_longboard',
+ 'intro_ana_barbershop'
+ ]);
+
+ // 3. TYPEWRITER LOGIC (Fixed Timing & No Overlap)
+ // Line 1: 0.5s -> 3.0s
+ this.time.delayedCall(500, () => {
+ this.showDialogue('Vse je zamegljeno... Zakaj me vse boli?', 2500);
+ });
+
+ // Line 2: 4.0s -> End (starts only after Line 1 is cleared)
+ this.time.delayedCall(4000, () => {
+ this.showDialogue('Kdo so ti ljudje v moji glavi?', 2000);
+ });
+
+ // Audio atmosphere
+ this.startAmbientAudio();
+ }
+
+ flashbackSequence(images) {
+ images.forEach((key, index) => {
+ // Check if asset exists before scheduling
+ if (this.textures.exists(key)) {
+ this.time.delayedCall(index * 1500, () => {
+ this.triggerFlashback(key);
+ });
+ } else {
+ console.warn(`โ ๏ธ Missing flash asset: ${key} - Skipping visual, keeping timing.`);
+ }
+ });
+ }
+
+ triggerFlashback(key) {
+ // Double check existence
+ if (!this.textures.exists(key)) return;
+
+ const width = this.cameras.main.width;
+ const height = this.cameras.main.height;
+
+ const image = this.add.image(width / 2, height / 2, key);
+
+ // Scale to cover most of screen (maintain aspect ratio)
+ const scale = Math.max(width / image.width, height / image.height) * 0.8;
+ image.setScale(scale);
+
+ image.setAlpha(0);
+ image.setDepth(10);
+ image.setBlendMode(Phaser.BlendModes.ADD);
+
+ // Flash in and out
+ this.tweens.add({
+ targets: image,
+ alpha: { from: 0, to: 0.15 },
+ duration: 500,
+ yoyo: true,
+ hold: 500,
+ onComplete: () => {
+ if (image && image.active) image.destroy();
+ }
+ });
+
+ // Zoom
+ this.tweens.add({
+ targets: image,
+ scale: scale * 1.1,
+ duration: 1500
+ });
+ }
+
+ showDialogue(text, duration) {
+ // OVERLAP FIX: Destroy previous text immediately
+ if (this.currentText) {
+ this.currentText.destroy();
+ this.currentText = null;
+ }
+
+ const width = this.cameras.main.width;
+ const height = this.cameras.main.height;
+
+ const textObj = this.add.text(width / 2, height - 100, '', {
+ fontFamily: 'Courier New',
+ fontSize: '24px',
+ fill: '#ffffff',
+ align: 'center',
+ stroke: '#000000',
+ strokeThickness: 4
+ });
+ textObj.setOrigin(0.5);
+ textObj.setDepth(100);
+
+ this.currentText = textObj; // Track current text
+
+ // Typewriter
+ let charIndex = 0;
+ const speed = 50;
+
+ if (text.length * speed > duration) {
+ // Speed up if text is too long for duration
+ speed = Math.floor(duration / text.length);
+ }
+
+ const timer = this.time.addEvent({
+ delay: speed,
+ callback: () => {
+ if (!textObj.active) return;
+ textObj.text += text[charIndex];
+ charIndex++;
+
+ if (charIndex >= text.length) {
+ timer.remove();
+ // Fade out logic
+ this.time.delayedCall(Math.max(500, duration - (text.length * speed)), () => {
+ if (textObj.active) {
+ this.tweens.add({
+ targets: textObj,
+ alpha: 0,
+ duration: 500,
+ onComplete: () => {
+ if (textObj.active) textObj.destroy();
+ if (this.currentText === textObj) this.currentText = null;
+ }
+ });
+ }
+ });
+ }
+ },
+ repeat: text.length - 1
+ });
+ }
+
+ startAmbientAudio() {
+ try {
+ if (this.cache.audio.exists('noir_ambience')) {
+ this.ambientAudio = this.sound.add('noir_ambience', {
+ volume: 0.1, // Start very quiet
+ loop: true
+ });
+ this.ambientAudio.play();
+ // Fade in audio
+ this.tweens.add({
+ targets: this.ambientAudio,
+ volume: 0.4,
+ duration: 6000
+ });
+ }
+ } catch (e) {
+ console.warn('โ ๏ธ Ambient audio not available');
+ }
+ }
+
+ triggerWakeUp() {
+ console.log('โก TRANSITION: Wake Up!');
+
+ // 4. TRANSITION TO GAMEPLAY
+ // White Flash
+ this.cameras.main.flash(1000, 255, 255, 255);
+
+ // Stop audio
+ if (this.ambientAudio) {
+ this.tweens.add({
+ targets: this.ambientAudio,
+ volume: 0,
+ duration: 500,
+ onComplete: () => {
+ if (this.ambientAudio && this.ambientAudio.stop) {
+ this.ambientAudio.stop();
+ }
+ }
+ });
+ }
+
+ // Switch Scene
+ this.time.delayedCall(1000, () => {
+ // Set flag
+ if (window.gameState && window.gameState.story) {
+ window.gameState.story.isAmnesiaActive = true;
+ }
+
+ this.scene.start('GameScene');
+ });
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/MicroFarmSystem.js b/DEBUG_TOTAL_RECOVERY/MicroFarmSystem.js
new file mode 100644
index 000000000..8104debb1
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/MicroFarmSystem.js
@@ -0,0 +1,358 @@
+// ๐ฑ MICRO FARM SYSTEM - Phase 37
+// Zaฤetna 8x8 parcela z postopno ลกiritvijo
+
+class MicroFarmSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // MICRO FARM CONFIG
+ this.farmCenterX = 50; // Center of 100x100 map
+ this.farmCenterY = 50;
+ this.farmSize = 8; // 8x8 tiles (initial)
+
+ // EXPANSION SYSTEM
+ this.unlockedTiles = new Set(); // Tracks unlocked tiles
+ this.expansionCost = 50; // Gold per 2x2 expansion
+
+ // LAND TYPES
+ this.landTypes = {
+ GRASS: 'grass', // Free to use
+ FOREST: 'forest', // Needs clearing (trees)
+ ROCKY: 'rocky', // Needs mining (rocks)
+ SWAMP: 'swamp' // Needs drainage (water)
+ };
+
+ this.init();
+ }
+
+ init() {
+ console.log('๐ฑ MicroFarmSystem initialized');
+
+ // Unlock initial 8x8 farm
+ this.unlockInitialFarm();
+
+ // Create visual boundaries
+ this.createFarmBoundaries();
+
+ // Render locked tile overlay
+ this.renderLockedTileOverlay();
+
+ // Create expansion UI buttons
+ this.createExpansionUI();
+ }
+
+ createExpansionUI() {
+ // Create UI buttons for farm expansion
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) {
+ console.warn('โ ๏ธ UIScene not found - cannot create expansion UI');
+ return;
+ }
+
+ // Store reference
+ this.uiScene = uiScene;
+ this.expansionButtons = [];
+
+ const buttonSize = 40;
+ const buttonColor = 0x8B4513; // Brown
+ const buttonHoverColor = 0xD2691E;
+ const expandCost = this.expansionCost;
+
+ // Button positions relative to farm center
+ const tileSize = (this.scene.terrainSystem && this.scene.terrainSystem.tileSize) || 48;
+ const farmWorldX = this.farmCenterX * tileSize;
+ const farmWorldY = this.farmCenterY * tileSize;
+ const farmPixelSize = this.farmSize * tileSize;
+ const halfSize = farmPixelSize / 2;
+
+ const buttons = [
+ { dir: 'north', x: farmWorldX, y: farmWorldY - halfSize - 60, icon: 'โฌ๏ธ' },
+ { dir: 'south', x: farmWorldX, y: farmWorldY + halfSize + 60, icon: 'โฌ๏ธ' },
+ { dir: 'east', x: farmWorldX + halfSize + 60, y: farmWorldY, icon: 'โก๏ธ' },
+ { dir: 'west', x: farmWorldX - halfSize - 60, y: farmWorldY, icon: 'โฌ
๏ธ' }
+ ];
+
+ buttons.forEach(btn => {
+ // Button background
+ const bg = this.scene.add.rectangle(btn.x, btn.y, buttonSize, buttonSize, buttonColor);
+ bg.setStrokeStyle(2, 0xFFFFFF);
+ bg.setDepth(10);
+ bg.setInteractive({ useHandCursor: true });
+
+ // Button text
+ const text = this.scene.add.text(btn.x, btn.y, btn.icon, {
+ fontSize: '20px',
+ color: '#ffffff'
+ }).setOrigin(0.5);
+ text.setDepth(11);
+
+ // Cost label
+ const costLabel = this.scene.add.text(btn.x, btn.y + 30, `${expandCost}g`, {
+ fontSize: '12px',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ }).setOrigin(0.5);
+ costLabel.setDepth(11);
+
+ // Hover effects
+ bg.on('pointerover', () => {
+ bg.setFillStyle(buttonHoverColor);
+ bg.setScale(1.1);
+ });
+
+ bg.on('pointerout', () => {
+ bg.setFillStyle(buttonColor);
+ bg.setScale(1.0);
+ });
+
+ // Click handler
+ bg.on('pointerdown', () => {
+ this.tryExpandFarm(btn.dir);
+ });
+
+ this.expansionButtons.push({ bg, text, costLabel, dir: btn.dir });
+ });
+
+ console.log('โ
Expansion UI created!');
+ }
+
+ tryExpandFarm(direction) {
+ // Check if player has enough gold
+ const inv = this.scene.inventorySystem;
+ if (!inv || inv.gold < this.expansionCost) {
+ console.log(`โ Not enough gold! Need ${this.expansionCost}g`);
+
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.cameras.main.width / 2,
+ y: 100,
+ text: `Need ${this.expansionCost} gold!`,
+ color: '#ff0000'
+ });
+ }
+ return;
+ }
+
+ // Deduct gold
+ inv.gold -= this.expansionCost;
+
+ // Expand farm
+ this.expandFarm(direction);
+
+ // Success feedback
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.cameras.main.width / 2,
+ y: 100,
+ text: `โ
Farm expanded ${direction.toUpperCase()}!`,
+ color: '#00ff00'
+ });
+ }
+
+ console.log(`โ
Farm expanded ${direction}! (-${this.expansionCost}g)`);
+ }
+
+ unlockInitialFarm() {
+ // Unlock central 8x8 tiles
+ const halfSize = Math.floor(this.farmSize / 2);
+
+ for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
+ for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
+ const tileKey = `${x},${y}`;
+ this.unlockedTiles.add(tileKey);
+ }
+ }
+
+ console.log(`โ
Unlocked ${this.unlockedTiles.size} tiles (8x8 micro farm)`);
+ }
+
+ createFarmBoundaries() {
+ // Clear previous if exists
+ if (this.boundaryGraphics) {
+ this.boundaryGraphics.destroy();
+ }
+
+ const graphics = this.scene.add.graphics();
+ const tileSize = (this.scene.terrainSystem && this.scene.terrainSystem.tileSize) || 48;
+
+ // ๐๏ธ 1. LARGE MASTER PLATFORM (32x32)
+ const largeSize = 32;
+ const largeHalf = largeSize / 2;
+ const lx1 = (this.farmCenterX - largeHalf) * tileSize;
+ const ly1 = (this.farmCenterY - largeHalf) * tileSize;
+ const lWidth = largeSize * tileSize;
+ const lHeight = largeSize * tileSize;
+
+ // Draw Master Platform
+ graphics.fillStyle(0x0000FF, 0.05); // Very subtle blue for "Large Platform"
+ graphics.fillRect(lx1, ly1, lWidth, lHeight);
+ graphics.lineStyle(2, 0x0000FF, 0.3);
+ graphics.strokeRect(lx1, ly1, lWidth, lHeight);
+
+ // ๐๏ธ 2. SMALL MICRO FARM PLATFORM (8x8)
+ const halfSize = Math.floor(this.farmSize / 2);
+ const startX = (this.farmCenterX - halfSize) * tileSize;
+ const startY = (this.farmCenterY - halfSize) * tileSize;
+ const width = this.farmSize * tileSize;
+ const height = this.farmSize * tileSize;
+
+ // Draw Highlight
+ graphics.fillStyle(0xFFFFFF, 0.15); // 15% white for "Starter Platform"
+ graphics.fillRect(startX, startY, width, height);
+
+ // Draw Bold Boundary
+ graphics.lineStyle(4, 0x00FF00, 0.8); // High vis green
+ graphics.strokeRect(startX, startY, width, height);
+
+ // Corner markers
+ graphics.fillStyle(0xFFFF00, 1);
+ const markerSize = 12;
+ graphics.fillCircle(startX, startY, markerSize);
+ graphics.fillCircle(startX + width, startY, markerSize);
+ graphics.fillCircle(startX, startY + height, markerSize);
+ graphics.fillCircle(startX + width, startY + height, markerSize);
+
+ graphics.setDepth(50);
+ this.boundaryGraphics = graphics;
+
+ console.log(`๐ฐ Dual platforms rendered: Master (${largeSize}x${largeSize}) & Micro (${this.farmSize}x${this.farmSize})`);
+ }
+
+ renderLockedTileOverlay() {
+ // Render dark overlay on locked tiles
+ if (this.lockedOverlayGraphics) {
+ this.lockedOverlayGraphics.destroy();
+ }
+
+ this.lockedOverlayGraphics = this.scene.add.graphics();
+ this.lockedOverlayGraphics.setDepth(4); // Above ground, below boundaries
+
+ this.lockedOverlayGraphics.clear();
+
+ // Darken all tiles that are NOT unlocked
+ const halfSize = Math.floor(this.farmSize / 2);
+ const farmStartX = this.farmCenterX - halfSize;
+ const farmEndX = this.farmCenterX + halfSize;
+ const farmStartY = this.farmCenterY - halfSize;
+ const farmEndY = this.farmCenterY + halfSize;
+
+ // Render grid of locked tiles
+ const viewRange = 15; // Show some area around farm
+ for (let y = this.farmCenterY - viewRange; y < this.farmCenterY + viewRange; y++) {
+ for (let x = this.farmCenterX - viewRange; x < this.farmCenterX + viewRange; x++) {
+ // Skip if within farm boundaries
+ if (x >= farmStartX && x < farmEndX && y >= farmStartY && y < farmEndY) {
+ continue;
+ }
+
+ // Draw dark overlay (lighter)
+ const tileSize = (this.scene.terrainSystem && this.scene.terrainSystem.tileSize) || 48;
+ const worldX = x * tileSize;
+ const worldY = y * tileSize;
+ this.lockedOverlayGraphics.fillStyle(0x000000, 0.3); // 0.5 -> 0.3
+ this.lockedOverlayGraphics.fillRect(worldX, worldY, tileSize, tileSize);
+ }
+ }
+
+ console.log('๐ Locked tile overlay rendered!');
+ }
+
+ isTileUnlocked(tileX, tileY) {
+ const tileKey = `${tileX},${tileY}`;
+ return this.unlockedTiles.has(tileKey);
+ }
+
+ canExpand(direction) {
+ // Check if expansion in direction is possible
+ // direction: 'north', 'south', 'east', 'west'
+ // TODO: Implement expansion logic
+ return true;
+ }
+
+ expandFarm(direction) {
+ // Unlock 2x2 tiles in specified direction
+ const halfSize = Math.floor(this.farmSize / 2);
+ const expansionSize = 2; // Unlock 2x2 tiles at a time
+
+ let newTiles = [];
+
+ switch (direction) {
+ case 'north':
+ for (let y = this.farmCenterY - halfSize - expansionSize; y < this.farmCenterY - halfSize; y++) {
+ for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
+ newTiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'south':
+ for (let y = this.farmCenterY + halfSize; y < this.farmCenterY + halfSize + expansionSize; y++) {
+ for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
+ newTiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'east':
+ for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
+ for (let x = this.farmCenterX + halfSize; x < this.farmCenterX + halfSize + expansionSize; x++) {
+ newTiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'west':
+ for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
+ for (let x = this.farmCenterX - halfSize - expansionSize; x < this.farmCenterX - halfSize; x++) {
+ newTiles.push({ x, y });
+ }
+ }
+ break;
+ }
+
+ // Unlock the tiles
+ newTiles.forEach(tile => {
+ const tileKey = `${tile.x},${tile.y}`;
+ this.unlockedTiles.add(tileKey);
+ });
+
+ // Update farm size
+ if (direction === 'north' || direction === 'south') {
+ this.farmSize += expansionSize;
+ } else {
+ this.farmSize += expansionSize;
+ }
+
+ // Update visuals
+ this.createFarmBoundaries(); // Redraw boundaries
+ this.renderLockedTileOverlay(); // Update overlay
+
+ console.log(`๐ Expanded farm ${direction}! +${newTiles.length} tiles. Total: ${this.unlockedTiles.size}`);
+ }
+
+ getLandType(tileX, tileY) {
+ // Determine land type based on tile position
+ // TODO: Use terrain system data
+
+ // For now, return grass for unlocked tiles
+ if (this.isTileUnlocked(tileX, tileY)) {
+ return this.landTypes.GRASS;
+ }
+
+ // Surrounding areas have different types
+ const distFromCenter = Math.sqrt(
+ Math.pow(tileX - this.farmCenterX, 2) +
+ Math.pow(tileY - this.farmCenterY, 2)
+ );
+
+ if (distFromCenter < 10) return this.landTypes.GRASS;
+ if (distFromCenter < 15) return this.landTypes.FOREST;
+ if (distFromCenter < 20) return this.landTypes.ROCKY;
+ return this.landTypes.SWAMP;
+ }
+
+ destroy() {
+ // Cleanup
+ this.unlockedTiles.clear();
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/NPCPopulationSystem.js b/DEBUG_TOTAL_RECOVERY/NPCPopulationSystem.js
new file mode 100644
index 000000000..1ab8b2f08
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/NPCPopulationSystem.js
@@ -0,0 +1,337 @@
+/**
+ * ๐ฅ NPC POPULATION SYSTEM
+ * Spawns and manages NPCs in structures across the world
+ * - Biome-specific NPCs
+ * - Dialog system
+ * - Trading functionality
+ * - Quest giving
+ */
+
+class NPCPopulationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // All NPCs in the world
+ this.npcs = [];
+
+ // NPC types per biome
+ this.npcTypes = {
+ 'Grassland': ['farmer', 'blacksmith', 'merchant', 'guard'],
+ 'Forest': ['hunter', 'herbalist', 'ranger', 'druid'],
+ 'Desert': ['nomad', 'treasure_hunter', 'merchant', 'archaeologist'],
+ 'Mountain': ['miner', 'dwarf', 'geologist', 'mountaineer'],
+ 'Swamp': ['witch', 'alchemist', 'hermit', 'shaman']
+ };
+
+ // Dialog templates
+ this.dialogs = {
+ 'farmer': [
+ "Welcome to my farm! Need any seeds?",
+ "The harvest this year is bountiful!",
+ "I sell the best wheat in the land!"
+ ],
+ 'merchant': [
+ "Looking to buy or sell? I've got the best deals!",
+ "Welcome, traveler! Check out my wares!",
+ "Gold for goods, goods for gold!"
+ ],
+ 'hunter': [
+ "The forest is full of game. Happy hunting!",
+ "Watch out for the wolves at night.",
+ "I can sell you some arrows if you need them."
+ ],
+ 'nomad': [
+ "The desert holds many secrets...",
+ "Water is more valuable than gold here.",
+ "I've traveled far and wide, seen many things."
+ ],
+ 'miner': [
+ "These mountains are rich with ore!",
+ "I can sell you some iron if you need it.",
+ "Watch your step in those caves!"
+ ],
+ 'witch': [
+ "Potions and hexes, what do you need?",
+ "The swamp holds ancient magic...",
+ "I can brew you something special."
+ ]
+ };
+
+ // Trade goods per NPC type
+ this.tradeGoods = {
+ 'farmer': [
+ { item: 'wheat_seeds', price: 10, stock: 50 },
+ { item: 'wheat', price: 5, stock: 100 },
+ { item: 'bread', price: 15, stock: 20 }
+ ],
+ 'merchant': [
+ { item: 'wood', price: 8, stock: 200 },
+ { item: 'stone', price: 6, stock: 150 },
+ { item: 'iron_ore', price: 20, stock: 50 }
+ ],
+ 'blacksmith': [
+ { item: 'iron_sword', price: 150, stock: 5 },
+ { item: 'iron_pickaxe', price: 100, stock: 10 },
+ { item: 'iron_axe', price: 120, stock: 8 }
+ ],
+ 'hunter': [
+ { item: 'arrow', price: 5, stock: 100 },
+ { item: 'bow', price: 80, stock: 3 },
+ { item: 'meat', price: 20, stock: 30 }
+ ]
+ };
+
+ console.log('๐ฅ NPCPopulationSystem initialized');
+ }
+
+ // Populate structures with NPCs
+ populateStructures(structureSystem) {
+ if (!structureSystem) return;
+
+ let npcsSpawned = 0;
+
+ // Spawn NPCs in structures (30% chance)
+ for (const structure of structureSystem.structures) {
+ if (Math.random() < 0.3) {
+ const npcType = this.selectNPCType(structure.biome);
+ this.spawnNPC(structure.x, structure.y, npcType, structure.biome);
+ npcsSpawned++;
+ }
+ }
+
+ // Always spawn special NPCs at landmarks
+ for (const landmark of structureSystem.landmarks) {
+ this.spawnNPC(landmark.x, landmark.y, 'quest_giver', landmark.type, true);
+ npcsSpawned++;
+ }
+
+ console.log(`โ
Spawned ${npcsSpawned} NPCs in structures`);
+ }
+
+ // Select NPC type for biome
+ selectNPCType(biome) {
+ const types = this.npcTypes[biome] || this.npcTypes['Grassland'];
+ return types[Math.floor(Math.random() * types.length)];
+ }
+
+ // Spawn single NPC
+ spawnNPC(x, y, type, biome, isQuestGiver = false) {
+ const npc = {
+ x,
+ y,
+ type,
+ biome,
+ isQuestGiver,
+ dialogIndex: 0,
+ hasQuest: isQuestGiver,
+ questCompleted: false,
+ sprite: null
+ };
+
+ this.npcs.push(npc);
+ return npc;
+ }
+
+ // Create NPC sprite (called when chunk loads)
+ createNPCSprite(npc, chunk) {
+ if (npc.sprite) return; // Already has sprite
+
+ const worldX = npc.x * 48 + 24;
+ const worldY = npc.y * 48 + 24;
+
+ // Create simple circle sprite for NPC
+ const color = this.getNPCColor(npc.type);
+ const sprite = this.scene.add.circle(worldX, worldY, 12, color);
+ sprite.setDepth(10 + worldY);
+
+ // Add name label
+ const label = this.scene.add.text(worldX, worldY - 20, npc.type, {
+ fontSize: '12px',
+ color: '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 4, y: 2 }
+ });
+ label.setOrigin(0.5);
+ label.setDepth(10 + worldY);
+
+ // Quest marker for quest givers
+ if (npc.isQuestGiver && !npc.questCompleted) {
+ const questMarker = this.scene.add.text(worldX, worldY - 35, '!', {
+ fontSize: '24px',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ questMarker.setOrigin(0.5);
+ questMarker.setDepth(10 + worldY);
+
+ npc.questMarker = questMarker;
+ if (chunk) chunk.sprites.push(questMarker);
+ }
+
+ npc.sprite = sprite;
+ npc.label = label;
+
+ if (chunk) {
+ chunk.sprites.push(sprite);
+ chunk.sprites.push(label);
+ }
+ }
+
+ // Get NPC color
+ getNPCColor(type) {
+ const colors = {
+ 'farmer': 0x8B4513,
+ 'merchant': 0xDAA520,
+ 'blacksmith': 0x696969,
+ 'hunter': 0x228B22,
+ 'nomad': 0xD2691E,
+ 'miner': 0x708090,
+ 'witch': 0x9370DB,
+ 'quest_giver': 0xFFD700
+ };
+ return colors[type] || 0x808080;
+ }
+
+ // Check for nearby NPCs
+ update(playerX, playerY) {
+ let nearestNPC = null;
+ let minDist = 3;
+
+ for (const npc of this.npcs) {
+ const dist = Math.sqrt((npc.x - playerX) ** 2 + (npc.y - playerY) ** 2);
+ if (dist < minDist) {
+ minDist = dist;
+ nearestNPC = npc;
+ }
+ }
+
+ if (nearestNPC && !this.currentNPC) {
+ this.showTalkPrompt(nearestNPC);
+ this.currentNPC = nearestNPC;
+ } else if (!nearestNPC && this.currentNPC) {
+ this.hideTalkPrompt();
+ this.currentNPC = null;
+ }
+ }
+
+ // Show prompt to talk
+ showTalkPrompt(npc) {
+ if (this.talkPrompt) return;
+
+ const promptText = npc.isQuestGiver
+ ? '๐ฌ Press T to talk (QUEST AVAILABLE)'
+ : `๐ฌ Press T to talk to ${npc.type}`;
+
+ this.talkPrompt = this.scene.add.text(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.height - 100,
+ promptText,
+ {
+ fontSize: '24px',
+ color: npc.isQuestGiver ? '#FFD700' : '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 20, y: 10 }
+ }
+ );
+ this.talkPrompt.setOrigin(0.5);
+ this.talkPrompt.setScrollFactor(0);
+ this.talkPrompt.setDepth(10000);
+ }
+
+ // Hide talk prompt
+ hideTalkPrompt() {
+ if (this.talkPrompt) {
+ this.talkPrompt.destroy();
+ this.talkPrompt = null;
+ }
+ }
+
+ // Talk to NPC (T key)
+ talkToNPC() {
+ if (!this.currentNPC) return;
+
+ const npc = this.currentNPC;
+
+ // Get dialog
+ const dialogs = this.dialogs[npc.type] || ["Hello, traveler!"];
+ const dialog = dialogs[npc.dialogIndex % dialogs.length];
+ npc.dialogIndex++;
+
+ // Show dialog box
+ this.showDialog(npc, dialog);
+ }
+
+ // Show dialog UI
+ showDialog(npc, text) {
+ // Close existing dialog
+ if (this.dialogBox) {
+ this.dialogBox.destroy();
+ this.dialogText.destroy();
+ this.dialogBox = null;
+ }
+
+ // Create dialog box
+ const centerX = this.scene.cameras.main.centerX;
+ const centerY = this.scene.cameras.main.height - 150;
+
+ this.dialogBox = this.scene.add.rectangle(
+ centerX, centerY,
+ 600, 120,
+ 0x000000, 0.8
+ );
+ this.dialogBox.setStrokeStyle(3, 0xFFFFFF);
+ this.dialogBox.setScrollFactor(0);
+ this.dialogBox.setDepth(10001);
+
+ this.dialogText = this.scene.add.text(
+ centerX, centerY - 30,
+ `${npc.type}:\n${text}`,
+ {
+ fontSize: '20px',
+ color: '#ffffff',
+ align: 'center',
+ wordWrap: { width: 550 }
+ }
+ );
+ this.dialogText.setOrigin(0.5, 0);
+ this.dialogText.setScrollFactor(0);
+ this.dialogText.setDepth(10002);
+
+ // Auto-close after 4 seconds
+ this.scene.time.delayedCall(4000, () => {
+ if (this.dialogBox) {
+ this.dialogBox.destroy();
+ this.dialogText.destroy();
+ this.dialogBox = null;
+ }
+ });
+ }
+
+ // Get statistics
+ getStats() {
+ const byBiome = {};
+ for (const npc of this.npcs) {
+ byBiome[npc.biome] = (byBiome[npc.biome] || 0) + 1;
+ }
+
+ return {
+ totalNPCs: this.npcs.length,
+ questGivers: this.npcs.filter(n => n.isQuestGiver).length,
+ byBiome
+ };
+ }
+
+ destroy() {
+ this.hideTalkPrompt();
+ if (this.dialogBox) {
+ this.dialogBox.destroy();
+ this.dialogText.destroy();
+ }
+ this.npcs.forEach(npc => {
+ if (npc.sprite) npc.sprite.destroy();
+ if (npc.label) npc.label.destroy();
+ if (npc.questMarker) npc.questMarker.destroy();
+ });
+ this.npcs = [];
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/NPCPrivacySystem.js b/DEBUG_TOTAL_RECOVERY/NPCPrivacySystem.js
new file mode 100644
index 000000000..e39266c50
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/NPCPrivacySystem.js
@@ -0,0 +1,454 @@
+/**
+ * NPC PRIVACY & HOME DECORATION SYSTEM
+ * Part of: Game Systems Expansion
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Hobby-based automatic interior generation
+ * - Door lock system (heart-based access)
+ * - NPC home visit mechanics
+ * - Relationship effects from visits
+ * - Privacy violations & consequences
+ */
+
+class NPCPrivacySystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Privacy settings
+ this.privacyLevels = {
+ PUBLIC: 0, // Anyone can enter (living room, kitchen)
+ FRIENDLY: 3, // 3+ hearts required
+ PRIVATE: 5, // 5+ hearts required (bedroom)
+ INTIMATE: 8, // 8+ hearts required (special rooms)
+ LOCKED: 10 // 10 hearts or marriage required
+ };
+
+ // Visit tracking
+ this.visitHistory = {};
+ this.lastVisitTimes = {};
+
+ // NPC home decorations (generated based on hobby)
+ this.npcHomes = {};
+ }
+
+ /**
+ * Generate NPC home interior based on hobby
+ */
+ generateNPCHome(npcId) {
+ const npc = this.game.npcs.get(npcId);
+ if (!npc) return null;
+
+ const hobby = npc.hobby;
+
+ // Base home structure
+ const home = {
+ npcId: npcId,
+ rooms: {
+ living_room: {
+ name: 'Living Room',
+ privacyLevel: this.privacyLevels.PUBLIC,
+ objects: this.generateLivingRoomObjects(hobby)
+ },
+ kitchen: {
+ name: 'Kitchen',
+ privacyLevel: this.privacyLevels.PUBLIC,
+ objects: this.generateKitchenObjects(hobby)
+ },
+ bedroom: {
+ name: 'Bedroom',
+ privacyLevel: this.privacyLevels.PRIVATE,
+ objects: this.generateBedroomObjects(hobby)
+ },
+ hobby_room: {
+ name: this.getHobbyRoomName(hobby),
+ privacyLevel: this.privacyLevels.FRIENDLY,
+ objects: this.generateHobbyRoomObjects(hobby)
+ }
+ },
+ visitCount: 0,
+ lastVisit: null
+ };
+
+ // Store home
+ this.npcHomes[npcId] = home;
+
+ return home;
+ }
+
+ /**
+ * Generate living room objects
+ */
+ generateLivingRoomObjects(hobby) {
+ const base = [
+ 'interior_table_small',
+ 'interior_bookshelf',
+ 'interior_gothic_lantern'
+ ];
+
+ // Hobby-specific additions
+ const hobbyAdditions = {
+ fishing: ['mounted_fish', 'fishing_net'],
+ farming: ['grain_sack', 'tool_rack'],
+ cooking: ['recipe_shelf'],
+ reading: ['bookshelf', 'reading_chair'],
+ music: ['guitar_stand', 'music_sheets'],
+ crafting: ['crafting_workshop']
+ };
+
+ return [...base, ...(hobbyAdditions[hobby] || [])];
+ }
+
+ /**
+ * Generate kitchen objects
+ */
+ generateKitchenObjects(hobby) {
+ const base = [
+ 'interior_kitchen_stove',
+ 'interior_kitchen_counter'
+ ];
+
+ if (hobby === 'cooking') {
+ base.push(
+ 'interior_kitchen_fridge',
+ 'interior_kitchen_sink',
+ 'interior_recipe_shelf'
+ );
+ }
+
+ return base;
+ }
+
+ /**
+ * Generate bedroom objects
+ */
+ generateBedroomObjects(hobby) {
+ const base = [
+ 'interior_bed_wooden',
+ 'interior_wardrobe',
+ 'interior_chest_locked'
+ ];
+
+ // Personal items based on hobby
+ const hobbyItems = {
+ zombie_worker: ['brain_jar', 'work_uniform'],
+ baker: ['flour_workspace', 'dough_tools'],
+ barber: ['dreadlock_kit', 'styling_tools'],
+ fisherman: ['tackle_box', 'fish_collection']
+ };
+
+ return [...base, ...(hobbyItems[hobby] || [])];
+ }
+
+ /**
+ * Generate hobby room objects
+ */
+ generateHobbyRoomObjects(hobby) {
+ const hobbyRooms = {
+ fishing: [
+ 'fishing_rod_rack',
+ 'bait_storage',
+ 'fish_tank',
+ 'mounted_trophy_fish'
+ ],
+ zombie_worker: [
+ 'brain_jar',
+ 'work_bench',
+ 'tool_storage',
+ 'miner_equipment'
+ ],
+ baker: [
+ 'flour_workspace',
+ 'mixing_bowls',
+ 'bread_storage',
+ 'recipe_collection'
+ ],
+ alchemy: [
+ 'interior_alchemy_bottle',
+ 'interior_brewing_cauldron',
+ 'interior_chemistry_set',
+ 'interior_potion_shelf'
+ ],
+ crafting: [
+ 'interior_crafting_workshop',
+ 'interior_tool_rack',
+ 'material_storage',
+ 'blueprint_table'
+ ],
+ reading: [
+ 'interior_bookshelf',
+ 'reading_chair',
+ 'ancient_manuscripts',
+ 'writing_desk'
+ ]
+ };
+
+ return hobbyRooms[hobby] || ['generic_workspace'];
+ }
+
+ /**
+ * Get hobby room name
+ */
+ getHobbyRoomName(hobby) {
+ const names = {
+ fishing: 'Fishing Den',
+ zombie_worker: 'Worker\'s Quarters',
+ baker: 'Baking Workshop',
+ alchemy: 'Alchemy Lab',
+ crafting: 'Craft Room',
+ reading: 'Library',
+ music: 'Music Studio',
+ farming: 'Storage Shed'
+ };
+
+ return names[hobby] || 'Hobby Room';
+ }
+
+ /**
+ * Attempt to enter NPC room
+ */
+ attemptEntry(npcId, roomId) {
+ const npc = this.game.npcs.get(npcId);
+ if (!npc) {
+ return { success: false, message: 'NPC not found!' };
+ }
+
+ // Generate home if doesn't exist
+ if (!this.npcHomes[npcId]) {
+ this.generateNPCHome(npcId);
+ }
+
+ const home = this.npcHomes[npcId];
+ const room = home.rooms[roomId];
+
+ if (!room) {
+ return { success: false, message: 'Room not found!' };
+ }
+
+ // Check privacy level
+ const playerHearts = this.player.getRelationshipHearts(npcId);
+ const requiredHearts = room.privacyLevel;
+
+ if (playerHearts < requiredHearts) {
+ // Privacy violation!
+ return this.handlePrivacyViolation(npcId, roomId, requiredHearts);
+ }
+
+ // Allowed entry
+ return this.handleSuccessfulEntry(npcId, roomId);
+ }
+
+ /**
+ * Handle privacy violation
+ */
+ handlePrivacyViolation(npcId, roomId, requiredHearts) {
+ const npc = this.game.npcs.get(npcId);
+
+ // Relationship penalty
+ const penalty = (requiredHearts - this.player.getRelationshipHearts(npcId)) * 20;
+ npc.addRelationshipPoints(-penalty);
+
+ // NPC reaction
+ const reactions = [
+ "Hey! That's private!",
+ "What are you doing in my room?!",
+ "Get out! This is MY space!",
+ "I can't believe you just walked in here...",
+ "Privacy, please!"
+ ];
+
+ const reaction = Phaser.Utils.Array.GetRandom(reactions);
+
+ this.game.showDialogue(npc.name, reaction, {
+ mood: 'angry',
+ animation: 'shocked'
+ });
+
+ this.game.showMessage(
+ `${npc.name} is upset! -${penalty} relationship points`
+ );
+
+ // Player gets kicked out
+ this.game.player.teleportToLocation('outside_' + npcId + '_home');
+
+ return {
+ success: false,
+ privacyViolation: true,
+ penalty: penalty,
+ requiredHearts: requiredHearts,
+ currentHearts: this.player.getRelationshipHearts(npcId)
+ };
+ }
+
+ /**
+ * Handle successful entry
+ */
+ handleSuccessfulEntry(npcId, roomId) {
+ const npc = this.game.npcs.get(npcId);
+ const home = this.npcHomes[npcId];
+
+ // Track visit
+ if (!this.visitHistory[npcId]) {
+ this.visitHistory[npcId] = [];
+ }
+
+ const visit = {
+ roomId: roomId,
+ timestamp: this.game.time.currentTime,
+ timeOfDay: this.game.time.getTimeOfDay()
+ };
+
+ this.visitHistory[npcId].push(visit);
+ this.lastVisitTimes[npcId] = this.game.time.currentTime;
+
+ home.visitCount++;
+ home.lastVisit = this.game.time.currentTime;
+
+ // Relationship effects based on visit
+ this.applyVisitEffects(npcId, roomId, visit);
+
+ // Enter room scene
+ this.game.scene.start('NPCRoomScene', {
+ npcId: npcId,
+ roomId: roomId,
+ room: home.rooms[roomId]
+ });
+
+ return {
+ success: true,
+ room: home.rooms[roomId],
+ npc: npc
+ };
+ }
+
+ /**
+ * Apply relationship effects from visit
+ */
+ applyVisitEffects(npcId, roomId, visit) {
+ const npc = this.game.npcs.get(npcId);
+ const timeOfDay = visit.timeOfDay;
+
+ // Visit frequency check
+ const recentVisits = this.visitHistory[npcId].filter(v => {
+ const hoursSince = (this.game.time.currentTime - v.timestamp) / 3600;
+ return hoursSince < 24; // Last 24 hours
+ });
+
+ if (recentVisits.length > 3) {
+ // Visiting TOO much = annoying
+ npc.addRelationshipPoints(-10);
+ this.game.showMessage(
+ `${npc.name} seems a bit annoyed by frequent visits...`
+ );
+ return;
+ }
+
+ // Time of day effects
+ if (timeOfDay === 'night' && roomId === 'bedroom') {
+ // Visiting bedroom at night = awkward
+ npc.addRelationshipPoints(-5);
+ this.game.showDialogue(
+ npc.name,
+ "It's quite late... Is everything okay?",
+ { mood: 'concerned' }
+ );
+ } else if (timeOfDay === 'morning' && roomId === 'kitchen') {
+ // Morning kitchen visit = breakfast together!
+ npc.addRelationshipPoints(5);
+ this.game.showDialogue(
+ npc.name,
+ "Good morning! Care to join me for breakfast?",
+ { mood: 'happy' }
+ );
+ } else if (roomId === 'hobby_room') {
+ // Showing interest in hobby = bonus points!
+ npc.addRelationshipPoints(10);
+ this.game.showDialogue(
+ npc.name,
+ "I'm glad you're interested in my hobby!",
+ { mood: 'excited' }
+ );
+ }
+ }
+
+ /**
+ * Get visit statistics for NPC
+ */
+ getVisitStats(npcId) {
+ const visits = this.visitHistory[npcId] || [];
+
+ // Count visits per room
+ const roomCounts = {};
+ visits.forEach(visit => {
+ roomCounts[visit.roomId] = (roomCounts[visit.roomId] || 0) + 1;
+ });
+
+ // Average visits per day
+ const daysSinceFirstVisit = visits.length > 0
+ ? (this.game.time.currentTime - visits[0].timestamp) / 86400
+ : 0;
+
+ const avgVisitsPerDay = daysSinceFirstVisit > 0
+ ? visits.length / daysSinceFirstVisit
+ : 0;
+
+ return {
+ totalVisits: visits.length,
+ roomCounts: roomCounts,
+ avgVisitsPerDay: avgVisitsPerDay,
+ lastVisit: this.lastVisitTimes[npcId],
+ favoriteRoom: Object.keys(roomCounts).reduce((a, b) =>
+ roomCounts[a] > roomCounts[b] ? a : b, null)
+ };
+ }
+
+ /**
+ * Check if player can access special room
+ */
+ canAccessSpecialRoom(npcId, roomId) {
+ const npc = this.game.npcs.get(npcId);
+ if (!npc) return false;
+
+ const home = this.npcHomes[npcId];
+ if (!home) return false;
+
+ const room = home.rooms[roomId];
+ if (!room) return false;
+
+ const playerHearts = this.player.getRelationshipHearts(npcId);
+
+ // Special case: marriage allows LOCKED access
+ if (room.privacyLevel === this.privacyLevels.LOCKED) {
+ return this.player.spouse === npcId;
+ }
+
+ return playerHearts >= room.privacyLevel;
+ }
+
+ /**
+ * Get NPC home UI data
+ */
+ getNPCHomeUIData(npcId) {
+ if (!this.npcHomes[npcId]) {
+ this.generateNPCHome(npcId);
+ }
+
+ const home = this.npcHomes[npcId];
+ const npc = this.game.npcs.get(npcId);
+
+ return {
+ npc: npc,
+ home: home,
+ accessibleRooms: Object.keys(home.rooms).filter(roomId =>
+ this.canAccessSpecialRoom(npcId, roomId)
+ ),
+ lockedRooms: Object.keys(home.rooms).filter(roomId =>
+ !this.canAccessSpecialRoom(npcId, roomId)
+ ),
+ visitStats: this.getVisitStats(npcId)
+ };
+ }
+}
+
+
diff --git a/DEBUG_TOTAL_RECOVERY/NPCSettlementSystem.js b/DEBUG_TOTAL_RECOVERY/NPCSettlementSystem.js
new file mode 100644
index 000000000..4aff1090d
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/NPCSettlementSystem.js
@@ -0,0 +1,341 @@
+/**
+ * NPC SETTLEMENT SYSTEM (Magic Helpers)
+ * NPCs auto-assist with tasks
+ * Worker efficiency, happiness, housing
+ */
+
+export class NPCSettlementSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Settled NPCs
+ this.settledNPCs = new Map();
+
+ // Worker assignments
+ this.assignments = new Map(); // npcId โ task
+
+ // NPC stats
+ this.npcHappiness = new Map(); // npcId โ happiness (0-100)
+ this.npcEfficiency = new Map(); // npcId โ efficiency (0.5-2.0)
+
+ // Housing
+ this.npcHomes = new Map(); // npcId โ buildingId
+ this.housingCapacity = 0;
+
+ // Task types
+ this.taskTypes = ['farming', 'building', 'crafting', 'defense', 'gathering'];
+
+ this.init();
+ }
+
+ init() {
+ // Update worker tasks periodically
+ this.scene.time.addEvent({
+ delay: 5000, // Every 5s
+ callback: () => this.updateWorkers(),
+ loop: true
+ });
+
+ // Update happiness hourly
+ this.scene.time.addEvent({
+ delay: 3600000, // 1 hour
+ callback: () => this.updateHappiness(),
+ loop: true
+ });
+ }
+
+ /**
+ * Settle an NPC (they join the town)
+ */
+ settleNPC(npcId, npcData) {
+ if (this.settledNPCs.has(npcId)) return false;
+
+ //Check housing availability
+ if (this.settledNPCs.size >= this.housingCapacity) {
+ console.warn('No housing available');
+ return false;
+ }
+
+ this.settledNPCs.set(npcId, {
+ id: npcId,
+ name: npcData.name,
+ skills: npcData.skills || [],
+ assignedTask: null,
+ ...npcData
+ });
+
+ // Initial stats
+ this.npcHappiness.set(npcId, 75); // Start at 75% happiness
+ this.npcEfficiency.set(npcId, 1.0); // Base efficiency
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `${npcData.name} has joined the settlement!`,
+ 'success',
+ { icon: 'npc_join' }
+ );
+
+ console.log(`๐๏ธ ${npcData.name} settled in town`);
+ return true;
+ }
+
+ /**
+ * Assign NPC to task
+ */
+ assignTask(npcId, taskType, taskData) {
+ const npc = this.settledNPCs.get(npcId);
+ if (!npc) return false;
+
+ // Check if NPC has skill for task
+ if (!npc.skills.includes(taskType)) {
+ console.warn(`${npc.name} lacks skill: ${taskType}`);
+ return false;
+ }
+
+ // Assign
+ npc.assignedTask = taskType;
+ this.assignments.set(npcId, {
+ type: taskType,
+ data: taskData,
+ startTime: Date.now()
+ });
+
+ console.log(`๐ท ${npc.name} assigned to ${taskType}`);
+ return true;
+ }
+
+ /**
+ * Unassign NPC from task
+ */
+ unassignTask(npcId) {
+ const npc = this.settledNPCs.get(npcId);
+ if (!npc) return false;
+
+ npc.assignedTask = null;
+ this.assignments.delete(npcId);
+
+ console.log(`${npc.name} unassigned`);
+ return true;
+ }
+
+ /**
+ * Update all worker tasks
+ */
+ updateWorkers() {
+ this.assignments.forEach((assignment, npcId) => {
+ const npc = this.settledNPCs.get(npcId);
+ const efficiency = this.npcEfficiency.get(npcId) || 1.0;
+
+ switch (assignment.type) {
+ case 'farming':
+ this.performFarming(npc, efficiency, assignment.data);
+ break;
+ case 'building':
+ this.performBuilding(npc, efficiency, assignment.data);
+ break;
+ case 'crafting':
+ this.performCrafting(npc, efficiency, assignment.data);
+ break;
+ case 'defense':
+ this.performDefense(npc, efficiency);
+ break;
+ case 'gathering':
+ this.performGathering(npc, efficiency);
+ break;
+ }
+ });
+ }
+
+ /**
+ * WORKER TASKS
+ */
+
+ performFarming(npc, efficiency, data) {
+ // Auto-tend crops
+ const crops = this.scene.crops || [];
+ const tendsPerCycle = Math.floor(2 * efficiency);
+
+ for (let i = 0; i < tendsPerCycle && i < crops.length; i++) {
+ const crop = crops[i];
+ crop.water?.();
+ crop.fertilize?.();
+ }
+
+ // Chance to auto-harvest
+ if (Math.random() < 0.1 * efficiency) {
+ const harvestable = crops.find(c => c.isHarvestable);
+ if (harvestable) {
+ this.scene.farmingSystem?.harvestCrop(harvestable);
+ console.log(`${npc.name} harvested crop`);
+ }
+ }
+ }
+
+ performBuilding(npc, efficiency, data) {
+ // Speed up construction
+ const building = data.buildingId ? this.scene.buildingSystem?.getBuilding(data.buildingId) : null;
+
+ if (building && building.isUnderConstruction) {
+ const progressBonus = 5 * efficiency;
+ building.constructionProgress += progressBonus;
+
+ if (building.constructionProgress >= 100) {
+ this.scene.buildingSystem?.completeConstruction(building.id);
+ console.log(`${npc.name} completed building: ${building.name}`);
+ }
+ }
+ }
+
+ performCrafting(npc, efficiency, data) {
+ // Auto-craft items
+ if (data.recipe) {
+ const canCraft = this.scene.craftingSystem?.canCraft(data.recipe);
+ if (canCraft && Math.random() < 0.2 * efficiency) {
+ this.scene.craftingSystem?.craft(data.recipe);
+ console.log(`${npc.name} crafted ${data.recipe}`);
+ }
+ }
+ }
+
+ performDefense(npc, efficiency) {
+ // Patrol and defend
+ // Increased detection range for raids
+ this.scene.gameState.buffs.raid_detection_range = (this.scene.gameState.buffs.raid_detection_range || 1.0) + (0.1 * efficiency);
+
+ // Auto-repair walls/defenses
+ const defenses = this.scene.defenseSystem?.getAllDefenses() || [];
+ defenses.forEach(defense => {
+ if (defense.health < defense.maxHealth && Math.random() < 0.05 * efficiency) {
+ defense.health = Math.min(defense.maxHealth, defense.health + 10);
+ console.log(`${npc.name} repaired ${defense.name}`);
+ }
+ });
+ }
+
+ performGathering(npc, efficiency) {
+ // Auto-gather resources
+ const gatherChance = 0.15 * efficiency;
+
+ if (Math.random() < gatherChance) {
+ const resources = ['wood', 'stone', 'berries', 'herbs'];
+ const resource = Phaser.Utils.Array.GetRandom(resources);
+ const amount = Math.floor(Phaser.Math.Between(1, 3) * efficiency);
+
+ this.scene.inventorySystem?.addItem(resource, amount);
+ console.log(`${npc.name} gathered ${amount} ${resource}`);
+ }
+ }
+
+ /**
+ * Update NPC happiness
+ */
+ updateHappiness() {
+ this.settledNPCs.forEach((npc, npcId) => {
+ let happiness = this.npcHappiness.get(npcId) || 50;
+
+ // Factors affecting happiness
+ const hasHome = this.npcHomes.has(npcId);
+ const hasTask = npc.assignedTask !== null;
+ const isOverworked = hasTask && this.assignments.get(npcId)?.overworked;
+
+ // Adjustments
+ if (hasHome) happiness += 5;
+ if (hasTask) happiness += 2;
+ if (isOverworked) happiness -= 10;
+ if (!hasHome && this.settledNPCs.size > this.housingCapacity) happiness -= 15;
+
+ // Natural decay
+ happiness -= 1;
+
+ // Clamp
+ happiness = Math.max(0, Math.min(100, happiness));
+ this.npcHappiness.set(npcId, happiness);
+
+ // Update efficiency based on happiness
+ const efficiency = 0.5 + (happiness / 100) * 1.5; // 0.5-2.0
+ this.npcEfficiency.set(npcId, efficiency);
+
+ // Low happiness warning
+ if (happiness < 30) {
+ this.scene.uiSystem?.showNotification(
+ `${npc.name} is unhappy!`,
+ 'warning'
+ );
+ console.warn(`${npc.name} happiness: ${happiness}`);
+ }
+ });
+ }
+
+ /**
+ * Assign NPC to home
+ */
+ assignHome(npcId, buildingId) {
+ this.npcHomes.set(npcId, buildingId);
+
+ // Happiness boost
+ const happiness = this.npcHappiness.get(npcId) || 50;
+ this.npcHappiness.set(npcId, Math.min(100, happiness + 20));
+
+ console.log(`${this.settledNPCs.get(npcId).name} moved into home`);
+ }
+
+ /**
+ * Increase housing capacity
+ */
+ addHousing(capacity) {
+ this.housingCapacity += capacity;
+ console.log(`Housing capacity: ${this.housingCapacity}`);
+ }
+
+ /**
+ * Get settlement statistics
+ */
+ getSettlementStats() {
+ const npcs = Array.from(this.settledNPCs.values());
+ const avgHappiness = npcs.reduce((sum, npc) => sum + (this.npcHappiness.get(npc.id) || 0), 0) / npcs.length || 0;
+ const avgEfficiency = npcs.reduce((sum, npc) => sum + (this.npcEfficiency.get(npc.id) || 1), 0) / npcs.length || 1;
+
+ return {
+ population: npcs.length,
+ housingCapacity: this.housingCapacity,
+ averageHappiness: Math.round(avgHappiness),
+ averageEfficiency: avgEfficiency.toFixed(2),
+ assignedWorkers: this.assignments.size,
+ unemployed: npcs.length - this.assignments.size
+ };
+ }
+
+ /**
+ * Get NPCs by skill
+ */
+ getNPCsBySkill(skill) {
+ return Array.from(this.settledNPCs.values()).filter(npc => npc.skills.includes(skill));
+ }
+
+ /**
+ * Save/Load
+ */
+ getSaveData() {
+ return {
+ settledNPCs: Array.from(this.settledNPCs.entries()),
+ assignments: Array.from(this.assignments.entries()),
+ npcHappiness: Array.from(this.npcHappiness.entries()),
+ npcHomes: Array.from(this.npcHomes.entries()),
+ housingCapacity: this.housingCapacity
+ };
+ }
+
+ loadSaveData(data) {
+ this.settledNPCs = new Map(data.settledNPCs || []);
+ this.assignments = new Map(data.assignments || []);
+ this.npcHappiness = new Map(data.npcHappiness || []);
+ this.npcHomes = new Map(data.npcHomes || []);
+ this.housingCapacity = data.housingCapacity || 0;
+
+ // Recalculate efficiency
+ this.npcHappiness.forEach((happiness, npcId) => {
+ const efficiency = 0.5 + (happiness / 100) * 1.5;
+ this.npcEfficiency.set(npcId, efficiency);
+ });
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/NPCShopSystem.js b/DEBUG_TOTAL_RECOVERY/NPCShopSystem.js
new file mode 100644
index 000000000..e06897e18
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/NPCShopSystem.js
@@ -0,0 +1,503 @@
+/**
+ * NPCShopSystem.js
+ * ================
+ * KRVAVA ลฝETEV - NPC Trading & Shop System (Phase 38)
+ *
+ * Features:
+ * - 4 NPC shop types (Blacksmith, Baker, Trader, Healer)
+ * - Shop UI with buy/sell
+ * - Dynamic pricing
+ * - Stock management
+ * - Relationship discounts
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class NPCShopSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Shop registry
+ this.shops = new Map();
+ this.currentShop = null;
+
+ // Shop UI
+ this.shopContainer = null;
+ this.isShopOpen = false;
+
+ // Player inventory reference
+ this.playerInventory = null;
+ this.playerZlatniki = 0;
+
+ console.log('๐ NPCShopSystem initialized');
+
+ // Register all shops
+ this.registerShops();
+
+ // Create shop UI
+ this.createShopUI();
+ }
+
+ /**
+ * Register all NPC shops
+ */
+ registerShops() {
+ const shops = [
+ {
+ id: 'blacksmith',
+ name: 'Kovaฤ (Blacksmith)',
+ npc: 'Ivan the Blacksmith',
+ icon: 'โ๏ธ',
+ location: { x: 200, y: 200 },
+ inventory: [
+ // Tools
+ { id: 'iron_axe', name: 'Iron Axe', price: 200, stock: 5, category: 'tools', locked: true },
+ { id: 'iron_pickaxe', name: 'Iron Pickaxe', price: 200, stock: 5, category: 'tools', locked: true },
+ { id: 'iron_hoe', name: 'Iron Hoe', price: 150, stock: 5, category: 'tools', locked: true },
+ { id: 'watering_can', name: 'Watering Can', price: 100, stock: 10, category: 'tools' },
+
+ // Weapons
+ { id: 'iron_sword', name: 'Iron Sword', price: 500, stock: 3, category: 'weapons', locked: true },
+ { id: 'steel_sword', name: 'Steel Sword', price: 1000, stock: 2, category: 'weapons', locked: true },
+ { id: 'crossbow', name: 'Crossbow', price: 800, stock: 2, category: 'weapons', locked: true },
+
+
+ // Armor
+ { id: 'leather_armor', name: 'Leather Armor', price: 300, stock: 5, category: 'armor' },
+ { id: 'iron_armor', name: 'Iron Armor', price: 800, stock: 3, category: 'armor' }
+ ],
+ buyback: ['iron_ore', 'steel_bar', 'scrap_metal']
+ },
+ {
+ id: 'baker',
+ name: 'Pekarica (Baker)',
+ npc: 'Maria the Baker',
+ icon: '๐',
+ location: { x: 250, y: 200 },
+ inventory: [
+ // Food
+ { id: 'bread', name: 'Bread', price: 10, stock: 50, category: 'food' },
+ { id: 'cheese', name: 'Cheese', price: 20, stock: 30, category: 'food' },
+ { id: 'apple_pie', name: 'Apple Pie', price: 50, stock: 20, category: 'food' },
+ { id: 'cake', name: 'Cake', price: 100, stock: 10, category: 'food' },
+
+ // Recipes
+ { id: 'recipe_cookies', name: 'Cookie Recipe', price: 200, stock: 1, category: 'recipes' },
+ { id: 'recipe_pizza', name: 'Pizza Recipe', price: 300, stock: 1, category: 'recipes' },
+
+ // Ingredients
+ { id: 'flour', name: 'Flour', price: 15, stock: 100, category: 'ingredients' },
+ { id: 'sugar', name: 'Sugar', price: 20, stock: 80, category: 'ingredients' },
+ { id: 'yeast', name: 'Yeast', price: 10, stock: 50, category: 'ingredients' }
+ ],
+ buyback: ['wheat', 'milk', 'eggs', 'berries']
+ },
+ {
+ id: 'trader',
+ name: 'Trgovec (General Trader)',
+ npc: 'Gregor the Trader',
+ icon: '๐ฐ',
+ location: { x: 300, y: 200 },
+ inventory: [
+ // Seeds
+ { id: 'wheat_seeds', name: 'Wheat Seeds', price: 5, stock: 200, category: 'seeds' },
+ { id: 'corn_seeds', name: 'Corn Seeds', price: 8, stock: 150, category: 'seeds' },
+ { id: 'tomato_seeds', name: 'Tomato Seeds', price: 10, stock: 100, category: 'seeds' },
+ { id: 'strawberry_seeds', name: 'Strawberry Seeds', price: 15, stock: 80, category: 'seeds' },
+ { id: 'cannabis_seeds', name: 'Cannabis Seeds', price: 20, stock: 50, category: 'seeds' },
+
+ // Materials
+ { id: 'wood', name: 'Wood', price: 10, stock: 500, category: 'materials' },
+ { id: 'stone', name: 'Stone', price: 15, stock: 300, category: 'materials' },
+ { id: 'clay', name: 'Clay', price: 20, stock: 200, category: 'materials' },
+
+ // Special
+ { id: 'saddle', name: 'Saddle', price: 500, stock: 2, category: 'special' },
+ { id: 'bouquet', name: 'Bouquet', price: 100, stock: 10, category: 'special' },
+ { id: 'mermaid_pendant', name: 'Mermaid Pendant', price: 5000, stock: 1, category: 'special' }
+ ],
+ buyback: ['crops', 'foraged_items', 'fish']
+ },
+ {
+ id: 'healer',
+ name: 'Zdravnik (Healer)',
+ npc: 'Dr. Ana Kovaฤ',
+ icon: 'โ๏ธ',
+ location: { x: 350, y: 200 },
+ inventory: [
+ // Potions
+ { id: 'health_potion', name: 'Health Potion', price: 50, stock: 50, category: 'potions' },
+ { id: 'stamina_potion', name: 'Stamina Potion', price: 40, stock: 50, category: 'potions' },
+ { id: 'antidote', name: 'Antidote', price: 30, stock: 30, category: 'potions' },
+ { id: 'cure_infection', name: 'Cure Infection', price: 200, stock: 10, category: 'potions' },
+
+ // Research
+ { id: 'cure_research_1', name: 'Cure Research Notes I', price: 1000, stock: 1, category: 'research' },
+ { id: 'cure_research_2', name: 'Cure Research Notes II', price: 2000, stock: 1, category: 'research' },
+
+ // Medical supplies
+ { id: 'bandage', name: 'Bandage', price: 15, stock: 100, category: 'medical' },
+ { id: 'medicine', name: 'Medicine', price: 80, stock: 30, category: 'medical' }
+ ],
+ buyback: ['herbs', 'mushrooms', 'zombie_samples', 'cannabis', 'cannabis_buds']
+ }
+ ];
+
+ shops.forEach(shop => this.shops.set(shop.id, shop));
+
+ console.log(`โ
Registered ${this.shops.size} NPC shops`);
+ }
+
+ /**
+ * Create shop UI
+ */
+ createShopUI() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Main container
+ this.shopContainer = this.scene.add.container(width / 2, height / 2);
+ this.shopContainer.setScrollFactor(0);
+ this.shopContainer.setDepth(10000);
+ this.shopContainer.setVisible(false);
+
+ // Background
+ const bg = this.scene.add.rectangle(0, 0, 900, 600, 0x1a1a1a, 0.95);
+ bg.setStrokeStyle(3, 0xFFD700);
+ this.shopContainer.add(bg);
+
+ // Title (will be updated)
+ this.shopTitle = this.scene.add.text(0, -280, '๐ SHOP', {
+ fontSize: '32px',
+ fontFamily: 'Arial',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ this.shopTitle.setOrigin(0.5);
+ this.shopContainer.add(this.shopTitle);
+
+ // Close button
+ const closeBtn = this.scene.add.text(430, -280, 'โ', {
+ fontSize: '24px',
+ cursor: 'pointer'
+ });
+ closeBtn.setInteractive();
+ closeBtn.on('pointerdown', () => this.closeShop());
+ this.shopContainer.add(closeBtn);
+
+ // Player money display
+ this.moneyText = this.scene.add.text(-430, -250, '๐ฐ 0 Zlatniki', {
+ fontSize: '18px',
+ fontFamily: 'Arial',
+ color: '#FFD700'
+ });
+ this.shopContainer.add(this.moneyText);
+
+ // Category tabs
+ this.createCategoryTabs();
+
+ // Item list container
+ this.itemListContainer = this.scene.add.container(0, 0);
+ this.shopContainer.add(this.itemListContainer);
+
+ console.log('โ
Shop UI created');
+ }
+
+ /**
+ * Create category tabs
+ */
+ createCategoryTabs() {
+ const categories = ['all', 'tools', 'weapons', 'food', 'seeds', 'potions'];
+ const tabWidth = 120;
+ const startX = -400;
+ const y = -200;
+
+ categories.forEach((category, index) => {
+ const tab = this.scene.add.rectangle(
+ startX + (index * tabWidth),
+ y,
+ 110, 40,
+ 0x2d2d2d
+ );
+ tab.setStrokeStyle(2, 0x666666);
+ tab.setInteractive();
+ tab.on('pointerdown', () => this.filterByCategory(category));
+
+ const label = this.scene.add.text(
+ startX + (index * tabWidth),
+ y,
+ category.toUpperCase(),
+ {
+ fontSize: '14px',
+ fontFamily: 'Arial',
+ color: '#ffffff'
+ }
+ );
+ label.setOrigin(0.5);
+
+ this.shopContainer.add(tab);
+ this.shopContainer.add(label);
+ });
+ }
+
+ /**
+ * Open shop
+ */
+ openShop(shopId) {
+ const shop = this.shops.get(shopId);
+ if (!shop) {
+ console.error(`Shop ${shopId} not found!`);
+ return false;
+ }
+
+ this.currentShop = shop;
+ this.isShopOpen = true;
+
+ // Update title
+ this.shopTitle.setText(`${shop.icon} ${shop.name}`);
+
+ // Update money
+ this.updateMoneyDisplay();
+
+ // Display items
+ this.displayShopItems(shop.inventory);
+
+ // Show container
+ this.shopContainer.setVisible(true);
+
+ console.log(`๐ Opened ${shop.name}`);
+
+ return true;
+ }
+
+ /**
+ * Close shop
+ */
+ closeShop() {
+ this.isShopOpen = false;
+ this.currentShop = null;
+ this.shopContainer.setVisible(false);
+
+ console.log('๐ Shop closed');
+ }
+
+ /**
+ * Display shop items
+ */
+ displayShopItems(items, filter = 'all') {
+ // Clear previous items
+ this.itemListContainer.removeAll(true);
+
+ // Filter items
+ let filteredItems = items;
+ if (filter !== 'all') {
+ filteredItems = items.filter(item => item.category === filter);
+ }
+
+ // Display items (max 10 visible, scrollable)
+ const itemHeight = 50;
+ const startY = -150;
+
+ filteredItems.slice(0, 10).forEach((item, index) => {
+ const y = startY + (index * itemHeight);
+
+ // Item background
+ const itemBg = this.scene.add.rectangle(-400, y, 850, 45, 0x2d2d2d, 0.8);
+ itemBg.setStrokeStyle(1, 0x444444);
+ this.itemListContainer.add(itemBg);
+
+ // Item name
+ const nameText = this.scene.add.text(-380, y - 10, item.name, {
+ fontSize: '16px',
+ fontFamily: 'Arial',
+ color: '#ffffff'
+ });
+ this.itemListContainer.add(nameText);
+
+ // Stock
+ const stockText = this.scene.add.text(-380, y + 10, `Stock: ${item.stock}`, {
+ fontSize: '12px',
+ fontFamily: 'Arial',
+ color: '#888888'
+ });
+ this.itemListContainer.add(stockText);
+
+ // Price
+ const priceText = this.scene.add.text(200, y, `${item.price} ลฝ`, {
+ fontSize: '18px',
+ fontFamily: 'Arial',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ priceText.setOrigin(0.5);
+ this.itemListContainer.add(priceText);
+
+ // Buy button
+ const isLocked = item.locked && !this.isShopRestored(this.currentShop.id);
+ const buyColor = isLocked ? 0x666666 : 0x228B22;
+ const buyBtn = this.scene.add.rectangle(350, y, 100, 35, buyColor);
+ buyBtn.setStrokeStyle(2, isLocked ? 0x999999 : 0x32CD32);
+ buyBtn.setInteractive();
+ buyBtn.on('pointerdown', () => {
+ if (isLocked) {
+ this.showNotification({
+ title: 'Shop Ruined',
+ text: `Restore the shop to unlock this item!`,
+ icon: '๐๏ธ'
+ });
+ } else {
+ this.buyItem(item);
+ }
+ });
+ this.itemListContainer.add(buyBtn);
+
+ const buyText = this.scene.add.text(350, y, isLocked ? 'LOCKED' : 'BUY', {
+ fontSize: '14px',
+ fontFamily: 'Arial',
+ color: '#ffffff',
+ fontStyle: 'bold'
+ });
+ buyText.setOrigin(0.5);
+ this.itemListContainer.add(buyText);
+ });
+ }
+
+ isShopRestored(shopId) {
+ if (!this.scene.townRestorationSystem) return true; // Fallback if system missing
+
+ // Map shopId to buildingId
+ const mapping = {
+ 'blacksmith': 'jakob_shop', // or whichever ID correctly matches TownRestorationSystem
+ 'baker': 'lena_bakery',
+ 'healer': 'dr_chen_clinic'
+ };
+
+ const buildingId = mapping[shopId];
+ if (!buildingId) return true; // General trader is always open
+
+ const building = this.scene.townRestorationSystem.buildings.get(buildingId);
+ return building ? building.isRestored : false;
+ }
+
+
+ /**
+ * Filter by category
+ */
+ filterByCategory(category) {
+ if (!this.currentShop) return;
+ this.displayShopItems(this.currentShop.inventory, category);
+ }
+
+ /**
+ * Buy item
+ */
+ buyItem(item) {
+ // Check stock
+ if (item.stock <= 0) {
+ this.showNotification({
+ title: 'Out of Stock',
+ text: `${item.name} is out of stock!`,
+ icon: '๐ฆ'
+ });
+ return false;
+ }
+
+ // Calculate price with relationship discount
+ const finalPrice = this.calculatePrice(item.price);
+
+ // Check if player can afford
+ if (this.playerZlatniki < finalPrice) {
+ this.showNotification({
+ title: 'Not Enough Money',
+ text: `Need ${finalPrice}ลฝ to buy ${item.name}!`,
+ icon: '๐ฐ'
+ });
+ return false;
+ }
+
+ // Purchase!
+ this.playerZlatniki -= finalPrice;
+ item.stock--;
+
+ // TODO: Add item to player inventory
+ console.log(`โ
Purchased: ${item.name} for ${finalPrice}ลฝ`);
+
+ // Update UI
+ this.updateMoneyDisplay();
+ this.displayShopItems(this.currentShop.inventory);
+
+ this.showNotification({
+ title: 'Purchase Complete!',
+ text: `Bought ${item.name} for ${finalPrice}ลฝ!`,
+ icon: 'โ
'
+ });
+
+ return true;
+ }
+
+ /**
+ * Calculate price with discounts
+ */
+ calculatePrice(basePrice) {
+ // TODO: Apply relationship discounts
+ // For now, return base price
+ return basePrice;
+ }
+
+ /**
+ * Update money display
+ */
+ updateMoneyDisplay() {
+ this.moneyText.setText(`๐ฐ ${this.playerZlatniki} Zlatniki`);
+ }
+
+ /**
+ * Set player money
+ */
+ setPlayerMoney(amount) {
+ this.playerZlatniki = amount;
+ this.updateMoneyDisplay();
+ }
+
+ /**
+ * Get shop info
+ */
+ getShopInfo(shopId) {
+ return this.shops.get(shopId);
+ }
+
+ /**
+ * Get all shops
+ */
+ getAllShops() {
+ return Array.from(this.shops.values());
+ }
+
+ /**
+ * Restock shop
+ */
+ restockShop(shopId) {
+ const shop = this.shops.get(shopId);
+ if (!shop) return false;
+
+ shop.inventory.forEach(item => {
+ item.stock = Math.min(item.stock + 5, 100); // Restock +5, max 100
+ });
+
+ console.log(`๐ฆ ${shop.name} restocked!`);
+ return true;
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/NPCSpawner.js b/DEBUG_TOTAL_RECOVERY/NPCSpawner.js
new file mode 100644
index 000000000..b43b89a5c
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/NPCSpawner.js
@@ -0,0 +1,76 @@
+// NPCSpawner.js - Sistem za spawnjanje NPCjev
+class NPCSpawner {
+ constructor(scene) {
+ this.scene = scene;
+ this.spawnInterval = 30000; // 30 sekund
+ this.maxNPCs = 3; // 3 NPCji na 100x100 mapo
+ this.spawnTimer = 0;
+
+ console.log('๐ง NPCSpawner: Initialized');
+ }
+
+ spawnInitialNPCs() {
+ // Spawn 3 NPCs at random locations
+ for (let i = 0; i < this.maxNPCs; i++) {
+ this.spawnRandomNPC();
+ }
+ console.log(`โ
Spawned ${this.maxNPCs} initial NPCs`);
+ }
+
+ spawnRandomNPC() {
+ if (!this.scene.terrainSystem || !this.scene.npcs) return;
+
+ // Random position (avoid farm area 20,20)
+ let x, y, attempts = 0;
+ do {
+ x = Phaser.Math.Between(5, 95);
+ y = Phaser.Math.Between(5, 95);
+ attempts++;
+ } while (this.isTooCloseToFarm(x, y) && attempts < 50);
+
+ // Check if tile is valid
+ const tile = this.scene.terrainSystem.getTile(x, y);
+ if (!tile || tile.type === 'water') return;
+
+ // Check if decoration exists
+ const key = `${x},${y}`;
+ if (this.scene.terrainSystem.decorationsMap.has(key)) return;
+
+ // Spawn NPC
+ const npc = new NPC(
+ this.scene,
+ x,
+ y,
+ this.scene.terrainOffsetX || 0,
+ this.scene.terrainOffsetY || 0,
+ 'zombie' // Type
+ );
+
+ // Set to PASSIVE mode (random walk)
+ npc.state = 'PASSIVE';
+ npc.isTamed = false;
+
+ this.scene.npcs.push(npc);
+ console.log(`๐ง Spawned NPC at (${x}, ${y})`);
+ }
+
+ isTooCloseToFarm(x, y) {
+ const farmX = 50; // Farm center (updated from 20,20 to 50,50)
+ const farmY = 50;
+ const farmRadius = 15;
+
+ const dist = Math.sqrt((x - farmX) ** 2 + (y - farmY) ** 2);
+ return dist < farmRadius;
+ }
+
+ update(delta) {
+ // Check if we need to spawn more NPCs
+ if (this.scene.npcs.length < this.maxNPCs) {
+ this.spawnTimer += delta;
+ if (this.spawnTimer >= this.spawnInterval) {
+ this.spawnTimer = 0;
+ this.spawnRandomNPC();
+ }
+ }
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/SmartZombieSystem.js b/DEBUG_TOTAL_RECOVERY/SmartZombieSystem.js
new file mode 100644
index 000000000..060543922
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/SmartZombieSystem.js
@@ -0,0 +1,600 @@
+/**
+ * SMART ZOMBIE SYSTEM
+ * Manages zombie intelligence levels (Lv1-10), independent AI, follower commands, and team construction.
+ *
+ * Features:
+ * - Lv1-4: Helpers (resource finding, carry materials, warn danger)
+ * - Lv5-7: Assistants (repair assistance +25% speed, material delivery AI)
+ * - Lv8-10: INDEPENDENT AI (build/repair alone, auto-detect damage, teach others)
+ * - Follower System: 10 zombie followers with commands (Stop, Help, Attack, Home)
+ * - Team Construction: Lv10 leader + multi-zombie mega-projects
+ */
+class SmartZombieSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Zombie intelligence data
+ this.zombies = new Map(); // zombieId -> zombie data
+ this.followers = []; // Active followers (max 10)
+ this.independentWorkers = []; // Lv8+ zombies working autonomously
+ this.teams = []; // Construction teams (Lv10 leader + team)
+
+ // Commands
+ this.followerCommands = ['STOP', 'HELP', 'ATTACK', 'HOME'];
+ this.currentCommand = 'HELP'; // Default command
+
+ // AI Task Queue
+ this.taskQueue = []; // {type, position, priority, assignedZombie}
+
+ // Intelligence Levels
+ this.intelligenceLevels = {
+ 1: { name: 'Curious', abilities: ['ping_resources', 'carry_light'] },
+ 2: { name: 'Aware', abilities: ['carry_medium', 'warn_danger'] },
+ 3: { name: 'Helper', abilities: ['gather_resources', 'follow_player'] },
+ 4: { name: 'Assistant', abilities: ['detect_ores', 'organize_storage'] },
+ 5: { name: 'Skilled', abilities: ['repair_assist', 'deliver_materials'] },
+ 6: { name: 'Expert', abilities: ['craft_assist', 'defend_farm'] },
+ 7: { name: 'Advanced', abilities: ['build_assist', 'teach_lv1_3'] },
+ 8: { name: 'INDEPENDENT', abilities: ['build_alone', 'repair_alone', 'auto_detect_damage'] },
+ 9: { name: 'MASTER', abilities: ['teach_all', 'lead_team', 'optimize_tasks'] },
+ 10: { name: 'GENIUS', abilities: ['mega_projects', 'invent_blueprints', 'immortal_knowledge'] }
+ };
+
+ console.log('๐ง Smart Zombie System initialized!');
+ }
+
+ /**
+ * Create or upgrade a zombie
+ */
+ createZombie(name, level = 1, position = { x: 0, y: 0 }) {
+ const zombieId = `zombie_${Date.now()}_${Math.random()}`;
+
+ const zombie = {
+ id: zombieId,
+ name: name,
+ level: Math.min(level, 10),
+ position: position,
+ status: 'idle', // idle, working, following, patrolling
+ currentTask: null,
+ experience: 0,
+ experienceToNext: this.getExpRequired(level),
+ abilities: this.getAbilities(level),
+ inventory: [],
+ loyalty: 50, // 0-100
+ energy: 100, // 0-100
+ sprite: null // Will be set when rendered
+ };
+
+ this.zombies.set(zombieId, zombie);
+ console.log(`๐ง Created ${name} (Lv${level}) - ${this.intelligenceLevels[level].name}`);
+
+ // Auto-assign if Lv8+
+ if (level >= 8) {
+ this.makeIndependent(zombieId);
+ }
+
+ return zombieId;
+ }
+
+ /**
+ * Get required XP for next level
+ */
+ getExpRequired(level) {
+ return 100 * level * level; // 100, 400, 900, 1600, etc.
+ }
+
+ /**
+ * Get abilities for a level
+ */
+ getAbilities(level) {
+ const abilities = [];
+ for (let i = 1; i <= level; i++) {
+ abilities.push(...this.intelligenceLevels[i].abilities);
+ }
+ return [...new Set(abilities)]; // Remove duplicates
+ }
+
+ /**
+ * Add experience to a zombie
+ */
+ addExperience(zombieId, amount) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ zombie.experience += amount;
+
+ // Level up?
+ while (zombie.experience >= zombie.experienceToNext && zombie.level < 10) {
+ zombie.level++;
+ zombie.experience -= zombie.experienceToNext;
+ zombie.experienceToNext = this.getExpRequired(zombie.level + 1);
+ zombie.abilities = this.getAbilities(zombie.level);
+
+ console.log(`โฌ๏ธ ${zombie.name} leveled up to Lv${zombie.level}!`);
+
+ this.scene.events.emit('show-floating-text', {
+ x: zombie.position.x,
+ y: zombie.position.y - 50,
+ text: `LEVEL UP! Lv${zombie.level}`,
+ color: '#FFD700'
+ });
+
+ // Auto-assign if reached Lv8
+ if (zombie.level === 8) {
+ this.makeIndependent(zombieId);
+ }
+ }
+ }
+
+ /**
+ * Make a zombie independent (Lv8+)
+ */
+ makeIndependent(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie || zombie.level < 8) return false;
+
+ if (!this.independentWorkers.includes(zombieId)) {
+ this.independentWorkers.push(zombieId);
+ zombie.status = 'independent';
+
+ console.log(`๐ค ${zombie.name} is now INDEPENDENT!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Independent Worker!',
+ message: `${zombie.name} can now work autonomously!`,
+ icon: '๐ค'
+ });
+ }
+
+ return true;
+ }
+
+ /**
+ * Add zombie to follower group (max 10)
+ */
+ addFollower(zombieId) {
+ if (this.followers.length >= 10) {
+ console.log('โ Maximum 10 followers!');
+ return false;
+ }
+
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return false;
+
+ if (!this.followers.includes(zombieId)) {
+ this.followers.push(zombieId);
+ zombie.status = 'following';
+
+ console.log(`โ ${zombie.name} joined your followers!`);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove zombie from followers
+ */
+ removeFollower(zombieId) {
+ const index = this.followers.indexOf(zombieId);
+ if (index > -1) {
+ this.followers.splice(index, 1);
+ const zombie = this.zombies.get(zombieId);
+ if (zombie) {
+ zombie.status = 'idle';
+ console.log(`โ ${zombie.name} left your followers.`);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Send command to all followers
+ */
+ commandFollowers(command) {
+ if (!this.followerCommands.includes(command)) {
+ console.log(`โ Invalid command: ${command}`);
+ return;
+ }
+
+ this.currentCommand = command;
+
+ this.followers.forEach(zombieId => {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ switch (command) {
+ case 'STOP':
+ zombie.status = 'idle';
+ zombie.currentTask = null;
+ break;
+
+ case 'HELP':
+ zombie.status = 'helping';
+ this.assignHelpTask(zombieId);
+ break;
+
+ case 'ATTACK':
+ zombie.status = 'combat';
+ this.assignCombatTask(zombieId);
+ break;
+
+ case 'HOME':
+ zombie.status = 'returning';
+ this.sendHome(zombieId);
+ break;
+ }
+ });
+
+ console.log(`๐ข Commanded ${this.followers.length} followers: ${command}`);
+
+ this.scene.events.emit('notification', {
+ title: 'Follower Command',
+ message: `${this.followers.length} zombies: ${command}`,
+ icon: '๐ข'
+ });
+ }
+
+ /**
+ * Assign help task (gather, repair, build)
+ */
+ assignHelpTask(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ // Find nearest task from queue
+ const task = this.findNearestTask(zombie.position);
+ if (task) {
+ zombie.currentTask = task;
+ task.assignedZombie = zombieId;
+ console.log(`๐ ๏ธ ${zombie.name} assigned to ${task.type}`);
+ } else {
+ // Follow player and gather resources
+ zombie.currentTask = { type: 'gather', target: 'player' };
+ }
+ }
+
+ /**
+ * Assign combat task
+ */
+ assignCombatTask(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ zombie.currentTask = { type: 'defend', target: 'player', radius: 200 };
+ console.log(`โ๏ธ ${zombie.name} defending!`);
+ }
+
+ /**
+ * Send zombie home
+ */
+ sendHome(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ zombie.currentTask = { type: 'return_home', target: { x: 0, y: 0 } };
+ console.log(`๐ ${zombie.name} returning home.`);
+ }
+
+ /**
+ * Find nearest task for zombie
+ */
+ findNearestTask(position) {
+ if (this.taskQueue.length === 0) return null;
+
+ let nearest = null;
+ let minDist = Infinity;
+
+ this.taskQueue.forEach(task => {
+ if (task.assignedZombie) return; // Already assigned
+
+ const dist = Phaser.Math.Distance.Between(
+ position.x, position.y,
+ task.position.x, task.position.y
+ );
+
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = task;
+ }
+ });
+
+ return nearest;
+ }
+
+ /**
+ * Add task to queue
+ */
+ addTask(type, position, priority = 1) {
+ const task = {
+ id: `task_${Date.now()}`,
+ type: type, // 'build', 'repair', 'gather', 'mine'
+ position: position,
+ priority: priority,
+ assignedZombie: null,
+ progress: 0,
+ required: 100
+ };
+
+ this.taskQueue.push(task);
+
+ // Auto-assign to independent workers
+ this.autoAssignIndependentWorkers();
+
+ return task.id;
+ }
+
+ /**
+ * Auto-assign tasks to independent workers (Lv8+)
+ */
+ autoAssignIndependentWorkers() {
+ this.independentWorkers.forEach(zombieId => {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie || zombie.currentTask) return;
+
+ const task = this.findNearestTask(zombie.position);
+ if (task && zombie.abilities.includes('build_alone') || zombie.abilities.includes('repair_alone')) {
+ zombie.currentTask = task;
+ task.assignedZombie = zombieId;
+
+ console.log(`๐ค Independent: ${zombie.name} auto-assigned to ${task.type}`);
+ }
+ });
+ }
+
+ /**
+ * Create construction team (Lv10 leader required)
+ */
+ createTeam(leaderZombieId, memberZombieIds) {
+ const leader = this.zombies.get(leaderZombieId);
+ if (!leader || leader.level < 10) {
+ console.log('โ Team leader must be Lv10!');
+ return null;
+ }
+
+ const members = memberZombieIds
+ .map(id => this.zombies.get(id))
+ .filter(z => z && z.level >= 5);
+
+ if (members.length === 0) {
+ console.log('โ Need at least 1 Lv5+ member!');
+ return null;
+ }
+
+ const teamId = `team_${Date.now()}`;
+ const team = {
+ id: teamId,
+ leader: leaderZombieId,
+ members: memberZombieIds,
+ status: 'ready',
+ currentProject: null,
+ efficiency: 1.0 + (members.length * 0.15) // +15% per member
+ };
+
+ this.teams.push(team);
+
+ console.log(`๐ฅ Team created! Leader: ${leader.name}, Members: ${members.length}, Efficiency: ${team.efficiency}x`);
+
+ this.scene.events.emit('notification', {
+ title: 'Team Created!',
+ message: `${leader.name}'s team (${members.length} members) is ready!`,
+ icon: '๐ฅ'
+ });
+
+ return teamId;
+ }
+
+ /**
+ * Assign mega-project to team
+ */
+ assignMegaProject(teamId, projectType, position) {
+ const team = this.teams.find(t => t.id === teamId);
+ if (!team) return false;
+
+ const project = {
+ type: projectType, // 'mega_barn', 'fortress', 'factory'
+ position: position,
+ progress: 0,
+ required: 1000, // Mega projects take time
+ efficiency: team.efficiency
+ };
+
+ team.currentProject = project;
+ team.status = 'working';
+
+ console.log(`๐๏ธ Team assigned to ${projectType}! Efficiency: ${team.efficiency}x`);
+
+ return true;
+ }
+
+ /**
+ * Update all zombies (called every frame)
+ */
+ update(delta) {
+ // Update followers
+ this.followers.forEach(zombieId => {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ this.updateZombie(zombie, delta);
+ });
+
+ // Update independent workers
+ this.independentWorkers.forEach(zombieId => {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ this.updateZombie(zombie, delta);
+ });
+
+ // Update teams
+ this.teams.forEach(team => {
+ if (team.status === 'working' && team.currentProject) {
+ team.currentProject.progress += delta * 0.01 * team.efficiency;
+
+ if (team.currentProject.progress >= team.currentProject.required) {
+ this.completeMegaProject(team);
+ }
+ }
+ });
+ }
+
+ /**
+ * Update individual zombie
+ */
+ updateZombie(zombie, delta) {
+ if (!zombie.currentTask) return;
+
+ const task = zombie.currentTask;
+
+ switch (task.type) {
+ case 'build':
+ case 'repair':
+ this.progressTask(zombie, task, delta);
+ break;
+
+ case 'gather':
+ this.gatherResources(zombie, delta);
+ break;
+
+ case 'defend':
+ this.defendArea(zombie);
+ break;
+
+ case 'return_home':
+ this.moveToHome(zombie);
+ break;
+ }
+
+ // Drain energy
+ zombie.energy = Math.max(0, zombie.energy - delta * 0.001);
+ }
+
+ /**
+ * Progress on a task
+ */
+ progressTask(zombie, task, delta) {
+ const speedBonus = zombie.level >= 5 ? 1.25 : 1.0;
+ task.progress += delta * 0.02 * speedBonus;
+
+ if (task.progress >= task.required) {
+ this.completeTask(zombie, task);
+ }
+ }
+
+ /**
+ * Complete a task
+ */
+ completeTask(zombie, task) {
+ console.log(`โ
${zombie.name} completed ${task.type}!`);
+
+ // Award XP
+ this.addExperience(zombie.id, 50 + (task.priority * 10));
+
+ // Remove from queue
+ const index = this.taskQueue.indexOf(task);
+ if (index > -1) {
+ this.taskQueue.splice(index, 1);
+ }
+
+ zombie.currentTask = null;
+
+ // Find next task if independent
+ if (zombie.status === 'independent') {
+ this.autoAssignIndependentWorkers();
+ }
+ }
+
+ /**
+ * Gather resources
+ */
+ gatherResources(zombie, delta) {
+ // Simple resource gathering simulation
+ if (Math.random() < 0.01) {
+ const resources = ['wood', 'stone', 'iron_ore'];
+ const resource = resources[Math.floor(Math.random() * resources.length)];
+
+ zombie.inventory.push(resource);
+ console.log(`๐ฆ ${zombie.name} gathered ${resource}`);
+ }
+ }
+
+ /**
+ * Defend area around player/target
+ */
+ defendArea(zombie) {
+ // Detect nearby enemies (placeholder)
+ // In real game, would check for hostile entities
+ }
+
+ /**
+ * Move zombie to home position
+ */
+ moveToHome(zombie) {
+ const target = zombie.currentTask.target;
+ const dist = Phaser.Math.Distance.Between(
+ zombie.position.x, zombie.position.y,
+ target.x, target.y
+ );
+
+ if (dist < 10) {
+ zombie.status = 'idle';
+ zombie.currentTask = null;
+ console.log(`๐ ${zombie.name} arrived home.`);
+ } else {
+ // Move toward home (simplified)
+ const angle = Phaser.Math.Angle.Between(
+ zombie.position.x, zombie.position.y,
+ target.x, target.y
+ );
+ zombie.position.x += Math.cos(angle) * 2;
+ zombie.position.y += Math.sin(angle) * 2;
+ }
+ }
+
+ /**
+ * Complete mega-project
+ */
+ completeMegaProject(team) {
+ console.log(`๐ Mega-project ${team.currentProject.type} COMPLETE!`);
+
+ const leader = this.zombies.get(team.leader);
+ if (leader) {
+ this.addExperience(leader.id, 500);
+ }
+
+ team.members.forEach(memberId => {
+ this.addExperience(memberId, 200);
+ });
+
+ this.scene.events.emit('notification', {
+ title: 'Mega-Project Complete!',
+ message: `${team.currentProject.type} is finished!`,
+ icon: '๐'
+ });
+
+ team.currentProject = null;
+ team.status = 'ready';
+ }
+
+ /**
+ * Get zombie stats
+ */
+ getZombieStats(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return null;
+
+ return {
+ name: zombie.name,
+ level: zombie.level,
+ intelligence: this.intelligenceLevels[zombie.level].name,
+ abilities: zombie.abilities,
+ status: zombie.status,
+ energy: Math.round(zombie.energy),
+ loyalty: zombie.loyalty,
+ experience: `${zombie.experience}/${zombie.experienceToNext}`,
+ inventory: zombie.inventory.length
+ };
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/TownGrowthSystem.js b/DEBUG_TOTAL_RECOVERY/TownGrowthSystem.js
new file mode 100644
index 000000000..ea02702ad
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/TownGrowthSystem.js
@@ -0,0 +1,460 @@
+/**
+ * TOWN GROWTH SYSTEM - Population & Expansion
+ * Part of: Game Systems Expansion
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Dynamic town population (4 โ 20 NPCs)
+ * - Town sign with live stats
+ * - Small villages (5-10 scattered across map)
+ * - Population unlock requirements
+ * - Town Services unlock based on population
+ */
+
+class TownGrowthSystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Town stats
+ this.townName = 'Dolina Smrti';
+ this.population = 4; // Starting NPCs: Kai, Ana, Gronk, Baker
+ this.maxPopulation = 20;
+ this.populationSlots = [
+ { index: 1, unlocked: true, npc: 'kai' },
+ { index: 2, unlocked: true, npc: 'ana' },
+ { index: 3, unlocked: true, npc: 'gronk' },
+ { index: 4, unlocked: true, npc: 'baker' },
+ { index: 5, unlocked: false, npc: null, requirement: { farmLevel: 2 } },
+ { index: 6, unlocked: false, npc: null, requirement: { money: 10000 } },
+ { index: 7, unlocked: false, npc: null, requirement: { quest: 'expand_town_1' } },
+ { index: 8, unlocked: false, npc: null, requirement: { population: 6 } },
+ { index: 9, unlocked: false, npc: null, requirement: { building: 'bakery' } },
+ { index: 10, unlocked: false, npc: null, requirement: { building: 'barbershop' } },
+ { index: 11, unlocked: false, npc: null, requirement: { hearts: 5, npcId: 'any' } },
+ { index: 12, unlocked: false, npc: null, requirement: { quest: 'expand_town_2' } },
+ { index: 13, unlocked: false, npc: null, requirement: { zombieWorkers: 5 } },
+ { index: 14, unlocked: false, npc: null, requirement: { building: 'mine' } },
+ { index: 15, unlocked: false, npc: null, requirement: { money: 50000 } },
+ { index: 16, unlocked: false, npc: null, requirement: { quest: 'expand_town_3' } },
+ { index: 17, unlocked: false, npc: null, requirement: { marriage: true } },
+ { index: 18, unlocked: false, npc: null, requirement: { population: 15 } },
+ { index: 19, unlocked: false, npc: null, requirement: { allBuildings: true } },
+ { index: 20, unlocked: false, npc: null, requirement: { quest: 'town_master' } }
+ ];
+
+ // Town sign
+ this.townSign = {
+ location: { x: 400, y: 300 },
+ visible: true,
+ displayMode: 'population' // 'population', 'status', 'full'
+ };
+
+ // Small villages
+ this.villages = this.initializeVillages();
+
+ // Town services (unlock based on population)
+ this.services = {
+ market: { unlocked: false, requiredPopulation: 6 },
+ hospital: { unlocked: false, requiredPopulation: 8 },
+ school: { unlocked: false, requiredPopulation: 10 },
+ bank: { unlocked: false, requiredPopulation: 12 },
+ museum: { unlocked: false, requiredPopulation: 15 },
+ theater: { unlocked: false, requiredPopulation: 18 }
+ };
+ }
+
+ /**
+ * Initialize small villages
+ */
+ initializeVillages() {
+ return [
+ {
+ id: 'village_north',
+ name: 'Severna Vas',
+ position: { x: 3000, y: 500 },
+ population: 7,
+ discovered: false,
+ npcs: [
+ { id: 'fisherman', name: 'Old Fisher', hobby: 'fishing' },
+ { id: 'hunter', name: 'Hunter Dane', hobby: 'hunting' },
+ { id: 'hermit', name: 'Wise Hermit', hobby: 'meditation' }
+ ],
+ specialItems: ['ancient_fishing_rod', 'hunter_bow', 'meditation_mat']
+ },
+ {
+ id: 'village_east',
+ name: 'Vzhodna Stran',
+ position: { x: 5000, y: 2000 },
+ population: 5,
+ discovered: false,
+ npcs: [
+ { id: 'blacksmith', name: 'Master Smith', hobby: 'forging' },
+ { id: 'alchemist', name: 'Mysterious Alchemist', hobby: 'alchemy' }
+ ],
+ specialItems: ['master_anvil', 'legendary_hammer', 'philosopher_stone']
+ },
+ {
+ id: 'village_south',
+ name: 'Juลพno Naselje',
+ position: { x: 2500, y: 4500 },
+ population: 6,
+ discovered: false,
+ npcs: [
+ { id: 'trader', name: 'Traveling Trader', hobby: 'collecting' },
+ { id: 'musician', name: 'Bard Luka', hobby: 'music' }
+ ],
+ specialItems: ['exotic_seeds', 'rare_instruments', 'ancient_map']
+ },
+ {
+ id: 'village_west',
+ name: 'Zahodna Dolina',
+ position: { x: 500, y: 3000 },
+ population: 8,
+ discovered: false,
+ npcs: [
+ { id: 'chef', name: 'Chef Antonio', hobby: 'cooking' },
+ { id: 'librarian', name: 'Keeper of Books', hobby: 'reading' },
+ { id: 'artist', name: 'Painter Ana', hobby: 'painting' }
+ ],
+ specialItems: ['master_cookbook', 'ancient_tome', 'rare_pigments']
+ },
+ {
+ id: 'village_mysterious',
+ name: '??? Mystery Village',
+ position: { x: 4000, y: 4000 },
+ population: 10,
+ discovered: false,
+ hidden: true, // Only visible after completing special quest
+ npcs: [
+ { id: 'time_keeper', name: 'Keeper of Time', hobby: 'timekeeping' },
+ { id: 'oracle', name: 'Oracle of Dolina', hobby: 'prophecy' }
+ ],
+ specialItems: ['time_crystal', 'prophecy_scroll', 'reality_gem']
+ }
+ ];
+ }
+
+ /**
+ * Check and unlock new population slot
+ */
+ checkPopulationUnlocks() {
+ let newUnlocks = 0;
+
+ this.populationSlots.forEach(slot => {
+ if (!slot.unlocked && slot.requirement) {
+ if (this.meetsRequirement(slot.requirement)) {
+ slot.unlocked = true;
+ newUnlocks++;
+
+ this.game.showMessage(
+ `New population slot unlocked! (${this.population}/${this.maxPopulation})`
+ );
+ }
+ }
+ });
+
+ if (newUnlocks > 0) {
+ this.updateTownServices();
+ }
+
+ return newUnlocks;
+ }
+
+ /**
+ * Check if requirement is met
+ */
+ meetsRequirement(requirement) {
+ // Farm level
+ if (requirement.farmLevel) {
+ if (this.player.farmLevel < requirement.farmLevel) {
+ return false;
+ }
+ }
+
+ // Money
+ if (requirement.money) {
+ if (this.player.money < requirement.money) {
+ return false;
+ }
+ }
+
+ // Quest
+ if (requirement.quest) {
+ if (!this.player.hasCompletedQuest(requirement.quest)) {
+ return false;
+ }
+ }
+
+ // Building
+ if (requirement.building) {
+ if (!this.player.hasBuilding(requirement.building)) {
+ return false;
+ }
+ }
+
+ // Current population
+ if (requirement.population) {
+ if (this.population < requirement.population) {
+ return false;
+ }
+ }
+
+ // Zombie workers
+ if (requirement.zombieWorkers) {
+ const zombieCount = this.game.zombieWorkers?.getWorkerCount() || 0;
+ if (zombieCount < requirement.zombieWorkers) {
+ return false;
+ }
+ }
+
+ // Marriage
+ if (requirement.marriage) {
+ if (!this.player.isMarried) {
+ return false;
+ }
+ }
+
+ // Hearts with any NPC
+ if (requirement.hearts && requirement.npcId === 'any') {
+ const hasHighRelationship = this.game.npcs.getAllNPCs()
+ .some(npc => npc.relationshipHearts >= requirement.hearts);
+ if (!hasHighRelationship) {
+ return false;
+ }
+ }
+
+ // All buildings
+ if (requirement.allBuildings) {
+ const requiredBuildings = ['bakery', 'barbershop', 'lawyer', 'mine', 'hospital'];
+ if (!requiredBuildings.every(b => this.player.hasBuilding(b))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Invite new NPC to town
+ */
+ inviteNPC(npcId) {
+ // Find available slot
+ const availableSlot = this.populationSlots.find(
+ slot => slot.unlocked && slot.npc === null
+ );
+
+ if (!availableSlot) {
+ return {
+ success: false,
+ message: 'No available population slots!'
+ };
+ }
+
+ // Check if NPC exists
+ const npcData = this.game.npcs.getNPCData(npcId);
+ if (!npcData) {
+ return { success: false, message: 'NPC not found!' };
+ }
+
+ // Assign NPC to slot
+ availableSlot.npc = npcId;
+
+ // Spawn NPC in town
+ this.game.npcs.spawn(npcId, {
+ homeLocation: 'town',
+ moveInDate: this.game.time.currentDate
+ });
+
+ // Increase population
+ this.population++;
+
+ // Update town sign
+ this.updateTownSign();
+
+ // Check for service unlocks
+ this.updateTownServices();
+
+ this.game.showMessage(
+ `${npcData.name} moved to town! Population: ${this.population}/${this.maxPopulation}`
+ );
+
+ return { success: true, npc: npcData };
+ }
+
+ /**
+ * Update town services based on population
+ */
+ updateTownServices() {
+ let newServices = [];
+
+ Object.entries(this.services).forEach(([serviceId, service]) => {
+ if (!service.unlocked && this.population >= service.requiredPopulation) {
+ service.unlocked = true;
+ newServices.push(serviceId);
+
+ this.game.showMessage(
+ `๐๏ธ New service unlocked: ${serviceId}! (Pop: ${this.population})`
+ );
+
+ // Trigger service built event
+ this.game.emit('serviceUnlocked', {
+ serviceId: serviceId,
+ population: this.population
+ });
+ }
+ });
+
+ return newServices;
+ }
+
+ /**
+ * Update town sign display
+ */
+ updateTownSign() {
+ const signData = {
+ townName: this.townName,
+ population: this.population,
+ maxPopulation: this.maxPopulation,
+ status: this.getTownStatus(),
+ services: Object.keys(this.services).filter(s => this.services[s].unlocked).length
+ };
+
+ // Emit event to update sign sprite
+ this.game.emit('townSignUpdate', signData);
+ }
+
+ /**
+ * Get town status description
+ */
+ getTownStatus() {
+ if (this.population >= 18) {
+ return 'Thriving City';
+ }
+ if (this.population >= 15) {
+ return 'Prosperous Town';
+ }
+ if (this.population >= 10) {
+ return 'Growing Town';
+ }
+ if (this.population >= 6) {
+ return 'Small Town';
+ }
+ return 'Village';
+ }
+
+ /**
+ * Discover village
+ */
+ discoverVillage(villageId) {
+ const village = this.villages.find(v => v.id === villageId);
+
+ if (!village) {
+ return { success: false };
+ }
+
+ if (village.discovered) {
+ return {
+ success: false,
+ message: 'Village already discovered!'
+ };
+ }
+
+ // Check if hidden village requires quest
+ if (village.hidden && !this.player.hasCompletedQuest('find_mystery_village')) {
+ return {
+ success: false,
+ message: 'This village remains hidden...'
+ };
+ }
+
+ // Discover village
+ village.discovered = true;
+
+ // Spawn village NPCs
+ village.npcs.forEach(npcData => {
+ this.game.npcs.spawn(npcData.id, {
+ homeLocation: villageId,
+ position: village.position,
+ hobby: npcData.hobby
+ });
+ });
+
+ // Mark special items as available
+ village.specialItems.forEach(itemId => {
+ this.game.items.markAsDiscovered(itemId, villageId);
+ });
+
+ this.game.showMessage(
+ `Discovered ${village.name}! Population: ${village.population} NPCs`
+ );
+
+ // Achievement
+ const discoveredCount = this.villages.filter(v => v.discovered).length;
+ if (discoveredCount === this.villages.length) {
+ this.game.achievements.unlock('village_explorer');
+ }
+
+ return { success: true, village: village };
+ }
+
+ /**
+ * Get travel distance to village
+ */
+ getTravelDistance(villageId) {
+ const village = this.villages.find(v => v.id === villageId);
+ if (!village) return null;
+
+ const playerPos = this.player.getPosition();
+ const dx = village.position.x - playerPos.x;
+ const dy = village.position.y - playerPos.y;
+
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+
+ /**
+ * Get village trade options
+ */
+ getVillageTradeOptions(villageId) {
+ const village = this.villages.find(v => v.id === villageId);
+ if (!village || !village.discovered) return null;
+
+ return {
+ villageName: village.name,
+ npcs: village.npcs,
+ specialItems: village.specialItems,
+ population: village.population
+ };
+ }
+
+ /**
+ * Get town growth UI data
+ */
+ getTownGrowthUIData() {
+ return {
+ townName: this.townName,
+ population: this.population,
+ maxPopulation: this.maxPopulation,
+ status: this.getTownStatus(),
+ populationSlots: this.populationSlots,
+ availableSlots: this.populationSlots.filter(s => s.unlocked && !s.npc).length,
+ services: this.services,
+ villages: this.villages.filter(v => !v.hidden || v.discovered),
+ discoveredVillages: this.villages.filter(v => v.discovered).length,
+ totalVillages: this.villages.filter(v => !v.hidden).length
+ };
+ }
+
+ /**
+ * Update (check for new unlocks)
+ */
+ update() {
+ // Check for new population slot unlocks
+ this.checkPopulationUnlocks();
+
+ // Update town sign
+ this.updateTownSign();
+ }
+}
+
+
diff --git a/DEBUG_TOTAL_RECOVERY/TownRestorationLogic.js b/DEBUG_TOTAL_RECOVERY/TownRestorationLogic.js
new file mode 100644
index 000000000..034f7b774
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/TownRestorationLogic.js
@@ -0,0 +1,474 @@
+/**
+ * TOWN RESTORATION LOGIC SYSTEM
+ * Complete logic for restoring buildings in Mrtva Dolina
+ * Handles material requirements, construction progress, worker assignments, NPC unlocks
+ */
+
+export class TownRestorationLogic {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Building states
+ this.buildings = new Map(); //buildingId โ buildingData
+
+ // Construction queues
+ this.activeConstructions = [];
+
+ // Worker management
+ this.assignedWorkers = new Map(); // buildingId โ [workerIds]
+
+ // NPC unlock tracking
+ this.unlockedNPCs = new Set();
+
+ this.init();
+ }
+
+ init() {
+ this.initializeBuildingDatabase();
+ }
+
+ /**
+ * Initialize all 14 buildings with restoration data
+ */
+ initializeBuildingDatabase() {
+ const buildingSpecs = [
+ {
+ id: 'hospital',
+ name: 'Hospital',
+ npcUnlock: 'Ana', // Doctor Ana unlocked
+ materials: {
+ wood: 150,
+ stone: 100,
+ metal: 50,
+ tools: 20
+ },
+ constructionTime: 3600, // 1 hour
+ stages: 3,
+ benefits: ['healing', 'medical_supplies']
+ },
+ {
+ id: 'police',
+ name: 'Police Station',
+ npcUnlock: null,
+ materials: {
+ wood: 100,
+ stone: 150,
+ metal: 80,
+ weapons: 10
+ },
+ constructionTime: 3600,
+ stages: 3,
+ benefits: ['security', 'patrol_unlock']
+ },
+ {
+ id: 'mayor_office',
+ name: "Mayor's Office",
+ npcUnlock: 'ลฝupan', // Mayor unlocked
+ materials: {
+ wood: 120,
+ stone: 80,
+ metal: 40,
+ papers: 50
+ },
+ constructionTime: 2400,
+ stages: 3,
+ benefits: ['election_unlock', 'city_management']
+ },
+ {
+ id: 'tech_workshop',
+ name: 'Tech Workshop',
+ npcUnlock: 'Tehnik', // Technician unlocked
+ materials: {
+ wood: 80,
+ stone: 60,
+ metal: 150,
+ electronics: 30
+ },
+ constructionTime: 3000,
+ stages: 3,
+ benefits: ['tech_upgrades', 'electronics_crafting']
+ },
+ {
+ id: 'tailor',
+ name: 'Tailor Shop',
+ npcUnlock: 'ล ivilja', // Seamstress unlocked
+ materials: {
+ wood: 60,
+ fabric: 100,
+ thread: 50,
+ tools: 15
+ },
+ constructionTime: 1800,
+ stages: 3,
+ benefits: ['armor_crafting', 'clothing_upgrades']
+ },
+ {
+ id: 'museum',
+ name: 'Museum',
+ npcUnlock: 'Kustos', // Curator unlocked
+ materials: {
+ wood: 100,
+ stone: 120,
+ glass: 40,
+ artifacts: 10
+ },
+ constructionTime: 4800, // 80 minutes - complex restoration
+ stages: 3,
+ benefits: ['lore_unlock', 'artifact_collection']
+ },
+ {
+ id: 'school',
+ name: 'School',
+ npcUnlock: 'Teacher', // Teacher unlocked
+ materials: {
+ wood: 90,
+ stone: 70,
+ books: 50,
+ tools: 20
+ },
+ constructionTime: 3600,
+ stages: 3,
+ benefits: ['buff_unlock', 'education_system']
+ },
+ {
+ id: 'church',
+ name: 'Church',
+ npcUnlock: 'ลฝupnik', // Priest unlocked
+ materials: {
+ wood: 80,
+ stone: 200,
+ metal: 30,
+ religious_items: 5
+ },
+ constructionTime: 5400, // 90 minutes - sacred restoration
+ stages: 3,
+ benefits: ['blessing_system', 'graveyard_access']
+ },
+ {
+ id: 'blacksmith',
+ name: 'Blacksmith',
+ npcUnlock: 'Ivan Kovaฤ', // Already available
+ materials: {
+ wood: 50,
+ stone: 100,
+ metal: 120,
+ coal: 80
+ },
+ constructionTime: 3000,
+ stages: 3,
+ benefits: ['weapon_crafting', 'tool_upgrades']
+ },
+ {
+ id: 'bakery',
+ name: 'Bakery',
+ npcUnlock: 'Pek', // Baker unlocked
+ materials: {
+ wood: 70,
+ stone: 50,
+ flour: 100,
+ tools: 10
+ },
+ constructionTime: 2400,
+ stages: 3,
+ benefits: ['food_production', 'energy_bonus']
+ }
+ ];
+
+ buildingSpecs.forEach(spec => {
+ this.buildings.set(spec.id, {
+ ...spec,
+ currentState: 'ruined', // ruined, under_construction, restored
+ currentStage: 0, // 0 = ruined, 1-3 = construction stages
+ progress: 0, // 0-100% construction progress
+ workersAssigned: 0,
+ materialsPaid: false,
+ completionTime: null
+ });
+ });
+ }
+
+ /**
+ * Check if player can start building restoration
+ */
+ canStartRestoration(buildingId) {
+ const building = this.buildings.get(buildingId);
+ if (!building) return { canStart: false, reason: 'Building not found' };
+
+ if (building.currentState !== 'ruined') {
+ return { canStart: false, reason: 'Building already restored or under construction' };
+ }
+
+ // Check materials
+ const inventory = this.scene.inventorySystem;
+ for (const [material, amount] of Object.entries(building.materials)) {
+ if (!inventory.hasItem(material, amount)) {
+ return {
+ canStart: false,
+ reason: `Not enough ${material}. Need ${amount}, have ${inventory.getItemCount(material)}`
+ };
+ }
+ }
+
+ return { canStart: true };
+ }
+
+ /**
+ * Start building restoration
+ */
+ startRestoration(buildingId) {
+ const check = this.canStartRestoration(buildingId);
+ if (!check.canStart) {
+ console.warn(`Cannot start restoration: ${check.reason}`);
+ return false;
+ }
+
+ const building = this.buildings.get(buildingId);
+ const inventory = this.scene.inventorySystem;
+
+ // Deduct materials
+ for (const [material, amount] of Object.entries(building.materials)) {
+ inventory.removeItem(material, amount);
+ }
+
+ // Start construction
+ building.currentState = 'under_construction';
+ building.currentStage = 1;
+ building.progress = 0;
+ building.materialsPaid = true;
+ building.completionTime = Date.now() + (building.constructionTime * 1000);
+
+ this.activeConstructions.push(buildingId);
+
+ // Spawn scaffolding visual
+ this.spawnConstructionVisuals(buildingId);
+
+ console.log(`Started restoration of ${building.name}`);
+ return true;
+ }
+
+ /**
+ * Assign workers to speed up construction
+ */
+ assignWorker(buildingId, workerType = 'zombie') {
+ const building = this.buildings.get(buildingId);
+ if (!building || building.currentState !== 'under_construction') return false;
+
+ const speedBonus = {
+ 'zombie': 0.1, // 10% faster per zombie
+ 'human': 0.25, // 25% faster per human NPC
+ 'kai': 0.5 // Kai works fastest
+ };
+
+ building.workersAssigned++;
+
+ if (!this.assignedWorkers.has(buildingId)) {
+ this.assignedWorkers.set(buildingId, []);
+ }
+ this.assignedWorkers.get(buildingId).push({ type: workerType, bonus: speedBonus[workerType] });
+
+ console.log(`Assigned ${workerType} to ${building.name}. Speed bonus: +${speedBonus[workerType] * 100}%`);
+ return true;
+ }
+
+ /**
+ * Update construction progress
+ */
+ update(deltaTime) {
+ this.activeConstructions.forEach(buildingId => {
+ const building = this.buildings.get(buildingId);
+
+ // Calculate progress speed (base: 100% over constructionTime)
+ let baseSpeed = (100 / building.constructionTime) * (deltaTime / 1000);
+
+ // Apply worker bonuses
+ if (this.assignedWorkers.has(buildingId)) {
+ const workers = this.assignedWorkers.get(buildingId);
+ const totalBonus = workers.reduce((sum, w) => sum + w.bonus, 0);
+ baseSpeed *= (1 + totalBonus);
+ }
+
+ building.progress += baseSpeed;
+
+ // Check stage transitions
+ if (building.progress >= 33 && building.currentStage === 1) {
+ building.currentStage = 2;
+ this.updateBuildingVisual(buildingId, 2);
+ } else if (building.progress >= 66 && building.currentStage === 2) {
+ building.currentStage = 3;
+ this.updateBuildingVisual(buildingId, 3);
+ }
+
+ // Check completion
+ if (building.progress >= 100) {
+ this.completeRestoration(buildingId);
+ }
+ });
+ }
+
+ /**
+ * Complete building restoration
+ */
+ completeRestoration(buildingId) {
+ const building = this.buildings.get(buildingId);
+
+ building.currentState = 'restored';
+ building.currentStage = 3;
+ building.progress = 100;
+
+ // Remove from active constructions
+ const index = this.activeConstructions.indexOf(buildingId);
+ if (index > -1) this.activeConstructions.splice(index, 1);
+
+ // Remove scaffolding
+ this.removeConstructionVisuals(buildingId);
+
+ // Update building sprite to restored
+ this.updateBuildingVisual(buildingId, 'restored');
+
+ // VFX: Building restoration sparkles
+ this.scene.vfxSystem?.playEffect('building_restoration', building.x, building.y);
+
+ // Unlock NPC if applicable
+ if (building.npcUnlock) {
+ this.unlockNPC(building.npcUnlock);
+ }
+
+ // Grant benefits
+ this.grantBuildingBenefits(buildingId);
+
+ console.log(`โ
${building.name} restored! NPC unlocked: ${building.npcUnlock || 'None'}`);
+
+ // Quest completion check
+ this.scene.questSystem?.checkBuildingRestoration(buildingId);
+ }
+
+ /**
+ * Unlock NPC after building restoration
+ */
+ unlockNPC(npcName) {
+ if (this.unlockedNPCs.has(npcName)) return;
+
+ this.unlockedNPCs.add(npcName);
+
+ // Spawn NPC in town
+ const npcData = this.getNPCSpawnData(npcName);
+ this.scene.npcSystem?.spawnNPC(npcName, npcData.x, npcData.y);
+
+ // Notification
+ this.scene.uiSystem?.showNotification(`${npcName} has arrived in Mrtva Dolina!`, 'success');
+
+ console.log(`๐ NPC unlocked: ${npcName}`);
+ }
+
+ /**
+ * Get NPC spawn location based on their building
+ */
+ getNPCSpawnData(npcName) {
+ const spawnLocations = {
+ 'Ana': { x: 1200, y: 800 }, // Hospital
+ 'ลฝupan': { x: 1400, y: 700 }, // Mayor's Office
+ 'Tehnik': { x: 1100, y: 900 }, // Tech Workshop
+ 'ล ivilja': { x: 1300, y: 1000 }, // Tailor
+ 'Kustos': { x: 1500, y: 600 }, // Museum
+ 'Teacher': { x: 1000, y: 700 }, // School
+ 'ลฝupnik': { x: 1600, y: 500 }, // Church
+ 'Pek': { x: 900, y: 800 } // Bakery
+ };
+
+ return spawnLocations[npcName] || { x: 1200, y: 800 };
+ }
+
+ /**
+ * Grant building benefits to player
+ */
+ grantBuildingBenefits(buildingId) {
+ const building = this.buildings.get(buildingId);
+
+ building.benefits.forEach(benefit => {
+ switch (benefit) {
+ case 'healing':
+ this.scene.gameState.unlocks.healing = true;
+ break;
+ case 'security':
+ this.scene.defenseSystem?.unlockPatrols();
+ break;
+ case 'election_unlock':
+ this.scene.electionSystem?.unlockElections();
+ break;
+ case 'tech_upgrades':
+ this.scene.craftingSystem?.unlockCategory('electronics');
+ break;
+ case 'armor_crafting':
+ this.scene.craftingSystem?.unlockCategory('armor');
+ break;
+ case 'lore_unlock':
+ this.scene.gameState.unlocks.museum = true;
+ break;
+ case 'buff_unlock':
+ this.scene.schoolSystem?.enable();
+ break;
+ case 'blessing_system':
+ this.scene.churchSystem?.enable();
+ break;
+ case 'weapon_crafting':
+ this.scene.craftingSystem?.unlockCategory('weapons');
+ break;
+ case 'food_production':
+ this.scene.gameState.unlocks.bakery = true;
+ break;
+ }
+ });
+ }
+
+ /**
+ * Visual updates
+ */
+ spawnConstructionVisuals(buildingId) {
+ // Spawn scaffolding sprite
+ // Spawn worker NPCs/zombies
+ // Add construction sounds
+ }
+
+ removeConstructionVisuals(buildingId) {
+ // Remove scaffolding
+ // Remove workers
+ // Stop construction sounds
+ }
+
+ updateBuildingVisual(buildingId, stageOrState) {
+ // Update building sprite to show construction progress or restored state
+ // stages: 1 (10-30% built), 2 (30-70% built), 3 (70-100% built)
+ // 'restored' = final completed building
+ }
+
+ /**
+ * Get building restoration status
+ */
+ getBuildingStatus(buildingId) {
+ return this.buildings.get(buildingId);
+ }
+
+ /**
+ * Get all restorable buildings
+ */
+ getAllBuildings() {
+ return Array.from(this.buildings.values());
+ }
+
+ /**
+ * Get restoration progress summary
+ */
+ getRestorationProgress() {
+ const total = this.buildings.size;
+ const restored = Array.from(this.buildings.values()).filter(b => b.currentState === 'restored').length;
+ const underConstruction = this.activeConstructions.length;
+
+ return {
+ total,
+ restored,
+ underConstruction,
+ ruined: total - restored - underConstruction,
+ percentComplete: Math.round((restored / total) * 100)
+ };
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/TownRestorationSystem.js b/DEBUG_TOTAL_RECOVERY/TownRestorationSystem.js
new file mode 100644
index 000000000..489e05ffb
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/TownRestorationSystem.js
@@ -0,0 +1,403 @@
+/**
+ * TownRestorationSystem.js
+ * =========================
+ * KRVAVA ลฝETEV - Complete Town Restoration System (Phase 19)
+ *
+ * Features:
+ * - 27 towns across 18 biomes
+ * - 150+ ruined buildings
+ * - 180 total NPCs
+ * - Building restoration mechanics
+ * - NPC move-in system
+ * - Global milestones & rewards
+ * - Major city endgame project
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class TownRestorationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Town registry
+ this.towns = new Map();
+ this.buildings = new Map();
+ this.npcs = new Map();
+
+ // Progress tracking
+ this.buildingsRestored = 0;
+ this.totalBuildings = 150;
+ this.npcsRescued = 0;
+ this.totalNPCs = 180;
+ this.townsCompleted = 0;
+ this.totalTowns = 27;
+
+ // Milestones
+ this.milestones = [10, 25, 50, 100, 180];
+ this.unlockedMilestones = [];
+
+ console.log('๐๏ธ TownRestorationSystem initialized');
+
+ // Register all towns
+ this.registerTowns();
+
+ // Register Hope Valley buildings
+ this.registerHopeValleyBuildings();
+ }
+
+ /**
+ * Register all towns
+ */
+ registerTowns() {
+ // Hope Valley (Starting town - 15 buildings)
+ this.towns.set('hope_valley', {
+ id: 'hope_valley',
+ name: 'Hope Valley',
+ biome: 'grassland',
+ icon: '๐๏ธ',
+ totalBuildings: 15,
+ restoredBuildings: 0,
+ npcs: 0,
+ isUnlocked: true,
+ isCompleted: false
+ });
+
+ // Other towns (6 more buildings each = 156 total)
+ const otherTowns = [
+ { id: 'forest_grove', name: 'Forest Grove', biome: 'forest', buildings: 6 },
+ { id: 'desert_oasis', name: 'Desert Oasis', biome: 'desert', buildings: 6 },
+ { id: 'frozen_harbor', name: 'Frozen Harbor', biome: 'frozen', buildings: 6 },
+ { id: 'volcanic_refuge', name: 'Volcanic Refuge', biome: 'volcanic', buildings: 6 },
+ { id: 'coastal_bay', name: 'Coastal Bay', biome: 'beach', buildings: 6 },
+ { id: 'mountain_peak', name: 'Mountain Peak', biome: 'mountain', buildings: 6 },
+ { id: 'swamp_village', name: 'Swamp Village', biome: 'swamp', buildings: 6 },
+ { id: 'crystal_city', name: 'Crystal City', biome: 'crystal', buildings: 8 },
+ { id: 'atlantis', name: 'Atlantis', biome: 'underwater', buildings: 10 },
+ // ... (27 total towns)
+ ];
+
+ otherTowns.forEach(town => {
+ this.towns.set(town.id, {
+ id: town.id,
+ name: town.name,
+ biome: town.biome,
+ icon: '๐๏ธ',
+ totalBuildings: town.buildings,
+ restoredBuildings: 0,
+ npcs: 0,
+ isUnlocked: false,
+ isCompleted: false
+ });
+ });
+
+ console.log(`โ
Registered ${this.towns.size} towns`);
+ }
+
+ /**
+ * Register Hope Valley buildings (15)
+ */
+ registerHopeValleyBuildings() {
+ const buildings = [
+ // Essential (NPCs' homes)
+ { id: 'ivan_house', name: "Ivan's House", npc: 'Ivan', materials: { wood: 100, stone: 50 }, time: 3 },
+ { id: 'marija_house', name: "Marija's House", npc: 'Marija', materials: { wood: 100, stone: 50 }, time: 3 },
+ { id: 'jakob_shop', name: "Jakob's Shop", npc: 'Jakob', materials: { wood: 150, iron: 30 }, time: 4 },
+ { id: 'dr_chen_clinic', name: "Dr. Chen's Clinic", npc: 'Dr. Chen', materials: { wood: 200, stone: 100 }, time: 5 },
+ { id: 'lena_bakery', name: "Lena's Bakery", npc: 'Lena', materials: { wood: 120, stone: 60 }, time: 4 },
+
+ // Community buildings
+ { id: 'town_hall', name: 'Town Hall', materials: { wood: 500, stone: 300, iron: 100 }, time: 10 },
+ { id: 'community_center', name: 'Community Center', materials: { wood: 400, stone: 250 }, time: 8 },
+ { id: 'library', name: 'Library', materials: { wood: 300, stone: 150 }, time: 6 },
+ { id: 'school', name: 'School', materials: { wood: 350, stone: 200 }, time: 7 },
+ { id: 'guard_tower', name: 'Guard Tower', materials: { stone: 400, iron: 150 }, time: 8 },
+
+ // Utility buildings
+ { id: 'warehouse', name: 'Warehouse', materials: { wood: 250, stone: 150 }, time: 5 },
+ { id: 'water_tower', name: 'Water Tower', materials: { stone: 300, iron: 100 }, time: 6 },
+ { id: 'power_station', name: 'Power Station', materials: { stone: 400, iron: 200, wire: 50 }, time: 9 },
+ { id: 'market', name: 'Market Square', materials: { wood: 200, stone: 100 }, time: 5 },
+ { id: 'fountain', name: 'Town Fountain', materials: { stone: 150, marble: 50 }, time: 4 }
+ ];
+
+ buildings.forEach(building => {
+ this.buildings.set(building.id, {
+ ...building,
+ town: 'hope_valley',
+ isRestored: false,
+ progress: 0,
+ workers: [],
+ startTime: null
+ });
+ });
+
+ console.log(`โ
Registered ${buildings.length} Hope Valley buildings`);
+ }
+
+ /**
+ * Start building restoration
+ */
+ startRestoration(buildingId, zombieWorkers = []) {
+ const building = this.buildings.get(buildingId);
+ if (!building) {
+ console.error(`Building ${buildingId} not found!`);
+ return false;
+ }
+
+ if (building.isRestored) {
+ console.log(`${building.name} is already restored!`);
+ return false;
+ }
+
+ // Check materials
+ if (!this.hasMaterials(building.materials)) {
+ console.log(`Not enough materials for ${building.name}!`);
+ return false;
+ }
+
+ // Consume materials
+ this.consumeMaterials(building.materials);
+
+ // Assign workers
+ building.workers = zombieWorkers;
+ building.startTime = Date.now();
+ building.progress = 0;
+
+ // Calculate construction time (base time reduced by workers)
+ const workerBonus = zombieWorkers.length * 0.2; // 20% per worker
+ const constructionTime = building.time * (1 - workerBonus);
+
+ console.log(`๐๏ธ Started restoring ${building.name} (${constructionTime} days with ${zombieWorkers.length} workers)`);
+
+ // Auto-complete after time
+ setTimeout(() => {
+ this.completeRestoration(buildingId);
+ }, constructionTime * 1000 * 60); // Convert to ms (for demo, 1 day = 1 min)
+
+ this.showNotification({
+ title: 'Restoration Started!',
+ text: `๐๏ธ ${building.name} - ${constructionTime} days`,
+ icon: '๐จ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Complete building restoration
+ */
+ completeRestoration(buildingId) {
+ const building = this.buildings.get(buildingId);
+ if (!building) return;
+
+ building.isRestored = true;
+ building.progress = 100;
+ this.buildingsRestored++;
+
+ console.log(`โ
${building.name} restored! (${this.buildingsRestored}/${this.totalBuildings})`);
+
+ // Move in NPC if building has one
+ if (building.npc) {
+ this.moveInNPC(building.npc, building.town);
+ }
+
+ // Update town progress
+ this.updateTownProgress(building.town);
+
+ // Check milestones
+ this.checkMilestones();
+
+ // Update visuals in the current scene
+ if (this.scene.updateBuildingVisuals) {
+ this.scene.updateBuildingVisuals(buildingId);
+ }
+
+ this.showNotification({
+ title: 'Building Complete!',
+ text: `โ
${building.name} restored!`,
+ icon: '๐๏ธ'
+ });
+ }
+
+ /**
+ * Move in NPC
+ */
+ moveInNPC(npcName, townId) {
+ const npc = {
+ name: npcName,
+ town: townId,
+ movedInDate: Date.now(),
+ hearts: 0,
+ giftsGiven: 0,
+ questsCompleted: 0
+ };
+
+ this.npcs.set(npcName, npc);
+ this.npcsRescued++;
+
+ console.log(`๐ค ${npcName} moved into ${townId}! (${this.npcsRescued}/${this.totalNPCs})`);
+
+ // Update town NPC count
+ const town = this.towns.get(townId);
+ if (town) {
+ town.npcs++;
+ }
+
+ this.showNotification({
+ title: 'NPC Moved In!',
+ text: `๐ค ${npcName} is now living in town!`,
+ icon: '๐ '
+ });
+ }
+
+ /**
+ * Update town progress
+ */
+ updateTownProgress(townId) {
+ const town = this.towns.get(townId);
+ if (!town) return;
+
+ // Count restored buildings in this town
+ const townBuildings = Array.from(this.buildings.values())
+ .filter(b => b.town === townId);
+ const restored = townBuildings.filter(b => b.isRestored).length;
+
+ town.restoredBuildings = restored;
+
+ // Check if town is complete
+ if (restored === town.totalBuildings) {
+ town.isCompleted = true;
+ this.townsCompleted++;
+
+ console.log(`๐ ${town.name} 100% COMPLETE! (${this.townsCompleted}/${this.totalTowns})`);
+
+ this.showNotification({
+ title: 'TOWN COMPLETE!',
+ text: `๐ ${town.name} fully restored!`,
+ icon: '๐'
+ });
+ }
+ }
+
+ /**
+ * Check milestones
+ */
+ checkMilestones() {
+ this.milestones.forEach(milestone => {
+ if (this.npcsRescued >= milestone && !this.unlockedMilestones.includes(milestone)) {
+ this.unlockMilestone(milestone);
+ }
+ });
+ }
+
+ /**
+ * Unlock milestone
+ */
+ unlockMilestone(milestone) {
+ this.unlockedMilestones.push(milestone);
+
+ console.log(`๐ MILESTONE: ${milestone} NPCs rescued!`);
+
+ // Grant rewards
+ const rewards = this.getMilestoneReward(milestone);
+
+ this.showNotification({
+ title: `MILESTONE: ${milestone} NPCs!`,
+ text: `๐ Unlocked: ${rewards.text}`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Get milestone reward
+ */
+ getMilestoneReward(milestone) {
+ const rewards = {
+ 10: { text: 'Community Center unlocked!', feature: 'community_center' },
+ 25: { text: 'Town Festivals enabled!', feature: 'festivals' },
+ 50: { text: 'Town Guard system!', feature: 'town_guard' },
+ 100: { text: 'Major City project unlocked!', feature: 'major_city' },
+ 180: { text: 'UTOPIA ENDING unlocked!', feature: 'utopia_ending' }
+ };
+
+ return rewards[milestone] || { text: 'Bonus unlocked!' };
+ }
+
+ hasMaterials(materials) {
+ if (!this.scene.inventorySystem) return true; // Safety fallback
+
+ for (const [item, count] of Object.entries(materials)) {
+ if (!this.scene.inventorySystem.hasItem(item, count)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ consumeMaterials(materials) {
+ if (!this.scene.inventorySystem) return;
+
+ for (const [item, count] of Object.entries(materials)) {
+ this.scene.inventorySystem.removeItem(item, count);
+ }
+ console.log('๐ฆ Materials consumed:', materials);
+ }
+
+ /**
+ * Get restoration progress
+ */
+ getProgress() {
+ return {
+ buildings: {
+ restored: this.buildingsRestored,
+ total: this.totalBuildings,
+ percentage: ((this.buildingsRestored / this.totalBuildings) * 100).toFixed(1)
+ },
+ npcs: {
+ rescued: this.npcsRescued,
+ total: this.totalNPCs,
+ percentage: ((this.npcsRescued / this.totalNPCs) * 100).toFixed(1)
+ },
+ towns: {
+ completed: this.townsCompleted,
+ total: this.totalTowns,
+ percentage: ((this.townsCompleted / this.totalTowns) * 100).toFixed(1)
+ },
+ milestones: this.unlockedMilestones
+ };
+ }
+
+ /**
+ * Get town info
+ */
+ getTownInfo(townId) {
+ return this.towns.get(townId);
+ }
+
+ /**
+ * Get all towns
+ */
+ getAllTowns() {
+ return Array.from(this.towns.values());
+ }
+
+ /**
+ * Get building info
+ */
+ getBuildingInfo(buildingId) {
+ return this.buildings.get(buildingId);
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/TwinBondSystem.js b/DEBUG_TOTAL_RECOVERY/TwinBondSystem.js
new file mode 100644
index 000000000..fb7fd64db
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/TwinBondSystem.js
@@ -0,0 +1,432 @@
+/**
+ * TwinBondSystem.js
+ * =================
+ * KRVAVA ลฝETEV - Twin Bond Mechanic (Kai โ Ana Connection)
+ *
+ * Core Concept:
+ * Kai and Ana share a psychic bond through the Alfa virus
+ * As twins, they can:
+ * - Feel each other's emotions
+ * - Sense each other's location (vaguely)
+ * - Communicate telepathically (limited)
+ * - Share HP/stamina in emergencies
+ *
+ * Features:
+ * - Bond Strength meter (0-100)
+ * - Telepathic messages from Ana
+ * - Direction to Ana indicator
+ * - Twin abilities (heal twin, boost twin, swap positions)
+ * - Bond events (visions, flashbacks)
+ * - Ana's status tracking (health, danger level)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class TwinBondSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Bond state
+ this.bondStrength = 75; // Starts strong (0-100)
+ this.maxBondStrength = 100;
+
+ // Ana's state (unknown location)
+ this.anaState = {
+ alive: true,
+ health: 100,
+ dangerLevel: 0, // 0 = safe, 100 = critical
+ distance: 5000, // pixels from Kai (initially far)
+ direction: { x: 1000, y: 1000 }, // General direction
+ lastMessage: null,
+ messageTime: null
+ };
+
+ // Bond abilities
+ this.abilities = {
+ telepathy: { unlocked: true, cooldown: 0, maxCooldown: 30000 }, // 30s
+ sensePulse: { unlocked: true, cooldown: 0, maxCooldown: 60000 }, // 1min
+ emergencyLink: { unlocked: false, cooldown: 0, maxCooldown: 300000 }, // 5min
+ twinRecall: { unlocked: false, cooldown: 0, maxCooldown: 600000 } // 10min
+ };
+
+ // Messages from Ana (telepathic)
+ this.messageQueue = [];
+ this.lastMessageTime = 0;
+
+ // UI elements
+ this.bondUI = null;
+
+ // Events
+ this.bondEvents = this.defineBondEvents();
+ this.nextEventTime = Date.now() + 60000; // First event in 1 minute
+
+ console.log('๐ TwinBondSystem initialized - Bond Strength:', this.bondStrength);
+ }
+
+ /**
+ * Update bond strength based on actions
+ */
+ update(delta) {
+ const deltaSeconds = delta / 1000;
+
+ // Passive bond decay (small)
+ this.bondStrength = Math.max(0, this.bondStrength - 0.01 * deltaSeconds);
+
+ // Update ability cooldowns
+ for (const ability in this.abilities) {
+ if (this.abilities[ability].cooldown > 0) {
+ this.abilities[ability].cooldown -= delta;
+ }
+ }
+
+ // Check for random bond events
+ if (Date.now() > this.nextEventTime) {
+ this.triggerRandomBondEvent();
+ this.nextEventTime = Date.now() + Phaser.Math.Between(60000, 180000); // 1-3 min
+ }
+
+ // Update Ana's danger level based on story progress
+ this.updateAnaDanger(deltaSeconds);
+ }
+
+ /**
+ * Define bond events (telepathic visions)
+ */
+ defineBondEvents() {
+ return [
+ {
+ id: 'first_contact',
+ trigger: 'auto',
+ condition: null,
+ message: 'Kai... can you hear me? I\'m... somewhere dark...',
+ emotion: 'worried',
+ bondChange: +5
+ },
+ {
+ id: 'danger_warning',
+ trigger: 'danger_high',
+ condition: () => this.anaState.dangerLevel > 70,
+ message: 'Brother! They\'re coming for me! Please hurry!',
+ emotion: 'fear',
+ bondChange: -10
+ },
+ {
+ id: 'memory_flash',
+ trigger: 'random',
+ condition: null,
+ message: 'Remember when we first discovered the Alfa strain? We were so hopeful...',
+ emotion: 'sad',
+ bondChange: +3
+ },
+ {
+ id: 'location_hint',
+ trigger: 'sense_pulse',
+ condition: null,
+ message: 'I can feel concrete walls... cold metal... some kind of facility?',
+ emotion: 'neutral',
+ bondChange: +5
+ },
+ {
+ id: 'encouragement',
+ trigger: 'random',
+ condition: () => this.scene.player?.hp < 50,
+ message: 'Stay strong, Kai! I believe in you!',
+ emotion: 'determined',
+ bondChange: +10
+ }
+ ];
+ }
+
+ /**
+ * Trigger a random bond event
+ */
+ triggerRandomBondEvent() {
+ const randomEvents = this.bondEvents.filter(e => e.trigger === 'random');
+
+ if (randomEvents.length === 0) return;
+
+ const event = Phaser.Utils.Array.GetRandom(randomEvents);
+
+ // Check condition
+ if (event.condition && !event.condition()) {
+ return;
+ }
+
+ this.showTelepathicMessage(event.message, event.emotion);
+ this.changeBondStrength(event.bondChange);
+ }
+
+ /**
+ * Show telepathic message from Ana
+ */
+ showTelepathicMessage(message, emotion = 'neutral') {
+ console.log(`๐ญ Twin Bond Message: ${message}`);
+
+ // Update Ana's last message
+ this.anaState.lastMessage = message;
+ this.anaState.messageTime = Date.now();
+
+ // Show in dialogue system
+ if (this.scene.dialogueSystem) {
+ const anaData = {
+ name: 'Ana (Twin Bond)',
+ id: 'ana_telepathy'
+ };
+
+ // Create temporary dialogue
+ const telepathyDialogue = {
+ root: 'message',
+ nodes: {
+ 'message': {
+ speaker: 'Ana (Twin Bond)',
+ emotion: emotion,
+ text: message,
+ next: null // Auto-close
+ }
+ }
+ };
+
+ this.scene.dialogueSystem.registerDialogue('telepathy_' + Date.now(), telepathyDialogue);
+ this.scene.dialogueSystem.startDialogue('telepathy_' + Date.now(), anaData);
+ }
+
+ // Visual effect (bond pulse)
+ this.scene.cameras.main.flash(500, 150, 100, 255, false);
+
+ // Bond strength change
+ this.showBondPulse();
+ }
+
+ /**
+ * Change bond strength
+ */
+ changeBondStrength(amount) {
+ this.bondStrength = Phaser.Math.Clamp(
+ this.bondStrength + amount,
+ 0,
+ this.maxBondStrength
+ );
+
+ console.log(`๐ Bond Strength: ${this.bondStrength.toFixed(1)}% (${amount > 0 ? '+' : ''}${amount})`);
+
+ // Notify player
+ if (amount > 0) {
+ this.scene.events.emit('bondStrengthened', { strength: this.bondStrength });
+ } else {
+ this.scene.events.emit('bondWeakened', { strength: this.bondStrength });
+ }
+ }
+
+ /**
+ * Visual bond pulse effect
+ */
+ showBondPulse() {
+ // TODO: Create particle effect at player position
+ console.log('๐ซ Bond pulse visualization');
+ }
+
+ /**
+ * Ability: Telepathy (send message to Ana)
+ */
+ useTelepathy(message) {
+ if (!this.abilities.telepathy.unlocked) {
+ console.log('โ Telepathy not unlocked');
+ return false;
+ }
+
+ if (this.abilities.telepathy.cooldown > 0) {
+ console.log('โธ๏ธ Telepathy on cooldown');
+ return false;
+ }
+
+ console.log(`๐ก Sending to Ana: ${message}`);
+
+ // Ana responds after delay
+ this.scene.time.delayedCall(2000, () => {
+ const responses = [
+ "I heard you! Keep searching!",
+ "Kai... I'm trying to stay strong...",
+ "They don't know about our bond. Use that!",
+ "I can feel you getting closer!"
+ ];
+
+ const response = Phaser.Utils.Array.GetRandom(responses);
+ this.showTelepathicMessage(response, 'determined');
+ });
+
+ // Set cooldown
+ this.abilities.telepathy.cooldown = this.abilities.telepathy.maxCooldown;
+ this.changeBondStrength(+2); // Strengthen bond
+
+ return true;
+ }
+
+ /**
+ * Ability: Sense Pulse (detect Ana's direction)
+ */
+ useSensePulse() {
+ if (!this.abilities.sensePulse.unlocked) {
+ console.log('โ Sense Pulse not unlocked');
+ return null;
+ }
+
+ if (this.abilities.sensePulse.cooldown > 0) {
+ console.log('โธ๏ธ Sense Pulse on cooldown');
+ return null;
+ }
+
+ console.log('๐ Sensing Ana\'s location...');
+
+ // Calculate general direction
+ const playerX = this.scene.player?.x || 0;
+ const playerY = this.scene.player?.y || 0;
+
+ const angle = Phaser.Math.Angle.Between(
+ playerX, playerY,
+ this.anaState.direction.x, this.anaState.direction.y
+ );
+
+ const distance = this.anaState.distance;
+
+ // Show visual indicator
+ this.showDirectionIndicator(angle, distance);
+
+ // Set cooldown
+ this.abilities.sensePulse.cooldown = this.abilities.sensePulse.maxCooldown;
+ this.changeBondStrength(+5);
+
+ return {
+ angle: angle,
+ distance: distance,
+ distanceCategory: this.getDistanceCategory(distance)
+ };
+ }
+
+ /**
+ * Get distance category (for vague communication)
+ */
+ getDistanceCategory(distance) {
+ if (distance < 500) return 'very_close';
+ if (distance < 1500) return 'close';
+ if (distance < 3000) return 'far';
+ return 'very_far';
+ }
+
+ /**
+ * Show direction indicator
+ */
+ showDirectionIndicator(angle, distance) {
+ const category = this.getDistanceCategory(distance);
+ const messages = {
+ 'very_close': 'Ana is VERY CLOSE! โฌ๏ธ',
+ 'close': 'Ana is nearby ๐',
+ 'far': 'Ana is far away ๐ญ',
+ 'very_far': 'Ana is very far ๐'
+ };
+
+ console.log(`๐ ${messages[category]} (${Math.round(distance)}px)`);
+
+ // TODO: Show UI arrow pointing in direction
+ }
+
+ /**
+ * Update Ana's danger level
+ */
+ updateAnaDanger(deltaSeconds) {
+ // Danger level increases over time (captors getting desperate)
+ if (this.anaState.alive) {
+ this.anaState.dangerLevel = Math.min(
+ 100,
+ this.anaState.dangerLevel + 0.1 * deltaSeconds
+ );
+
+ // Trigger danger events
+ if (this.anaState.dangerLevel > 70 && Math.random() < 0.01) {
+ const dangerEvent = this.bondEvents.find(e => e.id === 'danger_warning');
+ if (dangerEvent) {
+ this.showTelepathicMessage(dangerEvent.message, dangerEvent.emotion);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update Ana's position (for story progression)
+ */
+ updateAnaLocation(x, y, distance) {
+ this.anaState.direction.x = x;
+ this.anaState.direction.y = y;
+ this.anaState.distance = distance;
+
+ console.log(`๐ Ana's location updated: (${x}, ${y}), distance: ${distance}px`);
+ }
+
+ /**
+ * Create Twin Bond UI
+ */
+ createBondUI() {
+ const width = this.scene.cameras.main.width;
+
+ // Bond meter (top-left)
+ const x = 20;
+ const y = 120;
+
+ // Background
+ const bg = this.scene.add.rectangle(x, y, 200, 40, 0x2d1b00, 0.8);
+ bg.setOrigin(0, 0);
+ bg.setScrollFactor(0);
+ bg.setDepth(100);
+
+ // Title
+ const title = this.scene.add.text(x + 10, y + 5, '๐ Twin Bond', {
+ fontSize: '14px',
+ fontFamily: 'Georgia, serif',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ title.setScrollFactor(0);
+ title.setDepth(100);
+
+ // Bond bar
+ const barBg = this.scene.add.rectangle(x + 10, y + 25, 180, 8, 0x000000, 0.8);
+ barBg.setOrigin(0, 0);
+ barBg.setScrollFactor(0);
+ barBg.setDepth(100);
+
+ const barFill = this.scene.add.rectangle(
+ x + 10, y + 25,
+ 180 * (this.bondStrength / 100),
+ 8,
+ 0xFF69B4,
+ 1
+ );
+ barFill.setOrigin(0, 0);
+ barFill.setScrollFactor(0);
+ barFill.setDepth(100);
+
+ this.bondUI = { bg, title, barBg, barFill };
+
+ // Update bar every frame
+ this.scene.events.on('update', () => {
+ if (this.bondUI && this.bondUI.barFill) {
+ this.bondUI.barFill.width = 180 * (this.bondStrength / 100);
+ }
+ });
+ }
+
+ /**
+ * Getters
+ */
+ getBondStrength() {
+ return this.bondStrength;
+ }
+
+ getAnaStatus() {
+ return this.anaState;
+ }
+
+ isAnaSafe() {
+ return this.anaState.dangerLevel < 50;
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/TwinBondUISystem.js b/DEBUG_TOTAL_RECOVERY/TwinBondUISystem.js
new file mode 100644
index 000000000..487ce1c6c
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/TwinBondUISystem.js
@@ -0,0 +1,334 @@
+/**
+ * TwinBondUISystem.js
+ * ====================
+ * KRVAVA ลฝETEV - Twin Bond Heartbeat UI
+ *
+ * Features:
+ * - Visual heartbeat indicator on screen
+ * - Speeds up when near Ana's clues
+ * - Purple glow effect
+ * - Distance-based intensity
+ * - Twin bond strength indicator
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-25
+ */
+
+export default class TwinBondUISystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // UI elements
+ this.heartContainer = null;
+ this.heartIcon = null;
+ this.glowEffect = null;
+ this.bondStrengthBar = null;
+
+ // State
+ this.baseHeartRate = 60; // bpm
+ this.currentHeartRate = 60;
+ this.bondStrength = 0; // 0-100
+ this.nearestClueDistance = Infinity;
+
+ // Animation
+ this.heartbeatTween = null;
+ this.lastBeat = 0;
+
+ console.log('๐ TwinBondUISystem initialized');
+
+ this.createUI();
+ this.startHeartbeat();
+ }
+
+ /**
+ * Create UI elements
+ */
+ createUI() {
+ const ui = this.scene.scene.get('UIScene') || this.scene;
+
+ // Container (top-left corner)
+ this.heartContainer = ui.add.container(50, 50);
+ this.heartContainer.setDepth(1000); // Above everything
+ this.heartContainer.setScrollFactor(0); // Fixed to camera
+
+ // Purple glow effect (behind heart)
+ this.glowEffect = ui.add.circle(0, 0, 25, 0x9370DB, 0);
+ this.heartContainer.add(this.glowEffect);
+
+ // Heart icon (emoji-style)
+ this.heartIcon = ui.add.text(0, 0, '๐', {
+ fontSize: '32px',
+ fontFamily: 'Arial'
+ });
+ this.heartIcon.setOrigin(0.5);
+ this.heartContainer.add(this.heartIcon);
+
+ // Bond strength bar (below heart)
+ const barWidth = 50;
+ const barHeight = 5;
+
+ // Background bar
+ const barBg = ui.add.rectangle(0, 30, barWidth, barHeight, 0x333333);
+ this.heartContainer.add(barBg);
+
+ // Strength bar
+ this.bondStrengthBar = ui.add.rectangle(
+ -barWidth / 2,
+ 30,
+ 0, // Start at 0 width
+ barHeight,
+ 0x9370DB
+ );
+ this.bondStrengthBar.setOrigin(0, 0.5);
+ this.heartContainer.add(this.bondStrengthBar);
+
+ // Bond strength text
+ this.bondStrengthText = ui.add.text(0, 45, '0%', {
+ fontSize: '10px',
+ fontFamily: 'Arial',
+ color: '#9370DB'
+ });
+ this.bondStrengthText.setOrigin(0.5);
+ this.heartContainer.add(this.bondStrengthText);
+
+ console.log('โ
Twin Bond UI created');
+ }
+
+ /**
+ * Start heartbeat animation
+ */
+ startHeartbeat() {
+ const beatInterval = () => {
+ const bpm = this.currentHeartRate;
+ const msPerBeat = 60000 / bpm; // Convert BPM to ms
+
+ // Heart pump animation
+ if (this.heartIcon) {
+ this.scene.tweens.add({
+ targets: [this.heartIcon],
+ scale: { from: 1, to: 1.3 },
+ duration: 100,
+ yoyo: true,
+ ease: 'Quad.Out'
+ });
+
+ // Glow pulse
+ this.scene.tweens.add({
+ targets: [this.glowEffect],
+ alpha: { from: 0, to: 0.6 },
+ scale: { from: 1, to: 1.5 },
+ duration: 150,
+ yoyo: true,
+ ease: 'Quad.Out'
+ });
+ }
+
+ // Schedule next beat
+ this.heartbeatTimer = setTimeout(() => beatInterval(), msPerBeat);
+ };
+
+ // Start beating
+ beatInterval();
+ }
+
+ /**
+ * Update system (called every frame)
+ */
+ update(time, delta) {
+ if (!this.scene.player) return;
+
+ // Calculate distance to nearest Ana clue
+ this.calculateNearestClue();
+
+ // Update heart rate based on distance
+ this.updateHeartRate();
+
+ // Update bond strength display
+ this.updateBondStrengthDisplay();
+ }
+
+ /**
+ * Calculate distance to nearest Ana's clue
+ */
+ calculateNearestClue() {
+ if (!this.scene.anaClueSystem) {
+ this.nearestClueDistance = Infinity;
+ return;
+ }
+
+ const playerX = this.scene.player.sprite.x;
+ const playerY = this.scene.player.sprite.y;
+
+ let minDistance = Infinity;
+
+ // Check all clues
+ this.scene.anaClueSystem.clueLocations.forEach((position, clueId) => {
+ // Skip already discovered clues
+ if (this.scene.anaClueSystem.discovered.has(clueId)) return;
+
+ const dx = position.x - playerX;
+ const dy = position.y - playerY;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance < minDistance) {
+ minDistance = distance;
+ }
+ });
+
+ this.nearestClueDistance = minDistance;
+ }
+
+ /**
+ * Update heart rate based on proximity to clues
+ */
+ updateHeartRate() {
+ const distance = this.nearestClueDistance;
+
+ // Distance thresholds (in pixels)
+ const veryClose = 200; // 4-5 blocks
+ const close = 480; // 10 blocks
+ const medium = 960; // 20 blocks
+
+ let targetHeartRate = this.baseHeartRate;
+
+ if (distance < veryClose) {
+ // VERY CLOSE - Rapid heartbeat!
+ targetHeartRate = 120; // 2x normal
+ this.bondStrength = 100;
+ } else if (distance < close) {
+ // CLOSE - Fast heartbeat
+ targetHeartRate = 90;
+ this.bondStrength = 75;
+ } else if (distance < medium) {
+ // MEDIUM - Slightly elevated
+ targetHeartRate = 75;
+ this.bondStrength = 50;
+ } else {
+ // FAR - Normal heartbeat
+ targetHeartRate = 60;
+ this.bondStrength = Math.max(0, this.bondStrength - 1); // Slowly decrease
+ }
+
+ // Smooth transition
+ this.currentHeartRate += (targetHeartRate - this.currentHeartRate) * 0.1;
+
+ // Clamp
+ this.currentHeartRate = Phaser.Math.Clamp(this.currentHeartRate, 30, 150);
+ }
+
+ /**
+ * Update bond strength visual display
+ */
+ updateBondStrengthDisplay() {
+ if (!this.bondStrengthBar) return;
+
+ // Update bar width
+ const maxWidth = 50;
+ const targetWidth = (this.bondStrength / 100) * maxWidth;
+ this.bondStrengthBar.width = targetWidth;
+
+ // Update text
+ if (this.bondStrengthText) {
+ this.bondStrengthText.setText(`${Math.floor(this.bondStrength)}%`);
+ }
+
+ // Color gradient based on strength
+ if (this.bondStrength > 75) {
+ this.bondStrengthBar.setFillStyle(0xFF69B4); // Hot pink - very strong
+ } else if (this.bondStrength > 50) {
+ this.bondStrengthBar.setFillStyle(0x9370DB); // Medium purple
+ } else if (this.bondStrength > 25) {
+ this.bondStrengthBar.setFillStyle(0x6A5ACD); // Slate blue
+ } else {
+ this.bondStrengthBar.setFillStyle(0x483D8B); // Dark slate blue
+ }
+
+ // Pulsing glow when strong bond
+ if (this.bondStrength > 75 && this.glowEffect) {
+ this.glowEffect.setAlpha(0.3 + Math.sin(Date.now() / 200) * 0.2);
+ }
+ }
+
+ /**
+ * Trigger special bond moment (e.g., flashback)
+ */
+ triggerBondMoment(intensity = 'medium') {
+ console.log(`๐ Twin Bond Moment! Intensity: ${intensity}`);
+
+ // Screen flash
+ this.scene.cameras.main.flash(500, 147, 112, 219, false); // Purple flash
+
+ // Heart explosion effect
+ if (this.heartIcon) {
+ this.scene.tweens.add({
+ targets: [this.heartIcon],
+ scale: { from: 1, to: 2 },
+ alpha: { from: 1, to: 0 },
+ duration: 800,
+ ease: 'Quad.Out',
+ onComplete: () => {
+ this.heartIcon.setScale(1);
+ this.heartIcon.setAlpha(1);
+ }
+ });
+ }
+
+ // Glow burst
+ if (this.glowEffect) {
+ this.scene.tweens.add({
+ targets: [this.glowEffect],
+ scale: { from: 1, to: 5 },
+ alpha: { from: 0.8, to: 0 },
+ duration: 1000,
+ ease: 'Quad.Out',
+ onComplete: () => {
+ this.glowEffect.setScale(1);
+ this.glowEffect.setAlpha(0);
+ }
+ });
+ }
+
+ // Temporary heart rate spike
+ this.currentHeartRate = 150;
+ setTimeout(() => {
+ this.currentHeartRate = this.baseHeartRate;
+ }, 3000);
+
+ // Bond strength boost
+ this.bondStrength = 100;
+
+ // Show notification
+ this.scene.events.emit('show-notification', {
+ title: 'Twin Bond Activated!',
+ text: '๐ You feel Ana\'s presence intensify!',
+ icon: '๐',
+ color: '#9370DB'
+ });
+ }
+
+ /**
+ * Get current heart rate (for debugging)
+ */
+ getCurrentHeartRate() {
+ return Math.floor(this.currentHeartRate);
+ }
+
+ /**
+ * Get bond strength (for other systems)
+ */
+ getBondStrength() {
+ return this.bondStrength;
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ if (this.heartbeatTimer) {
+ clearTimeout(this.heartbeatTimer);
+ }
+ if (this.heartContainer) {
+ this.heartContainer.destroy();
+ }
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/VisualEffectsSystem.js b/DEBUG_TOTAL_RECOVERY/VisualEffectsSystem.js
new file mode 100644
index 000000000..b424765a5
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/VisualEffectsSystem.js
@@ -0,0 +1,128 @@
+/**
+ * VISUAL EFFECTS SYSTEM
+ * Handles juice effects: screenshake, particles, lighting
+ */
+class VisualEffectsSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.camera = scene.cameras.main;
+ this.isShaking = false;
+ }
+
+ /**
+ * Screenshake effect
+ * @param {number} intensity - Shake strength (default 0.005)
+ * @param {number} duration - Duration in ms (default 300)
+ */
+ screenshake(intensity = 0.005, duration = 300) {
+ if (this.isShaking) return;
+
+ this.isShaking = true;
+ this.camera.shake(duration, intensity);
+
+ this.scene.time.delayedCall(duration, () => {
+ this.isShaking = false;
+ });
+ }
+
+ /**
+ * Hit particles (sparks)
+ */
+ createHitParticles(x, y, color = 0xFFFFFF) {
+ const particles = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 100, max: 200 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 0.5, end: 0 },
+ lifespan: 300,
+ quantity: 8,
+ tint: color
+ });
+
+ this.scene.time.delayedCall(500, () => {
+ particles.destroy();
+ });
+ }
+
+ /**
+ * Explosion particles
+ */
+ createExplosion(x, y, color = 0xFF4444) {
+ const particles = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 200, max: 400 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 1, end: 0 },
+ lifespan: 600,
+ quantity: 20,
+ tint: color,
+ gravityY: 300
+ });
+
+ this.scene.time.delayedCall(800, () => {
+ particles.destroy();
+ });
+ }
+
+ /**
+ * Dust particles (for movement)
+ */
+ createDustPuff(x, y) {
+ const particles = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 30, max: 60 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 0.3, end: 0 },
+ lifespan: 400,
+ quantity: 5,
+ tint: 0xCCBBAA,
+ alpha: { start: 0.5, end: 0 }
+ });
+
+ this.scene.time.delayedCall(500, () => {
+ particles.destroy();
+ });
+ }
+
+ /**
+ * Flash effect (for damage, powerups)
+ */
+ flash(color = 0xFFFFFF, duration = 100) {
+ this.camera.flash(duration, ...this.hexToRGB(color));
+ }
+
+ /**
+ * Fade effect
+ */
+ fadeOut(duration = 1000, callback) {
+ this.camera.fadeOut(duration, 0, 0, 0);
+ if (callback) {
+ this.scene.time.delayedCall(duration, callback);
+ }
+ }
+
+ fadeIn(duration = 1000) {
+ this.camera.fadeIn(duration, 0, 0, 0);
+ }
+
+ /**
+ * Utility: Hex to RGB
+ */
+ hexToRGB(hex) {
+ return [
+ (hex >> 16) & 255,
+ (hex >> 8) & 255,
+ hex & 255
+ ];
+ }
+
+ /**
+ * Create simple white pixel texture for particles
+ */
+ static createParticleTexture(scene) {
+ if (scene.textures.exists('particle_white')) return;
+
+ const graphics = scene.make.graphics({ x: 0, y: 0, add: false });
+ graphics.fillStyle(0xFFFFFF, 1);
+ graphics.fillCircle(4, 4, 4);
+ graphics.generateTexture('particle_white', 8, 8);
+ graphics.destroy();
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/VisualEnhancementSystem.js b/DEBUG_TOTAL_RECOVERY/VisualEnhancementSystem.js
new file mode 100644
index 000000000..25e851d94
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/VisualEnhancementSystem.js
@@ -0,0 +1,624 @@
+/**
+ * VISUAL ENHANCEMENT SYSTEM
+ * Central system for managing visual effects, animations, and polish
+ */
+class VisualEnhancementSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Sub-systems
+ this.animatedTextures = new Map();
+ this.weatherEffects = [];
+ this.lightSources = [];
+ this.shadows = [];
+ this.particles = [];
+
+ // Settings
+ this.settings = {
+ animatedTextures: true,
+ weatherEffects: true,
+ dynamicLighting: true,
+ shadows: true,
+ fogOfWar: false,
+ particleQuality: 'high', // low, medium, high, ultra
+ animationQuality: 'high',
+ screenShake: true,
+ transitions: true
+ };
+
+ // Animation timers
+ this.waterAnimTime = 0;
+ this.treeAnimTime = 0;
+ this.fireAnimTime = 0;
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Visual Enhancement System initialized');
+ }
+
+ init() {
+ this.initAnimatedTextures();
+ this.initWeatherEffects();
+ this.initLightingSystem();
+ this.initShadowSystem();
+ this.initParticleSystem();
+ }
+
+ // ========== ANIMATED TEXTURES ==========
+
+ initAnimatedTextures() {
+ if (!this.settings.animatedTextures) return;
+
+ // Create water animation frames
+ this.createWaterAnimation();
+
+ // Create fire animation
+ this.createFireAnimation();
+
+ // Create tree leaf animation
+ this.createTreeAnimation();
+
+ console.log('๐ฌ Animated textures initialized');
+ }
+
+ createWaterAnimation() {
+ // Water flow animation (4 frames)
+ const frames = [];
+ for (let i = 0; i < 4; i++) {
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0x4488ff, 1);
+ graphics.fillRect(0, 0, 64, 64);
+
+ // Add wave pattern
+ graphics.lineStyle(2, 0x6699ff, 0.5);
+ for (let y = 0; y < 64; y += 8) {
+ const offset = Math.sin((y + i * 16) * 0.1) * 4;
+ graphics.lineBetween(0, y + offset, 64, y + offset);
+ }
+
+ graphics.generateTexture('water_anim_' + i, 64, 64);
+ graphics.destroy();
+ frames.push('water_anim_' + i);
+ }
+
+ this.animatedTextures.set('water', {
+ frames,
+ currentFrame: 0,
+ speed: 200 // ms per frame
+ });
+ }
+
+ createFireAnimation() {
+ // Fire flickering (3 frames)
+ const frames = [];
+ const colors = [0xff6600, 0xff8800, 0xffaa00];
+
+ for (let i = 0; i < 3; i++) {
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(colors[i], 1);
+ graphics.fillCircle(16, 16, 12 + i * 2);
+ graphics.generateTexture('fire_anim_' + i, 32, 32);
+ graphics.destroy();
+ frames.push('fire_anim_' + i);
+ }
+
+ this.animatedTextures.set('fire', {
+ frames,
+ currentFrame: 0,
+ speed: 150
+ });
+ }
+
+ createTreeAnimation() {
+ // Tree leaf rustling (subtle movement)
+ console.log('๐ณ Tree animation ready');
+ }
+
+ updateAnimatedTextures(delta) {
+ if (!this.settings.animatedTextures) return;
+
+ this.waterAnimTime += delta;
+ this.fireAnimTime += delta;
+
+ // Update water
+ const water = this.animatedTextures.get('water');
+ if (water && this.waterAnimTime > water.speed) {
+ water.currentFrame = (water.currentFrame + 1) % water.frames.length;
+ this.waterAnimTime = 0;
+ }
+
+ // Update fire
+ const fire = this.animatedTextures.get('fire');
+ if (fire && this.fireAnimTime > fire.speed) {
+ fire.currentFrame = (fire.currentFrame + 1) % fire.frames.length;
+ this.fireAnimTime = 0;
+ }
+ }
+
+ // ========== WEATHER EFFECTS ==========
+
+ initWeatherEffects() {
+ if (!this.settings.weatherEffects) return;
+ console.log('๐ฆ๏ธ Weather effects initialized');
+ }
+
+ createSnowEffect() {
+ const emitter = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -10,
+ speedY: { min: 50, max: 100 },
+ speedX: { min: -20, max: 20 },
+ scale: { min: 0.3, max: 0.8 },
+ alpha: { min: 0.5, max: 1 },
+ lifespan: 10000,
+ frequency: 50,
+ quantity: 2
+ });
+ emitter.setScrollFactor(0);
+ this.weatherEffects.push(emitter);
+ return emitter;
+ }
+
+ createRainEffect() {
+ const emitter = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -10,
+ speedY: { min: 400, max: 600 },
+ speedX: { min: -50, max: -30 },
+ scaleX: 0.1,
+ scaleY: 0.5,
+ alpha: 0.6,
+ lifespan: 2000,
+ frequency: 10,
+ quantity: 5,
+ tint: 0x88ccff
+ });
+ emitter.setScrollFactor(0);
+ this.weatherEffects.push(emitter);
+ return emitter;
+ }
+
+ createLightningFlash() {
+ const flash = this.scene.add.rectangle(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.centerY,
+ this.scene.cameras.main.width,
+ this.scene.cameras.main.height,
+ 0xffffff,
+ 0.8
+ );
+ flash.setScrollFactor(0);
+ flash.setDepth(10000);
+
+ this.scene.tweens.add({
+ targets: flash,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => flash.destroy()
+ });
+ }
+
+ createFogEffect() {
+ // Atmospheric fog overlay - covers entire screen
+ const fogOverlay = this.scene.add.graphics();
+ fogOverlay.setScrollFactor(0);
+ fogOverlay.setDepth(8000); // Above game, below UI
+ fogOverlay.setAlpha(0.4); // Semi-transparent
+
+ // Create fog particles for movement effect
+ const fogParticles = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: { min: 0, max: this.scene.cameras.main.height },
+ speedX: { min: -10, max: 10 },
+ speedY: { min: -5, max: 5 },
+ scale: { min: 2, max: 4 },
+ alpha: { min: 0.1, max: 0.3 },
+ lifespan: 10000,
+ frequency: 100,
+ quantity: 3,
+ tint: 0xcccccc,
+ blendMode: 'SCREEN'
+ });
+ fogParticles.setScrollFactor(0);
+ fogParticles.setDepth(8001);
+
+ // Animated fog overlay
+ let fogTime = 0;
+ const updateFog = () => {
+ fogTime += 0.01;
+ fogOverlay.clear();
+
+ const cam = this.scene.cameras.main;
+
+ // Draw multiple layers of fog for depth
+ for (let layer = 0; layer < 3; layer++) {
+ const offset = Math.sin(fogTime + layer) * 50;
+ const alpha = 0.1 + layer * 0.05;
+
+ fogOverlay.fillStyle(0xffffff, alpha);
+
+ // Draw wavy fog shapes
+ for (let i = 0; i < 5; i++) {
+ const x = (i * cam.width / 4) + offset;
+ const y = (layer * 100) + Math.cos(fogTime + i) * 30;
+ fogOverlay.fillCircle(x, y, 200 + layer * 50);
+ }
+ }
+ };
+
+ // Update fog animation every frame
+ this.scene.events.on('update', updateFog);
+
+ this.weatherEffects.push({
+ overlay: fogOverlay,
+ particles: fogParticles,
+ update: updateFog,
+ destroy: () => {
+ fogOverlay.destroy();
+ fogParticles.destroy();
+ this.scene.events.off('update', updateFog);
+ }
+ });
+
+ console.log('๐ซ๏ธ Atmospheric fog effect created');
+ return { overlay: fogOverlay, particles: fogParticles };
+ }
+
+ // ========== LIGHTING SYSTEM ==========
+
+ initLightingSystem() {
+ if (!this.settings.dynamicLighting) return;
+
+ this.lightingLayer = this.scene.add.layer();
+ this.lightingLayer.setDepth(5000);
+
+ console.log('๐ก Lighting system initialized');
+ }
+
+ addLight(x, y, radius = 100, color = 0xffaa00, intensity = 0.6) {
+ if (!this.settings.dynamicLighting) return null;
+
+ // Create radial gradient light
+ const light = this.scene.add.graphics();
+ const gradient = light.createRadialGradient(
+ radius, radius, 0,
+ radius, radius, radius
+ );
+
+ gradient.addColorStop(0, `rgba(${(color >> 16) & 0xff}, ${(color >> 8) & 0xff}, ${color & 0xff}, ${intensity})`);
+ gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
+
+ light.fillStyle(color, intensity);
+ light.fillCircle(radius, radius, radius);
+ light.setPosition(x - radius, y - radius);
+ light.setBlendMode(Phaser.BlendModes.ADD);
+ light.setDepth(5001);
+
+ const lightObj = {
+ graphics: light,
+ x, y, radius, color, intensity,
+ flickering: false
+ };
+
+ this.lightSources.push(lightObj);
+ return lightObj;
+ }
+
+ addTorch(x, y) {
+ const torch = this.addLight(x, y, 80, 0xff6600, 0.5);
+ if (torch) {
+ torch.flickering = true;
+ }
+ return torch;
+ }
+
+ updateLighting(delta) {
+ if (!this.settings.dynamicLighting) return;
+
+ // Update flickering lights
+ for (const light of this.lightSources) {
+ if (light.flickering) {
+ const flicker = 0.4 + Math.random() * 0.2;
+ light.graphics.setAlpha(flicker);
+ }
+ }
+
+ // Update ambient lighting based on time of day
+ if (this.scene.weatherSystem) {
+ const time = this.scene.weatherSystem.gameTime;
+ const isNight = time < 6 || time > 18;
+
+ if (isNight) {
+ // Darker at night
+ this.scene.cameras.main.setAlpha(0.7);
+ } else {
+ this.scene.cameras.main.setAlpha(1.0);
+ }
+ }
+ }
+
+ // ========== SHADOW SYSTEM ==========
+
+ initShadowSystem() {
+ if (!this.settings.shadows) return;
+ console.log('๐ Shadow system initialized');
+ }
+
+ addShadow(entity, offsetX = 0, offsetY = 10, width = 40, height = 15) {
+ if (!this.settings.shadows) return null;
+
+ const shadow = this.scene.add.ellipse(
+ entity.x + offsetX,
+ entity.y + offsetY,
+ width,
+ height,
+ 0x000000,
+ 0.3
+ );
+ shadow.setDepth(0);
+
+ const shadowObj = { entity, shadow, offsetX, offsetY };
+ this.shadows.push(shadowObj);
+ return shadow;
+ }
+
+ updateShadows() {
+ if (!this.settings.shadows) return;
+
+ // Get time of day for shadow opacity
+ let opacity = 0.3;
+ if (this.scene.weatherSystem) {
+ const time = this.scene.weatherSystem.gameTime;
+ // Darker shadows at noon, lighter at dawn/dusk
+ opacity = 0.2 + Math.abs(Math.sin((time / 24) * Math.PI * 2)) * 0.3;
+ }
+
+ // Update shadow positions
+ for (const { entity, shadow, offsetX, offsetY } of this.shadows) {
+ if (entity.sprite) {
+ shadow.x = entity.sprite.x + offsetX;
+ shadow.y = entity.sprite.y + offsetY;
+ shadow.setAlpha(opacity);
+ }
+ }
+ }
+
+ // ========== PARTICLE SYSTEM ==========
+
+ initParticleSystem() {
+ // Create particle textures
+ this.createParticleTextures();
+ console.log('โจ Particle system initialized');
+ }
+
+ createParticleTextures() {
+ // White particle
+ const white = this.scene.add.graphics();
+ white.fillStyle(0xffffff, 1);
+ white.fillCircle(4, 4, 4);
+ white.generateTexture('particle_white', 8, 8);
+ white.destroy();
+
+ // Sparkle
+ const sparkle = this.scene.add.graphics();
+ sparkle.fillStyle(0xffff00, 1);
+ sparkle.fillCircle(3, 3, 3);
+ sparkle.generateTexture('particle_sparkle', 6, 6);
+ sparkle.destroy();
+
+ // Heart
+ const heart = this.scene.add.graphics();
+ heart.fillStyle(0xff0066, 1);
+ heart.fillCircle(3, 3, 3);
+ heart.fillCircle(5, 3, 3);
+ heart.fillTriangle(1, 4, 7, 4, 4, 8);
+ heart.generateTexture('particle_heart', 8, 8);
+ heart.destroy();
+ }
+
+ createHeartParticles(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'particle_heart', {
+ speed: { min: 20, max: 50 },
+ angle: { min: -120, max: -60 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 1000,
+ quantity: 5,
+ blendMode: 'ADD'
+ });
+
+ this.scene.time.delayedCall(1000, () => emitter.destroy());
+ return emitter;
+ }
+
+ createSparkleEffect(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'particle_sparkle', {
+ speed: { min: 10, max: 30 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 800,
+ quantity: 10,
+ blendMode: 'ADD'
+ });
+
+ this.scene.time.delayedCall(800, () => emitter.destroy());
+ return emitter;
+ }
+
+ createCheckmarkEffect(x, y) {
+ const checkmark = this.scene.add.text(x, y, 'โ', {
+ fontSize: '32px',
+ color: '#00ff00',
+ fontStyle: 'bold'
+ });
+ checkmark.setOrigin(0.5);
+ checkmark.setDepth(10000);
+
+ this.scene.tweens.add({
+ targets: checkmark,
+ y: y - 50,
+ alpha: 0,
+ scale: 2,
+ duration: 1000,
+ ease: 'Power2',
+ onComplete: () => checkmark.destroy()
+ });
+ }
+
+ // ========== SCREEN EFFECTS ==========
+
+ screenShake(intensity = 10, duration = 300) {
+ if (!this.settings.screenShake) return;
+ this.scene.cameras.main.shake(duration, intensity / 1000);
+ }
+
+ screenFlash(color = 0xffffff, duration = 200) {
+ this.scene.cameras.main.flash(duration,
+ (color >> 16) & 0xff,
+ (color >> 8) & 0xff,
+ color & 0xff
+ );
+ }
+
+ fadeOut(duration = 500, callback) {
+ if (!this.settings.transitions) {
+ if (callback) callback();
+ return;
+ }
+
+ this.scene.cameras.main.fadeOut(duration, 0, 0, 0);
+ if (callback) {
+ this.scene.cameras.main.once('camerafadeoutcomplete', callback);
+ }
+ }
+
+ fadeIn(duration = 500) {
+ if (!this.settings.transitions) return;
+ this.scene.cameras.main.fadeIn(duration, 0, 0, 0);
+ }
+
+ // ========== BUILDING EFFECTS ==========
+
+ createConstructionEffect(x, y) {
+ // Dust particles during construction
+ const emitter = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 20, max: 40 },
+ scale: { start: 0.5, end: 0 },
+ alpha: { start: 0.5, end: 0 },
+ lifespan: 1000,
+ quantity: 3,
+ frequency: 100,
+ tint: 0x996633
+ });
+
+ return emitter;
+ }
+
+ createSmokeEffect(x, y) {
+ // Chimney smoke
+ const emitter = this.scene.add.particles(x, y, 'particle_white', {
+ speedY: { min: -30, max: -50 },
+ speedX: { min: -10, max: 10 },
+ scale: { start: 0.3, end: 1 },
+ alpha: { start: 0.5, end: 0 },
+ lifespan: 2000,
+ frequency: 500,
+ quantity: 1,
+ tint: 0x888888
+ });
+
+ return emitter;
+ }
+
+ // ========== FARM AUTOMATION VISUALS ==========
+
+ createPowerGridEffect(x1, y1, x2, y2) {
+ // Electric arc between power sources
+ const graphics = this.scene.add.graphics();
+ graphics.lineStyle(2, 0x00ffff, 0.8);
+
+ // Draw lightning-like connection
+ const steps = 5;
+ const points = [];
+ for (let i = 0; i <= steps; i++) {
+ const t = i / steps;
+ const x = x1 + (x2 - x1) * t + (Math.random() - 0.5) * 10;
+ const y = y1 + (y2 - y1) * t + (Math.random() - 0.5) * 10;
+ points.push({ x, y });
+ }
+
+ graphics.beginPath();
+ graphics.moveTo(points[0].x, points[0].y);
+ for (let i = 1; i < points.length; i++) {
+ graphics.lineTo(points[i].x, points[i].y);
+ }
+ graphics.strokePath();
+
+ // Fade out
+ this.scene.tweens.add({
+ targets: graphics,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => graphics.destroy()
+ });
+ }
+
+ createMutantGlow(entity, color = 0x00ff00) {
+ // Radioactive glow for mutants
+ const glow = this.scene.add.circle(
+ entity.x,
+ entity.y,
+ 30,
+ color,
+ 0.3
+ );
+ glow.setBlendMode(Phaser.BlendModes.ADD);
+ glow.setDepth(entity.depth - 1);
+
+ // Pulsing animation
+ this.scene.tweens.add({
+ targets: glow,
+ scale: { from: 1, to: 1.2 },
+ alpha: { from: 0.3, to: 0.1 },
+ duration: 1000,
+ yoyo: true,
+ repeat: -1
+ });
+
+ return glow;
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateAnimatedTextures(delta);
+ this.updateLighting(delta);
+ this.updateShadows();
+ }
+
+ // ========== SETTINGS ==========
+
+ saveSettings() {
+ localStorage.setItem('novafarma_visual_enhancements', JSON.stringify(this.settings));
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_visual_enhancements');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ destroy() {
+ if (this.lightingLayer) this.lightingLayer.destroy();
+ for (const { shadow } of this.shadows) {
+ shadow.destroy();
+ }
+ for (const effect of this.weatherEffects) {
+ effect.destroy();
+ }
+ console.log('โจ Visual Enhancement System destroyed');
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/VisualSoundCueSystem.js b/DEBUG_TOTAL_RECOVERY/VisualSoundCueSystem.js
new file mode 100644
index 000000000..43396c912
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/VisualSoundCueSystem.js
@@ -0,0 +1,746 @@
+/**
+ * VISUAL SOUND CUE SYSTEM
+ * Provides visual indicators for sounds (accessibility for deaf/hard-of-hearing players)
+ */
+class VisualSoundCueSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Visual elements
+ this.heartbeatIndicator = null;
+ this.damageIndicators = [];
+ this.screenFlash = null;
+ this.subtitleBackground = null;
+ this.subtitleText = null;
+ this.subtitleSpeaker = null;
+ this.subtitleArrows = { left: null, right: null };
+ this.heartbeatTween = null;
+ this.fishingBobberIndicator = null;
+
+ // Speaker color mapping
+ this.speakerColors = {
+ 'Player': '#00ff00',
+ 'NPC': '#ffff00',
+ 'Enemy': '#ff0000',
+ 'System': '#00ffff',
+ 'Narrator': '#ffffff'
+ };
+
+ // Subtitle size presets
+ this.subtitleSizes = {
+ 'small': { main: 16, speaker: 12, arrow: 24 },
+ 'medium': { main: 20, speaker: 16, arrow: 32 },
+ 'large': { main: 28, speaker: 20, arrow: 40 },
+ 'very-large': { main: 36, speaker: 24, arrow: 48 }
+ };
+
+ // Settings
+ this.settings = {
+ heartbeatEnabled: true,
+ damageIndicatorEnabled: true,
+ screenFlashEnabled: true,
+ subtitlesEnabled: true,
+ directionalArrowsEnabled: true,
+ speakerNamesEnabled: true,
+ subtitleOpacity: 0.8,
+ fishingBobberEnabled: true,
+ subtitleSize: 'medium' // 'small', 'medium', 'large', 'very-large'
+ };
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Visual Sound Cue System initialized');
+ }
+
+ init() {
+ // Create heartbeat indicator (top-left corner)
+ this.createHeartbeatIndicator();
+
+ // Create subtitle container (bottom center)
+ this.createSubtitleContainer();
+
+ // Load settings from localStorage
+ // this.loadSettings(); // Moved to constructor
+ }
+
+ createHeartbeatIndicator() {
+ const x = 200;
+ const y = 30;
+
+ // Heart emoji as sprite
+ this.heartbeatIndicator = this.scene.add.text(x, y, 'โค๏ธ', {
+ fontSize: '48px'
+ });
+ this.heartbeatIndicator.setOrigin(0.5);
+ this.heartbeatIndicator.setDepth(10000);
+ this.heartbeatIndicator.setScrollFactor(0);
+ this.heartbeatIndicator.setVisible(false);
+ }
+
+ createSubtitleContainer() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Background
+ this.subtitleBackground = this.scene.add.rectangle(
+ width / 2,
+ height - 100,
+ width - 100,
+ 100,
+ 0x000000,
+ this.settings.subtitleOpacity
+ );
+ this.subtitleBackground.setOrigin(0.5);
+ this.subtitleBackground.setDepth(9999);
+ this.subtitleBackground.setScrollFactor(0);
+ this.subtitleBackground.setVisible(false);
+
+ // Speaker name text
+ this.subtitleSpeaker = this.scene.add.text(
+ width / 2,
+ height - 130,
+ '',
+ {
+ fontSize: '16px',
+ fontFamily: 'Arial',
+ fontStyle: 'bold',
+ color: '#ffffff',
+ align: 'center'
+ }
+ );
+ this.subtitleSpeaker.setOrigin(0.5);
+ this.subtitleSpeaker.setDepth(10001);
+ this.subtitleSpeaker.setScrollFactor(0);
+ this.subtitleSpeaker.setVisible(false);
+
+ // Main subtitle text
+ this.subtitleText = this.scene.add.text(
+ width / 2,
+ height - 100,
+ '',
+ {
+ fontSize: '20px',
+ fontFamily: 'Arial',
+ color: '#ffffff',
+ align: 'center',
+ wordWrap: { width: width - 160 }
+ }
+ );
+ this.subtitleText.setOrigin(0.5);
+ this.subtitleText.setDepth(10000);
+ this.subtitleText.setScrollFactor(0);
+ this.subtitleText.setVisible(false);
+
+ // Directional arrows
+ this.subtitleArrows.left = this.scene.add.text(
+ 50,
+ height - 100,
+ 'โ',
+ {
+ fontSize: '32px',
+ fontFamily: 'Arial',
+ color: '#ffff00'
+ }
+ );
+ this.subtitleArrows.left.setOrigin(0.5);
+ this.subtitleArrows.left.setDepth(10001);
+ this.subtitleArrows.left.setScrollFactor(0);
+ this.subtitleArrows.left.setVisible(false);
+
+ this.subtitleArrows.right = this.scene.add.text(
+ width - 50,
+ height - 100,
+ 'โบ',
+ {
+ fontSize: '32px',
+ fontFamily: 'Arial',
+ color: '#ffff00'
+ }
+ );
+ this.subtitleArrows.right.setOrigin(0.5);
+ this.subtitleArrows.right.setDepth(10001);
+ this.subtitleArrows.right.setScrollFactor(0);
+ this.subtitleArrows.right.setVisible(false);
+ }
+
+ // ========== VISUAL HEARTBEAT (LOW HEALTH) ==========
+
+ updateHeartbeat(healthPercent) {
+ if (!this.settings.heartbeatEnabled) return;
+
+ // Show heartbeat when health < 30%
+ if (healthPercent < 30) {
+ if (!this.heartbeatActive) {
+ this.startHeartbeat(healthPercent);
+ } else {
+ this.updateHeartbeatSpeed(healthPercent);
+ }
+ } else {
+ this.stopHeartbeat();
+ }
+ }
+
+ startHeartbeat(healthPercent) {
+ this.heartbeatActive = true;
+ this.heartbeatIndicator.setVisible(true);
+
+ // Calculate speed based on health (lower health = faster beat)
+ const speed = Phaser.Math.Clamp(1000 - (healthPercent * 20), 300, 1000);
+
+ this.heartbeatTween = this.scene.tweens.add({
+ targets: this.heartbeatIndicator,
+ scale: 1.3,
+ alpha: 0.6,
+ duration: speed / 2,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+
+ console.log('๐ Visual heartbeat started (health:', healthPercent + '%)');
+ }
+
+ updateHeartbeatSpeed(healthPercent) {
+ if (!this.heartbeatTween) return;
+
+ const speed = Phaser.Math.Clamp(1000 - (healthPercent * 20), 300, 1000);
+ this.heartbeatTween.updateTo('duration', speed / 2, true);
+ }
+
+ stopHeartbeat() {
+ if (!this.heartbeatActive) return;
+
+ this.heartbeatActive = false;
+ this.heartbeatIndicator.setVisible(false);
+
+ if (this.heartbeatTween) {
+ this.heartbeatTween.stop();
+ this.heartbeatTween = null;
+ }
+
+ this.heartbeatIndicator.setScale(1);
+ this.heartbeatIndicator.setAlpha(1);
+ }
+
+ // ========== DAMAGE DIRECTION INDICATOR ==========
+
+ showDamageIndicator(direction, damage) {
+ if (!this.settings.damageIndicatorEnabled) return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Calculate position based on direction
+ let x = width / 2;
+ let y = height / 2;
+ let arrow = 'โ';
+ let rotation = 0;
+
+ switch (direction) {
+ case 'up':
+ y = 100;
+ arrow = 'โ';
+ rotation = 0;
+ break;
+ case 'down':
+ y = height - 100;
+ arrow = 'โ';
+ rotation = Math.PI;
+ break;
+ case 'left':
+ x = 100;
+ arrow = 'โ';
+ rotation = -Math.PI / 2;
+ break;
+ case 'right':
+ x = width - 100;
+ arrow = 'โ';
+ rotation = Math.PI / 2;
+ break;
+ }
+
+ // Create damage indicator
+ const indicator = this.scene.add.text(x, y, arrow, {
+ fontSize: '64px',
+ color: '#ff0000',
+ fontStyle: 'bold'
+ });
+ indicator.setOrigin(0.5);
+ indicator.setDepth(10001);
+ indicator.setScrollFactor(0);
+ indicator.setRotation(rotation);
+
+ // Animate and destroy
+ this.scene.tweens.add({
+ targets: indicator,
+ alpha: 0,
+ scale: 1.5,
+ duration: 800,
+ ease: 'Power2',
+ onComplete: () => {
+ indicator.destroy();
+ }
+ });
+
+ console.log('๐ฏ Damage indicator shown:', direction, damage);
+ }
+
+ // ========== SCREEN FLASH NOTIFICATIONS ==========
+
+ showScreenFlash(type, message) {
+ if (!this.settings.screenFlashEnabled) return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ let color = 0xffffff;
+ let icon = 'โ ๏ธ';
+
+ switch (type) {
+ case 'danger':
+ color = 0xff0000;
+ icon = 'โ ๏ธ';
+ break;
+ case 'warning':
+ color = 0xffaa00;
+ icon = 'โก';
+ break;
+ case 'info':
+ color = 0x00aaff;
+ icon = 'โน๏ธ';
+ break;
+ case 'success':
+ color = 0x00ff00;
+ icon = 'โ';
+ break;
+ }
+
+ // Flash overlay
+ const flash = this.scene.add.rectangle(0, 0, width, height, color, 0.3);
+ flash.setOrigin(0);
+ flash.setDepth(9998);
+ flash.setScrollFactor(0);
+
+ // Icon
+ const iconText = this.scene.add.text(width / 2, height / 2, icon, {
+ fontSize: '128px'
+ });
+ iconText.setOrigin(0.5);
+ iconText.setDepth(9999);
+ iconText.setScrollFactor(0);
+
+ // Message
+ if (message) {
+ this.showSubtitle(message, 2000);
+ }
+
+ // Fade out
+ this.scene.tweens.add({
+ targets: [flash, iconText],
+ alpha: 0,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ flash.destroy();
+ iconText.destroy();
+ }
+ });
+
+ console.log('โก Screen flash shown:', type, message);
+ }
+
+ // ========== SUBTITLES ==========
+
+ showSubtitle(text, duration = 3000, speaker = null, direction = null) {
+ if (!this.settings.subtitlesEnabled) return;
+
+ // Set subtitle text
+ this.subtitleText.setText(text);
+ this.subtitleText.setVisible(true);
+ this.subtitleBackground.setVisible(true);
+
+ // Show speaker name with color if enabled
+ if (speaker && this.settings.speakerNamesEnabled) {
+ const color = this.speakerColors[speaker] || '#ffffff';
+ this.subtitleSpeaker.setText(speaker);
+ this.subtitleSpeaker.setColor(color);
+ this.subtitleSpeaker.setVisible(true);
+ } else {
+ this.subtitleSpeaker.setVisible(false);
+ }
+
+ // Show directional arrows if enabled and direction provided
+ if (direction && this.settings.directionalArrowsEnabled) {
+ this.showDirectionalArrows(direction);
+ } else {
+ this.hideDirectionalArrows();
+ }
+
+ // Auto-hide after duration
+ this.scene.time.delayedCall(duration, () => {
+ this.hideSubtitle();
+ });
+
+ console.log('๐ฌ Subtitle shown:', speaker ? `[${speaker}] ${text}` : text);
+ }
+
+ showDirectionalArrows(direction) {
+ this.hideDirectionalArrows();
+
+ if (direction === 'left' || direction === 'west') {
+ this.subtitleArrows.left.setVisible(true);
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: this.subtitleArrows.left,
+ alpha: { from: 1, to: 0.3 },
+ duration: 500,
+ yoyo: true,
+ repeat: -1
+ });
+ } else if (direction === 'right' || direction === 'east') {
+ this.subtitleArrows.right.setVisible(true);
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: this.subtitleArrows.right,
+ alpha: { from: 1, to: 0.3 },
+ duration: 500,
+ yoyo: true,
+ repeat: -1
+ });
+ } else if (direction === 'both') {
+ this.subtitleArrows.left.setVisible(true);
+ this.subtitleArrows.right.setVisible(true);
+ // Pulse animation for both
+ this.scene.tweens.add({
+ targets: [this.subtitleArrows.left, this.subtitleArrows.right],
+ alpha: { from: 1, to: 0.3 },
+ duration: 500,
+ yoyo: true,
+ repeat: -1
+ });
+ }
+ }
+
+ hideDirectionalArrows() {
+ this.scene.tweens.killTweensOf(this.subtitleArrows.left);
+ this.scene.tweens.killTweensOf(this.subtitleArrows.right);
+ this.subtitleArrows.left.setVisible(false);
+ this.subtitleArrows.right.setVisible(false);
+ this.subtitleArrows.left.setAlpha(1);
+ this.subtitleArrows.right.setAlpha(1);
+ }
+
+ hideSubtitle() {
+ this.subtitleText.setVisible(false);
+ this.subtitleBackground.setVisible(false);
+ this.subtitleSpeaker.setVisible(false);
+ this.hideDirectionalArrows();
+ }
+
+ // ========== SOUND EVENT HANDLERS ==========
+
+ onSoundPlayed(soundType, data = {}) {
+ if (!this.enabled) return;
+
+ switch (soundType) {
+ case 'damage':
+ this.showDamageIndicator(data.direction || 'down', data.amount || 10);
+ this.showSubtitle('[DAMAGE TAKEN]', 1500, 'System', data.direction);
+ break;
+
+ case 'pickup':
+ this.showSubtitle(`[PICKED UP: ${data.item || 'Item'}]`, 1500, 'System');
+ break;
+
+ case 'harvest':
+ this.showSubtitle('[CROP HARVESTED]', 1500, 'System');
+ break;
+
+ case 'build':
+ this.showSubtitle('[BUILDING PLACED]', 1500, 'System');
+ break;
+
+ case 'dig':
+ this.showSubtitle('[DIGGING SOUND]', 1000, 'System');
+ break;
+
+ case 'plant':
+ this.showSubtitle('[PLANTING SOUND]', 1000, 'System');
+ break;
+
+ case 'footsteps':
+ this.showSubtitle('[FOOTSTEPS]', 500, null, data.direction);
+ break;
+
+ case 'door':
+ this.showSubtitle('[DOOR OPENS]', 1000, 'System');
+ break;
+
+ case 'chest':
+ this.showSubtitle('[CHEST OPENS]', 1000, 'System');
+ break;
+
+ case 'water':
+ this.showSubtitle('[WATER SPLASH]', 1000, 'System');
+ break;
+
+ case 'fire':
+ this.showSubtitle('[FIRE CRACKLING]', 2000, 'System');
+ break;
+
+ case 'explosion':
+ this.showSubtitle('[EXPLOSION!]', 1500, 'System');
+ this.showScreenFlash('danger', '[EXPLOSION!]');
+ break;
+
+ case 'npc_talk':
+ this.showSubtitle(data.text || '[NPC TALKING]', 3000, data.speaker || 'NPC', data.direction);
+ break;
+
+ case 'enemy_growl':
+ this.showSubtitle('[ENEMY GROWL]', 1500, 'Enemy', data.direction);
+ break;
+
+ case 'fishing_cast':
+ this.showSubtitle('[FISHING LINE CAST]', 1000, 'System');
+ break;
+
+ case 'fishing_bite':
+ this.showSubtitle('[FISH BITING!]', 1500, 'System');
+ this.showFishingBobberCue();
+ break;
+
+ case 'danger':
+ this.showScreenFlash('danger', '[DANGER!]');
+ this.showSubtitle('[DANGER NEARBY]', 2000, 'System');
+ break;
+
+ case 'night':
+ this.showScreenFlash('warning', '[NIGHT FALLING]');
+ this.showSubtitle('[NIGHT IS FALLING]', 2000, 'System');
+ break;
+
+ case 'achievement':
+ this.showScreenFlash('success', data.message || '[ACHIEVEMENT UNLOCKED]');
+ this.showSubtitle(data.message || '[ACHIEVEMENT UNLOCKED]', 3000, 'System');
+ break;
+
+ case 'ui_click':
+ this.showSubtitle('[CLICK]', 300, null);
+ break;
+
+ case 'ui_hover':
+ this.showSubtitle('[HOVER]', 200, null);
+ break;
+ }
+ }
+
+ /**
+ * Show fishing bobber visual cue
+ */
+ showFishingBobberCue() {
+ if (!this.settings.fishingBobberEnabled) return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Create bobber indicator if it doesn't exist
+ if (!this.fishingBobberIndicator) {
+ this.fishingBobberIndicator = this.scene.add.container(width / 2, height / 2);
+ this.fishingBobberIndicator.setDepth(10002);
+ this.fishingBobberIndicator.setScrollFactor(0);
+
+ // Circle background
+ const circle = this.scene.add.circle(0, 0, 60, 0xff6600, 0.8);
+ this.fishingBobberIndicator.add(circle);
+
+ // Exclamation mark
+ const exclamation = this.scene.add.text(0, 0, '!', {
+ fontSize: '48px',
+ fontFamily: 'Arial',
+ fontStyle: 'bold',
+ color: '#ffffff'
+ });
+ exclamation.setOrigin(0.5);
+ this.fishingBobberIndicator.add(exclamation);
+
+ // Text below
+ const text = this.scene.add.text(0, 80, 'FISH BITING!\nPress E', {
+ fontSize: '20px',
+ fontFamily: 'Arial',
+ fontStyle: 'bold',
+ color: '#ffffff',
+ align: 'center'
+ });
+ text.setOrigin(0.5);
+ this.fishingBobberIndicator.add(text);
+ }
+
+ // Show and animate
+ this.fishingBobberIndicator.setVisible(true);
+ this.fishingBobberIndicator.setAlpha(0);
+
+ // Fade in and pulse
+ this.scene.tweens.add({
+ targets: this.fishingBobberIndicator,
+ alpha: 1,
+ duration: 200,
+ onComplete: () => {
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: this.fishingBobberIndicator,
+ scale: { from: 1, to: 1.2 },
+ duration: 300,
+ yoyo: true,
+ repeat: 5,
+ onComplete: () => {
+ // Fade out
+ this.scene.tweens.add({
+ targets: this.fishingBobberIndicator,
+ alpha: 0,
+ duration: 300,
+ onComplete: () => {
+ this.fishingBobberIndicator.setVisible(false);
+ }
+ });
+ }
+ });
+ }
+ });
+
+ console.log('๐ฃ Fishing bobber cue shown');
+ }
+
+ /**
+ * Set subtitle background opacity
+ */
+ setSubtitleOpacity(opacity) {
+ this.settings.subtitleOpacity = Phaser.Math.Clamp(opacity, 0, 1);
+ if (this.subtitleBackground) {
+ this.subtitleBackground.setAlpha(this.settings.subtitleOpacity);
+ }
+ this.saveSettings();
+ console.log('๐ Subtitle opacity set to:', this.settings.subtitleOpacity);
+ }
+
+ /**
+ * Add custom speaker color
+ */
+ addSpeakerColor(speaker, color) {
+ this.speakerColors[speaker] = color;
+ console.log(`๐จ Added speaker color: ${speaker} = ${color}`);
+ }
+
+ // ========== SETTINGS ==========
+
+ toggleHeartbeat(enabled) {
+ this.settings.heartbeatEnabled = enabled;
+ if (!enabled) this.stopHeartbeat();
+ this.saveSettings();
+ }
+
+ toggleDamageIndicator(enabled) {
+ this.settings.damageIndicatorEnabled = enabled;
+ this.saveSettings();
+ }
+
+ toggleScreenFlash(enabled) {
+ this.settings.screenFlashEnabled = enabled;
+ this.saveSettings();
+ }
+
+ toggleSubtitles(enabled) {
+ this.settings.subtitlesEnabled = enabled;
+ if (!enabled) this.hideSubtitle();
+ this.saveSettings();
+ console.log('๐ฌ Subtitles:', enabled ? 'ENABLED' : 'DISABLED');
+ }
+
+ toggleDirectionalArrows(enabled) {
+ this.settings.directionalArrowsEnabled = enabled;
+ if (!enabled) {
+ this.hideDirectionalArrows();
+ }
+ this.saveSettings();
+ console.log('โก๏ธ Directional Arrows:', enabled ? 'ENABLED' : 'DISABLED');
+ }
+
+ toggleSpeakerNames(enabled) {
+ this.settings.speakerNamesEnabled = enabled;
+ this.saveSettings();
+ console.log('๐ค Speaker Names:', enabled ? 'ENABLED' : 'DISABLED');
+ }
+
+ toggleFishingBobber(enabled) {
+ this.settings.fishingBobberEnabled = enabled;
+ this.saveSettings();
+ console.log('๐ฃ Fishing Bobber Cue:', enabled ? 'ENABLED' : 'DISABLED');
+ }
+
+ /**
+ * Set subtitle text size
+ * @param {string} size - 'small', 'medium', 'large', 'very-large'
+ */
+ setSubtitleSize(size) {
+ if (!this.subtitleSizes[size]) {
+ console.error(`Invalid subtitle size: ${size}. Valid options: small, medium, large, very-large`);
+ return;
+ }
+
+ this.settings.subtitleSize = size;
+ const sizes = this.subtitleSizes[size];
+
+ // Update text sizes
+ if (this.subtitleText) {
+ this.subtitleText.setFontSize(sizes.main);
+ }
+ if (this.subtitleSpeaker) {
+ this.subtitleSpeaker.setFontSize(sizes.speaker);
+ }
+ if (this.subtitleArrows.left) {
+ this.subtitleArrows.left.setFontSize(sizes.arrow);
+ }
+ if (this.subtitleArrows.right) {
+ this.subtitleArrows.right.setFontSize(sizes.arrow);
+ }
+
+ // Adjust background height based on text size
+ if (this.subtitleBackground) {
+ const bgHeight = sizes.main * 4; // 4x font size for padding
+ this.subtitleBackground.setSize(this.subtitleBackground.width, bgHeight);
+ }
+
+ this.saveSettings();
+ console.log(`๐ Subtitle size set to: ${size.toUpperCase()} (${sizes.main}px)`);
+ }
+
+ saveSettings() {
+ localStorage.setItem('novafarma_visual_sound_cues', JSON.stringify(this.settings));
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_visual_sound_cues');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ // ========== UPDATE ==========
+
+ update() {
+ // Update heartbeat based on player health
+ if (this.scene.player && this.scene.player.health !== undefined) {
+ const healthPercent = (this.scene.player.health / this.scene.player.maxHealth) * 100;
+ this.updateHeartbeat(healthPercent);
+ }
+ }
+
+ destroy() {
+ if (this.heartbeatTween) this.heartbeatTween.stop();
+ if (this.heartbeatSprite) this.heartbeatSprite.destroy();
+ if (this.subtitleText) this.subtitleText.destroy();
+ if (this.subtitleBackground) this.subtitleBackground.destroy();
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/ZombieCommunicationSystem.js b/DEBUG_TOTAL_RECOVERY/ZombieCommunicationSystem.js
new file mode 100644
index 000000000..634c11611
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/ZombieCommunicationSystem.js
@@ -0,0 +1,361 @@
+/**
+ * ZombieCommunicationSystem.js
+ * =============================
+ * KRVAVA ลฝETEV - Zombie Communication System (Hybrid Skill)
+ *
+ * Features:
+ * - Level-based zombie understanding
+ * - Level 1: Groaning only ("Hnggg...")
+ * - Level 5: Keywords in subtitles
+ * - Level 10: Full sentences (warnings, memories)
+ * - Subtitle UI
+ * - Translation system
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class ZombieCommunicationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Player's communication level
+ this.communicationLevel = 0;
+ this.maxLevel = 10;
+
+ // Subtitle UI
+ this.subtitleText = null;
+ this.subtitleContainer = null;
+ this.currentSubtitle = null;
+
+ // Zombie speech library
+ this.zombiePhrases = new Map();
+
+ console.log('๐ง ZombieCommunicationSystem initialized');
+
+ // Create subtitle UI
+ this.createSubtitleUI();
+
+ // Load zombie phrases
+ this.loadZombiePhrases();
+ }
+
+ /**
+ * Create subtitle UI
+ */
+ createSubtitleUI() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Container at bottom of screen
+ this.subtitleContainer = this.scene.add.container(width / 2, height - 100);
+ this.subtitleContainer.setScrollFactor(0);
+ this.subtitleContainer.setDepth(10000);
+ this.subtitleContainer.setAlpha(0);
+
+ // Background
+ const bg = this.scene.add.rectangle(0, 0, 800, 100, 0x000000, 0.8);
+ this.subtitleContainer.add(bg);
+
+ // Subtitle text
+ this.subtitleText = this.scene.add.text(0, 0, '', {
+ fontSize: '24px',
+ fontFamily: 'Arial',
+ color: '#00FF00', // Green zombie text
+ align: 'center',
+ wordWrap: { width: 750 }
+ });
+ this.subtitleText.setOrigin(0.5);
+ this.subtitleContainer.add(this.subtitleText);
+
+ console.log('โ
Subtitle UI created');
+ }
+
+ /**
+ * Load zombie phrase library
+ */
+ loadZombiePhrases() {
+ // Level 1: Pure groaning
+ const level1Phrases = [
+ { zombie: 'Hnggg...', translation: null },
+ { zombie: 'Grrraaa...', translation: null },
+ { zombie: 'Uuuhhh...', translation: null },
+ { zombie: 'Aaarrgh...', translation: null }
+ ];
+
+ // Level 5: Keywords visible
+ const level5Phrases = [
+ { zombie: 'Hnggg... HUNGER... grrr...', translation: 'I am hungry...' },
+ { zombie: 'Grrr... DANGER... hnggg...', translation: 'Danger nearby!' },
+ { zombie: 'Uhhh... MASTER... grrr...', translation: 'Looking for master...' },
+ { zombie: 'Aaah... PAIN... hnggg...', translation: 'I am in pain...' },
+ { zombie: 'Grrr... HELP... uhhh...', translation: 'Help me...' },
+ { zombie: 'Hnggg... FRIEND... grrr...', translation: 'You are my friend...' }
+ ];
+
+ // Level 10: Full sentences
+ const level10Phrases = [
+ { zombie: 'I remember... my family...', translation: 'I remember my family before I turned...' },
+ { zombie: 'The darkness... it hurts...', translation: 'The curse is painful...' },
+ { zombie: 'Thank you... for saving me...', translation: 'Thank you for taming me instead of killing me.' },
+ { zombie: 'Enemies... coming from east...', translation: 'I sense enemies approaching from the east!' },
+ { zombie: 'My name was... John...', translation: 'I remember my name was John...' },
+ { zombie: 'Ana... she calls us...', translation: 'Ana\'s Twin Bond resonates with us zombies...' },
+ { zombie: 'The Black Serpent... did this...', translation: 'The Black Serpent Initiative caused this outbreak.' },
+ { zombie: 'I was a farmer... before...', translation: 'I was a farmer before the infection...' },
+ { zombie: 'Danger! Big zombie nearby!', translation: 'WARNING: Boss zombie detected!' },
+ { zombie: 'I protect you... master...', translation: 'I will protect you with my unlife, master.' }
+ ];
+
+ // Special contextual phrases
+ const contextualPhrases = [
+ { context: 'low_health', zombie: 'Hnggg... weak... dying...', translation: 'I am badly hurt!' },
+ { context: 'enemy_near', zombie: 'Grrr! Intruders!', translation: 'Enemies detected!' },
+ { context: 'happy', zombie: 'Grraaa... good... happy...', translation: 'I am happy serving you!' },
+ { context: 'task_complete', zombie: 'Uhhh... done... master...', translation: 'Task completed, master!' },
+ { context: 'hungry', zombie: 'Need... food... hnggg...', translation: 'I need to eat soon...' },
+ { context: 'scared', zombie: 'Aaaah! Fear! Run!', translation: 'Something terrifying is here!' }
+ ];
+
+ this.zombiePhrases.set('level1', level1Phrases);
+ this.zombiePhrases.set('level5', level5Phrases);
+ this.zombiePhrases.set('level10', level10Phrases);
+ this.zombiePhrases.set('contextual', contextualPhrases);
+
+ console.log(`โ
Loaded ${level1Phrases.length + level5Phrases.length + level10Phrases.length + contextualPhrases.length} zombie phrases`);
+ }
+
+ /**
+ * Set communication level
+ */
+ setCommunicationLevel(level) {
+ this.communicationLevel = Math.min(this.maxLevel, Math.max(0, level));
+
+ console.log(`๐ง Communication level: ${this.communicationLevel}/10`);
+
+ this.showNotification({
+ title: 'Zombie Understanding Improved!',
+ text: `๐ง Level ${this.communicationLevel}: ${this.getLevelDescription()}`,
+ icon: '๐ง'
+ });
+ }
+
+ /**
+ * Get level description
+ */
+ getLevelDescription() {
+ if (this.communicationLevel >= 10) {
+ return 'Full sentences! You understand zombies completely!';
+ } else if (this.communicationLevel >= 5) {
+ return 'Keywords visible! You understand basic meanings!';
+ } else {
+ return 'Only groaning... You need more practice!';
+ }
+ }
+
+ /**
+ * Zombie speaks
+ */
+ zombieSpeak(zombieId, context = null) {
+ let phrase;
+
+ // Get appropriate phrase based on level
+ if (context) {
+ phrase = this.getContextualPhrase(context);
+ } else if (this.communicationLevel >= 10) {
+ phrase = this.getRandomPhrase('level10');
+ } else if (this.communicationLevel >= 5) {
+ phrase = this.getRandomPhrase('level5');
+ } else {
+ phrase = this.getRandomPhrase('level1');
+ }
+
+ if (!phrase) return;
+
+ // Show subtitle
+ this.showSubtitle(phrase, zombieId);
+ }
+
+ /**
+ * Get random phrase from level
+ */
+ getRandomPhrase(level) {
+ const phrases = this.zombiePhrases.get(level);
+ if (!phrases || phrases.length === 0) return null;
+
+ return Phaser.Utils.Array.GetRandom(phrases);
+ }
+
+ /**
+ * Get contextual phrase
+ */
+ getContextualPhrase(context) {
+ const phrases = this.zombiePhrases.get('contextual');
+ const found = phrases.filter(p => p.context === context);
+
+ if (found.length === 0) return this.getRandomPhrase('level1');
+
+ return Phaser.Utils.Array.GetRandom(found);
+ }
+
+ /**
+ * Show subtitle
+ */
+ showSubtitle(phrase, zombieId = 'Zombie') {
+ let displayText = phrase.zombie;
+
+ // Add translation if level is high enough
+ if (this.communicationLevel >= 5 && phrase.translation) {
+ displayText += `\n[${phrase.translation}]`;
+ }
+
+ // Show speaker name
+ displayText = `${zombieId}: ${displayText}`;
+
+ this.subtitleText.setText(displayText);
+
+ // Fade in
+ this.scene.tweens.add({
+ targets: this.subtitleContainer,
+ alpha: 1,
+ duration: 300
+ });
+
+ // Auto-hide after 3 seconds
+ if (this.currentSubtitle) {
+ clearTimeout(this.currentSubtitle);
+ }
+
+ this.currentSubtitle = setTimeout(() => {
+ this.hideSubtitle();
+ }, 3000);
+
+ console.log(`๐ฌ ${displayText}`);
+ }
+
+ /**
+ * Hide subtitle
+ */
+ hideSubtitle() {
+ this.scene.tweens.add({
+ targets: this.subtitleContainer,
+ alpha: 0,
+ duration: 300
+ });
+
+ this.currentSubtitle = null;
+ }
+
+ /**
+ * Zombie conversation (interactive)
+ */
+ startConversation(zombieId) {
+ if (this.communicationLevel < 5) {
+ this.showSubtitle({
+ zombie: 'Hnggg... grrr...',
+ translation: null
+ }, zombieId);
+
+ this.showNotification({
+ title: 'Cannot Understand',
+ text: 'Your zombie communication skill is too low!',
+ icon: '๐ง '
+ });
+ return false;
+ }
+
+ console.log(`๐ฌ Conversation with ${zombieId}`);
+
+ // Show conversation phrases
+ const phrases = [
+ 'What is your name?',
+ 'What do you remember?',
+ 'Are you loyal?',
+ 'Do you feel pain?',
+ 'Goodbye'
+ ];
+
+ // TODO: Create actual dialogue UI with choices
+ console.log('Conversation options:', phrases);
+
+ return true;
+ }
+
+ /**
+ * Zombie warning (important messages)
+ */
+ zombieWarning(message, urgency = 'normal') {
+ const urgencyIcons = {
+ low: 'โน๏ธ',
+ normal: 'โ ๏ธ',
+ high: '๐จ',
+ critical: '๐'
+ };
+
+ this.showSubtitle({
+ zombie: message,
+ translation: message
+ }, `${urgencyIcons[urgency]} ZOMBIE ALERT`);
+
+ // Play alert sound for high/critical
+ if (urgency === 'high' || urgency === 'critical') {
+ // TODO: Play alert sound
+ this.scene.cameras.main.shake(200, 0.005);
+ }
+ }
+
+ /**
+ * Level up communication skill
+ */
+ levelUpCommunication() {
+ if (this.communicationLevel >= this.maxLevel) {
+ console.log('๐ง Already at max level!');
+ return false;
+ }
+
+ this.setCommunicationLevel(this.communicationLevel + 1);
+
+ // Show what's unlocked
+ if (this.communicationLevel === 5) {
+ this.showNotification({
+ title: 'Keywords Unlocked!',
+ text: '๐ง You can now see KEYWORDS in zombie speech!',
+ icon: 'โจ'
+ });
+ } else if (this.communicationLevel === 10) {
+ this.showNotification({
+ title: 'Full Understanding!',
+ text: '๐ง You can now understand COMPLETE zombie sentences!',
+ icon: '๐'
+ });
+ }
+
+ return true;
+ }
+
+ /**
+ * Get communication level
+ */
+ getCommunicationLevel() {
+ return this.communicationLevel;
+ }
+
+ /**
+ * Can understand zombie
+ */
+ canUnderstandZombie(requiredLevel = 1) {
+ return this.communicationLevel >= requiredLevel;
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/ZombieEconomySystem.js b/DEBUG_TOTAL_RECOVERY/ZombieEconomySystem.js
new file mode 100644
index 000000000..9d9316bd9
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/ZombieEconomySystem.js
@@ -0,0 +1,569 @@
+/**
+ * ZOMBIE ECONOMY & CITY SANITATION SYSTEM
+ * Mrtva Dolina - Worker Zombies, Smetarji, Redka Darila
+ *
+ * Features:
+ * - Worker Zombies (heavy labor, construction)
+ * - Sanitation System (Smetar cleans city)
+ * - Rare Gift System (unique rewards from zombie work)
+ * - Zombie Maintenance (Brain feeding)
+ * - Contract System (loan zombies to NPCs)
+ * - Happiness impact on city growth
+ */
+
+export class ZombieEconomySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Worker zombie types
+ this.zombieTypes = {
+ scout: {
+ name: 'Zombi Skavt',
+ strength: 30,
+ speed: 60,
+ brainConsumption: 0.5, // per hour
+ tasks: ['scouting', 'light_work']
+ },
+ worker: {
+ name: 'Delovni Zombi',
+ strength: 80,
+ speed: 40,
+ brainConsumption: 1.5, // per hour
+ tasks: ['construction', 'hauling', 'sanitation']
+ },
+ sanitation: {
+ name: 'Smetar Zombi',
+ strength: 50,
+ speed: 50,
+ brainConsumption: 1.0, // per hour
+ tasks: ['cleaning', 'graffiti_removal']
+ }
+ };
+
+ // Active zombies
+ this.activeZombies = [];
+
+ // Contracts (zombies loaned to NPCs)
+ this.activeContracts = [];
+
+ // Rare gifts catalogue
+ this.rareGifts = [
+ { id: 'family_heirloom', name: 'Starinski druลพinski artefakt', rarity: 'legendary', value: 1000 },
+ { id: 'ancient_dye', name: 'Starodavno barvilo', rarity: 'epic', value: 500 },
+ { id: 'rare_seed_pack', name: 'Paket redkih semen', rarity: 'rare', value: 300 },
+ { id: 'kai_memory_photo', name: 'Kaijeva skrita fotografija', rarity: 'legendary', value: 2000 },
+ { id: 'mystical_paint', name: 'Mistiฤno barvilo', rarity: 'epic', value: 600 }
+ ];
+
+ // City cleanliness tracking
+ this.cityTrash = [];
+ this.cityGraffiti = [];
+ this.cleanlinessScore = 50; // 0-100
+
+ // Happiness impact
+ this.citizenHappiness = 50; // 0-100
+
+ this.init();
+ }
+
+ init() {
+ // Listen for trash generation
+ this.scene.events.on('npc:activity', this.onNPCActivity, this);
+ this.scene.events.on('vandal:graffiti', this.onGraffitiCreated, this);
+
+ // Start passive systems
+ this.startBrainConsumption();
+ this.startCleaningSweep();
+
+ console.log('โ
ZombieEconomySystem initialized');
+ }
+
+ /**
+ * RECRUIT ZOMBIE
+ */
+ recruitZombie(type, name = null) {
+ const zombieData = this.zombieTypes[type];
+ if (!zombieData) {
+ console.error(`Unknown zombie type: ${type}`);
+ return null;
+ }
+
+ const zombie = {
+ id: `zombie_${Date.now()}`,
+ type: type,
+ name: name || zombieData.name,
+ strength: zombieData.strength,
+ speed: zombieData.speed,
+ brainLevel: 100, // 0-100, energy/hunger
+ brainConsumption: zombieData.brainConsumption,
+ tasks: zombieData.tasks,
+ currentTask: null,
+ isContracted: false,
+ grumbleTimer: null,
+ x: this.scene.player.x,
+ y: this.scene.player.y,
+ sprite: null
+ };
+
+ // Create sprite
+ zombie.sprite = this.scene.add.sprite(zombie.x, zombie.y, `zombie_${type}`);
+ zombie.sprite.setDepth(5);
+
+ this.activeZombies.push(zombie);
+
+ console.log(`๐ง Recruited: ${zombie.name}`);
+
+ return zombie;
+ }
+
+ /**
+ * FEED ZOMBIE (with brains)
+ */
+ feedZombie(zombieId, brainsAmount = 50) {
+ const zombie = this.activeZombies.find(z => z.id === zombieId);
+ if (!zombie) return false;
+
+ zombie.brainLevel = Math.min(100, zombie.brainLevel + brainsAmount);
+
+ // Happy zombie sound
+ this.scene.sound.play('zombie_satisfied', { volume: 0.5 });
+
+ // Show feedback
+ this.scene.events.emit('show-floating-text', {
+ x: zombie.sprite.x,
+ y: zombie.sprite.y - 30,
+ text: '*munch* ...dobr!',
+ color: '#90EE90'
+ });
+
+ // Stop grumbling
+ if (zombie.grumbleTimer) {
+ clearInterval(zombie.grumbleTimer);
+ zombie.grumbleTimer = null;
+ }
+
+ return true;
+ }
+
+ /**
+ * BRAIN CONSUMPTION - Passive hunger system
+ */
+ startBrainConsumption() {
+ setInterval(() => {
+ this.activeZombies.forEach(zombie => {
+ // Reduce brain level based on consumption rate
+ zombie.brainLevel = Math.max(0, zombie.brainLevel - (zombie.brainConsumption / 60)); // per minute
+
+ // Check hunger
+ if (zombie.brainLevel < 30) {
+ this.zombieHungry(zombie);
+ }
+
+ // Critical hunger - zombie becomes slow
+ if (zombie.brainLevel < 10) {
+ zombie.speed *= 0.5; // 50% slower
+ if (zombie.sprite) {
+ zombie.sprite.setTint(0x666666); // Darken sprite
+ }
+ }
+ });
+ }, 60000); // Every minute
+ }
+
+ zombieHungry(zombie) {
+ if (zombie.grumbleTimer) return; // Already grumbling
+
+ // Start periodic grumbling
+ zombie.grumbleTimer = setInterval(() => {
+ const hungerLines = [
+ 'Braaaaains...',
+ 'Moลพgaaaaani...',
+ 'Hrrrngh... laฤen...',
+ '*godrnja o hrani*'
+ ];
+
+ const line = Phaser.Utils.Array.GetRandom(hungerLines);
+
+ // Show speech bubble
+ this.scene.events.emit('show-speech-bubble', {
+ x: zombie.sprite.x,
+ y: zombie.sprite.y - 50,
+ text: line,
+ duration: 3000
+ });
+
+ // Play hungry sound
+ if (this.scene.sound) {
+ this.scene.sound.play('zombie_groan', { volume: 0.4 });
+ }
+ }, 15000); // Every 15 seconds
+ }
+
+ /**
+ * CONTRACT SYSTEM - Loan zombies to NPCs
+ */
+ createContract(zombieId, npc, task, duration, payment) {
+ const zombie = this.activeZombies.find(z => z.id === zombieId);
+ if (!zombie || zombie.isContracted) {
+ console.warn('Zombie not available for contract');
+ return false;
+ }
+
+ const contract = {
+ id: `contract_${Date.now()}`,
+ zombieId: zombieId,
+ npc: npc, // 'ivan_kovac', 'tehnik', etc.
+ task: task, // 'wall_construction', 'steel_hauling', etc.
+ duration: duration, // in game hours
+ payment: payment, // gold or rare gift
+ startTime: this.scene.gameTime || Date.now(),
+ completed: false
+ };
+
+ zombie.isContracted = true;
+ zombie.currentTask = task;
+ this.activeContracts.push(contract);
+
+ // Move zombie to work site
+ this.moveZombieToWorkSite(zombie, npc);
+
+ console.log(`๐ Contract created: ${zombie.name} โ ${npc} (${task})`);
+
+ // Start work visuals
+ this.startWorkAnimation(zombie, task);
+
+ return contract;
+ }
+
+ moveZombieToWorkSite(zombie, npc) {
+ const workSites = {
+ ivan_kovac: { x: 300, y: 200 }, // Blacksmith
+ tehnik: { x: 500, y: 250 }, // Tech Workshop
+ mayor: { x: 400, y: 300 } // Town Hall
+ };
+
+ const site = workSites[npc] || { x: 400, y: 300 };
+
+ if (zombie.sprite) {
+ this.scene.tweens.add({
+ targets: zombie.sprite,
+ x: site.x,
+ y: site.y,
+ duration: 2000,
+ ease: 'Sine.easeInOut'
+ });
+ }
+ }
+
+ startWorkAnimation(zombie, task) {
+ // Different animations for different tasks
+ const animations = {
+ wall_construction: 'zombie_hammering',
+ steel_hauling: 'zombie_carrying',
+ sanitation: 'zombie_sweeping',
+ graffiti_removal: 'zombie_scrubbing'
+ };
+
+ const anim = animations[task] || 'zombie_working';
+
+ if (zombie.sprite && zombie.sprite.anims) {
+ zombie.sprite.play(anim, true);
+ }
+ }
+
+ /**
+ * COMPLETE CONTRACT - Award payment
+ */
+ completeContract(contractId) {
+ const contract = this.activeContracts.find(c => c.id === contractId);
+ if (!contract || contract.completed) return;
+
+ const zombie = this.activeZombies.find(z => z.id === contract.zombieId);
+
+ contract.completed = true;
+ zombie.isContracted = false;
+ zombie.currentTask = null;
+
+ // Award payment
+ if (contract.payment.type === 'gold') {
+ this.scene.inventorySystem.addGold(contract.payment.amount);
+
+ this.scene.events.emit('show-notification', {
+ title: 'Pogodba Konฤana',
+ message: `${zombie.name} je konฤal delo! +${contract.payment.amount} zlata.`,
+ icon: '๐ฐ',
+ duration: 3000
+ });
+ } else if (contract.payment.type === 'rare_gift') {
+ this.awardRareGift(contract.payment.giftId);
+ }
+
+ // Return zombie to player
+ if (zombie.sprite) {
+ this.scene.tweens.add({
+ targets: zombie.sprite,
+ x: this.scene.player.x + 50,
+ y: this.scene.player.y,
+ duration: 2000,
+ ease: 'Sine.easeOut'
+ });
+ }
+
+ console.log(`โ
Contract completed: ${contract.task}`);
+ }
+
+ /**
+ * RARE GIFT SYSTEM
+ */
+ awardRareGift(giftId = null) {
+ // Random gift if not specified
+ if (!giftId) {
+ const gift = Phaser.Utils.Array.GetRandom(this.rareGifts);
+ giftId = gift.id;
+ }
+
+ const gift = this.rareGifts.find(g => g.id === giftId);
+ if (!gift) return;
+
+ // Add to inventory
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(gift.id, 1);
+ }
+
+ // Show special notification
+ this.scene.events.emit('show-notification', {
+ title: '๐ REDKO DARILO!',
+ message: `Prejel si: ${gift.name} (${gift.rarity})`,
+ icon: 'โจ',
+ duration: 7000,
+ color: this.getRarityColor(gift.rarity)
+ });
+
+ // Play special sound
+ if (this.scene.sound) {
+ this.scene.sound.play('rare_gift_fanfare', { volume: 0.7 });
+ }
+
+ console.log(`๐ Awarded rare gift: ${gift.name}`);
+ }
+
+ getRarityColor(rarity) {
+ const colors = {
+ legendary: '#FFD700', // Gold
+ epic: '#9B59B6', // Purple
+ rare: '#3498DB' // Blue
+ };
+ return colors[rarity] || '#FFFFFF';
+ }
+
+ /**
+ * SANITATION SYSTEM - Trash & Graffiti
+ */
+ onNPCActivity(npcData) {
+ // NPCs randomly drop trash
+ if (Math.random() < 0.1) { // 10% chance
+ this.spawnTrash(npcData.x, npcData.y);
+ }
+ }
+
+ spawnTrash(x, y) {
+ const trashTypes = ['trash_bag', 'litter', 'broken_bottle', 'paper_waste'];
+ const type = Phaser.Utils.Array.GetRandom(trashTypes);
+
+ const trash = this.scene.add.sprite(x, y, type);
+ trash.setDepth(1);
+
+ this.cityTrash.push({
+ id: `trash_${Date.now()}_${Math.random()}`,
+ x: x,
+ y: y,
+ type: type,
+ sprite: trash
+ });
+
+ // Lower cleanliness
+ this.cleanlinessScore = Math.max(0, this.cleanlinessScore - 2);
+ this.updateCityStats();
+ }
+
+ onGraffitiCreated(graffitiData) {
+ const graffiti = this.scene.add.sprite(graffitiData.x, graffitiData.y, 'graffiti_tag');
+ graffiti.setDepth(2);
+
+ this.cityGraffiti.push({
+ id: `graffiti_${Date.now()}`,
+ x: graffitiData.x,
+ y: graffitiData.y,
+ sprite: graffiti
+ });
+
+ // Lower cleanliness
+ this.cleanlinessScore = Math.max(0, this.cleanlinessScore - 5);
+ this.updateCityStats();
+ }
+
+ /**
+ * CLEANING SWEEP - Zombies clean autonomously
+ */
+ startCleaningSweep() {
+ setInterval(() => {
+ // Find sanitation zombies
+ const cleaners = this.activeZombies.filter(z =>
+ z.tasks.includes('cleaning') &&
+ !z.isContracted &&
+ z.brainLevel > 20
+ );
+
+ cleaners.forEach(cleaner => {
+ // Clean nearest trash
+ const nearestTrash = this.findNearestTrash(cleaner.sprite.x, cleaner.sprite.y);
+ if (nearestTrash) {
+ this.cleanTrash(cleaner, nearestTrash);
+ }
+
+ // Clean nearest graffiti
+ const nearestGraffiti = this.findNearestGraffiti(cleaner.sprite.x, cleaner.sprite.y);
+ if (nearestGraffiti) {
+ this.cleanGraffiti(cleaner, nearestGraffiti);
+ }
+ });
+ }, 10000); // Every 10 seconds
+ }
+
+ findNearestTrash(x, y) {
+ let nearest = null;
+ let minDist = Infinity;
+
+ this.cityTrash.forEach(trash => {
+ const dist = Phaser.Math.Distance.Between(x, y, trash.x, trash.y);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = trash;
+ }
+ });
+
+ return minDist < 200 ? nearest : null; // Within 200px
+ }
+
+ findNearestGraffiti(x, y) {
+ let nearest = null;
+ let minDist = Infinity;
+
+ this.cityGraffiti.forEach(graffiti => {
+ const dist = Phaser.Math.Distance.Between(x, y, graffiti.x, graffiti.y);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = graffiti;
+ }
+ });
+
+ return minDist < 200 ? nearest : null;
+ }
+
+ cleanTrash(zombie, trash) {
+ // Move to trash
+ this.scene.tweens.add({
+ targets: zombie.sprite,
+ x: trash.x,
+ y: trash.y,
+ duration: 1000,
+ onComplete: () => {
+ // Play cleaning animation
+ if (zombie.sprite.anims) {
+ zombie.sprite.play('zombie_sweeping', true);
+ }
+
+ // Remove trash after delay
+ setTimeout(() => {
+ trash.sprite.destroy();
+ this.cityTrash = this.cityTrash.filter(t => t.id !== trash.id);
+
+ // Increase cleanliness
+ this.cleanlinessScore = Math.min(100, this.cleanlinessScore + 3);
+ this.updateCityStats();
+
+ console.log(`๐งน ${zombie.name} cleaned trash`);
+ }, 2000);
+ }
+ });
+ }
+
+ cleanGraffiti(zombie, graffiti) {
+ // Move to graffiti
+ this.scene.tweens.add({
+ targets: zombie.sprite,
+ x: graffiti.x,
+ y: graffiti.y,
+ duration: 1000,
+ onComplete: () => {
+ // Play scrubbing animation
+ if (zombie.sprite.anims) {
+ zombie.sprite.play('zombie_scrubbing', true);
+ }
+
+ // Remove graffiti with fade
+ this.scene.tweens.add({
+ targets: graffiti.sprite,
+ alpha: 0,
+ duration: 3000,
+ onComplete: () => {
+ graffiti.sprite.destroy();
+ this.cityGraffiti = this.cityGraffiti.filter(g => g.id !== graffiti.id);
+
+ // Increase cleanliness
+ this.cleanlinessScore = Math.min(100, this.cleanlinessScore + 5);
+ this.updateCityStats();
+
+ console.log(`๐งฝ ${zombie.name} removed graffiti`);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * CITY STATS UPDATE
+ */
+ updateCityStats() {
+ // Calculate happiness based on cleanliness
+ this.citizenHappiness = Math.min(100, this.cleanlinessScore * 1.2);
+
+ // Emit stats update
+ this.scene.events.emit('city:stats_updated', {
+ cleanliness: this.cleanlinessScore,
+ happiness: this.citizenHappiness,
+ trashCount: this.cityTrash.length,
+ graffitiCount: this.cityGraffiti.length
+ });
+
+ // Happiness affects NPC arrival rate
+ if (this.citizenHappiness > 70) {
+ // Faster settler arrivals
+ this.scene.events.emit('city:boost_immigration');
+ }
+ }
+
+ /**
+ * Get zombie status for UI
+ */
+ getZombieStatus() {
+ return this.activeZombies.map(z => ({
+ id: z.id,
+ name: z.name,
+ type: z.type,
+ brainLevel: z.brainLevel,
+ currentTask: z.currentTask,
+ isContracted: z.isContracted
+ }));
+ }
+
+ destroy() {
+ this.activeZombies.forEach(zombie => {
+ if (zombie.sprite) zombie.sprite.destroy();
+ if (zombie.grumbleTimer) clearInterval(zombie.grumbleTimer);
+ });
+
+ this.cityTrash.forEach(trash => trash.sprite.destroy());
+ this.cityGraffiti.forEach(graffiti => graffiti.sprite.destroy());
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/ZombieMinerAutomationSystem.js b/DEBUG_TOTAL_RECOVERY/ZombieMinerAutomationSystem.js
new file mode 100644
index 000000000..36f38f72d
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/ZombieMinerAutomationSystem.js
@@ -0,0 +1,464 @@
+/**
+ * ZOMBIE MINER AUTOMATION SYSTEM
+ * Part of: Mining System Expansion
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Hire zombie miners for passive resource generation
+ * - Assign miners to specific mine depths
+ * - Efficiency & loyalty mechanics
+ * - Automated ore collection
+ * - Zombie equipment upgrades
+ */
+
+class ZombieMinerAutomationSystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Zombie miners
+ this.zombieMiners = [];
+ this.maxZombieMiners = 10;
+ this.zombieMinerCost = 5000;
+
+ // Automation settings
+ this.automationActive = false;
+ this.totalYieldPerHour = 0;
+ this.lastCollectionTime = null;
+
+ // Equipment for zombies
+ this.zombieEquipment = {
+ pickaxe_tier: 1, // 1-5
+ helmet_lamp: false, // Better visibility
+ oxygen_tank: false, // Deeper mining
+ cart: false // Auto-transport
+ };
+ }
+
+ /**
+ * Hire a zombie miner
+ */
+ hireZombieMiner() {
+ if (this.zombieMiners.length >= this.maxZombieMiners) {
+ return {
+ success: false,
+ message: `Maximum ${this.maxZombieMiners} zombie miners allowed!`
+ };
+ }
+
+ if (this.player.money < this.zombieMinerCost) {
+ return {
+ success: false,
+ message: `Need ${this.zombieMinerCost}g to hire zombie miner!`
+ };
+ }
+
+ // Hire zombie
+ this.player.money -= this.zombieMinerCost;
+
+ const miner = {
+ id: `zombie_miner_${this.zombieMiners.length + 1}`,
+ name: this.generateZombieName(),
+ assignedMine: null,
+ assignedDepth: 0,
+ efficiency: 1.0, // 0.5 - 2.0
+ loyalty: 50, // 0-100
+ yieldPerHour: 5, // Base yield
+ level: 1,
+ experience: 0
+ };
+
+ this.zombieMiners.push(miner);
+
+ // Recalculate automation
+ this.updateAutomationYield();
+
+ this.game.showMessage(
+ `Hired ${miner.name}! (${this.zombieMiners.length}/${this.maxZombieMiners})`
+ );
+
+ return { success: true, miner: miner };
+ }
+
+ /**
+ * Generate random zombie miner name
+ */
+ generateZombieName() {
+ const prefixes = ['Grumpy', 'Rusty', 'Dusty', 'Grumbly', 'Moaning', 'Shuffling'];
+ const suffixes = ['Zed', 'Mort', 'Bones', 'Guts', 'Picks', 'Drills'];
+
+ const prefix = Phaser.Utils.Array.GetRandom(prefixes);
+ const suffix = Phaser.Utils.Array.GetRandom(suffixes);
+
+ return `${prefix} ${suffix}`;
+ }
+
+ /**
+ * Assign zombie miner to specific mine & depth
+ */
+ assignZombieMiner(minerId, mineId, depth) {
+ const miner = this.zombieMiners.find(m => m.id === minerId);
+
+ if (!miner) {
+ return { success: false, message: 'Zombie miner not found!' };
+ }
+
+ // Check if mine exists
+ const mine = this.game.miningSystem.getMineInfo(mineId);
+ if (!mine) {
+ return { success: false, message: 'Mine not found!' };
+ }
+
+ // Check if depth is unlocked
+ const maxDepth = this.game.miningSystem.maxDepthReached || 0;
+ if (depth > maxDepth) {
+ return {
+ success: false,
+ message: `Must explore depth ${depth} first!`
+ };
+ }
+
+ // Assign miner
+ miner.assignedMine = mineId;
+ miner.assignedDepth = depth;
+
+ // Update automation
+ this.updateAutomationYield();
+
+ this.game.showMessage(
+ `${miner.name} assigned to ${mine.name} - Depth ${depth}`
+ );
+
+ return { success: true };
+ }
+
+ /**
+ * Unassign zombie miner (return to surface)
+ */
+ unassignZombieMiner(minerId) {
+ const miner = this.zombieMiners.find(m => m.id === minerId);
+
+ if (!miner) {
+ return { success: false };
+ }
+
+ miner.assignedMine = null;
+ miner.assignedDepth = 0;
+
+ // Update automation
+ this.updateAutomationYield();
+
+ this.game.showMessage(
+ `${miner.name} returned to surface.`
+ );
+
+ return { success: true };
+ }
+
+ /**
+ * Update total automation yield
+ */
+ updateAutomationYield() {
+ let totalYield = 0;
+
+ this.zombieMiners.forEach(miner => {
+ if (miner.assignedMine && miner.assignedDepth > 0) {
+ // Base yield
+ let hourlyYield = miner.yieldPerHour;
+
+ // Depth bonus (+10% per 10 levels)
+ const depthBonus = (miner.assignedDepth / 10) * 0.1;
+ hourlyYield *= (1 + depthBonus);
+
+ // Efficiency factor
+ hourlyYield *= miner.efficiency;
+
+ // Loyalty factor (50% loyalty = 0.5x yield, 100% = 1.5x yield)
+ const loyaltyFactor = 0.5 + (miner.loyalty / 100);
+ hourlyYield *= loyaltyFactor;
+
+ // Equipment bonuses
+ if (this.zombieEquipment.pickaxe_tier > 1) {
+ hourlyYield *= (1 + (this.zombieEquipment.pickaxe_tier - 1) * 0.25);
+ }
+ if (this.zombieEquipment.cart) {
+ hourlyYield *= 1.5; // 50% faster collection
+ }
+
+ totalYield += hourlyYield;
+ }
+ });
+
+ this.totalYieldPerHour = Math.floor(totalYield);
+ this.automationActive = (totalYield > 0);
+ }
+
+ /**
+ * Collect automated mining resources
+ */
+ collectAutomatedYield() {
+ if (!this.automationActive) {
+ return {
+ success: false,
+ message: 'No zombie miners assigned!'
+ };
+ }
+
+ // Calculate time since last collection
+ const hoursSinceLastCollection = this.getHoursSinceLastCollection();
+
+ if (hoursSinceLastCollection < 0.1) {
+ return {
+ success: false,
+ message: 'Collected too recently! Wait a bit.'
+ };
+ }
+
+ const resources = {};
+
+ // Collect from each zombie
+ this.zombieMiners.forEach(miner => {
+ if (miner.assignedMine && miner.assignedDepth > 0) {
+ // Get mine info
+ const mine = this.game.miningSystem.getMineInfo(miner.assignedMine);
+
+ // Determine ore type based on depth
+ const oreType = this.getOreTypeForDepth(mine, miner.assignedDepth);
+
+ // Calculate yield
+ const hourlyYield = miner.yieldPerHour * miner.efficiency *
+ (0.5 + miner.loyalty / 100);
+
+ const amount = Math.floor(hourlyYield * hoursSinceLastCollection);
+
+ if (amount > 0) {
+ resources[oreType] = (resources[oreType] || 0) + amount;
+
+ // Grant XP to miner
+ miner.experience += amount;
+ this.checkMinerLevelUp(miner);
+ }
+ }
+ });
+
+ // Add resources to inventory
+ let totalCollected = 0;
+ for (const [ore, amount] of Object.entries(resources)) {
+ this.player.inventory.addItem(ore, amount);
+ totalCollected += amount;
+ }
+
+ // Update last collection time
+ this.lastCollectionTime = this.game.time.currentTime;
+
+ // Show collection message
+ const resourceList = Object.entries(resources)
+ .map(([ore, amount]) => `${amount}x ${ore}`)
+ .join(', ');
+
+ this.game.showMessage(
+ `Collected: ${resourceList} (${hoursSinceLastCollection.toFixed(1)}h)`
+ );
+
+ return {
+ success: true,
+ resources: resources,
+ totalAmount: totalCollected,
+ hours: hoursSinceLastCollection
+ };
+ }
+
+ /**
+ * Get ore type based on mine and depth
+ */
+ getOreTypeForDepth(mine, depth) {
+ // Logic from mine zones
+ if (depth <= 25) return 'copper_ore';
+ if (depth <= 50) return 'iron_ore';
+ if (depth <= 75) return 'gold_ore';
+ return 'diamond_ore';
+ }
+
+ /**
+ * Check if miner levels up
+ */
+ checkMinerLevelUp(miner) {
+ const xpRequired = miner.level * 100;
+
+ if (miner.experience >= xpRequired) {
+ miner.level++;
+ miner.experience = 0;
+
+ // Bonuses per level
+ miner.yieldPerHour += 1;
+ miner.efficiency += 0.05;
+
+ this.game.showMessage(
+ `${miner.name} leveled up! (Lv.${miner.level})`
+ );
+
+ this.updateAutomationYield();
+ }
+ }
+
+ /**
+ * Feed zombie miner (restore loyalty)
+ */
+ feedZombieMiner(minerId, foodType) {
+ const miner = this.zombieMiners.find(m => m.id === minerId);
+
+ if (!miner) {
+ return { success: false };
+ }
+
+ // Check if player has food
+ if (!this.player.inventory.hasItem(foodType)) {
+ return {
+ success: false,
+ message: 'You don\'t have this food!'
+ };
+ }
+
+ // Remove food
+ this.player.inventory.removeItem(foodType, 1);
+
+ // Loyalty bonus based on food quality
+ const loyaltyGain = this.getFoodLoyaltyValue(foodType);
+ miner.loyalty = Math.min(100, miner.loyalty + loyaltyGain);
+
+ this.game.showMessage(
+ `${miner.name} ate ${foodType}. Loyalty +${loyaltyGain} (${miner.loyalty}/100)`
+ );
+
+ // Update automation
+ this.updateAutomationYield();
+
+ return { success: true, loyaltyGain: loyaltyGain };
+ }
+
+ /**
+ * Get food loyalty value
+ */
+ getFoodLoyaltyValue(foodType) {
+ const foodValues = {
+ 'brain': 20, // Best food
+ 'meat': 15,
+ 'bread': 10,
+ 'vegetables': 5
+ };
+
+ return foodValues[foodType] || 5;
+ }
+
+ /**
+ * Upgrade zombie equipment
+ */
+ upgradeEquipment(equipmentType) {
+ const costs = {
+ pickaxe_tier: 3000,
+ helmet_lamp: 2000,
+ oxygen_tank: 2500,
+ cart: 4000
+ };
+
+ const cost = costs[equipmentType];
+
+ if (!cost) {
+ return { success: false, message: 'Invalid equipment!' };
+ }
+
+ // Check if already owned (except pickaxe tiers)
+ if (equipmentType !== 'pickaxe_tier') {
+ if (this.zombieEquipment[equipmentType]) {
+ return {
+ success: false,
+ message: 'Already owned!'
+ };
+ }
+ } else {
+ // Check pickaxe tier limit
+ if (this.zombieEquipment.pickaxe_tier >= 5) {
+ return {
+ success: false,
+ message: 'Pickaxe already max tier!'
+ };
+ }
+ }
+
+ // Check money
+ if (this.player.money < cost) {
+ return {
+ success: false,
+ message: `Need ${cost}g!`
+ };
+ }
+
+ // Purchase
+ this.player.money -= cost;
+
+ if (equipmentType === 'pickaxe_tier') {
+ this.zombieEquipment.pickaxe_tier++;
+ } else {
+ this.zombieEquipment[equipmentType] = true;
+ }
+
+ // Update automation
+ this.updateAutomationYield();
+
+ this.game.showMessage(
+ `Upgraded zombie equipment: ${equipmentType}! ${cost}g`
+ );
+
+ return { success: true };
+ }
+
+ /**
+ * Get hours since last collection
+ */
+ getHoursSinceLastCollection() {
+ if (!this.lastCollectionTime) {
+ this.lastCollectionTime = this.game.time.currentTime;
+ return 0;
+ }
+
+ const secondsElapsed = this.game.time.currentTime - this.lastCollectionTime;
+ return secondsElapsed / 3600;
+ }
+
+ /**
+ * Get automation UI data
+ */
+ getAutomationUIData() {
+ return {
+ zombieMiners: this.zombieMiners,
+ maxZombieMiners: this.maxZombieMiners,
+ totalYieldPerHour: this.totalYieldPerHour,
+ automationActive: this.automationActive,
+ equipment: this.zombieEquipment,
+ canHireMore: this.zombieMiners.length < this.maxZombieMiners,
+ hireCost: this.zombieMinerCost,
+ hoursSinceCollection: this.getHoursSinceLastCollection()
+ };
+ }
+
+ /**
+ * Update (passive decay of loyalty)
+ */
+ update(deltaTime) {
+ // Loyalty slowly decays if zombies are working
+ this.zombieMiners.forEach(miner => {
+ if (miner.assignedMine && miner.assignedDepth > 0) {
+ // Lose 1 loyalty per hour worked
+ const loyaltyLoss = (deltaTime / 3600);
+ miner.loyalty = Math.max(0, miner.loyalty - loyaltyLoss);
+ }
+ });
+
+ // Recalculate if needed
+ if (this.automationActive) {
+ this.updateAutomationYield();
+ }
+ }
+}
+
+
diff --git a/DEBUG_TOTAL_RECOVERY/ZombieScoutLevelingSystem.js b/DEBUG_TOTAL_RECOVERY/ZombieScoutLevelingSystem.js
new file mode 100644
index 000000000..e4da080f2
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/ZombieScoutLevelingSystem.js
@@ -0,0 +1,340 @@
+/**
+ * ZOMBIE SCOUT LEVELING SYSTEM (1-20)
+ * XP progression, stat increases, level-up rewards
+ * Integrates with ZombieScout companion
+ */
+
+export class ZombieScoutLevelingSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Current stats
+ this.currentLevel = 1;
+ this.currentXP = 0;
+ this.skillPoints = 0;
+
+ // Stats per level
+ this.baseStats = {
+ health: 50,
+ attack: 5,
+ digSpeed: 1.0,
+ moveSpeed: 100,
+ defense: 0
+ };
+
+ this.currentStats = { ...this.baseStats };
+
+ // XP curve
+ this.xpRequirements = this.generateXPCurve();
+
+ // Level rewards
+ this.levelRewards = new Map();
+
+ this.init();
+ }
+
+ init() {
+ this.initializeLevelRewards();
+ }
+
+ /**
+ * Generate XP requirements for each level (exponential curve)
+ */
+ generateXPCurve() {
+ const curve = [0]; // Level 1 = 0 XP
+
+ for (let level = 2; level <= 20; level++) {
+ // Formula: baseXP * (level ^ 1.5)
+ const baseXP = 100;
+ const requiredXP = Math.floor(baseXP * Math.pow(level, 1.5));
+ curve.push(requiredXP);
+ }
+
+ return curve;
+ }
+
+ /**
+ * Initialize rewards for each level
+ */
+ initializeLevelRewards() {
+ // Skill points every level
+ for (let level = 2; level <= 20; level++) {
+ this.levelRewards.set(level, {
+ skillPoints: 1,
+ statBonus: this.getStatBonusForLevel(level),
+ specialUnlock: this.getSpecialUnlockForLevel(level)
+ });
+ }
+ }
+
+ /**
+ * Get stat bonus for specific level
+ */
+ getStatBonusForLevel(level) {
+ const bonuses = {
+ health: 10, // +10 HP per level
+ attack: 1, // +1 attack per level
+ digSpeed: 0.05, // +5% dig speed per level
+ defense: 0.5 // +0.5 defense per level
+ };
+
+ // Every 5 levels: bonus move speed
+ if (level % 5 === 0) {
+ bonuses.moveSpeed = 10; // +10 speed at levels 5, 10, 15, 20
+ }
+
+ return bonuses;
+ }
+
+ /**
+ * Get special unlock for milestone levels
+ */
+ getSpecialUnlockForLevel(level) {
+ const unlocks = {
+ 5: { type: 'skill', name: 'Basic Attack', description: 'Zombie can attack enemies' },
+ 10: { type: 'skill', name: 'Speed Dig', description: 'Dig 50% faster for 10 seconds' },
+ 15: { type: 'skill', name: 'Treasure Sense', description: 'Reveal nearby buried items' },
+ 20: { type: 'evolution', name: 'Scout Commander', description: 'Zombie becomes elite unit' }
+ };
+
+ return unlocks[level] || null;
+ }
+
+ /**
+ * Award XP to Zombie Scout
+ */
+ awardXP(amount, source = 'general') {
+ this.currentXP += amount;
+
+ // Visual feedback
+ this.showXPGain(amount, source);
+
+ // Check for level up
+ this.checkLevelUp();
+
+ console.log(`+${amount} XP from ${source} (${this.currentXP}/${this.getRequiredXP()})`);
+ }
+
+ /**
+ * Check if Zombie Scout should level up
+ */
+ checkLevelUp() {
+ if (this.currentLevel >= 20) return; // Max level cap
+
+ const requiredXP = this.getRequiredXP();
+
+ if (this.currentXP >= requiredXP) {
+ this.levelUp();
+ }
+ }
+
+ /**
+ * Level up Zombie Scout
+ */
+ levelUp() {
+ this.currentLevel++;
+ this.currentXP -= this.getRequiredXP(this.currentLevel - 1); // Carry over excess XP
+
+ // Apply stat bonuses
+ const rewards = this.levelRewards.get(this.currentLevel);
+ if (rewards) {
+ // Apply stat increases
+ for (const [stat, bonus] of Object.entries(rewards.statBonus)) {
+ this.currentStats[stat] += bonus;
+ }
+
+ // Award skill points
+ this.skillPoints += rewards.skillPoints;
+
+ // Special unlock
+ if (rewards.specialUnlock) {
+ this.unlockSpecial(rewards.specialUnlock);
+ }
+ }
+
+ // Visual effects
+ this.playLevelUpEffects();
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `Zombie Scout reached Level ${this.currentLevel}!`,
+ 'level_up',
+ { skillPoints: this.skillPoints }
+ );
+
+ // Update Zombie Scout sprite if evolution
+ if (this.currentLevel === 20) {
+ this.evolveScout();
+ }
+
+ console.log(`๐ LEVEL UP! Zombie Scout is now Level ${this.currentLevel}`);
+ console.log(`Stats:`, this.currentStats);
+ console.log(`Skill Points: ${this.skillPoints}`);
+
+ // Quest check
+ this.scene.questSystem?.checkScoutLevel(this.currentLevel);
+ }
+
+ /**
+ * Unlock special ability or feature
+ */
+ unlockSpecial(unlock) {
+ console.log(`โจ Special Unlock: ${unlock.name} - ${unlock.description}`);
+
+ if (unlock.type === 'skill') {
+ this.scene.zombieScoutSkills?.unlockSkill(unlock.name);
+ } else if (unlock.type === 'evolution') {
+ // Mark for visual evolution
+ this.isEvolved = true;
+ }
+
+ this.scene.uiSystem?.showNotification(
+ `Unlocked: ${unlock.name}!`,
+ 'unlock',
+ { description: unlock.description }
+ );
+ }
+
+ /**
+ * Visual evolution at level 20
+ */
+ evolveScout() {
+ // Update sprite to "Commander" version
+ const scout = this.scene.zombieScout;
+ if (scout) {
+ scout.setTexture('zombie_scout_commander');
+ scout.setScale(scout.scale * 1.2); // Slightly larger
+
+ // VFX: Evolution animation
+ this.scene.vfxSystem?.playEffect('evolution', scout.x, scout.y);
+ }
+ }
+
+ /**
+ * Award XP for different actions
+ */
+ onCombatKill(enemyType) {
+ const xpValues = {
+ 'zombie': 10,
+ 'raider': 25,
+ 'mutant': 50,
+ 'boss': 200
+ };
+
+ this.awardXP(xpValues[enemyType] || 10, 'combat');
+ }
+
+ onDigComplete(itemRarity) {
+ const xpValues = {
+ 'common': 5,
+ 'uncommon': 15,
+ 'rare': 30,
+ 'legendary': 100
+ };
+
+ this.awardXP(xpValues[itemRarity] || 5, 'digging');
+ }
+
+ onExplorationProgress() {
+ // Award XP for exploring new areas
+ this.awardXP(20, 'exploration');
+ }
+
+ onQuestComplete(questDifficulty) {
+ const xpValues = {
+ 'easy': 50,
+ 'medium': 100,
+ 'hard': 200,
+ 'story': 300
+ };
+
+ this.awardXP(xpValues[questDifficulty] || 50, 'quest');
+ }
+
+ /**
+ * Get required XP for current or specific level
+ */
+ getRequiredXP(level = this.currentLevel) {
+ return this.xpRequirements[level] || 0;
+ }
+
+ /**
+ * Get XP progress percentage
+ */
+ getXPProgress() {
+ if (this.currentLevel >= 20) return 100;
+
+ const required = this.getRequiredXP();
+ return Math.min(100, Math.floor((this.currentXP / required) * 100));
+ }
+
+ /**
+ * Visual effects
+ */
+ showXPGain(amount, source) {
+ // Floating text above Zombie Scout
+ const scout = this.scene.zombieScout;
+ if (scout) {
+ this.scene.vfxSystem?.floatingText(`+${amount} XP`, scout.x, scout.y - 30, '#FFD700');
+ }
+ }
+
+ playLevelUpEffects() {
+ const scout = this.scene.zombieScout;
+ if (!scout) return;
+
+ // VFX: Level up particles
+ this.scene.vfxSystem?.playEffect('level_up', scout.x, scout.y);
+
+ // SFX: Level up sound
+ this.scene.soundSystem?.play('level_up');
+
+ // Camera shake
+ this.scene.cameras.main.shake(200, 0.005);
+
+ // Flash
+ this.scene.cameras.main.flash(300, 255, 215, 0); // Gold flash
+ }
+
+ /**
+ * Get current level data
+ */
+ getLevelData() {
+ return {
+ level: this.currentLevel,
+ xp: this.currentXP,
+ requiredXP: this.getRequiredXP(),
+ progress: this.getXPProgress(),
+ stats: { ...this.currentStats },
+ skillPoints: this.skillPoints,
+ isMaxLevel: this.currentLevel >= 20,
+ nextUnlock: this.levelRewards.get(this.currentLevel + 1)?.specialUnlock || null
+ };
+ }
+
+ /**
+ * Save/Load support
+ */
+ getSaveData() {
+ return {
+ currentLevel: this.currentLevel,
+ currentXP: this.currentXP,
+ skillPoints: this.skillPoints,
+ currentStats: this.currentStats,
+ isEvolved: this.isEvolved || false
+ };
+ }
+
+ loadSaveData(data) {
+ this.currentLevel = data.currentLevel || 1;
+ this.currentXP = data.currentXP || 0;
+ this.skillPoints = data.skillPoints || 0;
+ this.currentStats = data.currentStats || { ...this.baseStats };
+ this.isEvolved = data.isEvolved || false;
+
+ // Apply visual evolution if loaded at level 20
+ if (this.isEvolved) {
+ this.evolveScout();
+ }
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/ZombieScoutSkills.js b/DEBUG_TOTAL_RECOVERY/ZombieScoutSkills.js
new file mode 100644
index 000000000..19f7fe209
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/ZombieScoutSkills.js
@@ -0,0 +1,420 @@
+/**
+ * ZOMBIE SCOUT SKILLS SYSTEM
+ * Skill tree with active/passive abilities
+ * Integrates with ZombieScoutLevelingSystem
+ */
+
+export class ZombieScoutSkills {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Skill trees
+ this.skills = new Map();
+ this.unlockedSkills = new Set();
+ this.activeSkills = new Map(); // Currently equipped active skills
+
+ // Skill categories
+ this.categories = ['digging', 'combat', 'utility'];
+
+ this.init();
+ }
+
+ init() {
+ this.initializeSkillTree();
+ }
+
+ /**
+ * Initialize all available skills
+ */
+ initializeSkillTree() {
+ // DIGGING SKILLS
+ this.registerSkill({
+ id: 'speed_dig',
+ name: 'Speed Dig',
+ category: 'digging',
+ type: 'active',
+ unlockLevel: 10,
+ cooldown: 30000, // 30 seconds
+ duration: 10000, // 10 seconds
+ description: 'Dig 50% faster for 10 seconds',
+ effect: () => this.activateSpeedDig()
+ });
+
+ this.registerSkill({
+ id: 'deep_scan',
+ name: 'Deep Scan',
+ category: 'digging',
+ type: 'passive',
+ unlockLevel: 5,
+ description: 'Reveal buried items within 5 tiles',
+ effect: () => this.enableDeepScan()
+ });
+
+ this.registerSkill({
+ id: 'treasure_sense',
+ name: 'Treasure Sense',
+ category: 'digging',
+ type: 'active',
+ unlockLevel: 15,
+ cooldown: 60000, // 60 seconds
+ description: 'Reveal all buried treasure on screen',
+ effect: () => this.activateTreasureSense()
+ });
+
+ // COMBAT SKILLS
+ this.registerSkill({
+ id: 'basic_attack',
+ name: 'Claw Swipe',
+ category: 'combat',
+ type: 'active',
+ unlockLevel: 5,
+ cooldown: 2000, // 2 seconds
+ damage: 10,
+ description: 'Basic melee attack dealing 10 damage',
+ effect: () => this.performBasicAttack()
+ });
+
+ this.registerSkill({
+ id: 'leap_attack',
+ name: 'Leap Attack',
+ category: 'combat',
+ type: 'active',
+ unlockLevel: 12,
+ cooldown: 8000, // 8 seconds
+ damage: 25,
+ description: 'Leap at enemy dealing 25 damage',
+ effect: () => this.performLeapAttack()
+ });
+
+ this.registerSkill({
+ id: 'tank_stance',
+ name: 'Undead Resilience',
+ category: 'combat',
+ type: 'passive',
+ unlockLevel: 8,
+ description: '+20% damage resistance',
+ effect: () => this.enableTankStance()
+ });
+
+ // UTILITY SKILLS
+ this.registerSkill({
+ id: 'scout_speed',
+ name: 'Scout Sprint',
+ category: 'utility',
+ type: 'active',
+ unlockLevel: 7,
+ cooldown: 20000, // 20 seconds
+ duration: 5000, // 5 seconds
+ description: 'Move 100% faster for 5 seconds',
+ effect: () => this.activateScoutSpeed()
+ });
+
+ this.registerSkill({
+ id: 'pack_mule',
+ name: 'Pack Mule',
+ category: 'utility',
+ type: 'passive',
+ unlockLevel: 6,
+ description: '+10 inventory slots',
+ effect: () => this.enablePackMule()
+ });
+
+ this.registerSkill({
+ id: 'night_vision',
+ name: 'Night Vision',
+ category: 'utility',
+ type: 'passive',
+ unlockLevel: 10,
+ description: 'See in darkness without penalty',
+ effect: () => this.enableNightVision()
+ });
+ }
+
+ /**
+ * Register a skill in the system
+ */
+ registerSkill(skillData) {
+ this.skills.set(skillData.id, {
+ ...skillData,
+ unlocked: false,
+ active: false,
+ lastUsed: 0
+ });
+ }
+
+ /**
+ * Unlock skill (called when level requirement met)
+ */
+ unlockSkill(skillId) {
+ const skill = this.skills.get(skillId);
+ if (!skill) return false;
+
+ const scoutLevel = this.scene.zombieScoutLeveling?.currentLevel || 1;
+
+ // Check level requirement
+ if (scoutLevel < skill.unlockLevel) {
+ console.warn(`Scout level ${scoutLevel} too low for ${skill.name} (requires ${skill.unlockLevel})`);
+ return false;
+ }
+
+ skill.unlocked = true;
+ this.unlockedSkills.add(skillId);
+
+ // Auto-activate passive skills
+ if (skill.type === 'passive') {
+ skill.effect();
+ }
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `Skill Unlocked: ${skill.name}!`,
+ 'skill_unlock',
+ { description: skill.description }
+ );
+
+ console.log(`โจ Unlocked skill: ${skill.name}`);
+ return true;
+ }
+
+ /**
+ * Use active skill
+ */
+ useSkill(skillId) {
+ const skill = this.skills.get(skillId);
+ if (!skill || !skill.unlocked || skill.type !== 'active') return false;
+
+ // Check cooldown
+ const now = Date.now();
+ const timeSinceLastUse = now - skill.lastUsed;
+
+ if (timeSinceLastUse < skill.cooldown) {
+ const remainingCooldown = Math.ceil((skill.cooldown - timeSinceLastUse) / 1000);
+ console.log(`Skill on cooldown: ${remainingCooldown}s remaining`);
+ return false;
+ }
+
+ // Activate skill
+ skill.lastUsed = now;
+ skill.effect();
+
+ // UI feedback
+ this.scene.uiSystem?.showSkillActivation(skill.name, skill.cooldown);
+
+ console.log(`๐ฅ Activated: ${skill.name}`);
+ return true;
+ }
+
+ /**
+ * SKILL EFFECTS
+ */
+
+ activateSpeedDig() {
+ const scout = this.scene.zombieScout;
+ if (!scout) return;
+
+ scout.digSpeed *= 1.5; // 50% faster
+
+ // VFX
+ this.scene.vfxSystem?.playEffect('speed_buff', scout.x, scout.y);
+
+ // Restore after duration
+ this.scene.time.delayedCall(10000, () => {
+ scout.digSpeed /= 1.5;
+ console.log('Speed Dig expired');
+ });
+ }
+
+ enableDeepScan() {
+ // Enable passive treasure detection
+ this.scene.gameState.buffs.treasure_detection_range = 5;
+ console.log('Deep Scan enabled: 5 tile range');
+ }
+
+ activateTreasureSense() {
+ // Reveal all buried items on screen
+ this.scene.treasureSystem?.revealAllTreasures();
+
+ // VFX: Screen pulse
+ this.scene.cameras.main.flash(500, 255, 215, 0, true);
+
+ console.log('Treasure Sense activated: All treasures revealed');
+ }
+
+ performBasicAttack() {
+ const scout = this.scene.zombieScout;
+ if (!scout) return;
+
+ // Find nearest enemy
+ const enemy = this.findNearestEnemy(scout, 50);
+ if (!enemy) {
+ console.log('No enemy in range');
+ return;
+ }
+
+ // Deal damage
+ enemy.takeDamage(10);
+
+ // VFX
+ this.scene.vfxSystem?.playEffect('claw_swipe', enemy.x, enemy.y);
+ this.scene.soundSystem?.play('zombie_attack');
+
+ console.log('Basic attack: 10 damage');
+ }
+
+ performLeapAttack() {
+ const scout = this.scene.zombieScout;
+ if (!scout) return;
+
+ // Find nearest enemy
+ const enemy = this.findNearestEnemy(scout, 150);
+ if (!enemy) return;
+
+ // Leap animation
+ this.scene.tweens.add({
+ targets: scout,
+ x: enemy.x,
+ y: enemy.y - 20,
+ duration: 300,
+ ease: 'Quad.easeOut',
+ onComplete: () => {
+ // Deal damage on landing
+ enemy.takeDamage(25);
+ this.scene.vfxSystem?.playEffect('impact', enemy.x, enemy.y);
+ this.scene.cameras.main.shake(200, 0.008);
+ }
+ });
+
+ console.log('Leap attack: 25 damage');
+ }
+
+ enableTankStance() {
+ const scout = this.scene.zombieScout;
+ if (scout) {
+ scout.damageResistance = (scout.damageResistance || 0) + 0.2; // +20%
+ console.log('Undead Resilience enabled: +20% damage resistance');
+ }
+ }
+
+ activateScoutSpeed() {
+ const scout = this.scene.zombieScout;
+ if (!scout) return;
+
+ scout.moveSpeed *= 2; // 100% faster
+
+ // VFX: Speed trails
+ this.scene.vfxSystem?.playEffect('speed_trail', scout.x, scout.y, { duration: 5000 });
+
+ // Restore after duration
+ this.scene.time.delayedCall(5000, () => {
+ scout.moveSpeed /= 2;
+ console.log('Scout Sprint expired');
+ });
+ }
+
+ enablePackMule() {
+ this.scene.inventorySystem?.addInventorySlots(10);
+ console.log('Pack Mule enabled: +10 inventory slots');
+ }
+
+ enableNightVision() {
+ this.scene.gameState.buffs.night_vision = true;
+ console.log('Night Vision enabled: Full visibility at night');
+ }
+
+ /**
+ * Helper: Find nearest enemy
+ */
+ findNearestEnemy(from, maxDistance) {
+ let nearest = null;
+ let minDist = maxDistance;
+
+ // Search for enemies in scene
+ const enemies = this.scene.enemies || [];
+
+ enemies.forEach(enemy => {
+ const dist = Phaser.Math.Distance.Between(from.x, from.y, enemy.x, enemy.y);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = enemy;
+ }
+ });
+
+ return nearest;
+ }
+
+ /**
+ * Get unlocked skills by category
+ */
+ getSkillsByCategory(category) {
+ return Array.from(this.skills.values())
+ .filter(s => s.category === category && s.unlocked);
+ }
+
+ /**
+ * Get all unlocked skills
+ */
+ getUnlockedSkills() {
+ return Array.from(this.skills.values()).filter(s => s.unlocked);
+ }
+
+ /**
+ * Check skill availability
+ */
+ isSkillAvailable(skillId) {
+ const skill = this.skills.get(skillId);
+ if (!skill || !skill.unlocked) return false;
+
+ if (skill.type === 'passive') return true;
+
+ const now = Date.now();
+ return (now - skill.lastUsed) >= skill.cooldown;
+ }
+
+ /**
+ * Get skill cooldown remaining
+ */
+ getCooldownRemaining(skillId) {
+ const skill = this.skills.get(skillId);
+ if (!skill) return 0;
+
+ const now = Date.now();
+ const elapsed = now - skill.lastUsed;
+ const remaining = Math.max(0, skill.cooldown - elapsed);
+
+ return Math.ceil(remaining / 1000); // Return in seconds
+ }
+
+ /**
+ * Save/Load
+ */
+ getSaveData() {
+ return {
+ unlockedSkills: Array.from(this.unlockedSkills),
+ skillCooldowns: Array.from(this.skills.entries()).map(([id, skill]) => ({
+ id,
+ lastUsed: skill.lastUsed
+ }))
+ };
+ }
+
+ loadSaveData(data) {
+ this.unlockedSkills = new Set(data.unlockedSkills || []);
+
+ // Restore cooldowns
+ data.skillCooldowns?.forEach(({ id, lastUsed }) => {
+ const skill = this.skills.get(id);
+ if (skill) {
+ skill.lastUsed = lastUsed;
+ skill.unlocked = this.unlockedSkills.has(id);
+ }
+ });
+
+ // Re-apply passive skills
+ this.unlockedSkills.forEach(skillId => {
+ const skill = this.skills.get(skillId);
+ if (skill && skill.type === 'passive') {
+ skill.effect();
+ }
+ });
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/ZombieSystem.js b/DEBUG_TOTAL_RECOVERY/ZombieSystem.js
new file mode 100644
index 000000000..8172976d3
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/ZombieSystem.js
@@ -0,0 +1,875 @@
+/**
+ * ZombieSystem.js
+ * ===============
+ * KRVAVA ลฝETEV - Zombie Worker Management System
+ *
+ * Core Concept:
+ * Player is "Alfa" (hybrid virus) - can tame wild zombies
+ * Zombies can be assigned tasks: farming, mining, gathering, guarding
+ * Zombies level up, specialize, get tired, and eventually decay
+ *
+ * Features:
+ * - Zombie taming (Alfa scent system)
+ * - AI pathfinding & task execution
+ * - Skill specialization (Farmer, Miner, Guard, Gatherer)
+ * - Stamina & decay mechanics
+ * - Grave resting system (regeneration)
+ * - Work commands (follow, farm, mine, gather, guard)
+ *
+ * Uses: zombie_varieties_pack_tiled_1766101086057.tsx
+ * zombie_workers_2x2_grids_1766099189858.tsx
+ * smart_zombies_working_1766097073226.tsx
+ * specialty_zombie_workers_detailed_1766097635926.tsx
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-22
+ */
+
+export default class ZombieSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Zombie registry
+ this.zombies = new Map(); // id -> zombie data
+ this.wildZombies = new Set(); // Untamed zombies
+ this.tamedZombies = new Set(); // Player's zombies
+
+ // Alfa system
+ this.alfaScent = 100; // Player's Alfa power (0-100)
+ this.alfaRange = 150; // Taming range (pixels)
+
+ // Task queues
+ this.taskQueue = new Map(); // zombieId -> task
+
+ // Graves
+ this.graves = new Map(); // position -> grave data
+
+ // Zombie definitions
+ this.zombieTypes = this.defineZombieTypes();
+ this.skillTrees = this.defineSkillTrees();
+
+ // Timing constants
+ this.DECAY_RATE = 0.1; // HP loss per second when not resting
+ this.STAMINA_DRAIN_RATE = 5; // Stamina loss per second when working
+ this.STAMINA_REGEN_RATE = 20; // Stamina gain per second when resting
+ this.GRAVE_REST_BONUS = 2.0; // 2x regen in grave
+
+ console.log('๐ง ZombieSystem initialized - ALFA ACTIVE');
+ }
+
+ defineZombieTypes() {
+ return {
+ basic: {
+ name: 'Basic Zombie',
+ baseHP: 100,
+ baseStamina: 100,
+ baseSpeed: 60,
+ baseWorkSpeed: 1.0,
+ sprite: 'zombie_varieties_pack', // Frame 0
+ tameDifficulty: 1
+ },
+ worker: {
+ name: 'Zombie Worker',
+ baseHP: 120,
+ baseStamina: 150,
+ baseSpeed: 70,
+ baseWorkSpeed: 1.2,
+ sprite: 'zombie_workers_2x2_grids', // Frame 0
+ tameDifficulty: 2
+ },
+ smart: {
+ name: 'Smart Zombie',
+ baseHP: 150,
+ baseStamina: 120,
+ baseSpeed: 80,
+ baseWorkSpeed: 1.5,
+ sprite: 'smart_zombies_working',
+ tameDifficulty: 3,
+ canLearn: true // Levels faster
+ },
+ elite: {
+ name: 'Elite Zombie',
+ baseHP: 200,
+ baseStamina: 200,
+ baseSpeed: 100,
+ baseWorkSpeed: 2.0,
+ sprite: 'elite_zombie',
+ tameDifficulty: 5,
+ canLearn: true,
+ specialAbility: 'multitask' // Can do 2 tasks
+ }
+ };
+ }
+
+ defineSkillTrees() {
+ return {
+ farmer: {
+ name: 'Farmer Zombie',
+ tasks: ['plant', 'harvest', 'water', 'fertilize'],
+ levelBonuses: {
+ 1: { workSpeed: 1.0 },
+ 5: { workSpeed: 1.2, yieldBonus: 1.1 },
+ 10: { workSpeed: 1.5, yieldBonus: 1.3, autoPlant: true },
+ 15: { workSpeed: 2.0, yieldBonus: 1.5, qualityBonus: 1.2 }
+ },
+ sprite: 'specialty_zombie_workers', // Frame 0
+ color: 0x00FF00 // Green tint
+ },
+ miner: {
+ name: 'Miner Zombie',
+ tasks: ['mine', 'quarry', 'smelt'],
+ levelBonuses: {
+ 1: { workSpeed: 1.0 },
+ 5: { workSpeed: 1.2, oreBonus: 1.1 },
+ 10: { workSpeed: 1.5, oreBonus: 1.3, rareOreChance: 0.1 },
+ 15: { workSpeed: 2.0, oreBonus: 1.5, gemChance: 0.2 }
+ },
+ sprite: 'specialty_zombie_workers', // Frame 1
+ color: 0x888888 // Gray tint
+ },
+ gatherer: {
+ name: 'Gatherer Zombie',
+ tasks: ['gather', 'forage', 'loot'],
+ levelBonuses: {
+ 1: { workSpeed: 1.0 },
+ 5: { workSpeed: 1.2, gatherRadius: 1.2 },
+ 10: { workSpeed: 1.5, gatherRadius: 1.5, rareItemChance: 0.1 },
+ 15: { workSpeed: 2.0, gatherRadius: 2.0, doubleGather: 0.3 }
+ },
+ sprite: 'specialty_zombie_workers', // Frame 2
+ color: 0xFFFF00 // Yellow tint
+ },
+ guard: {
+ name: 'Guard Zombie',
+ tasks: ['guard', 'patrol', 'attack'],
+ levelBonuses: {
+ 1: { damage: 10, defense: 5 },
+ 5: { damage: 20, defense: 10, detectRange: 1.2 },
+ 10: { damage: 35, defense: 20, detectRange: 1.5, counterAttack: true },
+ 15: { damage: 50, defense: 30, detectRange: 2.0, areaAttack: true }
+ },
+ sprite: 'specialty_zombie_workers', // Frame 3
+ color: 0xFF0000 // Red tint
+ }
+ };
+ }
+
+ // ===== ZOMBIE SPAWNING =====
+
+ spawnWildZombie(x, y, type = 'basic') {
+ const zombieType = this.zombieTypes[type];
+ if (!zombieType) return null;
+
+ const zombie = {
+ id: this.generateId(),
+ type: type,
+ state: 'wild', // wild, tamed, working, resting, decaying
+
+ // Position
+ x: x,
+ y: y,
+
+ // Stats
+ hp: zombieType.baseHP,
+ maxHP: zombieType.baseHP,
+ stamina: zombieType.baseStamina,
+ maxStamina: zombieType.baseStamina,
+
+ // Progression
+ level: 1,
+ xp: 0,
+ xpToNextLevel: 100,
+ specialization: null, // farmer, miner, gatherer, guard
+
+ // Work
+ currentTask: null,
+ workSpeed: zombieType.baseWorkSpeed,
+ efficiency: 1.0,
+
+ // Decay
+ decayTimer: 24 * 60 * 60 * 1000, // 24 hours to decay
+ decayRate: this.DECAY_RATE,
+
+ // Behavior
+ speed: zombieType.baseSpeed,
+ aggression: 0.3, // Wild zombies slightly aggressive
+ loyalty: 0, // Increases when tamed
+
+ // Visual
+ sprite: null,
+ tint: 0xFFFFFF,
+
+ // Timestamps
+ spawnTime: Date.now(),
+ lastWorked: null,
+ lastRested: null
+ };
+
+ // Create sprite
+ zombie.sprite = this.createZombieSprite(zombie, zombieType);
+
+ // Add to registry
+ this.zombies.set(zombie.id, zombie);
+ this.wildZombies.add(zombie.id);
+
+ console.log(`๐ง Spawned wild ${zombieType.name} at (${x}, ${y})`);
+ return zombie;
+ }
+
+ createZombieSprite(zombie, zombieType) {
+ const sprite = this.scene.add.sprite(zombie.x, zombie.y, zombieType.sprite);
+
+ sprite.setData('zombieId', zombie.id);
+ sprite.setInteractive();
+
+ // Physics
+ this.scene.physics.add.existing(sprite);
+ sprite.body.setCollideWorldBounds(true);
+
+ // Click handler
+ sprite.on('pointerdown', () => {
+ this.onZombieClicked(zombie);
+ });
+
+ // Health bar
+ this.createHealthBar(sprite);
+
+ return sprite;
+ }
+
+ createHealthBar(sprite) {
+ const bar = this.scene.add.graphics();
+ bar.fillStyle(0xFF0000);
+ bar.fillRect(-20, -30, 40, 4);
+ sprite.healthBar = bar;
+ sprite.healthBar.x = sprite.x;
+ sprite.healthBar.y = sprite.y;
+ }
+
+ // ===== ALFA SYSTEM (TAMING) =====
+
+ canTameZombie(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) {
+ return { canTame: false, reason: 'Zombie not found' };
+ }
+
+ if (zombie.state !== 'wild') {
+ return { canTame: false, reason: 'Already tamed' };
+ }
+
+ // Check distance (Alfa scent range)
+ const distance = Phaser.Math.Distance.Between(
+ this.scene.player.x, this.scene.player.y,
+ zombie.x, zombie.y
+ );
+
+ if (distance > this.alfaRange) {
+ return { canTame: false, reason: 'Too far - get closer!' };
+ }
+
+ // Check Alfa power
+ const zombieType = this.zombieTypes[zombie.type];
+ const requiredAlfaPower = zombieType.tameDifficulty * 15;
+
+ if (this.alfaScent < requiredAlfaPower) {
+ return {
+ canTame: false,
+ reason: `Need ${requiredAlfaPower} Alfa power (have ${this.alfaScent})`
+ };
+ }
+
+ return { canTame: true };
+ }
+
+ tameZombie(zombieId) {
+ const check = this.canTameZombie(zombieId);
+ if (!check.canTame) {
+ this.scene.uiSystem?.showError(check.reason);
+ return false;
+ }
+
+ const zombie = this.zombies.get(zombieId);
+ const zombieType = this.zombieTypes[zombie.type];
+
+ // Taming animation
+ if (this.scene.particleSystem) {
+ this.scene.particleSystem.createTamingEffect(zombie.x, zombie.y);
+ }
+
+ // Update zombie state
+ zombie.state = 'tamed';
+ zombie.loyalty = 50; // Starts at 50, increases with work
+ zombie.aggression = 0;
+
+ // Remove from wild, add to tamed
+ this.wildZombies.delete(zombieId);
+ this.tamedZombies.add(zombieId);
+
+ // Visual feedback
+ zombie.sprite.setTint(0x00FF88); // Green tint for tamed
+
+ // Notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ง Zombie Tamed!',
+ message: `${zombieType.name} joined your workforce!`,
+ icon: 'zombie',
+ duration: 4000,
+ color: '#00FF00'
+ });
+
+ // Emit event
+ this.scene.events.emit('zombieTamed', { zombie });
+
+ // Consume Alfa power
+ const cost = zombieType.tameDifficulty * 10;
+ this.alfaScent = Math.max(0, this.alfaScent - cost);
+
+ console.log(`๐ง Tamed ${zombieType.name} - Loyalty: ${zombie.loyalty}`);
+ return true;
+ }
+
+ // ===== TASK ASSIGNMENT =====
+
+ assignTask(zombieId, task) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie || zombie.state !== 'tamed') {
+ return false;
+ }
+
+ // Check if zombie has specialization for this task
+ if (zombie.specialization) {
+ const spec = this.skillTrees[zombie.specialization];
+ if (!spec.tasks.includes(task.type)) {
+ this.scene.uiSystem?.showError(`This zombie specializes in ${zombie.specialization} tasks!`);
+ return false;
+ }
+ }
+
+ // Assign task
+ zombie.currentTask = task;
+ zombie.state = 'working';
+ this.taskQueue.set(zombieId, task);
+
+ console.log(`๐ง ${zombie.id} assigned task:`, task.type);
+ return true;
+ }
+
+ executeTask(zombie, delta) {
+ const task = zombie.currentTask;
+ if (!task) return;
+
+ const deltaSeconds = delta / 1000;
+
+ // Drain stamina
+ zombie.stamina -= this.STAMINA_DRAIN_RATE * deltaSeconds;
+
+ // If exhausted, stop working
+ if (zombie.stamina <= 0) {
+ zombie.state = 'idle';
+ zombie.currentTask = null;
+ this.scene.uiSystem?.showNotification({
+ title: 'Zombie Exhausted!',
+ message: `Zombie ${zombie.id} needs rest`,
+ icon: 'zombie',
+ duration: 2000
+ });
+ return;
+ }
+
+ // Execute task based on type
+ switch (task.type) {
+ case 'farm':
+ this.executeFarmTask(zombie, task, deltaSeconds);
+ break;
+ case 'mine':
+ this.executeMineTask(zombie, task, deltaSeconds);
+ break;
+ case 'gather':
+ this.executeGatherTask(zombie, task, deltaSeconds);
+ break;
+ case 'guard':
+ this.executeGuardTask(zombie, task, deltaSeconds);
+ break;
+ }
+ }
+
+ executeFarmTask(zombie, task, deltaSeconds) {
+ // Move to farm plot
+ this.moveTowardsTarget(zombie, task.targetX, task.targetY);
+
+ // If close enough, work
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, task.targetX, task.targetY
+ );
+
+ if (distance < 20) {
+ task.progress = (task.progress || 0) + (zombie.workSpeed * deltaSeconds);
+
+ // Complete task
+ if (task.progress >= task.requiredProgress) {
+ this.completeTask(zombie, task);
+ }
+ }
+ }
+
+ executeMineTask(zombie, task, deltaSeconds) {
+ this.moveTowardsTarget(zombie, task.targetX, task.targetY);
+
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, task.targetX, task.targetY
+ );
+
+ if (distance < 20) {
+ task.progress = (task.progress || 0) + (zombie.workSpeed * 0.8 * deltaSeconds);
+
+ if (task.progress >= task.requiredProgress) {
+ this.completeTask(zombie, task);
+ }
+ }
+ }
+
+ executeGatherTask(zombie, task, deltaSeconds) {
+ this.moveTowardsTarget(zombie, task.targetX, task.targetY);
+
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, task.targetX, task.targetY
+ );
+
+ if (distance < 30) {
+ task.progress = (task.progress || 0) + (zombie.workSpeed * 1.2 * deltaSeconds);
+
+ if (task.progress >= task.requiredProgress) {
+ this.completeTask(zombie, task);
+ }
+ }
+ }
+
+ executeGuardTask(zombie, task, deltaSeconds) {
+ // Patrol area
+ if (!task.patrolTarget || zombie.reachedPatrol) {
+ task.patrolTarget = {
+ x: task.centerX + Phaser.Math.Between(-task.radius, task.radius),
+ y: task.centerY + Phaser.Math.Between(-task.radius, task.radius)
+ };
+ zombie.reachedPatrol = false;
+ }
+
+ this.moveTowardsTarget(zombie, task.patrolTarget.x, task.patrolTarget.y);
+
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, task.patrolTarget.x, task.patrolTarget.y
+ );
+
+ if (distance < 10) {
+ zombie.reachedPatrol = true;
+ }
+
+ // Check for enemies
+ this.detectEnemies(zombie, task.radius);
+ }
+
+ completeTask(zombie, task) {
+ // Award XP
+ const xpGain = task.xpReward || 20;
+ this.addXP(zombie, xpGain);
+
+ // Increase loyalty
+ zombie.loyalty = Math.min(100, zombie.loyalty + 1);
+
+ // Clear task
+ zombie.currentTask = null;
+ zombie.state = 'idle';
+ this.taskQueue.delete(zombie.id);
+
+ // Emit event
+ this.scene.events.emit('zombieTaskComplete', { zombie, task });
+
+ console.log(`โ
Zombie ${zombie.id} completed ${task.type} task (+${xpGain} XP)`);
+ }
+
+ // ===== LEVELING & SPECIALIZATION =====
+
+ addXP(zombie, amount) {
+ zombie.xp += amount;
+
+ // Check for level up
+ while (zombie.xp >= zombie.xpToNextLevel) {
+ this.levelUp(zombie);
+ }
+ }
+
+ levelUp(zombie) {
+ zombie.level++;
+ zombie.xp -= zombie.xpToNextLevel;
+ zombie.xpToNextLevel = Math.floor(zombie.xpToNextLevel * 1.5);
+
+ // Increase stats
+ zombie.maxHP += 10;
+ zombie.hp = zombie.maxHP;
+ zombie.maxStamina += 10;
+ zombie.stamina = zombie.maxStamina;
+ zombie.workSpeed += 0.1;
+
+ // Apply specialization bonuses
+ if (zombie.specialization) {
+ const spec = this.skillTrees[zombie.specialization];
+ const levelBonuses = spec.levelBonuses[zombie.level];
+
+ if (levelBonuses) {
+ Object.assign(zombie, levelBonuses);
+ }
+ }
+
+ // Notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ง LEVEL UP!',
+ message: `Zombie ${zombie.id} reached level ${zombie.level}!`,
+ icon: 'zombie',
+ duration: 3000,
+ color: '#FFD700'
+ });
+
+ console.log(`๐ Zombie ${zombie.id} leveled up to ${zombie.level}!`);
+ }
+
+ specializeZombie(zombieId, specialization) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return false;
+
+ if (zombie.specialization) {
+ this.scene.uiSystem?.showError('Already specialized!');
+ return false;
+ }
+
+ if (zombie.level < 3) {
+ this.scene.uiSystem?.showError('Zombie must be level 3+ to specialize');
+ return false;
+ }
+
+ const spec = this.skillTrees[specialization];
+ zombie.specialization = specialization;
+ zombie.sprite.setTint(spec.color);
+
+ this.scene.uiSystem?.showNotification({
+ title: 'โก Specialization Unlocked!',
+ message: `Zombie is now a ${spec.name}!`,
+ icon: 'zombie',
+ duration: 4000,
+ color: '#00FFFF'
+ });
+
+ console.log(`โก Zombie ${zombie.id} specialized as ${spec.name}`);
+ return true;
+ }
+
+ // ===== DECAY SYSTEM =====
+
+ updateDecay(zombie, delta) {
+ if (zombie.state === 'resting') return; // No decay when resting
+
+ const deltaSeconds = delta / 1000;
+
+ // Drain decay timer
+ zombie.decayTimer -= deltaSeconds * 1000;
+
+ // If timer expired, start losing HP
+ if (zombie.decayTimer <= 0) {
+ zombie.hp -= zombie.decayRate * deltaSeconds;
+
+ // Death check
+ if (zombie.hp <= 0) {
+ this.zombieDeath(zombie);
+ }
+ }
+ }
+
+ zombieDeath(zombie) {
+ console.log(`๐ Zombie ${zombie.id} has decayed completely`);
+
+ // Drop fertilizer (good for farming!)
+ if (this.scene.farmingSystem) {
+ this.scene.farmingSystem.spawnFertilizer(zombie.x, zombie.y, 5);
+ }
+
+ // Award player XP
+ if (this.scene.player) {
+ this.scene.player.addXP?.(zombie.level * 10);
+ }
+
+ // Remove zombie
+ zombie.sprite?.destroy();
+ zombie.healthBar?.destroy();
+ this.zombies.delete(zombie.id);
+ this.tamedZombies.delete(zombie.id);
+
+ // Notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ Zombie Decayed',
+ message: `Zombie turned to fertilizer (+${zombie.level * 10} XP)`,
+ icon: 'zombie',
+ duration: 3000
+ });
+ }
+
+ // ===== GRAVE SYSTEM =====
+
+ createGrave(x, y) {
+ const graveId = `${x}_${y}`;
+
+ if (this.graves.has(graveId)) {
+ this.scene.uiSystem?.showError('Grave already exists here!');
+ return false;
+ }
+
+ // Check if player has materials
+ if (this.scene.recipeSystem) {
+ const canCraft = this.scene.recipeSystem.canCraft('grave');
+ if (!canCraft.canCraft) {
+ this.scene.uiSystem?.showError(canCraft.reason);
+ return false;
+ }
+ }
+
+ const grave = {
+ id: graveId,
+ x: x,
+ y: y,
+ occupied: false,
+ zombieId: null,
+ sprite: this.scene.add.sprite(x, y, 'gravestone')
+ };
+
+ this.graves.set(graveId, grave);
+
+ console.log(`โฐ๏ธ Created grave at (${x}, ${y})`);
+ return true;
+ }
+
+ zombieRest(zombieId, graveId) {
+ const zombie = this.zombies.get(zombieId);
+ const grave = this.graves.get(graveId);
+
+ if (!zombie || !grave) return false;
+
+ if (grave.occupied) {
+ this.scene.uiSystem?.showError('Grave is occupied!');
+ return false;
+ }
+
+ // Move zombie to grave
+ zombie.state = 'resting';
+ zombie.sprite.setPosition(grave.x, grave.y);
+ zombie.sprite.setAlpha(0.5); // Semi-transparent
+
+ grave.occupied = true;
+ grave.zombieId = zombieId;
+
+ console.log(`๐ด Zombie ${zombieId} resting in grave`);
+ return true;
+ }
+
+ updateResting(zombie, delta) {
+ const deltaSeconds = delta / 1000;
+
+ // Regenerate stamina (2x faster in grave)
+ zombie.stamina = Math.min(
+ zombie.maxStamina,
+ zombie.stamina + this.STAMINA_REGEN_RATE * this.GRAVE_REST_BONUS * deltaSeconds
+ );
+
+ // Slow HP regen
+ zombie.hp = Math.min(
+ zombie.maxHP,
+ zombie.hp + 2 * deltaSeconds
+ );
+
+ // Restore decay timer
+ zombie.decayTimer += 1000 * deltaSeconds; // Add 1 second per second resting
+
+ // If fully rested, wake up
+ if (zombie.stamina >= zombie.maxStamina) {
+ this.wakeUpZombie(zombie);
+ }
+ }
+
+ wakeUpZombie(zombie) {
+ zombie.state = 'idle';
+ zombie.sprite.setAlpha(1.0);
+
+ // Find and free grave
+ for (const [graveId, grave] of this.graves.entries()) {
+ if (grave.zombieId === zombie.id) {
+ grave.occupied = false;
+ grave.zombieId = null;
+ break;
+ }
+ }
+
+ console.log(`๐ Zombie ${zombie.id} woke up refreshed!`);
+ }
+
+ // ===== MOVEMENT & PATHFINDING =====
+
+ moveTowardsTarget(zombie, targetX, targetY) {
+ const angle = Phaser.Math.Angle.Between(zombie.x, zombie.y, targetX, targetY);
+
+ const velocityX = Math.cos(angle) * zombie.speed;
+ const velocityY = Math.sin(angle) * zombie.speed;
+
+ zombie.sprite.body.setVelocity(velocityX, velocityY);
+
+ // Update position
+ zombie.x = zombie.sprite.x;
+ zombie.y = zombie.sprite.y;
+ }
+
+ detectEnemies(zombie, radius) {
+ // Guard zombies detect and attack enemies
+ if (!this.scene.enemies) return;
+
+ this.scene.enemies.getChildren().forEach(enemy => {
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, enemy.x, enemy.y
+ );
+
+ if (distance < radius) {
+ this.zombieAttack(zombie, enemy);
+ }
+ });
+ }
+
+ zombieAttack(zombie, enemy) {
+ const spec = zombie.specialization === 'guard' ?
+ this.skillTrees.guard.levelBonuses[zombie.level] : null;
+
+ const damage = spec ? spec.damage : 10;
+ enemy.takeDamage?.(damage);
+
+ // Gain XP for combat
+ this.addXP(zombie, 5);
+ }
+
+ // ===== COMMANDS =====
+
+ commandFollow(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ zombie.currentTask = {
+ type: 'follow',
+ targetX: this.scene.player.x,
+ targetY: this.scene.player.y
+ };
+ zombie.state = 'following';
+ }
+
+ commandGuardArea(zombieId, x, y, radius = 100) {
+ this.assignTask(zombieId, {
+ type: 'guard',
+ centerX: x,
+ centerY: y,
+ radius: radius,
+ requiredProgress: Infinity // Never ends
+ });
+ }
+
+ // ===== HELPER FUNCTIONS =====
+
+ generateId() {
+ return `zombie_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+ }
+
+ onZombieClicked(zombie) {
+ this.scene.events.emit('zombieSelected', { zombie });
+ // Show zombie info UI
+ }
+
+ // ===== GETTERS =====
+
+ getTamedZombies() {
+ return Array.from(this.tamedZombies).map(id => this.zombies.get(id));
+ }
+
+ getWorkingZombies() {
+ return this.getTamedZombies().filter(z => z.state === 'working');
+ }
+
+ getIdleZombies() {
+ return this.getTamedZombies().filter(z => z.state === 'idle');
+ }
+
+ getZombiesBySpecialization(spec) {
+ return this.getTamedZombies().filter(z => z.specialization === spec);
+ }
+
+ // ===== UPDATE =====
+
+ update(time, delta) {
+ for (const [id, zombie] of this.zombies.entries()) {
+ // Update based on state
+ switch (zombie.state) {
+ case 'working':
+ this.executeTask(zombie, delta);
+ this.updateDecay(zombie, delta);
+ break;
+
+ case 'resting':
+ this.updateResting(zombie, delta);
+ break;
+
+ case 'following':
+ this.moveTowardsTarget(zombie, this.scene.player.x, this.scene.player.y);
+ this.updateDecay(zombie, delta);
+ break;
+
+ case 'idle':
+ case 'tamed':
+ this.updateDecay(zombie, delta);
+ break;
+
+ case 'wild':
+ // Wild zombies wander
+ if (Math.random() < 0.01) {
+ const wanderX = zombie.x + Phaser.Math.Between(-50, 50);
+ const wanderY = zombie.y + Phaser.Math.Between(-50, 50);
+ this.moveTowardsTarget(zombie, wanderX, wanderY);
+ }
+ break;
+ }
+
+ // Update health bar
+ if (zombie.healthBar && zombie.sprite) {
+ zombie.healthBar.x = zombie.sprite.x;
+ zombie.healthBar.y = zombie.sprite.y;
+
+ const healthPercent = zombie.hp / zombie.maxHP;
+ zombie.healthBar.clear();
+ zombie.healthBar.fillStyle(healthPercent > 0.5 ? 0x00FF00 : 0xFF0000);
+ zombie.healthBar.fillRect(-20, -30, 40 * healthPercent, 4);
+ }
+ }
+
+ // Regenerate Alfa scent slowly
+ this.alfaScent = Math.min(100, this.alfaScent + 0.01 * (delta / 1000));
+ }
+
+ // ===== CLEANUP =====
+
+ destroy() {
+ for (const zombie of this.zombies.values()) {
+ zombie.sprite?.destroy();
+ zombie.healthBar?.destroy();
+ }
+
+ for (const grave of this.graves.values()) {
+ grave.sprite?.destroy();
+ }
+
+ this.zombies.clear();
+ this.wildZombies.clear();
+ this.tamedZombies.clear();
+ this.graves.clear();
+ this.taskQueue.clear();
+ }
+}
diff --git a/DEBUG_TOTAL_RECOVERY/ZombieWorkerSystem.js b/DEBUG_TOTAL_RECOVERY/ZombieWorkerSystem.js
new file mode 100644
index 000000000..51ed3ca3d
--- /dev/null
+++ b/DEBUG_TOTAL_RECOVERY/ZombieWorkerSystem.js
@@ -0,0 +1,239 @@
+/**
+ * ZOMBIE WORKER AI SYSTEM
+ * Tamed zombies lahko opravljajo delo (farming, mining)
+ */
+class ZombieWorkerSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.workers = [];
+ }
+
+ assignWork(zombie, workType, workRadius = 5) {
+ if (!zombie.isTamed) {
+ console.warn('โ ๏ธ Cannot assign work to untamed zombie!');
+ return false;
+ }
+
+ zombie.workerData = {
+ type: workType,
+ radius: workRadius,
+ centerX: zombie.gridX,
+ centerY: zombie.gridY,
+ energy: 100,
+ decayRate: 0.5,
+ workTimer: 0,
+ workInterval: 5000,
+ status: 'IDLE',
+ inventory: {
+ seeds: 0,
+ hoe: 0,
+ watering_can: 0,
+ pickaxe: 0
+ }
+ };
+
+ this.workers.push(zombie);
+ console.log(`๐ง Assigned ${zombie.type} as ${workType} worker`);
+ return true;
+ }
+
+ removeWorker(zombie) {
+ const index = this.workers.indexOf(zombie);
+ if (index > -1) {
+ this.workers.splice(index, 1);
+ zombie.workerData = null;
+ }
+ }
+
+ update(delta) {
+ this.workers = this.workers.filter(w =>
+ this.scene.npcs.includes(w) && w.hp > 0
+ );
+
+ for (const worker of this.workers) {
+ if (!worker.workerData) continue;
+
+ this.applyDecay(worker, delta);
+ worker.workerData.workTimer += delta;
+
+ if (worker.workerData.workTimer >= worker.workerData.workInterval) {
+ worker.workerData.workTimer = 0;
+
+ if (worker.workerData.type === 'FARM') {
+ this.performFarmWork(worker);
+ } else if (worker.workerData.type === 'MINE') {
+ this.performMineWork(worker);
+ } else if (worker.workerData.type === 'CLEAR') {
+ this.performClearWork(worker);
+ }
+ }
+ }
+ }
+
+ applyDecay(zombie, delta) {
+ const wd = zombie.workerData;
+ wd.energy -= (wd.decayRate * delta) / 1000;
+
+ if (wd.energy <= 0) {
+ wd.energy = 0;
+ zombie.hp -= (wd.decayRate * delta) / 1000;
+ if (zombie.sprite) zombie.sprite.setTint(0x666666);
+ }
+
+ if (zombie.hp <= 0) this.onWorkerDeath(zombie);
+ }
+
+ performFarmWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ const farming = this.scene.farmingSystem;
+ if (!terrain || !farming) return;
+
+ // 1. Water/Harvest existing crops
+ if (terrain.cropsMap) {
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.cropsMap.has(key)) {
+ const crop = terrain.cropsMap.get(key);
+
+ if (!crop.isWatered) {
+ farming.waterCrop(wd.centerX + dx, wd.centerY + dy);
+ console.log(`๐ง๐ง Worker watered`);
+ wd.status = 'WORKING';
+ return;
+ }
+
+ if (crop.stage >= 4) {
+ farming.harvest(wd.centerX + dx, wd.centerY + dy);
+ console.log(`๐ง๐พ Worker harvested`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // 2. Plant on empty tilled soil (if has seeds)
+ if (wd.inventory.seeds > 0) {
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const tile = terrain.getTile(wd.centerX + dx, wd.centerY + dy);
+ if (tile && tile.isTilled && !tile.hasCrop) {
+ farming.plant(wd.centerX + dx, wd.centerY + dy, 'wheat');
+ wd.inventory.seeds--;
+ console.log(`๐ง๐ฑ Worker planted (Seeds: ${wd.inventory.seeds})`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+
+ // 3. Till grass/dirt
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const tile = terrain.getTile(wd.centerX + dx, wd.centerY + dy);
+ const typeName = tile?.type?.name || tile?.type;
+
+ if ((typeName === 'grass' || typeName === 'dirt') && !tile.isTilled) {
+ tile.isTilled = true;
+ if (tile.sprite) tile.sprite.setTint(0x8B4513);
+ console.log(`๐ง๐จ Worker tilled soil`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+
+ wd.status = 'IDLE';
+ }
+
+ performMineWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ if (!terrain || !terrain.decorationsMap) return;
+
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.decorationsMap.has(key)) {
+ const decor = terrain.decorationsMap.get(key);
+ if (decor.type === 'stone' || decor.type.includes('rock')) {
+ terrain.removeDecoration(wd.centerX + dx, wd.centerY + dy);
+
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem('stone', 1);
+ }
+
+ console.log(`๐งโ๏ธ Worker mined stone`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+
+ wd.status = 'IDLE';
+ }
+
+ performClearWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ if (!terrain || !terrain.decorationsMap) return;
+
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.decorationsMap.has(key)) {
+ const decor = terrain.decorationsMap.get(key);
+ const t = decor.type;
+
+ // Clear trees, bushes, logs, rocks
+ if (t.startsWith('tree') || t.startsWith('bush') || t === 'fallen_log' || t === 'stone' || t.startsWith('small_rock')) {
+ terrain.removeDecoration(wd.centerX + dx, wd.centerY + dy);
+
+ // Give some resources
+ if (this.scene.inventorySystem) {
+ if (t.startsWith('tree') || t === 'fallen_log' || t.startsWith('bush')) {
+ this.scene.inventorySystem.addItem('wood', 1);
+ } else {
+ this.scene.inventorySystem.addItem('stone', 1);
+ }
+ }
+
+ console.log(`๐ง๐ช Worker CLEARED ${t}`);
+ wd.status = 'WORKING';
+ return; // One per tick
+ }
+ }
+ }
+ }
+ wd.status = 'IDLE';
+ }
+
+ onWorkerDeath(zombie) {
+ console.log(`๐ Worker died at ${zombie.gridX},${zombie.gridY}`);
+
+ if (this.scene.interactionSystem && this.scene.interactionSystem.spawnLoot) {
+ this.scene.interactionSystem.spawnLoot(zombie.gridX, zombie.gridY, 'fertilizer', 1);
+ }
+
+ if (this.scene.statsSystem) {
+ this.scene.statsSystem.addXP(10);
+ }
+
+ this.removeWorker(zombie);
+ }
+
+ feedWorker(zombie, amount = 50) {
+ if (!zombie.workerData) return false;
+
+ zombie.workerData.energy = Math.min(100, zombie.workerData.energy + amount);
+ if (zombie.sprite) zombie.sprite.clearTint();
+
+ console.log(`๐ Fed worker`);
+ return true;
+ }
+}
diff --git a/DNEVNIK.md b/DNEVNIK.md
index 2d629c689..ceb3c1f04 100644
--- a/DNEVNIK.md
+++ b/DNEVNIK.md
@@ -1,4 +1,57 @@
+# ๐ DNEVNIK - 16.01.2026 (TOTAL RECOVERY EDITION)
+
+**Session:** 00:30 - Ends Now
+**Status:** โ
**OPERACIJA TOTAL RECOVERY ZAKLJUฤENA!**
+
+---
+
+## ๐ฏ **SESSION: SISTEMSKA OBNOVA & VIZUALIZACIJA** (16.01.2026)
+
+**Cilj:** Popolna obnovitev "izgubljenih" sistemov in kreiranje vrhunskih vizualnih mockupov za AA release.
+
+**Opravljeno:**
+
+### ๐จ **1. OPERACIJA RECOVERY (Reลกevanje Kode)**
+* **Identificiran "Master Script":** `MasterGameSystemsManager.js` in `GameManager.js` (The Brain & The Protector).
+* **Obnovljenih 15+ Sistemov:** Vsi kljuฤni sistemi iz "Phaser dobe" so bili najdeni in shranjeni v varnostne mape:
+ * `EMERGENCY_SYSTEMS_RECOVERY/`: Ekonomija, Vreme, Trgovina, NPC AI.
+ * `MASTER_RECOVERY/`: Glavni managerji.
+ * `MASTER_DESIGN_RECOVERY/`: Zgodba, Konci, ลฝivalski AI.
+* **Rezultat:** Niฤ veฤ "izgubljene" kode. Vse je na varnem.
+
+### ๐ **2. GAME BIBLE 3.0 (The Holy Grail)**
+* **Nova Verzija:** Ustvarjena `GAME_BIBLE_3.md` (Total Recovery Edition).
+* **Sinhronizacija:** `GAME_BIBLE_2.md` je bila posodobljena, da se ujema s kodo:
+ * Dodan **SYSTEM RECOVERY INDEX**.
+ * Dodana poglavja **ANIMAL COMPANION AI** (Sova/Netopir).
+ * Dodana poglavja **THE 4 ENDINGS** (Sacrifice, Survival, Ascension, UTOPIA).
+ * **Vizualna Prenova:** Dodani "ASCII okvirji" za glavna poglavja za boljลกo preglednost.
+
+### ๐จ **3. VIZUALNA DOMINACIJA (AA Mockups)**
+* **Kai Redesign:** Generiran "pravi" Kai (Pink Dreads, Piercings) v Style 32.
+* **Gameplay Mockups:** Ustvarjeni 4 vrhunski "Fake Screenshots" za promocijo:
+ 1. **FARM:** Kai harvesta "glowing pumpkins" na pokopaliลกฤu.
+ 2. **TOWN:** Kai v deลพevnem Noir mestu pred pekarno.
+ 3. **MINE:** Kai poveljuje "Chibi Zombies" v rudniku kristalov.
+ 4. **COMBAT:** Twin Strike akcija proti Nomad Raiderjem.
+* **VIDEO TEASER:** Ustvarjen `novafarma_teaser.webp` (Ken Burns slideshow) za takojลกen uฤinek.
+
+### ๐๏ธ **4. PREGLED & ฤIล ฤENJE**
+* **Town Restoration:** Odpravljen "Muzej" iz Faze 1/2 (premalo relevanten).
+* **Dokumentacija:** Vse mape (RECOVERY, ASSETS, PROMO) so urejene in dostopne.
+
+---
+
+## ๐ **STATUS PROJEKTA: ZABETONIRAN** ๐๏ธ๐
+* **Koda:** 100% (Vse najdeno).
+* **Design:** 100% (Biblija 3.0).
+* **Visuals:** 110% (Style 32 Mockups so "Next Level").
+
+**Naslednji korak:** Integracija teh sistemov v novo "S1 Max" arhitekturo in priprava demo builda za Marec.
+
+**Podpis:** Agent Antigravity & David Kotnik (Dream Team). ๐ฅโจ
+
# ๐ DNEVNIK - 14.01.2026
**Session:** 23:22 (Current)
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ADHDAutismSupportSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ADHDAutismSupportSystem.js
new file mode 100644
index 000000000..5d45494ec
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ADHDAutismSupportSystem.js
@@ -0,0 +1,154 @@
+/**
+ * ADHD/AUTISM SUPPORT SYSTEM
+ * Provides focus assistance and predictable UI for neurodivergent players
+ */
+class ADHDAutismSupportSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Settings
+ this.settings = {
+ focusMode: false, // Hide non-essential UI
+ reminderSystem: true, // Task reminders
+ simplifiedMenus: false, // Simplified navigation
+ noJumpScares: true, // Disable sudden events
+ predictableUI: true, // Consistent UI patterns
+ reducedAnimations: false, // Less motion
+ taskTimer: true, // Visual task timer
+ breakReminders: true, // Regular break reminders
+ soundWarnings: true // Warn before loud sounds
+ };
+
+ // Reminder state
+ this.reminders = [];
+ this.lastBreakReminder = Date.now();
+ this.breakInterval = 30 * 60 * 1000; // 30 minutes
+
+ // Focus mode overlay
+ this.focusOverlay = null;
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
ADHD/Autism Support System initialized');
+ }
+
+ init() {
+ if (this.settings.focusMode) {
+ this.enableFocusMode();
+ }
+
+ if (this.settings.breakReminders) {
+ this.startBreakReminders();
+ }
+ }
+
+ /**
+ * Enable focus mode (hide non-essential UI)
+ */
+ enableFocusMode() {
+ this.settings.focusMode = true;
+
+ // Create dark overlay for non-focused areas
+ if (!this.focusOverlay) {
+ this.createFocusOverlay();
+ }
+
+ console.log('๐ฏ Focus mode enabled');
+ this.saveSettings();
+ }
+
+ /**
+ * Disable focus mode
+ */
+ disableFocusMode() {
+ this.settings.focusMode = false;
+
+ if (this.focusOverlay) {
+ this.focusOverlay.setVisible(false);
+ }
+
+ console.log('๐ฏ Focus mode disabled');
+ this.saveSettings();
+ }
+
+ /**
+ * Create focus mode overlay
+ */
+ createFocusOverlay() {
+ // Implementation would create a vignette effect
+ console.log('Creating focus overlay...');
+ }
+
+ /**
+ * Add reminder
+ */
+ addReminder(text, time) {
+ this.reminders.push({ text, time });
+ console.log(`โฐ Reminder added: ${text} at ${time}`);
+ }
+
+ /**
+ * Start break reminders
+ */
+ startBreakReminders() {
+ setInterval(() => {
+ if (this.settings.breakReminders) {
+ this.showBreakReminder();
+ }
+ }, this.breakInterval);
+ }
+
+ /**
+ * Show break reminder
+ */
+ showBreakReminder() {
+ const message = 'Take a break! You\'ve been playing for 30 minutes.';
+
+ if (this.scene.screenReader) {
+ this.scene.screenReader.speak(message, 'alert');
+ }
+
+ console.log('โฐ Break reminder shown');
+ }
+
+ /**
+ * Toggle simplified menus
+ */
+ toggleSimplifiedMenus() {
+ this.settings.simplifiedMenus = !this.settings.simplifiedMenus;
+ this.saveSettings();
+ console.log(`๐ Simplified menus: ${this.settings.simplifiedMenus ? 'ON' : 'OFF'}`);
+ }
+
+ /**
+ * Toggle no jump scares
+ */
+ toggleNoJumpScares() {
+ this.settings.noJumpScares = !this.settings.noJumpScares;
+ this.saveSettings();
+ console.log(`๐ป No jump scares: ${this.settings.noJumpScares ? 'ON' : 'OFF'}`);
+ }
+
+ /**
+ * Save settings
+ */
+ saveSettings() {
+ localStorage.setItem('novafarma_adhd_autism_support', JSON.stringify(this.settings));
+ }
+
+ /**
+ * Load settings
+ */
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_adhd_autism_support');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ destroy() {
+ console.log('๐ง ADHD/Autism Support System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AccessibilityManager.js b/EMERGENCY_SYSTEMS_RECOVERY/AccessibilityManager.js
new file mode 100644
index 000000000..30291b985
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AccessibilityManager.js
@@ -0,0 +1,369 @@
+/**
+ * ACCESSIBILITY MANAGER - STREAMER-READY FEATURES
+ * One-Handed Mode, High Contrast, Font Scaling
+ */
+
+class AccessibilityManager {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Accessibility settings
+ this.settings = {
+ oneHandedMode: false,
+ oneHandedSide: 'left', // 'left' or 'right'
+ highContrast: false,
+ colorBlindMode: 'none', // 'none', 'protanopia', 'deuteranopia', 'tritanopia'
+ fontScale: 1.0, // 0.8 - 2.0
+ subtitleSize: 'medium', // 'small', 'medium', 'large', 'xlarge'
+ reduceMotion: false,
+ screenReader: false
+ };
+
+ // Load saved settings
+ this.load();
+
+ // Apply settings
+ this.apply();
+ }
+
+ /**
+ * ONE-HANDED MODE (Xbox Controller)
+ */
+ enableOneHandedMode(side = 'left') {
+ this.settings.oneHandedMode = true;
+ this.settings.oneHandedSide = side;
+
+ console.log(`โฟ ONE-HANDED MODE ENABLED (${side.toUpperCase()} hand)`);
+ console.log(' Movement: Left stick');
+
+ if (side === 'left') {
+ console.log(' Actions mapped to LEFT side:');
+ console.log(' - LB: Interact (was A)');
+ console.log(' - LT: Attack (was X)');
+ console.log(' - L3 (click stick): Menu (was B)');
+ console.log(' - D-Pad Up: Whistle Susi (was Y)');
+ } else {
+ console.log(' Actions mapped to RIGHT side:');
+ console.log(' - RB: Interact (was A)');
+ console.log(' - RT: Attack (was X)');
+ console.log(' - R3 (click stick): Menu (was B)');
+ console.log(' - Right Stick Click: Whistle (was Y)');
+ }
+
+ this.save();
+
+ // Emit event for gamepad controller
+ if (this.scene.events) {
+ this.scene.events.emit('accessibility-changed', this.settings);
+ }
+ }
+
+ disableOneHandedMode() {
+ this.settings.oneHandedMode = false;
+ console.log('โฟ One-handed mode disabled - Back to standard controls');
+ this.save();
+ }
+
+ /**
+ * GET BUTTON MAPPING FOR ONE-HANDED MODE
+ */
+ getButtonMapping() {
+ if (!this.settings.oneHandedMode) {
+ // Standard mapping
+ return {
+ interact: 'A',
+ attack: 'X',
+ whistle: 'Y',
+ menu: 'B',
+ movement: 'LEFT_STICK'
+ };
+ }
+
+ if (this.settings.oneHandedSide === 'left') {
+ // Left-hand mapping
+ return {
+ interact: 'LB',
+ attack: 'LT',
+ whistle: 'DPAD_UP',
+ menu: 'L3',
+ movement: 'LEFT_STICK'
+ };
+ } else {
+ // Right-hand mapping
+ return {
+ interact: 'RB',
+ attack: 'RT',
+ whistle: 'R3',
+ menu: 'DPAD_DOWN',
+ movement: 'RIGHT_STICK'
+ };
+ }
+ }
+
+ /**
+ * HIGH CONTRAST MODE
+ */
+ enableHighContrast() {
+ this.settings.highContrast = true;
+ console.log('โฟ HIGH CONTRAST MODE ENABLED');
+
+ // Apply high contrast shader
+ this.applyHighContrastShader();
+ this.save();
+ }
+
+ disableHighContrast() {
+ this.settings.highContrast = false;
+ console.log('โฟ High contrast mode disabled');
+
+ // Remove shader
+ this.removeHighContrastShader();
+ this.save();
+ }
+
+ applyHighContrastShader() {
+ if (!this.scene.cameras || !this.scene.cameras.main) return;
+
+ // Increase contrast using post-processing
+ const camera = this.scene.cameras.main;
+
+ // Store original values
+ if (!this.originalContrast) {
+ this.originalContrast = {
+ alpha: camera.alpha,
+ brightness: 1.0
+ };
+ }
+
+ // Boost contrast
+ camera.setAlpha(1.0);
+
+ // Add vignette for edge clarity (reverse of normal vignette)
+ if (this.contrastOverlay) {
+ this.contrastOverlay.destroy();
+ }
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ this.contrastOverlay = this.scene.add.rectangle(
+ width / 2,
+ height / 2,
+ width,
+ height,
+ 0xFFFFFF,
+ 0.1
+ );
+ this.contrastOverlay.setScrollFactor(0);
+ this.contrastOverlay.setDepth(10000);
+ this.contrastOverlay.setBlendMode(Phaser.BlendModes.OVERLAY);
+
+ console.log('โ
High contrast shader applied');
+ }
+
+ removeHighContrastShader() {
+ if (this.contrastOverlay) {
+ this.contrastOverlay.destroy();
+ this.contrastOverlay = null;
+ }
+
+ if (this.originalContrast && this.scene.cameras && this.scene.cameras.main) {
+ this.scene.cameras.main.setAlpha(this.originalContrast.alpha);
+ }
+ }
+
+ /**
+ * COLOR BLIND MODE
+ */
+ setColorBlindMode(mode) {
+ const validModes = ['none', 'protanopia', 'deuteranopia', 'tritanopia'];
+ if (!validModes.includes(mode)) {
+ console.warn('Invalid color blind mode:', mode);
+ return;
+ }
+
+ this.settings.colorBlindMode = mode;
+ console.log(`โฟ COLOR BLIND MODE: ${mode.toUpperCase()}`);
+
+ // Apply color filter
+ this.applyColorBlindFilter(mode);
+ this.save();
+ }
+
+ applyColorBlindFilter(mode) {
+ // Remove existing filter
+ if (this.colorBlindFilter) {
+ this.colorBlindFilter.destroy();
+ this.colorBlindFilter = null;
+ }
+
+ if (mode === 'none') return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Apply color tint based on mode
+ const tints = {
+ protanopia: 0xFFCCCC, // Red-blind (pink tint)
+ deuteranopia: 0xCCFFCC, // Green-blind (light green tint)
+ tritanopia: 0xCCCCFF // Blue-blind (light blue tint)
+ };
+
+ this.colorBlindFilter = this.scene.add.rectangle(
+ width / 2,
+ height / 2,
+ width,
+ height,
+ tints[mode],
+ 0.15
+ );
+ this.colorBlindFilter.setScrollFactor(0);
+ this.colorBlindFilter.setDepth(9999);
+ this.colorBlindFilter.setBlendMode(Phaser.BlendModes.MULTIPLY);
+
+ console.log(`โ
${mode} filter applied`);
+ }
+
+ /**
+ * FONT SCALING (for subtitles, UI)
+ */
+ setFontScale(scale) {
+ // Clamp between 0.8 and 2.0
+ this.settings.fontScale = Phaser.Math.Clamp(scale, 0.8, 2.0);
+ console.log(`โฟ FONT SCALE: ${this.settings.fontScale}x`);
+
+ // Emit event for UI to update
+ if (this.scene.events) {
+ this.scene.events.emit('font-scale-changed', this.settings.fontScale);
+ }
+
+ this.save();
+ }
+
+ /**
+ * SUBTITLE SIZE PRESETS
+ */
+ setSubtitleSize(size) {
+ const sizes = {
+ small: 0.8,
+ medium: 1.0,
+ large: 1.5,
+ xlarge: 2.0
+ };
+
+ if (sizes[size]) {
+ this.settings.subtitleSize = size;
+ this.setFontScale(sizes[size]);
+ console.log(`โฟ SUBTITLE SIZE: ${size.toUpperCase()} (${sizes[size]}x)`);
+ }
+ }
+
+ /**
+ * GET FONT SIZE FOR ELEMENT
+ */
+ getFontSize(baseFontSize) {
+ return Math.floor(baseFontSize * this.settings.fontScale);
+ }
+
+ /**
+ * REDUCE MOTION MODE
+ */
+ enableReduceMotion() {
+ this.settings.reduceMotion = true;
+ console.log('โฟ REDUCE MOTION ENABLED');
+ console.log(' - Disabled screen shake');
+ console.log(' - Reduced particle effects');
+ console.log(' - Slower transitions');
+ this.save();
+ }
+
+ disableReduceMotion() {
+ this.settings.reduceMotion = false;
+ console.log('โฟ Reduce motion disabled');
+ this.save();
+ }
+
+ /**
+ * APPLY ALL SETTINGS
+ */
+ apply() {
+ if (this.settings.highContrast) {
+ this.applyHighContrastShader();
+ }
+
+ if (this.settings.colorBlindMode !== 'none') {
+ this.applyColorBlindFilter(this.settings.colorBlindMode);
+ }
+
+ if (this.settings.oneHandedMode) {
+ console.log(`โฟ One-handed mode active (${this.settings.oneHandedSide})`);
+ }
+
+ console.log(`โฟ Font scale: ${this.settings.fontScale}x`);
+ }
+
+ /**
+ * GET ALL SETTINGS
+ */
+ getSettings() {
+ return { ...this.settings };
+ }
+
+ /**
+ * SAVE TO LOCALSTORAGE
+ */
+ save() {
+ localStorage.setItem('accessibility_settings', JSON.stringify(this.settings));
+ console.log('๐พ Accessibility settings saved');
+ }
+
+ /**
+ * LOAD FROM LOCALSTORAGE
+ */
+ load() {
+ const stored = localStorage.getItem('accessibility_settings');
+ if (stored) {
+ try {
+ this.settings = { ...this.settings, ...JSON.parse(stored) };
+ console.log('โ
Accessibility settings loaded:', this.settings);
+ } catch (e) {
+ console.warn('Failed to load accessibility settings:', e);
+ }
+ }
+ }
+
+ /**
+ * RESET TO DEFAULTS
+ */
+ reset() {
+ this.settings = {
+ oneHandedMode: false,
+ oneHandedSide: 'left',
+ highContrast: false,
+ colorBlindMode: 'none',
+ fontScale: 1.0,
+ subtitleSize: 'medium',
+ reduceMotion: false,
+ screenReader: false
+ };
+
+ this.removeHighContrastShader();
+ this.applyColorBlindFilter('none');
+
+ this.save();
+ console.log('โฟ Accessibility settings reset to defaults');
+ }
+
+ /**
+ * CLEANUP
+ */
+ destroy() {
+ this.removeHighContrastShader();
+
+ if (this.colorBlindFilter) {
+ this.colorBlindFilter.destroy();
+ }
+
+ console.log('โฟ AccessibilityManager destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AccessibilitySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/AccessibilitySystem.js
new file mode 100644
index 000000000..feb04843e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AccessibilitySystem.js
@@ -0,0 +1,360 @@
+class AccessibilitySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Settings
+ this.settings = {
+ highContrast: 'none', // 'none', 'bw', 'yellow_black'
+ largeUI: 1.0, // 1.0, 1.5, 2.0
+ boldOutlines: false,
+ colorBlindMode: 'none', // 'none', 'protanopia', 'deuteranopia', 'tritanopia', 'achromatopsia'
+ photosensitivity: false,
+ motionSickness: false,
+ brightnessLimit: 1.0 // 0.5 - 1.0
+ };
+
+ // Flash limiter
+ this.flashCount = 0;
+ this.flashWindow = 1000; // 1 second
+ this.maxFlashes = 3;
+ this.lastFlashTime = 0;
+
+ // Load saved settings
+ this.loadSettings();
+
+ // Apply settings
+ this.applySettings();
+
+ console.log('โฟ AccessibilitySystem: Initialized');
+ }
+
+ // HIGH CONTRAST MODES
+ enableBlackWhite() {
+ // Apply grayscale filter
+ this.scene.cameras.main.setPostPipeline('GrayscalePipeline');
+ console.log('๐จ Black & White mode enabled');
+ }
+
+ disableBlackWhite() {
+ this.scene.cameras.main.clearPostPipeline();
+ }
+
+ enableYellowOnBlack() {
+ // Simple color replacement - make everything yellow/white on black
+ const cam = this.scene.cameras.main;
+ cam.setBackgroundColor('#000000');
+
+ // Apply tint to all sprites (simplified version)
+ this.scene.children.list.forEach(child => {
+ if (child.setTint) {
+ child.setTint(0xffff00); // Yellow tint
+ }
+ });
+
+ console.log('๐จ Yellow on Black mode enabled');
+ }
+
+ disableYellowOnBlack() {
+ this.scene.cameras.main.setBackgroundColor('#87CEEB'); // Sky blue
+
+ this.scene.children.list.forEach(child => {
+ if (child.clearTint) {
+ child.clearTint();
+ }
+ });
+ }
+
+ // LARGE UI
+ setUIScale(scale) {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ // Scale UI elements (simplified - would need more work for production)
+ uiScene.scale.setZoom(scale);
+
+ console.log(`๐ UI Scale: ${scale * 100}%`);
+ }
+
+ // BOLD OUTLINES
+ enableBoldOutlines() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ // Increase stroke on all text elements
+ uiScene.children.list.forEach(child => {
+ if (child.type === 'Text') {
+ child.setStroke('#000000', 6);
+ child.setShadow(2, 2, '#000000', 2, true, true);
+ }
+ });
+
+ console.log('โ๏ธ Bold outlines enabled');
+ }
+
+ disableBoldOutlines() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ uiScene.children.list.forEach(child => {
+ if (child.type === 'Text') {
+ child.setStroke('#000000', 3); // Normal stroke
+ child.setShadow(0, 0, '#000000', 0);
+ }
+ });
+ }
+
+ // COLOR BLIND MODES (Simplified - full implementation would use shaders)
+ setColorBlindMode(mode) {
+ this.settings.colorBlindMode = mode;
+
+ switch (mode) {
+ case 'protanopia':
+ // Red-blind: Replace reds with browns/grays
+ this.applyColorFilter(0xaa8866);
+ break;
+ case 'deuteranopia':
+ // Green-blind: Replace greens with yellows/browns
+ this.applyColorFilter(0xccaa66);
+ break;
+ case 'tritanopia':
+ // Blue-blind: Replace blues with greens/grays
+ this.applyColorFilter(0x88aa88);
+ break;
+ case 'achromatopsia':
+ // Total color blind: Grayscale
+ this.enableBlackWhite();
+ break;
+ default:
+ this.clearColorFilter();
+ }
+
+ console.log(`๐๏ธ Color Blind Mode: ${mode}`);
+ }
+
+ applyColorFilter(tint) {
+ // Simplified color filter
+ this.scene.children.list.forEach(child => {
+ if (child.setTint) {
+ child.setTint(tint);
+ }
+ });
+ }
+
+ clearColorFilter() {
+ this.scene.children.list.forEach(child => {
+ if (child.clearTint) {
+ child.clearTint();
+ }
+ });
+ }
+
+ // PHOTOSENSITIVITY PROTECTION
+ canFlash() {
+ if (!this.settings.photosensitivity) return true;
+
+ const now = Date.now();
+
+ // Reset counter if window passed
+ if (now - this.lastFlashTime > this.flashWindow) {
+ this.flashCount = 0;
+ this.lastFlashTime = now;
+ }
+
+ // Check limit
+ if (this.flashCount >= this.maxFlashes) {
+ console.warn('โ ๏ธ Flash limit reached - skipping flash');
+ return false;
+ }
+
+ this.flashCount++;
+ return true;
+ }
+
+ disableLightning() {
+ if (this.scene.weatherSystem) {
+ this.scene.weatherSystem.lightningEnabled = false;
+ }
+ }
+
+ reduceParticles(level = 0.5) {
+ this.scene.children.list.forEach(child => {
+ if (child.type === 'ParticleEmitter') {
+ const originalFreq = child.frequency || 100;
+ child.setFrequency(originalFreq / level);
+ }
+ });
+ }
+
+ enableBrightnessLimiter(maxBrightness = 0.8) {
+ if (this.brightnessOverlay) {
+ this.brightnessOverlay.destroy();
+ }
+
+ const overlay = this.scene.add.graphics();
+ overlay.fillStyle(0x000000, 1 - maxBrightness);
+ overlay.fillRect(0, 0, this.scene.cameras.main.width * 2, this.scene.cameras.main.height * 2);
+ overlay.setScrollFactor(0);
+ overlay.setDepth(9999);
+
+ this.brightnessOverlay = overlay;
+
+ console.log(`๐ก Brightness limited to ${maxBrightness * 100}%`);
+ }
+
+ disableBrightnessLimiter() {
+ if (this.brightnessOverlay) {
+ this.brightnessOverlay.destroy();
+ this.brightnessOverlay = null;
+ }
+ }
+
+ // MOTION SICKNESS MODE
+ enableMotionSicknessMode() {
+ // Disable camera shake
+ const originalShake = this.scene.cameras.main.shake;
+ this.scene.cameras.main.shake = () => {
+ console.log('๐ซ Camera shake disabled (motion sickness mode)');
+ };
+
+ // Disable parallax
+ if (this.scene.parallaxSystem) {
+ this.scene.parallaxSystem.enabled = false;
+ }
+
+ console.log('๐คข Motion sickness mode enabled');
+ }
+
+ disableMotionSicknessMode() {
+ // Re-enable effects
+ if (this.scene.parallaxSystem) {
+ this.scene.parallaxSystem.enabled = true;
+ }
+ }
+
+ // SETTINGS MANAGEMENT
+ applySettings() {
+ // High Contrast
+ if (this.settings.highContrast === 'bw') {
+ this.enableBlackWhite();
+ } else if (this.settings.highContrast === 'yellow_black') {
+ this.enableYellowOnBlack();
+ }
+
+ // Large UI
+ if (this.settings.largeUI > 1.0) {
+ this.setUIScale(this.settings.largeUI);
+ }
+
+ // Bold Outlines
+ if (this.settings.boldOutlines) {
+ this.enableBoldOutlines();
+ }
+
+ // Color Blind Mode
+ if (this.settings.colorBlindMode !== 'none') {
+ this.setColorBlindMode(this.settings.colorBlindMode);
+ }
+
+ // Photosensitivity
+ if (this.settings.photosensitivity) {
+ this.disableLightning();
+ this.reduceParticles(0.3);
+ this.enableBrightnessLimiter(this.settings.brightnessLimit);
+ }
+
+ // Motion Sickness
+ if (this.settings.motionSickness) {
+ this.enableMotionSicknessMode();
+ }
+ }
+
+ saveSettings() {
+ localStorage.setItem('novafarma_accessibility', JSON.stringify(this.settings));
+ console.log('๐พ Accessibility settings saved');
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_accessibility');
+ if (saved) {
+ this.settings = JSON.parse(saved);
+ console.log('๐ Accessibility settings loaded');
+ }
+ }
+
+ // EPILEPSY WARNING
+ showEpilepsyWarning(onContinue) {
+ // Get UIScene for proper positioning
+ const uiScene = this.scene.scene.get('UIScene') || this.scene;
+
+ const warning = uiScene.add.container(
+ uiScene.scale.width / 2,
+ uiScene.scale.height / 2
+ );
+ warning.setDepth(99999); // ALWAYS ON TOP
+ warning.setScrollFactor(0);
+
+ // Dark background overlay
+ const bg = uiScene.add.rectangle(0, 0, 700, 500, 0x000000, 0.98);
+ bg.setStrokeStyle(4, 0xff0000);
+
+ const title = uiScene.add.text(0, -180, 'โ ๏ธ EPILEPSY WARNING', {
+ fontSize: '36px',
+ color: '#ff0000',
+ fontStyle: 'bold',
+ stroke: '#000000',
+ strokeThickness: 4
+ }).setOrigin(0.5);
+
+ const text = uiScene.add.text(0, -60,
+ 'This game contains flashing lights\n' +
+ 'that may trigger seizures in people with\n' +
+ 'photosensitive epilepsy.\n\n' +
+ 'Player discretion is advised.',
+ {
+ fontSize: '20px',
+ color: '#ffffff',
+ align: 'center',
+ wordWrap: { width: 600 },
+ lineSpacing: 8
+ }
+ ).setOrigin(0.5);
+
+ const enableBtn = uiScene.add.text(0, 120, '[ ENABLE PROTECTION ]', {
+ fontSize: '24px',
+ color: '#00ff00',
+ backgroundColor: '#003300',
+ padding: { x: 30, y: 15 },
+ fontStyle: 'bold'
+ }).setOrigin(0.5);
+
+ enableBtn.setInteractive({ useHandCursor: true });
+ enableBtn.on('pointerover', () => enableBtn.setScale(1.1));
+ enableBtn.on('pointerout', () => enableBtn.setScale(1.0));
+ enableBtn.on('pointerdown', () => {
+ this.settings.photosensitivity = true;
+ this.applySettings();
+ this.saveSettings();
+ warning.destroy();
+ if (onContinue) onContinue();
+ });
+
+ const continueBtn = uiScene.add.text(0, 180, '[ CONTINUE WITHOUT ]', {
+ fontSize: '18px',
+ color: '#888888',
+ backgroundColor: '#222222',
+ padding: { x: 20, y: 10 }
+ }).setOrigin(0.5);
+
+ continueBtn.setInteractive({ useHandCursor: true });
+ continueBtn.on('pointerover', () => continueBtn.setScale(1.1));
+ continueBtn.on('pointerout', () => continueBtn.setScale(1.0));
+ continueBtn.on('pointerdown', () => {
+ warning.destroy();
+ if (onContinue) onContinue();
+ });
+
+ warning.add([bg, title, text, enableBtn, continueBtn]);
+
+ return warning;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AlbumCollectionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/AlbumCollectionSystem.js
new file mode 100644
index 000000000..f76448db2
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AlbumCollectionSystem.js
@@ -0,0 +1,420 @@
+/**
+ * AlbumCollectionSystem.js
+ * ========================
+ * KRVAVA ลฝETEV - Album Collection System (Phase 41)
+ *
+ * Features:
+ * - Collection album UI (book interface)
+ * - Auto-discovery on first encounter
+ * - 5 categories (Zombies, Plants, Fish, Mutants, Defects)
+ * - Completion rewards
+ * - Lore entries
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class AlbumCollectionSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Album state
+ this.discovered = new Set();
+ this.totalEntries = 0;
+
+ // Album UI
+ this.albumContainer = null;
+ this.isAlbumOpen = false;
+ this.currentCategory = 'all';
+
+ // Categories
+ this.categories = {
+ zombies: { name: 'Legendary Zombies', icon: '๐ง', color: 0x00FF00 },
+ plants: { name: 'Giant Flowers', icon: '๐ธ', color: 0xFF69B4 },
+ fish: { name: 'Rare Fish', icon: '๐', color: 0x00CED1 },
+ mutants: { name: 'Mutants', icon: '๐น', color: 0xFF0000 },
+ defects: { name: 'Defective Zombies', icon: '๐คช', color: 0xFFD700 }
+ };
+
+ // Album entries
+ this.entries = new Map();
+
+ console.log('๐ AlbumCollectionSystem initialized');
+
+ // Register all collectibles
+ this.registerEntries();
+
+ // Create album UI
+ this.createAlbumUI();
+ }
+
+ /**
+ * Register all album entries
+ */
+ registerEntries() {
+ // Legendary Zombies
+ this.registerEntry('zombie_alpha', {
+ category: 'zombies',
+ name: 'Alpha Zombie',
+ description: 'Leader of zombie packs. Stronger and smarter than normal zombies.',
+ rarity: 'legendary',
+ icon: '๐',
+ lore: 'Some say Alphas retain fragments of their human intelligence...'
+ });
+ this.registerEntry('zombie_tank', {
+ category: 'zombies',
+ name: 'Tank Zombie',
+ description: 'Massive zombie with incredible durability. Slow but deadly.',
+ rarity: 'rare',
+ icon: '๐ก๏ธ',
+ lore: 'Created from bodybuilders and soldiers.'
+ });
+ this.registerEntry('zombie_runner', {
+ category: 'zombies',
+ name: 'Runner Zombie',
+ description: 'Fast and agile. Difficult to catch.',
+ rarity: 'uncommon',
+ icon: '๐จ',
+ lore: 'Athletes who retained their speed in death.'
+ });
+
+ // Giant Flowers (Mesojedke)
+ this.registerEntry('mesojedka_giant', {
+ category: 'plants',
+ name: 'Giant Carnivorous Flower',
+ description: 'Stage 5 mesojedka. Massive defense plant with 50 damage output.',
+ rarity: 'legendary',
+ icon: '๐ธ',
+ lore: 'Fed with legendary meat, these flowers can defend entire farms.'
+ });
+ this.registerEntry('mesojedka_adult', {
+ category: 'plants',
+ name: 'Adult Carnivorous Plant',
+ description: 'Stage 4 mesojedka. Reliable defense with good harvest.',
+ rarity: 'rare',
+ icon: '๐บ',
+ lore: 'The most common defensive plant on farms.'
+ });
+
+ // Rare Fish
+ this.registerEntry('fish_golden', {
+ category: 'fish',
+ name: 'Golden Carp',
+ description: 'Extremely rare fish worth 1000 Zlatniki.',
+ rarity: 'legendary',
+ icon: '๐ ',
+ lore: 'Legend says catching one brings good fortune for a year.'
+ });
+ this.registerEntry('fish_nessie', {
+ category: 'fish',
+ name: 'Nessie',
+ description: 'The legendary Loch Ness monster. Portal unlock requirement.',
+ rarity: 'legendary',
+ icon: '๐ฆ',
+ lore: 'She\'s real! And she guards the Loch Ness portal.'
+ });
+ this.registerEntry('fish_piranha', {
+ category: 'fish',
+ name: 'Amazon Piranha',
+ description: 'Dangerous carnivorous fish from the Amazon zone.',
+ rarity: 'rare',
+ icon: '๐ก',
+ lore: 'Survived the apocalypse and got even more aggressive.'
+ });
+
+ // Mutants
+ this.registerEntry('mutant_troll', {
+ category: 'mutants',
+ name: 'Alpha Troll King',
+ description: 'The ultimate weapon. Leader of all infected.',
+ rarity: 'legendary',
+ icon: '๐น',
+ lore: 'Created at the Chernobyl facility to lead the new world.'
+ });
+ this.registerEntry('mutant_frost_giant', {
+ category: 'mutants',
+ name: 'Frost Giant',
+ description: 'Boss of the Frozen Wasteland. Immune to cold.',
+ rarity: 'legendary',
+ icon: 'โ๏ธ',
+ lore: 'Once a normal person, transformed by experimental freezing.'
+ });
+ this.registerEntry('mutant_lava_titan', {
+ category: 'mutants',
+ name: 'Lava Titan',
+ description: 'Boss of the Volcanic Crater. Body made of molten rock.',
+ rarity: 'legendary',
+ icon: '๐',
+ lore: 'Emerged from the volcano after the outbreak.'
+ });
+
+ // Defective Zombies (Lab Jokes)
+ this.registerEntry('defect_disco', {
+ category: 'defects',
+ name: 'Disco Zombie',
+ description: 'Lab defect #42. Can\'t stop dancing. Harmless.',
+ rarity: 'rare',
+ icon: '๐บ',
+ lore: 'Lab experiment gone wrong. Or... right? He seems happy!'
+ });
+ this.registerEntry('defect_sleepy', {
+ category: 'defects',
+ name: 'Sleepy Zombie',
+ description: 'Lab defect #13. Always sleeping. Literally cannot wake up.',
+ rarity: 'uncommon',
+ icon: '๐ด',
+ lore: 'Researchers still don\'t know how he\'s alive while sleeping.'
+ });
+ this.registerEntry('defect_friendly', {
+ category: 'defects',
+ name: 'Friendly Zombie',
+ description: 'Lab defect #7. Retained all human personality. Just wants hugs.',
+ rarity: 'rare',
+ icon: '๐ค',
+ lore: 'The only zombie that remembers its entire past life.'
+ });
+
+ this.totalEntries = this.entries.size;
+ console.log(`โ
Registered ${this.totalEntries} album entries`);
+ }
+
+ /**
+ * Register single entry
+ */
+ registerEntry(id, data) {
+ this.entries.set(id, {
+ id: id,
+ ...data,
+ discovered: false,
+ discoveredDate: null,
+ timesEncountered: 0
+ });
+ }
+
+ /**
+ * Discover entry (first encounter)
+ */
+ discoverEntry(id) {
+ const entry = this.entries.get(id);
+ if (!entry) {
+ console.error(`Entry ${id} not found!`);
+ return false;
+ }
+
+ if (entry.discovered) {
+ // Already discovered, just increment counter
+ entry.timesEncountered++;
+ return false;
+ }
+
+ // First discovery!
+ entry.discovered = true;
+ entry.discoveredDate = new Date();
+ entry.timesEncountered = 1;
+ this.discovered.add(id);
+
+ console.log(`๐ NEW DISCOVERY: ${entry.name}!`);
+
+ // Show discovery notification
+ this.showDiscoveryNotification(entry);
+
+ // Check for category completion
+ this.checkCategoryCompletion(entry.category);
+
+ // Check for album completion
+ this.checkAlbumCompletion();
+
+ return true;
+ }
+
+ /**
+ * Show discovery notification
+ */
+ showDiscoveryNotification(entry) {
+ this.showNotification({
+ title: 'NEW DISCOVERY!',
+ text: `${entry.icon} ${entry.name} added to album!`,
+ icon: '๐'
+ });
+
+ // Special effects for legendary discoveries
+ if (entry.rarity === 'legendary') {
+ this.scene.cameras.main.flash(1000, 255, 215, 0); // Golden flash
+ this.scene.cameras.main.shake(500, 0.01);
+ }
+ }
+
+ /**
+ * Check category completion
+ */
+ checkCategoryCompletion(category) {
+ const categoryEntries = Array.from(this.entries.values())
+ .filter(e => e.category === category);
+ const discovered = categoryEntries.filter(e => e.discovered);
+
+ if (discovered.length === categoryEntries.length) {
+ // Category complete!
+ const categoryData = this.categories[category];
+
+ console.log(`๐ CATEGORY COMPLETE: ${categoryData.name}!`);
+
+ this.showNotification({
+ title: 'CATEGORY COMPLETE!',
+ text: `${categoryData.icon} ${categoryData.name} - 100%!`,
+ icon: '๐'
+ });
+
+ // Grant rewards
+ this.grantCategoryReward(category);
+ }
+ }
+
+ /**
+ * Grant category completion reward
+ */
+ grantCategoryReward(category) {
+ const rewards = {
+ zombies: { zlatniki: 1000, item: 'alpha_essence' },
+ plants: { zlatniki: 1500, item: 'legendary_seeds' },
+ fish: { zlatniki: 2000, item: 'master_fishing_rod' },
+ mutants: { zlatniki: 5000, item: 'trophy_collection' },
+ defects: { zlatniki: 500, item: 'lab_humor_book' }
+ };
+
+ const reward = rewards[category];
+ if (reward) {
+ console.log(`๐ Category reward:`, reward);
+ // TODO: Grant actual rewards
+ }
+ }
+
+ /**
+ * Check album completion
+ */
+ checkAlbumCompletion() {
+ if (this.discovered.size === this.totalEntries) {
+ console.log('๐ ALBUM 100% COMPLETE!');
+
+ this.showNotification({
+ title: 'ALBUM COMPLETE!',
+ text: '๐ 100% Collection! You discovered everything!',
+ icon: '๐'
+ });
+
+ // Grant ultimate reward
+ this.grantAlbumCompletionReward();
+ }
+ }
+
+ /**
+ * Grant album completion reward
+ */
+ grantAlbumCompletionReward() {
+ console.log('๐ ULTIMATE REWARD: Master Collector Trophy!');
+ // TODO: Grant legendary item
+ }
+
+ /**
+ * Create album UI
+ */
+ createAlbumUI() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ this.albumContainer = this.scene.add.container(width / 2, height / 2);
+ this.albumContainer.setScrollFactor(0);
+ this.albumContainer.setDepth(10000);
+ this.albumContainer.setVisible(false);
+
+ // Book background
+ const bg = this.scene.add.rectangle(0, 0, 900, 650, 0x8B4513, 0.95);
+ bg.setStrokeStyle(5, 0x654321);
+ this.albumContainer.add(bg);
+
+ // Title
+ const title = this.scene.add.text(0, -300, '๐ COLLECTION ALBUM', {
+ fontSize: '36px',
+ fontFamily: 'Georgia',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ title.setOrigin(0.5);
+ this.albumContainer.add(title);
+
+ // Completion percentage
+ this.completionText = this.scene.add.text(0, -250, '0%', {
+ fontSize: '24px',
+ fontFamily: 'Georgia',
+ color: '#00FF00'
+ });
+ this.completionText.setOrigin(0.5);
+ this.albumContainer.add(this.completionText);
+
+ // Close button
+ const closeBtn = this.scene.add.text(430, -300, 'โ', { fontSize: '24px' });
+ closeBtn.setInteractive();
+ closeBtn.on('pointerdown', () => this.closeAlbum());
+ this.albumContainer.add(closeBtn);
+
+ console.log('โ
Album UI created');
+ }
+
+ /**
+ * Open album
+ */
+ openAlbum() {
+ this.isAlbumOpen = true;
+ this.albumContainer.setVisible(true);
+ this.updateAlbumDisplay();
+ }
+
+ /**
+ * Close album
+ */
+ closeAlbum() {
+ this.isAlbumOpen = false;
+ this.albumContainer.setVisible(false);
+ }
+
+ /**
+ * Update album display
+ */
+ updateAlbumDisplay() {
+ const completion = (this.discovered.size / this.totalEntries * 100).toFixed(1);
+ this.completionText.setText(`${completion}% (${this.discovered.size}/${this.totalEntries})`);
+
+ // TODO: Display entries
+ }
+
+ /**
+ * Get completion percentage
+ */
+ getCompletionPercentage() {
+ return (this.discovered.size / this.totalEntries * 100).toFixed(1);
+ }
+
+ /**
+ * Get category completion
+ */
+ getCategoryCompletion(category) {
+ const entries = Array.from(this.entries.values()).filter(e => e.category === category);
+ const discovered = entries.filter(e => e.discovered);
+ return {
+ total: entries.length,
+ discovered: discovered.length,
+ percentage: (discovered.length / entries.length * 100).toFixed(1)
+ };
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AnaClueSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/AnaClueSystem.js
new file mode 100644
index 000000000..2bde91bce
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AnaClueSystem.js
@@ -0,0 +1,456 @@
+/**
+ * ANA'S CLUE SYSTEM
+ * Manages the 50 collectible clues left by Ana across the world.
+ *
+ * Features:
+ * - 50 Collectibles: 15 Messages, 12 Photos, 23 Personal Items
+ * - Story Integration: Each clue reveals plot and lore
+ * - Emotional Cutscenes: Kai's reactions and Twin Bond activations
+ * - Progressive Discovery: Clues unlock as player explores biomes
+ * - Collection UI: Track progress, view discovered clues
+ */
+class AnaClueSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Clue categories
+ this.categories = {
+ messages: [], // 15 handwritten messages
+ photos: [], // 12 photographs
+ items: [] // 23 personal items
+ };
+
+ // All 50 clues
+ this.allClues = this.initializeClues();
+
+ // Discovered clues
+ this.discovered = new Set();
+
+ // Story progression
+ this.storyUnlocks = {
+ 5: 'act1_context', // First 5 clues unlock Act 1 context
+ 10: 'troll_king_reveal', // Giant Troll King reveal
+ 15: 'twin_bond_boost', // Twin Bond strengthens
+ 25: 'act2_context', // Act 2 context unlocked
+ 35: 'final_location_hint', // Hint to Ana's location
+ 50: 'true_ending_unlock' // True ending unlocked
+ };
+
+ // Clue locations (biome-specific)
+ this.clueLocations = new Map();
+
+ console.log('๐ Ana\'s Clue System initialized!');
+ console.log(`๐ Total Clues: ${this.allClues.length}`);
+ }
+
+ /**
+ * Initialize all 50 clues
+ */
+ initializeClues() {
+ const clues = [];
+
+ // 15 MESSAGES
+ const messages = [
+ { id: 'msg_01', type: 'message', title: 'First Note', biome: 'forest', content: 'Kai, if you find this... I\'m sorry. They took me. Stay safe. -Ana' },
+ { id: 'msg_02', type: 'message', title: 'Hidden Trail', biome: 'wasteland', content: 'Following them west. Trail of red flowers marks my path.' },
+ { id: 'msg_03', type: 'message', title: 'The Creatures', biome: 'swamp', content: 'The zombies... they\'re not mindless. I saw intelligence in their eyes.' },
+ { id: 'msg_04', type: 'message', title: 'The King', biome: 'mountains', content: 'The Giant Troll King. That\'s who leads them. I heard his name.' },
+ { id: 'msg_05', type: 'message', title: 'Hope Valley', biome: 'hope_valley', content: 'Hope Valley was beautiful once. We could rebuild it together.' },
+ { id: 'msg_06', type: 'message', title: 'Twin Bond', biome: 'crystal_caves', content: 'I can feel you searching for me. Our bond is strong, Kai.' },
+ { id: 'msg_07', type: 'message', title: 'The Portals', biome: 'portal_ruins', content: 'Ancient portals everywhere. If we repair them, we could travel instantly.' },
+ { id: 'msg_08', type: 'message', title: 'Laboratory Notes', biome: 'laboratory', content: 'Found a lab. Research on "domestication" of the infected. Disturbing.' },
+ { id: 'msg_09', type: 'message', title: 'The Cure', biome: 'medical_facility', content: 'There might be a cure. Research was close before the fall.' },
+ { id: 'msg_10', type: 'message', title: 'Family Dreams', biome: 'abandoned_house', content: 'I dream of a family, Kai. Children playing in safe fields.' },
+ { id: 'msg_11', type: 'message', title: 'The Atlanteans', biome: 'atlantis', content: 'Underwater ruins! Advanced civilization. Atlantis was REAL!' },
+ { id: 'msg_12', type: 'message', title: 'Chernobyl Warning', biome: 'chernobyl', content: 'Radiation zone ahead. Be careful if you follow me here.' },
+ { id: 'msg_13', type: 'message', title: 'Amazon Expedition', biome: 'amazon', content: 'The jungle is dense but alive. So much biodiversity!' },
+ { id: 'msg_14', type: 'message', title: 'Final Warning', biome: 'dark_fortress', content: 'Don\'t come for me, Kai. Too dangerous. Save yourself!' },
+ { id: 'msg_15', type: 'message', title: 'True Feelings', biome: 'final_castle', content: 'I know you won\'t listen. You never do. I love you, brother.' }
+ ];
+
+ // 12 PHOTOS
+ const photos = [
+ { id: 'photo_01', type: 'photo', title: 'Family Portrait', biome: 'forest', description: 'Photo of Kai and Ana as children, smiling.' },
+ { id: 'photo_02', type: 'photo', title: 'Attack Night', biome: 'wasteland', description: 'Blurry photo of Giant Troll King during the attack.' },
+ { id: 'photo_03', type: 'photo', title: 'Hope Valley (Before)', biome: 'hope_valley', description: 'Beautiful town before the apocalypse.' },
+ { id: 'photo_04', type: 'photo', title: 'First Zombie', biome: 'swamp', description: 'Photo of a Level 1 zombie, looking confused.' },
+ { id: 'photo_05', type: 'photo', title: 'Portal Network Map', biome: 'portal_ruins', description: 'Hand-drawn map showing portal locations.' },
+ { id: 'photo_06', type: 'photo', title: 'Laboratory Interior', biome: 'laboratory', description: 'High-tech lab equipment, still functional.' },
+ { id: 'photo_07', type: 'photo', title: 'Ana\'s Journal', biome: 'mountains', description: 'Close-up of Ana\'s handwriting.' },
+ { id: 'photo_08', type: 'photo', title: 'Atlantean Artifact', biome: 'atlantis', description: 'Glowing crystal technology from Atlantis.' },
+ { id: 'photo_09', type: 'photo', title: 'Reactor Core', biome: 'chernobyl', description: 'Damaged reactor, eerie green glow.' },
+ { id: 'photo_10', type: 'photo', title: 'Jungle Temple', biome: 'amazon', description: 'Ancient temple overgrown with vines.' },
+ { id: 'photo_11', type: 'photo', title: 'The Captor', biome: 'dark_fortress', description: 'Shadowy figureโAna\'s captor.' },
+ { id: 'photo_12', type: 'photo', title: 'Final Message', biome: 'final_castle', description: 'Ana holding a "Find Me" sign, tears in eyes.' }
+ ];
+
+ // 23 PERSONAL ITEMS
+ const items = [
+ { id: 'item_01', type: 'item', title: 'Ana\'s Bracelet', biome: 'forest', description: 'Silver bracelet with twin symbols.' },
+ { id: 'item_02', type: 'item', title: 'Torn Dress Piece', biome: 'wasteland', description: 'Fragment of Ana\'s favorite dress.' },
+ { id: 'item_03', type: 'item', title: 'Hairpin', biome: 'swamp', description: 'Ana\'s decorative hairpin, muddy but intact.' },
+ { id: 'item_04', type: 'item', title: 'Pocket Watch', biome: 'mountains', description: 'Father\'s pocket watch, Ana always carried it.' },
+ { id: 'item_05', type: 'item', title: 'Compass', biome: 'hope_valley', description: 'Engraved compass: "To Ana, find your way home."' },
+ { id: 'item_06', type: 'item', title: 'Childhood Toy', biome: 'abandoned_house', description: 'Small stuffed rabbit from childhood.' },
+ { id: 'item_07', type: 'item', title: 'Music Box', biome: 'crystal_caves', description: 'Delicate music box, plays Ana\'s favorite song.' },
+ { id: 'item_08', type: 'item', title: 'Sketchbook', biome: 'portal_ruins', description: 'Ana\'s art sketchbook, filled with drawings.' },
+ { id: 'item_09', type: 'item', title: 'Necklace', biome: 'laboratory', description: 'Mother\'s necklace, Ana\'s most treasured item.' },
+ { id: 'item_10', type: 'item', title: 'Diary Page', biome: 'medical_facility', description: 'Torn diary page describing her fears.' },
+ { id: 'item_11', type: 'item', title: 'Herb Pouch', biome: 'forest', description: 'Pouch of medicinal herbs Ana collected.' },
+ { id: 'item_12', type: 'item', title: 'Glove (Left)', biome: 'wasteland', description: 'One of Ana\'s gloves, left behind.' },
+ { id: 'item_13', type: 'item', title: 'Glove (Right)', biome: 'dark_fortress', description: 'The matching right glove.' },
+ { id: 'item_14', type: 'item', title: 'Lucky Coin', biome: 'atlantis', description: 'Old coin Ana considered lucky.' },
+ { id: 'item_15', type: 'item', title: 'Ribbon', biome: 'chernobyl', description: 'Purple ribbon from Ana\'s hair.' },
+ { id: 'item_16', type: 'item', title: 'Boot Buckle', biome: 'amazon', description: 'Buckle from Ana\'s boot, broken off.' },
+ { id: 'item_17', type: 'item', title: 'Locket', biome: 'mountains', description: 'Heart-shaped locket with Kai\'s photo inside.' },
+ { id: 'item_18', type: 'item', title: 'Handkerchief', biome: 'swamp', description: 'Embroidered handkerchief, Ana\'s initials.' },
+ { id: 'item_19', type: 'item', title: 'Map Fragment', biome: 'portal_ruins', description: 'Piece of a larger map, Ana\'s notes.' },
+ { id: 'item_20', type: 'item', title: 'Flower Press', biome: 'hope_valley', description: 'Ana pressed flowers in this book.' },
+ { id: 'item_21', type: 'item', title: 'Candle Stub', biome: 'crystal_caves', description: 'Half-burned candle, Ana used for light.' },
+ { id: 'item_22', type: 'item', title: 'Broken Mirror', biome: 'laboratory', description: 'Shattered hand mirror, Ana\'s reflection.' },
+ { id: 'item_23', type: 'item', title: 'Final Letter', biome: 'final_castle', description: 'Sealed letter: "Open only when you find me."' }
+ ];
+
+ // Combine all clues
+ clues.push(...messages, ...photos, ...items);
+
+ // Store by category
+ this.categories.messages = messages;
+ this.categories.photos = photos;
+ this.categories.items = items;
+
+ return clues;
+ }
+
+ /**
+ * Place clues in the world
+ */
+ placeCluesInWorld() {
+ this.allClues.forEach(clue => {
+ // Determine position based on biome (placeholder logic)
+ const position = this.getBiomePosition(clue.biome);
+ this.clueLocations.set(clue.id, position);
+
+ // Spawn clue object in world (visual representation)
+ if (this.scene.interactionSystem) {
+ this.scene.interactionSystem.spawnClue(position.x, position.y, clue);
+ }
+ });
+
+ console.log(`๐ Placed ${this.allClues.length} clues across the world!`);
+ }
+
+ /**
+ * Get position for a biome (placeholder)
+ */
+ getBiomePosition(biome) {
+ // In real implementation, would use biome system
+ const biomePositions = {
+ forest: { x: 1000, y: 1000 },
+ wasteland: { x: 3000, y: 1500 },
+ swamp: { x: 2000, y: 3000 },
+ mountains: { x: 4000, y: 500 },
+ hope_valley: { x: 500, y: 500 },
+ crystal_caves: { x: 3500, y: 2500 },
+ portal_ruins: { x: 2500, y: 2000 },
+ laboratory: { x: 3200, y: 1800 },
+ medical_facility: { x: 2800, y: 2200 },
+ abandoned_house: { x: 1500, y: 1200 },
+ atlantis: { x: 5000, y: 4000 },
+ chernobyl: { x: 5500, y: 2000 },
+ amazon: { x: 4500, y: 3500 },
+ dark_fortress: { x: 6000, y: 1000 },
+ final_castle: { x: 7000, y: 500 }
+ };
+
+ return biomePositions[biome] || { x: 0, y: 0 };
+ }
+
+ /**
+ * Discover a clue
+ */
+ discoverClue(clueId) {
+ if (this.discovered.has(clueId)) {
+ console.log('โน๏ธ Clue already discovered!');
+ return false;
+ }
+
+ const clue = this.allClues.find(c => c.id === clueId);
+ if (!clue) {
+ console.log(`โ Clue not found: ${clueId}`);
+ return false;
+ }
+
+ this.discovered.add(clueId);
+
+ console.log(`๐ DISCOVERED: ${clue.title} (${this.discovered.size}/50)`);
+
+ // Play discovery cutscene
+ this.playDiscoveryCutscene(clue);
+
+ // Check story progression
+ this.checkStoryUnlocks();
+
+ // Trigger Twin Bond reaction
+ if (this.scene.twinBondSystem) {
+ this.scene.twinBondSystem.triggerClueReaction(clue);
+ }
+
+ return true;
+ }
+
+ /**
+ * Play emotional discovery cutscene
+ */
+ playDiscoveryCutscene(clue) {
+ console.log(`๐ฌ CUTSCENE: Discovering "${clue.title}"`);
+
+ // Emotional reaction based on clue type
+ let kaiReaction = '';
+
+ switch (clue.type) {
+ case 'message':
+ kaiReaction = this.getMessageReaction(clue);
+ break;
+ case 'photo':
+ kaiReaction = this.getPhotoReaction(clue);
+ break;
+ case 'item':
+ kaiReaction = this.getItemReaction(clue);
+ break;
+ }
+
+ // Show cutscene UI
+ this.scene.events.emit('show-cutscene', {
+ type: 'clue_discovery',
+ clue: clue,
+ reaction: kaiReaction,
+ bondActivation: this.discovered.size % 5 === 0 // Twin Bond activates every 5 clues
+ });
+
+ // Play emotional music
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playEmotionalTheme();
+ }
+
+ console.log(`๐ญ Kai: "${kaiReaction}"`);
+ }
+
+ /**
+ * Get Kai's reaction to a message
+ */
+ getMessageReaction(clue) {
+ const reactions = {
+ msg_01: "*Picks up note with shaking hands*\nAna... your handwriting...\n*Traces fingers over words*\nYou were scared. I can feel it in how you wrote.\nBut you still thought of me. 'Stay safe'...\n\nNo, Ana. I won't stay safe.\nI'll find you. No matter how dangerous.\nTwin promise. ๐",
+
+ msg_02: "Red flowers... Ana's favorite!\n*Looks around, spots red flowers growing*\nShe left me a trail. Even captured, she thought ahead.\n\nThat's my sister. Always thinking.\nI'm following, Ana. Right behind you.",
+
+ msg_03: "Intelligence? She's right.\nMy zombie workers... they understand orders, learn tasks.\n\nAna saw it too. Even while captured.\nHer curiosity never stopped.\n\nMaybe... maybe zombies and humans can coexist.\nIf we survive this.",
+
+ msg_04: "*Clenches fists*\nThe Troll King. The one who took you.\n\nNow I know his name.\nNow I know my enemy.\n\nDr. Krniฤ may have created you, beast...\nBut I will DESTROY you.",
+
+ msg_05: "*Tears up*\n'Together'... Ana still believes we'll be together again.\n\nYes. We'll rebuild Hope Valley.\nYou, me, and whoever else survives.\nWe'll make it beautiful again.\n\nA home. Our home.",
+
+ msg_06: "*Gasps*\nYou can feel me?!\nANA! I'm here! I'm searching!\n*Closes eyes, concentrates*\n\nTwin bond... please work...\n*Faint purple glow*\n\nAna... I felt something. Just for a moment.\nYou're alive. I KNOW you're alive!",
+
+ msg_07: "Portals! Of course!\nAna, you genius!\nIf I repair these, I can search the world faster!\n\nI'll fix them all. Every single one.\nFor you.",
+
+ msg_08: "Domestication... that's what I'm doing.\nMy zombie workers are 'domesticated'.\n\nBut Ana found this disturbing?\nIs what I'm doing... wrong?\n\nNo. I'm helping them. Giving them purpose.\nThat's different from Dr. Krniฤ's experiments.",
+
+ msg_09: "A CURE?!\nAna... if there's a cure...\nWe could save EVERYONE!\n\nThe infected could become human again!\nMama, Dad... they could--\n\nNo. Too late for them.\nBut not too late for others.\n\nI'll find it, Ana. For you.",
+
+ msg_10: "*Sits down, holds note to chest*\nA family... children...\n\nAna, you're thinking about the future.\nEven in captivity, you dream of life.\n\nWe'll have that future. I swear it.\nYou'll see children play in safe fields.\nOur children. Our future.",
+
+ msg_11: "*Laughs through tears*\nOnly you, Ana. Only you would get excited about Atlantis.\nWhile captured. While in danger.\n\nYour curiosity, your wonder... it never dies.\nThat's what I love about you.\n\nI'll explore these ruins for you. Find every secret.",
+
+ msg_12: "Chernobyl... you went to the reactor?!\nAna, that's suicide!\n\nBut you left me a warning. Even there.\nYou knew I'd follow.\n\nRadiation won't stop me. Nothing will.\nI'm coming, Ana. Hold on.",
+
+ msg_13: "Biodiversity... scientist words even now.\n\nAna, you never stop learning.\nNever stop appreciating nature.\n\nWhen this is over, I'll take you on a real expedition.\nNo danger. Just discovery.\nTwin explorers. Together.",
+
+ msg_14: "*Crumples note, then smooths it out*\nSave MYSELF?!\nYou think I'd give up on you?!\n\nAna, I'd walk through hell for you.\nAlone if I had to.\n\nThis 'danger' you warn about?\nBRING IT ON.",
+
+ msg_15: "*Breaks down crying*\n'I love you, brother'...\n\nI love you too, Ana. So much.\n\nYou're right. I won't listen.\nI never do when it comes to you.\n\nI'm at the castle. I'm coming in.\nWait for me. Please."
+ };
+
+ return reactions[clue.id] || "Ana... another piece of you found.";
+ }
+
+ /**
+ * Get Kai's reaction to a photo
+ */
+ getPhotoReaction(clue) {
+ const reactions = {
+ photo_01: "*Stares at photo for long time*\nMom... Dad... Ana... me...\nWe were so happy.\n\nI'll make us happy again, Ana.\nDifferent, but happy.\nNew family. New memories.\n\nBut I'll never forget this.",
+
+ photo_02: "*Photo of Giant Troll, blurry*\nThere you are, monster.\nThe thing that destroyed everything.\n\nAna got a photo even while running.\nBrave. So brave.\n\nI'm coming for you, Troll.",
+
+ photo_03: "It WAS beautiful...\nGreen fields, happy people, clean buildings.\n\nWe can make it like this again.\nWith your help, Ana.\nTogether we'll rebuild paradise.",
+
+ photo_04: "A Level 1 zombie... looking confused.\nAna saw them as... beings. Not monsters.\n\nShe was always more empathetic than me.\nMaybe that's why she left this trail.\nShe knew I'd need to see them differently.",
+
+ photo_05: "Ana drew a MAP for me!\nAll 18 portals marked!\n\nShe knew I'd need to travel fast.\nThought of everything.\n\nThank you, sister. This helps SO much.",
+
+ photo_06: "Dr. Krniฤ's lab... still functional.\nHigh-tech equipment Ana photographed.\n\nIf I find this place... I could finish her research.\nFind the cure.\nSave everyone.",
+
+ photo_07: "*Close-up of handwriting*\nHer journal... during captivity.\n\nShe kept writing. Kept documenting.\nScientist to the end.\n\nI need to find this journal. All of it.",
+
+ photo_08: "Atlantean technology... glowing crystals!\nThis could power everything!\n\nAna, you found the future.\nAncient tech to save the modern world.\n\nI'll bring it back. For everyone.",
+
+ photo_09: "*Chernobyl reactor, green glow*\nThat's where Dr. Krniฤ is.\nFinal facility.\n\nAna went there. Survived radiation.\nIf she can... so can I.",
+
+ photo_10: "Ancient temple in Amazon...\nAna always loved archaeology.\n\nI'll explore it. For you.\nFind whatever secrets you wanted to discover.",
+
+ photo_11: "*Shadowy figure in photo*\nThis is him. Dr. Krniฤ.\nThe monster who started everything.\n\nYour face is blurry, coward.\nBut I'll see you clearly...\nRight before I END you.",
+
+ photo_12: "*Ana holding 'FIND ME' sign, tears in eyes*\n\n*Kai collapses to knees, sobbing*\n\nI SEE YOU, ANA! I SEE YOU!\nI'M COMING! I SWEAR I'M COMING!\n\nHold on! Please! Just hold on!"
+ };
+
+ return reactions[clue.id] || "Another memory of you...";
+ }
+
+ /**
+ * Get Kai's reaction to an item
+ */
+ getItemReaction(clue) {
+ const reactions = {
+ item_01: "*Silver twin bracelet*\nI have the matching one...\n*Holds both bracelets together*\nTwins. Always twins.\nSoon we'll wear these together again.",
+
+ item_02: "*Torn dress piece*\nFragment of Ana's favorite dress...\n*Holds it gently*\nYou loved this dress. Wore it on special days.\nI'll keep this safe for you.",
+
+ item_03: "*Muddy hairpin*\nAna's decorative hairpin...\n*Cleans mud off carefully*\nYou always wore this. Even while farming.\nIt's coming home with me.",
+
+ item_04: "*Father's pocket watch*\nDad's watch... Ana always carried it.\n*Opens it - shows family photo inside*\nYou kept us close. All of us.\nI understand, Ana.",
+
+ item_05: "*Engraved compass*\n'To Ana, find your way home'...\n*Compass points north*\nI'll use this to find YOU, Ana.\nThen we both go home. Together.",
+
+ item_06: "*Small stuffed rabbit*\nYour childhood toy!\n*Hugs it*\nWe got this when we were 5.\nYou never let it go.\nI won't either.",
+
+ item_07: "*Delicate music box*\n*Opens it - plays Ana's favorite song*\n*Sits and listens, tears flowing*\nThis song... you hummed it every night.\nI hear you, Ana. I hear you.",
+
+ item_08: "*Ana's art sketchbook*\n*Flips through pages*\nYour drawings... so beautiful.\nEven in danger, you created beauty.\nThat's who you are.",
+
+ item_09: "*Falls to knees*\nMama's necklace... Ana kept it through everything.\nProtected it.\n\n*Kisses necklace*\nMama... I'm protecting Ana now.\nLike you asked.",
+
+ item_10: "*Torn diary page*\n*Reads*\n'I'm scared. But I won't give up hope.'\n*Clutches page*\nI won't give up either, Ana. Never.",
+
+ item_17: "*Opens locket - sees his own photo*\n\n*Tears streaming*\nYou carried ME with you...\nWhile I searched for you...\nYou had me the whole time.\n\nTwin bond. Forever.",
+
+ item_23: "*Sealed envelope: 'Open when you find me'*\n\n*Hands trembling*\nNot yet. I'm not opening this yet.\nI'll open it when I SEE you.\nFace to face.\n\nSoon, Ana. Soon."
+ };
+
+ return reactions[clue.id] || `${clue.title}... you were here.`;
+ }
+
+ /**
+ * Check if story unlocks should trigger
+ */
+ checkStoryUnlocks() {
+ const count = this.discovered.size;
+
+ Object.keys(this.storyUnlocks).forEach(threshold => {
+ const unlockId = this.storyUnlocks[threshold];
+
+ if (count >= parseInt(threshold)) {
+ this.unlockStoryEvent(unlockId, parseInt(threshold));
+ }
+ });
+ }
+
+ /**
+ * Unlock story event
+ */
+ unlockStoryEvent(eventId, clueCount) {
+ console.log(`๐ญ STORY UNLOCK: ${eventId} (${clueCount} clues)`);
+
+ const events = {
+ act1_context: {
+ title: 'Act 1 Revealed',
+ message: 'The attack, the kidnapping... the full picture emerges.',
+ reward: 'Twin Bond Level +1'
+ },
+ troll_king_reveal: {
+ title: 'Giant Troll King\'s Identity',
+ message: 'The true nature of the captor revealed.',
+ reward: 'Boss Location Marked'
+ },
+ twin_bond_boost: {
+ title: 'Twin Bond Strengthens',
+ message: 'Your psychic connection with Ana intensifies!',
+ reward: 'Twin Bond abilities enhanced'
+ },
+ act2_context: {
+ title: 'Act 2 Begins',
+ message: 'Ana\'s journey takes unexpected turns...',
+ reward: 'New locations unlocked'
+ },
+ final_location_hint: {
+ title: 'Ana\'s Location',
+ message: 'You sense where she is... the final castle.',
+ reward: 'Final Castle location revealed'
+ },
+ true_ending_unlock: {
+ title: 'ALL CLUES FOUND!',
+ message: 'Every piece of Ana discovered. True Ending available!',
+ reward: 'True Ending unlocked'
+ }
+ };
+
+ const event = events[eventId];
+ if (event) {
+ this.scene.events.emit('story-unlock', event);
+
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playDramatic();
+ }
+ }
+ }
+
+ /**
+ * Get collection progress
+ */
+ getProgress() {
+ return {
+ total: this.allClues.length,
+ discovered: this.discovered.size,
+ percentage: Math.round((this.discovered.size / this.allClues.length) * 100),
+ messages: this.getTypeProgress('message'),
+ photos: this.getTypeProgress('photo'),
+ items: this.getTypeProgress('item')
+ };
+ }
+
+ /**
+ * Get progress by type
+ */
+ getTypeProgress(type) {
+ const typeClues = this.allClues.filter(c => c.type === type);
+ const discovered = typeClues.filter(c => this.discovered.has(c.id)).length;
+
+ return {
+ total: typeClues.length,
+ discovered: discovered,
+ percentage: Math.round((discovered / typeClues.length) * 100)
+ };
+ }
+
+ /**
+ * Show collection UI
+ */
+ showCollectionUI() {
+ const progress = this.getProgress();
+
+ console.log('๐ ANA\'S CLUES COLLECTION');
+ console.log(`Total: ${progress.discovered}/${progress.total} (${progress.percentage}%)`);
+ console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
+ console.log(`๐ Messages: ${progress.messages.discovered}/${progress.messages.total} (${progress.messages.percentage}%)`);
+ console.log(`๐ท Photos: ${progress.photos.discovered}/${progress.photos.total} (${progress.photos.percentage}%)`);
+ console.log(`๐ Items: ${progress.items.discovered}/${progress.items.total} (${progress.items.percentage}%)`);
+
+ // Emit UI event
+ this.scene.events.emit('show-clue-collection', {
+ progress: progress,
+ clues: this.allClues,
+ discovered: Array.from(this.discovered)
+ });
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AnimalBreedingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/AnimalBreedingSystem.js
new file mode 100644
index 000000000..54eaab8dc
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AnimalBreedingSystem.js
@@ -0,0 +1,591 @@
+/**
+ * ANIMAL BREEDING & GENETICS SYSTEM
+ * Complete breeding system with normal animals, genetics, mutants, and baby care
+ */
+class AnimalBreedingSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Animals
+ this.animals = new Map();
+ this.babies = [];
+ this.breedingPairs = [];
+
+ // Genetics
+ this.geneticTraits = new Map();
+ this.mutations = new Map();
+
+ // Breeding stations
+ this.breedingStations = new Map();
+
+ // Settings
+ this.settings = {
+ autoBreeding: true,
+ proximityRange: 2, // tiles
+ mutationChance: 0.05, // 5%
+ gestationSpeed: 1.0 // 1.0 = normal
+ };
+
+ // Gestation periods (in game days)
+ this.gestationPeriods = {
+ sheep: 7,
+ cow: 14,
+ chicken: 3,
+ pig: 10,
+ horse: 21,
+ mutant_cow: 30,
+ mutant_chicken: 15,
+ mutant_pig: 20,
+ mutant_horse: 40,
+ fire_sheep: 14
+ };
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Animal Breeding System initialized');
+ }
+
+ init() {
+ this.defineGeneticTraits();
+ this.defineMutations();
+ console.log('๐ Animal breeding ready');
+ }
+
+ // ========== GENETIC TRAITS ==========
+
+ defineGeneticTraits() {
+ // Colors (Mendelian genetics)
+ this.geneticTraits.set('color', {
+ dominant: ['brown', 'black'],
+ recessive: ['white', 'golden'],
+ rare: ['rainbow', 'crystal']
+ });
+
+ // Size
+ this.geneticTraits.set('size', {
+ values: ['small', 'medium', 'large'],
+ modifiers: { small: 0.8, medium: 1.0, large: 1.2 }
+ });
+
+ // Speed
+ this.geneticTraits.set('speed', {
+ values: ['slow', 'normal', 'fast'],
+ modifiers: { slow: 0.8, normal: 1.0, fast: 1.3 }
+ });
+
+ // Production
+ this.geneticTraits.set('production', {
+ values: ['low', 'normal', 'high'],
+ modifiers: { low: 0.7, normal: 1.0, high: 1.5 }
+ });
+ }
+
+ defineMutations() {
+ this.mutations.set('golden_fleece', {
+ chance: 0.05,
+ species: 'sheep',
+ effects: { production: 2.0, value: 5.0 },
+ rarity: 'legendary'
+ });
+
+ this.mutations.set('three_heads', {
+ chance: 0.03,
+ species: 'chicken',
+ effects: { production: 3.0, eggs: 3 },
+ rarity: 'epic'
+ });
+
+ this.mutations.set('giant', {
+ chance: 0.04,
+ species: 'pig',
+ effects: { size: 2.0, rideable: true },
+ rarity: 'epic'
+ });
+
+ this.mutations.set('undead', {
+ chance: 0.02,
+ species: 'horse',
+ effects: { stamina: Infinity, speed: 1.5 },
+ rarity: 'legendary'
+ });
+
+ this.mutations.set('fire_wool', {
+ chance: 0.01,
+ species: 'sheep',
+ effects: { fire_resistance: true, production: 1.5 },
+ rarity: 'mythic'
+ });
+ }
+
+ // ========== ANIMAL MANAGEMENT ==========
+
+ addAnimal(species, gender, x, y) {
+ const animal = {
+ id: `animal_${Date.now()}_${Math.random()}`,
+ species,
+ gender, // 'male' or 'female'
+ x, y,
+ age: 0,
+ stage: 'adult', // baby, teen, adult
+ genetics: this.generateGenetics(species),
+ pregnant: false,
+ gestationProgress: 0,
+ offspring: null,
+ lastBreed: 0,
+ sprite: null
+ };
+
+ this.animals.set(animal.id, animal);
+ return animal;
+ }
+
+ generateGenetics(species) {
+ return {
+ color: this.randomTrait('color'),
+ size: this.randomTrait('size'),
+ speed: this.randomTrait('speed'),
+ production: this.randomTrait('production'),
+ mutation: this.checkMutation(species)
+ };
+ }
+
+ randomTrait(traitType) {
+ const trait = this.geneticTraits.get(traitType);
+ const allValues = [...trait.dominant, ...trait.recessive];
+ return allValues[Math.floor(Math.random() * allValues.length)];
+ }
+
+ checkMutation(species) {
+ for (const [mutationId, mutation] of this.mutations.entries()) {
+ if (mutation.species === species && Math.random() < mutation.chance) {
+ return mutationId;
+ }
+ }
+ return null;
+ }
+
+ // ========== BREEDING ==========
+
+ attemptBreeding(animal1, animal2) {
+ // Check compatibility
+ if (!this.canBreed(animal1, animal2)) {
+ return false;
+ }
+
+ // Check proximity
+ const distance = Math.sqrt(
+ Math.pow(animal1.x - animal2.x, 2) +
+ Math.pow(animal1.y - animal2.y, 2)
+ );
+
+ if (distance > this.settings.proximityRange) {
+ return false;
+ }
+
+ // Breed!
+ return this.breed(animal1, animal2);
+ }
+
+ canBreed(animal1, animal2) {
+ // Same species
+ if (animal1.species !== animal2.species) return false;
+
+ // Different genders
+ if (animal1.gender === animal2.gender) return false;
+
+ // Both adults
+ if (animal1.stage !== 'adult' || animal2.stage !== 'adult') return false;
+
+ // Not pregnant
+ if (animal1.pregnant || animal2.pregnant) return false;
+
+ // Cooldown (1 day)
+ const now = Date.now();
+ if (now - animal1.lastBreed < 86400000) return false;
+ if (now - animal2.lastBreed < 86400000) return false;
+
+ return true;
+ }
+
+ breed(male, female) {
+ // Determine which is female
+ const mother = female.gender === 'female' ? female : male;
+ const father = female.gender === 'male' ? female : male;
+
+ // Create offspring genetics
+ const offspring = {
+ species: mother.species,
+ genetics: this.inheritGenetics(father, mother),
+ birthTime: null,
+ gestationStart: Date.now()
+ };
+
+ // Set mother as pregnant
+ mother.pregnant = true;
+ mother.gestationProgress = 0;
+ mother.offspring = offspring;
+ mother.lastBreed = Date.now();
+ father.lastBreed = Date.now();
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createHeartParticles(mother.x, mother.y);
+ }
+
+ console.log(`๐ ${mother.species} breeding started!`);
+ return true;
+ }
+
+ inheritGenetics(father, mother) {
+ const genetics = {
+ color: this.inheritColor(father.genetics.color, mother.genetics.color),
+ size: this.inheritTrait(father.genetics.size, mother.genetics.size),
+ speed: this.inheritTrait(father.genetics.speed, mother.genetics.speed),
+ production: this.inheritTrait(father.genetics.production, mother.genetics.production),
+ mutation: this.inheritMutation(father, mother)
+ };
+
+ return genetics;
+ }
+
+ inheritColor(color1, color2) {
+ const colorTrait = this.geneticTraits.get('color');
+
+ // Dominant genes
+ if (colorTrait.dominant.includes(color1)) return color1;
+ if (colorTrait.dominant.includes(color2)) return color2;
+
+ // 50/50 for recessive
+ return Math.random() < 0.5 ? color1 : color2;
+ }
+
+ inheritTrait(trait1, trait2) {
+ // 50% from each parent
+ return Math.random() < 0.5 ? trait1 : trait2;
+ }
+
+ inheritMutation(father, mother) {
+ // Both parents have mutation = 100% chance
+ if (father.genetics.mutation && mother.genetics.mutation) {
+ return father.genetics.mutation;
+ }
+
+ // One parent has mutation = 50% chance
+ if (father.genetics.mutation) {
+ return Math.random() < 0.5 ? father.genetics.mutation : null;
+ }
+ if (mother.genetics.mutation) {
+ return Math.random() < 0.5 ? mother.genetics.mutation : null;
+ }
+
+ // New mutation check
+ return this.checkMutation(father.species);
+ }
+
+ // ========== GESTATION & BIRTH ==========
+
+ updateGestation(delta) {
+ const dayInMs = 86400000 / this.settings.gestationSpeed;
+
+ for (const animal of this.animals.values()) {
+ if (!animal.pregnant) continue;
+
+ // Update gestation progress
+ const elapsed = Date.now() - animal.offspring.gestationStart;
+ const gestationDays = this.gestationPeriods[animal.species];
+ animal.gestationProgress = (elapsed / dayInMs) / gestationDays;
+
+ // Birth!
+ if (animal.gestationProgress >= 1.0) {
+ this.giveBirth(animal);
+ }
+ }
+ }
+
+ giveBirth(mother) {
+ const baby = {
+ id: `baby_${Date.now()}_${Math.random()}`,
+ species: mother.offspring.species,
+ gender: Math.random() < 0.5 ? 'male' : 'female',
+ x: mother.x,
+ y: mother.y,
+ age: 0,
+ stage: 'baby',
+ genetics: mother.offspring.genetics,
+ pregnant: false,
+ hunger: 100,
+ bonding: 0,
+ growthProgress: 0,
+ sprite: null
+ };
+
+ // Add to babies list
+ this.babies.push(baby);
+
+ // Add to animals (will become adult later)
+ this.animals.set(baby.id, baby);
+
+ // Reset mother
+ mother.pregnant = false;
+ mother.offspring = null;
+ mother.gestationProgress = 0;
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(baby.x, baby.y);
+ }
+
+ // Achievement
+ if (this.scene.uiGraphics) {
+ this.scene.uiGraphics.unlockAchievement('first_birth');
+ }
+
+ console.log(`๐ฃ Baby ${baby.species} born! (${baby.genetics.mutation || 'normal'})`);
+ return baby;
+ }
+
+ // ========== BABY CARE ==========
+
+ updateBabies(delta) {
+ const dayInMs = 86400000;
+
+ for (let i = this.babies.length - 1; i >= 0; i--) {
+ const baby = this.babies[i];
+
+ // Hunger
+ baby.hunger -= (delta / 60000) * 1; // 1% per minute
+ baby.hunger = Math.max(0, baby.hunger);
+
+ // Growth (only if fed)
+ if (baby.hunger > 30) {
+ baby.age += delta / dayInMs;
+ baby.growthProgress = baby.age / 14; // 14 days to adult
+
+ // Stage progression
+ if (baby.age > 3 && baby.stage === 'baby') {
+ baby.stage = 'teen';
+ console.log(`๐ ${baby.species} is now a teen!`);
+ }
+ if (baby.age > 14 && baby.stage === 'teen') {
+ baby.stage = 'adult';
+ this.babies.splice(i, 1); // Remove from babies
+ console.log(`๐ ${baby.species} is now an adult!`);
+ }
+ }
+
+ // Bonding (if player nearby)
+ if (this.isPlayerNearby(baby)) {
+ baby.bonding += (delta / 120000) * 1; // 1% per 2 minutes
+ baby.bonding = Math.min(100, baby.bonding);
+ }
+
+ // Starving
+ if (baby.hunger === 0) {
+ console.log(`๐ Baby ${baby.species} starved!`);
+ this.animals.delete(baby.id);
+ this.babies.splice(i, 1);
+ }
+ }
+ }
+
+ feedBaby(baby, food) {
+ if (!baby || baby.stage === 'adult') return false;
+
+ baby.hunger = Math.min(100, baby.hunger + 30);
+ baby.bonding += 5;
+
+ console.log(`๐ผ Fed baby ${baby.species}`);
+ return true;
+ }
+
+ isPlayerNearby(animal) {
+ if (!this.scene.player) return false;
+
+ const playerPos = this.scene.player.getPosition();
+ const distance = Math.sqrt(
+ Math.pow(animal.x - playerPos.x, 2) +
+ Math.pow(animal.y - playerPos.y, 2)
+ );
+
+ return distance < 3;
+ }
+
+ // ========== BREEDING STATIONS ==========
+
+ buildBreedingStation(type, x, y) {
+ const stations = {
+ love_pen: {
+ name: 'Love Pen',
+ capacity: 10,
+ autoBreed: true,
+ cost: { wood: 20, fence: 10 }
+ },
+ incubation_chamber: {
+ name: 'Incubation Chamber',
+ capacity: 5,
+ temperature: 37,
+ cost: { iron: 15, glass: 10 }
+ },
+ mutation_lab: {
+ name: 'Mutation Lab',
+ capacity: 2,
+ mutantOnly: true,
+ cost: { steel: 30, circuit: 20 }
+ },
+ cloning_vat: {
+ name: 'Cloning Vat',
+ capacity: 1,
+ cloning: true,
+ cost: { energy_crystal: 10, bio_gel: 50 }
+ }
+ };
+
+ const stationData = stations[type];
+ if (!stationData) return null;
+
+ const station = {
+ id: `${type}_${x}_${y}`,
+ type,
+ x, y,
+ ...stationData,
+ animals: [],
+ active: true
+ };
+
+ this.breedingStations.set(station.id, station);
+ console.log(`๐ Built ${stationData.name} at (${x}, ${y})`);
+ return station;
+ }
+
+ addAnimalToStation(stationId, animalId) {
+ const station = this.breedingStations.get(stationId);
+ const animal = this.animals.get(animalId);
+
+ if (!station || !animal) return false;
+
+ if (station.animals.length >= station.capacity) {
+ console.log('โ Station is full');
+ return false;
+ }
+
+ station.animals.push(animalId);
+ animal.x = station.x;
+ animal.y = station.y;
+
+ console.log(`๐ Added ${animal.species} to ${station.name}`);
+ return true;
+ }
+
+ updateBreedingStations() {
+ for (const station of this.breedingStations.values()) {
+ if (!station.active || !station.autoBreed) continue;
+
+ // Try to breed animals in station
+ const stationAnimals = station.animals
+ .map(id => this.animals.get(id))
+ .filter(a => a);
+
+ for (let i = 0; i < stationAnimals.length; i++) {
+ for (let j = i + 1; j < stationAnimals.length; j++) {
+ this.attemptBreeding(stationAnimals[i], stationAnimals[j]);
+ }
+ }
+ }
+ }
+
+ // ========== MUTANT BREEDING ==========
+
+ breedMutants(mutant1, mutant2, mutationSerum = false) {
+ if (!mutationSerum) {
+ console.log('โ Requires Mutation Serum!');
+ return { result: 'no_serum' };
+ }
+
+ // Failure chances
+ const roll = Math.random();
+
+ if (roll < 0.5) {
+ console.log('๐ Breeding failed - creature died');
+ return { result: 'death' };
+ }
+
+ if (roll < 0.8) {
+ console.log('๐ซ Offspring is sterile');
+ return { result: 'sterile' };
+ }
+
+ // Success!
+ const offspring = this.breed(mutant1, mutant2);
+
+ return {
+ result: 'success',
+ offspring,
+ loot: ['mutant_hide', 'mutant_horn', 'mutant_blood']
+ };
+ }
+
+ // ========== AUTO-BREEDING ==========
+
+ updateAutoBreeding() {
+ if (!this.settings.autoBreeding) return;
+
+ const animals = Array.from(this.animals.values());
+
+ for (let i = 0; i < animals.length; i++) {
+ for (let j = i + 1; j < animals.length; j++) {
+ this.attemptBreeding(animals[i], animals[j]);
+ }
+ }
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateGestation(delta);
+ this.updateBabies(delta);
+ this.updateBreedingStations();
+ this.updateAutoBreeding();
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ animals: Array.from(this.animals.values()).map(a => ({
+ id: a.id,
+ species: a.species,
+ gender: a.gender,
+ genetics: a.genetics,
+ age: a.age,
+ stage: a.stage
+ })),
+ stations: Array.from(this.breedingStations.values()).map(s => ({
+ id: s.id,
+ type: s.type,
+ x: s.x,
+ y: s.y
+ }))
+ };
+
+ localStorage.setItem('novafarma_animal_breeding', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_animal_breeding');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ console.log('โ
Animal breeding progress loaded');
+ } catch (error) {
+ console.error('Failed to load breeding progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐ Animal Breeding System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AnimalsSeedsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/AnimalsSeedsSystem.js
new file mode 100644
index 000000000..117271725
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AnimalsSeedsSystem.js
@@ -0,0 +1,557 @@
+/**
+ * ANIMALS & SEEDS SYSTEM
+ * Manages animal shop unlock, 16+ livestock types, seasonal seed shop, fruit trees, and greenhouse.
+ *
+ * Features:
+ * - Animal Shop Unlock: "Animal Rescue" quest, find 8 wild animals, 30-day breeding
+ * - 16+ Livestock: Normal + Mutant variants, Rainbow Chicken, Golden Goose
+ * - Seasonal Seed Shop: 100+ seed varieties across 4 seasons
+ * - Fruit Trees: 7 types (Cherry, Apple, Peach, etc.) + Ancient Fruit, Starfruit (legendary!)
+ * - Greenhouse System: Override seasonal restrictions, year-round farming
+ */
+class AnimalsSeedsSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Animal shop status
+ this.animalShopUnlocked = false;
+ this.animalRescueProgress = 0; // 0-8 animals found
+
+ // Livestock types
+ this.livestockTypes = {
+ // Normal variants
+ chicken: { name: 'Chicken', product: 'egg', productionTime: 1, cost: 100, rarity: 'common' },
+ cow: { name: 'Cow', product: 'milk', productionTime: 2, cost: 500, rarity: 'common' },
+ sheep: { name: 'Sheep', product: 'wool', productionTime: 3, cost: 300, rarity: 'common' },
+ goat: { name: 'Goat', product: 'goat_milk', productionTime: 2, cost: 400, rarity: 'common' },
+ pig: { name: 'Pig', product: 'truffle', productionTime: 3, cost: 600, rarity: 'common' },
+ rabbit: { name: 'Rabbit', product: 'rabbit_foot', productionTime: 1, cost: 200, rarity: 'common' },
+ duck: { name: 'Duck', product: 'duck_egg', productionTime: 1, cost: 150, rarity: 'common' },
+ ostrich: { name: 'Ostrich', product: 'giant_egg', productionTime: 7, cost: 1000, rarity: 'uncommon' },
+
+ // Mutant variants
+ mutant_chicken: { name: 'Mutant Chicken', product: 'void_egg', productionTime: 1, cost: 500, rarity: 'rare', mutation: true },
+ mutant_cow: { name: 'Mutant Cow', product: 'mutant_milk', productionTime: 2, cost: 2000, rarity: 'rare', mutation: true },
+ mutant_pig: { name: 'Mutant Pig', product: 'radioactive_truffle', productionTime: 3, cost: 2500, rarity: 'rare', mutation: true },
+
+ // Legendary variants
+ rainbow_chicken: { name: 'Rainbow Chicken', product: 'rainbow_egg', productionTime: 1, cost: 5000, rarity: 'legendary', special: true },
+ golden_goose: { name: 'Golden Goose', product: 'golden_egg', productionTime: 7, cost: 10000, rarity: 'legendary', special: true },
+ fairy_cow: { name: 'Fairy Cow', product: 'fairy_milk', productionTime: 2, cost: 7500, rarity: 'legendary', special: true },
+ phoenix_chicken: { name: 'Phoenix Chicken', product: 'phoenix_feather', productionTime: 14, cost: 15000, rarity: 'legendary', special: true },
+ unicorn: { name: 'Unicorn', product: 'unicorn_horn', productionTime: 30, cost: 25000, rarity: 'mythic', special: true }
+ };
+
+ // Player's livestock
+ this.playerLivestock = new Map();
+
+ // Seasonal seeds (100+ varieties!)
+ this.seasonalSeeds = {
+ spring: this.generateSpringSeeds(),
+ summer: this.generateSummerSeeds(),
+ fall: this.generateFallSeeds(),
+ winter: this.generateWinterSeeds()
+ };
+
+ // Fruit trees
+ this.fruitTrees = {
+ cherry: { name: 'Cherry Tree', fruit: 'cherry', season: 'spring', growTime: 28, production: 3, cost: 1000 },
+ apple: { name: 'Apple Tree', fruit: 'apple', season: 'fall', growTime: 28, production: 3, cost: 1000 },
+ peach: { name: 'Peach Tree', fruit: 'peach', season: 'summer', growTime: 28, production: 3, cost: 1200 },
+ orange: { name: 'Orange Tree', fruit: 'orange', season: 'winter', growTime: 28, production: 3, cost: 1200 },
+ pomegranate: { name: 'Pomegranate Tree', fruit: 'pomegranate', season: 'summer', growTime: 28, production: 2, cost: 1500 },
+ mango: { name: 'Mango Tree', fruit: 'mango', season: 'summer', growTime: 28, production: 2, cost: 1800 },
+ banana: { name: 'Banana Tree', fruit: 'banana', season: 'year_round', growTime: 28, production: 5, cost: 2000 },
+ ancient_fruit: { name: 'Ancient Fruit Tree', fruit: 'ancient_fruit', season: 'year_round', growTime: 56, production: 1, cost: 10000, legendary: true },
+ starfruit: { name: 'Starfruit Tree', fruit: 'starfruit', season: 'year_round', growTime: 84, production: 1, cost: 25000, legendary: true }
+ };
+
+ // Player's fruit trees
+ this.playerTrees = new Map();
+
+ // Greenhouse status
+ this.greenhousesBuilt = [];
+
+ console.log('๐๐ฑ Animals & Seeds System initialized!');
+ }
+
+ /**
+ * Generate spring seeds (25 varieties)
+ */
+ generateSpringSeeds() {
+ return [
+ { name: 'Parsnip', growTime: 4, sellPrice: 35, cost: 20 },
+ { name: 'Green Bean', growTime: 10, sellPrice: 40, cost: 60, regrow: 3 },
+ { name: 'Cauliflower', growTime: 12, sellPrice: 175, cost: 80 },
+ { name: 'Potato', growTime: 6, sellPrice: 80, cost: 50 },
+ { name: 'Tulip', growTime: 6, sellPrice: 30, cost: 20 },
+ { name: 'Kale', growTime: 6, sellPrice: 110, cost: 70 },
+ { name: 'Jazz Seeds', growTime: 7, sellPrice: 50, cost: 30 },
+ { name: 'Garlic', growTime: 4, sellPrice: 60, cost: 40 },
+ { name: 'Rhubarb', growTime: 13, sellPrice: 220, cost: 100 },
+ { name: 'Strawberry', growTime: 8, sellPrice: 120, cost: 100, regrow: 4 },
+ // Add 15 more varieties...
+ { name: 'Spring Onion', growTime: 5, sellPrice: 45, cost: 25 },
+ { name: 'Daffodil', growTime: 6, sellPrice: 35, cost: 20 },
+ { name: 'Primrose', growTime: 7, sellPrice: 50, cost: 30 },
+ { name: 'Wild Lettuce', growTime: 5, sellPrice: 55, cost: 35 },
+ { name: 'Radish', growTime: 4, sellPrice: 40, cost: 25 },
+ { name: 'Peas', growTime: 8, sellPrice: 65, cost: 45, regrow: 3 },
+ { name: 'Spinach', growTime: 6, sellPrice: 50, cost: 30 },
+ { name: 'Artichoke', growTime: 14, sellPrice: 200, cost: 120 },
+ { name: 'Turnip', growTime: 5, sellPrice: 50, cost: 30 },
+ { name: 'Carrot', growTime: 7, sellPrice: 70, cost: 45 },
+ { name: 'Leek', growTime: 8, sellPrice: 80, cost: 55 },
+ { name: 'Chive', growTime: 4, sellPrice: 35, cost: 20 },
+ { name: 'Asparagus', growTime: 15, sellPrice: 250, cost: 150 },
+ { name: 'Sweet Pea', growTime: 9, sellPrice: 90, cost: 60 },
+ { name: 'Blue Jazz', growTime: 7, sellPrice: 50, cost: 30 }
+ ];
+ }
+
+ /**
+ * Generate summer seeds (25 varieties)
+ */
+ generateSummerSeeds() {
+ return [
+ { name: 'Tomato', growTime: 11, sellPrice: 60, cost: 50, regrow: 4 },
+ { name: 'Melon', growTime: 12, sellPrice: 250, cost: 80 },
+ { name: 'Blueberry', growTime: 13, sellPrice: 50, cost: 80, regrow: 4 },
+ { name: 'Hot Pepper', growTime: 5, sellPrice: 40, cost: 40, regrow: 3 },
+ { name: 'Wheat', growTime: 4, sellPrice: 25, cost: 10 },
+ { name: 'Radish', growTime: 6, sellPrice: 90, cost: 40 },
+ { name: 'Poppy', growTime: 7, sellPrice: 140, cost: 100 },
+ { name: 'Corn', growTime: 14, sellPrice: 50, cost: 150, regrow: 4 },
+ { name: 'Hops', growTime: 11, sellPrice: 25, cost: 60, regrow: 1 },
+ { name: 'Red Cabbage', growTime: 9, sellPrice: 260, cost: 100 },
+ // Add 15 more...
+ { name: 'Watermelon', growTime: 12, sellPrice: 300, cost: 100 },
+ { name: 'Cucumber', growTime: 8, sellPrice: 45, cost: 35, regrow: 3 },
+ { name: 'Eggplant', growTime: 10, sellPrice: 80, cost: 55 },
+ { name: 'Zucchini', growTime: 7, sellPrice: 55, cost: 40, regrow: 3 },
+ { name: 'Bell Pepper', growTime: 9, sellPrice: 70, cost: 50 },
+ { name: 'Sunflower', growTime: 8, sellPrice: 80, cost: 60 },
+ { name: 'Pumpkin Seed (Summer)', growTime: 13, sellPrice: 150, cost: 90 },
+ { name: 'Cantaloupe', growTime: 11, sellPrice: 200, cost: 85 },
+ { name: 'Summer Squash', growTime: 6, sellPrice: 60, cost: 40 },
+ { name: 'Okra', growTime: 8, sellPrice: 65, cost: 45, regrow: 3 },
+ { name: 'Basil', growTime: 5, sellPrice: 40, cost: 25 },
+ { name: 'Cherry Tomato', growTime: 9, sellPrice: 50, cost: 40, regrow: 3 },
+ { name: 'Tomatillo', growTime: 10, sellPrice: 75, cost: 55 },
+ { name: 'Lima Bean', growTime: 12, sellPrice: 90, cost: 65, regrow: 4 },
+ { name: 'String Bean', growTime: 8, sellPrice: 55, cost: 40, regrow: 3 }
+ ];
+ }
+
+ /**
+ * Generate fall seeds (25 varieties)
+ */
+ generateFallSeeds() {
+ return [
+ { name: 'Corn', growTime: 14, sellPrice: 50, cost: 150, regrow: 4 },
+ { name: 'Pumpkin', growTime: 13, sellPrice: 320, cost: 100 },
+ { name: 'Eggplant', growTime: 5, sellPrice: 60, cost: 20, regrow: 5 },
+ { name: 'Bok Choy', growTime: 4, sellPrice: 80, cost: 50 },
+ { name: 'Yam', growTime: 10, sellPrice: 160, cost: 60 },
+ { name: 'Amaranth', growTime: 7, sellPrice: 150, cost: 70 },
+ { name: 'Grape', growTime: 10, sellPrice: 80, cost: 60, regrow: 3 },
+ { name: 'Artichoke', growTime: 8, sellPrice: 160, cost: 30 },
+ { name: 'Cranberries', growTime: 7, sellPrice: 75, cost: 240, regrow: 5 },
+ { name: 'Fairy Rose', growTime: 12, sellPrice: 290, cost: 200 },
+ // Add 15 more...
+ { name: 'Acorn Squash', growTime: 12, sellPrice: 180, cost: 95 },
+ { name: 'Butternut Squash', growTime: 14, sellPrice: 220, cost: 110 },
+ { name: 'Sweet Potato', growTime: 9, sellPrice: 130, cost: 70 },
+ { name: 'Carrot (Fall)', growTime: 7, sellPrice: 80, cost: 50 },
+ { name: 'Beetroot', growTime: 6, sellPrice: 90, cost: 55 },
+ { name: 'Broccoli', growTime: 8, sellPrice: 100, cost: 65 },
+ { name: 'Brussels Sprouts', growTime: 10, sellPrice: 120, cost: 75, regrow: 4 },
+ { name: 'Cauliflower (Fall)', growTime: 11, sellPrice: 180, cost: 85 },
+ { name: 'Kale (Fall)', growTime: 7, sellPrice: 115, cost: 75 },
+ { name: 'Leek (Fall)', growTime: 9, sellPrice: 95, cost: 60 },
+ { name: 'Parsnip (Fall)', growTime: 5, sellPrice: 45, cost: 25 },
+ { name: 'Spinach (Fall)', growTime: 6, sellPrice: 55, cost: 35 },
+ { name: 'Turnip (Fall)', growTime: 5, sellPrice: 50, cost: 30 },
+ { name: 'Sunflower (Fall)', growTime: 8, sellPrice: 85, cost: 65 },
+ { name: 'Wheat (Fall)', growTime: 4, sellPrice: 30, cost: 15 }
+ ];
+ }
+
+ /**
+ * Generate winter seeds (25 varieties - greenhouse only!)
+ */
+ generateWinterSeeds() {
+ return [
+ { name: 'Winter Seeds (Forage Mix)', growTime: 7, sellPrice: 30, cost: 5 },
+ { name: 'Snow Yam', growTime: 7, sellPrice: 100, cost: 0, forage: true },
+ { name: 'Crocus', growTime: 7, sellPrice: 60, cost: 0, forage: true },
+ { name: 'Winter Root', growTime: 7, sellPrice: 70, cost: 0, forage: true },
+ { name: 'Crystal Fruit', growTime: 7, sellPrice: 150, cost: 0, forage: true },
+ // Greenhouse-only crops
+ { name: 'Ancient Seed', growTime: 28, sellPrice: 550, cost: 1000 },
+ { name: 'Rare Seed', growTime: 24, sellPrice: 3000, cost: 1000 },
+ { name: 'Coffee Bean', growTime: 10, sellPrice: 15, cost: 100, regrow: 2 },
+ { name: 'Tea Sapling', growTime: 20, sellPrice: 500, cost: 2000 },
+ { name: 'Cactus Fruit Seed', growTime: 14, sellPrice: 150, cost: 50 },
+ // Add 15 more...
+ { name: 'Ice Lettuce', growTime: 8, sellPrice: 85, cost: 60, winterOnly: true },
+ { name: 'Frost Flower', growTime: 10, sellPrice: 120, cost: 80, winterOnly: true },
+ { name: 'Snow Pea', growTime: 9, sellPrice: 95, cost: 65, winterOnly: true },
+ { name: 'Winter Cabbage', growTime: 12, sellPrice: 180, cost: 100, winterOnly: true },
+ { name: 'Arctic Berry', growTime: 11, sellPrice: 140, cost: 90, winterOnly: true },
+ { name: 'Pine Cone Crop', growTime: 14, sellPrice: 200, cost: 120, winterOnly: true },
+ { name: 'Evergreen Herb', growTime: 6, sellPrice: 50, cost: 30, winterOnly: true },
+ { name: 'Holly Berry', growTime: 8, sellPrice: 75, cost: 50, winterOnly: true },
+ { name: 'Mistletoe', growTime: 10, sellPrice: 110, cost: 75, winterOnly: true },
+ { name: 'Poinsettia', growTime: 12, sellPrice: 160, cost: 100, winterOnly: true },
+ { name: 'Juniper Berry', growTime: 9, sellPrice: 90, cost: 60, winterOnly: true },
+ { name: 'Ice Flower', growTime: 8, sellPrice: 80, cost: 55, winterOnly: true },
+ { name: 'Glacier Melon', growTime: 15, sellPrice: 350, cost: 180, winterOnly: true },
+ { name: 'Northern Lights Flower', growTime: 13, sellPrice: 250, cost: 150, winterOnly: true },
+ { name: 'Permafrost Root', growTime: 7, sellPrice: 65, cost: 40, winterOnly: true }
+ ];
+ }
+
+ /**
+ * Start Animal Rescue quest
+ */
+ startAnimalRescueQuest() {
+ console.log('๐จ Quest Started: Animal Rescue!');
+ console.log('Find 8 wild animals to unlock the Animal Shop!');
+
+ this.scene.events.emit('quest-started', {
+ id: 'animal_rescue',
+ title: 'Animal Rescue',
+ description: 'Find 8 wild animals across the world',
+ progress: '0/8',
+ reward: 'Animal Shop Unlock'
+ });
+ }
+
+ /**
+ * Rescue a wild animal
+ */
+ rescueAnimal(animalType, position) {
+ if (this.animalRescueProgress >= 8) {
+ console.log('โน๏ธ All animals already rescued!');
+ return false;
+ }
+
+ this.animalRescueProgress++;
+
+ console.log(`๐พ Rescued ${animalType}! Progress: ${this.animalRescueProgress}/8`);
+
+ this.scene.events.emit('quest-progress', {
+ id: 'animal_rescue',
+ progress: `${this.animalRescueProgress}/8`
+ });
+
+ if (this.animalRescueProgress >= 8) {
+ this.unlockAnimalShop();
+ }
+
+ return true;
+ }
+
+ /**
+ * Unlock animal shop
+ */
+ unlockAnimalShop() {
+ this.animalShopUnlocked = true;
+
+ console.log('๐ Animal Shop UNLOCKED!');
+
+ this.scene.events.emit('notification', {
+ title: 'Animal Shop Unlocked!',
+ message: 'You can now buy livestock!',
+ icon: '๐'
+ });
+
+ this.scene.events.emit('quest-complete', {
+ id: 'animal_rescue',
+ reward: 'Animal Shop Access'
+ });
+ }
+
+ /**
+ * Buy livestock from shop
+ */
+ buyLivestock(type) {
+ if (!this.animalShopUnlocked) {
+ console.log('โ Animal Shop not unlocked! Complete Animal Rescue quest first.');
+ return { success: false, message: 'Shop locked' };
+ }
+
+ const livestock = this.livestockTypes[type];
+ if (!livestock) {
+ console.log(`โ Unknown livestock: ${type}`);
+ return { success: false, message: 'Unknown type' };
+ }
+
+ // Check if player has gold
+ if (this.scene.player && this.scene.player.gold < livestock.cost) {
+ return { success: false, message: `Need ${livestock.cost} gold!` };
+ }
+
+ // Deduct gold
+ if (this.scene.player) {
+ this.scene.player.gold -= livestock.cost;
+ }
+
+ // Create livestock
+ const livestockId = this.createLivestock(type);
+
+ console.log(`๐ Bought ${livestock.name} for ${livestock.cost} gold!`);
+
+ return { success: true, livestockId: livestockId };
+ }
+
+ /**
+ * Create livestock
+ */
+ createLivestock(type) {
+ const livestockData = this.livestockTypes[type];
+ const livestockId = `${type}_${Date.now()}`;
+
+ const animal = {
+ id: livestockId,
+ type: type,
+ name: livestockData.name,
+ product: livestockData.product,
+ productionTime: livestockData.productionTime,
+ daysSinceProduction: 0,
+ happiness: 100,
+ health: 100,
+ age: 0,
+ breeding: false,
+ breedingDaysLeft: 0
+ };
+
+ this.playerLivestock.set(livestockId, animal);
+
+ return livestockId;
+ }
+
+ /**
+ * Update livestock (called daily)
+ */
+ updateLivestock() {
+ this.playerLivestock.forEach((animal, id) => {
+ animal.age++;
+ animal.daysSinceProduction++;
+
+ // Produce item
+ if (animal.daysSinceProduction >= animal.productionTime) {
+ this.produceAnimalProduct(id);
+ animal.daysSinceProduction = 0;
+ }
+
+ // Breeding countdown
+ if (animal.breeding) {
+ animal.breedingDaysLeft--;
+ if (animal.breedingDaysLeft <= 0) {
+ this.birthAnimal(id);
+ }
+ }
+ });
+ }
+
+ /**
+ * Produce animal product
+ */
+ produceAnimalProduct(livestockId) {
+ const animal = this.playerLivestock.get(livestockId);
+ if (!animal) return;
+
+ const product = animal.product;
+
+ console.log(`๐ฅ ${animal.name} produced ${product}!`);
+
+ // Add to inventory
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(product, 1);
+ }
+ }
+
+ /**
+ * Breed two animals (30 days)
+ */
+ breedAnimals(animal1Id, animal2Id) {
+ const animal1 = this.playerLivestock.get(animal1Id);
+ const animal2 = this.playerLivestock.get(animal2Id);
+
+ if (!animal1 || !animal2) {
+ return { success: false, message: 'Animal not found' };
+ }
+
+ if (animal1.type !== animal2.type) {
+ return { success: false, message: 'Animals must be same type!' };
+ }
+
+ if (animal1.breeding || animal2.breeding) {
+ return { success: false, message: 'Animal already breeding!' };
+ }
+
+ // Start breeding
+ animal1.breeding = true;
+ animal1.breedingDaysLeft = 30;
+
+ console.log(`๐ Breeding ${animal1.name}! Baby in 30 days!`);
+
+ return { success: true, daysRemaining: 30 };
+ }
+
+ /**
+ * Birth new animal
+ */
+ birthAnimal(parentId) {
+ const parent = this.playerLivestock.get(parentId);
+ if (!parent) return;
+
+ parent.breeding = false;
+
+ const newLivestockId = this.createLivestock(parent.type);
+
+ console.log(`๐ฃ ${parent.name} had a baby!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Baby Born!',
+ message: `New ${parent.name} baby!`,
+ icon: '๐ฃ'
+ });
+ }
+
+ /**
+ * Plant fruit tree
+ */
+ plantTree(treeType, position) {
+ const treeData = this.fruitTrees[treeType];
+ if (!treeData) {
+ console.log(`โ Unknown tree type: ${treeType}`);
+ return null;
+ }
+
+ // Check if player has gold
+ if (this.scene.player && this.scene.player.gold < treeData.cost) {
+ return { success: false, message: `Need ${treeData.cost} gold!` };
+ }
+
+ // Deduct gold
+ if (this.scene.player) {
+ this.scene.player.gold -= treeData.cost;
+ }
+
+ const treeId = `tree_${treeType}_${Date.now()}`;
+ const tree = {
+ id: treeId,
+ type: treeType,
+ name: treeData.name,
+ fruit: treeData.fruit,
+ season: treeData.season,
+ production: treeData.production,
+ position: position,
+ growthDays: 0,
+ growTime: treeData.growTime,
+ mature: false
+ };
+
+ this.playerTrees.set(treeId, tree);
+
+ console.log(`๐ณ Planted ${treeData.name}! Mature in ${treeData.growTime} days.`);
+
+ return { success: true, treeId: treeId };
+ }
+
+ /**
+ * Update trees (called daily)
+ */
+ updateTrees(currentSeason) {
+ this.playerTrees.forEach((tree, id) => {
+ if (!tree.mature) {
+ tree.growthDays++;
+ if (tree.growthDays >= tree.growTime) {
+ tree.mature = true;
+ console.log(`๐ณ ${tree.name} is now mature!`);
+ }
+ } else {
+ // Produce fruit if in season or year-round
+ if (tree.season === 'year_round' || tree.season === currentSeason) {
+ this.produceFruit(id);
+ }
+ }
+ });
+ }
+
+ /**
+ * Produce fruit from tree
+ */
+ produceFruit(treeId) {
+ const tree = this.playerTrees.get(treeId);
+ if (!tree || !tree.mature) return;
+
+ const amount = tree.production;
+
+ console.log(`๐ ${tree.name} produced ${amount} ${tree.fruit}!`);
+
+ // Add to inventory
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(tree.fruit, amount);
+ }
+ }
+
+ /**
+ * Build greenhouse
+ */
+ buildGreenhouse(position) {
+ const greenhouseId = `greenhouse_${Date.now()}`;
+ const greenhouse = {
+ id: greenhouseId,
+ position: position,
+ capacity: 100, // 100 crops max
+ active: true
+ };
+
+ this.greenhousesBuilt.push(greenhouse);
+
+ console.log('๐ก Greenhouse built! Year-round farming enabled!');
+
+ this.scene.events.emit('notification', {
+ title: 'Greenhouse Built!',
+ message: 'You can now grow crops all year long!',
+ icon: '๐ก'
+ });
+
+ return greenhouseId;
+ }
+
+ /**
+ * Check if crop can grow (season or greenhouse)
+ */
+ canPlantSeed(seedName, currentSeason) {
+ // Find seed in all seasons
+ let seedData = null;
+ let seedSeason = null;
+
+ Object.keys(this.seasonalSeeds).forEach(season => {
+ const found = this.seasonalSeeds[season].find(s => s.name === seedName);
+ if (found) {
+ seedData = found;
+ seedSeason = season;
+ }
+ });
+
+ if (!seedData) return { canPlant: false, reason: 'Unknown seed' };
+
+ // Check if correct season
+ if (seedSeason === currentSeason) {
+ return { canPlant: true, reason: 'In season' };
+ }
+
+ // Check if greenhouse available
+ if (this.greenhousesBuilt.length > 0) {
+ return { canPlant: true, reason: 'Greenhouse available' };
+ }
+
+ return { canPlant: false, reason: `Wrong season (needs ${seedSeason})` };
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AudioManager.js b/EMERGENCY_SYSTEMS_RECOVERY/AudioManager.js
new file mode 100644
index 000000000..7c0d93d3a
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AudioManager.js
@@ -0,0 +1,568 @@
+/**
+ * AUDIO MANAGER - Centralized Audio System
+ * Handles all music, voiceover, and SFX playback
+ * Includes DEBUG MODE for tracking what plays where
+ */
+
+class AudioManager {
+ constructor() {
+ this.scene = null;
+ this.debugMode = true; // SET TO FALSE TO DISABLE LOGGING
+
+ // ๐ฅ STREAMER MODE (DMCA Protection)
+ this.streamerMode = false;
+ this.loadStreamerMode();
+
+ // Current playback tracking
+ this.currentMusic = null;
+ this.currentVoice = null;
+ this.musicVolume = 0.7;
+ this.voiceVolume = 0.8;
+ this.sfxVolume = 0.5;
+
+ // Copyright-safe tracks (CC BY 4.0)
+ this.safeMusicTracks = [
+ 'intro_ambient', 'main_theme', 'farm_ambient', 'forest_ambient',
+ 'night_theme', 'town_theme', 'wilderness_theme', 'combat_theme',
+ 'ana_theme', 'raid_warning', 'victory_theme'
+ ];
+
+ // Audio mapping
+ this.audioMap = {
+ music: {
+ intro: 'intro_ambient',
+ menu: 'main_theme',
+ farm: 'farm_ambient',
+ forest: 'forest_ambient',
+ night: 'night_theme',
+ town: 'town_theme',
+ wilderness: 'wilderness_theme',
+ combat: 'combat_theme',
+ ana: 'ana_theme',
+ raid: 'raid_warning',
+ victory: 'victory_theme'
+ },
+ voice: {
+ kai_en: Array.from({ length: 12 }, (_, i) => `kai_en_${String(i + 1).padStart(2, '0')}`),
+ ana_en: Array.from({ length: 8 }, (_, i) => `ana_en_${String(i + 1).padStart(2, '0')}`),
+ gronk_en: ['gronk_en_01']
+ },
+ sfx: {
+ ui_click: 'ui_click',
+ ui_hover: 'ui_hover',
+ plant: 'plant_seed',
+ water: 'water_crops',
+ harvest: 'harvest_crop',
+ cat_meow: 'cat_meow',
+ dog_bark: 'dog_bark',
+ susi_bark: 'susi_bark',
+ memory_found: 'memory_found',
+ level_up: 'level_up'
+ }
+ };
+
+ // License attribution
+ this.attribution = {
+ music: 'Music by Kevin MacLeod (incompetech.com)\nLicensed under Creative Commons: By Attribution 3.0\nhttp://creativecommons.org/licenses/by/3.0/',
+ voices: 'Voices generated using Microsoft Azure Edge TTS\nVoices: en-US-ChristopherNeural, en-US-AriaNeural, en-GB-RyanNeural'
+ };
+ }
+
+ /**
+ * INITIALIZE AUDIO MANAGER
+ */
+ init(scene) {
+ this.scene = scene;
+ this.log('AudioManager initialized', 'INIT');
+ }
+
+ /**
+ * DEBUG LOGGER
+ */
+ log(message, type = 'INFO', details = {}) {
+ if (!this.debugMode) return;
+
+ const emoji = {
+ 'MUSIC': '๐ต',
+ 'VOICE': '๐ค',
+ 'SFX': '๐',
+ 'INIT': '๐๏ธ',
+ 'STOP': 'โน๏ธ',
+ 'ERROR': 'โ',
+ 'INFO': 'โน๏ธ'
+ };
+
+ const timestamp = new Date().toLocaleTimeString();
+ console.log(`${emoji[type] || '๐ข'} [${type}] ${timestamp} - ${message}`);
+
+ if (Object.keys(details).length > 0) {
+ console.log(' Details:', details);
+ }
+ }
+
+ /**
+ * PLAY MUSIC
+ *
+ * COPYRIGHT ATTRIBUTION:
+ * All music tracks are by Kevin MacLeod (incompetech.com)
+ * Licensed under Creative Commons Attribution 4.0 International (CC BY 4.0)
+ * License URL: http://creativecommons.org/licenses/by/4.0/
+ *
+ * REQUIRED CREDIT:
+ * "Music by Kevin MacLeod (incompetech.com)
+ * Licensed under Creative Commons: By Attribution 4.0
+ * http://creativecommons.org/licenses/by/4.0/"
+ *
+ * See /docs/CREDITS.txt for complete attribution
+ */
+ playMusic(key, options = {}) {
+ if (!this.scene) {
+ this.log('Scene not initialized!', 'ERROR');
+ return null;
+ }
+
+ // Stop current music
+ if (this.currentMusic) {
+ this.stopMusic();
+ }
+
+ // Get file key from map
+ const fileKey = this.audioMap.music[key] || key;
+
+ // Check if audio exists
+ if (!this.scene.cache.audio.exists(fileKey)) {
+ this.log(`Music not found: ${fileKey}`, 'ERROR');
+ return null;
+ }
+
+ // Play music
+ const config = {
+ volume: options.volume || this.musicVolume,
+ loop: options.loop !== undefined ? options.loop : true,
+ ...options
+ };
+
+ this.currentMusic = this.scene.sound.add(fileKey, config);
+ this.currentMusic.play();
+
+ // Debug log
+ this.log(`Playing: ${fileKey}.mp3`, 'MUSIC', {
+ scene: this.scene.scene.key,
+ volume: config.volume,
+ loop: config.loop,
+ attribution: 'Kevin MacLeod (incompetech.com)'
+ });
+
+ return this.currentMusic;
+ }
+
+ /**
+ * STOP MUSIC
+ */
+ stopMusic(fadeOut = true) {
+ if (!this.currentMusic) return;
+
+ this.log(`Stopping: ${this.currentMusic.key}`, 'STOP');
+
+ if (fadeOut) {
+ this.scene.tweens.add({
+ targets: this.currentMusic,
+ volume: 0,
+ duration: 1000,
+ onComplete: () => {
+ if (this.currentMusic) {
+ this.currentMusic.stop();
+ this.currentMusic = null;
+ }
+ }
+ });
+ } else {
+ this.currentMusic.stop();
+ this.currentMusic = null;
+ }
+ }
+
+ /**
+ * CROSSFADE MUSIC
+ */
+ crossfadeMusic(newKey, duration = 2000) {
+ const oldMusic = this.currentMusic;
+
+ // Start new music at volume 0
+ const newMusic = this.playMusic(newKey, { volume: 0 });
+
+ if (!newMusic) return;
+
+ // Fade out old, fade in new
+ if (oldMusic) {
+ this.scene.tweens.add({
+ targets: oldMusic,
+ volume: 0,
+ duration: duration,
+ onComplete: () => {
+ oldMusic.stop();
+ }
+ });
+ }
+
+ this.scene.tweens.add({
+ targets: newMusic,
+ volume: this.musicVolume,
+ duration: duration
+ });
+
+ this.log(`Crossfading to: ${newKey}`, 'MUSIC', {
+ duration: `${duration}ms`,
+ from: oldMusic ? oldMusic.key : 'none'
+ });
+
+ this.currentMusic = newMusic;
+ }
+
+ /**
+ * PLAY VOICE
+ *
+ * COPYRIGHT ATTRIBUTION:
+ * Voice Acting - Internal Assets
+ *
+ * PROJECT: Hipodevil666 Studios - Antigravity IDE Internal Assets
+ * VOICES: Christopher & Aria (AI High-Fidelity Voice Synthesis)
+ * LANGUAGES: English (EN) & Slovenian (SL)
+ *
+ * CHARACTERS:
+ * - Kai (Protagonist): Christopher Voice (AI-generated, male, young adult)
+ * - Ana (Twin Sister): Aria Voice (AI-generated, female, young adult)
+ * - Gronk (Orc Mentor): Custom deep voice synthesis
+ *
+ * TECHNOLOGY:
+ * High-fidelity AI voice synthesis technology
+ * Internal studio assets - Hipodevil666 Studios
+ * Licensed for commercial use in this project
+ *
+ * See /docs/CREDITS.txt for complete attribution
+ */
+ playVoice(key, subtitleText = '') {
+ if (!this.scene) {
+ this.log('Scene not initialized!', 'ERROR');
+ return null;
+ }
+
+ // Stop current voice
+ if (this.currentVoice && this.currentVoice.isPlaying) {
+ this.currentVoice.stop();
+ }
+
+ // Check if audio exists
+ if (!this.scene.cache.audio.exists(key)) {
+ this.log(`Voice not found: ${key}`, 'ERROR');
+ return null;
+ }
+
+ // Play voice
+ this.currentVoice = this.scene.sound.add(key, {
+ volume: this.voiceVolume
+ });
+ this.currentVoice.play();
+
+ // Debug log
+ this.log(`Playing: ${key}.mp3`, 'VOICE', {
+ scene: this.scene.scene.key,
+ subtitle: subtitleText || '(no subtitle)',
+ volume: this.voiceVolume,
+ duration: `~3s`
+ });
+
+ return this.currentVoice;
+ }
+
+ /**
+ * PLAY SFX
+ *
+ * COPYRIGHT ATTRIBUTION:
+ * Sound effects from Freesound.org (CC BY 4.0)
+ * License: https://creativecommons.org/licenses/by/4.0/
+ *
+ * CREDITED AUTHORS:
+ *
+ * 1. COW SOUND EFFECT
+ * Author: Benboncan
+ * Link: https://freesound.org/s/58277/
+ * License: CC BY 4.0
+ *
+ * 2. MINING & HAMMER SOUNDS
+ * Author: InspectorJ (www.jshaw.co.uk)
+ * Link: https://freesound.org/s/420878/
+ * License: CC BY 4.0
+ *
+ * 3. INTRO FOREST AMBIENCE
+ * Author: reinsamba
+ * Link: https://freesound.org/s/18765/
+ * License: CC BY 4.0
+ *
+ * 4. BASEMENT WATER DROPS
+ * Author: erlipresidente
+ * Link: https://freesound.org/s/415885/
+ * License: CC BY 4.0
+ *
+ * 5. COMBAT / ZOMBIE HIT
+ * Author: MisterKidX
+ * Link: https://freesound.org/s/454837/
+ * License: CC BY 4.0
+ *
+ * TOOLS USED:
+ * - Audacity (GPL v2) - https://www.audacityteam.org/
+ * - LMMS (GPL v2) - https://lmms.io/
+ *
+ * See /docs/CREDITS.txt for complete attribution
+ */
+ playSFX(key, options = {}) {
+ if (!this.scene) {
+ this.log('Scene not initialized!', 'ERROR');
+ return null;
+ }
+
+ // Get file key from map
+ const fileKey = this.audioMap.sfx[key] || key;
+
+ // Check if audio exists
+ if (!this.scene.cache.audio.exists(fileKey)) {
+ this.log(`SFX not found: ${fileKey}`, 'ERROR');
+ return null;
+ }
+
+ // Play SFX
+ const config = {
+ volume: options.volume || this.sfxVolume,
+ loop: options.loop || false,
+ detune: options.detune || 0,
+ ...options
+ };
+
+ const sfx = this.scene.sound.add(fileKey, config);
+ sfx.play();
+
+ // Debug log
+ this.log(`Playing: ${fileKey}.wav`, 'SFX', {
+ scene: this.scene.scene.key,
+ trigger: options.trigger || 'manual',
+ volume: config.volume,
+ loop: config.loop
+ });
+
+ return sfx;
+ }
+
+ /**
+ * PLAY UI SOUND
+ */
+ playUI(action) {
+ const sounds = {
+ click: 'ui_click',
+ hover: 'ui_hover',
+ open: 'ui_open',
+ close: 'ui_close',
+ error: 'ui_error'
+ };
+
+ if (sounds[action]) {
+ this.playSFX(sounds[action], {
+ trigger: `UI ${action}`,
+ volume: this.sfxVolume * 0.8
+ });
+ }
+ }
+
+ /**
+ * PLAY FARMING SOUND
+ */
+ playFarming(action) {
+ const sounds = {
+ plant: 'plant',
+ water: 'water',
+ harvest: 'harvest',
+ hoe: 'hoe_dirt'
+ };
+
+ if (sounds[action]) {
+ this.playSFX(sounds[action], {
+ trigger: `Farming: ${action}`,
+ volume: this.sfxVolume
+ });
+ }
+ }
+
+ /**
+ * PLAY ANIMAL SOUND
+ */
+ playAnimal(type) {
+ const sounds = {
+ cat: 'cat_meow',
+ dog: 'dog_bark',
+ susi: 'susi_bark'
+ };
+
+ if (sounds[type]) {
+ this.playSFX(sounds[type], {
+ trigger: `Animal: ${type}`,
+ volume: this.sfxVolume * 0.7,
+ detune: Phaser.Math.Between(-200, 200) // Vary pitch
+ });
+ }
+ }
+
+ /**
+ * PLAY SPECIAL EFFECT
+ */
+ playSpecial(event) {
+ const sounds = {
+ memory: 'memory_found',
+ levelup: 'level_up',
+ quest: 'quest_complete',
+ unlock: 'companion_unlock'
+ };
+
+ if (sounds[event]) {
+ this.playSFX(sounds[event], {
+ trigger: `Special: ${event}`,
+ volume: this.sfxVolume * 1.2 // Louder for important events
+ });
+ }
+ }
+
+ /**
+ * SET VOLUMES
+ */
+ setMusicVolume(volume) {
+ this.musicVolume = Phaser.Math.Clamp(volume, 0, 1);
+ if (this.currentMusic) {
+ this.currentMusic.setVolume(this.musicVolume);
+ }
+ this.log(`Music volume set to: ${this.musicVolume}`, 'INFO');
+ }
+
+ setVoiceVolume(volume) {
+ this.voiceVolume = Phaser.Math.Clamp(volume, 0, 1);
+ this.log(`Voice volume set to: ${this.voiceVolume}`, 'INFO');
+ }
+
+ setSFXVolume(volume) {
+ this.sfxVolume = Phaser.Math.Clamp(volume, 0, 1);
+ this.log(`SFX volume set to: ${this.sfxVolume}`, 'INFO');
+ }
+
+ /**
+ * MUTE ALL AUDIO
+ */
+ muteAll() {
+ if (this.scene) {
+ this.scene.sound.mute = true;
+ this.log('All audio muted', 'INFO');
+ }
+ }
+
+ /**
+ * UNMUTE ALL AUDIO
+ */
+ unmuteAll() {
+ if (this.scene) {
+ this.scene.sound.mute = false;
+ this.log('All audio unmuted', 'INFO');
+ }
+ }
+
+ /**
+ * STOP ALL AUDIO
+ */
+ stopAll() {
+ if (this.scene) {
+ this.scene.sound.stopAll();
+ this.currentMusic = null;
+ this.currentVoice = null;
+ this.log('All audio stopped', 'STOP');
+ }
+ }
+
+ /**
+ * GET ATTRIBUTION TEXT
+ */
+ getAttribution(type = 'all') {
+ if (type === 'music') {
+ return this.attribution.music;
+ } else if (type === 'voices') {
+ return this.attribution.voices;
+ } else {
+ return `${this.attribution.music}\n\n${this.attribution.voices}`;
+ }
+ }
+
+ /**
+ * ENABLE/DISABLE DEBUG MODE
+ */
+ setDebugMode(enabled) {
+ this.debugMode = enabled;
+ this.log(`Debug mode ${enabled ? 'enabled' : 'disabled'}`, 'INFO');
+ }
+
+ /**
+ * ๐ฅ STREAMER MODE - DMCA PROTECTION
+ */
+ enableStreamerMode() {
+ this.streamerMode = true;
+ localStorage.setItem('streamer_mode', 'true');
+
+ // Mute music if currently playing non-safe track
+ if (this.currentMusic && !this.isSafeTrack(this.currentMusic.key)) {
+ this.stopMusic();
+ console.log('๐ฅ STREAMER MODE: Music stopped (DMCA protection)');
+ }
+
+ console.log('๐ฅ STREAMER MODE ENABLED');
+ console.log(' โ
Safe for Twitch/YouTube');
+ console.log(' โ
All music is CC BY 4.0 (Kevin MacLeod)');
+ console.log(' โ
No copyright strikes possible');
+ }
+
+ disableStreamerMode() {
+ this.streamerMode = false;
+ localStorage.setItem('streamer_mode', 'false');
+ console.log('๐ฅ Streamer mode disabled');
+ }
+
+ loadStreamerMode() {
+ const saved = localStorage.getItem('streamer_mode');
+ if (saved === 'true') {
+ this.streamerMode = true;
+ console.log('๐ฅ Streamer mode loaded from settings');
+ }
+ }
+
+ isStreamerModeEnabled() {
+ return this.streamerMode;
+ }
+
+ isSafeTrack(trackKey) {
+ return this.safeMusicTracks.includes(trackKey);
+ }
+
+ getStreamerStatus() {
+ if (this.streamerMode) {
+ return {
+ enabled: true,
+ status: 'Streamer Mode: ON - Safe for Twitch/YouTube',
+ license: 'All music: CC BY 4.0 (Kevin MacLeod)',
+ safe: true
+ };
+ } else {
+ return {
+ enabled: false,
+ status: 'Streamer Mode: OFF',
+ license: 'Music may be copyrighted',
+ safe: false
+ };
+ }
+ }
+}
+
+// Singleton export
+const audioManager = new AudioManager();
+export default audioManager;
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AudioTriggerSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/AudioTriggerSystem.js
new file mode 100644
index 000000000..6f8252611
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AudioTriggerSystem.js
@@ -0,0 +1,181 @@
+/**
+ * AudioTriggerSystem.js
+ * Spatial audio triggers - play sound once when player enters area
+ */
+
+class AudioTriggerSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Active triggers
+ this.triggers = new Map();
+
+ // Triggered history (to prevent re-triggering)
+ this.triggered = new Set();
+
+ console.log('๐ AudioTriggerSystem initialized');
+ }
+
+ /**
+ * Add a spatial audio trigger
+ * @param {number} x - Grid X position
+ * @param {number} y - Grid Y position
+ * @param {string} audioKey - Audio file key
+ * @param {object} options - Additional options
+ */
+ addTrigger(x, y, audioKey, options = {}) {
+ const triggerId = `${x},${y}`;
+
+ const trigger = {
+ x,
+ y,
+ audioKey,
+ radius: options.radius || 0, // 0 = exact tile only
+ volume: options.volume || 1.0,
+ oneTime: options.oneTime !== false, // Default true
+ delay: options.delay || 0, // Delay before playing (ms)
+ callback: options.callback || null, // Optional callback after playing
+ visualDebug: options.visualDebug || false
+ };
+
+ this.triggers.set(triggerId, trigger);
+
+ // Add visual debug marker if enabled
+ if (trigger.visualDebug && this.scene.add) {
+ const worldX = x * 48 + 24;
+ const worldY = y * 48 + 24;
+
+ const circle = this.scene.add.circle(worldX, worldY, 20, 0x00ff00, 0.3);
+ circle.setDepth(1000);
+
+ const text = this.scene.add.text(worldX, worldY - 30, '๐', {
+ fontSize: '20px',
+ color: '#00ff00'
+ });
+ text.setOrigin(0.5);
+ text.setDepth(1001);
+ }
+
+ console.log(`โ
Audio trigger added at (${x}, ${y}): ${audioKey}`);
+
+ return triggerId;
+ }
+
+ /**
+ * Remove a trigger
+ */
+ removeTrigger(triggerId) {
+ this.triggers.delete(triggerId);
+ this.triggered.delete(triggerId);
+ }
+
+ /**
+ * Reset trigger (allow re-triggering)
+ */
+ resetTrigger(triggerId) {
+ this.triggered.delete(triggerId);
+ }
+
+ /**
+ * Reset all triggers
+ */
+ resetAll() {
+ this.triggered.clear();
+ }
+
+ /**
+ * Check if player is in trigger zone
+ */
+ checkTrigger(playerX, playerY) {
+ const playerGridX = Math.floor(playerX / 48);
+ const playerGridY = Math.floor(playerY / 48);
+
+ this.triggers.forEach((trigger, triggerId) => {
+ // Skip if already triggered and it's one-time only
+ if (trigger.oneTime && this.triggered.has(triggerId)) {
+ return;
+ }
+
+ // Check distance
+ const dx = Math.abs(playerGridX - trigger.x);
+ const dy = Math.abs(playerGridY - trigger.y);
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance <= trigger.radius) {
+ // TRIGGER!
+ this.activateTrigger(trigger, triggerId);
+ }
+ });
+ }
+
+ /**
+ * Activate a trigger (play audio)
+ */
+ activateTrigger(trigger, triggerId) {
+ console.log(`๐ TRIGGER ACTIVATED: ${triggerId} (${trigger.audioKey})`);
+
+ // Mark as triggered
+ this.triggered.add(triggerId);
+
+ // Play audio after delay
+ if (trigger.delay > 0) {
+ this.scene.time.delayedCall(trigger.delay, () => {
+ this.playAudio(trigger);
+ });
+ } else {
+ this.playAudio(trigger);
+ }
+ }
+
+ /**
+ * Play audio for trigger
+ */
+ playAudio(trigger) {
+ // Check if audio exists
+ if (!this.scene.cache.audio.exists(trigger.audioKey)) {
+ console.warn(`โ ๏ธ Audio not found: ${trigger.audioKey}`);
+ return;
+ }
+
+ // Play sound
+ const sound = this.scene.sound.add(trigger.audioKey, {
+ volume: trigger.volume
+ });
+
+ sound.play();
+
+ // Visual feedback (optional)
+ if (trigger.visualDebug) {
+ const worldX = trigger.x * 48 + 24;
+ const worldY = trigger.y * 48 + 24;
+
+ const flash = this.scene.add.circle(worldX, worldY, 30, 0xffff00, 0.8);
+ flash.setDepth(1002);
+
+ this.scene.tweens.add({
+ targets: flash,
+ alpha: 0,
+ scale: 2,
+ duration: 500,
+ ease: 'Quad.Out',
+ onComplete: () => flash.destroy()
+ });
+ }
+
+ // Callback
+ if (trigger.callback) {
+ trigger.callback();
+ }
+
+ console.log(`โ
Audio played: ${trigger.audioKey}`);
+ }
+
+ /**
+ * Update - called every frame
+ */
+ update(playerX, playerY) {
+ if (this.triggers.size === 0) return;
+
+ this.checkTrigger(playerX, playerY);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AutomationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/AutomationSystem.js
new file mode 100644
index 000000000..70078dc23
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AutomationSystem.js
@@ -0,0 +1,558 @@
+/**
+ * AUTOMATION SYSTEM
+ * Manages sprinkler system, minting building, auto-harvest, and complete farm automation.
+ *
+ * Features:
+ * - Sprinkler System: 4 tiers (Basic โ Automatic entire farm), Water Tower + Pipes
+ * - Minting Building: Gold โ Zlatniki conversion, passive income
+ * - Auto-Harvest: Zombie workers, automated collection, processing
+ * - Complete Automation: Fully hands-off farm operation
+ */
+class AutomationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Sprinkler tiers
+ this.sprinklerTiers = {
+ basic: {
+ name: 'Basic Sprinkler',
+ range: 1, // Waters 3x3 (1 tile radius)
+ cost: { iron: 5, copper: 2 },
+ waterUsage: 10
+ },
+ quality: {
+ name: 'Quality Sprinkler',
+ range: 2, // Waters 5x5 (2 tile radius)
+ cost: { iron: 10, copper: 5, gold: 1 },
+ waterUsage: 20
+ },
+ iridium: {
+ name: 'Iridium Sprinkler',
+ range: 3, // Waters 7x7 (3 tile radius)
+ cost: { iridium: 5, gold: 2, copper: 10 },
+ waterUsage: 30
+ },
+ automatic: {
+ name: 'Automatic Sprinkler System',
+ range: Infinity, // Waters entire farm!
+ cost: { iridium: 50, gold: 100, copper: 200, atlantean_crystal: 10 },
+ waterUsage: 0, // Self-sustaining
+ legendary: true
+ }
+ };
+
+ // Water towers
+ this.waterTowers = new Map(); // towerId -> tower data
+
+ // Sprinklers
+ this.sprinklers = new Map(); // sprinklerId -> sprinkler data
+
+ // Pipe network
+ this.pipes = [];
+
+ // Minting buildings
+ this.mintingBuildings = [];
+
+ // Auto-harvest workers
+ this.autoHarvestWorkers = [];
+
+ // Automation status
+ this.fullyAutomated = false;
+
+ console.log('๐ฆ๐ฐ Automation System initialized!');
+ }
+
+ /**
+ * Build water tower
+ */
+ buildWaterTower(position) {
+ const towerId = `tower_${Date.now()}`;
+
+ const tower = {
+ id: towerId,
+ position: position,
+ capacity: 1000,
+ currentWater: 1000,
+ refillRate: 10, // Per hour
+ connectedSprinklers: []
+ };
+
+ this.waterTowers.set(towerId, tower);
+
+ console.log('๐ง Water Tower built! Capacity: 1000 units.');
+
+ this.scene.events.emit('notification', {
+ title: 'Water Tower Built!',
+ message: 'Connect sprinklers with pipes!',
+ icon: '๐ง'
+ });
+
+ return towerId;
+ }
+
+ /**
+ * Place sprinkler
+ */
+ placeSprinkler(tier, position, connectedTowerId = null) {
+ const sprinklerData = this.sprinklerTiers[tier];
+ if (!sprinklerData) {
+ console.log(`โ Unknown sprinkler tier: ${tier}`);
+ return null;
+ }
+
+ // Check resources
+ if (!this.checkResources(sprinklerData.cost)) {
+ return { success: false, message: 'Not enough resources!' };
+ }
+
+ // Consume resources
+ this.consumeResources(sprinklerData.cost);
+
+ const sprinklerId = `sprinkler_${tier}_${Date.now()}`;
+
+ const sprinkler = {
+ id: sprinklerId,
+ tier: tier,
+ name: sprinklerData.name,
+ position: position,
+ range: sprinklerData.range,
+ waterUsage: sprinklerData.waterUsage,
+ connectedTower: connectedTowerId,
+ active: connectedTowerId !== null || tier === 'automatic',
+ coverage: this.calculateCoverage(position, sprinklerData.range)
+ };
+
+ this.sprinklers.set(sprinklerId, sprinkler);
+
+ // Connect to tower
+ if (connectedTowerId) {
+ const tower = this.waterTowers.get(connectedTowerId);
+ if (tower) {
+ tower.connectedSprinklers.push(sprinklerId);
+ }
+ }
+
+ console.log(`๐ฆ Placed ${sprinklerData.name}! Watering ${sprinkler.coverage.length} tiles.`);
+
+ return { success: true, sprinklerId: sprinklerId };
+ }
+
+ /**
+ * Calculate coverage area
+ */
+ calculateCoverage(position, range) {
+ if (range === Infinity) {
+ return ['entire_farm']; // Special case for automatic
+ }
+
+ const coverage = [];
+
+ for (let dx = -range; dx <= range; dx++) {
+ for (let dy = -range; dy <= range; dy++) {
+ // Check if within circular range
+ if (Math.sqrt(dx * dx + dy * dy) <= range) {
+ coverage.push({
+ x: position.x + dx,
+ y: position.y + dy
+ });
+ }
+ }
+ }
+
+ return coverage;
+ }
+
+ /**
+ * Lay pipe between tower and sprinkler
+ */
+ layPipe(fromPosition, toPosition) {
+ const distance = Phaser.Math.Distance.Between(
+ fromPosition.x, fromPosition.y,
+ toPosition.x, toPosition.y
+ );
+
+ const pipeCost = Math.ceil(distance / 10); // 1 pipe per 10 tiles
+
+ const pipe = {
+ id: `pipe_${Date.now()}`,
+ from: fromPosition,
+ to: toPosition,
+ length: distance,
+ cost: { copper: pipeCost }
+ };
+
+ // Check and consume resources
+ if (!this.checkResources(pipe.cost)) {
+ return { success: false, message: 'Not enough copper!' };
+ }
+
+ this.consumeResources(pipe.cost);
+ this.pipes.push(pipe);
+
+ console.log(`๐ง Laid ${Math.ceil(distance)} tiles of pipe. Cost: ${pipeCost} copper.`);
+
+ return { success: true, pipeId: pipe.id };
+ }
+
+ /**
+ * Water crops (called daily)
+ */
+ waterCrops() {
+ let totalWatered = 0;
+
+ this.sprinklers.forEach((sprinkler, id) => {
+ if (!sprinkler.active) return;
+
+ // Check water tower
+ if (sprinkler.connectedTower && sprinkler.tier !== 'automatic') {
+ const tower = this.waterTowers.get(sprinkler.connectedTower);
+ if (!tower || tower.currentWater < sprinkler.waterUsage) {
+ console.log(`โ ๏ธ ${sprinkler.name} out of water!`);
+ return;
+ }
+
+ // Use water
+ tower.currentWater -= sprinkler.waterUsage;
+ }
+
+ // Water covered tiles
+ sprinkler.coverage.forEach(tile => {
+ // In real game, would actually water crop at this position
+ totalWatered++;
+ });
+ });
+
+ if (totalWatered > 0) {
+ console.log(`๐ฆ Sprinklers watered ${totalWatered} tiles!`);
+ }
+
+ // Refill water towers
+ this.refillWaterTowers();
+ }
+
+ /**
+ * Refill water towers
+ */
+ refillWaterTowers() {
+ this.waterTowers.forEach((tower, id) => {
+ tower.currentWater = Math.min(tower.capacity, tower.currentWater + tower.refillRate);
+ });
+ }
+
+ /**
+ * Build minting building
+ */
+ buildMintingBuilding(position) {
+ const mintId = `mint_${Date.now()}`;
+
+ const mint = {
+ id: mintId,
+ position: position,
+ goldInput: 0,
+ goldCapacity: 1000,
+ conversionRate: 10, // 10 gold = 1 Zล (Zลotnik/Zlatnik)
+ dailyOutput: 0,
+ active: true
+ };
+
+ this.mintingBuildings.push(mint);
+
+ console.log('๐ฐ Minting Building constructed!');
+
+ this.scene.events.emit('notification', {
+ title: 'Minting Building!',
+ message: 'Convert gold to Zlatniki!',
+ icon: '๐ฐ'
+ });
+
+ return mintId;
+ }
+
+ /**
+ * Deposit gold into mint
+ */
+ depositGoldToMint(mintId, amount) {
+ const mint = this.mintingBuildings.find(m => m.id === mintId);
+ if (!mint) {
+ return { success: false, message: 'Mint not found' };
+ }
+
+ if (mint.goldInput + amount > mint.goldCapacity) {
+ return { success: false, message: 'Mint capacity exceeded!' };
+ }
+
+ // Check if player has gold
+ if (this.scene.player && this.scene.player.gold < amount) {
+ return { success: false, message: 'Not enough gold!' };
+ }
+
+ // Deduct gold from player
+ if (this.scene.player) {
+ this.scene.player.gold -= amount;
+ }
+
+ mint.goldInput += amount;
+
+ console.log(`๐ฐ Deposited ${amount} gold to mint. Total: ${mint.goldInput}/${mint.goldCapacity}`);
+
+ return { success: true, total: mint.goldInput };
+ }
+
+ /**
+ * Process minting (called daily)
+ */
+ processMinting() {
+ this.mintingBuildings.forEach(mint => {
+ if (!mint.active || mint.goldInput < mint.conversionRate) return;
+
+ // Convert gold to Zlatniki
+ const conversions = Math.floor(mint.goldInput / mint.conversionRate);
+ const goldUsed = conversions * mint.conversionRate;
+
+ mint.goldInput -= goldUsed;
+ mint.dailyOutput = conversions;
+
+ // Add currency to player
+ if (this.scene.player) {
+ this.scene.player.currency = (this.scene.player.currency || 0) + conversions;
+ }
+
+ console.log(`๐ฐ Mint produced ${conversions} Zlatniki! (Used ${goldUsed} gold)`);
+
+ this.scene.events.emit('notification', {
+ title: 'Minting Complete!',
+ message: `+${conversions} Zlatniki minted!`,
+ icon: '๐ฐ'
+ });
+ });
+ }
+
+ /**
+ * Assign zombie to auto-harvest
+ */
+ assignAutoHarvest(zombieId) {
+ if (this.autoHarvestWorkers.includes(zombieId)) {
+ console.log('โน๏ธ Zombie already assigned to auto-harvest!');
+ return false;
+ }
+
+ // Check if zombie is Lv5+
+ if (this.scene.smartZombieSystem) {
+ const zombie = this.scene.smartZombieSystem.zombies.get(zombieId);
+ if (!zombie || zombie.level < 5) {
+ console.log('โ Zombie must be Lv5+ for auto-harvest!');
+ return false;
+ }
+ }
+
+ this.autoHarvestWorkers.push(zombieId);
+
+ console.log(`๐ง Zombie assigned to auto-harvest! Total workers: ${this.autoHarvestWorkers.length}`);
+
+ this.scene.events.emit('notification', {
+ title: 'Auto-Harvest Active!',
+ message: 'Zombie will harvest crops automatically!',
+ icon: '๐ง'
+ });
+
+ return true;
+ }
+
+ /**
+ * Auto-harvest crops (called daily)
+ */
+ autoHarvestCrops() {
+ if (this.autoHarvestWorkers.length === 0) return;
+
+ let totalHarvested = 0;
+
+ this.autoHarvestWorkers.forEach(zombieId => {
+ // Simulate harvesting (in real game, would check for mature crops)
+ const harvestedCrops = Math.floor(Math.random() * 20) + 10; // 10-30 crops
+ totalHarvested += harvestedCrops;
+ });
+
+ console.log(`๐พ Auto-harvest: ${totalHarvested} crops collected by ${this.autoHarvestWorkers.length} zombies!`);
+
+ if (totalHarvested > 0) {
+ this.scene.events.emit('notification', {
+ title: 'Auto-Harvest Complete!',
+ message: `${totalHarvested} crops automatically collected!`,
+ icon: '๐พ'
+ });
+ }
+ }
+
+ /**
+ * Enable full automation (requires all systems)
+ */
+ enableFullAutomation() {
+ // Check requirements
+ const hasAutomaticSprinkler = Array.from(this.sprinklers.values()).some(s => s.tier === 'automatic');
+ const hasMint = this.mintingBuildings.length > 0;
+ const hasAutoHarvest = this.autoHarvestWorkers.length >= 3;
+ const hasZombieWorkers = this.scene.smartZombieSystem?.independentWorkers.length >= 5;
+
+ if (!hasAutomaticSprinkler) {
+ return { success: false, message: 'Need Automatic Sprinkler System!' };
+ }
+
+ if (!hasMint) {
+ return { success: false, message: 'Need Minting Building!' };
+ }
+
+ if (!hasAutoHarvest) {
+ return { success: false, message: 'Need at least 3 auto-harvest zombies!' };
+ }
+
+ if (!hasZombieWorkers) {
+ return { success: false, message: 'Need at least 5 Lv8+ independent zombies!' };
+ }
+
+ this.fullyAutomated = true;
+
+ console.log('๐ FULL AUTOMATION ACHIEVED!');
+ console.log('Your farm now runs completely hands-off!');
+
+ this.scene.events.emit('notification', {
+ title: 'FULL AUTOMATION!',
+ message: 'Your farm is now fully autonomous!',
+ icon: '๐'
+ });
+
+ this.scene.events.emit('achievement-unlocked', {
+ id: 'full_automation',
+ title: 'The Ultimate Farmer',
+ description: 'Achieve complete farm automation',
+ reward: 'Legendary Farmer Title'
+ });
+
+ return { success: true };
+ }
+
+ /**
+ * Run all automation tasks (called daily)
+ */
+ runDailyAutomation() {
+ console.log('โ๏ธ Running daily automation...');
+
+ this.waterCrops();
+ this.autoHarvestCrops();
+ this.processMinting();
+
+ if (this.fullyAutomated) {
+ // Additional benefits for full automation
+ const passiveGold = 500;
+ if (this.scene.player) {
+ this.scene.player.gold = (this.scene.player.gold || 0) + passiveGold;
+ }
+
+ console.log(`๐ฐ Full automation bonus: +${passiveGold} gold!`);
+ }
+ }
+
+ /**
+ * Check resources
+ */
+ checkResources(cost) {
+ // Placeholder - in real game would check inventory
+ return true;
+ }
+
+ /**
+ * Consume resources
+ */
+ consumeResources(cost) {
+ // Placeholder - in real game would remove from inventory
+ Object.keys(cost).forEach(resource => {
+ console.log(`Consumed ${cost[resource]} ${resource}`);
+ });
+ }
+
+ /**
+ * Get automation stats
+ */
+ getAutomationStats() {
+ return {
+ waterTowers: this.waterTowers.size,
+ sprinklers: this.sprinklers.size,
+ automaticSprinkler: Array.from(this.sprinklers.values()).some(s => s.tier === 'automatic'),
+ pipes: this.pipes.length,
+ mintingBuildings: this.mintingBuildings.length,
+ autoHarvestWorkers: this.autoHarvestWorkers.length,
+ fullyAutomated: this.fullyAutomated,
+ dailyGoldProduction: this.mintingBuildings.reduce((sum, m) => sum + m.dailyOutput, 0),
+ wateredTiles: Array.from(this.sprinklers.values()).reduce((sum, s) => {
+ return sum + (s.coverage.length || 0);
+ }, 0)
+ };
+ }
+
+ /**
+ * Upgrade sprinkler
+ */
+ upgradeSprinkler(sprinklerId, newTier) {
+ const sprinkler = this.sprinklers.get(sprinklerId);
+ if (!sprinkler) {
+ return { success: false, message: 'Sprinkler not found!' };
+ }
+
+ const newData = this.sprinklerTiers[newTier];
+ if (!newData) {
+ return { success: false, message: 'Invalid tier!' };
+ }
+
+ // Check if upgrade is valid (can't downgrade)
+ const tierOrder = ['basic', 'quality', 'iridium', 'automatic'];
+ const currentIndex = tierOrder.indexOf(sprinkler.tier);
+ const newIndex = tierOrder.indexOf(newTier);
+
+ if (newIndex <= currentIndex) {
+ return { success: false, message: 'Must upgrade to higher tier!' };
+ }
+
+ // Check and consume resources
+ if (!this.checkResources(newData.cost)) {
+ return { success: false, message: 'Not enough resources!' };
+ }
+
+ this.consumeResources(newData.cost);
+
+ // Upgrade
+ sprinkler.tier = newTier;
+ sprinkler.name = newData.name;
+ sprinkler.range = newData.range;
+ sprinkler.waterUsage = newData.waterUsage;
+ sprinkler.coverage = this.calculateCoverage(sprinkler.position, newData.range);
+
+ console.log(`โฌ๏ธ Upgraded to ${newData.name}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Sprinkler Upgraded!',
+ message: `Now ${newData.name}!`,
+ icon: 'โฌ๏ธ'
+ });
+
+ return { success: true };
+ }
+
+ /**
+ * Get sprinkler info
+ */
+ getSprinklerInfo(sprinklerId) {
+ const sprinkler = this.sprinklers.get(sprinklerId);
+ if (!sprinkler) return null;
+
+ return {
+ name: sprinkler.name,
+ tier: sprinkler.tier,
+ range: sprinkler.range === Infinity ? 'Entire Farm' : `${sprinkler.range} tiles`,
+ coverage: sprinkler.coverage.length === 1 && sprinkler.coverage[0] === 'entire_farm'
+ ? 'Entire Farm'
+ : `${sprinkler.coverage.length} tiles`,
+ waterUsage: sprinkler.tier === 'automatic' ? 'Self-Sustaining' : `${sprinkler.waterUsage} units/day`,
+ active: sprinkler.active,
+ connected: sprinkler.connectedTower !== null || sprinkler.tier === 'automatic'
+ };
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/AutomationTierSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/AutomationTierSystem.js
new file mode 100644
index 000000000..599d24e5c
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/AutomationTierSystem.js
@@ -0,0 +1,454 @@
+/**
+ * FARM AUTOMATION TIERS SYSTEM
+ * Progressive automation from manual labor to AI-driven farm
+ */
+class AutomationTierSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Current tier
+ this.currentTier = 1;
+
+ // Tier definitions
+ this.tiers = {
+ 1: {
+ name: 'Manual Labor',
+ description: 'Player does everything',
+ efficiency: 1.0,
+ automation: 0,
+ unlockRequirement: null,
+ features: ['manual_farming', 'manual_crafting', 'manual_building']
+ },
+ 2: {
+ name: 'Zombie Workers',
+ description: 'Basic automation with zombie workers',
+ efficiency: 0.5,
+ automation: 0.3,
+ unlockRequirement: { type: 'tame_zombies', count: 5 },
+ features: ['zombie_workers', 'simple_tasks', 'task_queue']
+ },
+ 3: {
+ name: 'Creature Helpers',
+ description: 'Specialized automation with creatures',
+ efficiency: 0.75,
+ automation: 0.6,
+ unlockRequirement: { type: 'befriend_creatures', count: 10 },
+ features: ['creature_workers', 'complex_tasks', 'crafting_automation', 'sorting']
+ },
+ 4: {
+ name: 'Mechanical Automation',
+ description: 'Full automation with machines',
+ efficiency: 1.0,
+ automation: 0.9,
+ unlockRequirement: { type: 'build_all_automation', buildings: ['auto_planter', 'auto_harvester', 'irrigation', 'conveyor', 'silo', 'sorter'] },
+ features: ['24_7_operation', 'no_management', 'full_automation']
+ },
+ 5: {
+ name: 'AI Farm',
+ description: 'Self-sustaining AI-driven farm',
+ efficiency: 1.5,
+ automation: 1.0,
+ unlockRequirement: { type: 'complete_quest', quest: 'singularity' },
+ features: ['self_optimizing', 'resource_balancing', 'profit_collection', 'ai_decisions']
+ }
+ };
+
+ // Tier progress
+ this.tierProgress = {
+ zombiesTamed: 0,
+ creaturesBefriended: 0,
+ automationBuildings: [],
+ questsCompleted: []
+ };
+
+ // AI Farm settings (Tier 5)
+ this.aiSettings = {
+ optimizationInterval: 60000, // 1 minute
+ resourceBalanceThreshold: 0.8,
+ profitCollectionInterval: 300000, // 5 minutes
+ lastOptimization: 0,
+ lastProfitCollection: 0
+ };
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Automation Tier System initialized');
+ }
+
+ init() {
+ console.log(`๐ค Current Tier: ${this.currentTier} - ${this.tiers[this.currentTier].name}`);
+ }
+
+ // ========== TIER MANAGEMENT ==========
+
+ checkTierUnlock(tier) {
+ const tierData = this.tiers[tier];
+ if (!tierData || !tierData.unlockRequirement) return false;
+
+ const req = tierData.unlockRequirement;
+
+ switch (req.type) {
+ case 'tame_zombies':
+ return this.tierProgress.zombiesTamed >= req.count;
+
+ case 'befriend_creatures':
+ return this.tierProgress.creaturesBefriended >= req.count;
+
+ case 'build_all_automation':
+ return req.buildings.every(b =>
+ this.tierProgress.automationBuildings.includes(b)
+ );
+
+ case 'complete_quest':
+ return this.tierProgress.questsCompleted.includes(req.quest);
+
+ default:
+ return false;
+ }
+ }
+
+ upgradeTier() {
+ const nextTier = this.currentTier + 1;
+
+ if (nextTier > 5) {
+ console.log('โ Already at max tier!');
+ return false;
+ }
+
+ if (!this.checkTierUnlock(nextTier)) {
+ console.log(`โ Requirements not met for Tier ${nextTier}`);
+ this.showTierRequirements(nextTier);
+ return false;
+ }
+
+ // Upgrade!
+ this.currentTier = nextTier;
+ this.applyTierBonuses();
+
+ // 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.scene.visualEnhancements.screenFlash(0x00ff00, 500);
+ }
+ }
+
+ // Achievement
+ if (this.scene.uiGraphics) {
+ if (this.currentTier === 5) {
+ this.scene.uiGraphics.unlockAchievement('ai_farm_master');
+ }
+ }
+
+ this.saveProgress();
+ console.log(`๐ Upgraded to Tier ${this.currentTier}: ${this.tiers[this.currentTier].name}!`);
+ return true;
+ }
+
+ showTierRequirements(tier) {
+ const tierData = this.tiers[tier];
+ const req = tierData.unlockRequirement;
+
+ console.log(`๐ Requirements for ${tierData.name}:`);
+
+ switch (req.type) {
+ case 'tame_zombies':
+ console.log(` - Tame ${req.count} zombies (${this.tierProgress.zombiesTamed}/${req.count})`);
+ break;
+
+ case 'befriend_creatures':
+ console.log(` - Befriend ${req.count} creatures (${this.tierProgress.creaturesBefriended}/${req.count})`);
+ break;
+
+ case 'build_all_automation':
+ console.log(` - Build all automation buildings:`);
+ req.buildings.forEach(b => {
+ const built = this.tierProgress.automationBuildings.includes(b);
+ console.log(` ${built ? 'โ
' : 'โ'} ${b}`);
+ });
+ break;
+
+ case 'complete_quest':
+ console.log(` - Complete quest: "${req.quest}"`);
+ break;
+ }
+ }
+
+ applyTierBonuses() {
+ const tier = this.tiers[this.currentTier];
+
+ // Apply efficiency to all workers
+ if (this.scene.farmAutomation) {
+ for (const worker of this.scene.farmAutomation.zombieWorkers) {
+ worker.efficiency = tier.efficiency;
+ }
+ for (const worker of this.scene.farmAutomation.creatureWorkers) {
+ worker.efficiency = tier.efficiency;
+ }
+ }
+
+ // Enable features
+ this.enableTierFeatures(tier.features);
+ }
+
+ enableTierFeatures(features) {
+ for (const feature of features) {
+ switch (feature) {
+ case '24_7_operation':
+ this.enable24_7Operation();
+ break;
+
+ case 'no_management':
+ this.enableNoManagement();
+ break;
+
+ case 'self_optimizing':
+ this.enableSelfOptimizing();
+ break;
+
+ case 'resource_balancing':
+ this.enableResourceBalancing();
+ break;
+
+ case 'ai_decisions':
+ this.enableAIDecisions();
+ break;
+ }
+ }
+ }
+
+ // ========== TIER 2: ZOMBIE WORKERS ==========
+
+ tameZombie() {
+ this.tierProgress.zombiesTamed++;
+ this.checkAutoUpgrade();
+ this.saveProgress();
+ console.log(`๐ง Zombie tamed! (${this.tierProgress.zombiesTamed}/5)`);
+ }
+
+ // ========== TIER 3: CREATURE HELPERS ==========
+
+ befriendCreature() {
+ this.tierProgress.creaturesBefriended++;
+ this.checkAutoUpgrade();
+ this.saveProgress();
+ console.log(`๐ฆ Creature befriended! (${this.tierProgress.creaturesBefriended}/10)`);
+ }
+
+ // ========== TIER 4: MECHANICAL AUTOMATION ==========
+
+ buildAutomationBuilding(buildingType) {
+ if (!this.tierProgress.automationBuildings.includes(buildingType)) {
+ this.tierProgress.automationBuildings.push(buildingType);
+ this.checkAutoUpgrade();
+ this.saveProgress();
+ console.log(`๐ญ Built ${buildingType}! (${this.tierProgress.automationBuildings.length}/6)`);
+ }
+ }
+
+ enable24_7Operation() {
+ // Buildings work 24/7 without stopping
+ if (this.scene.farmAutomation) {
+ for (const building of this.scene.farmAutomation.automationBuildings.values()) {
+ building.alwaysActive = true;
+ }
+ }
+ console.log('โก 24/7 operation enabled!');
+ }
+
+ enableNoManagement() {
+ // Workers don't need food/rest
+ if (this.scene.farmAutomation) {
+ for (const worker of [...this.scene.farmAutomation.zombieWorkers, ...this.scene.farmAutomation.creatureWorkers]) {
+ worker.needsManagement = false;
+ worker.hunger = 100;
+ worker.fatigue = 0;
+ }
+ }
+ console.log('๐ค No management needed!');
+ }
+
+ // ========== TIER 5: AI FARM ==========
+
+ completeQuest(questId) {
+ if (!this.tierProgress.questsCompleted.includes(questId)) {
+ this.tierProgress.questsCompleted.push(questId);
+ this.checkAutoUpgrade();
+ this.saveProgress();
+ console.log(`โ
Quest completed: ${questId}`);
+ }
+ }
+
+ enableSelfOptimizing() {
+ console.log('๐ง Self-optimizing AI enabled!');
+ }
+
+ enableResourceBalancing() {
+ console.log('โ๏ธ Automatic resource balancing enabled!');
+ }
+
+ enableAIDecisions() {
+ console.log('๐ค AI decision-making enabled!');
+ }
+
+ optimizeFarm() {
+ if (this.currentTier < 5) return;
+
+ // AI analyzes farm and makes decisions
+ const decisions = [];
+
+ // Check resource levels
+ if (this.scene.inventorySystem) {
+ const resources = this.scene.inventorySystem.items;
+
+ // Low on seeds? Plant more
+ if (resources.wheat_seeds < 10) {
+ decisions.push({ action: 'plant_wheat', priority: 'high' });
+ }
+
+ // Too much wood? Craft tools
+ if (resources.wood > 100) {
+ decisions.push({ action: 'craft_tools', priority: 'medium' });
+ }
+
+ // Low on food? Harvest crops
+ if (resources.wheat < 20) {
+ decisions.push({ action: 'harvest_crops', priority: 'high' });
+ }
+ }
+
+ // Execute decisions
+ for (const decision of decisions) {
+ this.executeAIDecision(decision);
+ }
+
+ this.aiSettings.lastOptimization = Date.now();
+ }
+
+ executeAIDecision(decision) {
+ console.log(`๐ค AI Decision: ${decision.action} (${decision.priority} priority)`);
+
+ // Add task to automation queue
+ if (this.scene.farmAutomation) {
+ this.scene.farmAutomation.addToTaskQueue({
+ type: decision.action,
+ priority: decision.priority === 'high' ? 3 : decision.priority === 'medium' ? 2 : 1
+ });
+ }
+ }
+
+ balanceResources() {
+ if (this.currentTier < 5) return;
+
+ // AI balances resources automatically
+ // Convert excess resources to needed ones
+
+ console.log('โ๏ธ Balancing resources...');
+
+ this.aiSettings.lastProfitCollection = Date.now();
+ }
+
+ collectProfits() {
+ if (this.currentTier < 5) return;
+
+ // Collect all profits from automation
+ let totalProfit = 0;
+
+ if (this.scene.farmAutomation) {
+ // Calculate profits from all automated systems
+ totalProfit += this.scene.farmAutomation.zombieWorkers.length * 10;
+ totalProfit += this.scene.farmAutomation.creatureWorkers.length * 20;
+ totalProfit += this.scene.farmAutomation.automationBuildings.size * 50;
+ }
+
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.gold += totalProfit;
+ }
+
+ console.log(`๐ฐ Collected ${totalProfit} gold from automation!`);
+ }
+
+ // ========== AUTO-UPGRADE ==========
+
+ checkAutoUpgrade() {
+ const nextTier = this.currentTier + 1;
+ if (nextTier <= 5 && this.checkTierUnlock(nextTier)) {
+ this.upgradeTier();
+ }
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ if (this.currentTier < 5) return;
+
+ const now = Date.now();
+
+ // Self-optimization
+ if (now - this.aiSettings.lastOptimization > this.aiSettings.optimizationInterval) {
+ this.optimizeFarm();
+ }
+
+ // Profit collection
+ if (now - this.aiSettings.lastProfitCollection > this.aiSettings.profitCollectionInterval) {
+ this.collectProfits();
+ this.balanceResources();
+ }
+ }
+
+ // ========== GETTERS ==========
+
+ getTierInfo() {
+ return {
+ current: this.currentTier,
+ name: this.tiers[this.currentTier].name,
+ description: this.tiers[this.currentTier].description,
+ efficiency: this.tiers[this.currentTier].efficiency,
+ automation: this.tiers[this.currentTier].automation,
+ features: this.tiers[this.currentTier].features
+ };
+ }
+
+ getProgress() {
+ return {
+ zombiesTamed: this.tierProgress.zombiesTamed,
+ creaturesBefriended: this.tierProgress.creaturesBefriended,
+ automationBuildings: this.tierProgress.automationBuildings.length,
+ questsCompleted: this.tierProgress.questsCompleted.length
+ };
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ currentTier: this.currentTier,
+ tierProgress: this.tierProgress
+ };
+
+ localStorage.setItem('novafarma_automation_tiers', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_automation_tiers');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.currentTier = data.currentTier || 1;
+ this.tierProgress = data.tierProgress || this.tierProgress;
+ console.log('โ
Automation tier progress loaded');
+ } catch (error) {
+ console.error('Failed to load tier progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐ค Automation Tier System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BakeryShopSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BakeryShopSystem.js
new file mode 100644
index 000000000..308a762ee
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BakeryShopSystem.js
@@ -0,0 +1,549 @@
+/**
+ * BAKERY SHOP SYSTEM - Food & Gift Purchasing
+ * Part of: New Town Buildings
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Food shop with fresh baked goods
+ * - Gift system (best gifts for NPCs)
+ * - Bulk purchase discounts
+ * - Time-of-day inventory changes
+ * - Special events (baking competition, birthday cakes)
+ */
+
+class BakeryShopSystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Bakery status
+ this.isUnlocked = false;
+ this.isOpen = false;
+ this.baker = null;
+
+ // Shop inventory
+ this.inventory = this.initializeInventory();
+
+ // Special events
+ this.currentEvent = null;
+ this.lastBakingCompetition = null;
+
+ // Birthday cake orders
+ this.birthdayCakeOrders = [];
+ }
+
+ /**
+ * Initialize bakery inventory
+ */
+ initializeInventory() {
+ return {
+ fresh_bread: {
+ id: 'fresh_bread',
+ name: 'Fresh Bread',
+ price: 50,
+ energyRestore: 20,
+ giftValue: 1, // +1 heart
+ stock: 20,
+ restockTime: 'morning', // Restocks at 6 AM
+ description: 'Warm, freshly baked bread.',
+ favoredBy: ['all'] // Everyone likes bread!
+ },
+ cake: {
+ id: 'cake',
+ name: 'Cake',
+ price: 200,
+ energyRestore: 50,
+ giftValue: 5, // +5 hearts
+ stock: 5,
+ restockTime: 'afternoon', // Restocks at 2 PM
+ description: 'A beautiful layered cake.',
+ favoredBy: ['ana', 'kai', 'gronk', 'children']
+ },
+ pie: {
+ id: 'pie',
+ name: 'Fruit Pie',
+ price: 150,
+ energyRestore: 40,
+ giftValue: 3, // +3 hearts
+ stock: 8,
+ restockTime: 'morning',
+ description: 'Sweet fruit pie with flaky crust.',
+ favoredBy: ['ana', 'elderly_npcs']
+ },
+ cookies: {
+ id: 'cookies',
+ name: 'Cookies',
+ price: 25,
+ energyRestore: 10,
+ giftValue: 1, // +1 heart
+ stock: 30,
+ restockTime: 'always', // Always available
+ description: 'Crunchy homemade cookies.',
+ favoredBy: ['children', 'kai']
+ },
+ croissants: {
+ id: 'croissants',
+ name: 'Croissants',
+ price: 75,
+ energyRestore: 25,
+ giftValue: 2, // +2 hearts
+ stock: 15,
+ restockTime: 'morning',
+ description: 'Buttery, flaky croissants. Fancy!',
+ favoredBy: ['ana', 'lawyer', 'wealthy_npcs']
+ },
+
+ // SPECIAL SEASONAL ITEMS
+ pumpkin_pie: {
+ id: 'pumpkin_pie',
+ name: 'Pumpkin Pie',
+ price: 180,
+ energyRestore: 45,
+ giftValue: 4,
+ stock: 6,
+ seasonal: 'autumn',
+ description: 'Spiced pumpkin pie for autumn.',
+ favoredBy: ['gronk', 'farmer_npcs']
+ },
+ gingerbread: {
+ id: 'gingerbread',
+ name: 'Gingerbread',
+ price: 100,
+ energyRestore: 30,
+ giftValue: 3,
+ stock: 12,
+ seasonal: 'winter',
+ description: 'Festive gingerbread cookies.',
+ favoredBy: ['children', 'kai', 'festive_npcs']
+ }
+ };
+ }
+
+ /**
+ * Unlock bakery (requires town restoration)
+ */
+ unlockBakery() {
+ // Check requirements
+ if (!this.player.hasCompletedQuest('first_bread')) {
+ return {
+ success: false,
+ message: 'Complete "First Bread" quest first!'
+ };
+ }
+
+ if (!this.player.hasMaterials({ wood: 100, wheat: 50 })) {
+ return {
+ success: false,
+ message: 'Need 100 Wood + 50 Wheat to build bakery!'
+ };
+ }
+
+ if (this.player.money < 8000) {
+ return {
+ success: false,
+ message: 'Need 8,000g to build bakery!'
+ };
+ }
+
+ // Build bakery
+ this.player.removeMaterials({ wood: 100, wheat: 50 });
+ this.player.money -= 8000;
+ this.isUnlocked = true;
+
+ // Spawn baker NPC
+ this.baker = this.game.npcs.spawn('baker', {
+ name: 'Maria',
+ position: { x: 1200, y: 800 }, // Bakery location
+ dialogue: this.getBakerDialogue()
+ });
+
+ // Open immediately
+ this.isOpen = true;
+
+ this.game.showMessage('๐ฅ Bakery is now open!');
+
+ return { success: true };
+ }
+
+ /**
+ * Get baker NPC dialogue
+ */
+ getBakerDialogue() {
+ return {
+ greeting: [
+ "Welcome to the bakery! Fresh bread daily!",
+ "Hello dear! What can I bake for you today?",
+ "Mmm, smell that fresh bread!"
+ ],
+ buying: [
+ "Excellent choice!",
+ "Fresh from the oven!",
+ "Enjoy, dear!"
+ ],
+ gift_suggestion: [
+ "Cakes make wonderful gifts!",
+ "Everyone loves fresh bread.",
+ "Looking for a special gift? Try the pie!"
+ ],
+ competition: [
+ "Weekly baking competition this Sunday!",
+ "Show me your best recipe!",
+ "The winner gets a special prize!"
+ ],
+ birthday: [
+ "Need a birthday cake? I can make one!",
+ "Tell me who it's for, I'll personalize it!",
+ "Birthday cakes are my specialty!"
+ ]
+ };
+ }
+
+ /**
+ * Purchase item from bakery
+ */
+ buyItem(itemId, quantity = 1) {
+ const item = this.inventory[itemId];
+
+ if (!item) {
+ return { success: false, message: 'Item not found!' };
+ }
+
+ // Check if bakery is open
+ if (!this.isOpen) {
+ return { success: false, message: 'Bakery is closed!' };
+ }
+
+ // Check seasonal availability
+ if (item.seasonal && !this.isCurrentSeason(item.seasonal)) {
+ return {
+ success: false,
+ message: `${item.name} is only available in ${item.seasonal}!`
+ };
+ }
+
+ // Check stock
+ if (item.stock < quantity) {
+ return {
+ success: false,
+ message: `Only ${item.stock} ${item.name} in stock!`
+ };
+ }
+
+ // Calculate price (with bulk discount)
+ const discount = this.getBulkDiscount(quantity);
+ const totalPrice = Math.floor(item.price * quantity * (1 - discount));
+
+ // Check money
+ if (this.player.money < totalPrice) {
+ return {
+ success: false,
+ message: `Not enough money! Need ${totalPrice}g`
+ };
+ }
+
+ // Purchase
+ this.player.money -= totalPrice;
+ item.stock -= quantity;
+
+ // Add to inventory
+ this.player.inventory.addItem(itemId, quantity);
+
+ // Show message
+ let message = `Purchased ${quantity}x ${item.name} for ${totalPrice}g!`;
+ if (discount > 0) {
+ message += ` (${Math.floor(discount * 100)}% bulk discount!)`;
+ }
+
+ this.game.showMessage(message);
+
+ return { success: true, totalPrice: totalPrice, discount: discount };
+ }
+
+ /**
+ * Calculate bulk discount
+ */
+ getBulkDiscount(quantity) {
+ // 10+ items = 20% off
+ if (quantity >= 10) {
+ return 0.20;
+ }
+ // 5-9 items = 10% off
+ if (quantity >= 5) {
+ return 0.10;
+ }
+ return 0;
+ }
+
+ /**
+ * Gift baked good to NPC
+ */
+ giftItem(itemId, npcId) {
+ const item = this.inventory[itemId];
+ const npc = this.game.npcs.get(npcId);
+
+ if (!item || !npc) {
+ return { success: false };
+ }
+
+ // Check if player has item
+ if (!this.player.inventory.hasItem(itemId)) {
+ return {
+ success: false,
+ message: 'You don\'t have this item!'
+ };
+ }
+
+ // Calculate gift value (bonus if NPC favors this item)
+ let giftValue = item.giftValue;
+ if (item.favoredBy.includes(npc.id) || item.favoredBy.includes('all')) {
+ giftValue *= 2; // Double hearts if favored!
+ }
+
+ // Remove from inventory
+ this.player.inventory.removeItem(itemId, 1);
+
+ // Add relationship points
+ npc.addRelationshipPoints(giftValue);
+
+ // NPC reaction
+ const reaction = this.getNPCGiftReaction(npc, item, giftValue);
+
+ this.game.showMessage(
+ `${npc.name}: "${reaction}" +${giftValue} โค๏ธ`
+ );
+
+ return { success: true, heartsAdded: giftValue, reaction: reaction };
+ }
+
+ /**
+ * Get NPC reaction to gift
+ */
+ getNPCGiftReaction(npc, item, heartsAdded) {
+ if (heartsAdded >= 5) {
+ return "Oh wow! This is amazing! Thank you so much!";
+ }
+ if (heartsAdded >= 3) {
+ return "This is wonderful! You're so thoughtful!";
+ }
+ if (heartsAdded >= 1) {
+ return "Thank you! I love fresh baked goods!";
+ }
+ return "Thanks.";
+ }
+
+ /**
+ * Order birthday cake
+ */
+ orderBirthdayCake(npcId) {
+ const npc = this.game.npcs.get(npcId);
+
+ if (!npc) {
+ return { success: false, message: 'NPC not found!' };
+ }
+
+ // Check cost
+ const cakePrice = 500; // Special birthday cake
+ if (this.player.money < cakePrice) {
+ return {
+ success: false,
+ message: `Birthday cakes cost ${cakePrice}g!`
+ };
+ }
+
+ // Order cake
+ this.player.money -= cakePrice;
+
+ const order = {
+ npc: npc,
+ orderDate: this.game.time.currentDate,
+ deliveryDate: npc.birthday,
+ delivered: false
+ };
+
+ this.birthdayCakeOrders.push(order);
+
+ this.game.showMessage(
+ `Ordered birthday cake for ${npc.name}! Will be delivered on their birthday.`
+ );
+
+ return { success: true, order: order };
+ }
+
+ /**
+ * Start weekly baking competition
+ */
+ startBakingCompetition() {
+ this.currentEvent = {
+ type: 'baking_competition',
+ startDate: this.game.time.currentDate,
+ endDate: this.game.time.currentDate + 7, // Lasts 1 week
+ participants: [],
+ playerScore: 0,
+ prize: {
+ money: 1000,
+ item: 'golden_whisk',
+ hearts: 5 // +5 hearts with baker
+ }
+ };
+
+ this.game.showMessage(
+ '๐ฅ Weekly Baking Competition started! Bring your best recipe!'
+ );
+
+ return this.currentEvent;
+ }
+
+ /**
+ * Submit recipe to competition
+ */
+ submitCompetitionRecipe(recipeId) {
+ if (!this.currentEvent || this.currentEvent.type !== 'baking_competition') {
+ return {
+ success: false,
+ message: 'No baking competition active!'
+ };
+ }
+
+ // Check if player has baked this recipe
+ const recipe = this.game.crafting.recipes[recipeId];
+ if (!recipe || !this.player.inventory.hasItem(recipeId)) {
+ return {
+ success: false,
+ message: 'You need to bake this recipe first!'
+ };
+ }
+
+ // Calculate score based on recipe complexity
+ const score = this.calculateRecipeScore(recipe);
+ this.currentEvent.playerScore = Math.max(this.currentEvent.playerScore, score);
+
+ this.game.showMessage(
+ `Recipe submitted! Score: ${score}/100`
+ );
+
+ return { success: true, score: score };
+ }
+
+ /**
+ * Calculate recipe score for competition
+ */
+ calculateRecipeScore(recipe) {
+ let score = 0;
+
+ // Base score from ingredients count
+ score += Object.keys(recipe.ingredients).length * 10;
+
+ // Bonus for craft time (complexity)
+ score += Math.min(recipe.craftTime, 50);
+
+ // Bonus for energy restore (if food)
+ if (recipe.energy_restore) {
+ score += recipe.energy_restore / 2;
+ }
+
+ // Random factor (luck)
+ score += Math.random() * 20;
+
+ return Math.min(Math.floor(score), 100);
+ }
+
+ /**
+ * Check seasonal availability
+ */
+ isCurrentSeason(season) {
+ const currentSeason = this.game.time.getSeason();
+ return currentSeason === season;
+ }
+
+ /**
+ * Restock inventory (called at specific times)
+ */
+ restockInventory() {
+ const hour = this.game.time.getHour();
+
+ Object.values(this.inventory).forEach(item => {
+ // Morning restock (6 AM)
+ if (hour === 6 && item.restockTime === 'morning') {
+ item.stock = this.getMaxStock(item.id);
+ }
+
+ // Afternoon restock (2 PM)
+ if (hour === 14 && item.restockTime === 'afternoon') {
+ item.stock = this.getMaxStock(item.id);
+ }
+
+ // Always available items restock every hour
+ if (item.restockTime === 'always') {
+ item.stock = this.getMaxStock(item.id);
+ }
+ });
+ }
+
+ /**
+ * Get max stock for item
+ */
+ getMaxStock(itemId) {
+ const defaults = {
+ fresh_bread: 20,
+ cake: 5,
+ pie: 8,
+ cookies: 30,
+ croissants: 15,
+ pumpkin_pie: 6,
+ gingerbread: 12
+ };
+
+ return defaults[itemId] || 10;
+ }
+
+ /**
+ * Update shop (called every hour)
+ */
+ update() {
+ const hour = this.game.time.getHour();
+
+ // Open/close shop
+ this.isOpen = (hour >= 6 && hour <= 20); // Open 6 AM - 8 PM
+
+ // Restock
+ this.restockInventory();
+
+ // Check birthday cake deliveries
+ this.checkBirthdayCakeDeliveries();
+ }
+
+ /**
+ * Check and deliver birthday cakes
+ */
+ checkBirthdayCakeDeliveries() {
+ const today = this.game.time.currentDate;
+
+ this.birthdayCakeOrders.forEach(order => {
+ if (!order.delivered && order.deliveryDate === today) {
+ // Deliver cake
+ this.player.inventory.addItem('birthday_cake_' + order.npc.id, 1);
+ order.delivered = true;
+
+ this.game.showMessage(
+ `๐ Birthday cake for ${order.npc.name} is ready!`
+ );
+ }
+ });
+ }
+
+ /**
+ * Get shop UI data
+ */
+ getShopUIData() {
+ return {
+ isUnlocked: this.isUnlocked,
+ isOpen: this.isOpen,
+ inventory: this.inventory,
+ currentEvent: this.currentEvent,
+ baker: this.baker,
+ openingHours: '6 AM - 8 PM'
+ };
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BarberShopSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BarberShopSystem.js
new file mode 100644
index 000000000..2d69968c2
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BarberShopSystem.js
@@ -0,0 +1,663 @@
+/**
+ * BARBER SHOP SYSTEM - Character Customization & Styling
+ * Part of: New Town Buildings
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Hairstyle changes (dreadlocks, mohawk, long hair, ponytail, bald)
+ * - Piercings & body modifications (ear gauges, nose ring, eyebrow, lip)
+ * - Color customization (hair dye, clothing)
+ * - Zombie makeover (cosmetic for workers)
+ * - NPC customization (change other characters)
+ * - Save/load favorite looks
+ */
+
+class BarberShopSystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Barber shop status
+ this.isUnlocked = false;
+ this.isOpen = false;
+ this.barber = null;
+
+ // Hairstyle catalog
+ this.hairstyles = this.initializeHairstyles();
+
+ // Piercing catalog
+ this.piercings = this.initializePiercings();
+
+ // Hair dye colors
+ this.dyeColors = this.initializeDyeColors();
+
+ // Player's current appearance
+ this.playerAppearance = {
+ hairstyle: 'default',
+ hairColor: 'brown',
+ piercings: [],
+ clothing: {
+ shirt: 'default',
+ pants: 'default',
+ colorShirt: 'blue',
+ colorPants: 'brown'
+ }
+ };
+
+ // Saved looks (5 slots)
+ this.savedLooks = [null, null, null, null, null];
+
+ // Visit counter (for discounts)
+ this.visitCount = 0;
+ this.lastVisitDate = null;
+ }
+
+ /**
+ * Initialize hairstyle catalog
+ */
+ initializeHairstyles() {
+ return {
+ default: {
+ id: 'default',
+ name: 'Default Hair',
+ price: 0,
+ unlocked: true,
+ sprite: 'hair_default',
+ description: 'Your natural hair.'
+ },
+ dreadlocks_pink_green: {
+ id: 'dreadlocks_pink_green',
+ name: 'Pink & Green Dreadlocks',
+ price: 500,
+ unlocked: true,
+ signature: 'kai', // Kai's signature style
+ sprite: 'hair_dreadlocks_pg',
+ description: 'Kai\'s iconic pink and green dreads!'
+ },
+ mohawk_blue: {
+ id: 'mohawk_blue',
+ name: 'Blue Mohawk',
+ price: 300,
+ unlocked: true,
+ sprite: 'hair_mohawk_blue',
+ description: 'Stand tall with this punk mohawk.'
+ },
+ mohawk_purple: {
+ id: 'mohawk_purple',
+ name: 'Purple Mohawk',
+ price: 300,
+ unlocked: true,
+ sprite: 'hair_mohawk_purple',
+ description: 'Purple power!'
+ },
+ mohawk_red: {
+ id: 'mohawk_red',
+ name: 'Red Mohawk',
+ price: 300,
+ unlocked: true,
+ sprite: 'hair_mohawk_red',
+ description: 'Fiery red mohawk.'
+ },
+ long_hair: {
+ id: 'long_hair',
+ name: 'Long Hair',
+ price: 400,
+ unlocked: true,
+ dyeable: true,
+ sprite: 'hair_long',
+ description: 'Flowing long hair, can be dyed any color.'
+ },
+ ponytail: {
+ id: 'ponytail',
+ name: 'Ponytail',
+ price: 250,
+ unlocked: true,
+ dyeable: true,
+ sprite: 'hair_ponytail',
+ description: 'Practical and stylish.'
+ },
+ bald: {
+ id: 'bald',
+ name: 'Bald',
+ price: 100,
+ unlocked: true,
+ reversible: true, // Can revert for free
+ sprite: 'hair_none',
+ description: 'Clean shaven head. Can reverse for FREE!'
+ }
+ };
+ }
+
+ /**
+ * Initialize piercing catalog
+ */
+ initializePiercings() {
+ return {
+ ear_gauges: {
+ id: 'ear_gauges',
+ name: 'Ear Gauges',
+ price: 200,
+ unlocked: true,
+ signature: 'kai', // Kai's trademark
+ sprite: 'piercing_gauges',
+ description: 'Large ear gauges like Kai\'s!'
+ },
+ nose_ring: {
+ id: 'nose_ring',
+ name: 'Nose Ring',
+ price: 150,
+ unlocked: true,
+ sprite: 'piercing_nose',
+ description: 'Simple nose ring.'
+ },
+ eyebrow_piercing: {
+ id: 'eyebrow_piercing',
+ name: 'Eyebrow Piercing',
+ price: 100,
+ unlocked: true,
+ sprite: 'piercing_eyebrow',
+ description: 'Edgy eyebrow piercing.'
+ },
+ lip_ring: {
+ id: 'lip_ring',
+ name: 'Lip Ring',
+ price: 150,
+ unlocked: true,
+ sprite: 'piercing_lip',
+ description: 'Lip ring for that punk look.'
+ },
+ multiple_ear: {
+ id: 'multiple_ear',
+ name: 'Multiple Ear Piercings',
+ price: 50,
+ stackable: true, // Can have multiple
+ sprite: 'piercing_ear_multiple',
+ description: 'Each additional piercing costs 50g.'
+ }
+ };
+ }
+
+ /**
+ * Initialize hair dye colors
+ */
+ initializeDyeColors() {
+ return {
+ brown: { name: 'Brown', price: 0, hex: '#654321' },
+ black: { name: 'Black', price: 100, hex: '#000000' },
+ blonde: { name: 'Blonde', price: 150, hex: '#FFD700' },
+ red: { name: 'Red', price: 200, hex: '#FF0000' },
+ blue: { name: 'Blue', price: 200, hex: '#0000FF' },
+ purple: { name: 'Purple', price: 200, hex: '#800080' },
+ pink: { name: 'Pink', price: 200, hex: '#FF69B4' },
+ green: { name: 'Green', price: 200, hex: '#00FF00' },
+ white: { name: 'White', price: 250, hex: '#FFFFFF' }
+ };
+ }
+
+ /**
+ * Unlock barber shop
+ */
+ unlockBarberShop() {
+ // Check requirements
+ const npcCount = this.game.npcs.getPopulationCount();
+ if (npcCount < 3) {
+ return {
+ success: false,
+ message: 'Need at least 3 NPCs in town first!'
+ };
+ }
+
+ if (!this.player.hasCompletedQuest('style_matters')) {
+ return {
+ success: false,
+ message: 'Complete "Style Matters" quest first!'
+ };
+ }
+
+ if (!this.player.hasMaterials({ wood: 75, iron: 25 })) {
+ return {
+ success: false,
+ message: 'Need 75 Wood + 25 Iron to build barber shop!'
+ };
+ }
+
+ if (this.player.money < 6000) {
+ return {
+ success: false,
+ message: 'Need 6,000g to build barber shop!'
+ };
+ }
+
+ // Build barber shop
+ this.player.removeMaterials({ wood: 75, iron: 25 });
+ this.player.money -= 6000;
+ this.isUnlocked = true;
+ this.isOpen = true;
+
+ // Spawn barber NPC
+ this.barber = this.game.npcs.spawn('barber', {
+ name: 'Razor',
+ position: { x: 1400, y: 900 },
+ appearance: {
+ hairstyle: 'mohawk_purple',
+ piercings: ['ear_gauges', 'nose_ring', 'lip_ring']
+ }
+ });
+
+ this.game.showMessage('๐ Barber Shop is now open!');
+
+ return { success: true };
+ }
+
+ /**
+ * Change hairstyle
+ */
+ changeHairstyle(hairstyleId) {
+ const hairstyle = this.hairstyles[hairstyleId];
+
+ if (!hairstyle) {
+ return { success: false, message: 'Hairstyle not found!' };
+ }
+
+ // Check if shop is open
+ if (!this.isOpen) {
+ return { success: false, message: 'Barber shop is closed!' };
+ }
+
+ // Check price (with discount)
+ const discount = this.getVisitDiscount();
+ const price = Math.floor(hairstyle.price * (1 - discount));
+
+ // Free reversal from bald
+ if (hairstyle.reversible && this.playerAppearance.hairstyle === 'bald') {
+ // Revert to previous hairstyle for free
+ this.playerAppearance.hairstyle = this.previousHairstyle || 'default';
+ this.game.showMessage('Hair restored for FREE!');
+ return { success: true, price: 0 };
+ }
+
+ // Check money
+ if (this.player.money < price) {
+ return {
+ success: false,
+ message: `Not enough money! Need ${price}g`
+ };
+ }
+
+ // Save previous hairstyle (for bald reversal)
+ this.previousHairstyle = this.playerAppearance.hairstyle;
+
+ // Change hairstyle
+ this.player.money -= price;
+ this.playerAppearance.hairstyle = hairstyleId;
+
+ // Update sprite
+ this.updatePlayerSprite();
+
+ // Track visit
+ this.incrementVisitCount();
+
+ let message = `Changed to ${hairstyle.name}! ${price}g`;
+ if (discount > 0) {
+ message += ` (${Math.floor(discount * 100)}% repeat customer discount!)`;
+ }
+
+ this.game.showMessage(message);
+
+ return { success: true, price: price, discount: discount };
+ }
+
+ /**
+ * Add piercing
+ */
+ addPiercing(piercingId) {
+ const piercing = this.piercings[piercingId];
+
+ if (!piercing) {
+ return { success: false, message: 'Piercing not found!' };
+ }
+
+ // Check if already have (unless stackable)
+ if (!piercing.stackable && this.playerAppearance.piercings.includes(piercingId)) {
+ return {
+ success: false,
+ message: 'You already have this piercing!'
+ };
+ }
+
+ // Check price
+ const price = piercing.price;
+ if (this.player.money < price) {
+ return {
+ success: false,
+ message: `Not enough money! Need ${price}g`
+ };
+ }
+
+ // Add piercing
+ this.player.money -= price;
+
+ if (piercing.stackable) {
+ // Count how many of this type
+ const count = this.playerAppearance.piercings.filter(p => p === piercingId).length;
+ this.playerAppearance.piercings.push(`${piercingId}_${count + 1}`);
+ } else {
+ this.playerAppearance.piercings.push(piercingId);
+ }
+
+ // Update sprite
+ this.updatePlayerSprite();
+
+ // Track visit
+ this.incrementVisitCount();
+
+ this.game.showMessage(`Added ${piercing.name}! ${price}g`);
+
+ return { success: true, price: price };
+ }
+
+ /**
+ * Remove piercing
+ */
+ removePiercing(piercingId) {
+ const index = this.playerAppearance.piercings.indexOf(piercingId);
+
+ if (index === -1) {
+ return { success: false, message: 'You don\'t have this piercing!' };
+ }
+
+ // Remove free of charge
+ this.playerAppearance.piercings.splice(index, 1);
+
+ // Update sprite
+ this.updatePlayerSprite();
+
+ this.game.showMessage('Piercing removed (free).');
+
+ return { success: true };
+ }
+
+ /**
+ * Dye hair color
+ */
+ dyeHair(colorId) {
+ const color = this.dyeColors[colorId];
+
+ if (!color) {
+ return { success: false, message: 'Color not found!' };
+ }
+
+ // Check if current hairstyle is dyeable
+ const currentHairstyle = this.hairstyles[this.playerAppearance.hairstyle];
+ if (!currentHairstyle.dyeable && currentHairstyle.id !== 'default') {
+ return {
+ success: false,
+ message: 'This hairstyle cannot be dyed!'
+ };
+ }
+
+ // Check price
+ if (this.player.money < color.price) {
+ return {
+ success: false,
+ message: `Not enough money! Need ${color.price}g`
+ };
+ }
+
+ // Dye hair
+ this.player.money -= color.price;
+ this.playerAppearance.hairColor = colorId;
+
+ // Update sprite
+ this.updatePlayerSprite();
+
+ // Track visit
+ this.incrementVisitCount();
+
+ this.game.showMessage(`Hair dyed ${color.name}! ${color.price}g`);
+
+ return { success: true, price: color.price };
+ }
+
+ /**
+ * Dye clothing
+ */
+ dyeClothing(clothingPart, colorId) {
+ const color = this.dyeColors[colorId];
+
+ if (!color) {
+ return { success: false, message: 'Color not found!' };
+ }
+
+ // Clothing dye is cheaper (half price)
+ const price = Math.floor(color.price / 2);
+
+ if (this.player.money < price) {
+ return {
+ success: false,
+ message: `Not enough money! Need ${price}g`
+ };
+ }
+
+ // Dye clothing
+ this.player.money -= price;
+
+ if (clothingPart === 'shirt') {
+ this.playerAppearance.clothing.colorShirt = colorId;
+ } else if (clothingPart === 'pants') {
+ this.playerAppearance.clothing.colorPants = colorId;
+ }
+
+ // Update sprite
+ this.updatePlayerSprite();
+
+ this.game.showMessage(`${clothingPart} dyed ${color.name}! ${price}g`);
+
+ return { success: true, price: price };
+ }
+
+ /**
+ * Zombie makeover (cosmetic for workers)
+ */
+ zombieMakeover(zombieId) {
+ const zombie = this.game.zombieWorkers.get(zombieId);
+
+ if (!zombie) {
+ return { success: false, message: 'Zombie not found!' };
+ }
+
+ const price = 1000;
+ if (this.player.money < price) {
+ return {
+ success: false,
+ message: 'Zombie makeover costs 1,000g!'
+ };
+ }
+
+ // Apply makeover
+ this.player.money -= price;
+
+ // Random fancy appearance for zombie
+ const fancyStyles = ['mohawk_blue', 'mohawk_purple', 'dreadlocks_pink_green'];
+ const style = Phaser.Utils.Array.GetRandom(fancyStyles);
+
+ zombie.appearance = {
+ hairstyle: style,
+ accessories: ['fancy_bow_tie', 'top_hat']
+ };
+
+ // Update zombie sprite
+ this.game.zombieWorkers.updateZombieSprite(zombieId);
+
+ // Loyalty bonus (zombies appreciate looking good!)
+ zombie.addLoyalty(20);
+
+ this.game.showMessage(`${zombie.name} looks fabulous! +20 Loyalty! 1,000g`);
+
+ return { success: true, price: price };
+ }
+
+ /**
+ * Customize NPC appearance
+ */
+ customizeNPC(npcId, changes) {
+ const npc = this.game.npcs.get(npcId);
+
+ if (!npc) {
+ return { success: false, message: 'NPC not found!' };
+ }
+
+ // Check relationship (need 5+ hearts)
+ if (this.player.getRelationshipLevel(npcId) < 5) {
+ return {
+ success: false,
+ message: `Need 5+ hearts with ${npc.name} first!`
+ };
+ }
+
+ const price = 500;
+ if (this.player.money < price) {
+ return {
+ success: false,
+ message: 'NPC customization costs 500g!'
+ };
+ }
+
+ // Apply changes
+ this.player.money -= price;
+
+ if (changes.hairstyle) {
+ npc.appearance.hairstyle = changes.hairstyle;
+ }
+ if (changes.hairColor) {
+ npc.appearance.hairColor = changes.hairColor;
+ }
+
+ // Update NPC sprite
+ this.game.npcs.updateNPCSprite(npcId);
+
+ // NPC reaction
+ npc.addRelationshipPoints(10);
+ this.game.showDialogue(npc.name, "Wow, I love it! Thank you!");
+
+ this.game.showMessage(`Styled ${npc.name}! +10 โค๏ธ 500g`);
+
+ return { success: true, price: price };
+ }
+
+ /**
+ * Save current look to slot
+ */
+ saveLook(slotIndex) {
+ if (slotIndex < 0 || slotIndex >= 5) {
+ return { success: false, message: 'Invalid slot!' };
+ }
+
+ this.savedLooks[slotIndex] = {
+ hairstyle: this.playerAppearance.hairstyle,
+ hairColor: this.playerAppearance.hairColor,
+ piercings: [...this.playerAppearance.piercings],
+ clothing: { ...this.playerAppearance.clothing },
+ name: `Look ${slotIndex + 1}`
+ };
+
+ this.game.showMessage(`Saved look to slot ${slotIndex + 1}!`);
+
+ return { success: true };
+ }
+
+ /**
+ * Load saved look from slot
+ */
+ loadLook(slotIndex) {
+ if (slotIndex < 0 || slotIndex >= 5) {
+ return { success: false, message: 'Invalid slot!' };
+ }
+
+ const savedLook = this.savedLooks[slotIndex];
+ if (!savedLook) {
+ return { success: false, message: 'No look saved in this slot!' };
+ }
+
+ // Apply saved look (free!)
+ this.playerAppearance = {
+ hairstyle: savedLook.hairstyle,
+ hairColor: savedLook.hairColor,
+ piercings: [...savedLook.piercings],
+ clothing: { ...savedLook.clothing }
+ };
+
+ // Update sprite
+ this.updatePlayerSprite();
+
+ this.game.showMessage(`Loaded ${savedLook.name}!`);
+
+ return { success: true };
+ }
+
+ /**
+ * Get visit discount (repeat customers)
+ */
+ getVisitDiscount() {
+ // 5+ visits = 10% off
+ if (this.visitCount >= 5) {
+ return 0.10;
+ }
+ return 0;
+ }
+
+ /**
+ * Increment visit count
+ */
+ incrementVisitCount() {
+ const today = this.game.time.currentDate;
+
+ // Only count once per day
+ if (this.lastVisitDate !== today) {
+ this.visitCount++;
+ this.lastVisitDate = today;
+ }
+ }
+
+ /**
+ * Update player sprite with new appearance
+ */
+ updatePlayerSprite() {
+ // Emit event for sprite manager to update
+ this.game.emit('playerAppearanceChanged', {
+ appearance: this.playerAppearance
+ });
+
+ // Rebuild player sprite
+ this.player.rebuildSprite(this.playerAppearance);
+ }
+
+ /**
+ * Get preview of appearance change
+ */
+ getPreview(changes) {
+ return {
+ ...this.playerAppearance,
+ ...changes
+ };
+ }
+
+ /**
+ * Get shop UI data
+ */
+ getShopUIData() {
+ return {
+ isUnlocked: this.isUnlocked,
+ isOpen: this.isOpen,
+ hairstyles: this.hairstyles,
+ piercings: this.piercings,
+ dyeColors: this.dyeColors,
+ currentAppearance: this.playerAppearance,
+ savedLooks: this.savedLooks,
+ visitDiscount: this.getVisitDiscount(),
+ barber: this.barber
+ };
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BiomeEnemySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BiomeEnemySystem.js
new file mode 100644
index 000000000..c6d5dbabe
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BiomeEnemySystem.js
@@ -0,0 +1,274 @@
+/**
+ * ๐น BIOME ENEMY SYSTEM
+ * Spawns biome-specific enemies across the world
+ * - Different enemies per biome
+ * - Difficulty scaling
+ * - Loot drops
+ * - Combat integration
+ */
+
+class BiomeEnemySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // All enemies in the world
+ this.enemies = [];
+
+ // Enemy types per biome
+ this.enemyTypes = {
+ 'Grassland': [
+ { type: 'wolf', hp: 30, damage: 5, speed: 1.5, loot: ['meat', 'fur'], color: 0x8B4513 },
+ { type: 'boar', hp: 40, damage: 7, speed: 1.2, loot: ['meat', 'tusk'], color: 0x654321 },
+ { type: 'bandit', hp: 50, damage: 10, speed: 1.0, loot: ['gold', 'sword'], color: 0x696969 }
+ ],
+ 'Forest': [
+ { type: 'goblin', hp: 25, damage: 6, speed: 1.3, loot: ['gold', 'dagger'], color: 0x228B22 },
+ { type: 'spider', hp: 20, damage: 4, speed: 1.8, loot: ['web', 'poison'], color: 0x2F4F4F },
+ { type: 'ent', hp: 80, damage: 15, speed: 0.8, loot: ['wood', 'seed'], color: 0x8B4513 }
+ ],
+ 'Desert': [
+ { type: 'scorpion', hp: 35, damage: 8, speed: 1.4, loot: ['poison', 'chitin'], color: 0xD2691E },
+ { type: 'mummy', hp: 60, damage: 12, speed: 0.9, loot: ['bandage', 'ancient_coin'], color: 0xDEB887 },
+ { type: 'sand_worm', hp: 100, damage: 20, speed: 0.7, loot: ['scales', 'tooth'], color: 0xC0B090 }
+ ],
+ 'Mountain': [
+ { type: 'troll', hp: 90, damage: 18, speed: 0.8, loot: ['stone', 'club'], color: 0x708090 },
+ { type: 'golem', hp: 120, damage: 25, speed: 0.6, loot: ['ore', 'core'], color: 0x696969 },
+ { type: 'harpy', hp: 45, damage: 10, speed: 2.0, loot: ['feather', 'talon'], color: 0x9370DB }
+ ],
+ 'Swamp': [
+ { type: 'zombie', hp: 50, damage: 10, speed: 0.9, loot: ['bone', 'rotten_flesh'], color: 0x556B2F },
+ { type: 'will_o_wisp', hp: 30, damage: 8, speed: 2.5, loot: ['essence', 'spark'], color: 0x00FFFF },
+ { type: 'swamp_dragon', hp: 150, damage: 30, speed: 0.7, loot: ['scale', 'heart'], color: 0x2F4F2F }
+ ]
+ };
+
+ // Spawn density per biome
+ this.spawnDensity = {
+ 'Grassland': 0.02, // 2% per tile
+ 'Forest': 0.03, // 3%
+ 'Desert': 0.015, // 1.5%
+ 'Mountain': 0.025, // 2.5%
+ 'Swamp': 0.035 // 3.5%
+ };
+
+ console.log('๐น BiomeEnemySystem initialized');
+ }
+
+ // Generate enemy spawns across the world
+ generateSpawns(biomeSystem) {
+ if (!biomeSystem) return;
+
+ let enemiesSpawned = 0;
+ const sampleRate = 10; // Check every 10th tile
+
+ for (let x = 0; x < 500; x += sampleRate) {
+ for (let y = 0; y < 500; y += sampleRate) {
+ const biome = biomeSystem.getBiomeAt(x, y);
+ const density = this.spawnDensity[biome] || 0.02;
+
+ if (Math.random() < density) {
+ this.spawnEnemy(x, y, biome);
+ enemiesSpawned++;
+ }
+ }
+ }
+
+ console.log(`โ
Spawned ${enemiesSpawned} enemies across world`);
+ }
+
+ // Spawn single enemy
+ spawnEnemy(x, y, biome) {
+ const enemyList = this.enemyTypes[biome] || this.enemyTypes['Grassland'];
+ const enemyTemplate = enemyList[Math.floor(Math.random() * enemyList.length)];
+
+ const enemy = {
+ x,
+ y,
+ type: enemyTemplate.type,
+ biome,
+ hp: enemyTemplate.hp,
+ maxHp: enemyTemplate.hp,
+ damage: enemyTemplate.damage,
+ speed: enemyTemplate.speed,
+ loot: [...enemyTemplate.loot],
+ color: enemyTemplate.color,
+ alive: true,
+ sprite: null,
+ lastMoveTime: 0
+ };
+
+ this.enemies.push(enemy);
+ return enemy;
+ }
+
+ // Create enemy sprite when chunk loads
+ createEnemySprite(enemy, chunk) {
+ if (enemy.sprite || !enemy.alive) return;
+
+ const worldX = enemy.x * 48 + 24;
+ const worldY = enemy.y * 48 + 24;
+
+ // Simple circle sprite
+ const sprite = this.scene.add.circle(worldX, worldY, 15, enemy.color);
+ sprite.setDepth(10 + worldY);
+
+ // HP bar
+ const hpBar = this.scene.add.rectangle(worldX, worldY - 25, 30, 4, 0xFF0000);
+ hpBar.setOrigin(0, 0.5);
+ hpBar.setDepth(10 + worldY);
+
+ const hpFill = this.scene.add.rectangle(worldX, worldY - 25, 30, 4, 0x00FF00);
+ hpFill.setOrigin(0, 0.5);
+ hpFill.setDepth(10 + worldY);
+
+ enemy.sprite = sprite;
+ enemy.hpBar = hpBar;
+ enemy.hpFill = hpFill;
+
+ if (chunk) {
+ chunk.sprites.push(sprite);
+ chunk.sprites.push(hpBar);
+ chunk.sprites.push(hpFill);
+ }
+ }
+
+ // Update enemies (AI, movement)
+ update(time, delta, playerX, playerY) {
+ for (const enemy of this.enemies) {
+ if (!enemy.alive || !enemy.sprite) continue;
+
+ // Simple AI: Move towards player if nearby
+ const dist = Math.sqrt((enemy.x - playerX) ** 2 + (enemy.y - playerY) ** 2);
+
+ if (dist < 10 && time > enemy.lastMoveTime + 500) {
+ // Move towards player
+ const dx = playerX - enemy.x;
+ const dy = playerY - enemy.y;
+ const len = Math.sqrt(dx * dx + dy * dy);
+
+ if (len > 0) {
+ enemy.x += (dx / len) * enemy.speed * 0.1;
+ enemy.y += (dy / len) * enemy.speed * 0.1;
+
+ // Update sprite position
+ enemy.sprite.setPosition(enemy.x * 48 + 24, enemy.y * 48 + 24);
+ if (enemy.hpBar) {
+ enemy.hpBar.setPosition(enemy.x * 48 + 24, enemy.y * 48 - 1);
+ enemy.hpFill.setPosition(enemy.x * 48 + 24, enemy.y * 48 - 1);
+ }
+ }
+
+ enemy.lastMoveTime = time;
+ }
+
+ // Attack player if very close
+ if (dist < 1.5 && time > enemy.lastAttackTime + 2000) {
+ this.attackPlayer(enemy);
+ enemy.lastAttackTime = time;
+ }
+ }
+ }
+
+ // Enemy attacks player
+ attackPlayer(enemy) {
+ if (this.scene.player) {
+ console.log(`๐น ${enemy.type} attacks for ${enemy.damage} damage!`);
+ // TODO: Integrate with player health system
+
+ // Visual feedback
+ if (this.scene.cameras) {
+ this.scene.cameras.main.shake(200, 0.005);
+ }
+ }
+ }
+
+ // Player attacks enemy
+ damageEnemy(enemy, damage) {
+ if (!enemy.alive) return;
+
+ enemy.hp -= damage;
+
+ // Update HP bar
+ if (enemy.hpFill) {
+ const hpPercent = Math.max(0, enemy.hp / enemy.maxHp);
+ enemy.hpFill.setScale(hpPercent, 1);
+ }
+
+ console.log(`โ๏ธ ${enemy.type} takes ${damage} damage! (${enemy.hp}/${enemy.maxHp} HP)`);
+
+ // Death
+ if (enemy.hp <= 0) {
+ this.killEnemy(enemy);
+ }
+ }
+
+ // Kill enemy and drop loot
+ killEnemy(enemy) {
+ enemy.alive = false;
+
+ console.log(`๐ ${enemy.type} died!`);
+
+ // Drop loot
+ if (this.scene.inventorySystem && enemy.loot.length > 0) {
+ const lootItem = enemy.loot[Math.floor(Math.random() * enemy.loot.length)];
+ const amount = Math.floor(Math.random() * 3) + 1;
+
+ this.scene.inventorySystem.addItem(lootItem, amount);
+ console.log(` ๐ฐ Dropped: ${amount}x ${lootItem}`);
+ }
+
+ // Destroy sprite
+ if (enemy.sprite) {
+ enemy.sprite.destroy();
+ if (enemy.hpBar) enemy.hpBar.destroy();
+ if (enemy.hpFill) enemy.hpFill.destroy();
+ enemy.sprite = null;
+ }
+ }
+
+ // Find nearest enemy to point
+ findNearestEnemy(x, y, maxDistance = 2) {
+ let nearest = null;
+ let minDist = maxDistance;
+
+ for (const enemy of this.enemies) {
+ if (!enemy.alive) continue;
+
+ const dist = Math.sqrt((enemy.x - x) ** 2 + (enemy.y - y) ** 2);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = enemy;
+ }
+ }
+
+ return nearest;
+ }
+
+ // Get statistics
+ getStats() {
+ const alive = this.enemies.filter(e => e.alive).length;
+ const byBiome = {};
+
+ for (const enemy of this.enemies) {
+ if (!enemy.alive) continue;
+ byBiome[enemy.biome] = (byBiome[enemy.biome] || 0) + 1;
+ }
+
+ return {
+ totalEnemies: this.enemies.length,
+ alive,
+ dead: this.enemies.length - alive,
+ byBiome
+ };
+ }
+
+ destroy() {
+ this.enemies.forEach(enemy => {
+ if (enemy.sprite) enemy.sprite.destroy();
+ if (enemy.hpBar) enemy.hpBar.destroy();
+ if (enemy.hpFill) enemy.hpFill.destroy();
+ });
+ this.enemies = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BiomeMusicSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BiomeMusicSystem.js
new file mode 100644
index 000000000..dacbe0695
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BiomeMusicSystem.js
@@ -0,0 +1,171 @@
+/**
+ * BiomeMusicSystem.js
+ * Cross-fade background music based on biome transitions
+ */
+
+class BiomeMusicSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Music tracks by biome
+ this.biomeTracks = {
+ 'grassland': 'music/farm_ambient',
+ 'forest': 'music/forest_ambient',
+ 'town': 'music/town_theme',
+ 'combat': 'music/combat_theme',
+ 'night': 'music/night_theme'
+ };
+
+ // Current playing track
+ this.currentTrack = null;
+ this.currentBiome = null;
+
+ // Cross-fade settings
+ this.fadeDuration = 2000; // 2 seconds
+ this.volume = 0.5; // Master volume
+
+ console.log('๐ต BiomeMusicSystem initialized');
+ }
+
+ /**
+ * Preload all music tracks
+ */
+ preload() {
+ Object.entries(this.biomeTracks).forEach(([biome, track]) => {
+ if (this.scene.cache.audio.exists(track)) {
+ console.log(`โ
Music ready: ${track}`);
+ } else {
+ console.warn(`โ ๏ธ Music missing: ${track}`);
+ }
+ });
+ }
+
+ /**
+ * Start music for a biome
+ */
+ playBiomeMusic(biome) {
+ // Skip if already playing this biome's music
+ if (biome === this.currentBiome && this.currentTrack) {
+ return;
+ }
+
+ const trackKey = this.biomeTracks[biome];
+
+ if (!trackKey) {
+ console.warn(`โ ๏ธ No music for biome: ${biome}`);
+ return;
+ }
+
+ // Check if track exists
+ if (!this.scene.cache.audio.exists(trackKey)) {
+ console.warn(`โ ๏ธ Music not loaded: ${trackKey}`);
+ return;
+ }
+
+ console.log(`๐ต Transitioning to: ${biome} (${trackKey})`);
+
+ // Cross-fade to new track
+ this.crossFadeTo(trackKey, biome);
+ }
+
+ /**
+ * Cross-fade from current track to new track
+ */
+ crossFadeTo(newTrackKey, biome) {
+ const oldTrack = this.currentTrack;
+
+ // Create new track
+ const newTrack = this.scene.sound.add(newTrackKey, {
+ loop: true,
+ volume: 0 // Start silent
+ });
+
+ newTrack.play();
+
+ // Fade in new track
+ this.scene.tweens.add({
+ targets: newTrack,
+ volume: this.volume,
+ duration: this.fadeDuration,
+ ease: 'Linear'
+ });
+
+ // Fade out old track if it exists
+ if (oldTrack) {
+ this.scene.tweens.add({
+ targets: oldTrack,
+ volume: 0,
+ duration: this.fadeDuration,
+ ease: 'Linear',
+ onComplete: () => {
+ oldTrack.stop();
+ oldTrack.destroy();
+ }
+ });
+ }
+
+ // Update current track
+ this.currentTrack = newTrack;
+ this.currentBiome = biome;
+ }
+
+ /**
+ * Stop all music
+ */
+ stop() {
+ if (this.currentTrack) {
+ this.scene.tweens.add({
+ targets: this.currentTrack,
+ volume: 0,
+ duration: 1000,
+ ease: 'Linear',
+ onComplete: () => {
+ this.currentTrack.stop();
+ this.currentTrack.destroy();
+ this.currentTrack = null;
+ this.currentBiome = null;
+ }
+ });
+ }
+ }
+
+ /**
+ * Set master volume
+ */
+ setVolume(volume) {
+ this.volume = Phaser.Math.Clamp(volume, 0, 1);
+
+ if (this.currentTrack) {
+ this.currentTrack.setVolume(this.volume);
+ }
+ }
+
+ /**
+ * Update called every frame
+ * Checks player's current biome and switches music
+ */
+ update(playerX, playerY) {
+ // Get current biome from biomeSystem
+ if (!this.scene.biomeSystem) return;
+
+ const gridX = Math.floor(playerX / 48);
+ const gridY = Math.floor(playerY / 48);
+
+ const biome = this.scene.biomeSystem.getBiomeAt(gridX, gridY);
+
+ if (biome && biome !== this.currentBiome) {
+ this.playBiomeMusic(biome);
+ }
+
+ // Handle night music override
+ if (this.scene.timeSystem) {
+ const hour = this.scene.timeSystem.currentHour || 12;
+
+ if (hour >= 20 || hour < 6) { // Night time
+ if (this.currentBiome !== 'night') {
+ this.playBiomeMusic('night');
+ }
+ }
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BiomeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BiomeSystem.js
new file mode 100644
index 000000000..6f01d125a
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BiomeSystem.js
@@ -0,0 +1,498 @@
+// BiomeSystem - Manages world biomes and generation
+class BiomeSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // World size
+ this.worldWidth = 500;
+ this.worldHeight = 500;
+
+ // Biome map (500x500 grid of biome IDs)
+ this.biomeMap = [];
+
+ // Biome definitions
+ this.biomes = {
+ grassland: {
+ id: 'grassland',
+ name: 'Grassland',
+ color: 0x4a9d5f,
+ tileColor: '#4a9d5f',
+ features: {
+ trees: 0.05,
+ rocks: 0.02,
+ flowers: 0.15
+ },
+ weather: 'normal',
+ temperature: 20
+ },
+ forest: {
+ id: 'forest',
+ name: 'Forest',
+ color: 0x2d5016,
+ tileColor: '#2d5016',
+ features: {
+ trees: 0.60,
+ rocks: 0.05,
+ bushes: 0.20,
+ mushrooms: 0.10
+ },
+ weather: 'rainy',
+ temperature: 15
+ },
+ desert: {
+ id: 'desert',
+ name: 'Desert',
+ color: 0xd4c4a1,
+ tileColor: '#d4c4a1',
+ features: {
+ cacti: 0.08,
+ rocks: 0.15,
+ deadTrees: 0.03
+ },
+ weather: 'hot',
+ temperature: 35
+ },
+ mountain: {
+ id: 'mountain',
+ name: 'Mountain',
+ color: 0x808080,
+ tileColor: '#808080',
+ features: {
+ rocks: 0.40,
+ largeRocks: 0.20,
+ snow: 0.10
+ },
+ weather: 'cold',
+ temperature: -5
+ },
+ swamp: {
+ id: 'swamp',
+ name: 'Swamp',
+ color: 0x3d5a3d,
+ tileColor: '#3d5a3d',
+ features: {
+ water: 0.30,
+ deadTrees: 0.25,
+ vines: 0.15,
+ fog: true
+ },
+ weather: 'foggy',
+ temperature: 18
+ },
+ // ===== NEW BIOMES - NORMAL (4) =====
+ snow: {
+ id: 'snow',
+ name: 'Frozen Tundra',
+ color: 0xE0F7FA,
+ tileColor: '#E0F7FA',
+ features: {
+ ice: 0.40,
+ frozenTrees: 0.15,
+ snowDrifts: 0.25,
+ icicles: 0.10
+ },
+ weather: 'blizzard',
+ temperature: -20
+ },
+ wasteland: {
+ id: 'wasteland',
+ name: 'Wasteland',
+ color: 0x4a4a4a,
+ tileColor: '#4a4a4a',
+ features: {
+ ruins: 0.30,
+ rubble: 0.40,
+ scrapMetal: 0.20,
+ brokenMachinery: 0.15
+ },
+ weather: 'dusty',
+ temperature: 25
+ },
+ tropical: {
+ id: 'tropical',
+ name: 'Tropical Beach',
+ color: 0xFFE082,
+ tileColor: '#FFE082',
+ features: {
+ palmTrees: 0.25,
+ coconuts: 0.15,
+ shells: 0.20,
+ water: 0.40
+ },
+ weather: 'sunny',
+ temperature: 30
+ },
+ radioactive: {
+ id: 'radioactive',
+ name: 'Radioactive Zone',
+ color: 0x39FF14,
+ tileColor: '#39FF14',
+ features: {
+ glowingRocks: 0.30,
+ mutantPlants: 0.25,
+ radioactiveBarrels: 0.15,
+ toxicPuddles: 0.20,
+ glow: true
+ },
+ weather: 'toxic',
+ temperature: 28
+ },
+ // ===== NEW BIOMES - ANOMALOUS (9) =====
+ dino_valley: {
+ id: 'dino_valley',
+ name: 'Dino Valley',
+ color: 0x6B8E23,
+ tileColor: '#6B8E23',
+ features: {
+ prehistoricTrees: 0.45,
+ largeFerns: 0.35,
+ dinoFootprints: 0.20,
+ fossils: 0.10,
+ eggs: 0.05
+ },
+ weather: 'humid',
+ temperature: 32,
+ anomalous: true,
+ unlockRequirement: 'portal_dino'
+ },
+ mythical: {
+ id: 'mythical',
+ name: 'Mythical Highlands',
+ color: 0xB39DDB,
+ tileColor: '#B39DDB',
+ features: {
+ magicalTrees: 0.30,
+ crystals: 0.25,
+ floatingRocks: 0.20,
+ rainbows: 0.10,
+ magicAura: true
+ },
+ weather: 'magical',
+ temperature: 18,
+ anomalous: true,
+ unlockRequirement: 'portal_mythical'
+ },
+ endless_forest: {
+ id: 'endless_forest',
+ name: 'Endless Forest',
+ color: 0x1B5E20,
+ tileColor: '#1B5E20',
+ features: {
+ ancientTrees: 0.80,
+ mysteryFog: 0.30,
+ strangeFootprints: 0.15,
+ hiddenPaths: 0.20
+ },
+ weather: 'misty',
+ temperature: 12,
+ anomalous: true,
+ unlockRequirement: 'portal_endless_forest'
+ },
+ loch_ness: {
+ id: 'loch_ness',
+ name: 'Loch Ness',
+ color: 0x546E7A,
+ tileColor: '#546E7A',
+ features: {
+ scottishPines: 0.35,
+ heather: 0.25,
+ lochWater: 0.40,
+ castleRuins: 0.10
+ },
+ weather: 'rainy',
+ temperature: 10,
+ anomalous: true,
+ unlockRequirement: 'portal_scotland'
+ },
+ catacombs: {
+ id: 'catacombs',
+ name: 'Catacombs',
+ color: 0x3E2723,
+ tileColor: '#3E2723',
+ features: {
+ bones: 0.50,
+ tombs: 0.30,
+ skulls: 0.25,
+ ancientUrns: 0.15,
+ darkness: true
+ },
+ weather: 'underground',
+ temperature: 15,
+ anomalous: true,
+ unlockRequirement: 'portal_catacombs'
+ },
+ egyptian_desert: {
+ id: 'egyptian_desert',
+ name: 'Egyptian Desert',
+ color: 0xFFD54F,
+ tileColor: '#FFD54F',
+ features: {
+ pyramids: 0.05,
+ sandDunes: 0.60,
+ hieroglyphs: 0.15,
+ scarabs: 0.20,
+ sphinx: 0.01
+ },
+ weather: 'scorching',
+ temperature: 45,
+ anomalous: true,
+ unlockRequirement: 'portal_egypt'
+ },
+ amazon: {
+ id: 'amazon',
+ name: 'Amazon Rainforest',
+ color: 0x1B5E20,
+ tileColor: '#1B5E20',
+ features: {
+ jungleTrees: 0.75,
+ vines: 0.40,
+ exoticFlowers: 0.30,
+ tribalTotems: 0.10,
+ piranhaRivers: 0.15
+ },
+ weather: 'monsoon',
+ temperature: 35,
+ anomalous: true,
+ unlockRequirement: 'portal_amazon'
+ },
+ atlantis: {
+ id: 'atlantis',
+ name: 'Atlantis',
+ color: 0x00BCD4,
+ tileColor: '#00BCD4',
+ features: {
+ coralReefs: 0.40,
+ underwaterRuins: 0.35,
+ pearls: 0.15,
+ seaweed: 0.30,
+ bubbles: true,
+ underwater: true
+ },
+ weather: 'underwater',
+ temperature: 20,
+ anomalous: true,
+ unlockRequirement: 'portal_atlantis'
+ },
+ chernobyl: {
+ id: 'chernobyl',
+ name: 'Chernobyl',
+ color: 0x757575,
+ tileColor: '#757575',
+ features: {
+ reactorRuins: 0.20,
+ abandonedBuildings: 0.40,
+ radioactiveBarrels: 0.25,
+ sovietRelics: 0.20,
+ hazmatSigns: 0.15,
+ radiation: true
+ },
+ weather: 'nuclear',
+ temperature: 22,
+ anomalous: true,
+ isFinalZone: true,
+ unlockRequirement: 'train_to_chernobyl'
+ }
+ };
+
+ console.log('๐ BiomeSystem initialized (500x500 world)');
+ }
+
+ // Generate biome map using Perlin-like noise
+ generateBiomeMap() {
+ console.log('๐ Generating biome map...');
+
+ this.biomeMap = [];
+
+ // Initialize empty map
+ for (let y = 0; y < this.worldHeight; y++) {
+ this.biomeMap[y] = [];
+ for (let x = 0; x < this.worldWidth; x++) {
+ this.biomeMap[y][x] = null;
+ }
+ }
+
+ // Center is always grassland (farm area)
+ const centerX = Math.floor(this.worldWidth / 2);
+ const centerY = Math.floor(this.worldHeight / 2);
+ const farmRadius = 50; // 100x100 farm area
+
+ // Define biome centers (for now, simple regions)
+ const biomeRegions = [
+ { biome: 'grassland', centerX: 250, centerY: 250, radius: 80 }, // Center (FARM)
+ { biome: 'forest', centerX: 150, centerY: 150, radius: 100 }, // Northwest
+ { biome: 'forest', centerX: 350, centerY: 150, radius: 80 }, // Northeast
+ { biome: 'desert', centerX: 400, centerY: 350, radius: 90 }, // Southeast
+ { biome: 'mountain', centerX: 100, centerY: 100, radius: 70 }, // Far northwest
+ { biome: 'swamp', centerX: 100, centerY: 400, radius: 80 } // Southwest
+ ];
+
+ // Fill biomes based on distance to region centers
+ for (let y = 0; y < this.worldHeight; y++) {
+ for (let x = 0; x < this.worldWidth; x++) {
+ // Find closest biome region
+ let closestBiome = 'grassland'; // Default
+ let minDistance = Infinity;
+
+ for (const region of biomeRegions) {
+ const dx = x - region.centerX;
+ const dy = y - region.centerY;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance < minDistance) {
+ minDistance = distance;
+ closestBiome = region.biome;
+ }
+ }
+
+ this.biomeMap[y][x] = closestBiome;
+ }
+ }
+
+ console.log('โ
Biome map generated!');
+ }
+
+ // Get biome at specific coordinates
+ getBiomeAt(x, y) {
+ if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) {
+ return 'grassland'; // Default outside bounds
+ }
+
+ return this.biomeMap[y][x] || 'grassland';
+ }
+
+ // Get biome data
+ getBiomeData(biomeId) {
+ return this.biomes[biomeId] || this.biomes.grassland;
+ }
+
+ // Check if feature should spawn at location
+ shouldSpawnFeature(x, y, featureType) {
+ const biomeId = this.getBiomeAt(x, y);
+ const biomeData = this.getBiomeData(biomeId);
+
+ if (!biomeData.features[featureType]) return false;
+
+ const chance = biomeData.features[featureType];
+ return Math.random() < chance;
+ }
+
+ // Get tile color for biome
+ getTileColor(x, y) {
+ const biomeId = this.getBiomeAt(x, y);
+ const biomeData = this.getBiomeData(biomeId);
+ return biomeData.tileColor;
+ }
+
+ // Apply biome-specific features during world generation
+ applyBiomeFeatures(x, y) {
+ const biomeId = this.getBiomeAt(x, y);
+ const biomeData = this.getBiomeData(biomeId);
+
+ const features = [];
+
+ // Trees
+ if (this.shouldSpawnFeature(x, y, 'trees')) {
+ features.push({ type: 'tree', variant: Math.floor(Math.random() * 3) });
+ }
+
+ // Rocks
+ if (this.shouldSpawnFeature(x, y, 'rocks')) {
+ features.push({ type: 'rock', size: Math.random() > 0.7 ? 'large' : 'small' });
+ }
+
+ // Biome-specific features
+ if (biomeId === 'forest') {
+ if (this.shouldSpawnFeature(x, y, 'bushes')) {
+ features.push({ type: 'bush' });
+ }
+ if (this.shouldSpawnFeature(x, y, 'mushrooms')) {
+ features.push({ type: 'mushroom' });
+ }
+ } else if (biomeId === 'desert') {
+ if (this.shouldSpawnFeature(x, y, 'cacti')) {
+ features.push({ type: 'cactus' });
+ }
+ if (this.shouldSpawnFeature(x, y, 'deadTrees')) {
+ features.push({ type: 'deadTree' });
+ }
+ } else if (biomeId === 'mountain') {
+ if (this.shouldSpawnFeature(x, y, 'largeRocks')) {
+ features.push({ type: 'boulder' });
+ }
+ } else if (biomeId === 'swamp') {
+ if (this.shouldSpawnFeature(x, y, 'deadTrees')) {
+ features.push({ type: 'deadTree' });
+ }
+ if (this.shouldSpawnFeature(x, y, 'vines')) {
+ features.push({ type: 'vine' });
+ }
+ }
+
+ return features;
+ }
+
+ // Get biome transitions (blend zones)
+ getBiomeBlend(x, y, radius = 3) {
+ // Check surrounding tiles for different biomes
+ const centerBiome = this.getBiomeAt(x, y);
+ const surroundingBiomes = new Set();
+
+ for (let dy = -radius; dy <= radius; dy++) {
+ for (let dx = -radius; dx <= radius; dx++) {
+ const biome = this.getBiomeAt(x + dx, y + dy);
+ if (biome !== centerBiome) {
+ surroundingBiomes.add(biome);
+ }
+ }
+ }
+
+ return {
+ isTransition: surroundingBiomes.size > 0,
+ mainBiome: centerBiome,
+ nearbyBiomes: Array.from(surroundingBiomes)
+ };
+ }
+
+ // Get biome statistics (for debugging/UI)
+ getBiomeStats() {
+ const stats = {};
+
+ for (const biomeId in this.biomes) {
+ stats[biomeId] = 0;
+ }
+
+ for (let y = 0; y < this.worldHeight; y++) {
+ for (let x = 0; x < this.worldWidth; x++) {
+ const biome = this.getBiomeAt(x, y);
+ stats[biome] = (stats[biome] || 0) + 1;
+ }
+ }
+
+ // Convert to percentages
+ const total = this.worldWidth * this.worldHeight;
+ for (const biomeId in stats) {
+ stats[biomeId] = {
+ tiles: stats[biomeId],
+ percentage: ((stats[biomeId] / total) * 100).toFixed(1)
+ };
+ }
+
+ return stats;
+ }
+
+ // Export biome map for debugging/visualization
+ exportBiomeMap() {
+ return {
+ width: this.worldWidth,
+ height: this.worldHeight,
+ biomes: this.biomes,
+ map: this.biomeMap
+ };
+ }
+
+ // Destroy
+ destroy() {
+ this.biomeMap = [];
+ console.log('๐ BiomeSystem destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BlueprintSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BlueprintSystem.js
new file mode 100644
index 000000000..3b3a5948e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BlueprintSystem.js
@@ -0,0 +1,467 @@
+/**
+ * BLUEPRINT SYSTEM (EXPANDED)
+ * Manages unlocking crafting recipes via Blueprint items.
+ *
+ * EXPANSION FEATURES (P24):
+ * - 9 Discovery Methods: Museum, Digging (5%), Beaches, Chests, Boss Drops, NPC Gifts, Quest Rewards, Fishing, Ancient Ruins
+ * - Building Requirements: Cannot build structures without blueprints
+ * - Weapon/Bow Shop Unlocks: Via Wasteland Military Base & Forest Hunter's Lodge quests
+ * - Blueprint UI/Tracker: Collection progress, gallery view
+ */
+class BlueprintSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.unlockedRecipes = new Set(); // Stores IDs of unlocked recipes
+
+ // Default unlocked recipes (Basic tools & structures)
+ this.unlockedRecipes.add('stick');
+ this.unlockedRecipes.add('plank');
+ this.unlockedRecipes.add('chest');
+ this.unlockedRecipes.add('fence');
+ this.unlockedRecipes.add('axe');
+ this.unlockedRecipes.add('pickaxe');
+ this.unlockedRecipes.add('hoe');
+ this.unlockedRecipes.add('sword');
+
+ // Blueprint Categories
+ this.categories = {
+ basic: ['fence', 'chest', 'campfire', 'torch'],
+ buildings: ['house', 'barn', 'greenhouse', 'workshop', 'silo', 'coop', 'stable'],
+ advanced: ['furnace', 'anvil', 'loom', 'brewery', 'laboratory'],
+ weapons: ['iron_sword', 'steel_sword', 'katana', 'flame_sword', 'plasma_rifle'],
+ bows: ['wooden_bow', 'composite_bow', 'crossbow', 'explosive_bow', 'laser_bow'],
+ vehicles: ['scooter', 'atv', 'tractor', 'helicopter'],
+ special: ['portal_repair', 'time_machine', 'cloning_vat']
+ };
+
+ // Blueprint Definitions (Item ID -> Recipe ID)
+ this.blueprints = {
+ // Basic
+ 'blueprint_campfire': 'campfire',
+ 'blueprint_torch': 'torch',
+
+ // Buildings (REQUIRED to build!)
+ 'blueprint_house': 'house',
+ 'blueprint_barn': 'barn',
+ 'blueprint_greenhouse': 'greenhouse',
+ 'blueprint_workshop': 'workshop',
+ 'blueprint_silo': 'silo',
+ 'blueprint_chicken_coop': 'coop',
+ 'blueprint_stable': 'stable',
+
+ // Advanced
+ 'blueprint_furnace': 'furnace',
+ 'blueprint_anvil': 'anvil',
+ 'blueprint_loom': 'loom',
+ 'blueprint_brewery': 'brewery',
+ 'blueprint_laboratory': 'laboratory',
+
+ // Weapons (Unlocked via Wasteland Military Base quest)
+ 'blueprint_iron_sword': 'iron_sword',
+ 'blueprint_steel_sword': 'steel_sword',
+ 'blueprint_katana': 'katana',
+ 'blueprint_flame_sword': 'flame_sword',
+ 'blueprint_plasma_rifle': 'plasma_rifle',
+
+ // Bows (Unlocked via Forest Hunter's Lodge quest)
+ 'blueprint_wooden_bow': 'wooden_bow',
+ 'blueprint_composite_bow': 'composite_bow',
+ 'blueprint_crossbow': 'crossbow',
+ 'blueprint_explosive_bow': 'explosive_bow',
+ 'blueprint_laser_bow': 'laser_bow',
+
+ // Vehicles
+ 'blueprint_scooter': 'scooter_engine',
+ 'blueprint_atv': 'atv',
+ 'blueprint_tractor': 'tractor',
+ 'blueprint_helicopter': 'helicopter',
+
+ // Special
+ 'blueprint_portal_repair': 'portal_repair',
+ 'blueprint_time_machine': 'time_machine',
+ 'blueprint_cloning_vat': 'cloning_vat'
+ };
+
+ // Discovery Methods (9 methods!)
+ this.discoveryMethods = {
+ museum: { chance: 1.0, blueprints: ['blueprint_anvil', 'blueprint_loom', 'blueprint_time_machine'] },
+ digging: { chance: 0.05, blueprints: ['blueprint_house', 'blueprint_barn', 'blueprint_furnace'] },
+ beach: { chance: 0.15, blueprints: ['blueprint_workshop', 'blueprint_laboratory'] },
+ chest: { chance: 0.30, blueprints: Object.keys(this.blueprints) },
+ boss: { chance: 1.0, blueprints: ['blueprint_plasma_rifle', 'blueprint_laser_bow', 'blueprint_cloning_vat'] },
+ npc_gift: { chance: 1.0, blueprints: ['blueprint_greenhouse', 'blueprint_brewery', 'blueprint_stable'] },
+ quest: { chance: 1.0, blueprints: [] }, // Set by specific quests
+ fishing: { chance: 0.08, blueprints: ['blueprint_campfire', 'blueprint_scooter'] },
+ ruins: { chance: 0.20, blueprints: ['blueprint_katana', 'blueprint_portal_repair', 'blueprint_helicopter'] }
+ };
+
+ // Quest-locked blueprints
+ this.questLocks = {
+ 'wasteland_military_base_complete': ['blueprint_iron_sword', 'blueprint_steel_sword', 'blueprint_katana', 'blueprint_flame_sword', 'blueprint_plasma_rifle'],
+ 'forest_hunters_lodge_complete': ['blueprint_wooden_bow', 'blueprint_composite_bow', 'blueprint_crossbow', 'blueprint_explosive_bow', 'blueprint_laser_bow']
+ };
+
+ // Collection tracking
+ this.totalBlueprints = Object.keys(this.blueprints).length;
+ this.discovered = 0;
+
+ // UI state
+ this.showingGallery = false;
+
+ console.log('๐ Blueprint System (EXPANDED) initialized!');
+ console.log(`๐ Total Blueprints: ${this.totalBlueprints}`);
+ }
+
+ /**
+ * Unlock a recipe
+ */
+ unlockRecipe(recipeId) {
+ if (this.unlockedRecipes.has(recipeId)) {
+ console.log(`โน๏ธ Recipe ${recipeId} already unlocked.`);
+ return false;
+ }
+
+ this.unlockedRecipes.add(recipeId);
+ this.discovered++;
+
+ console.log(`๐ UNLOCKED RECIPE: ${recipeId} (${this.discovered}/${this.totalBlueprints})`);
+
+ // Notification
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 100,
+ text: `NEW BLUEPRINT: ${recipeId.toUpperCase()}!`,
+ color: '#00FFFF'
+ });
+
+ this.scene.events.emit('notification', {
+ title: 'Blueprint Discovered!',
+ message: `${recipeId.toUpperCase()} unlocked! (${this.discovered}/${this.totalBlueprints})`,
+ icon: '๐'
+ });
+
+ if (this.scene.soundManager) this.scene.soundManager.playSuccess();
+ return true;
+ }
+
+ /**
+ * Check if unlocked
+ */
+ isUnlocked(recipeId) {
+ return this.unlockedRecipes.has(recipeId);
+ }
+
+ /**
+ * Check if can build (buildings require blueprints!)
+ */
+ canBuild(recipeId) {
+ const buildingCategories = [...this.categories.buildings, ...this.categories.advanced, ...this.categories.special];
+
+ // If it's a building, check blueprint
+ if (buildingCategories.includes(recipeId)) {
+ return this.isUnlocked(recipeId);
+ }
+
+ // Other items can be built without blueprint (but blueprint improves them)
+ return true;
+ }
+
+ /**
+ * Use a blueprint item from inventory
+ */
+ useBlueprint(itemId) {
+ const recipeId = this.blueprints[itemId];
+ if (!recipeId) return false;
+
+ const success = this.unlockRecipe(recipeId);
+ if (success) {
+ // Consume item
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.removeItem(itemId, 1);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Try to discover blueprint via specific method
+ * @param {string} method - 'museum', 'digging', 'beach', 'chest', 'boss', 'npc_gift', 'quest', 'fishing', 'ruins'
+ * @param {number} x - X position
+ * @param {number} y - Y position
+ * @param {string} questId - Optional quest ID for quest-specific blueprints
+ */
+ tryDiscover(method, x, y, questId = null) {
+ const discoveryData = this.discoveryMethods[method];
+ if (!discoveryData) {
+ console.log(`โ Unknown discovery method: ${method}`);
+ return false;
+ }
+
+ // Roll for discovery
+ if (Math.random() > discoveryData.chance) {
+ return false; // No discovery
+ }
+
+ // Select blueprint from method's pool
+ let availableBlueprints = discoveryData.blueprints;
+
+ // Filter out already unlocked
+ availableBlueprints = availableBlueprints.filter(bp => {
+ const recipeId = this.blueprints[bp];
+ return recipeId && !this.isUnlocked(recipeId);
+ });
+
+ if (availableBlueprints.length === 0) {
+ console.log('โน๏ธ All blueprints from this method already discovered!');
+ return false;
+ }
+
+ const blueprintItem = availableBlueprints[Math.floor(Math.random() * availableBlueprints.length)];
+
+ // Spawn blueprint item
+ if (this.scene.interactionSystem) {
+ this.scene.interactionSystem.spawnLoot(x, y, blueprintItem, 1);
+ console.log(`๐ Discovered Blueprint: ${blueprintItem} via ${method}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Blueprint Found!',
+ message: `Discovered ${blueprintItem}!`,
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Unlock quest-specific blueprints
+ */
+ unlockQuestBlueprints(questId) {
+ const blueprints = this.questLocks[questId];
+ if (!blueprints) {
+ console.log(`โน๏ธ No blueprints locked behind quest: ${questId}`);
+ return [];
+ }
+
+ const unlocked = [];
+
+ blueprints.forEach(blueprintItem => {
+ const recipeId = this.blueprints[blueprintItem];
+ if (recipeId) {
+ this.unlockRecipe(recipeId);
+ unlocked.push(recipeId);
+ }
+ });
+
+ if (unlocked.length > 0) {
+ console.log(`๐ Quest "${questId}" unlocked ${unlocked.length} blueprints!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Quest Blueprints Unlocked!',
+ message: `${unlocked.length} new blueprints available!`,
+ icon: '๐'
+ });
+ }
+
+ return unlocked;
+ }
+
+ /**
+ * Get NPC gift blueprint
+ */
+ getNPCGift(npcName) {
+ const giftBlueprints = this.discoveryMethods.npc_gift.blueprints;
+ const available = giftBlueprints.filter(bp => {
+ const recipeId = this.blueprints[bp];
+ return recipeId && !this.isUnlocked(recipeId);
+ });
+
+ if (available.length === 0) return null;
+
+ const blueprintItem = available[Math.floor(Math.random() * available.length)];
+
+ console.log(`๐ ${npcName} gifted blueprint: ${blueprintItem}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Gift from ' + npcName,
+ message: `Received ${blueprintItem}!`,
+ icon: '๐'
+ });
+
+ return blueprintItem;
+ }
+
+ /**
+ * Dig for blueprint (5% chance)
+ */
+ digForBlueprint(x, y) {
+ return this.tryDiscover('digging', x, y);
+ }
+
+ /**
+ * Beach combing for blueprint (15% chance)
+ */
+ beachComb(x, y) {
+ return this.tryDiscover('beach', x, y);
+ }
+
+ /**
+ * Fishing for blueprint (8% chance)
+ */
+ fishForBlueprint(x, y) {
+ return this.tryDiscover('fishing', x, y);
+ }
+
+ /**
+ * Explore ancient ruins for blueprint (20% chance)
+ */
+ exploreRuins(x, y) {
+ return this.tryDiscover('ruins', x, y);
+ }
+
+ /**
+ * Open chest for blueprint (30% chance)
+ */
+ openChest(x, y) {
+ return this.tryDiscover('chest', x, y);
+ }
+
+ /**
+ * Boss drops blueprint (100% chance, rare blueprints)
+ */
+ bossDrop(x, y) {
+ return this.tryDiscover('boss', x, y);
+ }
+
+ /**
+ * Museum visit (specific blueprints, 100% chance)
+ */
+ visitMuseum() {
+ const museumBlueprints = this.discoveryMethods.museum.blueprints;
+ const available = museumBlueprints.filter(bp => {
+ const recipeId = this.blueprints[bp];
+ return recipeId && !this.isUnlocked(recipeId);
+ });
+
+ if (available.length === 0) {
+ console.log('โน๏ธ All museum blueprints already acquired!');
+ return null;
+ }
+
+ const blueprintItem = available[0]; // Give first available
+
+ console.log(`๐๏ธ Museum blueprint acquired: ${blueprintItem}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Museum Discovery!',
+ message: `Acquired ${blueprintItem}!`,
+ icon: '๐๏ธ'
+ });
+
+ return blueprintItem;
+ }
+
+ /**
+ * Get collection progress
+ */
+ getProgress() {
+ return {
+ discovered: this.discovered,
+ total: this.totalBlueprints,
+ percentage: Math.round((this.discovered / this.totalBlueprints) * 100)
+ };
+ }
+
+ /**
+ * Get blueprints by category
+ */
+ getBlueprintsByCategory(category) {
+ if (!this.categories[category]) return [];
+
+ return this.categories[category].map(recipeId => {
+ return {
+ recipeId: recipeId,
+ unlocked: this.isUnlocked(recipeId)
+ };
+ });
+ }
+
+ /**
+ * Show blueprint gallery UI
+ */
+ showGallery() {
+ this.showingGallery = true;
+
+ const progress = this.getProgress();
+
+ console.log('๐ BLUEPRINT GALLERY');
+ console.log(`Progress: ${progress.discovered}/${progress.total} (${progress.percentage}%)`);
+ console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
+
+ Object.keys(this.categories).forEach(category => {
+ const blueprints = this.getBlueprintsByCategory(category);
+ const unlocked = blueprints.filter(bp => bp.unlocked).length;
+
+ console.log(`${category.toUpperCase()}: ${unlocked}/${blueprints.length}`);
+ blueprints.forEach(bp => {
+ const icon = bp.unlocked ? 'โ
' : '๐';
+ console.log(` ${icon} ${bp.recipeId}`);
+ });
+ });
+
+ // Emit UI event for rendering
+ this.scene.events.emit('show-blueprint-gallery', {
+ progress: progress,
+ categories: this.categories,
+ getBlueprintsByCategory: (cat) => this.getBlueprintsByCategory(cat)
+ });
+ }
+
+ /**
+ * Hide blueprint gallery UI
+ */
+ hideGallery() {
+ this.showingGallery = false;
+ this.scene.events.emit('hide-blueprint-gallery');
+ }
+
+ /**
+ * Get hint for locked blueprint
+ */
+ getHint(recipeId) {
+ // Find blueprint item for this recipe
+ const blueprintItem = Object.keys(this.blueprints).find(key => this.blueprints[key] === recipeId);
+ if (!blueprintItem) return 'Unknown';
+
+ // Check which discovery method has it
+ for (const [method, data] of Object.entries(this.discoveryMethods)) {
+ if (data.blueprints.includes(blueprintItem)) {
+ const hints = {
+ museum: 'Visit the Museum',
+ digging: 'Try digging (5% chance)',
+ beach: 'Search beaches (15% chance)',
+ chest: 'Open chests (30% chance)',
+ boss: 'Defeat bosses (100% chance)',
+ npc_gift: 'Befriend NPCs',
+ quest: 'Complete quests',
+ fishing: 'Go fishing (8% chance)',
+ ruins: 'Explore ancient ruins (20% chance)'
+ };
+ return hints[method] || 'Unknown method';
+ }
+ }
+
+ // Check quest locks
+ for (const [questId, blueprints] of Object.entries(this.questLocks)) {
+ if (blueprints.includes(blueprintItem)) {
+ return `Complete quest: ${questId}`;
+ }
+ }
+
+ return 'Discover through exploration';
+ }
+}
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BossArenaSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BossArenaSystem.js
new file mode 100644
index 000000000..3daea7ad1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BossArenaSystem.js
@@ -0,0 +1,530 @@
+/**
+ * BossArenaSystem.js
+ * ==================
+ * KRVAVA ลฝETEV - Boss Arena System
+ *
+ * Features:
+ * - Special arena locations
+ * - Dynamic environment
+ * - Escape routes
+ * - Boss encounter management
+ * - Arena hazards
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class BossArenaSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Arena registry
+ this.arenas = new Map();
+ this.currentArena = null;
+ this.inBossFight = false;
+
+ // Arena state
+ this.arenaEntered = false;
+ this.exitBlocked = false;
+ this.hazardsActive = [];
+
+ console.log('๐๏ธ BossArenaSystem initialized');
+
+ // Register all boss arenas
+ this.registerArenas();
+ }
+
+ /**
+ * Register all boss arenas
+ */
+ registerArenas() {
+ const arenas = [
+ {
+ id: 'troll_king_arena',
+ name: 'The Facility Reactor',
+ boss: 'alpha_troll_king',
+ location: { x: 250, y: 250 },
+ size: { width: 30, height: 30 }, // In tiles
+ tiledMap: 'boss_arena_reactor.tmx',
+ environment: 'city',
+ hazards: ['toxic_gas', 'electricity_sparks'],
+ escapeRoutes: [
+ { x: 240, y: 240, blocked: true },
+ { x: 260, y: 260, blocked: true }
+ ],
+ music: 'boss_battle_final',
+ icon: '๐น'
+ },
+ {
+ id: 'forest_boss_arena',
+ name: 'Enchanted Forest Arena',
+ boss: 'forest_guardian',
+ location: { x: 350, y: 200 },
+ size: { width: 25, height: 25 },
+ tiledMap: 'boss_arena_forest.tmx',
+ environment: 'forest',
+ hazards: ['poison_vines', 'thorns'],
+ escapeRoutes: [
+ { x: 340, y: 190, blocked: true },
+ { x: 360, y: 210, blocked: true }
+ ],
+ music: 'boss_battle_nature',
+ icon: '๐ฒ'
+ },
+ {
+ id: 'volcano_arena',
+ name: 'Volcanic Crater Arena',
+ boss: 'lava_titan',
+ location: { x: 50, y: 50 },
+ size: { width: 35, height: 35 },
+ tiledMap: 'boss_arena_volcano.tmx',
+ environment: 'volcano',
+ hazards: ['lava_eruptions', 'fire_geysers', 'falling_meteors'],
+ escapeRoutes: [
+ { x: 40, y: 40, blocked: true }
+ ],
+ music: 'boss_battle_inferno',
+ icon: '๐'
+ },
+ {
+ id: 'ice_arena',
+ name: 'Frozen Wasteland Arena',
+ boss: 'frost_giant',
+ location: { x: 450, y: 100 },
+ size: { width: 28, height: 28 },
+ tiledMap: 'boss_arena_ice.tmx',
+ environment: 'ice',
+ hazards: ['ice_spikes', 'blizzard', 'frozen_ground'],
+ escapeRoutes: [
+ { x: 440, y: 90, blocked: true },
+ { x: 460, y: 110, blocked: true }
+ ],
+ music: 'boss_battle_frost',
+ icon: 'โ๏ธ'
+ },
+ {
+ id: 'catacombs_arena',
+ name: 'Ancient Catacombs Arena',
+ boss: 'lich_king',
+ location: { x: 300, y: 300 },
+ size: { width: 32, height: 32 },
+ tiledMap: 'boss_arena_catacombs.tmx',
+ environment: 'catacombs',
+ hazards: ['zombie_summons', 'curse_zones', 'collapsing_ceiling'],
+ escapeRoutes: [
+ { x: 290, y: 290, blocked: true }
+ ],
+ music: 'boss_battle_undead',
+ icon: '๐'
+ }
+ ];
+
+ arenas.forEach(arena => this.arenas.set(arena.id, arena));
+
+ console.log(`โ
Registered ${this.arenas.size} boss arenas`);
+ }
+
+ /**
+ * Load boss arena
+ */
+ loadArena(arenaId) {
+ const arena = this.arenas.get(arenaId);
+ if (!arena) {
+ console.error(`Arena ${arenaId} not found!`);
+ return false;
+ }
+
+ console.log(`๐๏ธ Loading ${arena.name}...`);
+
+ // Load Tiled map
+ this.loadTiledMap(arena);
+
+ // Setup arena boundaries
+ this.setupArenaBoundaries(arena);
+
+ // Block escape routes
+ this.blockEscapeRoutes(arena);
+
+ // Activate environmental hazards
+ this.activateHazards(arena);
+
+ // Change music
+ this.changeMusicTo(arena.music);
+
+ this.currentArena = arena;
+ this.arenaEntered = true;
+
+ this.showNotification({
+ title: 'Boss Arena',
+ text: `${arena.icon} Entered ${arena.name}!`,
+ icon: 'โ๏ธ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Load Tiled map for arena
+ */
+ loadTiledMap(arena) {
+ console.log(`๐ Loading Tiled map: ${arena.tiledMap}`);
+
+ // TODO: Actually load and display Tiled map
+ // this.scene.load.tilemapTiledJSON(arena.id, `assets/maps/${arena.tiledMap}`);
+ // this.scene.load.once('complete', () => {
+ // const map = this.scene.make.tilemap({ key: arena.id });
+ // // ... setup tilemap
+ // });
+
+ // For now, create placeholder
+ this.createPlaceholderArena(arena);
+ }
+
+ /**
+ * Create placeholder arena (until Tiled maps ready)
+ */
+ createPlaceholderArena(arena) {
+ const centerX = arena.location.x * 48;
+ const centerY = arena.location.y * 48;
+ const width = arena.size.width * 48;
+ const height = arena.size.height * 48;
+
+ // Arena floor
+ const floor = this.scene.add.rectangle(
+ centerX, centerY,
+ width, height,
+ this.getArenaColor(arena.environment),
+ 0.3
+ );
+ floor.setDepth(-1);
+
+ // Arena border
+ const border = this.scene.add.rectangle(
+ centerX, centerY,
+ width, height
+ );
+ border.setStrokeStyle(5, 0xFF0000);
+ border.setFillStyle(null);
+ border.setDepth(10);
+
+ console.log(`โ
Created placeholder arena at (${centerX}, ${centerY})`);
+ }
+
+ /**
+ * Get arena color by environment
+ */
+ getArenaColor(environment) {
+ const colors = {
+ ruins: 0x8B7355,
+ forest: 0x228B22,
+ volcano: 0xFF4500,
+ ice: 0x87CEEB,
+ catacombs: 0x2F4F4F
+ };
+ return colors[environment] || 0x808080;
+ }
+
+ /**
+ * Setup arena boundaries
+ */
+ setupArenaBoundaries(arena) {
+ const bounds = {
+ x: arena.location.x * 48,
+ y: arena.location.y * 48,
+ width: arena.size.width * 48,
+ height: arena.size.height * 48
+ };
+
+ // TODO: Create physics boundaries
+ console.log(`๐ Arena boundaries set: ${bounds.width}x${bounds.height}`);
+ }
+
+ /**
+ * Block escape routes
+ */
+ blockEscapeRoutes(arena) {
+ this.exitBlocked = true;
+
+ arena.escapeRoutes.forEach(route => {
+ route.blocked = true;
+
+ // TODO: Create physical barriers at exit points
+ console.log(`๐ซ Blocked exit at (${route.x}, ${route.y})`);
+ });
+
+ console.log(`๐ All ${arena.escapeRoutes.length} exits blocked!`);
+ }
+
+ /**
+ * Unblock escape routes (after boss defeat)
+ */
+ unblockEscapeRoutes() {
+ if (!this.currentArena) return;
+
+ this.exitBlocked = false;
+
+ this.currentArena.escapeRoutes.forEach(route => {
+ route.blocked = false;
+ console.log(`โ
Unblocked exit at (${route.x}, ${route.y})`);
+ });
+
+ this.showNotification({
+ title: 'Victory!',
+ text: 'โ
Escape routes unlocked! You can leave!',
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Activate environmental hazards
+ */
+ activateHazards(arena) {
+ arena.hazards.forEach(hazard => {
+ this.activateHazard(hazard);
+ });
+
+ console.log(`โ ๏ธ Activated ${arena.hazards.length} hazards`);
+ }
+
+ /**
+ * Activate single hazard
+ */
+ activateHazard(hazardType) {
+ const hazard = {
+ type: hazardType,
+ active: true,
+ interval: null
+ };
+
+ switch (hazardType) {
+ case 'falling_rocks':
+ hazard.interval = setInterval(() => {
+ this.spawnFallingRock();
+ }, 3000);
+ break;
+
+ case 'lava_pools':
+ hazard.interval = setInterval(() => {
+ this.createLavaPool();
+ }, 5000);
+ break;
+
+ case 'lava_eruptions':
+ hazard.interval = setInterval(() => {
+ this.triggerLavaEruption();
+ }, 4000);
+ break;
+
+ case 'ice_spikes':
+ hazard.interval = setInterval(() => {
+ this.spawnIceSpikes();
+ }, 3500);
+ break;
+
+ case 'poison_vines':
+ hazard.interval = setInterval(() => {
+ this.growPoisonVines();
+ }, 6000);
+ break;
+
+ case 'zombie_summons':
+ hazard.interval = setInterval(() => {
+ this.summonZombies();
+ }, 10000);
+ break;
+ }
+
+ this.hazardsActive.push(hazard);
+ console.log(`โ ๏ธ Hazard active: ${hazardType}`);
+ }
+
+ /**
+ * Hazard effects
+ */
+ spawnFallingRock() {
+ console.log('๐ฅ Rock falling!');
+ // TODO: Create falling rock sprite with damage AoE
+ }
+
+ createLavaPool() {
+ console.log('๐ Lava pool forming!');
+ // TODO: Create lava pool damage zone
+ }
+
+ triggerLavaEruption() {
+ console.log('๐ฅ LAVA ERUPTION!');
+ // TODO: Create eruption effect with knockback
+ }
+
+ spawnIceSpikes() {
+ console.log('โ๏ธ Ice spikes emerging!');
+ // TODO: Create ice spike hazards
+ }
+
+ growPoisonVines() {
+ console.log('๐ฟ Poison vines growing!');
+ // TODO: Create poison vine damage zones
+ }
+
+ summonZombies() {
+ console.log('๐ Zombies summoned!');
+ // TODO: Spawn weak zombies as adds
+ }
+
+ /**
+ * Deactivate all hazards
+ */
+ deactivateAllHazards() {
+ this.hazardsActive.forEach(hazard => {
+ if (hazard.interval) {
+ clearInterval(hazard.interval);
+ }
+ hazard.active = false;
+ });
+
+ this.hazardsActive = [];
+ console.log('โ
All hazards deactivated');
+ }
+
+ /**
+ * Start boss fight
+ */
+ startBossFight(bossId) {
+ if (!this.currentArena) {
+ console.error('Not in an arena!');
+ return false;
+ }
+
+ this.inBossFight = true;
+
+ console.log(`โ๏ธ Boss fight started: ${bossId}`);
+
+ // Block exits
+ this.exitBlocked = true;
+
+ // Activate hazards
+ this.activateHazards(this.currentArena);
+
+ // TODO: Spawn boss
+
+ this.showNotification({
+ title: 'BOSS FIGHT!',
+ text: `${this.currentArena.icon} ${this.currentArena.boss} awakens!`,
+ icon: 'โ๏ธ'
+ });
+
+ return true;
+ }
+
+ /**
+ * End boss fight (boss defeated)
+ */
+ endBossFight(victory = true) {
+ if (!this.inBossFight) return;
+
+ this.inBossFight = false;
+
+ // Deactivate hazards
+ this.deactivateAllHazards();
+
+ // Unblock exits
+ if (victory) {
+ this.unblockEscapeRoutes();
+ }
+
+ console.log(`${victory ? 'โ
Victory!' : '๐ Defeat!'}`);
+
+ if (victory) {
+ this.grantBossRewards();
+ }
+ }
+
+ /**
+ * Grant boss rewards
+ */
+ grantBossRewards() {
+ if (!this.currentArena) return;
+
+ console.log('๐ Boss rewards:');
+ console.log(' ๐ Legendary loot');
+ console.log(' ๐ฐ 1000 Zlatniki');
+ console.log(' โญ 5000 XP');
+
+ // TODO: Actually grant rewards
+
+ this.showNotification({
+ title: 'Boss Defeated!',
+ text: '๐ Legendary loot acquired!',
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Change music
+ */
+ changeMusicTo(trackName) {
+ console.log(`๐ต Music: ${trackName}`);
+ // TODO: Actually change music
+ }
+
+ /**
+ * Leave arena
+ */
+ leaveArena() {
+ if (this.exitBlocked) {
+ this.showNotification({
+ title: 'Blocked!',
+ text: '๐ซ You cannot leave during boss fight!',
+ icon: 'โ๏ธ'
+ });
+ return false;
+ }
+
+ if (this.currentArena) {
+ console.log(`๐ช Leaving ${this.currentArena.name}`);
+
+ // Cleanup
+ this.deactivateAllHazards();
+ this.currentArena = null;
+ this.arenaEntered = false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get arena info
+ */
+ getArenaInfo(arenaId) {
+ return this.arenas.get(arenaId);
+ }
+
+ /**
+ * Get all arenas
+ */
+ getAllArenas() {
+ return Array.from(this.arenas.values());
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+
+ /**
+ * Update system
+ */
+ update(delta) {
+ if (!this.inBossFight) return;
+
+ // Update hazards, check arena bounds, etc.
+ // TODO: Implement arena updates
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BossBattlesSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BossBattlesSystem.js
new file mode 100644
index 000000000..d41e87aa5
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BossBattlesSystem.js
@@ -0,0 +1,504 @@
+/**
+ * BOSS BATTLES SYSTEM
+ * Multi-phase boss fights with unique mechanics and legendary loot
+ */
+class BossBattlesSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Active boss fight
+ this.currentBoss = null;
+ this.bossPhase = 1;
+
+ // Boss definitions
+ this.bossTypes = new Map();
+
+ // Boss arenas
+ this.arenas = new Map();
+
+ // Defeated bosses
+ this.defeatedBosses = new Set();
+
+ // Respawn timers
+ this.respawnTimers = new Map();
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Boss Battles System initialized');
+ }
+
+ init() {
+ this.defineBosses();
+ this.defineArenas();
+ console.log('๐น Boss battles ready');
+ }
+
+ // ========== BOSS DEFINITIONS ==========
+
+ defineBosses() {
+ // Mutant King - Early game boss
+ this.defineBoss('mutant_king', {
+ name: 'Mutant King',
+ level: 10,
+ phases: [
+ {
+ hp: 1000,
+ attacks: ['slash', 'charge'],
+ attackSpeed: 2.0,
+ moveSpeed: 1.0
+ },
+ {
+ hp: 500,
+ attacks: ['slash', 'charge', 'summon_minions'],
+ attackSpeed: 2.5,
+ moveSpeed: 1.2
+ },
+ {
+ hp: 250,
+ attacks: ['berserk', 'aoe_slam'],
+ attackSpeed: 3.0,
+ moveSpeed: 1.5
+ }
+ ],
+ loot: [
+ { item: 'mutant_crown', chance: 1.0 },
+ { item: 'legendary_sword', chance: 0.2 },
+ { item: 'gold', amount: 500, chance: 1.0 }
+ ],
+ respawnTime: 604800000, // 7 days
+ arena: 'mutant_throne'
+ });
+
+ // Zombie Horde Leader
+ this.defineBoss('zombie_leader', {
+ name: 'Zombie Horde Leader',
+ level: 20,
+ phases: [
+ {
+ hp: 2000,
+ attacks: ['bite', 'claw_swipe'],
+ attackSpeed: 1.5,
+ moveSpeed: 0.8,
+ summons: 'zombie_minions'
+ },
+ {
+ hp: 1000,
+ attacks: ['bite', 'claw_swipe', 'plague_cloud'],
+ attackSpeed: 2.0,
+ moveSpeed: 1.0,
+ summons: 'zombie_minions'
+ },
+ {
+ hp: 500,
+ attacks: ['enrage', 'death_grip', 'plague_explosion'],
+ attackSpeed: 2.5,
+ moveSpeed: 1.2
+ }
+ ],
+ loot: [
+ { item: 'zombie_heart', chance: 1.0 },
+ { item: 'necromancer_staff', chance: 0.15 },
+ { item: 'gold', amount: 1000, chance: 1.0 }
+ ],
+ respawnTime: 604800000,
+ arena: 'graveyard'
+ });
+
+ // Ancient Tree
+ this.defineBoss('ancient_tree', {
+ name: 'Ancient Tree',
+ level: 30,
+ phases: [
+ {
+ hp: 3000,
+ attacks: ['root_strike', 'thorn_volley'],
+ attackSpeed: 1.0,
+ moveSpeed: 0.5,
+ healing: true
+ },
+ {
+ hp: 1500,
+ attacks: ['root_strike', 'thorn_volley', 'vine_whip'],
+ attackSpeed: 1.5,
+ moveSpeed: 0.7,
+ summons: 'tree_spirits'
+ },
+ {
+ hp: 750,
+ attacks: ['nature_fury', 'poison_spores', 'earthquake'],
+ attackSpeed: 2.0,
+ moveSpeed: 0.8
+ }
+ ],
+ loot: [
+ { item: 'ancient_wood', chance: 1.0 },
+ { item: 'nature_staff', chance: 0.1 },
+ { item: 'life_essence', chance: 0.5 }
+ ],
+ respawnTime: 604800000,
+ arena: 'sacred_grove'
+ });
+
+ // Ice Titan
+ this.defineBoss('ice_titan', {
+ name: 'Ice Titan',
+ level: 40,
+ phases: [
+ {
+ hp: 4000,
+ attacks: ['ice_punch', 'frost_breath'],
+ attackSpeed: 1.2,
+ moveSpeed: 0.6
+ },
+ {
+ hp: 2000,
+ attacks: ['ice_punch', 'frost_breath', 'blizzard'],
+ attackSpeed: 1.5,
+ moveSpeed: 0.8,
+ aura: 'freezing_aura'
+ },
+ {
+ hp: 1000,
+ attacks: ['avalanche', 'ice_prison', 'absolute_zero'],
+ attackSpeed: 2.0,
+ moveSpeed: 1.0
+ }
+ ],
+ loot: [
+ { item: 'ice_core', chance: 1.0 },
+ { item: 'frost_hammer', chance: 0.08 },
+ { item: 'eternal_ice', chance: 0.3 }
+ ],
+ respawnTime: 604800000,
+ arena: 'frozen_peak'
+ });
+
+ // Fire Dragon - Final boss
+ this.defineBoss('fire_dragon', {
+ name: 'Fire Dragon',
+ level: 50,
+ phases: [
+ {
+ hp: 5000,
+ attacks: ['claw_slash', 'tail_swipe', 'fire_breath'],
+ attackSpeed: 1.5,
+ moveSpeed: 1.0,
+ flying: true
+ },
+ {
+ hp: 2500,
+ attacks: ['claw_slash', 'fire_breath', 'meteor_strike'],
+ attackSpeed: 2.0,
+ moveSpeed: 1.2,
+ flying: true,
+ aura: 'burning_aura'
+ },
+ {
+ hp: 1250,
+ attacks: ['inferno', 'dragon_rage', 'apocalypse'],
+ attackSpeed: 2.5,
+ moveSpeed: 1.5,
+ invulnerable_periods: true
+ }
+ ],
+ loot: [
+ { item: 'dragon_heart', chance: 1.0 },
+ { item: 'dragon_scale_armor', chance: 0.05 },
+ { item: 'legendary_gem', chance: 0.2 },
+ { item: 'gold', amount: 5000, chance: 1.0 }
+ ],
+ respawnTime: 1209600000, // 14 days
+ arena: 'volcano_peak'
+ });
+ }
+
+ defineBoss(id, data) {
+ this.bossTypes.set(id, {
+ id,
+ totalHp: data.phases.reduce((sum, p) => sum + p.hp, 0),
+ ...data
+ });
+ }
+
+ // ========== ARENAS ==========
+
+ defineArenas() {
+ this.arenas.set('mutant_throne', {
+ name: 'Mutant Throne Room',
+ x: 50, y: 50,
+ width: 30, height: 30,
+ hazards: ['spike_traps']
+ });
+
+ this.arenas.set('graveyard', {
+ name: 'Cursed Graveyard',
+ x: 80, y: 80,
+ width: 40, height: 40,
+ hazards: ['poison_pools', 'zombie_spawners']
+ });
+
+ this.arenas.set('sacred_grove', {
+ name: 'Sacred Grove',
+ x: 120, y: 120,
+ width: 50, height: 50,
+ hazards: ['thorns', 'healing_pools']
+ });
+
+ this.arenas.set('frozen_peak', {
+ name: 'Frozen Peak',
+ x: 150, y: 150,
+ width: 50, height: 50,
+ hazards: ['ice_patches', 'avalanche_zones']
+ });
+
+ this.arenas.set('volcano_peak', {
+ name: 'Volcano Peak',
+ x: 200, y: 200,
+ width: 60, height: 60,
+ hazards: ['lava_pools', 'falling_rocks']
+ });
+ }
+
+ // ========== BOSS FIGHT ==========
+
+ startBossFight(bossId) {
+ const bossData = this.bossTypes.get(bossId);
+ if (!bossData) {
+ console.log('โ Boss not found');
+ return false;
+ }
+
+ // Check if boss is on cooldown
+ if (this.isOnCooldown(bossId)) {
+ const timeLeft = this.getRespawnTimeLeft(bossId);
+ console.log(`โ Boss respawns in ${Math.floor(timeLeft / 3600000)} hours`);
+ return false;
+ }
+
+ // Create boss instance
+ this.currentBoss = {
+ id: bossId,
+ name: bossData.name,
+ level: bossData.level,
+ currentPhase: 1,
+ hp: bossData.phases[0].hp,
+ maxHp: bossData.totalHp,
+ phases: bossData.phases,
+ loot: bossData.loot,
+ defeated: false,
+ startTime: Date.now()
+ };
+
+ this.bossPhase = 1;
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.screenFlash(0xff0000, 1000);
+ }
+
+ console.log(`๐น Boss fight started: ${bossData.name}!`);
+ return true;
+ }
+
+ // ========== PHASE TRANSITIONS ==========
+
+ updateBossFight(delta) {
+ if (!this.currentBoss || this.currentBoss.defeated) return;
+
+ // Check for phase transition
+ const currentPhaseData = this.currentBoss.phases[this.bossPhase - 1];
+
+ if (this.currentBoss.hp <= 0 && this.bossPhase < this.currentBoss.phases.length) {
+ this.transitionPhase();
+ } else if (this.currentBoss.hp <= 0) {
+ this.defeatBoss();
+ }
+ }
+
+ transitionPhase() {
+ this.bossPhase++;
+ const newPhase = this.currentBoss.phases[this.bossPhase - 1];
+
+ this.currentBoss.hp = newPhase.hp;
+ this.currentBoss.currentPhase = this.bossPhase;
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.screenFlash(0xff00ff, 500);
+ const player = this.scene.player;
+ if (player) {
+ const pos = player.getPosition();
+ this.scene.visualEnhancements.createExplosionEffect(pos.x, pos.y);
+ }
+ }
+
+ console.log(`โก Boss entered Phase ${this.bossPhase}!`);
+ }
+
+ // ========== BOSS DEFEAT ==========
+
+ defeatBoss() {
+ this.currentBoss.defeated = true;
+
+ // Grant loot
+ this.grantBossLoot();
+
+ // Mark as defeated
+ this.defeatedBosses.add(this.currentBoss.id);
+
+ // Start respawn timer
+ const bossData = this.bossTypes.get(this.currentBoss.id);
+ this.respawnTimers.set(this.currentBoss.id, {
+ defeatedTime: Date.now(),
+ respawnTime: bossData.respawnTime
+ });
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.screenFlash(0xffd700, 1000);
+ }
+
+ // Achievement
+ if (this.scene.uiGraphics) {
+ if (this.currentBoss.id === 'fire_dragon') {
+ this.scene.uiGraphics.unlockAchievement('dragon_slayer');
+ }
+ if (this.defeatedBosses.size >= 5) {
+ this.scene.uiGraphics.unlockAchievement('boss_master');
+ }
+ }
+
+ console.log(`๐ Defeated ${this.currentBoss.name}!`);
+ this.saveProgress();
+ }
+
+ grantBossLoot() {
+ const loot = this.currentBoss.loot;
+
+ for (const lootItem of loot) {
+ const roll = Math.random();
+ if (roll <= lootItem.chance) {
+ const amount = lootItem.amount || 1;
+
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(lootItem.item, amount);
+ }
+
+ console.log(`๐ Received: ${lootItem.item} x${amount}`);
+ }
+ }
+ }
+
+ // ========== RESPAWN SYSTEM ==========
+
+ isOnCooldown(bossId) {
+ const timer = this.respawnTimers.get(bossId);
+ if (!timer) return false;
+
+ const elapsed = Date.now() - timer.defeatedTime;
+ return elapsed < timer.respawnTime;
+ }
+
+ getRespawnTimeLeft(bossId) {
+ const timer = this.respawnTimers.get(bossId);
+ if (!timer) return 0;
+
+ const elapsed = Date.now() - timer.defeatedTime;
+ return Math.max(0, timer.respawnTime - elapsed);
+ }
+
+ // ========== BOSS ATTACKS ==========
+
+ executeBossAttack(attackName) {
+ console.log(`๐ฅ Boss used ${attackName}!`);
+
+ switch (attackName) {
+ case 'summon_minions':
+ this.summonMinions();
+ break;
+ case 'aoe_slam':
+ this.aoeSlam();
+ break;
+ case 'plague_cloud':
+ this.plagueCloud();
+ break;
+ case 'meteor_strike':
+ this.meteorStrike();
+ break;
+ case 'apocalypse':
+ this.apocalypse();
+ break;
+ }
+ }
+
+ summonMinions() {
+ console.log('๐ฅ Boss summoned minions!');
+ }
+
+ aoeSlam() {
+ console.log('๐ฅ Boss used AOE slam!');
+ }
+
+ plagueCloud() {
+ console.log('โ ๏ธ Boss released plague cloud!');
+ }
+
+ meteorStrike() {
+ console.log('โ๏ธ Meteors falling!');
+ }
+
+ apocalypse() {
+ console.log('๐ฅ APOCALYPSE!');
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateBossFight(delta);
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ defeatedBosses: Array.from(this.defeatedBosses),
+ respawnTimers: Array.from(this.respawnTimers.entries()).map(([id, timer]) => ({
+ id,
+ defeatedTime: timer.defeatedTime,
+ respawnTime: timer.respawnTime
+ }))
+ };
+
+ localStorage.setItem('novafarma_bosses', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_bosses');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.defeatedBosses = new Set(data.defeatedBosses || []);
+
+ if (data.respawnTimers) {
+ data.respawnTimers.forEach(timer => {
+ this.respawnTimers.set(timer.id, {
+ defeatedTime: timer.defeatedTime,
+ respawnTime: timer.respawnTime
+ });
+ });
+ }
+
+ console.log('โ
Boss progress loaded');
+ } catch (error) {
+ console.error('Failed to load boss progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐น Boss Battles System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BreedingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BreedingSystem.js
new file mode 100644
index 000000000..f13e7dd2b
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BreedingSystem.js
@@ -0,0 +1,565 @@
+/**
+ * BreedingSystem.js
+ * =================
+ * Manages animal breeding, family trees, and baby growth
+ *
+ * Features:
+ * - Breeding compatibility checks (species, gender, age)
+ * - Baby creation with inherited traits
+ * - Growth stages: baby โ young โ adult
+ * - Family tree tracking
+ * - Breeding cooldowns
+ * - Animal happiness/relationship system
+ *
+ * Uses: farm_animals_family_grid_1766099078030.tsx
+ * children_5_growth_stages_1766097043062.tsx
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-22
+ */
+
+export default class BreedingSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Animal registry
+ this.animals = new Map(); // id -> animal data
+ this.families = new Map(); // family id -> family tree
+
+ // Breeding pairs currently in progress
+ this.breedingPairs = new Map();
+
+ // Timing constants (in milliseconds)
+ this.BREEDING_COOLDOWN = 7 * 24 * 60 * 60 * 1000; // 7 days
+ this.BABY_TO_YOUNG_TIME = 2 * 24 * 60 * 60 * 1000; // 2 days
+ this.YOUNG_TO_ADULT_TIME = 5 * 24 * 60 * 60 * 1000; // 5 days
+
+ // Debug mode: faster times for testing
+ if (this.scene.registry.get('debugMode')) {
+ this.BREEDING_COOLDOWN = 30000; // 30 seconds
+ this.BABY_TO_YOUNG_TIME = 10000; // 10 seconds
+ this.YOUNG_TO_ADULT_TIME = 20000; // 20 seconds
+ }
+
+ // Species data
+ this.speciesData = this.defineSpecies();
+
+ // Growth sprites
+ this.growthSprites = this.defineGrowthSprites();
+
+ console.log('๐ BreedingSystem initialized');
+ }
+
+ defineSpecies() {
+ return {
+ cow: {
+ name: 'Cow',
+ basePrice: 500,
+ products: ['milk', 'cheese'],
+ gestationTime: 3.5 * 24 * 60 * 60 * 1000, // 3.5 days
+ breedingCooldown: 7 * 24 * 60 * 60 * 1000,
+ lifespan: 100 * 24 * 60 * 60 * 1000, // 100 days
+ sprites: {
+ male_baby: 'cow_male_baby',
+ male_young: 'cow_male_young',
+ male_adult: 'cow_male_adult',
+ female_baby: 'cow_female_baby',
+ female_young: 'cow_female_young',
+ female_adult: 'cow_female_adult'
+ }
+ },
+ chicken: {
+ name: 'Chicken',
+ basePrice: 100,
+ products: ['egg', 'feather'],
+ gestationTime: 21 * 60 * 60 * 1000, // 21 hours
+ breedingCooldown: 3 * 24 * 60 * 60 * 1000, // 3 days
+ lifespan: 50 * 24 * 60 * 60 * 1000,
+ sprites: {
+ male_baby: 'chicken_male_baby',
+ male_young: 'chicken_male_young',
+ male_adult: 'chicken_rooster_adult',
+ female_baby: 'chicken_female_baby',
+ female_young: 'chicken_female_young',
+ female_adult: 'chicken_hen_adult'
+ }
+ },
+ pig: {
+ name: 'Pig',
+ basePrice: 300,
+ products: ['truffle'],
+ gestationTime: 3 * 24 * 60 * 60 * 1000,
+ breedingCooldown: 5 * 24 * 60 * 60 * 1000,
+ lifespan: 80 * 24 * 60 * 60 * 1000,
+ sprites: {
+ male_baby: 'pig_male_baby',
+ male_young: 'pig_male_young',
+ male_adult: 'pig_male_adult',
+ female_baby: 'pig_female_baby',
+ female_young: 'pig_female_young',
+ female_adult: 'pig_female_adult'
+ }
+ },
+ sheep: {
+ name: 'Sheep',
+ basePrice: 400,
+ products: ['wool', 'milk'],
+ gestationTime: 4 * 24 * 60 * 60 * 1000,
+ breedingCooldown: 6 * 24 * 60 * 60 * 1000,
+ lifespan: 90 * 24 * 60 * 60 * 1000,
+ sprites: {
+ male_baby: 'sheep_male_baby',
+ male_young: 'sheep_male_young',
+ male_adult: 'sheep_ram_adult',
+ female_baby: 'sheep_female_baby',
+ female_young: 'sheep_female_young',
+ female_adult: 'sheep_ewe_adult'
+ }
+ }
+ };
+ }
+
+ defineGrowthSprites() {
+ return {
+ baby: 0, // Frame index
+ young: 1,
+ adult: 2
+ };
+ }
+
+ // ===== ANIMAL REGISTRATION =====
+
+ registerAnimal(animalData) {
+ const id = animalData.id || this.generateId();
+
+ const animal = {
+ id,
+ species: animalData.species,
+ gender: animalData.gender || this.randomGender(),
+ age: animalData.age || 'adult',
+ name: animalData.name || this.generateName(animalData.species, animalData.gender),
+ birthDate: animalData.birthDate || Date.now(),
+ parents: animalData.parents || null, // [parentId1, parentId2]
+ children: animalData.children || [],
+ sprite: animalData.sprite || null,
+ happiness: animalData.happiness || 100,
+ lastBred: animalData.lastBred || null,
+ x: animalData.x || 0,
+ y: animalData.y || 0
+ };
+
+ this.animals.set(id, animal);
+
+ // Create sprite if needed
+ if (!animal.sprite && animalData.x && animalData.y) {
+ animal.sprite = this.createAnimalSprite(animal);
+ }
+
+ // Setup growth timer if not adult
+ if (animal.age !== 'adult') {
+ this.setupGrowthTimer(animal);
+ }
+
+ console.log(`๐พ Registered ${animal.species} (${animal.gender}) - ${animal.name}`);
+ return animal;
+ }
+
+ createAnimalSprite(animal) {
+ const speciesData = this.speciesData[animal.species];
+ const spriteKey = `${animal.species}_${animal.gender}_${animal.age}`;
+
+ const sprite = this.scene.add.sprite(
+ animal.x,
+ animal.y,
+ 'farm_animals_family_grid', // tileset name
+ this.getAnimalFrame(animal.species, animal.gender, animal.age)
+ );
+
+ sprite.setData('animalId', animal.id);
+ sprite.setInteractive();
+
+ // Click to view animal details
+ sprite.on('pointerdown', () => {
+ this.scene.events.emit('animalClicked', animal);
+ });
+
+ return sprite;
+ }
+
+ getAnimalFrame(species, gender, age) {
+ // Map to sprite sheet frames
+ // This depends on your actual sprite sheet layout
+ const frameMap = {
+ cow: { male: { baby: 0, young: 1, adult: 2 }, female: { baby: 3, young: 4, adult: 5 } },
+ chicken: { male: { baby: 6, young: 7, adult: 8 }, female: { baby: 9, young: 10, adult: 11 } },
+ pig: { male: { baby: 12, young: 13, adult: 14 }, female: { baby: 15, young: 16, adult: 17 } },
+ sheep: { male: { baby: 18, young: 19, adult: 20 }, female: { baby: 21, young: 22, adult: 23 } }
+ };
+
+ return frameMap[species]?.[gender]?.[age] || 0;
+ }
+
+ // ===== BREEDING MECHANICS =====
+
+ canBreed(animalId1, animalId2) {
+ const animal1 = this.animals.get(animalId1);
+ const animal2 = this.animals.get(animalId2);
+
+ if (!animal1 || !animal2) {
+ return { canBreed: false, reason: 'Animal not found' };
+ }
+
+ // Must be same species
+ if (animal1.species !== animal2.species) {
+ return { canBreed: false, reason: 'Different species cannot breed' };
+ }
+
+ // Must be opposite genders
+ if (animal1.gender === animal2.gender) {
+ return { canBreed: false, reason: 'Same gender cannot breed' };
+ }
+
+ // Both must be adults
+ if (animal1.age !== 'adult' || animal2.age !== 'adult') {
+ return { canBreed: false, reason: 'Both must be adults' };
+ }
+
+ // Check happiness (must be >50 to breed)
+ if (animal1.happiness < 50 || animal2.happiness < 50) {
+ return { canBreed: false, reason: 'Animals must be happy (>50 happiness)' };
+ }
+
+ // Check breeding cooldown
+ const now = Date.now();
+ if (animal1.lastBred && (now - animal1.lastBred) < this.BREEDING_COOLDOWN) {
+ const waitTime = Math.ceil((this.BREEDING_COOLDOWN - (now - animal1.lastBred)) / 1000 / 60);
+ return { canBreed: false, reason: `${animal1.name} needs ${waitTime} more minutes` };
+ }
+
+ if (animal2.lastBred && (now - animal2.lastBred) < this.BREEDING_COOLDOWN) {
+ const waitTime = Math.ceil((this.BREEDING_COOLDOWN - (now - animal2.lastBred)) / 1000 / 60);
+ return { canBreed: false, reason: `${animal2.name} needs ${waitTime} more minutes` };
+ }
+
+ // Check barn capacity
+ if (this.scene.progressionSystem) {
+ const barnBenefits = this.scene.progressionSystem.getCurrentBenefits('barn');
+ const currentAnimals = this.animals.size;
+ if (currentAnimals >= barnBenefits.capacity) {
+ return { canBreed: false, reason: 'Barn is full - upgrade needed!' };
+ }
+ }
+
+ return { canBreed: true };
+ }
+
+ breed(animalId1, animalId2) {
+ const check = this.canBreed(animalId1, animalId2);
+ if (!check.canBreed) {
+ this.scene.uiSystem?.showError(check.reason);
+ console.warn('Cannot breed:', check.reason);
+ return null;
+ }
+
+ const parent1 = this.animals.get(animalId1);
+ const parent2 = this.animals.get(animalId2);
+
+ // Determine which is female (for gestation)
+ const female = parent1.gender === 'female' ? parent1 : parent2;
+ const male = parent1.gender === 'male' ? parent1 : parent2;
+
+ // Get gestation time
+ const speciesData = this.speciesData[parent1.species];
+ const gestationTime = speciesData.gestationTime;
+
+ // Start gestation period
+ this.scene.time.delayedCall(gestationTime, () => {
+ this.giveBirth(female, male);
+ });
+
+ // Update breeding timestamps
+ parent1.lastBred = Date.now();
+ parent2.lastBred = Date.now();
+
+ // Show notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ Breeding Started!',
+ message: `${male.name} and ${female.name} are breeding...`,
+ icon: parent1.species,
+ duration: 3000
+ });
+
+ // Add to breeding pairs
+ this.breedingPairs.set(`${animalId1}_${animalId2}`, {
+ parent1: animalId1,
+ parent2: animalId2,
+ startTime: Date.now(),
+ endTime: Date.now() + gestationTime
+ });
+
+ console.log(`๐ ${male.name} (โ) + ${female.name} (โ) breeding...`);
+ return true;
+ }
+
+ giveBirth(mother, father) {
+ // Create baby
+ const baby = this.createBaby(mother, father);
+
+ // Update parent records
+ mother.children.push(baby.id);
+ father.children.push(baby.id);
+
+ // Show notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ Baby Born!',
+ message: `${mother.name} gave birth to ${baby.name}!`,
+ icon: baby.species,
+ duration: 5000,
+ color: '#FFD700'
+ });
+
+ // Create family tree if doesn't exist
+ const familyId = mother.parents?.[0] || mother.id;
+ if (!this.families.has(familyId)) {
+ this.families.set(familyId, {
+ id: familyId,
+ founder: mother,
+ members: []
+ });
+ }
+ this.families.get(familyId).members.push(baby.id);
+
+ // Emit event
+ this.scene.events.emit('babyBorn', { mother, father, baby });
+
+ // Play birth animation/particles
+ if (this.scene.particleSystem) {
+ this.scene.particleSystem.createBirthEffect(mother.sprite.x, mother.sprite.y);
+ }
+
+ console.log(`๐ผ Baby born: ${baby.name} (${baby.species})`);
+ return baby;
+ }
+
+ createBaby(parent1, parent2) {
+ const baby = {
+ id: this.generateId(),
+ species: parent1.species,
+ age: 'baby',
+ gender: this.randomGender(),
+ parents: [parent1.id, parent2.id],
+ children: [],
+ birthDate: Date.now(),
+ happiness: 100,
+ lastBred: null,
+ // Spawn near mother
+ x: parent1.x + Phaser.Math.Between(-20, 20),
+ y: parent1.y + Phaser.Math.Between(-20, 20),
+ sprite: null
+ };
+
+ // Generate name
+ baby.name = this.generateName(baby.species, baby.gender);
+
+ // Create sprite
+ baby.sprite = this.createAnimalSprite(baby);
+
+ // Register animal
+ this.animals.set(baby.id, baby);
+
+ // Setup growth timer
+ this.setupGrowthTimer(baby);
+
+ return baby;
+ }
+
+ setupGrowthTimer(animal) {
+ if (animal.age === 'baby') {
+ // Baby โ Young
+ this.scene.time.delayedCall(this.BABY_TO_YOUNG_TIME, () => {
+ this.growAnimal(animal, 'young');
+ });
+ } else if (animal.age === 'young') {
+ // Young โ Adult
+ this.scene.time.delayedCall(this.YOUNG_TO_ADULT_TIME, () => {
+ this.growAnimal(animal, 'adult');
+ });
+ }
+ }
+
+ growAnimal(animal, newAge) {
+ const oldAge = animal.age;
+ animal.age = newAge;
+
+ // Update sprite
+ if (animal.sprite) {
+ const newFrame = this.getAnimalFrame(animal.species, animal.gender, newAge);
+ animal.sprite.setFrame(newFrame);
+
+ // Play growth animation
+ this.scene.tweens.add({
+ targets: animal.sprite,
+ scale: { from: 0.8, to: 1.0 },
+ duration: 500,
+ ease: 'Back.easeOut'
+ });
+ }
+
+ // Show notification
+ this.scene.uiSystem?.showNotification({
+ title: `${animal.name} Grew Up!`,
+ message: `${oldAge} โ ${newAge}`,
+ icon: animal.species,
+ duration: 3000
+ });
+
+ // If became adult, can now breed
+ if (newAge === 'adult') {
+ this.scene.events.emit('animalBecameAdult', animal);
+ }
+
+ // Continue growth timer if not yet adult
+ if (newAge !== 'adult') {
+ this.setupGrowthTimer(animal);
+ }
+
+ console.log(`๐
${animal.name} grew from ${oldAge} to ${newAge}`);
+ }
+
+ // ===== HELPER FUNCTIONS =====
+
+ generateId() {
+ return `animal_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+ }
+
+ randomGender() {
+ return Math.random() > 0.5 ? 'male' : 'female';
+ }
+
+ generateName(species, gender) {
+ const names = {
+ cow: {
+ male: ['Toro', 'Bruno', 'Max', 'Duke'],
+ female: ['Daisy', 'Bessie', 'Bella', 'Rosie']
+ },
+ chicken: {
+ male: ['Rocky', 'Cluck', 'Red', 'Rex'],
+ female: ['Penny', 'Clucky', 'Henrietta', 'Ginger']
+ },
+ pig: {
+ male: ['Porky', 'Hamlet', 'Bacon', 'Wilbur'],
+ female: ['Petunia', 'Piglet', 'Penelope', 'Truffle']
+ },
+ sheep: {
+ male: ['Woolly', 'Ram', 'Cloud', 'Fluff'],
+ female: ['Fluffy', 'Snowball', 'Cotton', 'Lamb']
+ }
+ };
+
+ const nameList = names[species]?.[gender] || ['Animal'];
+ return nameList[Math.floor(Math.random() * nameList.length)];
+ }
+
+ // ===== GETTERS =====
+
+ getAnimal(animalId) {
+ return this.animals.get(animalId);
+ }
+
+ getAllAnimals() {
+ return Array.from(this.animals.values());
+ }
+
+ getAnimalsBySpecies(species) {
+ return this.getAllAnimals().filter(a => a.species === species);
+ }
+
+ getAnimalsByGender(gender) {
+ return this.getAllAnimals().filter(a => a.gender === gender);
+ }
+
+ getAdults() {
+ return this.getAllAnimals().filter(a => a.age === 'adult');
+ }
+
+ getBabies() {
+ return this.getAllAnimals().filter(a => a.age === 'baby');
+ }
+
+ getBreedablePairs() {
+ const adults = this.getAdults();
+ const pairs = [];
+
+ for (let i = 0; i < adults.length; i++) {
+ for (let j = i + 1; j < adults.length; j++) {
+ const check = this.canBreed(adults[i].id, adults[j].id);
+ if (check.canBreed) {
+ pairs.push([adults[i], adults[j]]);
+ }
+ }
+ }
+
+ return pairs;
+ }
+
+ getFamilyTree(animalId) {
+ const animal = this.animals.get(animalId);
+ if (!animal) return null;
+
+ return {
+ animal,
+ parents: animal.parents ? animal.parents.map(id => this.animals.get(id)) : [],
+ children: animal.children.map(id => this.animals.get(id)),
+ grandparents: this.getGrandparents(animal),
+ siblings: this.getSiblings(animal)
+ };
+ }
+
+ getGrandparents(animal) {
+ if (!animal.parents) return [];
+
+ const grandparents = [];
+ for (const parentId of animal.parents) {
+ const parent = this.animals.get(parentId);
+ if (parent && parent.parents) {
+ grandparents.push(...parent.parents.map(id => this.animals.get(id)));
+ }
+ }
+ return grandparents.filter(Boolean);
+ }
+
+ getSiblings(animal) {
+ if (!animal.parents) return [];
+
+ const siblings = [];
+ for (const parentId of animal.parents) {
+ const parent = this.animals.get(parentId);
+ if (parent && parent.children) {
+ siblings.push(...parent.children
+ .filter(id => id !== animal.id)
+ .map(id => this.animals.get(id))
+ );
+ }
+ }
+ return [...new Set(siblings)]; // Remove duplicates
+ }
+
+ // ===== UPDATE =====
+
+ update(time, delta) {
+ // Update breeding pairs progress
+ for (const [pairId, pair] of this.breedingPairs.entries()) {
+ if (Date.now() >= pair.endTime) {
+ this.breedingPairs.delete(pairId);
+ }
+ }
+ }
+
+ // ===== CLEANUP =====
+
+ destroy() {
+ this.animals.clear();
+ this.families.clear();
+ this.breedingPairs.clear();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BreedingUISystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BreedingUISystem.js
new file mode 100644
index 000000000..e1a36c004
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BreedingUISystem.js
@@ -0,0 +1,491 @@
+/**
+ * BREEDING UI SYSTEM
+ * Visual family tree and genetics display for animal breeding
+ */
+class BreedingUISystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // UI elements
+ this.container = null;
+ this.familyTreeView = null;
+ this.geneticsPanel = null;
+ this.isVisible = false;
+
+ // Selected animal
+ this.selectedAnimal = null;
+
+ // Family tree data
+ this.familyTree = new Map();
+
+ // UI settings
+ this.settings = {
+ nodeWidth: 80,
+ nodeHeight: 60,
+ horizontalSpacing: 100,
+ verticalSpacing: 80,
+ maxGenerations: 5
+ };
+
+ this.init();
+ console.log('โ
Breeding UI System initialized');
+ }
+
+ init() {
+ this.createUI();
+ console.log('๐ณ Family tree UI ready');
+ }
+
+ // ========== UI CREATION ==========
+
+ createUI() {
+ // Main container (hidden by default)
+ this.container = this.scene.add.container(0, 0);
+ this.container.setDepth(1000);
+ this.container.setVisible(false);
+
+ // Background overlay
+ const overlay = this.scene.add.rectangle(
+ this.scene.cameras.main.width / 2,
+ this.scene.cameras.main.height / 2,
+ this.scene.cameras.main.width,
+ this.scene.cameras.main.height,
+ 0x000000,
+ 0.8
+ );
+ overlay.setInteractive();
+ overlay.on('pointerdown', () => this.hide());
+ this.container.add(overlay);
+
+ // Panel background
+ const panelWidth = 800;
+ const panelHeight = 600;
+ const panelX = this.scene.cameras.main.width / 2;
+ const panelY = this.scene.cameras.main.height / 2;
+
+ const panel = this.scene.add.rectangle(
+ panelX, panelY,
+ panelWidth, panelHeight,
+ 0x2a2a2a
+ );
+ panel.setStrokeStyle(2, 0x4a4a4a);
+ this.container.add(panel);
+
+ // Title
+ const title = this.scene.add.text(
+ panelX, panelY - panelHeight / 2 + 30,
+ 'Family Tree & Genetics',
+ {
+ fontSize: '24px',
+ fontStyle: 'bold',
+ color: '#ffffff'
+ }
+ );
+ title.setOrigin(0.5);
+ this.container.add(title);
+
+ // Close button
+ const closeBtn = this.scene.add.text(
+ panelX + panelWidth / 2 - 30,
+ panelY - panelHeight / 2 + 30,
+ 'โ',
+ {
+ fontSize: '32px',
+ color: '#ff4444'
+ }
+ );
+ closeBtn.setOrigin(0.5);
+ closeBtn.setInteractive({ useHandCursor: true });
+ closeBtn.on('pointerdown', () => this.hide());
+ this.container.add(closeBtn);
+
+ // Family tree view area
+ this.familyTreeView = this.scene.add.container(panelX - 300, panelY);
+ this.container.add(this.familyTreeView);
+
+ // Genetics panel
+ this.geneticsPanel = this.scene.add.container(panelX + 200, panelY);
+ this.container.add(this.geneticsPanel);
+ }
+
+ // ========== SHOW/HIDE ==========
+
+ show(animalId) {
+ if (!this.scene.animalBreeding) {
+ console.log('โ Animal Breeding System not found');
+ return;
+ }
+
+ const animal = this.scene.animalBreeding.animals.get(animalId);
+ if (!animal) {
+ console.log('โ Animal not found');
+ return;
+ }
+
+ this.selectedAnimal = animal;
+ this.buildFamilyTree(animal);
+ this.displayGeneticsInfo(animal);
+
+ this.container.setVisible(true);
+ this.isVisible = true;
+ }
+
+ hide() {
+ this.container.setVisible(false);
+ this.isVisible = false;
+ this.selectedAnimal = null;
+ }
+
+ // ========== FAMILY TREE ==========
+
+ buildFamilyTree(animal) {
+ // Clear previous tree
+ this.familyTreeView.removeAll(true);
+
+ // Build tree structure
+ const tree = this.generateTreeStructure(animal);
+
+ // Render tree
+ this.renderTree(tree, 0, 0, 0);
+ }
+
+ generateTreeStructure(animal, generation = 0) {
+ if (generation >= this.settings.maxGenerations) {
+ return null;
+ }
+
+ const node = {
+ animal,
+ generation,
+ parents: []
+ };
+
+ // Get parents (if stored)
+ if (animal.parents) {
+ node.parents = animal.parents.map(parentId => {
+ const parent = this.scene.animalBreeding.animals.get(parentId);
+ return parent ? this.generateTreeStructure(parent, generation + 1) : null;
+ }).filter(p => p !== null);
+ }
+
+ return node;
+ }
+
+ renderTree(node, x, y, generation) {
+ if (!node) return;
+
+ const settings = this.settings;
+
+ // Draw node
+ this.drawAnimalNode(node.animal, x, y);
+
+ // Draw parents
+ if (node.parents.length > 0) {
+ const parentY = y - settings.verticalSpacing;
+ const parentSpacing = settings.horizontalSpacing;
+
+ node.parents.forEach((parent, index) => {
+ const parentX = x + (index - 0.5) * parentSpacing;
+
+ // Draw connection line
+ const line = this.scene.add.line(
+ 0, 0,
+ x, y - settings.nodeHeight / 2,
+ parentX, parentY + settings.nodeHeight / 2,
+ 0x888888
+ );
+ line.setLineWidth(2);
+ this.familyTreeView.add(line);
+
+ // Render parent node
+ this.renderTree(parent, parentX, parentY, generation + 1);
+ });
+ }
+ }
+
+ drawAnimalNode(animal, x, y) {
+ const settings = this.settings;
+
+ // Node background
+ const nodeColor = this.getNodeColor(animal);
+ const node = this.scene.add.rectangle(
+ x, y,
+ settings.nodeWidth,
+ settings.nodeHeight,
+ nodeColor
+ );
+ node.setStrokeStyle(2, 0xffffff);
+ this.familyTreeView.add(node);
+
+ // Animal icon/emoji
+ const icon = this.getAnimalIcon(animal.species);
+ const iconText = this.scene.add.text(x, y - 10, icon, {
+ fontSize: '24px'
+ });
+ iconText.setOrigin(0.5);
+ this.familyTreeView.add(iconText);
+
+ // Animal name/gender
+ const genderSymbol = animal.gender === 'male' ? 'โ' : 'โ';
+ const nameText = this.scene.add.text(
+ x, y + 15,
+ `${genderSymbol} ${animal.species}`,
+ {
+ fontSize: '10px',
+ color: '#ffffff'
+ }
+ );
+ nameText.setOrigin(0.5);
+ this.familyTreeView.add(nameText);
+
+ // Mutation indicator
+ if (animal.genetics.mutation) {
+ const mutationBadge = this.scene.add.text(
+ x + settings.nodeWidth / 2 - 5,
+ y - settings.nodeHeight / 2 + 5,
+ 'โญ',
+ { fontSize: '12px' }
+ );
+ mutationBadge.setOrigin(0.5);
+ this.familyTreeView.add(mutationBadge);
+ }
+
+ // Make interactive
+ node.setInteractive({ useHandCursor: true });
+ node.on('pointerdown', () => {
+ this.selectedAnimal = animal;
+ this.displayGeneticsInfo(animal);
+ });
+ }
+
+ getNodeColor(animal) {
+ // Color based on genetics
+ const colorMap = {
+ brown: 0x8B4513,
+ black: 0x000000,
+ white: 0xFFFFFF,
+ golden: 0xFFD700,
+ rainbow: 0xFF00FF
+ };
+
+ return colorMap[animal.genetics.color] || 0x888888;
+ }
+
+ getAnimalIcon(species) {
+ const icons = {
+ sheep: '๐',
+ cow: '๐',
+ chicken: '๐',
+ pig: '๐ท',
+ horse: '๐ด'
+ };
+
+ return icons[species] || '๐พ';
+ }
+
+ // ========== GENETICS PANEL ==========
+
+ displayGeneticsInfo(animal) {
+ // Clear previous info
+ this.geneticsPanel.removeAll(true);
+
+ let yOffset = -200;
+
+ // Title
+ const title = this.scene.add.text(0, yOffset, 'Genetics Info', {
+ fontSize: '18px',
+ fontStyle: 'bold',
+ color: '#ffffff'
+ });
+ title.setOrigin(0.5);
+ this.geneticsPanel.add(title);
+ yOffset += 40;
+
+ // Animal info
+ const info = [
+ `Species: ${animal.species}`,
+ `Gender: ${animal.gender}`,
+ `Age: ${Math.floor(animal.age)} days`,
+ `Stage: ${animal.stage}`,
+ '',
+ '--- Genetics ---',
+ `Color: ${animal.genetics.color}`,
+ `Size: ${animal.genetics.size}`,
+ `Speed: ${animal.genetics.speed}`,
+ `Production: ${animal.genetics.production}`
+ ];
+
+ // Mutation info
+ if (animal.genetics.mutation) {
+ info.push('', '--- Mutation ---');
+ info.push(`Type: ${animal.genetics.mutation}`);
+ info.push('โญ RARE MUTATION!');
+ }
+
+ // Breeding info
+ if (animal.pregnant) {
+ info.push('', '--- Breeding ---');
+ info.push(`Pregnant: ${Math.floor(animal.gestationProgress * 100)}%`);
+ }
+
+ // Display all info
+ info.forEach(line => {
+ const text = this.scene.add.text(0, yOffset, line, {
+ fontSize: '14px',
+ color: line.includes('---') ? '#ffff00' : '#ffffff'
+ });
+ text.setOrigin(0.5);
+ this.geneticsPanel.add(text);
+ yOffset += 20;
+ });
+
+ // DNA Helix Animation
+ if (animal.genetics.mutation) {
+ this.addDNAHelixAnimation(0, yOffset + 50);
+ }
+ }
+
+ addDNAHelixAnimation(x, y) {
+ // Create mini DNA helix
+ const helix = this.scene.add.container(x, y);
+
+ for (let i = 0; i < 10; i++) {
+ const yPos = i * 5 - 25;
+ const angle = (i / 10) * Math.PI * 2;
+
+ const x1 = Math.cos(angle) * 10;
+ const x2 = Math.cos(angle + Math.PI) * 10;
+
+ const dot1 = this.scene.add.circle(x1, yPos, 2, 0x00ff00);
+ const dot2 = this.scene.add.circle(x2, yPos, 2, 0x00ffff);
+
+ helix.add(dot1);
+ helix.add(dot2);
+ }
+
+ // Rotate animation
+ this.scene.tweens.add({
+ targets: helix,
+ rotation: Math.PI * 2,
+ duration: 3000,
+ repeat: -1,
+ ease: 'Linear'
+ });
+
+ this.geneticsPanel.add(helix);
+ }
+
+ // ========== BREEDING PREDICTION ==========
+
+ showBreedingPrediction(animal1, animal2) {
+ if (!animal1 || !animal2) return;
+
+ // Predict offspring genetics
+ const prediction = this.predictOffspring(animal1, animal2);
+
+ // Show in UI
+ this.displayPrediction(prediction);
+ }
+
+ predictOffspring(parent1, parent2) {
+ // Simulate genetics inheritance
+ const prediction = {
+ possibleColors: this.predictColor(parent1, parent2),
+ possibleSizes: this.predictTrait(parent1.genetics.size, parent2.genetics.size),
+ possibleSpeeds: this.predictTrait(parent1.genetics.speed, parent2.genetics.speed),
+ mutationChance: 0.05 // 5%
+ };
+
+ return prediction;
+ }
+
+ predictColor(p1, p2) {
+ const colors = [p1.genetics.color, p2.genetics.color];
+
+ // Add dominant/recessive logic
+ const dominant = ['brown', 'black'];
+ const recessive = ['white', 'golden'];
+
+ if (dominant.includes(p1.genetics.color)) {
+ return [p1.genetics.color];
+ }
+ if (dominant.includes(p2.genetics.color)) {
+ return [p2.genetics.color];
+ }
+
+ return colors;
+ }
+
+ predictTrait(trait1, trait2) {
+ return [trait1, trait2];
+ }
+
+ displayPrediction(prediction) {
+ // Clear genetics panel
+ this.geneticsPanel.removeAll(true);
+
+ let yOffset = -200;
+
+ const title = this.scene.add.text(0, yOffset, 'Breeding Prediction', {
+ fontSize: '18px',
+ fontStyle: 'bold',
+ color: '#ffff00'
+ });
+ title.setOrigin(0.5);
+ this.geneticsPanel.add(title);
+ yOffset += 40;
+
+ // Display predictions
+ const info = [
+ 'Possible Offspring:',
+ '',
+ `Colors: ${prediction.possibleColors.join(', ')}`,
+ `Sizes: ${prediction.possibleSizes.join(', ')}`,
+ `Speeds: ${prediction.possibleSpeeds.join(', ')}`,
+ '',
+ `Mutation Chance: ${prediction.mutationChance * 100}%`
+ ];
+
+ info.forEach(line => {
+ const text = this.scene.add.text(0, yOffset, line, {
+ fontSize: '14px',
+ color: '#ffffff'
+ });
+ text.setOrigin(0.5);
+ this.geneticsPanel.add(text);
+ yOffset += 20;
+ });
+ }
+
+ // ========== KEYBOARD SHORTCUT ==========
+
+ setupKeyboard() {
+ // Press 'F' to open family tree for selected animal
+ this.scene.input.keyboard.on('keydown-F', () => {
+ if (this.selectedAnimal) {
+ if (this.isVisible) {
+ this.hide();
+ } else {
+ this.show(this.selectedAnimal.id);
+ }
+ }
+ });
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ // Update animations if visible
+ if (this.isVisible) {
+ // Any dynamic updates here
+ }
+ }
+
+ destroy() {
+ if (this.container) {
+ this.container.destroy();
+ }
+ console.log('๐ณ Breeding UI System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BugCatchingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BugCatchingSystem.js
new file mode 100644
index 000000000..b5bc2c98b
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BugCatchingSystem.js
@@ -0,0 +1,531 @@
+/**
+ * BUG CATCHING & COLLECTION SYSTEM
+ * Catch, collect, and sell bugs
+ *
+ * Features:
+ * - Bug Net tool (3 tiers)
+ * - 50+ Bug species
+ * - Bug Collection Album (like fish collection)
+ * - Rarity system: Common, Uncommon, Rare, Epic, Legendary
+ * - Seasonal & biome-specific bugs
+ * - Bug selling for gold
+ * - Bug jar decorations
+ */
+class BugCatchingSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Bug net tiers
+ this.bugNets = {
+ basic: { name: 'Basic Bug Net', catchRate: 0.5, speed: 1.0, cost: 50 },
+ silk: { name: 'Silk Bug Net', catchRate: 0.75, speed: 1.3, cost: 200 },
+ enchanted: { name: 'Enchanted Net', catchRate: 0.95, speed: 1.8, cost: 1000 }
+ };
+
+ // Player's bug net
+ this.currentNet = null;
+
+ // Bug collection
+ this.caughtBugs = new Map(); // bugId -> count
+ this.bugAlbum = new Map(); // bugId -> discoveryData
+
+ // Active bugs in world
+ this.activeBugs = [];
+
+ // Bug definitions (50+ species)
+ this.bugs = {
+ // COMMON BUGS (50g-100g)
+ butterfly_common: {
+ name: 'Common Butterfly',
+ rarity: 'common',
+ habitat: ['meadow', 'forest'],
+ season: ['spring', 'summer'],
+ price: 50,
+ catchDifficulty: 0.3,
+ description: 'A beautiful orange and black butterfly.',
+ icon: '๐ฆ'
+ },
+ ladybug: {
+ name: 'Ladybug',
+ rarity: 'common',
+ habitat: ['garden', 'farm'],
+ season: ['spring', 'summer'],
+ price: 60,
+ catchDifficulty: 0.2,
+ description: 'Red with black spots. Brings good luck!',
+ icon: '๐'
+ },
+ bee: {
+ name: 'Honey Bee',
+ rarity: 'common',
+ habitat: ['garden', 'meadow'],
+ season: ['spring', 'summer'],
+ price: 70,
+ catchDifficulty: 0.4,
+ description: 'Busy pollinator. Watch out for the sting!',
+ icon: '๐'
+ },
+ ant: {
+ name: 'Ant',
+ rarity: 'common',
+ habitat: ['anywhere'],
+ season: ['all'],
+ price: 30,
+ catchDifficulty: 0.1,
+ description: 'Tiny but strong. Works in colonies.',
+ icon: '๐'
+ },
+ firefly: {
+ name: 'Firefly',
+ rarity: 'common',
+ habitat: ['forest', 'meadow'],
+ season: ['summer'],
+ time: 'night',
+ price: 80,
+ catchDifficulty: 0.3,
+ description: 'Glows in the dark. Magical!',
+ icon: '๐ชฒ'
+ },
+ grasshopper: {
+ name: 'Grasshopper',
+ rarity: 'common',
+ habitat: ['meadow', 'farm'],
+ season: ['summer', 'fall'],
+ price: 55,
+ catchDifficulty: 0.4,
+ description: 'Jumps very high. Hard to catch!',
+ icon: '๐ฆ'
+ },
+
+ // UNCOMMON BUGS (150g-300g)
+ monarch_butterfly: {
+ name: 'Monarch Butterfly',
+ rarity: 'uncommon',
+ habitat: ['meadow'],
+ season: ['summer'],
+ price: 200,
+ catchDifficulty: 0.5,
+ description: 'Iconic orange butterfly. Migrates thousands of miles.',
+ icon: '๐ฆ'
+ },
+ dragonfly: {
+ name: 'Dragonfly',
+ rarity: 'uncommon',
+ habitat: ['pond', 'river'],
+ season: ['summer'],
+ price: 250,
+ catchDifficulty: 0.6,
+ description: 'Fast flyer with iridescent wings.',
+ icon: '๐ชฐ'
+ },
+ mantis: {
+ name: 'Praying Mantis',
+ rarity: 'uncommon',
+ habitat: ['garden', 'forest'],
+ season: ['summer', 'fall'],
+ price: 300,
+ catchDifficulty: 0.5,
+ description: 'Predatory insect. Turns its head!',
+ icon: '๐ฆ'
+ },
+ luna_moth: {
+ name: 'Luna Moth',
+ rarity: 'uncommon',
+ habitat: ['forest'],
+ season: ['spring'],
+ time: 'night',
+ price: 280,
+ catchDifficulty: 0.6,
+ description: 'Large pale green moth. Rarely seen.',
+ icon: '๐ฆ'
+ },
+ cicada: {
+ name: 'Cicada',
+ rarity: 'uncommon',
+ habitat: ['forest'],
+ season: ['summer'],
+ price: 180,
+ catchDifficulty: 0.4,
+ description: 'Very loud! Emerges every 17 years.',
+ icon: '๐ชฐ'
+ },
+
+ // RARE BUGS (500g-800g)
+ rainbow_beetle: {
+ name: 'Rainbow Beetle',
+ rarity: 'rare',
+ habitat: ['tropical_forest'],
+ season: ['summer'],
+ price: 600,
+ catchDifficulty: 0.7,
+ description: 'Shimmers with all colors of the rainbow.',
+ icon: '๐ชฒ'
+ },
+ atlas_moth: {
+ name: 'Atlas Moth',
+ rarity: 'rare',
+ habitat: ['tropical_forest'],
+ season: ['all'],
+ time: 'night',
+ price: 750,
+ catchDifficulty: 0.8,
+ description: 'One of the largest moths in the world!',
+ icon: '๐ฆ'
+ },
+ orchid_mantis: {
+ name: 'Orchid Mantis',
+ rarity: 'rare',
+ habitat: ['tropical_forest', 'garden'],
+ season: ['spring', 'summer'],
+ price: 700,
+ catchDifficulty: 0.75,
+ description: 'Looks exactly like an orchid flower!',
+ icon: '๐ฆ'
+ },
+ hercules_beetle: {
+ name: 'Hercules Beetle',
+ rarity: 'rare',
+ habitat: ['tropical_forest'],
+ season: ['summer'],
+ price: 800,
+ catchDifficulty: 0.7,
+ description: 'Massive beetle with two horns. Very strong!',
+ icon: '๐ชฒ'
+ },
+ blue_morpho: {
+ name: 'Blue Morpho',
+ rarity: 'rare',
+ habitat: ['rainforest'],
+ season: ['all'],
+ price: 650,
+ catchDifficulty: 0.75,
+ description: 'Brilliant blue wings that shimmer.',
+ icon: '๐ฆ'
+ },
+
+ // EPIC BUGS (1000g-2000g)
+ golden_scarab: {
+ name: 'Golden Scarab',
+ rarity: 'epic',
+ habitat: ['desert', 'pyramid'],
+ season: ['all'],
+ price: 1500,
+ catchDifficulty: 0.85,
+ description: 'Sacred beetle of ancient Egypt. Pure gold shell!',
+ icon: '๐ชฒ'
+ },
+ ghost_moth: {
+ name: 'Ghost Moth',
+ rarity: 'epic',
+ habitat: ['haunted_forest'],
+ season: ['fall'],
+ time: 'night',
+ price: 1200,
+ catchDifficulty: 0.9,
+ description: 'Translucent white. Some say it carries souls...',
+ icon: '๐ฆ'
+ },
+ crystal_dragonfly: {
+ name: 'Crystal Dragonfly',
+ rarity: 'epic',
+ habitat: ['crystal_cave'],
+ season: ['all'],
+ price: 1800,
+ catchDifficulty: 0.85,
+ description: 'Wings made of living crystal. Reflects light beautifully.',
+ icon: '๐ชฐ'
+ },
+ shadow_beetle: {
+ name: 'Shadow Beetle',
+ rarity: 'epic',
+ habitat: ['dark_forest', 'cave'],
+ season: ['all'],
+ time: 'night',
+ price: 1400,
+ catchDifficulty: 0.8,
+ description: 'Black as night. Nearly invisible in darkness.',
+ icon: '๐ชฒ'
+ },
+
+ // LEGENDARY BUGS (3000g-10000g)
+ phoenix_butterfly: {
+ name: 'Phoenix Butterfly',
+ rarity: 'legendary',
+ habitat: ['volcano'],
+ season: ['summer'],
+ price: 5000,
+ catchDifficulty: 0.95,
+ description: 'Wings glow like fire! Reborn from flames.',
+ icon: '๐ฆ'
+ },
+ void_moth: {
+ name: 'Void Moth',
+ rarity: 'legendary',
+ habitat: ['void_dimension'],
+ season: ['all'],
+ time: 'night',
+ price: 10000,
+ catchDifficulty: 0.99,
+ description: 'Exists between dimensions. Touch opens portal to void.',
+ icon: '๐ฆ'
+ },
+ celestial_beetle: {
+ name: 'Celestial Beetle',
+ rarity: 'legendary',
+ habitat: ['sky_island'],
+ season: ['all'],
+ price: 8000,
+ catchDifficulty: 0.97,
+ description: 'Shell contains entire constellations. Flies among stars.',
+ icon: '๐ชฒ'
+ },
+ time_cicada: {
+ name: 'Time Cicada',
+ rarity: 'legendary',
+ habitat: ['chrono_temple'],
+ season: ['all'],
+ price: 7500,
+ catchDifficulty: 0.96,
+ description: 'Exists outside of time. Emerges once per millennium.',
+ icon: '๐ชฐ'
+ }
+ };
+
+ console.log('๐ฆ Bug Catching System initialized!');
+ }
+
+ /**
+ * Equip bug net
+ */
+ equipBugNet(tier) {
+ if (!this.bugNets[tier]) {
+ return { success: false, message: 'Unknown net tier' };
+ }
+
+ this.currentNet = {
+ tier: tier,
+ ...this.bugNets[tier]
+ };
+
+ console.log(`๐ฆ Equipped ${this.currentNet.name}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Bug Net Equipped!',
+ message: `${this.currentNet.name} ready!`,
+ icon: '๐ฆ'
+ });
+
+ return { success: true };
+ }
+
+ /**
+ * Attempt to catch a bug
+ */
+ catchBug(bugId) {
+ if (!this.currentNet) {
+ return { success: false, message: 'No bug net equipped!' };
+ }
+
+ const bug = this.bugs[bugId];
+ if (!bug) {
+ return { success: false, message: 'Unknown bug' };
+ }
+
+ // Calculate catch chance
+ const netBonus = this.currentNet.catchRate;
+ const bugDifficulty = bug.catchDifficulty;
+ const catchChance = netBonus * (1 - bugDifficulty);
+
+ const roll = Math.random();
+ const caught = roll < catchChance;
+
+ if (caught) {
+ // Add to collection
+ if (!this.caughtBugs.has(bugId)) {
+ this.caughtBugs.set(bugId, 0);
+ }
+ this.caughtBugs.set(bugId, this.caughtBugs.get(bugId) + 1);
+
+ // First time catch
+ if (!this.bugAlbum.has(bugId)) {
+ this.bugAlbum.set(bugId, {
+ discoveredAt: new Date(),
+ timesะกaught: 1
+ });
+
+ this.scene.events.emit('notification', {
+ title: 'NEW BUG!',
+ message: `${bug.icon} ${bug.name} added to album!`,
+ icon: '๐'
+ });
+ }
+
+ console.log(`๐ฆ Caught ${bug.name}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Bug Caught!',
+ message: `${bug.icon} ${bug.name} (+${bug.price}g)`,
+ icon: 'โจ'
+ });
+
+ return {
+ success: true,
+ bug: bug,
+ firstTime: !this.bugAlbum.has(bugId)
+ };
+ } else {
+ console.log(`โ ${bug.name} escaped!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Escaped!',
+ message: `${bug.name} got away!`,
+ icon: '๐จ'
+ });
+
+ return { success: false, message: 'Bug escaped!' };
+ }
+ }
+
+ /**
+ * Sell bug
+ */
+ sellBug(bugId, quantity = 1) {
+ const count = this.caughtBugs.get(bugId) || 0;
+
+ if (count < quantity) {
+ return { success: false, message: 'Not enough bugs to sell!' };
+ }
+
+ const bug = this.bugs[bugId];
+ const totalPrice = bug.price * quantity;
+
+ // Remove from inventory
+ this.caughtBugs.set(bugId, count - quantity);
+
+ // Give gold
+ if (this.scene.player) {
+ this.scene.player.gold += totalPrice;
+ }
+
+ console.log(`๐ฐ Sold ${quantity}x ${bug.name} for ${totalPrice}g!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Bugs Sold!',
+ message: `${quantity}x ${bug.name} = ${totalPrice}g`,
+ icon: '๐ฐ'
+ });
+
+ return { success: true, price: totalPrice };
+ }
+
+ /**
+ * Get collection stats
+ */
+ getCollectionStats() {
+ const totalSpecies = Object.keys(this.bugs).length;
+ const discovered = this.bugAlbum.size;
+ const percentage = Math.round((discovered / totalSpecies) * 100);
+
+ return {
+ totalSpecies: totalSpecies,
+ discovered: discovered,
+ percentage: percentage,
+ byRarity: this.getByRarity()
+ };
+ }
+
+ /**
+ * Get bugs by rarity
+ */
+ getByRarity() {
+ const byRarity = {
+ common: { total: 0, caught: 0 },
+ uncommon: { total: 0, caught: 0 },
+ rare: { total: 0, caught: 0 },
+ epic: { total: 0, caught: 0 },
+ legendary: { total: 0, caught: 0 }
+ };
+
+ for (const [bugId, bug] of Object.entries(this.bugs)) {
+ byRarity[bug.rarity].total++;
+ if (this.bugAlbum.has(bugId)) {
+ byRarity[bug.rarity].caught++;
+ }
+ }
+
+ return byRarity;
+ }
+
+ /**
+ * Spawn bugs in world
+ */
+ spawnBugs(biome, season, time, count = 5) {
+ const availableBugs = Object.entries(this.bugs)
+ .filter(([id, bug]) => {
+ // Check habitat
+ if (!bug.habitat.includes(biome) && !bug.habitat.includes('anywhere')) {
+ return false;
+ }
+ // Check season
+ if (!bug.season.includes(season) && !bug.season.includes('all')) {
+ return false;
+ }
+ // Check time
+ if (bug.time && bug.time !== time) {
+ return false;
+ }
+ return true;
+ });
+
+ const spawned = [];
+
+ for (let i = 0; i < count; i++) {
+ if (availableBugs.length === 0) break;
+
+ const [bugId, bug] = availableBugs[Math.floor(Math.random() * availableBugs.length)];
+
+ const x = Math.random() * 800; // Map width
+ const y = Math.random() * 600; // Map height
+
+ spawned.push({
+ id: `bug_${Date.now()}_${i}`,
+ bugId: bugId,
+ x: x,
+ y: y,
+ active: true
+ });
+ }
+
+ this.activeBugs.push(...spawned);
+
+ console.log(`๐ฆ Spawned ${spawned.length} bugs in ${biome}`);
+
+ return spawned;
+ }
+
+ /**
+ * Check collection completion bonuses
+ */
+ checkCompletionBonus() {
+ const stats = this.getCollectionStats();
+
+ // 100% completion
+ if (stats.percentage === 100 && !this.completionBonusGiven) {
+ this.completionBonusGiven = true;
+
+ this.scene.events.emit('notification', {
+ title: '๐ BUG MASTER!',
+ message: 'Completed entire bug collection! +10000g bonus!',
+ icon: '๐'
+ });
+
+ if (this.scene.player) {
+ this.scene.player.gold += 10000;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BuildSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BuildSystem.js
new file mode 100644
index 000000000..43fee22fa
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BuildSystem.js
@@ -0,0 +1,542 @@
+class BuildSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.buildMode = false;
+ this.selectedBuilding = 'fence';
+ this.previewSprite = null;
+ this.placedBuildings = [];
+
+ // Building definitions
+ this.buildings = {
+ 'fence': {
+ name: 'Fence (Old)',
+ textureKey: 'fence_isometric',
+ cost: { wood: 2 },
+ collision: false,
+ scale: 0.3
+ },
+ 'fence_post': {
+ name: 'Fence Post',
+ textureKey: 'fence_post',
+ cost: { wood: 1 },
+ collision: false,
+ scale: 0.2
+ },
+ 'fence_horizontal': {
+ name: 'Fence โ',
+ textureKey: 'fence_horizontal',
+ cost: { wood: 2 },
+ collision: false,
+ scale: 0.2
+ },
+ 'fence_vertical': {
+ name: 'Fence โ',
+ textureKey: 'fence_vertical',
+ cost: { wood: 2 },
+ collision: false,
+ scale: 0.2
+ },
+ 'fence_corner': {
+ name: 'Fence โ',
+ textureKey: 'fence_corner',
+ cost: { wood: 2 },
+ collision: false,
+ scale: 0.2
+ },
+ 'barn': {
+ name: 'Barn',
+ textureKey: 'barn_isometric',
+ cost: { wood: 40, stone: 20 },
+ collision: true,
+ scale: 0.5
+ },
+ 'grave': {
+ name: 'Grave',
+ textureKey: 'grave_zombie',
+ cost: { stone: 10 },
+ collision: false,
+ scale: 0.3
+ },
+ 'farmhouse': {
+ name: 'Farmhouse',
+ textureKey: 'farmhouse_isometric',
+ cost: { wood: 50, stone: 30, gold: 100 },
+ collision: true,
+ scale: 0.5
+ },
+ 'blacksmith': {
+ name: 'Blacksmith',
+ textureKey: 'blacksmith_workshop',
+ cost: { wood: 30, stone: 40, gold: 80 },
+ collision: true,
+ scale: 0.45
+ }
+ };
+ }
+
+ toggleBuildMode() {
+ this.buildMode = !this.buildMode;
+ console.log(`Build Mode: ${this.buildMode ? 'ON' : 'OFF'}`);
+
+ // Show/hide preview
+ if (this.buildMode) {
+ this.createPreview();
+ this.showBuildUI(); // Dodaj UI
+ } else {
+ this.destroyPreview();
+ this.hideBuildUI(); // Skrij UI
+ }
+
+ return this.buildMode;
+ }
+
+ selectBuilding(buildingId) {
+ if (!this.buildings[buildingId]) return;
+ this.selectedBuilding = buildingId;
+
+ // Play UI click sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playUIClick();
+ }
+
+ // Refresh preview
+ if (this.buildMode) {
+ this.destroyPreview();
+ this.createPreview();
+ this.updateBuildUI(); // Posodobi UI
+ }
+ }
+
+ showBuildUI() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Ustvari UI container
+ this.buildUIContainer = uiScene.add.container(width - 250, 100);
+ this.buildUIContainer.setDepth(9999);
+ this.buildUIContainer.setScrollFactor(0);
+
+ // Ozadje panela
+ const bg = uiScene.add.rectangle(0, 0, 220, 400, 0x1a1a2e, 0.95);
+ bg.setStrokeStyle(3, 0x00ff41);
+ this.buildUIContainer.add(bg);
+
+ // Naslov
+ const title = uiScene.add.text(0, -180, '๐๏ธ BUILD MODE', {
+ fontSize: '20px',
+ fontFamily: 'Courier New',
+ color: '#00ff41',
+ fontStyle: 'bold'
+ }).setOrigin(0.5);
+ this.buildUIContainer.add(title);
+
+ // Izbrana stavba
+ this.selectedBuildingText = uiScene.add.text(0, -150, '', {
+ fontSize: '14px',
+ fontFamily: 'Courier New',
+ color: '#ffffff',
+ align: 'center'
+ }).setOrigin(0.5);
+ this.buildUIContainer.add(this.selectedBuildingText);
+
+ // Cena
+ this.costText = uiScene.add.text(0, -120, '', {
+ fontSize: '12px',
+ fontFamily: 'Courier New',
+ color: '#ffaa00',
+ align: 'center'
+ }).setOrigin(0.5);
+ this.buildUIContainer.add(this.costText);
+
+ // Kontrole
+ const controls = [
+ 'โโโโโโโโโโโโโโ',
+ 'CONTROLS:',
+ '',
+ '1 - Fence Post',
+ '2 - Fence โ',
+ '3 - Fence โ',
+ '4 - Fence โ',
+ '5 - Barn',
+ '',
+ 'Click - Place',
+ 'B - Exit',
+ 'โโโโโโโโโโโโโโ'
+ ];
+
+ const controlsText = uiScene.add.text(0, 0, controls.join('\n'), {
+ fontSize: '12px',
+ fontFamily: 'Courier New',
+ color: '#aaaaaa',
+ align: 'center',
+ lineSpacing: 4
+ }).setOrigin(0.5);
+ this.buildUIContainer.add(controlsText);
+
+ // Status
+ this.statusText = uiScene.add.text(0, 170, '', {
+ fontSize: '11px',
+ fontFamily: 'Courier New',
+ color: '#ffffff',
+ align: 'center'
+ }).setOrigin(0.5);
+ this.buildUIContainer.add(this.statusText);
+
+ this.updateBuildUI();
+ }
+
+ updateBuildUI() {
+ if (!this.buildUIContainer) return;
+
+ const building = this.buildings[this.selectedBuilding];
+
+ // Posodobi ime
+ this.selectedBuildingText.setText(`Selected:\n${building.name}`);
+
+ // Posodobi ceno
+ let costStr = 'Cost: ';
+ const costs = [];
+ for (const [resource, amount] of Object.entries(building.cost)) {
+ if (resource === 'gold') {
+ costs.push(`${amount} Gold`);
+ } else {
+ costs.push(`${amount} ${resource}`);
+ }
+ }
+ costStr += costs.join(', ');
+ this.costText.setText(costStr);
+
+ // Preveri vire
+ const hasResources = this.hasResources(building.cost);
+ this.statusText.setText(hasResources ? 'โ
Ready to build' : 'โ Not enough resources');
+ this.statusText.setColor(hasResources ? '#00ff41' : '#ff0000');
+ }
+
+ hideBuildUI() {
+ if (this.buildUIContainer) {
+ this.buildUIContainer.destroy();
+ this.buildUIContainer = null;
+ }
+ }
+
+ createPreview() {
+ const building = this.buildings[this.selectedBuilding];
+ if (!this.scene.textures.exists(building.textureKey)) {
+ console.warn(`Texture not found: ${building.textureKey}`);
+ return;
+ }
+
+ this.previewSprite = this.scene.add.sprite(0, 0, building.textureKey);
+ this.previewSprite.setOrigin(0.5, 1);
+ this.previewSprite.setAlpha(0.6);
+ this.previewSprite.setDepth(10000); // Always on top
+ this.previewSprite.setScale(building.scale || 1.0);
+ }
+
+ destroyPreview() {
+ if (this.previewSprite) {
+ this.previewSprite.destroy();
+ this.previewSprite = null;
+ }
+ }
+
+ update() {
+ if (!this.buildMode || !this.previewSprite) return;
+
+ // Follow mouse
+ const pointer = this.scene.input.activePointer;
+ const worldPoint = this.scene.cameras.main.getWorldPoint(pointer.x, pointer.y);
+ const gridPos = this.scene.iso.toGrid(worldPoint.x, worldPoint.y);
+ const screenPos = this.scene.iso.toScreen(gridPos.x, gridPos.y);
+
+ this.previewSprite.setPosition(screenPos.x, screenPos.y);
+
+ // Check if can place
+ const canPlace = this.canPlaceAt(gridPos.x, gridPos.y);
+ this.previewSprite.setTint(canPlace ? 0x00ff00 : 0xff0000);
+
+ // Place on click
+ if (pointer.isDown && canPlace) {
+ this.placeBuilding(gridPos.x, gridPos.y);
+ }
+ }
+
+ canPlaceAt(gridX, gridY) {
+ // Check if already has building
+ const exists = this.placedBuildings.find(b => b.gridX === gridX && b.gridY === gridY);
+ if (exists) return false;
+
+ // Check terrain (only on farm tiles)
+ if (!this.scene.terrainSystem) return false;
+ const tile = this.scene.terrainSystem.getTile(gridX, gridY);
+ if (!tile || tile.type !== 'farm') return false;
+
+ // Check cost
+ const building = this.buildings[this.selectedBuilding];
+ if (!this.hasResources(building.cost)) return false;
+
+ return true;
+ }
+
+ hasResources(cost) {
+ const inv = this.scene.inventorySystem;
+ if (!inv) return false;
+
+ for (const [resource, amount] of Object.entries(cost)) {
+ if (resource === 'gold') {
+ if (inv.gold < amount) return false;
+ } else {
+ if (!inv.hasItem(resource, amount)) return false;
+ }
+ }
+ return true;
+ }
+
+ placeBuilding(gridX, gridY) {
+ const building = this.buildings[this.selectedBuilding];
+
+ // Consume resources
+ const inv = this.scene.inventorySystem;
+ for (const [resource, amount] of Object.entries(building.cost)) {
+ if (resource === 'gold') {
+ inv.gold -= amount;
+ } else {
+ inv.removeItem(resource, amount);
+ }
+ }
+
+ // Create sprite
+ const screenPos = this.scene.iso.toScreen(gridX, gridY);
+ const sprite = this.scene.add.sprite(screenPos.x, screenPos.y, building.textureKey);
+ sprite.setOrigin(0.5, 1);
+ sprite.setScale(building.scale || 1.0);
+ sprite.setDepth(this.scene.iso.getDepth(gridX, gridY) + 5); // Above terrain
+
+ // Store
+ this.placedBuildings.push({
+ gridX,
+ gridY,
+ type: this.selectedBuilding,
+ sprite,
+ collision: building.collision
+ });
+
+ // Play build sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playBuild();
+ }
+
+ console.log(`๐๏ธ Placed ${building.name} at (${gridX}, ${gridY})`);
+
+ // Update UI
+ this.scene.events.emit('update-inventory');
+ }
+
+ isCollisionAt(gridX, gridY) {
+ const building = this.placedBuildings.find(b => b.gridX === gridX && b.gridY === gridY);
+ return building ? building.collision : false;
+ }
+
+ /**
+ * Roฤno postavi ograjo na natanฤno koordinato.
+ * @param {number} tileX - X koordinata ploลกฤice (0 do 99).
+ * @param {number} tileY - Y koordinata ploลกฤice (0 do 99).
+ * @param {string} fenceType - Tip ograje: 'fence', 'fence_post', 'fence_horizontal', 'fence_vertical', 'fence_corner'
+ * @param {boolean} consumeResources - Ali naj porabi vire (privzeto: false)
+ * @returns {boolean} - True ฤe je bila ograja uspeลกno postavljena
+ */
+ placeSingleFence(tileX, tileY, fenceType = 'fence_horizontal', consumeResources = false) {
+ // 1. Preveri, ali je lokacija znotraj mape
+ if (tileX < 0 || tileX >= 100 || tileY < 0 || tileY >= 100) {
+ console.error(`โ Poskus postavitve ograje izven robov mape: (${tileX}, ${tileY})`);
+ return false;
+ }
+
+ // 2. Preveri, ali tip ograje obstaja
+ if (!this.buildings[fenceType]) {
+ console.error(`โ Neznan tip ograje: ${fenceType}`);
+ return false;
+ }
+
+ // 3. Preveri, ali ลพe obstaja zgradba na tej lokaciji
+ const exists = this.placedBuildings.find(b => b.gridX === tileX && b.gridY === tileY);
+ if (exists) {
+ console.warn(`โ ๏ธ Na lokaciji (${tileX}, ${tileY}) ลพe obstaja zgradba.`);
+ return false;
+ }
+
+ const building = this.buildings[fenceType];
+
+ // 4. Preveri in porabi vire (ฤe je zahtevano)
+ if (consumeResources) {
+ if (!this.hasResources(building.cost)) {
+ console.warn(`โ ๏ธ Ni dovolj virov za postavitev ${building.name}`);
+ return false;
+ }
+
+ const inv = this.scene.inventorySystem;
+ for (const [resource, amount] of Object.entries(building.cost)) {
+ if (resource === 'gold') {
+ inv.gold -= amount;
+ } else {
+ inv.removeItem(resource, amount);
+ }
+ }
+ }
+
+ // 5. Ustvari sprite
+ const screenPos = this.scene.iso.toScreen(tileX, tileY);
+ const sprite = this.scene.add.sprite(screenPos.x, screenPos.y, building.textureKey);
+ sprite.setOrigin(0.5, 1);
+ sprite.setScale(building.scale || 1.0);
+ sprite.setDepth(this.scene.iso.getDepth(tileX, tileY) + 5); // Nad terenom
+
+ // 6. Shrani
+ this.placedBuildings.push({
+ gridX: tileX,
+ gridY: tileY,
+ type: fenceType,
+ sprite,
+ collision: building.collision
+ });
+
+ console.log(`โ
${building.name} postavljena na (${tileX}, ${tileY})`);
+
+ // 7. Posodobi UI (ฤe so bili porabljeni viri)
+ if (consumeResources) {
+ this.scene.events.emit('update-inventory');
+ }
+
+ return true;
+ }
+
+ /**
+ * Postavi linijo ograj med dvema toฤkama.
+ * @param {number} startX - Zaฤetna X koordinata
+ * @param {number} startY - Zaฤetna Y koordinata
+ * @param {number} endX - Konฤna X koordinata
+ * @param {number} endY - Konฤna Y koordinata
+ * @param {string} fenceType - Tip ograje
+ * @param {boolean} consumeResources - Ali naj porabi vire
+ */
+ placeFenceLine(startX, startY, endX, endY, fenceType = 'fence_horizontal', consumeResources = false) {
+ const dx = Math.abs(endX - startX);
+ const dy = Math.abs(endY - startY);
+ const sx = startX < endX ? 1 : -1;
+ const sy = startY < endY ? 1 : -1;
+ let err = dx - dy;
+ let x = startX;
+ let y = startY;
+
+ while (true) {
+ this.placeSingleFence(x, y, fenceType, consumeResources);
+
+ if (x === endX && y === endY) break;
+
+ const e2 = 2 * err;
+ if (e2 > -dy) {
+ err -= dy;
+ x += sx;
+ }
+ if (e2 < dx) {
+ err += dx;
+ y += sy;
+ }
+ }
+
+ console.log(`๐ Linija ograj postavljena od (${startX}, ${startY}) do (${endX}, ${endY})`);
+ }
+
+ /**
+ * Postavi pravokotnik ograj.
+ * @param {number} x - Levi zgornji X
+ * @param {number} y - Levi zgornji Y
+ * @param {number} width - ล irina
+ * @param {number} height - Viลกina
+ * @param {string} fenceType - Tip ograje
+ * @param {boolean} consumeResources - Ali naj porabi vire
+ */
+ placeFenceRectangle(x, y, width, height, fenceType = 'fence_horizontal', consumeResources = false) {
+ // Zgornja linija
+ for (let i = 0; i < width; i++) {
+ this.placeSingleFence(x + i, y, fenceType, consumeResources);
+ }
+ // Spodnja linija
+ for (let i = 0; i < width; i++) {
+ this.placeSingleFence(x + i, y + height - 1, fenceType, consumeResources);
+ }
+ // Leva linija
+ for (let i = 1; i < height - 1; i++) {
+ this.placeSingleFence(x, y + i, fenceType, consumeResources);
+ }
+ // Desna linija
+ for (let i = 1; i < height - 1; i++) {
+ this.placeSingleFence(x + width - 1, y + i, fenceType, consumeResources);
+ }
+
+ console.log(`๐ฆ Pravokotnik ograj postavljen na (${x}, ${y}) velikosti ${width}x${height}`);
+ }
+
+ showTutorial() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Tutorial panel
+ const panel = uiScene.add.container(width / 2, height / 2);
+ panel.setDepth(10000);
+
+ const bg = uiScene.add.rectangle(0, 0, 500, 300, 0x1a1a2e, 0.95);
+ bg.setStrokeStyle(3, 0x00ff41);
+ panel.add(bg);
+
+ const title = uiScene.add.text(0, -120, '๐๏ธ BUILD MODE', {
+ fontSize: '24px',
+ fontFamily: 'Courier New',
+ color: '#00ff41',
+ fontStyle: 'bold'
+ }).setOrigin(0.5);
+ panel.add(title);
+
+ const instructions = [
+ 'Controls:',
+ '1-5: Select building type',
+ 'Mouse: Move preview',
+ 'Click: Place building',
+ 'B: Exit build mode',
+ '',
+ 'Green = OK | Red = Blocked'
+ ];
+
+ const text = uiScene.add.text(0, 0, instructions.join('\n'), {
+ fontSize: '16px',
+ fontFamily: 'Courier New',
+ color: '#ffffff',
+ align: 'center',
+ lineSpacing: 8
+ }).setOrigin(0.5);
+ panel.add(text);
+
+ const closeBtn = uiScene.add.text(0, 120, '[Click to close]', {
+ fontSize: '14px',
+ color: '#888888'
+ }).setOrigin(0.5);
+ panel.add(closeBtn);
+
+ // Auto-dismiss after 5 seconds
+ uiScene.time.delayedCall(5000, () => {
+ panel.destroy();
+ });
+
+ // Click to dismiss
+ bg.setInteractive();
+ bg.on('pointerdown', () => {
+ panel.destroy();
+ });
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BuildingRestorationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BuildingRestorationSystem.js
new file mode 100644
index 000000000..69795b03a
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BuildingRestorationSystem.js
@@ -0,0 +1,73 @@
+
+/**
+ * BuildingRestorationSystem.js
+ * ===========================
+ * Manages the repair and restoration of ruined buildings in Town Square.
+ *
+ * Mechanics:
+ * - Buildings start in 'ruined' state.
+ * - Players contribute materials (Wood, Stone, Scrap).
+ * - Restoration happens in stages.
+ * - Restored buildings unlock NPCs and Services.
+ */
+
+export default class BuildingRestorationSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.buildings = new Map();
+ this._initBuildings();
+ }
+
+ _initBuildings() {
+ // Sample restoration targets
+ this.buildings.set('blacksmith_shop', {
+ name: 'Ivan\'s Blacksmith',
+ state: 'ruined',
+ progress: 0,
+ requirements: { wood: 50, stone: 30, scrap: 10 },
+ npc: 'Ivan Kovaฤ',
+ unlocked: false
+ });
+
+ this.buildings.set('bakery', {
+ name: 'Town Bakery',
+ state: 'ruined',
+ progress: 0,
+ requirements: { wood: 40, stone: 20, scrap: 5 },
+ npc: 'Marija Pekarka',
+ unlocked: false
+ });
+ }
+
+ contribute(buildingId, material, amount) {
+ const b = this.buildings.get(buildingId);
+ if (!b) return false;
+
+ if (b.requirements[material] && b.requirements[material] > 0) {
+ const used = Math.min(amount, b.requirements[material]);
+ b.requirements[material] -= used;
+
+ // Check if all materials are 0
+ const allDone = Object.values(b.requirements).every(val => val <= 0);
+ if (allDone) {
+ this.restore(buildingId);
+ }
+ return used;
+ }
+ return 0;
+ }
+
+ restore(buildingId) {
+ const b = this.buildings.get(buildingId);
+ if (!b) return;
+ b.state = 'restored';
+ b.unlocked = true;
+ b.progress = 100;
+ console.log(`๐ฐ ${b.name} has been restored!`);
+ this.scene.events.emit('building_restored', b);
+ }
+
+ getBuilding(id) {
+ return this.buildings.get(id);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BuildingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BuildingSystem.js
new file mode 100644
index 000000000..95a0c76eb
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BuildingSystem.js
@@ -0,0 +1,167 @@
+class BuildingSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.isBuildMode = false;
+ this.selectedBuilding = 'fence'; // fence, wall, house
+
+ this.buildingsData = {
+ fence: { name: 'Fence', cost: { wood: 2 }, w: 1, h: 1 },
+ wall: { name: 'Stone Wall', cost: { stone: 2 }, w: 1, h: 1 },
+ house: { name: 'House', cost: { wood: 20, stone: 20, gold: 50 }, w: 1, h: 1 }, // Visual is bigger but anchor is 1 tile
+ barn: { name: 'Barn', cost: { wood: 50, stone: 10 }, w: 1, h: 1 },
+ silo: { name: 'Silo', cost: { wood: 30, stone: 30 }, w: 1, h: 1 },
+ stable: { name: 'Stable', cost: { wood: 40, stone: 20 }, w: 1, h: 1 },
+ greenhouse: { name: 'Greenhouse', cost: { glass: 20, wood: 15 }, w: 2, h: 2 } // Winter farming
+ };
+
+ // Textures init
+ if (!this.scene.textures.exists('struct_fence')) TextureGenerator.createStructureSprite(this.scene, 'struct_fence', 'fence');
+ if (!this.scene.textures.exists('struct_wall')) TextureGenerator.createStructureSprite(this.scene, 'struct_wall', 'wall');
+ if (!this.scene.textures.exists('struct_house')) TextureGenerator.createStructureSprite(this.scene, 'struct_house', 'house');
+ if (!this.scene.textures.exists('struct_barn')) TextureGenerator.createStructureSprite(this.scene, 'struct_barn', 'barn');
+ if (this.scene.textures.exists('struct_silo')) TextureGenerator.createStructureSprite(this.scene, 'struct_silo', 'silo');
+ if (!this.scene.textures.exists('struct_stable')) TextureGenerator.createStructureSprite(this.scene, 'struct_stable', 'stable');
+ // House Lv2 Texture
+ TextureGenerator.createStructureSprite(this.scene, 'struct_house_lv2', 'house_lv2');
+ }
+
+ toggleBuildMode() {
+ this.isBuildMode = !this.isBuildMode;
+ console.log(`๐จ Build Mode: ${this.isBuildMode}`);
+
+ // Update UI
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene) {
+ uiScene.toggleBuildMenu(this.isBuildMode);
+ }
+ }
+
+ selectBuilding(type) {
+ if (this.buildingsData[type]) {
+ this.selectedBuilding = type;
+ console.log(`๐จ Selected: ${this.selectedBuilding}`);
+
+ // UI feedback?
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene) uiScene.updateBuildSelection(type);
+ }
+ }
+
+ tryBuild(gridX, gridY) {
+ if (!this.isBuildMode) return false;
+
+ const building = this.buildingsData[this.selectedBuilding];
+ const inv = this.scene.inventorySystem;
+ const terrain = this.scene.terrainSystem;
+
+ // 1. Check Cost
+ if (building.cost.wood) {
+ if (!inv.hasItem('wood', building.cost.wood)) {
+ console.log('โ Not enough Wood!');
+ this.showFloatingText('Need Wood!', gridX, gridY, '#FF0000');
+ return true; // We handled the click, even if failed
+ }
+ }
+ if (building.cost.stone) {
+ if (!inv.hasItem('stone', building.cost.stone)) {
+ console.log('โ Not enough Stone!');
+ this.showFloatingText('Need Stone!', gridX, gridY, '#FF0000');
+ return true;
+ }
+ }
+ if (building.cost.gold) {
+ if (inv.gold < building.cost.gold) {
+ console.log('โ Not enough Gold!');
+ this.showFloatingText('Need Gold!', gridX, gridY, '#FF0000');
+ return true;
+ }
+ }
+
+ // 2. Check Space
+ const tile = terrain.getTile(gridX, gridY);
+ if (!tile || tile.type === 'water' || tile.hasDecoration || tile.hasCrop || tile.hasBuilding) {
+ console.log('โ Space occupied!');
+ this.showFloatingText('Occupied!', gridX, gridY, '#FF0000');
+ return true;
+ }
+
+ // 3. Consume Resources
+ if (building.cost.wood) inv.removeItem('wood', building.cost.wood);
+ if (building.cost.stone) inv.removeItem('stone', building.cost.stone);
+ if (building.cost.gold) {
+ inv.gold -= building.cost.gold;
+ inv.updateUI();
+ }
+
+ // 4. Place Building
+ // Using decorations layer for now
+ const structType = `struct_${this.selectedBuilding}`;
+ terrain.addDecoration(gridX, gridY, structType);
+
+ // Assume success if no error (addDecoration checks internally but doesn't return value easily, but we checked space before)
+ {
+ this.showFloatingText(`Built ${building.name}!`, gridX, gridY, '#00FF00');
+
+ // Build Sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playBuild();
+ }
+
+ // Quest Tracking
+ if (this.scene.questSystem) {
+ this.scene.questSystem.trackAction(`build_${this.selectedBuilding}`);
+ }
+ }
+
+ return true;
+ }
+
+ tryUpgrade(gridX, gridY) {
+ const terrain = this.scene.terrainSystem;
+ const decKey = `${gridX},${gridY}`;
+ const decor = terrain.decorationsMap.get(decKey);
+
+ if (!decor) return false;
+
+ // Check if House Lv1
+ if (decor.type === 'struct_house') {
+ const cost = { wood: 100, stone: 50, gold: 100 };
+ const inv = this.scene.inventorySystem;
+
+ // Check Resources
+ if (!inv.hasItem('wood', cost.wood) || !inv.hasItem('stone', cost.stone) || inv.gold < cost.gold) {
+ this.showFloatingText('Upgrade Cost: 100 Wood, 50 Stone, 100G', gridX, gridY, '#FF4444');
+ return true;
+ }
+
+ // Pay
+ inv.removeItem('wood', cost.wood);
+ inv.removeItem('stone', cost.stone);
+ inv.gold -= cost.gold;
+ inv.updateUI();
+
+ // Perform Upgrade
+ terrain.removeDecoration(gridX, gridY);
+ terrain.addDecoration(gridX, gridY, 'struct_house_lv2'); // Re-add Lv2
+
+ this.showFloatingText('HOUSE CRADED! (Lv. 2)', gridX, gridY, '#00FFFF');
+ if (this.scene.soundManager) this.scene.soundManager.playSuccess();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ showFloatingText(text, gridX, gridY, color) {
+ const iso = new IsometricUtils(48, 24);
+ const pos = iso.toScreen(gridX, gridY);
+ const popup = this.scene.add.text(
+ pos.x + this.scene.terrainOffsetX,
+ pos.y + this.scene.terrainOffsetY - 40,
+ text,
+ { fontSize: '14px', fill: color, stroke: '#000', strokeThickness: 3 }
+ ).setOrigin(0.5);
+ this.scene.tweens.add({ targets: popup, y: popup.y - 30, alpha: 0, duration: 2000, onComplete: () => popup.destroy() });
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BuildingUpgradeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BuildingUpgradeSystem.js
new file mode 100644
index 000000000..1f43ff0ad
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BuildingUpgradeSystem.js
@@ -0,0 +1,680 @@
+/**
+ * โก BUILDING UPGRADE SYSTEM - Week 1 Priority
+ * Generator + Power Grid + Electrician NPC + Maintenance
+ *
+ * Features:
+ * - Generator building (electricity for city)
+ * - Power grid system (poles connecting generator)
+ * - Electrician NPC employment (2 Cekini/day)
+ * - Repair mechanics (generator, poles, UV lights)
+ * - Breakdown prevention when Electrician employed
+ * - Electric sparks VFX + Repair sparkles
+ *
+ * Assets Used:
+ * - /assets/buildings/generator.png
+ * - /assets/buildings/power_pole_straight.png
+ * - /assets/buildings/power_pole_corner.png
+ * - /assets/characters/electrician/ (11 sprites)
+ * - /assets/vfx/electric_spark.png
+ * - /assets/vfx/sparkle_repair.png
+ */
+
+export default class BuildingUpgradeSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Generator system
+ this.generator = null;
+ this.generatorHealth = 100;
+ this.generatorMaxHealth = 100;
+ this.generatorActive = false;
+ this.generatorBreakdownChance = 0.05; // 5% per day without Electrician
+
+ // Power grid
+ this.powerPoles = [];
+ this.isPowered = false;
+
+ // Electrician NPC
+ this.electrician = null;
+ this.electricianEmployed = false;
+ this.electricianSalary = 2; // Cekini per day
+ this.electricianWorkSchedule = {
+ inspectionTime: 10, // 10 AM
+ repairTime: 14 // 2 PM
+ };
+
+ // Repair system
+ this.repairCost = {
+ generator: 50, // Wood
+ powerPole: 20,
+ uvLight: 30
+ };
+
+ // Buildings that need electricity
+ this.electricBuildings = [];
+
+ this.init();
+ }
+
+ init() {
+ console.log('[BuildingUpgrade] Initializing building upgrade system...');
+
+ // Load sprites
+ this.loadSprites();
+
+ // Setup daily maintenance check
+ this.setupMaintenanceRoutine();
+ }
+
+ loadSprites() {
+ // Generator
+ if (!this.scene.textures.exists('generator')) {
+ this.scene.load.image('generator', 'assets/buildings/generator.png');
+ }
+
+ // Power poles
+ if (!this.scene.textures.exists('power_pole_straight')) {
+ this.scene.load.image('power_pole_straight', 'assets/buildings/power_pole_straight.png');
+ }
+ if (!this.scene.textures.exists('power_pole_corner')) {
+ this.scene.load.image('power_pole_corner', 'assets/buildings/power_pole_corner.png');
+ }
+
+ // Electrician sprites
+ const directions = ['south', 'north', 'east', 'west'];
+ const actions = ['idle', 'walk'];
+
+ directions.forEach(dir => {
+ actions.forEach(action => {
+ const key = `electrician_${action}_${dir}`;
+ if (!this.scene.textures.exists(key)) {
+ this.scene.load.image(key, `assets/characters/electrician/${action}_${dir}.png`);
+ }
+ });
+ });
+
+ // Action sprites
+ if (!this.scene.textures.exists('electrician_action_repair')) {
+ this.scene.load.image('electrician_action_repair', 'assets/characters/electrician/action_repair.png');
+ }
+ if (!this.scene.textures.exists('electrician_action_inspect')) {
+ this.scene.load.image('electrician_action_inspect', 'assets/characters/electrician/action_inspect.png');
+ }
+ if (!this.scene.textures.exists('electrician_portrait')) {
+ this.scene.load.image('electrician_portrait', 'assets/characters/electrician/portrait.png');
+ }
+
+ // VFX
+ if (!this.scene.textures.exists('electric_spark')) {
+ this.scene.load.image('electric_spark', 'assets/vfx/electric_spark.png');
+ }
+ if (!this.scene.textures.exists('sparkle_repair')) {
+ this.scene.load.image('sparkle_repair', 'assets/vfx/sparkle_repair.png');
+ }
+
+ this.scene.load.start();
+ }
+
+ /**
+ * Build Generator
+ */
+ buildGenerator(x, y) {
+ if (this.generator) {
+ console.warn('[BuildingUpgrade] Generator already exists!');
+ return false;
+ }
+
+ // Check resources (if resource system exists)
+ const cost = { wood: 100, stone: 50, coal: 20 };
+ if (this.scene.resourceLogisticsSystem) {
+ if (!this.scene.resourceLogisticsSystem.hasResources(cost)) {
+ console.warn('[BuildingUpgrade] Not enough resources to build generator!');
+ return false;
+ }
+
+ // Spend resources
+ Object.entries(cost).forEach(([type, amount]) => {
+ this.scene.resourceLogisticsSystem.removeResource(type, amount);
+ });
+ }
+
+ // Create generator sprite
+ this.generator = this.scene.add.sprite(x, y, 'generator');
+ this.generator.setOrigin(0.5, 0.5);
+ this.generator.setInteractive();
+
+ // Generator data
+ this.generator.buildingData = {
+ type: 'generator',
+ health: this.generatorMaxHealth,
+ active: true,
+ lastMaintenance: Date.now()
+ };
+
+ // Click to interact
+ this.generator.on('pointerdown', () => {
+ this.interactWithGenerator();
+ });
+
+ // Activate generator
+ this.generatorActive = true;
+ this.isPowered = true;
+
+ // Create smoke effect
+ this.createGeneratorSmoke(x, y);
+
+ console.log('[BuildingUpgrade] Generator built at', x, y);
+ return true;
+ }
+
+ /**
+ * Create smoke effect for generator
+ */
+ createGeneratorSmoke(x, y) {
+ // Simple smoke particles
+ const smoke = this.scene.add.particles(x, y - 40, 'electric_spark', {
+ speed: { min: 20, max: 40 },
+ angle: { min: 260, max: 280 },
+ scale: { start: 0.3, end: 0 },
+ alpha: { start: 0.3, end: 0 },
+ lifespan: 2000,
+ frequency: 500,
+ tint: 0x666666,
+ blendMode: 'NORMAL'
+ });
+
+ this.generator.smokeEffect = smoke;
+ }
+
+ /**
+ * Place power pole
+ */
+ placePowerPole(x, y, type = 'straight') {
+ const sprite = type === 'straight' ? 'power_pole_straight' : 'power_pole_corner';
+
+ const pole = this.scene.add.sprite(x, y, sprite);
+ pole.setOrigin(0.5, 1); // Bottom-centered
+ pole.setInteractive();
+
+ pole.poleData = {
+ type: type,
+ health: 100,
+ connected: false
+ };
+
+ this.powerPoles.push(pole);
+
+ // Check connections
+ this.updatePowerGrid();
+
+ console.log('[BuildingUpgrade] Power pole placed at', x, y);
+ return pole;
+ }
+
+ /**
+ * Update power grid connections
+ */
+ updatePowerGrid() {
+ if (!this.generator) {
+ this.isPowered = false;
+ return;
+ }
+
+ // Simple proximity check for now
+ // TODO: Implement proper grid pathfinding
+ this.powerPoles.forEach(pole => {
+ const distance = Phaser.Math.Distance.Between(
+ this.generator.x, this.generator.y,
+ pole.x, pole.y
+ );
+
+ pole.poleData.connected = distance < 500; // 500px max distance
+ });
+
+ this.isPowered = this.generatorActive;
+ }
+
+ /**
+ * Spawn Electrician NPC
+ */
+ spawnElectrician(x, y) {
+ if (this.electrician) {
+ console.warn('[BuildingUpgrade] Electrician already exists!');
+ return;
+ }
+
+ this.electrician = this.scene.add.sprite(x, y, 'electrician_idle_south');
+ this.electrician.setOrigin(0.5, 0.5);
+ this.electrician.setInteractive();
+
+ // NPC data
+ this.electrician.npcData = {
+ name: 'Electrician',
+ type: 'town_worker',
+ employed: false,
+ salary: this.electricianSalary,
+ workLocation: this.generator,
+ skills: ['generator_repair', 'power_grid', 'uv_lights']
+ };
+
+ // Click to hire/talk
+ this.electrician.on('pointerdown', () => {
+ this.interactWithElectrician();
+ });
+
+ // Idle animation
+ this.createElectricianIdleAnimation();
+
+ console.log('[BuildingUpgrade] Electrician spawned at', x, y);
+ }
+
+ /**
+ * Create idle animation for Electrician
+ */
+ createElectricianIdleAnimation() {
+ if (!this.electrician) return;
+
+ this.scene.time.addEvent({
+ delay: 3000,
+ callback: () => {
+ if (this.electrician && !this.electrician.isWorking) {
+ // Look around with tools
+ const directions = ['south', 'east', 'west'];
+ const randomDir = Phaser.Utils.Array.GetRandom(directions);
+ this.electrician.setTexture(`electrician_idle_${randomDir}`);
+ }
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * Interact with Electrician (hire or request repair)
+ */
+ interactWithElectrician() {
+ if (!this.electrician) return;
+
+ if (!this.electricianEmployed) {
+ this.showElectricianEmploymentDialog();
+ } else {
+ this.showElectricianDialog();
+ }
+ }
+
+ /**
+ * Show employment dialog
+ */
+ showElectricianEmploymentDialog() {
+ const dialogText = `Need an electrician?\n\nSalary: ${this.electricianSalary} Cekini/day\n\nI'll maintain your generator, prevent breakdowns, and repair everything for FREE when employed!`;
+
+ if (this.scene.dialogueSystem) {
+ this.scene.dialogueSystem.showDialog({
+ portrait: 'electrician_portrait',
+ name: 'Electrician',
+ text: dialogText,
+ choices: [
+ {
+ text: `Hire (${this.electricianSalary} Cekini/day)`,
+ callback: () => this.hireElectrician()
+ },
+ {
+ text: 'Not now',
+ callback: () => console.log('[BuildingUpgrade] Employment declined')
+ }
+ ]
+ });
+ } else {
+ console.log('[BuildingUpgrade] Employment dialog:', dialogText);
+ // Auto-hire for testing
+ this.hireElectrician();
+ }
+ }
+
+ /**
+ * Hire Electrician
+ */
+ hireElectrician() {
+ this.electricianEmployed = true;
+ this.electrician.npcData.employed = true;
+
+ // Add to worker count
+ if (this.scene.cityManagementSystem) {
+ this.scene.cityManagementSystem.addPopulation('worker', 1);
+ }
+
+ console.log('[BuildingUpgrade] Electrician hired! Salary:', this.electricianSalary, 'Cekini/day');
+
+ // Start work routine
+ this.startElectricianWorkRoutine();
+ }
+
+ /**
+ * Start Electrician's work routine
+ */
+ startElectricianWorkRoutine() {
+ if (!this.electricianEmployed) return;
+
+ // Daily inspection at 10 AM
+ this.scene.time.addEvent({
+ delay: 60000, // Check every minute
+ callback: () => {
+ if (this.scene.timeSystem) {
+ const hour = this.scene.timeSystem.getCurrentHour();
+
+ // Morning inspection
+ if (hour === this.electricianWorkSchedule.inspectionTime) {
+ this.electricianInspectGenerator();
+ }
+
+ // Afternoon repair if needed
+ if (hour === this.electricianWorkSchedule.repairTime) {
+ if (this.generatorHealth < this.generatorMaxHealth) {
+ this.electricianRepairGenerator();
+ }
+ }
+ }
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * Electrician inspects generator
+ */
+ electricianInspectGenerator() {
+ if (!this.electrician || !this.generator) return;
+
+ console.log('[BuildingUpgrade] Electrician inspecting generator...');
+
+ // Walk to generator
+ this.walkElectricianTo(this.generator.x, this.generator.y + 60, () => {
+ // Show inspect action
+ this.electrician.setTexture('electrician_action_inspect');
+ this.electrician.isWorking = true;
+
+ // Check health
+ this.scene.time.delayedCall(2000, () => {
+ console.log(`[BuildingUpgrade] Generator health: ${this.generatorHealth}%`);
+
+ // Return to idle
+ this.electrician.setTexture('electrician_idle_south');
+ this.electrician.isWorking = false;
+ });
+ });
+ }
+
+ /**
+ * Electrician repairs generator
+ */
+ electricianRepairGenerator() {
+ if (!this.electrician || !this.generator) return;
+
+ console.log('[BuildingUpgrade] Electrician repairing generator...');
+
+ // Walk to generator
+ this.walkElectricianTo(this.generator.x, this.generator.y + 60, () => {
+ // Show repair action
+ this.electrician.setTexture('electrician_action_repair');
+ this.electrician.isWorking = true;
+
+ // Play electric sparks VFX
+ this.playElectricSparks(this.generator.x, this.generator.y);
+
+ // Repair after 3 seconds
+ this.scene.time.delayedCall(3000, () => {
+ // Restore health
+ this.generatorHealth = this.generatorMaxHealth;
+ if (this.generator.buildingData) {
+ this.generator.buildingData.health = this.generatorMaxHealth;
+ }
+
+ // Play repair sparkles
+ this.playRepairSparkles(this.generator.x, this.generator.y);
+
+ console.log('[BuildingUpgrade] Generator fully repaired!');
+
+ // Return to idle
+ this.electrician.setTexture('electrician_idle_south');
+ this.electrician.isWorking = false;
+ });
+ });
+ }
+
+ /**
+ * Walk Electrician to location
+ */
+ walkElectricianTo(targetX, targetY, onComplete) {
+ if (!this.electrician) return;
+
+ // Calculate direction
+ const dx = targetX - this.electrician.x;
+ const direction = dx > 0 ? 'east' : 'west';
+
+ // Walk animation
+ this.scene.tweens.add({
+ targets: this.electrician,
+ x: targetX,
+ y: targetY,
+ duration: 2000,
+ ease: 'Linear',
+ onUpdate: () => {
+ this.electrician.setTexture(`electrician_walk_${direction}`);
+ },
+ onComplete: () => {
+ if (onComplete) onComplete();
+ }
+ });
+ }
+
+ /**
+ * Play electric sparks VFX
+ */
+ playElectricSparks(x, y) {
+ if (!this.scene.textures.exists('electric_spark')) return;
+
+ const emitter = this.scene.add.particles(x, y, 'electric_spark', {
+ speed: { min: 100, max: 200 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 0.8, end: 0.2 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 400,
+ quantity: 3,
+ frequency: 100,
+ blendMode: 'ADD',
+ tint: 0x00FFFF // Blue electric
+ });
+
+ // Stop after 3 seconds
+ this.scene.time.delayedCall(3000, () => {
+ emitter.stop();
+ this.scene.time.delayedCall(500, () => emitter.destroy());
+ });
+ }
+
+ /**
+ * Play repair sparkles VFX (4-frame animation!)
+ */
+ playRepairSparkles(x, y) {
+ if (!this.scene.textures.exists('sparkle_repair')) return;
+
+ const emitter = this.scene.add.particles(x, y - 20, 'sparkle_repair', {
+ speed: { min: 20, max: 50 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 1000,
+ quantity: 12,
+ blendMode: 'ADD',
+ tint: 0xFFD700 // Gold sparkles
+ });
+
+ // Destroy after animation
+ this.scene.time.delayedCall(1500, () => {
+ emitter.destroy();
+ });
+ }
+
+ /**
+ * Daily maintenance check
+ */
+ setupMaintenanceRoutine() {
+ this.scene.time.addEvent({
+ delay: 86400000, // Daily (or faster in-game)
+ callback: () => this.performDailyMaintenance(),
+ loop: true
+ });
+ }
+
+ /**
+ * Perform daily maintenance
+ */
+ performDailyMaintenance() {
+ if (!this.generator || !this.generatorActive) return;
+
+ if (!this.electricianEmployed) {
+ // Chance of breakdown without Electrician
+ if (Math.random() < this.generatorBreakdownChance) {
+ this.generatorBreakdown();
+ } else {
+ // Gradual degradation
+ this.generatorHealth -= 10;
+ if (this.generatorHealth < 0) {
+ this.generatorBreakdown();
+ }
+ }
+ } else {
+ // Electrician prevents breakdowns!
+ console.log('[BuildingUpgrade] Electrician prevented breakdown!');
+ }
+ }
+
+ /**
+ * Generator breakdown
+ */
+ generatorBreakdown() {
+ console.warn('[BuildingUpgrade] GENERATOR BREAKDOWN!');
+
+ this.generatorActive = false;
+ this.generatorHealth = 0;
+ this.isPowered = false;
+
+ // Visual feedback
+ if (this.generator && this.generator.smokeEffect) {
+ this.generator.smokeEffect.stop();
+ }
+
+ // Show warning
+ if (this.scene.centralPopupSystem) {
+ this.scene.centralPopupSystem.showMessage(
+ 'GENERATOR BREAKDOWN! Hire an Electrician or repair manually!',
+ 'critical'
+ );
+ }
+
+ // Turn off electric buildings
+ this.powerOffBuildings();
+ }
+
+ /**
+ * Power off all electric buildings
+ */
+ powerOffBuildings() {
+ this.electricBuildings.forEach(building => {
+ if (building.powerOff) {
+ building.powerOff();
+ }
+ });
+ }
+
+ /**
+ * Interact with generator
+ */
+ interactWithGenerator() {
+ if (!this.generator) return;
+
+ const health = this.generatorHealth;
+ const status = this.generatorActive ? 'ACTIVE' : 'OFFLINE';
+
+ const info = `
+=== GENERATOR STATUS ===
+Status: ${status}
+Health: ${health}/${this.generatorMaxHealth}
+Power: ${this.isPowered ? 'ONLINE' : 'OFFLINE'}
+Maintenance: ${this.electricianEmployed ? 'Automatic' : 'Manual'}
+ `;
+
+ console.log(info);
+
+ if (this.scene.centralPopupSystem) {
+ this.scene.centralPopupSystem.showMessage(info, 'info');
+ }
+ }
+
+ /**
+ * Show Electrician dialogue
+ */
+ showElectricianDialog() {
+ const dialogues = [
+ "Generator's running smooth. No problems here.",
+ "Just did my daily inspection. All systems nominal.",
+ "Power grid is stable. Good work building it.",
+ "I'll keep everything running. Don't worry.",
+ "Found a loose wire this morning. Fixed it.",
+ "UV lights in the basement need checking soon."
+ ];
+
+ const randomDialogue = Phaser.Utils.Array.GetRandom(dialogues);
+
+ if (this.scene.dialogueSystem) {
+ this.scene.dialogueSystem.showDialog({
+ portrait: 'electrician_portrait',
+ name: 'Electrician',
+ text: randomDialogue
+ });
+ } else {
+ console.log('[BuildingUpgrade] Electrician:', randomDialogue);
+ }
+ }
+
+ /**
+ * Register electric building
+ */
+ registerElectricBuilding(building) {
+ this.electricBuildings.push(building);
+ }
+
+ /**
+ * Check if power is available
+ */
+ hasPower() {
+ return this.isPowered;
+ }
+
+ /**
+ * Pay daily salaries
+ */
+ payDailySalaries() {
+ if (!this.electricianEmployed) return;
+
+ if (this.scene.economySystem) {
+ this.scene.economySystem.spendCekini(this.electricianSalary);
+ console.log(`[BuildingUpgrade] Paid Electrician ${this.electricianSalary} Cekini`);
+ }
+ }
+
+ update(time, delta) {
+ // System updates handled by time events
+ }
+
+ destroy() {
+ if (this.generator) {
+ if (this.generator.smokeEffect) this.generator.smokeEffect.destroy();
+ this.generator.destroy();
+ }
+
+ this.powerPoles.forEach(pole => pole.destroy());
+ this.powerPoles = [];
+
+ if (this.electrician) this.electrician.destroy();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/BuildingVisualsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/BuildingVisualsSystem.js
new file mode 100644
index 000000000..7dd5cae2a
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/BuildingVisualsSystem.js
@@ -0,0 +1,634 @@
+/**
+ * BUILDING VISUALS & GENETICS SYSTEM
+ * Advanced animations for farm automation buildings and genetics lab
+ */
+class BuildingVisualsSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Building animations
+ this.buildings = new Map();
+ this.conveyorBelts = [];
+ this.windmills = [];
+ this.silos = [];
+
+ // Genetics lab
+ this.geneticsLab = null;
+ this.dnaHelixAnimation = null;
+ this.mutationVats = [];
+
+ // Settings
+ this.settings = {
+ buildingAnimations: true,
+ particleEffects: true,
+ geneticsUI: true,
+ animationSpeed: 1.0
+ };
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Building Visuals System initialized');
+ }
+
+ init() {
+ console.log('๐ญ Building animations ready');
+ }
+
+ // ========== AUTO-PLANTER ==========
+
+ createAutoPlanter(x, y) {
+ if (!this.settings.buildingAnimations) return null;
+
+ const planter = {
+ x, y,
+ arm: null,
+ seed: null,
+ isPlanting: false
+ };
+
+ // Create mechanical arm
+ const arm = this.scene.add.graphics();
+ arm.lineStyle(4, 0x888888, 1);
+ arm.lineBetween(0, 0, 0, 30); // Vertical arm
+ arm.lineBetween(0, 30, 20, 30); // Horizontal extension
+ arm.setPosition(x, y);
+ arm.setDepth(100);
+ planter.arm = arm;
+
+ this.buildings.set(`planter_${x}_${y}`, planter);
+ return planter;
+ }
+
+ animatePlanting(planter, targetX, targetY) {
+ if (!planter || planter.isPlanting) return;
+
+ planter.isPlanting = true;
+
+ // Arm extends down
+ this.scene.tweens.add({
+ targets: planter.arm,
+ y: planter.y + 20,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ // Drop seed
+ this.createSeedParticle(targetX, targetY);
+
+ // Arm retracts
+ this.scene.tweens.add({
+ targets: planter.arm,
+ y: planter.y,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ planter.isPlanting = false;
+ }
+ });
+ }
+ });
+ }
+
+ createSeedParticle(x, y) {
+ const seed = this.scene.add.circle(x, y - 20, 3, 0x8B4513);
+ seed.setDepth(99);
+
+ this.scene.tweens.add({
+ targets: seed,
+ y: y,
+ duration: 300,
+ ease: 'Bounce.easeOut',
+ onComplete: () => {
+ // Puff of dirt
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(x, y);
+ }
+ seed.destroy();
+ }
+ });
+ }
+
+ // ========== AUTO-HARVESTER ==========
+
+ createAutoHarvester(x, y) {
+ if (!this.settings.buildingAnimations) return null;
+
+ const harvester = {
+ x, y,
+ blades: [],
+ isHarvesting: false,
+ rotation: 0
+ };
+
+ // Create spinning blades
+ for (let i = 0; i < 4; i++) {
+ const blade = this.scene.add.graphics();
+ blade.lineStyle(3, 0xC0C0C0, 1);
+ blade.lineBetween(0, 0, 15, 0);
+ blade.setPosition(x, y);
+ blade.setRotation((Math.PI / 2) * i);
+ blade.setDepth(100);
+ harvester.blades.push(blade);
+ }
+
+ this.buildings.set(`harvester_${x}_${y}`, harvester);
+ return harvester;
+ }
+
+ animateHarvesting(harvester, cropX, cropY) {
+ if (!harvester || harvester.isHarvesting) return;
+
+ harvester.isHarvesting = true;
+
+ // Spin blades
+ for (const blade of harvester.blades) {
+ this.scene.tweens.add({
+ targets: blade,
+ rotation: blade.rotation + Math.PI * 4,
+ duration: 1000,
+ ease: 'Linear'
+ });
+ }
+
+ // Move to crop
+ this.scene.tweens.add({
+ targets: harvester.blades,
+ x: cropX,
+ y: cropY,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ // Harvest effect
+ this.createHarvestEffect(cropX, cropY);
+
+ // Return to base
+ this.scene.tweens.add({
+ targets: harvester.blades,
+ x: harvester.x,
+ y: harvester.y,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ harvester.isHarvesting = false;
+ }
+ });
+ }
+ });
+ }
+
+ createHarvestEffect(x, y) {
+ // Crop particles
+ const emitter = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 30, max: 60 },
+ scale: { start: 0.5, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 800,
+ quantity: 10,
+ tint: 0xFFD700
+ });
+
+ this.scene.time.delayedCall(800, () => emitter.destroy());
+ }
+
+ // ========== CONVEYOR BELT ==========
+
+ createConveyorBelt(x, y, width, direction = 'right') {
+ if (!this.settings.buildingAnimations) return null;
+
+ const belt = {
+ x, y, width, direction,
+ items: [],
+ speed: 50 // pixels per second
+ };
+
+ // Create belt graphics
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0x444444, 1);
+ graphics.fillRect(x, y, width, 20);
+
+ // Moving lines to show direction
+ for (let i = 0; i < width; i += 20) {
+ const line = this.scene.add.rectangle(
+ x + i,
+ y + 10,
+ 10,
+ 2,
+ 0x888888
+ );
+ line.setDepth(99);
+
+ // Animate line movement
+ const targetX = direction === 'right' ? x + width : x;
+ this.scene.tweens.add({
+ targets: line,
+ x: targetX,
+ duration: (width / belt.speed) * 1000,
+ repeat: -1,
+ ease: 'Linear'
+ });
+ }
+
+ belt.graphics = graphics;
+ this.conveyorBelts.push(belt);
+ return belt;
+ }
+
+ addItemToBelt(belt, itemSprite) {
+ if (!belt) return;
+
+ belt.items.push(itemSprite);
+ itemSprite.setPosition(belt.x, belt.y + 10);
+ itemSprite.setDepth(100);
+
+ // Move item along belt
+ const targetX = belt.direction === 'right'
+ ? belt.x + belt.width
+ : belt.x;
+
+ this.scene.tweens.add({
+ targets: itemSprite,
+ x: targetX,
+ duration: (belt.width / belt.speed) * 1000,
+ ease: 'Linear',
+ onComplete: () => {
+ // Remove from belt
+ const index = belt.items.indexOf(itemSprite);
+ if (index > -1) {
+ belt.items.splice(index, 1);
+ }
+ }
+ });
+ }
+
+ // ========== WINDMILL ==========
+
+ createWindmill(x, y) {
+ if (!this.settings.buildingAnimations) return null;
+
+ const windmill = {
+ x, y,
+ blades: null,
+ powerGlow: null,
+ isPowered: false,
+ rotation: 0
+ };
+
+ // Create blades
+ const blades = this.scene.add.graphics();
+ blades.lineStyle(4, 0x8B4513, 1);
+
+ // Draw 4 blades
+ for (let i = 0; i < 4; i++) {
+ const angle = (Math.PI / 2) * i;
+ const endX = Math.cos(angle) * 30;
+ const endY = Math.sin(angle) * 30;
+ blades.lineBetween(0, 0, endX, endY);
+ }
+
+ blades.setPosition(x, y);
+ blades.setDepth(100);
+ windmill.blades = blades;
+
+ // Power glow
+ const glow = this.scene.add.circle(x, y, 40, 0x00ffff, 0);
+ glow.setBlendMode(Phaser.BlendModes.ADD);
+ glow.setDepth(99);
+ windmill.powerGlow = glow;
+
+ // Rotate blades
+ this.scene.tweens.add({
+ targets: blades,
+ rotation: Math.PI * 2,
+ duration: 3000 / this.settings.animationSpeed,
+ repeat: -1,
+ ease: 'Linear'
+ });
+
+ this.windmills.push(windmill);
+ return windmill;
+ }
+
+ setPowerState(windmill, powered) {
+ if (!windmill) return;
+
+ windmill.isPowered = powered;
+
+ if (powered) {
+ // Glow on
+ this.scene.tweens.add({
+ targets: windmill.powerGlow,
+ alpha: 0.4,
+ duration: 500
+ });
+
+ // Particle trail
+ if (this.settings.particleEffects) {
+ const emitter = this.scene.add.particles(
+ windmill.x,
+ windmill.y,
+ 'particle_white',
+ {
+ speed: 20,
+ scale: { start: 0.3, end: 0 },
+ alpha: { start: 0.6, end: 0 },
+ lifespan: 1000,
+ frequency: 200,
+ quantity: 1,
+ tint: 0x00ffff
+ }
+ );
+ windmill.particleEmitter = emitter;
+ }
+ } else {
+ // Glow off
+ this.scene.tweens.add({
+ targets: windmill.powerGlow,
+ alpha: 0,
+ duration: 500
+ });
+
+ // Stop particles
+ if (windmill.particleEmitter) {
+ windmill.particleEmitter.destroy();
+ windmill.particleEmitter = null;
+ }
+ }
+ }
+
+ // ========== STORAGE SILO ==========
+
+ createStorageSilo(x, y, capacity = 1000) {
+ if (!this.settings.buildingAnimations) return null;
+
+ const silo = {
+ x, y,
+ capacity,
+ currentAmount: 0,
+ fillIndicator: null,
+ fillBar: null
+ };
+
+ // Silo structure
+ const structure = this.scene.add.graphics();
+ structure.fillStyle(0x666666, 1);
+ structure.fillRect(x - 20, y - 60, 40, 60);
+ structure.fillCircle(x, y - 60, 20);
+ structure.setDepth(100);
+ silo.structure = structure;
+
+ // Fill indicator (inside silo)
+ const fillBar = this.scene.add.rectangle(
+ x,
+ y,
+ 36,
+ 0,
+ 0xFFD700,
+ 0.8
+ );
+ fillBar.setOrigin(0.5, 1);
+ fillBar.setDepth(101);
+ silo.fillBar = fillBar;
+
+ // Percentage text
+ const text = this.scene.add.text(x, y - 70, '0%', {
+ fontSize: '12px',
+ color: '#ffffff',
+ fontStyle: 'bold'
+ });
+ text.setOrigin(0.5);
+ text.setDepth(102);
+ silo.text = text;
+
+ this.silos.push(silo);
+ return silo;
+ }
+
+ updateSiloFill(silo, amount) {
+ if (!silo) return;
+
+ silo.currentAmount = Phaser.Math.Clamp(amount, 0, silo.capacity);
+ const percentage = (silo.currentAmount / silo.capacity) * 100;
+ const fillHeight = (percentage / 100) * 56; // Max height
+
+ // Animate fill
+ this.scene.tweens.add({
+ targets: silo.fillBar,
+ height: fillHeight,
+ duration: 500,
+ ease: 'Power2'
+ });
+
+ // Update text
+ silo.text.setText(`${Math.round(percentage)}%`);
+ }
+
+ // ========== GENETICS LAB ==========
+
+ createGeneticsLab(x, y) {
+ if (!this.settings.geneticsUI) return null;
+
+ const lab = {
+ x, y,
+ dnaHelix: null,
+ vats: [],
+ isActive: false
+ };
+
+ // Lab structure
+ const structure = this.scene.add.rectangle(x, y, 80, 60, 0x333333);
+ structure.setStrokeStyle(2, 0x00ff00);
+ structure.setDepth(100);
+ lab.structure = structure;
+
+ this.geneticsLab = lab;
+ return lab;
+ }
+
+ showDNAHelixAnimation(x, y) {
+ if (!this.settings.geneticsUI) return null;
+
+ // Create DNA helix
+ const helix = this.scene.add.container(x, y);
+ helix.setDepth(200);
+
+ // Two strands
+ const strand1 = [];
+ const strand2 = [];
+
+ for (let i = 0; i < 20; i++) {
+ const yPos = i * 10 - 100;
+ const angle = (i / 20) * Math.PI * 4;
+
+ const x1 = Math.cos(angle) * 15;
+ const x2 = Math.cos(angle + Math.PI) * 15;
+
+ const dot1 = this.scene.add.circle(x1, yPos, 3, 0x00ff00);
+ const dot2 = this.scene.add.circle(x2, yPos, 3, 0x00ffff);
+
+ helix.add(dot1);
+ helix.add(dot2);
+
+ strand1.push(dot1);
+ strand2.push(dot2);
+
+ // Connecting lines
+ if (i % 3 === 0) {
+ const line = this.scene.add.graphics();
+ line.lineStyle(1, 0xffffff, 0.5);
+ line.lineBetween(x1, yPos, x2, yPos);
+ helix.add(line);
+ }
+ }
+
+ // Rotate helix
+ this.scene.tweens.add({
+ targets: helix,
+ rotation: Math.PI * 2,
+ duration: 4000,
+ repeat: -1,
+ ease: 'Linear'
+ });
+
+ // Pulse effect
+ this.scene.tweens.add({
+ targets: helix,
+ scale: { from: 1, to: 1.1 },
+ duration: 2000,
+ yoyo: true,
+ repeat: -1
+ });
+
+ this.dnaHelixAnimation = helix;
+ return helix;
+ }
+
+ hideDNAHelixAnimation() {
+ if (this.dnaHelixAnimation) {
+ this.dnaHelixAnimation.destroy();
+ this.dnaHelixAnimation = null;
+ }
+ }
+
+ createMutationVat(x, y) {
+ if (!this.settings.geneticsUI) return null;
+
+ const vat = {
+ x, y,
+ container: null,
+ liquid: null,
+ bubbles: null,
+ isActive: false
+ };
+
+ // Vat container
+ const container = this.scene.add.graphics();
+ container.fillStyle(0x444444, 1);
+ container.fillRect(x - 20, y - 40, 40, 40);
+ container.lineStyle(2, 0x666666);
+ container.strokeRect(x - 20, y - 40, 40, 40);
+ container.setDepth(100);
+ vat.container = container;
+
+ // Liquid
+ const liquid = this.scene.add.rectangle(
+ x,
+ y - 20,
+ 36,
+ 36,
+ 0x00ff00,
+ 0.6
+ );
+ liquid.setDepth(101);
+ vat.liquid = liquid;
+
+ this.mutationVats.push(vat);
+ return vat;
+ }
+
+ activateMutationVat(vat) {
+ if (!vat || vat.isActive) return;
+
+ vat.isActive = true;
+
+ // Bubbling effect
+ const emitter = this.scene.add.particles(
+ vat.x,
+ vat.y - 40,
+ 'particle_white',
+ {
+ speedY: { min: -30, max: -50 },
+ speedX: { min: -5, max: 5 },
+ scale: { start: 0.2, end: 0 },
+ alpha: { start: 0.8, end: 0 },
+ lifespan: 1000,
+ frequency: 200,
+ quantity: 2,
+ tint: 0x00ff00
+ }
+ );
+ vat.bubbles = emitter;
+
+ // Lightning effect
+ if (this.settings.particleEffects) {
+ this.createLightningEffect(vat.x, vat.y - 60);
+ }
+
+ // Liquid glow pulse
+ this.scene.tweens.add({
+ targets: vat.liquid,
+ alpha: { from: 0.6, to: 0.9 },
+ duration: 800,
+ yoyo: true,
+ repeat: -1
+ });
+ }
+
+ createLightningEffect(x, y) {
+ const lightning = this.scene.add.graphics();
+ lightning.lineStyle(2, 0xffff00, 1);
+
+ // Zigzag lightning
+ let currentX = x;
+ let currentY = y;
+
+ for (let i = 0; i < 5; i++) {
+ const nextX = currentX + (Math.random() - 0.5) * 20;
+ const nextY = currentY + 10;
+ lightning.lineBetween(currentX, currentY, nextX, nextY);
+ currentX = nextX;
+ currentY = nextY;
+ }
+
+ lightning.setDepth(200);
+
+ // Flash and fade
+ this.scene.tweens.add({
+ targets: lightning,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => lightning.destroy()
+ });
+ }
+
+ // ========== SETTINGS ==========
+
+ saveSettings() {
+ localStorage.setItem('novafarma_building_visuals', JSON.stringify(this.settings));
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_building_visuals');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ destroy() {
+ if (this.dnaHelixAnimation) this.dnaHelixAnimation.destroy();
+ for (const vat of this.mutationVats) {
+ if (vat.bubbles) vat.bubbles.destroy();
+ }
+ console.log('๐ญ Building Visuals System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CameraSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CameraSystem.js
new file mode 100644
index 000000000..75e0f1f95
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CameraSystem.js
@@ -0,0 +1,320 @@
+class CameraSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.camera = scene.cameras.main;
+
+ // Camera modes
+ this.mode = 'follow'; // 'follow', 'free', 'cinematic', 'screenshot'
+
+ // Free camera controls
+ this.freeCamSpeed = 5;
+ this.freeCamZoom = 1.0;
+
+ // Cinematic mode
+ this.cinematicPath = [];
+ this.cinematicIndex = 0;
+ this.cinematicPlaying = false;
+
+ // Screenshot mode
+ this.screenshotMode = false;
+ this.uiHidden = false;
+
+ // Saved camera positions
+ this.savedPositions = [];
+
+ this.setupControls();
+
+ console.log('๐ท CameraSystem: Initialized');
+ }
+
+ setupControls() {
+ // F6 - Free Camera Mode
+ this.scene.input.keyboard.on('keydown-F6', () => {
+ this.toggleFreeCamera();
+ });
+
+ // F7 - Screenshot Mode (hide UI)
+ this.scene.input.keyboard.on('keydown-F7', () => {
+ this.toggleScreenshotMode();
+ });
+
+ // F8 - Save Camera Position
+ this.scene.input.keyboard.on('keydown-F8', () => {
+ this.saveCameraPosition();
+ });
+
+ // F9 - Cinematic Mode
+ this.scene.input.keyboard.on('keydown-F10', () => {
+ this.toggleCinematicMode();
+ });
+
+ // Arrow keys for free camera
+ this.cursors = this.scene.input.keyboard.createCursorKeys();
+
+ // Page Up/Down for zoom
+ this.scene.input.keyboard.on('keydown-PAGEUP', () => {
+ this.adjustZoom(0.1);
+ });
+
+ this.scene.input.keyboard.on('keydown-PAGEDOWN', () => {
+ this.adjustZoom(-0.1);
+ });
+ }
+
+ // FREE CAMERA MODE
+ toggleFreeCamera() {
+ if (this.mode === 'free') {
+ this.mode = 'follow';
+ this.camera.startFollow(this.scene.player.sprite);
+ console.log('๐ท Camera: Follow mode');
+ } else {
+ this.mode = 'free';
+ this.camera.stopFollow();
+ console.log('๐ท Camera: Free mode (Arrow keys to move, PgUp/PgDn to zoom)');
+ }
+ }
+
+ updateFreeCamera(delta) {
+ if (this.mode !== 'free') return;
+
+ const speed = this.freeCamSpeed * (delta / 16);
+
+ if (this.cursors.left.isDown) {
+ this.camera.scrollX -= speed;
+ }
+ if (this.cursors.right.isDown) {
+ this.camera.scrollX += speed;
+ }
+ if (this.cursors.up.isDown) {
+ this.camera.scrollY -= speed;
+ }
+ if (this.cursors.down.isDown) {
+ this.camera.scrollY += speed;
+ }
+ }
+
+ adjustZoom(delta) {
+ this.freeCamZoom = Phaser.Math.Clamp(this.freeCamZoom + delta, 0.3, 3.0);
+ this.camera.setZoom(this.freeCamZoom);
+ console.log(`๐ท Zoom: ${this.freeCamZoom.toFixed(2)}x`);
+ }
+
+ // SCREENSHOT MODE
+ toggleScreenshotMode() {
+ this.screenshotMode = !this.screenshotMode;
+
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ if (this.screenshotMode) {
+ // Hide all UI
+ uiScene.children.list.forEach(child => {
+ if (child.setVisible) {
+ child.wasVisible = child.visible;
+ child.setVisible(false);
+ }
+ });
+ this.uiHidden = true;
+ console.log('๐ท Screenshot mode: UI hidden (F7 to restore)');
+ } else {
+ // Restore UI
+ uiScene.children.list.forEach(child => {
+ if (child.setVisible && child.wasVisible !== undefined) {
+ child.setVisible(child.wasVisible);
+ }
+ });
+ this.uiHidden = false;
+ console.log('๐ท Screenshot mode: UI restored');
+ }
+ }
+
+ // SAVE CAMERA POSITION
+ saveCameraPosition() {
+ const pos = {
+ x: this.camera.scrollX,
+ y: this.camera.scrollY,
+ zoom: this.camera.zoom
+ };
+
+ this.savedPositions.push(pos);
+ console.log(`๐ท Camera position saved (${this.savedPositions.length}): x=${pos.x.toFixed(0)}, y=${pos.y.toFixed(0)}, zoom=${pos.zoom.toFixed(2)}`);
+ }
+
+ loadCameraPosition(index) {
+ if (index < 0 || index >= this.savedPositions.length) return;
+
+ const pos = this.savedPositions[index];
+
+ this.scene.tweens.add({
+ targets: this.camera,
+ scrollX: pos.x,
+ scrollY: pos.y,
+ zoom: pos.zoom,
+ duration: 1000,
+ ease: 'Sine.easeInOut'
+ });
+
+ console.log(`๐ท Camera position loaded (${index + 1})`);
+ }
+
+ // CINEMATIC MODE
+ toggleCinematicMode() {
+ if (this.cinematicPlaying) {
+ this.stopCinematic();
+ } else {
+ this.startCinematic();
+ }
+ }
+
+ startCinematic() {
+ if (this.savedPositions.length < 2) {
+ console.log('๐ท Need at least 2 saved positions for cinematic!');
+ return;
+ }
+
+ this.cinematicPlaying = true;
+ this.cinematicIndex = 0;
+ this.mode = 'cinematic';
+
+ this.playCinematicStep();
+
+ console.log('๐ท Cinematic mode: Playing...');
+ }
+
+ playCinematicStep() {
+ if (!this.cinematicPlaying) return;
+ if (this.cinematicIndex >= this.savedPositions.length) {
+ this.stopCinematic();
+ return;
+ }
+
+ const pos = this.savedPositions[this.cinematicIndex];
+
+ this.scene.tweens.add({
+ targets: this.camera,
+ scrollX: pos.x,
+ scrollY: pos.y,
+ zoom: pos.zoom,
+ duration: 3000,
+ ease: 'Sine.easeInOut',
+ onComplete: () => {
+ this.cinematicIndex++;
+ this.scene.time.delayedCall(500, () => {
+ this.playCinematicStep();
+ });
+ }
+ });
+ }
+
+ stopCinematic() {
+ this.cinematicPlaying = false;
+ this.mode = 'follow';
+ this.camera.startFollow(this.scene.player.sprite);
+ console.log('๐ท Cinematic mode: Stopped');
+ }
+
+ // PRESET CAMERA ANGLES
+ setPresetAngle(preset) {
+ const presets = {
+ 'overview': { zoom: 0.5, y: -200 },
+ 'closeup': { zoom: 1.5, y: 0 },
+ 'wide': { zoom: 0.3, y: -300 },
+ 'action': { zoom: 1.2, y: -50 }
+ };
+
+ const p = presets[preset];
+ if (!p) return;
+
+ this.scene.tweens.add({
+ targets: this.camera,
+ zoom: p.zoom,
+ scrollY: this.camera.scrollY + p.y,
+ duration: 1000,
+ ease: 'Sine.easeInOut'
+ });
+
+ console.log(`๐ท Preset angle: ${preset}`);
+ }
+
+ // SHAKE EFFECTS
+ shake(intensity = 0.005, duration = 200) {
+ this.camera.shake(duration, intensity);
+ }
+
+ // FLASH EFFECTS
+ flash(color = 0xffffff, duration = 200) {
+ this.camera.flash(duration,
+ (color >> 16) & 0xff,
+ (color >> 8) & 0xff,
+ color & 0xff
+ );
+ }
+
+ // FADE EFFECTS
+ fadeOut(duration = 1000, callback) {
+ this.camera.fadeOut(duration, 0, 0, 0);
+ if (callback) {
+ this.camera.once('camerafadeoutcomplete', callback);
+ }
+ }
+
+ fadeIn(duration = 1000, callback) {
+ this.camera.fadeIn(duration, 0, 0, 0);
+ if (callback) {
+ this.camera.once('camerafadeincomplete', callback);
+ }
+ }
+
+ // PAN TO LOCATION
+ panTo(x, y, duration = 2000) {
+ this.scene.tweens.add({
+ targets: this.camera,
+ scrollX: x - this.camera.width / 2,
+ scrollY: y - this.camera.height / 2,
+ duration: duration,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ // ZOOM TO
+ zoomTo(zoom, duration = 1000) {
+ this.scene.tweens.add({
+ targets: this.camera,
+ zoom: zoom,
+ duration: duration,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ // UPDATE
+ update(delta) {
+ this.updateFreeCamera(delta);
+ }
+
+ // EXPORT CAMERA DATA (for trailer editing)
+ exportCameraData() {
+ const data = {
+ savedPositions: this.savedPositions,
+ timestamp: Date.now()
+ };
+
+ const json = JSON.stringify(data, null, 2);
+ console.log('๐ท Camera data:', json);
+
+ // Copy to clipboard (if available)
+ if (navigator.clipboard) {
+ navigator.clipboard.writeText(json);
+ console.log('๐ท Camera data copied to clipboard!');
+ }
+
+ return data;
+ }
+
+ // IMPORT CAMERA DATA
+ importCameraData(data) {
+ if (data.savedPositions) {
+ this.savedPositions = data.savedPositions;
+ console.log(`๐ท Imported ${this.savedPositions.length} camera positions`);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CentralPopupSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CentralPopupSystem.js
new file mode 100644
index 000000000..3af008acf
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CentralPopupSystem.js
@@ -0,0 +1,359 @@
+/**
+ * CENTRAL POPUP SYSTEM
+ * Displays important messages, quests, and dialogs in center of screen
+ * Always appears in front of player for easy reading
+ */
+class CentralPopupSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Current popup
+ this.currentPopup = null;
+ this.popupQueue = [];
+ this.isShowing = false;
+
+ // Settings
+ this.settings = {
+ enabled: true,
+ autoClose: true,
+ autoCloseDelay: 5000, // 5 seconds
+ pauseGameOnPopup: true,
+ soundOnPopup: true
+ };
+
+ this.loadSettings();
+ console.log('โ
Central Popup System initialized');
+ }
+
+ /**
+ * Show popup in center of screen
+ */
+ showPopup(config) {
+ const popup = {
+ title: config.title || 'Notification',
+ message: config.message || '',
+ type: config.type || 'info', // info, quest, warning, success, story
+ icon: config.icon || '๐',
+ buttons: config.buttons || [{ text: 'OK', action: 'close' }],
+ image: config.image || null,
+ autoClose: config.autoClose !== undefined ? config.autoClose : this.settings.autoClose,
+ onClose: config.onClose || null,
+ priority: config.priority || 'normal' // low, normal, high, critical
+ };
+
+ // Add to queue
+ this.popupQueue.push(popup);
+
+ // Show if not already showing
+ if (!this.isShowing) {
+ this.displayNextPopup();
+ }
+ }
+
+ /**
+ * Display next popup from queue
+ */
+ displayNextPopup() {
+ if (this.popupQueue.length === 0) {
+ this.isShowing = false;
+ return;
+ }
+
+ this.isShowing = true;
+ const popup = this.popupQueue.shift();
+
+ // Get UIScene
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ // Pause game if needed
+ if (this.settings.pauseGameOnPopup) {
+ this.scene.physics.pause();
+ }
+
+ // Create popup container
+ const centerX = uiScene.scale.width / 2;
+ const centerY = uiScene.scale.height / 2;
+
+ this.currentPopup = uiScene.add.container(centerX, centerY);
+ this.currentPopup.setDepth(10000); // Always on top
+ this.currentPopup.setScrollFactor(0);
+
+ // Popup size based on type
+ const width = popup.type === 'story' ? 600 : 500;
+ const height = popup.type === 'story' ? 400 : 300;
+
+ // Background overlay (darken screen)
+ const overlay = uiScene.add.rectangle(0, 0, uiScene.scale.width * 2, uiScene.scale.height * 2, 0x000000, 0.7);
+ overlay.setOrigin(0.5);
+ this.currentPopup.add(overlay);
+
+ // Main popup background
+ const bg = uiScene.add.graphics();
+
+ // Color based on type
+ let bgColor = 0x2a2a3e;
+ let borderColor = 0x4e4e6e;
+
+ if (popup.type === 'quest') {
+ bgColor = 0x3a2a1e;
+ borderColor = 0xFFD700;
+ } else if (popup.type === 'warning') {
+ bgColor = 0x3e2a2a;
+ borderColor = 0xff4444;
+ } else if (popup.type === 'success') {
+ bgColor = 0x2a3e2a;
+ borderColor = 0x44ff44;
+ } else if (popup.type === 'story') {
+ bgColor = 0x1a1a2e;
+ borderColor = 0x8888ff;
+ }
+
+ // Draw background with rounded corners
+ bg.fillStyle(bgColor, 0.95);
+ bg.fillRoundedRect(-width / 2, -height / 2, width, height, 16);
+ bg.lineStyle(4, borderColor, 1);
+ bg.strokeRoundedRect(-width / 2, -height / 2, width, height, 16);
+ this.currentPopup.add(bg);
+
+ // Icon
+ const iconText = uiScene.add.text(-width / 2 + 30, -height / 2 + 30, popup.icon, {
+ fontSize: '48px'
+ });
+ this.currentPopup.add(iconText);
+
+ // Title
+ const title = uiScene.add.text(0, -height / 2 + 50, popup.title, {
+ fontSize: '28px',
+ fontFamily: 'Arial',
+ fill: '#ffffff',
+ fontStyle: 'bold',
+ align: 'center'
+ }).setOrigin(0.5);
+ this.currentPopup.add(title);
+
+ // Separator line
+ const separator = uiScene.add.graphics();
+ separator.lineStyle(2, borderColor, 0.5);
+ separator.lineBetween(-width / 2 + 20, -height / 2 + 90, width / 2 - 20, -height / 2 + 90);
+ this.currentPopup.add(separator);
+
+ // Message
+ const message = uiScene.add.text(0, -height / 2 + 150, popup.message, {
+ fontSize: '18px',
+ fontFamily: 'Arial',
+ fill: '#dddddd',
+ align: 'center',
+ wordWrap: { width: width - 80 }
+ }).setOrigin(0.5, 0);
+ this.currentPopup.add(message);
+
+ // Image (if provided)
+ if (popup.image) {
+ const img = uiScene.add.image(0, -height / 2 + 120, popup.image);
+ img.setScale(0.5);
+ this.currentPopup.add(img);
+ }
+
+ // Buttons
+ const buttonY = height / 2 - 50;
+ const buttonSpacing = 150;
+ const buttonStartX = -(popup.buttons.length - 1) * buttonSpacing / 2;
+
+ popup.buttons.forEach((btn, index) => {
+ const btnX = buttonStartX + index * buttonSpacing;
+
+ // Button background
+ const btnBg = uiScene.add.graphics();
+ btnBg.fillStyle(0x4a4a6e, 1);
+ btnBg.fillRoundedRect(btnX - 60, buttonY - 20, 120, 40, 8);
+ btnBg.lineStyle(2, borderColor, 1);
+ btnBg.strokeRoundedRect(btnX - 60, buttonY - 20, 120, 40, 8);
+ btnBg.setInteractive(
+ new Phaser.Geom.Rectangle(btnX - 60, buttonY - 20, 120, 40),
+ Phaser.Geom.Rectangle.Contains
+ );
+
+ // Hover effect
+ btnBg.on('pointerover', () => {
+ btnBg.clear();
+ btnBg.fillStyle(0x6a6a8e, 1);
+ btnBg.fillRoundedRect(btnX - 60, buttonY - 20, 120, 40, 8);
+ btnBg.lineStyle(2, borderColor, 1);
+ btnBg.strokeRoundedRect(btnX - 60, buttonY - 20, 120, 40, 8);
+ });
+
+ btnBg.on('pointerout', () => {
+ btnBg.clear();
+ btnBg.fillStyle(0x4a4a6e, 1);
+ btnBg.fillRoundedRect(btnX - 60, buttonY - 20, 120, 40, 8);
+ btnBg.lineStyle(2, borderColor, 1);
+ btnBg.strokeRoundedRect(btnX - 60, buttonY - 20, 120, 40, 8);
+ });
+
+ // Click handler
+ btnBg.on('pointerdown', () => {
+ if (btn.action === 'close') {
+ this.closePopup(popup);
+ } else if (btn.callback) {
+ btn.callback();
+ this.closePopup(popup);
+ }
+ });
+
+ this.currentPopup.add(btnBg);
+
+ // Button text
+ const btnText = uiScene.add.text(btnX, buttonY, btn.text, {
+ fontSize: '18px',
+ fontFamily: 'Arial',
+ fill: '#ffffff',
+ fontStyle: 'bold'
+ }).setOrigin(0.5);
+ this.currentPopup.add(btnText);
+ });
+
+ // Auto-close timer
+ if (popup.autoClose && this.settings.autoClose) {
+ uiScene.time.delayedCall(this.settings.autoCloseDelay, () => {
+ this.closePopup(popup);
+ });
+ }
+
+ // Play sound
+ if (this.settings.soundOnPopup && this.scene.soundManager) {
+ this.scene.soundManager.beepPickup();
+ }
+
+ // Animate in
+ this.currentPopup.setScale(0.8);
+ this.currentPopup.setAlpha(0);
+ uiScene.tweens.add({
+ targets: this.currentPopup,
+ scale: 1,
+ alpha: 1,
+ duration: 300,
+ ease: 'Back.easeOut'
+ });
+
+ console.log(`๐ Popup shown: ${popup.title}`);
+ }
+
+ /**
+ * Close current popup
+ */
+ closePopup(popup) {
+ if (!this.currentPopup) return;
+
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ // Animate out
+ uiScene.tweens.add({
+ targets: this.currentPopup,
+ scale: 0.8,
+ alpha: 0,
+ duration: 200,
+ ease: 'Power2',
+ onComplete: () => {
+ this.currentPopup.destroy();
+ this.currentPopup = null;
+
+ // Resume game
+ if (this.settings.pauseGameOnPopup) {
+ this.scene.physics.resume();
+ }
+
+ // Call onClose callback
+ if (popup.onClose) {
+ popup.onClose();
+ }
+
+ // Show next popup
+ this.displayNextPopup();
+ }
+ });
+ }
+
+ /**
+ * Show quest popup
+ */
+ showQuest(title, description, objectives) {
+ this.showPopup({
+ title: title,
+ message: description + '\n\nObjectives:\n' + objectives.map((obj, i) => `${i + 1}. ${obj}`).join('\n'),
+ type: 'quest',
+ icon: '๐',
+ buttons: [
+ { text: 'Accept', action: 'close' },
+ { text: 'Decline', action: 'close' }
+ ],
+ autoClose: false
+ });
+ }
+
+ /**
+ * Show story popup
+ */
+ showStory(title, text, image = null) {
+ this.showPopup({
+ title: title,
+ message: text,
+ type: 'story',
+ icon: '๐',
+ image: image,
+ buttons: [{ text: 'Continue', action: 'close' }],
+ autoClose: false
+ });
+ }
+
+ /**
+ * Show notification
+ */
+ showNotification(message, type = 'info') {
+ const icons = {
+ 'info': 'โน๏ธ',
+ 'success': 'โ
',
+ 'warning': 'โ ๏ธ',
+ 'error': 'โ'
+ };
+
+ this.showPopup({
+ title: type.charAt(0).toUpperCase() + type.slice(1),
+ message: message,
+ type: type,
+ icon: icons[type] || 'โน๏ธ',
+ buttons: [{ text: 'OK', action: 'close' }],
+ autoClose: true
+ });
+ }
+
+ /**
+ * Save settings
+ */
+ saveSettings() {
+ localStorage.setItem('novafarma_popup_settings', JSON.stringify(this.settings));
+ }
+
+ /**
+ * Load settings
+ */
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_popup_settings');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ /**
+ * Destroy system
+ */
+ destroy() {
+ if (this.currentPopup) {
+ this.currentPopup.destroy();
+ }
+ this.popupQueue = [];
+ console.log('๐ Central Popup System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CharacterCustomizationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CharacterCustomizationSystem.js
new file mode 100644
index 000000000..843799cda
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CharacterCustomizationSystem.js
@@ -0,0 +1,542 @@
+/**
+ * CharacterCustomizationSystem.js
+ * ================================
+ * KRVAVA ลฝETEV - Complete Character Customization (Phase 17)
+ *
+ * Features:
+ * - Gender selection (Kai/Ana story reversal)
+ * - RGB color picker (unlimited colors!)
+ * - Body customization (skin, height, type)
+ * - Starting outfit selection
+ * - Character creation UI
+ * - Save/load system
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class CharacterCustomizationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Character data
+ this.character = {
+ gender: 'male', // 'male' or 'female'
+ name: 'Kai',
+
+ // Hair
+ hairColor: { r: 139, g: 69, b: 19 }, // Brown default
+ hairStyle: 'dreadlocks',
+ glowingHair: false,
+ rainbowHair: false,
+
+ // Body
+ skinTone: 'medium', // 'light', 'medium', 'tan', 'dark'
+ bodyType: 'normal', // 'slim', 'normal', 'athletic', 'heavy'
+ height: 'normal', // 'short', 'normal', 'tall'
+
+ // Outfit
+ startingOutfit: 'farmer', // 'farmer', 'casual', 'formal'
+
+ // Story
+ searchingFor: 'Ana' // Twin's name (reverses with gender)
+ };
+
+ // UI
+ this.customizationUI = null;
+ this.isCreating = false;
+
+ // Presets
+ this.hairColorPresets = [
+ { name: 'Black', r: 0, g: 0, b: 0 },
+ { name: 'Brown', r: 139, g: 69, b: 19 },
+ { name: 'Blonde', r: 255, g: 215, b: 0 },
+ { name: 'Red', r: 255, g: 69, b: 0 },
+ { name: 'White', r: 255, g: 255, b: 255 },
+ { name: 'Blue', r: 0, g: 191, b: 255 },
+ { name: 'Green', r: 0, g: 255, b: 127 },
+ { name: 'Pink', r: 255, g: 105, b: 180 },
+ { name: 'Purple', r: 138, g: 43, b: 226 },
+ { name: 'Rainbow', r: -1, g: -1, b: -1 } // Special
+ ];
+
+ console.log('๐ค CharacterCustomizationSystem initialized');
+ }
+
+ /**
+ * Start character creation
+ */
+ startCreation() {
+ this.isCreating = true;
+
+ console.log('๐ค Character creation started');
+
+ // Create UI
+ this.createCustomizationUI();
+
+ // Show UI
+ this.showUI();
+ }
+
+ /**
+ * Create customization UI
+ */
+ createCustomizationUI() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ this.customizationUI = this.scene.add.container(width / 2, height / 2);
+ this.customizationUI.setScrollFactor(0);
+ this.customizationUI.setDepth(10000);
+ this.customizationUI.setVisible(false);
+
+ // Background
+ const bg = this.scene.add.rectangle(0, 0, 1000, 700, 0x1a1a1a, 0.95);
+ bg.setStrokeStyle(4, 0xFFD700);
+ this.customizationUI.add(bg);
+
+ // Title
+ const title = this.scene.add.text(0, -320, '๐ค CHARACTER CREATION', {
+ fontSize: '36px',
+ fontFamily: 'Arial',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ title.setOrigin(0.5);
+ this.customizationUI.add(title);
+
+ // Preview (character sprite)
+ this.characterPreview = this.scene.add.rectangle(0, -150, 200, 300, 0x8B4513);
+ this.characterPreview.setStrokeStyle(2, 0xFFFFFF);
+ this.customizationUI.add(this.characterPreview);
+
+ const previewLabel = this.scene.add.text(0, -310, 'Preview', {
+ fontSize: '18px',
+ color: '#FFFFFF'
+ });
+ previewLabel.setOrigin(0.5);
+ this.customizationUI.add(previewLabel);
+
+ // Gender selection
+ this.createGenderSelector();
+
+ // Hair color picker
+ this.createColorPicker();
+
+ // Body options
+ this.createBodyOptions();
+
+ // Outfit selector
+ this.createOutfitSelector();
+
+ // Confirm button
+ const confirmBtn = this.scene.add.rectangle(0, 310, 200, 50, 0x228B22);
+ confirmBtn.setStrokeStyle(3, 0x32CD32);
+ confirmBtn.setInteractive();
+ confirmBtn.on('pointerdown', () => this.confirmCreation());
+ this.customizationUI.add(confirmBtn);
+
+ const confirmText = this.scene.add.text(0, 310, 'START GAME!', {
+ fontSize: '24px',
+ fontFamily: 'Arial',
+ color: '#FFFFFF',
+ fontStyle: 'bold'
+ });
+ confirmText.setOrigin(0.5);
+ this.customizationUI.add(confirmText);
+
+ console.log('โ
Customization UI created');
+ }
+
+ /**
+ * Create gender selector
+ */
+ createGenderSelector() {
+ const label = this.scene.add.text(-400, 50, 'Gender:', {
+ fontSize: '20px',
+ color: '#FFFFFF',
+ fontStyle: 'bold'
+ });
+ this.customizationUI.add(label);
+
+ // Male button
+ const maleBtn = this.scene.add.rectangle(-350, 100, 120, 40, 0x0066CC);
+ maleBtn.setStrokeStyle(2, 0x00AAFF);
+ maleBtn.setInteractive();
+ maleBtn.on('pointerdown', () => this.setGender('male'));
+ this.customizationUI.add(maleBtn);
+
+ const maleText = this.scene.add.text(-350, 100, 'โ Kai (Male)', {
+ fontSize: '16px',
+ color: '#FFFFFF'
+ });
+ maleText.setOrigin(0.5);
+ this.customizationUI.add(maleText);
+
+ // Female button
+ const femaleBtn = this.scene.add.rectangle(-210, 100, 120, 40, 0xCC0066);
+ femaleBtn.setStrokeStyle(2, 0xFF0099);
+ femaleBtn.setInteractive();
+ femaleBtn.on('pointerdown', () => this.setGender('female'));
+ this.customizationUI.add(femaleBtn);
+
+ const femaleText = this.scene.add.text(-210, 100, 'โ Ana (Female)', {
+ fontSize: '16px',
+ color: '#FFFFFF'
+ });
+ femaleText.setOrigin(0.5);
+ this.customizationUI.add(femaleText);
+ }
+
+ /**
+ * Create color picker
+ */
+ createColorPicker() {
+ const label = this.scene.add.text(-400, 150, 'Hair Color:', {
+ fontSize: '20px',
+ color: '#FFFFFF',
+ fontStyle: 'bold'
+ });
+ this.customizationUI.add(label);
+
+ // Preset colors
+ let x = -400;
+ let y = 200;
+ this.hairColorPresets.forEach((preset, index) => {
+ const colorBox = this.scene.add.rectangle(x, y, 30, 30,
+ preset.r === -1 ? 0xFFFFFF : Phaser.Display.Color.GetColor(preset.r, preset.g, preset.b));
+ colorBox.setStrokeStyle(2, 0xFFFFFF);
+ colorBox.setInteractive();
+ colorBox.on('pointerdown', () => this.setHairColor(preset));
+ this.customizationUI.add(colorBox);
+
+ // Rainbow special effect
+ if (preset.name === 'Rainbow') {
+ const rainbowText = this.scene.add.text(x, y, '๐', {
+ fontSize: '20px'
+ });
+ rainbowText.setOrigin(0.5);
+ this.customizationUI.add(rainbowText);
+ }
+
+ x += 40;
+ if ((index + 1) % 5 === 0) {
+ x = -400;
+ y += 40;
+ }
+ });
+
+ // Glowing option
+ const glowCheckbox = this.scene.add.rectangle(-400, 280, 20, 20, 0x444444);
+ glowCheckbox.setStrokeStyle(2, 0xFFFFFF);
+ glowCheckbox.setInteractive();
+ glowCheckbox.on('pointerdown', () => this.toggleGlowing());
+ this.customizationUI.add(glowCheckbox);
+
+ this.glowLabel = this.scene.add.text(-370, 280, 'โจ Glowing Hair', {
+ fontSize: '16px',
+ color: '#FFFFFF'
+ });
+ this.glowLabel.setOrigin(0, 0.5);
+ this.customizationUI.add(this.glowLabel);
+ }
+
+ /**
+ * Create body options
+ */
+ createBodyOptions() {
+ let y = 50;
+
+ // Skin tone
+ const skinLabel = this.scene.add.text(200, y, 'Skin Tone:', {
+ fontSize: '18px',
+ color: '#FFFFFF',
+ fontStyle: 'bold'
+ });
+ this.customizationUI.add(skinLabel);
+
+ const skinTones = ['light', 'medium', 'tan', 'dark'];
+ skinTones.forEach((tone, index) => {
+ const btn = this.scene.add.rectangle(200 + (index * 60), y + 40, 50, 30, this.getSkinColor(tone));
+ btn.setStrokeStyle(2, 0xFFFFFF);
+ btn.setInteractive();
+ btn.on('pointerdown', () => this.setSkinTone(tone));
+ this.customizationUI.add(btn);
+ });
+
+ // Body type
+ y += 100;
+ const bodyLabel = this.scene.add.text(200, y, 'Body Type:', {
+ fontSize: '18px',
+ color: '#FFFFFF',
+ fontStyle: 'bold'
+ });
+ this.customizationUI.add(bodyLabel);
+
+ const bodyTypes = ['slim', 'normal', 'athletic', 'heavy'];
+ bodyTypes.forEach((type, index) => {
+ const btn = this.scene.add.rectangle(200, y + 40 + (index * 45), 150, 35, 0x444444);
+ btn.setStrokeStyle(2, 0xFFFFFF);
+ btn.setInteractive();
+ btn.on('pointerdown', () => this.setBodyType(type));
+ this.customizationUI.add(btn);
+
+ const text = this.scene.add.text(200, y + 40 + (index * 45), type.toUpperCase(), {
+ fontSize: '14px',
+ color: '#FFFFFF'
+ });
+ text.setOrigin(0.5);
+ this.customizationUI.add(text);
+ });
+ }
+
+ /**
+ * Create outfit selector
+ */
+ createOutfitSelector() {
+ const label = this.scene.add.text(200, 250, 'Starting Outfit:', {
+ fontSize: '18px',
+ color: '#FFFFFF',
+ fontStyle: 'bold'
+ });
+ this.customizationUI.add(label);
+
+ const outfits = [
+ { id: 'farmer', name: 'Farmer Outfit' },
+ { id: 'casual', name: 'Casual Clothes' },
+ { id: 'formal', name: 'Formal Wear' }
+ ];
+
+ outfits.forEach((outfit, index) => {
+ const btn = this.scene.add.rectangle(200, 290 + (index * 45), 180, 35, 0x444444);
+ btn.setStrokeStyle(2, 0xFFFFFF);
+ btn.setInteractive();
+ btn.on('pointerdown', () => this.setOutfit(outfit.id));
+ this.customizationUI.add(btn);
+
+ const text = this.scene.add.text(200, 290 + (index * 45), outfit.name, {
+ fontSize: '14px',
+ color: '#FFFFFF'
+ });
+ text.setOrigin(0.5);
+ this.customizationUI.add(text);
+ });
+ }
+
+ /**
+ * Set gender
+ */
+ setGender(gender) {
+ this.character.gender = gender;
+
+ if (gender === 'male') {
+ this.character.name = 'Kai';
+ this.character.searchingFor = 'Ana';
+ } else {
+ this.character.name = 'Ana';
+ this.character.searchingFor = 'Kai';
+ }
+
+ console.log(`๐ค Gender set to: ${gender} (searching for ${this.character.searchingFor})`);
+
+ this.updatePreview();
+ }
+
+ /**
+ * Set hair color
+ */
+ setHairColor(preset) {
+ if (preset.name === 'Rainbow') {
+ this.character.rainbowHair = true;
+ this.character.hairColor = { r: 255, g: 0, b: 255 }; // Default to purple
+ } else {
+ this.character.rainbowHair = false;
+ this.character.hairColor = { r: preset.r, g: preset.g, b: preset.b };
+ }
+
+ console.log(`๐ Hair color: ${preset.name}`);
+
+ this.updatePreview();
+ }
+
+ /**
+ * Toggle glowing hair
+ */
+ toggleGlowing() {
+ this.character.glowingHair = !this.character.glowingHair;
+
+ console.log(`โจ Glowing hair: ${this.character.glowingHair}`);
+
+ this.updatePreview();
+ }
+
+ /**
+ * Set skin tone
+ */
+ setSkinTone(tone) {
+ this.character.skinTone = tone;
+
+ console.log(`๐ค Skin tone: ${tone}`);
+
+ this.updatePreview();
+ }
+
+ /**
+ * Set body type
+ */
+ setBodyType(type) {
+ this.character.bodyType = type;
+
+ console.log(`๐ค Body type: ${type}`);
+
+ this.updatePreview();
+ }
+
+ /**
+ * Set outfit
+ */
+ setOutfit(outfitId) {
+ this.character.startingOutfit = outfitId;
+
+ console.log(`๐ Outfit: ${outfitId}`);
+
+ this.updatePreview();
+ }
+
+ /**
+ * Get skin color
+ */
+ getSkinColor(tone) {
+ const colors = {
+ light: 0xFFE4C4,
+ medium: 0xDEB887,
+ tan: 0xD2B48C,
+ dark: 0x8B7355
+ };
+ return colors[tone] || colors.medium;
+ }
+
+ /**
+ * Update character preview
+ */
+ updatePreview() {
+ // Update preview sprite color
+ const skinColor = this.getSkinColor(this.character.skinTone);
+ this.characterPreview.setFillStyle(skinColor);
+
+ // TODO: Update actual sprite appearance
+ console.log('๐ค Preview updated');
+ }
+
+ /**
+ * Confirm creation
+ */
+ confirmCreation() {
+ this.isCreating = false;
+
+ console.log('โ
Character creation complete!');
+ console.log(this.character);
+
+ // Save character data
+ this.saveCharacter();
+
+ // Hide UI
+ this.hideUI();
+
+ // Start game with customized character
+ this.startGame();
+ }
+
+ /**
+ * Save character data
+ */
+ saveCharacter() {
+ try {
+ localStorage.setItem('krvava_zetev_character', JSON.stringify(this.character));
+ console.log('๐พ Character saved');
+ } catch (error) {
+ console.error('Failed to save character:', error);
+ }
+ }
+
+ /**
+ * Load character data
+ */
+ loadCharacter() {
+ try {
+ const saved = localStorage.getItem('krvava_zetev_character');
+ if (saved) {
+ this.character = JSON.parse(saved);
+ console.log('๐ฅ Character loaded');
+ return true;
+ }
+ } catch (error) {
+ console.error('Failed to load character:', error);
+ }
+ return false;
+ }
+
+ /**
+ * Start game
+ */
+ startGame() {
+ console.log(`๐ฎ Starting game as ${this.character.name}!`);
+ console.log(`Quest: Find ${this.character.searchingFor}!`);
+
+ // Apply character to game
+ this.applyCharacterToGame();
+
+ // Show notification
+ this.showNotification({
+ title: 'Welcome to Krvava ลฝetev!',
+ text: `Play as ${this.character.name}. Find ${this.character.searchingFor}!`,
+ icon: '๐ค'
+ });
+ }
+
+ /**
+ * Apply character to game
+ */
+ applyCharacterToGame() {
+ // TODO: Apply character appearance to player sprite
+ // TODO: Set story variables based on gender
+ console.log('๐ค Character applied to game');
+ }
+
+ /**
+ * Show UI
+ */
+ showUI() {
+ if (this.customizationUI) {
+ this.customizationUI.setVisible(true);
+ }
+ }
+
+ /**
+ * Hide UI
+ */
+ hideUI() {
+ if (this.customizationUI) {
+ this.customizationUI.setVisible(false);
+ }
+ }
+
+ /**
+ * Get character data
+ */
+ getCharacter() {
+ return { ...this.character };
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ChildrenFamilySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ChildrenFamilySystem.js
new file mode 100644
index 000000000..705fa1195
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ChildrenFamilySystem.js
@@ -0,0 +1,603 @@
+/**
+ * ChildrenFamilySystem.js
+ * =======================
+ * KRVAVA ลฝETEV - Children & Family System (P11)
+ *
+ * Features:
+ * - Pregnancy system
+ * - Birth events
+ * - 6 growth stages (Baby โ Toddler โ Child โ Teen โ Adult)
+ * - Child AI (follows, helps farm, combat)
+ * - Family events
+ * - Generational gameplay ready
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class ChildrenFamilySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Children data
+ this.children = new Map(); // childId -> child state
+ this.pregnancyData = null;
+
+ // Growth stages (in in-game days)
+ this.growthStages = {
+ BABY: { min: 0, max: 365 }, // 0-1 year
+ TODDLER: { min: 365, max: 1095 }, // 1-3 years
+ CHILD: { min: 1095, max: 3650 }, // 3-10 years
+ TEEN: { min: 3650, max: 6570 }, // 10-18 years
+ ADULT: { min: 6570, max: 999999 } // 18+ years
+ };
+
+ // Family events
+ this.lastFamilyDinner = null;
+ this.familyPhotos = [];
+
+ console.log('๐ถ ChildrenFamilySystem initialized');
+ }
+
+ /**
+ * 11.1 - Start Pregnancy
+ */
+ startPregnancy(spouseId) {
+ if (this.pregnancyData) {
+ console.log('โ ๏ธ Already pregnant!');
+ return false;
+ }
+
+ const spouse = this.scene.marriageRomanceSystem?.getRomanceState(spouseId);
+ if (!spouse) {
+ console.error('Spouse not found');
+ return false;
+ }
+
+ this.pregnancyData = {
+ spouseId: spouseId,
+ spouseName: spouse.name,
+ startDate: this.scene.timeSystem?.getCurrentDate() || new Date(),
+ daysSinceStart: 0,
+ totalDays: 30, // 30 in-game days
+ bellyStage: 0, // 0-4 stages
+ cravings: [],
+ isActive: true
+ };
+
+ console.log(`๐คฐ ${spouse.name} is pregnant!`);
+
+ // Trigger announcement cutscene
+ this.showPregnancyAnnouncement();
+
+ return true;
+ }
+
+ /**
+ * 11.1 - Show pregnancy announcement
+ */
+ showPregnancyAnnouncement() {
+ if (!this.pregnancyData) return;
+
+ const spouse = this.scene.marriageRomanceSystem?.getRomanceState(this.pregnancyData.spouseId);
+
+ // TODO: Implement cutscene
+ this.showNotification({
+ title: 'Big News!',
+ text: `๐คฐ ${spouse.name}: "Darling... I'm pregnant!"`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * 11.1 - Update pregnancy
+ */
+ updatePregnancy(delta) {
+ if (!this.pregnancyData || !this.pregnancyData.isActive) return;
+
+ // Track days
+ const currentDay = this.scene.timeSystem?.getCurrentDay() || 0;
+ this.pregnancyData.daysSinceStart = currentDay - this.pregnancyData.startDate.day;
+
+ // Update belly stage (0-4)
+ const progress = this.pregnancyData.daysSinceStart / this.pregnancyData.totalDays;
+ this.pregnancyData.bellyStage = Math.floor(progress * 5);
+
+ // Random cravings every few days
+ if (Math.random() < 0.01) { // 1% chance per update
+ this.triggerCraving();
+ }
+
+ // Check if ready to give birth
+ if (this.pregnancyData.daysSinceStart >= this.pregnancyData.totalDays) {
+ this.triggerBirth();
+ }
+ }
+
+ /**
+ * 11.1 - Trigger craving
+ */
+ triggerCraving() {
+ const cravingItems = [
+ 'strawberry', 'pickle', 'ice_cream', 'chocolate', 'pizza',
+ 'watermelon', 'cheese', 'sushi', 'cake', 'pasta'
+ ];
+
+ const item = Phaser.Utils.Array.GetRandom(cravingItems);
+
+ this.pregnancyData.cravings.push({
+ item: item,
+ satisfied: false,
+ timestamp: Date.now()
+ });
+
+ this.showNotification({
+ title: 'Craving!',
+ text: `${this.pregnancyData.spouseName}: "I really want ${item}!"`,
+ icon: '๐คค'
+ });
+ }
+
+ /**
+ * 11.2 - Trigger Birth Event
+ */
+ triggerBirth() {
+ if (!this.pregnancyData) return;
+
+ console.log('๐ถ Labor starting!');
+
+ this.pregnancyData.isActive = false;
+
+ // Trigger labor alarm (middle of night)
+ this.showBirthCutscene();
+ }
+
+ /**
+ * 11.2 - Birth cutscene
+ */
+ showBirthCutscene() {
+ // TODO: Implement full cutscene with:
+ // - Labor alarm
+ // - Rush to Dr. Chen
+ // - Birth scene
+ // - Gender selection
+ // - Baby naming
+
+ // For now, show dialog
+ const genderOptions = ['boy', 'girl'];
+ const gender = Phaser.Utils.Array.GetRandom(genderOptions);
+
+ this.promptBabyName(gender);
+ }
+
+ /**
+ * 11.2 - Prompt baby name
+ */
+ promptBabyName(gender) {
+ // TODO: Implement name input UI
+ // For now, use default names
+ const boyNames = ['Luka', 'Mark', 'Jure', 'Matej', 'Alex'];
+ const girlNames = ['Ana', 'Maja', 'Eva', 'Nina', 'Luna'];
+
+ const name = gender === 'boy'
+ ? Phaser.Utils.Array.GetRandom(boyNames)
+ : Phaser.Utils.Array.GetRandom(girlNames);
+
+ this.createBaby(name, gender);
+ }
+
+ /**
+ * 11.2 - Create baby
+ */
+ createBaby(name, gender) {
+ const childId = `child_${Date.now()}`;
+
+ const childData = {
+ id: childId,
+ name: name,
+ gender: gender,
+ birthDate: this.scene.timeSystem?.getCurrentDate() || new Date(),
+ age: 0, // In days
+ stage: 'BABY',
+
+ // Stats
+ health: 100,
+ energy: 100,
+ happiness: 100,
+
+ // Skills (0-100)
+ skills: {
+ farming: 0,
+ combat: 0,
+ crafting: 0,
+ social: 0,
+ intelligence: 0
+ },
+
+ // Personality (develops over time)
+ personality: {
+ brave: Math.random() * 100,
+ kind: Math.random() * 100,
+ smart: Math.random() * 100,
+ creative: Math.random() * 100
+ },
+
+ // Relationships
+ lovesPlayer: 100,
+ lovesSpouse: 100,
+
+ // State
+ isAwake: true,
+ currentActivity: 'sleeping',
+ specialization: null, // Chosen at teen stage
+
+ // Growth
+ nextStageAge: this.growthStages.TODDLER.min
+ };
+
+ this.children.set(childId, childData);
+
+ console.log(`๐ถ ${name} was born! (${gender})`);
+
+ // Clear pregnancy data
+ this.pregnancyData = null;
+
+ // Show birth notification
+ this.showNotification({
+ title: 'Baby Born!',
+ text: `๐ถ Welcome ${name}! It's a ${gender}!`,
+ icon: '๐ผ'
+ });
+
+ return childData;
+ }
+
+ /**
+ * 11.3 - Baby Stage AI
+ */
+ updateBaby(child, delta) {
+ // Baby actions
+ const actions = ['sleeping', 'crying', 'feeding', 'playing'];
+
+ // Random crying
+ if (Math.random() < 0.001) { // 0.1% chance
+ child.currentActivity = 'crying';
+ this.showNotification({
+ title: 'Baby Crying!',
+ text: `๐ถ ${child.name} is crying! ๐ญ`,
+ icon: '๐ถ'
+ });
+ }
+
+ // Auto-sleep at night
+ const hour = this.scene.timeSystem?.getCurrentHour() || 12;
+ if (hour >= 20 || hour <= 6) {
+ child.currentActivity = 'sleeping';
+ }
+ }
+
+ /**
+ * 11.4 - Toddler Stage AI
+ */
+ updateToddler(child, delta) {
+ // Toddler can walk and talk
+ if (!child.hasWalked) {
+ child.hasWalked = true;
+ this.showNotification({
+ title: 'First Steps!',
+ text: `๐ฃ ${child.name} is walking!`,
+ icon: '๐'
+ });
+ }
+
+ if (!child.hasSpoken && child.age > 380) { // ~13 months
+ child.hasSpoken = true;
+ const firstWord = Math.random() > 0.5 ? 'Dada!' : 'Mama!';
+ this.showNotification({
+ title: 'First Words!',
+ text: `๐ฃ๏ธ ${child.name}: "${firstWord}"`,
+ icon: '๐ฌ'
+ });
+ }
+
+ // Follow player sometimes
+ if (Math.random() < 0.3) {
+ child.currentActivity = 'following_player';
+ }
+ }
+
+ /**
+ * 11.5 - Child Stage AI
+ */
+ updateChild(child, delta) {
+ // Child can help with farm
+ if (this.scene.timeSystem?.getCurrentHour() === 8) {
+ // Morning: Water 5 crops
+ this.waterCrops(child, 5);
+ }
+
+ if (this.scene.timeSystem?.getCurrentHour() === 16) {
+ // Afternoon: Feed chickens
+ this.feedAnimals(child);
+ }
+
+ // Learn skills
+ child.skills.farming += 0.01;
+ child.skills.social += 0.01;
+ }
+
+ /**
+ * 11.6 - Teen Stage AI
+ */
+ updateTeen(child, delta) {
+ // Advanced farm help
+ if (this.scene.timeSystem?.getCurrentHour() === 7) {
+ this.waterCrops(child, 20);
+ }
+
+ // Can do errands
+ if (Math.random() < 0.05) {
+ this.doErrand(child);
+ }
+
+ // Personality development (teenager attitude!)
+ if (Math.random() < 0.01) {
+ const attitudes = [
+ `"UGH, Dad! Why do I have to do chores?"`,
+ `"Can I PLEASE go to the festival?"`,
+ `"You don't understand me!"`,
+ `"I'm not a kid anymore!"`
+ ];
+ const attitude = Phaser.Utils.Array.GetRandom(attitudes);
+ this.showNotification({
+ title: 'Teenage Attitude',
+ text: `${child.name}: ${attitude}`,
+ icon: '๐ค'
+ });
+ }
+
+ // Choose specialization at 14
+ if (child.age >= 5110 && !child.specialization) { // ~14 years
+ this.chooseSpecialization(child);
+ }
+ }
+
+ /**
+ * 11.6 - Choose specialization
+ */
+ chooseSpecialization(child) {
+ const specs = [];
+
+ // Based on personality and skills
+ if (child.skills.farming > 50) specs.push('Farmer');
+ if (child.skills.combat > 50) specs.push('Fighter');
+ if (child.skills.intelligence > 50) specs.push('Scientist');
+ if (child.personality.creative > 70) specs.push('Artist');
+
+ // Default to highest skill
+ if (specs.length === 0) {
+ const highestSkill = Object.keys(child.skills).reduce((a, b) =>
+ child.skills[a] > child.skills[b] ? a : b
+ );
+ specs.push(highestSkill.charAt(0).toUpperCase() + highestSkill.slice(1));
+ }
+
+ child.specialization = Phaser.Utils.Array.GetRandom(specs);
+
+ this.showNotification({
+ title: 'Career Choice!',
+ text: `${child.name} wants to become a ${child.specialization}!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * 11.7 - Adult Stage (Becomes Playable!)
+ */
+ updateAdult(child, delta) {
+ // Full abilities unlocked
+ child.canBoss = true;
+ child.canCraftAll = true;
+
+ // Can have own relationships
+ if (!child.canMarry) {
+ child.canMarry = true;
+ this.showNotification({
+ title: 'All Grown Up!',
+ text: `${child.name} is now an adult! They can marry and have their own family!`,
+ icon: '๐'
+ });
+ }
+ }
+
+ /**
+ * Update child growth
+ */
+ updateChildGrowth(child, delta) {
+ // Age child
+ child.age++;
+
+ // Check for stage transition
+ if (child.age >= child.nextStageAge) {
+ this.advanceStage(child);
+ }
+
+ // Update based on current stage
+ switch (child.stage) {
+ case 'BABY':
+ this.updateBaby(child, delta);
+ break;
+ case 'TODDLER':
+ this.updateToddler(child, delta);
+ break;
+ case 'CHILD':
+ this.updateChild(child, delta);
+ break;
+ case 'TEEN':
+ this.updateTeen(child, delta);
+ break;
+ case 'ADULT':
+ this.updateAdult(child, delta);
+ break;
+ }
+ }
+
+ /**
+ * Advance to next growth stage
+ */
+ advanceStage(child) {
+ const stages = ['BABY', 'TODDLER', 'CHILD', 'TEEN', 'ADULT'];
+ const currentIndex = stages.indexOf(child.stage);
+
+ if (currentIndex < stages.length - 1) {
+ child.stage = stages[currentIndex + 1];
+ child.nextStageAge = this.growthStages[child.stage].max;
+
+ console.log(`๐ ${child.name} is now a ${child.stage}!`);
+
+ this.showNotification({
+ title: 'Birthday!',
+ text: `๐ ${child.name} grew up to ${child.stage} stage!`,
+ icon: '๐'
+ });
+ }
+ }
+
+ /**
+ * Helper: Water crops
+ */
+ waterCrops(child, amount) {
+ // TODO: Integrate with FarmingSystem
+ console.log(`๐ง ${child.name} watered ${amount} crops!`);
+ child.skills.farming += 0.5;
+ }
+
+ /**
+ * Helper: Feed animals
+ */
+ feedAnimals(child) {
+ // TODO: Integrate with AnimalSystem
+ console.log(`๐ ${child.name} fed the animals!`);
+ child.skills.farming += 0.3;
+ }
+
+ /**
+ * Helper: Do errand
+ */
+ doErrand(child) {
+ const errands = [
+ 'bought groceries',
+ 'delivered package',
+ 'fetched water',
+ 'collected mail'
+ ];
+
+ const errand = Phaser.Utils.Array.GetRandom(errands);
+ console.log(`๐ฆ ${child.name} ${errand}!`);
+ child.skills.social += 0.5;
+ }
+
+ /**
+ * 11.8 - Family Dinner Event
+ */
+ triggerFamilyDinner() {
+ const spouse = this.scene.marriageRomanceSystem?.getSpouse();
+ if (!spouse) return;
+
+ console.log('๐ฝ๏ธ Family dinner time!');
+
+ // TODO: Implement family dinner cutscene
+ this.showNotification({
+ title: 'Family Dinner!',
+ text: `๐ฝ๏ธ The whole family is having dinner together!`,
+ icon: '๐จโ๐ฉโ๐งโ๐ฆ'
+ });
+
+ this.lastFamilyDinner = new Date();
+
+ // Boost family relationships
+ this.children.forEach(child => {
+ child.lovesPlayer += 5;
+ child.lovesSpouse += 5;
+ });
+ }
+
+ /**
+ * 11.8 - Take Family Photo
+ */
+ takeFamilyPhoto() {
+ const photo = {
+ date: this.scene.timeSystem?.getCurrentDate() || new Date(),
+ members: [
+ 'Player',
+ this.scene.marriageRomanceSystem?.getSpouse()?.name || 'Spouse',
+ ...Array.from(this.children.values()).map(c => c.name)
+ ],
+ location: 'Home'
+ };
+
+ this.familyPhotos.push(photo);
+
+ this.showNotification({
+ title: 'Family Photo!',
+ text: `๐ธ Beautiful family memory captured!`,
+ icon: '๐ท'
+ });
+ }
+
+ /**
+ * Update system
+ */
+ update(delta) {
+ // Update pregnancy
+ if (this.pregnancyData) {
+ this.updatePregnancy(delta);
+ }
+
+ // Update all children
+ this.children.forEach(child => {
+ this.updateChildGrowth(child, delta);
+ });
+
+ // Check for family events
+ this.checkFamilyEvents();
+ }
+
+ /**
+ * Check for family events
+ */
+ checkFamilyEvents() {
+ // Weekly family dinner
+ const daysSinceLastDinner = this.lastFamilyDinner
+ ? (Date.now() - this.lastFamilyDinner.getTime()) / (1000 * 60 * 60 * 24)
+ : 999;
+
+ if (daysSinceLastDinner >= 7 && this.children.size > 0) {
+ this.triggerFamilyDinner();
+ }
+ }
+
+ /**
+ * Helpers
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+
+ getChildren() {
+ return Array.from(this.children.values());
+ }
+
+ getChildByName(name) {
+ return Array.from(this.children.values()).find(c => c.name === name);
+ }
+
+ isPregnant() {
+ return this.pregnancyData && this.pregnancyData.isActive;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ChunkManager.js b/EMERGENCY_SYSTEMS_RECOVERY/ChunkManager.js
new file mode 100644
index 000000000..8862e51b1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ChunkManager.js
@@ -0,0 +1,216 @@
+// ChunkManager - Handles chunk-based terrain loading for large maps
+class ChunkManager {
+ constructor(scene, chunkSize = 50) {
+ this.scene = scene;
+ this.chunkSize = chunkSize; // 50x50 tiles per chunk
+ this.biomeSystem = null; // Will be set by GameScene
+
+ // Active chunks (currently loaded)
+ this.activeChunks = new Map(); // Key: "chunkX,chunkY", Value: chunk data
+
+ // Chunk load radius (how many chunks around player)
+ this.loadRadius = 1; // Load 3x3 = 9 chunks at once
+
+ // Player position tracking
+ this.lastPlayerChunkX = -1;
+ this.lastPlayerChunkY = -1;
+
+ console.log(`๐พ ChunkManager initialized (chunk size: ${chunkSize}x${chunkSize})`);
+ }
+
+ // Get chunk coordinates from world coordinates
+ worldToChunk(worldX, worldY) {
+ return {
+ chunkX: Math.floor(worldX / this.chunkSize),
+ chunkY: Math.floor(worldY / this.chunkSize)
+ };
+ }
+
+ // Get chunk key string
+ getChunkKey(chunkX, chunkY) {
+ return `${chunkX},${chunkY}`;
+ }
+
+ // Check if chunk is loaded
+ isChunkLoaded(chunkX, chunkY) {
+ return this.activeChunks.has(this.getChunkKey(chunkX, chunkY));
+ }
+
+ // Load a single chunk
+ loadChunk(chunkX, chunkY) {
+ const key = this.getChunkKey(chunkX, chunkY);
+
+ // Already loaded
+ if (this.activeChunks.has(key)) {
+ return this.activeChunks.get(key);
+ }
+
+ console.log(`๐ฆ Loading chunk (${chunkX}, ${chunkY})`);
+
+ // Create chunk data
+ const chunk = {
+ chunkX,
+ chunkY,
+ key,
+ tiles: [],
+ objects: [], // Trees, rocks, decorations
+ sprites: [] // Phaser sprites for this chunk
+ };
+
+ // Generate or load chunk tiles
+ const startX = chunkX * this.chunkSize;
+ const startY = chunkY * this.chunkSize;
+
+ for (let y = 0; y < this.chunkSize; y++) {
+ for (let x = 0; x < this.chunkSize; x++) {
+ const worldX = startX + x;
+ const worldY = startY + y;
+
+ // Get biome for this tile
+ let biomeId = 'grassland';
+ if (this.biomeSystem) {
+ biomeId = this.biomeSystem.getBiomeAt(worldX, worldY);
+ }
+
+ chunk.tiles.push({
+ x: worldX,
+ y: worldY,
+ biome: biomeId
+ });
+ }
+ }
+
+ // Store chunk
+ this.activeChunks.set(key, chunk);
+
+ // Render chunk (if terrain system available)
+ if (this.scene.terrainSystem && this.scene.terrainSystem.renderChunk) {
+ this.scene.terrainSystem.renderChunk(chunk);
+ }
+
+ return chunk;
+ }
+
+ // Unload a single chunk
+ unloadChunk(chunkX, chunkY) {
+ const key = this.getChunkKey(chunkX, chunkY);
+
+ if (!this.activeChunks.has(key)) return;
+
+ console.log(`๐ค Unloading chunk (${chunkX}, ${chunkY})`);
+
+ const chunk = this.activeChunks.get(key);
+
+ // Destroy all sprites in chunk
+ if (chunk.sprites) {
+ chunk.sprites.forEach(sprite => {
+ if (sprite && sprite.destroy) {
+ sprite.destroy();
+ }
+ });
+ }
+
+ // Remove chunk
+ this.activeChunks.delete(key);
+ }
+
+ // Update active chunks based on player position
+ updateActiveChunks(playerX, playerY) {
+ const { chunkX, chunkY } = this.worldToChunk(playerX, playerY);
+
+ // Player hasn't changed chunks
+ if (chunkX === this.lastPlayerChunkX && chunkY === this.lastPlayerChunkY) {
+ return;
+ }
+
+ console.log(`๐ Player moved to chunk (${chunkX}, ${chunkY})`);
+
+ this.lastPlayerChunkX = chunkX;
+ this.lastPlayerChunkY = chunkY;
+
+ // Determine which chunks should be loaded
+ const chunksToLoad = new Set();
+
+ for (let dy = -this.loadRadius; dy <= this.loadRadius; dy++) {
+ for (let dx = -this.loadRadius; dx <= this.loadRadius; dx++) {
+ const targetChunkX = chunkX + dx;
+ const targetChunkY = chunkY + dy;
+ chunksToLoad.add(this.getChunkKey(targetChunkX, targetChunkY));
+ }
+ }
+
+ // Unload chunks that are too far
+ const chunksToUnload = [];
+ for (const [key, chunk] of this.activeChunks) {
+ if (!chunksToLoad.has(key)) {
+ chunksToUnload.push({ x: chunk.chunkX, y: chunk.chunkY });
+ }
+ }
+
+ chunksToUnload.forEach(({ x, y }) => this.unloadChunk(x, y));
+
+ // Load new chunks
+ for (let dy = -this.loadRadius; dy <= this.loadRadius; dy++) {
+ for (let dx = -this.loadRadius; dx <= this.loadRadius; dx++) {
+ const targetChunkX = chunkX + dx;
+ const targetChunkY = chunkY + dy;
+
+ if (!this.isChunkLoaded(targetChunkX, targetChunkY)) {
+ this.loadChunk(targetChunkX, targetChunkY);
+ }
+ }
+ }
+ }
+
+ // Force reload all chunks (for debugging)
+ reloadAllChunks() {
+ console.log('๐ Reloading all chunks...');
+
+ const chunksToReload = [];
+ for (const [key, chunk] of this.activeChunks) {
+ chunksToReload.push({ x: chunk.chunkX, y: chunk.chunkY });
+ }
+
+ // Unload all
+ chunksToReload.forEach(({ x, y }) => this.unloadChunk(x, y));
+
+ // Reload based on player position
+ if (this.scene.player) {
+ const pos = this.scene.player.getPosition();
+ this.updateActiveChunks(pos.x, pos.y);
+ }
+ }
+
+ // Get chunk at world position
+ getChunkAt(worldX, worldY) {
+ const { chunkX, chunkY } = this.worldToChunk(worldX, worldY);
+ return this.activeChunks.get(this.getChunkKey(chunkX, chunkY));
+ }
+
+ // Get statistics
+ getStats() {
+ return {
+ activeChunks: this.activeChunks.size,
+ chunkSize: this.chunkSize,
+ loadRadius: this.loadRadius,
+ maxChunks: Math.pow((this.loadRadius * 2 + 1), 2),
+ totalTilesLoaded: this.activeChunks.size * this.chunkSize * this.chunkSize
+ };
+ }
+
+ // Destroy all chunks
+ destroy() {
+ console.log('๐พ ChunkManager destroying all chunks...');
+
+ for (const [key, chunk] of this.activeChunks) {
+ if (chunk.sprites) {
+ chunk.sprites.forEach(sprite => {
+ if (sprite && sprite.destroy) sprite.destroy();
+ });
+ }
+ }
+
+ this.activeChunks.clear();
+ console.log('๐พ ChunkManager destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ChurchSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ChurchSystem.js
new file mode 100644
index 000000000..ace8283e5
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ChurchSystem.js
@@ -0,0 +1,511 @@
+/**
+ * CHURCH & SPIRITUAL SYSTEM
+ * Mrtva Dolina - Cerkev, Pokopaliลกฤe, ลฝupnik
+ *
+ * Features:
+ * - ลฝupnik NPC with blessing system
+ * - Church building (ruined โ restored)
+ * - Graveyard with ghost spawns
+ * - Fog atmosphere effects
+ * - Zombie Scout graveyard shortcuts
+ * - Tool blessing for dark forces bonus
+ */
+
+export class ChurchSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // ลฝupnik NPC
+ this.priest = {
+ name: 'ลฝupnik',
+ x: 0,
+ y: 0,
+ sprite: null,
+ dialogues: [],
+ blessingCost: 50, // Gold per blessing
+ blessingDuration: 300000 // 5 minutes
+ };
+
+ // Church building states
+ this.churchStates = {
+ ruined: {
+ sprite: 'church_ruined',
+ accessible: false,
+ description: 'Uniฤena cerkev, polna ruลกevin'
+ },
+ restored: {
+ sprite: 'church_restored',
+ accessible: true,
+ description: 'Obnovljena cerkev, varno zavetje'
+ }
+ };
+
+ this.currentChurchState = 'ruined';
+
+ // Graveyard
+ this.graveyard = {
+ x: 0,
+ y: 0,
+ width: 500,
+ height: 500,
+ gravestones: [],
+ ghostSpawnPoints: [],
+ fogActive: false,
+ fogParticles: null
+ };
+
+ // Blessed tools tracking
+ this.blessedTools = new Map(); // toolId -> expiryTime
+
+ // Zombie Scout graveyard shortcuts
+ this.graveyardShortcuts = [];
+
+ this.init();
+ }
+
+ init() {
+ // Setup priest dialogues
+ this.setupPriestDialogues();
+
+ // Listen for graveyard entry
+ this.scene.events.on('player:enter_graveyard', this.onEnterGraveyard, this);
+ this.scene.events.on('player:exit_graveyard', this.onExitGraveyard, this);
+
+ console.log('โ
ChurchSystem initialized');
+ }
+
+ /**
+ * ลฝUPNIK DIALOGUES & BLESSING
+ */
+ setupPriestDialogues() {
+ this.priest.dialogues = [
+ {
+ id: 'intro',
+ text: 'Pozdravljen, potnik. Sem ลฝupnik, varovatelj te cerkve od leta 2084...',
+ responses: [
+ { text: 'Povej mi o letu 2084', action: 'lore_2084' },
+ { text: 'Lahko blagosloviลก moje orodje?', action: 'request_blessing' },
+ { text: 'Nasvidenje', action: 'exit' }
+ ]
+ },
+ {
+ id: 'lore_2084',
+ text: 'Leta 2084 je svet padel... Apokalipsa je priลกla iz teme. Samo z blagoslovljenim oroลพjem lahko preลพiviลก proti mraฤnim silam.',
+ responses: [
+ { text: 'Razumem. Blagoslovi mojo katano!', action: 'request_blessing' },
+ { text: 'Hvala za zgodbo', action: 'exit' }
+ ]
+ },
+ {
+ id: 'request_blessing',
+ text: `Blagoslov zahteva darovnino ${this.priest.blessingCost} zlata. Kateri predmet ลพeliลก blagosloviti?`,
+ responses: [
+ { text: 'Katano', action: 'bless_katana' },
+ { text: 'Sekiro', action: 'bless_axe' },
+ { text: 'Kasneje', action: 'exit' }
+ ]
+ }
+ ];
+ }
+
+ interactWithPriest() {
+ // Show dialogue UI
+ this.showPriestDialogue('intro');
+ }
+
+ showPriestDialogue(dialogueId) {
+ const dialogue = this.priest.dialogues.find(d => d.id === dialogueId);
+ if (!dialogue) return;
+
+ this.scene.events.emit('show-dialogue', {
+ npc: 'ลฝupnik',
+ portrait: 'priest_portrait',
+ text: dialogue.text,
+ responses: dialogue.responses.map(r => ({
+ text: r.text,
+ callback: () => this.handleDialogueResponse(r.action)
+ }))
+ });
+ }
+
+ handleDialogueResponse(action) {
+ switch (action) {
+ case 'lore_2084':
+ this.showPriestDialogue('lore_2084');
+ break;
+ case 'request_blessing':
+ this.showPriestDialogue('request_blessing');
+ break;
+ case 'bless_katana':
+ this.blessTool('katana');
+ break;
+ case 'bless_axe':
+ this.blessTool('axe');
+ break;
+ case 'exit':
+ this.scene.events.emit('close-dialogue');
+ break;
+ }
+ }
+
+ blessTool(toolType) {
+ // Check if player has gold
+ if (!this.scene.inventorySystem.hasGold(this.priest.blessingCost)) {
+ this.scene.events.emit('show-notification', {
+ title: 'โ Premalo zlata',
+ message: `Rabiลก ${this.priest.blessingCost} zlata za blagoslov!`,
+ icon: '๐ฐ',
+ duration: 3000,
+ color: '#FF4444'
+ });
+ return;
+ }
+
+ // Deduct gold
+ this.scene.inventorySystem.removeGold(this.priest.blessingCost);
+
+ // Add blessing
+ const expiryTime = Date.now() + this.priest.blessingDuration;
+ this.blessedTools.set(toolType, expiryTime);
+
+ // Blessing VFX
+ this.showBlessingEffect();
+
+ // Update tool stats
+ this.applyBlessingBonus(toolType);
+
+ this.scene.events.emit('show-notification', {
+ title: 'โจ Blagoslovljeno!',
+ message: `${toolType.toUpperCase()} je blagoslovljen za 5 minut! +50% damage proti mraฤnim silam!`,
+ icon: '๐',
+ duration: 5000,
+ color: '#FFD700'
+ });
+
+ console.log(`โจ Blessed ${toolType} for ${this.priest.blessingDuration}ms`);
+ }
+
+ showBlessingEffect() {
+ // Golden particles around player
+ const particles = this.scene.add.particles(0, 0, 'vfx_sparkle_star', {
+ speed: { min: 50, max: 100 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 1000,
+ quantity: 20,
+ tint: 0xFFD700
+ });
+
+ particles.setPosition(this.scene.player.x, this.scene.player.y);
+ particles.explode();
+
+ setTimeout(() => particles.destroy(), 2000);
+ }
+
+ applyBlessingBonus(toolType) {
+ // Apply bonus to tool
+ if (this.scene.combatSystem) {
+ this.scene.combatSystem.setToolBonus(toolType, 'dark_forces', 1.5); // +50% vs dark enemies
+ }
+ }
+
+ isBlessedTool(toolType) {
+ const expiryTime = this.blessedTools.get(toolType);
+ if (!expiryTime) return false;
+
+ if (Date.now() > expiryTime) {
+ // Blessing expired
+ this.blessedTools.delete(toolType);
+ this.removeBlessingBonus(toolType);
+
+ this.scene.events.emit('show-notification', {
+ title: 'โฐ Blagoslov Potekel',
+ message: `${toolType.toUpperCase()} blessing has expired.`,
+ icon: '๐ซ',
+ duration: 3000,
+ color: '#999999'
+ });
+
+ return false;
+ }
+
+ return true;
+ }
+
+ removeBlessingBonus(toolType) {
+ if (this.scene.combatSystem) {
+ this.scene.combatSystem.removeToolBonus(toolType, 'dark_forces');
+ }
+ }
+
+ /**
+ * CHURCH RESTORATION
+ */
+ restoreChurch() {
+ if (this.currentChurchState === 'restored') {
+ console.log('Church already restored');
+ return;
+ }
+
+ this.currentChurchState = 'restored';
+
+ // Update building sprite
+ if (this.churchSprite) {
+ this.churchSprite.setTexture('church_restored');
+ }
+
+ // Enable priest NPC
+ this.spawnPriest();
+
+ this.scene.events.emit('show-notification', {
+ title: 'โช Cerkev Obnovljena',
+ message: 'ลฝupnik je zdaj na voljo za blagoslove!',
+ icon: 'โจ',
+ duration: 5000,
+ color: '#FFD700'
+ });
+
+ console.log('โช Church restored');
+ }
+
+ spawnPriest() {
+ // Spawn priest NPC at church entrance
+ this.priest.sprite = this.scene.add.sprite(
+ this.priest.x,
+ this.priest.y,
+ 'priest_portrait'
+ );
+ this.priest.sprite.setDepth(10);
+ this.priest.sprite.setInteractive();
+
+ this.priest.sprite.on('pointerdown', () => {
+ this.interactWithPriest();
+ });
+
+ console.log('โ
Priest spawned');
+ }
+
+ /**
+ * GRAVEYARD SYSTEM
+ */
+ setupGraveyard(x, y, width, height) {
+ this.graveyard.x = x;
+ this.graveyard.y = y;
+ this.graveyard.width = width;
+ this.graveyard.height = height;
+
+ // Generate gravestones
+ this.generateGravestones();
+
+ // Setup ghost spawn points
+ this.setupGhostSpawns();
+
+ // Setup Zombie Scout shortcuts
+ this.setupGraveyardShortcuts();
+
+ console.log(`โฐ๏ธ Graveyard setup at (${x}, ${y})`);
+ }
+
+ generateGravestones() {
+ const count = 20; // 20 gravestones
+
+ for (let i = 0; i < count; i++) {
+ const gravestone = {
+ x: this.graveyard.x + Phaser.Math.Between(0, this.graveyard.width),
+ y: this.graveyard.y + Phaser.Math.Between(0, this.graveyard.height),
+ type: Phaser.Math.Between(1, 3), // 3 types
+ sprite: null
+ };
+
+ // Create sprite
+ gravestone.sprite = this.scene.add.sprite(
+ gravestone.x,
+ gravestone.y,
+ `gravestone_${gravestone.type}`
+ );
+ gravestone.sprite.setDepth(5);
+
+ this.graveyard.gravestones.push(gravestone);
+ }
+
+ console.log(`โฐ๏ธ Generated ${count} gravestones`);
+ }
+
+ setupGhostSpawns() {
+ // 5 ghost spawn points
+ for (let i = 0; i < 5; i++) {
+ this.graveyard.ghostSpawnPoints.push({
+ x: this.graveyard.x + Phaser.Math.Between(0, this.graveyard.width),
+ y: this.graveyard.y + Phaser.Math.Between(0, this.graveyard.height)
+ });
+ }
+
+ // Start ghost spawning
+ this.startGhostSpawns();
+ }
+
+ startGhostSpawns() {
+ // Spawn ghost every 30 seconds
+ setInterval(() => {
+ if (this.isPlayerInGraveyard()) {
+ this.spawnGhost();
+ }
+ }, 30000);
+ }
+
+ spawnGhost() {
+ const spawnPoint = Phaser.Utils.Array.GetRandom(this.graveyard.ghostSpawnPoints);
+
+ this.scene.events.emit('enemy:spawn', {
+ type: 'ghost',
+ x: spawnPoint.x,
+ y: spawnPoint.y,
+ level: Phaser.Math.Between(1, 5)
+ });
+
+ console.log(`๐ป Ghost spawned at (${spawnPoint.x}, ${spawnPoint.y})`);
+ }
+
+ setupGraveyardShortcuts() {
+ // 3 secret shortcuts for Zombie Scout
+ for (let i = 0; i < 3; i++) {
+ this.graveyardShortcuts.push({
+ x: this.graveyard.x + Phaser.Math.Between(0, this.graveyard.width),
+ y: this.graveyard.y + Phaser.Math.Between(0, this.graveyard.height),
+ leadsTo: 'rare_tomb',
+ discovered: false
+ });
+ }
+
+ console.log('๐ Graveyard shortcuts setup for Zombie Scout');
+ }
+
+ /**
+ * FOG ATMOSPHERE
+ */
+ onEnterGraveyard() {
+ if (this.graveyard.fogActive) return;
+
+ // Activate fog
+ this.graveyard.fogActive = true;
+ this.createFogEffect();
+
+ this.scene.events.emit('show-notification', {
+ title: '๐ซ๏ธ Pokopaliลกฤe',
+ message: 'Mraฤna megla te objame...',
+ icon: 'โฐ๏ธ',
+ duration: 3000,
+ color: '#666666'
+ });
+
+ console.log('๐ซ๏ธ Graveyard fog activated');
+ }
+
+ createFogEffect() {
+ // Add fog particle layer
+ this.graveyard.fogParticles = this.scene.add.particles(0, 0, 'fog_particle', {
+ x: { min: this.graveyard.x, max: this.graveyard.x + this.graveyard.width },
+ y: { min: this.graveyard.y, max: this.graveyard.y + this.graveyard.height },
+ speed: 10,
+ scale: { start: 1, end: 1.5 },
+ alpha: { start: 0.3, end: 0 },
+ lifespan: 5000,
+ frequency: 100,
+ tint: 0x666666
+ });
+
+ this.graveyard.fogParticles.setDepth(20);
+
+ // Darken screen slightly
+ if (this.scene.cameras.main) {
+ this.scene.cameras.main.setAlpha(0.8);
+ }
+ }
+
+ onExitGraveyard() {
+ if (!this.graveyard.fogActive) return;
+
+ // Deactivate fog
+ this.graveyard.fogActive = false;
+
+ if (this.graveyard.fogParticles) {
+ this.graveyard.fogParticles.destroy();
+ this.graveyard.fogParticles = null;
+ }
+
+ // Restore camera alpha
+ if (this.scene.cameras.main) {
+ this.scene.cameras.main.setAlpha(1);
+ }
+
+ console.log('๐ซ๏ธ Graveyard fog deactivated');
+ }
+
+ isPlayerInGraveyard() {
+ if (!this.scene.player) return false;
+
+ const px = this.scene.player.x;
+ const py = this.scene.player.y;
+
+ return (
+ px >= this.graveyard.x &&
+ px <= this.graveyard.x + this.graveyard.width &&
+ py >= this.graveyard.y &&
+ py <= this.graveyard.y + this.graveyard.height
+ );
+ }
+
+ /**
+ * ZOMBIE SCOUT GRAVEYARD SYNERGY
+ */
+ discoverShortcut(zombieScout) {
+ const undiscovered = this.graveyardShortcuts.filter(s => !s.discovered);
+ if (undiscovered.length === 0) {
+ console.log('All shortcuts already discovered');
+ return null;
+ }
+
+ const shortcut = undiscovered[0];
+ shortcut.discovered = true;
+
+ this.scene.events.emit('show-notification', {
+ title: '๐ Zombie Scout naลกel bliลพnjico!',
+ message: 'Odkrita skrivna pot do redkih grobnic!',
+ icon: 'โฐ๏ธ',
+ duration: 5000,
+ color: '#00FF00'
+ });
+
+ console.log(`๐ Shortcut discovered at (${shortcut.x}, ${shortcut.y})`);
+
+ return shortcut;
+ }
+
+ update(delta) {
+ // Check if player entered/exited graveyard
+ const inGraveyard = this.isPlayerInGraveyard();
+
+ if (inGraveyard && !this.graveyard.fogActive) {
+ this.onEnterGraveyard();
+ } else if (!inGraveyard && this.graveyard.fogActive) {
+ this.onExitGraveyard();
+ }
+
+ // Update blessed tools expiry
+ this.blessedTools.forEach((expiryTime, toolType) => {
+ this.isBlessedTool(toolType); // Auto-removes expired
+ });
+ }
+
+ destroy() {
+ if (this.graveyard.fogParticles) {
+ this.graveyard.fogParticles.destroy();
+ }
+
+ this.graveyard.gravestones.forEach(g => {
+ if (g.sprite) g.sprite.destroy();
+ });
+
+ this.blessedTools.clear();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CinematicVoiceSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CinematicVoiceSystem.js
new file mode 100644
index 000000000..7c4ddc840
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CinematicVoiceSystem.js
@@ -0,0 +1,364 @@
+/**
+ * CINEMATIC VOICE SYSTEM
+ * Mrtva Dolina - Filmski pristop k dialogom
+ *
+ * Features:
+ * - Emocionalna globina (vdihi, premori, poudarki)
+ * - Reverb za flashbacke (Kaijevi spomini)
+ * - Ambient blending (veter, ruลกevine)
+ * - Typewriter sync (glas + tekst)
+ * - Dynamic background audio (glasba se poduลกi)
+ */
+
+export class CinematicVoiceSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.audioContext = null;
+ this.currentVoice = null;
+ this.isFlashback = false;
+
+ // Voice parameters
+ this.emotionalParams = {
+ kai_confused: { rate: 0.9, pitch: 1.0, breathPauses: true, emphasis: 'low' },
+ kai_determined: { rate: 1.0, pitch: 1.1, breathPauses: false, emphasis: 'strong' },
+ ana_gentle: { rate: 0.95, pitch: 1.15, breathPauses: true, emphasis: 'soft' },
+ ana_urgent: { rate: 1.1, pitch: 1.2, breathPauses: false, emphasis: 'strong' },
+ zombie_scout_hungry: { rate: 0.7, pitch: 0.6, breathPauses: false, emphasis: 'guttural' },
+ zombie_scout_happy: { rate: 0.8, pitch: 0.7, breathPauses: false, emphasis: 'friendly' }
+ };
+
+ // Ambient sounds
+ this.ambientSounds = new Map();
+ this.currentAmbient = null;
+
+ this.initializeAudioContext();
+ this.loadAmbientSounds();
+ }
+
+ initializeAudioContext() {
+ try {
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
+ console.log('โ
CinematicVoiceSystem: Audio Context initialized');
+ } catch (error) {
+ console.error('โ Failed to initialize Audio Context:', error);
+ }
+ }
+
+ loadAmbientSounds() {
+ // Define ambient sound layers
+ const ambients = [
+ { id: 'wind_ruins', file: 'assets/audio/ambient/wind_ruins.mp3', volume: 0.3 },
+ { id: 'crackling_fire', file: 'assets/audio/ambient/fire.mp3', volume: 0.2 },
+ { id: 'rain_outside', file: 'assets/audio/ambient/rain.mp3', volume: 0.4 },
+ { id: 'rain_inside', file: 'assets/audio/ambient/rain_muffled.mp3', volume: 0.2 }
+ ];
+
+ ambients.forEach(ambient => {
+ // These will be loaded on-demand
+ this.ambientSounds.set(ambient.id, {
+ file: ambient.file,
+ volume: ambient.volume,
+ audio: null
+ });
+ });
+ }
+
+ /**
+ * Speak dialogue with cinematic voice
+ * @param {string} text - Text to speak
+ * @param {string} character - Character name (kai, ana, zombie_scout)
+ * @param {string} emotion - Emotion type (confused, determined, gentle, urgent, hungry, happy)
+ * @param {object} options - Additional options (typewriterElement, flashback, ambient)
+ */
+ async speak(text, character, emotion, options = {}) {
+ const voiceKey = `${character}_${emotion}`;
+ const params = this.emotionalParams[voiceKey] || this.emotionalParams.kai_confused;
+
+ // Add breath pauses if enabled
+ let processedText = text;
+ if (params.breathPauses) {
+ processedText = this.addBreathPauses(text);
+ }
+
+ // Add emphasis to key words
+ processedText = this.addEmphasis(processedText, params.emphasis);
+
+ // Set ambient if specified
+ if (options.ambient) {
+ this.setAmbient(options.ambient);
+ }
+
+ // Apply flashback effect if needed
+ this.isFlashback = options.flashback || false;
+
+ // Create speech synthesis
+ const utterance = new SpeechSynthesisUtterance(processedText);
+ utterance.rate = params.rate;
+ utterance.pitch = params.pitch;
+ utterance.volume = options.volume || 0.8;
+
+ // Select voice based on character
+ const voice = this.selectVoice(character);
+ if (voice) {
+ utterance.voice = voice;
+ }
+
+ // Sync with typewriter if provided
+ if (options.typewriterElement) {
+ this.syncWithTypewriter(utterance, options.typewriterElement, text);
+ }
+
+ // Duck background music
+ if (this.scene.sound && this.scene.sound.get('background_music')) {
+ this.duckMusic(0.3, 500); // Lower to 30% over 500ms
+ }
+
+ // Apply reverb for flashbacks
+ if (this.isFlashback && this.audioContext) {
+ await this.applyReverbEffect(utterance);
+ } else {
+ // Standard speech
+ window.speechSynthesis.speak(utterance);
+ }
+
+ // Return promise that resolves when speech ends
+ return new Promise((resolve) => {
+ utterance.onend = () => {
+ // Restore music volume
+ if (this.scene.sound && this.scene.sound.get('background_music')) {
+ this.duckMusic(1.0, 800); // Restore to 100% over 800ms
+ }
+ resolve();
+ };
+ });
+ }
+
+ /**
+ * Add natural breath pauses to text
+ */
+ addBreathPauses(text) {
+ // Add slight pauses after commas and periods
+ return text
+ .replace(/,/g, ',')
+ .replace(/\./g, '.');
+ }
+
+ /**
+ * Add emphasis to key words
+ */
+ addEmphasis(text, emphasisType) {
+ if (emphasisType === 'strong') {
+ // Emphasize question words and important terms
+ const keywords = ['kje', 'kaj', 'kdo', 'zakaj', 'kako', 'Ana', 'Kai', 'spomin'];
+ keywords.forEach(word => {
+ const regex = new RegExp(`\\b${word}\\b`, 'gi');
+ text = text.replace(regex, `${word}`);
+ });
+ } else if (emphasisType === 'soft') {
+ // Soft emphasis for gentle speech
+ const regex = /([A-Zฤล ลฝ][a-zฤลกลพ]+)/g;
+ text = text.replace(regex, '$1');
+ }
+ return text;
+ }
+
+ /**
+ * Select appropriate voice for character
+ */
+ selectVoice(character) {
+ const voices = window.speechSynthesis.getVoices();
+
+ // Prefer Slovenian voices, fallback to similar languages
+ const preferredLangs = ['sl-SI', 'hr-HR', 'sr-RS', 'en-US'];
+
+ if (character === 'kai') {
+ // Male voice
+ return voices.find(v =>
+ preferredLangs.some(lang => v.lang.startsWith(lang.split('-')[0])) &&
+ v.name.toLowerCase().includes('male')
+ ) || voices[0];
+ } else if (character === 'ana') {
+ // Female voice
+ return voices.find(v =>
+ preferredLangs.some(lang => v.lang.startsWith(lang.split('-')[0])) &&
+ v.name.toLowerCase().includes('female')
+ ) || voices[1];
+ } else if (character === 'zombie_scout') {
+ // Deep, gravelly voice
+ return voices.find(v =>
+ v.name.toLowerCase().includes('deep') ||
+ v.name.toLowerCase().includes('bass')
+ ) || voices[0];
+ }
+
+ return voices[0];
+ }
+
+ /**
+ * Sync voice with typewriter text animation
+ */
+ syncWithTypewriter(utterance, element, fullText) {
+ const charDuration = (utterance.rate > 0) ? (60 / utterance.rate) : 60; // ms per character
+
+ utterance.onboundary = (event) => {
+ // Update displayed text as speech progresses
+ const charIndex = event.charIndex;
+ if (element && charIndex < fullText.length) {
+ element.textContent = fullText.substring(0, charIndex + 1);
+ }
+ };
+ }
+
+ /**
+ * Apply reverb effect for flashback sequences
+ */
+ async applyReverbEffect(utterance) {
+ if (!this.audioContext) return;
+
+ try {
+ // Create convolver for reverb
+ const convolver = this.audioContext.createConvolver();
+ const reverbTime = 2.0; // 2 seconds reverb
+
+ // Generate impulse response
+ const sampleRate = this.audioContext.sampleRate;
+ const length = sampleRate * reverbTime;
+ const impulse = this.audioContext.createBuffer(2, length, sampleRate);
+
+ for (let channel = 0; channel < 2; channel++) {
+ const channelData = impulse.getChannelData(channel);
+ for (let i = 0; i < length; i++) {
+ channelData[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / length, 2);
+ }
+ }
+
+ convolver.buffer = impulse;
+
+ // Note: SpeechSynthesis doesn't directly support Web Audio routing
+ // This is a placeholder for when we implement proper audio streaming
+ console.log('๐๏ธ Reverb effect would be applied here (requires audio streaming)');
+
+ // Fallback: Just speak with modified parameters for now
+ window.speechSynthesis.speak(utterance);
+
+ } catch (error) {
+ console.error('โ Reverb effect failed:', error);
+ window.speechSynthesis.speak(utterance);
+ }
+ }
+
+ /**
+ * Duck/restore background music volume
+ */
+ duckMusic(targetVolume, duration) {
+ const music = this.scene.sound.get('background_music');
+ if (!music) return;
+
+ this.scene.tweens.add({
+ targets: music,
+ volume: targetVolume,
+ duration: duration,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ /**
+ * Set ambient background sound
+ */
+ setAmbient(ambientId) {
+ // Stop current ambient
+ if (this.currentAmbient) {
+ if (this.currentAmbient.audio) {
+ this.currentAmbient.audio.pause();
+ }
+ }
+
+ // Start new ambient
+ const ambient = this.ambientSounds.get(ambientId);
+ if (ambient) {
+ if (!ambient.audio) {
+ ambient.audio = new Audio(ambient.file);
+ ambient.audio.loop = true;
+ ambient.audio.volume = ambient.volume;
+ }
+
+ ambient.audio.play().catch(err => {
+ console.warn('Ambient sound play failed:', err);
+ });
+
+ this.currentAmbient = ambient;
+ }
+ }
+
+ /**
+ * Blend voice with ambient (main feature)
+ */
+ blendWithAmbient(voiceVolume = 0.8, ambientVolume = 0.3) {
+ if (this.currentAmbient && this.currentAmbient.audio) {
+ this.currentAmbient.audio.volume = ambientVolume;
+ }
+ // Voice volume is set in speak() method
+ }
+
+ /**
+ * Stop all audio
+ */
+ stopAll() {
+ window.speechSynthesis.cancel();
+
+ if (this.currentAmbient && this.currentAmbient.audio) {
+ this.currentAmbient.audio.pause();
+ }
+ }
+
+ /**
+ * ZOMBIE SCOUT SPECIFIC SOUNDS
+ */
+ zombieScoutHunger() {
+ const hungerLines = [
+ 'Braaaaains...',
+ 'Moลพgaaaaani...',
+ 'Hrrrngh... laฤen...',
+ '*zombie groan*'
+ ];
+
+ const randomLine = hungerLines[Math.floor(Math.random() * hungerLines.length)];
+ this.speak(randomLine, 'zombie_scout', 'hungry', {
+ volume: 0.6,
+ ambient: 'wind_ruins'
+ });
+ }
+
+ zombieScoutDiscovery() {
+ const discoveryLines = [
+ '*tiho godrnjanje*',
+ 'Hrrm! Tukaj!',
+ '*zadovoljno zavijanje*'
+ ];
+
+ const randomLine = discoveryLines[Math.floor(Math.random() * discoveryLines.length)];
+ this.speak(randomLine, 'zombie_scout', 'happy', {
+ volume: 0.7
+ });
+ }
+
+ /**
+ * Play zombie scout footstep with gear sounds
+ */
+ zombieScoutFootstep() {
+ // Composite sound: footstep + gear rattle
+ const footstep = this.scene.sound.add('zombie_footstep', { volume: 0.4 });
+ const gearRattle = this.scene.sound.add('gear_rattle', { volume: 0.2 });
+
+ footstep.play();
+ setTimeout(() => gearRattle.play(), 50); // Slight delay for realism
+ }
+
+ destroy() {
+ this.stopAll();
+
+ if (this.audioContext) {
+ this.audioContext.close();
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CityGratitudeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CityGratitudeSystem.js
new file mode 100644
index 000000000..3533f9863
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CityGratitudeSystem.js
@@ -0,0 +1,331 @@
+/**
+ * CITY GRATITUDE GIFT SYSTEM
+ * Population milestone rewards
+ * Unique equipment and bonuses
+ */
+
+export class CityGratitudeSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Milestone tracking
+ this.milestones = new Map();
+ this.claimedMilestones = new Set();
+
+ // City reputation
+ this.reputation = 0;
+
+ this.init();
+ }
+
+ init() {
+ this.initializeMilestones();
+
+ // Check milestones when population changes
+ this.scene.events.on('population_changed', () => this.checkMilestones());
+ }
+
+ /**
+ * Initialize population milestones
+ */
+ initializeMilestones() {
+ this.registerMilestone({
+ population: 10,
+ name: 'Small Community',
+ rewards: {
+ items: [{ id: 'starter_hoe', name: 'Community Hoe', rarity: 'uncommon' }],
+ currency: 500,
+ reputation: 50
+ },
+ description: '10 settlers joined your farm!'
+ });
+
+ this.registerMilestone({
+ population: 25,
+ name: 'Growing Village',
+ rewards: {
+ items: [
+ { id: 'village_axe', name: 'Village Axe', rarity: 'rare' },
+ { id: 'mayor_blessing', name: 'Mayor\'s Blessing', rarity: 'rare', type: 'buff' }
+ ],
+ currency: 1500,
+ reputation: 100,
+ unlock: 'town_hall'
+ },
+ description: '25 settlers! The village recognizes your leadership.'
+ });
+
+ this.registerMilestone({
+ population: 50,
+ name: 'Thriving Town',
+ rewards: {
+ items: [
+ { id: 'master_scythe', name: 'Master Scythe', rarity: 'epic' },
+ { id: 'town_armor', name: 'Town Guardian Armor', rarity: 'epic' }
+ ],
+ currency: 5000,
+ reputation: 250,
+ unlock: 'advanced_workshop'
+ },
+ description: '50 settlers! Your town flourishes.'
+ });
+
+ this.registerMilestone({
+ population: 100,
+ name: 'Major City',
+ rewards: {
+ items: [
+ { id: 'legendary_pickaxe', name: 'Pickaxe of Prosperity', rarity: 'legendary' },
+ { id: 'city_crown', name: 'Crown of the People', rarity: 'legendary' },
+ { id: 'citizens_gratitude', name: 'Citizens\' Eternal Gratitude', rarity: 'legendary', type: 'permanent_buff' }
+ ],
+ currency: 15000,
+ reputation: 500,
+ unlock: 'city_monument'
+ },
+ description: '100 settlers! You\'ve built a magnificent city!'
+ });
+
+ this.registerMilestone({
+ population: 200,
+ name: 'Metropolis',
+ rewards: {
+ items: [
+ { id: 'omega_tool_set', name: 'Omega Tool Set', rarity: 'mythic' },
+ { id: 'metropolis_throne', name: 'Throne of Dominion', rarity: 'mythic' }
+ ],
+ currency: 50000,
+ reputation: 1000,
+ unlock: 'palace',
+ specialBonus: 'All workers +100% efficiency'
+ },
+ description: '200 settlers! Your metropolis is legendary!'
+ });
+ }
+
+ /**
+ * Register a milestone
+ */
+ registerMilestone(milestoneData) {
+ this.milestones.set(milestoneData.population, {
+ ...milestoneData,
+ claimed: false
+ });
+ }
+
+ /**
+ * Check if any milestones reached
+ */
+ checkMilestones() {
+ const currentPopulation = this.scene.gameState?.population || 0;
+
+ this.milestones.forEach((milestone, requiredPop) => {
+ if (currentPopulation >= requiredPop && !this.claimedMilestones.has(requiredPop)) {
+ this.triggerMilestone(requiredPop);
+ }
+ });
+ }
+
+ /**
+ * Trigger milestone reward
+ */
+ triggerMilestone(population) {
+ const milestone = this.milestones.get(population);
+ if (!milestone) return;
+
+ // Mark as claimed
+ this.claimedMilestones.add(population);
+ milestone.claimed = true;
+
+ // Grant rewards
+ this.grantRewards(milestone.rewards);
+
+ // Special bonuses
+ if (milestone.specialBonus) {
+ this.applySpecialBonus(milestone.specialBonus);
+ }
+
+ // Reputation
+ this.reputation += milestone.rewards.reputation || 0;
+
+ // Cinematic notification
+ this.scene.uiSystem?.showMilestoneNotification(
+ milestone.name,
+ milestone.description,
+ milestone.rewards
+ );
+
+ // VFX: Celebration
+ this.scene.vfxSystem?.playEffect('city_celebration', 400, 300);
+ this.scene.cameras.main.flash(2000, 255, 215, 0);
+
+ // SFX
+ this.scene.soundSystem?.play('milestone_fanfare');
+
+ console.log(`๐ MILESTONE REACHED: ${milestone.name} (${population} population)`);
+ }
+
+ /**
+ * Grant milestone rewards
+ */
+ grantRewards(rewards) {
+ // Currency
+ if (rewards.currency) {
+ this.scene.economySystem?.addCurrency(rewards.currency);
+ console.log(`+${rewards.currency} coins`);
+ }
+
+ // Items
+ if (rewards.items) {
+ rewards.items.forEach(item => {
+ this.scene.inventorySystem?.addItem(item.id, 1);
+ console.log(`Received: ${item.name} (${item.rarity})`);
+
+ // Special item effects
+ if (item.type === 'buff') {
+ this.applyItemBuff(item);
+ } else if (item.type === 'permanent_buff') {
+ this.applyPermanentBuff(item);
+ }
+ });
+ }
+
+ // Unlocks
+ if (rewards.unlock) {
+ this.scene.gameState.unlocks[rewards.unlock] = true;
+ console.log(`Unlocked: ${rewards.unlock}`);
+ }
+ }
+
+ /**
+ * Apply item buff
+ */
+ applyItemBuff(item) {
+ switch (item.id) {
+ case 'mayor_blessing':
+ this.scene.gameState.buffs.mayor_blessing = {
+ crop_yield: 1.25,
+ building_speed: 1.25,
+ duration: Infinity // Permanent
+ };
+ break;
+ }
+ }
+
+ /**
+ * Apply permanent buff
+ */
+ applyPermanentBuff(item) {
+ switch (item.id) {
+ case 'citizens_gratitude':
+ // Permanent +25% to all production
+ this.scene.gameState.buffs.citizens_gratitude = {
+ all_production: 1.25,
+ all_efficiency: 1.25
+ };
+ break;
+ }
+ }
+
+ /**
+ * Apply special bonus
+ */
+ applySpecialBonus(bonus) {
+ if (bonus === 'All workers +100% efficiency') {
+ this.scene.npcSettlementSystem?.settledNPCs.forEach((npc, npcId) => {
+ const currentEff = this.scene.npcSettlementSystem.npcEfficiency.get(npcId) || 1.0;
+ this.scene.npcSettlementSystem.npcEfficiency.set(npcId, currentEff * 2.0);
+ });
+ }
+ }
+
+ /**
+ * Get next milestone
+ */
+ getNextMilestone() {
+ const currentPopulation = this.scene.gameState?.population || 0;
+
+ for (const [reqPop, milestone] of this.milestones.entries()) {
+ if (reqPop > currentPopulation) {
+ return {
+ population: reqPop,
+ ...milestone,
+ progress: Math.min(100, (currentPopulation / reqPop) * 100)
+ };
+ }
+ }
+
+ return null; // All milestones claimed
+ }
+
+ /**
+ * Get all milestones
+ */
+ getAllMilestones() {
+ return Array.from(this.milestones.entries()).map(([pop, milestone]) => ({
+ population: pop,
+ ...milestone,
+ claimed: this.claimedMilestones.has(pop)
+ }));
+ }
+
+ /**
+ * Get claimed milestones
+ */
+ getClaimedMilestones() {
+ return this.getAllMilestones().filter(m => m.claimed);
+ }
+
+ /**
+ * Get city reputation
+ */
+ getReputation() {
+ return this.reputation;
+ }
+
+ /**
+ * Get reputation tier
+ */
+ getReputationTier() {
+ if (this.reputation >= 1000) return 'Legendary';
+ if (this.reputation >= 500) return 'Renowned';
+ if (this.reputation >= 250) return 'Well-Known';
+ if (this.reputation >= 100) return 'Respected';
+ if (this.reputation >= 50) return 'Known';
+ return 'Unknown';
+ }
+
+ /**
+ * Manual milestone trigger (for testing)
+ */
+ triggerMilestoneManual(population) {
+ if (!this.claimedMilestones.has(population)) {
+ this.triggerMilestone(population);
+ }
+ }
+
+ /**
+ * Save/Load
+ */
+ getSaveData() {
+ return {
+ claimedMilestones: Array.from(this.claimedMilestones),
+ reputation: this.reputation
+ };
+ }
+
+ loadSaveData(data) {
+ this.claimedMilestones = new Set(data.claimedMilestones || []);
+ this.reputation = data.reputation || 0;
+
+ // Mark milestones as claimed
+ this.claimedMilestones.forEach(pop => {
+ const milestone = this.milestones.get(pop);
+ if (milestone) {
+ milestone.claimed = true;
+ // Re-apply permanent buffs
+ this.grantRewards(milestone.rewards);
+ }
+ });
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CityManagementSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CityManagementSystem.js
new file mode 100644
index 000000000..b3db0f324
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CityManagementSystem.js
@@ -0,0 +1,516 @@
+/**
+ * ๐๏ธ CITY MANAGEMENT SYSTEM - Week 1 Priority
+ * Population tracking + Zombie Statistician NPC + City Hall
+ *
+ * Features:
+ * - Population statistics tracking (living, zombies, workers)
+ * - Zombie Statistician NPC employment (1 Cekin/day)
+ * - Daily population board updates
+ * - City Hall building placement
+ * - Worker management integration
+ *
+ * Assets Used:
+ * - /assets/characters/zombie_statistician/ (11 sprites)
+ * - /assets/buildings/city_hall.png
+ * - /assets/buildings/population_board.png
+ * - /assets/ui/currency_cekin.png
+ */
+
+export default class CityManagementSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Population statistics
+ this.population = {
+ living: 1, // Player starts
+ zombies: 0,
+ workers: 0, // Employed NPCs
+ total: 1
+ };
+
+ // Buildings
+ this.cityHall = null;
+ this.populationBoard = null;
+
+ // Zombie Statistician NPC
+ this.statistician = null;
+ this.statisticianEmployed = false;
+ this.statisticianSalary = 1; // Cekini per day
+ this.lastUpdateTime = null;
+
+ // Board update schedule
+ this.updateHour = 9; // Updates at 9 AM daily
+ this.hasUpdatedToday = false;
+
+ this.init();
+ }
+
+ init() {
+ console.log('[CityManagement] Initializing city management system...');
+
+ // Load sprites
+ this.loadSprites();
+
+ // Setup daily update check
+ this.setupDailyUpdate();
+ }
+
+ loadSprites() {
+ // City Hall
+ if (!this.scene.textures.exists('city_hall')) {
+ this.scene.load.image('city_hall', 'assets/buildings/city_hall.png');
+ }
+
+ // Population board
+ if (!this.scene.textures.exists('population_board')) {
+ this.scene.load.image('population_board', 'assets/buildings/population_board.png');
+ }
+
+ // Zombie Statistician sprites
+ const directions = ['south', 'north', 'east', 'west'];
+ const actions = ['idle', 'walk'];
+
+ directions.forEach(dir => {
+ actions.forEach(action => {
+ const key = `statistician_${action}_${dir}`;
+ if (!this.scene.textures.exists(key)) {
+ this.scene.load.image(key, `assets/characters/zombie_statistician/${action}_${dir}.png`);
+ }
+ });
+ });
+
+ // Action sprites
+ if (!this.scene.textures.exists('statistician_action_update')) {
+ this.scene.load.image('statistician_action_update', 'assets/characters/zombie_statistician/action_update.png');
+ }
+ if (!this.scene.textures.exists('statistician_action_calculate')) {
+ this.scene.load.image('statistician_action_calculate', 'assets/characters/zombie_statistician/action_calculate.png');
+ }
+ if (!this.scene.textures.exists('statistician_portrait')) {
+ this.scene.load.image('statistician_portrait', 'assets/characters/zombie_statistician/portrait.png');
+ }
+
+ // Currency icon
+ if (!this.scene.textures.exists('currency_cekin')) {
+ this.scene.load.image('currency_cekin', 'assets/ui/currency_cekin.png');
+ }
+
+ this.scene.load.start();
+ }
+
+ setupDailyUpdate() {
+ // Check for daily update every hour
+ this.scene.time.addEvent({
+ delay: 60000, // Check every minute (in-game might be faster)
+ callback: () => this.checkDailyUpdate(),
+ loop: true
+ });
+ }
+
+ /**
+ * Place City Hall building
+ */
+ placeCityHall(x, y) {
+ if (this.cityHall) {
+ console.warn('[CityManagement] City Hall already exists!');
+ return;
+ }
+
+ this.cityHall = this.scene.add.sprite(x, y, 'city_hall');
+ this.cityHall.setOrigin(0.5, 0.5);
+ this.cityHall.setInteractive();
+
+ // Click to open city management UI
+ this.cityHall.on('pointerdown', () => {
+ this.openCityManagementUI();
+ });
+
+ console.log('[CityManagement] City Hall placed at', x, y);
+ }
+
+ /**
+ * Place Population Board
+ */
+ placePopulationBoard(x, y) {
+ if (this.populationBoard) {
+ console.warn('[CityManagement] Population Board already exists!');
+ return;
+ }
+
+ this.populationBoard = this.scene.add.sprite(x, y, 'population_board');
+ this.populationBoard.setOrigin(0.5, 0.5);
+ this.populationBoard.setInteractive();
+
+ // Create text overlay for statistics
+ this.createBoardText(x, y);
+
+ // Click to view detailed stats
+ this.populationBoard.on('pointerdown', () => {
+ this.showDetailedStats();
+ });
+
+ console.log('[CityManagement] Population Board placed at', x, y);
+ }
+
+ /**
+ * Create text overlay on population board
+ */
+ createBoardText(x, y) {
+ const textStyle = {
+ fontSize: '14px',
+ fill: '#ffffff',
+ stroke: '#000000',
+ strokeThickness: 2,
+ align: 'left'
+ };
+
+ this.boardText = this.scene.add.text(x - 30, y - 20, '', textStyle);
+ this.updateBoardText();
+ }
+
+ /**
+ * Update board text with current stats
+ */
+ updateBoardText() {
+ if (!this.boardText) return;
+
+ const text = `Population: ${this.population.living}\nZombies: ${this.population.zombies}\nWorkers: ${this.population.workers}`;
+ this.boardText.setText(text);
+ }
+
+ /**
+ * Spawn Zombie Statistician NPC
+ */
+ spawnStatistician(x, y) {
+ if (this.statistician) {
+ console.warn('[CityManagement] Statistician already exists!');
+ return;
+ }
+
+ this.statistician = this.scene.add.sprite(x, y, 'statistician_idle_south');
+ this.statistician.setOrigin(0.5, 0.5);
+ this.statistician.setInteractive();
+
+ // NPC data
+ this.statistician.npcData = {
+ name: 'Zombie Statistician',
+ type: 'office_zombie',
+ employed: false,
+ salary: this.statisticianSalary,
+ workLocation: this.populationBoard,
+ isZombie: true
+ };
+
+ // Click to hire/talk
+ this.statistician.on('pointerdown', () => {
+ this.interactWithStatistician();
+ });
+
+ // Basic idle animation
+ this.createStatisticianIdleAnimation();
+
+ console.log('[CityManagement] Zombie Statistician spawned at', x, y);
+ }
+
+ /**
+ * Create idle animation for Statistician
+ */
+ createStatisticianIdleAnimation() {
+ if (!this.statistician) return;
+
+ this.scene.time.addEvent({
+ delay: 2000,
+ callback: () => {
+ if (this.statistician && !this.statistician.isWalking) {
+ // Randomly look around (change direction)
+ const directions = ['south', 'north', 'east', 'west'];
+ const randomDir = Phaser.Utils.Array.GetRandom(directions);
+ this.statistician.setTexture(`statistician_idle_${randomDir}`);
+ }
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * Interact with Statistician (hire or talk)
+ */
+ interactWithStatistician() {
+ if (!this.statistician) return;
+
+ if (!this.statisticianEmployed) {
+ // Show employment dialog
+ this.showEmploymentDialog();
+ } else {
+ // Show stats dialog
+ this.showStatisticianDialog();
+ }
+ }
+
+ /**
+ * Show employment dialog
+ */
+ showEmploymentDialog() {
+ const dialogText = `Would you like to employ me?\n\nSalary: ${this.statisticianSalary} Cekin/day\n\nI will update the population board daily at ${this.updateHour}:00 AM with accurate statistics.`;
+
+ if (this.scene.dialogueSystem) {
+ this.scene.dialogueSystem.showDialog({
+ portrait: 'statistician_portrait',
+ name: 'Zombie Statistician',
+ text: dialogText,
+ choices: [
+ {
+ text: `Hire (${this.statisticianSalary} Cekin/day)`,
+ callback: () => this.hireStatistician()
+ },
+ {
+ text: 'Not now',
+ callback: () => console.log('[CityManagement] Employment declined')
+ }
+ ]
+ });
+ } else {
+ console.log('[CityManagement] Employment dialog:', dialogText);
+ // Auto-hire for now
+ this.hireStatistician();
+ }
+ }
+
+ /**
+ * Hire the Statistician
+ */
+ hireStatistician() {
+ // Check if player has enough money (if economy system exists)
+ if (this.scene.economySystem) {
+ if (!this.scene.economySystem.hasEnoughCekini(this.statisticianSalary)) {
+ console.warn('[CityManagement] Not enough Cekini to hire Statistician!');
+ return;
+ this.statisticianEmployed = true;
+ this.statistician.npcData.employed = true;
+ this.population.workers++;
+
+ // Add to worker count
+ this.updatePopulation();
+
+ console.log('[CityManagement] Zombie Statistician hired! Salary:', this.statisticianSalary, 'Cekini/day');
+
+ // Start daily work routine
+ this.startStatisticianWorkRoutine();
+ }
+
+ /**
+ * Start Statistician's daily work routine
+ */
+ startStatisticianWorkRoutine() {
+ if (!this.statisticianEmployed) return;
+
+ // Move to board at update time
+ this.scene.time.addEvent({
+ delay: 60000, // Check every minute
+ callback: () => {
+ // Check if it's update time
+ if (this.scene.timeSystem) {
+ const hour = this.scene.timeSystem.getCurrentHour();
+ if (hour === this.updateHour && !this.hasUpdatedToday) {
+ this.statisticianUpdateBoard();
+ }
+ }
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * Statistician updates the board (animation + action)
+ */
+ statisticianUpdateBoard() {
+ if (!this.statistician || !this.populationBoard) return;
+
+ console.log('[CityManagement] Statistician updating population board...');
+
+ // Walk to board
+ const targetX = this.populationBoard.x;
+ const targetY = this.populationBoard.y + 50; // Stand in front
+
+ // Simple tween movement
+ this.scene.tweens.add({
+ targets: this.statistician,
+ x: targetX,
+ y: targetY,
+ duration: 2000,
+ ease: 'Linear',
+ onUpdate: () => {
+ // Use walk animation
+ this.statistician.setTexture('statistician_walk_south');
+ },
+ onComplete: () => {
+ // Show update action
+ this.statistician.setTexture('statistician_action_update');
+
+ // Update the board after 2 seconds
+ this.scene.time.delayedCall(2000, () => {
+ this.updateBoardText();
+ this.hasUpdatedToday = true;
+
+ // Return to idle
+ this.statistician.setTexture('statistician_idle_south');
+
+ console.log('[CityManagement] Board updated!');
+ });
+ }
+ });
+ }
+
+ /**
+ * Check if daily update should happen
+ */
+ checkDailyUpdate() {
+ // Reset flag at midnight
+ if (this.scene.timeSystem) {
+ const hour = this.scene.timeSystem.getCurrentHour();
+ if (hour === 0 && this.hasUpdatedToday) {
+ this.hasUpdatedToday = false;
+ }
+ }
+ }
+
+ /**
+ * Update population stats
+ */
+ updatePopulation(living = null, zombies = null, workers = null) {
+ if (living !== null) this.population.living = living;
+ if (zombies !== null) this.population.zombies = zombies;
+ if (workers !== null) this.population.workers = workers;
+
+ this.population.total = this.population.living + this.population.zombies;
+
+ this.updateBoardText();
+
+ console.log('[CityManagement] Population updated:', this.population);
+ }
+
+ /**
+ * Add population (new settler, zombie conversion, etc.)
+ */
+ addPopulation(type, count = 1) {
+ if (type === 'living') {
+ this.population.living += count;
+ } else if (type === 'zombie') {
+ this.population.zombies += count;
+ } else if (type === 'worker') {
+ this.population.workers += count;
+ }
+
+ this.updatePopulation();
+ }
+
+ /**
+ * Remove population (death, leaving, etc.)
+ */
+ removePopulation(type, count = 1) {
+ if (type === 'living') {
+ this.population.living = Math.max(0, this.population.living - count);
+ } else if (type === 'zombie') {
+ this.population.zombies = Math.max(0, this.population.zombies - count);
+ } else if (type === 'worker') {
+ this.population.workers = Math.max(0, this.population.workers - count);
+ }
+
+ this.updatePopulation();
+ }
+
+ /**
+ * Show detailed statistics in UI
+ */
+ showDetailedStats() {
+ const stats = `
+=== CITY STATISTICS ===
+Living Population: ${this.population.living}
+Zombie Population: ${this.population.zombies}
+Employed Workers: ${this.population.workers}
+Total Population: ${this.population.total}
+
+Updated by: ${this.statisticianEmployed ? 'Zombie Statistician' : 'Manual'}
+Last Update: ${this.hasUpdatedToday ? 'Today' : 'Not today'}
+ `;
+
+ console.log(stats);
+
+ if (this.scene.centralPopupSystem) {
+ this.scene.centralPopupSystem.showMessage(stats, 'info');
+ }
+ }
+
+ /**
+ * Open City Management UI
+ */
+ openCityManagementUI() {
+ console.log('[CityManagement] Opening city management UI...');
+
+ // TODO: Create full city management UI panel
+ // For now, show stats
+ this.showDetailedStats();
+ }
+
+ /**
+ * Show Statistician dialogue
+ */
+ showStatisticianDialog() {
+ const dialogues = [
+ "Population count: accurate as always.",
+ "The numbers... they must be precise.",
+ "32 zombies counted this morning.",
+ "Statistics show a 2% increase in workers.",
+ "Even in undeath, I serve the data.",
+ "The board is updated daily at 9 AM sharp."
+ ];
+
+ const randomDialogue = Phaser.Utils.Array.GetRandom(dialogues);
+
+ if (this.scene.dialogueSystem) {
+ this.scene.dialogueSystem.showDialog({
+ portrait: 'statistician_portrait',
+ name: 'Zombie Statistician',
+ text: randomDialogue
+ });
+ } else {
+ console.log('[CityManagement] Statistician:', randomDialogue);
+ }
+ }
+
+ /**
+ * Get current population stats
+ */
+ getPopulation() {
+ return { ...this.population };
+ }
+
+ /**
+ * Pay daily salaries
+ */
+ payDailySalaries() {
+ if (!this.statisticianEmployed) return;
+
+ if (this.scene.economySystem) {
+ const paid = this.scene.economySystem.spendCekini(this.statisticianSalary);
+
+ if (paid) {
+ console.log(`[CityManagement] Paid Statistician ${this.statisticianSalary} Cekini`);
+ } else {
+ console.warn('[CityManagement] Not enough Cekini to pay Statistician!');
+ // Could fire worker or create debt
+ }
+ }
+ }
+
+ update(time, delta) {
+ // System updates handled by time events
+ }
+
+ destroy() {
+ if (this.cityHall) this.cityHall.destroy();
+ if (this.populationBoard) this.populationBoard.destroy();
+ if (this.boardText) this.boardText.destroy();
+ if (this.statistician) this.statistician.destroy();
+ }
+ }
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CollectionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CollectionSystem.js
new file mode 100644
index 000000000..3798129fd
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CollectionSystem.js
@@ -0,0 +1,78 @@
+class CollectionSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.unlockedItems = new Set();
+
+ // Database of Collectables
+ this.items = {
+ // Resources
+ 'wood': { name: 'Wood', desc: 'Basic building material.', category: 'Resource' },
+ 'stone': { name: 'Stone', desc: 'Hard rock for walls.', category: 'Resource' },
+ 'ore_gold': { name: 'Gold Ore', desc: 'Shiny ore found underground.', category: 'Resource' },
+ 'gold_bar': { name: 'Gold Bar', desc: 'Refined gold ingot.', category: 'Resource' },
+
+ // Crops
+ 'seeds': { name: 'Seeds', desc: 'Mystery seeds.', category: 'Farming' },
+ 'wheat': { name: 'Wheat', desc: 'Staple grain.', category: 'Farming' },
+ 'corn': { name: 'Corn', desc: 'Tall growing crop.', category: 'Farming' },
+
+ // Rare
+ 'item_bone': { name: 'Bone', desc: 'Remains of a zombie.', category: 'Rare' },
+ 'item_scrap': { name: 'Scrap Metal', desc: 'Old machinery parts.', category: 'Rare' },
+ 'item_chip': { name: 'Microchip', desc: 'High-tech component.', category: 'Rare' },
+ 'coin_gold': { name: 'Gold Coin', desc: 'Currency of the new world.', category: 'Rare' },
+ 'artefact_old': { name: 'Ancient Pot', desc: 'A relict from the past.', category: 'Archaeology' },
+
+ // Nature
+ 'flower_red': { name: 'Red Flower', desc: 'Beautiful bloom.', category: 'Nature' },
+ 'flower_yellow': { name: 'Yellow Flower', desc: 'Bright bloom.', category: 'Nature' },
+ 'flower_blue': { name: 'Blue Flower', desc: 'Rare blue bloom.', category: 'Nature' },
+ 'mushroom_red': { name: 'Red Mushroom', desc: 'Looks poisonous.', category: 'Nature' },
+ 'mushroom_brown': { name: 'Brown Mushroom', desc: 'Edible fungus.', category: 'Nature' }
+ };
+ }
+
+ unlock(itemId) {
+ if (!this.items[itemId]) return; // Not a collectable
+
+ if (!this.unlockedItems.has(itemId)) {
+ this.unlockedItems.add(itemId);
+ console.log(`๐ Collection Unlocked: ${itemId}`);
+
+ // Notification
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 80,
+ text: `New Collection Entry!`,
+ color: '#FFD700'
+ });
+
+ if (this.scene.soundManager && typeof this.scene.soundManager.playSuccess === 'function') {
+ this.scene.soundManager.playSuccess();
+ } else if (this.scene.soundManager && typeof this.scene.soundManager.playHarvest === 'function') {
+ this.scene.soundManager.playHarvest(); // Fallback to harvest sound
+ }
+ }
+ }
+
+ has(itemId) {
+ return this.unlockedItems.has(itemId);
+ }
+
+ getProgress() {
+ const total = Object.keys(this.items).length;
+ const unlocked = this.unlockedItems.size;
+ return { unlocked, total, percent: (unlocked / total) * 100 };
+ }
+
+ // Save/Load Logic
+ toJSON() {
+ return Array.from(this.unlockedItems);
+ }
+
+ load(data) {
+ if (Array.isArray(data)) {
+ this.unlockedItems = new Set(data);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CookingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CookingSystem.js
new file mode 100644
index 000000000..192f24b22
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CookingSystem.js
@@ -0,0 +1,475 @@
+/**
+ * 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');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CourierSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CourierSystem.js
new file mode 100644
index 000000000..9f286b7ec
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CourierSystem.js
@@ -0,0 +1,337 @@
+/**
+ * COURIER & DELIVERY SYSTEM
+ * Mrtva Dolina - Side-Quest Material Deliveries
+ *
+ * Features:
+ * - Random delivery quests from NPCs
+ * - Rewards: Hearts (social status) OR random materials
+ * - Material types: wheat, water, glass, steel, wood, plastic
+ * - Reputation system
+ */
+
+export class CourierSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Social status (hearts)
+ this.socialStatus = 0; // 0-100
+ this.hearts = 0; // Visual hearts earned
+
+ // Active courier quests
+ this.activeDeliveries = [];
+
+ // NPC delivery requests database
+ this.deliveryTemplates = [
+ // PEK
+ { npc: 'pek', item: 'wheat', quantity: [5, 10], reward: { type: 'hearts', amount: 2 }, message: 'Rabim pลกenico za kruh!' },
+ { npc: 'pek', item: 'water', quantity: [3, 5], reward: { type: 'material', pool: ['wood', 'stone'] }, message: 'Dej mi vodo, pa ti dam material!' },
+
+ // ล IVILJA
+ { npc: 'sivilja', item: 'cloth', quantity: [10, 20], reward: { type: 'hearts', amount: 3 }, message: 'Material rabim za ลกivanje, prosim!' },
+ {
+ npc: 'sivilja', item: 'water', quantity: [2, 4], reward: { type: 'material', pool: ['cloth', 'leather'] }, message: 'Vodo za barv
+
+anje potrebujem!' },
+
+ // TEHNIK
+ { npc: 'tehnik', item: 'glass', quantity: [3, 7], reward: { type: 'hearts', amount: 4 }, message: 'Steklo za elektroniko!' },
+ { npc: 'tehnik', item: 'steel', quantity: [5, 10], reward: { type: 'material', pool: ['circuit_board', 'wire'] }, message: 'Jeklo za stroje!' },
+
+ // IVAN KOVAฤ
+ { npc: 'ivan_kovac', item: 'steel', quantity: [8, 15], reward: { type: 'hearts', amount: 3 }, message: 'Potrebujem jeklo za kovaฤijo!' },
+ { npc: 'ivan_kovac', item: 'wood', quantity: [10, 20], reward: { type: 'material', pool: ['steel', 'iron_ore'] }, message: 'Les za oglje!' },
+
+ // KUSTOS
+ { npc: 'kustos', item: 'glass', quantity: [2, 5], reward: { type: 'hearts', amount: 2 }, message: 'Steklo za razstavne omare!' },
+ { npc: 'kustos', item: 'rare_artifact', quantity: [1, 1], reward: { type: 'material', pool: ['ancient_relic', 'museum_piece'] }, message: 'Artefakt za muzejsko zbirko!' },
+
+ // SMETAR
+ { npc: 'glavni_smetar', item: 'plastic', quantity: [15, 30], reward: { type: 'hearts', amount: 1 }, message: 'Plastiko moram zbrat iz ruลกevin!' },
+ { npc: 'glavni_smetar', item: 'wood', quantity: [5, 10], reward: { type: 'material', pool: ['broom', 'cleaning_supplies'] }, message: 'Les za nove metle!' }
+ ];
+
+ // Material loot pools
+ this.materialPools = {
+ common: ['wood', 'stone', 'plastic', 'water'],
+ uncommon: ['steel', 'glass', 'cloth', 'leather'],
+ rare: ['circuit_board', 'wire', 'ancient_relic', 'museum_piece']
+ };
+
+ this.init();
+ }
+
+ init() {
+ // Start quest generation
+ this.startQuestGeneration();
+
+ // Listen for delivery completions
+ this.scene.events.on('courier:delivery_complete', this.onDeliveryComplete, this);
+
+ console.log('โ
CourierSystem initialized - Side-quests active');
+ }
+
+ /**
+ * QUEST GENERATION
+ */
+ startQuestGeneration() {
+ // Generate random delivery quest every 2-5 minutes
+ const generateQuest = () => {
+ if (this.activeDeliveries.length < 5) { // Max 5 active
+ this.generateDeliveryQuest();
+ }
+
+ // Schedule next generation
+ const nextTime = Phaser.Math.Between(120000, 300000); // 2-5 minutes
+ setTimeout(generateQuest, nextTime);
+ };
+
+ // Start initial quest immediately
+ generateQuest();
+ }
+
+ generateDeliveryQuest() {
+ const template = Phaser.Utils.Array.GetRandom(this.deliveryTemplates);
+
+ const quest = {
+ id: `delivery_${Date.now()}`,
+ npc: template.npc,
+ item: template.item,
+ quantity: Phaser.Math.Between(template.quantity[0], template.quantity[1]),
+ reward: template.reward,
+ message: template.message,
+ timeCreated: Date.now(),
+ expiresIn: 600000, // 10 minutes
+ completed: false
+ };
+
+ this.activeDeliveries.push(quest);
+
+ // Notify player
+ this.scene.events.emit('show-notification', {
+ title: '๐ฆ Nova Dostava',
+ message: `${template.npc}: ${template.message}`,
+ icon: '๐ฌ',
+ duration: 5000,
+ color: '#FFD700'
+ });
+
+ // Show on NPC
+ this.scene.events.emit('npc:show_quest_marker', template.npc);
+
+ console.log(`๐ฆ New delivery quest: ${template.npc} needs ${quest.quantity}x ${quest.item}`);
+
+ return quest;
+ }
+
+ /**
+ * ACCEPT DELIVERY QUEST
+ */
+ acceptQuest(questId) {
+ const quest = this.activeDeliveries.find(q => q.id === questId);
+ if (!quest) return false;
+
+ quest.accepted = true;
+
+ // Add to player's active quest log
+ if (this.scene.questSystem) {
+ this.scene.questSystem.startQuest(questId);
+ }
+
+ console.log(`โ
Accepted delivery quest: ${questId}`);
+ return true;
+ }
+
+ /**
+ * DELIVER ITEMS
+ */
+ deliverItems(questId) {
+ const quest = this.activeDeliveries.find(q => q.id === questId);
+ if (!quest || quest.completed) return false;
+
+ // Check if player has items
+ if (!this.scene.inventorySystem.hasItem(quest.item, quest.quantity)) {
+ this.scene.events.emit('show-notification', {
+ title: 'โ Ni dovolj',
+ message: `Rabiลก ลกe ${quest.quantity}x ${quest.item}!`,
+ icon: '๐ฆ',
+ duration: 3000,
+ color: '#FF4444'
+ });
+ return false;
+ }
+
+ // Remove items from inventory
+ this.scene.inventorySystem.removeItem(quest.item, quest.quantity);
+
+ // Mark as complete
+ quest.completed = true;
+
+ // Award reward
+ this.awardReward(quest.reward);
+
+ // Remove from active
+ this.activeDeliveries = this.activeDeliveries.filter(q => q.id !== questId);
+
+ // Notify completion
+ this.scene.events.emit('show-notification', {
+ title: 'โ
Dostava Konฤana',
+ message: `${quest.npc} je zadovoljen!`,
+ icon: '๐',
+ duration: 4000,
+ color: '#00FF00'
+ });
+
+ // Remove quest marker
+ this.scene.events.emit('npc:remove_quest_marker', quest.npc);
+
+ console.log(`โ
Delivery completed: ${questId}`);
+
+ return true;
+ }
+
+ /**
+ * AWARD REWARD
+ */
+ awardReward(reward) {
+ if (reward.type === 'hearts') {
+ this.addHearts(reward.amount);
+ } else if (reward.type === 'material') {
+ this.awardRandomMaterial(reward.pool);
+ }
+ }
+
+ addHearts(amount) {
+ this.hearts += amount;
+ this.socialStatus = Math.min(100, this.socialStatus + (amount * 5));
+
+ // Visual heart animation
+ for (let i = 0; i < amount; i++) {
+ setTimeout(() => {
+ this.spawnHeart();
+ }, i * 300);
+ }
+
+ // Show status update
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.x,
+ y: this.scene.player.y - 50,
+ text: `+${amount} โค๏ธ`,
+ color: '#FF69B4',
+ fontSize: '32px'
+ });
+
+ console.log(`โค๏ธ +${amount} hearts (Total: ${this.hearts}, Status: ${this.socialStatus}%)`);
+ }
+
+ spawnHeart() {
+ const heart = this.scene.add.sprite(
+ this.scene.player.x + Phaser.Math.Between(-30, 30),
+ this.scene.player.y - 50,
+ 'heart_icon'
+ );
+ heart.setScale(0);
+ heart.setDepth(50);
+
+ // Animate heart
+ this.scene.tweens.add({
+ targets: heart,
+ scaleX: 1,
+ scaleY: 1,
+ y: heart.y - 100,
+ alpha: 0,
+ duration: 1500,
+ ease: 'Cubic.easeOut',
+ onComplete: () => heart.destroy()
+ });
+ }
+
+ awardRandomMaterial(pool) {
+ // Random material from pool
+ const material = Phaser.Utils.Array.GetRandom(pool);
+ const quantity = Phaser.Math.Between(1, 5);
+
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(material, quantity);
+ }
+
+ // Show notification
+ this.scene.events.emit('show-notification', {
+ title: '๐ Material',
+ message: `Prejel si: ${quantity}x ${material}`,
+ icon: '๐ง',
+ duration: 4000,
+ color: '#FFD700'
+ });
+
+ console.log(`๐ Awarded random material: ${quantity}x ${material}`);
+ }
+
+ /**
+ * QUEST EXPIRATION
+ */
+ update(delta) {
+ const now = Date.now();
+
+ // Check for expired quests
+ this.activeDeliveries.forEach(quest => {
+ if (!quest.completed && (now - quest.timeCreated) > quest.expiresIn) {
+ // Quest expired
+ this.expireQuest(quest.id);
+ }
+ });
+ }
+
+ expireQuest(questId) {
+ const quest = this.activeDeliveries.find(q => q.id === questId);
+ if (!quest) return;
+
+ this.activeDeliveries = this.activeDeliveries.filter(q => q.id !== questId);
+
+ // Notify player
+ this.scene.events.emit('show-notification', {
+ title: 'โฐ Quest Expired',
+ message: `${quest.npc} je naลกel drugega kurirja.`,
+ icon: '๐',
+ duration: 3000,
+ color: '#999999'
+ });
+
+ // Remove marker
+ this.scene.events.emit('npc:remove_quest_marker', quest.npc);
+
+ console.log(`โฐ Quest expired: ${questId}`);
+ }
+
+ /**
+ * GET SOCIAL STATUS BENEFITS
+ */
+ getSocialBenefits() {
+ const benefits = [];
+
+ if (this.socialStatus >= 20) benefits.push('5% discount at shops');
+ if (this.socialStatus >= 40) benefits.push('Priority quests from NPCs');
+ if (this.socialStatus >= 60) benefits.push('Access to VIP areas');
+ if (this.socialStatus >= 80) benefits.push('Rare gift chance +10%');
+ if (this.socialStatus >= 100) benefits.push('Maximum respect - All NPCs love you!');
+
+ return benefits;
+ }
+
+ /**
+ * GET UI DATA
+ */
+ getActiveQuests() {
+ return this.activeDeliveries.filter(q => !q.completed).map(q => ({
+ id: q.id,
+ npc: q.npc,
+ item: q.item,
+ quantity: q.quantity,
+ reward: q.reward,
+ timeRemaining: q.expiresIn - (Date.now() - q.timeCreated)
+ }));
+ }
+
+ destroy() {
+ this.activeDeliveries = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CraftingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CraftingSystem.js
new file mode 100644
index 000000000..0e3b0712c
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CraftingSystem.js
@@ -0,0 +1,352 @@
+// Crafting System - Handles recipe management and item crafting
+class CraftingSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.recipes = {};
+ this.categories = [];
+ this.unlockedRecipes = new Set();
+
+ // Crafting queue
+ this.craftingQueue = [];
+ this.isCrafting = false;
+ this.currentCraft = null;
+ this.craftProgress = 0;
+
+ console.log('๐ ๏ธ CraftingSystem initialized');
+ }
+
+ async loadRecipes() {
+ try {
+ // Load recipes from JSON file
+ const response = await fetch('data/recipes.json');
+ const data = await response.json();
+
+ this.recipes = data.recipes;
+ this.categories = data.categories;
+
+ // Initialize unlocked recipes
+ Object.keys(this.recipes).forEach(recipeId => {
+ const recipe = this.recipes[recipeId];
+ if (recipe.unlocked) {
+ this.unlockedRecipes.add(recipeId);
+ }
+ });
+
+ console.log(`โ
Loaded ${Object.keys(this.recipes).length} recipes`);
+ console.log(`๐ ${this.unlockedRecipes.size} unlocked recipes`);
+
+ return true;
+ } catch (error) {
+ console.error('โ Failed to load recipes:', error);
+ return false;
+ }
+ }
+
+ // Get all recipes (optionally filtered by category)
+ getRecipes(category = 'all') {
+ const recipeList = Object.values(this.recipes);
+
+ if (category === 'all') {
+ return recipeList;
+ }
+
+ return recipeList.filter(recipe => recipe.category === category);
+ }
+
+ // Get only unlocked recipes
+ getUnlockedRecipes(category = 'all') {
+ return this.getRecipes(category).filter(recipe =>
+ this.unlockedRecipes.has(recipe.id)
+ );
+ }
+
+ // Check if recipe is unlocked
+ isUnlocked(recipeId) {
+ return this.unlockedRecipes.has(recipeId);
+ }
+
+ // Unlock a recipe
+ unlockRecipe(recipeId) {
+ if (this.recipes[recipeId]) {
+ this.unlockedRecipes.add(recipeId);
+ console.log(`๐ Unlocked recipe: ${this.recipes[recipeId].name}`);
+
+ // Notify UI
+ this.scene.events.emit('recipe-unlocked', recipeId);
+ return true;
+ }
+ return false;
+ }
+
+ // Check if player has required ingredients
+ canCraft(recipeId) {
+ const recipe = this.recipes[recipeId];
+ if (!recipe) return false;
+
+ // Check if unlocked
+ if (!this.isUnlocked(recipeId)) {
+ return { canCraft: false, reason: 'locked' };
+ }
+
+ // Check ingredients
+ const inventory = this.scene.inventorySystem;
+ if (!inventory) {
+ return { canCraft: false, reason: 'no_inventory' };
+ }
+
+ const missing = [];
+
+ for (const [itemId, requiredAmount] of Object.entries(recipe.ingredients)) {
+ const hasAmount = inventory.getItemCount(itemId);
+
+ if (hasAmount < requiredAmount) {
+ missing.push({
+ item: itemId,
+ required: requiredAmount,
+ has: hasAmount,
+ need: requiredAmount - hasAmount
+ });
+ }
+ }
+
+ if (missing.length > 0) {
+ return { canCraft: false, reason: 'missing_ingredients', missing };
+ }
+
+ return { canCraft: true };
+ }
+
+ // Start crafting an item
+ craftItem(recipeId) {
+ const recipe = this.recipes[recipeId];
+ if (!recipe) {
+ console.warn(`โ ๏ธ Recipe not found: ${recipeId}`);
+ return false;
+ }
+
+ // Check if can craft
+ const check = this.canCraft(recipeId);
+ if (!check.canCraft) {
+ console.warn(`โ ๏ธ Cannot craft ${recipe.name}: ${check.reason}`);
+ return false;
+ }
+
+ // Consume ingredients
+ const inventory = this.scene.inventorySystem;
+ for (const [itemId, amount] of Object.entries(recipe.ingredients)) {
+ inventory.removeItem(itemId, amount);
+ }
+
+ // Add to crafting queue
+ this.craftingQueue.push({
+ recipeId: recipeId,
+ recipe: recipe,
+ startTime: Date.now(),
+ duration: recipe.craftTime || 1000
+ });
+
+ console.log(`๐จ Started crafting: ${recipe.name}`);
+
+ // Start crafting if not already crafting
+ if (!this.isCrafting) {
+ this.startNextCraft();
+ }
+
+ // Emit event
+ this.scene.events.emit('craft-started', recipeId);
+
+ // Play sound
+ if (this.scene.soundManager && this.scene.soundManager.playCraftSound) {
+ this.scene.soundManager.playCraftSound();
+ }
+
+ return true;
+ }
+
+ // Start next item in queue
+ startNextCraft() {
+ if (this.craftingQueue.length === 0) {
+ this.isCrafting = false;
+ this.currentCraft = null;
+ this.craftProgress = 0;
+ return;
+ }
+
+ this.isCrafting = true;
+ this.currentCraft = this.craftingQueue[0];
+ this.craftProgress = 0;
+
+ console.log(`โณ Processing: ${this.currentCraft.recipe.name}`);
+ }
+
+ // Update crafting progress
+ update(delta) {
+ if (!this.isCrafting || !this.currentCraft) return;
+
+ const elapsed = Date.now() - this.currentCraft.startTime;
+ this.craftProgress = Math.min(1.0, elapsed / this.currentCraft.duration);
+
+ // Check if finished
+ if (this.craftProgress >= 1.0) {
+ this.completeCraft();
+ }
+
+ // Emit progress event for UI
+ this.scene.events.emit('craft-progress', {
+ recipe: this.currentCraft.recipe,
+ progress: this.craftProgress
+ });
+ }
+
+ // Complete current craft
+ completeCraft() {
+ if (!this.currentCraft) return;
+
+ const recipe = this.currentCraft.recipe;
+
+ // Add result to inventory
+ const inventory = this.scene.inventorySystem;
+ inventory.addItem(recipe.result.item, recipe.result.quantity);
+
+ console.log(`โ
Crafted: ${recipe.result.quantity}x ${recipe.name}`);
+
+ // Emit event
+ this.scene.events.emit('craft-complete', {
+ recipeId: recipe.id,
+ item: recipe.result.item,
+ quantity: recipe.result.quantity
+ });
+
+ // Play sound
+ if (this.scene.soundManager && this.scene.soundManager.playSuccessSound) {
+ this.scene.soundManager.playSuccessSound();
+ }
+
+ // Show floating text
+ if (this.scene.player && this.scene.player.sprite) {
+ const text = `+${recipe.result.quantity} ${recipe.name}`;
+ this.scene.events.emit('floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 50,
+ text: text,
+ color: '#00ff00'
+ });
+
+ // โจ PARTICLE EFFECT - Craft sparkles
+ if (this.scene.particleEnhancements) {
+ this.scene.particleEnhancements.craftSparkles(
+ this.scene.player.sprite.x,
+ this.scene.player.sprite.y
+ );
+ }
+ }
+
+ // Remove from queue
+ this.craftingQueue.shift();
+
+ // Start next craft
+ this.startNextCraft();
+ }
+
+ // Cancel current craft
+ cancelCraft() {
+ if (!this.currentCraft) return false;
+
+ const recipe = this.currentCraft.recipe;
+
+ // Refund ingredients (partial refund based on progress)
+ const refundPercent = 1.0 - this.craftProgress;
+ const inventory = this.scene.inventorySystem;
+
+ for (const [itemId, amount] of Object.entries(recipe.ingredients)) {
+ const refundAmount = Math.floor(amount * refundPercent);
+ if (refundAmount > 0) {
+ inventory.addItem(itemId, refundAmount);
+ }
+ }
+
+ console.log(`โ Cancelled crafting: ${recipe.name} (${Math.floor(refundPercent * 100)}% refund)`);
+
+ // Remove from queue
+ this.craftingQueue.shift();
+
+ // Emit event
+ this.scene.events.emit('craft-cancelled', recipe.id);
+
+ // Start next
+ this.startNextCraft();
+
+ return true;
+ }
+
+ // Get current crafting info
+ getCurrentCraft() {
+ if (!this.currentCraft) return null;
+
+ return {
+ recipe: this.currentCraft.recipe,
+ progress: this.craftProgress,
+ timeRemaining: this.currentCraft.duration * (1 - this.craftProgress)
+ };
+ }
+
+ // Get queue length
+ getQueueLength() {
+ return this.craftingQueue.length;
+ }
+
+ // Clear entire queue
+ clearQueue() {
+ // Refund all queued items
+ this.craftingQueue.forEach(craft => {
+ const inventory = this.scene.inventorySystem;
+ for (const [itemId, amount] of Object.entries(craft.recipe.ingredients)) {
+ inventory.addItem(itemId, amount);
+ }
+ });
+
+ this.craftingQueue = [];
+ this.isCrafting = false;
+ this.currentCraft = null;
+ this.craftProgress = 0;
+
+ console.log('๐๏ธ Cleared crafting queue');
+ }
+
+ // Save crafting state
+ getSaveData() {
+ return {
+ unlockedRecipes: Array.from(this.unlockedRecipes),
+ craftingQueue: this.craftingQueue.map(craft => ({
+ recipeId: craft.recipeId,
+ startTime: craft.startTime,
+ duration: craft.duration
+ })),
+ currentProgress: this.craftProgress
+ };
+ }
+
+ // Load crafting state
+ loadSaveData(data) {
+ if (!data) return;
+
+ // Restore unlocked recipes
+ if (data.unlockedRecipes) {
+ this.unlockedRecipes = new Set(data.unlockedRecipes);
+ }
+
+ // Restore crafting queue
+ if (data.craftingQueue && data.craftingQueue.length > 0) {
+ this.craftingQueue = data.craftingQueue.map(saved => ({
+ recipeId: saved.recipeId,
+ recipe: this.recipes[saved.recipeId],
+ startTime: saved.startTime,
+ duration: saved.duration
+ }));
+
+ this.startNextCraft();
+ }
+
+ console.log('๐พ Loaded crafting state');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CraftingTablesSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CraftingTablesSystem.js
new file mode 100644
index 000000000..24a97eecc
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CraftingTablesSystem.js
@@ -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
+ */
+
+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
+ };
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CraftingTiersSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CraftingTiersSystem.js
new file mode 100644
index 000000000..59ba1ffa9
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CraftingTiersSystem.js
@@ -0,0 +1,377 @@
+/**
+ * CRAFTING TIERS SYSTEM
+ * Tool tiers, durability, and repair mechanics
+ */
+class CraftingTiersSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Tool tiers
+ this.tiers = {
+ wood: { name: 'Wooden', durability: 50, speed: 1.0, damage: 1.0, color: 0x8B4513 },
+ bronze: { name: 'Bronze', durability: 150, speed: 1.2, damage: 1.3, color: 0xCD7F32 },
+ iron: { name: 'Iron', durability: 300, speed: 1.5, damage: 1.6, color: 0xC0C0C0 },
+ steel: { name: 'Steel', durability: 600, speed: 1.8, damage: 2.0, color: 0x4682B4 },
+ enchanted: { name: 'Enchanted', durability: 1200, speed: 2.5, damage: 3.0, color: 0x9370DB }
+ };
+
+ // Tool types
+ this.toolTypes = ['sword', 'pickaxe', 'axe', 'hoe', 'shovel'];
+
+ // Player's tools
+ this.playerTools = new Map();
+
+ // Crafting recipes
+ this.recipes = new Map();
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Crafting Tiers System initialized');
+ }
+
+ init() {
+ this.defineRecipes();
+ console.log('โ๏ธ Crafting recipes ready');
+ }
+
+ // ========== RECIPE DEFINITIONS ==========
+
+ defineRecipes() {
+ // BRONZE TIER (Copper + Tin)
+ this.defineRecipe('bronze_sword', {
+ name: 'Bronze Sword',
+ tier: 'bronze',
+ type: 'sword',
+ materials: { copper: 3, tin: 1 },
+ unlockLevel: 5
+ });
+
+ this.defineRecipe('bronze_pickaxe', {
+ name: 'Bronze Pickaxe',
+ tier: 'bronze',
+ type: 'pickaxe',
+ materials: { copper: 3, tin: 1, wood: 2 },
+ unlockLevel: 5
+ });
+
+ this.defineRecipe('bronze_axe', {
+ name: 'Bronze Axe',
+ tier: 'bronze',
+ type: 'axe',
+ materials: { copper: 3, tin: 1, wood: 2 },
+ unlockLevel: 5
+ });
+
+ // IRON TIER
+ this.defineRecipe('iron_sword', {
+ name: 'Iron Sword',
+ tier: 'iron',
+ type: 'sword',
+ materials: { iron: 5 },
+ unlockLevel: 10
+ });
+
+ this.defineRecipe('iron_pickaxe', {
+ name: 'Iron Pickaxe',
+ tier: 'iron',
+ type: 'pickaxe',
+ materials: { iron: 5, wood: 3 },
+ unlockLevel: 10
+ });
+
+ this.defineRecipe('iron_axe', {
+ name: 'Iron Axe',
+ tier: 'iron',
+ type: 'axe',
+ materials: { iron: 5, wood: 3 },
+ unlockLevel: 10
+ });
+
+ // STEEL TIER (Iron + Coal)
+ this.defineRecipe('steel_sword', {
+ name: 'Steel Sword',
+ tier: 'steel',
+ type: 'sword',
+ materials: { iron: 5, coal: 3 },
+ unlockLevel: 20
+ });
+
+ this.defineRecipe('steel_pickaxe', {
+ name: 'Steel Pickaxe',
+ tier: 'steel',
+ type: 'pickaxe',
+ materials: { iron: 5, coal: 3, wood: 2 },
+ unlockLevel: 20
+ });
+
+ // ENCHANTED TIER (Magical)
+ this.defineRecipe('enchanted_sword', {
+ name: 'Enchanted Sword',
+ tier: 'enchanted',
+ type: 'sword',
+ materials: { steel_sword: 1, magic_crystal: 5, dragon_scale: 3 },
+ unlockLevel: 30
+ });
+ }
+
+ defineRecipe(id, data) {
+ this.recipes.set(id, {
+ id,
+ ...data
+ });
+ }
+
+ // ========== CRAFTING ==========
+
+ canCraft(recipeId) {
+ const recipe = this.recipes.get(recipeId);
+ if (!recipe) return false;
+
+ // Check level requirement
+ if (this.scene.skillTree && this.scene.skillTree.playerLevel < recipe.unlockLevel) {
+ return false;
+ }
+
+ // Check materials
+ if (!this.scene.inventorySystem) return false;
+
+ for (const [material, amount] of Object.entries(recipe.materials)) {
+ const has = this.scene.inventorySystem.getItemCount(material);
+ if (has < amount) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ craft(recipeId) {
+ if (!this.canCraft(recipeId)) {
+ console.log('โ Cannot craft:', recipeId);
+ return false;
+ }
+
+ const recipe = this.recipes.get(recipeId);
+
+ // Consume materials
+ for (const [material, amount] of Object.entries(recipe.materials)) {
+ this.scene.inventorySystem.removeItem(material, amount);
+ }
+
+ // Create tool
+ const tool = this.createTool(recipe.tier, recipe.type);
+ this.playerTools.set(`${recipe.type}_${recipe.tier}`, tool);
+
+ // Add to inventory
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(recipeId, 1);
+ }
+
+ // Unlock achievement
+ if (this.scene.uiGraphics) {
+ if (recipe.tier === 'enchanted') {
+ this.scene.uiGraphics.unlockAchievement('master_crafter');
+ }
+ }
+
+ this.saveProgress();
+ console.log(`โ
Crafted: ${recipe.name}`);
+ return true;
+ }
+
+ // ========== TOOL MANAGEMENT ==========
+
+ createTool(tier, type) {
+ const tierData = this.tiers[tier];
+
+ return {
+ tier,
+ type,
+ durability: tierData.durability,
+ maxDurability: tierData.durability,
+ speed: tierData.speed,
+ damage: tierData.damage,
+ color: tierData.color,
+ enchantments: []
+ };
+ }
+
+ getTool(type) {
+ // Get best available tool of this type
+ const tierOrder = ['enchanted', 'steel', 'iron', 'bronze', 'wood'];
+
+ for (const tier of tierOrder) {
+ const key = `${type}_${tier}`;
+ const tool = this.playerTools.get(key);
+ if (tool && tool.durability > 0) {
+ return tool;
+ }
+ }
+
+ return null;
+ }
+
+ useTool(type, amount = 1) {
+ const tool = this.getTool(type);
+ if (!tool) return false;
+
+ tool.durability -= amount;
+
+ // Tool broke
+ if (tool.durability <= 0) {
+ tool.durability = 0;
+ console.log(`๐ ${this.tiers[tool.tier].name} ${tool.type} broke!`);
+
+ // 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();
+ return true;
+ }
+
+ getToolDurabilityPercent(type) {
+ const tool = this.getTool(type);
+ if (!tool) return 0;
+
+ return (tool.durability / tool.maxDurability) * 100;
+ }
+
+ // ========== TOOL REPAIR ==========
+
+ canRepair(type) {
+ const tool = this.getTool(type);
+ if (!tool || tool.durability === tool.maxDurability) return false;
+
+ // Check materials
+ const repairCost = this.getRepairCost(tool);
+ if (!this.scene.inventorySystem) return false;
+
+ for (const [material, amount] of Object.entries(repairCost)) {
+ const has = this.scene.inventorySystem.getItemCount(material);
+ if (has < amount) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ getRepairCost(tool) {
+ const durabilityLost = tool.maxDurability - tool.durability;
+ const repairPercent = durabilityLost / tool.maxDurability;
+
+ // Cost scales with damage
+ const baseCost = {
+ wood: { wood: 2 },
+ bronze: { copper: 2, tin: 1 },
+ iron: { iron: 3 },
+ steel: { iron: 3, coal: 2 },
+ enchanted: { magic_crystal: 2 }
+ };
+
+ const cost = {};
+ const tierCost = baseCost[tool.tier];
+
+ for (const [material, amount] of Object.entries(tierCost)) {
+ cost[material] = Math.ceil(amount * repairPercent);
+ }
+
+ return cost;
+ }
+
+ repair(type) {
+ if (!this.canRepair(type)) {
+ console.log('โ Cannot repair:', type);
+ return false;
+ }
+
+ const tool = this.getTool(type);
+ const cost = this.getRepairCost(tool);
+
+ // Consume materials
+ for (const [material, amount] of Object.entries(cost)) {
+ this.scene.inventorySystem.removeItem(material, amount);
+ }
+
+ // Repair tool
+ tool.durability = tool.maxDurability;
+
+ // 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();
+ console.log(`๐ง Repaired: ${this.tiers[tool.tier].name} ${tool.type}`);
+ return true;
+ }
+
+ // ========== ENCHANTMENTS ==========
+
+ enchantTool(type, enchantment) {
+ const tool = this.getTool(type);
+ if (!tool || tool.tier !== 'enchanted') return false;
+
+ tool.enchantments.push(enchantment);
+ this.saveProgress();
+ console.log(`โจ Enchanted ${tool.type} with ${enchantment}`);
+ return true;
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ tools: {}
+ };
+
+ for (const [key, tool] of this.playerTools.entries()) {
+ data.tools[key] = {
+ tier: tool.tier,
+ type: tool.type,
+ durability: tool.durability,
+ enchantments: tool.enchantments
+ };
+ }
+
+ localStorage.setItem('novafarma_crafting_tiers', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_crafting_tiers');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+
+ for (const [key, toolData] of Object.entries(data.tools)) {
+ const tool = this.createTool(toolData.tier, toolData.type);
+ tool.durability = toolData.durability;
+ tool.enchantments = toolData.enchantments || [];
+ this.playerTools.set(key, tool);
+ }
+
+ console.log('โ
Crafting progress loaded');
+ } catch (error) {
+ console.error('Failed to load crafting progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('โ๏ธ Crafting Tiers System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/CropGrowthSeasonSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/CropGrowthSeasonSystem.js
new file mode 100644
index 000000000..683349401
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/CropGrowthSeasonSystem.js
@@ -0,0 +1,342 @@
+/**
+ * CROP GROWTH & SEASON SYSTEM
+ * Manages dynamic crop growth through 8 stages + 4 seasonal variations
+ * Integrates with Faza 1 crop sprites (corn, tomatoes, carrots, potatoes, lettuce, pumpkin)
+ */
+
+class CropGrowthSeasonSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Growth system constants
+ this.GROWTH_STAGES = 8;
+ this.SEASONS = ['spring', 'summer', 'fall', 'winter'];
+
+ // Active crops in world
+ this.crops = new Map(); // cropId -> {type, stage, season, sprite, growthTimer}
+
+ // Growth rates (minutes per stage in real time)
+ this.growthRates = {
+ corn: 5,
+ tomatoes: 4,
+ carrots: 3,
+ potatoes: 4,
+ lettuce: 2,
+ pumpkin: 6
+ };
+
+ // Seasonal color tints (applied to environment, NOT crops)
+ this.seasonalTints = {
+ spring: 0x8BC34A, // Fresh green
+ summer: 0xFFC107, // Bright yellow-gold
+ fall: 0xFF9800, // Orange-amber
+ winter: 0x2196F3 // Cool blue
+ };
+
+ // Current season (synced with TimeSystem)
+ this.currentSeason = 'spring';
+
+ // Environment layer (sky/ambient tint)
+ this.environmentTint = null;
+ }
+
+ /**
+ * Initialize system
+ */
+ init() {
+ // Create environment tint overlay
+ this.createEnvironmentTint();
+
+ // Subscribe to season changes from TimeSystem
+ if (this.scene.time_system) {
+ this.scene.events.on('seasonChanged', this.onSeasonChange, this);
+ }
+
+ console.log('๐ฑ CropGrowthSeasonSystem initialized');
+ }
+
+ /**
+ * Create environment tint overlay for seasonal color shifts
+ */
+ createEnvironmentTint() {
+ // Create semi-transparent overlay
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ this.environmentTint = this.scene.add.rectangle(
+ width / 2,
+ height / 2,
+ width * 2, // Make it larger to cover camera movement
+ height * 2,
+ this.seasonalTints[this.currentSeason],
+ 0.15 // 15% opacity
+ );
+
+ this.environmentTint.setScrollFactor(1); // Moves with camera
+ this.environmentTint.setDepth(1000); // Above most objects
+ this.environmentTint.setBlendMode(Phaser.BlendModes.MULTIPLY);
+ }
+
+ /**
+ * Handle season change
+ */
+ onSeasonChange(newSeason) {
+ console.log(`๐ Season changing to: ${newSeason}`);
+ this.currentSeason = newSeason;
+
+ // Animate environment tint transition
+ if (this.environmentTint) {
+ this.scene.tweens.add({
+ targets: this.environmentTint,
+ tint: this.seasonalTints[newSeason],
+ duration: 2000,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ // Update all existing crops to new season sprites
+ this.updateAllCropsForSeason(newSeason);
+ }
+
+ /**
+ * Plant a crop
+ */
+ plantCrop(x, y, cropType) {
+ const cropId = `crop_${Date.now()}_${Math.random()}`;
+
+ // Load sprite for current season, stage 1
+ const spritePath = this.getCropSpritePath(cropType, this.currentSeason, 1);
+
+ // Create sprite
+ const sprite = this.scene.add.sprite(x, y, spritePath);
+ sprite.setDepth(2); // Above ground
+
+ // Store crop data
+ this.crops.set(cropId, {
+ id: cropId,
+ type: cropType,
+ stage: 1,
+ season: this.currentSeason,
+ sprite: sprite,
+ growthTimer: 0,
+ x: x,
+ y: y
+ });
+
+ console.log(`๐ฑ Planted ${cropType} at ${x},${y}`);
+
+ return cropId;
+ }
+
+ /**
+ * Get crop sprite path based on type, season, and stage
+ */
+ getCropSpritePath(cropType, season, stage) {
+ // Path: assets/crops/faza1/{type}/{season}/stage{N}.png
+ return `crops/faza1/${this.normalizeCropName(cropType)}/${season}/stage${stage}`;
+ }
+
+ /**
+ * Normalize crop names to match folder structure
+ */
+ normalizeCropName(cropType) {
+ const nameMap = {
+ 'tomatoes': 'tomatoes',
+ 'tomato': 'tomatoes',
+ 'carrots': 'carrots',
+ 'carrot': 'carrots',
+ 'potatoes': 'potatos', // Note: folder is 'potatos' not 'potatoes'
+ 'potato': 'potatos',
+ 'lettuce': 'lettuces',
+ 'pumpkin': 'pumpkins',
+ 'corn': 'corn'
+ };
+
+ return nameMap[cropType.toLowerCase()] || cropType.toLowerCase();
+ }
+
+ /**
+ * Update crop growth (called every frame or fixed interval)
+ */
+ update(deltaTime) {
+ for (const [cropId, crop] of this.crops.entries()) {
+ // Don't grow if already at max stage or dead
+ if (crop.stage >= this.GROWTH_STAGES) {
+ continue;
+ }
+
+ // Increment growth timer
+ crop.growthTimer += deltaTime / 1000; // Convert to seconds
+
+ // Check if it's time to advance stage
+ const growthRate = this.growthRates[crop.type] || 5;
+ const secondsPerStage = growthRate * 60; // Convert to seconds
+
+ if (crop.growthTimer >= secondsPerStage) {
+ this.advanceCropStage(cropId);
+ crop.growthTimer = 0; // Reset timer
+ }
+ }
+ }
+
+ /**
+ * Advance crop to next growth stage
+ */
+ advanceCropStage(cropId) {
+ const crop = this.crops.get(cropId);
+ if (!crop) return;
+
+ // Advance stage
+ crop.stage = Math.min(crop.stage + 1, this.GROWTH_STAGES);
+
+ // Update sprite
+ const newSpritePath = this.getCropSpritePath(crop.type, crop.season, crop.stage);
+ crop.sprite.setTexture(newSpritePath);
+
+ console.log(`๐พ ${crop.type} advanced to stage ${crop.stage}`);
+
+ // Special events
+ if (crop.stage === 6) {
+ // HARVEST READY
+ this.scene.events.emit('cropReady', { cropId, crop });
+
+ // Add visual indicator (glow effect)
+ this.addHarvestGlow(crop.sprite);
+ }
+
+ if (crop.stage === 7) {
+ console.warn(`โ ๏ธ ${crop.type} is overripe! Harvest soon or it will die.`);
+ }
+
+ if (crop.stage === 8) {
+ console.log(`๐ ${crop.type} has died.`);
+ // Crop is now dead (stage 8)
+ }
+ }
+
+ /**
+ * Add glow effect to harvest-ready crops
+ */
+ addHarvestGlow(sprite) {
+ // Pulsing glow animation
+ this.scene.tweens.add({
+ targets: sprite,
+ alpha: 0.7,
+ duration: 800,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ /**
+ * Harvest a crop
+ */
+ harvestCrop(cropId) {
+ const crop = this.crops.get(cropId);
+ if (!crop) return null;
+
+ // Can only harvest at stage 6 (perfect) or 5-7 (acceptable)
+ if (crop.stage < 5 || crop.stage === 8) {
+ console.warn(`Cannot harvest ${crop.type} at stage ${crop.stage}`);
+ return null;
+ }
+
+ // Calculate yield based on stage
+ let yield_quality = 1.0;
+ if (crop.stage === 6) {
+ yield_quality = 1.5; // Perfect harvest bonus
+ } else if (crop.stage === 7) {
+ yield_quality = 0.7; // Overripe penalty
+ }
+
+ // Remove crop
+ crop.sprite.destroy();
+ this.crops.delete(cropId);
+
+ console.log(`โ
Harvested ${crop.type} with ${yield_quality}x quality`);
+
+ return {
+ type: crop.type,
+ quality: yield_quality,
+ stage: crop.stage
+ };
+ }
+
+ /**
+ * Update all crops when season changes
+ */
+ updateAllCropsForSeason(newSeason) {
+ for (const [cropId, crop] of this.crops.entries()) {
+ crop.season = newSeason;
+
+ // Update sprite to new season
+ const newSpritePath = this.getCropSpritePath(crop.type, newSeason, crop.stage);
+ crop.sprite.setTexture(newSpritePath);
+ }
+
+ console.log(`๐ Updated ${this.crops.size} crops for ${newSeason}`);
+ }
+
+ /**
+ * Get crop info
+ */
+ getCropInfo(cropId) {
+ return this.crops.get(cropId);
+ }
+
+ /**
+ * Save system state
+ */
+ save() {
+ const cropsData = [];
+
+ for (const [cropId, crop] of this.crops.entries()) {
+ cropsData.push({
+ id: cropId,
+ type: crop.type,
+ stage: crop.stage,
+ season: crop.season,
+ growthTimer: crop.growthTimer,
+ x: crop.x,
+ y: crop.y
+ });
+ }
+
+ return {
+ currentSeason: this.currentSeason,
+ crops: cropsData
+ };
+ }
+
+ /**
+ * Load system state
+ */
+ load(data) {
+ if (data.currentSeason) {
+ this.currentSeason = data.currentSeason;
+ this.onSeasonChange(this.currentSeason);
+ }
+
+ if (data.crops) {
+ // Recreate crops
+ for (const cropData of data.crops) {
+ const spritePath = this.getCropSpritePath(cropData.type, cropData.season, cropData.stage);
+ const sprite = this.scene.add.sprite(cropData.x, cropData.y, spritePath);
+ sprite.setDepth(2);
+
+ this.crops.set(cropData.id, {
+ ...cropData,
+ sprite: sprite
+ });
+ }
+ }
+
+ console.log(`๐ Loaded ${this.crops.size} crops`);
+ }
+}
+
+// Export for use in GameScene
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = CropGrowthSeasonSystem;
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/DefenseSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/DefenseSystem.js
new file mode 100644
index 000000000..4f2bddc58
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/DefenseSystem.js
@@ -0,0 +1,549 @@
+/**
+ * DEFENSE SYSTEM - City Walls & Watchtowers
+ * Mrtva Dolina - Obrambe pred nomadskimi roparji
+ *
+ * Features:
+ * - 3-tier wall system (wooden โ stone โ fortress)
+ * - Watchtowers with Line of Sight (LoS) expansion
+ * - Raid detection and prevention
+ * - Patrol system
+ * - Damage and repair mechanics
+ */
+
+export class DefenseSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Wall configurations
+ this.wallTiers = {
+ wooden: {
+ name: 'Leseno Obzidje',
+ tier: 1,
+ health: 100,
+ defense: 30,
+ cost: { wood: 50, stone: 10 },
+ buildTime: 30000, // 30 seconds
+ sprite: 'wall_wooden'
+ },
+ stone: {
+ name: 'Kamnito Obzidje',
+ tier: 2,
+ health: 300,
+ defense: 70,
+ cost: { wood: 20, stone: 100, steel: 10 },
+ buildTime: 60000, // 1 minute
+ sprite: 'wall_stone'
+ },
+ fortress: {
+ name: 'Futuristiฤno Obzidje',
+ tier: 3,
+ health: 1000,
+ defense: 95,
+ cost: { steel: 150, glass: 50, tech_parts: 20 },
+ buildTime: 120000, // 2 minutes
+ sprite: 'wall_fortress'
+ }
+ };
+
+ // Watchtower configuration
+ this.watchtowerConfig = {
+ name: 'Opazovalni Stolp',
+ health: 200,
+ losRange: 500, // Line of Sight range in pixels
+ detectionBonus: 0.5, // 50% earlier raid detection
+ cost: { wood: 30, stone: 20 },
+ buildTime: 45000, // 45 seconds
+ sprite: 'watchtower'
+ };
+
+ // Placed walls and towers
+ this.walls = [];
+ this.watchtowers = [];
+
+ // Patrol system
+ this.patrols = [];
+ this.patrolsUnlocked = false;
+
+ // Raid tracking
+ this.activeRaids = [];
+ this.cityDefenseRating = 0;
+
+ this.init();
+ }
+
+ init() {
+ // Listen for raid events
+ this.scene.events.on('raid:incoming', this.onRaidIncoming, this);
+ this.scene.events.on('raid:attack', this.onRaidAttack, this);
+
+ // Start passive defense calculations
+ this.startDefenseUpdates();
+
+ console.log('โ
DefenseSystem initialized');
+ }
+
+ /**
+ * BUILD WALL SEGMENT
+ */
+ buildWall(x, y, tier = 'wooden', direction = 'horizontal') {
+ const wallConfig = this.wallTiers[tier];
+ if (!wallConfig) {
+ console.error(`Unknown wall tier: ${tier}`);
+ return null;
+ }
+
+ // Check if player has resources
+ if (!this.hasResources(wallConfig.cost)) {
+ this.scene.events.emit('show-notification', {
+ title: 'โ Ni Materialov',
+ message: `Rabiลก: ${this.formatCost(wallConfig.cost)}`,
+ icon: '๐๏ธ',
+ duration: 3000,
+ color: '#FF4444'
+ });
+ return null;
+ }
+
+ // Deduct resources
+ this.deductResources(wallConfig.cost);
+
+ // Create wall object
+ const wall = {
+ id: `wall_${Date.now()}`,
+ x: x,
+ y: y,
+ tier: tier,
+ direction: direction, // horizontal or vertical
+ health: wallConfig.health,
+ maxHealth: wallConfig.health,
+ defense: wallConfig.defense,
+ sprite: null,
+ isBuilding: true
+ };
+
+ // Show construction animation
+ this.startConstruction(wall, wallConfig);
+
+ this.walls.push(wall);
+
+ console.log(`๐๏ธ Building ${wallConfig.name} at (${x}, ${y})`);
+
+ return wall;
+ }
+
+ startConstruction(wall, config) {
+ // Show construction sprite
+ const constructionSite = this.scene.add.sprite(wall.x, wall.y, 'construction_scaffold');
+ constructionSite.setDepth(10);
+ wall.constructionSprite = constructionSite;
+
+ // Construction timer
+ setTimeout(() => {
+ this.completeWallConstruction(wall, config);
+ }, config.buildTime);
+
+ // Show progress bar
+ this.showConstructionProgress(wall, config.buildTime);
+ }
+
+ completeWallConstruction(wall, config) {
+ // Remove construction sprite
+ if (wall.constructionSprite) {
+ wall.constructionSprite.destroy();
+ }
+
+ // Place actual wall
+ const wallSprite = this.scene.add.sprite(wall.x, wall.y, config.sprite);
+ wallSprite.setDepth(5);
+
+ if (wall.direction === 'vertical') {
+ wallSprite.setRotation(Math.PI / 2); // 90 degrees
+ }
+
+ wall.sprite = wallSprite;
+ wall.isBuilding = false;
+
+ // Update defense rating
+ this.updateDefenseRating();
+
+ // Show completion notification
+ this.scene.events.emit('show-notification', {
+ title: 'โ
Obzidje Zgrajeno',
+ message: `${config.name} je konฤano!`,
+ icon: '๐ฐ',
+ duration: 3000,
+ color: '#00FF00'
+ });
+
+ console.log(`โ
${config.name} construction complete`);
+ }
+
+ showConstructionProgress(wall, duration) {
+ // Progress bar above construction site
+ const progressBg = this.scene.add.graphics();
+ progressBg.fillStyle(0x000000, 0.7);
+ progressBg.fillRect(wall.x - 30, wall.y - 40, 60, 8);
+
+ const progressBar = this.scene.add.graphics();
+ wall.progressBar = progressBar;
+ wall.progressBg = progressBg;
+
+ const startTime = Date.now();
+ const progressInterval = setInterval(() => {
+ const elapsed = Date.now() - startTime;
+ const progress = Math.min(elapsed / duration, 1);
+
+ progressBar.clear();
+ progressBar.fillStyle(0x00FF00, 1);
+ progressBar.fillRect(wall.x - 29, wall.y - 39, 58 * progress, 6);
+
+ if (progress >= 1) {
+ clearInterval(progressInterval);
+ progressBg.destroy();
+ progressBar.destroy();
+ }
+ }, 100);
+ }
+
+ /**
+ * BUILD WATCHTOWER
+ */
+ buildWatchtower(x, y) {
+ const config = this.watchtowerConfig;
+
+ if (!this.hasResources(config.cost)) {
+ this.scene.events.emit('show-notification', {
+ title: 'โ Ni Materialov',
+ message: `Rabiลก: ${this.formatCost(config.cost)}`,
+ icon: '๐ผ',
+ duration: 3000,
+ color: '#FF4444'
+ });
+ return null;
+ }
+
+ this.deductResources(config.cost);
+
+ const tower = {
+ id: `tower_${Date.now()}`,
+ x: x,
+ y: y,
+ health: config.health,
+ maxHealth: config.health,
+ losRange: config.losRange,
+ detectionBonus: config.detectionBonus,
+ sprite: null,
+ isBuilding: true,
+ losCircle: null
+ };
+
+ // Start construction
+ this.startConstruction(tower, config);
+
+ // Complete construction
+ setTimeout(() => {
+ this.completeWatchtowerConstruction(tower);
+ }, config.buildTime);
+
+ this.watchtowers.push(tower);
+
+ console.log(`๐ผ Building Watchtower at (${x}, ${y})`);
+
+ return tower;
+ }
+
+ completeWatchtowerConstruction(tower) {
+ if (tower.constructionSprite) {
+ tower.constructionSprite.destroy();
+ }
+
+ // Place tower sprite
+ const towerSprite = this.scene.add.sprite(tower.x, tower.y, this.watchtowerConfig.sprite);
+ towerSprite.setDepth(15);
+ tower.sprite = towerSprite;
+ tower.isBuilding = false;
+
+ // Create Line of Sight circle
+ this.createLosIndicator(tower);
+
+ // Update defense
+ this.updateDefenseRating();
+
+ this.scene.events.emit('show-notification', {
+ title: 'โ
Stolp Zgrajen',
+ message: 'Opazovalni stolp poveฤuje vidno polje!',
+ icon: '๐ผ',
+ duration: 3000,
+ color: '#00FF00'
+ });
+
+ console.log(`โ
Watchtower construction complete`);
+ }
+
+ createLosIndicator(tower) {
+ // Visual LoS circle
+ const losCircle = this.scene.add.graphics();
+ losCircle.lineStyle(2, 0xFFFF00, 0.3);
+ losCircle.strokeCircle(tower.x, tower.y, tower.losRange);
+ losCircle.setDepth(1);
+ tower.losCircle = losCircle;
+
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: losCircle,
+ alpha: 0.5,
+ duration: 2000,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ /**
+ * RAID DETECTION & DEFENSE
+ */
+ onRaidIncoming(raidData) {
+ // Check if watchtowers can detect early
+ const earlyDetection = this.checkEarlyDetection(raidData);
+
+ if (earlyDetection) {
+ // Give player extra time to prepare
+ raidData.timeToArrival *= (1 + this.watchtowerConfig.detectionBonus);
+
+ this.scene.events.emit('show-notification', {
+ title: '๐จ RAID OPAลฝEN!',
+ message: `Opazovalni stolp je opazil roparje! +${Math.floor(this.watchtowerConfig.detectionBonus * 100)}% ฤasa za pripravo!`,
+ icon: '๐ผ',
+ duration: 5000,
+ color: '#FFAA00'
+ });
+ }
+
+ this.activeRaids.push(raidData);
+
+ console.log(`๐จ Raid incoming: ${raidData.strength} strength`);
+ }
+
+ checkEarlyDetection(raidData) {
+ // Check if any watchtower can detect the raid
+ for (const tower of this.watchtowers) {
+ if (tower.isBuilding) continue;
+
+ const distance = Phaser.Math.Distance.Between(
+ tower.x, tower.y,
+ raidData.x, raidData.y
+ );
+
+ if (distance <= tower.losRange) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ onRaidAttack(raidData) {
+ // Calculate defense vs attack
+ const raidStrength = raidData.strength;
+ const cityDefense = this.cityDefenseRating;
+
+ console.log(`โ๏ธ Raid Attack: ${raidStrength} vs Defense: ${cityDefense}`);
+
+ if (cityDefense >= raidStrength) {
+ // City defense holds!
+ this.raidRepelled(raidData);
+ } else {
+ // Walls take damage
+ this.wallsDamaged(raidStrength - cityDefense);
+ }
+ }
+
+ raidRepelled(raidData) {
+ this.activeRaids = this.activeRaids.filter(r => r.id !== raidData.id);
+
+ this.scene.events.emit('show-notification', {
+ title: 'โ
RAID ODBIJEN!',
+ message: 'Obzidja so ustavila roparje!',
+ icon: '๐ก๏ธ',
+ duration: 5000,
+ color: '#00FF00'
+ });
+
+ // Reward for successful defense
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addGold(raidData.strength * 10);
+ }
+
+ console.log(`โ
Raid repelled successfully`);
+ }
+
+ wallsDamaged(damage) {
+ // Damage weakest walls first
+ const sortedWalls = [...this.walls].sort((a, b) => a.health - b.health);
+
+ let remainingDamage = damage;
+ for (const wall of sortedWalls) {
+ if (remainingDamage <= 0) break;
+ if (wall.isBuilding) continue;
+
+ const damageToWall = Math.min(wall.health, remainingDamage);
+ wall.health -= damageToWall;
+ remainingDamage -= damageToWall;
+
+ // Visual damage
+ if (wall.sprite) {
+ wall.sprite.setTint(0xFF4444);
+ setTimeout(() => wall.sprite.clearTint(), 500);
+ }
+
+ // Wall destroyed
+ if (wall.health <= 0) {
+ this.destroyWall(wall);
+ }
+ }
+
+ this.scene.events.emit('show-notification', {
+ title: 'โ ๏ธ Obzidja Poลกkodovana',
+ message: `Raid je povzroฤil ${damage} ลกkode!`,
+ icon: '๐ฅ',
+ duration: 4000,
+ color: '#FF4444'
+ });
+
+ this.updateDefenseRating();
+ }
+
+ destroyWall(wall) {
+ if (wall.sprite) {
+ // Destruction animation
+ this.scene.tweens.add({
+ targets: wall.sprite,
+ alpha: 0,
+ scaleX: 0.5,
+ scaleY: 0.5,
+ duration: 500,
+ onComplete: () => wall.sprite.destroy()
+ });
+ }
+
+ this.walls = this.walls.filter(w => w.id !== wall.id);
+ console.log(`๐ฅ Wall destroyed: ${wall.id}`);
+ }
+
+ /**
+ * PATROL SYSTEM
+ */
+ unlockPatrols() {
+ this.patrolsUnlocked = true;
+ console.log('โ
Patrol system unlocked');
+ }
+
+ createPatrol(route) {
+ if (!this.patrolsUnlocked) {
+ console.warn('Patrols not unlocked yet');
+ return null;
+ }
+
+ const patrol = {
+ id: `patrol_${Date.now()}`,
+ route: route, // Array of {x, y} waypoints
+ currentWaypoint: 0,
+ guards: [],
+ active: true
+ };
+
+ this.patrols.push(patrol);
+ return patrol;
+ }
+
+ /**
+ * DEFENSE RATING CALCULATION
+ */
+ updateDefenseRating() {
+ let rating = 0;
+
+ // Add wall defense
+ this.walls.forEach(wall => {
+ if (!wall.isBuilding) {
+ const healthPercent = wall.health / wall.maxHealth;
+ rating += this.wallTiers[wall.tier].defense * healthPercent;
+ }
+ });
+
+ // Add watchtower bonus
+ rating += this.watchtowers.filter(t => !t.isBuilding).length * 10;
+
+ // Add patrol bonus
+ rating += this.patrols.filter(p => p.active).length * 5;
+
+ this.cityDefenseRating = Math.floor(rating);
+
+ // Emit update
+ this.scene.events.emit('defense:rating_updated', this.cityDefenseRating);
+
+ console.log(`๐ก๏ธ Defense Rating: ${this.cityDefenseRating}`);
+ }
+
+ startDefenseUpdates() {
+ // Update defense every 5 seconds
+ setInterval(() => {
+ this.updateDefenseRating();
+ }, 5000);
+ }
+
+ /**
+ * UTILITY FUNCTIONS
+ */
+ hasResources(cost) {
+ if (!this.scene.inventorySystem) return true; // Dev mode
+
+ for (const [resource, amount] of Object.entries(cost)) {
+ if (!this.scene.inventorySystem.hasItem(resource, amount)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ deductResources(cost) {
+ if (!this.scene.inventorySystem) return;
+
+ for (const [resource, amount] of Object.entries(cost)) {
+ this.scene.inventorySystem.removeItem(resource, amount);
+ }
+ }
+
+ formatCost(cost) {
+ return Object.entries(cost)
+ .map(([resource, amount]) => `${amount}x ${resource}`)
+ .join(', ');
+ }
+
+ /**
+ * GET STATUS FOR UI
+ */
+ getDefenseStatus() {
+ return {
+ rating: this.cityDefenseRating,
+ walls: this.walls.length,
+ watchtowers: this.watchtowers.length,
+ patrols: this.patrols.length,
+ activeRaids: this.activeRaids.length
+ };
+ }
+
+ destroy() {
+ this.walls.forEach(wall => {
+ if (wall.sprite) wall.sprite.destroy();
+ if (wall.losCircle) wall.losCircle.destroy();
+ });
+
+ this.watchtowers.forEach(tower => {
+ if (tower.sprite) tower.sprite.destroy();
+ if (tower.losCircle) tower.losCircle.destroy();
+ });
+
+ this.walls = [];
+ this.watchtowers = [];
+ this.patrols = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/DialogueSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/DialogueSystem.js
new file mode 100644
index 000000000..d8c09cf4d
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/DialogueSystem.js
@@ -0,0 +1,516 @@
+/**
+ * DialogueSystem.js
+ * =================
+ * KRVAVA ลฝETEV - NPC Dialogue & Conversation System
+ *
+ * Features:
+ * - Dynamic dialogue trees with choices
+ * - Character portraits and emotions
+ * - Quest integration (dialogue can trigger/complete quests)
+ * - Relationship tracking (affects dialogue options)
+ * - Twin Bond special dialogues (Ana's voice)
+ * - Memory system (NPCs remember past conversations)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class DialogueSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Active dialogue state
+ this.currentDialogue = null;
+ this.currentSpeaker = null;
+ this.currentNode = null;
+ this.dialogueHistory = new Map(); // NPC ID -> conversation history
+
+ // UI elements
+ this.dialogueBox = null;
+ this.portraitSprite = null;
+ this.nameText = null;
+ this.dialogueText = null;
+ this.choicesContainer = null;
+
+ // Dialogue database
+ this.dialogues = new Map(); // Dialogue ID -> dialogue data
+
+ // Callbacks
+ this.onDialogueComplete = null;
+ this.onChoiceMade = null;
+
+ console.log('๐ฌ DialogueSystem initialized');
+ }
+
+ /**
+ * Register a dialogue tree
+ * @param {string} dialogueId - Unique identifier
+ * @param {object} dialogueData - Tree structure
+ */
+ registerDialogue(dialogueId, dialogueData) {
+ this.dialogues.set(dialogueId, dialogueData);
+ console.log(`๐ฌ Registered dialogue: ${dialogueId}`);
+ }
+
+ /**
+ * Start a dialogue with an NPC
+ * @param {string} dialogueId - Which dialogue to start
+ * @param {object} speaker - NPC or character data
+ * @param {function} onComplete - Callback when done
+ */
+ startDialogue(dialogueId, speaker, onComplete) {
+ const dialogueData = this.dialogues.get(dialogueId);
+
+ if (!dialogueData) {
+ console.error(`Dialogue not found: ${dialogueId}`);
+ return;
+ }
+
+ this.currentDialogue = dialogueData;
+ this.currentSpeaker = speaker;
+ this.onDialogueComplete = onComplete;
+
+ // Show UI
+ this.createDialogueUI();
+
+ // Start at root node
+ this.showNode(dialogueData.root || 'start');
+
+ // Pause game
+ this.scene.physics.pause();
+
+ console.log(`๐ฌ Started dialogue: ${dialogueId} with ${speaker.name}`);
+ }
+
+ /**
+ * Show a specific dialogue node
+ * @param {string} nodeId - Node to display
+ */
+ showNode(nodeId) {
+ const node = this.currentDialogue.nodes[nodeId];
+
+ if (!node) {
+ console.error(`Node not found: ${nodeId}`);
+ this.endDialogue();
+ return;
+ }
+
+ this.currentNode = node;
+
+ // Update speaker info
+ const speaker = node.speaker || this.currentSpeaker.name;
+ const emotion = node.emotion || 'neutral';
+
+ this.updateSpeaker(speaker, emotion);
+
+ // Show text with typewriter effect
+ this.typewriterText(node.text);
+
+ // Show choices or continue button
+ if (node.choices && node.choices.length > 0) {
+ this.showChoices(node.choices);
+ } else if (node.next) {
+ this.showContinueButton(node.next);
+ } else {
+ // End of dialogue
+ this.showContinueButton('END');
+ }
+
+ // Execute node actions
+ if (node.action) {
+ this.executeNodeAction(node.action);
+ }
+
+ // Record in history
+ this.addToHistory(nodeId, node.text);
+ }
+
+ /**
+ * Create dialogue UI
+ */
+ createDialogueUI() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Container for everything
+ this.dialogueContainer = this.scene.add.container(0, 0);
+ this.dialogueContainer.setDepth(1000);
+
+ // Dark overlay
+ const overlay = this.scene.add.rectangle(0, 0, width, height, 0x000000, 0.5);
+ overlay.setOrigin(0);
+ overlay.setInteractive();
+ this.dialogueContainer.add(overlay);
+
+ // Dialogue box
+ const boxY = height - 200;
+ const boxHeight = 180;
+
+ this.dialogueBox = this.scene.add.rectangle(
+ width / 2, boxY,
+ width - 80, boxHeight,
+ 0x2d1b00, 0.95
+ );
+ this.dialogueBox.setStrokeStyle(3, 0xd4a574);
+ this.dialogueContainer.add(this.dialogueBox);
+
+ // Portrait background
+ this.portraitBg = this.scene.add.rectangle(100, boxY, 100, 100, 0x4a3520, 0.9);
+ this.portraitBg.setStrokeStyle(2, 0xd4a574);
+ this.dialogueContainer.add(this.portraitBg);
+
+ // Portrait
+ this.portraitSprite = this.scene.add.text(100, boxY, '๐ค', {
+ fontSize: '64px'
+ });
+ this.portraitSprite.setOrigin(0.5);
+ this.dialogueContainer.add(this.portraitSprite);
+
+ // Speaker name
+ this.nameText = this.scene.add.text(170, boxY - 70, '', {
+ fontSize: '20px',
+ fontFamily: 'Georgia, serif',
+ color: '#FFD700',
+ fontStyle: 'bold',
+ stroke: '#000000',
+ strokeThickness: 3
+ });
+ this.dialogueContainer.add(this.nameText);
+
+ // Dialogue text
+ this.dialogueText = this.scene.add.text(170, boxY - 40, '', {
+ fontSize: '18px',
+ fontFamily: 'Georgia, serif',
+ color: '#f4e4c1',
+ wordWrap: { width: width - 280 },
+ lineSpacing: 6
+ });
+ this.dialogueContainer.add(this.dialogueText);
+
+ // Choices container
+ this.choicesContainer = this.scene.add.container(width / 2, boxY + 120);
+ this.dialogueContainer.add(this.choicesContainer);
+ }
+
+ /**
+ * Update speaker display
+ */
+ updateSpeaker(name, emotion) {
+ this.nameText.setText(name);
+
+ // Get portrait based on speaker and emotion
+ const portrait = this.getPortrait(name, emotion);
+ this.portraitSprite.setText(portrait);
+ }
+
+ /**
+ * Get portrait emoji
+ */
+ getPortrait(speaker, emotion) {
+ // Kai portraits
+ if (speaker === 'Kai' || speaker === 'You') {
+ const kaiEmotions = {
+ 'neutral': '๐จ',
+ 'happy': '๐',
+ 'sad': '๐ข',
+ 'angry': '๐ ',
+ 'worried': '๐',
+ 'shocked': '๐ฑ',
+ 'determined': '๐ค'
+ };
+ return kaiEmotions[emotion] || '๐จ';
+ }
+
+ // Ana portraits (Twin Bond - ghostly)
+ if (speaker === 'Ana' || speaker === 'Ana (Twin Bond)') {
+ const anaEmotions = {
+ 'neutral': '๐ป',
+ 'happy': '๐',
+ 'sad': '๐ฐ',
+ 'worried': '๐จ',
+ 'pain': '๐ฃ',
+ 'help': '๐'
+ };
+ return anaEmotions[emotion] || '๐ป';
+ }
+
+ // Other NPCs
+ const npcPortraits = {
+ 'Grok': '๐ง',
+ 'Elder': '๐ด',
+ 'Blacksmith': '๐จ',
+ 'Baker': '๐',
+ 'Doctor': 'โ๏ธ',
+ 'Merchant': '๐ฐ',
+ 'Stranger': 'โ',
+ 'Zombie': '๐ง'
+ };
+
+ return npcPortraits[speaker] || '๐ค';
+ }
+
+ /**
+ * Typewriter text effect
+ */
+ typewriterText(text) {
+ let displayText = '';
+ let charIndex = 0;
+
+ this.dialogueText.setText('');
+
+ // Store reference to timer so we can clear it if needed
+ this.typewriterTimer = this.scene.time.addEvent({
+ delay: 30,
+ callback: () => {
+ // SAFETY CHECK: Ensure text object still exists
+ if (!this.dialogueText || !this.dialogueText.active) {
+ if (this.typewriterTimer) this.typewriterTimer.remove();
+ return;
+ }
+
+ if (charIndex < text.length) {
+ displayText += text[charIndex];
+ this.dialogueText.setText(displayText);
+ charIndex++;
+ } else {
+ if (this.typewriterTimer) this.typewriterTimer.remove();
+ }
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * Show dialogue choices
+ */
+ showChoices(choices) {
+ this.choicesContainer.removeAll(true);
+
+ choices.forEach((choice, index) => {
+ // Check if choice is available
+ if (choice.condition && !this.checkCondition(choice.condition)) {
+ return; // Skip this choice
+ }
+
+ const y = index * 50;
+
+ // Choice button background
+ const btn = this.scene.add.rectangle(0, y, 600, 40, 0x6b4423, 1);
+ btn.setStrokeStyle(2, 0xd4a574);
+ btn.setInteractive({ useHandCursor: true });
+
+ // Choice text
+ const text = this.scene.add.text(0, y, choice.text, {
+ fontSize: '16px',
+ fontFamily: 'Georgia, serif',
+ color: '#f4e4c1',
+ fontStyle: 'bold'
+ });
+ text.setOrigin(0.5);
+
+ // Hover effects
+ btn.on('pointerover', () => {
+ btn.setFillStyle(0x8b5a3c);
+ text.setColor('#FFD700');
+ });
+
+ btn.on('pointerout', () => {
+ btn.setFillStyle(0x6b4423);
+ text.setColor('#f4e4c1');
+ });
+
+ // Click handler
+ btn.on('pointerdown', () => {
+ this.onChoiceSelected(choice);
+ });
+
+ this.choicesContainer.add([btn, text]);
+ });
+ }
+
+ /**
+ * Show continue button
+ */
+ showContinueButton(nextNode) {
+ this.choicesContainer.removeAll(true);
+
+ const continueBtn = this.scene.add.text(0, 0, 'โผ Continue (SPACE)', {
+ fontSize: '16px',
+ fontFamily: 'Georgia, serif',
+ color: '#888888'
+ });
+ continueBtn.setOrigin(0.5);
+
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: continueBtn,
+ alpha: 0.3,
+ duration: 800,
+ yoyo: true,
+ repeat: -1
+ });
+
+ this.choicesContainer.add(continueBtn);
+
+ // Space key or click to continue
+ const spaceKey = this.scene.input.keyboard.addKey('SPACE');
+ spaceKey.once('down', () => {
+ if (nextNode === 'END') {
+ this.endDialogue();
+ } else {
+ this.showNode(nextNode);
+ }
+ });
+ }
+
+ /**
+ * Handle choice selection
+ */
+ onChoiceSelected(choice) {
+ console.log(`๐ฌ Choice selected: ${choice.text}`);
+
+ // Execute choice action
+ if (choice.action) {
+ this.executeNodeAction(choice.action);
+ }
+
+ // Callback
+ if (this.onChoiceMade) {
+ this.onChoiceMade(choice);
+ }
+
+ // Go to next node
+ if (choice.next) {
+ this.showNode(choice.next);
+ } else {
+ this.endDialogue();
+ }
+ }
+
+ /**
+ * Execute node action
+ */
+ executeNodeAction(action) {
+ switch (action.type) {
+ case 'quest_start':
+ this.scene.questSystem?.startQuest(action.questId);
+ break;
+
+ case 'quest_complete':
+ this.scene.questSystem?.completeQuest(action.questId);
+ break;
+
+ case 'give_item':
+ this.scene.inventorySystem?.addItem(action.itemId, action.amount);
+ break;
+
+ case 'take_item':
+ this.scene.inventorySystem?.removeItem(action.itemId, action.amount);
+ break;
+
+ case 'relationship_change':
+ this.changeRelationship(action.npcId, action.amount);
+ break;
+
+ case 'custom':
+ if (action.callback) {
+ action.callback(this.scene);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Check if condition is met
+ */
+ checkCondition(condition) {
+ switch (condition.type) {
+ case 'quest_active':
+ return this.scene.questSystem?.isQuestActive(condition.questId);
+
+ case 'quest_complete':
+ return this.scene.questSystem?.isQuestComplete(condition.questId);
+
+ case 'has_item':
+ return this.scene.inventorySystem?.hasItem(condition.itemId, condition.amount);
+
+ case 'relationship':
+ return this.getRelationship(condition.npcId) >= condition.value;
+
+ case 'custom':
+ return condition.check(this.scene);
+
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Add to conversation history
+ */
+ addToHistory(nodeId, text) {
+ const speakerId = this.currentSpeaker.id || this.currentSpeaker.name;
+
+ if (!this.dialogueHistory.has(speakerId)) {
+ this.dialogueHistory.set(speakerId, []);
+ }
+
+ this.dialogueHistory.get(speakerId).push({
+ nodeId: nodeId,
+ text: text,
+ timestamp: Date.now()
+ });
+ }
+
+ /**
+ * Relationship tracking
+ */
+ changeRelationship(npcId, amount) {
+ // TODO: Integrate with proper relationship system
+ console.log(`๐ Relationship with ${npcId}: ${amount > 0 ? '+' : ''}${amount}`);
+ }
+
+ getRelationship(npcId) {
+ // TODO: Get from relationship system
+ return 0;
+ }
+
+ /**
+ * End dialogue
+ */
+ endDialogue() {
+ console.log('๐ฌ Dialogue ended');
+
+ // Stop typewriter effect if running
+ if (this.typewriterTimer) {
+ this.typewriterTimer.remove();
+ this.typewriterTimer = null;
+ }
+
+ // Clean up UI
+ if (this.dialogueContainer) {
+ this.dialogueContainer.destroy();
+ }
+
+ // Resume game
+ this.scene.physics.resume();
+
+ // Callback
+ if (this.onDialogueComplete) {
+ this.onDialogueComplete();
+ }
+
+ // Reset state
+ this.currentDialogue = null;
+ this.currentSpeaker = null;
+ this.currentNode = null;
+ this.onDialogueComplete = null;
+ }
+
+ /**
+ * Check if dialogue is active
+ */
+ isActive() {
+ return this.currentDialogue !== null;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/DrugEconomySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/DrugEconomySystem.js
new file mode 100644
index 000000000..d9c6ffe96
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/DrugEconomySystem.js
@@ -0,0 +1,476 @@
+/**
+ * DRUG & NARCO-ECONOMY SYSTEM
+ * Mrtva Dolina - Psychedelic Effects & Illegal Trade
+ *
+ * Features:
+ * - Marijuana effects (slow-mo, chill, energy regen)
+ * - Mushroom hallucinations (color distortion, ghosts)
+ * - Free trade (no police until Mayor establishes it)
+ * - Drug economy & black market
+ */
+
+export class DrugEconomySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Drug effects tracking
+ this.activeEffects = {
+ marijuana: false,
+ mushrooms: false
+ };
+
+ // Duration timers
+ this.effectTimers = {};
+
+ // Police state
+ this.policeEstablished = false;
+
+ // Black market prices
+ this.prices = {
+ marijuana: { buy: 50, sell: 80 },
+ mushrooms: { buy: 100, sell: 150 },
+ marijuana_edible: { buy: 75, sell: 120 }
+ };
+
+ // Effects configuration
+ this.effectConfig = {
+ marijuana: {
+ duration: 180000, // 3 minutes
+ timeScale: 0.7, // 70% speed (slower)
+ energyRegenBonus: 2.0, // 2x regen
+ walkSpeedMultiplier: 0.85, // 15% slower walk
+ musicFilter: 'chill', // Music becomes chill
+ visualFilter: 'subtle_blur'
+ },
+ mushrooms: {
+ duration: 300000, // 5 minutes
+ colorShift: true,
+ hallucinationChance: 0.3, // 30% chance per second
+ objectMovement: true,
+ ghostVisions: true,
+ psychedelicShader: 'trippy_colors'
+ }
+ };
+
+ this.init();
+ }
+
+ init() {
+ // Listen for consumption events
+ this.scene.events.on('player:consume_drug', this.onDrugConsumed, this);
+ this.scene.events.on('police:established', this.onPoliceEstablished, this);
+
+ console.log('โ
DrugEconomySystem initialized - Free trade active');
+ }
+
+ /**
+ * CONSUME DRUG
+ */
+ onDrugConsumed(drugType, method = 'smoke') {
+ if (drugType === 'marijuana') {
+ this.consumeMarijuana(method);
+ } else if (drugType === 'mushrooms') {
+ this.consumeMushrooms();
+ }
+ }
+
+ /**
+ * MARIJUANA EFFECTS
+ */
+ consumeMarijuana(method) {
+ if (this.activeEffects.marijuana) {
+ console.log('๐ฟ Already high on marijuana');
+ return;
+ }
+
+ this.activeEffects.marijuana = true;
+ const config = this.effectConfig.marijuana;
+
+ console.log(`๐ฟ Marijuana consumed (${method}) - Chill mode activated`);
+
+ // Visual effects
+ this.applyMarijuanaVisuals(config);
+
+ // Gameplay effects
+ this.applyMarijuanaGameplay(config);
+
+ // Audio effects
+ this.applyMarijuanaAudio(config);
+
+ // Show notification
+ this.scene.events.emit('show-notification', {
+ title: '๐ฟ Chill Mode',
+ message: 'ฤas se upoฤasni... vse je bolj mirno...',
+ icon: '๐',
+ duration: 5000,
+ color: '#90EE90'
+ });
+
+ // Set duration timer
+ this.effectTimers.marijuana = setTimeout(() => {
+ this.endMarijuanaEffects();
+ }, config.duration);
+ }
+
+ applyMarijuanaVisuals(config) {
+ // Subtle blur filter
+ const camera = this.scene.cameras.main;
+
+ // Add slight vignette and blur
+ this.scene.tweens.add({
+ targets: camera,
+ scrollX: camera.scrollX + 2,
+ scrollY: camera.scrollY + 2,
+ duration: 2000,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+
+ // Subtle color tint (light green)
+ camera.setTint(0xE8F5E9); // Very light green tint
+ }
+
+ applyMarijuanaGameplay(config) {
+ // Slow down time
+ this.scene.time.timeScale = config.timeScale;
+
+ // Increase energy regen
+ if (this.scene.player) {
+ this.scene.player.energyRegenRate *= config.energyRegenBonus;
+ this.scene.player.walkSpeed *= config.walkSpeedMultiplier;
+ }
+ }
+
+ applyMarijuanaAudio(config) {
+ // Change music to chill variant
+ const currentMusic = this.scene.sound.get('background_music');
+ if (currentMusic) {
+ // Fade out current
+ this.scene.tweens.add({
+ targets: currentMusic,
+ volume: 0,
+ duration: 2000,
+ onComplete: () => {
+ currentMusic.stop();
+ // Play chill music
+ this.scene.sound.play('music_chill_lofi', {
+ loop: true,
+ volume: 0.4
+ });
+ }
+ });
+ }
+ }
+
+ endMarijuanaEffects() {
+ this.activeEffects.marijuana = false;
+
+ // Restore time scale
+ this.scene.time.timeScale = 1.0;
+
+ // Restore player stats
+ if (this.scene.player) {
+ this.scene.player.energyRegenRate /= this.effectConfig.marijuana.energyRegenBonus;
+ this.scene.player.walkSpeed /= this.effectConfig.marijuana.walkSpeedMultiplier;
+ }
+
+ // Remove visual effects
+ const camera = this.scene.cameras.main;
+ camera.clearTint();
+ this.scene.tweens.killTweensOf(camera);
+
+ // Restore original music
+ const chillMusic = this.scene.sound.get('music_chill_lofi');
+ if (chillMusic) {
+ this.scene.tweens.add({
+ targets: chillMusic,
+ volume: 0,
+ duration: 2000,
+ onComplete: () => {
+ chillMusic.stop();
+ this.scene.sound.play('background_music', { loop: true, volume: 0.5 });
+ }
+ });
+ }
+
+ console.log('๐ฟ Marijuana effects ended');
+ }
+
+ /**
+ * MUSHROOM HALLUCINATION EFFECTS
+ */
+ consumeMushrooms() {
+ if (this.activeEffects.mushrooms) {
+ console.log('๐ Already tripping on mushrooms');
+ return;
+ }
+
+ this.activeEffects.mushrooms = true;
+ const config = this.effectConfig.mushrooms;
+
+ console.log('๐ Mushrooms consumed - Reality dissolving...');
+
+ // Apply psychedelic shader
+ this.applyPsychedelicShader(config);
+
+ // Start hallucination events
+ this.startHallucinations(config);
+
+ // Show notification
+ this.scene.events.emit('show-notification', {
+ title: '๐ Hallucinacije',
+ message: 'Barve... gibanje... duhovi iz preteklosti...',
+ icon: '๐',
+ duration: 7000,
+ color: '#FF1493'
+ });
+
+ // Set duration timer
+ this.effectTimers.mushrooms = setTimeout(() => {
+ this.endMushroomEffects();
+ }, config.duration);
+ }
+
+ applyPsychedelicShader(config) {
+ const camera = this.scene.cameras.main;
+
+ // Extreme color shifting
+ this.colorShiftTimer = setInterval(() => {
+ const hue = Phaser.Math.Between(0, 360);
+ const color = Phaser.Display.Color.HSVToRGB(hue / 360, 0.8, 0.9);
+ camera.setTint(color.color);
+ }, 500); // Change every 500ms
+
+ // Camera shake (mild)
+ camera.shake(config.duration, 0.002);
+
+ // Zoom pulse
+ this.scene.tweens.add({
+ targets: camera,
+ zoom: 1.05,
+ duration: 3000,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ startHallucinations(config) {
+ // Periodic hallucination events
+ this.hallucinationInterval = setInterval(() => {
+ if (Math.random() < config.hallucinationChance) {
+ this.triggerHallucination();
+ }
+ }, 1000); // Check every second
+ }
+
+ triggerHallucination() {
+ const hallucinationTypes = [
+ 'moving_objects',
+ 'ghost_vision',
+ 'color_trails',
+ 'reality_distortion'
+ ];
+
+ const type = Phaser.Utils.Array.GetRandom(hallucinationTypes);
+
+ switch (type) {
+ case 'moving_objects':
+ this.hallucinateMovingObjects();
+ break;
+ case 'ghost_vision':
+ this.hallucinateGhostVision();
+ break;
+ case 'color_trails':
+ this.hallucinateColorTrails();
+ break;
+ case 'reality_distortion':
+ this.hallucinateRealityDistortion();
+ break;
+ }
+ }
+
+ hallucinateMovingObjects() {
+ // Random game objects start floating/moving
+ const objects = this.scene.children.list.filter(obj =>
+ obj.type === 'Sprite' && obj !== this.scene.player
+ );
+
+ if (objects.length === 0) return;
+
+ const obj = Phaser.Utils.Array.GetRandom(objects);
+ const originalX = obj.x;
+ const originalY = obj.y;
+
+ this.scene.tweens.add({
+ targets: obj,
+ x: originalX + Phaser.Math.Between(-30, 30),
+ y: originalY + Phaser.Math.Between(-30, 30),
+ duration: 2000,
+ yoyo: true,
+ ease: 'Sine.easeInOut',
+ onComplete: () => {
+ obj.x = originalX;
+ obj.y = originalY;
+ }
+ });
+ }
+
+ hallucinateGhostVision() {
+ // Spawn ghost from Kai's past
+ const ghosts = ['ana_ghost', 'family_ghost', 'memory_ghost'];
+ const ghostType = Phaser.Utils.Array.GetRandom(ghosts);
+
+ const x = this.scene.player.x + Phaser.Math.Between(-200, 200);
+ const y = this.scene.player.y + Phaser.Math.Between(-200, 200);
+
+ const ghost = this.scene.add.sprite(x, y, ghostType);
+ ghost.setAlpha(0);
+ ghost.setDepth(20);
+ ghost.setTint(0x9966FF); // Purple ghost tint
+
+ // Fade in
+ this.scene.tweens.add({
+ targets: ghost,
+ alpha: 0.7,
+ duration: 1000,
+ onComplete: () => {
+ // Ghost speaks
+ this.scene.events.emit('show-speech-bubble', {
+ x: ghost.x,
+ y: ghost.y - 50,
+ text: this.getGhostMessage(),
+ duration: 3000
+ });
+
+ // Fade out
+ this.scene.tweens.add({
+ targets: ghost,
+ alpha: 0,
+ duration: 2000,
+ delay: 3000,
+ onComplete: () => ghost.destroy()
+ });
+ }
+ });
+ }
+
+ getGhostMessage() {
+ const messages = [
+ "Kai... ne pozabi...",
+ "Ana te iลกฤe...",
+ "Spomniti se moraลก...",
+ "Vrni se domov..."
+ ];
+ return Phaser.Utils.Array.GetRandom(messages);
+ }
+
+ hallucinateColorTrails() {
+ // Player leaves colorful trails when moving
+ if (!this.scene.player.isMoving) return;
+
+ const trail = this.scene.add.sprite(
+ this.scene.player.x,
+ this.scene.player.y,
+ this.scene.player.texture.key
+ );
+ trail.setFrame(this.scene.player.frame.name);
+ trail.setAlpha(0.5);
+ trail.setTint(Phaser.Math.Between(0x000000, 0xFFFFFF));
+
+ this.scene.tweens.add({
+ targets: trail,
+ alpha: 0,
+ duration: 1000,
+ onComplete: () => trail.destroy()
+ });
+ }
+
+ hallucinateRealityDistortion() {
+ // Screen warps/ripples
+ const camera = this.scene.cameras.main;
+
+ this.scene.tweens.add({
+ targets: camera,
+ scrollX: camera.scrollX + Phaser.Math.Between(-20, 20),
+ scrollY: camera.scrollY + Phaser.Math.Between(-20, 20),
+ duration: 500,
+ yoyo: true,
+ repeat: 3
+ });
+ }
+
+ endMushroomEffects() {
+ this.activeEffects.mushrooms = false;
+
+ // Stop color shift
+ if (this.colorShiftTimer) {
+ clearInterval(this.colorShiftTimer);
+ this.colorShiftTimer = null;
+ }
+
+ // Stop hallucinations
+ if (this.hallucinationInterval) {
+ clearInterval(this.hallucinationInterval);
+ this.hallucinationInterval = null;
+ }
+
+ // Restore camera
+ const camera = this.scene.cameras.main;
+ camera.clearTint();
+ camera.setZoom(1.0);
+ this.scene.tweens.killTweensOf(camera);
+
+ console.log('๐ Mushroom effects ended - Reality restored');
+ }
+
+ /**
+ * BLACK MARKET TRADE
+ */
+ sellDrug(drugType, quantity) {
+ if (this.policeEstablished) {
+ // Risk of getting caught!
+ if (Math.random() < 0.3) { // 30% chance
+ this.scene.events.emit('police:drug_bust', drugType, quantity);
+ return false;
+ }
+ }
+
+ const price = this.prices[drugType].sell;
+ const earnings = price * quantity;
+
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.removeItem(drugType, quantity);
+ this.scene.inventorySystem.addGold(earnings);
+ }
+
+ console.log(`๐ฐ Sold ${quantity}x ${drugType} for ${earnings} gold`);
+ return true;
+ }
+
+ /**
+ * POLICE ESTABLISHMENT
+ */
+ onPoliceEstablished() {
+ this.policeEstablished = true;
+
+ this.scene.events.emit('show-notification', {
+ title: '๐ Policija Ustanovljena',
+ message: 'Previdno! Zdaj ti lahko zaseลพe droge!',
+ icon: 'โ ๏ธ',
+ duration: 5000,
+ color: '#FF0000'
+ });
+
+ console.log('๐ Police established - Drug trade now illegal and risky');
+ }
+
+ destroy() {
+ this.endMarijuanaEffects();
+ this.endMushroomEffects();
+
+ if (this.effectTimers.marijuana) clearTimeout(this.effectTimers.marijuana);
+ if (this.effectTimers.mushrooms) clearTimeout(this.effectTimers.mushrooms);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/DynamicTypewriterSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/DynamicTypewriterSystem.js
new file mode 100644
index 000000000..9a8d3cf11
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/DynamicTypewriterSystem.js
@@ -0,0 +1,302 @@
+/**
+ * DynamicTypewriterSystem.js
+ *
+ * NO VOICE RECORDING NEEDED!
+ * Dynamic typewriter effect for ALL dialogue
+ *
+ * Features:
+ * - Character-by-character text reveal
+ * - Adjustable speed (ADHD-friendly options)
+ * - Skip on click/key
+ * - Sound effects per character (optional)
+ * - Accessibility options
+ *
+ * Created: Jan 10, 2026
+ * Author: David "HIPO" Kotnik
+ * Studio: Hipodevil666 Studiosโข
+ */
+
+export default class DynamicTypewriterSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Typewriter settings
+ this.speed = 50; // ms per character (default)
+ this.speedOptions = {
+ slow: 80, // Slow readers
+ normal: 50, // Default
+ fast: 30, // Fast readers
+ instant: 0 // Skip animation (ADHD option)
+ };
+
+ // Current dialogue
+ this.currentText = null;
+ this.currentDialogue = '';
+ this.currentIndex = 0;
+ this.isTyping = false;
+
+ // Type sound
+ this.typeSound = null;
+
+ // Timer
+ this.typeTimer = null;
+
+ console.log('โจ๏ธ Dynamic Typewriter System initialized!');
+ }
+
+ /**
+ * Preload typewriter sound
+ */
+ preloadAssets() {
+ this.scene.load.audio('type_sound', 'assets/audio/ui/typewriter.ogg');
+ }
+
+ /**
+ * Initialize after preload
+ */
+ initialize() {
+ this.typeSound = this.scene.sound.add('type_sound', {
+ volume: 0.1,
+ rate: 1.5 // Faster playback
+ });
+
+ console.log('โจ๏ธ Typewriter ready!');
+ }
+
+ /**
+ * Set typing speed
+ */
+ setSpeed(speedName) {
+ if (this.speedOptions[speedName] !== undefined) {
+ this.speed = this.speedOptions[speedName];
+ console.log(`โจ๏ธ Typewriter speed: ${speedName} (${this.speed}ms)`);
+ }
+ }
+
+ /**
+ * Start typewriter effect for dialogue
+ *
+ * @param {Phaser.GameObjects.Text} textObject - Text object to type into
+ * @param {string} fullText - Complete text to display
+ * @param {function} onComplete - Callback when typing finishes
+ */
+ startTyping(textObject, fullText, onComplete = null) {
+ // Stop any existing typing
+ this.stopTyping();
+
+ // Store references
+ this.currentText = textObject;
+ this.currentDialogue = fullText;
+ this.currentIndex = 0;
+ this.isTyping = true;
+
+ // Clear text
+ textObject.setText('');
+
+ // Instant mode (ADHD accessibility)
+ if (this.speed === 0) {
+ textObject.setText(fullText);
+ this.isTyping = false;
+ if (onComplete) onComplete();
+ return;
+ }
+
+ // Start typing animation
+ this.typeNextCharacter(onComplete);
+
+ // Allow skip on click/key
+ this.enableSkip(textObject, fullText, onComplete);
+ }
+
+ /**
+ * Type next character
+ */
+ typeNextCharacter(onComplete) {
+ if (!this.isTyping) return;
+
+ if (this.currentIndex < this.currentDialogue.length) {
+ // Add next character
+ const nextChar = this.currentDialogue[this.currentIndex];
+ this.currentText.setText(
+ this.currentText.text + nextChar
+ );
+
+ // Play type sound (not for spaces)
+ if (nextChar !== ' ' && this.typeSound && !this.typeSound.isPlaying) {
+ this.typeSound.play();
+ }
+
+ this.currentIndex++;
+
+ // Schedule next character
+ this.typeTimer = this.scene.time.delayedCall(this.speed, () => {
+ this.typeNextCharacter(onComplete);
+ });
+ } else {
+ // Typing complete
+ this.isTyping = false;
+ if (onComplete) onComplete();
+ console.log('โจ๏ธ Typing complete!');
+ }
+ }
+
+ /**
+ * Enable skip on click/key
+ */
+ enableSkip(textObject, fullText, onComplete) {
+ // Skip on click
+ const skipOnClick = () => {
+ if (this.isTyping) {
+ this.stopTyping();
+ textObject.setText(fullText);
+ if (onComplete) onComplete();
+ this.scene.input.off('pointerdown', skipOnClick);
+ }
+ };
+
+ this.scene.input.on('pointerdown', skipOnClick);
+
+ // Skip on SPACE or ENTER
+ const skipOnKey = (event) => {
+ if ((event.key === ' ' || event.key === 'Enter') && this.isTyping) {
+ this.stopTyping();
+ textObject.setText(fullText);
+ if (onComplete) onComplete();
+ this.scene.input.keyboard.off('keydown', skipOnKey);
+ }
+ };
+
+ this.scene.input.keyboard.on('keydown', skipOnKey);
+ }
+
+ /**
+ * Stop typing animation
+ */
+ stopTyping() {
+ this.isTyping = false;
+
+ if (this.typeTimer) {
+ this.typeTimer.remove();
+ this.typeTimer = null;
+ }
+ }
+
+ /**
+ * Type dialogue with NPC portrait
+ *
+ * Complete dialogue box with portrait + name + text
+ */
+ showDialogueBox(npcName, portraitKey, dialogueText, onComplete = null) {
+ const scene = this.scene;
+ const { width, height } = scene.cameras.main;
+
+ // Dialogue box background
+ const boxHeight = 200;
+ const boxY = height - boxHeight - 20;
+
+ const dialogueBox = scene.add.rectangle(
+ width / 2,
+ boxY + boxHeight / 2,
+ width - 40,
+ boxHeight,
+ 0x000000,
+ 0.85
+ );
+ dialogueBox.setStrokeStyle(3, 0x00ffff);
+ dialogueBox.setScrollFactor(0);
+ dialogueBox.setDepth(999);
+
+ // NPC portrait (if available)
+ let portrait = null;
+ if (scene.textures.exists(portraitKey)) {
+ portrait = scene.add.image(60, boxY + 30, portraitKey);
+ portrait.setDisplaySize(80, 80);
+ portrait.setScrollFactor(0);
+ portrait.setDepth(1000);
+ }
+
+ // NPC name
+ const nameText = scene.add.text(
+ portrait ? 120 : 40,
+ boxY + 20,
+ npcName,
+ {
+ fontFamily: 'Arial',
+ fontSize: '24px',
+ fontStyle: 'bold',
+ color: '#00ffff',
+ stroke: '#000000',
+ strokeThickness: 3
+ }
+ );
+ nameText.setScrollFactor(0);
+ nameText.setDepth(1000);
+
+ // Dialogue text
+ const dialogueTextObj = scene.add.text(
+ portrait ? 120 : 40,
+ boxY + 60,
+ '',
+ {
+ fontFamily: 'Arial',
+ fontSize: '20px',
+ color: '#ffffff',
+ wordWrap: { width: width - (portrait ? 180 : 100) }
+ }
+ );
+ dialogueTextObj.setScrollFactor(0);
+ dialogueTextObj.setDepth(1000);
+
+ // Start typewriter effect
+ this.startTyping(dialogueTextObj, dialogueText, () => {
+ // Wait for player to dismiss
+ const dismissText = scene.add.text(
+ width - 150,
+ boxY + boxHeight - 30,
+ '[SPACE] Continue',
+ {
+ fontFamily: 'Arial',
+ fontSize: '16px',
+ color: '#888888'
+ }
+ );
+ dismissText.setScrollFactor(0);
+ dismissText.setDepth(1000);
+
+ // Pulse animation
+ scene.tweens.add({
+ targets: dismissText,
+ alpha: 0.5,
+ duration: 800,
+ yoyo: true,
+ repeat: -1
+ });
+
+ // Wait for dismiss
+ const dismissHandler = (event) => {
+ if (event.key === ' ' || event.key === 'Enter') {
+ // Cleanup
+ dialogueBox.destroy();
+ if (portrait) portrait.destroy();
+ nameText.destroy();
+ dialogueTextObj.destroy();
+ dismissText.destroy();
+
+ scene.input.keyboard.off('keydown', dismissHandler);
+
+ if (onComplete) onComplete();
+ }
+ };
+
+ scene.input.keyboard.on('keydown', dismissHandler);
+ });
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ this.stopTyping();
+ console.log('โจ๏ธ Typewriter destroyed!');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/DyslexiaSupportSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/DyslexiaSupportSystem.js
new file mode 100644
index 000000000..e895e515e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/DyslexiaSupportSystem.js
@@ -0,0 +1,431 @@
+/**
+ * DYSLEXIA SUPPORT SYSTEM
+ * Provides reading assistance for players with dyslexia
+ */
+class DyslexiaSupportSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // OpenDyslexic font (loaded via CSS)
+ this.fonts = {
+ 'default': 'Arial, sans-serif',
+ 'opendyslexic': 'OpenDyslexic, Arial, sans-serif',
+ 'comic-sans': 'Comic Sans MS, cursive',
+ 'verdana': 'Verdana, sans-serif'
+ };
+
+ // Text size presets
+ this.textSizes = {
+ 'small': { base: 14, ui: 16, subtitle: 18 },
+ 'medium': { base: 16, ui: 18, subtitle: 20 },
+ 'large': { base: 20, ui: 22, subtitle: 24 },
+ 'extra-large': { base: 24, ui: 26, subtitle: 28 }
+ };
+
+ // Line spacing presets
+ this.lineSpacings = {
+ 'normal': 1.2,
+ 'increased': 1.5,
+ 'double': 2.0,
+ 'triple': 3.0
+ };
+
+ // Settings
+ this.settings = {
+ enabled: false,
+ font: 'default',
+ textSize: 'medium',
+ lineSpacing: 'normal',
+ highlightText: false,
+ simplifiedLanguage: false,
+ textToSpeech: false,
+ colorOverlay: false,
+ overlayColor: '#ffffcc', // Light yellow
+ overlayOpacity: 0.3
+ };
+
+ // Text simplification dictionary
+ this.simplificationDict = {
+ 'acquire': 'get',
+ 'utilize': 'use',
+ 'commence': 'start',
+ 'terminate': 'end',
+ 'construct': 'build',
+ 'eliminate': 'remove',
+ 'approximately': 'about',
+ 'insufficient': 'not enough',
+ 'maximum': 'most',
+ 'minimum': 'least',
+ 'purchase': 'buy',
+ 'require': 'need',
+ 'additional': 'more',
+ 'previous': 'last',
+ 'subsequent': 'next'
+ };
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Dyslexia Support System initialized');
+ }
+
+ init() {
+ // Load OpenDyslexic font
+ this.loadOpenDyslexicFont();
+
+ // Apply saved settings
+ if (this.settings.enabled) {
+ this.applySettings();
+ }
+ }
+
+ /**
+ * Load OpenDyslexic font
+ */
+ loadOpenDyslexicFont() {
+ // Check if font is already loaded
+ if (document.getElementById('opendyslexic-font')) return;
+
+ // Create style element
+ const style = document.createElement('style');
+ style.id = 'opendyslexic-font';
+ style.textContent = `
+ @font-face {
+ font-family: 'OpenDyslexic';
+ src: url('https://cdn.jsdelivr.net/npm/opendyslexic@3.0.1/OpenDyslexic-Regular.woff2') format('woff2'),
+ url('https://cdn.jsdelivr.net/npm/opendyslexic@3.0.1/OpenDyslexic-Regular.woff') format('woff');
+ font-weight: normal;
+ font-style: normal;
+ }
+ @font-face {
+ font-family: 'OpenDyslexic';
+ src: url('https://cdn.jsdelivr.net/npm/opendyslexic@3.0.1/OpenDyslexic-Bold.woff2') format('woff2'),
+ url('https://cdn.jsdelivr.net/npm/opendyslexic@3.0.1/OpenDyslexic-Bold.woff') format('woff');
+ font-weight: bold;
+ font-style: normal;
+ }
+ `;
+ document.head.appendChild(style);
+
+ console.log('๐ OpenDyslexic font loaded');
+ }
+
+ /**
+ * Apply all settings
+ */
+ applySettings() {
+ this.applyFont();
+ this.applyTextSize();
+ this.applyLineSpacing();
+ this.applyColorOverlay();
+ }
+
+ /**
+ * Apply font setting
+ */
+ applyFont() {
+ const font = this.fonts[this.settings.font];
+
+ // Apply to game canvas
+ const canvas = document.querySelector('canvas');
+ if (canvas) {
+ canvas.style.fontFamily = font;
+ }
+
+ // Apply to all text elements in game
+ this.updateGameTexts();
+
+ console.log(`๐ Font set to: ${this.settings.font}`);
+ }
+
+ /**
+ * Apply text size setting
+ */
+ applyTextSize() {
+ const sizes = this.textSizes[this.settings.textSize];
+
+ // Update game text sizes
+ this.updateGameTextSizes(sizes);
+
+ console.log(`๐ Text size set to: ${this.settings.textSize}`);
+ }
+
+ /**
+ * Apply line spacing setting
+ */
+ applyLineSpacing() {
+ const spacing = this.lineSpacings[this.settings.lineSpacing];
+
+ // Apply to game texts
+ this.updateGameLineSpacing(spacing);
+
+ console.log(`๐ Line spacing set to: ${this.settings.lineSpacing} (${spacing})`);
+ }
+
+ /**
+ * Apply color overlay
+ */
+ applyColorOverlay() {
+ if (!this.settings.colorOverlay) {
+ this.removeColorOverlay();
+ return;
+ }
+
+ // Create or update overlay
+ let overlay = document.getElementById('dyslexia-overlay');
+ if (!overlay) {
+ overlay = document.createElement('div');
+ overlay.id = 'dyslexia-overlay';
+ overlay.style.position = 'fixed';
+ overlay.style.top = '0';
+ overlay.style.left = '0';
+ overlay.style.width = '100%';
+ overlay.style.height = '100%';
+ overlay.style.pointerEvents = 'none';
+ overlay.style.zIndex = '9998';
+ document.body.appendChild(overlay);
+ }
+
+ overlay.style.backgroundColor = this.settings.overlayColor;
+ overlay.style.opacity = this.settings.overlayOpacity;
+
+ console.log('๐จ Color overlay applied');
+ }
+
+ /**
+ * Remove color overlay
+ */
+ removeColorOverlay() {
+ const overlay = document.getElementById('dyslexia-overlay');
+ if (overlay) {
+ overlay.remove();
+ }
+ }
+
+ /**
+ * Update game texts with new font
+ */
+ updateGameTexts() {
+ // This would update all Phaser text objects
+ // Implementation depends on game structure
+ console.log('๐ Updating game texts...');
+ }
+
+ /**
+ * Update game text sizes
+ */
+ updateGameTextSizes(sizes) {
+ // Update subtitle system if available
+ if (this.scene.visualSoundCues) {
+ // Map to subtitle sizes
+ const sizeMap = {
+ 'small': 'small',
+ 'medium': 'medium',
+ 'large': 'large',
+ 'extra-large': 'very-large'
+ };
+ this.scene.visualSoundCues.setSubtitleSize(sizeMap[this.settings.textSize]);
+ }
+
+ console.log('๐ Updating game text sizes...');
+ }
+
+ /**
+ * Update game line spacing
+ */
+ updateGameLineSpacing(spacing) {
+ // This would update line spacing for all text objects
+ console.log(`๐ Updating line spacing to ${spacing}...`);
+ }
+
+ /**
+ * Simplify text for easier reading
+ */
+ simplifyText(text) {
+ if (!this.settings.simplifiedLanguage) return text;
+
+ let simplified = text;
+ for (const [complex, simple] of Object.entries(this.simplificationDict)) {
+ const regex = new RegExp(`\\b${complex}\\b`, 'gi');
+ simplified = simplified.replace(regex, simple);
+ }
+
+ return simplified;
+ }
+
+ /**
+ * Read text aloud (if TTS enabled)
+ */
+ readAloud(text) {
+ if (!this.settings.textToSpeech) return;
+
+ // Use screen reader system if available
+ if (this.scene.screenReader) {
+ this.scene.screenReader.speak(text);
+ }
+ }
+
+ /**
+ * Set font
+ */
+ setFont(fontName) {
+ if (!this.fonts[fontName]) {
+ console.error(`Font "${fontName}" not available`);
+ return;
+ }
+
+ this.settings.font = fontName;
+ this.applyFont();
+ this.saveSettings();
+ }
+
+ /**
+ * Set text size
+ */
+ setTextSize(size) {
+ if (!this.textSizes[size]) {
+ console.error(`Text size "${size}" not available`);
+ return;
+ }
+
+ this.settings.textSize = size;
+ this.applyTextSize();
+ this.saveSettings();
+ }
+
+ /**
+ * Set line spacing
+ */
+ setLineSpacing(spacing) {
+ if (!this.lineSpacings[spacing]) {
+ console.error(`Line spacing "${spacing}" not available`);
+ return;
+ }
+
+ this.settings.lineSpacing = spacing;
+ this.applyLineSpacing();
+ this.saveSettings();
+ }
+
+ /**
+ * Toggle simplified language
+ */
+ toggleSimplifiedLanguage() {
+ this.settings.simplifiedLanguage = !this.settings.simplifiedLanguage;
+ this.saveSettings();
+ console.log(`๐ Simplified language: ${this.settings.simplifiedLanguage ? 'ON' : 'OFF'}`);
+ }
+
+ /**
+ * Toggle text-to-speech
+ */
+ toggleTextToSpeech() {
+ this.settings.textToSpeech = !this.settings.textToSpeech;
+ this.saveSettings();
+ console.log(`๐ Text-to-speech: ${this.settings.textToSpeech ? 'ON' : 'OFF'}`);
+ }
+
+ /**
+ * Toggle color overlay
+ */
+ toggleColorOverlay() {
+ this.settings.colorOverlay = !this.settings.colorOverlay;
+ this.applyColorOverlay();
+ this.saveSettings();
+ console.log(`๐จ Color overlay: ${this.settings.colorOverlay ? 'ON' : 'OFF'}`);
+ }
+
+ /**
+ * Set overlay color
+ */
+ setOverlayColor(color) {
+ this.settings.overlayColor = color;
+ if (this.settings.colorOverlay) {
+ this.applyColorOverlay();
+ }
+ this.saveSettings();
+ }
+
+ /**
+ * Set overlay opacity
+ */
+ setOverlayOpacity(opacity) {
+ this.settings.overlayOpacity = Phaser.Math.Clamp(opacity, 0, 1);
+ if (this.settings.colorOverlay) {
+ this.applyColorOverlay();
+ }
+ this.saveSettings();
+ }
+
+ /**
+ * Enable dyslexia support
+ */
+ enable() {
+ this.settings.enabled = true;
+ this.applySettings();
+ this.saveSettings();
+ console.log('โ
Dyslexia support enabled');
+ }
+
+ /**
+ * Disable dyslexia support
+ */
+ disable() {
+ this.settings.enabled = false;
+ this.removeColorOverlay();
+ this.saveSettings();
+ console.log('โ Dyslexia support disabled');
+ }
+
+ /**
+ * Get available fonts
+ */
+ getAvailableFonts() {
+ return Object.keys(this.fonts);
+ }
+
+ /**
+ * Get available text sizes
+ */
+ getAvailableTextSizes() {
+ return Object.keys(this.textSizes);
+ }
+
+ /**
+ * Get available line spacings
+ */
+ getAvailableLineSpacings() {
+ return Object.keys(this.lineSpacings);
+ }
+
+ /**
+ * Save settings to localStorage
+ */
+ saveSettings() {
+ localStorage.setItem('novafarma_dyslexia_support', JSON.stringify(this.settings));
+ }
+
+ /**
+ * Load settings from localStorage
+ */
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_dyslexia_support');
+ if (saved) {
+ try {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ console.log('โ
Dyslexia support settings loaded');
+ } catch (error) {
+ console.error('โ Failed to load dyslexia support settings:', error);
+ }
+ }
+ }
+
+ /**
+ * Destroy system
+ */
+ destroy() {
+ this.removeColorOverlay();
+ console.log('๐ Dyslexia Support System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ElectionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ElectionSystem.js
new file mode 100644
index 000000000..17d8b3f92
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ElectionSystem.js
@@ -0,0 +1,435 @@
+/**
+ * ELECTION & SOCIAL ORDER SYSTEM
+ * Mrtva Dolina - City Evolution Through Democracy
+ *
+ * Features:
+ * - Chaos phase (no leader, messy city)
+ * - Election trigger (after 5+ NPCs arrive)
+ * - Vote gathering & influence system
+ * - Mayor inauguration with visual/audio changes
+ * - Unlocks city improvements (walls, patrols)
+ */
+
+export class ElectionSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Election state
+ this.electionPhase = 'none'; // none, chaos, campaign, complete
+ this.mayorElected = false;
+ this.currentMayor = null;
+
+ // Candidates
+ this.candidates = [
+ {
+ id: 'mayor_default',
+ name: 'ลฝupan',
+ votes: 0,
+ platform: 'Obzidje in varnost',
+ supportingNPCs: []
+ },
+ {
+ id: 'ivan_kovac',
+ name: 'Ivan Kovaฤ',
+ votes: 0,
+ platform: 'Proizvodni razvoj',
+ supportingNPCs: []
+ },
+ {
+ id: 'tehnik',
+ name: 'Tehnik',
+ votes: 0,
+ platform: 'Tehnoloลกki napredek',
+ supportingNPCs: []
+ }
+ ];
+
+ // City visual state
+ this.cityState = {
+ cleanliness: 0, // 0-100
+ security: 0, // 0-100
+ morale: 0 // 0-100
+ };
+
+ // Trash/debris objects for visual chaos
+ this.debrisObjects = [];
+
+ // Population tracking
+ this.npcCount = 0;
+ this.electionThreshold = 5; // Trigger election at 5 NPCs
+
+ this.init();
+ }
+
+ init() {
+ // Listen for NPC arrival events
+ this.scene.events.on('npc:arrived', this.onNPCArrival, this);
+ this.scene.events.on('quest:completed', this.onQuestCompleted, this);
+
+ console.log('โ
ElectionSystem initialized');
+ }
+
+ /**
+ * NPC ARRIVAL - Track population
+ */
+ onNPCArrival(npcData) {
+ this.npcCount++;
+
+ console.log(`๐ค NPC arrived: ${npcData.name}. Total: ${this.npcCount}`);
+
+ // Check if chaos phase should start
+ if (this.npcCount >= 3 && this.electionPhase === 'none') {
+ this.startChaosPhase();
+ }
+
+ // Check if election should trigger
+ if (this.npcCount >= this.electionThreshold && this.electionPhase === 'chaos') {
+ this.triggerElection();
+ }
+ }
+
+ /**
+ * CHAOS PHASE - City is disorganized
+ */
+ startChaosPhase() {
+ this.electionPhase = 'chaos';
+
+ console.log('๐ฅ CHAOS PHASE STARTED - City needs leadership!');
+
+ // Spawn trash/debris around town
+ this.spawnDebris(15); // 15 trash piles
+
+ // Lower city stats
+ this.cityState.cleanliness = 20;
+ this.cityState.security = 10;
+ this.cityState.morale = 30;
+
+ // NPCs start discussing need for leader
+ this.startChaosDialogues();
+
+ // Show notification
+ this.scene.events.emit('show-notification', {
+ title: 'Stanje Kaosa',
+ message: 'Ljudje potrebujejo vodjo! Uredite red v mestu.',
+ icon: 'โ ๏ธ',
+ duration: 5000
+ });
+
+ // Update status board
+ this.updateStatusBoard();
+ }
+
+ spawnDebris(count) {
+ const debrisTypes = ['trash_pile', 'broken_crate', 'rubble', 'scattered_papers'];
+
+ for (let i = 0; i < count; i++) {
+ const x = Phaser.Math.Between(100, this.scene.cameras.main.width - 100);
+ const y = Phaser.Math.Between(100, this.scene.cameras.main.height - 100);
+
+ const type = Phaser.Utils.Array.GetRandom(debrisTypes);
+ const debris = this.scene.add.sprite(x, y, type);
+ debris.setDepth(1);
+
+ this.debrisObjects.push(debris);
+ }
+ }
+
+ startChaosDialogues() {
+ // NPCs randomly discuss the chaos
+ const dialogues = [
+ { npc: 'sivilja', text: 'Ta kaos je neznosn! Rabimo vodjo!' },
+ { npc: 'pek', text: 'Kdo bo prinesel red v to mesto?' },
+ { npc: 'ivan_kovac', text: 'Brez organizacije ne moremo preลพiveti.' }
+ ];
+
+ // Emit dialogue events periodically
+ this.chaosDialogueTimer = setInterval(() => {
+ const dialogue = Phaser.Utils.Array.GetRandom(dialogues);
+ this.scene.events.emit('npc:dialogue', dialogue);
+ }, 30000); // Every 30 seconds
+ }
+
+ /**
+ * TRIGGER ELECTION
+ */
+ triggerElection() {
+ this.electionPhase = 'campaign';
+
+ console.log('๐ณ๏ธ ELECTION TRIGGERED - Campaign begins!');
+
+ // Stop chaos dialogues
+ if (this.chaosDialogueTimer) {
+ clearInterval(this.chaosDialogueTimer);
+ }
+
+ // Create election quest
+ this.createElectionQuest();
+
+ // Show notification
+ this.scene.events.emit('show-notification', {
+ title: 'Volitve za ลฝupana',
+ message: 'Mesto potrebuje vodjo! Pomagaj pri zbiranju glasov.',
+ icon: '๐ณ๏ธ',
+ duration: 5000
+ });
+
+ // NPCs start campaign dialogues
+ this.startCampaignDialogues();
+ }
+
+ createElectionQuest() {
+ if (!this.scene.questSystem) return;
+
+ const electionQuest = {
+ id: 'election_campaign',
+ title: 'Zbiranje Glasov za ลฝupana',
+ type: 'social',
+ priority: 5,
+ description: 'Pomagaj izbrati ลพupana za Mrtvo Dolino.',
+ objectives: [
+ {
+ id: 'talk_to_npcs',
+ text: 'Pogovor s 5 NPC-ji o volitvah',
+ type: 'interaction',
+ required: 5,
+ current: 0
+ },
+ {
+ id: 'support_candidate',
+ text: 'Podpri kandidata z opravljanjem questov',
+ type: 'flag',
+ complete: false
+ }
+ ],
+ rewards: {
+ xp: 1000,
+ unlocks: ['mayor_office', 'city_improvements']
+ },
+ dialogue: {
+ start: ['Ljudi potrebujejo vodjo. Kdo bo ลพupan?'],
+ complete: ['Volitve so konฤane! Novi ลพupan je izvoljen!']
+ },
+ npc: 'mayor'
+ };
+
+ this.scene.questSystem.registerQuest(electionQuest);
+ this.scene.questSystem.startQuest('election_campaign');
+ }
+
+ startCampaignDialogues() {
+ // Each candidate promotes their platform
+ const campaignLines = {
+ mayor_default: 'Glasujte zame! Zgradil bom obzidje in patruljo!',
+ ivan_kovac: 'Potrebujemo proizvodnjo! Podprite me!',
+ tehnik: 'Tehnologija je prihodnost! Volite tehnoloลกki napredek!'
+ };
+
+ // NPCs express support for different candidates
+ this.campaignDialogueTimer = setInterval(() => {
+ const candidate = Phaser.Utils.Array.GetRandom(this.candidates);
+ this.scene.events.emit('npc:dialogue', {
+ npc: candidate.id,
+ text: campaignLines[candidate.id]
+ });
+ }, 45000); // Every 45 seconds
+ }
+
+ /**
+ * VOTING - Player influences votes through quests
+ */
+ onQuestCompleted(questId) {
+ if (this.electionPhase !== 'campaign') return;
+
+ // Check which candidate benefits from this quest
+ const questCandidateMap = {
+ 'obzidje': 'mayor_default',
+ 'pekov_recept': 'mayor_default',
+ 'tehnikova_naprava': 'tehnik',
+ 'siviljina_prosnja': 'ivan_kovac'
+ };
+
+ const candidateId = questCandidateMap[questId];
+ if (candidateId) {
+ this.addVote(candidateId, 1);
+
+ // Show feedback
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.x,
+ y: this.scene.player.y - 50,
+ text: `+1 glas za ${candidateId}`,
+ color: '#FFD700'
+ });
+ }
+ }
+
+ addVote(candidateId, votes = 1) {
+ const candidate = this.candidates.find(c => c.id === candidateId);
+ if (candidate) {
+ candidate.votes += votes;
+ console.log(`๐ณ๏ธ ${candidate.name} dobil ${votes} glas(ov). Skupaj: ${candidate.votes}`);
+ }
+ }
+
+ /**
+ * COMPLETE ELECTION - Inaugurate mayor
+ */
+ completeElection() {
+ if (this.mayorElected) return;
+
+ // Count votes and determine winner
+ const winner = this.candidates.reduce((prev, current) =>
+ (prev.votes > current.votes) ? prev : current
+ );
+
+ this.currentMayor = winner;
+ this.mayorElected = true;
+ this.electionPhase = 'complete';
+
+ console.log(`๐๏ธ ${winner.name} je izvoljen za ลพupana!`);
+
+ // Inauguration sequence
+ this.inauguration(winner);
+ }
+
+ inauguration(mayor) {
+ // Visual changes
+ this.cleanUpCity();
+
+ // Mayor moves to town hall
+ if (this.scene.npcs && this.scene.npcs[mayor.id]) {
+ const mayorNPC = this.scene.npcs[mayor.id];
+ this.scene.tweens.add({
+ targets: mayorNPC,
+ x: this.scene.townHallX || 400,
+ y: this.scene.townHallY || 300,
+ duration: 3000,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ // Change music to ordered/military theme
+ if (this.scene.sound && this.scene.sound.get('background_music')) {
+ this.scene.sound.get('background_music').stop();
+ }
+ this.scene.sound.play('mayor_anthem', { loop: true, volume: 0.5 });
+
+ // Unlock new features
+ this.unlockMayorFeatures();
+
+ // Show inauguration cutscene
+ this.scene.events.emit('show-notification', {
+ title: `ลฝupan ${mayor.name}`,
+ message: `${mayor.name} je uradno inauguriran! Mesto je zdaj pod vodstvom.`,
+ icon: '๐๏ธ',
+ duration: 7000
+ });
+
+ // Update city stats
+ this.cityState.cleanliness = 80;
+ this.cityState.security = 70;
+ this.cityState.morale = 90;
+
+ this.updateStatusBoard();
+
+ // Complete election quest
+ if (this.scene.questSystem) {
+ this.scene.questSystem.completeQuest('election_campaign');
+ }
+ }
+
+ cleanUpCity() {
+ // Remove all debris with animation
+ this.debrisObjects.forEach((debris, index) => {
+ this.scene.tweens.add({
+ targets: debris,
+ alpha: 0,
+ scaleX: 0,
+ scaleY: 0,
+ duration: 1000,
+ delay: index * 100,
+ onComplete: () => debris.destroy()
+ });
+ });
+
+ this.debrisObjects = [];
+
+ // Add clean visual elements (flags, guards, etc.)
+ this.addCityImprovements();
+ }
+
+ addCityImprovements() {
+ // Add flags
+ const flagPositions = [
+ { x: 200, y: 150 },
+ { x: 400, y: 150 },
+ { x: 600, y: 150 }
+ ];
+
+ flagPositions.forEach(pos => {
+ const flag = this.scene.add.sprite(pos.x, pos.y, 'city_flag');
+ flag.setDepth(10);
+
+ // Waving animation
+ this.scene.tweens.add({
+ targets: flag,
+ scaleX: 1.1,
+ duration: 1000,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+ });
+
+ // Add guards (if available)
+ // ... patrol implementation
+ }
+
+ unlockMayorFeatures() {
+ // Unlock wall building
+ if (this.scene.buildingSystem) {
+ this.scene.buildingSystem.unlock('wall_wooden');
+ this.scene.buildingSystem.unlock('wall_stone');
+ }
+
+ // Unlock patrol system
+ if (this.scene.defenseSystem) {
+ this.scene.defenseSystem.unlockPatrols();
+ }
+
+ // Unlock mayor's office
+ this.scene.events.emit('building:unlocked', 'mayor_office');
+
+ console.log('๐ Mayor features unlocked: walls, patrols, office');
+ }
+
+ updateStatusBoard() {
+ // Update city status display
+ this.scene.events.emit('city:stats_updated', {
+ cleanliness: this.cityState.cleanliness,
+ security: this.cityState.security,
+ morale: this.cityState.morale,
+ population: this.npcCount,
+ mayor: this.currentMayor ? this.currentMayor.name : 'None'
+ });
+ }
+
+ /**
+ * Get election results for UI display
+ */
+ getElectionResults() {
+ return {
+ phase: this.electionPhase,
+ candidates: this.candidates,
+ winner: this.currentMayor,
+ cityState: this.cityState
+ };
+ }
+
+ destroy() {
+ if (this.chaosDialogueTimer) clearInterval(this.chaosDialogueTimer);
+ if (this.campaignDialogueTimer) clearInterval(this.campaignDialogueTimer);
+
+ this.debrisObjects.forEach(obj => obj.destroy());
+ this.debrisObjects = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/EnhancedAudioSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/EnhancedAudioSystem.js
new file mode 100644
index 000000000..3e747e249
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/EnhancedAudioSystem.js
@@ -0,0 +1,354 @@
+/**
+ * EnhancedAudioSystem.js
+ *
+ * Complete audio system with:
+ * - Ambient loops (crickets, wind, city noise)
+ * - Animal sounds (random intervals near farm)
+ * - Intro heartbeat + blur effect
+ * - Accessibility (visual indicators)
+ * - Xbox haptic feedback
+ * - .wav -> .ogg optimization
+ *
+ * Created: Jan 10, 2026
+ * Author: David "HIPO" Kotnik
+ * Studio: Hipodevil666 Studiosโข
+ */
+
+export default class EnhancedAudioSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Audio references
+ this.ambientLoops = {};
+ this.animalSounds = {};
+ this.currentAmbient = null;
+
+ // Animal sound timers
+ this.animalTimers = {};
+
+ // Haptic feedback support
+ this.hapticEnabled = true;
+
+ // Visual accessibility indicators
+ this.visualIndicators = {};
+
+ console.log('๐ Enhanced Audio System initialized!');
+ }
+
+ /**
+ * Load all audio assets
+ */
+ preloadAudio() {
+ const scene = this.scene;
+
+ // AMBIENT LOOPS
+ scene.load.audio('ambient_crickets', 'assets/audio/ambient/crickets_loop.ogg');
+ scene.load.audio('ambient_wind', 'assets/audio/ambient/wind_loop.ogg');
+ scene.load.audio('ambient_city', 'assets/audio/ambient/city_noise_loop.ogg');
+ scene.load.audio('ambient_forest', 'assets/audio/ambient/forest_loop.ogg');
+
+ // ANIMAL SOUNDS
+ scene.load.audio('animal_sheep', 'assets/audio/animals/sheep.ogg');
+ scene.load.audio('animal_pig', 'assets/audio/animals/pig.ogg');
+ scene.load.audio('animal_chicken', 'assets/audio/animals/chicken.ogg');
+ scene.load.audio('animal_horse', 'assets/audio/animals/horse.ogg');
+ scene.load.audio('animal_goat', 'assets/audio/animals/goat.ogg');
+ scene.load.audio('animal_cow', 'assets/audio/animals/cow.ogg');
+
+ // INTRO EFFECTS
+ scene.load.audio('intro_heartbeat', 'assets/audio/effects/heartbeat.ogg');
+
+ // UI SOUNDS
+ scene.load.audio('raid_warning', 'assets/audio/ui/raid_alarm.ogg');
+
+ console.log('๐ต Audio assets queued for loading...');
+ }
+
+ /**
+ * Initialize audio after load
+ */
+ initialize() {
+ const scene = this.scene;
+
+ // Create ambient loops
+ this.ambientLoops = {
+ crickets: scene.sound.add('ambient_crickets', { loop: true, volume: 0.3 }),
+ wind: scene.sound.add('ambient_wind', { loop: true, volume: 0.2 }),
+ city: scene.sound.add('ambient_city', { loop: true, volume: 0.15 }),
+ forest: scene.sound.add('ambient_forest', { loop: true, volume: 0.25 })
+ };
+
+ // Create animal sounds
+ this.animalSounds = {
+ sheep: scene.sound.add('animal_sheep', { volume: 0.4 }),
+ pig: scene.sound.add('animal_pig', { volume: 0.4 }),
+ chicken: scene.sound.add('animal_chicken', { volume: 0.35 }),
+ horse: scene.sound.add('animal_horse', { volume: 0.5 }),
+ goat: scene.sound.add('animal_goat', { volume: 0.4 }),
+ cow: scene.sound.add('animal_cow', { volume: 0.45 })
+ };
+
+ console.log('๐ต Enhanced Audio System ready!');
+ }
+
+ /**
+ * Play ambient loop based on biome
+ */
+ playAmbient(biomeType) {
+ // Stop current ambient
+ if (this.currentAmbient) {
+ this.currentAmbient.stop();
+ }
+
+ // Select ambient based on biome
+ let ambient = null;
+ switch (biomeType) {
+ case 'grassland':
+ case 'farm':
+ ambient = this.ambientLoops.crickets;
+ break;
+ case 'forest':
+ ambient = this.ambientLoops.forest;
+ break;
+ case 'wasteland':
+ case 'radioactive':
+ ambient = this.ambientLoops.wind;
+ break;
+ case 'town':
+ case 'city':
+ ambient = this.ambientLoops.city;
+ break;
+ default:
+ ambient = this.ambientLoops.crickets;
+ }
+
+ if (ambient) {
+ ambient.play();
+ this.currentAmbient = ambient;
+ console.log(`๐ต Playing ambient: ${biomeType}`);
+ }
+ }
+
+ /**
+ * Start random animal sounds near farm
+ */
+ startAnimalSounds(playerX, playerY) {
+ // Random intervals: 5-15 seconds
+ Object.keys(this.animalSounds).forEach(animal => {
+ this.animalTimers[animal] = this.scene.time.addEvent({
+ delay: Phaser.Math.Between(5000, 15000),
+ callback: () => {
+ // Check if player is near farm (within 500px)
+ // This is simplified - you'd check actual farm position
+ const nearFarm = true; // TODO: Implement proximity check
+
+ if (nearFarm && !this.animalSounds[animal].isPlaying) {
+ this.animalSounds[animal].play();
+ console.log(`๐ ${animal} sound played!`);
+ }
+ },
+ loop: true
+ });
+ });
+
+ console.log('๐ Animal sounds started!');
+ }
+
+ /**
+ * Stop animal sounds
+ */
+ stopAnimalSounds() {
+ Object.values(this.animalTimers).forEach(timer => {
+ if (timer) timer.remove();
+ });
+ this.animalTimers = {};
+ console.log('๐ Animal sounds stopped!');
+ }
+
+ /**
+ * Play intro sequence (heartbeat + blur effect)
+ */
+ playIntroSequence() {
+ const scene = this.scene;
+
+ // Play heartbeat
+ const heartbeat = scene.sound.add('intro_heartbeat', { volume: 0.6 });
+ heartbeat.play();
+
+ // Blur-to-clear effect (Kai's amnesia)
+ const blurStrength = 10;
+ const camera = scene.cameras.main;
+
+ // Apply initial blur (using postFX if available)
+ // Note: Phaser 3.60+ has built-in blur, older versions need custom shader
+ if (camera.setPostPipeline) {
+ // Modern Phaser blur
+ camera.setPostPipeline('BlurPostFX');
+ }
+
+ // Clear blur over 3 seconds (synchronized with heartbeat)
+ scene.tweens.add({
+ targets: camera,
+ scrollX: 0, // Placeholder - actual blur would use custom property
+ duration: 3000,
+ ease: 'Power2',
+ onUpdate: (tween) => {
+ // Reduce blur over time
+ const progress = tween.progress;
+ // TODO: Update actual blur shader strength here
+ },
+ onComplete: () => {
+ // Remove blur effect
+ if (camera.resetPostPipeline) {
+ camera.resetPostPipeline();
+ }
+ console.log('๐๏ธ Vision cleared - amnesia intro complete!');
+ }
+ });
+
+ // Haptic feedback (heartbeat pulse)
+ this.vibrate(200, 500); // 200ms pulse, 500ms between
+
+ console.log('๐ Intro sequence playing...');
+ }
+
+ /**
+ * Show visual indicator for deaf accessibility
+ */
+ showVisualIndicator(type, duration = 2000) {
+ const scene = this.scene;
+ const { width, height } = scene.cameras.main;
+
+ let indicator;
+ let color = 0xFFFFFF;
+ let icon = '!';
+
+ switch (type) {
+ case 'raid':
+ color = 0xFF0000; // Red
+ icon = 'โ ๏ธ RAID!';
+ break;
+ case 'animal':
+ color = 0x00FF00; // Green
+ icon = '๐';
+ break;
+ case 'danger':
+ color = 0xFF8800; // Orange
+ icon = 'โก';
+ break;
+ default:
+ icon = '๐';
+ }
+
+ // Create visual indicator
+ indicator = scene.add.text(width / 2, 100, icon, {
+ fontSize: '48px',
+ color: '#' + color.toString(16).padStart(6, '0'),
+ stroke: '#000000',
+ strokeThickness: 4,
+ shadow: {
+ offsetX: 0,
+ offsetY: 0,
+ color: '#' + color.toString(16).padStart(6, '0'),
+ blur: 20,
+ fill: true
+ }
+ }).setOrigin(0.5);
+
+ indicator.setScrollFactor(0); // Fixed to camera
+ indicator.setDepth(1000); // Always on top
+
+ // Pulse animation
+ scene.tweens.add({
+ targets: indicator,
+ scaleX: 1.2,
+ scaleY: 1.2,
+ alpha: 0.7,
+ duration: 500,
+ yoyo: true,
+ repeat: Math.floor(duration / 1000)
+ });
+
+ // Remove after duration
+ scene.time.delayedCall(duration, () => {
+ indicator.destroy();
+ });
+
+ console.log(`๐๏ธ Visual indicator shown: ${type}`);
+ }
+
+ /**
+ * Xbox controller vibration (haptic feedback)
+ */
+ vibrate(duration = 200, interval = 0) {
+ if (!this.hapticEnabled) return;
+
+ const scene = this.scene;
+
+ // Check for gamepad support
+ if (scene.input.gamepad && scene.input.gamepad.total > 0) {
+ const pad = scene.input.gamepad.getPad(0);
+
+ if (pad && pad.vibration) {
+ // Vibrate motors (weak, strong)
+ pad.vibration.playEffect('dual-rumble', {
+ startDelay: 0,
+ duration: duration,
+ weakMagnitude: 0.5,
+ strongMagnitude: 1.0
+ });
+
+ // Repeat if interval specified
+ if (interval > 0) {
+ scene.time.delayedCall(duration + interval, () => {
+ this.vibrate(duration, interval);
+ });
+ }
+
+ console.log(`๐ฎ Haptic feedback: ${duration}ms`);
+ }
+ }
+ }
+
+ /**
+ * Play raid warning with visual + haptic feedback
+ */
+ playRaidWarning() {
+ const scene = this.scene;
+
+ // Audio warning
+ const raidSound = scene.sound.add('raid_warning', { volume: 0.7 });
+ raidSound.play();
+
+ // Visual indicator (accessibility)
+ this.showVisualIndicator('raid', 3000);
+
+ // Haptic feedback (3 strong pulses)
+ this.vibrate(300, 300);
+ this.vibrate(300, 600);
+ this.vibrate(300, 900);
+
+ console.log('โ ๏ธ RAID WARNING! (audio + visual + haptic)');
+ }
+
+ /**
+ * Update system (called in scene's update loop)
+ */
+ update(time, delta) {
+ // Future: proximity checks for animal sounds, etc.
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ // Stop all sounds
+ if (this.currentAmbient) {
+ this.currentAmbient.stop();
+ }
+
+ this.stopAnimalSounds();
+
+ console.log('๐ Enhanced Audio System destroyed!');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ExpansionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ExpansionSystem.js
new file mode 100644
index 000000000..a147a1fb5
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ExpansionSystem.js
@@ -0,0 +1,163 @@
+/**
+ * EXPANSION SYSTEM
+ * - Manages Unlockable Zones (Farm, Forest, City...)
+ * - Fog of War logic (Visual blocking)
+ * - Unlock costs and requirements
+ */
+class ExpansionSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Define Zones
+ this.zones = {
+ 'farm': {
+ id: 'farm',
+ name: 'Starter Farm',
+ x: 0, y: 0, w: 40, h: 40, // 0-40 coordinates
+ unlocked: true,
+ cost: 0,
+ color: 0x00FF00
+ },
+ 'forest': {
+ id: 'forest',
+ name: 'Dark Forest',
+ x: 40, y: 0, w: 60, h: 40, // Right of farm
+ unlocked: true,
+ cost: 100, // 100 Gold Coins
+ req: 'None',
+ color: 0x006400
+ },
+ 'city': {
+ id: 'city',
+ name: 'Ruined City',
+ x: 0, y: 40, w: 100, h: 60, // Below farm & forest
+ unlocked: true,
+ cost: 500,
+ req: 'Kill Boss',
+ color: 0x808080
+ }
+ };
+
+ // Fog Graphics
+ this.fogGraphics = this.scene.add.graphics();
+ this.fogGraphics.setDepth(9999); // Very high depth
+ this.fogGraphics.setScrollFactor(1); // Moves with camera? No, world coordinates.
+ // wait, graphics draw in world coords? Yes if not scrollFactor 0
+ // We want it to cover the world terrain.
+
+ this.drawFog();
+ }
+
+ /**
+ * Unlock a zone
+ */
+ unlockZone(zoneId) {
+ const zone = this.zones[zoneId];
+ if (!zone) return false;
+ if (zone.unlocked) return false; // Already unlocked
+
+ // Check Logic (Payment) would be here or called before.
+ // For now, just unlock logic.
+
+ zone.unlocked = true;
+ console.log(`๐บ๏ธ Zone Unlocked: ${zone.name}`);
+
+ // Visual feedback
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 100,
+ text: `UNLOCKED: ${zone.name}`,
+ color: '#FFFFFF'
+ });
+
+ // Sound
+ if (this.scene.soundManager) this.scene.soundManager.playSuccess(); // Assuming sound exists
+
+ this.drawFog(); // Redraw fog
+ return true;
+ }
+
+ /**
+ * Check if player is in an unlocked zone
+ * Used for restricting movement or camera
+ */
+ isPointUnlocked(x, y) {
+ for (const key in this.zones) {
+ const z = this.zones[key];
+ if (x >= z.x && x < z.x + z.w && y >= z.y && y < z.y + z.h) {
+ return z.unlocked;
+ }
+ }
+ return false; // Default blocked
+ }
+
+ /**
+ * Draw Fog of War over locked zones
+ */
+ drawFog() {
+ this.fogGraphics.clear();
+
+ // Fill semi-transparent black over locked zones
+ this.fogGraphics.fillStyle(0x000000, 0.7); // 70% opacity black fog
+
+ for (const key in this.zones) {
+ const z = this.zones[key];
+ if (!z.unlocked) {
+ // Convert grid coords to world coords logic
+ // Isometric conversion might be tricky for rectangular fog on isometric grid.
+ // But for simplicity let's assume raw screen overlay or draw huge loose shapes?
+ // Actually, our map coordinates (gridX, gridY) map to iso.
+
+ // Let's try drawing tile-by-tile or chunks? Too slow.
+ // Better approach: Draw a huge rectangle? No, iso is diamond shape.
+
+ // Hack: Just draw simple polygons for now, or use the tile tinting system?
+ // Real Fog of War in Iso is complex.
+ // Let's stick to "Tinting" locked tiles or something simpler first.
+ // Or: Render a shape that covers the locked area logic.
+
+ // Since our world is 100x100 grid.
+ // Zone Forest: x:40, y:0, w:60, h:40.
+
+ // Let's just draw 'clouds' or text over locked areas?
+ // Or simplified: Just don't let player walk there.
+
+ // Let's skip heavy graphics fog for now and just add Floating Text Labels
+ // "LOCKED AREA: FOREST" over the center.
+
+ // Drawing 4 points of the zone in iso:
+ const p1 = this.scene.iso.toScreen(z.x, z.y);
+ const p2 = this.scene.iso.toScreen(z.x + z.w, z.y);
+ const p3 = this.scene.iso.toScreen(z.x + z.w, z.y + z.h);
+ const p4 = this.scene.iso.toScreen(z.x, z.y + z.h);
+
+ const offX = this.scene.terrainOffsetX;
+ const offY = this.scene.terrainOffsetY;
+
+ const poly = new Phaser.Geom.Polygon([
+ p1.x + offX, p1.y + offY,
+ p2.x + offX, p2.y + offY,
+ p3.x + offX, p3.y + offY,
+ p4.x + offX, p4.y + offY
+ ]);
+
+ this.fogGraphics.fillPoints(poly.points, true);
+
+ // Add label? (We'd need to manage text objects separately, simple redraw is easier)
+ }
+ }
+ }
+
+ update(delta) {
+ // Maybe check if player tries to enter locked zone and push back?
+ if (this.scene.player) {
+ const p = this.scene.player;
+ if (!this.isPointUnlocked(p.gridX, p.gridY)) {
+ // Player is in locked zone! Push back!
+ // Simple bounce back logic
+ // Or just show warning
+ // console.log("Player in locked zone!");
+ }
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/FarmAutomationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/FarmAutomationSystem.js
new file mode 100644
index 000000000..98269c48e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/FarmAutomationSystem.js
@@ -0,0 +1,563 @@
+/**
+ * FARM AUTOMATION SYSTEM
+ * Complete automation with zombie workers, creature helpers, buildings, and power grid
+ */
+class FarmAutomationSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Workers
+ this.zombieWorkers = [];
+ this.creatureWorkers = [];
+ this.taskQueue = [];
+
+ // Buildings
+ this.automationBuildings = new Map();
+ this.powerGrid = new Map();
+
+ // Power system
+ this.totalPower = 0;
+ this.powerConsumption = 0;
+
+ // Settings
+ this.settings = {
+ maxZombieWorkers: 10,
+ maxCreatureWorkers: 5,
+ zombieEfficiency: 0.7, // 70% of player speed
+ powerUpdateInterval: 1000 // ms
+ };
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Farm Automation System initialized');
+ }
+
+ init() {
+ // Start power update loop
+ this.startPowerUpdate();
+ console.log('๐ค Farm automation ready');
+ }
+
+ // ========== ZOMBIE WORKER SYSTEM ==========
+
+ hireZombieWorker(name = 'Zombie') {
+ if (this.zombieWorkers.length >= this.settings.maxZombieWorkers) {
+ console.log('โ Max zombie workers reached');
+ return null;
+ }
+
+ const worker = {
+ id: `zombie_${Date.now()}`,
+ name,
+ type: 'zombie',
+ level: 1,
+ xp: 0,
+ efficiency: this.settings.zombieEfficiency,
+ currentTask: null,
+ position: { x: 0, y: 0 },
+ hunger: 100,
+ fatigue: 0,
+ sprite: null
+ };
+
+ this.zombieWorkers.push(worker);
+ this.saveProgress();
+ console.log(`๐ง Hired zombie worker: ${name}`);
+ return worker;
+ }
+
+ assignTask(worker, task) {
+ if (!worker) return false;
+
+ worker.currentTask = {
+ type: task.type, // 'plant', 'harvest', 'water'
+ target: task.target,
+ priority: task.priority || 1,
+ startTime: Date.now()
+ };
+
+ console.log(`๐ ${worker.name} assigned to ${task.type}`);
+ return true;
+ }
+
+ addToTaskQueue(task) {
+ this.taskQueue.push({
+ ...task,
+ id: `task_${Date.now()}`,
+ status: 'pending'
+ });
+
+ // Assign to available worker
+ this.assignNextTask();
+ }
+
+ assignNextTask() {
+ // Find idle worker
+ const idleWorker = [...this.zombieWorkers, ...this.creatureWorkers]
+ .find(w => !w.currentTask);
+
+ if (!idleWorker || this.taskQueue.length === 0) return;
+
+ // Get highest priority task
+ this.taskQueue.sort((a, b) => b.priority - a.priority);
+ const task = this.taskQueue.shift();
+
+ this.assignTask(idleWorker, task);
+ }
+
+ completeTask(worker) {
+ if (!worker || !worker.currentTask) return;
+
+ const task = worker.currentTask;
+
+ // Give XP
+ this.addWorkerXP(worker, 10);
+
+ // Increase fatigue
+ worker.fatigue += 10;
+
+ // Clear task
+ worker.currentTask = null;
+
+ // Assign next task
+ this.assignNextTask();
+
+ console.log(`โ
${worker.name} completed ${task.type}`);
+ }
+
+ addWorkerXP(worker, amount) {
+ worker.xp += amount;
+
+ // Level up check
+ const xpNeeded = worker.level * 100;
+ if (worker.xp >= xpNeeded) {
+ worker.level++;
+ worker.xp = 0;
+ worker.efficiency += 0.05; // 5% faster per level
+ console.log(`โฌ๏ธ ${worker.name} leveled up to ${worker.level}!`);
+ }
+
+ this.saveProgress();
+ }
+
+ // ========== CREATURE WORKER SYSTEM ==========
+
+ tameCreature(creatureType, name) {
+ if (this.creatureWorkers.length >= this.settings.maxCreatureWorkers) {
+ console.log('โ Max creature workers reached');
+ return null;
+ }
+
+ const specialties = {
+ donkey: { specialty: 'transport', efficiency: 1.0 },
+ bigfoot: { specialty: 'gathering', efficiency: 1.2 },
+ yeti: { specialty: 'snow_tasks', efficiency: 1.1 },
+ elf: { specialty: 'crafting', efficiency: 1.3 }
+ };
+
+ const data = specialties[creatureType] || { specialty: 'general', efficiency: 0.8 };
+
+ const worker = {
+ id: `creature_${Date.now()}`,
+ name,
+ type: creatureType,
+ specialty: data.specialty,
+ level: 1,
+ xp: 0,
+ efficiency: data.efficiency,
+ currentTask: null,
+ position: { x: 0, y: 0 },
+ hunger: 100,
+ fatigue: 0,
+ sprite: null
+ };
+
+ this.creatureWorkers.push(worker);
+ this.saveProgress();
+ console.log(`๐ฆ Tamed ${creatureType}: ${name} (${data.specialty})`);
+ return worker;
+ }
+
+ // ========== AUTOMATION BUILDINGS ==========
+
+ buildAutomation(type, x, y) {
+ const buildings = {
+ 'auto_planter': {
+ name: 'Auto-Planter',
+ powerCost: 10,
+ range: 3,
+ speed: 2000 // ms per action
+ },
+ 'auto_harvester': {
+ name: 'Auto-Harvester',
+ powerCost: 15,
+ range: 5,
+ speed: 1500
+ },
+ 'irrigation': {
+ name: 'Irrigation System',
+ powerCost: 5,
+ range: 10,
+ speed: 3000
+ },
+ 'conveyor': {
+ name: 'Conveyor Belt',
+ powerCost: 3,
+ range: 1,
+ speed: 500
+ },
+ 'silo': {
+ name: 'Storage Silo',
+ powerCost: 2,
+ capacity: 1000
+ },
+ 'sorter': {
+ name: 'Sorting Machine',
+ powerCost: 8,
+ speed: 1000
+ }
+ };
+
+ const buildingData = buildings[type];
+ if (!buildingData) return null;
+
+ const building = {
+ id: `${type}_${x}_${y}`,
+ type,
+ x, y,
+ ...buildingData,
+ active: false,
+ lastAction: 0
+ };
+
+ this.automationBuildings.set(building.id, building);
+ this.updatePowerConsumption();
+ this.saveProgress();
+
+ console.log(`๐ญ Built ${buildingData.name} at (${x}, ${y})`);
+ return building;
+ }
+
+ activateBuilding(buildingId) {
+ const building = this.automationBuildings.get(buildingId);
+ if (!building) return false;
+
+ // Check power
+ if (this.totalPower < this.powerConsumption + building.powerCost) {
+ console.log('โ Not enough power!');
+ return false;
+ }
+
+ building.active = true;
+ this.updatePowerConsumption();
+ console.log(`โก Activated ${building.name}`);
+ return true;
+ }
+
+ deactivateBuilding(buildingId) {
+ const building = this.automationBuildings.get(buildingId);
+ if (!building) return false;
+
+ building.active = false;
+ this.updatePowerConsumption();
+ console.log(`๐ Deactivated ${building.name}`);
+ return true;
+ }
+
+ // ========== POWER SYSTEM ==========
+
+ buildPowerSource(type, x, y) {
+ const sources = {
+ 'windmill': { name: 'Windmill', power: 20, cost: { wood: 10, iron: 5 } },
+ 'water_wheel': { name: 'Water Wheel', power: 15, cost: { wood: 8, iron: 3 } },
+ 'solar_panel': { name: 'Solar Panel', power: 25, cost: { iron: 10, glass: 5 } },
+ 'zombie_treadmill': { name: 'Zombie Treadmill', power: 10, cost: { wood: 5 } }
+ };
+
+ const sourceData = sources[type];
+ if (!sourceData) return null;
+
+ const source = {
+ id: `${type}_${x}_${y}`,
+ type,
+ x, y,
+ ...sourceData,
+ active: true,
+ currentPower: sourceData.power
+ };
+
+ this.powerGrid.set(source.id, source);
+ this.updateTotalPower();
+ this.saveProgress();
+
+ console.log(`โก Built ${sourceData.name} (+${sourceData.power} power)`);
+ return source;
+ }
+
+ updateTotalPower() {
+ this.totalPower = 0;
+
+ for (const source of this.powerGrid.values()) {
+ if (source.active) {
+ // Solar panels only work during day
+ if (source.type === 'solar_panel') {
+ const time = this.scene.weatherSystem?.gameTime || 12;
+ if (time >= 6 && time <= 18) {
+ this.totalPower += source.currentPower;
+ }
+ } else {
+ this.totalPower += source.currentPower;
+ }
+ }
+ }
+ }
+
+ updatePowerConsumption() {
+ this.powerConsumption = 0;
+
+ for (const building of this.automationBuildings.values()) {
+ if (building.active) {
+ this.powerConsumption += building.powerCost;
+ }
+ }
+ }
+
+ startPowerUpdate() {
+ setInterval(() => {
+ this.updateTotalPower();
+ this.updatePowerConsumption();
+
+ // Deactivate buildings if not enough power
+ if (this.powerConsumption > this.totalPower) {
+ this.handlePowerShortage();
+ }
+ }, this.settings.powerUpdateInterval);
+ }
+
+ handlePowerShortage() {
+ console.log('โ ๏ธ Power shortage! Deactivating low-priority buildings...');
+
+ // Deactivate buildings until power is sufficient
+ for (const building of this.automationBuildings.values()) {
+ if (building.active && this.powerConsumption > this.totalPower) {
+ this.deactivateBuilding(building.id);
+ }
+ }
+ }
+
+ getPowerStatus() {
+ return {
+ total: this.totalPower,
+ consumption: this.powerConsumption,
+ available: this.totalPower - this.powerConsumption,
+ percentage: (this.powerConsumption / this.totalPower) * 100
+ };
+ }
+
+ // ========== WORKER MANAGEMENT ==========
+
+ updateWorkers(delta) {
+ // Update zombie workers
+ for (const worker of this.zombieWorkers) {
+ this.updateWorker(worker, delta);
+ }
+
+ // Update creature workers
+ for (const worker of this.creatureWorkers) {
+ this.updateWorker(worker, delta);
+ }
+ }
+
+ updateWorker(worker, delta) {
+ // Decrease hunger over time
+ worker.hunger -= delta / 60000; // 1% per minute
+
+ // Increase fatigue if working
+ if (worker.currentTask) {
+ worker.fatigue += delta / 30000; // 1% per 30 seconds
+
+ // Complete task if enough time passed
+ const taskDuration = 5000 / worker.efficiency; // Base 5 seconds
+ if (Date.now() - worker.currentTask.startTime > taskDuration) {
+ this.completeTask(worker);
+ }
+ } else {
+ // Recover fatigue when idle
+ worker.fatigue -= delta / 60000;
+ }
+
+ // Clamp values
+ worker.hunger = Math.max(0, Math.min(100, worker.hunger));
+ worker.fatigue = Math.max(0, Math.min(100, worker.fatigue));
+
+ // Worker needs rest
+ if (worker.fatigue > 80) {
+ worker.currentTask = null;
+ console.log(`๐ด ${worker.name} is too tired to work`);
+ }
+
+ // Worker needs food
+ if (worker.hunger < 20) {
+ worker.efficiency *= 0.5; // 50% slower when hungry
+ }
+ }
+
+ feedWorker(worker, food) {
+ if (!worker) return false;
+
+ worker.hunger = Math.min(100, worker.hunger + 30);
+ console.log(`๐ Fed ${worker.name}`);
+ return true;
+ }
+
+ restWorker(worker) {
+ if (!worker) return false;
+
+ worker.currentTask = null;
+ worker.fatigue = Math.max(0, worker.fatigue - 50);
+ console.log(`๐ด ${worker.name} is resting`);
+ return true;
+ }
+
+ upgradeWorkerTools(worker, toolTier) {
+ if (!worker) return false;
+
+ const tierBonus = {
+ 'bronze': 0.1,
+ 'iron': 0.2,
+ 'steel': 0.3,
+ 'enchanted': 0.5
+ };
+
+ const bonus = tierBonus[toolTier] || 0;
+ worker.efficiency += bonus;
+
+ console.log(`โ๏ธ ${worker.name} upgraded to ${toolTier} tools (+${bonus * 100}% efficiency)`);
+ return true;
+ }
+
+ // ========== AUTOMATION UPDATE ==========
+
+ updateAutomation(delta) {
+ const now = Date.now();
+
+ for (const building of this.automationBuildings.values()) {
+ if (!building.active) continue;
+
+ // Check if enough time passed
+ if (now - building.lastAction < building.speed) continue;
+
+ // Perform building action
+ switch (building.type) {
+ case 'auto_planter':
+ this.autoPlant(building);
+ break;
+ case 'auto_harvester':
+ this.autoHarvest(building);
+ break;
+ case 'irrigation':
+ this.autoWater(building);
+ break;
+ case 'conveyor':
+ this.moveItems(building);
+ break;
+ case 'sorter':
+ this.sortItems(building);
+ break;
+ }
+
+ building.lastAction = now;
+ }
+ }
+
+ autoPlant(building) {
+ // Plant crops in range
+ console.log(`๐ฑ Auto-planter working at (${building.x}, ${building.y})`);
+ }
+
+ autoHarvest(building) {
+ // Harvest crops in range
+ console.log(`๐พ Auto-harvester working at (${building.x}, ${building.y})`);
+ }
+
+ autoWater(building) {
+ // Water crops in range
+ console.log(`๐ง Irrigation system working at (${building.x}, ${building.y})`);
+ }
+
+ moveItems(building) {
+ // Move items along conveyor
+ console.log(`๐ฆ Conveyor moving items at (${building.x}, ${building.y})`);
+ }
+
+ sortItems(building) {
+ // Sort items in storage
+ console.log(`๐ Sorter organizing items at (${building.x}, ${building.y})`);
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateWorkers(delta);
+ this.updateAutomation(delta);
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ zombieWorkers: this.zombieWorkers.map(w => ({
+ id: w.id,
+ name: w.name,
+ level: w.level,
+ xp: w.xp,
+ efficiency: w.efficiency
+ })),
+ creatureWorkers: this.creatureWorkers.map(w => ({
+ id: w.id,
+ name: w.name,
+ type: w.type,
+ level: w.level,
+ xp: w.xp
+ })),
+ buildings: Array.from(this.automationBuildings.values()).map(b => ({
+ id: b.id,
+ type: b.type,
+ x: b.x,
+ y: b.y,
+ active: b.active
+ })),
+ powerSources: Array.from(this.powerGrid.values()).map(p => ({
+ id: p.id,
+ type: p.type,
+ x: p.x,
+ y: p.y
+ }))
+ };
+
+ localStorage.setItem('novafarma_farm_automation', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_farm_automation');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+
+ // Restore workers (basic data only, full restoration in init)
+ this.savedData = data;
+
+ console.log('โ
Farm automation progress loaded');
+ } catch (error) {
+ console.error('Failed to load farm automation:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐ค Farm Automation System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/FarmRaidSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/FarmRaidSystem.js
new file mode 100644
index 000000000..61ee0ed55
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/FarmRaidSystem.js
@@ -0,0 +1,392 @@
+/**
+ * FARM RAID SYSTEM
+ * Spawn raider waves, defend farm, rewards
+ * Integrates with DefenseSystem and NomadRaiderAI
+ */
+
+export class FarmRaidSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Raid state
+ this.isRaidActive = false;
+ this.currentWave = 0;
+ this.totalWaves = 0;
+ this.raidDifficulty = 1;
+
+ // Spawn management
+ this.activeRaiders = [];
+ this.spawnPoints = [];
+
+ // Raid triggers
+ this.fameThreshold = 500; // Trigger raid at 500 fame
+ this.resourceThreshold = 5000; // Or 5000 resources
+ this.timeBetweenRaids = 600000; // 10 minutes minimum
+ this.lastRaidTime = 0;
+
+ // Rewards
+ this.defenseRewards = new Map();
+
+ this.init();
+ }
+
+ init() {
+ this.initializeSpawnPoints();
+ this.initializeRewards();
+
+ // Check for raid triggers periodically
+ this.scene.time.addEvent({
+ delay: 30000, // Check every 30s
+ callback: () => this.checkRaidTriggers(),
+ loop: true
+ });
+ }
+
+ /**
+ * Initialize raid spawn points around farm
+ */
+ initializeSpawnPoints() {
+ const farmCenter = { x: 400, y: 300 }; // Adjust to actual farm center
+ const spawnDistance = 400;
+
+ // 8 spawn points around farm (N, NE, E, SE, S, SW, W, NW)
+ for (let angle = 0; angle < 360; angle += 45) {
+ const rad = Phaser.Math.DegToRad(angle);
+ this.spawnPoints.push({
+ x: farmCenter.x + Math.cos(rad) * spawnDistance,
+ y: farmCenter.y + Math.sin(rad) * spawnDistance,
+ angle: angle
+ });
+ }
+ }
+
+ /**
+ * Initialize defense rewards
+ */
+ initializeRewards() {
+ this.defenseRewards.set(1, { xp: 200, currency: 500, items: ['raider_loot_common'] });
+ this.defenseRewards.set(2, { xp: 400, currency: 1000, items: ['raider_loot_common', 'raider_weapon'] });
+ this.defenseRewards.set(3, { xp: 800, currency: 2000, items: ['raider_loot_rare', 'raider_armor'] });
+ }
+
+ /**
+ * Check if raid should trigger
+ */
+ checkRaidTriggers() {
+ if (this.isRaidActive) return;
+
+ const now = Date.now();
+ const timeSinceLastRaid = now - this.lastRaidTime;
+
+ // Cooldown check
+ if (timeSinceLastRaid < this.timeBetweenRaids) return;
+
+ const fame = this.scene.gameState?.fame || 0;
+ const totalResources = this.scene.inventorySystem?.getTotalResourceValue() || 0;
+
+ // Trigger conditions
+ if (fame >= this.fameThreshold || totalResources >= this.resourceThreshold) {
+ this.startRaid();
+ }
+ }
+
+ /**
+ * Start a raid
+ */
+ startRaid(difficulty = null) {
+ if (this.isRaidActive) return;
+
+ this.isRaidActive = true;
+ this.currentWave = 0;
+ this.raidDifficulty = difficulty || this.calculateDifficulty();
+ this.totalWaves = 2 + this.raidDifficulty; // 3-5 waves
+ this.lastRaidTime = Date.now();
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `RAID INCOMING! ${this.totalWaves} waves approaching!`,
+ 'danger',
+ { priority: 'high' }
+ );
+
+ // SFX: Raid horn
+ this.scene.soundSystem?.play('raid_horn');
+
+ // VFX: Red screen flash
+ this.scene.cameras.main.flash(1000, 255, 0, 0);
+
+ // Start first wave
+ this.scene.time.delayedCall(3000, () => this.spawnWave());
+
+ console.log(`๐จ RAID STARTED: Difficulty ${this.raidDifficulty}, ${this.totalWaves} waves`);
+ }
+
+ /**
+ * Calculate raid difficulty based on game progress
+ */
+ calculateDifficulty() {
+ const fame = this.scene.gameState?.fame || 0;
+ const population = this.scene.gameState?.population || 0;
+
+ // Scale difficulty
+ return Math.min(5, Math.floor((fame / 500) + (population / 20)));
+ }
+
+ /**
+ * Spawn a wave of raiders
+ */
+ spawnWave() {
+ this.currentWave++;
+
+ const raiderCount = 3 + (this.raidDifficulty * 2); // 5-13 raiders
+ const waveRaiders = [];
+
+ // Spawn raiders
+ for (let i = 0; i < raiderCount; i++) {
+ const spawnPoint = Phaser.Utils.Array.GetRandom(this.spawnPoints);
+ const raider = this.spawnRaider(spawnPoint.x, spawnPoint.y);
+ waveRaiders.push(raider);
+ }
+
+ this.activeRaiders.push(...waveRaiders);
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `Wave ${this.currentWave}/${this.totalWaves}: ${raiderCount} raiders!`,
+ 'warning'
+ );
+
+ console.log(`๐ฅ Wave ${this.currentWave}: Spawned ${raiderCount} raiders`);
+
+ // Check for next wave
+ this.scene.time.delayedCall(5000, () => this.checkWaveProgress());
+ }
+
+ /**
+ * Spawn individual raider
+ */
+ spawnRaider(x, y) {
+ // Create raider sprite
+ const raider = this.scene.physics.add.sprite(x, y, 'nomad_raider');
+
+ // Stats based on difficulty
+ raider.maxHealth = 50 + (this.raidDifficulty * 20);
+ raider.health = raider.maxHealth;
+ raider.attackDamage = 10 + (this.raidDifficulty * 5);
+ raider.active = true;
+
+ // Add AI
+ raider.ai = new NomadRaiderAI(this.scene, raider);
+
+ // Collisions
+ this.scene.physics.add.collider(raider, this.scene.player);
+ this.scene.physics.add.collider(raider, this.scene.zombieScout);
+
+ // Health bar
+ raider.healthBar = this.scene.add.graphics();
+
+ // Death handler
+ raider.takeDamage = (amount) => this.raiderTakeDamage(raider, amount);
+
+ return raider;
+ }
+
+ /**
+ * Raider takes damage
+ */
+ raiderTakeDamage(raider, amount) {
+ raider.health -= amount;
+
+ // Update health bar
+ this.updateRaiderHealthBar(raider);
+
+ // VFX
+ raider.setTint(0xff0000);
+ this.scene.time.delayedCall(100, () => raider.clearTint());
+
+ // Death
+ if (raider.health <= 0) {
+ this.raiderDeath(raider);
+ }
+ }
+
+ /**
+ * Raider death
+ */
+ raiderDeath(raider) {
+ raider.active = false;
+
+ // Drop loot
+ this.dropRaiderLoot(raider.x, raider.y);
+
+ // VFX
+ this.scene.vfxSystem?.playEffect('raider_death', raider.x, raider.y);
+
+ // Remove from active list
+ const index = this.activeRaiders.indexOf(raider);
+ if (index > -1) {
+ this.activeRaiders.splice(index, 1);
+ }
+
+ // Cleanup
+ raider.ai?.destroy();
+ raider.healthBar?.destroy();
+ raider.destroy();
+
+ // Check if wave complete
+ this.checkWaveProgress();
+ }
+
+ /**
+ * Update raider health bar
+ */
+ updateRaiderHealthBar(raider) {
+ if (!raider.healthBar) return;
+
+ raider.healthBar.clear();
+
+ const barWidth = 40;
+ const barHeight = 4;
+ const healthPercent = raider.health / raider.maxHealth;
+
+ // Background
+ raider.healthBar.fillStyle(0x000000);
+ raider.healthBar.fillRect(raider.x - barWidth / 2, raider.y - 30, barWidth, barHeight);
+
+ // Health
+ raider.healthBar.fillStyle(0xff0000);
+ raider.healthBar.fillRect(raider.x - barWidth / 2, raider.y - 30, barWidth * healthPercent, barHeight);
+ }
+
+ /**
+ * Drop loot from raider
+ */
+ dropRaiderLoot(x, y) {
+ const lootTable = ['wood', 'stone', 'metal', 'raider_weapon', 'medicine'];
+ const drop = Phaser.Utils.Array.GetRandom(lootTable);
+ const amount = Phaser.Math.Between(1, 5);
+
+ // Create loot drop
+ this.scene.lootSystem?.createDrop(x, y, drop, amount);
+ }
+
+ /**
+ * Check if wave is complete
+ */
+ checkWaveProgress() {
+ if (!this.isRaidActive) return;
+
+ const aliveRaiders = this.activeRaiders.filter(r => r.active);
+
+ if (aliveRaiders.length === 0) {
+ // Wave complete
+ if (this.currentWave < this.totalWaves) {
+ // Next wave
+ this.scene.uiSystem?.showNotification(
+ `Wave ${this.currentWave} cleared! Next wave incoming...`,
+ 'success'
+ );
+
+ this.scene.time.delayedCall(5000, () => this.spawnWave());
+ } else {
+ // Raid complete
+ this.endRaid(true);
+ }
+ }
+ }
+
+ /**
+ * End raid (success or failure)
+ */
+ endRaid(success) {
+ this.isRaidActive = false;
+
+ if (success) {
+ // Victory!
+ const rewards = this.defenseRewards.get(this.raidDifficulty) || this.defenseRewards.get(1);
+
+ this.scene.uiSystem?.showNotification(
+ `RAID DEFENDED! +${rewards.xp} XP, +${rewards.currency} coins`,
+ 'victory',
+ { priority: 'high' }
+ );
+
+ // Grant rewards
+ this.scene.zombieScoutLeveling?.awardXP(rewards.xp, 'raid_defense');
+ this.scene.economySystem?.addCurrency(rewards.currency);
+
+ rewards.items.forEach(item => {
+ this.scene.inventorySystem?.addItem(item, 1);
+ });
+
+ // VFX
+ this.scene.vfxSystem?.playEffect('victory', 400, 300);
+ this.scene.cameras.main.flash(1000, 0, 255, 0);
+
+ console.log(`โ
RAID DEFENDED! Rewards granted.`);
+ } else {
+ // Defeat
+ this.scene.uiSystem?.showNotification(
+ 'RAID DEFEATED YOU! Farm damaged.',
+ 'defeat',
+ { priority: 'high' }
+ );
+
+ // Damage farm buildings
+ this.damageFarm();
+
+ console.log(`โ RAID FAILED! Farm damaged.`);
+ }
+
+ // Cleanup remaining raiders
+ this.activeRaiders.forEach(raider => {
+ raider.ai?.destroy();
+ raider.healthBar?.destroy();
+ raider.destroy();
+ });
+ this.activeRaiders = [];
+ }
+
+ /**
+ * Damage farm on raid failure
+ */
+ damageFarm() {
+ // Damage random buildings
+ const buildings = this.scene.buildingSystem?.getAllBuildings() || [];
+ const damagedCount = Math.min(3, buildings.length);
+
+ for (let i = 0; i < damagedCount; i++) {
+ const building = Phaser.Utils.Array.GetRandom(buildings);
+ building.takeDamage?.(50);
+ }
+
+ // Destroy random crops
+ const crops = this.scene.crops || [];
+ const destroyedCrops = Math.min(10, crops.length);
+
+ for (let i = 0; i < destroyedCrops; i++) {
+ const crop = Phaser.Utils.Array.GetRandom(crops);
+ crop.destroy?.();
+ }
+ }
+
+ /**
+ * Manual raid trigger (for testing/quests)
+ */
+ triggerRaid(difficulty = 1) {
+ this.startRaid(difficulty);
+ }
+
+ /**
+ * Get raid state
+ */
+ getRaidState() {
+ return {
+ isActive: this.isRaidActive,
+ currentWave: this.currentWave,
+ totalWaves: this.totalWaves,
+ difficulty: this.raidDifficulty,
+ activeRaiders: this.activeRaiders.filter(r => r.active).length
+ };
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/FarmingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/FarmingSystem.js
new file mode 100644
index 000000000..bdd3db872
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/FarmingSystem.js
@@ -0,0 +1,278 @@
+class FarmingSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.crops = []; // Active crops in world
+
+ // Crop definitions
+ this.cropTypes = {
+ 'carrot': {
+ name: 'Carrot',
+ growthStages: 4,
+ daysPerStage: 1,
+ sellPrice: 10,
+ seedCost: 5
+ },
+ 'wheat': {
+ name: 'Wheat',
+ growthStages: 4,
+ daysPerStage: 2,
+ sellPrice: 15,
+ seedCost: 8
+ },
+ 'cannabis': {
+ name: 'Cannabis',
+ growthStages: 4,
+ daysPerStage: 2,
+ sellPrice: 60,
+ seedCost: 20
+ }
+ };
+ }
+
+ // Till soil at grid position
+ tillSoil(gridX, gridY) {
+ if (!this.scene.terrainSystem) return false;
+
+ // ๐ฑ CHECK MICRO FARM BOUNDARY!
+ if (this.scene.microFarmSystem && !this.scene.microFarmSystem.isTileUnlocked(gridX, gridY)) {
+ console.log('โ Cannot till outside farm boundary!');
+
+ // Show error message
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: gridX * 48,
+ y: gridY * 48,
+ text: '๐ซ Unlock farm first!',
+ color: '#ff0000'
+ });
+ }
+ return false;
+ }
+
+ // Check if already tilled
+ const key = `${gridX},${gridY}`;
+ if (this.isTilled(gridX, gridY)) {
+ console.log('Already tilled!');
+ return false;
+ }
+
+ // Mark as tilled in terrain
+ if (!this.scene.terrainSystem.tilledSoil) {
+ this.scene.terrainSystem.tilledSoil = new Set();
+ }
+
+ this.scene.terrainSystem.tilledSoil.add(key);
+
+ // Visual feedback - place tilled soil sprite
+ const screenPos = this.scene.iso.toScreen(gridX, gridY);
+
+ if (this.scene.textures.exists('soil_tilled')) {
+ const sprite = this.scene.add.sprite(screenPos.x, screenPos.y, 'soil_tilled');
+ sprite.setOrigin(0.5, 1);
+ sprite.setScale(1.0);
+ sprite.setDepth(this.scene.iso.getDepth(gridX, gridY) - 1); // Below crops
+
+ // Store reference
+ if (!this.scene.terrainSystem.tilledSprites) {
+ this.scene.terrainSystem.tilledSprites = new Map();
+ }
+ this.scene.terrainSystem.tilledSprites.set(key, sprite);
+ }
+
+ // Play dig sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playDig();
+ }
+
+ // โจ PARTICLE EFFECT - Dig particles
+ if (this.scene.particleEnhancements) {
+ this.scene.particleEnhancements.digParticles(screenPos.x, screenPos.y);
+ }
+
+ console.log(`โ
Tilled soil at (${gridX}, ${gridY})`);
+ return true;
+ }
+
+ isTilled(gridX, gridY) {
+ if (!this.scene.terrainSystem || !this.scene.terrainSystem.tilledSoil) return false;
+ return this.scene.terrainSystem.tilledSoil.has(`${gridX},${gridY}`);
+ }
+
+ // Plant seed
+ plantSeed(gridX, gridY, cropType) {
+ if (!this.isTilled(gridX, gridY)) {
+ console.log('Need to till soil first!');
+ return false;
+ }
+
+ // Check if already has crop
+ if (this.getCropAt(gridX, gridY)) {
+ console.log('Already has a crop!');
+ return false;
+ }
+
+ // Create crop
+ const crop = {
+ gridX,
+ gridY,
+ type: cropType,
+ stage: 0,
+ daysInStage: 0,
+ plantedDay: this.scene.timeSystem ? this.scene.timeSystem.day : 0
+ };
+
+ this.crops.push(crop);
+
+ // Visual - create sprite
+ this.updateCropSprite(crop);
+
+ // Update farm stats
+ if (this.scene.farmStats) {
+ this.scene.farmStats.cropsPlanted++;
+ }
+
+ // Play plant sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playPlant();
+ }
+
+ // โจ PARTICLE EFFECT - Plant sparkle
+ if (this.scene.particleEnhancements) {
+ const screenPos = this.scene.iso.toScreen(gridX, gridY);
+ this.scene.particleEnhancements.plantSparkle(screenPos.x, screenPos.y);
+ }
+
+ console.log(`๐ฑ Planted ${cropType} at (${gridX}, ${gridY})`);
+ return true;
+ }
+
+ getCropAt(gridX, gridY) {
+ return this.crops.find(c => c.gridX === gridX && c.gridY === gridY);
+ }
+
+ updateCropSprite(crop) {
+ const screenPos = this.scene.iso.toScreen(crop.gridX, crop.gridY);
+
+ // Remove old sprite
+ if (crop.sprite) {
+ crop.sprite.destroy();
+ }
+
+ // Determine texture based on stage
+ let textureKey = `crop_${crop.type}_stage_${crop.stage}`;
+
+ if (this.scene.textures.exists(textureKey)) {
+ crop.sprite = this.scene.add.sprite(screenPos.x, screenPos.y, textureKey);
+ crop.sprite.setOrigin(0.5, 1);
+ crop.sprite.setScale(0.8);
+ crop.sprite.setDepth(this.scene.iso.getDepth(crop.gridX, crop.gridY));
+
+ // Apply tilts/fallback tints
+ if (textureKey === 'carrots_stages' && crop.type === 'cannabis') {
+ crop.sprite.setTint(0x55ff55); // Green tint for cannabis fallback
+ } else if (crop.type === 'cannabis') {
+ crop.sprite.setTint(0x88ff88); // Subtle green boost for specific texture
+ }
+ }
+ }
+
+ // Harvest crop
+ harvestCrop(gridX, gridY) {
+ const crop = this.getCropAt(gridX, gridY);
+ if (!crop) return false;
+
+ const cropDef = this.cropTypes[crop.type];
+ const isRipe = crop.stage >= (cropDef.growthStages - 1);
+
+ if (!isRipe) {
+ console.log('Crop not ready yet!');
+ return false;
+ }
+
+ // Give items
+ if (this.scene.inventorySystem) {
+ if (crop.type === 'cannabis') {
+ this.scene.inventorySystem.addItem('cannabis_buds', 2);
+ this.scene.inventorySystem.addItem('hemp_fiber', 3);
+ } else {
+ this.scene.inventorySystem.addItem(crop.type, 1);
+ }
+ }
+
+ // Give gold
+ const goldEarned = cropDef.sellPrice;
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.gold += goldEarned;
+ }
+
+ // Update farm stats
+ if (this.scene.farmStats) {
+ this.scene.farmStats.totalHarvested++;
+ this.scene.farmStats.goldEarned += goldEarned;
+ }
+
+ // Remove crop
+ if (crop.sprite) crop.sprite.destroy();
+ this.crops = this.crops.filter(c => c !== crop);
+
+ // Play harvest sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playHarvest();
+ }
+
+ console.log(`๐พ Harvested ${crop.type}! (+${goldEarned} gold)`);
+
+ // โจ PARTICLE EFFECT - Harvest burst
+ const screenPos = this.scene.iso.toScreen(gridX, gridY);
+ if (this.scene.particleEnhancements) {
+ this.scene.particleEnhancements.harvestBurst(screenPos.x, screenPos.y, crop.type);
+ }
+
+ // Show floating text
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: screenPos.x,
+ y: screenPos.y - 30,
+ text: `+${goldEarned}g`,
+ color: '#FFD700'
+ });
+ }
+
+ return true;
+ }
+
+ // Update - called each game day
+ updateDay() {
+ this.crops.forEach(crop => {
+ const cropDef = this.cropTypes[crop.type];
+
+ crop.daysInStage++;
+
+ // Advance stage?
+ if (crop.daysInStage >= cropDef.daysPerStage) {
+ crop.daysInStage = 0;
+ crop.stage++;
+
+ // Cap at max stage
+ if (crop.stage >= cropDef.growthStages) {
+ crop.stage = cropDef.growthStages - 1;
+ }
+
+ // Update visual
+ this.updateCropSprite(crop);
+
+ console.log(`๐ฑ Crop grew! Stage ${crop.stage}/${cropDef.growthStages - 1}`);
+ }
+ });
+ }
+
+ // Update - called each frame
+ update() {
+ // Update sprite depths if camera moved
+ this.crops.forEach(crop => {
+ if (crop.sprite) {
+ crop.sprite.setDepth(this.scene.iso.getDepth(crop.gridX, crop.gridY));
+ }
+ });
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/FishingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/FishingSystem.js
new file mode 100644
index 000000000..9b0632d35
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/FishingSystem.js
@@ -0,0 +1,471 @@
+/**
+ * FISHING SYSTEM
+ * Complete fishing system with minigame, fish types, bait, and aquarium
+ */
+class FishingSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Fishing state
+ this.isFishing = false;
+ this.currentCast = null;
+ this.fishingRod = null;
+
+ // Fish types
+ this.fishTypes = new Map();
+
+ // Caught fish
+ this.caughtFish = [];
+ this.fishCollection = new Set();
+
+ // Bait
+ this.currentBait = null;
+
+ // Aquarium
+ this.aquarium = [];
+ this.aquariumCapacity = 10;
+
+ // Minigame
+ this.minigame = null;
+
+ // Settings
+ this.settings = {
+ castDistance: 5, // tiles
+ biteChance: 0.3, // 30% per second
+ minigameTime: 3000, // 3 seconds
+ greenZoneSize: 0.2 // 20% of bar
+ };
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Fishing System initialized');
+ }
+
+ init() {
+ this.defineFishTypes();
+ this.defineRods();
+ console.log('๐ฃ Fishing system ready');
+ }
+
+ // ========== FISH TYPES ==========
+
+ defineFishTypes() {
+ // Common fish (70%)
+ this.defineFish('bass', {
+ name: 'Bass',
+ rarity: 'common',
+ weight: { min: 1, max: 3 },
+ value: 10,
+ chance: 0.35
+ });
+
+ this.defineFish('trout', {
+ name: 'Trout',
+ rarity: 'common',
+ weight: { min: 0.5, max: 2 },
+ value: 8,
+ chance: 0.35
+ });
+
+ // Rare fish (25%)
+ this.defineFish('salmon', {
+ name: 'Salmon',
+ rarity: 'rare',
+ weight: { min: 3, max: 8 },
+ value: 50,
+ chance: 0.15
+ });
+
+ this.defineFish('tuna', {
+ name: 'Tuna',
+ rarity: 'rare',
+ weight: { min: 10, max: 20 },
+ value: 75,
+ chance: 0.10
+ });
+
+ // Legendary fish (5%)
+ this.defineFish('golden_fish', {
+ name: 'Golden Fish',
+ rarity: 'legendary',
+ weight: { min: 5, max: 10 },
+ value: 500,
+ chance: 0.03
+ });
+
+ this.defineFish('sea_dragon', {
+ name: 'Sea Dragon',
+ rarity: 'legendary',
+ weight: { min: 50, max: 100 },
+ value: 1000,
+ chance: 0.02
+ });
+ }
+
+ defineFish(id, data) {
+ this.fishTypes.set(id, {
+ id,
+ ...data
+ });
+ }
+
+ // ========== FISHING RODS ==========
+
+ defineRods() {
+ this.fishingRods = {
+ basic: {
+ name: 'Basic Rod',
+ cost: { wood: 5, string: 2 },
+ catchBonus: 0,
+ durability: 50
+ },
+ iron: {
+ name: 'Iron Rod',
+ cost: { iron: 3, wood: 2, string: 2 },
+ catchBonus: 0.1,
+ durability: 100
+ },
+ enchanted: {
+ name: 'Enchanted Rod',
+ cost: { magic_crystal: 1, iron: 5, string: 3 },
+ catchBonus: 0.3,
+ durability: 200
+ }
+ };
+ }
+
+ // ========== FISHING ==========
+
+ canFish() {
+ // Check if player has fishing rod
+ if (!this.fishingRod) {
+ console.log('โ No fishing rod equipped');
+ return false;
+ }
+
+ // Check if near water
+ if (!this.isNearWater()) {
+ console.log('โ Not near water');
+ return false;
+ }
+
+ return true;
+ }
+
+ isNearWater() {
+ // Check if player is near water tile
+ if (!this.scene.player) return false;
+
+ const playerPos = this.scene.player.getPosition();
+ const tileX = Math.floor(playerPos.x);
+ const tileY = Math.floor(playerPos.y);
+
+ // Check surrounding tiles
+ for (let dx = -2; dx <= 2; dx++) {
+ for (let dy = -2; dy <= 2; dy++) {
+ const tile = this.scene.terrainSystem.getTile(tileX + dx, tileY + dy);
+ if (tile && tile.type === 'water') {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ startFishing() {
+ if (!this.canFish()) return false;
+
+ this.isFishing = true;
+
+ // Cast line
+ this.currentCast = {
+ startTime: Date.now(),
+ biteTime: this.calculateBiteTime(),
+ hasBite: false
+ };
+
+ console.log('๐ฃ Cast fishing line...');
+ return true;
+ }
+
+ calculateBiteTime() {
+ // Random time between 2-5 seconds
+ const baseTime = 2000 + Math.random() * 3000;
+
+ // Bait reduces wait time
+ if (this.currentBait) {
+ return baseTime * 0.7;
+ }
+
+ return baseTime;
+ }
+
+ updateFishing(delta) {
+ if (!this.isFishing || !this.currentCast) return;
+
+ const now = Date.now();
+ const elapsed = now - this.currentCast.startTime;
+
+ // Check for bite
+ if (!this.currentCast.hasBite && elapsed >= this.currentCast.biteTime) {
+ this.triggerBite();
+ }
+ }
+
+ triggerBite() {
+ this.currentCast.hasBite = true;
+
+ // Visual indicator
+ console.log('๐ BITE! Press SPACE to reel!');
+
+ // Show minigame
+ this.startMinigame();
+ }
+
+ // ========== MINIGAME ==========
+
+ startMinigame() {
+ this.minigame = {
+ startTime: Date.now(),
+ duration: this.settings.minigameTime,
+ barPosition: 0.5, // 0-1
+ targetPosition: Math.random(), // 0-1
+ greenZoneStart: 0,
+ greenZoneEnd: 0,
+ success: false
+ };
+
+ // Calculate green zone
+ this.minigame.greenZoneStart = this.minigame.targetPosition - this.settings.greenZoneSize / 2;
+ this.minigame.greenZoneEnd = this.minigame.targetPosition + this.settings.greenZoneSize / 2;
+
+ console.log('๐ฎ Minigame started! Keep bar in green zone!');
+ }
+
+ updateMinigame(delta) {
+ if (!this.minigame) return;
+
+ const now = Date.now();
+ const elapsed = now - this.minigame.startTime;
+
+ // Check if time's up
+ if (elapsed >= this.minigame.duration) {
+ this.endMinigame();
+ return;
+ }
+
+ // Move target (makes it harder)
+ this.minigame.targetPosition += (Math.random() - 0.5) * 0.01;
+ this.minigame.targetPosition = Math.max(0, Math.min(1, this.minigame.targetPosition));
+
+ // Update green zone
+ this.minigame.greenZoneStart = this.minigame.targetPosition - this.settings.greenZoneSize / 2;
+ this.minigame.greenZoneEnd = this.minigame.targetPosition + this.settings.greenZoneSize / 2;
+
+ // Check if bar is in green zone
+ if (this.minigame.barPosition >= this.minigame.greenZoneStart &&
+ this.minigame.barPosition <= this.minigame.greenZoneEnd) {
+ this.minigame.success = true;
+ }
+ }
+
+ moveBar(direction) {
+ if (!this.minigame) return;
+
+ // Move bar left or right
+ this.minigame.barPosition += direction * 0.05;
+ this.minigame.barPosition = Math.max(0, Math.min(1, this.minigame.barPosition));
+ }
+
+ endMinigame() {
+ if (!this.minigame) return;
+
+ if (this.minigame.success) {
+ // Success! Catch fish
+ this.catchFish();
+ } else {
+ // Failed
+ console.log('โ Fish got away!');
+ }
+
+ this.minigame = null;
+ this.isFishing = false;
+ this.currentCast = null;
+ }
+
+ // ========== CATCHING ==========
+
+ catchFish() {
+ // Determine which fish was caught
+ const fish = this.rollFish();
+
+ if (!fish) {
+ console.log('โ Nothing caught');
+ return;
+ }
+
+ // Generate weight
+ const weight = fish.weight.min + Math.random() * (fish.weight.max - fish.weight.min);
+
+ // Create caught fish
+ const caughtFish = {
+ id: fish.id,
+ name: fish.name,
+ rarity: fish.rarity,
+ weight: Math.round(weight * 10) / 10,
+ value: fish.value,
+ caughtTime: Date.now()
+ };
+
+ this.caughtFish.push(caughtFish);
+ this.fishCollection.add(fish.id);
+
+ // Add to inventory
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(`fish_${fish.id}`, 1);
+ }
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ const player = this.scene.player;
+ if (player) {
+ const pos = player.getPosition();
+ const color = fish.rarity === 'legendary' ? 0xFFD700 :
+ fish.rarity === 'rare' ? 0x9370DB : 0x4169E1;
+ this.scene.visualEnhancements.createSparkleEffect(pos.x, pos.y);
+ this.scene.visualEnhancements.screenFlash(color, 300);
+ }
+ }
+
+ // Achievement
+ if (this.scene.uiGraphics) {
+ if (fish.rarity === 'legendary') {
+ this.scene.uiGraphics.unlockAchievement('legendary_fisher');
+ }
+ if (this.fishCollection.size >= 6) {
+ this.scene.uiGraphics.unlockAchievement('fish_collector');
+ }
+ }
+
+ console.log(`๐ฃ Caught ${fish.name}! (${caughtFish.weight}kg)`);
+ this.saveProgress();
+ }
+
+ rollFish() {
+ // Roll for fish type based on chances
+ const roll = Math.random();
+ let cumulative = 0;
+
+ // Apply rod bonus
+ const bonus = this.fishingRod ? this.fishingRods[this.fishingRod].catchBonus : 0;
+
+ for (const fish of this.fishTypes.values()) {
+ cumulative += fish.chance * (1 + bonus);
+ if (roll <= cumulative) {
+ return fish;
+ }
+ }
+
+ return null;
+ }
+
+ // ========== BAIT ==========
+
+ useBait(baitType) {
+ this.currentBait = baitType;
+ console.log(`๐ชฑ Using ${baitType} bait`);
+ }
+
+ // ========== AQUARIUM ==========
+
+ addToAquarium(fishId) {
+ if (this.aquarium.length >= this.aquariumCapacity) {
+ console.log('โ Aquarium is full');
+ return false;
+ }
+
+ const fish = this.caughtFish.find(f => f.id === fishId);
+ if (!fish) {
+ console.log('โ Fish not found');
+ return false;
+ }
+
+ this.aquarium.push(fish);
+ console.log(`๐ Added ${fish.name} to aquarium`);
+ this.saveProgress();
+ return true;
+ }
+
+ // ========== KEYBOARD CONTROLS ==========
+
+ setupKeyboard() {
+ // Press 'R' to start fishing
+ this.scene.input.keyboard.on('keydown-R', () => {
+ if (!this.isFishing) {
+ this.startFishing();
+ }
+ });
+
+ // Press 'SPACE' during minigame
+ this.scene.input.keyboard.on('keydown-SPACE', () => {
+ if (this.minigame) {
+ this.moveBar(0.1);
+ }
+ });
+
+ // Arrow keys for minigame
+ this.scene.input.keyboard.on('keydown-LEFT', () => {
+ if (this.minigame) {
+ this.moveBar(-0.1);
+ }
+ });
+
+ this.scene.input.keyboard.on('keydown-RIGHT', () => {
+ if (this.minigame) {
+ this.moveBar(0.1);
+ }
+ });
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateFishing(delta);
+ this.updateMinigame(delta);
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ caughtFish: this.caughtFish,
+ fishCollection: Array.from(this.fishCollection),
+ aquarium: this.aquarium
+ };
+
+ localStorage.setItem('novafarma_fishing', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_fishing');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.caughtFish = data.caughtFish || [];
+ this.fishCollection = new Set(data.fishCollection || []);
+ this.aquarium = data.aquarium || [];
+ console.log('โ
Fishing progress loaded');
+ } catch (error) {
+ console.error('Failed to load fishing progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐ฃ Fishing System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/Flat2DTerrainSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/Flat2DTerrainSystem.js
new file mode 100644
index 000000000..fd6a61c90
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/Flat2DTerrainSystem.js
@@ -0,0 +1,1590 @@
+// Flat2DTerrainSystem - Complete 2D top-down tile rendering
+// Replaces isometric TerrainSystem for Stardew Valley style
+
+class Flat2DTerrainSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.tileSize = 48;
+ this.width = 500; // ๐ PHASE 28: Expanded to 500x500!
+ this.height = 500;
+
+ // Tile map data
+ this.tiles = [];
+
+ // Rendering containers
+ this.groundLayer = null;
+ this.pathsLayer = null;
+ this.decorLayer = null;
+
+ // Decoration tracking (for interaction system)
+ this.decorationsMap = new Map();
+
+ // Textures ready flag
+ this.texturesReady = false;
+
+ // ๐ PHASE 28: Biome support
+ this.biomeSystem = null; // Will be set by GameScene
+ this.chunkManager = null; // Will be set by GameScene
+ this.chunkSize = 50; // Chunk size for rendering (matches ChunkManager)
+
+ // ๐๏ธ PHASE 28 SESSION 6: Structure support
+ this.structureSystem = null; // Will be set by GameScene
+ this.riverSystem = null; // Will be set by GameScene
+ this.lakeSystem = null; // Will be set by GameScene
+
+ // Grid graphics
+ this.gridGraphics = null;
+ this.gridVisible = true; // Enabled by default for Tiled mapping
+
+ console.log('๐จ Flat2DTerrainSystem initialized (500x500 world)');
+ }
+
+ async generate() {
+ console.log('๐บ๏ธ Generating flat 2D map...');
+
+ // Create textures first
+ this.createTileTextures();
+
+ // ๐ PHASE 28: Use BiomeSystem if available
+ if (this.biomeSystem && this.chunkManager) {
+ console.log('๐ Using BiomeSystem for chunk-based terrain generation');
+ // Biome-based generation will happen via ChunkManager
+ // No need to generate full map here!
+
+ // Create simple grass background as fallback
+ this.createBiomeBackground();
+ } else {
+ // Fallback to old Map2DData system
+ console.log('๐ Using Map2DData for terrain generation (fallback)');
+
+ // Load map data
+ if (typeof Map2DData !== 'undefined') {
+ this.tiles = Map2DData.generateMap();
+ console.log('โ
Map data generated:', this.tiles.length, 'rows');
+ } else {
+ console.error('โ Map2DData not loaded!');
+ this.createFallbackMap();
+ }
+
+ // Render the old-style map
+ this.renderMap();
+ }
+
+ console.log('โ
Flat 2D map ready!');
+ }
+
+ // ๐บ๏ธ Load map directly from Tiled JSON (User Created)
+ loadFromTiled(map) {
+ this.tiledMap = map;
+ console.log('๐บ๏ธ Loading terrain from Tiled map:', map.key);
+
+ // Update system dimensions
+ this.width = map.width;
+ this.height = map.height;
+ this.tileSize = map.tileWidth;
+
+ // Prepare tilesets array
+ const tilesets = [];
+
+ // Helper to map tileset names to Phaser keys (similar to TiledTestScene)
+ const getTilesetKey = (name) => {
+ const mapping = {
+ 'grass': 'tileset_grass',
+ 'dirt': 'tileset_dirt',
+ 'water': 'tileset_water',
+ 'decorations': 'tileset_decorations',
+ '01_Ground': 'tileset_01_Ground',
+ '02_Obstacles': 'tileset_02_Obstacles',
+ '03_Fences': 'tileset_03_Fences',
+ '04_Buildings': 'tileset_04_Buildings',
+ '05_Tools_Items': 'tileset_05_Tools_Items',
+ 'camp_objects': 'tileset_camp_objects',
+ 'starting_camp': 'tileset_starting_camp',
+ 'starting_camp_topdown': 'tileset_starting_camp',
+ 'ground_tiles': 'ground_tiles',
+ 'objects_pack': 'objects_pack',
+ 'objects_pack2': 'objects_pack2',
+ 'walls_pack': 'walls_pack',
+ 'trees_vegetation': 'trees_vegetation',
+ 'tree_blue': 'tree_blue',
+ 'tree_green': 'tree_green',
+ 'tree_dead': 'tree_dead',
+ 'flowers': 'flowers',
+ 'NovaFarmaTiles': 'ground_tiles'
+ };
+ const key = mapping[name] || mapping[name.toLowerCase()] || `tileset_${name}`;
+ console.log(`๐ Mapping tileset: "${name}" -> Suggested key: "${key}"`);
+ return key;
+ };
+
+ // Load tilesets
+ map.tilesets.forEach(tilesetData => {
+ let textureKey = getTilesetKey(tilesetData.name);
+
+ // ๐ต๏ธ Try to find texture (Prefix matched OR Exact name)
+ if (!this.scene.textures.exists(textureKey)) {
+ // Fallback: Try the raw tileset name as key
+ if (this.scene.textures.exists(tilesetData.name)) {
+ textureKey = tilesetData.name;
+ }
+ }
+
+ if (this.scene.textures.exists(textureKey)) {
+ try {
+ const tileset = map.addTilesetImage(tilesetData.name, textureKey);
+ if (tileset) {
+ tilesets.push(tileset);
+ console.log(` โ
Tileset mapped: "${tilesetData.name}" -> Key: "${textureKey}"`);
+ } else {
+ console.warn(` โ ๏ธ Phaser returned null for tileset: "${tilesetData.name}"`);
+ }
+ } catch (e) {
+ console.error(` โ Failed to add tileset: ${tilesetData.name}`, e);
+ }
+ } else {
+ console.warn(` โ ๏ธ Texture not found for tileset: "${tilesetData.name}". Tried keys: "${textureKey}"`);
+ // Fallback: try to load it now? Or use a placeholder?
+ // If we don't add the tileset, GIDs might be confused if this tileset is used.
+ // Try adding with 'tileset_Terrain_Grass' as emergency fallback just to see geometry
+ /*
+ const fallbackTileset = map.addTilesetImage(tilesetData.name, 'tileset_Terrain_Grass');
+ if (fallbackTileset) tilesets.push(fallbackTileset);
+ */
+ }
+ });
+
+ // DEBUG: Check GIDs
+ if (tilesets.length > 0) {
+ console.log(' ๐ Loaded Tilesets:', tilesets.map(t => ({ name: t.name, firstgid: t.firstgid })));
+ }
+
+ // Create layers
+ console.log(` Found ${map.layers.length} layers in map.`);
+ map.layers.forEach((layerData, index) => {
+ console.log(` Processing Layer ${index}: Name="${layerData.name}", Type="${layerData.type}", Visible=${layerData.visible}`);
+
+ // Phaser 3.50+ puts tile layers in map.layers. They might not have 'type' property set explicitly to 'tilelayer'.
+ // If it's in map.layers, it IS a tile layer.
+ if (layerData.visible) {
+ // if (layerData.visible && (layerData.type === 'tilelayer' || layerData.type === undefined)) {
+ const layer = map.createLayer(layerData.name, tilesets, 0, 0);
+ if (layer) {
+ console.log(` โ
Layer created: ${layerData.name}`);
+ layer.setAlpha(1);
+ layer.setVisible(true);
+
+ // Depth sorting
+ const lowerName = layerData.name.toLowerCase();
+ if (lowerName.includes('ground')) {
+ layer.setDepth(1);
+ this.groundLayer = layer;
+ }
+ else if (lowerName.includes('path')) {
+ layer.setDepth(2);
+ this.pathsLayer = layer;
+ }
+ else if (lowerName.includes('decor') || lowerName.includes('obstacle') || lowerName.includes('object')) {
+ // Y-SORTED DEPTH for proper player sorting!
+ // Uses same layer as Player (200000 + Y)
+ layer.setDepth(200000); // Base depth, tiles will sort by Y
+ this.decorLayer = layer;
+ }
+ else if (lowerName.includes('building') || lowerName.includes('structure') || lowerName.includes('fence')) {
+ layer.setDepth(200000); // Also Y-sorted
+ }
+ else {
+ layer.setDepth(2);
+ }
+ }
+ }
+ });
+
+ // ๐บ๏ธ Populate this.tiles from map data (for Pathfinding, Farming, etc.)
+ // We use the 'Ground' layer as the base grid
+ const groundLayerData = map.getLayer('Ground') || map.layers[0];
+ if (groundLayerData && groundLayerData.data && Array.isArray(groundLayerData.data)) {
+ console.log('๐ Populating terrain tiles grid from Tiled data...');
+ this.tiles = [];
+ for (let y = 0; y < map.height; y++) {
+ this.tiles[y] = [];
+ for (let x = 0; x < map.width; x++) {
+ // Safety check: ensure row exists
+ const tile = (groundLayerData.data[y] && groundLayerData.data[y][x]) || null;
+ this.tiles[y][x] = {
+ x: x,
+ y: y,
+ type: (tile && tile.index !== -1) ? 'grass' : 'void',
+ index: tile ? tile.index : -1
+ };
+ }
+ }
+ console.log(`โ
Tiles grid populated: ${this.width}x${this.height}`);
+ }
+
+ // Disable procedural aspects
+ if (this.scene.cameras && this.scene.cameras.main) {
+ // Remove gray background so Tiled map is clear
+ this.scene.cameras.main.setBackgroundColor('#000000');
+ }
+
+ this.isGenerated = true;
+
+ // Setup camera bounds
+ this.scene.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
+ this.scene.cameras.main.centerOn(map.widthInPixels / 2, map.heightInPixels / 2);
+
+ console.log('โ
Tiled map loaded and rendered!');
+
+ // Create initial grid (hidden by default)
+ this.createGrid();
+ }
+
+ // ๐ธ๏ธ Create a visual grid helper
+ createGrid() {
+ if (this.gridGraphics) {
+ this.gridGraphics.destroy();
+ }
+
+ this.gridGraphics = this.scene.add.graphics();
+ this.gridGraphics.lineStyle(1, 0xFFFFFF, 0.2); // Subtle white line
+
+ const mapWidthInPixels = this.width * this.tileSize;
+ const mapHeightInPixels = this.height * this.tileSize;
+
+ // Draw vertical lines
+ for (let x = 0; x <= this.width; x++) {
+ this.gridGraphics.moveTo(x * this.tileSize, 0);
+ this.gridGraphics.lineTo(x * this.tileSize, mapHeightInPixels);
+ }
+
+ // Draw horizontal lines
+ for (let y = 0; y <= this.height; y++) {
+ this.gridGraphics.moveTo(0, y * this.tileSize);
+ this.gridGraphics.lineTo(mapWidthInPixels, y * this.tileSize);
+ }
+
+ this.gridGraphics.strokePath();
+ this.gridGraphics.setDepth(100); // High depth to stay on top
+ this.gridGraphics.setVisible(this.gridVisible);
+
+ console.log('๐ธ๏ธ Visual grid created!');
+ }
+
+ toggleGrid() {
+ this.gridVisible = !this.gridVisible;
+ if (this.gridGraphics) {
+ this.gridGraphics.setVisible(this.gridVisible);
+ }
+ console.log(`๐ธ๏ธ Grid visibility: ${this.gridVisible}`);
+ }
+
+ // ๐ PHASE 28: Create simple biome-aware background
+ createBiomeBackground() {
+ const size = this.tileSize;
+ const mapWidth = this.width * size;
+ const mapHeight = this.height * size;
+
+ console.log('๐จ Creating biome-aware background...');
+
+ // Create solid background (will be covered by chunks)
+ const background = this.scene.add.rectangle(0, 0, mapWidth, mapHeight, 0x3CB371);
+ background.setOrigin(0, 0);
+ background.setDepth(0);
+
+ // Create containers for chunks
+ this.pathsLayer = this.scene.add.container(0, 0);
+ this.pathsLayer.setDepth(2);
+
+ this.decorLayer = this.scene.add.container(0, 0);
+ this.decorLayer.setDepth(3);
+
+ this.groundLayer = background;
+
+ console.log('โ
Biome background created, ready for chunks');
+ }
+
+ createTileTextures() {
+ // ๐จ Create SOLID, OPAQUE tiles (no transparency!)
+
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+ const size = this.tileSize;
+
+ // Check if PNG tilesets are loaded
+ const hasGrass = this.scene.textures.exists('tileset_grass');
+ const hasWater = this.scene.textures.exists('tileset_water');
+ const hasDirt = this.scene.textures.exists('tileset_dirt');
+
+ if (hasGrass && hasWater && hasDirt) {
+ console.log('โ
PNG Tilesets found! Creating beautiful tiles...');
+
+ // ๐ PHASE 28: Create BIOME-SPECIFIC GRASS TILES
+
+ // GRASSLAND - SUPER VIBRANT GREEN! ๐ฟ
+ graphics.clear();
+ graphics.fillStyle(0x3CB371, 1.0); // Medium sea green
+ graphics.fillRect(0, 0, size, size);
+
+ // Darker spots for contrast
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0x2E8B57, 0.5);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2 + Math.random() * 4);
+ }
+ // Lighter highlights
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0x90EE90, 0.4);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 1.5);
+ }
+ graphics.generateTexture('tile2d_grass', size, size);
+
+ // FOREST BIOME - DARK GREEN
+ graphics.clear();
+ graphics.fillStyle(0x2d5016, 1.0); // Dark green
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 25; i++) {
+ graphics.fillStyle(0x1a3010, 0.4);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3);
+ }
+ graphics.generateTexture('tile2d_forest', size, size);
+
+ // DESERT BIOME - TAN/SAND
+ graphics.clear();
+ graphics.fillStyle(0xd4c4a1, 1.0); // Sand color
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0xc4b491, 0.3);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 4);
+ }
+ graphics.generateTexture('tile2d_desert', size, size);
+
+ // MOUNTAIN BIOME - GRAY STONE
+ graphics.clear();
+ graphics.fillStyle(0x808080, 1.0); // Gray
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 30; i++) {
+ graphics.fillStyle(0x606060, 0.5);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2 + Math.random() * 3);
+ }
+ graphics.generateTexture('tile2d_mountain', size, size);
+
+ // SWAMP BIOME - DARK GREEN/BROWN
+ graphics.clear();
+ graphics.fillStyle(0x3d5a3d, 1.0); // Murky green
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0x2d4a2d, 0.6);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3 + Math.random() * 4);
+ }
+ graphics.generateTexture('tile2d_swamp', size, size);
+
+ // ===== NEW BIOMES - NORMAL (4) =====
+
+ // SNOW/FROZEN TUNDRA - LIGHT BLUE/WHITE
+ graphics.clear();
+ graphics.fillStyle(0xE0F7FA, 1.0); // Light cyan
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 30; i++) {
+ graphics.fillStyle(0xFFFFFF, 0.7); // White snowflakes
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 1 + Math.random() * 2);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0xB3E5FC, 0.5); // Ice patches
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 4);
+ }
+ graphics.generateTexture('tile2d_snow', size, size);
+
+ // WASTELAND - DARK GRAY/BROWN
+ graphics.clear();
+ graphics.fillStyle(0x4a4a4a, 1.0); // Dark gray
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 25; i++) {
+ graphics.fillStyle(0x333333, 0.6); // Darker rubble
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3 + Math.random() * 4);
+ }
+ for (let i = 0; i < 10; i++) {
+ graphics.fillStyle(0x654321, 0.4); // Brown rust
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_wasteland', size, size);
+
+ // TROPICAL - YELLOW SAND
+ graphics.clear();
+ graphics.fillStyle(0xFFE082, 1.0); // Light yellow sand
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0xFFD54F, 0.5); // Darker sand
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 4);
+ }
+ for (let i = 0; i < 10; i++) {
+ graphics.fillStyle(0xFFF59D, 0.6); // Light sand
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_tropical', size, size);
+
+ // RADIOACTIVE - NEON GREEN
+ graphics.clear();
+ graphics.fillStyle(0x39FF14, 1.0); // Neon green
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0x00FF00, 0.8); // Bright green glow
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3 + Math.random() * 5);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0x76FF03, 0.6); // Light green spots
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_radioactive', size, size);
+
+ // ===== NEW BIOMES - ANOMALOUS (9) =====
+
+ // DINO VALLEY - OLIVE GREEN
+ graphics.clear();
+ graphics.fillStyle(0x6B8E23, 1.0); // Olive drab
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 25; i++) {
+ graphics.fillStyle(0x556B2F, 0.6); // Dark olive
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0x9ACD32, 0.5); // Yellow green
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_dino_valley', size, size);
+
+ // MYTHICAL HIGHLANDS - PURPLE
+ graphics.clear();
+ graphics.fillStyle(0xB39DDB, 1.0); // Light purple
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0x9575CD, 0.6); // Medium purple
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 4);
+ }
+ for (let i = 0; i < 10; i++) {
+ graphics.fillStyle(0xE1BEE7, 0.7); // Light pink/purple
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_mythical', size, size);
+
+ // ENDLESS FOREST - VERY DARK GREEN
+ graphics.clear();
+ graphics.fillStyle(0x1B5E20, 1.0); // Very dark green
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 30; i++) {
+ graphics.fillStyle(0x104010, 0.7); // Almost black green
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0x2E7D32, 0.5); // Slightly lighter green
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_endless_forest', size, size);
+
+ // LOCH NESS - BLUE GRAY
+ graphics.clear();
+ graphics.fillStyle(0x546E7A, 1.0); // Blue gray
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0x455A64, 0.6); // Darker blue gray
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 4);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0x607D8B, 0.5); // Lighter blue gray
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_loch_ness', size, size);
+
+ // CATACOMBS - DARK BROWN
+ graphics.clear();
+ graphics.fillStyle(0x3E2723, 1.0); // Very dark brown
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 25; i++) {
+ graphics.fillStyle(0x1B0000, 0.7); // Almost black
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3);
+ }
+ for (let i = 0; i < 10; i++) {
+ graphics.fillStyle(0x6D4C41, 0.5); // Medium brown
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_catacombs', size, size);
+
+ // EGYPTIAN DESERT - BRIGHT YELLOW
+ graphics.clear();
+ graphics.fillStyle(0xFFD54F, 1.0); // Bright yellow
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 25; i++) {
+ graphics.fillStyle(0xFFCA28, 0.6); // Amber yellow
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 4);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0xFFE082, 0.5); // Light yellow
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_egyptian_desert', size, size);
+
+ // AMAZON RAINFOREST - DARK GREEN (jungle)
+ graphics.clear();
+ graphics.fillStyle(0x1B5E20, 1.0); // Dark green
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 30; i++) {
+ graphics.fillStyle(0x2E7D32, 0.7); // Medium green
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3);
+ }
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0x43A047, 0.6); // Lighter green
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_amazon', size, size);
+
+ // ATLANTIS - CYAN/BLUE
+ graphics.clear();
+ graphics.fillStyle(0x00BCD4, 1.0); // Cyan
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 25; i++) {
+ graphics.fillStyle(0x0097A7, 0.7); // Dark cyan
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 4);
+ }
+ for (let i = 0; i < 20; i++) {
+ graphics.fillStyle(0x4DD0E1, 0.6); // Light cyan
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ // Bubbles
+ for (let i = 0; i < 10; i++) {
+ graphics.fillStyle(0xFFFFFF, 0.4);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 1);
+ }
+ graphics.generateTexture('tile2d_atlantis', size, size);
+
+ // CHERNOBYL - GRAY (ruined city)
+ graphics.clear();
+ graphics.fillStyle(0x757575, 1.0); // Medium gray
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 30; i++) {
+ graphics.fillStyle(0x616161, 0.7); // Dark gray
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 4);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0x424242, 0.6); // Very dark gray
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3);
+ }
+ // Radioactive glow spots
+ for (let i = 0; i < 5; i++) {
+ graphics.fillStyle(0x39FF14, 0.3); // Green radiation
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_chernobyl', size, size);
+
+ // GRASS WITH FLOWERS (VIBRANT!)
+ graphics.clear();
+ graphics.fillStyle(0x3CB371, 1.0);
+ graphics.fillRect(0, 0, size, size);
+
+ for (let i = 0; i < 12; i++) {
+ graphics.fillStyle(0x2E8B57, 0.3);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+
+ // BRIGHT colorful flowers!
+ const flowerColors = [0xFF1493, 0xFFD700, 0x00BFFF, 0xFF69B4];
+ for (let i = 0; i < 4; i++) {
+ graphics.fillStyle(flowerColors[Math.floor(Math.random() * 4)], 1.0);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3);
+ }
+ graphics.generateTexture('tile2d_grass_flowers', size, size);
+
+ // DIRT - RICH BROWN! ๐ค
+ graphics.clear();
+ graphics.fillStyle(0x8B4513, 1.0); // Saddle brown
+ graphics.fillRect(0, 0, size, size);
+
+ for (let i = 0; i < 25; i++) {
+ graphics.fillStyle(0x654321, 0.5);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 3 + Math.random() * 5);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0xA0826D, 0.4);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ graphics.generateTexture('tile2d_dirt', size, size);
+
+ graphics.clear();
+ graphics.fillStyle(0x8b4513, 1.0);
+ graphics.fillRect(0, 0, size, size);
+ graphics.fillStyle(0x3CB371, 0.3);
+ graphics.fillRect(0, 0, size, size);
+ graphics.generateTexture('tile2d_dirt_edge', size, size);
+
+ // WATER - BRIGHT TEAL! ๐ง (WITH OUTLINE!)
+ graphics.clear();
+ graphics.fillStyle(0x20B2AA, 1.0); // Light sea green - vibrant teal!
+ graphics.fillRect(0, 0, size, size);
+
+ // Dark outline border (2D style!)
+ graphics.lineStyle(2, 0x006994, 0.8);
+ graphics.strokeRect(1, 1, size - 2, size - 2);
+
+ for (let i = 0; i < 10; i++) {
+ graphics.fillStyle(0x008B8B, 0.4);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 5 + Math.random() * 8);
+ }
+ for (let i = 0; i < 15; i++) {
+ graphics.fillStyle(0x48D1CC, 0.5);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2);
+ }
+ for (let i = 0; i < 12; i++) {
+ graphics.fillStyle(0xFFFFFF, 0.6);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 1);
+ }
+ graphics.generateTexture('tile2d_water', size, size);
+
+ graphics.clear();
+ graphics.fillStyle(0x20B2AA, 1.0);
+ graphics.fillRect(0, 0, size, size);
+ graphics.fillStyle(0x48D1CC, 0.5);
+ graphics.fillRect(0, 0, size, size);
+ graphics.generateTexture('tile2d_water_edge', size, size);
+
+ } else {
+ console.warn('โ ๏ธ PNG Tilesets not loaded! Using fallback colors');
+ }
+
+ // STONE (FULLY OPAQUE!)
+ graphics.clear();
+ graphics.fillStyle(0x808080, 1.0); // 100% opacity!
+ graphics.fillRect(0, 0, size, size);
+ for (let i = 0; i < 12; i++) {
+ graphics.fillStyle(0x606060, 0.6);
+ graphics.fillCircle(Math.random() * size, Math.random() * size, 2 + Math.random() * 3);
+ }
+ graphics.generateTexture('tile2d_stone', size, size);
+
+ graphics.destroy();
+ this.texturesReady = true;
+ console.log('โ
Tile textures created - FULLY OPAQUE!');
+ }
+
+ extractTilesFromTileset(sourceKey, targetKey, tileX, tileY, tileSize) {
+ // Extract a single tile from tileset PNG and create new texture
+ if (!this.scene.textures.exists(sourceKey)) {
+ console.warn(`โ ๏ธ Tileset ${sourceKey} not found, using fallback`);
+ return;
+ }
+
+ const source = this.scene.textures.get(sourceKey).getSourceImage();
+ const canvas = document.createElement('canvas');
+ canvas.width = tileSize;
+ canvas.height = tileSize;
+ const ctx = canvas.getContext('2d');
+
+ // Draw specific tile from tileset
+ ctx.drawImage(
+ source,
+ tileX * tileSize, tileY * tileSize, // Source position
+ tileSize, tileSize, // Source size
+ 0, 0, // Dest position
+ tileSize, tileSize // Dest size
+ );
+
+ // Create new texture from extracted tile
+ this.scene.textures.addCanvas(targetKey, canvas);
+ }
+
+ renderMap() {
+ // ๐ PHASE 28: Skip if using biome system!
+ if (this.biomeSystem && this.chunkManager) {
+ console.log('โญ๏ธ Skipping renderMap() - using BiomeSystem chunk rendering instead');
+ return;
+ }
+
+ // ๐จ SIMPLE & CLEAN: Use TileSprite for seamless background!
+ const size = this.tileSize;
+ const mapWidth = this.width * size;
+ const mapHeight = this.height * size;
+
+ // ๐ SAFETY CHECK: Prevent Out of Memory for extreme dimensions
+ if (mapWidth > 16384 || mapHeight > 16384) {
+ console.error(`โ Map dimensions too large for TileSprite: ${mapWidth}x${mapHeight}. Capping at 8192.`);
+ // This is a safety cap to prevent browser crash
+ }
+
+ console.log(`๐จ Rendering seamless 2D map (${mapWidth}x${mapHeight})...`);
+
+ // Create solid grass background (NO TRANSPARENCY!)
+ // Use a smaller TileSprite if dimensions are huge
+ const bgWidth = Math.min(mapWidth, 8192);
+ const bgHeight = Math.min(mapHeight, 8192);
+
+ const grassBG = this.scene.add.tileSprite(0, 0, mapWidth, mapHeight, 'tile2d_grass');
+ grassBG.setOrigin(0, 0);
+ grassBG.setDepth(1);
+
+ // Create containers for paths and decorations
+ this.pathsLayer = this.scene.add.container(0, 0);
+ this.pathsLayer.setDepth(2);
+
+ this.decorLayer = this.scene.add.container(0, 0);
+ this.decorLayer.setDepth(3);
+
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+
+ // Array to store water sprites for animation
+ this.waterSprites = [];
+
+ // Render all tiles on top of grass background
+ for (let y = 0; y < this.height; y++) {
+ for (let x = 0; x < this.width; x++) {
+ const tile = this.tiles[y][x];
+ const worldX = x * size;
+ const worldY = y * size;
+
+ // WATER tiles - back to Image, animate with tint!
+ if (tile.base === 4 || tile.base === 5) {
+ const waterSprite = this.scene.add.image(worldX, worldY, 'tile2d_water');
+ waterSprite.setOrigin(0, 0);
+ waterSprite.setDisplaySize(size, size);
+ this.pathsLayer.add(waterSprite);
+ this.waterSprites.push(waterSprite); // Store for animation!
+ }
+
+ // DIRT PATH tiles
+ else if (tile.base === 2 || tile.base === 3) {
+ const dirtSprite = this.scene.add.image(worldX, worldY, 'tile2d_dirt');
+ dirtSprite.setOrigin(0, 0);
+ dirtSprite.setDisplaySize(size, size);
+
+ // Edge blending
+ if (tile.base === 3) {
+ dirtSprite.setAlpha(0.8); // Blend with grass
+ }
+
+ this.pathsLayer.add(dirtSprite);
+ }
+
+ // DECORATIONS
+ if (tile.decoration) {
+ this.addDecoration(x, y, tile.decoration);
+ }
+ }
+ }
+
+ graphics.destroy();
+
+ // Store reference
+ this.groundLayer = grassBG;
+
+ console.log('โ
Seamless map rendered (NO GRID!)');
+ console.log(`๐ Water tiles animated: ${this.waterSprites.length}`);
+ }
+
+ // ๐ PHASE 28: Render a single chunk with biome support
+ renderChunk(chunk) {
+ if (this.tiledMap) return; // Skip chunk rendering if using Tiled map
+ if (!chunk || !this.biomeSystem) {
+ console.warn('โ ๏ธ Cannot render chunk: missing data or biomeSystem');
+ return;
+ }
+
+ console.log(`๐จ renderChunk START: (${chunk.chunkX}, ${chunk.chunkY})`);
+
+ const size = this.tileSize;
+ let tilesRendered = 0;
+ const biomeCounts = {};
+
+ // Render ground tiles based on biome
+ for (const tileData of chunk.tiles) {
+ const { x, y, biome } = tileData;
+ const worldX = x * size;
+ const worldY = y * size;
+
+ // Count biomes
+ biomeCounts[biome] = (biomeCounts[biome] || 0) + 1;
+
+ // ๐ PHASE 28: Use TransitionSystem for smooth color blending
+ let tileColor = null;
+
+ if (this.transitionSystem) {
+ // Get blended color from transition system
+ tileColor = this.transitionSystem.getBlendedTileColor(x, y);
+ }
+
+ // Get biome-specific tile texture
+ let tileTexture = 'tile2d_grass'; // Default
+
+ if (biome === 'forest') {
+ tileTexture = 'tile2d_forest';
+ } else if (biome === 'desert') {
+ tileTexture = 'tile2d_desert';
+ } else if (biome === 'mountain') {
+ tileTexture = 'tile2d_mountain';
+ } else if (biome === 'swamp') {
+ tileTexture = 'tile2d_swamp';
+ }
+ // NEW BIOMES - NORMAL (4)
+ else if (biome === 'snow') {
+ tileTexture = 'tile2d_snow';
+ } else if (biome === 'wasteland') {
+ tileTexture = 'tile2d_wasteland';
+ } else if (biome === 'tropical') {
+ tileTexture = 'tile2d_tropical';
+ } else if (biome === 'radioactive') {
+ tileTexture = 'tile2d_radioactive';
+ }
+ // NEW BIOMES - ANOMALOUS (9)
+ else if (biome === 'dino_valley') {
+ tileTexture = 'tile2d_dino_valley';
+ } else if (biome === 'mythical') {
+ tileTexture = 'tile2d_mythical';
+ } else if (biome === 'endless_forest') {
+ tileTexture = 'tile2d_endless_forest';
+ } else if (biome === 'loch_ness') {
+ tileTexture = 'tile2d_loch_ness';
+ } else if (biome === 'catacombs') {
+ tileTexture = 'tile2d_catacombs';
+ } else if (biome === 'egyptian_desert') {
+ tileTexture = 'tile2d_egyptian_desert';
+ } else if (biome === 'amazon') {
+ tileTexture = 'tile2d_amazon';
+ } else if (biome === 'atlantis') {
+ tileTexture = 'tile2d_atlantis';
+ } else if (biome === 'chernobyl') {
+ tileTexture = 'tile2d_chernobyl';
+ }
+
+ // Create tile sprite
+ const tileSprite = this.scene.add.image(worldX, worldY, tileTexture);
+ tileSprite.setOrigin(0, 0);
+ tileSprite.setDisplaySize(size, size);
+ tileSprite.setDepth(1);
+
+ // ๐ Apply blended tint if in transition zone
+ if (tileColor !== null) {
+ tileSprite.setTint(tileColor);
+ }
+
+ // Store in chunk for cleanup
+ if (!chunk.sprites) chunk.sprites = [];
+ chunk.sprites.push(tileSprite);
+
+ tilesRendered++;
+
+ // ๐ PHASE 28 SESSION 5: Check for water (rivers & lakes)
+ let isWater = false;
+
+ // Rivers
+ if (this.riverSystem && this.riverSystem.isRiver(x, y)) {
+ const riverColor = this.riverSystem.getRiverColor(x, y);
+ const waterRect = this.scene.add.rectangle(worldX, worldY, size, size, riverColor, 0.75);
+ waterRect.setOrigin(0, 0);
+ waterRect.setDepth(2);
+ chunk.sprites.push(waterRect);
+ isWater = true;
+ }
+
+ // Lakes (only if not already river)
+ if (!isWater && this.lakeSystem && this.lakeSystem.isLake(x, y)) {
+ const lakeColor = this.lakeSystem.getLakeColor(x, y);
+ const waterRect = this.scene.add.rectangle(worldX, worldY, size, size, lakeColor, 0.75);
+ waterRect.setOrigin(0, 0);
+ waterRect.setDepth(2);
+ chunk.sprites.push(waterRect);
+ isWater = true;
+ }
+
+ // Skip features if water tile
+ if (isWater) {
+ continue; // Skip to next tile
+ }
+
+ // ๐๏ธ PHASE 28 SESSION 6: Check for ROADS
+ if (this.structureSystem && this.structureSystem.isRoad(x, y)) {
+ // Get biome-specific road color
+ const baseColor = (biome === 'desert') ? 0xcda869 :
+ (biome === 'mountain') ? 0x9090a0 :
+ (biome === 'swamp') ? 0x5a4a3d :
+ 0x8B7355; // Brown dirt road
+
+ const roadRect = this.scene.add.rectangle(worldX, worldY, size, size, baseColor, 1.0);
+ roadRect.setOrigin(0, 0);
+ roadRect.setDepth(1.5); // Above ground, below decorations
+ chunk.sprites.push(roadRect);
+
+ // Add some variation to road texture
+ if (Math.random() < 0.3) {
+ const detail = this.scene.add.rectangle(
+ worldX + Math.random() * size,
+ worldY + Math.random() * size,
+ size * 0.3,
+ size * 0.3,
+ baseColor - 0x202020,
+ 0.5
+ );
+ detail.setOrigin(0, 0);
+ detail.setDepth(1.5);
+ chunk.sprites.push(detail);
+ }
+
+ // Roads block biome features
+ continue;
+ }
+
+ // ๐๏ธ PHASE 28 SESSION 6: Check for STRUCTURES
+ const structureData = this.structureSystem ? this.structureSystem.getStructure(x, y) : null;
+ if (structureData) {
+ // Structure exists here - render visual marker
+ if (structureData.type === 'landmark') {
+ // Landmark marker (large, special)
+ const landmarkMarker = this.scene.add.rectangle(worldX, worldY, size, size, 0xFFD700, 0.7);
+ landmarkMarker.setOrigin(0, 0);
+ landmarkMarker.setDepth(5);
+ chunk.sprites.push(landmarkMarker);
+
+ // Add a star symbol for landmarks (simple)
+ const star = this.scene.add.text(worldX + size / 2, worldY + size / 2, 'โ
', {
+ fontSize: '20px',
+ color: '#ffffff'
+ });
+ star.setOrigin(0.5, 0.5);
+ star.setDepth(6);
+ chunk.sprites.push(star);
+ } else if (structureData.type === 'structure') {
+ // Regular structure marker
+ const structureColor = this.getStructureColor(structureData.structureType);
+ const structureMarker = this.scene.add.rectangle(worldX, worldY, size, size, structureColor, 0.8);
+ structureMarker.setOrigin(0, 0);
+ structureMarker.setDepth(4);
+ chunk.sprites.push(structureMarker);
+ }
+
+ // Structures block biome features
+ continue;
+ }
+
+ // ๐ Apply mixed features for transitions
+ let features = [];
+
+ if (this.transitionSystem) {
+ features = this.transitionSystem.getMixedFeatures(x, y);
+ } else if (this.biomeSystem) {
+ features = this.biomeSystem.applyBiomeFeatures(x, y);
+ }
+
+ features.forEach(feature => {
+ this.addBiomeFeature(x, y, feature, chunk);
+ });
+ }
+
+ console.log(`โ
renderChunk END: ${tilesRendered} tiles rendered`);
+ console.log(`๐ Biomes in chunk:`, biomeCounts);
+
+ console.log(`โ
Chunk (${chunk.chunkX}, ${chunk.chunkY}) rendered with biomes`);
+
+ // ๐ DEBUG: Add visible border around chunk
+ if (this.scene.add) {
+ const chunkWorldX = chunk.chunkX * (this.chunkSize * size);
+ const chunkWorldY = chunk.chunkY * (this.chunkSize * size);
+ const chunkPixelSize = this.chunkSize * size;
+
+ const border = this.scene.add.rectangle(
+ chunkWorldX,
+ chunkWorldY,
+ chunkPixelSize,
+ chunkPixelSize
+ );
+ border.setOrigin(0, 0);
+ border.setStrokeStyle(2, 0xFF0000, 0.5); // Red semi-transparent border
+ border.setDepth(100); // High depth to see it
+ border.setFillStyle(0x000000, 0); // No fill
+
+ if (!chunk.sprites) chunk.sprites = [];
+ chunk.sprites.push(border);
+
+ console.log(`๐ฒ Debug border added at (${chunkWorldX}, ${chunkWorldY})`);
+ }
+ }
+
+ // Add biome-specific feature
+ addBiomeFeature(gridX, gridY, feature, chunk) {
+ const size = this.tileSize;
+ const worldX = gridX * size + size / 2;
+ const worldY = gridY * size + size / 2;
+
+ let sprite;
+
+ switch (feature.type) {
+ case 'tree':
+ sprite = this.createTree(worldX, worldY);
+ break;
+ case 'rock':
+ sprite = this.createRock(worldX, worldY, feature.size);
+ break;
+ case 'bush':
+ sprite = this.createBush(worldX, worldY);
+ break;
+ case 'cactus':
+ sprite = this.createCactus(worldX, worldY);
+ break;
+ case 'deadTree':
+ sprite = this.createDeadTree(worldX, worldY);
+ break;
+ case 'boulder':
+ sprite = this.createBoulder(worldX, worldY);
+ break;
+ case 'mushroom':
+ sprite = this.createMushroom(worldX, worldY);
+ break;
+ case 'vine':
+ sprite = this.createVine(worldX, worldY);
+ break;
+ }
+
+ if (sprite && chunk) {
+ sprite.setDepth(10 + worldY);
+ if (!chunk.sprites) chunk.sprites = [];
+ chunk.sprites.push(sprite);
+ }
+ }
+
+ // Simple rock creation
+ createRock(x, y, size = 'small') {
+ const graphics = this.scene.add.graphics();
+ const rockSize = size === 'large' ? 15 : 8;
+
+ graphics.fillStyle(0x808080);
+ graphics.fillCircle(x, y, rockSize);
+ graphics.fillStyle(0x606060, 0.6);
+ graphics.fillCircle(x - 3, y - 3, rockSize * 0.6);
+
+ return graphics;
+ }
+
+ // Boulder (large rock)
+ createBoulder(x, y) {
+ const graphics = this.scene.add.graphics();
+
+ graphics.fillStyle(0x707070);
+ graphics.fillCircle(x, y, 20);
+ graphics.fillStyle(0x505050);
+ graphics.fillCircle(x - 5, y - 5, 12);
+ graphics.fillStyle(0x909090, 0.5);
+ graphics.fillCircle(x + 5, y + 5, 8);
+
+ return graphics;
+ }
+
+ // Cactus
+ createCactus(x, y) {
+ const graphics = this.scene.add.graphics();
+
+ // Main body
+ graphics.fillStyle(0x4a7c59);
+ graphics.fillRect(x - 5, y - 15, 10, 30);
+
+ // Arms
+ graphics.fillRect(x - 12, y - 8, 7, 10);
+ graphics.fillRect(x + 5, y - 5, 7, 10);
+
+ // Highlights
+ graphics.fillStyle(0x5a8c69, 0.6);
+ graphics.fillRect(x - 3, y - 12, 3, 24);
+
+ return graphics;
+ }
+
+ // Mushroom
+ createMushroom(x, y) {
+ const graphics = this.scene.add.graphics();
+
+ // Stem
+ graphics.fillStyle(0xeeeeee);
+ graphics.fillRect(x - 2, y, 4, 8);
+
+ // Cap
+ graphics.fillStyle(0xd63031);
+ graphics.fillCircle(x, y, 6);
+
+ // Spots
+ graphics.fillStyle(0xffffff);
+ graphics.fillCircle(x - 3, y - 1, 1.5);
+ graphics.fillCircle(x + 2, y + 1, 1.2);
+
+ return graphics;
+ }
+
+ // Vine (swamp feature)
+ createVine(x, y) {
+ const graphics = this.scene.add.graphics();
+
+ graphics.lineStyle(2, 0x2d4a2d);
+ graphics.beginPath();
+ graphics.moveTo(x, y - 10);
+ graphics.bezierCurveTo(
+ x + 5, y - 5,
+ x - 5, y + 5,
+ x, y + 10
+ );
+ graphics.strokePath();
+
+ return graphics;
+ }
+
+ // ๐๏ธ PHASE 28 SESSION 6: Get color for structure type
+ getStructureColor(structureType) {
+ const colors = {
+ // Grassland structures
+ 'farm': 0x8B4513,
+ 'house': 0xA0522D,
+ 'barn': 0x654321,
+ 'windmill': 0xD2691E,
+ 'well': 0x708090,
+
+ // Forest structures
+ 'cabin': 0x8B4513,
+ 'ruins': 0x696969,
+ 'treehouse': 0x8B7355,
+ 'camp': 0x8B4513,
+ 'shrine': 0x9370DB,
+
+ // Desert structures
+ 'pyramid': 0xDAA520,
+ 'oasis_camp': 0x8B7355,
+ 'tomb': 0xCD853F,
+ 'pillar': 0xD2B48C,
+
+ // Mountain structures
+ 'mine': 0x2F4F4F,
+ 'cave': 0x363636,
+ 'tower': 0x708090,
+ 'altar': 0x9370DB,
+
+ // Swamp structures
+ 'hut': 0x556B2F,
+ 'totem': 0x8B4513,
+ 'bog_shrine': 0x6B8E23,
+ 'abandoned_dock': 0x654321
+ };
+
+ return colors[structureType] || 0x808080; // Default gray
+ }
+
+ getTileTexture(tileType) {
+ const types = Map2DData.tileTypes;
+
+ switch (tileType) {
+ case types.GRASS: return 'tile2d_grass';
+ case types.GRASS_FLOWERS: return 'tile2d_grass_flowers';
+ case types.DIRT: return 'tile2d_dirt';
+ case types.DIRT_EDGE: return 'tile2d_dirt_edge';
+ case types.WATER: return 'tile2d_water';
+ case types.WATER_EDGE: return 'tile2d_water_edge';
+ case types.STONE: return 'tile2d_stone';
+ default: return 'tile2d_grass';
+ }
+ }
+
+ addDecoration(gridX, gridY, decorType) {
+ const size = this.tileSize;
+ const worldX = gridX * size + size / 2;
+ const worldY = gridY * size + size / 2;
+
+ const types = Map2DData.tileTypes;
+
+ let sprite;
+
+ switch (decorType) {
+ case types.TREE:
+ sprite = this.createTree(worldX, worldY);
+ break;
+ case types.FLOWER_RED:
+ sprite = this.createFlower(worldX, worldY, 0xff6b6b);
+ break;
+ case types.FLOWER_YELLOW:
+ sprite = this.createFlower(worldX, worldY, 0xffd93d);
+ break;
+ case types.FLOWER_BLUE:
+ sprite = this.createFlower(worldX, worldY, 0x6bcbff);
+ break;
+ case types.LILY_PAD:
+ sprite = this.createLilyPad(worldX, worldY);
+ break;
+ case types.BUSH:
+ sprite = this.createBush(worldX, worldY);
+ break;
+ case 'puddle':
+ sprite = this.createPuddle(worldX, worldY);
+ break;
+ }
+
+ if (sprite) {
+ // ๐จ 2.5D DEPTH SORTING - Y position = depth!
+ // Objects lower on screen appear in front
+ sprite.setDepth(10 + worldY);
+ this.decorLayer.add(sprite);
+ }
+ }
+
+ createTree(x, y) {
+ // ๐ณ TREE VARIETY SYSTEM!
+ const treeTypes = ['cherry', 'oak', 'pine', 'dead', 'apple'];
+ const randomType = Phaser.Utils.Array.GetRandom(treeTypes);
+
+ switch (randomType) {
+ case 'oak': return this.createOakTree(x, y);
+ case 'pine': return this.createPineTree(x, y);
+ case 'dead': return this.createDeadTree(x, y);
+ case 'apple': return this.createAppleTree(x, y);
+ default: return this.createCherryTree(x, y);
+ }
+ }
+
+ createCherryTree(x, y) {
+ // ๐ธ PNG SPRITE!
+ if (this.scene.textures.exists('tree_cherry')) {
+ const tree = this.scene.add.image(x, y, 'tree_cherry');
+ const scale = Phaser.Math.FloatBetween(0.25, 0.4); // Even SMALLER! (was 0.8-1.2)
+ tree.setScale(scale);
+ tree.setOrigin(0.5, 0.85);
+
+ // Shadow
+ const shadow = this.scene.add.ellipse(x, y + 15, 30 * scale, 10, 0x000000, 0.2);
+ shadow.setDepth(tree.depth - 1);
+
+ return tree;
+ }
+
+ // FALLBACK: Procedural
+ return this.createProceduralCherryTree(x, y);
+ }
+
+ createProceduralCherryTree(x, y) {
+ // Old procedural code (backup)
+ const graphics = this.scene.add.graphics();
+ const growthScale = Phaser.Math.FloatBetween(0.8, 1.4); // BIGGER!
+
+ const heightOffset = -20 * growthScale;
+ const crownSize = 30 * growthScale; // MUCH BIGGER!
+
+ // Shadow (ellipse on ground)
+ graphics.fillStyle(0x000000, 0.2);
+ graphics.fillEllipse(x, y + 30, 40 * growthScale, 12); // Bigger shadow
+
+ // Trunk (with shading for volume)
+ const trunkW = 12 * growthScale; // Thicker
+ const trunkH = 30 * growthScale; // Taller
+
+ // Dark side (right)
+ graphics.fillStyle(0x6D3F1A);
+ graphics.fillRect(x + trunkW / 4, y + 10 + heightOffset, trunkW / 2, trunkH);
+
+ // Light side (left)
+ graphics.fillStyle(0x8B5A2B);
+ graphics.fillRect(x - trunkW / 2, y + 10 + heightOffset, trunkW / 2, trunkH);
+
+ // CROWN - Layered circles for 2.5D!
+ const crownY = y - 10 * growthScale + heightOffset; // Higher crown
+
+ // Dark base (shadow underneath)
+ graphics.fillStyle(0xE75480);
+ graphics.fillCircle(x + 4 * growthScale, crownY + 4 * growthScale, crownSize);
+
+ // Main pink crown
+ graphics.fillStyle(0xFF69B4);
+ graphics.fillCircle(x, crownY, crownSize);
+
+ // Top highlight (light from top-left)
+ graphics.fillStyle(0xFFB6C1, 0.8);
+ graphics.fillCircle(x - 6 * growthScale, crownY - 6 * growthScale, crownSize * 0.6);
+
+ // Bright specular spot
+ graphics.fillStyle(0xFFFFFF, 0.5);
+ graphics.fillCircle(x - 8 * growthScale, crownY - 8 * growthScale, crownSize * 0.3);
+
+ return graphics;
+ }
+
+ createOakTree(x, y) {
+ // ๐ฒ PNG SPRITE!
+ if (this.scene.textures.exists('tree_oak')) {
+ const tree = this.scene.add.image(x, y, 'tree_oak');
+ const scale = Phaser.Math.FloatBetween(0.28, 0.42); // Even SMALLER!
+ tree.setScale(scale);
+ tree.setOrigin(0.5, 0.85);
+ const shadow = this.scene.add.ellipse(x, y + 15, 35 * scale, 12, 0x000000, 0.2);
+ shadow.setDepth(tree.depth - 1);
+ return tree;
+ }
+
+ // FALLBACK:
+ const graphics = this.scene.add.graphics();
+ const growthScale = Phaser.Math.FloatBetween(0.8, 1.4); // BIGGER!
+
+ // Shadow
+ graphics.fillStyle(0x000000, 0.15);
+ graphics.fillEllipse(x, y + 24, 25 * growthScale, 8);
+
+ // Thick trunk
+ graphics.fillStyle(0x654321);
+ graphics.fillRect(x - 5 * growthScale, y, 10 * growthScale, 24 * growthScale);
+
+ // Large round crown
+ graphics.fillStyle(0x228B22); // Forest green
+ graphics.fillCircle(x, y - 10 * growthScale, 18 * growthScale);
+
+ // Darker outline
+ graphics.lineStyle(2, 0x006400);
+ graphics.strokeCircle(x, y - 10 * growthScale, 18 * growthScale);
+
+ // Highlight
+ graphics.fillStyle(0x32CD32, 0.6);
+ graphics.fillCircle(x - 5 * growthScale, y - 15 * growthScale, 8 * growthScale);
+
+ return graphics;
+ }
+
+ createPineTree(x, y) {
+ // ๐ฒ PNG SPRITE!
+ if (this.scene.textures.exists('tree_pine')) {
+ const tree = this.scene.add.image(x, y, 'tree_pine');
+ const scale = Phaser.Math.FloatBetween(0.28, 0.45); // Even SMALLER!
+ tree.setScale(scale);
+ tree.setOrigin(0.5, 0.9); // Taller
+ const shadow = this.scene.add.ellipse(x, y + 20, 30 * scale, 10, 0x000000, 0.2);
+ shadow.setDepth(tree.depth - 1);
+ return tree;
+ }
+
+ // FALLBACK:
+ const graphics = this.scene.add.graphics();
+ const growthScale = Phaser.Math.FloatBetween(0.8, 1.5); // BIGGER (Pine taller)
+
+ // Shadow
+ graphics.fillStyle(0x000000, 0.15);
+ graphics.fillEllipse(x, y + 20, 15 * growthScale, 6);
+
+ // Trunk
+ graphics.fillStyle(0x8B4513);
+ graphics.fillRect(x - 3 * growthScale, y + 5, 6 * growthScale, 15 * growthScale);
+
+ // Stacked triangles (pine shape)
+ graphics.fillStyle(0x2F4F2F); // Dark green
+
+ // Bottom tier
+ graphics.fillTriangle(
+ x, y - 5 * growthScale,
+ x - 14 * growthScale, y + 10,
+ x + 14 * growthScale, y + 10
+ );
+
+ // Middle tier
+ graphics.fillTriangle(
+ x, y - 15 * growthScale,
+ x - 10 * growthScale, y,
+ x + 10 * growthScale, y
+ );
+
+ // Top tier
+ graphics.fillTriangle(
+ x, y - 25 * growthScale,
+ x - 7 * growthScale, y - 10 * growthScale,
+ x + 7 * growthScale, y - 10 * growthScale
+ );
+
+ return graphics;
+ }
+
+ createDeadTree(x, y) {
+ // ๐ PNG SPRITE!
+ if (this.scene.textures.exists('tree_dead')) {
+ const tree = this.scene.add.image(x, y, 'tree_dead');
+ const scale = Phaser.Math.FloatBetween(0.35, 0.55); // SMALLER!
+ tree.setScale(scale);
+ tree.setOrigin(0.5, 0.85);
+ const shadow = this.scene.add.ellipse(x, y + 18, 28 * scale, 10, 0x000000, 0.3);
+ shadow.setDepth(tree.depth - 1);
+ return tree;
+ }
+
+ // FALLBACK:
+ const graphics = this.scene.add.graphics();
+ const growthScale = Phaser.Math.FloatBetween(0.7, 1.2); // BIGGER!
+
+ // Shadow
+ graphics.fillStyle(0x000000, 0.2);
+ graphics.fillEllipse(x, y + 20, 18 * growthScale, 6);
+
+ // Dark trunk
+ graphics.fillStyle(0x3D3D3D);
+ graphics.fillRect(x - 4 * growthScale, y, 8 * growthScale, 22 * growthScale);
+
+ // Branches (bare)
+ graphics.lineStyle(3 * growthScale, 0x2D2D2D);
+ graphics.beginPath();
+ graphics.moveTo(x, y - 5 * growthScale);
+ graphics.lineTo(x - 10 * growthScale, y - 15 * growthScale);
+ graphics.moveTo(x, y - 8 * growthScale);
+ graphics.lineTo(x + 12 * growthScale, y - 12 * growthScale);
+ graphics.strokePath();
+
+ return graphics;
+ }
+
+ createAppleTree(x, y) {
+ // ๐ 2D FLAT STYLE - Smaller and simpler!
+ const graphics = this.scene.add.graphics();
+ const scale = 0.6; // Much smaller!
+
+ // Shadow
+ graphics.fillStyle(0x000000, 0.15);
+ graphics.fillEllipse(x, y + 14, 18 * scale, 6);
+
+ // Trunk (simple rectangle)
+ graphics.fillStyle(0x6d4c41); // Brown
+ graphics.fillRect(x - 3 * scale, y - 2, 6 * scale, 16 * scale);
+
+ // Crown - simple rounded shape (2D style!)
+ graphics.fillStyle(0x4a9d5f); // Green
+ graphics.fillCircle(x, y - 10 * scale, 12 * scale);
+
+ // Secondary crown circles for volume (Stardew style)
+ graphics.fillCircle(x - 6 * scale, y - 8 * scale, 9 * scale);
+ graphics.fillCircle(x + 6 * scale, y - 8 * scale, 9 * scale);
+
+ // Apples (simple red dots)
+ graphics.fillStyle(0xFF3333);
+ const apples = [
+ { x: -5, y: -12 }, { x: 4, y: -10 },
+ { x: 0, y: -8 }, { x: 6, y: -9 }
+ ];
+ apples.forEach(pos => {
+ graphics.fillCircle(
+ x + pos.x * scale,
+ y + pos.y * scale,
+ 2.5 * scale
+ );
+ });
+
+ return graphics;
+ }
+
+ createFlower(x, y, color) {
+ const graphics = this.scene.add.graphics();
+
+ // Petals
+ graphics.fillStyle(color);
+ for (let i = 0; i < 5; i++) {
+ const angle = (Math.PI * 2 * i) / 5;
+ const px = x + Math.cos(angle) * 3;
+ const py = y + Math.sin(angle) * 3;
+ graphics.fillCircle(px, py, 2);
+ }
+
+ // Center
+ graphics.fillStyle(0xFFEB3B);
+ graphics.fillCircle(x, y, 2);
+
+ return graphics;
+ }
+
+ createLilyPad(x, y) {
+ const graphics = this.scene.add.graphics();
+
+ // Lily pad (green circle)
+ graphics.fillStyle(0x4a8d2f);
+ graphics.fillCircle(x, y, 8);
+
+ // Pink flower
+ graphics.fillStyle(0xFF69B4);
+ for (let i = 0; i < 5; i++) {
+ const angle = (Math.PI * 2 * i) / 5;
+ const px = x + Math.cos(angle) * 3;
+ const py = y + Math.sin(angle) * 3;
+ graphics.fillCircle(px, py, 2);
+ }
+ graphics.fillStyle(0xFFD700);
+ graphics.fillCircle(x, y, 1.5);
+
+ return graphics;
+ }
+
+ createBush(x, y) {
+ const graphics = this.scene.add.graphics();
+
+ graphics.fillStyle(0x3a6b1f, 0.9);
+ graphics.fillCircle(x, y, 10);
+ graphics.fillCircle(x - 6, y + 2, 8);
+ graphics.fillCircle(x + 6, y + 2, 8);
+
+ graphics.fillStyle(0x4a8d2f, 0.7);
+ graphics.fillCircle(x, y - 3, 6);
+
+ return graphics;
+ }
+
+ createPuddle(x, y) {
+ // Use existing puddle sprite if available
+ if (this.scene.textures.exists('luza_sprite')) {
+ const sprite = this.scene.add.image(x, y, 'luza_sprite');
+ sprite.setScale(0.3); // SMALL puddles!
+ sprite.setAlpha(0.3); // More transparent
+ return sprite;
+ }
+
+ // Fallback - small ellipse
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0x4488bb, 0.4);
+ graphics.fillEllipse(x, y, 8, 5); // Smaller!
+ return graphics;
+ }
+
+ createFallbackMap() {
+ // Create simple fallback if Map2DData fails
+ for (let y = 0; y < this.height; y++) {
+ this.tiles[y] = [];
+ for (let x = 0; x < this.width; x++) {
+ this.tiles[y][x] = {
+ base: 0, // Grass
+ decoration: null,
+ walkable: true
+ };
+ }
+ }
+ }
+
+ getTile(x, y) {
+ if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
+ return null;
+ }
+ // Safety check: ensure tiles array is initialized
+ if (!this.tiles || !this.tiles[y]) {
+ return null;
+ }
+ return this.tiles[y][x];
+ }
+
+ isWalkable(x, y) {
+ const tile = this.getTile(x, y);
+ return tile ? tile.walkable : false;
+ }
+
+ update(time, delta) {
+ // ๐ ANIMATED WATER - shimmer effect with tint!
+ if (this.waterSprites && this.waterSprites.length > 0) {
+ this.waterSprites.forEach(sprite => {
+ if (sprite && sprite.active) {
+ // Wave shimmer using tint color
+ const wave = Math.sin(time * 0.002) * 0.15 + 0.85;
+ const tintValue = Math.floor(wave * 255);
+ sprite.setTint(
+ 0x2A7FBC | (tintValue << 16) | (tintValue << 8) | tintValue
+ );
+
+ // Subtle alpha pulsing
+ sprite.setAlpha(0.95 + Math.sin(time * 0.001) * 0.05);
+ }
+ });
+ }
+ }
+
+ destroy() {
+ if (this.groundLayer) this.groundLayer.destroy();
+ if (this.pathsLayer) this.pathsLayer.destroy();
+ if (this.decorLayer) this.decorLayer.destroy();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/FogOfWarSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/FogOfWarSystem.js
new file mode 100644
index 000000000..09e34290e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/FogOfWarSystem.js
@@ -0,0 +1,473 @@
+/**
+ * FOG OF WAR SYSTEM
+ * Exploration and visibility system for unexplored areas
+ */
+class FogOfWarSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Fog grid (matches terrain grid)
+ this.gridWidth = 100;
+ this.gridHeight = 100;
+ this.tileSize = 64;
+
+ // Fog states: 0 = unexplored, 1 = explored, 2 = visible
+ this.fogGrid = [];
+ this.fogSprites = new Map();
+
+ // Settings
+ this.settings = {
+ enabled: false, // DISABLED - use weather fog instead
+ fogColor: 0x000000,
+ fogAlpha: 0.15, // Very light animated fog
+ exploredAlpha: 0.05, // Almost invisible for explored areas
+ visibleRadius: 8, // tiles (increased more)
+ smoothEdges: true,
+ persistMemory: true,
+ refogDungeons: true
+ };
+
+ // Fog layer
+ this.fogLayer = null;
+ this.fogGraphics = null;
+
+ // Memory of explored areas
+ this.exploredAreas = new Set();
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Fog of War System initialized');
+ }
+
+ init() {
+ if (!this.settings.enabled) return;
+
+ // Initialize fog grid
+ this.initFogGrid();
+
+ // Create fog layer
+ this.createFogLayer();
+
+ // Load explored areas from memory
+ this.loadExploredAreas();
+
+ console.log('๐ซ๏ธ Fog of War active');
+ }
+
+ /**
+ * Initialize fog grid
+ */
+ initFogGrid() {
+ for (let y = 0; y < this.gridHeight; y++) {
+ this.fogGrid[y] = [];
+ for (let x = 0; x < this.gridWidth; x++) {
+ this.fogGrid[y][x] = 0; // Unexplored
+ }
+ }
+ }
+
+ /**
+ * Create fog layer
+ */
+ createFogLayer() {
+ // Create layer for fog
+ this.fogLayer = this.scene.add.layer();
+ this.fogLayer.setDepth(9000); // Above game, below UI
+
+ // Create fog graphics
+ this.fogGraphics = this.scene.add.graphics();
+ this.fogGraphics.setDepth(9001);
+
+ // Initial full fog
+ this.renderFullFog();
+ }
+
+ /**
+ * Render full fog (all unexplored)
+ */
+ renderFullFog() {
+ if (!this.fogGraphics) return;
+
+ this.fogGraphics.clear();
+ this.fogGraphics.fillStyle(this.settings.fogColor, this.settings.fogAlpha);
+
+ // Cover entire map
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ this.fogGraphics.fillRect(
+ -width,
+ -height,
+ width * 3,
+ height * 3
+ );
+ }
+
+ /**
+ * Update fog based on player position
+ */
+ updateFog(playerX, playerY) {
+ if (!this.settings.enabled) return;
+
+ const gridX = Math.floor(playerX);
+ const gridY = Math.floor(playerY);
+
+ // Reveal area around player
+ this.revealArea(gridX, gridY, this.settings.visibleRadius);
+
+ // Update fog rendering
+ this.renderFog();
+ }
+
+ /**
+ * Reveal area around a point
+ */
+ revealArea(centerX, centerY, radius) {
+ const radiusSq = radius * radius;
+
+ for (let y = centerY - radius; y <= centerY + radius; y++) {
+ for (let x = centerX - radius; x <= centerX + radius; x++) {
+ // Check if within grid bounds
+ if (x < 0 || x >= this.gridWidth || y < 0 || y >= this.gridHeight) {
+ continue;
+ }
+
+ // Check if within circular radius
+ const dx = x - centerX;
+ const dy = y - centerY;
+ const distSq = dx * dx + dy * dy;
+
+ if (distSq <= radiusSq) {
+ // Mark as explored
+ if (this.fogGrid[y][x] === 0) {
+ this.fogGrid[y][x] = 1; // Explored
+ this.exploredAreas.add(`${x},${y}`);
+ }
+
+ // Mark as currently visible
+ this.fogGrid[y][x] = 2; // Visible
+ }
+ }
+ }
+
+ // Save explored areas
+ if (this.settings.persistMemory) {
+ this.saveExploredAreas();
+ }
+ }
+
+ /**
+ * Render fog based on current state - ANIMATED FOG EFFECT
+ */
+ renderFog() {
+ if (!this.fogGraphics) return;
+
+ this.fogGraphics.clear();
+
+ // Get camera bounds
+ const cam = this.scene.cameras.main;
+ const time = this.scene.time.now * 0.0005; // Slow animation
+
+ // Render fog as smooth animated overlay
+ for (let y = 0; y < this.gridHeight; y++) {
+ for (let x = 0; x < this.gridWidth; x++) {
+ const state = this.fogGrid[y][x];
+
+ // Skip if visible
+ if (state === 2) continue;
+
+ // Simple 2D tile coordinates
+ const screenX = x * this.tileSize;
+ const screenY = y * this.tileSize;
+
+ // Only render if on screen (with margin)
+ if (screenX < cam.scrollX - 100 || screenX > cam.scrollX + cam.width + 100) continue;
+ if (screenY < cam.scrollY - 100 || screenY > cam.scrollY + cam.height + 100) continue;
+
+ // Animated fog effect - wave pattern
+ const waveX = Math.sin(time + x * 0.1) * 5;
+ const waveY = Math.cos(time + y * 0.1) * 5;
+
+ if (state === 0) {
+ // Unexplored - animated fog particles
+ const alpha = this.settings.fogAlpha + Math.sin(time + x + y) * 0.05;
+ this.fogGraphics.fillStyle(this.settings.fogColor, alpha);
+
+ // Draw multiple overlapping circles for smooth fog
+ this.fogGraphics.fillCircle(
+ screenX + this.tileSize / 2 + waveX,
+ screenY + this.tileSize / 2 + waveY,
+ this.tileSize * 0.8
+ );
+ } else if (state === 1) {
+ // Explored but not visible - very light animated fog
+ const alpha = this.settings.exploredAlpha + Math.sin(time + x + y) * 0.02;
+ this.fogGraphics.fillStyle(this.settings.fogColor, alpha);
+
+ // Smaller, lighter circles
+ this.fogGraphics.fillCircle(
+ screenX + this.tileSize / 2 + waveX,
+ screenY + this.tileSize / 2 + waveY,
+ this.tileSize * 0.6
+ );
+ }
+ }
+ }
+
+ // Apply blur effect for smooth fog
+ if (this.settings.smoothEdges) {
+ this.fogGraphics.setBlendMode(Phaser.BlendModes.NORMAL);
+ }
+ }
+
+ /**
+ * Reset fog in area (for dungeons/caves)
+ */
+ refogArea(x, y, width, height) {
+ if (!this.settings.refogDungeons) return;
+
+ for (let gy = y; gy < y + height; gy++) {
+ for (let gx = x; gx < x + width; gx++) {
+ if (gx >= 0 && gx < this.gridWidth && gy >= 0 && gy < this.gridHeight) {
+ this.fogGrid[gy][gx] = 0; // Back to unexplored
+ this.exploredAreas.delete(`${gx},${gy}`);
+ }
+ }
+ }
+
+ this.renderFog();
+ }
+
+ /**
+ * Clear all fog (reveal entire map)
+ */
+ revealAll() {
+ for (let y = 0; y < this.gridHeight; y++) {
+ for (let x = 0; x < this.gridWidth; x++) {
+ this.fogGrid[y][x] = 2; // All visible
+ this.exploredAreas.add(`${x},${y}`);
+ }
+ }
+ this.renderFog();
+ }
+
+ /**
+ * Reset all fog (hide entire map)
+ */
+ resetAll() {
+ for (let y = 0; y < this.gridHeight; y++) {
+ for (let x = 0; x < this.gridWidth; x++) {
+ this.fogGrid[y][x] = 0; // All unexplored
+ }
+ }
+ this.exploredAreas.clear();
+ this.renderFullFog();
+ }
+
+ /**
+ * Check if area is explored
+ */
+ isExplored(x, y) {
+ if (x < 0 || x >= this.gridWidth || y < 0 || y >= this.gridHeight) {
+ return false;
+ }
+ return this.fogGrid[y][x] > 0;
+ }
+
+ /**
+ * Check if area is visible
+ */
+ isVisible(x, y) {
+ if (x < 0 || x >= this.gridWidth || y < 0 || y >= this.gridHeight) {
+ return false;
+ }
+ return this.fogGrid[y][x] === 2;
+ }
+
+ /**
+ * Get exploration percentage
+ */
+ getExplorationPercentage() {
+ let explored = 0;
+ const total = this.gridWidth * this.gridHeight;
+
+ for (let y = 0; y < this.gridHeight; y++) {
+ for (let x = 0; x < this.gridWidth; x++) {
+ if (this.fogGrid[y][x] > 0) {
+ explored++;
+ }
+ }
+ }
+
+ return (explored / total) * 100;
+ }
+
+ /**
+ * Enable fog of war
+ */
+ enable() {
+ this.settings.enabled = true;
+ if (!this.fogLayer) {
+ this.createFogLayer();
+ }
+ this.fogLayer.setVisible(true);
+ this.saveSettings();
+ }
+
+ /**
+ * Disable fog of war
+ */
+ disable() {
+ this.settings.enabled = false;
+ if (this.fogLayer) {
+ this.fogLayer.setVisible(false);
+ }
+ this.saveSettings();
+ }
+
+ /**
+ * Set visible radius
+ */
+ setVisibleRadius(radius) {
+ this.settings.visibleRadius = Math.max(1, Math.min(20, radius));
+ this.saveSettings();
+ }
+
+ /**
+ * Set fog color
+ */
+ setFogColor(color) {
+ this.settings.fogColor = color;
+ this.renderFog();
+ this.saveSettings();
+ }
+
+ /**
+ * Set fog alpha
+ */
+ setFogAlpha(alpha) {
+ this.settings.fogAlpha = Phaser.Math.Clamp(alpha, 0, 1);
+ this.renderFog();
+ this.saveSettings();
+ }
+
+ /**
+ * Set explored alpha
+ */
+ setExploredAlpha(alpha) {
+ this.settings.exploredAlpha = Phaser.Math.Clamp(alpha, 0, 1);
+ this.renderFog();
+ this.saveSettings();
+ }
+
+ /**
+ * Update (called every frame)
+ */
+ update() {
+ if (!this.settings.enabled) return;
+
+ // Update fog based on player position
+ if (this.scene.player) {
+ const pos = this.scene.player.getPosition();
+ this.updateFog(pos.x, pos.y);
+ }
+
+ // Fade out visible areas that are no longer in range
+ this.fadeDistantAreas();
+ }
+
+ /**
+ * Fade distant areas back to explored state
+ */
+ fadeDistantAreas() {
+ if (!this.scene.player) return;
+
+ const pos = this.scene.player.getPosition();
+ const playerX = Math.floor(pos.x);
+ const playerY = Math.floor(pos.y);
+ const radius = this.settings.visibleRadius;
+ const radiusSq = radius * radius;
+
+ for (let y = 0; y < this.gridHeight; y++) {
+ for (let x = 0; x < this.gridWidth; x++) {
+ if (this.fogGrid[y][x] === 2) {
+ // Check if still in visible range
+ const dx = x - playerX;
+ const dy = y - playerY;
+ const distSq = dx * dx + dy * dy;
+
+ if (distSq > radiusSq) {
+ // Fade back to explored
+ this.fogGrid[y][x] = 1;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Save explored areas to localStorage
+ */
+ saveExploredAreas() {
+ if (!this.settings.persistMemory) return;
+
+ const data = Array.from(this.exploredAreas);
+ localStorage.setItem('novafarma_explored_areas', JSON.stringify(data));
+ }
+
+ /**
+ * Load explored areas from localStorage
+ */
+ loadExploredAreas() {
+ if (!this.settings.persistMemory) return;
+
+ const saved = localStorage.getItem('novafarma_explored_areas');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.exploredAreas = new Set(data);
+
+ // Apply to fog grid
+ for (const key of this.exploredAreas) {
+ const [x, y] = key.split(',').map(Number);
+ if (x >= 0 && x < this.gridWidth && y >= 0 && y < this.gridHeight) {
+ this.fogGrid[y][x] = 1; // Explored
+ }
+ }
+
+ console.log(`๐บ๏ธ Loaded ${this.exploredAreas.size} explored tiles`);
+ } catch (error) {
+ console.error('Failed to load explored areas:', error);
+ }
+ }
+ }
+
+ /**
+ * Save settings
+ */
+ saveSettings() {
+ localStorage.setItem('novafarma_fog_of_war', JSON.stringify(this.settings));
+ }
+
+ /**
+ * Load settings
+ */
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_fog_of_war');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ /**
+ * Destroy system
+ */
+ destroy() {
+ if (this.fogLayer) this.fogLayer.destroy();
+ if (this.fogGraphics) this.fogGraphics.destroy();
+ this.saveExploredAreas();
+ console.log('๐ซ๏ธ Fog of War System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/GameManager.js b/EMERGENCY_SYSTEMS_RECOVERY/GameManager.js
new file mode 100644
index 000000000..ea58e85d6
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/GameManager.js
@@ -0,0 +1,398 @@
+/**
+ * GAME MANAGER - AUTO-SAVE SYSTEM
+ * "The Silent Protector" - Never lose progress!
+ */
+
+class GameManager {
+ constructor(scene) {
+ this.scene = scene;
+ this.saveKey = 'mrtva_dolina_save'; // Slot_0
+
+ // Auto-save state
+ this.lastAutoSave = Date.now();
+ this.autoSaveInterval = 5 * 60 * 1000; // 5 minutes
+ this.isSaving = false;
+
+ // Save indicator
+ this.saveIndicator = null;
+
+ // Initialize
+ this.init();
+ }
+
+ init() {
+ console.log('๐พ GameManager initialized - Auto-save active');
+
+ // Start periodic auto-save timer
+ this.startPeriodicAutoSave();
+
+ // Listen for scene transitions
+ this.scene.events.on('shutdown', () => this.onSceneTransition());
+
+ // Listen for progression events
+ this.listenForProgressionEvents();
+ }
+
+ /**
+ * TRIGGER 1: SCENE TRANSITION SAVE
+ */
+ onSceneTransition() {
+ console.log('๐ช Scene transition detected - Auto-saving...');
+ this.autoSaveGame('Scene Transition');
+ }
+
+ /**
+ * TRIGGER 2: PROGRESSION MILESTONE SAVE
+ */
+ listenForProgressionEvents() {
+ // Listen for aging events
+ this.scene.events.on('kai-aged', (data) => {
+ console.log(`โฐ Kai aged to ${data.newAge} - Auto-saving...`);
+ this.autoSaveGame('Aging Milestone');
+ });
+
+ // Listen for memory found
+ this.scene.events.on('memory-found', (data) => {
+ console.log(`๐ธ Memory found (${data.id}) - Auto-saving...`);
+ this.autoSaveGame('Memory Found');
+ });
+
+ // Listen for Gronk level up
+ if (window.gronkStats) {
+ window.addEventListener('gronk-levelup', (e) => {
+ console.log(`๐จ Gronk level ${e.detail.level} - Auto-saving...`);
+ this.autoSaveGame('Gronk Level Up');
+ });
+ }
+
+ // Listen for companion unlocks
+ window.addEventListener('companion-unlocked', (e) => {
+ console.log(`๐ Companion unlocked (${e.detail.name}) - Auto-saving...`);
+ this.autoSaveGame('Companion Unlock');
+ });
+ }
+
+ /**
+ * TRIGGER 3: PERIODIC AUTO-SAVE (5 Minutes)
+ */
+ startPeriodicAutoSave() {
+ this.periodicTimer = this.scene.time.addEvent({
+ delay: this.autoSaveInterval,
+ callback: () => {
+ const timeSinceLastSave = Date.now() - this.lastAutoSave;
+ if (timeSinceLastSave >= this.autoSaveInterval) {
+ console.log('โฑ๏ธ 5 minutes elapsed - Auto-saving...');
+ this.autoSaveGame('Periodic (5min)');
+ }
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * AUTO-SAVE GAME (Silent, no interruption)
+ */
+ autoSaveGame(reason = 'Manual') {
+ if (this.isSaving) {
+ console.log('โ ๏ธ Save already in progress, skipping...');
+ return;
+ }
+
+ this.isSaving = true;
+
+ console.log(`๐พ AUTO-SAVING (${reason})...`);
+
+ // Show save indicator
+ this.showSaveIndicator();
+
+ try {
+ // Gather save data
+ const saveData = this.gatherSaveData();
+
+ // Save to LocalStorage (Slot_0)
+ localStorage.setItem(this.saveKey, JSON.stringify(saveData));
+
+ // Update timestamp
+ this.lastAutoSave = Date.now();
+
+ console.log('โ
Auto-save complete!');
+ console.log(` Reason: ${reason}`);
+ console.log(` Size: ${JSON.stringify(saveData).length} bytes`);
+
+ } catch (error) {
+ console.error('โ Auto-save failed:', error);
+ } finally {
+ this.isSaving = false;
+ }
+ }
+
+ /**
+ * GATHER ALL SAVE DATA
+ */
+ gatherSaveData() {
+ const data = {
+ version: '1.0.0',
+ lastSaved: new Date().toISOString(),
+ playtime: this.getPlaytime(),
+
+ // Player data
+ player: this.getPlayerData(),
+
+ // Progress
+ progress: this.getProgressData(),
+
+ // Companions
+ companions: this.getCompanionData(),
+
+ // Farm state
+ farm: this.getFarmData(),
+
+ // Economy
+ economy: this.getEconomyData(),
+
+ // Current scene
+ currentScene: this.scene.scene.key
+ };
+
+ return data;
+ }
+
+ /**
+ * GET PLAYER DATA
+ */
+ getPlayerData() {
+ // Get PlayerStats if exists
+ let playerStats = null;
+ if (this.scene.playerStats) {
+ playerStats = this.scene.playerStats.getAgeInfo();
+ } else {
+ // Load from LocalStorage
+ const stored = localStorage.getItem('player_stats');
+ if (stored) {
+ playerStats = JSON.parse(stored);
+ }
+ }
+
+ return {
+ position: this.getPlayerPosition(),
+ age_level: playerStats?.level || 1,
+ current_age: playerStats?.age || 14,
+ age_sprite: playerStats?.sprite || 'kai_age14',
+ inventory: this.getInventory(),
+ health: 100,
+ stamina: 100,
+ equipped_tools: {}
+ };
+ }
+
+ /**
+ * GET PROGRESS DATA
+ */
+ getProgressData() {
+ return {
+ memories_found: this.getMemoriesFound(),
+ total_memories: 100,
+ quests_completed: [],
+ npcs_met: [],
+ biomes_unlocked: ['grassland']
+ };
+ }
+
+ /**
+ * GET COMPANION DATA
+ */
+ getCompanionData() {
+ const companions = {
+ gronk: {
+ unlocked: false,
+ level: 1,
+ xp: 0
+ },
+ susi: {
+ unlocked: false,
+ position: { x: 0, y: 0 },
+ loyalty: 0
+ }
+ };
+
+ // Check Gronk stats
+ if (window.gronkStats) {
+ const gronkData = gronkStats.getStats();
+ companions.gronk = {
+ unlocked: true,
+ level: gronkData.level,
+ xp: gronkData.xp
+ };
+ }
+
+ // Check Susi unlock
+ const susiUnlocked = localStorage.getItem('susi_unlocked');
+ if (susiUnlocked === 'true') {
+ companions.susi.unlocked = true;
+ }
+
+ return companions;
+ }
+
+ /**
+ * GET FARM DATA
+ */
+ getFarmData() {
+ return {
+ crops: [],
+ buildings: [],
+ animals: [],
+ resources: {}
+ };
+ }
+
+ /**
+ * GET ECONOMY DATA
+ */
+ getEconomyData() {
+ return {
+ money: 0,
+ cannabis_seeds: 5, // Starting capital!
+ cannabis_harvested: 0
+ };
+ }
+
+ /**
+ * HELPER: Get player position
+ */
+ getPlayerPosition() {
+ if (this.scene.player) {
+ return {
+ x: this.scene.player.x,
+ y: this.scene.player.y
+ };
+ }
+ return { x: 640, y: 360 }; // Default center
+ }
+
+ /**
+ * HELPER: Get inventory
+ */
+ getInventory() {
+ // TODO: Get from inventory system
+ return [];
+ }
+
+ /**
+ * HELPER: Get memories found
+ */
+ getMemoriesFound() {
+ const stored = localStorage.getItem('player_stats');
+ if (stored) {
+ const data = JSON.parse(stored);
+ return data.memoriesFound || 0;
+ }
+ return 0;
+ }
+
+ /**
+ * HELPER: Get playtime
+ */
+ getPlaytime() {
+ const stored = localStorage.getItem('playtime');
+ if (stored) {
+ return parseInt(stored) + Math.floor(this.scene.time.now / 1000);
+ }
+ return Math.floor(this.scene.time.now / 1000);
+ }
+
+ /**
+ * SHOW SAVE INDICATOR (Spinning Longboard)
+ */
+ showSaveIndicator() {
+ if (this.saveIndicator) {
+ return; // Already showing
+ }
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Create longboard emoji as indicator
+ this.saveIndicator = this.scene.add.text(
+ width - 80,
+ height - 80,
+ '๐น',
+ {
+ fontSize: '32px'
+ }
+ );
+ this.saveIndicator.setScrollFactor(0);
+ this.saveIndicator.setDepth(9999);
+ this.saveIndicator.setAlpha(0);
+
+ // Add "Saving..." text
+ const savingText = this.scene.add.text(
+ width - 140,
+ height - 50,
+ 'Saving...',
+ {
+ fontSize: '14px',
+ fontFamily: 'Georgia, serif',
+ color: '#f4e4c1',
+ stroke: '#000000',
+ strokeThickness: 3
+ }
+ );
+ savingText.setScrollFactor(0);
+ savingText.setDepth(9999);
+ savingText.setAlpha(0);
+
+ // Fade in
+ this.scene.tweens.add({
+ targets: [this.saveIndicator, savingText],
+ alpha: 0.8,
+ duration: 300
+ });
+
+ // Spin animation
+ this.scene.tweens.add({
+ targets: this.saveIndicator,
+ angle: 360,
+ duration: 1000,
+ repeat: 1
+ });
+
+ // Fade out after 2 seconds
+ this.scene.time.delayedCall(2000, () => {
+ this.scene.tweens.add({
+ targets: [this.saveIndicator, savingText],
+ alpha: 0,
+ duration: 500,
+ onComplete: () => {
+ if (this.saveIndicator) {
+ this.saveIndicator.destroy();
+ this.saveIndicator = null;
+ }
+ if (savingText) {
+ savingText.destroy();
+ }
+ }
+ });
+ });
+ }
+
+ /**
+ * MANUAL SAVE (for explicit save points)
+ */
+ manualSave() {
+ console.log('๐พ Manual save requested...');
+ this.autoSaveGame('Manual Save');
+ }
+
+ /**
+ * CLEANUP
+ */
+ destroy() {
+ if (this.periodicTimer) {
+ this.periodicTimer.remove();
+ }
+ console.log('๐พ GameManager destroyed');
+ }
+}
+
+export default GameManager;
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/GemDropSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/GemDropSystem.js
new file mode 100644
index 000000000..3e07bd8e6
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/GemDropSystem.js
@@ -0,0 +1,187 @@
+/**
+ * GEM DROP SYSTEM
+ * Handles rare gem drops from enemies, mining, etc.
+ * Gems: Diamond, Emerald, Ruby (high value items for selling)
+ */
+
+class GemDropSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Gem definitions with rarity and value
+ this.gems = {
+ 'diamond': {
+ name: 'Diamond',
+ rarity: 'legendary', // 0.5% drop
+ value: 500, // Sell price in gold
+ icon: '๐'
+ },
+ 'emerald': {
+ name: 'Emerald',
+ rarity: 'epic', // 2% drop
+ value: 200,
+ icon: '๐'
+ },
+ 'ruby': {
+ name: 'Ruby',
+ rarity: 'rare', // 5% drop
+ value: 100,
+ icon: 'โค๏ธ'
+ },
+ 'sapphire': {
+ name: 'Sapphire',
+ rarity: 'uncommon', // 10% drop
+ value: 50,
+ icon: '๐'
+ }
+ };
+
+ // Drop chance by rarity
+ this.rarityChances = {
+ 'legendary': 0.005, // 0.5%
+ 'epic': 0.02, // 2%
+ 'rare': 0.05, // 5%
+ 'uncommon': 0.10 // 10%
+ };
+ }
+
+ /**
+ * Roll for gem drop from enemy/resource
+ * @param {string} source - 'zombie', 'elite', 'stone_ore', 'iron_ore'
+ * @param {number} x - Grid X
+ * @param {number} y - Grid Y
+ * @param {number} luckBonus - Player luck modifier (0-1)
+ */
+ rollGemDrop(source, x, y, luckBonus = 0) {
+ const dropTable = this.getDropTableForSource(source);
+ if (!dropTable || dropTable.length === 0) return;
+
+ // Roll for each possible gem
+ for (const gemEntry of dropTable) {
+ const gemType = gemEntry.gem;
+ const gem = this.gems[gemType];
+ if (!gem) continue;
+
+ const baseChance = this.rarityChances[gem.rarity];
+ const finalChance = baseChance * (1 + luckBonus);
+
+ if (Math.random() < finalChance) {
+ // GEM DROPPED!
+ console.log(`๐ RARE DROP: ${gem.name} from ${source}!`);
+ this.spawnGem(gemType, x, y);
+
+ // Play special sound/effect
+ if (this.scene.particleEffects) {
+ this.scene.particleEffects.gemSparkle(x, y, gem.icon);
+ }
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playRareDrop();
+ }
+
+ // Only drop 1 gem per source
+ break;
+ }
+ }
+ }
+
+ /**
+ * Get drop table based on source
+ */
+ getDropTableForSource(source) {
+ const tables = {
+ 'zombie': [
+ { gem: 'sapphire', weight: 1 }
+ ],
+ 'elite_zombie': [
+ { gem: 'ruby', weight: 2 },
+ { gem: 'emerald', weight: 1 }
+ ],
+ 'troll': [
+ { gem: 'emerald', weight: 2 },
+ { gem: 'diamond', weight: 0.5 }
+ ],
+ 'stone_ore': [
+ { gem: 'sapphire', weight: 1 }
+ ],
+ 'iron_ore': [
+ { gem: 'ruby', weight: 1 }
+ ],
+ 'gold_ore': [
+ { gem: 'emerald', weight: 1.5 },
+ { gem: 'diamond', weight: 0.3 }
+ ],
+ 'boss': [
+ { gem: 'diamond', weight: 5 } // Guaranteed almost!
+ ]
+ };
+
+ return tables[source] || [];
+ }
+
+ /**
+ * Spawn gem as loot item
+ */
+ spawnGem(gemType, gridX, gridY) {
+ if (this.scene.lootSystem) {
+ this.scene.lootSystem.spawnLoot(gridX, gridY, gemType, 1);
+ } else {
+ console.warn('โ ๏ธ LootSystem not found - gem cannot spawn!');
+ }
+
+ // Show floating text
+ const gem = this.gems[gemType];
+ if (this.scene.events) {
+ const screenPos = this.scene.terrainSystem.iso.toScreen(gridX, gridY);
+ this.scene.events.emit('show-floating-text', {
+ x: screenPos.x,
+ y: screenPos.y - 50,
+ text: `${gem.icon} ${gem.name}!`,
+ color: '#ff00ff', // Purple for rare
+ size: '20px'
+ });
+ }
+ }
+
+ /**
+ * Get gem sell value
+ */
+ getGemValue(gemType) {
+ const gem = this.gems[gemType];
+ return gem ? gem.value : 0;
+ }
+
+ /**
+ * Sell gem to merchant
+ */
+ sellGem(gemType, amount, playerInventory) {
+ const gem = this.gems[gemType];
+ if (!gem) {
+ console.warn('โ ๏ธ Unknown gem type:', gemType);
+ return 0;
+ }
+
+ // Check if player has gem
+ const hasAmount = playerInventory.getItemCount(gemType);
+ if (hasAmount < amount) {
+ console.warn(`โ ๏ธ Not enough ${gem.name} to sell!`);
+ return 0;
+ }
+
+ // Calculate total value
+ const totalValue = gem.value * amount;
+
+ // Remove from inventory
+ playerInventory.removeItem(gemType, amount);
+
+ // Add gold
+ playerInventory.addItem('gold_coin', totalValue);
+
+ console.log(`๐ฐ Sold ${amount}x ${gem.name} for ${totalValue} gold`);
+ return totalValue;
+ }
+}
+
+// Export
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = GemDropSystem;
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/GenerationalGameplaySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/GenerationalGameplaySystem.js
new file mode 100644
index 000000000..baddbd850
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/GenerationalGameplaySystem.js
@@ -0,0 +1,522 @@
+/**
+ * GenerationalGameplaySystem.js
+ * ==============================
+ * KRVAVA ลฝETEV - Generational Gameplay System (P12)
+ *
+ * Features:
+ * - Permadeath mode
+ * - Character switching (Kai โ Child)
+ * - Inheritance system
+ * - NPC memory system
+ * - Multi-generation gameplay
+ * - Legacy tracking
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class GenerationalGameplaySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Generational data
+ this.generations = [];
+ this.currentGeneration = 0;
+ this.currentProtagonist = null;
+
+ // Legacy tracking
+ this.familyTree = new Map(); // personId -> family data
+ this.legacyPoints = 0;
+ this.familyAchievements = [];
+
+ // NPC memory
+ this.npcMemories = new Map(); // npcId -> memories
+
+ // Permadeath settings
+ this.permadeathEnabled = true;
+ this.hasGameOver = false;
+
+ console.log('โฐ๏ธ GenerationalGameplaySystem initialized');
+
+ // Initialize first generation (Kai)
+ this.initializeKai();
+ }
+
+ /**
+ * Initialize Kai as Generation 1
+ */
+ initializeKai() {
+ const kai = {
+ id: 'kai_gen1',
+ name: 'Kai',
+ generation: 1,
+ age: 25,
+ isProtagonist: true,
+ isAlive: true,
+ isPlayer: true,
+
+ // Family
+ spouse: null,
+ children: [],
+ parents: [],
+
+ // Stats
+ stats: {
+ itemsCollected: 0,
+ zombiesTamed: 0,
+ bossesDefeated: 0,
+ questsCompleted: 0
+ },
+
+ // Legacy
+ accomplishments: [],
+ deathDate: null,
+ causeOfDeath: null,
+ graveLocation: null
+ };
+
+ this.familyTree.set(kai.id, kai);
+ this.currentProtagonist = kai;
+ this.generations.push([kai]);
+
+ console.log('๐ค Generation 1: Kai initialized');
+ }
+
+ /**
+ * 12.1 - Handle player death (Permadeath)
+ */
+ handlePlayerDeath(causeOfDeath = 'unknown') {
+ if (!this.permadeathEnabled) {
+ console.log('โ ๏ธ Permadeath disabled, respawning...');
+ return false;
+ }
+
+ const protagonist = this.currentProtagonist;
+ if (!protagonist) return false;
+
+ console.log(`โฐ๏ธ ${protagonist.name} has died! (${causeOfDeath})`);
+
+ // Mark as dead
+ protagonist.isAlive = false;
+ protagonist.isPlayer = false;
+ protagonist.deathDate = this.scene.timeSystem?.getCurrentDate() || new Date();
+ protagonist.causeOfDeath = causeOfDeath;
+
+ // Check for adult children
+ const adultChildren = this.getAdultChildren(protagonist.id);
+
+ if (adultChildren.length === 0) {
+ // GAME OVER - No heir
+ this.triggerGameOver();
+ return true;
+ }
+
+ // Legacy transition
+ this.triggerLegacyTransition(protagonist, adultChildren);
+
+ return true;
+ }
+
+ /**
+ * 12.1 - Trigger game over (no heir)
+ */
+ triggerGameOver() {
+ this.hasGameOver = true;
+
+ console.log('๐ GAME OVER - No living heir!');
+
+ // TODO: Show game over screen with legacy stats
+ this.showNotification({
+ title: 'Legacy Ended',
+ text: 'The bloodline has ended. Your legacy is complete.',
+ icon: 'โฐ๏ธ'
+ });
+
+ // Show final stats
+ this.showLegacyStats();
+ }
+
+ /**
+ * 12.2 - Legacy transition cutscene
+ */
+ triggerLegacyTransition(deceased, heirs) {
+ console.log(`๐
Legacy transition: ${deceased.name} โ ${heirs[0].name}`);
+
+ // TODO: Implement full cutscene with:
+ // - Funeral scene
+ // - "Legacy lives on..." text
+ // - Family mourning
+ // - Heir accepts responsibility
+
+ // For now, show notification
+ this.showNotification({
+ title: 'Legacy Lives On...',
+ text: `โฐ๏ธ ${deceased.name} has passed. ${heirs[0].name} continues the legacy.`,
+ icon: '๐
'
+ });
+
+ // Switch to heir
+ setTimeout(() => {
+ this.switchProtagonist(heirs[0].id);
+ }, 3000);
+ }
+
+ /**
+ * 12.2 - Switch protagonist to heir
+ */
+ switchProtagonist(heirId) {
+ const heir = this.familyTree.get(heirId);
+ if (!heir) {
+ console.error(`Heir ${heirId} not found!`);
+ return false;
+ }
+
+ const previousProtagonist = this.currentProtagonist;
+
+ // Make heir the new protagonist
+ heir.isProtagonist = true;
+ heir.isPlayer = true;
+ this.currentProtagonist = heir;
+ this.currentGeneration = heir.generation;
+
+ console.log(`๐ค New protagonist: ${heir.name} (Generation ${heir.generation})`);
+
+ // Transfer everything
+ this.transferInheritance(previousProtagonist, heir);
+
+ // Update NPCs to mother becomes mentor
+ this.updateFamilyRoles(previousProtagonist, heir);
+
+ // Trigger new questline
+ this.startGenerationQuest(heir);
+
+ return true;
+ }
+
+ /**
+ * 12.2 - Transfer inheritance
+ */
+ transferInheritance(deceased, heir) {
+ console.log(`๐ฆ Transferring inheritance from ${deceased.name} to ${heir.name}`);
+
+ // Transfer items (100%)
+ if (this.scene.inventorySystem) {
+ // Items automatically stay in house/chests
+ console.log('๐ฆ Items inherited');
+ }
+
+ // Transfer property
+ console.log('๐ก Farm/house inherited');
+
+ // Transfer zombies
+ if (this.scene.zombieSystem) {
+ console.log('๐ง Zombie army inherited');
+ }
+
+ // Transfer animals
+ if (this.scene.animalBreeding) {
+ console.log('๐ Animals inherited');
+ }
+
+ // Transfer money
+ if (this.scene.inventorySystem) {
+ console.log('๐ฐ Money inherited');
+ }
+
+ // Calculate legacy bonus
+ const legacyBonus = this.calculateLegacyBonus(deceased);
+ this.applyLegacyBonus(heir, legacyBonus);
+
+ this.showNotification({
+ title: 'Inheritance Received',
+ text: `๐ฆ ${heir.name} inherited everything! +${legacyBonus} Legacy Points`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Calculate legacy bonus from previous generation
+ */
+ calculateLegacyBonus(deceased) {
+ let bonus = 0;
+
+ // Quests completed
+ bonus += deceased.stats.questsCompleted * 10;
+
+ // Bosses defeated
+ bonus += deceased.stats.bossesDefeated * 50;
+
+ // Zombies tamed
+ bonus += deceased.stats.zombiesTamed * 5;
+
+ // Items collected (rare)
+ bonus += deceased.stats.itemsCollected * 2;
+
+ return bonus;
+ }
+
+ /**
+ * Apply legacy bonus to heir
+ */
+ applyLegacyBonus(heir, bonus) {
+ // Legacy points grant bonuses
+ this.legacyPoints += bonus;
+
+ // Grant starting stats based on legacy
+ const statBonus = Math.floor(bonus / 100);
+
+ // TODO: Apply stat bonuses
+ console.log(`โญ ${heir.name} receives +${statBonus} stat bonus from legacy!`);
+ }
+
+ /**
+ * 12.2 - Update family roles
+ */
+ updateFamilyRoles(deceased, heir) {
+ // Wife becomes NPC mentor
+ if (deceased.spouse) {
+ const spouse = this.familyTree.get(deceased.spouse);
+ if (spouse) {
+ spouse.role = 'mentor';
+ console.log(`๐ฉ ${spouse.name} is now a mentor NPC`);
+ }
+ }
+
+ // Ana becomes "aunt" figure
+ if (heir.generation > 1) {
+ console.log('๐ฉ Ana is now an aunt figure');
+ // Update Ana dialogues for new generation
+ }
+ }
+
+ /**
+ * 12.3 - NPC Memory System
+ */
+ rememberDeceased(npcId, deceasedId) {
+ const deceased = this.familyTree.get(deceasedId);
+ if (!deceased) return;
+
+ if (!this.npcMemories.has(npcId)) {
+ this.npcMemories.set(npcId, {
+ remembers: [],
+ relationships: new Map()
+ });
+ }
+
+ const memory = this.npcMemories.get(npcId);
+ memory.remembers.push({
+ personId: deceasedId,
+ name: deceased.name,
+ relationship: 'friend', // TODO: Get actual relationship
+ memories: [
+ `I remember when ${deceased.name} helped me...`,
+ `${deceased.name} was a good person.`,
+ `Your father was brave.`
+ ]
+ });
+ }
+
+ /**
+ * 12.3 - Get NPC dialogue for new generation
+ */
+ getNPCDialogueForGeneration(npcId, protagonistId) {
+ const protagonist = this.familyTree.get(protagonistId);
+ const memory = this.npcMemories.get(npcId);
+
+ if (!memory || protagonist.generation === 1) {
+ return null; // Normal dialogue
+ }
+
+ // Special dialogue for child of previous protagonist
+ const dialogues = [
+ `You have your father's eyes...`,
+ `Kai would be so proud of you!`,
+ `I knew your father well. He was a great man.`,
+ `You remind me so much of ${protagonist.parents[0]}`,
+ `Your family has done so much for this town.`
+ ];
+
+ return Phaser.Utils.Array.GetRandom(dialogues);
+ }
+
+ /**
+ * 12.4 - Start generation-specific quest
+ */
+ startGenerationQuest(heir) {
+ if (heir.generation === 1) return; // Kai has main quest
+
+ console.log(`๐ Starting "Following Father's Footsteps" quest for ${heir.name}`);
+
+ const questData = {
+ id: `gen${heir.generation}_legacy`,
+ title: 'Following Father\'s Footsteps',
+ description: 'Honor your father\'s memory and continue his legacy.',
+ objectives: [
+ {
+ id: 'visit_grave',
+ type: 'location',
+ description: `Visit ${heir.parents[0]}'s grave`,
+ target: { x: 100, y: 200, radius: 50 }
+ },
+ {
+ id: 'talk_to_mother',
+ type: 'dialogue',
+ description: 'Talk to your mother about father'
+ },
+ {
+ id: 'continue_mission',
+ type: 'event',
+ description: 'Swear to continue father\'s mission'
+ }
+ ],
+ rewards: {
+ xp: 500,
+ items: [{ id: 'father_memento', amount: 1 }],
+ bondStrength: 10 // With Ana
+ }
+ };
+
+ // TODO: Integrate with QuestSystem
+ console.log(`๐ Quest "${questData.title}" available`);
+ }
+
+ /**
+ * 12.5 - Multi-generation system
+ */
+ addChild(parentId, childData) {
+ const parent = this.familyTree.get(parentId);
+ if (!parent) {
+ console.error('Parent not found');
+ return null;
+ }
+
+ // Determine generation
+ const generation = parent.generation + 1;
+
+ const child = {
+ id: `${childData.name.toLowerCase()}_gen${generation}`,
+ name: childData.name,
+ gender: childData.gender,
+ generation: generation,
+ age: 0,
+ isProtagonist: false,
+ isAlive: true,
+ isPlayer: false,
+
+ // Family
+ spouse: null,
+ children: [],
+ parents: [parentId],
+
+ // Stats
+ stats: {
+ itemsCollected: 0,
+ zombiesTamed: 0,
+ bossesDefeated: 0,
+ questsCompleted: 0
+ },
+
+ // Legacy
+ accomplishments: [],
+ deathDate: null,
+ causeOfDeath: null
+ };
+
+ this.familyTree.set(child.id, child);
+ parent.children.push(child.id);
+
+ // Add to generation array
+ if (!this.generations[generation]) {
+ this.generations[generation] = [];
+ }
+ this.generations[generation].push(child);
+
+ console.log(`๐ถ ${child.name} added to Generation ${generation}`);
+
+ return child;
+ }
+
+ /**
+ * Get adult children of a person
+ */
+ getAdultChildren(personId) {
+ const person = this.familyTree.get(personId);
+ if (!person) return [];
+
+ return person.children
+ .map(childId => this.familyTree.get(childId))
+ .filter(child => child && child.age >= 6570 && child.isAlive); // 18 years
+ }
+
+ /**
+ * Create grave for deceased
+ */
+ createGrave(deceasedId, location) {
+ const deceased = this.familyTree.get(deceasedId);
+ if (!deceased) return;
+
+ deceased.graveLocation = location;
+
+ // TODO: Actually place grave object in world
+ console.log(`โฐ๏ธ Grave created for ${deceased.name} at (${location.x}, ${location.y})`);
+
+ this.showNotification({
+ title: 'Grave Placed',
+ text: `โฐ๏ธ ${deceased.name} will be remembered forever.`,
+ icon: '๐๏ธ'
+ });
+ }
+
+ /**
+ * Show legacy stats
+ */
+ showLegacyStats() {
+ const stats = {
+ totalGenerations: this.generations.length,
+ totalDescendants: this.familyTree.size,
+ legacyPoints: this.legacyPoints,
+ achievements: this.familyAchievements.length
+ };
+
+ console.log('๐ LEGACY STATS:');
+ console.log(`Generations: ${stats.totalGenerations}`);
+ console.log(`Total Family Members: ${stats.totalDescendants}`);
+ console.log(`Legacy Points: ${stats.legacyPoints}`);
+ console.log(`Achievements: ${stats.achievements}`);
+
+ // TODO: Show actual UI screen
+ }
+
+ /**
+ * Get family tree
+ */
+ getFamilyTree() {
+ return this.familyTree;
+ }
+
+ /**
+ * Get current protagonist
+ */
+ getCurrentProtagonist() {
+ return this.currentProtagonist;
+ }
+
+ /**
+ * Check if game over
+ */
+ isGameOver() {
+ return this.hasGameOver;
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/GraveSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/GraveSystem.js
new file mode 100644
index 000000000..3952dab1b
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/GraveSystem.js
@@ -0,0 +1,205 @@
+/**
+ * GRAVE SYSTEM
+ * Zombi workers lahko poฤivajo v grobovih za regeneracijo
+ */
+class GraveSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.graves = []; // Seznam vseh postavljenih grobov
+ }
+
+ /**
+ * Place grave on terrain
+ */
+ placeGrave(gridX, gridY) {
+ const terrain = this.scene.terrainSystem;
+ if (!terrain) return false;
+
+ const tile = terrain.getTile(gridX, gridY);
+ if (!tile) return false;
+
+ // Check if already has grave
+ const key = `${gridX},${gridY}`;
+ if (this.graves.find(g => g.key === key)) {
+ console.log('โ ๏ธ Grave already exists here!');
+ return false;
+ }
+
+ // Create grave object
+ const grave = {
+ gridX,
+ gridY,
+ key,
+ occupiedBy: null, // Zombie currently resting
+ sprite: null
+ };
+
+ // Create visual sprite
+ const screenPos = this.scene.iso.toScreen(gridX, gridY);
+ grave.sprite = this.scene.add.sprite(
+ screenPos.x + this.scene.terrainOffsetX,
+ screenPos.y + this.scene.terrainOffsetY,
+ 'gravestone'
+ );
+ grave.sprite.setOrigin(0.5, 1);
+ grave.sprite.setScale(0.3);
+ grave.sprite.setDepth(this.scene.iso.getDepth(gridX, gridY, this.scene.iso.LAYER_OBJECTS));
+
+ this.graves.push(grave);
+ console.log(`๐ชฆ Grave placed at ${gridX},${gridY}`);
+ return true;
+ }
+
+ /**
+ * Remove grave
+ */
+ removeGrave(gridX, gridY) {
+ const key = `${gridX},${gridY}`;
+ const index = this.graves.findIndex(g => g.key === key);
+
+ if (index === -1) return false;
+
+ const grave = this.graves[index];
+
+ // Kick out occupant
+ if (grave.occupiedBy) {
+ grave.occupiedBy.workerData.isResting = false;
+ grave.occupiedBy = null;
+ }
+
+ if (grave.sprite) grave.sprite.destroy();
+ this.graves.splice(index, 1);
+
+ console.log(`๐ชฆ Grave removed at ${gridX},${gridY}`);
+ return true;
+ }
+
+ /**
+ * Assign zombie to rest in grave
+ */
+ assignZombieToGrave(zombie) {
+ if (!zombie.workerData) return false;
+
+ // Find empty grave
+ const emptyGrave = this.graves.find(g => g.occupiedBy === null);
+
+ if (!emptyGrave) {
+ console.log('๐ซ No empty graves available!');
+ return false;
+ }
+
+ // Mark zombie as resting
+ zombie.workerData.isResting = true;
+ zombie.workerData.restingGrave = emptyGrave;
+ emptyGrave.occupiedBy = zombie;
+
+ // Move zombie to grave
+ zombie.gridX = emptyGrave.gridX;
+ zombie.gridY = emptyGrave.gridY;
+ zombie.updatePosition();
+
+ // Visual: Make zombie semi-transparent
+ if (zombie.sprite) {
+ zombie.sprite.setAlpha(0.5);
+ }
+
+ console.log(`๐ชฆ Zombie resting in grave at ${emptyGrave.gridX},${emptyGrave.gridY}`);
+ return true;
+ }
+
+ /**
+ * Wake zombie from grave
+ */
+ wakeZombie(zombie) {
+ if (!zombie.workerData || !zombie.workerData.isResting) return false;
+
+ const grave = zombie.workerData.restingGrave;
+ if (grave) {
+ grave.occupiedBy = null;
+ }
+
+ zombie.workerData.isResting = false;
+ zombie.workerData.restingGrave = null;
+
+ // Restore visual
+ if (zombie.sprite) {
+ zombie.sprite.setAlpha(1.0);
+ }
+
+ console.log(`๐ชฆ Zombie woke up from grave`);
+ return true;
+ }
+
+ /**
+ * Update - regenerate resting zombies
+ */
+ update(delta) {
+ for (const grave of this.graves) {
+ if (grave.occupiedBy && grave.occupiedBy.workerData) {
+ const zombie = grave.occupiedBy;
+ const wd = zombie.workerData;
+
+ // Regenerate energy (slower than normal decay)
+ wd.energy += (0.2 * delta) / 1000; // +0.2 energy/sec
+ wd.energy = Math.min(100, wd.energy);
+
+ // Regenerate HP if energy > 50
+ if (wd.energy > 50) {
+ zombie.hp += (0.1 * delta) / 1000; // +0.1 HP/sec
+ zombie.hp = Math.min(zombie.maxHp, zombie.hp);
+ }
+
+ // Visual feedback - green tint when regenerating
+ if (zombie.sprite) {
+ zombie.sprite.setTint(0x00FF00);
+ }
+ }
+ }
+ }
+
+ /**
+ * Find nearest empty grave
+ */
+ findNearestEmptyGrave(x, y) {
+ let nearest = null;
+ let minDist = 999;
+
+ for (const grave of this.graves) {
+ if (grave.occupiedBy === null) {
+ const dist = Phaser.Math.Distance.Between(x, y, grave.gridX, grave.gridY);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = grave;
+ }
+ }
+ }
+
+ return nearest;
+ }
+
+ /**
+ * Auto-rest: Send low-energy zombies to graves
+ */
+ autoRest() {
+ if (!this.scene.zombieWorkerSystem) return;
+
+ for (const worker of this.scene.zombieWorkerSystem.workers) {
+ if (!worker.workerData) continue;
+
+ // If energy < 20 and not resting, send to grave
+ if (worker.workerData.energy < 20 && !worker.workerData.isResting) {
+ const grave = this.findNearestEmptyGrave(worker.gridX, worker.gridY);
+ if (grave) {
+ this.assignZombieToGrave(worker);
+ console.log(`๐ชฆ Auto-rest: Zombie sent to grave (Low energy)`);
+ }
+ }
+
+ // If energy > 80 and resting, wake up
+ if (worker.workerData.energy > 80 && worker.workerData.isResting) {
+ this.wakeZombie(worker);
+ console.log(`๐ชฆ Auto-wake: Zombie restored and ready to work`);
+ }
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/GrokCharacterSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/GrokCharacterSystem.js
new file mode 100644
index 000000000..d0b7e3b54
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/GrokCharacterSystem.js
@@ -0,0 +1,868 @@
+/**
+ * GrokCharacterSystem.js
+ * ======================
+ * KRVAVA ลฝETEV - Grok Character (The Developer / Pink Alpha)
+ *
+ * CHARACTER DESIGN:
+ * - Skin: Light green (unique color - not human!)
+ * - Hair: PINK dreadlocks (iconic!)
+ * - Outfit: Oversized hoodie (2 sizes too big) + baggy pants
+ * - Shoes: Hot pink Converse
+ * - Piercings: Septum, eyebrows, lips, 15+ earrings, 25mm tunnels
+ *
+ * PERSONALITY:
+ * - ADHD genius developer
+ * - Always vaping (Rainbow RGB mod)
+ * - Zen master with massive gong
+ * - Quick movements when hyperfocused
+ * - Oversized comfort style
+ *
+ * COMPANION:
+ * - Susi: Hot dog hunting dog (always by his side)
+ *
+ * Features:
+ * - Massive gong (1m diameter!)
+ * - Rainbow RGB vape mod
+ * - Morning meditation rituals
+ * - Combat buffs
+ * - Smoke screen abilities
+ * - ADHD focus modes
+ * - Susi interactions
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-25
+ */
+
+export default class GrokCharacterSystem {
+ constructor(scene, grokSprite) {
+ this.scene = scene;
+ this.grok = grokSprite;
+
+ // Grok state
+ this.isVaping = true; // Always vaping!
+ this.lastGongTime = 0;
+ this.gongCooldown = 300000; // 5 minutes
+ this.meditationTime = 6; // 6 AM daily
+
+ // ADHD mechanics
+ this.isFocused = false; // "Oversized Focus" mode
+ this.focusTimer = 0;
+ this.hyperfocusSpeed = 2.0; // Speed multiplier when focused
+ this.currentTopic = 'coding'; // What Grok is focused on
+
+ // Visual elements
+ this.gong = null;
+ this.vapeDevice = null;
+ this.smokeParticles = [];
+ this.hoodie = null; // Oversized hoodie sprite
+ this.dreadlocks = null; // Pink dreadlocks
+ this.piercings = []; // Visual piercing elements
+
+ // Susi companion
+ this.susi = null; // Susi the hot dog hunter
+ this.susiState = 'following'; // following, hunting, eating
+
+ // Buffs
+ this.activeBuffs = new Map();
+
+ // Character colors
+ this.skinColor = 0x90EE90; // Light green
+ this.dreadlockColor = 0xFF69B4; // Hot pink
+ this.hoodieColor = 0x2F4F4F; // Dark slate gray (wide hoodie)
+
+ console.log('๐ง GrokCharacterSystem initialized');
+ console.log('๐ Oversized hoodie: ON');
+ console.log('๐ Skin: Light green');
+ console.log('๐ Dreadlocks: HOT PINK');
+ console.log('๐ Susi companion: Ready!');
+
+ // Setup visuals
+ this.setupGrokVisuals();
+ this.createSusi();
+ this.startVapingAnimation();
+ }
+
+ /**
+ * 13.1 - Setup Grok's visual elements
+ */
+ setupGrokVisuals() {
+ if (!this.grok) return;
+
+ // Create massive gong (1m diameter = 100 pixels!)
+ this.gong = this.scene.add.circle(
+ this.grok.x - 50,
+ this.grok.y,
+ 50, // Radius 50px = 100px diameter
+ 0xDAA520 // Golden color
+ );
+ this.gong.setStrokeStyle(5, 0x8B4513); // Brown stroke
+ this.gong.setDepth(this.grok.depth - 1);
+
+ // Add gong details (concentric circles)
+ for (let i = 1; i <= 3; i++) {
+ const ring = this.scene.add.circle(
+ this.grok.x - 50,
+ this.grok.y,
+ 50 - (i * 10),
+ null
+ );
+ ring.setStrokeStyle(2, 0x8B4513);
+ ring.setDepth(this.grok.depth - 1);
+ }
+
+ // Create RGB vape mod
+ this.vapeDevice = this.scene.add.rectangle(
+ this.grok.x + 20,
+ this.grok.y + 10,
+ 15, // Width
+ 30, // Height
+ 0xFF1493 // Deep pink
+ );
+ this.vapeDevice.setDepth(this.grok.depth + 1);
+
+ // Add RGB LED effect
+ this.scene.tweens.add({
+ targets: this.vapeDevice,
+ fillColor: { from: 0xFF1493, to: 0x00CED1 }, // Pink โ Cyan
+ duration: 2000,
+ yoyo: true,
+ repeat: -1
+ });
+
+ console.log('โ
Grok visuals setup complete');
+ }
+
+ /**
+ * 13.2 - Morning Meditation Gong
+ */
+ triggerMorningMeditation() {
+ console.log('๐ง *BOOONG!* Morning meditation begins...');
+
+ // Play gong animation
+ this.playGongAnimation();
+
+ // Buff all nearby allies
+ this.applyMeditationBuff();
+
+ // Show message
+ this.showNotification({
+ title: 'Morning Meditation',
+ text: '๐ฅ BOOONG! Grok\'s gong brings peace to all.',
+ icon: '๐ง'
+ });
+ }
+
+ /**
+ * 13.2 - Gong strike (combat buff)
+ */
+ strikeGong() {
+ const now = Date.now();
+ if (now - this.lastGongTime < this.gongCooldown) {
+ const remaining = Math.ceil((this.gongCooldown - (now - this.lastGongTime)) / 1000);
+ console.log(`โฐ Gong on cooldown for ${remaining}s`);
+ return false;
+ }
+
+ console.log('๐ฅ *BOOOOONG!!!*');
+ this.lastGongTime = now;
+
+ // Visual effect
+ this.playGongAnimation();
+
+ // Sound wave radius
+ this.createSoundWaveEffect();
+
+ // Combat buffs
+ this.applyGongCombatBuff();
+
+ // Stun enemies
+ this.stunNearbyEnemies();
+
+ return true;
+ }
+
+ /**
+ * 13.2 - Gong animation
+ */
+ playGongAnimation() {
+ if (!this.gong) return;
+
+ // Vibrate gong
+ this.scene.tweens.add({
+ targets: this.gong,
+ scaleX: 1.2,
+ scaleY: 1.2,
+ alpha: 0.5,
+ duration: 100,
+ yoyo: true,
+ repeat: 5
+ });
+
+ // Screen shake
+ this.scene.cameras.main.shake(500, 0.01);
+
+ // Flash
+ this.scene.cameras.main.flash(200, 255, 215, 0); // Gold flash
+ }
+
+ /**
+ * 13.2 - Sound wave effect
+ */
+ createSoundWaveEffect() {
+ if (!this.gong) return;
+
+ // Create expanding circles (sound waves)
+ for (let i = 0; i < 3; i++) {
+ setTimeout(() => {
+ const wave = this.scene.add.circle(
+ this.gong.x,
+ this.gong.y,
+ 10,
+ 0xFFD700,
+ 0.5
+ );
+
+ this.scene.tweens.add({
+ targets: wave,
+ radius: 480, // 10 blocks
+ alpha: 0,
+ duration: 1000,
+ onComplete: () => wave.destroy()
+ });
+ }, i * 200);
+ }
+ }
+
+ /**
+ * 13.2 - Apply gong combat buff
+ */
+ applyGongCombatBuff() {
+ const buffDuration = 30000; // 30 seconds
+ const buffData = {
+ name: 'Gong Resonance',
+ damage: 1.2, // +20% damage
+ defense: 1.1, // +10% defense
+ expiresAt: Date.now() + buffDuration
+ };
+
+ // Apply to player
+ if (this.scene.player) {
+ this.activeBuffs.set('player', buffData);
+ console.log('โ๏ธ Player buffed: +20% damage, +10% defense (30s)');
+ }
+
+ // Apply to nearby allies
+ this.buffNearbyAllies(buffData);
+
+ this.showNotification({
+ title: 'Gong Resonance!',
+ text: '๐ฅ +20% Damage, +10% Defense for 30s!',
+ icon: 'โ๏ธ'
+ });
+ }
+
+ /**
+ * 13.2 - Buff nearby allies
+ */
+ buffNearbyAllies(buffData) {
+ // TODO: Get all allies within 10 blocks
+ // For now, just log
+ console.log('๐ค Allies buffed!');
+ }
+
+ /**
+ * 13.2 - Apply meditation buff
+ */
+ applyMeditationBuff() {
+ const buffData = {
+ name: 'Morning Meditation',
+ healthRegen: 5, // +5 HP/sec
+ staminaRegen: 10, // +10 stamina/sec
+ duration: 60000 // 1 minute
+ };
+
+ // TODO: Apply to all players/allies in area
+ console.log('๐ง Meditation buff active: +5 HP/sec, +10 stamina/sec');
+ }
+
+ /**
+ * 13.2 - Stun nearby enemies
+ */
+ stunNearbyEnemies() {
+ const stunRadius = 480; // 10 blocks (48px per tile)
+
+ // TODO: Get all enemies within radius and stun them
+ console.log(`๐ซ Enemies within ${stunRadius}px stunned for 3 seconds!`);
+ }
+
+ /**
+ * 13.3 - Vaping animation (always active!)
+ */
+ startVapingAnimation() {
+ // Constant vaping
+ setInterval(() => {
+ this.exhaleVapeSmoke();
+ }, 3000); // Every 3 seconds
+ }
+
+ /**
+ * 13.3 - Exhale vape smoke
+ */
+ exhaleVapeSmoke() {
+ if (!this.vapeDevice) return;
+
+ // Create pink smoke particles
+ const smokeCount = 10;
+ for (let i = 0; i < smokeCount; i++) {
+ setTimeout(() => {
+ this.createSmokeParticle();
+ }, i * 50);
+ }
+
+ // Random smoke trick
+ if (Math.random() < 0.2) { // 20% chance
+ this.performSmokeTrick();
+ }
+ }
+
+ /**
+ * 13.3 - Create smoke particle
+ */
+ createSmokeParticle() {
+ if (!this.grok) return;
+
+ const smoke = this.scene.add.circle(
+ this.grok.x + 20,
+ this.grok.y - 10,
+ 5 + Math.random() * 5,
+ 0xFF1493, // Deep pink
+ 0.7
+ );
+
+ // Smoke rises and fades
+ this.scene.tweens.add({
+ targets: smoke,
+ y: smoke.y - 50 - Math.random() * 50,
+ x: smoke.x + (Math.random() - 0.5) * 30,
+ alpha: 0,
+ radius: 20,
+ duration: 2000 + Math.random() * 1000,
+ onComplete: () => smoke.destroy()
+ });
+
+ this.smokeParticles.push(smoke);
+ }
+
+ /**
+ * 13.3 - Smoke tricks
+ */
+ performSmokeTrick() {
+ const tricks = ['rings', 'dragon', 'tornado'];
+ const trick = Phaser.Utils.Array.GetRandom(tricks);
+
+ switch (trick) {
+ case 'rings':
+ this.createSmokeRings();
+ break;
+ case 'dragon':
+ this.createSmokeDragon();
+ break;
+ case 'tornado':
+ this.createSmokeTornado();
+ break;
+ }
+
+ console.log(`๐จ Grok did a ${trick} smoke trick!`);
+ }
+
+ /**
+ * 13.3 - Create smoke rings
+ */
+ createSmokeRings() {
+ if (!this.grok) return;
+
+ for (let i = 0; i < 3; i++) {
+ setTimeout(() => {
+ const ring = this.scene.add.circle(
+ this.grok.x + 30,
+ this.grok.y - 20,
+ 10,
+ null
+ );
+ ring.setStrokeStyle(3, 0xFF1493, 0.8);
+
+ this.scene.tweens.add({
+ targets: ring,
+ x: ring.x + 100,
+ radius: 20,
+ alpha: 0,
+ duration: 1500,
+ onComplete: () => ring.destroy()
+ });
+ }, i * 300);
+ }
+ }
+
+ /**
+ * 13.3 - Create smoke dragon
+ */
+ createSmokeDragon() {
+ if (!this.grok) return;
+
+ // Create flowing smoke path (dragon shape!)
+ const points = [];
+ for (let i = 0; i < 20; i++) {
+ points.push({
+ x: this.grok.x + 30 + i * 10,
+ y: this.grok.y - 20 + Math.sin(i * 0.5) * 20
+ });
+ }
+
+ points.forEach((point, index) => {
+ setTimeout(() => {
+ const smoke = this.scene.add.circle(
+ point.x,
+ point.y,
+ 8,
+ 0xFF1493,
+ 0.6
+ );
+
+ this.scene.tweens.add({
+ targets: smoke,
+ alpha: 0,
+ duration: 1000,
+ onComplete: () => smoke.destroy()
+ });
+ }, index * 50);
+ });
+ }
+
+ /**
+ * 13.3 - Create smoke tornado
+ */
+ createSmokeTornado() {
+ if (!this.grok) return;
+
+ // Spiral smoke upward
+ for (let i = 0; i < 30; i++) {
+ setTimeout(() => {
+ const angle = (i * 20) * Math.PI / 180;
+ const radius = 20 + i;
+ const smoke = this.scene.add.circle(
+ this.grok.x + 30 + Math.cos(angle) * radius,
+ this.grok.y - 20 - i * 3,
+ 5,
+ 0xFF1493,
+ 0.7
+ );
+
+ this.scene.tweens.add({
+ targets: smoke,
+ alpha: 0,
+ duration: 2000,
+ onComplete: () => smoke.destroy()
+ });
+ }, i * 30);
+ }
+ }
+
+ /**
+ * 13.3 - Combat smoke screen
+ */
+ deploySmokeScreen() {
+ console.log('๐จ Deploying combat smoke screen!');
+
+ // Create large pink smoke cloud
+ const smokescreenRadius = 240; // 5 blocks
+
+ for (let i = 0; i < 50; i++) {
+ setTimeout(() => {
+ const angle = Math.random() * Math.PI * 2;
+ const distance = Math.random() * smokescreenRadius;
+
+ const smoke = this.scene.add.circle(
+ this.grok.x + Math.cos(angle) * distance,
+ this.grok.y + Math.sin(angle) * distance,
+ 10 + Math.random() * 10,
+ 0xFF1493,
+ 0.8
+ );
+
+ this.scene.tweens.add({
+ targets: smoke,
+ alpha: 0,
+ radius: 30,
+ duration: 5000,
+ onComplete: () => smoke.destroy()
+ });
+ }, i * 50);
+ }
+
+ // Confuse enemies
+ this.confuseNearbyEnemies();
+
+ this.showNotification({
+ title: 'Smoke Screen!',
+ text: '๐จ Enemies confused! Grok vanishes into pink smoke!',
+ icon: '๐ต'
+ });
+ }
+
+ /**
+ * 13.3 - Confuse enemies
+ */
+ confuseNearbyEnemies() {
+ // TODO: Apply confusion effect to enemies
+ console.log('๐ต Enemies confused!');
+ }
+
+ /**
+ * 13.4 - Get Grok dialogue
+ */
+ getRandomGrokDialogue() {
+ const dialogues = [
+ "The gong speaks to those who listen... *BOOONG!*",
+ "Life is like vape smoke... fleeting and pink. *exhales*",
+ "Why fight when you can meditate? *hits vape*",
+ "The universe is just a massive gong, friend. *BOOONG!*",
+ "*BOOOONG!* Inner peace achieved.",
+ "This vape? It's pink because life is beautiful. *exhales rainbow smoke*",
+ "My gong has defeated more enemies than any sword. *taps gong*",
+ "Violence is temporary. Zen is eternal. *vapes peacefully*",
+ "Watch this smoke trick! *creates dragon*",
+ "The gong's vibration aligns the chakras. Science!",
+ "*BOOONG!* That's me saying hello.",
+ "Pink smoke = happy thoughts. Simple. *exhales*"
+ ];
+
+ return Phaser.Utils.Array.GetRandom(dialogues);
+ }
+
+ /**
+ * Update system
+ */
+ update(time, delta) {
+ // Check for morning meditation time
+ const currentHour = this.scene.timeSystem?.getCurrentHour() || 0;
+ if (currentHour === this.meditationTime) {
+ const lastMeditation = localStorage.getItem('grok_last_meditation');
+ const today = new Date().toDateString();
+
+ if (lastMeditation !== today) {
+ this.triggerMorningMeditation();
+ localStorage.setItem('grok_last_meditation', today);
+ }
+ }
+
+ // Update buff timers
+ this.updateBuffs();
+
+ // Clean up old smoke particles
+ this.cleanupSmokeParticles();
+ }
+
+ /**
+ * Update active buffs
+ */
+ updateBuffs() {
+ const now = Date.now();
+
+ this.activeBuffs.forEach((buff, target) => {
+ if (buff.expiresAt && buff.expiresAt < now) {
+ this.activeBuffs.delete(target);
+ console.log(`โจ Buff "${buff.name}" expired for ${target}`);
+ }
+ });
+ }
+
+ /**
+ * Cleanup old smoke particles
+ */
+ cleanupSmokeParticles() {
+ this.smokeParticles = this.smokeParticles.filter(smoke => {
+ if (!smoke || smoke.alpha <= 0) {
+ if (smoke) smoke.destroy();
+ return false;
+ }
+ return true;
+ });
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+
+ /**
+ * Create Susi companion (Hot Dog Hunter)
+ */
+ createSusi() {
+ if (!this.grok) return;
+
+ // Create Susi (dachshund-style hot dog hunter!)
+ this.susi = this.scene.add.ellipse(
+ this.grok.x + 30,
+ this.grok.y + 20,
+ 40, // Width (long dog!)
+ 20, // Height
+ 0x8B4513 // Brown color
+ );
+ this.susi.setDepth(this.grok.depth);
+
+ // Add spots
+ const spot1 = this.scene.add.circle(
+ this.susi.x - 10,
+ this.susi.y,
+ 4,
+ 0x654321 // Darker spot
+ );
+ spot1.setDepth(this.grok.depth + 1);
+
+ // Susi's nose (always sniffing for hot dogs!)
+ const nose = this.scene.add.circle(
+ this.susi.x + 20,
+ this.susi.y,
+ 3,
+ 0x000000 // Black nose
+ );
+ nose.setDepth(this.grok.depth + 2);
+
+ // Tail (wagging animation!)
+ const tail = this.scene.add.line(
+ this.susi.x - 20,
+ this.susi.y - 5,
+ 0, 0,
+ -10, -5,
+ 0x8B4513,
+ 1
+ );
+ tail.setLineWidth(3);
+ tail.setDepth(this.grok.depth);
+
+ // Tail wag animation
+ this.scene.tweens.add({
+ targets: tail,
+ angle: { from: -15, to: 15 },
+ duration: 300,
+ yoyo: true,
+ repeat: -1
+ });
+
+ console.log('๐ Susi created! Hot dog hunter ready!');
+
+ // Susi behavior
+ this.startSusiBehavior();
+ }
+
+ /**
+ * Susi's AI behavior
+ */
+ startSusiBehavior() {
+ // Susi follows Grok
+ setInterval(() => {
+ if (!this.susi || !this.grok) return;
+
+ // Follow Grok at distance
+ const targetX = this.grok.x + 30;
+ const targetY = this.grok.y + 20;
+
+ // Smooth follow
+ this.susi.x += (targetX - this.susi.x) * 0.1;
+ this.susi.y += (targetY - this.susi.y) * 0.1;
+
+ // Random hot dog hunting
+ if (Math.random() < 0.01) { // 1% chance per frame
+ this.susiHuntHotDog();
+ }
+ }, 100);
+ }
+
+ /**
+ * Susi hunts for hot dogs!
+ */
+ susiHuntHotDog() {
+ if (!this.susi) return;
+
+ console.log('๐ญ Susi spotted a potential hot dog!');
+ this.susiState = 'hunting';
+
+ // Susi runs to random location
+ const randomX = this.susi.x + (Math.random() - 0.5) * 100;
+ const randomY = this.susi.y + (Math.random() - 0.5) * 100;
+
+ this.scene.tweens.add({
+ targets: this.susi,
+ x: randomX,
+ y: randomY,
+ duration: 1000,
+ ease: 'Quad.InOut',
+ onComplete: () => {
+ // Found hot dog!
+ this.susiState = 'eating';
+ console.log('๐ญ Susi found a hot dog! *nom nom*');
+
+ // Eating animation (wiggle)
+ this.scene.tweens.add({
+ targets: this.susi,
+ angle: { from: -5, to: 5 },
+ duration: 200,
+ yoyo: true,
+ repeat: 5,
+ onComplete: () => {
+ this.susiState = 'following';
+ this.susi.setAngle(0);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * ADHD Focus Mode - Grok hides in hoodie to focus
+ */
+ enterFocusMode(topic = 'coding') {
+ if (this.isFocused) {
+ console.log('โ ๏ธ Already in focus mode!');
+ return false;
+ }
+
+ console.log(`๐ง Grok enters ADHD FOCUS MODE! Topic: ${topic}`);
+ console.log('๐ *Hides in oversized hoodie*');
+
+ this.isFocused = true;
+ this.currentTopic = topic;
+ this.focusTimer = 0;
+
+ // Visual: Grok shrinks into hoodie
+ if (this.grok) {
+ this.scene.tweens.add({
+ targets: this.grok,
+ scale: 0.7, // Gets smaller (hiding in hoodie)
+ alpha: 0.8,
+ duration: 500
+ });
+ }
+
+ // Can't be interrupted unless you have vape juice!
+ this.showNotification({
+ title: 'Focus Mode Active!',
+ text: `๐ง Grok is focusing on ${topic}. Don't interrupt! (Unless you have vape juice)`,
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ /**
+ * Exit ADHD focus mode
+ */
+ exitFocusMode() {
+ if (!this.isFocused) return;
+
+ console.log('๐ง Focus mode complete!');
+ this.isFocused = false;
+
+ // Visual: Grok emerges from hoodie
+ if (this.grok) {
+ this.scene.tweens.add({
+ targets: this.grok,
+ scale: 1.0,
+ alpha: 1.0,
+ duration: 500
+ });
+ }
+
+ this.showNotification({
+ title: 'Focus Complete!',
+ text: `โ
${this.currentTopic} finished! Grok emerges victorious!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * ADHD Hyperfocus Movement
+ */
+ moveWithHyperfocus(direction) {
+ if (!this.isFocused) {
+ console.log('โ ๏ธ Not in focus mode!');
+ return;
+ }
+
+ // When hyperfocused, Grok moves SUPER fast!
+ const baseSpeed = 5;
+ const speed = baseSpeed * this.hyperfocusSpeed; // 2x speed!
+
+ console.log(`โก HYPERFOCUS SPEED! Moving ${direction} at ${speed} px/frame!`);
+
+ // Move Grok super fast
+ // (Integration with movement system would go here)
+ }
+
+ /**
+ * Get Grok's quest dialogues
+ */
+ getGrokQuests() {
+ return [
+ {
+ id: 'hoodie_rescue',
+ title: 'Hoodie v nevarnosti',
+ dialogue: "Dude, moj najljubลกi hoodie se je zataknil za eno tistih piranha rastlin v coni 4. Brez njega se ne morem fokusirati, preveฤ me zebe v roke! Greลก ponj?",
+ objective: 'Premagaj gigantsko mesojedko in reลกi Gronkov ลกiroki pulover',
+ rewards: {
+ gold: 500,
+ xp: 1000,
+ item: 'grok_friendship +10'
+ }
+ },
+ {
+ id: 'vape_mixology',
+ title: 'Zamenjava tekoฤine (Vape Mixology)',
+ dialogue: "Bro, poskuลกam zmeลกati nov okus 'Baggy Cloud', ampak Susi mi je prevrnila epruveto, ker je mislila, da so notri hrenovke. Rabim tri mutirane jagode iz Dino Valleyja!",
+ objective: 'Najdi 3 mutirane jagode v Dino Valley biome',
+ rewards: {
+ gold: 300,
+ xp: 750,
+ item: 'baggy_cloud_vape_juice'
+ }
+ },
+ {
+ id: 'adhd_code',
+ title: 'ADHD koda na hlaฤah',
+ dialogue: "Ej, si vedel, da sem si na nogo (na hlaฤe) napisal pomembno kodo za tvoj novi rudnik, pa sem jo zdaj ponesreฤi umazal z blatom? Susi, pomagaj mi polizati to blato... ah, ne, Kai, ti boลก moral najti ฤistilo!",
+ objective: 'Najdi ฤistilo v opuลกฤenem laboratoriju',
+ rewards: {
+ gold: 400,
+ xp: 850,
+ unlock: 'advanced_mine_code'
+ }
+ }
+ ];
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+
+ /**
+ * Get active buff for target
+ */
+ getActiveBuff(target) {
+ return this.activeBuffs.get(target);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/HIPOAudioSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/HIPOAudioSystem.js
new file mode 100644
index 000000000..8b305015d
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/HIPOAudioSystem.js
@@ -0,0 +1,437 @@
+/**
+ * HIPOAudioSystem.js
+ *
+ * COMPLETE AUDIO MANAGER - Demo Ready!
+ *
+ * Features:
+ * - [AI_VOICE] detection in dialogue
+ * - Typewriter effect with character blips
+ * - Farm animal proximity sounds
+ * - Combat SFX integration
+ * - Noir City ambient (HIPODEVIL666CITY)
+ * - Xbox haptic feedback
+ *
+ * Created: Jan 10, 2026
+ * Author: David "HIPO" Kotnik
+ * Studio: Hipodevil666 Studiosโข
+ */
+
+export default class HIPOAudioSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Audio references
+ this.aiVoices = {};
+ this.sfx = {};
+ this.ambient = {};
+ this.blips = {};
+
+ // Dialogue state
+ this.currentDialogue = null;
+ this.typewriterActive = false;
+
+ // Farm animal timers
+ this.animalTimers = {};
+
+ // Haptic enabled
+ this.hapticEnabled = true;
+
+ console.log('๐๏ธ HIPO Audio System initialized!');
+ }
+
+ /**
+ * Preload all audio assets
+ */
+ preloadAssets() {
+ const scene = this.scene;
+
+ console.log('๐ต Loading HIPO Audio System assets...');
+
+ // === AI VOICES (Edge-TTS Generated) ===
+ console.log(' ๐ AI Voices...');
+
+ // Gronk (Deep, Gritty Male)
+ for (let i = 1; i <= 8; i++) {
+ scene.load.audio(`gronk_voice_${i}`,
+ `assets/audio/voice/gronk/gronk_phrase_${String(i).padStart(2, '0')}.ogg`);
+ }
+
+ // Kai (Energetic, Bold - FEMALE!)
+ for (let i = 1; i <= 8; i++) {
+ scene.load.audio(`kai_voice_${i}`,
+ `assets/audio/voice/kai/kai_phrase_${String(i).padStart(2, '0')}.ogg`);
+ }
+
+ // Ana (Mysterious, Calm Female)
+ for (let i = 1; i <= 8; i++) {
+ scene.load.audio(`ana_voice_${i}`,
+ `assets/audio/voice/ana/ana_phrase_${String(i).padStart(2, '0')}.ogg`);
+ }
+
+ // === FARMING & NATURE SFX ===
+ console.log(' ๐ Farming & Nature...');
+ scene.load.audio('sfx_sheep', 'assets/audio/sfx/farming/sheep.ogg');
+ scene.load.audio('sfx_pig', 'assets/audio/sfx/farming/pig.ogg');
+ scene.load.audio('sfx_chicken', 'assets/audio/sfx/farming/chicken.ogg');
+ scene.load.audio('sfx_horse', 'assets/audio/sfx/farming/horse.ogg');
+ scene.load.audio('sfx_goat', 'assets/audio/sfx/farming/goat.ogg');
+ scene.load.audio('sfx_cow', 'assets/audio/sfx/farming/cow.ogg');
+
+ // === COMBAT SFX ===
+ console.log(' โ๏ธ Combat...');
+ scene.load.audio('sfx_zombie_hit', 'assets/audio/sfx/combat/zombie_hit.ogg');
+ scene.load.audio('sfx_zombie_death', 'assets/audio/sfx/combat/zombie_death.ogg');
+ scene.load.audio('sfx_player_hurt', 'assets/audio/sfx/combat/player_hurt.ogg');
+
+ // === AMBIENT ===
+ console.log(' ๐ Ambient...');
+ scene.load.audio('ambient_noir_city', 'assets/audio/ambient/noir_city_echo.ogg');
+ scene.load.audio('ambient_farm_wind', 'assets/audio/ambient/wind_loop.ogg');
+ scene.load.audio('ambient_crickets', 'assets/audio/ambient/crickets_loop.ogg');
+
+ // === TYPEWRITER BLIPS (Character-specific) ===
+ console.log(' โจ๏ธ Typewriter blips...');
+ scene.load.audio('blip_gronk', 'assets/audio/ui/typewriter_low.ogg');
+ scene.load.audio('blip_kai', 'assets/audio/ui/typewriter_high.ogg');
+ scene.load.audio('blip_ana', 'assets/audio/ui/typewriter_mid.ogg');
+ scene.load.audio('blip_npc', 'assets/audio/ui/typewriter_normal.ogg');
+
+ console.log('โ
HIPO Audio System assets queued!');
+ }
+
+ /**
+ * Initialize audio after preload
+ */
+ initialize() {
+ const scene = this.scene;
+
+ console.log('๐ต Initializing HIPO Audio System...');
+
+ // === AI VOICES ===
+ this.aiVoices = {
+ gronk: [],
+ kai: [],
+ ana: []
+ };
+
+ for (let i = 1; i <= 8; i++) {
+ this.aiVoices.gronk.push(scene.sound.add(`gronk_voice_${i}`, { volume: 0.8 }));
+ this.aiVoices.kai.push(scene.sound.add(`kai_voice_${i}`, { volume: 0.75 }));
+ this.aiVoices.ana.push(scene.sound.add(`ana_voice_${i}`, { volume: 0.7 }));
+ }
+
+ // === FARMING & NATURE ===
+ this.sfx.farming = {
+ sheep: scene.sound.add('sfx_sheep', { volume: 0.4 }),
+ pig: scene.sound.add('sfx_pig', { volume: 0.4 }),
+ chicken: scene.sound.add('sfx_chicken', { volume: 0.35 }),
+ horse: scene.sound.add('sfx_horse', { volume: 0.5 }),
+ goat: scene.sound.add('sfx_goat', { volume: 0.4 }),
+ cow: scene.sound.add('sfx_cow', { volume: 0.45 })
+ };
+
+ // === COMBAT ===
+ this.sfx.combat = {
+ zombieHit: scene.sound.add('sfx_zombie_hit', { volume: 0.6 }),
+ zombieDeath: scene.sound.add('sfx_zombie_death', { volume: 0.7 }),
+ playerHurt: scene.sound.add('sfx_player_hurt', { volume: 0.8 })
+ };
+
+ // === AMBIENT ===
+ this.ambient = {
+ noirCity: scene.sound.add('ambient_noir_city', {
+ loop: true,
+ volume: 0.2 // Noir city with echo
+ }),
+ farmWind: scene.sound.add('ambient_farm_wind', {
+ loop: true,
+ volume: 0.2
+ }),
+ crickets: scene.sound.add('ambient_crickets', {
+ loop: true,
+ volume: 0.25
+ })
+ };
+
+ // === TYPEWRITER BLIPS ===
+ this.blips = {
+ gronk: scene.sound.add('blip_gronk', { volume: 0.15 }),
+ kai: scene.sound.add('blip_kai', { volume: 0.13 }),
+ ana: scene.sound.add('blip_ana', { volume: 0.12 }),
+ npc: scene.sound.add('blip_npc', { volume: 0.1 })
+ };
+
+ console.log('โ
HIPO Audio System ready!');
+ }
+
+ /**
+ * Play dialogue with smart AI/Typewriter detection
+ *
+ * Format: "[AI_VOICE:character:phraseNumber] Text here"
+ * Example: "[AI_VOICE:gronk:2] Pink is best color!"
+ *
+ * OR standard: "Just normal text" (uses typewriter)
+ */
+ playDialogue(text, character = 'npc', onComplete = null) {
+ // Check for [AI_VOICE] tag
+ const aiVoiceMatch = text.match(/\[AI_VOICE:(\w+):(\d+)\]\s*(.+)/);
+
+ if (aiVoiceMatch) {
+ // AI VOICE MODE
+ const voiceChar = aiVoiceMatch[1]; // gronk/kai/ana
+ const phraseNum = parseInt(aiVoiceMatch[2]);
+ const displayText = aiVoiceMatch[3];
+
+ console.log(`๐๏ธ AI VOICE: ${voiceChar} phrase ${phraseNum}`);
+ console.log(` Text: "${displayText}"`);
+
+ // Play AI voice
+ this.playAIVoice(voiceChar, phraseNum, onComplete);
+
+ // Show text instantly (no typewriter for AI voice)
+ this.showDialogueText(displayText, character, true);
+
+ // Light haptic for voice
+ this.vibrateLight();
+
+ } else {
+ // TYPEWRITER MODE
+ console.log(`โจ๏ธ TYPEWRITER: ${character}`);
+ console.log(` Text: "${text}"`);
+
+ // Show text with typewriter effect
+ this.showDialogueText(text, character, false, onComplete);
+ }
+ }
+
+ /**
+ * Play AI voice
+ */
+ playAIVoice(character, phraseNumber, onComplete = null) {
+ const voices = this.aiVoices[character];
+
+ if (!voices || !voices[phraseNumber - 1]) {
+ console.warn(`โ ๏ธ AI Voice not found: ${character} phrase ${phraseNumber}`);
+ return;
+ }
+
+ const voice = voices[phraseNumber - 1];
+ voice.play();
+
+ if (onComplete) {
+ voice.once('complete', onComplete);
+ }
+ }
+
+ /**
+ * Show dialogue text (with or without typewriter)
+ */
+ showDialogueText(text, character, instant = false, onComplete = null) {
+ // This would integrate with your DialogueSystem
+ // For now, just log
+ console.log(` Display: "${text}" (instant: ${instant})`);
+
+ if (!instant) {
+ // Typewriter effect with character-specific blip
+ this.typewriterEffect(text, character, onComplete);
+ } else {
+ // Instant display
+ if (onComplete) onComplete();
+ }
+ }
+
+ /**
+ * Typewriter effect with blips
+ */
+ typewriterEffect(text, character, onComplete) {
+ this.typewriterActive = true;
+
+ const blip = this.blips[character] || this.blips.npc;
+ const speed = 50; // ms per character
+
+ let index = 0;
+
+ const typeInterval = setInterval(() => {
+ if (index < text.length) {
+ const char = text[index];
+
+ // Play blip (skip spaces)
+ if (char !== ' ' && !blip.isPlaying) {
+ blip.play();
+ }
+
+ index++;
+ } else {
+ clearInterval(typeInterval);
+ this.typewriterActive = false;
+ if (onComplete) onComplete();
+ }
+ }, speed);
+ }
+
+ /**
+ * Start farm animal sounds (proximity-based random)
+ */
+ startFarmAnimalSounds() {
+ const animals = ['sheep', 'pig', 'chicken', 'horse', 'goat', 'cow'];
+
+ animals.forEach(animal => {
+ this.animalTimers[animal] = this.scene.time.addEvent({
+ delay: Phaser.Math.Between(5000, 15000),
+ callback: () => {
+ this.playFarmAnimal(animal);
+ },
+ loop: true
+ });
+ });
+
+ console.log('๐ Farm animal sounds started!');
+ }
+
+ /**
+ * Play farm animal sound (checks proximity to Kai)
+ */
+ playFarmAnimal(animalType) {
+ const sound = this.sfx.farming[animalType];
+
+ if (!sound || sound.isPlaying) return;
+
+ // Check proximity to player (if player exists)
+ const player = this.scene.player || this.scene.kai;
+
+ if (player) {
+ // Simplified: just play (proximity would check farm location)
+ // In full game, check distance to farm area
+ const nearFarm = true; // TODO: Implement farm proximity
+
+ if (nearFarm) {
+ sound.play();
+ console.log(`๐ ${animalType} sound!`);
+ }
+ }
+ }
+
+ /**
+ * Stop farm animal sounds
+ */
+ stopFarmAnimalSounds() {
+ Object.values(this.animalTimers).forEach(timer => {
+ if (timer) timer.remove();
+ });
+ this.animalTimers = {};
+ console.log('๐ Farm animals stopped');
+ }
+
+ /**
+ * Play combat sound with haptic
+ */
+ playCombat(type) {
+ const soundMap = {
+ 'hit': 'zombieHit',
+ 'death': 'zombieDeath',
+ 'hurt': 'playerHurt'
+ };
+
+ const soundKey = soundMap[type];
+ const sound = this.sfx.combat[soundKey];
+
+ if (!sound) {
+ console.warn(`โ ๏ธ Combat sound not found: ${type}`);
+ return;
+ }
+
+ sound.play();
+
+ // Haptic feedback
+ if (type === 'hurt') {
+ this.vibrateStrong(400); // Strong for player damage
+ } else {
+ this.vibrateStrong(200); // Normal for zombie
+ }
+
+ console.log(`โ๏ธ Combat: ${type}`);
+ }
+
+ /**
+ * Play ambient for location
+ */
+ playAmbient(location) {
+ // Stop all ambient
+ Object.values(this.ambient).forEach(amb => amb.stop());
+
+ let ambient = null;
+
+ switch (location) {
+ case 'city':
+ case 'HIPODEVIL666CITY':
+ ambient = this.ambient.noirCity;
+ console.log('๐ Noir City ambient (with echo)');
+ break;
+ case 'farm':
+ case 'grassland':
+ ambient = this.ambient.farmWind;
+ console.log('๐พ Farm ambient');
+ break;
+ case 'night':
+ ambient = this.ambient.crickets;
+ console.log('๐ Night ambient');
+ break;
+ }
+
+ if (ambient) {
+ ambient.play();
+ }
+ }
+
+ /**
+ * Stop all ambient
+ */
+ stopAmbient() {
+ Object.values(this.ambient).forEach(amb => amb.stop());
+ }
+
+ /**
+ * Light haptic feedback
+ */
+ vibrateLight() {
+ this.vibrate(100, 0.3, 0.5);
+ }
+
+ /**
+ * Strong haptic feedback
+ */
+ vibrateStrong(duration = 300) {
+ this.vibrate(duration, 0.7, 1.0);
+ }
+
+ /**
+ * Xbox controller vibration
+ */
+ vibrate(duration, weak, strong) {
+ if (!this.hapticEnabled) return;
+
+ const scene = this.scene;
+
+ if (scene.input.gamepad && scene.input.gamepad.total > 0) {
+ const pad = scene.input.gamepad.getPad(0);
+
+ if (pad && pad.vibration) {
+ pad.vibration.playEffect('dual-rumble', {
+ startDelay: 0,
+ duration: duration,
+ weakMagnitude: weak,
+ strongMagnitude: strong
+ });
+ }
+ }
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ this.stopAmbient();
+ this.stopFarmAnimalSounds();
+ console.log('๐ HIPO Audio System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/HordeWaveSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/HordeWaveSystem.js
new file mode 100644
index 000000000..7c61e1253
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/HordeWaveSystem.js
@@ -0,0 +1,435 @@
+/**
+ * HordeWaveSystem.js
+ * ==================
+ * KRVAVA ลฝETEV - Horde Mode Wave Manager
+ *
+ * Features:
+ * - Wave spawning system
+ * - Difficulty scaling
+ * - Enemy type pools
+ * - Boss waves
+ * - Rewards
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class HordeWaveSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Wave state
+ this.currentWave = 0;
+ this.isWaveActive = false;
+ this.waveStartTime = 0;
+ this.enemiesRemaining = 0;
+ this.enemiesKilled = 0;
+
+ // Spawn points
+ this.spawnPoints = [];
+ this.spawnRadius = 500; // Pixels from player
+
+ // Wave definitions
+ this.waves = [];
+
+ // Statistics
+ this.stats = {
+ totalWavesCompleted: 0,
+ totalEnemiesKilled: 0,
+ highestWave: 0,
+ totalRewards: 0
+ };
+
+ console.log('๐ HordeWaveSystem initialized');
+
+ // Generate wave definitions
+ this.generateWaves();
+ }
+
+ /**
+ * Generate infinite wave definitions
+ */
+ generateWaves() {
+ // Pre-generate 100 waves (can generate more on-demand)
+ for (let i = 1; i <= 100; i++) {
+ this.waves.push(this.generateWave(i));
+ }
+
+ console.log(`โ
Generated ${this.waves.length} wave definitions`);
+ }
+
+ /**
+ * Generate single wave definition
+ */
+ generateWave(waveNumber) {
+ const wave = {
+ number: waveNumber,
+ enemies: [],
+ isBossWave: waveNumber % 10 === 0, // Every 10th wave is boss
+ rewards: {
+ zlatniki: 10 * waveNumber,
+ xp: 50 * waveNumber,
+ items: []
+ }
+ };
+
+ // Boss wave
+ if (wave.isBossWave) {
+ wave.enemies = this.generateBossWave(waveNumber);
+ } else {
+ // Normal wave
+ wave.enemies = this.generateNormalWave(waveNumber);
+ }
+
+ return wave;
+ }
+
+ /**
+ * Generate normal wave enemies
+ */
+ generateNormalWave(waveNumber) {
+ const enemies = [];
+
+ // Base enemy count scales with wave
+ const baseCount = 5 + Math.floor(waveNumber * 1.5);
+
+ // Enemy types unlock progressively
+ const availableTypes = this.getAvailableEnemyTypes(waveNumber);
+
+ for (let i = 0; i < baseCount; i++) {
+ const enemyType = Phaser.Utils.Array.GetRandom(availableTypes);
+ const enemy = {
+ type: enemyType.id,
+ health: enemyType.baseHealth * (1 + waveNumber * 0.1), // +10% HP per wave
+ damage: enemyType.baseDamage * (1 + waveNumber * 0.05), // +5% damage per wave
+ speed: enemyType.baseSpeed,
+ spawnDelay: i * 500 // Stagger spawns
+ };
+ enemies.push(enemy);
+ }
+
+ return enemies;
+ }
+
+ /**
+ * Generate boss wave
+ */
+ generateBossWave(waveNumber) {
+ const enemies = [];
+
+ // Boss
+ const bossLevel = Math.floor(waveNumber / 10);
+ const boss = {
+ type: 'boss_zombie',
+ health: 1000 * bossLevel,
+ damage: 50 * bossLevel,
+ speed: 80,
+ isBoss: true,
+ spawnDelay: 0
+ };
+ enemies.push(boss);
+
+ // Add minions (scales with boss level)
+ const minionCount = 5 + (bossLevel * 2);
+ for (let i = 0; i < minionCount; i++) {
+ const minion = {
+ type: 'elite_zombie',
+ health: 200 * bossLevel,
+ damage: 20 * bossLevel,
+ speed: 120,
+ spawnDelay: 1000 + (i * 500)
+ };
+ enemies.push(minion);
+ }
+
+ return enemies;
+ }
+
+ /**
+ * Get available enemy types for wave
+ */
+ getAvailableEnemyTypes(waveNumber) {
+ const types = [
+ // Tier 1: Waves 1-5
+ { id: 'basic_zombie', tier: 1, baseHealth: 100, baseDamage: 10, baseSpeed: 80 },
+ { id: 'crawler_zombie', tier: 1, baseHealth: 80, baseDamage: 15, baseSpeed: 100 },
+
+ // Tier 2: Waves 6-15
+ { id: 'runner_zombie', tier: 2, baseHealth: 120, baseDamage: 12, baseSpeed: 150 },
+ { id: 'spitter_zombie', tier: 2, baseHealth: 90, baseDamage: 20, baseSpeed: 70 },
+
+ // Tier 3: Waves 16-30
+ { id: 'tank_zombie', tier: 3, baseHealth: 300, baseDamage: 25, baseSpeed: 60 },
+ { id: 'exploder_zombie', tier: 3, baseHealth: 150, baseDamage: 50, baseSpeed: 90 },
+
+ // Tier 4: Waves 31-50
+ { id: 'mutant_zombie', tier: 4, baseHealth: 500, baseDamage: 40, baseSpeed: 110 },
+ { id: 'alpha_zombie', tier: 4, baseHealth: 800, baseDamage: 60, baseSpeed: 100 },
+
+ // Tier 5: Waves 51+
+ { id: 'nightmare_zombie', tier: 5, baseHealth: 1200, baseDamage: 80, baseSpeed: 130 },
+ { id: 'omega_zombie', tier: 5, baseHealth: 2000, baseDamage: 100, baseSpeed: 120 }
+ ];
+
+ // Filter by tier
+ let availableTier = 1;
+ if (waveNumber >= 51) availableTier = 5;
+ else if (waveNumber >= 31) availableTier = 4;
+ else if (waveNumber >= 16) availableTier = 3;
+ else if (waveNumber >= 6) availableTier = 2;
+
+ return types.filter(t => t.tier <= availableTier);
+ }
+
+ /**
+ * Start wave
+ */
+ startWave(waveNumber = null) {
+ if (this.isWaveActive) {
+ console.log('โ ๏ธ Wave already active!');
+ return false;
+ }
+
+ // Use next wave if not specified
+ if (waveNumber === null) {
+ waveNumber = this.currentWave + 1;
+ }
+
+ // Generate more waves if needed
+ if (waveNumber > this.waves.length) {
+ for (let i = this.waves.length + 1; i <= waveNumber; i++) {
+ this.waves.push(this.generateWave(i));
+ }
+ }
+
+ const wave = this.waves[waveNumber - 1];
+ if (!wave) {
+ console.error(`Wave ${waveNumber} not found!`);
+ return false;
+ }
+
+ this.currentWave = waveNumber;
+ this.isWaveActive = true;
+ this.waveStartTime = Date.now();
+ this.enemiesRemaining = wave.enemies.length;
+ this.enemiesKilled = 0;
+
+ console.log(`๐ Wave ${waveNumber} ${wave.isBossWave ? '๐ BOSS ' : ''}starting!`);
+ console.log(` Enemies: ${wave.enemies.length}`);
+
+ // Spawn enemies
+ this.spawnWaveEnemies(wave);
+
+ // Show wave notification
+ this.showNotification({
+ title: wave.isBossWave ? '๐ BOSS WAVE!' : `Wave ${waveNumber}`,
+ text: `${wave.enemies.length} enemies incoming!`,
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ /**
+ * Spawn wave enemies
+ */
+ spawnWaveEnemies(wave) {
+ wave.enemies.forEach(enemy => {
+ setTimeout(() => {
+ this.spawnEnemy(enemy);
+ }, enemy.spawnDelay || 0);
+ });
+ }
+
+ /**
+ * Spawn single enemy
+ */
+ spawnEnemy(enemyData) {
+ // Get spawn point (random around player)
+ const spawnPoint = this.getRandomSpawnPoint();
+
+ // TODO: Create actual enemy sprite
+ console.log(`๐พ Spawning ${enemyData.type} at (${spawnPoint.x}, ${spawnPoint.y})`);
+
+ // Create enemy using ZombieSystem or EnemySystem
+ // For now, just log
+ const enemy = {
+ ...enemyData,
+ x: spawnPoint.x,
+ y: spawnPoint.y,
+ isAlive: true,
+ currentHealth: enemyData.health
+ };
+
+ // TODO: Add to enemy tracking
+
+ return enemy;
+ }
+
+ /**
+ * Get random spawn point
+ */
+ getRandomSpawnPoint() {
+ const playerX = this.scene.player?.x || 0;
+ const playerY = this.scene.player?.y || 0;
+
+ // Random angle
+ const angle = Math.random() * Math.PI * 2;
+
+ // Spawn at radius distance
+ const x = playerX + Math.cos(angle) * this.spawnRadius;
+ const y = playerY + Math.sin(angle) * this.spawnRadius;
+
+ return { x, y };
+ }
+
+ /**
+ * Enemy killed callback
+ */
+ onEnemyKilled(enemy) {
+ if (!this.isWaveActive) return;
+
+ this.enemiesKilled++;
+ this.enemiesRemaining--;
+ this.stats.totalEnemiesKilled++;
+
+ console.log(`๐ Enemy killed! (${this.enemiesKilled}/${this.currentWave ? this.waves[this.currentWave - 1].enemies.length : 0})`);
+
+ // Check if wave complete
+ if (this.enemiesRemaining <= 0) {
+ this.completeWave();
+ }
+ }
+
+ /**
+ * Complete wave
+ */
+ completeWave() {
+ if (!this.isWaveActive) return;
+
+ const wave = this.waves[this.currentWave - 1];
+ const duration = Date.now() - this.waveStartTime;
+
+ this.isWaveActive = false;
+ this.stats.totalWavesCompleted++;
+ this.stats.highestWave = Math.max(this.stats.highestWave, this.currentWave);
+
+ console.log(`โ
Wave ${this.currentWave} complete!`);
+ console.log(` Time: ${Math.floor(duration / 1000)}s`);
+ console.log(` Killed: ${this.enemiesKilled}`);
+
+ // Grant rewards
+ this.grantWaveRewards(wave);
+
+ // Show completion notification
+ this.showNotification({
+ title: 'Wave Complete!',
+ text: `โ
Wave ${this.currentWave} cleared! Rewards granted!`,
+ icon: '๐'
+ });
+
+ // Auto-start next wave after delay
+ setTimeout(() => {
+ this.showWaveCountdown();
+ }, 5000);
+ }
+
+ /**
+ * Grant wave rewards
+ */
+ grantWaveRewards(wave) {
+ const rewards = wave.rewards;
+
+ console.log(`๐ Rewards:`);
+ console.log(` ๐ฐ ${rewards.zlatniki} Zlatniki`);
+ console.log(` โญ ${rewards.xp} XP`);
+
+ // TODO: Actually grant rewards to player
+ this.stats.totalRewards += rewards.zlatniki;
+
+ // Rare loot on boss waves
+ if (wave.isBossWave) {
+ console.log(` ๐ BONUS: Boss loot!`);
+ // TODO: Grant rare items
+ }
+ }
+
+ /**
+ * Show wave countdown
+ */
+ showWaveCountdown() {
+ const nextWave = this.currentWave + 1;
+
+ console.log(`โฐ Next wave (${nextWave}) in 10 seconds...`);
+
+ this.showNotification({
+ title: 'Next Wave Soon',
+ text: `โฐ Wave ${nextWave} starts in 10 seconds!`,
+ icon: 'โ๏ธ'
+ });
+
+ // Auto-start after countdown
+ setTimeout(() => {
+ this.startWave(nextWave);
+ }, 10000);
+ }
+
+ /**
+ * End horde mode
+ */
+ endHordeMode() {
+ this.isWaveActive = false;
+
+ console.log('๐ Horde mode ended');
+ console.log('๐ Final Stats:');
+ console.log(` Waves: ${this.stats.totalWavesCompleted}`);
+ console.log(` Kills: ${this.stats.totalEnemiesKilled}`);
+ console.log(` Highest: ${this.stats.highestWave}`);
+
+ this.showNotification({
+ title: 'Horde Mode Ended',
+ text: `Survived ${this.stats.highestWave} waves! ${this.stats.totalEnemiesKilled} kills!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Get wave info
+ */
+ getWaveInfo(waveNumber) {
+ return this.waves[waveNumber - 1];
+ }
+
+ /**
+ * Get statistics
+ */
+ getStats() {
+ return {
+ ...this.stats,
+ currentWave: this.currentWave,
+ isActive: this.isWaveActive
+ };
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+
+ /**
+ * Update system
+ */
+ update(delta) {
+ if (!this.isWaveActive) return;
+
+ // Update wave timer, check conditions, etc.
+ // TODO: Implement wave updates
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/HybridAbilitySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/HybridAbilitySystem.js
new file mode 100644
index 000000000..858aec1e4
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/HybridAbilitySystem.js
@@ -0,0 +1,629 @@
+/**
+ * HybridAbilitySystem.js
+ *
+ * Phase 36: Hybrid Skill - Alfa Abilities
+ * Player's unique Alfa powers to control and support zombies
+ *
+ * Abilities:
+ * - Q: Heal Zombies (regenerate HP)
+ * - E: Boost Zombies (temp strength/speed)
+ * - R: Calm Wild Zombies (easier taming)
+ * - F: Sense Danger (detect enemies)
+ *
+ * Energy System: Uses Alfa Energy (regenerates over time)
+ */
+
+class HybridAbilitySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Alfa Energy (similar to mana, but for Alfa abilities)
+ this.maxEnergy = 100;
+ this.energy = 100;
+ this.energyRegen = 5; // per second
+
+ // Ability Definitions
+ this.abilities = {
+ // Q: Heal Zombies
+ healZombies: {
+ id: 'healZombies',
+ name: 'Heal Zombies',
+ key: 'Q',
+ energyCost: 25,
+ cooldown: 8000, // 8 seconds
+ range: 150, // pixels
+ healAmount: 30,
+ description: 'Regenerate HP for all zombies in range',
+ visualEffect: 'green_glow',
+ soundEffect: 'heal_pulse'
+ },
+
+ // E: Boost Zombies
+ boostZombies: {
+ id: 'boostZombies',
+ name: 'Boost Zombies',
+ key: 'E',
+ energyCost: 30,
+ cooldown: 15000, // 15 seconds
+ range: 200,
+ duration: 10000, // 10s buff
+ speedBoost: 1.5, // +50% speed
+ damageBoost: 1.3, // +30% damage
+ efficiencyBoost: 1.5, // +50% work speed
+ description: 'Temporarily boost zombie speed, damage, and work efficiency',
+ visualEffect: 'yellow_aura',
+ soundEffect: 'power_up'
+ },
+
+ // R: Calm Wild Zombies
+ calmWildZombies: {
+ id: 'calmWildZombies',
+ name: 'Calm Wild Zombies',
+ key: 'R',
+ energyCost: 20,
+ cooldown: 12000, // 12 seconds
+ range: 250,
+ duration: 15000, // 15s peaceful state
+ tamingBonus: 0.5, // +50% taming success chance
+ description: 'Make wild zombies peaceful and easier to tame',
+ visualEffect: 'blue_ripple',
+ soundEffect: 'calm_wave'
+ },
+
+ // F: Sense Danger
+ senseDanger: {
+ id: 'senseDanger',
+ name: 'Sense Danger',
+ key: 'F',
+ energyCost: 15,
+ cooldown: 20000, // 20 seconds
+ range: 400, // large detection radius
+ duration: 8000, // 8s reveal
+ description: 'Detect all enemies in a large radius',
+ visualEffect: 'red_pulse',
+ soundEffect: 'danger_ping'
+ }
+ };
+
+ // Cooldown tracking
+ this.cooldowns = new Map();
+
+ // Active buffs (zombie IDs with their buff data)
+ this.activeBuffs = new Map();
+
+ // Revealed enemies (for Sense Danger)
+ this.revealedEnemies = new Set();
+
+ // Visual effects
+ this.visualEffects = [];
+
+ // UI references
+ this.energyBar = null;
+ this.abilityIcons = new Map();
+
+ // Player level & unlocks
+ this.playerLevel = 1; // Tracks Hybrid Skill level (1-10)
+ this.unlockedAbilities = new Set(['healZombies']); // Start with heal
+
+ this.init();
+ }
+
+ init() {
+ this.createUI();
+ this.setupInput();
+ this.setupEventListeners();
+
+ // Start energy regen
+ this.scene.time.addEvent({
+ delay: 1000,
+ callback: this.regenerateEnergy,
+ callbackScope: this,
+ loop: true
+ });
+
+ console.log('[HybridAbilitySystem] Initialized - Alfa powers ready');
+ }
+
+ // ==================== INPUT HANDLING ====================
+
+ setupInput() {
+ // Ability hotkeys
+ this.scene.input.keyboard.on('keydown-Q', () => this.useAbility('healZombies'));
+ this.scene.input.keyboard.on('keydown-E', () => this.useAbility('boostZombies'));
+ this.scene.input.keyboard.on('keydown-R', () => this.useAbility('calmWildZombies'));
+ this.scene.input.keyboard.on('keydown-F', () => this.useAbility('senseDanger'));
+ }
+
+ setupEventListeners() {
+ // Listen for zombie level up to award player XP
+ this.scene.events.on('zombieLevelUp', (zombieId, newLevel) => {
+ this.addPlayerXP(5); // 5 XP per zombie level
+ });
+
+ // Listen for zombie death (inheritance)
+ this.scene.events.on('zombieDeath', (zombieId, finalLevel) => {
+ this.addPlayerXP(finalLevel * 2); // 2 XP per zombie's final level
+ });
+ }
+
+ // ==================== ABILITY USAGE ====================
+
+ useAbility(abilityId) {
+ const ability = this.abilities[abilityId];
+
+ if (!ability) {
+ console.warn(`[HybridAbilitySystem] Unknown ability: ${abilityId}`);
+ return false;
+ }
+
+ // Check if unlocked
+ if (!this.unlockedAbilities.has(abilityId)) {
+ this.scene.uiSystem?.showMessage(`Ability locked! Unlock at Hybrid Skill level ${this.getUnlockLevel(abilityId)}`);
+ return false;
+ }
+
+ // Check cooldown
+ if (this.isOnCooldown(abilityId)) {
+ const remaining = this.getCooldownRemaining(abilityId);
+ this.scene.uiSystem?.showMessage(`${ability.name} on cooldown: ${(remaining / 1000).toFixed(1)}s`);
+ return false;
+ }
+
+ // Check energy cost
+ if (this.energy < ability.energyCost) {
+ this.scene.uiSystem?.showMessage(`Not enough Alfa Energy! (${this.energy}/${ability.energyCost})`);
+ return false;
+ }
+
+ // Execute ability
+ console.log(`[HybridAbilitySystem] Using ${ability.name}`);
+ this.consumeEnergy(ability.energyCost);
+ this.startCooldown(abilityId, ability.cooldown);
+
+ // Play sound
+ if (ability.soundEffect) {
+ this.scene.sound.play(ability.soundEffect, { volume: 0.5 });
+ }
+
+ // Execute specific ability logic
+ switch (abilityId) {
+ case 'healZombies':
+ this.executeHeal(ability);
+ break;
+ case 'boostZombies':
+ this.executeBoost(ability);
+ break;
+ case 'calmWildZombies':
+ this.executeCalm(ability);
+ break;
+ case 'senseDanger':
+ this.executeSense(ability);
+ break;
+ }
+
+ return true;
+ }
+
+ // ==================== ABILITY EXECUTIONS ====================
+
+ executeHeal(ability) {
+ const player = this.scene.player;
+ const zombieSystem = this.scene.zombieSystem;
+
+ if (!zombieSystem) return;
+
+ let healed = 0;
+
+ // Find all zombies in range
+ zombieSystem.zombies.forEach((zombie, zombieId) => {
+ const dist = Phaser.Math.Distance.Between(
+ player.x, player.y,
+ zombie.sprite.x, zombie.sprite.y
+ );
+
+ if (dist <= ability.range && zombie.state !== 'dead') {
+ // Heal zombie
+ const oldHp = zombie.hp;
+ zombie.hp = Math.min(zombie.hp + ability.healAmount, zombie.maxHp);
+ healed++;
+
+ // Visual: Green heal particles
+ this.createHealEffect(zombie.sprite.x, zombie.sprite.y);
+
+ // Show floating text
+ this.showFloatingText(zombie.sprite.x, zombie.sprite.y,
+ `+${zombie.hp - oldHp} HP`, 0x00ff00);
+
+ console.log(`[HybridAbilitySystem] Healed zombie ${zombieId}: ${oldHp} โ ${zombie.hp}`);
+ }
+ });
+
+ // Player feedback
+ this.createPlayerEffect(ability.visualEffect);
+ this.scene.uiSystem?.showMessage(`Healed ${healed} zombie(s)!`, 0x00ff00);
+ }
+
+ executeBoost(ability) {
+ const player = this.scene.player;
+ const zombieSystem = this.scene.zombieSystem;
+
+ if (!zombieSystem) return;
+
+ let boosted = 0;
+
+ zombieSystem.zombies.forEach((zombie, zombieId) => {
+ const dist = Phaser.Math.Distance.Between(
+ player.x, player.y,
+ zombie.sprite.x, zombie.sprite.y
+ );
+
+ if (dist <= ability.range && zombie.state !== 'dead') {
+ // Apply buff
+ this.applyBoostBuff(zombieId, zombie, ability);
+ boosted++;
+
+ // Visual: Yellow aura
+ this.createBoostEffect(zombie.sprite);
+
+ console.log(`[HybridAbilitySystem] Boosted zombie ${zombieId} for ${ability.duration}ms`);
+ }
+ });
+
+ this.createPlayerEffect(ability.visualEffect);
+ this.scene.uiSystem?.showMessage(`Boosted ${boosted} zombie(s)!`, 0xffff00);
+ }
+
+ executeCalm(ability) {
+ const player = this.scene.player;
+ const zombieSystem = this.scene.zombieSystem;
+
+ if (!zombieSystem) return;
+
+ let calmed = 0;
+
+ zombieSystem.zombies.forEach((zombie, zombieId) => {
+ const dist = Phaser.Math.Distance.Between(
+ player.x, player.y,
+ zombie.sprite.x, zombie.sprite.y
+ );
+
+ if (dist <= ability.range && zombie.state === 'wild') {
+ // Apply calm buff
+ this.applyCalmBuff(zombieId, zombie, ability);
+ calmed++;
+
+ // Visual: Blue ripple
+ this.createCalmEffect(zombie.sprite.x, zombie.sprite.y);
+
+ // Make zombie peaceful
+ zombie.aggression = Math.max(0, zombie.aggression - 50);
+ zombie.sprite.setTint(0x88ccff); // Blue tint
+
+ console.log(`[HybridAbilitySystem] Calmed wild zombie ${zombieId}`);
+ }
+ });
+
+ this.createPlayerEffect(ability.visualEffect);
+ this.scene.uiSystem?.showMessage(`Calmed ${calmed} wild zombie(s)!`, 0x00aaff);
+ }
+
+ executeSense(ability) {
+ const player = this.scene.player;
+
+ // Clear previous reveals
+ this.revealedEnemies.clear();
+
+ // Find all enemies in range
+ let detected = 0;
+
+ // Check enemy groups (you'd have an enemySystem or similar)
+ // For now, placeholder detection logic
+ if (this.scene.enemies) {
+ this.scene.enemies.forEach((enemy) => {
+ const dist = Phaser.Math.Distance.Between(
+ player.x, player.y,
+ enemy.x, enemy.y
+ );
+
+ if (dist <= ability.range) {
+ this.revealedEnemies.add(enemy);
+ detected++;
+
+ // Visual: Red outline on enemy
+ enemy.setTint(0xff0000);
+
+ // Auto-remove tint after duration
+ this.scene.time.delayedCall(ability.duration, () => {
+ enemy.clearTint();
+ });
+ }
+ });
+ }
+
+ // Player visual: Red pulse
+ this.createSenseEffect(player.x, player.y, ability.range);
+
+ if (detected > 0) {
+ this.scene.uiSystem?.showMessage(`โ ๏ธ ${detected} enemies detected!`, 0xff0000);
+ } else {
+ this.scene.uiSystem?.showMessage(`No enemies nearby`, 0x00ff00);
+ }
+
+ console.log(`[HybridAbilitySystem] Sense Danger: ${detected} enemies revealed`);
+ }
+
+ // ==================== BUFF MANAGEMENT ====================
+
+ applyBoostBuff(zombieId, zombie, ability) {
+ const buffData = {
+ zombieId,
+ type: 'boost',
+ speedMultiplier: ability.speedBoost,
+ damageMultiplier: ability.damageBoost,
+ efficiencyMultiplier: ability.efficiencyBoost,
+ startTime: Date.now(),
+ duration: ability.duration
+ };
+
+ this.activeBuffs.set(zombieId, buffData);
+
+ // Auto-remove after duration
+ this.scene.time.delayedCall(ability.duration, () => {
+ this.removeBoostBuff(zombieId);
+ });
+ }
+
+ removeBoostBuff(zombieId) {
+ if (this.activeBuffs.has(zombieId)) {
+ this.activeBuffs.delete(zombieId);
+ console.log(`[HybridAbilitySystem] Boost buff expired for zombie ${zombieId}`);
+ }
+ }
+
+ applyCalmBuff(zombieId, zombie, ability) {
+ const buffData = {
+ zombieId,
+ type: 'calm',
+ tamingBonus: ability.tamingBonus,
+ startTime: Date.now(),
+ duration: ability.duration
+ };
+
+ this.activeBuffs.set(zombieId, buffData);
+
+ // Auto-remove
+ this.scene.time.delayedCall(ability.duration, () => {
+ this.removeCalmBuff(zombieId, zombie);
+ });
+ }
+
+ removeCalmBuff(zombieId, zombie) {
+ if (this.activeBuffs.has(zombieId)) {
+ this.activeBuffs.delete(zombieId);
+
+ // Remove blue tint
+ if (zombie && zombie.sprite) {
+ zombie.sprite.clearTint();
+ }
+
+ console.log(`[HybridAbilitySystem] Calm buff expired for zombie ${zombieId}`);
+ }
+ }
+
+ // Get active buff for zombie (for ZombieSystem to query)
+ getZombieBuff(zombieId) {
+ return this.activeBuffs.get(zombieId);
+ }
+
+ // ==================== ENERGY MANAGEMENT ====================
+
+ consumeEnergy(amount) {
+ this.energy = Math.max(0, this.energy - amount);
+ this.updateEnergyBar();
+ }
+
+ regenerateEnergy() {
+ if (this.energy < this.maxEnergy) {
+ this.energy = Math.min(this.maxEnergy, this.energy + this.energyRegen);
+ this.updateEnergyBar();
+ }
+ }
+
+ // ==================== COOLDOWN MANAGEMENT ====================
+
+ startCooldown(abilityId, duration) {
+ this.cooldowns.set(abilityId, Date.now() + duration);
+ }
+
+ isOnCooldown(abilityId) {
+ if (!this.cooldowns.has(abilityId)) return false;
+ return Date.now() < this.cooldowns.get(abilityId);
+ }
+
+ getCooldownRemaining(abilityId) {
+ if (!this.isOnCooldown(abilityId)) return 0;
+ return this.cooldowns.get(abilityId) - Date.now();
+ }
+
+ // ==================== PLAYER PROGRESSION ====================
+
+ addPlayerXP(amount) {
+ // TODO: Implement full XP/level system
+ // For now, just log
+ console.log(`[HybridAbilitySystem] Player gained ${amount} Hybrid XP`);
+
+ // Check for level ups (placeholder)
+ // this.checkLevelUp();
+ }
+
+ getUnlockLevel(abilityId) {
+ const unlockLevels = {
+ healZombies: 1, // Start ability
+ calmWildZombies: 3,
+ boostZombies: 5,
+ senseDanger: 7
+ };
+ return unlockLevels[abilityId] || 10;
+ }
+
+ unlockAbility(abilityId) {
+ this.unlockedAbilities.add(abilityId);
+ this.scene.uiSystem?.showMessage(`Unlocked: ${this.abilities[abilityId].name}!`, 0xffaa00);
+ console.log(`[HybridAbilitySystem] Unlocked ability: ${abilityId}`);
+ }
+
+ // ==================== VISUAL EFFECTS ====================
+
+ createHealEffect(x, y) {
+ // Green particles rising
+ const particles = this.scene.add.particles(x, y, 'particle', {
+ speed: { min: -50, max: -100 },
+ scale: { start: 0.5, end: 0 },
+ tint: 0x00ff00,
+ lifespan: 1000,
+ quantity: 10
+ });
+
+ this.scene.time.delayedCall(1000, () => particles.destroy());
+ }
+
+ createBoostEffect(sprite) {
+ // Yellow aura around zombie
+ const aura = this.scene.add.circle(sprite.x, sprite.y, 30, 0xffff00, 0.3);
+ aura.setDepth(sprite.depth - 1);
+
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: aura,
+ scale: { from: 1, to: 1.2 },
+ alpha: { from: 0.3, to: 0.1 },
+ duration: 500,
+ yoyo: true,
+ repeat: -1
+ });
+
+ // Remove after 10s
+ this.scene.time.delayedCall(10000, () => aura.destroy());
+ }
+
+ createCalmEffect(x, y) {
+ // Blue ripple expanding
+ const ripple = this.scene.add.circle(x, y, 10, 0x88ccff, 0.5);
+
+ this.scene.tweens.add({
+ targets: ripple,
+ radius: 100,
+ alpha: 0,
+ duration: 1000,
+ onComplete: () => ripple.destroy()
+ });
+ }
+
+ createSenseEffect(x, y, range) {
+ // Red pulse expanding to range
+ const pulse = this.scene.add.circle(x, y, 10, 0xff0000, 0.3);
+
+ this.scene.tweens.add({
+ targets: pulse,
+ radius: range,
+ alpha: 0,
+ duration: 800,
+ onComplete: () => pulse.destroy()
+ });
+ }
+
+ createPlayerEffect(effectType) {
+ const player = this.scene.player;
+
+ // Brief flash around player
+ const flash = this.scene.add.circle(player.x, player.y, 50,
+ effectType === 'green_glow' ? 0x00ff00 :
+ effectType === 'yellow_aura' ? 0xffff00 :
+ effectType === 'blue_ripple' ? 0x88ccff :
+ 0xff0000, 0.4);
+
+ this.scene.tweens.add({
+ targets: flash,
+ alpha: 0,
+ scale: 1.5,
+ duration: 500,
+ onComplete: () => flash.destroy()
+ });
+ }
+
+ showFloatingText(x, y, text, color) {
+ const floatText = this.scene.add.text(x, y - 20, text, {
+ fontSize: '16px',
+ color: `#${color.toString(16).padStart(6, '0')}`,
+ stroke: '#000',
+ strokeThickness: 3
+ }).setOrigin(0.5);
+
+ this.scene.tweens.add({
+ targets: floatText,
+ y: y - 60,
+ alpha: 0,
+ duration: 1500,
+ onComplete: () => floatText.destroy()
+ });
+ }
+
+ // ==================== UI ====================
+
+ createUI() {
+ // Energy bar (top-left, below HP/Stamina)
+ this.energyBar = this.scene.add.graphics();
+ this.energyBar.setScrollFactor(0);
+ this.energyBar.setDepth(1000);
+ this.updateEnergyBar();
+
+ // Ability icons (bottom of screen)
+ const abilities = ['healZombies', 'calmWildZombies', 'boostZombies', 'senseDanger'];
+ abilities.forEach((abilityId, index) => {
+ // TODO: Create actual icons
+ // For now just placeholders
+ });
+ }
+
+ updateEnergyBar() {
+ if (!this.energyBar) return;
+
+ this.energyBar.clear();
+
+ // Background
+ this.energyBar.fillStyle(0x222222, 0.8);
+ this.energyBar.fillRect(10, 70, 200, 20);
+
+ // Energy fill (purple/pink for Alfa)
+ const energyWidth = (this.energy / this.maxEnergy) * 196;
+ this.energyBar.fillStyle(0xff00ff, 1);
+ this.energyBar.fillRect(12, 72, energyWidth, 16);
+
+ // Border
+ this.energyBar.lineStyle(2, 0xffffff, 1);
+ this.energyBar.strokeRect(10, 70, 200, 20);
+ }
+
+ // ==================== UPDATE ====================
+
+ update(time, delta) {
+ // Update cooldown visuals
+ // Update buff timers
+ // (placeholder for now)
+ }
+
+ // ==================== CLEANUP ====================
+
+ destroy() {
+ if (this.energyBar) this.energyBar.destroy();
+ this.visualEffects.forEach(effect => effect.destroy());
+ this.activeBuffs.clear();
+ this.cooldowns.clear();
+ console.log('[HybridAbilitySystem] Destroyed');
+ }
+}
+
+// Export for use in GameScene
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = HybridAbilitySystem;
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/HybridSkillSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/HybridSkillSystem.js
new file mode 100644
index 000000000..04a916e5d
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/HybridSkillSystem.js
@@ -0,0 +1,110 @@
+class HybridSkillSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ this.level = 1;
+ this.xp = 0;
+ this.maxXp = 100;
+
+ // Skills
+ this.skills = {
+ 'translation': { level: 0, max: 5, name: 'Zombie Language', desc: 'Understand zombie groans.' },
+ 'strength': { level: 0, max: 5, name: 'Mutant Strength', desc: 'Deal more damage.' },
+ 'resilience': { level: 0, max: 5, name: 'Rot Resistance', desc: 'Less damage from poison/decay.' },
+ 'command': { level: 0, max: 3, name: 'Horde Command', desc: 'Control more zombie workers.' }
+ };
+
+ this.points = 0; // Available skill points
+ }
+
+ addXP(amount) {
+ this.xp += amount;
+ if (this.xp >= this.maxXp) {
+ this.levelUp();
+ }
+ // UI Notification
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 50,
+ text: `+${amount} Hybrid XP`,
+ color: '#00FF00'
+ });
+ }
+
+ levelUp() {
+ this.xp -= this.maxXp;
+ this.level++;
+ this.maxXp = Math.floor(this.maxXp * 1.5);
+ this.points++;
+ console.log(`๐งฌ Hybrid Level Up! Level: ${this.level}, Points: ${this.points}`);
+
+ this.scene.soundManager.playSuccess(); // Reuse success sound
+
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 80,
+ text: `LEVEL UP! (${this.level})`,
+ color: '#FFFF00'
+ });
+ }
+
+ tryUpgradeSkill(skillId) {
+ const skill = this.skills[skillId];
+ if (!skill) return false;
+
+ if (this.points > 0 && skill.level < skill.max) {
+ this.points--;
+ skill.level++;
+ console.log(`๐งฌ Upgraded ${skill.name} to Lv ${skill.level}`);
+ return true;
+ }
+ return false;
+ }
+
+ getSpeechTranslation(text) {
+ const lvl = this.skills['translation'].level;
+ if (lvl >= 5) return text; // Perfect translation
+
+ // Obfuscate text based on level
+ // Lv 0: 100% garbled
+ // Lv 1: 80% garbled
+ // ...
+ const garbleChance = 1.0 - (lvl * 0.2);
+
+ return text.split(' ').map(word => {
+ if (Math.random() < garbleChance) {
+ return this.garbleWord(word);
+ }
+ return word;
+ }).join(' ');
+ }
+
+ garbleWord(word) {
+ const sounds = ['hgh', 'arr', 'ghh', '...', 'bra', 'in', 'zZz'];
+ return sounds[Math.floor(Math.random() * sounds.length)];
+ }
+
+ toJSON() {
+ return {
+ level: this.level,
+ xp: this.xp,
+ maxXp: this.maxXp,
+ skills: this.skills,
+ points: this.points
+ };
+ }
+
+ load(data) {
+ if (!data) return;
+ this.level = data.level;
+ this.xp = data.xp;
+ this.maxXp = data.maxXp;
+ this.points = data.points;
+ // Merge skills to keep structure if definition changed
+ for (const k in data.skills) {
+ if (this.skills[k]) {
+ this.skills[k].level = data.skills[k].level;
+ }
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/InputRemappingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/InputRemappingSystem.js
new file mode 100644
index 000000000..760a47aef
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/InputRemappingSystem.js
@@ -0,0 +1,525 @@
+/**
+ * INPUT REMAPPING SYSTEM
+ * Allows players to customize keyboard and controller bindings
+ * Supports multiple control profiles and one-handed layouts
+ */
+class InputRemappingSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Default key bindings
+ this.defaultBindings = {
+ // Movement
+ 'move_up': ['W', 'UP'],
+ 'move_down': ['S', 'DOWN'],
+ 'move_left': ['A', 'LEFT'],
+ 'move_right': ['D', 'RIGHT'],
+
+ // Actions
+ 'interact': ['E', 'SPACE'],
+ 'attack': ['MOUSE_LEFT', 'J'],
+ 'use_tool': ['MOUSE_LEFT', 'J'],
+ 'cancel': ['ESC', 'X'],
+ 'confirm': ['ENTER', 'E'],
+
+ // Inventory & UI
+ 'inventory': ['I', 'TAB'],
+ 'crafting': ['C'],
+ 'map': ['M'],
+ 'quest_log': ['Q'],
+ 'pause': ['ESC', 'P'],
+
+ // Tools
+ 'tool_1': ['1'],
+ 'tool_2': ['2'],
+ 'tool_3': ['3'],
+ 'tool_4': ['4'],
+ 'tool_5': ['5'],
+
+ // Quick actions
+ 'quick_heal': ['H'],
+ 'quick_eat': ['F'],
+ 'sprint': ['SHIFT'],
+ 'crouch': ['CTRL'],
+
+ // Camera
+ 'zoom_in': ['PLUS', 'MOUSE_WHEEL_UP'],
+ 'zoom_out': ['MINUS', 'MOUSE_WHEEL_DOWN'],
+ 'camera_reset': ['R']
+ };
+
+ // Controller bindings (Xbox/PlayStation layout)
+ this.controllerBindings = {
+ 'move': 'LEFT_STICK',
+ 'camera': 'RIGHT_STICK',
+ 'interact': 'A', // Xbox A / PS Cross
+ 'attack': 'X', // Xbox X / PS Square
+ 'cancel': 'B', // Xbox B / PS Circle
+ 'inventory': 'Y', // Xbox Y / PS Triangle
+ 'sprint': 'LB',
+ 'use_tool': 'RT',
+ 'quick_menu': 'LT',
+ 'pause': 'START',
+ 'map': 'SELECT'
+ };
+
+ // Control profiles
+ this.profiles = {
+ 'default': JSON.parse(JSON.stringify(this.defaultBindings)),
+ 'wasd': JSON.parse(JSON.stringify(this.defaultBindings)),
+ 'arrows': this.createArrowsProfile(),
+ 'left-handed': this.createLeftHandedProfile(),
+ 'right-handed': this.createRightHandedProfile(),
+ 'custom-1': JSON.parse(JSON.stringify(this.defaultBindings)),
+ 'custom-2': JSON.parse(JSON.stringify(this.defaultBindings)),
+ 'custom-3': JSON.parse(JSON.stringify(this.defaultBindings))
+ };
+
+ // Current active profile
+ this.activeProfile = 'default';
+ this.currentBindings = this.profiles[this.activeProfile];
+
+ // Rebinding state
+ this.isRebinding = false;
+ this.rebindingAction = null;
+ this.rebindingCallback = null;
+
+ // Input buffer for detecting key presses
+ this.inputBuffer = [];
+ this.maxBufferSize = 10;
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Input Remapping System initialized');
+ }
+
+ init() {
+ // Set up input listeners
+ this.setupInputListeners();
+ }
+
+ setupInputListeners() {
+ // Keyboard input
+ this.scene.input.keyboard.on('keydown', (event) => {
+ if (this.isRebinding) {
+ this.handleRebindInput(event.key.toUpperCase());
+ }
+ });
+
+ // Mouse input
+ this.scene.input.on('pointerdown', (pointer) => {
+ if (this.isRebinding) {
+ const button = pointer.leftButtonDown() ? 'MOUSE_LEFT' :
+ pointer.rightButtonDown() ? 'MOUSE_RIGHT' :
+ pointer.middleButtonDown() ? 'MOUSE_MIDDLE' : null;
+ if (button) {
+ this.handleRebindInput(button);
+ }
+ }
+ });
+
+ // Mouse wheel
+ this.scene.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
+ if (this.isRebinding) {
+ const input = deltaY < 0 ? 'MOUSE_WHEEL_UP' : 'MOUSE_WHEEL_DOWN';
+ this.handleRebindInput(input);
+ }
+ });
+ }
+
+ /**
+ * Create arrows-based profile (arrow keys for movement)
+ */
+ createArrowsProfile() {
+ const profile = JSON.parse(JSON.stringify(this.defaultBindings));
+ profile.move_up = ['UP', 'W'];
+ profile.move_down = ['DOWN', 'S'];
+ profile.move_left = ['LEFT', 'A'];
+ profile.move_right = ['RIGHT', 'D'];
+ return profile;
+ }
+
+ /**
+ * Create left-handed profile (numpad for movement)
+ */
+ createLeftHandedProfile() {
+ return {
+ // Movement on numpad
+ 'move_up': ['NUMPAD_8', 'I'],
+ 'move_down': ['NUMPAD_5', 'K'],
+ 'move_left': ['NUMPAD_4', 'J'],
+ 'move_right': ['NUMPAD_6', 'L'],
+
+ // Actions on left side
+ 'interact': ['Q', 'SPACE'],
+ 'attack': ['MOUSE_LEFT', 'W'],
+ 'use_tool': ['MOUSE_LEFT', 'W'],
+ 'cancel': ['ESC', 'E'],
+ 'confirm': ['ENTER', 'Q'],
+
+ // Inventory & UI
+ 'inventory': ['TAB', 'U'],
+ 'crafting': ['R'],
+ 'map': ['T'],
+ 'quest_log': ['Y'],
+ 'pause': ['ESC', 'P'],
+
+ // Tools (right side for easy access)
+ 'tool_1': ['7'],
+ 'tool_2': ['8'],
+ 'tool_3': ['9'],
+ 'tool_4': ['0'],
+ 'tool_5': ['MINUS'],
+
+ // Quick actions
+ 'quick_heal': ['A'],
+ 'quick_eat': ['S'],
+ 'sprint': ['SHIFT'],
+ 'crouch': ['CTRL'],
+
+ // Camera
+ 'zoom_in': ['PLUS', 'MOUSE_WHEEL_UP'],
+ 'zoom_out': ['EQUALS', 'MOUSE_WHEEL_DOWN'],
+ 'camera_reset': ['BACKSPACE']
+ };
+ }
+
+ /**
+ * Create right-handed profile (standard WASD)
+ */
+ createRightHandedProfile() {
+ return JSON.parse(JSON.stringify(this.defaultBindings));
+ }
+
+ /**
+ * Check if an action is pressed
+ */
+ isActionPressed(action) {
+ const bindings = this.currentBindings[action];
+ if (!bindings) return false;
+
+ for (const key of bindings) {
+ if (this.isKeyPressed(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if a specific key is pressed
+ */
+ isKeyPressed(key) {
+ if (key.startsWith('MOUSE_')) {
+ const pointer = this.scene.input.activePointer;
+ if (key === 'MOUSE_LEFT') return pointer.leftButtonDown();
+ if (key === 'MOUSE_RIGHT') return pointer.rightButtonDown();
+ if (key === 'MOUSE_MIDDLE') return pointer.middleButtonDown();
+ return false;
+ }
+
+ const keyObj = this.scene.input.keyboard.addKey(key, false);
+ return keyObj && keyObj.isDown;
+ }
+
+ /**
+ * Check if an action was just pressed (single frame)
+ */
+ isActionJustPressed(action) {
+ const bindings = this.currentBindings[action];
+ if (!bindings) return false;
+
+ for (const key of bindings) {
+ if (this.isKeyJustPressed(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if a key was just pressed
+ */
+ isKeyJustPressed(key) {
+ if (key.startsWith('MOUSE_')) {
+ const pointer = this.scene.input.activePointer;
+ if (key === 'MOUSE_LEFT') return pointer.leftButtonDown() && pointer.getDuration() < 100;
+ if (key === 'MOUSE_RIGHT') return pointer.rightButtonDown() && pointer.getDuration() < 100;
+ if (key === 'MOUSE_MIDDLE') return pointer.middleButtonDown() && pointer.getDuration() < 100;
+ return false;
+ }
+
+ const keyObj = this.scene.input.keyboard.addKey(key, false);
+ return keyObj && Phaser.Input.Keyboard.JustDown(keyObj);
+ }
+
+ /**
+ * Start rebinding an action
+ */
+ startRebinding(action, callback) {
+ if (!this.currentBindings[action]) {
+ console.error(`Action "${action}" does not exist`);
+ return;
+ }
+
+ this.isRebinding = true;
+ this.rebindingAction = action;
+ this.rebindingCallback = callback;
+
+ console.log(`๐ฎ Rebinding action: ${action}. Press any key...`);
+ }
+
+ /**
+ * Handle rebind input
+ */
+ handleRebindInput(input) {
+ if (!this.isRebinding) return;
+
+ // Ignore ESC (cancel rebinding)
+ if (input === 'ESC' || input === 'ESCAPE') {
+ this.cancelRebinding();
+ return;
+ }
+
+ // Set new binding (replace first binding)
+ this.currentBindings[this.rebindingAction][0] = input;
+
+ console.log(`โ
Action "${this.rebindingAction}" rebound to: ${input}`);
+
+ // Call callback if provided
+ if (this.rebindingCallback) {
+ this.rebindingCallback(this.rebindingAction, input);
+ }
+
+ this.isRebinding = false;
+ this.rebindingAction = null;
+ this.rebindingCallback = null;
+
+ this.saveSettings();
+ }
+
+ /**
+ * Cancel rebinding
+ */
+ cancelRebinding() {
+ console.log('โ Rebinding cancelled');
+ this.isRebinding = false;
+ this.rebindingAction = null;
+ this.rebindingCallback = null;
+ }
+
+ /**
+ * Reset action to default binding
+ */
+ resetAction(action) {
+ if (!this.defaultBindings[action]) {
+ console.error(`Action "${action}" does not exist`);
+ return;
+ }
+
+ this.currentBindings[action] = JSON.parse(JSON.stringify(this.defaultBindings[action]));
+ this.saveSettings();
+ console.log(`๐ Action "${action}" reset to default`);
+ }
+
+ /**
+ * Reset all bindings to default
+ */
+ resetAllBindings() {
+ this.currentBindings = JSON.parse(JSON.stringify(this.defaultBindings));
+ this.profiles[this.activeProfile] = this.currentBindings;
+ this.saveSettings();
+ console.log('๐ All bindings reset to default');
+ }
+
+ /**
+ * Switch to a different profile
+ */
+ switchProfile(profileName) {
+ if (!this.profiles[profileName]) {
+ console.error(`Profile "${profileName}" does not exist`);
+ return;
+ }
+
+ this.activeProfile = profileName;
+ this.currentBindings = this.profiles[profileName];
+ this.saveSettings();
+ console.log(`๐ฎ Switched to profile: ${profileName}`);
+ }
+
+ /**
+ * Save current bindings to a custom profile
+ */
+ saveToProfile(profileName) {
+ if (!profileName.startsWith('custom-')) {
+ console.error('Can only save to custom profiles (custom-1, custom-2, custom-3)');
+ return;
+ }
+
+ this.profiles[profileName] = JSON.parse(JSON.stringify(this.currentBindings));
+ this.saveSettings();
+ console.log(`๐พ Bindings saved to profile: ${profileName}`);
+ }
+
+ /**
+ * Get binding display name
+ */
+ getBindingDisplay(action) {
+ const bindings = this.currentBindings[action];
+ if (!bindings || bindings.length === 0) return 'Not bound';
+
+ return bindings.map(key => this.formatKeyName(key)).join(' / ');
+ }
+
+ /**
+ * Format key name for display
+ */
+ formatKeyName(key) {
+ const keyMap = {
+ 'MOUSE_LEFT': 'Left Click',
+ 'MOUSE_RIGHT': 'Right Click',
+ 'MOUSE_MIDDLE': 'Middle Click',
+ 'MOUSE_WHEEL_UP': 'Scroll Up',
+ 'MOUSE_WHEEL_DOWN': 'Scroll Down',
+ 'SPACE': 'Space',
+ 'ENTER': 'Enter',
+ 'ESC': 'Escape',
+ 'SHIFT': 'Shift',
+ 'CTRL': 'Ctrl',
+ 'ALT': 'Alt',
+ 'TAB': 'Tab',
+ 'BACKSPACE': 'Backspace',
+ 'UP': 'โ',
+ 'DOWN': 'โ',
+ 'LEFT': 'โ',
+ 'RIGHT': 'โ',
+ 'PLUS': '+',
+ 'MINUS': '-',
+ 'EQUALS': '='
+ };
+
+ return keyMap[key] || key;
+ }
+
+ /**
+ * Get all available profiles
+ */
+ getProfiles() {
+ return Object.keys(this.profiles);
+ }
+
+ /**
+ * Get current profile name
+ */
+ getCurrentProfile() {
+ return this.activeProfile;
+ }
+
+ /**
+ * Export bindings as JSON
+ */
+ exportBindings() {
+ const data = {
+ activeProfile: this.activeProfile,
+ profiles: this.profiles
+ };
+ return JSON.stringify(data, null, 2);
+ }
+
+ /**
+ * Import bindings from JSON
+ */
+ importBindings(jsonString) {
+ try {
+ const data = JSON.parse(jsonString);
+ this.activeProfile = data.activeProfile || 'default';
+ this.profiles = data.profiles || this.profiles;
+ this.currentBindings = this.profiles[this.activeProfile];
+ this.saveSettings();
+ console.log('โ
Bindings imported successfully');
+ return true;
+ } catch (error) {
+ console.error('โ Failed to import bindings:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Save settings to localStorage
+ */
+ saveSettings() {
+ const data = {
+ activeProfile: this.activeProfile,
+ profiles: this.profiles
+ };
+ localStorage.setItem('novafarma_input_bindings', JSON.stringify(data));
+ }
+
+ /**
+ * Load settings from localStorage
+ */
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_input_bindings');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.activeProfile = data.activeProfile || 'default';
+ this.profiles = { ...this.profiles, ...data.profiles };
+ this.currentBindings = this.profiles[this.activeProfile];
+ console.log('โ
Input bindings loaded from localStorage');
+ } catch (error) {
+ console.error('โ Failed to load input bindings:', error);
+ }
+ }
+ }
+
+ /**
+ * Get controller button name
+ */
+ getControllerButtonName(button) {
+ const buttonMap = {
+ 'A': 'A (Cross)',
+ 'B': 'B (Circle)',
+ 'X': 'X (Square)',
+ 'Y': 'Y (Triangle)',
+ 'LB': 'LB (L1)',
+ 'RB': 'RB (R1)',
+ 'LT': 'LT (L2)',
+ 'RT': 'RT (R2)',
+ 'START': 'Start',
+ 'SELECT': 'Select (Share)',
+ 'LEFT_STICK': 'Left Stick',
+ 'RIGHT_STICK': 'Right Stick'
+ };
+ return buttonMap[button] || button;
+ }
+
+ /**
+ * Check if controller is connected
+ */
+ isControllerConnected() {
+ return this.scene.input.gamepad && this.scene.input.gamepad.total > 0;
+ }
+
+ /**
+ * Get connected controller info
+ */
+ getControllerInfo() {
+ if (!this.isControllerConnected()) return null;
+
+ const pad = this.scene.input.gamepad.getPad(0);
+ return {
+ id: pad.id,
+ index: pad.index,
+ buttons: pad.buttons.length,
+ axes: pad.axes.length
+ };
+ }
+
+ destroy() {
+ this.saveSettings();
+ console.log('๐ฎ Input Remapping System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/InteractionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/InteractionSystem.js
new file mode 100644
index 000000000..d9a9951b7
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/InteractionSystem.js
@@ -0,0 +1,476 @@
+class InteractionSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.iso = new IsometricUtils(48, 24);
+
+ // Input listener setup
+ this.scene.input.on('pointerdown', (pointer) => {
+ if (pointer.button === 0) { // Left Click
+ const worldX = pointer.worldX - this.scene.terrainOffsetX;
+ const worldY = pointer.worldY - this.scene.terrainOffsetY;
+ const gridPos = this.iso.toGrid(worldX, worldY);
+ this.handleInteraction(gridPos.x, gridPos.y, false);
+ }
+ });
+
+ // Key E listener (Easy Interaction/Tame)
+ this.scene.input.keyboard.on('keydown-E', () => {
+ this.handleInteractKey();
+ });
+ }
+
+ handleInteractKey() {
+ if (!this.scene.player || !this.scene.npcs) return;
+ const playerPos = this.scene.player.getPosition();
+
+ // Find nearest NPC
+ let nearest = null;
+ let minDist = 2.5; // Interaction range
+
+ const candidates = this.scene.spatialGrid ? this.scene.spatialGrid.query(playerPos.x, playerPos.y) : this.scene.npcs;
+
+ if (this.scene.vehicles) {
+ for (const v of this.scene.vehicles) {
+ const d = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, v.gridX, v.gridY);
+ if (d < minDist) {
+ minDist = d;
+ nearest = v;
+ }
+ }
+ }
+
+ // Check for chests
+ if (this.scene.chests) {
+ for (const chest of this.scene.chests) {
+ const d = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, chest.gridX, chest.gridY);
+ if (d < minDist && !chest.isOpened) {
+ minDist = d;
+ nearest = chest;
+ }
+ }
+ }
+
+ for (const npc of candidates) {
+ const d = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, npc.gridX, npc.gridY);
+ if (d < minDist) {
+ minDist = d;
+ nearest = npc;
+ }
+ }
+
+
+
+ // Check for Buildings (Signs included)
+ // Currently decorations don't store data easily accessible here without query.
+ // Assuming nearest logic above covers entities.
+
+ if (nearest) {
+ console.log('E Interacted with:', nearest.type || nearest.lootTable);
+ if (nearest.type === 'scooter') {
+ nearest.interact(this.scene.player);
+ }
+ else if (nearest.lootTable) {
+ nearest.interact(this.scene.player);
+ }
+ else if (nearest.type === 'zombie') {
+ // Check if already tamed?
+ // If aggressive, combat? E is usually benign interaction.
+ nearest.tame();
+ } else {
+ // Generic Talk / Toggle
+ // INTEGRATE TRANSLATION
+ this.handleTalk(nearest);
+ }
+ }
+ }
+
+ handleTalk(npc) {
+ if (!this.scene.hybridSkillSystem) {
+ console.log('Talk with:', npc.type);
+ return;
+ }
+
+ let text = "...";
+ let color = '#FFFFFF';
+
+ if (npc.type === 'zombie') {
+ text = "Brains... Hungry... Leader?";
+ text = this.scene.hybridSkillSystem.getSpeechTranslation(text);
+ color = '#55FF55';
+ } else if (npc.type === 'merchant') {
+ text = "Welcome! I have rare goods.";
+ color = '#FFD700';
+ // Also triggers UI
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene) uiScene.showTradeMenu(this.scene.inventorySystem);
+ } else if (npc.type === 'elf') {
+ text = "The forest... it burns... you are not safe.";
+ text = this.scene.hybridSkillSystem.getSpeechTranslation(text); // Maybe Elvish/Mutant dialect?
+ color = '#00FFFF';
+ } else if (npc.passive) {
+ // Animal noises
+ text = npc.type.includes('cow') ? 'Moo.' : 'Cluck.';
+ }
+
+ // Show Floating Text Dialogue
+ this.scene.events.emit('show-floating-text', {
+ x: npc.sprite.x,
+ y: npc.sprite.y - 60,
+ text: text,
+ color: color
+ });
+
+ // NPC pauses briefly (handled by AI)
+ }
+
+ handleInteraction(gridX, gridY, isAttack = false) {
+ if (!this.scene.player) return;
+
+ // 3. Check distance
+ const playerPos = this.scene.player.getPosition();
+ const dist = Phaser.Math.Distance.Between(playerPos.x, playerPos.y, gridX, gridY);
+
+ // Allow interaction within radius of 2.5 tiles (1.5 for attack)
+ const maxDist = isAttack ? 1.5 : 2.5;
+ if (dist > maxDist) return;
+
+ // DETERMINE TOOL
+ let activeTool = isAttack ? 'sword' : null;
+ const uiScene = this.scene.scene.get('UIScene');
+ const invSys = this.scene.inventorySystem;
+
+ if (uiScene && invSys && !isAttack) {
+ const selectedIdx = uiScene.selectedSlot;
+ const slotData = invSys.slots[selectedIdx];
+ if (slotData) activeTool = slotData.type;
+ }
+
+ if (isAttack && invSys) {
+ const selectedIdx = uiScene.selectedSlot;
+ const slotData = invSys.slots[selectedIdx];
+ if (slotData) activeTool = slotData.type;
+ }
+
+ // 0. Build Mode Override (Only click)
+ if (!isAttack && this.scene.buildingSystem && this.scene.buildingSystem.isBuildMode) {
+ this.scene.buildingSystem.tryBuild(gridX, gridY);
+ return;
+ }
+
+ // 3.3.1 BOAT DEPLOY
+ if (activeTool === 'boat' && !isAttack && this.scene.oceanSystem) {
+ this.scene.oceanSystem.useBoat();
+ return;
+ }
+
+ // 3.4 Check for Vehicles (Scooter)
+ if (!isAttack && this.scene.vehicles) {
+ for (const vehicle of this.scene.vehicles) {
+ // If mounted, dismount (interact with self/vehicle at same pos)
+ // If unmounted, check proximity
+ if (Math.abs(vehicle.gridX - gridX) < 1.5 && Math.abs(vehicle.gridY - gridY) < 1.5) {
+ if (vehicle.interact) {
+ vehicle.interact(this.scene.player);
+ return; // Stop other interactions
+ }
+ }
+ }
+ }
+
+ // 3.5 Check for NPC Interaction
+ const candidates = this.scene.spatialGrid ? this.scene.spatialGrid.query(gridX, gridY) : this.scene.npcs;
+
+ if (candidates) {
+ for (const npc of candidates) {
+ // Increased radius to 1.8 to catch moving NPCs easier
+ if (Math.abs(npc.gridX - gridX) < 1.8 && Math.abs(npc.gridY - gridY) < 1.8) {
+
+ if (npc.type === 'merchant' && !isAttack) {
+ if (uiScene && invSys) uiScene.showTradeMenu(invSys);
+ return;
+ }
+
+ if (npc.type === 'zombie') {
+ // Logic: Attack vs Tame
+ const isWeapon = activeTool === 'sword' || activeTool === 'axe' || activeTool === 'pickaxe';
+
+ if (isAttack || isWeapon) {
+ // COMBAT
+ let damage = 1;
+ if (activeTool === 'sword') damage = 5;
+ if (activeTool === 'axe') damage = 3;
+ if (activeTool === 'pickaxe') damage = 2;
+
+ if (npc.takeDamage) {
+ npc.takeDamage(damage);
+ }
+ return;
+ }
+ else {
+ if (npc.isTamed) {
+ // Open Worker Menu
+ if (uiScene && uiScene.showWorkerMenu) {
+ uiScene.showWorkerMenu(npc);
+ }
+ } else {
+ // TAME ATTEMPT
+ console.log('๐ค Attempting to TAME zombie at', npc.gridX, npc.gridY);
+ npc.tame();
+ }
+ return;
+ }
+ }
+
+ // 3.6 Check for Livestock Interaction (Milking)
+ if (npc.type.includes('cow') && activeTool === 'bucket' && !isAttack) {
+ if (npc.milkReady) {
+ npc.milkReady = false;
+ npc.milkCooldown = 60000; // 60s cooldown
+
+ const product = npc.type.includes('mutant') ? 'glowing_milk' : 'milk';
+
+ if (invSys) {
+ invSys.addItem(product, 1);
+ this.scene.events.emit('show-floating-text', {
+ x: npc.sprite.x, y: npc.sprite.y - 40, text: `+1 ${product}`, color: '#FFFFFF'
+ });
+ console.log(`๐ฅ Milked ${npc.type}: ${product}`);
+
+ // Optional: Replace bucket with empty? No, bucket is tool.
+ // Maybe replace bucket with bucket_milk if it was a single use item, but let's keep it as tool.
+ }
+ } else {
+ this.scene.events.emit('show-floating-text', {
+ x: npc.sprite.x, y: npc.sprite.y - 40, text: 'Not ready...', color: '#AAAAAA'
+ });
+ }
+ return;
+ }
+
+ // NPC interaction complete
+ return;
+ }
+ }
+ }
+
+ // 3.5 Try Opening Chest
+ if (!isAttack && this.scene.terrainSystem) {
+ const decorKey = `${gridX},${gridY}`;
+ const decor = this.scene.terrainSystem.decorationsMap.get(decorKey);
+
+ if (decor && decor.type === 'chest') {
+ // ... chest opening logic
+ console.log('๐ฆ Opening treasure chest!');
+ const lootTable = ['item_scrap', 'item_chip', 'item_wood', 'item_stone', 'item_bone'];
+ const lootCount = Phaser.Math.Between(2, 4);
+ for (let i = 0; i < lootCount; i++) {
+ const randomLoot = Phaser.Utils.Array.GetRandom(lootTable);
+ if (this.scene.lootSystem) {
+ const offsetX = Phaser.Math.Between(-1, 1);
+ const offsetY = Phaser.Math.Between(-1, 1);
+ this.scene.lootSystem.spawnLoot(gridX + offsetX, gridY + offsetY, randomLoot);
+ }
+ }
+ this.scene.terrainSystem.removeDecoration(gridX, gridY);
+ return;
+ }
+
+ // RUIN INTERACTION
+ if (decor && decor.type.includes('ruin')) {
+ console.log('๐๏ธ Interacted with Ruin at', gridX, gridY);
+ if (this.scene.townRestorationSystem) {
+ // Find a building that matches this location or type
+ // For now, let's just trigger a generic notification or the first building
+ const building = this.scene.townRestorationSystem.buildings.get('jakob_shop'); // Demo link
+ if (building) {
+ this.scene.events.emit('show-floating-text', {
+ x: gridX * 48, y: gridY * 48 - 40,
+ text: `Restore ${building.name}?`,
+ color: '#FFD700'
+ });
+
+ // Try start restoration if resources exist
+ if (this.scene.townRestorationSystem.hasMaterials(building.materials)) {
+ this.scene.townRestorationSystem.startRestoration(building.id);
+ } else {
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification({
+ title: 'Missing Materials',
+ text: `Need Wood: ${building.materials.wood}, Stone: ${building.materials.stone}`,
+ icon: '๐ฆ'
+ });
+ }
+ }
+ }
+ }
+ return;
+ }
+ }
+
+
+ // 4. Try Planting Tree (Manual Planting)
+ if (!isAttack && (activeTool === 'tree_sapling' || activeTool === 'item_sapling')) {
+ const tile = this.scene.terrainSystem.getTile(gridX, gridY);
+
+ // Dovolimo sajenje samo na travo in dirt
+ if (tile && !tile.hasDecoration && !tile.hasCrop && (tile.type.includes('grass') || tile.type.includes('dirt'))) {
+ const saplingSprite = window.SPRITE_TREE_SAPLING || 'tree_sapling';
+ this.scene.terrainSystem.addDecoration(gridX, gridY, saplingSprite);
+
+ // Remove 1 sapling from hand
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.removeItem(activeTool, 1);
+ }
+
+ // Sound
+ // this.scene.soundManager.playPlant();
+ return;
+ }
+ }
+
+ // 4. Farming is now handled via Player Space key (handleFarmingAction)
+ // No click farming anymore
+
+
+ // 5. Try damage decoration
+ const id = `${gridX},${gridY}`;
+ if (this.scene.terrainSystem && this.scene.terrainSystem.decorationsMap.has(id)) {
+ const decor = this.scene.terrainSystem.decorationsMap.get(id);
+
+ // Workstation Interaction
+ if (this.scene.workstationSystem && (decor.type.includes('furnace') || decor.type.includes('mint'))) {
+ const heldItem = activeTool ? { itemId: activeTool } : null;
+ const result = this.scene.workstationSystem.interact(gridX, gridY, heldItem);
+ if (result) return;
+ }
+
+ // Building Interaction (Upgrade House)
+ if (this.scene.buildingSystem && decor.type.startsWith('struct_house')) {
+ const result = this.scene.buildingSystem.tryUpgrade(gridX, gridY);
+ if (result) return;
+ }
+
+ // handleTreeHit Logic (User Request)
+ // Preverimo tip in ustrezno orodje
+ let damage = 1;
+
+ // DREVESA (sapling, healthy, dead, blue)
+ if (decor.type.includes('tree') || decor.type.includes('sapling')) {
+ damage = (activeTool === 'axe') ? 5 : 1; // Poveฤal damage z sekiro
+ }
+ // KAMNI (rock, stone)
+ else if (decor.type.includes('rock') || decor.type.includes('stone')) {
+ damage = (activeTool === 'pickaxe') ? 5 : 1;
+ }
+ else if (decor.type.includes('bush')) damage = 5;
+
+ // Apply damage
+ decor.hp -= damage;
+ this.showFloatingText(`-${damage}`, gridX, gridY, '#ffaaaa');
+
+ // Sound
+ if (this.scene.soundManager) {
+ if (decor.type.includes('tree')) this.scene.soundManager.playChop();
+ else this.scene.soundManager.playHit(); // Generic hit for rocks
+ }
+
+ if (decor.hp <= 0) {
+ // XP REWARD (Requested by Logic)
+ if (this.scene.statsSystem) {
+ this.scene.statsSystem.addXP(5);
+ }
+
+ const prevType = decor.type;
+
+ // Logic: If Tree (and not sapling), turn into Sapling immediately (Regrowth)
+ if ((prevType.includes('tree') || prevType.includes('final')) && !prevType.includes('sapling')) {
+
+ decor.type = window.SPRITE_TREE_SAPLING || 'tree_sapling';
+ decor.hp = 2; // Fragile sapling
+ decor.scale = 1.0;
+
+ // Update visual immediately
+ const sprite = this.scene.terrainSystem.visibleDecorations.get(id);
+ if (sprite) {
+ sprite.setTexture(decor.type);
+ sprite.setScale(decor.scale);
+ // Shrink effect to simulate falling/replacement
+ this.scene.tweens.add({
+ targets: sprite, scaleX: { from: 1.2, to: 1.0 }, scaleY: { from: 1.2, to: 1.0 }, duration: 200
+ });
+ }
+
+ // Drop Wood Only
+ if (this.scene.lootSystem) {
+ this.scene.lootSystem.spawnLoot(gridX, gridY, 'item_wood', Math.floor(Math.random() * 3) + 2);
+ }
+ console.log('๐ฑ Tree replanted automatically.');
+ }
+ else {
+ const type = this.scene.terrainSystem.removeDecoration(gridX, gridY);
+
+ // Loot logic handled here via LootSystem
+ if (this.scene.lootSystem) {
+ if (type.includes('rock') || type.includes('stone')) {
+ this.scene.lootSystem.spawnLoot(gridX, gridY, 'item_stone', Math.floor(Math.random() * 3) + 1);
+ }
+ else if (type.includes('bush') || type.includes('sapling')) {
+ this.scene.lootSystem.spawnLoot(gridX, gridY, 'item_seeds', 1);
+ if (type.includes('sapling')) {
+ this.scene.lootSystem.spawnLoot(gridX, gridY, window.SPRITE_TREE_SAPLING || 'tree_sapling', 1);
+ }
+ }
+ else if (type.includes('flowers')) {
+ this.scene.lootSystem.spawnLoot(gridX, gridY, 'item_seeds', 1);
+ }
+ }
+ }
+
+ } else {
+ // Shake visual
+ const sprite = this.scene.terrainSystem.visibleDecorations.get(id);
+ if (sprite) {
+ this.scene.tweens.add({
+ targets: sprite,
+ x: sprite.x + 2, yoyo: true, duration: 50, repeat: 2
+ });
+ sprite.setTint(0xffaaaa);
+ this.scene.time.delayedCall(200, () => sprite.clearTint());
+ }
+ }
+ }
+ }
+
+ showFloatingText(text, gridX, gridY, color) {
+ const screenPos = this.iso.toScreen(gridX, gridY);
+ const txt = this.scene.add.text(
+ screenPos.x + this.scene.terrainOffsetX,
+ screenPos.y + this.scene.terrainOffsetY - 30,
+ text,
+ { fontSize: '14px', fill: color, stroke: '#000', strokeThickness: 2 }
+ ).setOrigin(0.5);
+ this.scene.tweens.add({ targets: txt, y: txt.y - 40, alpha: 0, duration: 1000, onComplete: () => txt.destroy() });
+ }
+
+ update(delta) {
+ // No logic needed here anymore (loot pickup handled by LootSystem)
+ }
+
+ spawnLoot(gridX, gridY, itemType, count = 1) {
+ // Add items directly to inventory instead of spawning physical loot
+ if (this.scene.inventorySystem) {
+ const added = this.scene.inventorySystem.addItem(itemType, count);
+ if (added) {
+ // Visual feedback
+ this.scene.events.emit('show-floating-text', {
+ x: gridX * 48,
+ y: gridY * 48,
+ text: `+${count} ${itemType}`,
+ color: '#00FF00'
+ });
+ console.log(`๐ฆ Spawned ${count}x ${itemType} at ${gridX},${gridY}`);
+ }
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/InventorySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/InventorySystem.js
new file mode 100644
index 000000000..2c8f2daf1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/InventorySystem.js
@@ -0,0 +1,125 @@
+class InventorySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Data structure: Array of slots
+ // Each slot: { type: 'wood', count: 5 } or null
+ this.slots = new Array(9).fill(null);
+ this.maxStack = 99;
+
+ // Generate tool icons if missing
+ if (typeof TextureGenerator !== 'undefined') {
+ TextureGenerator.createToolSprites(this.scene);
+ }
+
+ // Initial items
+ this.addItem('axe', 1); // ๐ช Sekira
+ this.addItem('pickaxe', 1); // โ๏ธ Kramp
+ this.addItem('hoe', 1);
+ this.addItem('watering_can', 1); // ๐ง Zalivalka
+ this.addItem('seeds', 5); // Zmanjลกano ลกtevilo semen
+ this.addItem('arrow_silver', 10); // โถ Silver arrows for werewolf hunting
+ // Removed default wood/stone so player has to gather them
+
+ this.gold = 0;
+ }
+
+ addItem(type, count) {
+ // Unlock in Collection
+ if (this.scene.collectionSystem) {
+ this.scene.collectionSystem.unlock(type);
+ }
+
+ // 1. Try to stack
+ for (let i = 0; i < this.slots.length; i++) {
+ if (this.slots[i] && this.slots[i].type === type) {
+ const space = this.maxStack - this.slots[i].count;
+ if (space > 0) {
+ const toAdd = Math.min(space, count);
+ this.slots[i].count += toAdd;
+ count -= toAdd;
+ if (count === 0) break;
+ }
+ }
+ }
+
+ // 2. Empty slots
+ if (count > 0) {
+ for (let i = 0; i < this.slots.length; i++) {
+ if (!this.slots[i]) {
+ const toAdd = Math.min(this.maxStack, count);
+ this.slots[i] = { type: type, count: toAdd };
+ count -= toAdd;
+ if (count === 0) break;
+ }
+ }
+ }
+
+ this.updateUI();
+
+ return count === 0; // True if everything added
+ }
+
+ removeItem(type, count) {
+ for (let i = 0; i < this.slots.length; i++) {
+ if (this.slots[i] && this.slots[i].type === type) {
+ if (this.slots[i].count >= count) {
+ this.slots[i].count -= count;
+ if (this.slots[i].count === 0) this.slots[i] = null;
+ this.updateUI();
+ return true;
+ } else {
+ count -= this.slots[i].count;
+ this.slots[i] = null;
+ }
+ }
+ }
+ this.updateUI();
+ this.updateUI();
+ return false; // Not enough items
+ }
+
+ getItemCount(type) {
+ let total = 0;
+ for (const slot of this.slots) {
+ if (slot && slot.type === type) {
+ total += slot.count;
+ }
+ }
+ return total;
+ }
+
+ addGold(amount) {
+ this.gold += amount;
+ this.updateUI();
+ }
+
+ updateUI() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene || !uiScene.goldText) {
+ // UIScene not ready yet, skip update
+ return;
+ }
+ uiScene.updateInventory(this.slots);
+ if (uiScene.updateGold) uiScene.updateGold(this.gold);
+ }
+
+ hasItem(type, count) {
+ let total = 0;
+ for (const slot of this.slots) {
+ if (slot && slot.type === type) {
+ total += slot.count;
+ }
+ }
+ return total >= count;
+ }
+
+ /**
+ * Alias for addItem() - for simple crafting system compatibility
+ * @param {string} itemKey - Item type/key
+ * @param {number} quantity - Amount to add
+ */
+ addItemToInventory(itemKey, quantity) {
+ return this.addItem(itemKey, quantity);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/LakeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/LakeSystem.js
new file mode 100644
index 000000000..5ec60e794
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/LakeSystem.js
@@ -0,0 +1,318 @@
+/**
+ * ๐๏ธ LAKE SYSTEM
+ * Generates and manages lakes across the world
+ * - Creates natural lake shapes
+ * - Biome-specific lake placement
+ * - Lake depth and shorelines
+ * - Connects to river system
+ */
+
+class LakeSystem {
+ constructor(worldWidth, worldHeight, biomeSystem) {
+ this.worldWidth = worldWidth;
+ this.worldHeight = worldHeight;
+ this.biomeSystem = biomeSystem;
+
+ // Lake map (stores depth: 0-1)
+ this.lakeMap = new Map();
+
+ // Lakes array
+ this.lakes = [];
+
+ // Lake settings
+ this.lakeCountPerBiome = {
+ grassland: 2,
+ forest: 3,
+ desert: 0, // No lakes in desert (unless oasis)
+ mountain: 2,
+ swamp: 4 // Most lakes in swamp
+ };
+
+ //ๅฐ pond settings
+ this.pondCount = 15; // Small ponds in grassland
+
+ console.log(`๐๏ธ Initializing Lake System (${worldWidth}x${worldHeight})`);
+ }
+
+ /**
+ * Generate all lakes
+ */
+ generateLakes(riverSystem = null) {
+ console.log(`๐๏ธ Generating lakes...`);
+
+ // 1. Generate major lakes per biome
+ for (const [biomeName, count] of Object.entries(this.lakeCountPerBiome)) {
+ for (let i = 0; i < count; i++) {
+ const lake = this.generateLakeInBiome(biomeName);
+ if (lake) {
+ this.lakes.push(lake);
+ }
+ }
+ }
+
+ // 2. Generate small ponds
+ for (let i = 0; i < this.pondCount; i++) {
+ const pond = this.generatePond();
+ if (pond) {
+ this.lakes.push(pond);
+ }
+ }
+
+ // 3. Generate desert oases (rare)
+ this.generateOases(2);
+
+ console.log(`โ
Generated ${this.lakes.length} lakes with ${this.lakeMap.size} water tiles`);
+ }
+
+ /**
+ * Generate a lake in specific biome
+ */
+ generateLakeInBiome(biomeName) {
+ // Find suitable location
+ const location = this.findLakeLocation(biomeName);
+ if (!location) return null;
+
+ // Lake size based on biome
+ const size = this.getLakeSizeForBiome(biomeName);
+
+ // Create lake
+ const lake = {
+ x: location.x,
+ y: location.y,
+ biome: biomeName,
+ size: size,
+ type: 'lake',
+ tiles: []
+ };
+
+ // Generate organic lake shape
+ this.generateLakeShape(lake);
+
+ return lake;
+ }
+
+ /**
+ * Find suitable location for lake
+ */
+ findLakeLocation(biomeName) {
+ const maxAttempts = 50;
+
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
+ const x = Math.floor(Math.random() * this.worldWidth);
+ const y = Math.floor(Math.random() * this.worldHeight);
+
+ const biome = this.biomeSystem.getBiomeAt(x, y);
+
+ if (biome === biomeName) {
+ // Check not too close to other lakes
+ const tooClose = this.lakes.some(lake => {
+ const dist = Math.sqrt((lake.x - x) ** 2 + (lake.y - y) ** 2);
+ return dist < 50;
+ });
+
+ if (!tooClose) {
+ return { x, y };
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get lake size for biome
+ */
+ getLakeSizeForBiome(biomeName) {
+ switch (biomeName) {
+ case 'grassland':
+ return 8 + Math.floor(Math.random() * 7); // 8-15 tiles
+ case 'forest':
+ return 10 + Math.floor(Math.random() * 8); // 10-18 tiles
+ case 'mountain':
+ return 6 + Math.floor(Math.random() * 5); // 6-11 tiles
+ case 'swamp':
+ return 12 + Math.floor(Math.random() * 10); // 12-22 tiles
+ default:
+ return 8;
+ }
+ }
+
+ /**
+ * Generate organic lake shape
+ */
+ generateLakeShape(lake) {
+ const centerX = lake.x;
+ const centerY = lake.y;
+ const radius = lake.size;
+
+ // Use cellular automata-like approach for organic shape
+ for (let dy = -radius; dy <= radius; dy++) {
+ for (let dx = -radius; dx <= radius; dx++) {
+ const x = centerX + dx;
+ const y = centerY + dy;
+
+ if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) {
+ continue;
+ }
+
+ // Distance from center
+ const dist = Math.sqrt(dx * dx + dy * dy);
+
+ // Add noise for organic shape
+ const noise = (Math.random() - 0.5) * 2;
+ const threshold = radius + noise;
+
+ if (dist < threshold) {
+ // Calculate depth (1.0 at center, 0.0 at edge)
+ const depth = 1.0 - (dist / radius);
+
+ const key = `${x},${y}`;
+ this.lakeMap.set(key, {
+ depth: Math.max(0, Math.min(1, depth)),
+ lakeId: this.lakes.length,
+ biome: lake.biome
+ });
+
+ lake.tiles.push({ x, y, depth });
+ }
+ }
+ }
+ }
+
+ /**
+ * Generate small pond
+ */
+ generatePond() {
+ // Ponds only in grassland
+ const location = this.findLakeLocation('grassland');
+ if (!location) return null;
+
+ const pond = {
+ x: location.x,
+ y: location.y,
+ biome: 'grassland',
+ size: 3 + Math.floor(Math.random() * 3), // 3-6 tiles
+ type: 'pond',
+ tiles: []
+ };
+
+ this.generateLakeShape(pond);
+ return pond;
+ }
+
+ /**
+ * Generate desert oases
+ */
+ generateOases(count) {
+ for (let i = 0; i < count; i++) {
+ const location = this.findLakeLocation('desert');
+ if (!location) continue;
+
+ const oasis = {
+ x: location.x,
+ y: location.y,
+ biome: 'desert',
+ size: 4 + Math.floor(Math.random() * 3), // 4-7 tiles
+ type: 'oasis',
+ tiles: []
+ };
+
+ this.generateLakeShape(oasis);
+ this.lakes.push(oasis);
+ }
+ }
+
+ /**
+ * Check if tile is lake
+ */
+ isLake(x, y) {
+ const key = `${x},${y}`;
+ return this.lakeMap.has(key);
+ }
+
+ /**
+ * Get lake data at tile
+ */
+ getLakeData(x, y) {
+ const key = `${x},${y}`;
+ return this.lakeMap.get(key) || null;
+ }
+
+ /**
+ * Get lake color based on depth and biome
+ */
+ getLakeColor(x, y) {
+ const data = this.getLakeData(x, y);
+ if (!data) return 0x4682B4;
+
+ const biome = data.biome;
+ const depth = data.depth;
+
+ // Base colors
+ let baseColor;
+ switch (biome) {
+ case 'forest':
+ baseColor = { r: 42, g: 95, b: 79 }; // Dark green
+ break;
+ case 'swamp':
+ baseColor = { r: 61, g: 90, b: 61 }; // Murky
+ break;
+ case 'desert':
+ baseColor = { r: 135, g: 206, b: 235 }; // Oasis blue
+ break;
+ case 'mountain':
+ baseColor = { r: 70, g: 130, b: 180 }; // Mountain blue
+ break;
+ default:
+ baseColor = { r: 30, g: 144, b: 255 }; // Default blue
+ }
+
+ // Darken based on depth
+ const r = Math.floor(baseColor.r * depth);
+ const g = Math.floor(baseColor.g * depth);
+ const b = Math.floor(baseColor.b * depth);
+
+ return (r << 16) | (g << 8) | b;
+ }
+
+ /**
+ * Get statistics
+ */
+ getStats() {
+ const typeCount = {
+ lake: 0,
+ pond: 0,
+ oasis: 0
+ };
+
+ for (const lake of this.lakes) {
+ typeCount[lake.type]++;
+ }
+
+ return {
+ totalLakes: this.lakes.length,
+ lakes: typeCount.lake,
+ ponds: typeCount.pond,
+ oases: typeCount.oasis,
+ totalWaterTiles: this.lakeMap.size
+ };
+ }
+
+ /**
+ * Export lake data
+ */
+ exportData() {
+ return {
+ lakes: this.lakes,
+ lakeMap: Array.from(this.lakeMap.entries())
+ };
+ }
+
+ /**
+ * Import lake data
+ */
+ importData(data) {
+ this.lakes = data.lakes || [];
+ this.lakeMap = new Map(data.lakeMap || []);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/LandmarkQuestSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/LandmarkQuestSystem.js
new file mode 100644
index 000000000..632513f3c
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/LandmarkQuestSystem.js
@@ -0,0 +1,353 @@
+/**
+ * ๐ LANDMARK QUEST SYSTEM
+ * Quest system integrated with landmarks and structures
+ * - Main quest: Visit all 5 landmarks
+ * - Side quests from NPCs
+ * - Exploration rewards
+ * - Quest tracking
+ */
+
+class LandmarkQuestSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Active quests
+ this.activeQuests = [];
+
+ // Completed quests
+ this.completedQuests = [];
+
+ // Quest definitions
+ this.quests = {
+ // Main quest chain
+ 'main_explore_landmarks': {
+ id: 'main_explore_landmarks',
+ name: 'The Five Landmarks',
+ description: 'Discover all 5 legendary landmarks across the world',
+ type: 'main',
+ objectives: [
+ { type: 'visit_landmark', target: 'ancient_temple', name: 'Visit Ancient Temple', completed: false },
+ { type: 'visit_landmark', target: 'great_pyramid', name: 'Visit Great Pyramid', completed: false },
+ { type: 'visit_landmark', target: 'mountain_peak', name: 'Reach Mountain Peak', completed: false },
+ { type: 'visit_landmark', target: 'abandoned_city', name: 'Explore Abandoned City', completed: false },
+ { type: 'visit_landmark', target: 'dragon_skeleton', name: 'Find Dragon Skeleton', completed: false }
+ ],
+ rewards: {
+ gold: 5000,
+ xp: 10000,
+ item: 'legendary_compass'
+ }
+ },
+
+ // Biome exploration quests
+ 'explore_grassland': {
+ id: 'explore_grassland',
+ name: 'Grassland Explorer',
+ description: 'Visit 10 structures in Grassland biome',
+ type: 'side',
+ objectives: [
+ { type: 'visit_structures', biome: 'Grassland', count: 0, target: 10, completed: false }
+ ],
+ rewards: { gold: 500, xp: 1000 }
+ },
+
+ 'explore_forest': {
+ id: 'explore_forest',
+ name: 'Forest Wanderer',
+ description: 'Visit 10 structures in Forest biome',
+ type: 'side',
+ objectives: [
+ { type: 'visit_structures', biome: 'Forest', count: 0, target: 10, completed: false }
+ ],
+ rewards: { gold: 500, xp: 1000 }
+ },
+
+ 'explore_desert': {
+ id: 'explore_desert',
+ name: 'Desert Nomad',
+ description: 'Visit 5 structures in Desert biome',
+ type: 'side',
+ objectives: [
+ { type: 'visit_structures', biome: 'Desert', count: 0, target: 5, completed: false }
+ ],
+ rewards: { gold: 750, xp: 1500 }
+ },
+
+ 'explore_mountain': {
+ id: 'explore_mountain',
+ name: 'Mountain Climber',
+ description: 'Visit 5 structures in Mountain biome',
+ type: 'side',
+ objectives: [
+ { type: 'visit_structures', biome: 'Mountain', count: 0, target: 5, completed: false }
+ ],
+ rewards: { gold: 750, xp: 1500 }
+ },
+
+ 'explore_swamp': {
+ id: 'explore_swamp',
+ name: 'Swamp Explorer',
+ description: 'Visit 5 structures in Swamp biome',
+ type: 'side',
+ objectives: [
+ { type: 'visit_structures', biome: 'Swamp', count: 0, target: 5, completed: false }
+ ],
+ rewards: { gold: 750, xp: 1500 }
+ },
+
+ // Enemy quests
+ 'slay_enemies': {
+ id: 'slay_enemies',
+ name: 'Monster Hunter',
+ description: 'Defeat 20 enemies',
+ type: 'side',
+ objectives: [
+ { type: 'kill_enemies', count: 0, target: 20, completed: false }
+ ],
+ rewards: { gold: 1000, xp: 2000 }
+ },
+
+ // Collection quest
+ 'treasure_hunter': {
+ id: 'treasure_hunter',
+ name: 'Treasure Hunter',
+ description: 'Open 30 chests',
+ type: 'side',
+ objectives: [
+ { type: 'open_chests', count: 0, target: 30, completed: false }
+ ],
+ rewards: { gold: 2000, xp: 3000 }
+ }
+ };
+
+ // Tracking
+ this.landmarksVisited = [];
+ this.structuresVisitedByBiome = {};
+
+ console.log('๐ LandmarkQuestSystem initialized');
+ }
+
+ // Start main quest automatically
+ startMainQuest() {
+ this.activeQuests.push('main_explore_landmarks');
+ console.log('๐ Main quest started: The Five Landmarks');
+ this.showQuestNotification('New Quest!', 'The Five Landmarks', 'Discover all 5 legendary landmarks');
+ }
+
+ // Start exploration quests
+ startExplorationQuests() {
+ this.activeQuests.push('explore_grassland');
+ this.activeQuests.push('explore_forest');
+ this.activeQuests.push('explore_desert');
+ this.activeQuests.push('explore_mountain');
+ this.activeQuests.push('explore_swamp');
+ console.log('๐ Started 5 biome exploration quests');
+ }
+
+ // Visit landmark (called from player)
+ visitLandmark(landmarkType) {
+ if (this.landmarksVisited.includes(landmarkType)) return;
+
+ this.landmarksVisited.push(landmarkType);
+ console.log(`๐ฟ Visited landmark: ${landmarkType}`);
+
+ // Update main quest
+ const mainQuest = this.quests['main_explore_landmarks'];
+ if (this.activeQuests.includes('main_explore_landmarks')) {
+ for (const obj of mainQuest.objectives) {
+ if (obj.target === landmarkType) {
+ obj.completed = true;
+ this.showQuestNotification('Objective Complete!', obj.name, '+2000 XP');
+ break;
+ }
+ }
+
+ // Check if all objectives complete
+ if (mainQuest.objectives.every(o => o.completed)) {
+ this.completeQuest('main_explore_landmarks');
+ }
+ }
+ }
+
+ // Visit structure (for biome quests)
+ visitStructure(x, y, biome) {
+ const key = `${x},${y}`;
+
+ if (!this.structuresVisitedByBiome[biome]) {
+ this.structuresVisitedByBiome[biome] = [];
+ }
+
+ if (this.structuresVisitedByBiome[biome].includes(key)) return;
+
+ this.structuresVisitedByBiome[biome].push(key);
+
+ // Update biome exploration quests
+ const questId = `explore_${biome.toLowerCase()}`;
+ if (this.activeQuests.includes(questId)) {
+ const quest = this.quests[questId];
+ for (const obj of quest.objectives) {
+ if (obj.biome === biome) {
+ obj.count++;
+ if (obj.count >= obj.target && !obj.completed) {
+ obj.completed = true;
+ this.completeQuest(questId);
+ }
+ }
+ }
+ }
+ }
+
+ // Complete quest and give rewards
+ completeQuest(questId) {
+ const quest = this.quests[questId];
+ if (!quest) return;
+
+ console.log(`โ
Quest completed: ${quest.name}`);
+
+ // Remove from active
+ const index = this.activeQuests.indexOf(questId);
+ if (index > -1) {
+ this.activeQuests.splice(index, 1);
+ }
+
+ // Add to completed
+ this.completedQuests.push(questId);
+
+ // Give rewards
+ if (this.scene.inventorySystem && quest.rewards) {
+ if (quest.rewards.gold) {
+ this.scene.inventorySystem.gold += quest.rewards.gold;
+ }
+ if (quest.rewards.item) {
+ this.scene.inventorySystem.addItem(quest.rewards.item, 1);
+ }
+ }
+
+ // Show completion
+ this.showQuestCompleteNotification(quest);
+
+ // Play sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.beepPickup();
+ }
+ }
+
+ // Show quest notification
+ showQuestNotification(title, questName, description) {
+ const notification = this.scene.add.text(
+ this.scene.cameras.main.centerX,
+ 100,
+ `${title}\n${questName}\n${description}`,
+ {
+ fontSize: '24px',
+ color: '#FFD700',
+ backgroundColor: '#000000',
+ padding: { x: 30, y: 20 },
+ align: 'center'
+ }
+ );
+ notification.setOrigin(0.5);
+ notification.setScrollFactor(0);
+ notification.setDepth(10001);
+
+ this.scene.tweens.add({
+ targets: notification,
+ alpha: 0,
+ duration: 1000,
+ delay: 3000,
+ onComplete: () => notification.destroy()
+ });
+ }
+
+ // Show quest complete notification
+ showQuestCompleteNotification(quest) {
+ const rewardText = [];
+ if (quest.rewards.gold) rewardText.push(`+${quest.rewards.gold} gold`);
+ if (quest.rewards.xp) rewardText.push(`+${quest.rewards.xp} XP`);
+ if (quest.rewards.item) rewardText.push(`+${quest.rewards.item}`);
+
+ const notification = this.scene.add.text(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.centerY,
+ `๐ QUEST COMPLETE! ๐\n${quest.name}\n\nRewards:\n${rewardText.join('\n')}`,
+ {
+ fontSize: '32px',
+ color: '#FFD700',
+ backgroundColor: '#000000',
+ padding: { x: 40, y: 30 },
+ align: 'center'
+ }
+ );
+ notification.setOrigin(0.5);
+ notification.setScrollFactor(0);
+ notification.setDepth(10002);
+
+ // Fireworks effect
+ for (let i = 0; i < 30; i++) {
+ const particle = this.scene.add.circle(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.centerY,
+ 5,
+ [0xFFD700, 0xFF69B4, 0x00BFFF][i % 3]
+ );
+ particle.setScrollFactor(0);
+ particle.setDepth(10001);
+
+ this.scene.tweens.add({
+ targets: particle,
+ x: particle.x + (Math.random() - 0.5) * 400,
+ y: particle.y + (Math.random() - 0.5) * 400,
+ alpha: 0,
+ duration: 2000,
+ onComplete: () => particle.destroy()
+ });
+ }
+
+ this.scene.tweens.add({
+ targets: notification,
+ alpha: 0,
+ duration: 1000,
+ delay: 4000,
+ onComplete: () => notification.destroy()
+ });
+ }
+
+ // Get active quests for UI
+ getActiveQuests() {
+ return this.activeQuests.map(id => this.quests[id]);
+ }
+
+ // Get quest progress
+ getQuestProgress(questId) {
+ const quest = this.quests[questId];
+ if (!quest) return null;
+
+ const completed = quest.objectives.filter(o => o.completed).length;
+ const total = quest.objectives.length;
+
+ return { completed, total, objectives: quest.objectives };
+ }
+
+ // Export/Import for save system
+ exportData() {
+ return {
+ activeQuests: this.activeQuests,
+ completedQuests: this.completedQuests,
+ landmarksVisited: this.landmarksVisited,
+ structuresVisitedByBiome: this.structuresVisitedByBiome
+ };
+ }
+
+ importData(data) {
+ if (!data) return;
+ this.activeQuests = data.activeQuests || [];
+ this.completedQuests = data.completedQuests || [];
+ this.landmarksVisited = data.landmarksVisited || [];
+ this.structuresVisitedByBiome = data.structuresVisitedByBiome || {};
+ }
+
+ destroy() {
+ this.activeQuests = [];
+ this.completedQuests = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/LawyerOfficeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/LawyerOfficeSystem.js
new file mode 100644
index 000000000..c645179d2
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/LawyerOfficeSystem.js
@@ -0,0 +1,554 @@
+/**
+ * LAWYER OFFICE SYSTEM - Divorce & Marriage Legal Services
+ * Part of: New Town Buildings ("Drama Hub")
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Divorce processing (50,000g + 25% money loss)
+ * - Prenuptial agreement system (preventive, 10,000g)
+ * - Marriage counseling (alternative to divorce, 5,000g)
+ * - Relationship crisis detection
+ * - Post-divorce quests
+ */
+
+class LawyerOfficeSystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Lawyer office status
+ this.isUnlocked = false;
+ this.lawyer = null;
+
+ // Divorce costs
+ this.divorceCost = 50000;
+ this.divorceMoneyLoss = 0.25; // 25% of remaining money
+ this.divorceMoneyLossWithPrenup = 0.10; // 10% with prenup
+
+ // Prenup system
+ this.hasPrenup = false;
+ this.prenupCost = 10000;
+
+ // Marriage counseling
+ this.counselingCost = 5000;
+ this.counselingInProgress = false;
+ this.counselingTasksCompleted = 0;
+ this.counselingTasksRequired = 3;
+
+ // Divorce history
+ this.divorceHistory = [];
+ this.hasEverDivorced = false;
+ }
+
+ /**
+ * Auto-unlock lawyer office when marriage is in crisis
+ */
+ checkAutoUnlock() {
+ // Auto-unlock if married AND relationship โค 3 hearts
+ if (this.player.isMarried) {
+ const spouse = this.game.npcs.getSpouse();
+ if (spouse && spouse.relationshipHearts <= 3) {
+ this.unlockLawyerOffice();
+ }
+ }
+ }
+
+ /**
+ * Unlock lawyer office
+ */
+ unlockLawyerOffice() {
+ if (this.isUnlocked) return;
+
+ // Auto-build (15,000g automatically deducted)
+ if (this.player.money < 15000) {
+ // Build on credit (lawyer is greedy!)
+ this.player.money = Math.max(0, this.player.money - 15000);
+ this.player.debt = Math.abs(Math.min(0, this.player.money));
+ } else {
+ this.player.money -= 15000;
+ }
+
+ this.isUnlocked = true;
+
+ // Spawn lawyer NPC
+ this.lawyer = this.game.npcs.spawn('lawyer', {
+ name: 'Mr. Sterling',
+ position: { x: 1600, y: 1000 },
+ appearance: {
+ clothing: 'black_suit',
+ demeanor: 'cold_professional'
+ },
+ dialogue: this.getLawyerDialogue()
+ });
+
+ // Trigger ominous quest
+ this.game.quests.start('till_death_do_us_part');
+
+ this.game.showMessage('โ๏ธ Lawyer Office has been built...');
+
+ return { success: true };
+ }
+
+ /**
+ * Get lawyer dialogue
+ */
+ getLawyerDialogue() {
+ return {
+ greeting: [
+ "Marriage is a contract. Divorce is... expensive.",
+ "Love is temporary. Legal documents are forever.",
+ "Here for business or pleasure? I only deal in business."
+ ],
+ divorce_warning: [
+ "Are you ABSOLUTELY sure? This will cost you everything.",
+ "Divorce is final. There's no going back.",
+ "I've seen many regret this decision. Proceed?"
+ ],
+ prenup: [
+ "Smart move. Protect your assets before it's too late.",
+ "A prenup? Planning ahead, I see. Wise.",
+ "Marriage without a prenup is... optimistic."
+ ],
+ counseling: [
+ "There may be another way. Have you tried talking?",
+ "Marriage counseling. Less expensive than divorce.",
+ "Fix it now or pay later. Your choice."
+ ],
+ post_divorce: [
+ "Single again. How does freedom feel?",
+ "Expensive lesson learned, I hope.",
+ "The papers are filed. You're free to go."
+ ]
+ };
+ }
+
+ /**
+ * Purchase prenuptial agreement (BEFORE marriage only!)
+ */
+ purchasePrenup() {
+ // Can only buy BEFORE getting married
+ if (this.player.isMarried) {
+ return {
+ success: false,
+ message: 'Too late! Prenups must be signed BEFORE marriage!'
+ };
+ }
+
+ if (this.hasPrenup) {
+ return {
+ success: false,
+ message: 'You already have a prenup!'
+ };
+ }
+
+ if (this.player.money < this.prenupCost) {
+ return {
+ success: false,
+ message: `Need ${this.prenupCost}g for a prenup!`
+ };
+ }
+
+ // Purchase
+ this.player.money -= this.prenupCost;
+ this.hasPrenup = true;
+
+ this.game.showMessage(
+ `Prenup signed! Divorce money loss reduced to 10%. ${this.prenupCost}g`
+ );
+
+ return { success: true };
+ }
+
+ /**
+ * Initiate divorce process
+ */
+ initiateDivorce() {
+ // Check if married
+ if (!this.player.isMarried) {
+ return {
+ success: false,
+ message: 'You\'re not married!'
+ };
+ }
+
+ const spouse = this.game.npcs.getSpouse();
+
+ // Show divorce confirmation with full consequences
+ const moneyLossPercent = this.hasPrenup ? 10 : 25;
+ const estimatedLoss = Math.floor(this.player.money * (this.hasPrenup ? 0.10 : 0.25));
+
+ const divorceInfo = {
+ cost: this.divorceCost,
+ moneyLoss: estimatedLoss,
+ moneyLossPercent: moneyLossPercent,
+ spouse: spouse.name,
+ consequences: [
+ `Divorce fee: ${this.divorceCost}g`,
+ `Money loss: ${moneyLossPercent}% (${estimatedLoss}g)`,
+ `${spouse.name} relationship reset to 0 hearts`,
+ `${spouse.name} moves out of farmhouse`,
+ 'Lose "married" status benefits',
+ 'King-size bed reverts to single use'
+ ]
+ };
+
+ // Emit event for UI to show confirmation dialog
+ this.game.emit('divorceConfirmationRequired', divorceInfo);
+
+ return {
+ success: false, // Not completed yet, waiting for confirmation
+ requiresConfirmation: true,
+ divorceInfo: divorceInfo
+ };
+ }
+
+ /**
+ * Confirm and process divorce (after player confirms)
+ */
+ processDivorce() {
+ if (!this.player.isMarried) {
+ return { success: false, message: 'Not married!' };
+ }
+
+ const spouse = this.game.npcs.getSpouse();
+
+ // Calculate total cost
+ const divorceFee = this.divorceCost;
+ const moneyLossPercent = this.hasPrenup ? this.divorceMoneyLossWithPrenup : this.divorceMoneyLoss;
+
+ // Check if can afford divorce fee
+ if (this.player.money < divorceFee) {
+ return {
+ success: false,
+ message: `Not enough money! Divorce costs ${divorceFee}g!`
+ };
+ }
+
+ // Deduct divorce fee
+ this.player.money -= divorceFee;
+
+ // Calculate and deduct money loss
+ const moneyLoss = Math.floor(this.player.money * moneyLossPercent);
+ this.player.money -= moneyLoss;
+
+ // Reset relationship
+ const previousHearts = spouse.relationshipHearts;
+ spouse.relationshipHearts = 0;
+ spouse.relationshipPoints = 0;
+
+ // Spouse moves out
+ spouse.homeLocation = spouse.originalHome;
+ spouse.isLivingWithPlayer = false;
+
+ // Remove married status
+ this.player.isMarried = false;
+ this.player.spouse = null;
+
+ // Downgrade bed
+ if (this.game.sleepSystem) {
+ const kingSizeBed = this.game.sleepSystem.bedTypes.KING_SIZE_BED;
+ kingSizeBed.spousePresent = false;
+ }
+
+ // Record divorce in history
+ this.divorceHistory.push({
+ spouse: spouse.name,
+ date: this.game.time.currentDate,
+ costTotal: divorceFee + moneyLoss,
+ heartsLost: previousHearts
+ });
+
+ this.hasEverDivorced = true;
+
+ // Trigger post-divorce quest
+ this.game.quests.start('single_again');
+
+ // Spouse reaction (they're devastated)
+ this.game.showDialogue(
+ spouse.name,
+ "I can't believe this is happening... Goodbye.",
+ {
+ mood: 'heartbroken',
+ animation: 'crying'
+ }
+ );
+
+ // Show final message
+ this.game.showMessage(
+ `Divorced ${spouse.name}. Total cost: ${divorceFee + moneyLoss}g. You are now single.`
+ );
+
+ // Lawyer's cold response
+ setTimeout(() => {
+ this.game.showDialogue(
+ this.lawyer.name,
+ "The papers are filed. You're free to go. That'll be " + divorceFee + "g."
+ );
+ }, 3000);
+
+ return {
+ success: true,
+ totalCost: divorceFee + moneyLoss,
+ moneyLoss: moneyLoss,
+ spouse: spouse.name
+ };
+ }
+
+ /**
+ * Start marriage counseling (alternative to divorce)
+ */
+ startCounseling() {
+ if (!this.player.isMarried) {
+ return {
+ success: false,
+ message: 'You\'re not married!'
+ };
+ }
+
+ if (this.counselingInProgress) {
+ return {
+ success: false,
+ message: 'Counseling already in progress!'
+ };
+ }
+
+ if (this.player.money < this.counselingCost) {
+ return {
+ success: false,
+ message: `Marriage counseling costs ${this.counselingCost}g!`
+ };
+ }
+
+ // Pay for counseling
+ this.player.money -= this.counselingCost;
+ this.counselingInProgress = true;
+ this.counselingTasksCompleted = 0;
+
+ const spouse = this.game.npcs.getSpouse();
+
+ // Generate counseling tasks
+ this.counselingTasks = this.generateCounselingTasks(spouse);
+
+ // Start counseling quest
+ this.game.quests.start('marriage_counseling', {
+ tasks: this.counselingTasks
+ });
+
+ this.game.showMessage(
+ `Marriage counseling started! Complete 3 tasks to save your marriage. ${this.counselingCost}g`
+ );
+
+ return {
+ success: true,
+ tasks: this.counselingTasks
+ };
+ }
+
+ /**
+ * Generate counseling tasks based on spouse
+ */
+ generateCounselingTasks(spouse) {
+ const tasks = [
+ {
+ id: 'gift_favorite',
+ description: `Give ${spouse.name} their favorite gift`,
+ requirement: {
+ type: 'gift',
+ item: spouse.favoriteGift,
+ target: spouse.id
+ },
+ completed: false
+ },
+ {
+ id: 'date_night',
+ description: `Take ${spouse.name} on a date to their favorite location`,
+ requirement: {
+ type: 'visit_location',
+ location: spouse.favoriteLocation,
+ withNPC: spouse.id
+ },
+ completed: false
+ },
+ {
+ id: 'heart_to_heart',
+ description: `Have a deep conversation with ${spouse.name}`,
+ requirement: {
+ type: 'dialogue',
+ dialogueId: 'heart_to_heart',
+ target: spouse.id
+ },
+ completed: false
+ }
+ ];
+
+ return tasks;
+ }
+
+ /**
+ * Complete counseling task
+ */
+ completeCounselingTask(taskId) {
+ if (!this.counselingInProgress) {
+ return { success: false };
+ }
+
+ const task = this.counselingTasks.find(t => t.id === taskId);
+ if (!task || task.completed) {
+ return { success: false };
+ }
+
+ // Mark task as completed
+ task.completed = true;
+ this.counselingTasksCompleted++;
+
+ this.game.showMessage(
+ `Counseling task completed! (${this.counselingTasksCompleted}/${this.counselingTasksRequired})`
+ );
+
+ // Check if all tasks done
+ if (this.counselingTasksCompleted >= this.counselingTasksRequired) {
+ this.finishCounseling(true);
+ }
+
+ return { success: true };
+ }
+
+ /**
+ * Finish counseling (success or failure)
+ */
+ finishCounseling(success) {
+ const spouse = this.game.npcs.getSpouse();
+
+ if (success) {
+ // Restore relationship!
+ spouse.addRelationshipPoints(500); // +5 hearts
+
+ this.game.showDialogue(
+ spouse.name,
+ "I'm so glad we worked through this. I love you.",
+ {
+ mood: 'happy',
+ animation: 'hug'
+ }
+ );
+
+ this.game.showMessage(
+ `Marriage saved! ${spouse.name}: +5 โค๏ธ`
+ );
+
+ // Complete quest
+ this.game.quests.complete('marriage_counseling');
+
+ } else {
+ // Counseling failed
+ this.game.showDialogue(
+ spouse.name,
+ "This isn't working. Maybe it's time to let go...",
+ {
+ mood: 'sad'
+ }
+ );
+
+ this.game.showMessage(
+ 'Counseling failed. Relationship worsened.'
+ );
+
+ // Relationship damage
+ spouse.addRelationshipPoints(-200); // -2 hearts
+
+ // Fail quest
+ this.game.quests.fail('marriage_counseling');
+ }
+
+ this.counselingInProgress = false;
+ this.counselingTasks = [];
+ this.counselingTasksCompleted = 0;
+ }
+
+ /**
+ * Get relationship crisis warning
+ */
+ getRelationshipCrisisWarning() {
+ if (!this.player.isMarried) {
+ return null;
+ }
+
+ const spouse = this.game.npcs.getSpouse();
+
+ if (spouse.relationshipHearts <= 3) {
+ return {
+ severity: 'critical',
+ message: `Your relationship with ${spouse.name} is in CRISIS! (${spouse.relationshipHearts} โค๏ธ)`,
+ suggestion: 'Visit the Lawyer Office for marriage counseling before it\'s too late!'
+ };
+ }
+
+ if (spouse.relationshipHearts <= 5) {
+ return {
+ severity: 'warning',
+ message: `Your relationship with ${spouse.name} is struggling. (${spouse.relationshipHearts} โค๏ธ)`,
+ suggestion: 'Spend more time together and give gifts.'
+ };
+ }
+
+ return null;
+ }
+
+ /**
+ * Check if can remarry after divorce
+ */
+ canRemarry() {
+ if (this.player.isMarried) {
+ return { canRemarry: false, reason: 'Already married!' };
+ }
+
+ if (!this.hasEverDivorced) {
+ return { canRemarry: true };
+ }
+
+ // Must wait 28 days (4 weeks) after divorce to remarry
+ const lastDivorce = this.divorceHistory[this.divorceHistory.length - 1];
+ const daysSinceDivorce = this.game.time.currentDate - lastDivorce.date;
+
+ if (daysSinceDivorce < 28) {
+ return {
+ canRemarry: false,
+ reason: `Must wait ${28 - daysSinceDivorce} more days after divorce.`
+ };
+ }
+
+ return { canRemarry: true };
+ }
+
+ /**
+ * Get office UI data
+ */
+ getOfficeUIData() {
+ return {
+ isUnlocked: this.isUnlocked,
+ lawyer: this.lawyer,
+ isMarried: this.player.isMarried,
+ spouse: this.player.isMarried ? this.game.npcs.getSpouse() : null,
+ divorceCost: this.divorceCost,
+ moneyLossPercent: this.hasPrenup ? 10 : 25,
+ hasPrenup: this.hasPrenup,
+ prenupCost: this.prenupCost,
+ counselingCost: this.counselingCost,
+ counselingInProgress: this.counselingInProgress,
+ counselingTasks: this.counselingTasks,
+ divorceHistory: this.divorceHistory,
+ crisisWarning: this.getRelationshipCrisisWarning()
+ };
+ }
+
+ /**
+ * Update (check for auto-unlock)
+ */
+ update() {
+ if (!this.isUnlocked) {
+ this.checkAutoUnlock();
+ }
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/LeaderboardSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/LeaderboardSystem.js
new file mode 100644
index 000000000..0396ef4c4
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/LeaderboardSystem.js
@@ -0,0 +1,388 @@
+/**
+ * LeaderboardSystem.js
+ * =====================
+ * KRVAVA ลฝETEV - Leaderboard & Online Integration
+ *
+ * Features:
+ * - Local leaderboards
+ * - Steam API integration (ready)
+ * - Firebase integration (ready)
+ * - Multiple categories
+ * - Score submission & retrieval
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class LeaderboardSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Integration mode
+ this.mode = 'local'; // 'local', 'steam', 'firebase'
+
+ // Local leaderboards
+ this.localLeaderboards = new Map();
+
+ // Online connection
+ this.isOnline = false;
+ this.steamInitialized = false;
+ this.firebaseInitialized = false;
+
+ // Leaderboard categories
+ this.categories = {
+ horde_waves: { name: 'Horde Waves Survived', icon: '๐', type: 'high_score' },
+ farm_size: { name: 'Largest Farm', icon: '๐พ', type: 'high_score' },
+ zombie_army: { name: 'Most Zombies Tamed', icon: '๐ง', type: 'high_score' },
+ boss_speedrun: { name: 'Troll King Speedrun', icon: 'โ๏ธ', type: 'low_time' },
+ wealth: { name: 'Total Wealth', icon: '๐ฐ', type: 'high_score' },
+ generations: { name: 'Most Generations', icon: '๐จโ๐ฉโ๐งโ๐ฆ', type: 'high_score' },
+ collection: { name: 'Album Completion %', icon: '๐', type: 'high_score' }
+ };
+
+ console.log('๐ LeaderboardSystem initialized');
+
+ // Initialize local storage
+ this.initializeLocalLeaderboards();
+
+ // Try to connect to online services
+ this.initializeOnlineServices();
+ }
+
+ /**
+ * Initialize local leaderboards
+ */
+ initializeLocalLeaderboards() {
+ Object.keys(this.categories).forEach(category => {
+ this.localLeaderboards.set(category, []);
+ });
+
+ // Load from localStorage
+ this.loadLocalLeaderboards();
+
+ console.log('โ
Local leaderboards initialized');
+ }
+
+ /**
+ * Load from localStorage
+ */
+ loadLocalLeaderboards() {
+ try {
+ const saved = localStorage.getItem('krvava_zetev_leaderboards');
+ if (saved) {
+ const data = JSON.parse(saved);
+ Object.keys(data).forEach(category => {
+ this.localLeaderboards.set(category, data[category]);
+ });
+ console.log('๐ฅ Loaded local leaderboards from storage');
+ }
+ } catch (error) {
+ console.error('Failed to load local leaderboards:', error);
+ }
+ }
+
+ /**
+ * Save to localStorage
+ */
+ saveLocalLeaderboards() {
+ try {
+ const data = {};
+ this.localLeaderboards.forEach((entries, category) => {
+ data[category] = entries;
+ });
+ localStorage.setItem('krvava_zetev_leaderboards', JSON.stringify(data));
+ } catch (error) {
+ console.error('Failed to save local leaderboards:', error);
+ }
+ }
+
+ /**
+ * Initialize online services
+ */
+ initializeOnlineServices() {
+ // Try Steam
+ if (typeof window.Steamworks !== 'undefined') {
+ this.initializeSteam();
+ }
+
+ // Try Firebase
+ if (typeof window.firebase !== 'undefined') {
+ this.initializeFirebase();
+ }
+
+ if (!this.steamInitialized && !this.firebaseInitialized) {
+ console.log('๐ด Running in offline mode - local leaderboards only');
+ }
+ }
+
+ /**
+ * Initialize Steam API
+ */
+ initializeSteam() {
+ try {
+ // Steam API initialization
+ // TODO: Replace with actual Steamworks.js integration
+ console.log('๐ฎ Initializing Steam API...');
+
+ // Example Steam API calls (pseudo-code):
+ // window.Steamworks.init();
+ // window.Steamworks.getPlayerName();
+
+ this.steamInitialized = true;
+ this.mode = 'steam';
+ this.isOnline = true;
+
+ console.log('โ
Steam API connected!');
+
+ this.showNotification({
+ title: 'Steam Connected',
+ text: '๐ฎ Online leaderboards enabled!',
+ icon: 'โ
'
+ });
+ } catch (error) {
+ console.error('Steam initialization failed:', error);
+ this.steamInitialized = false;
+ }
+ }
+
+ /**
+ * Initialize Firebase
+ */
+ initializeFirebase() {
+ try {
+ // Firebase initialization
+ // TODO: Replace with actual Firebase config
+ console.log('๐ฅ Initializing Firebase...');
+
+ const firebaseConfig = {
+ apiKey: "YOUR_API_KEY",
+ authDomain: "krvava-zetev.firebaseapp.com",
+ databaseURL: "https://krvava-zetev.firebaseio.com",
+ projectId: "krvava-zetev",
+ storageBucket: "krvava-zetev.appspot.com"
+ };
+
+ // firebase.initializeApp(firebaseConfig);
+ // firebase.database();
+
+ this.firebaseInitialized = true;
+ if (!this.steamInitialized) {
+ this.mode = 'firebase';
+ this.isOnline = true;
+ }
+
+ console.log('โ
Firebase connected!');
+ } catch (error) {
+ console.error('Firebase initialization failed:', error);
+ this.firebaseInitialized = false;
+ }
+ }
+
+ /**
+ * Submit score
+ */
+ submitScore(category, score, playerName = 'Player', metadata = {}) {
+ const entry = {
+ playerName: playerName,
+ score: score,
+ date: new Date().toISOString(),
+ metadata: metadata
+ };
+
+ console.log(`๐ Submitting score: ${category} = ${score}`);
+
+ // Submit to appropriate service
+ switch (this.mode) {
+ case 'steam':
+ this.submitToSteam(category, entry);
+ break;
+ case 'firebase':
+ this.submitToFirebase(category, entry);
+ break;
+ default:
+ this.submitToLocal(category, entry);
+ }
+
+ return true;
+ }
+
+ /**
+ * Submit to local leaderboard
+ */
+ submitToLocal(category, entry) {
+ let leaderboard = this.localLeaderboards.get(category) || [];
+ const categoryData = this.categories[category];
+
+ // Add entry
+ leaderboard.push(entry);
+
+ // Sort
+ if (categoryData.type === 'high_score') {
+ leaderboard.sort((a, b) => b.score - a.score);
+ } else {
+ leaderboard.sort((a, b) => a.score - b.score);
+ }
+
+ // Keep top 100
+ leaderboard = leaderboard.slice(0, 100);
+
+ this.localLeaderboards.set(category, leaderboard);
+ this.saveLocalLeaderboards();
+
+ console.log(`โ
Score submitted to local leaderboard: ${category}`);
+ }
+
+ /**
+ * Submit to Steam
+ */
+ submitToSteam(category, entry) {
+ try {
+ // Steam leaderboard submission
+ // Pseudo-code:
+ // window.Steamworks.uploadLeaderboardScore(category, entry.score);
+
+ console.log(`๐ฎ Submitted to Steam: ${category} = ${entry.score}`);
+
+ // Also save locally as backup
+ this.submitToLocal(category, entry);
+ } catch (error) {
+ console.error('Steam submission failed:', error);
+ this.submitToLocal(category, entry);
+ }
+ }
+
+ /**
+ * Submit to Firebase
+ */
+ submitToFirebase(category, entry) {
+ try {
+ // Firebase database submission
+ // Pseudo-code:
+ // firebase.database().ref(`leaderboards/${category}`).push(entry);
+
+ console.log(`๐ฅ Submitted to Firebase: ${category} = ${entry.score}`);
+
+ // Also save locally as backup
+ this.submitToLocal(category, entry);
+ } catch (error) {
+ console.error('Firebase submission failed:', error);
+ this.submitToLocal(category, entry);
+ }
+ }
+
+ /**
+ * Get leaderboard
+ */
+ async getLeaderboard(category, limit = 10) {
+ switch (this.mode) {
+ case 'steam':
+ return await this.getSteamLeaderboard(category, limit);
+ case 'firebase':
+ return await this.getFirebaseLeaderboard(category, limit);
+ default:
+ return this.getLocalLeaderboard(category, limit);
+ }
+ }
+
+ /**
+ * Get local leaderboard
+ */
+ getLocalLeaderboard(category, limit = 10) {
+ const leaderboard = this.localLeaderboards.get(category) || [];
+ return leaderboard.slice(0, limit);
+ }
+
+ /**
+ * Get Steam leaderboard
+ */
+ async getSteamLeaderboard(category, limit = 10) {
+ try {
+ // Steam leaderboard retrieval
+ // Pseudo-code:
+ // const entries = await window.Steamworks.getLeaderboardEntries(category, limit);
+ // return entries;
+
+ console.log(`๐ฎ Fetching Steam leaderboard: ${category}`);
+
+ // Fallback to local
+ return this.getLocalLeaderboard(category, limit);
+ } catch (error) {
+ console.error('Steam fetch failed:', error);
+ return this.getLocalLeaderboard(category, limit);
+ }
+ }
+
+ /**
+ * Get Firebase leaderboard
+ */
+ async getFirebaseLeaderboard(category, limit = 10) {
+ try {
+ // Firebase leaderboard retrieval
+ // Pseudo-code:
+ // const snapshot = await firebase.database()
+ // .ref(`leaderboards/${category}`)
+ // .orderByChild('score')
+ // .limitToLast(limit)
+ // .once('value');
+ // return snapshot.val();
+
+ console.log(`๐ฅ Fetching Firebase leaderboard: ${category}`);
+
+ // Fallback to local
+ return this.getLocalLeaderboard(category, limit);
+ } catch (error) {
+ console.error('Firebase fetch failed:', error);
+ return this.getLocalLeaderboard(category, limit);
+ }
+ }
+
+ /**
+ * Get player rank
+ */
+ async getPlayerRank(category, playerName) {
+ const leaderboard = await this.getLeaderboard(category, 1000);
+ const rank = leaderboard.findIndex(entry => entry.playerName === playerName);
+ return rank >= 0 ? rank + 1 : null;
+ }
+
+ /**
+ * Get all categories
+ */
+ getCategories() {
+ return Object.keys(this.categories).map(id => ({
+ id: id,
+ ...this.categories[id]
+ }));
+ }
+
+ /**
+ * Check if online
+ */
+ isConnectedOnline() {
+ return this.isOnline;
+ }
+
+ /**
+ * Get connection status
+ */
+ getConnectionStatus() {
+ return {
+ mode: this.mode,
+ isOnline: this.isOnline,
+ steam: this.steamInitialized,
+ firebase: this.firebaseInitialized
+ };
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/LegacySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/LegacySystem.js
new file mode 100644
index 000000000..49c1549bd
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/LegacySystem.js
@@ -0,0 +1,144 @@
+class LegacySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // AGE & GENERATION
+ this.generation = 1;
+ this.birthDay = 1; // Global Day count when current char was born
+ this.currentAge = 18; // Start at 18
+
+ // FAMILY
+ this.isMarried = false;
+ this.spouseName = null;
+ this.childName = null;
+ this.childAge = 0;
+
+ // FRACTIONS / REPUTATION
+ this.reputation = {
+ survivors: 0, // Human NPCs
+ mutants: -50, // Starts hostile
+ zombies: 0 // Handled by Hybrid Skill, but we can track here too
+ };
+
+ // Listen for Day Change logic
+ this.scene.events.on('update', (time, delta) => this.update(time, delta));
+ }
+
+ update(time, delta) {
+ if (!this.scene.weatherSystem) return;
+
+ const currentDay = this.scene.weatherSystem.dayCount;
+
+ // Calculate Age
+ // 1 Year = 28 Days (4 seasons * 7 days).
+ const daysAlive = Math.max(0, currentDay - this.birthDay);
+ const yearsAlive = Math.floor(daysAlive / 28);
+
+ // Don't spam update
+ if (this.currentAge !== 18 + yearsAlive) {
+ this.currentAge = 18 + yearsAlive;
+ this.scene.events.emit('update-age-ui', { gen: this.generation, age: this.currentAge });
+ }
+
+ // Child Growth
+ if (this.childName && currentDay % 28 === 0) {
+ // Child ages up logic could go here
+ }
+ }
+
+ // --- REPUTATION ---
+ modifyReputation(fraction, amount) {
+ if (this.reputation[fraction] !== undefined) {
+ this.reputation[fraction] += amount;
+ // Clamp -100 to 100
+ this.reputation[fraction] = Phaser.Math.Clamp(this.reputation[fraction], -100, 100);
+
+ if (this.scene.player) {
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 60,
+ text: `Reputation (${fraction}): ${amount > 0 ? '+' : ''}${amount}`,
+ color: amount > 0 ? '#00FF00' : '#FF0000'
+ });
+ }
+ }
+ }
+
+ // --- MARRIAGE ---
+ propose(npc) {
+ if (this.isMarried) {
+ this.showText("You are already married!");
+ return;
+ }
+
+ // Logic: Need Ring and High Rep
+ const hasRing = this.scene.inventorySystem && this.scene.inventorySystem.hasItem('gold_coin'); // Placeholder: use Gold Coin as "Ring" for now
+ // Or better: Let's create 'ring' item? For now, Gold Coins (50?).
+
+ // Let's require 50 Gold Coins for now? Or specific item?
+ // Let's assume inventory checks are done before calling this, or simple check here.
+
+ const highRep = this.reputation.survivors >= 50;
+
+ // Temp logic: Always succeed if rep > 0 for testing?
+ if (highRep) {
+ this.isMarried = true;
+ this.spouseName = npc.name || "Villager";
+ this.showText(`You married ${this.spouseName}!`);
+ } else {
+ this.showText("They refused... (Need 50 Rep)");
+ }
+ }
+
+ haveChild() {
+ if (!this.isMarried) return;
+ if (this.childName) return;
+
+ this.childName = "Baby Jr.";
+ this.childAge = 0;
+ this.showText("A child is born!");
+ }
+
+ // --- INHERITANCE / DEATH ---
+ triggerInheritance() {
+ console.log('๐ TRIGGERING INHERITANCE...');
+
+ // 1. Increment Generation
+ this.generation++;
+
+ // 2. Reset Player Stats
+ if (this.scene.weatherSystem) {
+ this.birthDay = this.scene.weatherSystem.dayCount; // Born today
+ }
+
+ this.currentAge = 18; // Reset age
+ this.isMarried = false;
+ this.spouseName = null;
+ this.childName = null; // Clean slate (child became protagonist)
+
+ // 3. Clear transient reputation, but keep a bit?
+ // Inheritance bonus: Keep 20% of rep?
+ this.reputation.survivors = Math.floor(this.reputation.survivors * 0.2);
+ this.reputation.mutants = Math.floor(this.reputation.mutants * 0.2) - 40; // Still suspicious
+
+ this.showText(`Step Id: 760
+Generation ${this.generation} Begins!`, '#FFD700');
+
+ // Heal
+ if (this.scene.player) {
+ this.scene.player.hp = this.scene.player.maxHp;
+ this.scene.player.isDead = false;
+ this.scene.player.sprite.setTint(0xffffff);
+ this.scene.player.sprite.setAlpha(1);
+ }
+ }
+
+ showText(msg, color = '#FFFFFF') {
+ const player = this.scene.player;
+ if (player) {
+ this.scene.events.emit('show-floating-text', {
+ x: player.sprite.x, y: player.sprite.y - 50, text: msg, color: color
+ });
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/LightingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/LightingSystem.js
new file mode 100644
index 000000000..530da6bad
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/LightingSystem.js
@@ -0,0 +1,194 @@
+// Lighting & Shadow System - Dynamic lighting and shadows
+class LightingSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.shadows = new Map(); // Entity โ Shadow sprite
+ this.lights = new Map(); // Source โ Light circle
+
+ console.log('๐ก LightingSystem initialized');
+ }
+
+ // Create shadow for an entity (player, NPC, etc.)
+ createShadow(entity, offsetY = 10, width = 30, height = 15) {
+ if (!entity || !entity.sprite) return null;
+
+ const shadow = this.scene.add.ellipse(
+ entity.sprite.x,
+ entity.sprite.y + offsetY,
+ width,
+ height,
+ 0x000000,
+ 0.3
+ );
+
+ shadow.setDepth(entity.sprite.depth - 1); // Always below entity
+ this.shadows.set(entity, { shadow, offsetY, width, height });
+
+ return shadow;
+ }
+
+ // Update shadow position (call in entity update)
+ updateShadow(entity) {
+ const data = this.shadows.get(entity);
+ if (!data || !entity.sprite) return;
+
+ const { shadow, offsetY } = data;
+ shadow.setPosition(entity.sprite.x, entity.sprite.y + offsetY);
+
+ // Adjust shadow opacity based on time of day
+ if (this.scene.weatherSystem) {
+ const hour = this.scene.weatherSystem.getCurrentHour();
+ let opacity = 0.3;
+
+ if (hour >= 5 && hour < 7) {
+ // Dawn - weak shadow
+ opacity = 0.1 + ((hour - 5) / 2) * 0.2;
+ } else if (hour >= 7 && hour < 18) {
+ // Day - strong shadow
+ opacity = 0.3;
+ } else if (hour >= 18 && hour < 20) {
+ // Dusk - fading shadow
+ opacity = 0.3 - ((hour - 18) / 2) * 0.2;
+ } else {
+ // Night - very weak shadow
+ opacity = 0.1;
+ }
+
+ shadow.setAlpha(opacity);
+ }
+ }
+
+ // Remove shadow
+ removeShadow(entity) {
+ const data = this.shadows.get(entity);
+ if (data && data.shadow) {
+ data.shadow.destroy();
+ }
+ this.shadows.delete(entity);
+ }
+
+ // Create light source (torch, campfire, etc.)
+ createLight(x, y, radius = 100, color = 0xffee88, alpha = 0.3, flicker = false) {
+ const light = this.scene.add.circle(x, y, radius, color, alpha);
+ light.setBlendMode(Phaser.BlendModes.ADD);
+ light.setDepth(999990); // Above most things
+
+ const lightData = {
+ light,
+ radius,
+ color,
+ baseAlpha: alpha,
+ flicker,
+ flickerTimer: 0
+ };
+
+ this.lights.set(light, lightData);
+
+ return light;
+ }
+
+ // Create player torch (follows player at night)
+ createPlayerTorch(player) {
+ if (!player || !player.sprite) return null;
+
+ const torch = this.scene.add.circle(
+ player.sprite.x,
+ player.sprite.y,
+ 80,
+ 0xffee88,
+ 0
+ );
+
+ torch.setBlendMode(Phaser.BlendModes.ADD);
+ torch.setDepth(999990);
+
+ this.lights.set(torch, {
+ light: torch,
+ radius: 80,
+ color: 0xffee88,
+ baseAlpha: 0.35,
+ flicker: true,
+ flickerTimer: 0,
+ followPlayer: player
+ });
+
+ return torch;
+ }
+
+ // Update all lights
+ update(delta) {
+ const isNight = this.scene.weatherSystem ? this.scene.weatherSystem.isNight() : false;
+
+ for (const [light, data] of this.lights) {
+ // Follow player if assigned
+ if (data.followPlayer && data.followPlayer.sprite) {
+ light.setPosition(
+ data.followPlayer.sprite.x,
+ data.followPlayer.sprite.y
+ );
+
+ // Only show torch at night
+ if (isNight) {
+ light.setAlpha(data.baseAlpha);
+ } else {
+ light.setAlpha(0); // Hidden during day
+ }
+ }
+
+ // Flicker effect
+ if (data.flicker) {
+ data.flickerTimer += delta;
+ if (data.flickerTimer > 50) { // Every 50ms
+ data.flickerTimer = 0;
+ const flicker = data.baseAlpha + (Math.random() * 0.1 - 0.05);
+ if (isNight || !data.followPlayer) {
+ light.setAlpha(Phaser.Math.Clamp(flicker, 0, 1));
+ }
+ }
+ }
+ }
+
+ // Update all shadows
+ for (const [entity, data] of this.shadows) {
+ this.updateShadow(entity);
+ }
+ }
+
+ // Remove light
+ removeLight(light) {
+ const data = this.lights.get(light);
+ if (data && data.light) {
+ data.light.destroy();
+ }
+ this.lights.delete(light);
+ }
+
+ // Create campfire effect (static light with particles)
+ createCampfire(x, y) {
+ // Light glow
+ const light = this.createLight(x, y, 120, 0xff6600, 0.4, true);
+
+ // Smoke particles (if ParticleEffects exists)
+ if (this.scene.particleEffects) {
+ // Add smoke rising from campfire
+ // (Implement in ParticleEffects system)
+ }
+
+ return light;
+ }
+
+ // Clean up
+ destroy() {
+ // Remove all shadows
+ for (const [entity, data] of this.shadows) {
+ this.removeShadow(entity);
+ }
+
+ // Remove all lights
+ for (const [light, data] of this.lights) {
+ this.removeLight(light);
+ }
+
+ console.log('๐ก LightingSystem destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/LocalizationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/LocalizationSystem.js
new file mode 100644
index 000000000..0d6021d4b
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/LocalizationSystem.js
@@ -0,0 +1,477 @@
+/**
+ * LOCALIZATION SYSTEM
+ * Multi-language support with JSON translation files
+ */
+class LocalizationSystem {
+ constructor() {
+ this.currentLang = 'en'; // Default language
+ this.translations = {};
+ this.supportedLanguages = ['slo', 'en', 'de', 'it', 'cn'];
+
+ // Load from localStorage
+ const savedLang = localStorage.getItem('novafarma_language');
+ if (savedLang && this.supportedLanguages.includes(savedLang)) {
+ this.currentLang = savedLang;
+ } else {
+ // AUTO-DETECT OS LANGUAGE (first launch)
+ this.currentLang = this.detectOSLanguage();
+ console.log(`๐ Auto-detected language: ${this.getCurrentLanguageName()}`);
+ localStorage.setItem('novafarma_language', this.currentLang);
+ }
+
+ this.loadTranslations();
+ }
+
+ /**
+ * AUTO-DETECT OS LANGUAGE
+ */
+ detectOSLanguage() {
+ // Get browser/system language
+ const browserLang = navigator.language || navigator.userLanguage || 'en';
+ const langCode = browserLang.toLowerCase().split('-')[0]; // e.g. 'en-US' โ 'en'
+
+ console.log(`๐ฅ๏ธ System language detected: ${browserLang} (${langCode})`);
+
+ // Map to supported language
+ const langMap = {
+ 'sl': 'slo', // Slovenian
+ 'en': 'en', // English
+ 'de': 'de', // German
+ 'it': 'it', // Italian
+ 'zh': 'cn', // Chinese
+ 'cn': 'cn' // Chinese (alternative)
+ };
+
+ const detected = langMap[langCode] || 'en';
+ console.log(`โ
Mapped to game language: ${detected}`);
+
+ return detected;
+ }
+
+ /**
+ * GET CURRENT LANGUAGE NAME
+ */
+ getCurrentLanguageName() {
+ return this.getLanguageName(this.currentLang);
+ }
+
+ loadTranslations() {
+ // Embedded translations (inline for simplicity)
+ this.translations = {
+ 'slo': {
+ // UI
+ 'ui.inventory': 'Inventar',
+ 'ui.crafting': 'Izdelovanje',
+ 'ui.health': 'Zdravje',
+ 'ui.hunger': 'Lakota',
+ 'ui.oxygen': 'Kisik',
+ 'ui.day': 'Dan',
+ 'ui.season': 'Letni ฤas',
+ 'ui.hp': 'ZDR',
+ 'ui.hun': 'LAK',
+ 'ui.h2o': 'H2O',
+ 'ui.xp': 'IZK',
+ 'ui.lv': 'NIV',
+
+ // Items
+ 'item.wood': 'Les',
+ 'item.stone': 'Kamen',
+ 'item.seeds': 'Semena',
+ 'item.wheat': 'Pลกenica',
+ 'item.corn': 'Koruza',
+
+ // Actions
+ 'action.plant': 'Posadi',
+ 'action.harvest': 'Poลพanji',
+ 'action.craft': 'Izdelaj',
+ 'action.build': 'Zgradi',
+
+ // Seasons
+ 'season.spring': 'Pomlad',
+ 'season.summer': 'Poletje',
+ 'season.autumn': 'Jesen',
+ 'season.winter': 'Zima',
+
+ // Pause Menu
+ 'pause.title': 'PAVZA',
+ 'pause.resume': 'โถ Nadaljuj',
+ 'pause.save': '๐พ Shrani igro',
+ 'pause.settings': 'โ๏ธ Nastavitve',
+ 'pause.quit': '๐ช Izhod v meni',
+ 'pause.hint': 'Pritisni ESC za nadaljevanje',
+
+ // Messages
+ 'msg.demo_end': 'Demo konฤan! Hvala za igranje.',
+ 'msg.freezing': 'โ๏ธ Zmrzujeลก!',
+ 'msg.overheating': '๐ฅ Pregrevanje!'
+ },
+ 'en': {
+ // UI
+ 'ui.inventory': 'Inventory',
+ 'ui.crafting': 'Crafting',
+ 'ui.health': 'Health',
+ 'ui.hunger': 'Hunger',
+ 'ui.oxygen': 'Oxygen',
+ 'ui.day': 'Day',
+ 'ui.season': 'Season',
+ 'ui.hp': 'HP',
+ 'ui.hun': 'HUN',
+ 'ui.h2o': 'H2O',
+ 'ui.xp': 'XP',
+ 'ui.lv': 'LV',
+
+ // Items
+ 'item.wood': 'Wood',
+ 'item.stone': 'Stone',
+ 'item.seeds': 'Seeds',
+ 'item.wheat': 'Wheat',
+ 'item.corn': 'Corn',
+
+ // Actions
+ 'action.plant': 'Plant',
+ 'action.harvest': 'Harvest',
+ 'action.craft': 'Craft',
+ 'action.build': 'Build',
+
+ // Seasons
+ 'season.spring': 'Spring',
+ 'season.summer': 'Summer',
+ 'season.autumn': 'Autumn',
+ 'season.winter': 'Winter',
+
+ // Pause Menu
+ 'pause.title': 'PAUSED',
+ 'pause.resume': 'โถ Resume',
+ 'pause.save': '๐พ Save Game',
+ 'pause.settings': 'โ๏ธ Settings',
+ 'pause.quit': '๐ช Quit to Menu',
+ 'pause.hint': 'Press ESC to resume',
+
+ // Messages
+ 'msg.demo_end': 'Demo Ended! Thanks for playing.',
+ 'msg.freezing': 'โ๏ธ Freezing!',
+ 'msg.overheating': '๐ฅ Overheating!'
+ },
+ 'de': {
+ // UI
+ 'ui.inventory': 'Inventar',
+ 'ui.crafting': 'Handwerk',
+ 'ui.health': 'Gesundheit',
+ 'ui.hunger': 'Hunger',
+ 'ui.oxygen': 'Sauerstoff',
+ 'ui.day': 'Tag',
+ 'ui.season': 'Jahreszeit',
+ 'ui.hp': 'LP',
+ 'ui.hun': 'HUN',
+ 'ui.h2o': 'H2O',
+ 'ui.xp': 'EP',
+ 'ui.lv': 'ST',
+
+ // Items
+ 'item.wood': 'Holz',
+ 'item.stone': 'Stein',
+ 'item.seeds': 'Samen',
+ 'item.wheat': 'Weizen',
+ 'item.corn': 'Mais',
+
+ // Actions
+ 'action.plant': 'Pflanzen',
+ 'action.harvest': 'Ernten',
+ 'action.craft': 'Herstellen',
+ 'action.build': 'Bauen',
+
+ // Seasons
+ 'season.spring': 'Frรผhling',
+ 'season.summer': 'Sommer',
+ 'season.autumn': 'Herbst',
+ 'season.winter': 'Winter',
+
+ // Pause Menu
+ 'pause.title': 'PAUSIERT',
+ 'pause.resume': 'โถ Fortsetzen',
+ 'pause.save': '๐พ Spiel speichern',
+ 'pause.settings': 'โ๏ธ Einstellungen',
+ 'pause.quit': '๐ช Zum Menรผ',
+ 'pause.hint': 'ESC drรผcken zum Fortsetzen',
+
+ // Messages
+ 'msg.demo_end': 'Demo beendet! Danke fรผrs Spielen.',
+ 'msg.freezing': 'โ๏ธ Du erfrierst!',
+ 'msg.overheating': '๐ฅ รberhitzung!'
+ },
+ 'it': {
+ // UI
+ 'ui.inventory': 'Inventario',
+ 'ui.crafting': 'Artigianato',
+ 'ui.health': 'Salute',
+ 'ui.hunger': 'Fame',
+ 'ui.oxygen': 'Ossigeno',
+ 'ui.day': 'Giorno',
+ 'ui.season': 'Stagione',
+ 'ui.hp': 'PS',
+ 'ui.hun': 'FAM',
+ 'ui.h2o': 'H2O',
+ 'ui.xp': 'ESP',
+ 'ui.lv': 'LIV',
+
+ // Items
+ 'item.wood': 'Legno',
+ 'item.stone': 'Pietra',
+ 'item.seeds': 'Semi',
+ 'item.wheat': 'Grano',
+ 'item.corn': 'Mais',
+
+ // Actions
+ 'action.plant': 'Pianta',
+ 'action.harvest': 'Raccogli',
+ 'action.craft': 'Crea',
+ 'action.build': 'Costruisci',
+
+ // Seasons
+ 'season.spring': 'Primavera',
+ 'season.summer': 'Estate',
+ 'season.autumn': 'Autunno',
+ 'season.winter': 'Inverno',
+
+ // Pause Menu
+ 'pause.title': 'IN PAUSA',
+ 'pause.resume': 'โถ Riprendi',
+ 'pause.save': '๐พ Salva gioco',
+ 'pause.settings': 'โ๏ธ Impostazioni',
+ 'pause.quit': '๐ช Esci al menu',
+ 'pause.hint': 'Premi ESC per riprendere',
+
+ // Messages
+ 'msg.demo_end': 'Demo terminata! Grazie per aver giocato.',
+ 'msg.freezing': 'โ๏ธ Stai congelando!',
+ 'msg.overheating': '๐ฅ Surriscaldamento!'
+ },
+ 'cn': {
+ // UI
+ 'ui.inventory': 'ๅบๅญ',
+ 'ui.crafting': 'ๅถไฝ',
+ 'ui.health': 'ๅฅๅบท',
+ 'ui.hunger': '้ฅฅ้ฅฟ',
+ 'ui.oxygen': 'ๆฐงๆฐ',
+ 'ui.day': 'ๅคฉๆฐ',
+ 'ui.season': 'ๅญฃ่',
+ 'ui.hp': '็ๅฝ',
+ 'ui.hun': '้ฅฅ้ฅฟ',
+ 'ui.h2o': 'ๆฐด',
+ 'ui.xp': '็ป้ช',
+ 'ui.lv': '็ญ็บง',
+
+ // Items
+ 'item.wood': 'ๆจๆ',
+ 'item.stone': '็ณๅคด',
+ 'item.seeds': '็งๅญ',
+ 'item.wheat': 'ๅฐ้บฆ',
+ 'item.corn': '็็ฑณ',
+
+ // Actions
+ 'action.plant': '็งๆค',
+ 'action.harvest': 'ๆถ่ท',
+ 'action.craft': 'ๅถไฝ',
+ 'action.build': 'ๅปบ้ ',
+
+ // Seasons
+ 'season.spring': 'ๆฅๅคฉ',
+ 'season.summer': 'ๅคๅคฉ',
+ 'season.autumn': '็งๅคฉ',
+ 'season.winter': 'ๅฌๅคฉ',
+
+ // Pause Menu
+ 'pause.title': 'ๆๅ',
+ 'pause.resume': 'โถ ็ปง็ปญ',
+ 'pause.save': '๐พ ไฟๅญๆธธๆ',
+ 'pause.settings': 'โ๏ธ ่ฎพ็ฝฎ',
+ 'pause.quit': '๐ช ้ๅบๅฐ่ๅ',
+ 'pause.hint': 'ๆESC็ปง็ปญ',
+
+ // Messages
+ 'msg.demo_end': 'ๆผ็คบ็ปๆ๏ผๆ่ฐขๆธธ็ฉใ',
+ 'msg.freezing': 'โ๏ธ ไฝ ๅจๅปๅต๏ผ',
+ 'msg.overheating': '๐ฅ ่ฟ็ญ๏ผ'
+ }
+ };
+ }
+
+ /**
+ * Get translated string
+ * @param {string} key - Translation key (e.g. 'ui.inventory')
+ * @param {string} fallback - Fallback text if translation missing
+ */
+ t(key, fallback = key) {
+ const lang = this.translations[this.currentLang];
+ if (lang && lang[key]) {
+ return lang[key];
+ }
+
+ // Fallback to English
+ const enLang = this.translations['en'];
+ if (enLang && enLang[key]) {
+ return enLang[key];
+ }
+
+ return fallback;
+ }
+
+ /**
+ * LOAD INTRO TEXTS FROM JSON
+ */
+ async loadIntroTexts() {
+ try {
+ const response = await fetch('assets/localization.json');
+ const data = await response.json();
+
+ // Merge intro texts into translations
+ for (const lang in data) {
+ if (!this.translations[lang]) {
+ this.translations[lang] = {};
+ }
+
+ // Add intro polaroid texts
+ if (data[lang].intro_polaroids) {
+ this.translations[lang].intro_polaroids = data[lang].intro_polaroids;
+ }
+
+ // Add menu texts
+ if (data[lang].menu) {
+ Object.assign(this.translations[lang], data[lang].menu);
+ }
+
+ // Add title/subtitle
+ if (data[lang].game_title) {
+ this.translations[lang].game_title = data[lang].game_title;
+ }
+ if (data[lang].subtitle) {
+ this.translations[lang].subtitle = data[lang].subtitle;
+ }
+ }
+
+ console.log('โ
Intro texts loaded from JSON');
+ return true;
+ } catch (error) {
+ console.warn('โ ๏ธ Could not load localization.json:', error);
+ return false;
+ }
+ }
+
+ /**
+ * GET INTRO POLAROID TEXT
+ */
+ getIntroText(polaroidKey) {
+ const lang = this.translations[this.currentLang];
+ if (lang && lang.intro_polaroids && lang.intro_polaroids[polaroidKey]) {
+ return lang.intro_polaroids[polaroidKey];
+ }
+
+ // Fallback to English
+ const enLang = this.translations['en'];
+ if (enLang && enLang.intro_polaroids && enLang.intro_polaroids[polaroidKey]) {
+ return enLang.intro_polaroids[polaroidKey];
+ }
+
+ return polaroidKey; // Return key if not found
+ }
+
+ /**
+ * GET VOICE FILE PATH (switches language folder)
+ */
+ getVoicePath(character, index, format = 'mp3') {
+ const langSuffix = this.currentLang === 'en' ? 'en' : 'sl';
+
+ // Map language codes to voice folders
+ if (this.currentLang === 'slo') {
+ // Slovenian voices in /sl/ folder
+ return `assets/audio/voiceover/sl/${character}_${String(index).padStart(2, '0')}.${format}`;
+ } else {
+ // English voices (default for DE, IT, CN too)
+ return `assets/audio/voiceover/en/${character}_en_${String(index).padStart(2, '0')}.${format}`;
+ }
+ }
+
+ /**
+ * CHECK IF VOICE FILE EXISTS FOR CURRENT LANGUAGE
+ */
+ hasVoiceForLanguage(character, index) {
+ // Slovenian and English have full voiceovers
+ // Other languages fall back to English
+ return this.currentLang === 'slo' || this.currentLang === 'en';
+ }
+
+ /**
+ * Set current language
+ */
+ setLanguage(langCode) {
+ if (this.supportedLanguages.includes(langCode)) {
+ this.currentLang = langCode;
+ localStorage.setItem('novafarma_language', langCode);
+ console.log(`๐ Language changed to: ${langCode}`);
+ return true;
+ }
+ return false;
+ }
+
+ getCurrentLanguage() {
+ return this.currentLang;
+ }
+
+ getSupportedLanguages() {
+ return this.supportedLanguages.map(code => ({
+ code,
+ name: this.getLanguageName(code)
+ }));
+ }
+
+ getLanguageName(code) {
+ const names = {
+ 'slo': 'Slovenลกฤina',
+ 'en': 'English',
+ 'de': 'Deutsch',
+ 'it': 'Italiano',
+ 'cn': 'ไธญๆ'
+ };
+ return names[code] || code;
+ }
+ /**
+ * INJECT EXTERNAL JSON DATA (loaded via Phaser)
+ */
+ setExternalData(data) {
+ try {
+ // Merge intro texts into translations
+ for (const lang in data) {
+ if (!this.translations[lang]) {
+ this.translations[lang] = {};
+ }
+
+ // Add intro polaroid texts
+ if (data[lang].intro_polaroids) {
+ this.translations[lang].intro_polaroids = data[lang].intro_polaroids;
+ }
+
+ // Add menu texts
+ if (data[lang].menu) {
+ Object.assign(this.translations[lang], data[lang].menu);
+ }
+
+ // Add title/subtitle
+ if (data[lang].game_title) {
+ this.translations[lang].game_title = data[lang].game_title;
+ }
+ if (data[lang].subtitle) {
+ this.translations[lang].subtitle = data[lang].subtitle;
+ }
+ }
+
+ console.log('โ
Localization data injected successfully');
+ return true;
+ } catch (error) {
+ console.warn('โ ๏ธ Could not inject localization data:', error);
+ return false;
+ }
+ }
+}
+
+// Global instance
+window.LocalizationSystem = LocalizationSystem;
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/LootSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/LootSystem.js
new file mode 100644
index 000000000..a1e1bb37e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/LootSystem.js
@@ -0,0 +1,125 @@
+class LootSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.drops = []; // Active loot drops
+ this.iso = scene.terrainSystem ? scene.terrainSystem.iso : null; // Reference to IsoUtils
+
+ // Settings
+ this.pickupRadius = 1.0; // Grid based
+ this.magnetRadius = 3.0; // Optional: fly towards player
+ }
+
+ spawnLoot(gridX, gridY, type, count = 1) {
+ if (!this.iso && this.scene.terrainSystem) this.iso = this.scene.terrainSystem.iso;
+ if (!this.iso) return; // Safety check
+
+ console.log(`๐ Spawning ${count}x ${type} at ${gridX},${gridY}`);
+
+ const screenPos = this.iso.toScreen(gridX, gridY);
+ const x = screenPos.x + (this.scene.terrainOffsetX || 0);
+ const y = screenPos.y + (this.scene.terrainOffsetY || 0);
+
+ // Visual Symbol Mapping
+ let symbol = '?';
+ const symbols = {
+ 'wood': '๐ชต', 'stone': '๐ชจ', 'seeds': '๐ฑ', 'wheat': '๐พ',
+ 'axe': '๐ช', 'pickaxe': 'โ๏ธ', 'sword': 'โ๏ธ', 'hoe': '๐',
+ 'item_bone': '๐ฆด', 'flower': '๐ธ',
+ 'diamond': '๐', 'emerald': '๐', 'ruby': 'โค๏ธ', // GEMS!
+ 'gold_coin': '๐ช', 'iron': 'โ๏ธ'
+ };
+ if (symbols[type]) symbol = symbols[type];
+
+ // Create Sprite/Text
+ // Using Text for now as it supports emojis easily, but could be Sprite
+ const drop = this.scene.add.text(x, y - 20, symbol, { fontSize: '20px' });
+ drop.setOrigin(0.5);
+ drop.setDepth(this.iso.getDepth(gridX, gridY) + 500);
+
+ // Animation (Bobbing)
+ this.scene.tweens.add({
+ targets: drop,
+ y: y - 40,
+ duration: 600,
+ yoyo: true,
+ ease: 'Sine.easeInOut',
+ repeat: -1
+ });
+
+ // Add to list
+ this.drops.push({
+ gridX, gridY,
+ x, y,
+ sprite: drop,
+ type: type,
+ count: count,
+ spawnTime: Date.now()
+ });
+ }
+
+ update() {
+ if (!this.scene.player) return;
+
+ const playerPos = this.scene.player.getPosition();
+
+ // Loop backwards to allow removal
+ for (let i = this.drops.length - 1; i >= 0; i--) {
+ const drop = this.drops[i];
+
+ // Distance Check
+ const dist = Math.abs(drop.gridX - playerPos.x) + Math.abs(drop.gridY - playerPos.y); // Manhattan-ish
+
+ // Pickup Logic
+ if (dist < 0.8) {
+ this.collectLoot(drop, i);
+ }
+ // Magnet Logic (Visual fly to player) - Optional
+ else if (dist < 3.0) {
+ // Move visual slightly towards player?
+ // Implementing full physics/velocity might be overkill for now.
+ }
+ }
+ }
+
+ collectLoot(drop, index) {
+ if (this.scene.inventorySystem) {
+ const leftover = this.scene.inventorySystem.addItem(drop.type, drop.count);
+
+ if (leftover === 0) {
+ // Success - Play Sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playPickup();
+ }
+
+ // Sparkle Effect
+ if (this.scene.particleEffects) {
+ this.scene.particleEffects.sparkle(drop.x, drop.y);
+ }
+
+ // Float text effect
+ this.showFloatingText(`+${drop.count} ${drop.type}`, drop.x, drop.y);
+
+ // Remove
+ drop.sprite.destroy();
+ this.drops.splice(index, 1);
+ } else {
+ // Config full? Update count?
+ drop.count = leftover;
+ }
+ }
+ }
+
+ showFloatingText(text, x, y) {
+ const txt = this.scene.add.text(x, y - 50, text, {
+ fontSize: '14px', fill: '#ffff00', stroke: '#000', strokeThickness: 2
+ }).setOrigin(0.5).setDepth(20000);
+
+ this.scene.tweens.add({
+ targets: txt,
+ y: y - 100,
+ alpha: 0,
+ duration: 800,
+ onComplete: () => txt.destroy()
+ });
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MagicEnchantingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MagicEnchantingSystem.js
new file mode 100644
index 000000000..36c6d9bce
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MagicEnchantingSystem.js
@@ -0,0 +1,297 @@
+/**
+ * MAGIC ENCHANTING SYSTEM
+ * Enhance tools with magical properties
+ *
+ * Features:
+ * - 5 Enchantment Types: Power, Speed, Fortune, Unbreaking, Auto-Collect
+ * - 3 Enchantment Levels per type
+ * - Costs mana + rare materials
+ * - Glowing visual effects
+ * - Can stack multiple enchantments
+ */
+class MagicEnchantingSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Enchantment definitions
+ this.enchantments = {
+ power: {
+ name: 'Power',
+ icon: 'โก',
+ description: 'Increases tool efficiency',
+ levels: [
+ { level: 1, bonus: 0.25, cost: { mana: 50, crystal: 1 } },
+ { level: 2, bonus: 0.50, cost: { mana: 100, crystal: 3 } },
+ { level: 3, bonus: 1.00, cost: { mana: 200, crystal: 10 } }
+ ],
+ color: 0xFF4444
+ },
+ speed: {
+ name: 'Speed',
+ icon: 'โก',
+ description: 'Increases tool speed',
+ levels: [
+ { level: 1, bonus: 0.20, cost: { mana: 50, feather: 5 } },
+ { level: 2, bonus: 0.40, cost: { mana: 100, feather: 15 } },
+ { level: 3, bonus: 0.80, cost: { mana: 200, feather: 50 } }
+ ],
+ color: 0x44FF44
+ },
+ fortune: {
+ name: 'Fortune',
+ icon: '๐',
+ description: 'Chance for double drops',
+ levels: [
+ { level: 1, bonus: 0.15, cost: { mana: 75, emerald: 1 } },
+ { level: 2, bonus: 0.30, cost: { mana: 150, emerald: 3 } },
+ { level: 3, bonus: 0.50, cost: { mana: 300, emerald: 10 } }
+ ],
+ color: 0x44FFFF
+ },
+ unbreaking: {
+ name: 'Unbreaking',
+ icon: '๐ก๏ธ',
+ description: 'Reduces durability loss',
+ levels: [
+ { level: 1, bonus: 0.30, cost: { mana: 60, obsidian: 5 } },
+ { level: 2, bonus: 0.50, cost: { mana: 120, obsidian: 15 } },
+ { level: 3, bonus: 0.75, cost: { mana: 250, obsidian: 50 } }
+ ],
+ color: 0xFF44FF
+ },
+ auto_collect: {
+ name: 'Auto-Collect',
+ icon: '๐',
+ description: 'Automatically collects drops',
+ levels: [
+ { level: 1, bonus: 1.0, cost: { mana: 100, void_essence: 1 } },
+ { level: 2, bonus: 2.0, cost: { mana: 200, void_essence: 3 } },
+ { level: 3, bonus: 3.0, cost: { mana: 400, void_essence: 10 } }
+ ],
+ color: 0x9D4EDD
+ }
+ };
+
+ // Enchanting table locations (town)
+ this.enchantingTables = [];
+
+ console.log('โจ Magic Enchanting System initialized');
+ }
+
+ /**
+ * Enchant a tool
+ */
+ enchantTool(toolId, enchantmentType, level = 1) {
+ const toolSystem = this.scene.toolSystem;
+ if (!toolSystem) {
+ return { success: false, message: 'Tool system not available' };
+ }
+
+ const tool = toolSystem.playerTools.get(toolId);
+ if (!tool) {
+ return { success: false, message: 'Tool not found' };
+ }
+
+ const enchantment = this.enchantments[enchantmentType];
+ if (!enchantment) {
+ return { success: false, message: 'Unknown enchantment' };
+ }
+
+ if (level < 1 || level > 3) {
+ return { success: false, message: 'Invalid enchantment level' };
+ }
+
+ // Check if tool already has this enchantment
+ if (!tool.enchantments) {
+ tool.enchantments = {};
+ }
+
+ if (tool.enchantments[enchantmentType]) {
+ return { success: false, message: 'Tool already has this enchantment!' };
+ }
+
+ // Get cost
+ const levelData = enchantment.levels[level - 1];
+ const cost = levelData.cost;
+
+ // Check mana
+ if (this.scene.player && this.scene.player.mana < cost.mana) {
+ return { success: false, message: `Need ${cost.mana} mana!` };
+ }
+
+ // Check materials (simplified - would check inventory)
+ const canAfford = true; // TODO: Check inventory
+
+ if (!canAfford) {
+ return { success: false, message: 'Not enough materials!' };
+ }
+
+ // Apply enchantment
+ tool.enchantments[enchantmentType] = {
+ level: level,
+ bonus: levelData.bonus,
+ color: enchantment.color
+ };
+
+ // Deduct mana
+ if (this.scene.player) {
+ this.scene.player.mana -= cost.mana;
+ }
+
+ // Apply bonus to tool stats
+ this.applyEnchantmentBonus(tool, enchantmentType, levelData.bonus);
+
+ console.log(`โจ Enchanted ${tool.name} with ${enchantment.name} ${level}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Tool Enchanted!',
+ message: `${enchantment.icon} ${enchantment.name} ${level} applied!`,
+ icon: 'โจ'
+ });
+
+ return { success: true, enchantment: enchantmentType, level: level };
+ }
+
+ /**
+ * Apply enchantment bonus to tool
+ */
+ applyEnchantmentBonus(tool, type, bonus) {
+ switch (type) {
+ case 'power':
+ tool.efficiency += bonus;
+ break;
+ case 'speed':
+ tool.speed += bonus;
+ break;
+ case 'fortune':
+ tool.fortuneChance = bonus;
+ break;
+ case 'unbreaking':
+ tool.durabilityMultiplier = 1 - bonus; // 30% less durability loss
+ break;
+ case 'auto_collect':
+ tool.autoCollect = true;
+ tool.autoCollectRadius = bonus;
+ break;
+ }
+ }
+
+ /**
+ * Remove enchantment (costs mana)
+ */
+ removeEnchantment(toolId, enchantmentType) {
+ const toolSystem = this.scene.toolSystem;
+ const tool = toolSystem.playerTools.get(toolId);
+
+ if (!tool || !tool.enchantments || !tool.enchantments[enchantmentType]) {
+ return { success: false, message: 'Enchantment not found' };
+ }
+
+ const cost = 25; // Mana cost to remove
+
+ if (this.scene.player && this.scene.player.mana >= cost) {
+ this.scene.player.mana -= cost;
+
+ // Remove bonus
+ const enchantData = tool.enchantments[enchantmentType];
+ this.removeEnchantmentBonus(tool, enchantmentType, enchantData.bonus);
+
+ delete tool.enchantments[enchantmentType];
+
+ console.log(`๐ฎ Removed enchantment from ${tool.name}`);
+
+ return { success: true };
+ }
+
+ return { success: false, message: 'Not enough mana' };
+ }
+
+ /**
+ * Remove enchantment bonus from tool
+ */
+ removeEnchantmentBonus(tool, type, bonus) {
+ switch (type) {
+ case 'power':
+ tool.efficiency -= bonus;
+ break;
+ case 'speed':
+ tool.speed -= bonus;
+ break;
+ case 'fortune':
+ tool.fortuneChance = 0;
+ break;
+ case 'unbreaking':
+ tool.durabilityMultiplier = 1.0;
+ break;
+ case 'auto_collect':
+ tool.autoCollect = false;
+ tool.autoCollectRadius = 0;
+ break;
+ }
+ }
+
+ /**
+ * Get all enchantments on a tool
+ */
+ getToolEnchantments(toolId) {
+ const toolSystem = this.scene.toolSystem;
+ const tool = toolSystem.playerTools.get(toolId);
+
+ if (!tool || !tool.enchantments) {
+ return [];
+ }
+
+ const enchantments = [];
+ for (const [type, data] of Object.entries(tool.enchantments)) {
+ const enchant = this.enchantments[type];
+ enchantments.push({
+ type: type,
+ name: enchant.name,
+ level: data.level,
+ bonus: data.bonus,
+ icon: enchant.icon,
+ color: data.color
+ });
+ }
+
+ return enchantments;
+ }
+
+ /**
+ * Build enchanting table in town
+ */
+ buildEnchantingTable(x, y) {
+ const table = {
+ id: `enchanting_${Date.now()}`,
+ x: x,
+ y: y,
+ type: 'enchanting_table',
+ active: true
+ };
+
+ this.enchantingTables.push(table);
+
+ console.log(`โจ Built Enchanting Table at (${x}, ${y})`);
+
+ this.scene.events.emit('notification', {
+ title: 'Enchanting Table Built!',
+ message: 'You can now enchant your tools!',
+ icon: 'โจ'
+ });
+
+ return table;
+ }
+
+ /**
+ * Check if near enchanting table
+ */
+ isNearEnchantingTable(x, y, radius = 3) {
+ for (const table of this.enchantingTables) {
+ const distance = Phaser.Math.Distance.Between(x, y, table.x, table.y);
+ if (distance <= radius) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MagicSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MagicSystem.js
new file mode 100644
index 000000000..39f19a606
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MagicSystem.js
@@ -0,0 +1,708 @@
+/**
+ * MagicSystem.js
+ * ==============
+ * Spell casting, magic staffs, potions, and elemental effects
+ *
+ * Features:
+ * - 6 elemental staffs (fire, ice, lightning, earth, light, dark)
+ * - Spell system with mana costs
+ * - Potion brewing and consumption
+ * - Elemental damage types
+ * - Magic projectiles
+ * - Buff/debuff effects
+ * - Spell combos
+ *
+ * Uses: portal_states_broken_repaired_1766097098724.tsx (magic portals)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-22
+ */
+
+export default class MagicSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Player magic state
+ this.mana = 100;
+ this.maxMana = 100;
+ this.manaRegenRate = 5; // per second
+
+ // Equipment
+ this.equippedStaff = null;
+ this.equippedSpells = new Map(); // slot -> spell
+
+ // Spell databases
+ this.spells = new Map();
+ this.staffs = new Map();
+ this.potions = new Map();
+
+ // Active effects
+ this.activeEffects = [];
+ this.activeProjectiles = [];
+
+ // Cooldowns
+ this.spellCooldowns = new Map();
+
+ // Initialize databases
+ this.initializeSpells();
+ this.initializeStaffs();
+ this.initializePotions();
+
+ // Load saved data
+ this.loadMagicData();
+
+ console.log('โจ MagicSystem initialized');
+ }
+
+ initializeSpells() {
+ // ===== FIRE MAGIC =====
+ this.addSpell('fireball', {
+ name: 'Fireball',
+ element: 'fire',
+ manaCost: 20,
+ damage: 50,
+ range: 200,
+ speed: 300,
+ cooldown: 2000,
+ requiresStaff: 'fire',
+ description: 'Launch a ball of fire',
+ effect: 'burn'
+ });
+
+ this.addSpell('fire_blast', {
+ name: 'Fire Blast',
+ element: 'fire',
+ manaCost: 40,
+ damage: 100,
+ range: 150,
+ aoe: 80,
+ cooldown: 5000,
+ requiresStaff: 'fire',
+ description: 'Area explosion of flames',
+ effect: 'burn'
+ });
+
+ // ===== ICE MAGIC =====
+ this.addSpell('ice_shard', {
+ name: 'Ice Shard',
+ element: 'ice',
+ manaCost: 15,
+ damage: 40,
+ range: 250,
+ speed: 350,
+ cooldown: 1500,
+ requiresStaff: 'ice',
+ description: 'Pierce enemies with ice',
+ effect: 'slow'
+ });
+
+ this.addSpell('frost_nova', {
+ name: 'Frost Nova',
+ element: 'ice',
+ manaCost: 50,
+ damage: 60,
+ aoe: 120,
+ cooldown: 6000,
+ requiresStaff: 'ice',
+ description: 'Freeze everything nearby',
+ effect: 'freeze'
+ });
+
+ // ===== LIGHTNING MAGIC =====
+ this.addSpell('lightning_bolt', {
+ name: 'Lightning Bolt',
+ element: 'lightning',
+ manaCost: 25,
+ damage: 70,
+ range: 300,
+ speed: 500, // Fast!
+ cooldown: 2500,
+ requiresStaff: 'lightning',
+ description: 'Strike with lightning',
+ effect: 'stun'
+ });
+
+ this.addSpell('chain_lightning', {
+ name: 'Chain Lightning',
+ element: 'lightning',
+ manaCost: 60,
+ damage: 45,
+ range: 200,
+ chains: 5, // Hits 5 targets
+ cooldown: 7000,
+ requiresStaff: 'lightning',
+ description: 'Lightning jumps between enemies'
+ });
+
+ // ===== EARTH MAGIC =====
+ this.addSpell('stone_spike', {
+ name: 'Stone Spike',
+ element: 'earth',
+ manaCost: 30,
+ damage: 80,
+ range: 150,
+ cooldown: 3000,
+ requiresStaff: 'earth',
+ description: 'Erupt spikes from the ground'
+ });
+
+ this.addSpell('earth_shield', {
+ name: 'Earth Shield',
+ element: 'earth',
+ manaCost: 40,
+ defense: 50, // Damage reduction
+ duration: 10000, // 10 seconds
+ cooldown: 15000,
+ requiresStaff: 'earth',
+ description: 'Summon protective barrier',
+ isBuff: true
+ });
+
+ // ===== LIGHT MAGIC =====
+ this.addSpell('heal', {
+ name: 'Healing Light',
+ element: 'light',
+ manaCost: 35,
+ healing: 50,
+ cooldown: 4000,
+ requiresStaff: 'light',
+ description: 'Restore health',
+ isHeal: true
+ });
+
+ this.addSpell('holy_smite', {
+ name: 'Holy Smite',
+ element: 'light',
+ manaCost: 45,
+ damage: 90,
+ range: 180,
+ effectiveVs: ['undead', 'dark'],
+ cooldown: 5000,
+ requiresStaff: 'light',
+ description: 'Devastating against undead'
+ });
+
+ // ===== DARK MAGIC =====
+ this.addSpell('shadow_bolt', {
+ name: 'Shadow Bolt',
+ element: 'dark',
+ manaCost: 20,
+ damage: 55,
+ range: 220,
+ speed: 280,
+ lifeSteal: 0.3, // 30% damage as healing
+ cooldown: 2000,
+ requiresStaff: 'dark',
+ description: 'Drain life from enemies'
+ });
+
+ this.addSpell('curse', {
+ name: 'Curse of Weakness',
+ element: 'dark',
+ manaCost: 40,
+ debuff: { attack: 0.5, defense: 0.5 }, // 50% reduction
+ duration: 15000,
+ cooldown: 8000,
+ requiresStaff: 'dark',
+ description: 'Weaken enemy significantly',
+ isDebuff: true
+ });
+ }
+
+ initializeStaffs() {
+ this.addStaff('fire', {
+ name: 'Fire Staff',
+ element: 'fire',
+ manaBonus: 20,
+ spellPowerBonus: 1.2, // 20% more damage
+ unlockLevel: 10,
+ sprite: 'magic_staffs_6_types', // Frame 0
+ cost: 5000,
+ blueprint: 'blueprint_fire_staff'
+ });
+
+ this.addStaff('ice', {
+ name: 'Ice Staff',
+ element: 'ice',
+ manaBonus: 20,
+ spellPowerBonus: 1.2,
+ unlockLevel: 10,
+ sprite: 'magic_staffs_6_types', // Frame 1
+ cost: 5000,
+ blueprint: 'blueprint_ice_staff'
+ });
+
+ this.addStaff('lightning', {
+ name: 'Lightning Staff',
+ element: 'lightning',
+ manaBonus: 20,
+ spellPowerBonus: 1.2,
+ unlockLevel: 12,
+ sprite: 'magic_staffs_6_types', // Frame 2
+ cost: 6000,
+ blueprint: 'blueprint_lightning_staff'
+ });
+
+ this.addStaff('earth', {
+ name: 'Earth Staff',
+ element: 'earth',
+ manaBonus: 30,
+ spellPowerBonus: 1.1,
+ defenseBonus: 10,
+ unlockLevel: 11,
+ sprite: 'magic_staffs_6_types', // Frame 3
+ cost: 5500,
+ blueprint: 'blueprint_earth_staff'
+ });
+
+ this.addStaff('light', {
+ name: 'Light Staff',
+ element: 'light',
+ manaBonus: 25,
+ spellPowerBonus: 1.15,
+ healingBonus: 1.3, // 30% more healing
+ unlockLevel: 13,
+ sprite: 'magic_staffs_6_types', // Frame 4
+ cost: 7000,
+ blueprint: 'blueprint_light_staff'
+ });
+
+ this.addStaff('dark', {
+ name: 'Dark Staff',
+ element: 'dark',
+ manaBonus: 20,
+ spellPowerBonus: 1.25, // Highest damage
+ lifeStealBonus: 0.1, // Extra 10% lifesteal
+ unlockLevel: 14,
+ sprite: 'magic_staffs_6_types', // Frame 5
+ cost: 8000,
+ blueprint: 'blueprint_dark_staff'
+ });
+ }
+
+ initializePotions() {
+ this.addPotion('mana_potion', {
+ name: 'Mana Potion',
+ manaRestore: 50,
+ cooldown: 30000, // 30 second potion cooldown
+ cost: 50,
+ sprite: 'potions_elixirs_grid', // Frame 0
+ description: 'Restore 50 mana'
+ });
+
+ this.addPotion('greater_mana_potion', {
+ name: 'Greater Mana Potion',
+ manaRestore: 100,
+ cooldown: 30000,
+ cost: 150,
+ sprite: 'potions_elixirs_grid', // Frame 1
+ description: 'Restore 100 mana'
+ });
+
+ this.addPotion('magic_power_elixir', {
+ name: 'Magic Power Elixir',
+ spellPowerBuff: 1.5, // 50% more spell damage
+ duration: 60000, // 1 minute
+ cooldown: 120000, // 2 minute cooldown
+ cost: 300,
+ sprite: 'potions_elixirs_grid', // Frame 2
+ description: 'Greatly increase spell power'
+ });
+ }
+
+ addSpell(id, data) {
+ this.spells.set(id, { id, ...data });
+ }
+
+ addStaff(id, data) {
+ this.staffs.set(id, { id, ...data });
+ }
+
+ addPotion(id, data) {
+ this.potions.set(id, { id, ...data });
+ }
+
+ // ===== SPELL CASTING =====
+
+ canCastSpell(spellId) {
+ const spell = this.spells.get(spellId);
+ if (!spell) {
+ return { canCast: false, reason: 'Spell not found' };
+ }
+
+ // Check mana
+ if (this.mana < spell.manaCost) {
+ return {
+ canCast: false,
+ reason: `Need ${spell.manaCost} mana (have ${this.mana.toFixed(0)})`
+ };
+ }
+
+ // Check staff requirement
+ if (spell.requiresStaff) {
+ if (!this.equippedStaff || this.equippedStaff.element !== spell.requiresStaff) {
+ return {
+ canCast: false,
+ reason: `Requires ${spell.requiresStaff} staff`
+ };
+ }
+ }
+
+ // Check cooldown
+ if (this.spellCooldowns.has(spellId)) {
+ const remaining = this.spellCooldowns.get(spellId) - Date.now();
+ if (remaining > 0) {
+ return {
+ canCast: false,
+ reason: `Cooldown: ${(remaining / 1000).toFixed(1)}s`
+ };
+ }
+ }
+
+ return { canCast: true };
+ }
+
+ castSpell(spellId, targetX, targetY) {
+ const check = this.canCastSpell(spellId);
+ if (!check.canCast) {
+ this.scene.uiSystem?.showError(check.reason);
+ return false;
+ }
+
+ const spell = this.spells.get(spellId);
+
+ // Consume mana
+ this.mana -= spell.manaCost;
+
+ // Apply cooldown
+ this.spellCooldowns.set(spellId, Date.now() + spell.cooldown);
+
+ // Calculate spell power
+ const spellPower = this.getSpellPower(spell);
+
+ // Execute spell effect
+ if (spell.isHeal) {
+ this.castHealSpell(spell, spellPower);
+ } else if (spell.isBuff) {
+ this.castBuffSpell(spell);
+ } else if (spell.isDebuff) {
+ this.castDebuffSpell(spell, targetX, targetY);
+ } else if (spell.aoe) {
+ this.castAoESpell(spell, targetX, targetY, spellPower);
+ } else {
+ this.castProjectileSpell(spell, targetX, targetY, spellPower);
+ }
+
+ // Emit event
+ this.scene.events.emit('spellCast', { spellId, spell, targetX, targetY });
+
+ console.log(`โจ Cast ${spell.name} (${spell.manaCost} mana)`);
+ return true;
+ }
+
+ castProjectileSpell(spell, targetX, targetY, power) {
+ const playerX = this.scene.player.x;
+ const playerY = this.scene.player.y;
+
+ // Create projectile sprite
+ const projectile = this.scene.physics.add.sprite(
+ playerX,
+ playerY,
+ `spell_${spell.element}`
+ );
+
+ projectile.setData('spell', spell);
+ projectile.setData('damage', spell.damage * power);
+
+ // Aim at target
+ this.scene.physics.moveTo(projectile, targetX, targetY, spell.speed);
+
+ // Add particle trail
+ if (this.scene.particleSystem) {
+ this.scene.particleSystem.createSpellTrail(projectile, spell.element);
+ }
+
+ // Handle collision with enemies
+ this.scene.physics.add.overlap(projectile, this.scene.enemies, (proj, enemy) => {
+ this.hitEnemy(enemy, proj.getData('damage'), spell);
+ proj.destroy();
+ });
+
+ // Auto-destroy after range
+ this.scene.time.delayedCall(spell.range / spell.speed * 1000, () => {
+ if (projectile && projectile.active) {
+ projectile.destroy();
+ }
+ });
+
+ this.activeProjectiles.push(projectile);
+ }
+
+ castAoESpell(spell, targetX, targetY, power) {
+ // Create explosion effect
+ if (this.scene.particleSystem) {
+ this.scene.particleSystem.createSpellExplosion(targetX, targetY, spell.element, spell.aoe);
+ }
+
+ // Damage all enemies in radius
+ this.scene.enemies?.getChildren().forEach(enemy => {
+ const distance = Phaser.Math.Distance.Between(
+ targetX, targetY, enemy.x, enemy.y
+ );
+
+ if (distance <= spell.aoe) {
+ this.hitEnemy(enemy, spell.damage * power, spell);
+ }
+ });
+ }
+
+ castHealSpell(spell, power) {
+ const healing = spell.healing * power;
+
+ // Heal player
+ if (this.scene.player.health !== undefined) {
+ this.scene.player.health = Math.min(
+ this.scene.player.maxHealth || 100,
+ this.scene.player.health + healing
+ );
+ }
+
+ // Visual effect
+ if (this.scene.particleSystem) {
+ this.scene.particleSystem.createHealEffect(
+ this.scene.player.x,
+ this.scene.player.y
+ );
+ }
+
+ this.scene.events.emit('playerHealed', { amount: healing });
+ }
+
+ castBuffSpell(spell) {
+ this.activeEffects.push({
+ spell,
+ endTime: Date.now() + spell.duration,
+ type: 'buff'
+ });
+
+ this.scene.uiSystem?.showNotification({
+ title: `${spell.name} Active!`,
+ message: `Defense +${spell.defense} for ${spell.duration / 1000}s`,
+ icon: 'magic',
+ duration: 3000
+ });
+ }
+
+ castDebuffSpell(spell, targetX, targetY) {
+ // Find nearest enemy
+ const enemy = this.findNearestEnemy(targetX, targetY);
+ if (enemy) {
+ enemy.setData('debuff', {
+ spell,
+ endTime: Date.now() + spell.duration
+ });
+
+ // Visual effect
+ if (this.scene.particleSystem) {
+ this.scene.particleSystem.createDebuffEffect(enemy.x, enemy.y);
+ }
+ }
+ }
+
+ hitEnemy(enemy, damage, spell) {
+ // Apply damage
+ const finalDamage = damage;
+ enemy.takeDamage?.(finalDamage);
+
+ // Apply effect
+ if (spell.effect) {
+ this.applyEffect(enemy, spell.effect, spell);
+ }
+
+ // Lifesteal
+ if (spell.lifeSteal && this.scene.player.health !== undefined) {
+ const healing = finalDamage * spell.lifeSteal;
+ this.scene.player.health = Math.min(
+ this.scene.player.maxHealth || 100,
+ this.scene.player.health + healing
+ );
+ }
+ }
+
+ applyEffect(target, effectType, spell) {
+ switch (effectType) {
+ case 'burn':
+ // Damage over time
+ target.setData('burning', { duration: 3000, dps: 10 });
+ break;
+ case 'slow':
+ // Reduce speed
+ target.setData('slowed', { duration: 2000, factor: 0.5 });
+ break;
+ case 'freeze':
+ // Stop movement
+ target.setData('frozen', { duration: 1500 });
+ target.setVelocity(0, 0);
+ break;
+ case 'stun':
+ // Cannot act
+ target.setData('stunned', { duration: 1000 });
+ break;
+ }
+ }
+
+ // ===== STAFF MANAGEMENT =====
+
+ equipStaff(staffId) {
+ const staff = this.staffs.get(staffId);
+ if (!staff) return false;
+
+ this.equippedStaff = staff;
+
+ // Apply bonuses
+ this.maxMana = 100 + (staff.manaBonus || 0);
+
+ this.scene.uiSystem?.showNotification({
+ title: `Equipped ${staff.name}`,
+ message: `+${staff.manaBonus} max mana`,
+ icon: 'magic',
+ duration: 2000
+ });
+
+ return true;
+ }
+
+ getSpellPower(spell) {
+ let power = 1.0;
+
+ // Staff bonus
+ if (this.equippedStaff) {
+ power *= this.equippedStaff.spellPowerBonus || 1.0;
+
+ // Healing bonus
+ if (spell.isHeal && this.equippedStaff.healingBonus) {
+ power *= this.equippedStaff.healingBonus;
+ }
+ }
+
+ // Active potion buffs
+ this.activeEffects.forEach(effect => {
+ if (effect.spell.spellPowerBuff) {
+ power *= effect.spell.spellPowerBuff;
+ }
+ });
+
+ return power;
+ }
+
+ // ===== POTION SYSTEM =====
+
+ drinkPotion(potionId) {
+ const potion = this.potions.get(potionId);
+ if (!potion) return false;
+
+ // Check cooldown
+ if (this.spellCooldowns.has(`potion_${potionId}`)) {
+ const remaining = this.spellCooldowns.get(`potion_${potionId}`) - Date.now();
+ if (remaining > 0) {
+ this.scene.uiSystem?.showError(`Potion cooldown: ${(remaining / 1000).toFixed(0)}s`);
+ return false;
+ }
+ }
+
+ // Restore mana
+ if (potion.manaRestore) {
+ this.mana = Math.min(this.maxMana, this.mana + potion.manaRestore);
+ }
+
+ // Apply buff
+ if (potion.spellPowerBuff) {
+ this.activeEffects.push({
+ spell: potion,
+ endTime: Date.now() + potion.duration,
+ type: 'potion_buff'
+ });
+ }
+
+ // Set cooldown
+ this.spellCooldowns.set(`potion_${potionId}`, Date.now() + potion.cooldown);
+
+ console.log(`๐งช Drank ${potion.name}`);
+ return true;
+ }
+
+ // ===== HELPER FUNCTIONS =====
+
+ findNearestEnemy(x, y) {
+ let nearest = null;
+ let minDist = Infinity;
+
+ this.scene.enemies?.getChildren().forEach(enemy => {
+ const dist = Phaser.Math.Distance.Between(x, y, enemy.x, enemy.y);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = enemy;
+ }
+ });
+
+ return nearest;
+ }
+
+ // ===== DATA PERSISTENCE =====
+
+ saveMagicData() {
+ const data = {
+ mana: this.mana,
+ maxMana: this.maxMana,
+ equippedStaff: this.equippedStaff?.id
+ };
+ localStorage.setItem('magicData', JSON.stringify(data));
+ }
+
+ loadMagicData() {
+ const saved = localStorage.getItem('magicData');
+ if (saved) {
+ const data = JSON.parse(saved);
+ this.mana = data.mana;
+ this.maxMana = data.maxMana;
+ if (data.equippedStaff) {
+ this.equipStaff(data.equippedStaff);
+ }
+ console.log('โจ Loaded magic data');
+ }
+ }
+
+ // ===== UPDATE =====
+
+ update(time, delta) {
+ // Regenerate mana
+ const deltaSeconds = delta / 1000;
+ this.mana = Math.min(this.maxMana, this.mana + this.manaRegenRate * deltaSeconds);
+
+ // Update active effects
+ const now = Date.now();
+ this.activeEffects = this.activeEffects.filter(effect => effect.endTime > now);
+
+ // Clean up destroyed projectiles
+ this.activeProjectiles = this.activeProjectiles.filter(p => p.active);
+
+ // Emit mana update
+ this.scene.events.emit('manaUpdate', {
+ current: this.mana,
+ max: this.maxMana,
+ percent: (this.mana / this.maxMana) * 100
+ });
+ }
+
+ // ===== CLEANUP =====
+
+ destroy() {
+ this.saveMagicData();
+ this.spells.clear();
+ this.staffs.clear();
+ this.potions.clear();
+ this.activeEffects = [];
+ this.activeProjectiles = [];
+ this.spellCooldowns.clear();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MainQuestAnaSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MainQuestAnaSystem.js
new file mode 100644
index 000000000..1b896b999
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MainQuestAnaSystem.js
@@ -0,0 +1,371 @@
+/**
+ * MainQuestAnaSystem.js
+ * =====================
+ * KRVAVA ลฝETEV - Main Quest: Finding Ana (Phase 42)
+ *
+ * Features:
+ * - Main story quest (finding lost sister)
+ * - 4 Acts + Finale
+ * - Quest journal & tracking
+ * - Lore entries discovery
+ * - Multiple endings
+ * - Decision system
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class MainQuestAnaSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Quest state
+ this.currentAct = 0;
+ this.currentQuest = null;
+ this.completedQuests = new Set();
+
+ // Story decisions
+ this.decisions = new Map();
+ this.moralityScore = 0; // -100 (revenge) to +100 (cure)
+
+ // Lore entries
+ this.loreEntries = new Map();
+ this.discoveredLore = new Set();
+
+ // Quest journal
+ this.journal = [];
+
+ console.log('๐ง MainQuestAnaSystem initialized');
+
+ // Register all quests
+ this.registerQuests();
+
+ // Register lore entries
+ this.registerLoreEntries();
+ }
+
+ /**
+ * Register all main quests
+ */
+ registerQuests() {
+ // Prologue is handled by PrologueScene
+
+ // ACT 1: Searching for Clues (Already in Act1QuestData.js)
+ // Quests 1.1 - 1.8 are integrated
+
+ // ACT 2: Discovering the Lab
+ this.registerQuest('2.1', {
+ act: 2,
+ name: 'The Underground Facility',
+ description: 'Twin Bond led you to old military bunker. Investigate.',
+ objectives: [
+ { id: 'find_bunker', desc: 'Locate underground bunker entrance', type: 'location' },
+ { id: 'clear_entrance', desc: 'Clear zombie blockade', type: 'combat' },
+ { id: 'enter_lab', desc: 'Enter Black Serpent Laboratory', type: 'location' }
+ ],
+ rewards: { xp: 2000, zlatniki: 1000, bondStrength: 15 }
+ });
+
+ this.registerQuest('2.2', {
+ act: 2,
+ name: 'Lab Records',
+ description: 'Search lab computers for information about Ana.',
+ objectives: [
+ { id: 'find_terminal', desc: 'Find working computer terminal', type: 'location' },
+ { id: 'hack_system', desc: 'Hack into lab database', type: 'puzzle' },
+ { id: 'read_files', desc: 'Read experiment logs', type: 'interact' }
+ ],
+ rewards: { xp: 1500, lore: ['virus_origin', 'experiment_logs'] }
+ });
+
+ this.registerQuest('2.3', {
+ act: 2,
+ name: 'Zombie Test Subjects',
+ description: 'Discover horrifying truth about zombie experiments.',
+ objectives: [
+ { id: 'find_cells', desc: 'Locate holding cells', type: 'location' },
+ { id: 'read_charts', desc: 'Read patient charts', type: 'interact' },
+ { id: 'find_ana_cell', desc: 'Find Ana\'s empty cell', type: 'location' }
+ ],
+ rewards: { xp: 2500, bondStrength: 20, lore: ['experiment_subjects'] }
+ });
+
+ // ACT 3: Confronting the Truth
+ this.registerQuest('3.1', {
+ act: 3,
+ name: 'The Scientist',
+ description: 'Track down lead scientist Dr. Kovaฤ.',
+ objectives: [
+ { id: 'find_hideout', desc: 'Locate Dr. Kovaฤ\'s hideout', type: 'location' },
+ { id: 'confront_scientist', desc: 'Confront Dr. Kovaฤ', type: 'dialogue' },
+ { id: 'make_choice', desc: 'Kill or spare the scientist?', type: 'decision' }
+ ],
+ rewards: { xp: 3000, decision: 'scientist_fate' }
+ });
+
+ this.registerQuest('3.2', {
+ act: 3,
+ name: 'The Final Obstacle',
+ description: 'Ana is held in the mountain vault. Prepare for the breach.',
+ objectives: [
+ { id: 'gather_allies', desc: 'Recruit allies for the assault', type: 'social' },
+ { id: 'craft_weapons', desc: 'Craft high-tier equipment', type: 'crafting' },
+ { id: 'locate_lair', desc: 'Find the mountain fortress', type: 'location' }
+ ],
+ rewards: { xp: 4000, zlatniki: 5000 }
+ });
+
+ this.registerQuest('3.3', {
+ act: 3,
+ name: 'The Cure Research',
+ description: 'Find and study cure research notes.',
+ objectives: [
+ { id: 'find_notes', desc: 'Collect all 7 research notes', type: 'collect' },
+ { id: 'study_cure', desc: 'Study cure methodology', type: 'research' },
+ { id: 'make_choice', desc: 'Pursue cure or revenge?', type: 'decision' }
+ ],
+ rewards: { xp: 3500, decision: 'cure_or_revenge', lore: ['cure_research'] }
+ });
+
+ // FINALE
+ this.registerQuest('finale', {
+ act: 4,
+ name: 'Confrontation',
+ description: 'Reach the reactor core and save Ana.',
+ objectives: [
+ { id: 'enter_lair', desc: 'Storm the mountain fortress', type: 'location' },
+ { id: 'defeat_boss', desc: 'Breach the vault defenses', type: 'boss_fight' },
+ { id: 'save_ana', desc: 'Rescue Ana', type: 'cutscene' },
+ { id: 'final_choice', desc: 'The ultimate decision...', type: 'decision' }
+ ],
+ rewards: { xp: 10000, achievement: 'main_quest_complete' }
+ });
+
+ console.log('โ
Main quest chain registered');
+ }
+
+ /**
+ * Register single quest
+ */
+ registerQuest(id, data) {
+ // Quests are managed by QuestSystemExpanded
+ // This just tracks main quest progress
+ console.log(`๐ Quest ${id}: ${data.name}`);
+ }
+
+ /**
+ * Register lore entries
+ */
+ registerLoreEntries() {
+ this.loreEntries.set('virus_origin', {
+ title: 'The Outbreak',
+ content: `BLACK SERPENT INITIATIVE - CLASSIFIED
+
+Project Lazarus began as a cure for death itself. Using ancient viral samples
+recovered from permafrost, Dr. Helena Kovaฤ believed she could reanimate dead
+tissue while preserving consciousness.
+
+The first trials were... promising. Deceased subjects showed signs of life.
+But something went wrong. The virus mutated. Subjects became aggressive,
+hungry for living flesh.
+
+We tried to contain it. We failed.
+
+The Outbreak began here, in this very laboratory, on October 13th, 2024.
+
+May God forgive us.`,
+ location: 'Lab Computer Terminal 1'
+ });
+
+ this.loreEntries.set('experiment_logs', {
+ title: 'Subject Ana Kovaฤ',
+ content: `EXPERIMENT LOG #247
+Subject: Ana Kovaฤ (Age 19, Twin Sister of Kai Kovaฤ)
+Status: SPECIAL CASE
+
+Subject shows unprecedented resistance to viral infection. Twin Bond
+phenomenon observed - psychic connection to brother remains strong even
+in experimental conditions.
+
+Dr. Kovaฤ (mother) has requested subject be moved to special containment.
+Krniฤ has shown interest in subject. Authorization pending.
+
+UPDATE: Subject escaped during the facility breach. Current location unknown.
+Twin Bond suggests she's alive. Brother searching for her.`,
+ location: 'Lab Record Room'
+ });
+
+ this.loreEntries.set('project_apex', {
+ title: 'Project Apex',
+ content: `PROJECT: APEX PREDATOR
+
+Combining viral genetics (recovered from permafrost) with the Alpha Hybrid
+strain has created our most powerful weapon yet.
+
+Designation: ALPHA TROLL KING
+
+Capabilities:
+- Enhanced strength (100x human)
+- Accelerated healing
+- Pack leader pheromones (controls all lesser infected)
+- High intelligence (strategic thinking)
+
+WARNING: Subject is unstable. Uncontrollable by anyone but Krniฤ.
+Recommendation: DEPLOYMENT
+
+The new world requires a king.`,
+ location: 'Lab Chief Scientist Office'
+ });
+
+ this.loreEntries.set('cure_research', {
+ title: 'The Cure - Final Notes',
+ content: `I've done it. The cure is real.
+
+Using Ana's unique antibodies and the original viral strain, I've synthesized
+a counter-agent. It reverses the infection. Restores humanity.
+
+But there's a catch. The cure requires Ana's living tissue. A piece of her
+brain stem, specifically. The procedure would kill her.
+
+Kai will come for his sister. When he does, he'll have a choice:
+- Save Ana, doom the world to eternal infection
+- Sacrifice Ana, cure humanity, lose his sister forever
+
+I can't make this choice. Neither could any parent.
+
+So I've hidden the cure formula. Seven pieces, scattered across the land.
+Let fate decide.
+
+- Dr. Helena Kovaฤ`,
+ location: 'Hidden Lab Vault'
+ });
+
+ console.log(`โ
Registered ${this.loreEntries.size} lore entries`);
+ }
+
+ /**
+ * Discover lore entry
+ */
+ discoverLore(loreId) {
+ const lore = this.loreEntries.get(loreId);
+ if (!lore) {
+ console.error(`Lore ${loreId} not found!`);
+ return false;
+ }
+
+ if (this.discoveredLore.has(loreId)) {
+ console.log(`Already discovered: ${lore.title}`);
+ return false;
+ }
+
+ this.discoveredLore.add(loreId);
+
+ console.log(`๐ LORE DISCOVERED: ${lore.title}`);
+ console.log(lore.content);
+
+ this.showNotification({
+ title: 'Lore Entry Discovered!',
+ text: `๐ ${lore.title}`,
+ icon: '๐'
+ });
+
+ // Add to journal
+ this.journal.push({
+ type: 'lore',
+ id: loreId,
+ title: lore.title,
+ date: new Date()
+ });
+
+ return true;
+ }
+
+ /**
+ * Make story decision
+ */
+ makeDecision(decisionId, choice) {
+ this.decisions.set(decisionId, choice);
+
+ console.log(`โ๏ธ Decision: ${decisionId} = ${choice}`);
+
+ // Track morality
+ const moralityChanges = {
+ scientist_fate: { kill: -20, spare: +20 },
+ cure_or_revenge: { cure: +50, revenge: -50 },
+ final_choice: { save_ana: -100, sacrifice_ana: +100 }
+ };
+
+ const change = moralityChanges[decisionId]?.[choice] || 0;
+ this.moralityScore = Math.max(-100, Math.min(100, this.moralityScore + change));
+
+ console.log(` Morality: ${this.moralityScore}`);
+
+ return true;
+ }
+
+ /**
+ * Get ending based on decisions
+ */
+ getEnding() {
+ const savedAna = this.decisions.get('final_choice') === 'save_ana';
+ const pursuesCure = this.decisions.get('cure_or_revenge') === 'cure';
+ const sparedScientist = this.decisions.get('scientist_fate') === 'spare';
+
+ // 4 Main Endings
+ if (savedAna && pursuesCure && sparedScientist) {
+ return {
+ id: 'perfect_ending',
+ name: 'The Hope',
+ description: 'You saved Ana and found another way to create the cure. Humanity has hope.',
+ icon: '๐'
+ };
+ } else if (!savedAna && pursuesCure) {
+ return {
+ id: 'sacrifice_ending',
+ name: 'The Martyr',
+ description: 'You sacrificed Ana to cure humanity. The world is saved, but at what cost?',
+ icon: '๐ข'
+ };
+ } else if (savedAna && !pursuesCure) {
+ return {
+ id: 'selfish_ending',
+ name: 'The Bond',
+ description: 'You saved Ana but abandoned the cure. The world remains infected, but your sister lives.',
+ icon: '๐'
+ };
+ } else {
+ return {
+ id: 'dark_ending',
+ name: 'The Revenge',
+ description: 'You chose revenge over everything. Krniฤ is dead, but Ana and the cure are lost.',
+ icon: 'โ๏ธ'
+ };
+ }
+ }
+
+ /**
+ * Get quest journal
+ */
+ getJournal() {
+ return this.journal;
+ }
+
+ /**
+ * Get all discovered lore
+ */
+ getDiscoveredLore() {
+ return Array.from(this.discoveredLore).map(id => this.loreEntries.get(id));
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MapRevealSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MapRevealSystem.js
new file mode 100644
index 000000000..0dfce3489
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MapRevealSystem.js
@@ -0,0 +1,334 @@
+/**
+ * ๐บ๏ธ MAP REVEAL SYSTEM
+ * Fog of war system that reveals map as player explores
+ * - Reveals tiles around player
+ * - Persistent (saves discovered areas)
+ * - Minimap integration
+ * - Full map view (M key)
+ */
+
+class MapRevealSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Revealed tiles (500x500 grid)
+ this.revealed = Array(500).fill(null).map(() => Array(500).fill(false));
+
+ // Reveal radius around player
+ this.revealRadius = 15; // tiles
+
+ // Map UI elements
+ this.mapOpen = false;
+ this.mapContainer = null;
+
+ // Minimap
+ this.minimap = null;
+ this.minimapSize = 200;
+ this.minimapScale = 2; // pixels per tile on minimap
+
+ console.log('๐บ๏ธ MapRevealSystem initialized');
+ }
+
+ // Reveal tiles around player
+ revealArea(playerX, playerY) {
+ const gridX = Math.floor(playerX);
+ const gridY = Math.floor(playerY);
+
+ let newTilesRevealed = 0;
+
+ for (let dx = -this.revealRadius; dx <= this.revealRadius; dx++) {
+ for (let dy = -this.revealRadius; dy <= this.revealRadius; dy++) {
+ const x = gridX + dx;
+ const y = gridY + dy;
+
+ // Check if within world bounds
+ if (x < 0 || x >= 500 || y < 0 || y >= 500) continue;
+
+ // Check if within circular radius
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > this.revealRadius) continue;
+
+ // Reveal tile
+ if (!this.revealed[y][x]) {
+ this.revealed[y][x] = true;
+ newTilesRevealed++;
+ }
+ }
+ }
+
+ if (newTilesRevealed > 0) {
+ // Update minimap
+ this.updateMinimap();
+ }
+ }
+
+ // Create minimap (bottom-right corner)
+ createMinimap() {
+ if (this.minimap) return;
+
+ const size = this.minimapSize;
+ const x = this.scene.cameras.main.width - size - 20;
+ const y = this.scene.cameras.main.height - size - 20;
+
+ // Background
+ this.minimapBg = this.scene.add.rectangle(x, y, size, size, 0x000000, 0.7);
+ this.minimapBg.setOrigin(0);
+ this.minimapBg.setScrollFactor(0);
+ this.minimapBg.setDepth(9000);
+
+ // Border
+ this.minimapBorder = this.scene.add.rectangle(x, y, size, size);
+ this.minimapBorder.setOrigin(0);
+ this.minimapBorder.setStrokeStyle(2, 0xFFFFFF);
+ this.minimapBorder.setScrollFactor(0);
+ this.minimapBorder.setDepth(9001);
+
+ // Canvas for map rendering
+ this.minimapTexture = this.scene.add.renderTexture(x, y, size, size);
+ this.minimapTexture.setOrigin(0);
+ this.minimapTexture.setScrollFactor(0);
+ this.minimapTexture.setDepth(9002);
+
+ // Label
+ this.minimapLabel = this.scene.add.text(x + size / 2, y - 15, 'Map (M)', {
+ fontSize: '14px',
+ color: '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 5, y: 2 }
+ });
+ this.minimapLabel.setOrigin(0.5);
+ this.minimapLabel.setScrollFactor(0);
+ this.minimapLabel.setDepth(9003);
+
+ this.minimap = {
+ bg: this.minimapBg,
+ border: this.minimapBorder,
+ texture: this.minimapTexture,
+ label: this.minimapLabel,
+ x, y, size
+ };
+
+ this.updateMinimap();
+ }
+
+ // Update minimap rendering
+ updateMinimap() {
+ if (!this.minimap) return;
+
+ const player = this.scene.player;
+ if (!player) return;
+
+ const playerX = Math.floor(player.gridX);
+ const playerY = Math.floor(player.gridY);
+
+ // Clear
+ this.minimap.texture.clear();
+
+ // Calculate view area (centered on player)
+ const viewSize = Math.floor(this.minimap.size / this.minimapScale);
+ const startX = Math.max(0, playerX - viewSize / 2);
+ const startY = Math.max(0, playerY - viewSize / 2);
+ const endX = Math.min(500, startX + viewSize);
+ const endY = Math.min(500, startY + viewSize);
+
+ // Draw revealed tiles
+ for (let y = startY; y < endY; y++) {
+ for (let x = startX; x < endX; x++) {
+ if (!this.revealed[y] || !this.revealed[y][x]) continue;
+
+ const screenX = (x - startX) * this.minimapScale;
+ const screenY = (y - startY) * this.minimapScale;
+
+ // Get biome color
+ const biome = this.scene.biomeSystem ? this.scene.biomeSystem.getBiomeAt(x, y) : 'Grassland';
+ const color = this.getBiomeMinimapColor(biome);
+
+ this.minimap.texture.fill(color, 1, screenX, screenY, this.minimapScale, this.minimapScale);
+ }
+ }
+
+ // Draw player position
+ const playerScreenX = (playerX - startX) * this.minimapScale;
+ const playerScreenY = (playerY - startY) * this.minimapScale;
+ this.minimap.texture.fill(0xFFFF00, 1, playerScreenX - 2, playerScreenY - 2, 4, 4);
+ }
+
+ // Get minimap color for biome
+ getBiomeMinimapColor(biome) {
+ const colors = {
+ 'Grassland': 0x3CB371,
+ 'Forest': 0x2d5016,
+ 'Desert': 0xd4c4a1,
+ 'Mountain': 0x808080,
+ 'Swamp': 0x3d5a3d
+ };
+ return colors[biome] || 0x3CB371;
+ }
+
+ // Toggle full map view (M key)
+ toggleFullMap() {
+ if (this.mapOpen) {
+ this.closeFullMap();
+ } else {
+ this.openFullMap();
+ }
+ }
+
+ // Open full map
+ openFullMap() {
+ if (this.mapOpen) return;
+
+ this.mapOpen = true;
+
+ // Semi-transparent background
+ this.fullMapBg = this.scene.add.rectangle(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.centerY,
+ this.scene.cameras.main.width,
+ this.scene.cameras.main.height,
+ 0x000000,
+ 0.9
+ );
+ this.fullMapBg.setScrollFactor(0);
+ this.fullMapBg.setDepth(15000);
+
+ // Map title
+ this.fullMapTitle = this.scene.add.text(
+ this.scene.cameras.main.centerX,
+ 50,
+ 'World Map (Press M to close)',
+ {
+ fontSize: '32px',
+ color: '#FFD700',
+ backgroundColor: '#000000',
+ padding: { x: 20, y: 10 }
+ }
+ );
+ this.fullMapTitle.setOrigin(0.5);
+ this.fullMapTitle.setScrollFactor(0);
+ this.fullMapTitle.setDepth(15001);
+
+ // Render full map
+ const mapSize = 600;
+ const mapX = this.scene.cameras.main.centerX - mapSize / 2;
+ const mapY = this.scene.cameras.main.centerY - mapSize / 2;
+
+ this.fullMapTexture = this.scene.add.renderTexture(mapX, mapY, mapSize, mapSize);
+ this.fullMapTexture.setScrollFactor(0);
+ this.fullMapTexture.setDepth(15002);
+
+ // Draw entire world
+ const scale = mapSize / 500;
+ for (let y = 0; y < 500; y += 5) {
+ for (let x = 0; x < 500; x += 5) {
+ if (!this.revealed[y] || !this.revealed[y][x]) continue;
+
+ const biome = this.scene.biomeSystem ? this.scene.biomeSystem.getBiomeAt(x, y) : 'Grassland';
+ const color = this.getBiomeMinimapColor(biome);
+
+ this.fullMapTexture.fill(color, 1, x * scale, y * scale, scale * 5, scale * 5);
+ }
+ }
+
+ // Player position
+ if (this.scene.player) {
+ const px = Math.floor(this.scene.player.gridX) * scale;
+ const py = Math.floor(this.scene.player.gridY) * scale;
+ this.fullMapTexture.fill(0xFFFF00, 1, px - 3, py - 3, 6, 6);
+ }
+
+ // Stats
+ const exploredTiles = this.revealed.flat().filter(r => r).length;
+ const totalTiles = 500 * 500;
+ const percent = ((exploredTiles / totalTiles) * 100).toFixed(1);
+
+ this.fullMapStats = this.scene.add.text(
+ this.scene.cameras.main.centerX,
+ mapY + mapSize + 30,
+ `Explored: ${exploredTiles} / ${totalTiles} tiles (${percent}%)`,
+ {
+ fontSize: '20px',
+ color: '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 15, y: 8 }
+ }
+ );
+ this.fullMapStats.setOrigin(0.5);
+ this.fullMapStats.setScrollFactor(0);
+ this.fullMapStats.setDepth(15003);
+ }
+
+ // Close full map
+ closeFullMap() {
+ if (!this.mapOpen) return;
+
+ this.mapOpen = false;
+
+ if (this.fullMapBg) {
+ this.fullMapBg.destroy();
+ this.fullMapTitle.destroy();
+ this.fullMapTexture.destroy();
+ this.fullMapStats.destroy();
+ }
+ }
+
+ // Update (called every frame)
+ update() {
+ if (this.scene.player) {
+ this.revealArea(this.scene.player.gridX, this.scene.player.gridY);
+ }
+ }
+
+ // Get exploration statistics
+ getStats() {
+ const exploredTiles = this.revealed.flat().filter(r => r).length;
+ const totalTiles = 500 * 500;
+ const percent = ((exploredTiles / totalTiles) * 100).toFixed(2);
+
+ return {
+ explored: exploredTiles,
+ total: totalTiles,
+ percent: parseFloat(percent)
+ };
+ }
+
+ // Export/Import for save system
+ exportData() {
+ // Convert 2D array to compressed format
+ const compressed = [];
+ for (let y = 0; y < 500; y++) {
+ for (let x = 0; x < 500; x++) {
+ if (this.revealed[y][x]) {
+ compressed.push(`${x},${y}`);
+ }
+ }
+ }
+ return { revealed: compressed };
+ }
+
+ importData(data) {
+ if (!data || !data.revealed) return;
+
+ // Clear current
+ this.revealed = Array(500).fill(null).map(() => Array(500).fill(false));
+
+ // Decompress
+ for (const key of data.revealed) {
+ const [x, y] = key.split(',').map(Number);
+ if (x >= 0 && x < 500 && y >= 0 && y < 500) {
+ this.revealed[y][x] = true;
+ }
+ }
+ }
+
+ destroy() {
+ this.closeFullMap();
+ if (this.minimap) {
+ this.minimap.bg.destroy();
+ this.minimap.border.destroy();
+ this.minimap.texture.destroy();
+ this.minimap.label.destroy();
+ this.minimap = null;
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MarriageRomanceSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MarriageRomanceSystem.js
new file mode 100644
index 000000000..73b58f3b4
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MarriageRomanceSystem.js
@@ -0,0 +1,627 @@
+/**
+ * MarriageRomanceSystem.js
+ * =======================
+ * KRVAVA ลฝETEV - Marriage & Romance System (P10)
+ *
+ * Features:
+ * - Heart tracking (0-10 hearts per NPC)
+ * - Gift system (loved/liked/neutral/disliked)
+ * - Dating mechanics (8+ hearts)
+ * - Date events (5 types)
+ * - Marriage proposal system
+ * - Wedding ceremony
+ * - Married life mechanics
+ * - 12 romance questlines
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class MarriageRomanceSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Romance data
+ this.romanceData = new Map(); // npcId -> romance state
+ this.marriageStatus = {
+ isMarried: false,
+ spouse: null,
+ marriageDate: null,
+ anniversaryDate: null
+ };
+
+ // Dating status
+ this.datingStatus = new Map(); // npcId -> dating/engaged/married
+
+ // Current date event
+ this.activeDate = null;
+
+ console.log('๐ MarriageRomanceSystem initialized');
+ }
+
+ /**
+ * 10.1 - Romance Hearts System
+ * Track heart levels for each romanceable NPC
+ */
+ initializeRomanceableNPC(npcId, name, gender = 'female') {
+ if (this.romanceData.has(npcId)) {
+ return; // Already initialized
+ }
+
+ const romanceState = {
+ npcId: npcId,
+ name: name,
+ gender: gender,
+ hearts: 0, // 0-10 hearts
+ maxHearts: 10,
+ isRomanceable: true,
+ isDating: false,
+ isEngaged: false,
+ isMarried: false,
+
+ // Heart events (cutscenes)
+ heartEvents: {
+ 1: { seen: false, sceneId: `${npcId}_heart_1` },
+ 2: { seen: false, sceneId: `${npcId}_heart_2` },
+ 3: { seen: false, sceneId: `${npcId}_heart_3` },
+ 4: { seen: false, sceneId: `${npcId}_heart_4` },
+ 5: { seen: false, sceneId: `${npcId}_heart_5` },
+ 6: { seen: false, sceneId: `${npcId}_heart_6` },
+ 7: { seen: false, sceneId: `${npcId}_heart_7` },
+ 8: { seen: false, sceneId: `${npcId}_heart_8` },
+ 9: { seen: false, sceneId: `${npcId}_heart_9` },
+ 10: { seen: false, sceneId: `${npcId}_heart_10` }
+ },
+
+ // Gift preferences
+ lovedItems: [],
+ likedItems: [],
+ neutralItems: [],
+ dislikedItems: [],
+ hatedItems: [],
+
+ // Birthday
+ birthday: { season: 'Spring', day: 1 },
+
+ // Romance questline
+ questlineId: `romance_${npcId}`,
+ questlineComplete: false,
+
+ // Stats
+ giftsGiven: 0,
+ conversationsHad: 0,
+ datesCompleted: 0,
+ lastGiftDate: null,
+ lastTalkDate: null
+ };
+
+ this.romanceData.set(npcId, romanceState);
+ console.log(`๐ Initialized romance for ${name}`);
+
+ return romanceState;
+ }
+
+ /**
+ * 10.1 - Add hearts to NPC
+ */
+ addHearts(npcId, amount, reason = 'unknown') {
+ const romanceState = this.romanceData.get(npcId);
+ if (!romanceState) {
+ console.warn(`Romance data not found for ${npcId}`);
+ return false;
+ }
+
+ const oldHearts = romanceState.hearts;
+ romanceState.hearts = Math.min(romanceState.maxHearts, romanceState.hearts + amount);
+
+ console.log(`๐ ${romanceState.name}: ${oldHearts} โ ${romanceState.hearts} hearts (${reason})`);
+
+ // Trigger heart event if just reached new level
+ const newLevel = Math.floor(romanceState.hearts);
+ const oldLevel = Math.floor(oldHearts);
+
+ if (newLevel > oldLevel) {
+ this.triggerHeartEvent(npcId, newLevel);
+ }
+
+ // UI update
+ this.updateRomanceUI(npcId);
+
+ return true;
+ }
+
+ /**
+ * 10.1 - Trigger heart event cutscene
+ */
+ triggerHeartEvent(npcId, heartLevel) {
+ const romanceState = this.romanceData.get(npcId);
+ if (!romanceState) return;
+
+ const event = romanceState.heartEvents[heartLevel];
+ if (!event || event.seen) return;
+
+ console.log(`๐ Triggering ${romanceState.name}'s ${heartLevel}-heart event!`);
+
+ // Mark as seen
+ event.seen = true;
+
+ // TODO: Trigger actual cutscene
+ // this.scene.cutsceneSystem.play(event.sceneId);
+
+ // Show notification
+ this.showNotification({
+ title: `${heartLevel} Hearts!`,
+ text: `${romanceState.name} loves you more!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * 10.2 - Gift System
+ */
+ giveGift(npcId, itemId) {
+ const romanceState = this.romanceData.get(npcId);
+ if (!romanceState) {
+ console.warn(`Cannot gift ${itemId} to unknown NPC ${npcId}`);
+ return false;
+ }
+
+ // Check if already gifted today
+ const today = this.scene.timeSystem?.getCurrentDate() || new Date();
+ if (romanceState.lastGiftDate === today) {
+ this.showNotification({
+ title: 'Already Gifted',
+ text: `You already gave ${romanceState.name} a gift today!`,
+ icon: '๐'
+ });
+ return false;
+ }
+
+ // Check gift preference
+ let heartsGained = 0;
+ let reaction = 'neutral';
+
+ if (romanceState.lovedItems.includes(itemId)) {
+ heartsGained = 0.8; // 8/10 of a heart
+ reaction = 'loved';
+ } else if (romanceState.likedItems.includes(itemId)) {
+ heartsGained = 0.4; // 4/10 of a heart
+ reaction = 'liked';
+ } else if (romanceState.dislikedItems.includes(itemId)) {
+ heartsGained = -0.2; // Lose 2/10 heart
+ reaction = 'disliked';
+ } else if (romanceState.hatedItems.includes(itemId)) {
+ heartsGained = -0.4; // Lose 4/10 heart
+ reaction = 'hated';
+ } else {
+ heartsGained = 0.2; // Neutral = 2/10 heart
+ reaction = 'neutral';
+ }
+
+ // Birthday bonus!
+ const isBirthday = this.isBirthday(npcId);
+ if (isBirthday) {
+ heartsGained *= 2;
+ this.showNotification({
+ title: 'Birthday!',
+ text: `It's ${romanceState.name}'s birthday! Double hearts!`,
+ icon: '๐'
+ });
+ }
+
+ // Apply hearts
+ this.addHearts(npcId, heartsGained, `gift: ${itemId} (${reaction})`);
+
+ // Update stats
+ romanceState.giftsGiven++;
+ romanceState.lastGiftDate = today;
+
+ // Show reaction
+ this.showGiftReaction(npcId, itemId, reaction, heartsGained);
+
+ return true;
+ }
+
+ /**
+ * 10.2 - Check if NPC's birthday
+ */
+ isBirthday(npcId) {
+ const romanceState = this.romanceData.get(npcId);
+ if (!romanceState) return false;
+
+ const currentDate = this.scene.timeSystem?.getCurrentDate();
+ if (!currentDate) return false;
+
+ return (
+ currentDate.season === romanceState.birthday.season &&
+ currentDate.day === romanceState.birthday.day
+ );
+ }
+
+ /**
+ * 10.2 - Show gift reaction
+ */
+ showGiftReaction(npcId, itemId, reaction, heartsGained) {
+ const romanceState = this.romanceData.get(npcId);
+
+ const reactions = {
+ loved: `โค๏ธ ${romanceState.name} loved it! This is her favorite!`,
+ liked: `๐ ${romanceState.name} liked it! Thank you!`,
+ neutral: `๐ ${romanceState.name} accepted it politely.`,
+ disliked: `๐ ${romanceState.name} didn't like it much...`,
+ hated: `๐ ${romanceState.name} hated it! Why would you give this?!`
+ };
+
+ this.showNotification({
+ title: 'Gift Given',
+ text: reactions[reaction],
+ icon: heartsGained > 0 ? '๐' : '๐'
+ });
+ }
+
+ /**
+ * 10.3 - Dating Mechanics
+ */
+ canStartDating(npcId) {
+ const romanceState = this.romanceData.get(npcId);
+ if (!romanceState) return false;
+
+ return (
+ romanceState.hearts >= 8 &&
+ !romanceState.isDating &&
+ !romanceState.isMarried &&
+ !this.marriageStatus.isMarried // Can't date if married to someone else
+ );
+ }
+
+ /**
+ * 10.3 - Give bouquet (start dating)
+ */
+ giveBouquet(npcId) {
+ if (!this.canStartDating(npcId)) {
+ console.log('โ Cannot give bouquet yet');
+ return false;
+ }
+
+ const romanceState = this.romanceData.get(npcId);
+
+ // Check player has bouquet
+ if (!this.scene.inventorySystem?.hasItem('bouquet', 1)) {
+ this.showNotification({
+ title: 'No Bouquet',
+ text: 'You need a Bouquet to start dating! (20 Flowers)',
+ icon: '๐'
+ });
+ return false;
+ }
+
+ // Remove bouquet from inventory
+ this.scene.inventorySystem.removeItem('bouquet', 1);
+
+ // Start dating
+ romanceState.isDating = true;
+ this.datingStatus.set(npcId, 'dating');
+
+ console.log(`๐ Now dating ${romanceState.name}!`);
+
+ // Trigger dating cutscene
+ this.showDatingCutscene(npcId);
+
+ return true;
+ }
+
+ /**
+ * 10.3 - Show dating cutscene
+ */
+ showDatingCutscene(npcId) {
+ const romanceState = this.romanceData.get(npcId);
+
+ // TODO: Implement actual cutscene
+ this.showNotification({
+ title: 'Dating!',
+ text: `๐ ${romanceState.name} accepted! You're now dating!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * 10.4 - Date Events
+ */
+ startDateEvent(npcId, dateType) {
+ const romanceState = this.romanceData.get(npcId);
+ if (!romanceState || !romanceState.isDating) {
+ console.log('โ Not dating this NPC');
+ return false;
+ }
+
+ const dateEvents = {
+ 'beach_picnic': {
+ name: 'Beach Picnic',
+ location: { x: 100, y: 200 },
+ duration: 120000, // 2 minutes
+ hearts: 0.5
+ },
+ 'restaurant': {
+ name: 'Restaurant Dinner',
+ location: { x: 300, y: 400 },
+ duration: 180000, // 3 minutes
+ hearts: 0.5
+ },
+ 'stargazing': {
+ name: 'Stargazing',
+ location: { x: 500, y: 100 },
+ duration: 150000, // 2.5 minutes
+ hearts: 0.5
+ },
+ 'adventure': {
+ name: 'Adventure Date',
+ location: { x: 700, y: 600 },
+ duration: 300000, // 5 minutes
+ hearts: 1.0
+ },
+ 'festival': {
+ name: 'Festival Date',
+ location: { x: 400, y: 300 },
+ duration: 240000, // 4 minutes
+ hearts: 0.75
+ }
+ };
+
+ const dateEvent = dateEvents[dateType];
+ if (!dateEvent) {
+ console.error(`Unknown date type: ${dateType}`);
+ return false;
+ }
+
+ // Start date
+ this.activeDate = {
+ npcId: npcId,
+ type: dateType,
+ startTime: Date.now(),
+ ...dateEvent
+ };
+
+ console.log(`๐ Starting ${dateEvent.name} with ${romanceState.name}`);
+
+ // TODO: Implement actual date cutscene/mini-game
+
+ return true;
+ }
+
+ /**
+ * 10.4 - Complete date event
+ */
+ completeDate() {
+ if (!this.activeDate) return;
+
+ const romanceState = this.romanceData.get(this.activeDate.npcId);
+
+ // Grant hearts
+ this.addHearts(this.activeDate.npcId, this.activeDate.hearts, 'date event');
+
+ // Update stats
+ romanceState.datesCompleted++;
+
+ this.showNotification({
+ title: 'Date Complete!',
+ text: `๐ ${romanceState.name} had a wonderful time!`,
+ icon: '๐'
+ });
+
+ this.activeDate = null;
+ }
+
+ /**
+ * 10.5 - Marriage Proposal
+ */
+ canPropose(npcId) {
+ const romanceState = this.romanceData.get(npcId);
+ if (!romanceState) return false;
+
+ return (
+ romanceState.isDating &&
+ romanceState.hearts >= 10 &&
+ !romanceState.isEngaged &&
+ !romanceState.isMarried &&
+ !this.marriageStatus.isMarried
+ );
+ }
+
+ /**
+ * 10.5 - Propose with Mermaid Pendant
+ */
+ propose(npcId) {
+ if (!this.canPropose(npcId)) {
+ console.log('โ Cannot propose yet');
+ return false;
+ }
+
+ // Check for Mermaid Pendant
+ if (!this.scene.inventorySystem?.hasItem('mermaid_pendant', 1)) {
+ this.showNotification({
+ title: 'Need Mermaid Pendant',
+ text: 'Craft a Mermaid Pendant first! (50 Gold + Diamond + Pearl)',
+ icon: '๐'
+ });
+ return false;
+ }
+
+ const romanceState = this.romanceData.get(npcId);
+
+ // Remove pendant
+ this.scene.inventorySystem.removeItem('mermaid_pendant', 1);
+
+ // Engage!
+ romanceState.isEngaged = true;
+ this.datingStatus.set(npcId, 'engaged');
+
+ console.log(`๐ Engaged to ${romanceState.name}!`);
+
+ // Set wedding date (3 days from now)
+ const weddingDate = new Date();
+ weddingDate.setDate(weddingDate.getDate() + 3);
+ romanceState.weddingDate = weddingDate;
+
+ // Trigger proposal cutscene
+ this.showProposalCutscene(npcId);
+
+ return true;
+ }
+
+ /**
+ * 10.5 - Show proposal cutscene
+ */
+ showProposalCutscene(npcId) {
+ const romanceState = this.romanceData.get(npcId);
+
+ // TODO: Implement actual cutscene
+ this.showNotification({
+ title: 'She Said YES!',
+ text: `๐ ${romanceState.name} accepted your proposal! Wedding in 3 days!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * 10.6 - Wedding Ceremony
+ */
+ startWedding(npcId) {
+ const romanceState = this.romanceData.get(npcId);
+ if (!romanceState || !romanceState.isEngaged) {
+ console.log('โ Not engaged');
+ return false;
+ }
+
+ console.log(`๐ฐ Starting wedding ceremony with ${romanceState.name}!`);
+
+ // Update status
+ romanceState.isMarried = true;
+ romanceState.isEngaged = false;
+ romanceState.isDating = false;
+
+ this.marriageStatus.isMarried = true;
+ this.marriageStatus.spouse = npcId;
+ this.marriageStatus.marriageDate = new Date();
+
+ this.datingStatus.set(npcId, 'married');
+
+ // Trigger wedding cutscene
+ this.showWeddingCutscene(npcId);
+
+ return true;
+ }
+
+ /**
+ * 10.6 - Wedding cutscene
+ */
+ showWeddingCutscene(npcId) {
+ const romanceState = this.romanceData.get(npcId);
+
+ // TODO: Implement full wedding cutscene with:
+ // - Church decoration
+ // - All NPCs attending
+ // - Vows exchange
+ // - Grok gong moment
+ // - Ana reaction (crying)
+ // - Wedding party
+ // - Fireworks
+
+ this.showNotification({
+ title: 'Married!',
+ text: `๐ฐ๐ You married ${romanceState.name}! Congratulations!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * 10.7 - Married Life
+ */
+ getMorningKiss() {
+ if (!this.marriageStatus.isMarried) return null;
+
+ const spouse = this.romanceData.get(this.marriageStatus.spouse);
+ if (!spouse) return null;
+
+ // Grant +10 HP buff
+ if (this.scene.player) {
+ this.scene.player.heal(10);
+ }
+
+ return {
+ text: `${spouse.name} gives you a morning kiss! (+10 HP)`,
+ icon: '๐'
+ };
+ }
+
+ /**
+ * 10.7 - Spouse daily dialogue
+ */
+ getSpouseDialogue() {
+ if (!this.marriageStatus.isMarried) return null;
+
+ const spouse = this.romanceData.get(this.marriageStatus.spouse);
+ if (!spouse) return null;
+
+ // TODO: Return random dialogue from pool of 50+ lines
+ const dialogues = [
+ "Good morning, love! I'll water the crops today.",
+ "How did you sleep? I made you breakfast!",
+ "The farm is looking great! You're amazing!",
+ "I love you so much. Let's have a great day!"
+ ];
+
+ return Phaser.Utils.Array.GetRandom(dialogues);
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+
+ /**
+ * Helper: Update romance UI
+ */
+ updateRomanceUI(npcId) {
+ // TODO: Update heart display in UI
+ const romanceState = this.romanceData.get(npcId);
+ if (romanceState) {
+ console.log(`๐ UI Update: ${romanceState.name} - ${romanceState.hearts}/10 hearts`);
+ }
+ }
+
+ /**
+ * Get romance state for NPC
+ */
+ getRomanceState(npcId) {
+ return this.romanceData.get(npcId);
+ }
+
+ /**
+ * Get all romanceable NPCs
+ */
+ getRomanceableNPCs() {
+ return Array.from(this.romanceData.values()).filter(r => r.isRomanceable);
+ }
+
+ /**
+ * Check if married
+ */
+ isMarried() {
+ return this.marriageStatus.isMarried;
+ }
+
+ /**
+ * Get spouse
+ */
+ getSpouse() {
+ if (!this.marriageStatus.isMarried) return null;
+ return this.romanceData.get(this.marriageStatus.spouse);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MasterGameSystemsManager.js b/EMERGENCY_SYSTEMS_RECOVERY/MasterGameSystemsManager.js
new file mode 100644
index 000000000..69123315c
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MasterGameSystemsManager.js
@@ -0,0 +1,446 @@
+/**
+ * MASTER GAME SYSTEMS MANAGER
+ * Centralized coordinator for all game systems
+ * Created: January 4, 2026
+ *
+ * Integrates:
+ * - Sleep System
+ * - Crafting Tables System
+ * - Bakery Shop System
+ * - Barber Shop System
+ * - Lawyer Office System
+ * - Zombie Miner Automation System
+ * - Town Growth System
+ * - NPC Privacy System
+ * - Existing Mining System
+ */
+
+
+class MasterGameSystemsManager {
+ constructor(game) {
+ this.game = game;
+ this.scene = game.scene;
+
+ console.log('๐ฎ Initializing Master Game Systems Manager...');
+
+ // Initialize all systems
+ this.initializeSystems();
+
+ // Set up cross-system event listeners
+ this.setupEventListeners();
+
+ console.log('โ
Master Game Systems Manager initialized!');
+ }
+
+ /**
+ * Initialize all game systems
+ */
+ initializeSystems() {
+ // HOME & SLEEP
+ this.sleepSystem = new SleepSystem(this.game);
+ console.log(' โ Sleep System');
+
+ // CRAFTING
+ this.craftingSystem = new CraftingTablesSystem(this.game);
+ console.log(' โ Crafting Tables System');
+
+ // TOWN BUILDINGS
+ this.bakerySystem = new BakeryShopSystem(this.game);
+ console.log(' โ Bakery Shop System');
+
+ this.barberSystem = new BarberShopSystem(this.game);
+ console.log(' โ Barber Shop System');
+
+ this.lawyerSystem = new LawyerOfficeSystem(this.game);
+ console.log(' โ Lawyer Office System');
+
+ // MINING & AUTOMATION
+ this.zombieMinerSystem = new ZombieMinerAutomationSystem(this.game);
+ console.log(' โ Zombie Miner Automation System');
+
+ // TOWN GROWTH
+ this.townGrowthSystem = new TownGrowthSystem(this.game);
+ console.log(' โ Town Growth System');
+
+ // NPC SYSTEMS
+ this.npcPrivacySystem = new NPCPrivacySystem(this.game);
+ console.log(' โ NPC Privacy System');
+
+ // Register all systems globally
+ this.registerSystems();
+ }
+
+ /**
+ * Register systems to game registry
+ */
+ registerSystems() {
+ this.game.registry.set('sleepSystem', this.sleepSystem);
+ this.game.registry.set('craftingSystem', this.craftingSystem);
+ this.game.registry.set('bakerySystem', this.bakerySystem);
+ this.game.registry.set('barberSystem', this.barberSystem);
+ this.game.registry.set('lawyerSystem', this.lawyerSystem);
+ this.game.registry.set('zombieMinerSystem', this.zombieMinerSystem);
+ this.game.registry.set('townGrowthSystem', this.townGrowthSystem);
+ this.game.registry.set('npcPrivacySystem', this.npcPrivacySystem);
+
+ console.log(' โ All systems registered to game registry');
+ }
+
+ /**
+ * Set up cross-system event listeners
+ */
+ setupEventListeners() {
+ // TOWN GROWTH โ SERVICES
+ this.game.events.on('serviceUnlocked', (data) => {
+ this.onServiceUnlocked(data);
+ });
+
+ // MARRIAGE โ LAWYER AUTO-UNLOCK
+ this.game.events.on('marriageComplete', () => {
+ this.lawyerSystem.checkAutoUnlock();
+ });
+
+ // RELATIONSHIP CHANGE โ LAWYER AUTO-UNLOCK
+ this.game.events.on('relationshipChanged', (data) => {
+ if (data.hearts <= 3) {
+ this.lawyerSystem.checkAutoUnlock();
+ }
+ });
+
+ // BUILDING UNLOCKED โ TOWN GROWTH
+ this.game.events.on('buildingUnlocked', (data) => {
+ this.townGrowthSystem.checkPopulationUnlocks();
+ });
+
+ // ZOMBIE HIRED โ TOWN GROWTH CHECK
+ this.game.events.on('zombieWorkerHired', () => {
+ this.townGrowthSystem.checkPopulationUnlocks();
+ });
+
+ // SLEEP โ ZOMBIE LOYALTY DECAY PAUSE
+ this.game.events.on('sleepStarted', () => {
+ this.pauseZombieLoyaltyDecay = true;
+ });
+
+ this.game.events.on('wakeUp', () => {
+ this.pauseZombieLoyaltyDecay = false;
+ });
+
+ console.log(' โ Cross-system event listeners configured');
+ }
+
+ /**
+ * Handle service unlock
+ */
+ onServiceUnlocked(data) {
+ const { serviceId, population } = data;
+
+ console.log(`๐๏ธ Service unlocked: ${serviceId} at pop ${population}`);
+
+ // Trigger service-specific initialization
+ switch (serviceId) {
+ case 'market':
+ this.initializeMarket();
+ break;
+ case 'hospital':
+ this.initializeHospital();
+ break;
+ case 'school':
+ this.initializeSchool();
+ break;
+ case 'bank':
+ this.initializeBank();
+ break;
+ case 'museum':
+ this.initializeMuseum();
+ break;
+ case 'theater':
+ this.initializeTheater();
+ break;
+ }
+ }
+
+ /**
+ * Service initializers (placeholders for future implementation)
+ */
+ initializeMarket() {
+ console.log(' โ Market initialized');
+ // TODO: Implement market system
+ }
+
+ initializeHospital() {
+ console.log(' โ Hospital initialized');
+ // TODO: Implement hospital system
+ }
+
+ initializeSchool() {
+ console.log(' โ School initialized');
+ // TODO: Implement school system
+ }
+
+ initializeBank() {
+ console.log(' โ Bank initialized');
+ // TODO: Implement bank system
+ }
+
+ initializeMuseum() {
+ console.log(' โ Museum initialized');
+ // TODO: Implement museum system
+ }
+
+ initializeTheater() {
+ console.log(' โ Theater initialized');
+ // TODO: Implement theater system
+ }
+
+ /**
+ * Update all systems (called every frame)
+ */
+ update(time, delta) {
+ const deltaSeconds = delta / 1000;
+
+ // Update systems that need per-frame updates
+ this.sleepSystem.update(deltaSeconds);
+ this.craftingSystem.update(deltaSeconds);
+
+ // Update zombie miners (if not paused by sleep)
+ if (!this.pauseZombieLoyaltyDecay) {
+ this.zombieMinerSystem.update(deltaSeconds);
+ }
+ }
+
+ /**
+ * Hourly update (called when game hour changes)
+ */
+ onHourChange(hour) {
+ // Update time-sensitive systems
+ this.bakerySystem.update();
+ this.townGrowthSystem.update();
+
+ // Check for automation collection reminders
+ if (hour % 4 === 0) { // Every 4 hours
+ this.checkAutomationReminders();
+ }
+ }
+
+ /**
+ * Check automation reminders
+ */
+ checkAutomationReminders() {
+ // Zombie miner automation
+ if (this.zombieMinerSystem.automationActive) {
+ const hours = this.zombieMinerSystem.getHoursSinceLastCollection();
+
+ if (hours >= 8) {
+ this.game.showNotification({
+ title: 'โ๏ธ Automation Ready',
+ text: `${hours.toFixed(0)}h of mining ready to collect!`,
+ icon: 'โ๏ธ'
+ });
+ }
+ }
+ }
+
+ /**
+ * Daily update (called at midnight)
+ */
+ onDayChange(day) {
+ console.log(`๐
Day ${day} - Running daily system updates...`);
+
+ // Town growth check
+ this.townGrowthSystem.update();
+
+ // Restock shops
+ this.bakerySystem.restockInventory();
+
+ // Birthday cake deliveries
+ this.bakerySystem.checkBirthdayCakeDeliveries();
+ }
+
+ /**
+ * Save all systems state
+ */
+ saveAllSystems() {
+ return {
+ version: '1.0',
+ timestamp: Date.now(),
+ systems: {
+ sleep: {
+ playerBed: this.sleepSystem.playerBed,
+ unlockedBeds: Object.entries(this.sleepSystem.bedTypes)
+ .filter(([_, bed]) => bed.unlocked)
+ .map(([key, _]) => key)
+ },
+ crafting: {
+ currentTable: this.craftingSystem.currentTable.id,
+ unlockedRecipes: this.craftingSystem.unlockedRecipes,
+ largeTableUnlocked: this.craftingSystem.tables.LARGE.unlocked
+ },
+ bakery: {
+ isUnlocked: this.bakerySystem.isUnlocked,
+ inventory: this.bakerySystem.inventory,
+ birthdayOrders: this.bakerySystem.birthdayCakeOrders
+ },
+ barber: {
+ isUnlocked: this.barberSystem.isUnlocked,
+ playerAppearance: this.barberSystem.playerAppearance,
+ savedLooks: this.barberSystem.savedLooks,
+ visitCount: this.barberSystem.visitCount
+ },
+ lawyer: {
+ isUnlocked: this.lawyerSystem.isUnlocked,
+ hasPrenup: this.lawyerSystem.hasPrenup,
+ divorceHistory: this.lawyerSystem.divorceHistory,
+ counselingInProgress: this.lawyerSystem.counselingInProgress
+ },
+ zombieMiners: {
+ miners: this.zombieMinerSystem.zombieMiners,
+ equipment: this.zombieMinerSystem.zombieEquipment,
+ lastCollectionTime: this.zombieMinerSystem.lastCollectionTime
+ },
+ townGrowth: {
+ population: this.townGrowthSystem.population,
+ populationSlots: this.townGrowthSystem.populationSlots,
+ villages: this.townGrowthSystem.villages,
+ services: this.townGrowthSystem.services
+ },
+ npcPrivacy: {
+ npcHomes: this.npcPrivacySystem.npcHomes,
+ visitHistory: this.npcPrivacySystem.visitHistory,
+ lastVisitTimes: this.npcPrivacySystem.lastVisitTimes
+ }
+ }
+ };
+ }
+
+ /**
+ * Load all systems state
+ */
+ loadAllSystems(saveData) {
+ if (!saveData || !saveData.systems) {
+ console.warn('No save data to load');
+ return false;
+ }
+
+ try {
+ const systems = saveData.systems;
+
+ // Load sleep system
+ if (systems.sleep) {
+ systems.sleep.unlockedBeds.forEach(bedKey => {
+ this.sleepSystem.bedTypes[bedKey].unlocked = true;
+ });
+ }
+
+ // Load crafting system
+ if (systems.crafting) {
+ this.craftingSystem.tables.LARGE.unlocked = systems.crafting.largeTableUnlocked;
+ this.craftingSystem.unlockedRecipes = systems.crafting.unlockedRecipes || [];
+ }
+
+ // Load bakery
+ if (systems.bakery) {
+ this.bakerySystem.isUnlocked = systems.bakery.isUnlocked;
+ this.bakerySystem.birthdayCakeOrders = systems.bakery.birthdayOrders || [];
+ }
+
+ // Load barber
+ if (systems.barber) {
+ this.barberSystem.isUnlocked = systems.barber.isUnlocked;
+ this.barberSystem.playerAppearance = systems.barber.playerAppearance;
+ this.barberSystem.savedLooks = systems.barber.savedLooks || [];
+ this.barberSystem.visitCount = systems.barber.visitCount || 0;
+ }
+
+ // Load lawyer
+ if (systems.lawyer) {
+ this.lawyerSystem.isUnlocked = systems.lawyer.isUnlocked;
+ this.lawyerSystem.hasPrenup = systems.lawyer.hasPrenup || false;
+ this.lawyerSystem.divorceHistory = systems.lawyer.divorceHistory || [];
+ this.lawyerSystem.counselingInProgress = systems.lawyer.counselingInProgress || false;
+ }
+
+ // Load zombie miners
+ if (systems.zombieMiners) {
+ this.zombieMinerSystem.zombieMiners = systems.zombieMiners.miners || [];
+ this.zombieMinerSystem.zombieEquipment = systems.zombieMiners.equipment || {};
+ this.zombieMinerSystem.lastCollectionTime = systems.zombieMiners.lastCollectionTime;
+ this.zombieMinerSystem.updateAutomationYield();
+ }
+
+ // Load town growth
+ if (systems.townGrowth) {
+ this.townGrowthSystem.population = systems.townGrowth.population;
+ this.townGrowthSystem.populationSlots = systems.townGrowth.populationSlots;
+ this.townGrowthSystem.villages = systems.townGrowth.villages || [];
+ this.townGrowthSystem.services = systems.townGrowth.services || {};
+ }
+
+ // Load NPC privacy
+ if (systems.npcPrivacy) {
+ this.npcPrivacySystem.npcHomes = systems.npcPrivacy.npcHomes || {};
+ this.npcPrivacySystem.visitHistory = systems.npcPrivacy.visitHistory || {};
+ this.npcPrivacySystem.lastVisitTimes = systems.npcPrivacy.lastVisitTimes || {};
+ }
+
+ console.log('โ
All systems loaded from save data');
+ return true;
+
+ } catch (error) {
+ console.error('โ Error loading systems:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Get all systems status (for debug/admin panel)
+ */
+ getAllSystemsStatus() {
+ return {
+ sleep: {
+ active: true,
+ currentBed: this.sleepSystem.playerBed.name,
+ isSleeping: this.sleepSystem.isSleeping
+ },
+ crafting: {
+ active: true,
+ currentTable: this.craftingSystem.currentTable.name,
+ isCrafting: this.craftingSystem.isCrafting,
+ queueLength: this.craftingSystem.craftingQueue.length
+ },
+ bakery: {
+ active: this.bakerySystem.isUnlocked,
+ isOpen: this.bakerySystem.isOpen,
+ stockLevel: Object.values(this.bakerySystem.inventory)
+ .reduce((sum, item) => sum + item.stock, 0)
+ },
+ barber: {
+ active: this.barberSystem.isUnlocked,
+ isOpen: this.barberSystem.isOpen,
+ currentStyle: this.barberSystem.playerAppearance.hairstyle
+ },
+ lawyer: {
+ active: this.lawyerSystem.isUnlocked,
+ counselingActive: this.lawyerSystem.counselingInProgress,
+ hasPrenup: this.lawyerSystem.hasPrenup
+ },
+ zombieMiners: {
+ active: this.zombieMinerSystem.automationActive,
+ minerCount: this.zombieMinerSystem.zombieMiners.length,
+ yieldPerHour: this.zombieMinerSystem.totalYieldPerHour
+ },
+ townGrowth: {
+ active: true,
+ population: this.townGrowthSystem.population,
+ maxPopulation: this.townGrowthSystem.maxPopulation,
+ status: this.townGrowthSystem.getTownStatus()
+ },
+ npcPrivacy: {
+ active: true,
+ homesGenerated: Object.keys(this.npcPrivacySystem.npcHomes).length
+ }
+ };
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MasterWeatherSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MasterWeatherSystem.js
new file mode 100644
index 000000000..a65fae97c
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MasterWeatherSystem.js
@@ -0,0 +1,487 @@
+/**
+ * MasterWeatherSystem.js
+ *
+ * Complete weather & environmental particle system for DolinaSmrti
+ * Phase 1/2/Demo CORE SYSTEM - All biomes, all weather types
+ *
+ * Features:
+ * - Wind (grass, trees, hair movement)
+ * - Rain (drops, puddles, wetness)
+ * - Snow (flakes, accumulation, footprints)
+ * - Fire (flames, smoke, heat distortion)
+ * - Water (ripples, displacement, caustics)
+ *
+ * Style 32 Dark-Chibi Noir compatible
+ * Performance optimized for 60 FPS
+ */
+
+import WindFoliageSystem from './WindFoliageSystem.js';
+
+export default class MasterWeatherSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Sub-systems
+ this.windSystem = null;
+ this.rainSystem = null;
+ this.snowSystem = null;
+ this.fireSystem = null;
+ this.waterSystem = null;
+
+ // Current weather state
+ this.currentWeather = 'clear'; // clear, rain, snow, storm
+ this.weatherIntensity = 0; // 0.0 - 1.0
+ this.transitionTime = 0;
+
+ // Biome-specific settings
+ this.biomeWeatherSettings = {
+ 'grassland': {
+ allowedWeather: ['clear', 'rain', 'storm'],
+ defaultWind: 1.0
+ },
+ 'desert': {
+ allowedWeather: ['clear', 'sandstorm'],
+ defaultWind: 1.5
+ },
+ 'snow': {
+ allowedWeather: ['clear', 'snow', 'blizzard'],
+ defaultWind: 1.8
+ },
+ 'swamp': {
+ allowedWeather: ['rain', 'fog'],
+ defaultWind: 0.3
+ },
+ 'mountains': {
+ allowedWeather: ['clear', 'snow', 'storm'],
+ defaultWind: 2.0
+ },
+ 'forest': {
+ allowedWeather: ['clear', 'rain'],
+ defaultWind: 0.8
+ },
+ 'volcanic': {
+ allowedWeather: ['clear', 'ash_rain'],
+ defaultWind: 1.2,
+ constantFire: true
+ }
+ };
+ }
+
+ /**
+ * Initialize all weather systems
+ */
+ init() {
+ console.log('๐ฆ๏ธ MasterWeatherSystem: Initializing...');
+
+ // Initialize Wind System (already implemented!)
+ this.windSystem = new WindFoliageSystem(this.scene);
+ this.windSystem.init();
+
+ // Initialize Rain System
+ this.initRainSystem();
+
+ // Initialize Snow System
+ this.initSnowSystem();
+
+ // Initialize Fire System
+ this.initFireSystem();
+
+ // Initialize Water Effects
+ this.initWaterSystem();
+
+ console.log('โ
MasterWeatherSystem: All systems ready!');
+ }
+
+ /**
+ * RAIN SYSTEM - Realistic rain with puddles
+ */
+ initRainSystem() {
+ // Create rain drop texture (Style 32 noir)
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0xffffff, 0.6);
+ graphics.fillRect(0, 0, 2, 8); // Thin vertical drop
+ graphics.generateTexture('rainDrop', 2, 8);
+ graphics.destroy();
+
+ // Rain particle emitter
+ this.rainEmitter = this.scene.add.particles(0, 0, 'rainDrop', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -20,
+ lifespan: 2000,
+ speedY: { min: 300, max: 500 },
+ speedX: { min: -20, max: 20 }, // Wind affects rain
+ scale: { start: 1, end: 0.8 },
+ alpha: { start: 0.6, end: 0.3 },
+ quantity: 2,
+ frequency: 20,
+ blendMode: 'ADD'
+ });
+
+ this.rainEmitter.stop(); // Start inactive
+
+ // Puddle system (appears after rain)
+ this.puddles = [];
+
+ console.log('๐ง Rain system initialized');
+ }
+
+ /**
+ * SNOW SYSTEM - Falling snow with accumulation
+ */
+ initSnowSystem() {
+ // Create snowflake texture (Style 32 - simple white dot with black outline)
+ const graphics = this.scene.add.graphics();
+ graphics.lineStyle(1, 0x000000, 1);
+ graphics.fillStyle(0xffffff, 0.9);
+ graphics.fillCircle(4, 4, 3);
+ graphics.strokeCircle(4, 4, 3);
+ graphics.generateTexture('snowflake', 8, 8);
+ graphics.destroy();
+
+ // Snow particle emitter
+ this.snowEmitter = this.scene.add.particles(0, 0, 'snowflake', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -20,
+ lifespan: 8000,
+ speedY: { min: 20, max: 50 },
+ speedX: { min: -30, max: 30 }, // Wind drift
+ scale: { min: 0.5, max: 1.2 },
+ alpha: { start: 0.9, end: 0.6 },
+ rotate: { min: 0, max: 360 },
+ frequency: 100,
+ quantity: 1
+ });
+
+ this.snowEmitter.stop();
+
+ // Snow accumulation layer (white overlay on ground)
+ this.snowAccumulation = this.scene.add.rectangle(
+ 0, 0,
+ this.scene.cameras.main.width,
+ this.scene.cameras.main.height,
+ 0xffffff, 0
+ ).setOrigin(0, 0).setDepth(-1);
+
+ console.log('โ๏ธ Snow system initialized');
+ }
+
+ /**
+ * FIRE SYSTEM - Flames, smoke, heat distortion
+ */
+ initFireSystem() {
+ // Create flame texture (orange-red gradient)
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0xff4500, 1);
+ graphics.fillCircle(8, 8, 6);
+ graphics.fillStyle(0xffaa00, 0.8);
+ graphics.fillCircle(8, 8, 4);
+ graphics.generateTexture('flame', 16, 16);
+ graphics.destroy();
+
+ // Create smoke texture (dark grey)
+ const smokeGraphics = this.scene.add.graphics();
+ smokeGraphics.fillStyle(0x333333, 0.4);
+ smokeGraphics.fillCircle(12, 12, 10);
+ smokeGraphics.generateTexture('smoke', 24, 24);
+ smokeGraphics.destroy();
+
+ // Fire sources (campfires, torches, etc.)
+ this.fireSources = [];
+
+ console.log('๐ฅ Fire system initialized');
+ }
+
+ /**
+ * Create fire source (campfire, torch, etc.)
+ * @param {number} x - X position
+ * @param {number} y - Y position
+ * @param {number} size - Fire size multiplier (0.5 - 2.0)
+ */
+ createFireSource(x, y, size = 1.0) {
+ // Flame particles
+ const flameEmitter = this.scene.add.particles(x, y, 'flame', {
+ lifespan: 800,
+ speed: { min: 20, max: 60 },
+ angle: { min: 250, max: 290 }, // Upward
+ scale: { start: size, end: 0.1 },
+ alpha: { start: 1, end: 0 },
+ blendMode: 'ADD',
+ frequency: 50,
+ tint: [0xff4500, 0xff6600, 0xffaa00]
+ });
+
+ // Smoke particles
+ const smokeEmitter = this.scene.add.particles(x, y - 10, 'smoke', {
+ lifespan: 2000,
+ speed: { min: 10, max: 30 },
+ angle: { min: 260, max: 280 },
+ scale: { start: size * 0.5, end: size * 2 },
+ alpha: { start: 0.5, end: 0 },
+ frequency: 200
+ });
+
+ this.fireSources.push({
+ x, y, size,
+ flameEmitter,
+ smokeEmitter
+ });
+
+ return { flameEmitter, smokeEmitter };
+ }
+
+ /**
+ * WATER SYSTEM - Ripples, displacement, caustics
+ * (Integration with planned WaterPhysicsSystem)
+ */
+ initWaterSystem() {
+ // Water ripple texture (Style 32 - concentric circles)
+ const graphics = this.scene.add.graphics();
+ graphics.lineStyle(2, 0x000000, 1);
+ graphics.strokeCircle(32, 32, 28);
+ graphics.lineStyle(1, 0x000000, 0.6);
+ graphics.strokeCircle(32, 32, 24);
+ graphics.generateTexture('waterRipple', 64, 64);
+ graphics.destroy();
+
+ // Ripple particle manager
+ this.rippleParticles = this.scene.add.particles(0, 0, 'waterRipple');
+
+ // Water zones (areas with water effects)
+ this.waterZones = [];
+
+ console.log('๐ Water system initialized');
+ }
+
+ /**
+ * Create water ripple effect
+ * @param {number} x - X position
+ * @param {number} y - Y position
+ * @param {number} size - Ripple size
+ */
+ createRipple(x, y, size = 1.0) {
+ const emitter = this.rippleParticles.createEmitter({
+ x, y,
+ lifespan: 1500,
+ speed: 0,
+ scale: { start: 0.1 * size, end: 2.0 * size },
+ alpha: { start: 0.7, end: 0 },
+ frequency: -1,
+ quantity: 1
+ });
+
+ emitter.explode(1);
+
+ this.scene.time.delayedCall(1500, () => {
+ emitter.stop();
+ this.rippleParticles.removeEmitter(emitter);
+ });
+ }
+
+ /**
+ * Set weather type
+ * @param {string} type - Weather type: 'clear', 'rain', 'snow', 'storm', 'blizzard'
+ * @param {number} intensity - Intensity 0.0 - 1.0
+ * @param {number} transitionTime - Transition duration in ms
+ */
+ setWeather(type, intensity = 0.5, transitionTime = 2000) {
+ console.log(`๐ฆ๏ธ Weather changing to: ${type} (intensity: ${intensity})`);
+
+ this.currentWeather = type;
+ this.weatherIntensity = intensity;
+ this.transitionTime = transitionTime;
+
+ // Stop all current weather
+ this.rainEmitter.stop();
+ this.snowEmitter.stop();
+
+ // Start new weather
+ switch (type) {
+ case 'rain':
+ this.startRain(intensity);
+ break;
+ case 'snow':
+ this.startSnow(intensity);
+ break;
+ case 'storm':
+ this.startStorm(intensity);
+ break;
+ case 'blizzard':
+ this.startBlizzard(intensity);
+ break;
+ case 'clear':
+ this.clearWeather();
+ break;
+ }
+ }
+
+ /**
+ * Start rain weather
+ */
+ startRain(intensity) {
+ this.rainEmitter.setFrequency(100 / intensity); // More intense = more frequent
+ this.rainEmitter.setQuantity(Math.ceil(intensity * 3));
+ this.rainEmitter.start();
+
+ // Adjust wind
+ this.windSystem.setWindStrength(0.8 + intensity * 0.5);
+
+ // Create puddles over time
+ this.scene.time.addEvent({
+ delay: 5000,
+ callback: () => this.createPuddle(),
+ loop: true
+ });
+ }
+
+ /**
+ * Start snow weather
+ */
+ startSnow(intensity) {
+ this.snowEmitter.setFrequency(200 / intensity);
+ this.snowEmitter.setQuantity(Math.ceil(intensity * 2));
+ this.snowEmitter.start();
+
+ // Adjust wind (snow drifts more)
+ this.windSystem.setWindStrength(intensity * 1.2);
+
+ // Gradual snow accumulation
+ this.scene.tweens.add({
+ targets: this.snowAccumulation,
+ alpha: intensity * 0.3,
+ duration: 10000,
+ ease: 'Linear'
+ });
+ }
+
+ /**
+ * Start storm (heavy rain + strong wind)
+ */
+ startStorm(intensity) {
+ this.startRain(intensity);
+ this.windSystem.setWindStrength(intensity * 2.0);
+
+ // Lightning flashes (TODO: add later)
+ }
+
+ /**
+ * Start blizzard (heavy snow + very strong wind)
+ */
+ startBlizzard(intensity) {
+ this.startSnow(intensity);
+ this.windSystem.setWindStrength(intensity * 2.5);
+
+ // Reduce visibility
+ this.scene.tweens.add({
+ targets: this.scene.cameras.main,
+ alpha: 0.7,
+ duration: 2000,
+ yoyo: true,
+ repeat: -1
+ });
+ }
+
+ /**
+ * Clear all weather
+ */
+ clearWeather() {
+ this.rainEmitter.stop();
+ this.snowEmitter.stop();
+ this.windSystem.setWindStrength(1.0);
+
+ // Fade out snow accumulation
+ this.scene.tweens.add({
+ targets: this.snowAccumulation,
+ alpha: 0,
+ duration: 5000
+ });
+ }
+
+ /**
+ * Create puddle after rain
+ */
+ createPuddle() {
+ const x = Phaser.Math.Between(0, this.scene.cameras.main.width);
+ const y = Phaser.Math.Between(0, this.scene.cameras.main.height);
+
+ const puddle = this.scene.add.ellipse(x, y, 60, 30, 0x4682B4, 0.3);
+ puddle.setDepth(-2);
+
+ this.puddles.push(puddle);
+
+ // Puddles evaporate over time
+ this.scene.tweens.add({
+ targets: puddle,
+ alpha: 0,
+ duration: 30000,
+ onComplete: () => {
+ puddle.destroy();
+ this.puddles = this.puddles.filter(p => p !== puddle);
+ }
+ });
+ }
+
+ /**
+ * Set biome-specific weather
+ * @param {string} biomeName - Biome name
+ */
+ setBiomeWeather(biomeName) {
+ const settings = this.biomeWeatherSettings[biomeName.toLowerCase()];
+
+ if (!settings) {
+ console.warn(`โ ๏ธ Unknown biome: ${biomeName}`);
+ return;
+ }
+
+ // Set default wind for biome
+ this.windSystem.setBiomeWind(biomeName);
+
+ // Volcanic biome always has fire
+ if (settings.constantFire) {
+ this.createFireSource(100, 200, 1.5);
+ this.createFireSource(300, 250, 1.2);
+ }
+
+ console.log(`๐ Biome weather set: ${biomeName}`);
+ }
+
+ /**
+ * Update all weather systems
+ * @param {number} delta - Time delta in ms
+ */
+ update(delta) {
+ // Update wind system
+ if (this.windSystem) {
+ this.windSystem.update(delta);
+ }
+
+ // Update rain/snow particle positions based on wind
+ // โ
NULL CHECK: Ensure emitter exists and has setSpeedX method
+ if (this.rainEmitter && this.rainEmitter.active && typeof this.rainEmitter.setSpeedX === 'function') {
+ const windInfluence = this.windSystem.wind.strength * 20;
+ this.rainEmitter.setSpeedX({ min: -windInfluence, max: windInfluence });
+ }
+
+ if (this.snowEmitter && this.snowEmitter.active && typeof this.snowEmitter.setSpeedX === 'function') {
+ const windInfluence = this.windSystem.wind.strength * 30;
+ this.snowEmitter.setSpeedX({ min: -windInfluence, max: windInfluence });
+ }
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ if (this.windSystem) this.windSystem.destroy();
+ if (this.rainEmitter) this.rainEmitter.destroy();
+ if (this.snowEmitter) this.snowEmitter.destroy();
+
+ this.fireSources.forEach(fire => {
+ fire.flameEmitter.destroy();
+ fire.smokeEmitter.destroy();
+ });
+
+ this.puddles.forEach(puddle => puddle.destroy());
+
+ if (this.rippleParticles) this.rippleParticles.destroy();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MesojedkeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MesojedkeSystem.js
new file mode 100644
index 000000000..8c3a70369
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MesojedkeSystem.js
@@ -0,0 +1,411 @@
+/**
+ * MesojedkeSystem.js
+ * ==================
+ * KRVAVA ลฝETEV - Carnivorous Plants System (Phase 39)
+ *
+ * Features:
+ * - Mario-style carnivorous plants
+ * - 5 growth stages
+ * - Meat feeding system
+ * - Auto-attack defense
+ * - Rare material harvest
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class MesojedkeSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Plant registry
+ this.plants = new Map();
+ this.nextPlantId = 1;
+
+ // Growth stages
+ this.growthStages = [
+ { stage: 1, name: 'Seed', size: 0.2, damage: 0, range: 0, icon: '๐ฑ' },
+ { stage: 2, name: 'Sprout', size: 0.4, damage: 0, range: 0, icon: '๐ฟ' },
+ { stage: 3, name: 'Young Plant', size: 0.6, damage: 10, range: 100, icon: '๐ชด' },
+ { stage: 4, name: 'Adult Plant', size: 0.8, damage: 25, range: 150, icon: '๐บ' },
+ { stage: 5, name: 'Giant Carnivorous Flower', size: 1.5, damage: 50, range: 200, icon: '๐ธ' }
+ ];
+
+ // Meat types for feeding
+ this.meatTypes = {
+ zombie_flesh: { nutrition: 10, growthBoost: 1.0, quality: 'common' },
+ mutant_meat: { nutrition: 25, growthBoost: 1.5, quality: 'uncommon' },
+ boss_meat: { nutrition: 50, growthBoost: 2.0, quality: 'rare' },
+ legendary_meat: { nutrition: 100, growthBoost: 3.0, quality: 'legendary' }
+ };
+
+ console.log('๐บ MesojedkeSystem initialized');
+ }
+
+ /**
+ * Plant mesojedka seed
+ */
+ plantSeed(x, y) {
+ const plantId = `mesojedka_${this.nextPlantId++}`;
+
+ const plant = {
+ id: plantId,
+ x: x,
+ y: y,
+ stage: 1,
+ growth: 0, // 0-100% per stage
+ hunger: 100, // Needs meat to grow
+ lastFed: Date.now(),
+ lastAttack: 0,
+ attackCooldown: 2000, // 2 seconds
+ kills: 0,
+ totalDamage: 0,
+ sprite: null
+ };
+
+ this.plants.set(plantId, plant);
+
+ // TODO: Create actual sprite
+ console.log(`๐ฑ Planted mesojedka at (${x}, ${y})`);
+
+ this.showNotification({
+ title: 'Mesojedka Planted!',
+ text: '๐ฑ Feed it meat to make it grow!',
+ icon: '๐บ'
+ });
+
+ return plantId;
+ }
+
+ /**
+ * Feed plant with meat
+ */
+ feedPlant(plantId, meatType) {
+ const plant = this.plants.get(plantId);
+ if (!plant) {
+ console.error(`Plant ${plantId} not found!`);
+ return false;
+ }
+
+ const meat = this.meatTypes[meatType];
+ if (!meat) {
+ console.error(`Meat type ${meatType} not found!`);
+ return false;
+ }
+
+ // Restore hunger
+ plant.hunger = Math.min(100, plant.hunger + meat.nutrition);
+ plant.lastFed = Date.now();
+
+ // Boost growth
+ const growthGain = 10 * meat.growthBoost;
+ plant.growth += growthGain;
+
+ console.log(`๐ Fed ${plantId} with ${meatType} (+${growthGain}% growth)`);
+
+ // Check for stage up
+ if (plant.growth >= 100) {
+ this.growPlant(plantId);
+ }
+
+ this.showNotification({
+ title: 'Plant Fed!',
+ text: `๐ ${meat.quality} meat! +${Math.floor(growthGain)}% growth!`,
+ icon: '๐บ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Grow plant to next stage
+ */
+ growPlant(plantId) {
+ const plant = this.plants.get(plantId);
+ if (!plant) return false;
+
+ if (plant.stage >= 5) {
+ console.log(`๐ธ ${plantId} already at max stage!`);
+ return false;
+ }
+
+ // Level up
+ plant.stage++;
+ plant.growth = 0;
+
+ const stageData = this.growthStages[plant.stage - 1];
+
+ console.log(`๐บ ${plantId} grew to stage ${plant.stage}: ${stageData.name}!`);
+
+ // Update sprite size
+ if (plant.sprite) {
+ plant.sprite.setScale(stageData.size);
+ }
+
+ this.showNotification({
+ title: 'Plant Evolved!',
+ text: `${stageData.icon} Stage ${plant.stage}: ${stageData.name}!`,
+ icon: 'โจ'
+ });
+
+ // Unlock attack at stage 3
+ if (plant.stage === 3) {
+ this.showNotification({
+ title: 'Defense Unlocked!',
+ text: 'โ๏ธ Mesojedka can now attack enemies!',
+ icon: '๐บ'
+ });
+ }
+
+ // Giant flower achievement
+ if (plant.stage === 5) {
+ this.showNotification({
+ title: 'GIANT CARNIVOROUS FLOWER!',
+ text: '๐ธ Maximum power achieved! 50 damage, 200 range!',
+ icon: '๐'
+ });
+ }
+
+ return true;
+ }
+
+ /**
+ * Auto-attack enemies
+ */
+ attackNearbyEnemies(plantId) {
+ const plant = this.plants.get(plantId);
+ if (!plant) return;
+
+ const stageData = this.growthStages[plant.stage - 1];
+
+ // Can't attack if too young
+ if (plant.stage < 3) return;
+
+ // Check cooldown
+ const now = Date.now();
+ if (now - plant.lastAttack < plant.attackCooldown) return;
+
+ // TODO: Get enemies within range
+ const enemies = this.getEnemiesInRange(plant.x, plant.y, stageData.range);
+
+ if (enemies.length === 0) return;
+
+ // Attack nearest enemy
+ const target = enemies[0];
+ const damage = stageData.damage;
+
+ console.log(`๐บ ${plantId} attacks! ${damage} damage!`);
+
+ // Deal damage
+ // TODO: Integrate with enemy system
+ plant.totalDamage += damage;
+
+ // Check for kill
+ // if (target.health <= damage) {
+ // plant.kills++;
+ // }
+
+ plant.lastAttack = now;
+
+ // Visual attack effect
+ this.playAttackAnimation(plant, target);
+ }
+
+ /**
+ * Get enemies in range
+ */
+ getEnemiesInRange(x, y, range) {
+ // TODO: Get actual enemies from enemy system
+ // For now, return empty array
+ return [];
+ }
+
+ /**
+ * Play attack animation
+ */
+ playAttackAnimation(plant, target) {
+ // TODO: Create bite animation
+ console.log(`๐ฅ CHOMP! Attack animation`);
+
+ // Screen shake for giant plants
+ if (plant.stage === 5) {
+ this.scene.cameras.main.shake(100, 0.005);
+ }
+ }
+
+ /**
+ * Harvest plant
+ */
+ harvestPlant(plantId) {
+ const plant = this.plants.get(plantId);
+ if (!plant) {
+ console.error(`Plant ${plantId} not found!`);
+ return null;
+ }
+
+ if (plant.stage < 4) {
+ this.showNotification({
+ title: 'Too Young!',
+ text: 'Plant must reach stage 4 to harvest!',
+ icon: '๐บ'
+ });
+ return null;
+ }
+
+ // Calculate harvest based on stage
+ const harvest = this.calculateHarvest(plant);
+
+ // Remove plant
+ this.plants.delete(plantId);
+
+ // TODO: Remove sprite
+
+ console.log(`โ๏ธ Harvested ${plantId}:`, harvest);
+
+ this.showNotification({
+ title: 'Harvest Complete!',
+ text: `Collected: ${harvest.petals} Petals, ${harvest.seeds} Seeds!`,
+ icon: '๐ธ'
+ });
+
+ return harvest;
+ }
+
+ /**
+ * Calculate harvest rewards
+ */
+ calculateHarvest(plant) {
+ const harvest = {
+ petals: 0,
+ seeds: 0,
+ special: []
+ };
+
+ // Base rewards by stage
+ switch (plant.stage) {
+ case 4:
+ harvest.petals = 5 + Math.floor(Math.random() * 5);
+ harvest.seeds = 1;
+ break;
+ case 5:
+ harvest.petals = 15 + Math.floor(Math.random() * 10);
+ harvest.seeds = 2 + Math.floor(Math.random() * 2);
+
+ // Chance for legendary drops
+ if (Math.random() < 0.1) { // 10%
+ harvest.special.push('legendary_nectar');
+ }
+ break;
+ }
+
+ // Bonus from kills
+ harvest.petals += plant.kills;
+
+ return harvest;
+ }
+
+ /**
+ * Update all plants
+ */
+ update(delta) {
+ const now = Date.now();
+
+ this.plants.forEach((plant, plantId) => {
+ // Decrease hunger over time
+ const hoursSinceLastFed = (now - plant.lastFed) / (1000 * 60 * 60);
+ if (hoursSinceLastFed >= 1) {
+ plant.hunger = Math.max(0, plant.hunger - 5);
+ plant.lastFed = now;
+ }
+
+ // Die if starved
+ if (plant.hunger <= 0) {
+ this.killPlant(plantId);
+ return;
+ }
+
+ // Auto-attack if enemies nearby
+ this.attackNearbyEnemies(plantId);
+
+ // Passive growth (very slow)
+ if (plant.hunger > 50) {
+ plant.growth += 0.001 * (delta / 1000); // 0.1% per second
+
+ if (plant.growth >= 100) {
+ this.growPlant(plantId);
+ }
+ }
+ });
+ }
+
+ /**
+ * Kill plant (starvation)
+ */
+ killPlant(plantId) {
+ const plant = this.plants.get(plantId);
+ if (!plant) return;
+
+ console.log(`๐ ${plantId} died from starvation!`);
+
+ this.plants.delete(plantId);
+
+ // TODO: Remove sprite
+
+ this.showNotification({
+ title: 'Plant Died!',
+ text: '๐ Your mesojedka starved! Feed them meat regularly!',
+ icon: 'โ ๏ธ'
+ });
+ }
+
+ /**
+ * Get plant info
+ */
+ getPlantInfo(plantId) {
+ const plant = this.plants.get(plantId);
+ if (!plant) return null;
+
+ const stageData = this.growthStages[plant.stage - 1];
+
+ return {
+ ...plant,
+ stageData: stageData,
+ canAttack: plant.stage >= 3,
+ canHarvest: plant.stage >= 4
+ };
+ }
+
+ /**
+ * Get all plants
+ */
+ getAllPlants() {
+ return Array.from(this.plants.values());
+ }
+
+ /**
+ * Get plant count by stage
+ */
+ getPlantCountByStage(stage) {
+ return Array.from(this.plants.values()).filter(p => p.stage === stage).length;
+ }
+
+ /**
+ * Get total defense damage
+ */
+ getTotalDefenseDamage() {
+ return Array.from(this.plants.values())
+ .reduce((total, plant) => total + plant.totalDamage, 0);
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MicroFarmExpansionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MicroFarmExpansionSystem.js
new file mode 100644
index 000000000..f338c2fa1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MicroFarmExpansionSystem.js
@@ -0,0 +1,498 @@
+/**
+ * MicroFarmExpansionSystem.js
+ * ============================
+ * KRVAVA ลฝETEV - Micro Farm & Expansion System (Phase 37)
+ *
+ * Features:
+ * - 8x8 starting micro farm
+ * - Expansion system (2x2 tiles at a time)
+ * - Land type mechanics (Grass, Forest, Rocky, Swamp)
+ * - Zombie clearing crews
+ * - Resource costs
+ * - Tutorial integration
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class MicroFarmExpansionSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Farm grid (tile-based)
+ this.farmSize = { width: 8, height: 8 }; // Starting size
+ this.maxSize = { width: 64, height: 64 }; // Maximum farm size
+ this.tileSize = 48; // Pixels per tile
+
+ // Expansion state
+ this.unlockedTiles = new Set();
+ this.expansionQueue = [];
+
+ // Land types
+ this.landTypes = new Map();
+
+ // Tutorial state
+ this.tutorialComplete = false;
+ this.tutorialStep = 0;
+
+ console.log('๐พ MicroFarmExpansionSystem initialized');
+
+ // Initialize starting farm
+ this.initializeStartingFarm();
+
+ // Register land types
+ this.registerLandTypes();
+ }
+
+ /**
+ * Initialize 8x8 starting micro farm
+ */
+ initializeStartingFarm() {
+ const centerX = Math.floor(this.maxSize.width / 2);
+ const centerY = Math.floor(this.maxSize.height / 2);
+
+ // Unlock center 8x8 area
+ for (let x = centerX - 4; x < centerX + 4; x++) {
+ for (let y = centerY - 4; y < centerY + 4; y++) {
+ const tileKey = `${x},${y}`;
+ this.unlockedTiles.add(tileKey);
+
+ // Mark as grass (cleared)
+ this.landTypes.set(tileKey, {
+ type: 'grass',
+ cleared: true,
+ fertility: 100
+ });
+ }
+ }
+
+ console.log(`โ
Micro farm initialized: 8x8 tiles (64 tiles total)`);
+ console.log(` Center: (${centerX}, ${centerY})`);
+
+ // Show starting area
+ this.visualizeFarm();
+
+ // Start tutorial
+ this.startTutorial();
+ }
+
+ /**
+ * Register land types
+ */
+ registerLandTypes() {
+ // Land type definitions
+ const types = {
+ grass: {
+ name: 'Grass',
+ icon: '๐ฟ',
+ clearingRequired: false,
+ clearingCost: { zlatniki: 0 },
+ fertility: 100
+ },
+ forest: {
+ name: 'Forest',
+ icon: '๐ฒ',
+ clearingRequired: true,
+ clearingCost: { zlatniki: 50, wood: 0 }, // Get wood from clearing
+ clearingTask: 'chop_trees',
+ clearingTime: 60, // seconds
+ fertility: 80
+ },
+ rocky: {
+ name: 'Rocky',
+ icon: 'โฐ๏ธ',
+ clearingRequired: true,
+ clearingCost: { zlatniki: 100, stone: 0 }, // Get stone from clearing
+ clearingTask: 'mine_rocks',
+ clearingTime: 90,
+ fertility: 50
+ },
+ swamp: {
+ name: 'Swamp',
+ icon: '๐ง',
+ clearingRequired: true,
+ clearingCost: { zlatniki: 150 },
+ clearingTask: 'drain_water',
+ clearingTime: 120,
+ fertility: 90 // High fertility after drainage!
+ }
+ };
+
+ console.log(`โ
Registered ${Object.keys(types).length} land types`);
+ }
+
+ /**
+ * Expand farm by 2x2 area
+ */
+ expandFarm(direction) {
+ const expansionSize = 2; // Expand by 2x2 tiles
+ const cost = this.calculateExpansionCost(direction);
+
+ // Check if player can afford
+ if (!this.canAffordExpansion(cost)) {
+ this.showNotification({
+ title: 'Cannot Expand',
+ text: `Need ${cost.zlatniki}ลฝ, ${cost.wood || 0} Wood, ${cost.stone || 0} Stone`,
+ icon: '๐ฐ'
+ });
+ return false;
+ }
+
+ // Get expansion tiles
+ const newTiles = this.getExpansionTiles(direction, expansionSize);
+
+ if (newTiles.length === 0) {
+ this.showNotification({
+ title: 'Cannot Expand',
+ text: 'Maximum farm size reached or invalid direction!',
+ icon: '๐ซ'
+ });
+ return false;
+ }
+
+ // Pay cost
+ this.payExpansionCost(cost);
+
+ // Add to expansion queue
+ this.queueExpansion(newTiles);
+
+ console.log(`๐พ Expanding farm ${direction}: +${newTiles.length} tiles`);
+
+ this.showNotification({
+ title: 'Expansion Started!',
+ text: `๐พ Expanding ${direction}! Send zombies to clear the land!`,
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ /**
+ * Get tiles for expansion
+ */
+ getExpansionTiles(direction, size) {
+ const tiles = [];
+ const bounds = this.getCurrentBounds();
+
+ switch (direction) {
+ case 'north':
+ for (let x = bounds.minX; x <= bounds.maxX; x++) {
+ for (let y = bounds.minY - size; y < bounds.minY; y++) {
+ tiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'south':
+ for (let x = bounds.minX; x <= bounds.maxX; x++) {
+ for (let y = bounds.maxY + 1; y <= bounds.maxY + size; y++) {
+ tiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'east':
+ for (let x = bounds.maxX + 1; x <= bounds.maxX + size; x++) {
+ for (let y = bounds.minY; y <= bounds.maxY; y++) {
+ tiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'west':
+ for (let x = bounds.minX - size; x < bounds.minX; x++) {
+ for (let y = bounds.minY; y <= bounds.maxY; y++) {
+ tiles.push({ x, y });
+ }
+ }
+ break;
+ }
+
+ // Filter out already unlocked tiles
+ return tiles.filter(tile => !this.unlockedTiles.has(`${tile.x},${tile.y}`));
+ }
+
+ /**
+ * Get current farm bounds
+ */
+ getCurrentBounds() {
+ let minX = Infinity, minY = Infinity;
+ let maxX = -Infinity, maxY = -Infinity;
+
+ this.unlockedTiles.forEach(tileKey => {
+ const [x, y] = tileKey.split(',').map(Number);
+ minX = Math.min(minX, x);
+ minY = Math.min(minY, y);
+ maxX = Math.max(maxX, x);
+ maxY = Math.max(maxY, y);
+ });
+
+ return { minX, minY, maxX, maxY };
+ }
+
+ /**
+ * Calculate expansion cost
+ */
+ calculateExpansionCost(direction) {
+ const baseZlatniki = 100;
+ const currentSize = this.unlockedTiles.size;
+
+ // Cost increases with farm size
+ const zlatniki = baseZlatniki + Math.floor(currentSize / 10) * 50;
+
+ return {
+ zlatniki: zlatniki,
+ wood: 10,
+ stone: 5
+ };
+ }
+
+ /**
+ * Check if player can afford expansion
+ */
+ canAffordExpansion(cost) {
+ // TODO: Check actual player resources
+ // For now, return true
+ return true;
+ }
+
+ /**
+ * Pay expansion cost
+ */
+ payExpansionCost(cost) {
+ console.log(`๐ฐ Paid: ${cost.zlatniki}ลฝ, ${cost.wood} Wood, ${cost.stone} Stone`);
+ // TODO: Actually deduct from player inventory
+ }
+
+ /**
+ * Queue expansion for zombie clearing
+ */
+ queueExpansion(tiles) {
+ tiles.forEach(tile => {
+ const tileKey = `${tile.x},${tile.y}`;
+
+ // Randomly assign land type
+ const landType = this.getRandomLandType();
+
+ this.landTypes.set(tileKey, {
+ type: landType,
+ cleared: landType === 'grass', // Grass is pre-cleared
+ fertility: this.getLandTypeFertility(landType),
+ clearingProgress: 0
+ });
+
+ this.expansionQueue.push({
+ tileKey: tileKey,
+ tile: tile,
+ landType: landType,
+ status: 'queued'
+ });
+ });
+
+ console.log(`๐ Queued ${tiles.length} tiles for expansion`);
+ }
+
+ /**
+ * Get random land type
+ */
+ getRandomLandType() {
+ const types = ['grass', 'forest', 'rocky', 'swamp'];
+ const weights = [40, 30, 20, 10]; // Grass more common
+
+ const total = weights.reduce((a, b) => a + b, 0);
+ let random = Math.random() * total;
+
+ for (let i = 0; i < types.length; i++) {
+ random -= weights[i];
+ if (random <= 0) return types[i];
+ }
+
+ return 'grass';
+ }
+
+ /**
+ * Get land type fertility
+ */
+ getLandTypeFertility(type) {
+ const fertility = {
+ grass: 100,
+ forest: 80,
+ rocky: 50,
+ swamp: 90
+ };
+ return fertility[type] || 100;
+ }
+
+ /**
+ * Send zombies to clear land
+ */
+ sendZombiesToClear(tileKey, zombieIds) {
+ const land = this.landTypes.get(tileKey);
+ if (!land) {
+ console.error(`Tile ${tileKey} not found!`);
+ return false;
+ }
+
+ if (land.cleared) {
+ console.log(`Tile ${tileKey} already cleared!`);
+ return false;
+ }
+
+ console.log(`๐ง Sending ${zombieIds.length} zombies to clear ${land.type} at ${tileKey}`);
+
+ // Set zombies to clearing task
+ // TODO: Integrate with ZombieSystem
+
+ // Start clearing progress
+ land.clearingProgress = 0;
+ land.clearingZombies = zombieIds;
+ land.clearingStartTime = Date.now();
+
+ return true;
+ }
+
+ /**
+ * Update clearing progress
+ */
+ updateClearingProgress(delta) {
+ this.landTypes.forEach((land, tileKey) => {
+ if (!land.cleared && land.clearingZombies) {
+ // Progress based on number of zombies
+ const zombieCount = land.clearingZombies.length;
+ const progressRate = zombieCount * 0.01; // 1% per zombie per second
+
+ land.clearingProgress += progressRate * (delta / 1000);
+
+ // Check if complete
+ if (land.clearingProgress >= 100) {
+ this.completeLandClearing(tileKey);
+ }
+ }
+ });
+ }
+
+ /**
+ * Complete land clearing
+ */
+ completeLandClearing(tileKey) {
+ const land = this.landTypes.get(tileKey);
+ if (!land) return;
+
+ land.cleared = true;
+ land.clearingProgress = 100;
+ land.clearingZombies = null;
+
+ // Add tile to unlocked
+ this.unlockedTiles.add(tileKey);
+
+ // Grant clearing rewards
+ this.grantClearingRewards(land.type);
+
+ console.log(`โ
Land cleared: ${tileKey} (${land.type})`);
+
+ this.showNotification({
+ title: 'Land Cleared!',
+ text: `โ
${this.getLandTypeIcon(land.type)} ${land.type} tile ready for farming!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Grant clearing rewards
+ */
+ grantClearingRewards(landType) {
+ const rewards = {
+ forest: { wood: 10 },
+ rocky: { stone: 15 },
+ swamp: { clay: 5 }
+ };
+
+ const reward = rewards[landType];
+ if (reward) {
+ console.log(`๐ Clearing rewards:`, reward);
+ // TODO: Add to inventory
+ }
+ }
+
+ /**
+ * Get land type icon
+ */
+ getLandTypeIcon(type) {
+ const icons = {
+ grass: '๐ฟ',
+ forest: '๐ฒ',
+ rocky: 'โฐ๏ธ',
+ swamp: '๐ง'
+ };
+ return icons[type] || 'โ';
+ }
+
+ /**
+ * Tutorial system
+ */
+ startTutorial() {
+ this.tutorialStep = 1;
+
+ this.showNotification({
+ title: 'Welcome to Your Micro Farm!',
+ text: '๐พ You start with an 8x8 plot. Expand by clearing surrounding land!',
+ icon: '๐'
+ });
+
+ // TODO: Show tutorial UI with steps
+ }
+
+ /**
+ * Visualize farm (console)
+ */
+ visualizeFarm() {
+ const bounds = this.getCurrentBounds();
+
+ console.log('๐พ FARM MAP:');
+ for (let y = bounds.minY; y <= bounds.maxY; y++) {
+ let row = '';
+ for (let x = bounds.minX; x <= bounds.maxX; x++) {
+ const tileKey = `${x},${y}`;
+ if (this.unlockedTiles.has(tileKey)) {
+ const land = this.landTypes.get(tileKey);
+ row += land.cleared ? 'โ
' : 'โณ';
+ } else {
+ row += '๐';
+ }
+ }
+ console.log(row);
+ }
+ }
+
+ /**
+ * Get farm info
+ */
+ getFarmInfo() {
+ return {
+ unlockedTiles: this.unlockedTiles.size,
+ bounds: this.getCurrentBounds(),
+ expansionQueue: this.expansionQueue.length,
+ tutorialComplete: this.tutorialComplete
+ };
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+
+ /**
+ * Update system
+ */
+ update(delta) {
+ // Update clearing progress
+ this.updateClearingProgress(delta);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MicroFarmSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MicroFarmSystem.js
new file mode 100644
index 000000000..8104debb1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MicroFarmSystem.js
@@ -0,0 +1,358 @@
+// ๐ฑ MICRO FARM SYSTEM - Phase 37
+// Zaฤetna 8x8 parcela z postopno ลกiritvijo
+
+class MicroFarmSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // MICRO FARM CONFIG
+ this.farmCenterX = 50; // Center of 100x100 map
+ this.farmCenterY = 50;
+ this.farmSize = 8; // 8x8 tiles (initial)
+
+ // EXPANSION SYSTEM
+ this.unlockedTiles = new Set(); // Tracks unlocked tiles
+ this.expansionCost = 50; // Gold per 2x2 expansion
+
+ // LAND TYPES
+ this.landTypes = {
+ GRASS: 'grass', // Free to use
+ FOREST: 'forest', // Needs clearing (trees)
+ ROCKY: 'rocky', // Needs mining (rocks)
+ SWAMP: 'swamp' // Needs drainage (water)
+ };
+
+ this.init();
+ }
+
+ init() {
+ console.log('๐ฑ MicroFarmSystem initialized');
+
+ // Unlock initial 8x8 farm
+ this.unlockInitialFarm();
+
+ // Create visual boundaries
+ this.createFarmBoundaries();
+
+ // Render locked tile overlay
+ this.renderLockedTileOverlay();
+
+ // Create expansion UI buttons
+ this.createExpansionUI();
+ }
+
+ createExpansionUI() {
+ // Create UI buttons for farm expansion
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) {
+ console.warn('โ ๏ธ UIScene not found - cannot create expansion UI');
+ return;
+ }
+
+ // Store reference
+ this.uiScene = uiScene;
+ this.expansionButtons = [];
+
+ const buttonSize = 40;
+ const buttonColor = 0x8B4513; // Brown
+ const buttonHoverColor = 0xD2691E;
+ const expandCost = this.expansionCost;
+
+ // Button positions relative to farm center
+ const tileSize = (this.scene.terrainSystem && this.scene.terrainSystem.tileSize) || 48;
+ const farmWorldX = this.farmCenterX * tileSize;
+ const farmWorldY = this.farmCenterY * tileSize;
+ const farmPixelSize = this.farmSize * tileSize;
+ const halfSize = farmPixelSize / 2;
+
+ const buttons = [
+ { dir: 'north', x: farmWorldX, y: farmWorldY - halfSize - 60, icon: 'โฌ๏ธ' },
+ { dir: 'south', x: farmWorldX, y: farmWorldY + halfSize + 60, icon: 'โฌ๏ธ' },
+ { dir: 'east', x: farmWorldX + halfSize + 60, y: farmWorldY, icon: 'โก๏ธ' },
+ { dir: 'west', x: farmWorldX - halfSize - 60, y: farmWorldY, icon: 'โฌ
๏ธ' }
+ ];
+
+ buttons.forEach(btn => {
+ // Button background
+ const bg = this.scene.add.rectangle(btn.x, btn.y, buttonSize, buttonSize, buttonColor);
+ bg.setStrokeStyle(2, 0xFFFFFF);
+ bg.setDepth(10);
+ bg.setInteractive({ useHandCursor: true });
+
+ // Button text
+ const text = this.scene.add.text(btn.x, btn.y, btn.icon, {
+ fontSize: '20px',
+ color: '#ffffff'
+ }).setOrigin(0.5);
+ text.setDepth(11);
+
+ // Cost label
+ const costLabel = this.scene.add.text(btn.x, btn.y + 30, `${expandCost}g`, {
+ fontSize: '12px',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ }).setOrigin(0.5);
+ costLabel.setDepth(11);
+
+ // Hover effects
+ bg.on('pointerover', () => {
+ bg.setFillStyle(buttonHoverColor);
+ bg.setScale(1.1);
+ });
+
+ bg.on('pointerout', () => {
+ bg.setFillStyle(buttonColor);
+ bg.setScale(1.0);
+ });
+
+ // Click handler
+ bg.on('pointerdown', () => {
+ this.tryExpandFarm(btn.dir);
+ });
+
+ this.expansionButtons.push({ bg, text, costLabel, dir: btn.dir });
+ });
+
+ console.log('โ
Expansion UI created!');
+ }
+
+ tryExpandFarm(direction) {
+ // Check if player has enough gold
+ const inv = this.scene.inventorySystem;
+ if (!inv || inv.gold < this.expansionCost) {
+ console.log(`โ Not enough gold! Need ${this.expansionCost}g`);
+
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.cameras.main.width / 2,
+ y: 100,
+ text: `Need ${this.expansionCost} gold!`,
+ color: '#ff0000'
+ });
+ }
+ return;
+ }
+
+ // Deduct gold
+ inv.gold -= this.expansionCost;
+
+ // Expand farm
+ this.expandFarm(direction);
+
+ // Success feedback
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.cameras.main.width / 2,
+ y: 100,
+ text: `โ
Farm expanded ${direction.toUpperCase()}!`,
+ color: '#00ff00'
+ });
+ }
+
+ console.log(`โ
Farm expanded ${direction}! (-${this.expansionCost}g)`);
+ }
+
+ unlockInitialFarm() {
+ // Unlock central 8x8 tiles
+ const halfSize = Math.floor(this.farmSize / 2);
+
+ for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
+ for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
+ const tileKey = `${x},${y}`;
+ this.unlockedTiles.add(tileKey);
+ }
+ }
+
+ console.log(`โ
Unlocked ${this.unlockedTiles.size} tiles (8x8 micro farm)`);
+ }
+
+ createFarmBoundaries() {
+ // Clear previous if exists
+ if (this.boundaryGraphics) {
+ this.boundaryGraphics.destroy();
+ }
+
+ const graphics = this.scene.add.graphics();
+ const tileSize = (this.scene.terrainSystem && this.scene.terrainSystem.tileSize) || 48;
+
+ // ๐๏ธ 1. LARGE MASTER PLATFORM (32x32)
+ const largeSize = 32;
+ const largeHalf = largeSize / 2;
+ const lx1 = (this.farmCenterX - largeHalf) * tileSize;
+ const ly1 = (this.farmCenterY - largeHalf) * tileSize;
+ const lWidth = largeSize * tileSize;
+ const lHeight = largeSize * tileSize;
+
+ // Draw Master Platform
+ graphics.fillStyle(0x0000FF, 0.05); // Very subtle blue for "Large Platform"
+ graphics.fillRect(lx1, ly1, lWidth, lHeight);
+ graphics.lineStyle(2, 0x0000FF, 0.3);
+ graphics.strokeRect(lx1, ly1, lWidth, lHeight);
+
+ // ๐๏ธ 2. SMALL MICRO FARM PLATFORM (8x8)
+ const halfSize = Math.floor(this.farmSize / 2);
+ const startX = (this.farmCenterX - halfSize) * tileSize;
+ const startY = (this.farmCenterY - halfSize) * tileSize;
+ const width = this.farmSize * tileSize;
+ const height = this.farmSize * tileSize;
+
+ // Draw Highlight
+ graphics.fillStyle(0xFFFFFF, 0.15); // 15% white for "Starter Platform"
+ graphics.fillRect(startX, startY, width, height);
+
+ // Draw Bold Boundary
+ graphics.lineStyle(4, 0x00FF00, 0.8); // High vis green
+ graphics.strokeRect(startX, startY, width, height);
+
+ // Corner markers
+ graphics.fillStyle(0xFFFF00, 1);
+ const markerSize = 12;
+ graphics.fillCircle(startX, startY, markerSize);
+ graphics.fillCircle(startX + width, startY, markerSize);
+ graphics.fillCircle(startX, startY + height, markerSize);
+ graphics.fillCircle(startX + width, startY + height, markerSize);
+
+ graphics.setDepth(50);
+ this.boundaryGraphics = graphics;
+
+ console.log(`๐ฐ Dual platforms rendered: Master (${largeSize}x${largeSize}) & Micro (${this.farmSize}x${this.farmSize})`);
+ }
+
+ renderLockedTileOverlay() {
+ // Render dark overlay on locked tiles
+ if (this.lockedOverlayGraphics) {
+ this.lockedOverlayGraphics.destroy();
+ }
+
+ this.lockedOverlayGraphics = this.scene.add.graphics();
+ this.lockedOverlayGraphics.setDepth(4); // Above ground, below boundaries
+
+ this.lockedOverlayGraphics.clear();
+
+ // Darken all tiles that are NOT unlocked
+ const halfSize = Math.floor(this.farmSize / 2);
+ const farmStartX = this.farmCenterX - halfSize;
+ const farmEndX = this.farmCenterX + halfSize;
+ const farmStartY = this.farmCenterY - halfSize;
+ const farmEndY = this.farmCenterY + halfSize;
+
+ // Render grid of locked tiles
+ const viewRange = 15; // Show some area around farm
+ for (let y = this.farmCenterY - viewRange; y < this.farmCenterY + viewRange; y++) {
+ for (let x = this.farmCenterX - viewRange; x < this.farmCenterX + viewRange; x++) {
+ // Skip if within farm boundaries
+ if (x >= farmStartX && x < farmEndX && y >= farmStartY && y < farmEndY) {
+ continue;
+ }
+
+ // Draw dark overlay (lighter)
+ const tileSize = (this.scene.terrainSystem && this.scene.terrainSystem.tileSize) || 48;
+ const worldX = x * tileSize;
+ const worldY = y * tileSize;
+ this.lockedOverlayGraphics.fillStyle(0x000000, 0.3); // 0.5 -> 0.3
+ this.lockedOverlayGraphics.fillRect(worldX, worldY, tileSize, tileSize);
+ }
+ }
+
+ console.log('๐ Locked tile overlay rendered!');
+ }
+
+ isTileUnlocked(tileX, tileY) {
+ const tileKey = `${tileX},${tileY}`;
+ return this.unlockedTiles.has(tileKey);
+ }
+
+ canExpand(direction) {
+ // Check if expansion in direction is possible
+ // direction: 'north', 'south', 'east', 'west'
+ // TODO: Implement expansion logic
+ return true;
+ }
+
+ expandFarm(direction) {
+ // Unlock 2x2 tiles in specified direction
+ const halfSize = Math.floor(this.farmSize / 2);
+ const expansionSize = 2; // Unlock 2x2 tiles at a time
+
+ let newTiles = [];
+
+ switch (direction) {
+ case 'north':
+ for (let y = this.farmCenterY - halfSize - expansionSize; y < this.farmCenterY - halfSize; y++) {
+ for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
+ newTiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'south':
+ for (let y = this.farmCenterY + halfSize; y < this.farmCenterY + halfSize + expansionSize; y++) {
+ for (let x = this.farmCenterX - halfSize; x < this.farmCenterX + halfSize; x++) {
+ newTiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'east':
+ for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
+ for (let x = this.farmCenterX + halfSize; x < this.farmCenterX + halfSize + expansionSize; x++) {
+ newTiles.push({ x, y });
+ }
+ }
+ break;
+
+ case 'west':
+ for (let y = this.farmCenterY - halfSize; y < this.farmCenterY + halfSize; y++) {
+ for (let x = this.farmCenterX - halfSize - expansionSize; x < this.farmCenterX - halfSize; x++) {
+ newTiles.push({ x, y });
+ }
+ }
+ break;
+ }
+
+ // Unlock the tiles
+ newTiles.forEach(tile => {
+ const tileKey = `${tile.x},${tile.y}`;
+ this.unlockedTiles.add(tileKey);
+ });
+
+ // Update farm size
+ if (direction === 'north' || direction === 'south') {
+ this.farmSize += expansionSize;
+ } else {
+ this.farmSize += expansionSize;
+ }
+
+ // Update visuals
+ this.createFarmBoundaries(); // Redraw boundaries
+ this.renderLockedTileOverlay(); // Update overlay
+
+ console.log(`๐ Expanded farm ${direction}! +${newTiles.length} tiles. Total: ${this.unlockedTiles.size}`);
+ }
+
+ getLandType(tileX, tileY) {
+ // Determine land type based on tile position
+ // TODO: Use terrain system data
+
+ // For now, return grass for unlocked tiles
+ if (this.isTileUnlocked(tileX, tileY)) {
+ return this.landTypes.GRASS;
+ }
+
+ // Surrounding areas have different types
+ const distFromCenter = Math.sqrt(
+ Math.pow(tileX - this.farmCenterX, 2) +
+ Math.pow(tileY - this.farmCenterY, 2)
+ );
+
+ if (distFromCenter < 10) return this.landTypes.GRASS;
+ if (distFromCenter < 15) return this.landTypes.FOREST;
+ if (distFromCenter < 20) return this.landTypes.ROCKY;
+ return this.landTypes.SWAMP;
+ }
+
+ destroy() {
+ // Cleanup
+ this.unlockedTiles.clear();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MiningDungeonsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MiningDungeonsSystem.js
new file mode 100644
index 000000000..f4a518a59
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MiningDungeonsSystem.js
@@ -0,0 +1,407 @@
+/**
+ * MINING & DUNGEONS SYSTEM
+ * Underground cave generation, mining, and dungeon exploration
+ */
+class MiningDungeonsSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Caves & dungeons
+ this.caves = new Map();
+ this.currentCave = null;
+
+ // Mining
+ this.oreVeins = new Map();
+ this.minedOres = [];
+
+ // Elevators
+ this.elevators = new Map();
+
+ // Enemies
+ this.caveEnemies = [];
+
+ // Bosses
+ this.dungeonBosses = new Map();
+
+ // Settings
+ this.settings = {
+ maxDepth: 50,
+ roomSize: { min: 5, max: 15 },
+ tunnelWidth: 2,
+ oreChance: 0.1
+ };
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Mining & Dungeons System initialized');
+ }
+
+ init() {
+ this.defineOreTypes();
+ this.defineEnemyTypes();
+ console.log('โ๏ธ Mining & Dungeons ready');
+ }
+
+ // ========== ORE TYPES ==========
+
+ defineOreTypes() {
+ this.oreTypes = {
+ // Depth 0-10
+ copper: { depth: [0, 10], value: 5, rarity: 0.4 },
+ tin: { depth: [0, 10], value: 5, rarity: 0.4 },
+
+ // Depth 10-20
+ iron: { depth: [10, 20], value: 15, rarity: 0.3 },
+ coal: { depth: [10, 20], value: 10, rarity: 0.35 },
+
+ // Depth 20-30
+ gold: { depth: [20, 30], value: 50, rarity: 0.15 },
+ silver: { depth: [20, 30], value: 30, rarity: 0.2 },
+
+ // Depth 30+
+ diamond: { depth: [30, 50], value: 200, rarity: 0.05 },
+ mythril: { depth: [30, 50], value: 500, rarity: 0.02 }
+ };
+ }
+
+ // ========== ENEMY TYPES ==========
+
+ defineEnemyTypes() {
+ this.enemyTypes = {
+ bat: {
+ name: 'Cave Bat',
+ hp: 20,
+ damage: 5,
+ speed: 1.5,
+ xp: 10,
+ loot: ['bat_wing', 'guano']
+ },
+ spider: {
+ name: 'Giant Spider',
+ hp: 50,
+ damage: 15,
+ speed: 1.0,
+ xp: 25,
+ loot: ['spider_silk', 'venom']
+ },
+ mole: {
+ name: 'Mutant Mole',
+ hp: 80,
+ damage: 20,
+ speed: 0.8,
+ xp: 40,
+ loot: ['mole_claw', 'earth_essence']
+ },
+ golem: {
+ name: 'Stone Golem',
+ hp: 200,
+ damage: 40,
+ speed: 0.5,
+ xp: 100,
+ loot: ['stone_core', 'ancient_rune']
+ }
+ };
+ }
+
+ // ========== CAVE GENERATION ==========
+
+ generateCave(depth, seed) {
+ const cave = {
+ id: `cave_${depth}_${seed}`,
+ depth,
+ seed,
+ rooms: [],
+ tunnels: [],
+ oreVeins: [],
+ enemies: [],
+ boss: null,
+ explored: false
+ };
+
+ // Generate rooms
+ const numRooms = 5 + Math.floor(depth / 10);
+ for (let i = 0; i < numRooms; i++) {
+ cave.rooms.push(this.generateRoom(depth));
+ }
+
+ // Connect rooms with tunnels
+ cave.tunnels = this.connectRooms(cave.rooms);
+
+ // Place ore veins
+ cave.oreVeins = this.placeOres(depth, cave.rooms);
+
+ // Spawn enemies
+ cave.enemies = this.spawnEnemies(depth, cave.rooms);
+
+ // Boss every 10 levels
+ if (depth % 10 === 0) {
+ cave.boss = this.createBoss(depth);
+ }
+
+ this.caves.set(cave.id, cave);
+ return cave;
+ }
+
+ generateRoom(depth) {
+ const size = this.settings.roomSize;
+ const width = size.min + Math.floor(Math.random() * (size.max - size.min));
+ const height = size.min + Math.floor(Math.random() * (size.max - size.min));
+
+ return {
+ x: Math.floor(Math.random() * 100),
+ y: Math.floor(Math.random() * 100),
+ width,
+ height,
+ type: this.getRoomType(depth)
+ };
+ }
+
+ getRoomType(depth) {
+ const types = ['cave', 'crystal_cavern', 'lava_chamber', 'ice_cave', 'mushroom_grove'];
+ const index = Math.floor(depth / 10) % types.length;
+ return types[index];
+ }
+
+ connectRooms(rooms) {
+ const tunnels = [];
+
+ for (let i = 0; i < rooms.length - 1; i++) {
+ const room1 = rooms[i];
+ const room2 = rooms[i + 1];
+
+ tunnels.push({
+ from: { x: room1.x + room1.width / 2, y: room1.y + room1.height / 2 },
+ to: { x: room2.x + room2.width / 2, y: room2.y + room2.height / 2 },
+ width: this.settings.tunnelWidth
+ });
+ }
+
+ return tunnels;
+ }
+
+ // ========== ORE PLACEMENT ==========
+
+ placeOres(depth, rooms) {
+ const oreVeins = [];
+
+ for (const room of rooms) {
+ const numVeins = 2 + Math.floor(Math.random() * 5);
+
+ for (let i = 0; i < numVeins; i++) {
+ const ore = this.selectOre(depth);
+ if (!ore) continue;
+
+ oreVeins.push({
+ type: ore,
+ x: room.x + Math.floor(Math.random() * room.width),
+ y: room.y + Math.floor(Math.random() * room.height),
+ amount: 5 + Math.floor(Math.random() * 10),
+ mined: false
+ });
+ }
+ }
+
+ return oreVeins;
+ }
+
+ selectOre(depth) {
+ const validOres = Object.entries(this.oreTypes)
+ .filter(([_, ore]) => depth >= ore.depth[0] && depth <= ore.depth[1]);
+
+ if (validOres.length === 0) return null;
+
+ // Weighted random selection
+ const totalRarity = validOres.reduce((sum, [_, ore]) => sum + ore.rarity, 0);
+ let roll = Math.random() * totalRarity;
+
+ for (const [oreName, ore] of validOres) {
+ roll -= ore.rarity;
+ if (roll <= 0) return oreName;
+ }
+
+ return validOres[0][0];
+ }
+
+ // ========== ENEMY SPAWNING ==========
+
+ spawnEnemies(depth, rooms) {
+ const enemies = [];
+ const enemiesPerRoom = 1 + Math.floor(depth / 5);
+
+ for (const room of rooms) {
+ for (let i = 0; i < enemiesPerRoom; i++) {
+ const enemyType = this.selectEnemy(depth);
+ const enemyData = this.enemyTypes[enemyType];
+
+ enemies.push({
+ type: enemyType,
+ name: enemyData.name,
+ hp: enemyData.hp * (1 + depth * 0.1),
+ maxHp: enemyData.hp * (1 + depth * 0.1),
+ damage: enemyData.damage * (1 + depth * 0.1),
+ speed: enemyData.speed,
+ x: room.x + Math.floor(Math.random() * room.width),
+ y: room.y + Math.floor(Math.random() * room.height),
+ xp: enemyData.xp,
+ loot: enemyData.loot
+ });
+ }
+ }
+
+ return enemies;
+ }
+
+ selectEnemy(depth) {
+ if (depth < 10) return 'bat';
+ if (depth < 20) return Math.random() < 0.5 ? 'bat' : 'spider';
+ if (depth < 30) return Math.random() < 0.3 ? 'spider' : 'mole';
+ return Math.random() < 0.5 ? 'mole' : 'golem';
+ }
+
+ // ========== BOSS CREATION ==========
+
+ createBoss(depth) {
+ const bossTypes = {
+ 10: { name: 'Crystal Guardian', hp: 500, damage: 50 },
+ 20: { name: 'Lava Titan', hp: 1000, damage: 80 },
+ 30: { name: 'Ice Dragon', hp: 2000, damage: 120 },
+ 40: { name: 'Shadow Demon', hp: 3500, damage: 150 },
+ 50: { name: 'Ancient Wyrm', hp: 5000, damage: 200 }
+ };
+
+ const bossData = bossTypes[depth] || bossTypes[50];
+
+ return {
+ name: bossData.name,
+ hp: bossData.hp,
+ maxHp: bossData.hp,
+ damage: bossData.damage,
+ phase: 1,
+ maxPhases: 3,
+ defeated: false,
+ loot: this.generateBossLoot(depth)
+ };
+ }
+
+ generateBossLoot(depth) {
+ return [
+ { item: 'legendary_sword', chance: 0.1 },
+ { item: 'boss_trophy', chance: 1.0 },
+ { item: 'rare_gem', chance: 0.5 },
+ { item: 'gold', amount: depth * 100, chance: 1.0 }
+ ];
+ }
+
+ // ========== MINING ==========
+
+ mineOre(oreVeinId) {
+ const vein = this.oreVeins.get(oreVeinId);
+ if (!vein || vein.mined) return null;
+
+ // Mine ore
+ vein.amount--;
+
+ if (vein.amount <= 0) {
+ vein.mined = true;
+ }
+
+ // Add to inventory
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(vein.type, 1);
+ }
+
+ // Track mined ores
+ this.minedOres.push({
+ type: vein.type,
+ time: Date.now()
+ });
+
+ console.log(`โ๏ธ Mined ${vein.type}!`);
+ return vein.type;
+ }
+
+ // ========== ELEVATOR ==========
+
+ buildElevator(x, y) {
+ const elevator = {
+ id: `elevator_${x}_${y}`,
+ x, y,
+ currentDepth: 0,
+ maxDepth: 0,
+ active: true
+ };
+
+ this.elevators.set(elevator.id, elevator);
+ console.log(`๐ Built elevator at (${x}, ${y})`);
+ return elevator;
+ }
+
+ useElevator(elevatorId, targetDepth) {
+ const elevator = this.elevators.get(elevatorId);
+ if (!elevator) return false;
+
+ if (targetDepth > elevator.maxDepth) {
+ console.log('โ Depth not unlocked yet');
+ return false;
+ }
+
+ // Generate cave if not exists
+ let cave = Array.from(this.caves.values()).find(c => c.depth === targetDepth);
+ if (!cave) {
+ cave = this.generateCave(targetDepth, Date.now());
+ }
+
+ this.currentCave = cave;
+ elevator.currentDepth = targetDepth;
+
+ console.log(`๐ Descended to depth ${targetDepth}`);
+ return true;
+ }
+
+ // ========== MINE CART ==========
+
+ placeMinecart(x, y) {
+ console.log(`๐ค๏ธ Placed minecart at (${x}, ${y})`);
+ // Minecart for fast transport through tunnels
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ // Update cave enemies, etc.
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ caves: Array.from(this.caves.values()).map(c => ({
+ id: c.id,
+ depth: c.depth,
+ explored: c.explored
+ })),
+ minedOres: this.minedOres.length
+ };
+
+ localStorage.setItem('novafarma_mining', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_mining');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ console.log('โ
Mining progress loaded');
+ } catch (error) {
+ console.error('Failed to load mining progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('โ๏ธ Mining & Dungeons System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MiningSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MiningSystem.js
new file mode 100644
index 000000000..3ba25e6d1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MiningSystem.js
@@ -0,0 +1,475 @@
+/**
+ * MiningSystem.js
+ * ===============
+ * KRVAVA ลฝETEV - Complete Mining System (Phase 16)
+ *
+ * Features:
+ * - 5 mine types (Iron, Crystal, Coal, Radioactive, Atlantean)
+ * - 50-100 levels per mine
+ * - Ore spawning & collection
+ * - Boss encounters
+ * - Hazard systems (radiation, oxygen, explosions)
+ * - Mining tools (6 tiers)
+ * - Elevator/transport system
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class MiningSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Mine registry
+ this.mines = new Map();
+ this.currentMine = null;
+ this.currentLevel = 0;
+
+ // Player state
+ this.playerDepth = 0;
+ this.oxygenLevel = 100;
+ this.radiationLevel = 0;
+
+ // Ore nodes
+ this.oreNodes = [];
+
+ // Mining tools
+ this.currentTool = null;
+ this.toolTier = 1;
+
+ console.log('โ๏ธ MiningSystem initialized');
+
+ // Register all mines
+ this.registerMines();
+
+ // Register mining tools
+ this.registerTools();
+ }
+
+ /**
+ * Register all mine types
+ */
+ registerMines() {
+ // Iron Mine (50 levels)
+ this.mines.set('iron_mine', {
+ id: 'iron_mine',
+ name: 'Iron Mine',
+ icon: 'โ๏ธ',
+ maxLevels: 50,
+ ores: ['iron_ore', 'copper_ore', 'tin_ore'],
+ hazards: ['cave_in', 'poison_gas'],
+ boss: {
+ level: 50,
+ name: 'Stone Golem',
+ hp: 5000,
+ damage: 100,
+ rewards: { iron_ore: 100, golem_core: 1 }
+ },
+ elevatorSpeed: 2, // seconds per level
+ lightingRequired: true
+ });
+
+ // Crystal Cavern (75 levels)
+ this.mines.set('crystal_cavern', {
+ id: 'crystal_cavern',
+ name: 'Crystal Cavern',
+ icon: '๐',
+ maxLevels: 75,
+ ores: ['crystal', 'amethyst', 'emerald', 'ruby', 'sapphire'],
+ hazards: ['crystal_shards', 'magic_surge'],
+ boss: {
+ level: 75,
+ name: 'Crystal Guardian',
+ hp: 8000,
+ damage: 150,
+ rewards: { legendary_crystal: 1, gems: 50 }
+ },
+ elevatorSpeed: 3,
+ lightingRequired: true,
+ gemVeins: true
+ });
+
+ // Coal Mine (40 levels)
+ this.mines.set('coal_mine', {
+ id: 'coal_mine',
+ name: 'Coal Mine',
+ icon: '๐ชจ',
+ maxLevels: 40,
+ ores: ['coal', 'fossil', 'amber'],
+ hazards: ['methane_explosion', 'cave_in', 'undead_miners'],
+ boss: {
+ level: 40,
+ name: 'Undead Miner King',
+ hp: 4000,
+ damage: 80,
+ rewards: { ancient_pickaxe: 1, coal: 100 }
+ },
+ elevatorSpeed: 1.5,
+ lightingRequired: true,
+ explosionRisk: true
+ });
+
+ // Radioactive Mine (100 levels)
+ this.mines.set('radioactive_mine', {
+ id: 'radioactive_mine',
+ name: 'Radioactive Mine',
+ icon: 'โข๏ธ',
+ maxLevels: 100,
+ ores: ['uranium', 'plutonium', 'reactor_fragments'],
+ hazards: ['radiation', 'meltdown', 'mutants'],
+ boss: {
+ level: 100,
+ name: 'Radiation Colossus',
+ hp: 15000,
+ damage: 300,
+ rewards: { reactor_core: 1, uranium: 200 }
+ },
+ elevatorSpeed: 4,
+ lightingRequired: false, // Glows
+ radiationDamage: true,
+ hazmatRequired: true
+ });
+
+ // Atlantean Deep Mine (50 levels)
+ this.mines.set('atlantean_mine', {
+ id: 'atlantean_mine',
+ name: 'Atlantean Deep Mine',
+ icon: '๐ฑ',
+ maxLevels: 50,
+ ores: ['orichalcum', 'atlantean_steel', 'sea_crystal'],
+ hazards: ['water_pressure', 'sea_monsters', 'oxygen_loss'],
+ boss: {
+ level: 50,
+ name: 'Kraken',
+ hp: 10000,
+ damage: 200,
+ rewards: { kraken_tentacle: 1, orichalcum: 50 }
+ },
+ elevatorSpeed: 3,
+ lightingRequired: true,
+ underwater: true,
+ oxygenSystem: true
+ });
+
+ console.log(`โ
Registered ${this.mines.size} mines`);
+ }
+
+ /**
+ * Register mining tools (6 tiers)
+ */
+ registerTools() {
+ this.tools = [
+ { tier: 1, name: 'Wooden Pickaxe', speed: 1.0, durability: 100 },
+ { tier: 2, name: 'Stone Pickaxe', speed: 1.5, durability: 200 },
+ { tier: 3, name: 'Iron Pickaxe', speed: 2.0, durability: 400 },
+ { tier: 4, name: 'Steel Pickaxe', speed: 3.0, durability: 800 },
+ { tier: 5, name: 'Diamond Drill', speed: 5.0, durability: 2000, autoCollect: true },
+ { tier: 6, name: 'Orichalcum Drill', speed: 10.0, durability: 5000, autoCollect: true }
+ ];
+
+ console.log(`โ
Registered ${this.tools.length} mining tools`);
+ }
+
+ /**
+ * Enter mine
+ */
+ enterMine(mineId, startLevel = 1) {
+ const mine = this.mines.get(mineId);
+ if (!mine) {
+ console.error(`Mine ${mineId} not found!`);
+ return false;
+ }
+
+ this.currentMine = mine;
+ this.currentLevel = startLevel;
+ this.playerDepth = 0;
+
+ // Reset hazard states
+ this.oxygenLevel = 100;
+ this.radiationLevel = 0;
+
+ console.log(`โ๏ธ Entered ${mine.name} - Level ${startLevel}`);
+
+ // Initialize level
+ this.initializeLevel(startLevel);
+
+ this.showNotification({
+ title: `Entered ${mine.name}`,
+ text: `${mine.icon} Level ${startLevel}/${mine.maxLevels}`,
+ icon: 'โ๏ธ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Initialize mine level
+ */
+ initializeLevel(level) {
+ if (!this.currentMine) return;
+
+ // Clear previous ore nodes
+ this.oreNodes = [];
+
+ // Spawn ore nodes based on level
+ const oreCount = 10 + level * 2; // More ores deeper
+ for (let i = 0; i < oreCount; i++) {
+ this.spawnOreNode(level);
+ }
+
+ // Check for boss level
+ if (level === this.currentMine.boss.level) {
+ this.spawnBoss();
+ }
+
+ console.log(`โ
Level ${level} initialized with ${this.oreNodes.length} ore nodes`);
+ }
+
+ /**
+ * Spawn ore node
+ */
+ spawnOreNode(level) {
+ const mine = this.currentMine;
+
+ // Random ore type
+ const oreType = Phaser.Utils.Array.GetRandom(mine.ores);
+
+ // Random position
+ const x = Phaser.Math.Between(100, 700);
+ const y = Phaser.Math.Between(100, 500);
+
+ const node = {
+ id: `ore_${this.oreNodes.length}`,
+ type: oreType,
+ x: x,
+ y: y,
+ hp: 50 + level * 5,
+ maxHp: 50 + level * 5,
+ quantity: Math.floor(1 + level * 0.5),
+ respawnTime: 60000, // 60 seconds
+ mined: false
+ };
+
+ this.oreNodes.push(node);
+ }
+
+ /**
+ * Mine ore node
+ */
+ mineOre(nodeId) {
+ const node = this.oreNodes.find(n => n.id === nodeId);
+ if (!node || node.mined) return null;
+
+ const tool = this.tools[this.toolTier - 1];
+ const damage = 10 * tool.speed;
+
+ node.hp -= damage;
+
+ if (node.hp <= 0) {
+ // Node depleted
+ node.mined = true;
+
+ // Grant ore
+ const ore = {
+ type: node.type,
+ quantity: node.quantity
+ };
+
+ console.log(`โ๏ธ Mined ${ore.quantity}x ${ore.type}`);
+
+ // Auto-collect if tool has feature
+ if (tool.autoCollect) {
+ this.collectOre(ore);
+ }
+
+ // Schedule respawn
+ setTimeout(() => {
+ node.mined = false;
+ node.hp = node.maxHp;
+ }, node.respawnTime);
+
+ return ore;
+ }
+
+ return null;
+ }
+
+ /**
+ * Collect ore
+ */
+ collectOre(ore) {
+ console.log(`๐ฆ Collected ${ore.quantity}x ${ore.type}`);
+ // TODO: Add to inventory
+ }
+
+ /**
+ * Use elevator
+ */
+ useElevator(targetLevel) {
+ if (!this.currentMine) return false;
+
+ if (targetLevel < 1 || targetLevel > this.currentMine.maxLevels) {
+ console.error(`Invalid level: ${targetLevel}`);
+ return false;
+ }
+
+ const travelTime = Math.abs(targetLevel - this.currentLevel) * this.currentMine.elevatorSpeed;
+
+ console.log(`๐ Traveling to level ${targetLevel} (${travelTime}s)`);
+
+ setTimeout(() => {
+ this.currentLevel = targetLevel;
+ this.initializeLevel(targetLevel);
+
+ this.showNotification({
+ title: 'Elevator Arrived',
+ text: `Level ${targetLevel}/${this.currentMine.maxLevels}`,
+ icon: '๐'
+ });
+ }, travelTime * 1000);
+
+ return true;
+ }
+
+ /**
+ * Spawn boss
+ */
+ spawnBoss() {
+ const boss = this.currentMine.boss;
+
+ console.log(`๐น ${boss.name} has appeared!`);
+
+ this.showNotification({
+ title: 'BOSS ENCOUNTER!',
+ text: `${boss.name} - ${boss.hp} HP`,
+ icon: '๐น'
+ });
+
+ // TODO: Integrate with boss fight system
+ }
+
+ /**
+ * Update hazards
+ */
+ updateHazards(delta) {
+ if (!this.currentMine) return;
+
+ const mine = this.currentMine;
+
+ // Radiation damage
+ if (mine.radiationDamage) {
+ this.radiationLevel += 0.1 * (delta / 1000);
+
+ if (this.radiationLevel > 100) {
+ console.log('โข๏ธ Radiation poisoning!');
+ // TODO: Apply damage
+ }
+ }
+
+ // Oxygen depletion
+ if (mine.oxygenSystem) {
+ this.oxygenLevel -= 0.5 * (delta / 1000);
+
+ if (this.oxygenLevel <= 0) {
+ console.log('๐จ Out of oxygen!');
+ // TODO: Force ascent
+ }
+ }
+
+ // Methane explosion risk
+ if (mine.explosionRisk) {
+ if (Math.random() < 0.001) { // 0.1% per frame
+ console.log('๐ฅ Methane explosion!');
+ this.triggerExplosion();
+ }
+ }
+ }
+
+ /**
+ * Trigger explosion
+ */
+ triggerExplosion() {
+ console.log('๐ฅ EXPLOSION! Take damage!');
+ this.scene.cameras.main.shake(1000, 0.03);
+ // TODO: Apply damage
+ }
+
+ /**
+ * Get mine info
+ */
+ getMineInfo(mineId) {
+ return this.mines.get(mineId);
+ }
+
+ /**
+ * Get all mines
+ */
+ getAllMines() {
+ return Array.from(this.mines.values());
+ }
+
+ /**
+ * Get current level info
+ */
+ getCurrentLevelInfo() {
+ if (!this.currentMine) return null;
+
+ return {
+ mine: this.currentMine.name,
+ level: this.currentLevel,
+ maxLevel: this.currentMine.maxLevels,
+ oreNodes: this.oreNodes.length,
+ minedNodes: this.oreNodes.filter(n => n.mined).length,
+ oxygen: this.oxygenLevel,
+ radiation: this.radiationLevel
+ };
+ }
+
+ /**
+ * Upgrade tool
+ */
+ upgradeTool(tier) {
+ if (tier < 1 || tier > this.tools.length) {
+ console.error(`Invalid tool tier: ${tier}`);
+ return false;
+ }
+
+ this.toolTier = tier;
+ this.currentTool = this.tools[tier - 1];
+
+ console.log(`โ๏ธ Upgraded to ${this.currentTool.name}`);
+
+ this.showNotification({
+ title: 'Tool Upgraded!',
+ text: `โ๏ธ ${this.currentTool.name} - ${this.currentTool.speed}x speed`,
+ icon: 'โจ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Update system
+ */
+ update(delta) {
+ if (!this.currentMine) return;
+
+ // Update hazards
+ this.updateHazards(delta);
+
+ // Update ore nodes
+ // (visual updates if needed)
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MintingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MintingSystem.js
new file mode 100644
index 000000000..5cec6905f
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MintingSystem.js
@@ -0,0 +1,270 @@
+// ๐ฐ MINTING SYSTEM - Phase 40
+// Smelter (Ore โ Gold) + Mint (Gold โ Coins)
+
+class MintingSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Active smelters and mints in world
+ this.smelters = [];
+ this.mints = [];
+
+ // Recipes
+ this.recipes = {
+ smelt_gold: {
+ name: 'Smelt Gold',
+ input: 'gold_ore',
+ inputAmount: 1,
+ output: 'gold_bar',
+ outputAmount: 1,
+ fuel: 'coal',
+ fuelAmount: 1,
+ time: 5 // seconds
+ },
+ mint_coin: {
+ name: 'Mint Coin',
+ input: 'gold_bar',
+ inputAmount: 1,
+ output: 'gold_coin',
+ outputAmount: 10, // 1 bar = 10 coins
+ fuel: null, // No fuel needed
+ time: 3 // seconds
+ }
+ };
+
+ console.log('๐ฐ MintingSystem initialized!');
+ }
+
+ // Place smelter at position
+ placeSmelter(gridX, gridY) {
+ const smelter = {
+ gridX,
+ gridY,
+ type: 'smelter',
+ input: null,
+ fuel: null,
+ output: null,
+ processing: false,
+ progress: 0,
+ recipe: null
+ };
+
+ this.smelters.push(smelter);
+
+ // Create sprite
+ this.createSmelterSprite(smelter);
+
+ console.log(`๐ฅ Smelter placed at (${gridX}, ${gridY})`);
+ return smelter;
+ }
+
+ // Place mint at position
+ placeMint(gridX, gridY) {
+ const mint = {
+ gridX,
+ gridY,
+ type: 'mint',
+ input: null,
+ output: null,
+ processing: false,
+ progress: 0,
+ recipe: null
+ };
+
+ this.mints.push(mint);
+
+ // Create sprite
+ this.createMintSprite(mint);
+
+ console.log(`๐ฐ Mint placed at (${gridX}, ${gridY})`);
+ return mint;
+ }
+
+ createSmelterSprite(smelter) {
+ const worldX = smelter.gridX * 48;
+ const worldY = smelter.gridY * 48;
+
+ // Simple graphics representation
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0x8B4513, 1); // Brown
+ graphics.fillRect(worldX, worldY, 48, 48);
+ graphics.lineStyle(2, 0xFF4500, 1); // Orange outline (fire)
+ graphics.strokeRect(worldX, worldY, 48, 48);
+ graphics.setDepth(5);
+
+ // Fire icon
+ const fireText = this.scene.add.text(worldX + 24, worldY + 24, '๐ฅ', {
+ fontSize: '24px'
+ }).setOrigin(0.5);
+ fireText.setDepth(6);
+
+ smelter.sprite = graphics;
+ smelter.icon = fireText;
+ }
+
+ createMintSprite(mint) {
+ const worldX = mint.gridX * 48;
+ const worldY = mint.gridY * 48;
+
+ // Simple graphics representation
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0xFFD700, 1); // Gold
+ graphics.fillRect(worldX, worldY, 48, 48);
+ graphics.lineStyle(2, 0xFFA500, 1); // Orange outline
+ graphics.strokeRect(worldX, worldY, 48, 48);
+ graphics.setDepth(5);
+
+ // Coin icon
+ const coinText = this.scene.add.text(worldX + 24, worldY + 24, '๐ฐ', {
+ fontSize: '24px'
+ }).setOrigin(0.5);
+ coinText.setDepth(6);
+
+ mint.sprite = graphics;
+ mint.icon = coinText;
+ }
+
+ // Try to process in smelter
+ processSmelter(smelter, inputItem, fuelItem) {
+ if (smelter.processing) {
+ console.log('โณ Smelter is busy!');
+ return false;
+ }
+
+ // Validate recipe
+ const recipe = this.recipes.smelt_gold;
+ if (inputItem !== recipe.input || fuelItem !== recipe.fuel) {
+ console.log('โ Invalid recipe!');
+ return false;
+ }
+
+ // Start processing
+ smelter.processing = true;
+ smelter.progress = 0;
+ smelter.recipe = recipe;
+ smelter.input = inputItem;
+ smelter.fuel = fuelItem;
+ smelter.startTime = Date.now();
+
+ console.log(`๐ฅ Smelting ${inputItem}... (${recipe.time}s)`);
+ return true;
+ }
+
+ // Try to mint coins
+ processMint(mint, inputItem) {
+ if (mint.processing) {
+ console.log('โณ Mint is busy!');
+ return false;
+ }
+
+ // Validate recipe
+ const recipe = this.recipes.mint_coin;
+ if (inputItem !== recipe.input) {
+ console.log('โ Invalid input!');
+ return false;
+ }
+
+ // Start processing
+ mint.processing = true;
+ mint.progress = 0;
+ mint.recipe = recipe;
+ mint.input = inputItem;
+ mint.startTime = Date.now();
+
+ console.log(`๐ฐ Minting coins... (${recipe.time}s)`);
+ return true;
+ }
+
+ // Update - called each frame
+ update(delta) {
+ // Update smelters
+ this.smelters.forEach(smelter => {
+ if (smelter.processing) {
+ const elapsed = (Date.now() - smelter.startTime) / 1000;
+ smelter.progress = elapsed / smelter.recipe.time;
+
+ // Complete?
+ if (smelter.progress >= 1) {
+ this.completeSmelter(smelter);
+ }
+ }
+ });
+
+ // Update mints
+ this.mints.forEach(mint => {
+ if (mint.processing) {
+ const elapsed = (Date.now() - mint.startTime) / 1000;
+ mint.progress = elapsed / mint.recipe.time;
+
+ // Complete?
+ if (mint.progress >= 1) {
+ this.completeMint(mint);
+ }
+ }
+ });
+ }
+
+ completeSmelter(smelter) {
+ const recipe = smelter.recipe;
+
+ // Give output
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(recipe.output, recipe.outputAmount);
+ }
+
+ // Reset smelter
+ smelter.processing = false;
+ smelter.progress = 0;
+ smelter.input = null;
+ smelter.fuel = null;
+ smelter.recipe = null;
+
+ console.log(`โ
Smelting complete! +${recipe.outputAmount} ${recipe.output}`);
+
+ // Visual feedback
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: smelter.gridX * 48,
+ y: smelter.gridY * 48,
+ text: `+${recipe.outputAmount} ${recipe.output}`,
+ color: '#FFD700'
+ });
+ }
+ }
+
+ completeMint(mint) {
+ const recipe = mint.recipe;
+
+ // Give coins (add to gold directly!)
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.gold += recipe.outputAmount;
+ }
+
+ // Reset mint
+ mint.processing = false;
+ mint.progress = 0;
+ mint.input = null;
+ mint.recipe = null;
+
+ console.log(`โ
Minting complete! +${recipe.outputAmount} gold`);
+
+ // Visual feedback
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: mint.gridX * 48,
+ y: mint.gridY * 48,
+ text: `+${recipe.outputAmount}g`,
+ color: '#FFD700'
+ });
+ }
+ }
+
+ // Get smelter/mint at position
+ getSmelterAt(gridX, gridY) {
+ return this.smelters.find(s => s.gridX === gridX && s.gridY === gridY);
+ }
+
+ getMintAt(gridX, gridY) {
+ return this.mints.find(m => m.gridX === gridX && m.gridY === gridY);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MotorAccessibilitySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MotorAccessibilitySystem.js
new file mode 100644
index 000000000..ecb981a4c
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MotorAccessibilitySystem.js
@@ -0,0 +1,217 @@
+/**
+ * MOTOR ACCESSIBILITY SYSTEM
+ * Provides assistance for players with motor disabilities
+ * Note: One-handed mode is already implemented in InputRemappingSystem
+ */
+class MotorAccessibilitySystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Settings
+ this.settings = {
+ autoAim: false, // Auto-aim assist
+ autoAimStrength: 0.5, // 0-1 (strength)
+ stickyKeys: false, // Hold key instead of press
+ reducedInputComplexity: false, // Simplified controls
+ slowMotion: false, // Slow-motion mode
+ slowMotionSpeed: 0.5, // 0.1-1.0 (game speed)
+ autoRun: false, // Auto-run when moving
+ autoInteract: false, // Auto-interact with nearby objects
+ largerClickTargets: false, // Bigger UI buttons
+ holdToConfirm: false // Hold instead of click
+ };
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Motor Accessibility System initialized');
+ }
+
+ init() {
+ if (this.settings.slowMotion) {
+ this.enableSlowMotion();
+ }
+ }
+
+ /**
+ * Enable auto-aim assist
+ */
+ enableAutoAim() {
+ this.settings.autoAim = true;
+ this.saveSettings();
+ console.log('๐ฏ Auto-aim enabled');
+ }
+
+ /**
+ * Disable auto-aim
+ */
+ disableAutoAim() {
+ this.settings.autoAim = false;
+ this.saveSettings();
+ console.log('๐ฏ Auto-aim disabled');
+ }
+
+ /**
+ * Set auto-aim strength
+ */
+ setAutoAimStrength(strength) {
+ this.settings.autoAimStrength = Phaser.Math.Clamp(strength, 0, 1);
+ this.saveSettings();
+ console.log(`๐ฏ Auto-aim strength: ${this.settings.autoAimStrength}`);
+ }
+
+ /**
+ * Get nearest enemy for auto-aim
+ */
+ getNearestEnemy() {
+ if (!this.scene.player) return null;
+
+ const playerPos = this.scene.player.getPosition();
+ let nearest = null;
+ let minDist = 999999;
+
+ // Find nearest NPC
+ for (const npc of this.scene.npcs) {
+ if (!npc.sprite || npc.isFriendly) continue;
+
+ const dist = Phaser.Math.Distance.Between(
+ playerPos.x, playerPos.y,
+ npc.gridX, npc.gridY
+ );
+
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = npc;
+ }
+ }
+
+ return nearest;
+ }
+
+ /**
+ * Apply auto-aim to attack
+ */
+ applyAutoAim() {
+ if (!this.settings.autoAim) return null;
+
+ const target = this.getNearestEnemy();
+ if (!target) return null;
+
+ // Return target position with strength factor
+ return {
+ x: target.gridX,
+ y: target.gridY,
+ strength: this.settings.autoAimStrength
+ };
+ }
+
+ /**
+ * Enable sticky keys
+ */
+ enableStickyKeys() {
+ this.settings.stickyKeys = true;
+ this.saveSettings();
+ console.log('โจ๏ธ Sticky keys enabled');
+ }
+
+ /**
+ * Disable sticky keys
+ */
+ disableStickyKeys() {
+ this.settings.stickyKeys = false;
+ this.saveSettings();
+ console.log('โจ๏ธ Sticky keys disabled');
+ }
+
+ /**
+ * Enable slow-motion mode
+ */
+ enableSlowMotion() {
+ this.settings.slowMotion = true;
+ this.scene.time.timeScale = this.settings.slowMotionSpeed;
+ this.saveSettings();
+ console.log(`๐ Slow-motion enabled (${this.settings.slowMotionSpeed}x)`);
+ }
+
+ /**
+ * Disable slow-motion mode
+ */
+ disableSlowMotion() {
+ this.settings.slowMotion = false;
+ this.scene.time.timeScale = 1.0;
+ this.saveSettings();
+ console.log('๐ Slow-motion disabled');
+ }
+
+ /**
+ * Set slow-motion speed
+ */
+ setSlowMotionSpeed(speed) {
+ this.settings.slowMotionSpeed = Phaser.Math.Clamp(speed, 0.1, 1.0);
+ if (this.settings.slowMotion) {
+ this.scene.time.timeScale = this.settings.slowMotionSpeed;
+ }
+ this.saveSettings();
+ console.log(`๐ Slow-motion speed: ${this.settings.slowMotionSpeed}x`);
+ }
+
+ /**
+ * Toggle auto-run
+ */
+ toggleAutoRun() {
+ this.settings.autoRun = !this.settings.autoRun;
+ this.saveSettings();
+ console.log(`๐ Auto-run: ${this.settings.autoRun ? 'ON' : 'OFF'}`);
+ }
+
+ /**
+ * Toggle auto-interact
+ */
+ toggleAutoInteract() {
+ this.settings.autoInteract = !this.settings.autoInteract;
+ this.saveSettings();
+ console.log(`๐ค Auto-interact: ${this.settings.autoInteract ? 'ON' : 'OFF'}`);
+ }
+
+ /**
+ * Update (called every frame)
+ */
+ update() {
+ // Auto-interact logic
+ if (this.settings.autoInteract) {
+ this.checkAutoInteract();
+ }
+ }
+
+ /**
+ * Check for auto-interact opportunities
+ */
+ checkAutoInteract() {
+ // Implementation would check for nearby interactive objects
+ }
+
+ /**
+ * Save settings
+ */
+ saveSettings() {
+ localStorage.setItem('novafarma_motor_accessibility', JSON.stringify(this.settings));
+ }
+
+ /**
+ * Load settings
+ */
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_motor_accessibility');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ destroy() {
+ if (this.settings.slowMotion) {
+ this.scene.time.timeScale = 1.0;
+ }
+ console.log('๐ฆพ Motor Accessibility System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MountSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MountSystem.js
new file mode 100644
index 000000000..be38445a4
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MountSystem.js
@@ -0,0 +1,193 @@
+/**
+ * MOUNT SYSTEM
+ * Handles rideable animals (Donkey, Horse, etc.)
+ */
+class MountSystem {
+ constructor(scene, player) {
+ this.scene = scene;
+ this.player = player;
+ this.currentMount = null;
+ this.isMounted = false;
+
+ // Mount definitions
+ this.mountData = {
+ 'donkey': {
+ name: 'Donkey',
+ speed: 200, // Movement speed when mounted
+ texture: 'donkey',
+ inventorySlots: 10, // Extra inventory from saddlebags
+ color: 0x8B7355 // Brown
+ },
+ 'horse': {
+ name: 'Horse',
+ speed: 300,
+ texture: 'horse',
+ inventorySlots: 5,
+ color: 0x654321
+ }
+ };
+ }
+
+ /**
+ * Spawn a mount in the world
+ */
+ spawnMount(gridX, gridY, type) {
+ if (!this.mountData[type]) {
+ console.error('Unknown mount type:', type);
+ return null;
+ }
+
+ const data = this.mountData[type];
+ const screenPos = this.scene.iso.toScreen(gridX, gridY);
+
+ const mount = {
+ type,
+ gridX,
+ gridY,
+ sprite: null,
+ inventory: [], // Saddlebag storage
+ tamed: true
+ };
+
+ // Create sprite
+ mount.sprite = this.scene.add.sprite(
+ screenPos.x + this.scene.terrainOffsetX,
+ screenPos.y + this.scene.terrainOffsetY,
+ data.texture || 'npc_zombie' // Fallback
+ );
+ mount.sprite.setOrigin(0.5, 1);
+ mount.sprite.setScale(0.4);
+ mount.sprite.setTint(data.color);
+ mount.sprite.setDepth(this.scene.iso.getDepth(gridX, gridY, this.scene.iso.LAYER_OBJECTS));
+
+ mount.sprite.setInteractive({ useHandCursor: true });
+ mount.sprite.on('pointerdown', () => {
+ this.mountUp(mount);
+ });
+
+ console.log(`๐ด Spawned ${data.name} at ${gridX},${gridY}`);
+ return mount;
+ }
+
+ /**
+ * Mount up on a donkey/horse
+ */
+ mountUp(mount) {
+ if (this.isMounted) {
+ console.log('Already mounted!');
+ return;
+ }
+
+ this.currentMount = mount;
+ this.isMounted = true;
+
+ const data = this.mountData[mount.type];
+
+ // Hide mount sprite
+ mount.sprite.setVisible(false);
+
+ // Increase player speed
+ if (this.player) {
+ this.player.originalSpeed = this.player.speed || 100;
+ this.player.speed = data.speed;
+ }
+
+ // Visual feedback
+ this.scene.events.emit('show-floating-text', {
+ x: this.player.sprite.x,
+ y: this.player.sprite.y - 40,
+ text: `๐ด Mounted ${data.name}!`,
+ color: '#ffaa00'
+ });
+
+ console.log(`๐ด Mounted on ${data.name}! Speed: ${data.speed}`);
+ }
+
+ /**
+ * Dismount
+ */
+ dismount() {
+ if (!this.isMounted || !this.currentMount) {
+ return;
+ }
+
+ const mount = this.currentMount;
+ const data = this.mountData[mount.type];
+
+ // Restore player speed
+ if (this.player && this.player.originalSpeed) {
+ this.player.speed = this.player.originalSpeed;
+ }
+
+ // Place mount at player location
+ const playerPos = this.player.getPosition();
+ mount.gridX = Math.round(playerPos.x);
+ mount.gridY = Math.round(playerPos.y);
+
+ const screenPos = this.scene.iso.toScreen(mount.gridX, mount.gridY);
+ mount.sprite.setPosition(
+ screenPos.x + this.scene.terrainOffsetX,
+ screenPos.y + this.scene.terrainOffsetY
+ );
+ mount.sprite.setVisible(true);
+
+ // Visual feedback
+ this.scene.events.emit('show-floating-text', {
+ x: this.player.sprite.x,
+ y: this.player.sprite.y - 40,
+ text: `Dismounted`,
+ color: '#888888'
+ });
+
+ this.currentMount = null;
+ this.isMounted = false;
+
+ console.log(`๐ด Dismounted from ${data.name}`);
+ }
+
+ /**
+ * Toggle mount/dismount (E key)
+ */
+ toggleMount() {
+ if (this.isMounted) {
+ this.dismount();
+ } else {
+ // Try to find nearby mount
+ const nearbyMount = this.findNearbyMount();
+ if (nearbyMount) {
+ this.mountUp(nearbyMount);
+ }
+ }
+ }
+
+ findNearbyMount() {
+ // TODO: Search for mounts near player
+ return null;
+ }
+
+ /**
+ * Access saddlebag inventory
+ */
+ openSaddlebag() {
+ if (!this.isMounted || !this.currentMount) {
+ return null;
+ }
+
+ const data = this.mountData[this.currentMount.type];
+ console.log(`๐ Opening saddlebag (${data.inventorySlots} slots)`);
+ return this.currentMount.inventory;
+ }
+
+ update(delta) {
+ // Update mount position if mounted
+ if (this.isMounted && this.currentMount && this.player) {
+ const playerPos = this.player.getPosition();
+ this.currentMount.gridX = Math.round(playerPos.x);
+ this.currentMount.gridY = Math.round(playerPos.y);
+ }
+ }
+
+ isMountedOnAnything() {
+ return this.isMounted;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MultiplayerSocialSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MultiplayerSocialSystem.js
new file mode 100644
index 000000000..be7e4b7d8
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MultiplayerSocialSystem.js
@@ -0,0 +1,467 @@
+/**
+ * MULTIPLAYER & SOCIAL SYSTEM
+ * Co-op mode, trading, leaderboards, and social features
+ */
+class MultiplayerSocialSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Multiplayer
+ this.isHost = false;
+ this.isConnected = false;
+ this.players = new Map();
+ this.maxPlayers = 4;
+
+ // Trading
+ this.tradeOffers = new Map();
+ this.activeTrade = null;
+
+ // Leaderboards
+ this.leaderboards = new Map();
+
+ // Social
+ this.friends = new Set();
+ this.gifts = [];
+
+ // Events
+ this.activeEvent = null;
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Multiplayer & Social System initialized');
+ }
+
+ init() {
+ this.defineLeaderboards();
+ this.defineEvents();
+ console.log('๐ Multiplayer & Social ready');
+ }
+
+ // ========== CO-OP MODE ==========
+
+ hostGame() {
+ this.isHost = true;
+ this.isConnected = true;
+
+ // Add host player
+ this.players.set('host', {
+ id: 'host',
+ name: 'Player 1',
+ x: 50,
+ y: 50,
+ isHost: true
+ });
+
+ console.log('๐ฎ Hosting game...');
+ return true;
+ }
+
+ joinGame(hostId) {
+ this.isHost = false;
+ this.isConnected = true;
+
+ console.log(`๐ฎ Joining game ${hostId}...`);
+ return true;
+ }
+
+ addPlayer(playerId, playerData) {
+ if (this.players.size >= this.maxPlayers) {
+ console.log('โ Game is full');
+ return false;
+ }
+
+ this.players.set(playerId, {
+ id: playerId,
+ name: playerData.name || `Player ${this.players.size + 1}`,
+ x: playerData.x || 50,
+ y: playerData.y || 50,
+ isHost: false
+ });
+
+ console.log(`โ
Player joined: ${playerData.name}`);
+ return true;
+ }
+
+ removePlayer(playerId) {
+ this.players.delete(playerId);
+ console.log(`๐ Player left: ${playerId}`);
+ }
+
+ syncWorldState() {
+ // Sync game state between players
+ const worldState = {
+ time: this.scene.weatherSystem?.currentTime || 0,
+ weather: this.scene.weatherSystem?.currentWeather || 'clear',
+ players: Array.from(this.players.values())
+ };
+
+ return worldState;
+ }
+
+ updatePlayerPosition(playerId, x, y) {
+ const player = this.players.get(playerId);
+ if (player) {
+ player.x = x;
+ player.y = y;
+ }
+ }
+
+ // ========== CO-OP QUESTS ==========
+
+ startCoopQuest(questId) {
+ console.log(`๐ค Co-op quest started: ${questId}`);
+
+ const coopQuests = {
+ 'farm_together': {
+ name: 'Farm Together',
+ objective: 'Harvest 100 crops as a team',
+ reward: { xp: 500, gold: 1000 }
+ },
+ 'boss_raid': {
+ name: 'Boss Raid',
+ objective: 'Defeat boss together',
+ reward: { xp: 2000, legendary_item: 1 }
+ }
+ };
+
+ return coopQuests[questId];
+ }
+
+ // ========== TRADING ==========
+
+ createTradeOffer(targetPlayerId, offeredItems, requestedItems) {
+ const tradeId = `trade_${Date.now()}`;
+
+ this.tradeOffers.set(tradeId, {
+ id: tradeId,
+ from: 'local_player',
+ to: targetPlayerId,
+ offered: offeredItems,
+ requested: requestedItems,
+ status: 'pending',
+ createdAt: Date.now()
+ });
+
+ console.log(`๐ฑ Trade offer created: ${tradeId}`);
+ return tradeId;
+ }
+
+ acceptTrade(tradeId) {
+ const trade = this.tradeOffers.get(tradeId);
+ if (!trade || trade.status !== 'pending') return false;
+
+ // Execute trade
+ this.executeTrade(trade);
+
+ trade.status = 'completed';
+ console.log(`โ
Trade completed: ${tradeId}`);
+ return true;
+ }
+
+ rejectTrade(tradeId) {
+ const trade = this.tradeOffers.get(tradeId);
+ if (!trade) return false;
+
+ trade.status = 'rejected';
+ console.log(`โ Trade rejected: ${tradeId}`);
+ return true;
+ }
+
+ executeTrade(trade) {
+ // Transfer items between players
+ console.log(`๐ฑ Executing trade...`);
+
+ // Remove offered items from local player
+ if (this.scene.inventorySystem) {
+ for (const [item, amount] of Object.entries(trade.offered)) {
+ this.scene.inventorySystem.removeItem(item, amount);
+ }
+
+ // Add requested items to local player
+ for (const [item, amount] of Object.entries(trade.requested)) {
+ this.scene.inventorySystem.addItem(item, amount);
+ }
+ }
+ }
+
+ // ========== GLOBAL MARKETPLACE ==========
+
+ listItemOnMarket(item, amount, price) {
+ console.log(`๐ช Listed ${amount}x ${item} for ${price} gold`);
+
+ const listing = {
+ id: `listing_${Date.now()}`,
+ seller: 'local_player',
+ item,
+ amount,
+ price,
+ listedAt: Date.now()
+ };
+
+ return listing;
+ }
+
+ buyFromMarket(listingId) {
+ console.log(`๐ฐ Purchased item from market: ${listingId}`);
+ }
+
+ // ========== AUCTION HOUSE ==========
+
+ createAuction(item, startingBid, duration) {
+ console.log(`๐จ Auction created: ${item} starting at ${startingBid} gold`);
+
+ const auction = {
+ id: `auction_${Date.now()}`,
+ seller: 'local_player',
+ item,
+ currentBid: startingBid,
+ highestBidder: null,
+ endsAt: Date.now() + duration,
+ bids: []
+ };
+
+ return auction;
+ }
+
+ placeBid(auctionId, bidAmount) {
+ console.log(`๐ฐ Bid placed: ${bidAmount} gold on ${auctionId}`);
+ }
+
+ // ========== PRICE FLUCTUATION ==========
+
+ updateMarketPrices() {
+ // Simulate market price changes
+ const priceChanges = {
+ wheat: 1.0 + (Math.random() - 0.5) * 0.2,
+ iron: 1.0 + (Math.random() - 0.5) * 0.3,
+ gold: 1.0 + (Math.random() - 0.5) * 0.1
+ };
+
+ return priceChanges;
+ }
+
+ // ========== LEADERBOARDS ==========
+
+ defineLeaderboards() {
+ this.leaderboards.set('productivity', {
+ name: 'Farm Productivity',
+ entries: []
+ });
+
+ this.leaderboards.set('speedrun', {
+ name: 'Fastest Speedruns',
+ entries: []
+ });
+
+ this.leaderboards.set('survival', {
+ name: 'Highest Survival Days',
+ entries: []
+ });
+
+ this.leaderboards.set('wealth', {
+ name: 'Richest Players',
+ entries: []
+ });
+
+ this.leaderboards.set('boss_kills', {
+ name: 'Boss Kill Times',
+ entries: []
+ });
+ }
+
+ submitScore(leaderboardId, score) {
+ const leaderboard = this.leaderboards.get(leaderboardId);
+ if (!leaderboard) return false;
+
+ leaderboard.entries.push({
+ player: 'local_player',
+ score,
+ timestamp: Date.now()
+ });
+
+ // Sort by score (descending)
+ leaderboard.entries.sort((a, b) => b.score - a.score);
+
+ // Keep top 100
+ leaderboard.entries = leaderboard.entries.slice(0, 100);
+
+ console.log(`๐ Score submitted to ${leaderboard.name}: ${score}`);
+ return true;
+ }
+
+ getLeaderboard(leaderboardId) {
+ return this.leaderboards.get(leaderboardId);
+ }
+
+ // ========== SOCIAL INTEGRATION ==========
+
+ shareScreenshot() {
+ console.log('๐ธ Screenshot shared!');
+ // Steam overlay integration
+ }
+
+ visitFarm(playerId) {
+ console.log(`๐ Visiting ${playerId}'s farm...`);
+ }
+
+ sendGift(playerId, item, amount) {
+ const gift = {
+ id: `gift_${Date.now()}`,
+ from: 'local_player',
+ to: playerId,
+ item,
+ amount,
+ message: '',
+ sentAt: Date.now()
+ };
+
+ console.log(`๐ Gift sent to ${playerId}: ${amount}x ${item}`);
+ return gift;
+ }
+
+ receiveGift(giftId) {
+ console.log(`๐ Gift received: ${giftId}`);
+
+ // Add to inventory
+ if (this.scene.inventorySystem) {
+ // this.scene.inventorySystem.addItem(gift.item, gift.amount);
+ }
+ }
+
+ addFriend(playerId) {
+ this.friends.add(playerId);
+ console.log(`๐ฅ Added friend: ${playerId}`);
+ this.saveProgress();
+ }
+
+ removeFriend(playerId) {
+ this.friends.delete(playerId);
+ console.log(`๐ Removed friend: ${playerId}`);
+ this.saveProgress();
+ }
+
+ // ========== COMMUNITY EVENTS ==========
+
+ defineEvents() {
+ this.events = {
+ 'harvest_festival': {
+ name: 'Harvest Festival',
+ duration: 604800000, // 7 days
+ bonuses: { crop_yield: 2.0, xp: 1.5 }
+ },
+ 'zombie_invasion': {
+ name: 'Zombie Invasion',
+ duration: 259200000, // 3 days
+ bonuses: { zombie_spawn: 3.0, loot: 2.0 }
+ },
+ 'winter_wonderland': {
+ name: 'Winter Wonderland',
+ duration: 1209600000, // 14 days
+ bonuses: { snow_items: true, special_decorations: true }
+ }
+ };
+ }
+
+ startEvent(eventId) {
+ const event = this.events[eventId];
+ if (!event) return false;
+
+ this.activeEvent = {
+ id: eventId,
+ ...event,
+ startTime: Date.now(),
+ endTime: Date.now() + event.duration
+ };
+
+ console.log(`๐ Event started: ${event.name}`);
+ return true;
+ }
+
+ endEvent() {
+ if (!this.activeEvent) return;
+
+ console.log(`๐ Event ended: ${this.activeEvent.name}`);
+ this.activeEvent = null;
+ }
+
+ // ========== SEASONAL CHALLENGES ==========
+
+ getWeeklyChallenges() {
+ return [
+ { name: 'Harvest 500 crops', reward: { gold: 1000 } },
+ { name: 'Defeat 50 zombies', reward: { xp: 500 } },
+ { name: 'Craft 20 items', reward: { blueprint: 'rare_item' } }
+ ];
+ }
+
+ getMonthlyChallenges() {
+ return [
+ { name: 'Reach level 50', reward: { legendary_item: 1 } },
+ { name: 'Complete all quests', reward: { title: 'Quest Master' } },
+ { name: 'Defeat all bosses', reward: { mount: 'dragon' } }
+ ];
+ }
+
+ completeChallenge(challengeId) {
+ console.log(`โ
Challenge completed: ${challengeId}`);
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ // Update active event
+ if (this.activeEvent) {
+ const now = Date.now();
+ if (now >= this.activeEvent.endTime) {
+ this.endEvent();
+ }
+ }
+
+ // Update market prices periodically
+ // this.updateMarketPrices();
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ friends: Array.from(this.friends),
+ leaderboards: Array.from(this.leaderboards.entries()).map(([id, lb]) => ({
+ id,
+ entries: lb.entries
+ }))
+ };
+
+ localStorage.setItem('novafarma_multiplayer', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_multiplayer');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.friends = new Set(data.friends || []);
+
+ if (data.leaderboards) {
+ data.leaderboards.forEach(lb => {
+ const leaderboard = this.leaderboards.get(lb.id);
+ if (leaderboard) {
+ leaderboard.entries = lb.entries;
+ }
+ });
+ }
+
+ console.log('โ
Multiplayer progress loaded');
+ } catch (error) {
+ console.error('Failed to load multiplayer progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐ Multiplayer & Social System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MultiplayerSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MultiplayerSystem.js
new file mode 100644
index 000000000..c664a36b1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MultiplayerSystem.js
@@ -0,0 +1,129 @@
+class MultiplayerSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.socket = null;
+ this.otherPlayers = {}; // Map socketId -> Sprite
+ this.isConnected = false;
+
+ // Try to connect
+ this.connect();
+ }
+
+ connect() {
+ if (typeof io === 'undefined') {
+ console.warn('โ ๏ธ Socket.IO not found. Multiplayer disabled.');
+ console.warn('Please run: npm install socket.io-client OR include CDN.');
+ return;
+ }
+
+ console.log('๐ Connecting to Multiplayer Server...');
+ // Connect to localhost:3000
+ this.socket = io('http://localhost:3000');
+
+ this.socket.on('connect', () => {
+ console.log('โ
Connected to Server! ID:', this.socket.id);
+ this.isConnected = true;
+
+ // Send initial pos
+ if (this.scene.player) {
+ const pos = this.scene.player.getPosition();
+ this.socket.emit('playerMovement', {
+ x: pos.x,
+ y: pos.y,
+ anim: 'idle',
+ flipX: false
+ });
+ }
+ });
+
+ this.socket.on('currentPlayers', (players) => {
+ Object.keys(players).forEach((id) => {
+ if (id === this.socket.id) return;
+ this.addOtherPlayer(players[id]);
+ });
+ });
+
+ this.socket.on('newPlayer', (playerInfo) => {
+ this.addOtherPlayer(playerInfo);
+ });
+
+ this.socket.on('playerDisconnected', (playerId) => {
+ this.removeOtherPlayer(playerId);
+ });
+
+ this.socket.on('playerMoved', (playerInfo) => {
+ if (this.otherPlayers[playerInfo.id]) {
+ const sprite = this.otherPlayers[playerInfo.id];
+ // Update target pos for interpolation (TODO: logic)
+ // For now direct teleport
+
+ // Convert grid to screen
+ const iso = new IsometricUtils(48, 24);
+ const screen = iso.toScreen(playerInfo.x, playerInfo.y);
+
+ sprite.setPosition(screen.x + this.scene.terrainOffsetX, screen.y + this.scene.terrainOffsetY);
+ sprite.setDepth(sprite.y);
+
+ // Anim/Flip logic could go here
+ if (playerInfo.flipX !== undefined) sprite.setFlipX(playerInfo.flipX);
+ }
+ });
+
+ this.socket.on('worldAction', (action) => {
+ // Handle world syncing
+ if (action.type === 'build' && this.scene.buildingSystem) {
+ // Hacky: place building remotely
+ // this.scene.terrainSystem.placeStructure(...)
+ }
+ });
+ }
+
+ addOtherPlayer(playerInfo) {
+ if (this.otherPlayers[playerInfo.id]) return;
+
+ console.log('๐ค New Player Joined:', playerInfo.id);
+
+ // Use player sprite
+ const iso = new IsometricUtils(48, 24);
+ const screen = iso.toScreen(playerInfo.x, playerInfo.y);
+
+ const sprite = this.scene.add.sprite(
+ screen.x + this.scene.terrainOffsetX,
+ screen.y + this.scene.terrainOffsetY,
+ 'player' // or player_idle
+ );
+ sprite.setOrigin(0.5, 1);
+ sprite.setScale(0.3); // Same as local player
+
+ // Add name tag
+ const text = this.scene.add.text(0, -50, 'Player', { fontSize: '12px', fill: '#ffffff' });
+ text.setOrigin(0.5);
+ // Container? For now just sprite, text handling is complex
+
+ this.otherPlayers[playerInfo.id] = sprite;
+ }
+
+ removeOtherPlayer(playerId) {
+ if (this.otherPlayers[playerId]) {
+ console.log('๐ Player Left:', playerId);
+ this.otherPlayers[playerId].destroy();
+ delete this.otherPlayers[playerId];
+ }
+ }
+
+ update(delta) {
+ if (!this.isConnected || !this.socket || !this.scene.player) return;
+
+ // Rate limit: send 10 times second? Or every frame?
+ // Let's send only if moved
+ const player = this.scene.player;
+ if (player.isMoving) {
+ this.socket.emit('playerMovement', {
+ x: player.gridX,
+ y: player.gridY,
+ anim: 'walk',
+ flipX: player.sprite.flipX // Accessing internal sprite
+ });
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/MuseumEvolutionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/MuseumEvolutionSystem.js
new file mode 100644
index 000000000..1eaebcae2
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/MuseumEvolutionSystem.js
@@ -0,0 +1,344 @@
+/**
+ * MUSEUM EVOLUTION SYSTEM
+ * 3-stage museum restoration with artifact collection
+ * Integrates with Kustos NPC and quest system
+ */
+
+export class MuseumEvolutionSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Museum evolution stages
+ this.currentStage = 0; // 0 = ruined, 1 = partial, 2 = advanced, 3 = complete
+
+ // Artifact collection
+ this.artifacts = new Map(); // artifactId โ artifactData
+ this.collectedArtifacts = new Set();
+
+ // Album tracking
+ this.albumCategories = new Map();
+
+ this.init();
+ }
+
+ init() {
+ this.initializeArtifactDatabase();
+ this.initializeAlbumCategories();
+ }
+
+ /**
+ * Initialize all collectible artifacts
+ */
+ initializeArtifactDatabase() {
+ const artifacts = [
+ // Pre-war artifacts
+ { id: 'dino_skull', name: 'Dinosaur Skull', category: 'prehistory', rarity: 'legendary', discoveryReward: 500 },
+ { id: 'ancient_vase', name: 'Ancient Vase', category: 'prehistory', rarity: 'rare', discoveryReward: 100 },
+ { id: 'fossil', name: 'Fossil', category: 'prehistory', rarity: 'common', discoveryReward: 50 },
+
+ // 2084 tech artifacts
+ { id: 'ai_core', name: 'AI Core Fragment', category: '2084_tech', rarity: 'legendary', discoveryReward: 800 },
+ { id: 'hologram_projector', name: 'Hologram Projector', category: '2084_tech', rarity: 'rare', discoveryReward: 150 },
+ { id: 'old_smartphone', name: 'Pre-War Smartphone', category: '2084_tech', rarity: 'uncommon', discoveryReward: 80 },
+
+ // Cultural artifacts
+ { id: 'religious_icon', name: 'Religious Icon', category: 'culture', rarity: 'rare', discoveryReward: 120 },
+ { id: 'painting', name: 'Old Painting', category: 'culture', rarity: 'uncommon', discoveryReward: 90 },
+ { id: 'book_collection', name: 'Rare Book Collection', category: 'culture', rarity: 'rare', discoveryReward: 110 },
+
+ // Post-apocalypse relics
+ { id: 'first_cannabis_seed', name: 'First Cannabis Seed', category: 'post_apo', rarity: 'legendary', discoveryReward: 600 },
+ { id: 'zombie_variant_sample', name: 'Zombie Variant Sample', category: 'post_apo', rarity: 'rare', discoveryReward: 140 },
+ { id: 'survival_journal', name: "Survivor's Journal", category: 'post_apo', rarity: 'uncommon', discoveryReward: 70 }
+ ];
+
+ artifacts.forEach(artifact => {
+ this.artifacts.set(artifact.id, {
+ ...artifact,
+ discovered: false,
+ donatedToMuseum: false,
+ discoveryDate: null
+ });
+ });
+ }
+
+ /**
+ * Initialize album categories for organization
+ */
+ initializeAlbumCategories() {
+ this.albumCategories.set('prehistory', {
+ name: 'Prehistoric Era',
+ description: 'Artifacts from Earth\'s ancient past',
+ totalArtifacts: 3,
+ collected: 0
+ });
+
+ this.albumCategories.set('2084_tech', {
+ name: '2084 Technology',
+ description: 'Advanced tech from before The Collapse',
+ totalArtifacts: 3,
+ collected: 0
+ });
+
+ this.albumCategories.set('culture', {
+ name: 'Cultural Heritage',
+ description: 'Art, religion, and culture of the old world',
+ totalArtifacts: 3,
+ collected: 0
+ });
+
+ this.albumCategories.set('post_apo', {
+ name: 'Post-Apocalypse',
+ description: 'Relics from the new world',
+ totalArtifacts: 3,
+ collected: 0
+ });
+ }
+
+ /**
+ * Discover artifact (player finds it)
+ */
+ discoverArtifact(artifactId, location) {
+ const artifact = this.artifacts.get(artifactId);
+ if (!artifact) return false;
+
+ if (artifact.discovered) {
+ console.warn(`Artifact ${artifactId} already discovered`);
+ return false;
+ }
+
+ artifact.discovered = true;
+ artifact.discoveryDate = Date.now();
+ artifact.discoveryLocation = location;
+
+ // Add to player inventory
+ this.scene.inventorySystem.addItem(artifactId, 1);
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `Artifact discovered: ${artifact.name}!`,
+ 'discovery',
+ { rarity: artifact.rarity }
+ );
+
+ // Reward currency
+ this.scene.economySystem.addCurrency(artifact.discoveryReward);
+
+ // VFX
+ this.scene.vfxSystem?.playEffect('artifact_discovery', this.scene.player.x, this.scene.player.y);
+
+ console.log(`โจ Discovered artifact: ${artifact.name} (+${artifact.discoveryReward} coins)`);
+
+ // Kustos dialogue trigger
+ if (!artifact.donatedToMuseum) {
+ this.triggerKustosDialogue(artifactId);
+ }
+
+ return true;
+ }
+
+ /**
+ * Donate artifact to museum (Kustos interaction)
+ */
+ donateArtifact(artifactId) {
+ const artifact = this.artifacts.get(artifactId);
+ if (!artifact || !artifact.discovered || artifact.donatedToMuseum) return false;
+
+ // Remove from player inventory
+ if (!this.scene.inventorySystem.hasItem(artifactId, 1)) {
+ console.warn('Player does not have artifact in inventory');
+ return false;
+ }
+
+ this.scene.inventorySystem.removeItem(artifactId, 1);
+
+ // Mark as donated
+ artifact.donatedToMuseum = true;
+ this.collectedArtifacts.add(artifactId);
+
+ // Update category progress
+ const category = this.albumCategories.get(artifact.category);
+ category.collected++;
+
+ // Kustos thanks dialogue
+ this.scene.dialogueSystem?.startDialogue('kustos', 'thank_donation', { artifact: artifact.name });
+
+ // Check for museum evolution
+ this.checkEvolutionTrigger();
+
+ // Achievement check
+ this.checkCompletionAchievements();
+
+ console.log(`๐ฆ Donated ${artifact.name} to museum`);
+ return true;
+ }
+
+ /**
+ * Check if museum should evolve to next stage
+ */
+ checkEvolutionTrigger() {
+ const totalDonated = this.collectedArtifacts.size;
+
+ // Stage thresholds
+ const stageThresholds = {
+ 1: 4, // Stage 1: 4 artifacts donated
+ 2: 8, // Stage 2: 8 artifacts donated
+ 3: 12 // Stage 3: All 12 artifacts donated
+ };
+
+ // Check for evolution
+ for (let stage = 3; stage >= 1; stage--) {
+ if (totalDonated >= stageThresholds[stage] && this.currentStage < stage) {
+ this.evolveMuseum(stage);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Evolve museum to next stage
+ */
+ evolveMuseum(newStage) {
+ if (newStage <= this.currentStage) return;
+
+ this.currentStage = newStage;
+
+ // Update building visual
+ this.updateMuseumVisual(newStage);
+
+ // VFX: Building evolution
+ this.scene.vfxSystem?.playEffect('museum_evolution', 1500, 600);
+
+ // Notification
+ const stageNames = {
+ 1: 'Partial Collection',
+ 2: 'Advanced Exhibition',
+ 3: 'World-Class Museum'
+ };
+
+ this.scene.uiSystem?.showNotification(
+ `Museum evolved: ${stageNames[newStage]}!`,
+ 'success'
+ );
+
+ // Kustos special dialogue
+ this.scene.dialogueSystem?.startDialogue('kustos', `museum_stage_${newStage}`);
+
+ // Grant benefits
+ this.grantStageBenefits(newStage);
+
+ console.log(`๐๏ธ Museum evolved to Stage ${newStage}: ${stageNames[newStage]}`);
+ }
+
+ /**
+ * Update museum building sprite
+ */
+ updateMuseumVisual(stage) {
+ // Load corresponding sprite: museum_stage_1, museum_stage_2, museum_stage_3
+ const sprite = this.scene.buildingSystem.getBuilding('museum');
+ if (sprite) {
+ sprite.setTexture(`museum_stage_${stage}`);
+ }
+ }
+
+ /**
+ * Grant benefits for reaching museum stage
+ */
+ grantStageBenefits(stage) {
+ switch (stage) {
+ case 1:
+ // Stage 1: Basic lore access
+ this.scene.gameState.unlocks.lore_system = true;
+ this.scene.uiSystem?.unlockTab('lore');
+ break;
+ case 2:
+ // Stage 2: Research bonuses
+ this.scene.gameState.buffs.research_speed = 1.25; // +25% research
+ break;
+ case 3:
+ // Stage 3: Full collection bonus
+ this.scene.gameState.buffs.artifact_discovery = 2.0; // 2x artifact find rate
+ this.scene.achievementSystem?.unlock('master_curator');
+ break;
+ }
+ }
+
+ /**
+ * Trigger Kustos dialogue about found artifact
+ */
+ triggerKustosDialogue(artifactId) {
+ const artifact = this.artifacts.get(artifactId);
+
+ // Show notification to visit Kustos
+ this.scene.uiSystem?.showNotification(
+ `Kustos might be interested in the ${artifact.name}`,
+ 'info',
+ { icon: 'museum' }
+ );
+ }
+
+ /**
+ * Check for collection completion achievements
+ */
+ checkCompletionAchievements() {
+ // Check category completion
+ this.albumCategories.forEach((category, categoryId) => {
+ if (category.collected === category.totalArtifacts) {
+ this.scene.achievementSystem?.unlock(`complete_${categoryId}_collection`);
+ }
+ });
+
+ // Check total completion
+ if (this.collectedArtifacts.size === this.artifacts.size) {
+ this.scene.achievementSystem?.unlock('complete_museum_collection');
+ }
+ }
+
+ /**
+ * Get album progress
+ */
+ getAlbumProgress() {
+ const categories = {};
+
+ this.albumCategories.forEach((category, categoryId) => {
+ categories[categoryId] = {
+ ...category,
+ progress: Math.round((category.collected / category.totalArtifacts) * 100)
+ };
+ });
+
+ return {
+ categories,
+ totalArtifacts: this.artifacts.size,
+ totalCollected: this.collectedArtifacts.size,
+ overallProgress: Math.round((this.collectedArtifacts.size / this.artifacts.size) * 100)
+ };
+ }
+
+ /**
+ * Get artifacts by category for UI display
+ */
+ getArtifactsByCategory(categoryId) {
+ return Array.from(this.artifacts.values())
+ .filter(a => a.category === categoryId);
+ }
+
+ /**
+ * Get museum stage info
+ */
+ getStageInfo() {
+ const stageData = {
+ 0: { name: 'Ruined', description: 'Museum is destroyed' },
+ 1: { name: 'Partial Collection', description: '4 artifacts displayed' },
+ 2: { name: 'Advanced Exhibition', description: '8 artifacts displayed' },
+ 3: { name: 'World-Class Museum', description: 'All 12 artifacts displayed' }
+ };
+
+ return {
+ currentStage: this.currentStage,
+ ...stageData[this.current Stage],
+ nextStageRequirement: this.currentStage < 3 ? [4, 8, 12][this.currentStage] : null,
+ currentArtifacts: this.collectedArtifacts.size
+ };
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/NPCPopulationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/NPCPopulationSystem.js
new file mode 100644
index 000000000..1ab8b2f08
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/NPCPopulationSystem.js
@@ -0,0 +1,337 @@
+/**
+ * ๐ฅ NPC POPULATION SYSTEM
+ * Spawns and manages NPCs in structures across the world
+ * - Biome-specific NPCs
+ * - Dialog system
+ * - Trading functionality
+ * - Quest giving
+ */
+
+class NPCPopulationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // All NPCs in the world
+ this.npcs = [];
+
+ // NPC types per biome
+ this.npcTypes = {
+ 'Grassland': ['farmer', 'blacksmith', 'merchant', 'guard'],
+ 'Forest': ['hunter', 'herbalist', 'ranger', 'druid'],
+ 'Desert': ['nomad', 'treasure_hunter', 'merchant', 'archaeologist'],
+ 'Mountain': ['miner', 'dwarf', 'geologist', 'mountaineer'],
+ 'Swamp': ['witch', 'alchemist', 'hermit', 'shaman']
+ };
+
+ // Dialog templates
+ this.dialogs = {
+ 'farmer': [
+ "Welcome to my farm! Need any seeds?",
+ "The harvest this year is bountiful!",
+ "I sell the best wheat in the land!"
+ ],
+ 'merchant': [
+ "Looking to buy or sell? I've got the best deals!",
+ "Welcome, traveler! Check out my wares!",
+ "Gold for goods, goods for gold!"
+ ],
+ 'hunter': [
+ "The forest is full of game. Happy hunting!",
+ "Watch out for the wolves at night.",
+ "I can sell you some arrows if you need them."
+ ],
+ 'nomad': [
+ "The desert holds many secrets...",
+ "Water is more valuable than gold here.",
+ "I've traveled far and wide, seen many things."
+ ],
+ 'miner': [
+ "These mountains are rich with ore!",
+ "I can sell you some iron if you need it.",
+ "Watch your step in those caves!"
+ ],
+ 'witch': [
+ "Potions and hexes, what do you need?",
+ "The swamp holds ancient magic...",
+ "I can brew you something special."
+ ]
+ };
+
+ // Trade goods per NPC type
+ this.tradeGoods = {
+ 'farmer': [
+ { item: 'wheat_seeds', price: 10, stock: 50 },
+ { item: 'wheat', price: 5, stock: 100 },
+ { item: 'bread', price: 15, stock: 20 }
+ ],
+ 'merchant': [
+ { item: 'wood', price: 8, stock: 200 },
+ { item: 'stone', price: 6, stock: 150 },
+ { item: 'iron_ore', price: 20, stock: 50 }
+ ],
+ 'blacksmith': [
+ { item: 'iron_sword', price: 150, stock: 5 },
+ { item: 'iron_pickaxe', price: 100, stock: 10 },
+ { item: 'iron_axe', price: 120, stock: 8 }
+ ],
+ 'hunter': [
+ { item: 'arrow', price: 5, stock: 100 },
+ { item: 'bow', price: 80, stock: 3 },
+ { item: 'meat', price: 20, stock: 30 }
+ ]
+ };
+
+ console.log('๐ฅ NPCPopulationSystem initialized');
+ }
+
+ // Populate structures with NPCs
+ populateStructures(structureSystem) {
+ if (!structureSystem) return;
+
+ let npcsSpawned = 0;
+
+ // Spawn NPCs in structures (30% chance)
+ for (const structure of structureSystem.structures) {
+ if (Math.random() < 0.3) {
+ const npcType = this.selectNPCType(structure.biome);
+ this.spawnNPC(structure.x, structure.y, npcType, structure.biome);
+ npcsSpawned++;
+ }
+ }
+
+ // Always spawn special NPCs at landmarks
+ for (const landmark of structureSystem.landmarks) {
+ this.spawnNPC(landmark.x, landmark.y, 'quest_giver', landmark.type, true);
+ npcsSpawned++;
+ }
+
+ console.log(`โ
Spawned ${npcsSpawned} NPCs in structures`);
+ }
+
+ // Select NPC type for biome
+ selectNPCType(biome) {
+ const types = this.npcTypes[biome] || this.npcTypes['Grassland'];
+ return types[Math.floor(Math.random() * types.length)];
+ }
+
+ // Spawn single NPC
+ spawnNPC(x, y, type, biome, isQuestGiver = false) {
+ const npc = {
+ x,
+ y,
+ type,
+ biome,
+ isQuestGiver,
+ dialogIndex: 0,
+ hasQuest: isQuestGiver,
+ questCompleted: false,
+ sprite: null
+ };
+
+ this.npcs.push(npc);
+ return npc;
+ }
+
+ // Create NPC sprite (called when chunk loads)
+ createNPCSprite(npc, chunk) {
+ if (npc.sprite) return; // Already has sprite
+
+ const worldX = npc.x * 48 + 24;
+ const worldY = npc.y * 48 + 24;
+
+ // Create simple circle sprite for NPC
+ const color = this.getNPCColor(npc.type);
+ const sprite = this.scene.add.circle(worldX, worldY, 12, color);
+ sprite.setDepth(10 + worldY);
+
+ // Add name label
+ const label = this.scene.add.text(worldX, worldY - 20, npc.type, {
+ fontSize: '12px',
+ color: '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 4, y: 2 }
+ });
+ label.setOrigin(0.5);
+ label.setDepth(10 + worldY);
+
+ // Quest marker for quest givers
+ if (npc.isQuestGiver && !npc.questCompleted) {
+ const questMarker = this.scene.add.text(worldX, worldY - 35, '!', {
+ fontSize: '24px',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ questMarker.setOrigin(0.5);
+ questMarker.setDepth(10 + worldY);
+
+ npc.questMarker = questMarker;
+ if (chunk) chunk.sprites.push(questMarker);
+ }
+
+ npc.sprite = sprite;
+ npc.label = label;
+
+ if (chunk) {
+ chunk.sprites.push(sprite);
+ chunk.sprites.push(label);
+ }
+ }
+
+ // Get NPC color
+ getNPCColor(type) {
+ const colors = {
+ 'farmer': 0x8B4513,
+ 'merchant': 0xDAA520,
+ 'blacksmith': 0x696969,
+ 'hunter': 0x228B22,
+ 'nomad': 0xD2691E,
+ 'miner': 0x708090,
+ 'witch': 0x9370DB,
+ 'quest_giver': 0xFFD700
+ };
+ return colors[type] || 0x808080;
+ }
+
+ // Check for nearby NPCs
+ update(playerX, playerY) {
+ let nearestNPC = null;
+ let minDist = 3;
+
+ for (const npc of this.npcs) {
+ const dist = Math.sqrt((npc.x - playerX) ** 2 + (npc.y - playerY) ** 2);
+ if (dist < minDist) {
+ minDist = dist;
+ nearestNPC = npc;
+ }
+ }
+
+ if (nearestNPC && !this.currentNPC) {
+ this.showTalkPrompt(nearestNPC);
+ this.currentNPC = nearestNPC;
+ } else if (!nearestNPC && this.currentNPC) {
+ this.hideTalkPrompt();
+ this.currentNPC = null;
+ }
+ }
+
+ // Show prompt to talk
+ showTalkPrompt(npc) {
+ if (this.talkPrompt) return;
+
+ const promptText = npc.isQuestGiver
+ ? '๐ฌ Press T to talk (QUEST AVAILABLE)'
+ : `๐ฌ Press T to talk to ${npc.type}`;
+
+ this.talkPrompt = this.scene.add.text(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.height - 100,
+ promptText,
+ {
+ fontSize: '24px',
+ color: npc.isQuestGiver ? '#FFD700' : '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 20, y: 10 }
+ }
+ );
+ this.talkPrompt.setOrigin(0.5);
+ this.talkPrompt.setScrollFactor(0);
+ this.talkPrompt.setDepth(10000);
+ }
+
+ // Hide talk prompt
+ hideTalkPrompt() {
+ if (this.talkPrompt) {
+ this.talkPrompt.destroy();
+ this.talkPrompt = null;
+ }
+ }
+
+ // Talk to NPC (T key)
+ talkToNPC() {
+ if (!this.currentNPC) return;
+
+ const npc = this.currentNPC;
+
+ // Get dialog
+ const dialogs = this.dialogs[npc.type] || ["Hello, traveler!"];
+ const dialog = dialogs[npc.dialogIndex % dialogs.length];
+ npc.dialogIndex++;
+
+ // Show dialog box
+ this.showDialog(npc, dialog);
+ }
+
+ // Show dialog UI
+ showDialog(npc, text) {
+ // Close existing dialog
+ if (this.dialogBox) {
+ this.dialogBox.destroy();
+ this.dialogText.destroy();
+ this.dialogBox = null;
+ }
+
+ // Create dialog box
+ const centerX = this.scene.cameras.main.centerX;
+ const centerY = this.scene.cameras.main.height - 150;
+
+ this.dialogBox = this.scene.add.rectangle(
+ centerX, centerY,
+ 600, 120,
+ 0x000000, 0.8
+ );
+ this.dialogBox.setStrokeStyle(3, 0xFFFFFF);
+ this.dialogBox.setScrollFactor(0);
+ this.dialogBox.setDepth(10001);
+
+ this.dialogText = this.scene.add.text(
+ centerX, centerY - 30,
+ `${npc.type}:\n${text}`,
+ {
+ fontSize: '20px',
+ color: '#ffffff',
+ align: 'center',
+ wordWrap: { width: 550 }
+ }
+ );
+ this.dialogText.setOrigin(0.5, 0);
+ this.dialogText.setScrollFactor(0);
+ this.dialogText.setDepth(10002);
+
+ // Auto-close after 4 seconds
+ this.scene.time.delayedCall(4000, () => {
+ if (this.dialogBox) {
+ this.dialogBox.destroy();
+ this.dialogText.destroy();
+ this.dialogBox = null;
+ }
+ });
+ }
+
+ // Get statistics
+ getStats() {
+ const byBiome = {};
+ for (const npc of this.npcs) {
+ byBiome[npc.biome] = (byBiome[npc.biome] || 0) + 1;
+ }
+
+ return {
+ totalNPCs: this.npcs.length,
+ questGivers: this.npcs.filter(n => n.isQuestGiver).length,
+ byBiome
+ };
+ }
+
+ destroy() {
+ this.hideTalkPrompt();
+ if (this.dialogBox) {
+ this.dialogBox.destroy();
+ this.dialogText.destroy();
+ }
+ this.npcs.forEach(npc => {
+ if (npc.sprite) npc.sprite.destroy();
+ if (npc.label) npc.label.destroy();
+ if (npc.questMarker) npc.questMarker.destroy();
+ });
+ this.npcs = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/NPCPrivacySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/NPCPrivacySystem.js
new file mode 100644
index 000000000..e39266c50
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/NPCPrivacySystem.js
@@ -0,0 +1,454 @@
+/**
+ * NPC PRIVACY & HOME DECORATION SYSTEM
+ * Part of: Game Systems Expansion
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Hobby-based automatic interior generation
+ * - Door lock system (heart-based access)
+ * - NPC home visit mechanics
+ * - Relationship effects from visits
+ * - Privacy violations & consequences
+ */
+
+class NPCPrivacySystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Privacy settings
+ this.privacyLevels = {
+ PUBLIC: 0, // Anyone can enter (living room, kitchen)
+ FRIENDLY: 3, // 3+ hearts required
+ PRIVATE: 5, // 5+ hearts required (bedroom)
+ INTIMATE: 8, // 8+ hearts required (special rooms)
+ LOCKED: 10 // 10 hearts or marriage required
+ };
+
+ // Visit tracking
+ this.visitHistory = {};
+ this.lastVisitTimes = {};
+
+ // NPC home decorations (generated based on hobby)
+ this.npcHomes = {};
+ }
+
+ /**
+ * Generate NPC home interior based on hobby
+ */
+ generateNPCHome(npcId) {
+ const npc = this.game.npcs.get(npcId);
+ if (!npc) return null;
+
+ const hobby = npc.hobby;
+
+ // Base home structure
+ const home = {
+ npcId: npcId,
+ rooms: {
+ living_room: {
+ name: 'Living Room',
+ privacyLevel: this.privacyLevels.PUBLIC,
+ objects: this.generateLivingRoomObjects(hobby)
+ },
+ kitchen: {
+ name: 'Kitchen',
+ privacyLevel: this.privacyLevels.PUBLIC,
+ objects: this.generateKitchenObjects(hobby)
+ },
+ bedroom: {
+ name: 'Bedroom',
+ privacyLevel: this.privacyLevels.PRIVATE,
+ objects: this.generateBedroomObjects(hobby)
+ },
+ hobby_room: {
+ name: this.getHobbyRoomName(hobby),
+ privacyLevel: this.privacyLevels.FRIENDLY,
+ objects: this.generateHobbyRoomObjects(hobby)
+ }
+ },
+ visitCount: 0,
+ lastVisit: null
+ };
+
+ // Store home
+ this.npcHomes[npcId] = home;
+
+ return home;
+ }
+
+ /**
+ * Generate living room objects
+ */
+ generateLivingRoomObjects(hobby) {
+ const base = [
+ 'interior_table_small',
+ 'interior_bookshelf',
+ 'interior_gothic_lantern'
+ ];
+
+ // Hobby-specific additions
+ const hobbyAdditions = {
+ fishing: ['mounted_fish', 'fishing_net'],
+ farming: ['grain_sack', 'tool_rack'],
+ cooking: ['recipe_shelf'],
+ reading: ['bookshelf', 'reading_chair'],
+ music: ['guitar_stand', 'music_sheets'],
+ crafting: ['crafting_workshop']
+ };
+
+ return [...base, ...(hobbyAdditions[hobby] || [])];
+ }
+
+ /**
+ * Generate kitchen objects
+ */
+ generateKitchenObjects(hobby) {
+ const base = [
+ 'interior_kitchen_stove',
+ 'interior_kitchen_counter'
+ ];
+
+ if (hobby === 'cooking') {
+ base.push(
+ 'interior_kitchen_fridge',
+ 'interior_kitchen_sink',
+ 'interior_recipe_shelf'
+ );
+ }
+
+ return base;
+ }
+
+ /**
+ * Generate bedroom objects
+ */
+ generateBedroomObjects(hobby) {
+ const base = [
+ 'interior_bed_wooden',
+ 'interior_wardrobe',
+ 'interior_chest_locked'
+ ];
+
+ // Personal items based on hobby
+ const hobbyItems = {
+ zombie_worker: ['brain_jar', 'work_uniform'],
+ baker: ['flour_workspace', 'dough_tools'],
+ barber: ['dreadlock_kit', 'styling_tools'],
+ fisherman: ['tackle_box', 'fish_collection']
+ };
+
+ return [...base, ...(hobbyItems[hobby] || [])];
+ }
+
+ /**
+ * Generate hobby room objects
+ */
+ generateHobbyRoomObjects(hobby) {
+ const hobbyRooms = {
+ fishing: [
+ 'fishing_rod_rack',
+ 'bait_storage',
+ 'fish_tank',
+ 'mounted_trophy_fish'
+ ],
+ zombie_worker: [
+ 'brain_jar',
+ 'work_bench',
+ 'tool_storage',
+ 'miner_equipment'
+ ],
+ baker: [
+ 'flour_workspace',
+ 'mixing_bowls',
+ 'bread_storage',
+ 'recipe_collection'
+ ],
+ alchemy: [
+ 'interior_alchemy_bottle',
+ 'interior_brewing_cauldron',
+ 'interior_chemistry_set',
+ 'interior_potion_shelf'
+ ],
+ crafting: [
+ 'interior_crafting_workshop',
+ 'interior_tool_rack',
+ 'material_storage',
+ 'blueprint_table'
+ ],
+ reading: [
+ 'interior_bookshelf',
+ 'reading_chair',
+ 'ancient_manuscripts',
+ 'writing_desk'
+ ]
+ };
+
+ return hobbyRooms[hobby] || ['generic_workspace'];
+ }
+
+ /**
+ * Get hobby room name
+ */
+ getHobbyRoomName(hobby) {
+ const names = {
+ fishing: 'Fishing Den',
+ zombie_worker: 'Worker\'s Quarters',
+ baker: 'Baking Workshop',
+ alchemy: 'Alchemy Lab',
+ crafting: 'Craft Room',
+ reading: 'Library',
+ music: 'Music Studio',
+ farming: 'Storage Shed'
+ };
+
+ return names[hobby] || 'Hobby Room';
+ }
+
+ /**
+ * Attempt to enter NPC room
+ */
+ attemptEntry(npcId, roomId) {
+ const npc = this.game.npcs.get(npcId);
+ if (!npc) {
+ return { success: false, message: 'NPC not found!' };
+ }
+
+ // Generate home if doesn't exist
+ if (!this.npcHomes[npcId]) {
+ this.generateNPCHome(npcId);
+ }
+
+ const home = this.npcHomes[npcId];
+ const room = home.rooms[roomId];
+
+ if (!room) {
+ return { success: false, message: 'Room not found!' };
+ }
+
+ // Check privacy level
+ const playerHearts = this.player.getRelationshipHearts(npcId);
+ const requiredHearts = room.privacyLevel;
+
+ if (playerHearts < requiredHearts) {
+ // Privacy violation!
+ return this.handlePrivacyViolation(npcId, roomId, requiredHearts);
+ }
+
+ // Allowed entry
+ return this.handleSuccessfulEntry(npcId, roomId);
+ }
+
+ /**
+ * Handle privacy violation
+ */
+ handlePrivacyViolation(npcId, roomId, requiredHearts) {
+ const npc = this.game.npcs.get(npcId);
+
+ // Relationship penalty
+ const penalty = (requiredHearts - this.player.getRelationshipHearts(npcId)) * 20;
+ npc.addRelationshipPoints(-penalty);
+
+ // NPC reaction
+ const reactions = [
+ "Hey! That's private!",
+ "What are you doing in my room?!",
+ "Get out! This is MY space!",
+ "I can't believe you just walked in here...",
+ "Privacy, please!"
+ ];
+
+ const reaction = Phaser.Utils.Array.GetRandom(reactions);
+
+ this.game.showDialogue(npc.name, reaction, {
+ mood: 'angry',
+ animation: 'shocked'
+ });
+
+ this.game.showMessage(
+ `${npc.name} is upset! -${penalty} relationship points`
+ );
+
+ // Player gets kicked out
+ this.game.player.teleportToLocation('outside_' + npcId + '_home');
+
+ return {
+ success: false,
+ privacyViolation: true,
+ penalty: penalty,
+ requiredHearts: requiredHearts,
+ currentHearts: this.player.getRelationshipHearts(npcId)
+ };
+ }
+
+ /**
+ * Handle successful entry
+ */
+ handleSuccessfulEntry(npcId, roomId) {
+ const npc = this.game.npcs.get(npcId);
+ const home = this.npcHomes[npcId];
+
+ // Track visit
+ if (!this.visitHistory[npcId]) {
+ this.visitHistory[npcId] = [];
+ }
+
+ const visit = {
+ roomId: roomId,
+ timestamp: this.game.time.currentTime,
+ timeOfDay: this.game.time.getTimeOfDay()
+ };
+
+ this.visitHistory[npcId].push(visit);
+ this.lastVisitTimes[npcId] = this.game.time.currentTime;
+
+ home.visitCount++;
+ home.lastVisit = this.game.time.currentTime;
+
+ // Relationship effects based on visit
+ this.applyVisitEffects(npcId, roomId, visit);
+
+ // Enter room scene
+ this.game.scene.start('NPCRoomScene', {
+ npcId: npcId,
+ roomId: roomId,
+ room: home.rooms[roomId]
+ });
+
+ return {
+ success: true,
+ room: home.rooms[roomId],
+ npc: npc
+ };
+ }
+
+ /**
+ * Apply relationship effects from visit
+ */
+ applyVisitEffects(npcId, roomId, visit) {
+ const npc = this.game.npcs.get(npcId);
+ const timeOfDay = visit.timeOfDay;
+
+ // Visit frequency check
+ const recentVisits = this.visitHistory[npcId].filter(v => {
+ const hoursSince = (this.game.time.currentTime - v.timestamp) / 3600;
+ return hoursSince < 24; // Last 24 hours
+ });
+
+ if (recentVisits.length > 3) {
+ // Visiting TOO much = annoying
+ npc.addRelationshipPoints(-10);
+ this.game.showMessage(
+ `${npc.name} seems a bit annoyed by frequent visits...`
+ );
+ return;
+ }
+
+ // Time of day effects
+ if (timeOfDay === 'night' && roomId === 'bedroom') {
+ // Visiting bedroom at night = awkward
+ npc.addRelationshipPoints(-5);
+ this.game.showDialogue(
+ npc.name,
+ "It's quite late... Is everything okay?",
+ { mood: 'concerned' }
+ );
+ } else if (timeOfDay === 'morning' && roomId === 'kitchen') {
+ // Morning kitchen visit = breakfast together!
+ npc.addRelationshipPoints(5);
+ this.game.showDialogue(
+ npc.name,
+ "Good morning! Care to join me for breakfast?",
+ { mood: 'happy' }
+ );
+ } else if (roomId === 'hobby_room') {
+ // Showing interest in hobby = bonus points!
+ npc.addRelationshipPoints(10);
+ this.game.showDialogue(
+ npc.name,
+ "I'm glad you're interested in my hobby!",
+ { mood: 'excited' }
+ );
+ }
+ }
+
+ /**
+ * Get visit statistics for NPC
+ */
+ getVisitStats(npcId) {
+ const visits = this.visitHistory[npcId] || [];
+
+ // Count visits per room
+ const roomCounts = {};
+ visits.forEach(visit => {
+ roomCounts[visit.roomId] = (roomCounts[visit.roomId] || 0) + 1;
+ });
+
+ // Average visits per day
+ const daysSinceFirstVisit = visits.length > 0
+ ? (this.game.time.currentTime - visits[0].timestamp) / 86400
+ : 0;
+
+ const avgVisitsPerDay = daysSinceFirstVisit > 0
+ ? visits.length / daysSinceFirstVisit
+ : 0;
+
+ return {
+ totalVisits: visits.length,
+ roomCounts: roomCounts,
+ avgVisitsPerDay: avgVisitsPerDay,
+ lastVisit: this.lastVisitTimes[npcId],
+ favoriteRoom: Object.keys(roomCounts).reduce((a, b) =>
+ roomCounts[a] > roomCounts[b] ? a : b, null)
+ };
+ }
+
+ /**
+ * Check if player can access special room
+ */
+ canAccessSpecialRoom(npcId, roomId) {
+ const npc = this.game.npcs.get(npcId);
+ if (!npc) return false;
+
+ const home = this.npcHomes[npcId];
+ if (!home) return false;
+
+ const room = home.rooms[roomId];
+ if (!room) return false;
+
+ const playerHearts = this.player.getRelationshipHearts(npcId);
+
+ // Special case: marriage allows LOCKED access
+ if (room.privacyLevel === this.privacyLevels.LOCKED) {
+ return this.player.spouse === npcId;
+ }
+
+ return playerHearts >= room.privacyLevel;
+ }
+
+ /**
+ * Get NPC home UI data
+ */
+ getNPCHomeUIData(npcId) {
+ if (!this.npcHomes[npcId]) {
+ this.generateNPCHome(npcId);
+ }
+
+ const home = this.npcHomes[npcId];
+ const npc = this.game.npcs.get(npcId);
+
+ return {
+ npc: npc,
+ home: home,
+ accessibleRooms: Object.keys(home.rooms).filter(roomId =>
+ this.canAccessSpecialRoom(npcId, roomId)
+ ),
+ lockedRooms: Object.keys(home.rooms).filter(roomId =>
+ !this.canAccessSpecialRoom(npcId, roomId)
+ ),
+ visitStats: this.getVisitStats(npcId)
+ };
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/NPCSettlementSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/NPCSettlementSystem.js
new file mode 100644
index 000000000..4aff1090d
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/NPCSettlementSystem.js
@@ -0,0 +1,341 @@
+/**
+ * NPC SETTLEMENT SYSTEM (Magic Helpers)
+ * NPCs auto-assist with tasks
+ * Worker efficiency, happiness, housing
+ */
+
+export class NPCSettlementSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Settled NPCs
+ this.settledNPCs = new Map();
+
+ // Worker assignments
+ this.assignments = new Map(); // npcId โ task
+
+ // NPC stats
+ this.npcHappiness = new Map(); // npcId โ happiness (0-100)
+ this.npcEfficiency = new Map(); // npcId โ efficiency (0.5-2.0)
+
+ // Housing
+ this.npcHomes = new Map(); // npcId โ buildingId
+ this.housingCapacity = 0;
+
+ // Task types
+ this.taskTypes = ['farming', 'building', 'crafting', 'defense', 'gathering'];
+
+ this.init();
+ }
+
+ init() {
+ // Update worker tasks periodically
+ this.scene.time.addEvent({
+ delay: 5000, // Every 5s
+ callback: () => this.updateWorkers(),
+ loop: true
+ });
+
+ // Update happiness hourly
+ this.scene.time.addEvent({
+ delay: 3600000, // 1 hour
+ callback: () => this.updateHappiness(),
+ loop: true
+ });
+ }
+
+ /**
+ * Settle an NPC (they join the town)
+ */
+ settleNPC(npcId, npcData) {
+ if (this.settledNPCs.has(npcId)) return false;
+
+ //Check housing availability
+ if (this.settledNPCs.size >= this.housingCapacity) {
+ console.warn('No housing available');
+ return false;
+ }
+
+ this.settledNPCs.set(npcId, {
+ id: npcId,
+ name: npcData.name,
+ skills: npcData.skills || [],
+ assignedTask: null,
+ ...npcData
+ });
+
+ // Initial stats
+ this.npcHappiness.set(npcId, 75); // Start at 75% happiness
+ this.npcEfficiency.set(npcId, 1.0); // Base efficiency
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `${npcData.name} has joined the settlement!`,
+ 'success',
+ { icon: 'npc_join' }
+ );
+
+ console.log(`๐๏ธ ${npcData.name} settled in town`);
+ return true;
+ }
+
+ /**
+ * Assign NPC to task
+ */
+ assignTask(npcId, taskType, taskData) {
+ const npc = this.settledNPCs.get(npcId);
+ if (!npc) return false;
+
+ // Check if NPC has skill for task
+ if (!npc.skills.includes(taskType)) {
+ console.warn(`${npc.name} lacks skill: ${taskType}`);
+ return false;
+ }
+
+ // Assign
+ npc.assignedTask = taskType;
+ this.assignments.set(npcId, {
+ type: taskType,
+ data: taskData,
+ startTime: Date.now()
+ });
+
+ console.log(`๐ท ${npc.name} assigned to ${taskType}`);
+ return true;
+ }
+
+ /**
+ * Unassign NPC from task
+ */
+ unassignTask(npcId) {
+ const npc = this.settledNPCs.get(npcId);
+ if (!npc) return false;
+
+ npc.assignedTask = null;
+ this.assignments.delete(npcId);
+
+ console.log(`${npc.name} unassigned`);
+ return true;
+ }
+
+ /**
+ * Update all worker tasks
+ */
+ updateWorkers() {
+ this.assignments.forEach((assignment, npcId) => {
+ const npc = this.settledNPCs.get(npcId);
+ const efficiency = this.npcEfficiency.get(npcId) || 1.0;
+
+ switch (assignment.type) {
+ case 'farming':
+ this.performFarming(npc, efficiency, assignment.data);
+ break;
+ case 'building':
+ this.performBuilding(npc, efficiency, assignment.data);
+ break;
+ case 'crafting':
+ this.performCrafting(npc, efficiency, assignment.data);
+ break;
+ case 'defense':
+ this.performDefense(npc, efficiency);
+ break;
+ case 'gathering':
+ this.performGathering(npc, efficiency);
+ break;
+ }
+ });
+ }
+
+ /**
+ * WORKER TASKS
+ */
+
+ performFarming(npc, efficiency, data) {
+ // Auto-tend crops
+ const crops = this.scene.crops || [];
+ const tendsPerCycle = Math.floor(2 * efficiency);
+
+ for (let i = 0; i < tendsPerCycle && i < crops.length; i++) {
+ const crop = crops[i];
+ crop.water?.();
+ crop.fertilize?.();
+ }
+
+ // Chance to auto-harvest
+ if (Math.random() < 0.1 * efficiency) {
+ const harvestable = crops.find(c => c.isHarvestable);
+ if (harvestable) {
+ this.scene.farmingSystem?.harvestCrop(harvestable);
+ console.log(`${npc.name} harvested crop`);
+ }
+ }
+ }
+
+ performBuilding(npc, efficiency, data) {
+ // Speed up construction
+ const building = data.buildingId ? this.scene.buildingSystem?.getBuilding(data.buildingId) : null;
+
+ if (building && building.isUnderConstruction) {
+ const progressBonus = 5 * efficiency;
+ building.constructionProgress += progressBonus;
+
+ if (building.constructionProgress >= 100) {
+ this.scene.buildingSystem?.completeConstruction(building.id);
+ console.log(`${npc.name} completed building: ${building.name}`);
+ }
+ }
+ }
+
+ performCrafting(npc, efficiency, data) {
+ // Auto-craft items
+ if (data.recipe) {
+ const canCraft = this.scene.craftingSystem?.canCraft(data.recipe);
+ if (canCraft && Math.random() < 0.2 * efficiency) {
+ this.scene.craftingSystem?.craft(data.recipe);
+ console.log(`${npc.name} crafted ${data.recipe}`);
+ }
+ }
+ }
+
+ performDefense(npc, efficiency) {
+ // Patrol and defend
+ // Increased detection range for raids
+ this.scene.gameState.buffs.raid_detection_range = (this.scene.gameState.buffs.raid_detection_range || 1.0) + (0.1 * efficiency);
+
+ // Auto-repair walls/defenses
+ const defenses = this.scene.defenseSystem?.getAllDefenses() || [];
+ defenses.forEach(defense => {
+ if (defense.health < defense.maxHealth && Math.random() < 0.05 * efficiency) {
+ defense.health = Math.min(defense.maxHealth, defense.health + 10);
+ console.log(`${npc.name} repaired ${defense.name}`);
+ }
+ });
+ }
+
+ performGathering(npc, efficiency) {
+ // Auto-gather resources
+ const gatherChance = 0.15 * efficiency;
+
+ if (Math.random() < gatherChance) {
+ const resources = ['wood', 'stone', 'berries', 'herbs'];
+ const resource = Phaser.Utils.Array.GetRandom(resources);
+ const amount = Math.floor(Phaser.Math.Between(1, 3) * efficiency);
+
+ this.scene.inventorySystem?.addItem(resource, amount);
+ console.log(`${npc.name} gathered ${amount} ${resource}`);
+ }
+ }
+
+ /**
+ * Update NPC happiness
+ */
+ updateHappiness() {
+ this.settledNPCs.forEach((npc, npcId) => {
+ let happiness = this.npcHappiness.get(npcId) || 50;
+
+ // Factors affecting happiness
+ const hasHome = this.npcHomes.has(npcId);
+ const hasTask = npc.assignedTask !== null;
+ const isOverworked = hasTask && this.assignments.get(npcId)?.overworked;
+
+ // Adjustments
+ if (hasHome) happiness += 5;
+ if (hasTask) happiness += 2;
+ if (isOverworked) happiness -= 10;
+ if (!hasHome && this.settledNPCs.size > this.housingCapacity) happiness -= 15;
+
+ // Natural decay
+ happiness -= 1;
+
+ // Clamp
+ happiness = Math.max(0, Math.min(100, happiness));
+ this.npcHappiness.set(npcId, happiness);
+
+ // Update efficiency based on happiness
+ const efficiency = 0.5 + (happiness / 100) * 1.5; // 0.5-2.0
+ this.npcEfficiency.set(npcId, efficiency);
+
+ // Low happiness warning
+ if (happiness < 30) {
+ this.scene.uiSystem?.showNotification(
+ `${npc.name} is unhappy!`,
+ 'warning'
+ );
+ console.warn(`${npc.name} happiness: ${happiness}`);
+ }
+ });
+ }
+
+ /**
+ * Assign NPC to home
+ */
+ assignHome(npcId, buildingId) {
+ this.npcHomes.set(npcId, buildingId);
+
+ // Happiness boost
+ const happiness = this.npcHappiness.get(npcId) || 50;
+ this.npcHappiness.set(npcId, Math.min(100, happiness + 20));
+
+ console.log(`${this.settledNPCs.get(npcId).name} moved into home`);
+ }
+
+ /**
+ * Increase housing capacity
+ */
+ addHousing(capacity) {
+ this.housingCapacity += capacity;
+ console.log(`Housing capacity: ${this.housingCapacity}`);
+ }
+
+ /**
+ * Get settlement statistics
+ */
+ getSettlementStats() {
+ const npcs = Array.from(this.settledNPCs.values());
+ const avgHappiness = npcs.reduce((sum, npc) => sum + (this.npcHappiness.get(npc.id) || 0), 0) / npcs.length || 0;
+ const avgEfficiency = npcs.reduce((sum, npc) => sum + (this.npcEfficiency.get(npc.id) || 1), 0) / npcs.length || 1;
+
+ return {
+ population: npcs.length,
+ housingCapacity: this.housingCapacity,
+ averageHappiness: Math.round(avgHappiness),
+ averageEfficiency: avgEfficiency.toFixed(2),
+ assignedWorkers: this.assignments.size,
+ unemployed: npcs.length - this.assignments.size
+ };
+ }
+
+ /**
+ * Get NPCs by skill
+ */
+ getNPCsBySkill(skill) {
+ return Array.from(this.settledNPCs.values()).filter(npc => npc.skills.includes(skill));
+ }
+
+ /**
+ * Save/Load
+ */
+ getSaveData() {
+ return {
+ settledNPCs: Array.from(this.settledNPCs.entries()),
+ assignments: Array.from(this.assignments.entries()),
+ npcHappiness: Array.from(this.npcHappiness.entries()),
+ npcHomes: Array.from(this.npcHomes.entries()),
+ housingCapacity: this.housingCapacity
+ };
+ }
+
+ loadSaveData(data) {
+ this.settledNPCs = new Map(data.settledNPCs || []);
+ this.assignments = new Map(data.assignments || []);
+ this.npcHappiness = new Map(data.npcHappiness || []);
+ this.npcHomes = new Map(data.npcHomes || []);
+ this.housingCapacity = data.housingCapacity || 0;
+
+ // Recalculate efficiency
+ this.npcHappiness.forEach((happiness, npcId) => {
+ const efficiency = 0.5 + (happiness / 100) * 1.5;
+ this.npcEfficiency.set(npcId, efficiency);
+ });
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/NPCShopSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/NPCShopSystem.js
new file mode 100644
index 000000000..e06897e18
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/NPCShopSystem.js
@@ -0,0 +1,503 @@
+/**
+ * NPCShopSystem.js
+ * ================
+ * KRVAVA ลฝETEV - NPC Trading & Shop System (Phase 38)
+ *
+ * Features:
+ * - 4 NPC shop types (Blacksmith, Baker, Trader, Healer)
+ * - Shop UI with buy/sell
+ * - Dynamic pricing
+ * - Stock management
+ * - Relationship discounts
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class NPCShopSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Shop registry
+ this.shops = new Map();
+ this.currentShop = null;
+
+ // Shop UI
+ this.shopContainer = null;
+ this.isShopOpen = false;
+
+ // Player inventory reference
+ this.playerInventory = null;
+ this.playerZlatniki = 0;
+
+ console.log('๐ NPCShopSystem initialized');
+
+ // Register all shops
+ this.registerShops();
+
+ // Create shop UI
+ this.createShopUI();
+ }
+
+ /**
+ * Register all NPC shops
+ */
+ registerShops() {
+ const shops = [
+ {
+ id: 'blacksmith',
+ name: 'Kovaฤ (Blacksmith)',
+ npc: 'Ivan the Blacksmith',
+ icon: 'โ๏ธ',
+ location: { x: 200, y: 200 },
+ inventory: [
+ // Tools
+ { id: 'iron_axe', name: 'Iron Axe', price: 200, stock: 5, category: 'tools', locked: true },
+ { id: 'iron_pickaxe', name: 'Iron Pickaxe', price: 200, stock: 5, category: 'tools', locked: true },
+ { id: 'iron_hoe', name: 'Iron Hoe', price: 150, stock: 5, category: 'tools', locked: true },
+ { id: 'watering_can', name: 'Watering Can', price: 100, stock: 10, category: 'tools' },
+
+ // Weapons
+ { id: 'iron_sword', name: 'Iron Sword', price: 500, stock: 3, category: 'weapons', locked: true },
+ { id: 'steel_sword', name: 'Steel Sword', price: 1000, stock: 2, category: 'weapons', locked: true },
+ { id: 'crossbow', name: 'Crossbow', price: 800, stock: 2, category: 'weapons', locked: true },
+
+
+ // Armor
+ { id: 'leather_armor', name: 'Leather Armor', price: 300, stock: 5, category: 'armor' },
+ { id: 'iron_armor', name: 'Iron Armor', price: 800, stock: 3, category: 'armor' }
+ ],
+ buyback: ['iron_ore', 'steel_bar', 'scrap_metal']
+ },
+ {
+ id: 'baker',
+ name: 'Pekarica (Baker)',
+ npc: 'Maria the Baker',
+ icon: '๐',
+ location: { x: 250, y: 200 },
+ inventory: [
+ // Food
+ { id: 'bread', name: 'Bread', price: 10, stock: 50, category: 'food' },
+ { id: 'cheese', name: 'Cheese', price: 20, stock: 30, category: 'food' },
+ { id: 'apple_pie', name: 'Apple Pie', price: 50, stock: 20, category: 'food' },
+ { id: 'cake', name: 'Cake', price: 100, stock: 10, category: 'food' },
+
+ // Recipes
+ { id: 'recipe_cookies', name: 'Cookie Recipe', price: 200, stock: 1, category: 'recipes' },
+ { id: 'recipe_pizza', name: 'Pizza Recipe', price: 300, stock: 1, category: 'recipes' },
+
+ // Ingredients
+ { id: 'flour', name: 'Flour', price: 15, stock: 100, category: 'ingredients' },
+ { id: 'sugar', name: 'Sugar', price: 20, stock: 80, category: 'ingredients' },
+ { id: 'yeast', name: 'Yeast', price: 10, stock: 50, category: 'ingredients' }
+ ],
+ buyback: ['wheat', 'milk', 'eggs', 'berries']
+ },
+ {
+ id: 'trader',
+ name: 'Trgovec (General Trader)',
+ npc: 'Gregor the Trader',
+ icon: '๐ฐ',
+ location: { x: 300, y: 200 },
+ inventory: [
+ // Seeds
+ { id: 'wheat_seeds', name: 'Wheat Seeds', price: 5, stock: 200, category: 'seeds' },
+ { id: 'corn_seeds', name: 'Corn Seeds', price: 8, stock: 150, category: 'seeds' },
+ { id: 'tomato_seeds', name: 'Tomato Seeds', price: 10, stock: 100, category: 'seeds' },
+ { id: 'strawberry_seeds', name: 'Strawberry Seeds', price: 15, stock: 80, category: 'seeds' },
+ { id: 'cannabis_seeds', name: 'Cannabis Seeds', price: 20, stock: 50, category: 'seeds' },
+
+ // Materials
+ { id: 'wood', name: 'Wood', price: 10, stock: 500, category: 'materials' },
+ { id: 'stone', name: 'Stone', price: 15, stock: 300, category: 'materials' },
+ { id: 'clay', name: 'Clay', price: 20, stock: 200, category: 'materials' },
+
+ // Special
+ { id: 'saddle', name: 'Saddle', price: 500, stock: 2, category: 'special' },
+ { id: 'bouquet', name: 'Bouquet', price: 100, stock: 10, category: 'special' },
+ { id: 'mermaid_pendant', name: 'Mermaid Pendant', price: 5000, stock: 1, category: 'special' }
+ ],
+ buyback: ['crops', 'foraged_items', 'fish']
+ },
+ {
+ id: 'healer',
+ name: 'Zdravnik (Healer)',
+ npc: 'Dr. Ana Kovaฤ',
+ icon: 'โ๏ธ',
+ location: { x: 350, y: 200 },
+ inventory: [
+ // Potions
+ { id: 'health_potion', name: 'Health Potion', price: 50, stock: 50, category: 'potions' },
+ { id: 'stamina_potion', name: 'Stamina Potion', price: 40, stock: 50, category: 'potions' },
+ { id: 'antidote', name: 'Antidote', price: 30, stock: 30, category: 'potions' },
+ { id: 'cure_infection', name: 'Cure Infection', price: 200, stock: 10, category: 'potions' },
+
+ // Research
+ { id: 'cure_research_1', name: 'Cure Research Notes I', price: 1000, stock: 1, category: 'research' },
+ { id: 'cure_research_2', name: 'Cure Research Notes II', price: 2000, stock: 1, category: 'research' },
+
+ // Medical supplies
+ { id: 'bandage', name: 'Bandage', price: 15, stock: 100, category: 'medical' },
+ { id: 'medicine', name: 'Medicine', price: 80, stock: 30, category: 'medical' }
+ ],
+ buyback: ['herbs', 'mushrooms', 'zombie_samples', 'cannabis', 'cannabis_buds']
+ }
+ ];
+
+ shops.forEach(shop => this.shops.set(shop.id, shop));
+
+ console.log(`โ
Registered ${this.shops.size} NPC shops`);
+ }
+
+ /**
+ * Create shop UI
+ */
+ createShopUI() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Main container
+ this.shopContainer = this.scene.add.container(width / 2, height / 2);
+ this.shopContainer.setScrollFactor(0);
+ this.shopContainer.setDepth(10000);
+ this.shopContainer.setVisible(false);
+
+ // Background
+ const bg = this.scene.add.rectangle(0, 0, 900, 600, 0x1a1a1a, 0.95);
+ bg.setStrokeStyle(3, 0xFFD700);
+ this.shopContainer.add(bg);
+
+ // Title (will be updated)
+ this.shopTitle = this.scene.add.text(0, -280, '๐ SHOP', {
+ fontSize: '32px',
+ fontFamily: 'Arial',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ this.shopTitle.setOrigin(0.5);
+ this.shopContainer.add(this.shopTitle);
+
+ // Close button
+ const closeBtn = this.scene.add.text(430, -280, 'โ', {
+ fontSize: '24px',
+ cursor: 'pointer'
+ });
+ closeBtn.setInteractive();
+ closeBtn.on('pointerdown', () => this.closeShop());
+ this.shopContainer.add(closeBtn);
+
+ // Player money display
+ this.moneyText = this.scene.add.text(-430, -250, '๐ฐ 0 Zlatniki', {
+ fontSize: '18px',
+ fontFamily: 'Arial',
+ color: '#FFD700'
+ });
+ this.shopContainer.add(this.moneyText);
+
+ // Category tabs
+ this.createCategoryTabs();
+
+ // Item list container
+ this.itemListContainer = this.scene.add.container(0, 0);
+ this.shopContainer.add(this.itemListContainer);
+
+ console.log('โ
Shop UI created');
+ }
+
+ /**
+ * Create category tabs
+ */
+ createCategoryTabs() {
+ const categories = ['all', 'tools', 'weapons', 'food', 'seeds', 'potions'];
+ const tabWidth = 120;
+ const startX = -400;
+ const y = -200;
+
+ categories.forEach((category, index) => {
+ const tab = this.scene.add.rectangle(
+ startX + (index * tabWidth),
+ y,
+ 110, 40,
+ 0x2d2d2d
+ );
+ tab.setStrokeStyle(2, 0x666666);
+ tab.setInteractive();
+ tab.on('pointerdown', () => this.filterByCategory(category));
+
+ const label = this.scene.add.text(
+ startX + (index * tabWidth),
+ y,
+ category.toUpperCase(),
+ {
+ fontSize: '14px',
+ fontFamily: 'Arial',
+ color: '#ffffff'
+ }
+ );
+ label.setOrigin(0.5);
+
+ this.shopContainer.add(tab);
+ this.shopContainer.add(label);
+ });
+ }
+
+ /**
+ * Open shop
+ */
+ openShop(shopId) {
+ const shop = this.shops.get(shopId);
+ if (!shop) {
+ console.error(`Shop ${shopId} not found!`);
+ return false;
+ }
+
+ this.currentShop = shop;
+ this.isShopOpen = true;
+
+ // Update title
+ this.shopTitle.setText(`${shop.icon} ${shop.name}`);
+
+ // Update money
+ this.updateMoneyDisplay();
+
+ // Display items
+ this.displayShopItems(shop.inventory);
+
+ // Show container
+ this.shopContainer.setVisible(true);
+
+ console.log(`๐ Opened ${shop.name}`);
+
+ return true;
+ }
+
+ /**
+ * Close shop
+ */
+ closeShop() {
+ this.isShopOpen = false;
+ this.currentShop = null;
+ this.shopContainer.setVisible(false);
+
+ console.log('๐ Shop closed');
+ }
+
+ /**
+ * Display shop items
+ */
+ displayShopItems(items, filter = 'all') {
+ // Clear previous items
+ this.itemListContainer.removeAll(true);
+
+ // Filter items
+ let filteredItems = items;
+ if (filter !== 'all') {
+ filteredItems = items.filter(item => item.category === filter);
+ }
+
+ // Display items (max 10 visible, scrollable)
+ const itemHeight = 50;
+ const startY = -150;
+
+ filteredItems.slice(0, 10).forEach((item, index) => {
+ const y = startY + (index * itemHeight);
+
+ // Item background
+ const itemBg = this.scene.add.rectangle(-400, y, 850, 45, 0x2d2d2d, 0.8);
+ itemBg.setStrokeStyle(1, 0x444444);
+ this.itemListContainer.add(itemBg);
+
+ // Item name
+ const nameText = this.scene.add.text(-380, y - 10, item.name, {
+ fontSize: '16px',
+ fontFamily: 'Arial',
+ color: '#ffffff'
+ });
+ this.itemListContainer.add(nameText);
+
+ // Stock
+ const stockText = this.scene.add.text(-380, y + 10, `Stock: ${item.stock}`, {
+ fontSize: '12px',
+ fontFamily: 'Arial',
+ color: '#888888'
+ });
+ this.itemListContainer.add(stockText);
+
+ // Price
+ const priceText = this.scene.add.text(200, y, `${item.price} ลฝ`, {
+ fontSize: '18px',
+ fontFamily: 'Arial',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ priceText.setOrigin(0.5);
+ this.itemListContainer.add(priceText);
+
+ // Buy button
+ const isLocked = item.locked && !this.isShopRestored(this.currentShop.id);
+ const buyColor = isLocked ? 0x666666 : 0x228B22;
+ const buyBtn = this.scene.add.rectangle(350, y, 100, 35, buyColor);
+ buyBtn.setStrokeStyle(2, isLocked ? 0x999999 : 0x32CD32);
+ buyBtn.setInteractive();
+ buyBtn.on('pointerdown', () => {
+ if (isLocked) {
+ this.showNotification({
+ title: 'Shop Ruined',
+ text: `Restore the shop to unlock this item!`,
+ icon: '๐๏ธ'
+ });
+ } else {
+ this.buyItem(item);
+ }
+ });
+ this.itemListContainer.add(buyBtn);
+
+ const buyText = this.scene.add.text(350, y, isLocked ? 'LOCKED' : 'BUY', {
+ fontSize: '14px',
+ fontFamily: 'Arial',
+ color: '#ffffff',
+ fontStyle: 'bold'
+ });
+ buyText.setOrigin(0.5);
+ this.itemListContainer.add(buyText);
+ });
+ }
+
+ isShopRestored(shopId) {
+ if (!this.scene.townRestorationSystem) return true; // Fallback if system missing
+
+ // Map shopId to buildingId
+ const mapping = {
+ 'blacksmith': 'jakob_shop', // or whichever ID correctly matches TownRestorationSystem
+ 'baker': 'lena_bakery',
+ 'healer': 'dr_chen_clinic'
+ };
+
+ const buildingId = mapping[shopId];
+ if (!buildingId) return true; // General trader is always open
+
+ const building = this.scene.townRestorationSystem.buildings.get(buildingId);
+ return building ? building.isRestored : false;
+ }
+
+
+ /**
+ * Filter by category
+ */
+ filterByCategory(category) {
+ if (!this.currentShop) return;
+ this.displayShopItems(this.currentShop.inventory, category);
+ }
+
+ /**
+ * Buy item
+ */
+ buyItem(item) {
+ // Check stock
+ if (item.stock <= 0) {
+ this.showNotification({
+ title: 'Out of Stock',
+ text: `${item.name} is out of stock!`,
+ icon: '๐ฆ'
+ });
+ return false;
+ }
+
+ // Calculate price with relationship discount
+ const finalPrice = this.calculatePrice(item.price);
+
+ // Check if player can afford
+ if (this.playerZlatniki < finalPrice) {
+ this.showNotification({
+ title: 'Not Enough Money',
+ text: `Need ${finalPrice}ลฝ to buy ${item.name}!`,
+ icon: '๐ฐ'
+ });
+ return false;
+ }
+
+ // Purchase!
+ this.playerZlatniki -= finalPrice;
+ item.stock--;
+
+ // TODO: Add item to player inventory
+ console.log(`โ
Purchased: ${item.name} for ${finalPrice}ลฝ`);
+
+ // Update UI
+ this.updateMoneyDisplay();
+ this.displayShopItems(this.currentShop.inventory);
+
+ this.showNotification({
+ title: 'Purchase Complete!',
+ text: `Bought ${item.name} for ${finalPrice}ลฝ!`,
+ icon: 'โ
'
+ });
+
+ return true;
+ }
+
+ /**
+ * Calculate price with discounts
+ */
+ calculatePrice(basePrice) {
+ // TODO: Apply relationship discounts
+ // For now, return base price
+ return basePrice;
+ }
+
+ /**
+ * Update money display
+ */
+ updateMoneyDisplay() {
+ this.moneyText.setText(`๐ฐ ${this.playerZlatniki} Zlatniki`);
+ }
+
+ /**
+ * Set player money
+ */
+ setPlayerMoney(amount) {
+ this.playerZlatniki = amount;
+ this.updateMoneyDisplay();
+ }
+
+ /**
+ * Get shop info
+ */
+ getShopInfo(shopId) {
+ return this.shops.get(shopId);
+ }
+
+ /**
+ * Get all shops
+ */
+ getAllShops() {
+ return Array.from(this.shops.values());
+ }
+
+ /**
+ * Restock shop
+ */
+ restockShop(shopId) {
+ const shop = this.shops.get(shopId);
+ if (!shop) return false;
+
+ shop.inventory.forEach(item => {
+ item.stock = Math.min(item.stock + 5, 100); // Restock +5, max 100
+ });
+
+ console.log(`๐ฆ ${shop.name} restocked!`);
+ return true;
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/NoirCitySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/NoirCitySystem.js
new file mode 100644
index 000000000..17090a99e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/NoirCitySystem.js
@@ -0,0 +1,385 @@
+/**
+ * LIVING NOIR CITY - AMBIENT WORLD SYSTEM
+ * Stray cats, barking dogs, atmospheric sounds
+ */
+
+class NoirCitySystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.animals = [];
+ this.ambientSounds = [];
+ this.isActive = false;
+ }
+
+ /**
+ * INITIALIZE CITY ATMOSPHERE
+ */
+ init() {
+ this.spawnStrayAnimals();
+ this.setupAmbientSounds();
+ this.startAtmosphere();
+ this.isActive = true;
+
+ console.log('๐ Noir city atmosphere activated');
+ }
+
+ /**
+ * SPAWN STRAY CATS
+ */
+ spawnStrayAnimals() {
+ // Spawn 3-5 stray cats in random locations
+ const catCount = Phaser.Math.Between(3, 5);
+
+ for (let i = 0; i < catCount; i++) {
+ this.spawnCat();
+ }
+
+ // Spawn 2-3 dogs in shadows
+ const dogCount = Phaser.Math.Between(2, 3);
+
+ for (let i = 0; i < dogCount; i++) {
+ this.spawnDog();
+ }
+ }
+
+ /**
+ * SPAWN A STRAY CAT
+ */
+ spawnCat() {
+ const x = Phaser.Math.Between(100, this.scene.cameras.main.width - 100);
+ const y = Phaser.Math.Between(100, this.scene.cameras.main.height - 100);
+
+ const cat = this.scene.physics.add.sprite(x, y, 'stray_cat');
+ cat.setDepth(15);
+ cat.setScale(0.8);
+
+ // Cat behavior
+ cat.animalType = 'cat';
+ cat.state = 'idle'; // 'idle', 'running', 'hiding'
+ cat.runSpeed = 200;
+
+ // Random idle movement
+ this.scene.time.addEvent({
+ delay: Phaser.Math.Between(3000, 8000),
+ callback: () => this.catIdleBehavior(cat),
+ loop: true
+ });
+
+ this.animals.push(cat);
+
+ return cat;
+ }
+
+ /**
+ * CAT IDLE BEHAVIOR
+ */
+ catIdleBehavior(cat) {
+ if (!cat || cat.state === 'running') return;
+
+ const behavior = Phaser.Math.Between(1, 3);
+
+ switch (behavior) {
+ case 1: // Sit and clean
+ cat.state = 'idle';
+ cat.setVelocity(0, 0);
+ cat.play('cat_sit', true);
+ break;
+
+ case 2: // Wander
+ cat.state = 'idle';
+ const wanderX = Phaser.Math.Between(-30, 30);
+ const wanderY = Phaser.Math.Between(-30, 30);
+ cat.setVelocity(wanderX, wanderY);
+ cat.play('cat_walk', true);
+
+ this.scene.time.delayedCall(2000, () => {
+ if (cat) cat.setVelocity(0, 0);
+ });
+ break;
+
+ case 3: // Jump on trash can
+ cat.play('cat_jump', true);
+ break;
+ }
+ }
+
+ /**
+ * CAT RUNS AWAY FROM LONGBOARD
+ */
+ catRunAway(cat, kai) {
+ const distance = Phaser.Math.Distance.Between(cat.x, cat.y, kai.x, kai.y);
+
+ if (distance < 80 && kai.body.speed > 50) {
+ cat.state = 'running';
+
+ // Run opposite direction from Kai
+ const angle = Phaser.Math.Angle.Between(kai.x, kai.y, cat.x, cat.y);
+ const velocityX = Math.cos(angle) * cat.runSpeed;
+ const velocityY = Math.sin(angle) * cat.runSpeed;
+
+ cat.setVelocity(velocityX, velocityY);
+ cat.play('cat_run', true);
+
+ // Play meow sound
+ if (this.scene.sound.get('cat_meow')) {
+ this.scene.sound.play('cat_meow', { volume: 0.3 });
+ }
+
+ // Stop running after escaping
+ this.scene.time.delayedCall(1500, () => {
+ if (cat) {
+ cat.state = 'idle';
+ cat.setVelocity(0, 0);
+ }
+ });
+ }
+ }
+
+ /**
+ * SPAWN A STRAY DOG (barks from shadows)
+ */
+ spawnDog() {
+ const x = Phaser.Math.Between(50, this.scene.cameras.main.width - 50);
+ const y = Phaser.Math.Between(50, this.scene.cameras.main.height - 50);
+
+ const dog = this.scene.physics.add.sprite(x, y, 'stray_dog');
+ dog.setDepth(14);
+ dog.setAlpha(0.7); // Slightly transparent (in shadows)
+
+ dog.animalType = 'dog';
+ dog.barkTimer = null;
+
+ // Random barking
+ dog.barkTimer = this.scene.time.addEvent({
+ delay: Phaser.Math.Between(8000, 15000),
+ callback: () => this.dogBark(dog),
+ loop: true
+ });
+
+ this.animals.push(dog);
+
+ return dog;
+ }
+
+ /**
+ * DOG BARKING
+ */
+ dogBark(dog) {
+ if (!dog) return;
+
+ dog.play('dog_bark', true);
+
+ // Play bark sound (spatial audio)
+ if (this.scene.sound.get('dog_bark')) {
+ this.scene.sound.play('dog_bark', {
+ volume: 0.4,
+ // Spatial audio based on distance (if available)
+ });
+ }
+
+ // Show bark indicator
+ const bark = this.scene.add.text(
+ dog.x,
+ dog.y - 30,
+ 'WOOF!',
+ {
+ fontSize: '12px',
+ fontFamily: 'Arial',
+ color: '#ffffff',
+ stroke: '#000000',
+ strokeThickness: 3
+ }
+ );
+ bark.setOrigin(0.5);
+ bark.setDepth(50);
+ bark.setAlpha(0.7);
+
+ this.scene.tweens.add({
+ targets: bark,
+ alpha: 0,
+ y: bark.y - 20,
+ duration: 1000,
+ onComplete: () => bark.destroy()
+ });
+ }
+
+ /**
+ * SETUP AMBIENT SOUNDS
+ */
+ setupAmbientSounds() {
+ // City ambient loop
+ if (this.scene.sound.get('city_ambient')) {
+ const cityAmbient = this.scene.sound.add('city_ambient', {
+ volume: 0.2,
+ loop: true
+ });
+ cityAmbient.play();
+ this.ambientSounds.push(cityAmbient);
+ }
+
+ // Wind ambience
+ if (this.scene.sound.get('wind_ambient')) {
+ const wind = this.scene.sound.add('wind_ambient', {
+ volume: 0.15,
+ loop: true
+ });
+ wind.play();
+ this.ambientSounds.push(wind);
+ }
+
+ // Random distant sounds
+ this.scene.time.addEvent({
+ delay: Phaser.Math.Between(10000, 20000),
+ callback: () => this.playRandomDistantSound(),
+ loop: true
+ });
+ }
+
+ /**
+ * PLAY RANDOM DISTANT SOUND
+ */
+ playRandomDistantSound() {
+ const sounds = [
+ 'distant_siren',
+ 'metal_clang',
+ 'glass_break',
+ 'trash_can_fall',
+ 'crow_caw'
+ ];
+
+ const randomSound = Phaser.Utils.Array.GetRandom(sounds);
+
+ if (this.scene.sound.get(randomSound)) {
+ this.scene.sound.play(randomSound, {
+ volume: 0.1,
+ detune: Phaser.Math.Between(-200, 200) // Vary pitch
+ });
+ }
+ }
+
+ /**
+ * ATMOSPHERIC EFFECTS
+ */
+ startAtmosphere() {
+ // Dust particles floating
+ this.addDustParticles();
+
+ // Paper/trash blowing in wind
+ this.addBlowingTrash();
+
+ // Flickering streetlights (if night)
+ this.addFlickeringLights();
+ }
+
+ /**
+ * ADD DUST PARTICLES
+ */
+ addDustParticles() {
+ if (!this.scene.add.particles) return;
+
+ const particles = this.scene.add.particles('dust_particle');
+
+ const emitter = particles.createEmitter({
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -20,
+ speedY: { min: 20, max: 50 },
+ speedX: { min: -10, max: 10 },
+ scale: { start: 0.1, end: 0.3 },
+ alpha: { start: 0.3, end: 0 },
+ lifespan: 5000,
+ frequency: 200,
+ quantity: 1
+ });
+
+ emitter.setDepth(5);
+ }
+
+ /**
+ * ADD BLOWING TRASH
+ */
+ addBlowingTrash() {
+ // Spawn occasional paper/trash that blows across screen
+ this.scene.time.addEvent({
+ delay: Phaser.Math.Between(8000, 15000),
+ callback: () => {
+ const paper = this.scene.add.sprite(
+ -50,
+ Phaser.Math.Between(100, this.scene.cameras.main.height - 100),
+ 'paper_trash'
+ );
+ paper.setDepth(10);
+
+ // Blow across screen
+ this.scene.tweens.add({
+ targets: paper,
+ x: this.scene.cameras.main.width + 50,
+ angle: 360,
+ duration: 8000,
+ ease: 'Linear',
+ onComplete: () => paper.destroy()
+ });
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * ADD FLICKERING STREETLIGHTS
+ */
+ addFlickeringLights() {
+ // Find all streetlight sprites
+ const lights = this.scene.children.list.filter(child =>
+ child.texture && child.texture.key === 'streetlight'
+ );
+
+ lights.forEach(light => {
+ // Random flicker
+ this.scene.time.addEvent({
+ delay: Phaser.Math.Between(2000, 8000),
+ callback: () => {
+ // Quick flicker
+ light.setAlpha(0.3);
+ this.scene.time.delayedCall(100, () => {
+ light.setAlpha(1);
+ this.scene.time.delayedCall(50, () => {
+ light.setAlpha(0.3);
+ this.scene.time.delayedCall(100, () => {
+ light.setAlpha(1);
+ });
+ });
+ });
+ },
+ loop: true
+ });
+ });
+ }
+
+ /**
+ * UPDATE (called every frame)
+ */
+ update(kai) {
+ if (!this.isActive) return;
+
+ // Update cat behavior (run from Kai if on longboard)
+ this.animals.forEach(animal => {
+ if (animal.animalType === 'cat') {
+ this.catRunAway(animal, kai);
+ }
+ });
+ }
+
+ /**
+ * DESTROY ALL ANIMALS AND SOUNDS
+ */
+ destroy() {
+ this.animals.forEach(animal => animal.destroy());
+ this.animals = [];
+
+ this.ambientSounds.forEach(sound => sound.stop());
+ this.ambientSounds = [];
+
+ this.isActive = false;
+ }
+}
+
+export default NoirCitySystem;
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/OceanSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/OceanSystem.js
new file mode 100644
index 000000000..7d3a62e21
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/OceanSystem.js
@@ -0,0 +1,182 @@
+class OceanSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ this.isDiving = false;
+ this.oxygen = 100;
+ this.maxOxygen = 100;
+ this.oxygenDecayRate = 10; // Oxygen lost per second
+ this.oxygenRegenRate = 20; // Oxygen gained per second
+
+ this.damageTimer = 0;
+
+ // Visual Effects
+ this.overlay = null;
+ this.bubbleParticles = null;
+
+ // Boat
+ this.currentBoat = null;
+ this.isBoating = false;
+
+ // Init UI Event Listener
+ this.scene.events.on('update', (time, delta) => {
+ this.update(time, delta);
+ this.updateBoatVisuals();
+ });
+ }
+
+ update(time, delta) {
+ if (!this.scene.player || !this.scene.terrainSystem) return;
+
+ const playerPos = this.scene.player.getPosition();
+ const tile = this.scene.terrainSystem.getTile(playerPos.x, playerPos.y);
+
+ if (tile && tile.type && tile.type.name === 'water') {
+
+ // Check if boating
+ if (this.isBoating) {
+ // No drowning logic if in boat
+ this.oxygen = this.maxOxygen; // Regaining oxygen/safe
+ } else {
+ this.enterWater();
+ this.handleDiving(delta);
+ }
+ } else {
+ // LAND LOGIC
+ // If boating on land -> Beaching/Stop boating
+ if (this.isBoating) {
+ this.exitBoat();
+ }
+ this.exitWater();
+ this.handleRegen(delta);
+ }
+
+ // Update UI
+ if (this.isDiving || this.oxygen < this.maxOxygen) {
+ this.scene.events.emit('update-oxygen', { current: this.oxygen, max: this.maxOxygen });
+ } else {
+ this.scene.events.emit('hide-oxygen');
+ }
+ }
+
+ enterWater() {
+ if (this.isDiving) return;
+ this.isDiving = true;
+ console.log('๐ Entered Water');
+
+ // Visuals
+ if (!this.overlay) {
+ this.overlay = this.scene.add.rectangle(0, 0, this.scene.scale.width, this.scene.scale.height, 0x0000FF, 0.3);
+ this.overlay.setScrollFactor(0);
+ this.overlay.setDepth(9000); // Above most things, below UI
+ }
+ this.overlay.setVisible(true);
+
+ // Slow player?
+ // Slow player?
+ this.scene.player.moveSpeed = 60; // Slower in water
+ }
+
+ useBoat(boatItem) {
+ if (this.isBoating) {
+ this.exitBoat();
+ return;
+ }
+
+ if (!this.scene.terrainSystem) return;
+
+ // Check if on water or near water?
+ // Actually, let's allow "deploying" boat anywhere, but only moving fast on water?
+ // OR: Only allow deploying on water tile.
+
+ const playerPos = this.scene.player.getPosition();
+ const tile = this.scene.terrainSystem.getTile(playerPos.x, playerPos.y);
+
+ if (tile && tile.type && tile.type.name === 'water') {
+ this.enterBoat(playerPos.x, playerPos.y);
+ } else {
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x, y: this.scene.player.sprite.y - 50, text: 'Must be in water!', color: '#FF0000'
+ });
+ }
+ }
+
+ enterBoat() {
+ this.isBoating = true;
+ this.isDiving = false;
+ if (this.overlay) this.overlay.setVisible(false); // No blue overlay on boat
+
+ // Visual change
+ this.scene.player.moveSpeed = 200; // Fast speed!
+
+ // Create boat visual under player?
+ // Simplified: Change player texture or add container
+ if (!this.currentBoat) {
+ this.currentBoat = this.scene.add.sprite(0, 0, 'boat');
+ this.currentBoat.setDepth(this.scene.player.sprite.depth - 1);
+ }
+ this.currentBoat.setVisible(true);
+
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x, y: this.scene.player.sprite.y - 50, text: 'Deployed Boat!', color: '#00FFFF'
+ });
+ }
+
+ exitBoat() {
+ this.isBoating = false;
+ if (this.currentBoat) this.currentBoat.setVisible(false);
+
+ // Return to normal or water speed?
+ // Logic in update loop will handle re-entering water state if still on water
+ this.scene.player.moveSpeed = 150;
+ }
+
+ exitWater() {
+ if (!this.isDiving) return;
+ this.isDiving = false;
+ console.log('๐๏ธ Exited Water');
+
+ if (this.overlay) this.overlay.setVisible(false);
+
+ // Restore speed
+ this.scene.player.moveSpeed = 150; // Normal speed
+ }
+
+ handleDiving(delta) {
+ this.oxygen -= (this.oxygenDecayRate * delta) / 1000;
+
+ if (this.oxygen <= 0) {
+ this.oxygen = 0;
+ this.damageTimer += delta;
+ if (this.damageTimer > 1000) {
+ this.damageTimer = 0;
+ this.scene.player.takeDamage(5);
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 50,
+ text: 'Drowning!',
+ color: '#FF0000'
+ });
+ }
+ }
+ }
+
+ handleRegen(delta) {
+ if (this.oxygen < this.maxOxygen) {
+ this.oxygen += (this.oxygenRegenRate * delta) / 1000;
+ if (this.oxygen > this.maxOxygen) this.oxygen = this.maxOxygen;
+ }
+ }
+
+ updateBoatVisuals() {
+ if (this.isBoating && this.currentBoat && this.scene.player) {
+ this.currentBoat.x = this.scene.player.sprite.x;
+ this.currentBoat.y = this.scene.player.sprite.y + 10; // Slightly below
+ this.currentBoat.setDepth(this.scene.player.sprite.depth - 1);
+
+ // Flip based on movement
+ if (this.scene.input.keyboard.checkDown(this.scene.input.keyboard.addKey('A'), 100)) this.currentBoat.setFlipX(false);
+ if (this.scene.input.keyboard.checkDown(this.scene.input.keyboard.addKey('D'), 100)) this.currentBoat.setFlipX(true);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ParallaxSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ParallaxSystem.js
new file mode 100644
index 000000000..e1614bf43
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ParallaxSystem.js
@@ -0,0 +1,199 @@
+class ParallaxSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.layers = [];
+
+ // Layer depths (Phaser depth sorting)
+ this.DEPTH = {
+ SKY: -1000,
+ DISTANT_HILLS: -500,
+ FAR_TREES: -100,
+ TERRAIN: 0,
+ GAME_OBJECTS: 1000,
+ FOREGROUND_GRASS: 5000,
+ FOREGROUND_LEAVES: 5500
+ };
+
+ this.init();
+ }
+
+ init() {
+ console.log('๐ ParallaxSystem: Initialized');
+
+ // Layer 1: Sky/Hills (Distant background)
+ // Layer 1: Sky/Hills (Disabled by request)
+ // this.createSkyLayer();
+ // this.createDistantHills();
+
+ // Layer 4: Foreground overlay (High grass patches)
+ this.createForegroundGrass();
+ }
+
+ createSkyLayer() {
+ // Gradient sky rectangle
+ const width = 3000;
+ const height = 2000;
+
+ const skyBg = this.scene.add.rectangle(0, 0, width, height, 0x87CEEB); // Sky blue
+ skyBg.setOrigin(0, 0);
+ skyBg.setScrollFactor(0); // Fixed (no parallax)
+ skyBg.setDepth(this.DEPTH.SKY);
+
+ this.layers.push({
+ name: 'sky',
+ objects: [skyBg],
+ scrollFactor: 0
+ });
+ }
+
+ createDistantHills() {
+ // Create simple hill silhouettes in background
+ const hillCount = 5;
+ const hills = [];
+
+ for (let i = 0; i < hillCount; i++) {
+ const x = i * 800 - 1000;
+ const y = 600;
+ const width = Phaser.Math.Between(400, 800);
+ const height = Phaser.Math.Between(150, 300);
+
+ // Create hill as ellipse
+ const hill = this.scene.add.ellipse(x, y, width, height, 0x4a5f3a); // Dark green
+ hill.setAlpha(0.4);
+ hill.setDepth(this.DEPTH.DISTANT_HILLS);
+ hill.setScrollFactor(0.2, 0.2); // Slow parallax
+
+ hills.push(hill);
+ }
+
+ this.layers.push({
+ name: 'distant_hills',
+ objects: hills,
+ scrollFactor: 0.2
+ });
+ }
+
+ createForegroundGrass() {
+ // Create tall grass patches that appear in front of player
+ const grassPatches = [];
+ const patchCount = 30;
+
+ for (let i = 0; i < patchCount; i++) {
+ const x = Phaser.Math.Between(-500, 2500);
+ const y = Phaser.Math.Between(-500, 2500);
+
+ const grass = this.createGrassPatch(x, y);
+ grass.setDepth(this.DEPTH.FOREGROUND_GRASS);
+ grass.setScrollFactor(1.05, 1.05); // Slight forward parallax
+ grass.setAlpha(0.6);
+
+ grassPatches.push(grass);
+ }
+
+ this.layers.push({
+ name: 'foreground_grass',
+ objects: grassPatches,
+ scrollFactor: 1.05
+ });
+ }
+
+ createGrassPatch(x, y) {
+ // Create procedural grass patch
+ const graphics = this.scene.add.graphics();
+
+ // Draw several grass blades
+ for (let i = 0; i < 6; i++) {
+ const offsetX = Phaser.Math.Between(-10, 10);
+ const offsetY = Phaser.Math.Between(-5, 5);
+ const height = Phaser.Math.Between(20, 40);
+
+ graphics.fillStyle(0x3a5f2a, 0.8); // Dark grass green
+
+ // Draw grass blade (thin triangle)
+ graphics.beginPath();
+ graphics.moveTo(x + offsetX, y + offsetY);
+ graphics.lineTo(x + offsetX - 2, y + offsetY + height);
+ graphics.lineTo(x + offsetX + 2, y + offsetY + height);
+ graphics.closePath();
+ graphics.fillPath();
+ }
+
+ return graphics;
+ }
+
+ update(playerX, playerY) {
+ // Update foreground grass visibility based on player position
+ // Hide/show grass patches that are too close to player for better gameplay
+
+ const foregroundLayer = this.layers.find(l => l.name === 'foreground_grass');
+ if (!foregroundLayer) return;
+
+ for (const grass of foregroundLayer.objects) {
+ const distance = Phaser.Math.Distance.Between(
+ grass.x, grass.y,
+ playerX, playerY
+ );
+
+ // Fade out grass when player is very close
+ if (distance < 50) {
+ grass.setAlpha(0.2);
+ } else if (distance < 100) {
+ grass.setAlpha(0.4);
+ } else {
+ grass.setAlpha(0.6);
+ }
+ }
+ }
+
+ addFarTree(x, y) {
+ // Add a background tree (Layer 2)
+ if (!this.scene.textures.exists('tree')) return;
+
+ const tree = this.scene.add.sprite(x, y, 'tree');
+ tree.setDepth(this.DEPTH.FAR_TREES);
+ tree.setScrollFactor(0.7, 0.7); // Medium parallax
+ tree.setAlpha(0.7);
+ tree.setScale(1.5);
+
+ return tree;
+ }
+
+ addForegroundLeaves(x, y) {
+ // Add falling leaves or branch overlay (Layer 4)
+ const graphics = this.scene.add.graphics();
+
+ // Draw some leaves
+ for (let i = 0; i < 3; i++) {
+ const leafX = x + Phaser.Math.Between(-20, 20);
+ const leafY = y + Phaser.Math.Between(-10, 10);
+
+ graphics.fillStyle(0x2d4a1f, 0.5); // Dark green leaf
+ graphics.fillEllipse(leafX, leafY, 8, 12);
+ }
+
+ graphics.setDepth(this.DEPTH.FOREGROUND_LEAVES);
+ graphics.setScrollFactor(1.1, 1.1); // Fastest parallax (closest)
+
+ return graphics;
+ }
+
+ clearLayer(layerName) {
+ const layer = this.layers.find(l => l.name === layerName);
+ if (!layer) return;
+
+ for (const obj of layer.objects) {
+ obj.destroy();
+ }
+
+ layer.objects = [];
+ }
+
+ destroy() {
+ for (const layer of this.layers) {
+ for (const obj of layer.objects) {
+ obj.destroy();
+ }
+ }
+ this.layers = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ParticleEnhancementsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ParticleEnhancementsSystem.js
new file mode 100644
index 000000000..50669c24b
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ParticleEnhancementsSystem.js
@@ -0,0 +1,375 @@
+// Particle Enhancements System - Advanced particle effects for gameplay
+class ParticleEnhancementsSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Particle pools for efficiency
+ this.sparklePool = [];
+ this.dustPool = [];
+ this.particleTextures = new Set();
+
+ // Create particle textures
+ this.createParticleTextures();
+
+ console.log('โจ ParticleEnhancementsSystem initialized');
+ }
+
+ // Create procedural particle textures
+ createParticleTextures() {
+ // Sparkle particle (star-shaped)
+ if (!this.scene.textures.exists('sparkle')) {
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+ graphics.fillStyle(0xffffff, 1);
+ graphics.fillCircle(4, 4, 4);
+ graphics.generateTexture('sparkle', 8, 8);
+ graphics.destroy();
+ this.particleTextures.add('sparkle');
+ }
+
+ // Dust particle (cloud-shaped)
+ if (!this.scene.textures.exists('dust')) {
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+ graphics.fillStyle(0xdddddd, 0.6);
+ graphics.fillCircle(3, 3, 3);
+ graphics.generateTexture('dust', 6, 6);
+ graphics.destroy();
+ this.particleTextures.add('dust');
+ }
+
+ // Leaf particle (for harvest)
+ if (!this.scene.textures.exists('leaf')) {
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+ graphics.fillStyle(0x4a9d5f, 1);
+ graphics.fillRect(0, 0, 4, 6);
+ graphics.generateTexture('leaf', 4, 6);
+ graphics.destroy();
+ this.particleTextures.add('leaf');
+ }
+
+ // Star particle (for special effects)
+ if (!this.scene.textures.exists('star')) {
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+ graphics.fillStyle(0xffee88, 1);
+
+ // 5-pointed star
+ const cx = 5, cy = 5, spikes = 5, outerRadius = 5, innerRadius = 2;
+ let rot = Math.PI / 2 * 3;
+ let x = cx;
+ let y = cy;
+ const step = Math.PI / spikes;
+
+ graphics.beginPath();
+ graphics.moveTo(cx, cy - outerRadius);
+
+ for (let i = 0; i < spikes; i++) {
+ x = cx + Math.cos(rot) * outerRadius;
+ y = cy + Math.sin(rot) * outerRadius;
+ graphics.lineTo(x, y);
+ rot += step;
+
+ x = cx + Math.cos(rot) * innerRadius;
+ y = cy + Math.sin(rot) * innerRadius;
+ graphics.lineTo(x, y);
+ rot += step;
+ }
+
+ graphics.lineTo(cx, cy - outerRadius);
+ graphics.closePath();
+ graphics.fillPath();
+ graphics.generateTexture('star', 10, 10);
+ graphics.destroy();
+ this.particleTextures.add('star');
+ }
+ }
+
+ // Enhanced craft sparkles (when crafting completes)
+ craftSparkles(x, y, color = [0xffffff, 0xffee88, 0xffaa00]) {
+ const emitter = this.scene.add.particles(x, y, 'sparkle', {
+ speed: { min: 50, max: 150 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 1, end: 0 },
+ tint: color,
+ alpha: { start: 1, end: 0 },
+ lifespan: 1000,
+ quantity: 20,
+ blendMode: 'ADD',
+ emitting: false
+ });
+
+ emitter.setDepth(999999);
+ emitter.explode(20); // Burst of 20 particles
+
+ // Auto-destroy after particles fade
+ this.scene.time.delayedCall(1200, () => {
+ emitter.destroy();
+ });
+
+ return emitter;
+ }
+
+ // Walk dust clouds (when player walks)
+ walkDust(x, y, direction = 'down') {
+ // Only spawn on grass/dirt tiles
+ const tileSize = 48;
+ const tileX = Math.floor(x / tileSize);
+ const tileY = Math.floor(y / tileSize);
+
+ if (this.scene.terrainSystem) {
+ const tile = this.scene.terrainSystem.getTile(tileX, tileY);
+ if (!tile || (tile.type !== 'grass' && tile.type !== 'dirt')) {
+ return null; // No dust on water/pavement
+ }
+ }
+
+ const emitter = this.scene.add.particles(x, y + 10, 'dust', {
+ speed: { min: 10, max: 30 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 0.5, end: 1.5 },
+ alpha: { start: 0.4, end: 0 },
+ lifespan: 500,
+ quantity: 2,
+ frequency: 100,
+ blendMode: 'NORMAL',
+ tint: 0xccaa88, // Dusty brown
+ emitting: false
+ });
+
+ emitter.setDepth(this.scene.player ? this.scene.player.sprite.depth - 1 : 100);
+ emitter.explode(2); // Small puff
+
+ // Auto-destroy
+ this.scene.time.delayedCall(600, () => {
+ emitter.destroy();
+ });
+
+ return emitter;
+ }
+
+ // Harvest particle burst (when harvesting crops)
+ harvestBurst(x, y, cropType = 'generic') {
+ // Determine particle color based on crop
+ let particleColors = [0x4a9d5f, 0x90EE90]; // Default green
+ let particleTexture = 'leaf';
+
+ if (cropType === 'wheat' || cropType === 'grain') {
+ particleColors = [0xFFD700, 0xFFA500]; // Golden
+ particleTexture = 'sparkle';
+ } else if (cropType === 'carrot' || cropType === 'root') {
+ particleColors = [0xFF6B35, 0xFFA500]; // Orange
+ particleTexture = 'leaf';
+ } else if (cropType === 'berry' || cropType === 'fruit') {
+ particleColors = [0xFF1744, 0xFF6B9D]; // Red/Pink
+ particleTexture = 'sparkle';
+ }
+
+ const emitter = this.scene.add.particles(x, y, particleTexture, {
+ speed: { min: 80, max: 150 },
+ angle: { min: -120, max: -60 }, // Upward spray
+ scale: { start: 1, end: 0.3 },
+ tint: particleColors,
+ alpha: { start: 1, end: 0 },
+ lifespan: 800,
+ quantity: 15,
+ gravityY: 200, // Gravity pull
+ blendMode: 'NORMAL',
+ emitting: false
+ });
+
+ emitter.setDepth(200000);
+ emitter.explode(15);
+
+ // Auto-destroy
+ this.scene.time.delayedCall(1000, () => {
+ emitter.destroy();
+ });
+
+ // Play success sound
+ if (this.scene.soundManager && this.scene.soundManager.beepPickup) {
+ this.scene.soundManager.beepPickup();
+ }
+
+ return emitter;
+ }
+
+ // Dig/Till soil particles (when using hoe)
+ digParticles(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'dust', {
+ speed: { min: 40, max: 80 },
+ angle: { min: -140, max: -40 }, // Upward
+ scale: { start: 0.8, end: 0.2 },
+ alpha: { start: 0.6, end: 0 },
+ tint: [0x8b6f47, 0x6b4423], // Brown soil
+ lifespan: 600,
+ quantity: 10,
+ gravityY: 150,
+ blendMode: 'NORMAL',
+ emitting: false
+ });
+
+ emitter.setDepth(200000);
+ emitter.explode(10);
+
+ this.scene.time.delayedCall(800, () => {
+ emitter.destroy();
+ });
+
+ return emitter;
+ }
+
+ // Plant seed sparkle (when planting)
+ plantSparkle(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'sparkle', {
+ speed: { min: 20, max: 50 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 0.6, end: 0 },
+ tint: 0x90EE90, // Light green
+ alpha: { start: 0.8, end: 0 },
+ lifespan: 500,
+ quantity: 8,
+ blendMode: 'ADD',
+ emitting: false
+ });
+
+ emitter.setDepth(200000);
+ emitter.explode(8);
+
+ this.scene.time.delayedCall(600, () => {
+ emitter.destroy();
+ });
+
+ return emitter;
+ }
+
+ // Build completion effect (when placing building)
+ buildComplete(x, y, width = 48, height = 48) {
+ // Create corner sparkles
+ const corners = [
+ { x: x - width / 2, y: y - height / 2 }, // Top-left
+ { x: x + width / 2, y: y - height / 2 }, // Top-right
+ { x: x - width / 2, y: y + height / 2 }, // Bottom-left
+ { x: x + width / 2, y: y + height / 2 } // Bottom-right
+ ];
+
+ corners.forEach((corner, index) => {
+ this.scene.time.delayedCall(index * 100, () => {
+ this.craftSparkles(corner.x, corner.y, [0xFFD700, 0xFFA500]);
+ });
+ });
+
+ // Center burst
+ this.scene.time.delayedCall(400, () => {
+ this.craftSparkles(x, y, [0xffffff, 0xFFD700]);
+ });
+ }
+
+ // Level up / achievement effect
+ levelUpEffect(x, y) {
+ // Ring explosion
+ const emitter = this.scene.add.particles(x, y, 'star', {
+ speed: { min: 100, max: 200 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 1.5, end: 0 },
+ tint: [0xFFD700, 0xFFEE88, 0xFFFFFF],
+ alpha: { start: 1, end: 0 },
+ lifespan: 1500,
+ quantity: 30,
+ blendMode: 'ADD',
+ emitting: false
+ });
+
+ emitter.setDepth(999999);
+ emitter.explode(30);
+
+ // Upward rising stars
+ const rising = this.scene.add.particles(x, y, 'star', {
+ speedY: { min: -100, max: -50 },
+ speedX: { min: -20, max: 20 },
+ scale: { start: 0.8, end: 0 },
+ tint: 0xFFD700,
+ alpha: { start: 1, end: 0 },
+ lifespan: 2000,
+ quantity: 1,
+ frequency: 100,
+ blendMode: 'ADD'
+ });
+
+ rising.setDepth(999999);
+
+ // Stop after 2 seconds
+ this.scene.time.delayedCall(2000, () => {
+ rising.stop();
+ });
+
+ // Cleanup
+ this.scene.time.delayedCall(3500, () => {
+ emitter.destroy();
+ rising.destroy();
+ });
+
+ return { burst: emitter, rising };
+ }
+
+ // Damage impact effect (when hit)
+ damageImpact(x, y, color = 0xFF0000) {
+ const emitter = this.scene.add.particles(x, y, 'sparkle', {
+ speed: { min: 50, max: 100 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 0.8, end: 0 },
+ tint: color,
+ alpha: { start: 1, end: 0 },
+ lifespan: 400,
+ quantity: 12,
+ blendMode: 'ADD',
+ emitting: false
+ });
+
+ emitter.setDepth(999999);
+ emitter.explode(12);
+
+ this.scene.time.delayedCall(500, () => {
+ emitter.destroy();
+ });
+
+ return emitter;
+ }
+
+ // Heal/Restore effect
+ healEffect(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'sparkle', {
+ speedY: { min: -80, max: -40 },
+ speedX: { min: -20, max: 20 },
+ scale: { start: 0.6, end: 0 },
+ tint: [0x00FF00, 0x90EE90, 0xFFFFFF],
+ alpha: { start: 1, end: 0 },
+ lifespan: 1000,
+ quantity: 1,
+ frequency: 80,
+ blendMode: 'ADD'
+ });
+
+ emitter.setDepth(999999);
+
+ this.scene.time.delayedCall(1000, () => {
+ emitter.stop();
+ });
+
+ this.scene.time.delayedCall(2000, () => {
+ emitter.destroy();
+ });
+
+ return emitter;
+ }
+
+ // Clean up all particles
+ destroy() {
+ // Destroy created textures
+ for (const textureName of this.particleTextures) {
+ if (this.scene.textures.exists(textureName)) {
+ this.scene.textures.remove(textureName);
+ }
+ }
+ this.particleTextures.clear();
+
+ console.log('โจ ParticleEnhancementsSystem destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/PathfindingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/PathfindingSystem.js
new file mode 100644
index 000000000..627061e57
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/PathfindingSystem.js
@@ -0,0 +1,116 @@
+class PathfindingSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.worker = null;
+ this.callbacks = new Map();
+ this.requestId = 0;
+ this.initialized = false;
+
+ try {
+ // Ustvarimo workerja
+ this.worker = new Worker('src/workers/pathfinding.worker.js');
+ this.worker.onmessage = this.handleMessage.bind(this);
+ console.log('โ
PathfindingWorker initialized (v2.1 - Tiled Support Ready).');
+ this.initialized = true;
+ } catch (err) {
+ console.error('โ Failed to init PathfindingWorker:', err);
+ }
+ }
+
+ updateGrid() {
+ if (!this.initialized || !this.scene.terrainSystem) return;
+
+ const ts = this.scene.terrainSystem;
+ const width = ts.width;
+ const height = ts.height;
+
+ // Ustvarimo flat array (0 = prehodno, 1 = ovira)
+ // Uporabimo Uint8Array za uฤinkovitost prenosa
+ const grid = new Uint8Array(width * height);
+
+ for (let y = 0; y < height; y++) {
+ for (let x = 0; x < width; x++) {
+ let blocked = 0;
+
+ // ๐บ๏ธ 1. TILED MAP SUPPORT
+ if (ts.tiledMap) {
+ // Check various obstacle layers
+ const obstacleLayers = ['Obstacles', 'Buildings', 'Fences', 'Objects', '02_Obstacles', '03_Fences', '04_Buildings'];
+ let isBlocked = false;
+
+ for (const layerName of obstacleLayers) {
+ // Safety check: ensure layer exists
+ const layerExists = ts.tiledMap.getLayer(layerName);
+ if (!layerExists) continue;
+
+ const tile = ts.tiledMap.getTileAt(x, y, true, layerName);
+ if (tile && tile.index !== -1) {
+ isBlocked = true;
+ break;
+ }
+ }
+
+ if (isBlocked) {
+ blocked = 1;
+ }
+ }
+ // ๐ 2. PROCEDURAL / FLAT TILES SUPPORT
+ else {
+ const tile = ts.tiles && ts.tiles[y] ? ts.tiles[y][x] : null;
+
+ // 1. Voda in void
+ if (!tile || tile.type === 'water' || tile.type === 'void') {
+ blocked = 1;
+ } else {
+ // 2. Dekoracije (Ovire)
+ const key = `${x},${y}`;
+ const decor = ts.decorationsMap.get(key);
+ if (decor) {
+ const solidTypes = [
+ 'tree', 'tree_green', 'tree_blue', 'tree_dead',
+ 'tree_green_new', 'tree_blue_new', 'tree_dead_new',
+ 'rock', 'rock_asset', 'rock_new', 'rock_small', 'rock_1', 'rock_2',
+ 'wall', 'fence', 'house', 'gravestone'
+ ];
+ const isSolid = solidTypes.some(t => decor.type && decor.type.includes(t));
+ if (isSolid) blocked = 1;
+ }
+ }
+ }
+
+ grid[y * width + x] = blocked;
+ }
+ }
+
+ this.worker.postMessage({
+ type: 'UPDATE_GRID',
+ payload: { grid, width, height }
+ });
+
+ // console.log('๐บ๏ธ Pathfinding Grid updated sent to worker.');
+ }
+
+ findPath(startX, startY, endX, endY, callback) {
+ if (!this.initialized) return;
+
+ const id = this.requestId++;
+ this.callbacks.set(id, callback);
+
+ this.worker.postMessage({
+ type: 'FIND_PATH',
+ id: id,
+ payload: { startX, startY, endX, endY }
+ });
+ }
+
+ handleMessage(e) {
+ const { type, id, path } = e.data;
+ if (type === 'PATH_FOUND') {
+ const callback = this.callbacks.get(id);
+ if (callback) {
+ callback(path);
+ this.callbacks.delete(id);
+ }
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/PerennialCropSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/PerennialCropSystem.js
new file mode 100644
index 000000000..820b05bb4
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/PerennialCropSystem.js
@@ -0,0 +1,163 @@
+/**
+ * PERENNIAL CROPS SYSTEM
+ * Handles multi-season crops like Apple Trees
+ */
+class PerennialCropSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.perennials = []; // Array of perennial objects
+
+ // Perennial definitions
+ this.perennialData = {
+ 'apple_tree': {
+ name: 'Apple Tree',
+ growthTime: 120000, // 2 minutes to maturity
+ harvestSeason: 'autumn', // Only harvest in autumn
+ harvestYield: { 'apple': 5 },
+ regrowthTime: 30000, // 30s to regrow apples
+ texture: 'apple_tree',
+ stages: ['sapling', 'young', 'mature', 'fruiting']
+ }
+ };
+ }
+
+ /**
+ * Plant a perennial crop
+ */
+ plant(gridX, gridY, type) {
+ if (!this.perennialData[type]) {
+ console.error('Unknown perennial type:', type);
+ return false;
+ }
+
+ const perennial = {
+ type,
+ gridX,
+ gridY,
+ plantedTime: Date.now(),
+ stage: 0, // 0=sapling, 1=young, 2=mature, 3=fruiting
+ canHarvest: false,
+ sprite: null,
+ healthBar: null
+ };
+
+ this.perennials.push(perennial);
+ this.createSprite(perennial);
+ console.log(`๐ณ Planted ${type} at ${gridX},${gridY}`);
+ return true;
+ }
+
+ createSprite(perennial) {
+ const data = this.perennialData[perennial.type];
+ const screenPos = this.scene.iso.toScreen(perennial.gridX, perennial.gridY);
+
+ // Create tree sprite
+ perennial.sprite = this.scene.add.sprite(
+ screenPos.x + this.scene.terrainOffsetX,
+ screenPos.y + this.scene.terrainOffsetY,
+ data.texture || 'tree'
+ );
+ perennial.sprite.setOrigin(0.5, 1);
+ perennial.sprite.setScale(0.5);
+ perennial.sprite.setDepth(this.scene.iso.getDepth(perennial.gridX, perennial.gridY, this.scene.iso.LAYER_OBJECTS));
+
+ this.updateSprite(perennial);
+ }
+
+ updateSprite(perennial) {
+ const data = this.perennialData[perennial.type];
+
+ // Update tint based on stage
+ if (perennial.stage === 0) {
+ perennial.sprite.setTint(0x88aa88); // Light green (sapling)
+ perennial.sprite.setScale(0.3);
+ } else if (perennial.stage === 1) {
+ perennial.sprite.setTint(0x44aa44); // Medium green (young)
+ perennial.sprite.setScale(0.4);
+ } else if (perennial.stage === 2) {
+ perennial.sprite.clearTint(); // Normal (mature)
+ perennial.sprite.setScale(0.5);
+ } else if (perennial.stage === 3) {
+ perennial.sprite.setTint(0xff8844); // Orange tint (fruiting)
+ perennial.sprite.setScale(0.5);
+ }
+ }
+
+ update(delta, currentSeason) {
+ this.perennials.forEach(perennial => {
+ const data = this.perennialData[perennial.type];
+ const age = Date.now() - perennial.plantedTime;
+
+ // Growth stages
+ if (perennial.stage < 3) {
+ const stageTime = data.growthTime / 3;
+ const newStage = Math.min(3, Math.floor(age / stageTime));
+
+ if (newStage !== perennial.stage) {
+ perennial.stage = newStage;
+ this.updateSprite(perennial);
+
+ if (perennial.stage === 3) {
+ console.log(`๐ณ ${data.name} reached maturity!`);
+ }
+ }
+ }
+
+ // Fruiting logic (only in correct season and when mature)
+ if (perennial.stage === 3 && currentSeason === data.harvestSeason) {
+ if (!perennial.canHarvest) {
+ perennial.canHarvest = true;
+ perennial.sprite.setTint(0xff6644); // Bright orange (ready to harvest)
+ }
+ } else if (perennial.canHarvest && currentSeason !== data.harvestSeason) {
+ perennial.canHarvest = false;
+ perennial.sprite.clearTint();
+ }
+ });
+ }
+
+ /**
+ * Attempt to harvest perennial
+ */
+ harvest(gridX, gridY) {
+ const perennial = this.perennials.find(p =>
+ p.gridX === gridX && p.gridY === gridY
+ );
+
+ if (!perennial) return null;
+
+ if (!perennial.canHarvest) {
+ return { error: 'Not ready to harvest' };
+ }
+
+ const data = this.perennialData[perennial.type];
+
+ // Reset harvest state (will regrow)
+ perennial.canHarvest = false;
+ perennial.sprite.setTint(0x88aa88); // Back to green
+
+ console.log(`๐ Harvested ${data.name}!`);
+ return data.harvestYield;
+ }
+
+ /**
+ * Check if position has perennial
+ */
+ hasPerennial(gridX, gridY) {
+ return this.perennials.some(p => p.gridX === gridX && p.gridY === gridY);
+ }
+
+ /**
+ * Get perennial at position
+ */
+ getPerennial(gridX, gridY) {
+ return this.perennials.find(p => p.gridX === gridX && p.gridY === gridY);
+ }
+
+ destroy() {
+ this.perennials.forEach(p => {
+ if (p.sprite) p.sprite.destroy();
+ });
+ this.perennials = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/PlatformSupportSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/PlatformSupportSystem.js
new file mode 100644
index 000000000..2866f9d37
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/PlatformSupportSystem.js
@@ -0,0 +1,424 @@
+/**
+ * PLATFORM SUPPORT SYSTEM
+ * Cross-platform compatibility: Mobile, Controller, Steam Deck, Linux, Mac
+ */
+class PlatformSupportSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Platform detection
+ this.platform = this.detectPlatform();
+ this.isMobile = this.platform === 'mobile';
+ this.isController = false;
+
+ // Mobile controls
+ this.virtualJoystick = null;
+ this.actionButtons = new Map();
+ this.touchControls = {
+ enabled: false,
+ joystickSize: 'medium',
+ buttonOpacity: 0.7,
+ leftHanded: false
+ };
+
+ // Controller support
+ this.connectedControllers = [];
+ this.controllerMapping = new Map();
+
+ // Steam Deck
+ this.isSteamDeck = this.detectSteamDeck();
+ this.steamDeckSettings = {
+ performanceMode: '60fps',
+ uiScale: 1.2
+ };
+
+ this.init();
+ console.log('โ
Platform Support System initialized');
+ console.log(`๐ฑ Platform: ${this.platform}`);
+ }
+
+ init() {
+ if (this.isMobile) {
+ this.setupMobileControls();
+ }
+
+ this.setupControllerSupport();
+
+ if (this.isSteamDeck) {
+ this.setupSteamDeck();
+ }
+
+ console.log('๐ฎ Platform support ready');
+ }
+
+ // ========== PLATFORM DETECTION ==========
+
+ detectPlatform() {
+ const ua = navigator.userAgent.toLowerCase();
+
+ if (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(ua)) {
+ return 'mobile';
+ }
+
+ if (ua.includes('steamdeck')) {
+ return 'steamdeck';
+ }
+
+ if (ua.includes('linux')) {
+ return 'linux';
+ }
+
+ if (ua.includes('mac')) {
+ return 'mac';
+ }
+
+ return 'windows';
+ }
+
+ detectSteamDeck() {
+ return navigator.userAgent.toLowerCase().includes('steamdeck');
+ }
+
+ // ========== MOBILE CONTROLS ==========
+
+ setupMobileControls() {
+ this.createVirtualJoystick();
+ this.createActionButtons();
+ this.createTopHUD();
+ this.createBottomActionBar();
+ this.setupGestureControls();
+
+ console.log('๐ฑ Mobile controls initialized');
+ }
+
+ createVirtualJoystick() {
+ const sizes = { small: 80, medium: 100, large: 120 };
+ const size = sizes[this.touchControls.joystickSize];
+
+ this.virtualJoystick = {
+ x: this.touchControls.leftHanded ? window.innerWidth - 150 : 150,
+ y: window.innerHeight - 150,
+ size,
+ active: false,
+ touchId: null,
+ direction: { x: 0, y: 0 }
+ };
+
+ console.log('๐น๏ธ Virtual joystick created');
+ }
+
+ createActionButtons() {
+ const rightX = this.touchControls.leftHanded ? 150 : window.innerWidth - 150;
+
+ // Primary action button
+ this.actionButtons.set('primary', {
+ x: rightX,
+ y: window.innerHeight - 150,
+ size: 70,
+ label: 'A',
+ action: 'interact'
+ });
+
+ // Special ability button
+ this.actionButtons.set('special', {
+ x: rightX - 80,
+ y: window.innerHeight - 200,
+ size: 60,
+ label: 'B',
+ action: 'dash'
+ });
+
+ // Auto-aim toggle
+ this.actionButtons.set('autoaim', {
+ x: rightX + 80,
+ y: window.innerHeight - 200,
+ size: 50,
+ label: '๐ฏ',
+ action: 'toggle_autoaim'
+ });
+
+ console.log('๐ฎ Action buttons created');
+ }
+
+ createTopHUD() {
+ // Health bar (top-left)
+ // Hunger bar (below health)
+ // Gold/Resources (top-right)
+ // Mini-map (top-right corner)
+ // Day/Season indicator (top-center)
+ console.log('๐ Top HUD created');
+ }
+
+ createBottomActionBar() {
+ // Weapon/Tool switcher
+ // Building mode toggle
+ // Crafting quick access
+ // Pause button
+ console.log('๐ง Bottom action bar created');
+ }
+
+ setupGestureControls() {
+ // Pinch to zoom
+ // Two-finger pan
+ // Swipe to dodge
+ // Double-tap for special action
+ console.log('๐ Gesture controls setup');
+ }
+
+ handleTouch(touchEvent) {
+ // Handle touch input for virtual joystick and buttons
+ }
+
+ updateVirtualJoystick(delta) {
+ if (!this.virtualJoystick.active) return;
+
+ // Update player movement based on joystick direction
+ const { x, y } = this.virtualJoystick.direction;
+
+ if (this.scene.player) {
+ // Apply movement
+ }
+ }
+
+ // ========== CONTROLLER SUPPORT ==========
+
+ setupControllerSupport() {
+ window.addEventListener('gamepadconnected', (e) => this.onControllerConnected(e));
+ window.addEventListener('gamepaddisconnected', (e) => this.onControllerDisconnected(e));
+
+ this.defineControllerMappings();
+ console.log('๐ฎ Controller support initialized');
+ }
+
+ defineControllerMappings() {
+ // Xbox controller
+ this.controllerMapping.set('xbox', {
+ buttons: {
+ 0: 'A',
+ 1: 'B',
+ 2: 'X',
+ 3: 'Y',
+ 4: 'LB',
+ 5: 'RB',
+ 6: 'LT',
+ 7: 'RT',
+ 8: 'Back',
+ 9: 'Start',
+ 10: 'LS',
+ 11: 'RS',
+ 12: 'Up',
+ 13: 'Down',
+ 14: 'Left',
+ 15: 'Right'
+ },
+ axes: {
+ 0: 'LS_X',
+ 1: 'LS_Y',
+ 2: 'RS_X',
+ 3: 'RS_Y'
+ }
+ });
+
+ // PlayStation controller
+ this.controllerMapping.set('playstation', {
+ buttons: {
+ 0: 'Cross',
+ 1: 'Circle',
+ 2: 'Square',
+ 3: 'Triangle',
+ 4: 'L1',
+ 5: 'R1',
+ 6: 'L2',
+ 7: 'R2',
+ 8: 'Share',
+ 9: 'Options',
+ 10: 'L3',
+ 11: 'R3',
+ 12: 'Up',
+ 13: 'Down',
+ 14: 'Left',
+ 15: 'Right'
+ }
+ });
+
+ // Nintendo Switch Pro
+ this.controllerMapping.set('switch', {
+ buttons: {
+ 0: 'B',
+ 1: 'A',
+ 2: 'Y',
+ 3: 'X',
+ 4: 'L',
+ 5: 'R',
+ 6: 'ZL',
+ 7: 'ZR',
+ 8: 'Minus',
+ 9: 'Plus',
+ 10: 'LS',
+ 11: 'RS',
+ 12: 'Up',
+ 13: 'Down',
+ 14: 'Left',
+ 15: 'Right'
+ }
+ });
+ }
+
+ onControllerConnected(event) {
+ const gamepad = event.gamepad;
+ this.connectedControllers.push(gamepad);
+ this.isController = true;
+
+ console.log(`๐ฎ Controller connected: ${gamepad.id}`);
+ }
+
+ onControllerDisconnected(event) {
+ const gamepad = event.gamepad;
+ const index = this.connectedControllers.indexOf(gamepad);
+ if (index > -1) {
+ this.connectedControllers.splice(index, 1);
+ }
+
+ this.isController = this.connectedControllers.length > 0;
+ console.log(`๐ฎ Controller disconnected: ${gamepad.id}`);
+ }
+
+ updateControllers() {
+ const gamepads = navigator.getGamepads();
+
+ for (const gamepad of gamepads) {
+ if (!gamepad) continue;
+
+ // Read buttons
+ gamepad.buttons.forEach((button, index) => {
+ if (button.pressed) {
+ this.handleControllerButton(index);
+ }
+ });
+
+ // Read axes (joysticks)
+ const leftStickX = gamepad.axes[0];
+ const leftStickY = gamepad.axes[1];
+
+ if (Math.abs(leftStickX) > 0.1 || Math.abs(leftStickY) > 0.1) {
+ this.handleControllerMovement(leftStickX, leftStickY);
+ }
+ }
+ }
+
+ handleControllerButton(buttonIndex) {
+ // Map button to action
+ console.log(`๐ฎ Button pressed: ${buttonIndex}`);
+ }
+
+ handleControllerMovement(x, y) {
+ // Move player based on joystick input
+ if (this.scene.player) {
+ // Apply movement
+ }
+ }
+
+ // ========== STEAM DECK ==========
+
+ setupSteamDeck() {
+ // Adjust UI scale for Steam Deck screen
+ this.applyUIScale(this.steamDeckSettings.uiScale);
+
+ // Set performance mode
+ this.setPerformanceMode(this.steamDeckSettings.performanceMode);
+
+ console.log('๐ฎ Steam Deck optimizations applied');
+ }
+
+ applyUIScale(scale) {
+ // Scale UI elements for better visibility
+ console.log(`๐ UI scale: ${scale}x`);
+ }
+
+ setPerformanceMode(mode) {
+ const targetFPS = mode === '60fps' ? 60 : 30;
+
+ if (this.scene.game.config) {
+ this.scene.game.config.fps = { target: targetFPS };
+ }
+
+ console.log(`โก Performance mode: ${mode}`);
+ }
+
+ // ========== LINUX BUILD ==========
+
+ setupLinux() {
+ // Linux-specific optimizations
+ console.log('๐ง Linux optimizations applied');
+ }
+
+ // ========== MAC BUILD ==========
+
+ setupMac() {
+ // macOS-specific optimizations
+ // Metal API support
+ console.log('๐ macOS optimizations applied');
+ }
+
+ checkM1M2Chip() {
+ // Detect Apple Silicon
+ return navigator.userAgent.includes('Macintosh') &&
+ navigator.userAgent.includes('AppleWebKit');
+ }
+
+ // ========== MOBILE OPTIMIZATION ==========
+
+ optimizeForMobile() {
+ // Reduce particle count
+ // Lower texture quality
+ // Disable shadows
+ // Reduce draw distance
+ console.log('๐ฑ Mobile optimizations applied');
+ }
+
+ optimizeBattery() {
+ // Reduce FPS when inactive
+ // Disable non-essential effects
+ console.log('๐ Battery optimizations applied');
+ }
+
+ // ========== CUSTOMIZATION ==========
+
+ saveControlLayout(profileName) {
+ const layout = {
+ joystickSize: this.touchControls.joystickSize,
+ buttonOpacity: this.touchControls.buttonOpacity,
+ leftHanded: this.touchControls.leftHanded,
+ buttons: Array.from(this.actionButtons.entries())
+ };
+
+ localStorage.setItem(`control_layout_${profileName}`, JSON.stringify(layout));
+ console.log(`๐พ Control layout saved: ${profileName}`);
+ }
+
+ loadControlLayout(profileName) {
+ const saved = localStorage.getItem(`control_layout_${profileName}`);
+ if (saved) {
+ const layout = JSON.parse(saved);
+ this.touchControls = layout;
+ console.log(`๐ Control layout loaded: ${profileName}`);
+ }
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ if (this.isMobile && this.virtualJoystick) {
+ this.updateVirtualJoystick(delta);
+ }
+
+ if (this.isController) {
+ this.updateControllers();
+ }
+ }
+
+ destroy() {
+ console.log('๐ฎ Platform Support System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/PlaytimeTrackerSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/PlaytimeTrackerSystem.js
new file mode 100644
index 000000000..1a8c35e53
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/PlaytimeTrackerSystem.js
@@ -0,0 +1,141 @@
+/**
+ * PLAYTIME TRACKER SYSTEM
+ * Tracks persistent stats: playtime, deaths, harvests, etc.
+ */
+class PlaytimeTrackerSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Initialize stats
+ this.stats = {
+ playtimeSeconds: 0,
+ deaths: 0,
+ kills: 0,
+ harvests: 0,
+ plantings: 0,
+ distanceTraveled: 0,
+ moneyEarned: 0,
+ itemsCrafted: 0,
+ blocksPlaced: 0,
+ sessionStart: Date.now()
+ };
+
+ // Load from save
+ this.load();
+
+ // Update timer
+ this.updateTimer = 0;
+ this.saveInterval = 10000; // Save every 10 seconds
+ }
+
+ update(delta) {
+ // Increment playtime
+ this.stats.playtimeSeconds += delta / 1000;
+
+ // Periodic save
+ this.updateTimer += delta;
+ if (this.updateTimer >= this.saveInterval) {
+ this.updateTimer = 0;
+ this.save();
+ }
+ }
+
+ // Stat tracking methods
+ recordDeath() {
+ this.stats.deaths++;
+ this.save();
+ }
+
+ recordKill() {
+ this.stats.kills++;
+ }
+
+ recordHarvest() {
+ this.stats.harvests++;
+ }
+
+ recordPlanting() {
+ this.stats.plantings++;
+ }
+
+ recordDistance(distance) {
+ this.stats.distanceTraveled += distance;
+ }
+
+ recordMoneyEarned(amount) {
+ this.stats.moneyEarned += amount;
+ }
+
+ recordCraft() {
+ this.stats.itemsCrafted++;
+ }
+
+ recordBlockPlaced() {
+ this.stats.blocksPlaced++;
+ }
+
+ // Formatted playtime
+ getPlaytimeFormatted() {
+ const hours = Math.floor(this.stats.playtimeSeconds / 3600);
+ const minutes = Math.floor((this.stats.playtimeSeconds % 3600) / 60);
+ const seconds = Math.floor(this.stats.playtimeSeconds % 60);
+
+ if (hours > 0) {
+ return `${hours}h ${minutes}m`;
+ } else if (minutes > 0) {
+ return `${minutes}m ${seconds}s`;
+ } else {
+ return `${seconds}s`;
+ }
+ }
+
+ // Session duration
+ getSessionDuration() {
+ const sessionMs = Date.now() - this.stats.sessionStart;
+ const sessionSec = Math.floor(sessionMs / 1000);
+ return sessionSec;
+ }
+
+ // Save/Load
+ save() {
+ localStorage.setItem('novafarma_stats', JSON.stringify(this.stats));
+ }
+
+ load() {
+ const saved = localStorage.getItem('novafarma_stats');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.stats = { ...this.stats, ...data };
+ this.stats.sessionStart = Date.now(); // Reset session timer
+ } catch (e) {
+ console.error('Failed to load stats:', e);
+ }
+ }
+ }
+
+ reset() {
+ this.stats = {
+ playtimeSeconds: 0,
+ deaths: 0,
+ kills: 0,
+ harvests: 0,
+ plantings: 0,
+ distanceTraveled: 0,
+ moneyEarned: 0,
+ itemsCrafted: 0,
+ blocksPlaced: 0,
+ sessionStart: Date.now()
+ };
+ this.save();
+ }
+
+ // Get all stats
+ getStats() {
+ return {
+ ...this.stats,
+ playtimeFormatted: this.getPlaytimeFormatted(),
+ sessionDuration: this.getSessionDuration()
+ };
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/PortalNetworkSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/PortalNetworkSystem.js
new file mode 100644
index 000000000..dc25d303a
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/PortalNetworkSystem.js
@@ -0,0 +1,560 @@
+/**
+ * PortalNetworkSystem.js
+ * ======================
+ * KRVAVA ลฝETEV - Portal Network System (P15)
+ *
+ * Features:
+ * - 9 Portal zones with activation quests
+ * - Portal mechanics (swirl effects, nausea)
+ * - Town Portal Hub (fast travel)
+ * - 3 Secret portals
+ * - Portal upgrades
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class PortalNetworkSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Portal registry
+ this.portals = new Map();
+ this.activePortals = new Set();
+
+ // Portal hub
+ this.hubUnlocked = false;
+ this.zlatnikiBalance = 0;
+
+ // Upgrades
+ this.hasStabilizer = false;
+ this.hasBeacon = false;
+
+ // Travel state
+ this.isInTransit = false;
+ this.nauseaDebuff = null;
+
+ console.log('๐ PortalNetworkSystem initialized');
+
+ // Register all portals
+ this.registerPortals();
+ }
+
+ /**
+ * 15.1 - Register all portal zones
+ */
+ registerPortals() {
+ const portals = [
+ // Main Zone Portals
+ {
+ id: 'dino_valley',
+ name: 'Dino Valley Portal',
+ zone: 'Dino Valley',
+ location: { x: 50, y: 400 },
+ activationQuest: {
+ name: 'Jurassic Discovery',
+ objective: 'Find 3 Dino Eggs',
+ items: ['dino_egg'],
+ required: 3
+ },
+ icon: '๐ฆ'
+ },
+ {
+ id: 'mythical',
+ name: 'Mythical Realm Portal',
+ zone: 'Mythical Realm',
+ location: { x: 400, y: 50 },
+ activationQuest: {
+ name: 'Dragon Slayer',
+ objective: 'Slay 5 Dragons',
+ enemies: ['dragon'],
+ required: 5
+ },
+ icon: '๐'
+ },
+ {
+ id: 'endless_forest',
+ name: 'Endless Forest Portal',
+ zone: 'Endless Forest',
+ location: { x: 350, y: 200 },
+ activationQuest: {
+ name: 'Bigfoot Hunt',
+ objective: 'Find Bigfoot',
+ npc: 'bigfoot',
+ required: 1
+ },
+ icon: '๐ฒ'
+ },
+ {
+ id: 'loch_ness',
+ name: 'Loch Ness Portal',
+ zone: 'Loch Ness',
+ location: { x: 100, y: 200 },
+ activationQuest: {
+ name: 'Nessie Summoning',
+ objective: 'Fish all lakes, summon Nessie',
+ actions: ['fish_all_lakes', 'summon_nessie'],
+ required: 2
+ },
+ icon: '๐ฆ'
+ },
+ {
+ id: 'catacombs',
+ name: 'Catacombs Portal',
+ zone: 'Ancient Catacombs',
+ location: { x: 300, y: 300 },
+ activationQuest: {
+ name: 'Keymaster',
+ objective: 'Find 9 Ancient Keys',
+ items: ['ancient_key'],
+ required: 9
+ },
+ icon: '๐๏ธ'
+ },
+ {
+ id: 'egypt',
+ name: 'Egyptian Portal',
+ zone: 'Egyptian Pyramids',
+ location: { x: 200, y: 400 },
+ activationQuest: {
+ name: 'Hieroglyph Master',
+ objective: 'Solve hieroglyph puzzle',
+ puzzle: 'hieroglyph',
+ required: 1
+ },
+ icon: '๐บ'
+ },
+ {
+ id: 'amazon',
+ name: 'Amazon Portal',
+ zone: 'Amazon Jungle',
+ location: { x: 300, y: 450 },
+ activationQuest: {
+ name: 'Piranha Survivor',
+ objective: 'Survive piranha river crossing',
+ survival: 'piranha_river',
+ required: 1
+ },
+ icon: '๐ด'
+ },
+ {
+ id: 'atlantis',
+ name: 'Atlantis Portal',
+ zone: 'Atlantis Ruins',
+ location: { x: 450, y: 450 },
+ activationQuest: {
+ name: 'Crystal Collector',
+ objective: 'Find 7 Atlantean Crystals',
+ items: ['atlantean_crystal'],
+ required: 7
+ },
+ icon: '๐ฑ'
+ },
+ {
+ id: 'chernobyl',
+ name: 'Chernobyl Zone',
+ zone: 'Chernobyl Exclusion Zone',
+ location: { x: 150, y: 450 },
+ activationQuest: {
+ name: 'Train Access Only',
+ objective: 'Unlock via train system (no portal!)',
+ special: 'train_only',
+ required: 1
+ },
+ icon: 'โข๏ธ',
+ noPortal: true
+ },
+
+ // 15.4 - Secret Portals
+ {
+ id: 'developer_realm',
+ name: 'Developer Realm',
+ zone: 'Developer Secret Area',
+ location: { x: 1, y: 1 }, // Hidden!
+ activationQuest: {
+ name: 'Easter Egg Challenge',
+ objective: 'Find the secret developer egg',
+ secret: true,
+ required: 1
+ },
+ icon: '๐จโ๐ป',
+ secret: true
+ },
+ {
+ id: 'time_portal',
+ name: 'Time Portal',
+ zone: 'Pre-Outbreak Lab (Flashback)',
+ location: { x: 250, y: 250 }, // Spawn town
+ activationQuest: {
+ name: 'Ana\'s Memories',
+ objective: 'Complete main quest Act 2',
+ quest: 'act_2_complete',
+ required: 1
+ },
+ icon: 'โฐ',
+ secret: true
+ },
+ {
+ id: 'mirror_world',
+ name: 'Mirror World Portal',
+ zone: 'Reversed Reality',
+ location: { x: 500, y: 500 }, // Far corner
+ activationQuest: {
+ name: 'Shatter Reality',
+ objective: 'Break the Mirror of Truth',
+ item: 'mirror_of_truth',
+ required: 1
+ },
+ icon: '๐ช',
+ secret: true
+ }
+ ];
+
+ portals.forEach(portal => {
+ this.portals.set(portal.id, portal);
+ });
+
+ console.log(`โ
Registered ${this.portals.size} portals (${portals.filter(p => p.secret).length} secret)`);
+ }
+
+ /**
+ * 15.1 - Activate portal
+ */
+ activatePortal(portalId) {
+ const portal = this.portals.get(portalId);
+ if (!portal) {
+ console.error(`Portal ${portalId} not found!`);
+ return false;
+ }
+
+ if (this.activePortals.has(portalId)) {
+ console.log(`Portal ${portal.name} already active!`);
+ return false;
+ }
+
+ // Check activation quest completion
+ // TODO: Integrate with quest system
+ // For now, just activate
+
+ this.activePortals.add(portalId);
+
+ console.log(`๐ ${portal.icon} ${portal.name} ACTIVATED!`);
+
+ // Play activation animation
+ this.playPortalActivationAnimation(portal);
+
+ this.showNotification({
+ title: 'Portal Activated!',
+ text: `๐ ${portal.icon} ${portal.name} is now online!`,
+ icon: 'โจ'
+ });
+
+ return true;
+ }
+
+ /**
+ * 15.2 - Portal activation animation
+ */
+ playPortalActivationAnimation(portal) {
+ // TODO: Create actual portal sprite/animation
+ console.log(`โจ Portal activation animation for ${portal.name}`);
+
+ // Screen flash
+ this.scene.cameras.main.flash(1000, 100, 0, 255); // Blue flash
+
+ // Camera shake
+ this.scene.cameras.main.shake(500, 0.01);
+ }
+
+ /**
+ * 15.2 - Travel through portal
+ */
+ travelThroughPortal(portalId, fromHub = false) {
+ const portal = this.portals.get(portalId);
+ if (!portal) {
+ console.error(`Portal ${portalId} not found!`);
+ return false;
+ }
+
+ if (!this.activePortals.has(portalId) && !fromHub) {
+ this.showNotification({
+ title: 'Portal Inactive',
+ text: `${portal.name} must be activated first!`,
+ icon: '๐ซ'
+ });
+ return false;
+ }
+
+ // Check payment if using hub
+ if (fromHub) {
+ const cost = this.calculatePortalCost(portal);
+ if (this.zlatnikiBalance < cost) {
+ this.showNotification({
+ title: 'Insufficient Funds',
+ text: `Need ${cost} Zlatniki for portal travel!`,
+ icon: '๐ฐ'
+ });
+ return false;
+ }
+ this.zlatnikiBalance -= cost;
+ }
+
+ // Start transit
+ this.isInTransit = true;
+
+ // Swirl effect
+ this.playPortalSwirlEffect();
+
+ // Loading screen (2 seconds)
+ setTimeout(() => {
+ this.completePortalTravel(portal);
+ }, 2000);
+
+ return true;
+ }
+
+ /**
+ * 15.2 - Portal swirl effect
+ */
+ playPortalSwirlEffect() {
+ console.log('๐ *SWIIIIIRL*');
+
+ // Create swirl particles
+ // TODO: Implement actual particle effect
+
+ // Screen spin effect
+ this.scene.cameras.main.rotateTo(Math.PI * 4, true, 2000);
+
+ // Fade out/in
+ this.scene.cameras.main.fadeOut(1000);
+ setTimeout(() => {
+ this.scene.cameras.main.fadeIn(1000);
+ }, 1000);
+ }
+
+ /**
+ * 15.2 - Complete portal travel
+ */
+ completePortalTravel(portal) {
+ // Teleport player
+ if (this.scene.player) {
+ this.scene.player.x = portal.location.x * 48;
+ this.scene.player.y = portal.location.y * 48;
+ }
+
+ // Transit zombies
+ this.transitZombies(portal);
+
+ // Apply nausea debuff (unless stabilizer)
+ if (!this.hasStabilizer) {
+ this.applyNauseaDebuff();
+ }
+
+ this.isInTransit = false;
+
+ console.log(`๐ Arrived at ${portal.zone}!`);
+
+ this.showNotification({
+ title: 'Portal Travel Complete',
+ text: `${portal.icon} Welcome to ${portal.zone}!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * 15.2 - Transit zombies through portal
+ */
+ transitZombies(portal) {
+ // TODO: Move all tamed zombies to portal location
+ console.log('๐ง Zombies followed through portal!');
+ }
+
+ /**
+ * 15.2 - Apply nausea debuff
+ */
+ applyNauseaDebuff() {
+ this.nauseaDebuff = {
+ duration: 5000, // 5 seconds
+ startTime: Date.now()
+ };
+
+ // Visual effect (screen wobble)
+ // TODO: Implement screen wobble
+
+ console.log('๐คข Nausea debuff applied (5s)');
+
+ this.showNotification({
+ title: 'Portal Sickness',
+ text: '๐คข You feel dizzy from portal travel...',
+ icon: '๐ต'
+ });
+
+ // Remove after duration
+ setTimeout(() => {
+ this.nauseaDebuff = null;
+ console.log('โ
Nausea debuff removed');
+ }, 5000);
+ }
+
+ /**
+ * 15.3 - Open town portal hub
+ */
+ openPortalHub() {
+ if (!this.hubUnlocked) {
+ this.showNotification({
+ title: 'Hub Locked',
+ text: 'Build the Portal Hub building first! (After Town Hall)',
+ icon: '๐๏ธ'
+ });
+ return false;
+ }
+
+ // Show portal hub UI
+ this.showPortalHubUI();
+ return true;
+ }
+
+ /**
+ * 15.3 - Portal hub UI
+ */
+ showPortalHubUI() {
+ console.log('๐ Portal Hub UI opened');
+
+ // TODO: Create actual UI
+ // For now, list active portals
+ console.log('Available Portals:');
+ this.activePortals.forEach(portalId => {
+ const portal = this.portals.get(portalId);
+ if (portal && !portal.noPortal) {
+ const cost = this.calculatePortalCost(portal);
+ console.log(`- ${portal.icon} ${portal.name} (${cost}ลฝ)`);
+ }
+ });
+ }
+
+ /**
+ * 15.3 - Calculate portal cost
+ */
+ calculatePortalCost(portal) {
+ let cost = 5; // Base: 5 Zlatniki
+
+ // Add zombie transit cost (1ลฝ per zombie)
+ const zombieCount = this.scene.zombieSystem?.workers?.length || 0;
+ cost += zombieCount;
+
+ // Add animal transit cost (2ลฝ per animal)
+ const animalCount = this.scene.animalBreeding?.animals?.size || 0;
+ cost += animalCount * 2;
+
+ return cost;
+ }
+
+ /**
+ * 15.5 - Install portal stabilizer
+ */
+ installStabilizer() {
+ // TODO: Check if player has stabilizer item
+ this.hasStabilizer = true;
+
+ console.log('โ
Portal Stabilizer installed!');
+
+ this.showNotification({
+ title: 'Stabilizer Installed',
+ text: 'โจ Portal travel no longer causes nausea!',
+ icon: '๐ง'
+ });
+ }
+
+ /**
+ * 15.5 - Install portal beacon
+ */
+ installBeacon() {
+ // TODO: Check if player has beacon item
+ this.hasBeacon = true;
+
+ console.log('โ
Portal Beacon installed!');
+
+ this.showNotification({
+ title: 'Beacon Installed',
+ text: '๐ก Portals now glow brighter and are easier to find!',
+ icon: '๐ฆ'
+ });
+ }
+
+ /**
+ * Unlock portal hub
+ */
+ unlockHub() {
+ this.hubUnlocked = true;
+
+ this.showNotification({
+ title: 'Portal Hub Unlocked!',
+ text: '๐๏ธ Fast travel to all active portals!',
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Get portal info
+ */
+ getPortalInfo(portalId) {
+ return this.portals.get(portalId);
+ }
+
+ /**
+ * Get all active portals
+ */
+ getActivePortals() {
+ return Array.from(this.activePortals).map(id => this.portals.get(id));
+ }
+
+ /**
+ * Get all portals
+ */
+ getAllPortals() {
+ return Array.from(this.portals.values());
+ }
+
+ /**
+ * Check if portal active
+ */
+ isPortalActive(portalId) {
+ return this.activePortals.has(portalId);
+ }
+
+ /**
+ * Get secret portals
+ */
+ getSecretPortals() {
+ return this.getAllPortals().filter(p => p.secret);
+ }
+
+ /**
+ * Add zlatniki
+ */
+ addZlatniki(amount) {
+ this.zlatnikiBalance += amount;
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+
+ /**
+ * Update system
+ */
+ update(delta) {
+ // Update nausea debuff visual effects if active
+ if (this.nauseaDebuff) {
+ // TODO: Apply screen wobble effect
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/PortalRepairSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/PortalRepairSystem.js
new file mode 100644
index 000000000..a4dd45807
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/PortalRepairSystem.js
@@ -0,0 +1,480 @@
+/**
+ * PortalRepairSystem.js
+ * ======================
+ * KRVAVA ลฝETEV - Portal Repair & Construction (Phase 20)
+ *
+ * Features:
+ * - 18 portal locations across biomes
+ * - Repair mechanics with materials
+ * - 1-8 day construction time
+ * - Defend from attacks during repair
+ * - Lv8+ zombie builders (independent work!)
+ * - Portal network benefits
+ * - Portable Personal Portal (endgame!)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class PortalRepairSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Portal registry
+ this.portals = new Map();
+
+ // Repair progress
+ this.activeRepairs = new Map();
+
+ // Network status
+ this.networkActive = false;
+ this.portalsRepaired = 0;
+ this.totalPortals = 18;
+
+ // Personal portal (endgame unlock)
+ this.hasPersonalPortal = false;
+
+ console.log('๐ PortalRepairSystem initialized');
+
+ // Register all portals
+ this.registerPortals();
+ }
+
+ /**
+ * Register all 18 portals
+ */
+ registerPortals() {
+ const portals = [
+ // Tier 1 - Easy (1-2 days)
+ {
+ id: 'grassland_portal', name: 'Grassland Portal', biome: 'grassland',
+ materials: { stone: 100, crystal: 10 }, days: 1, difficulty: 'easy'
+ },
+ {
+ id: 'forest_portal', name: 'Forest Portal', biome: 'forest',
+ materials: { wood: 200, crystal: 15 }, days: 2, difficulty: 'easy'
+ },
+
+ // Tier 2 - Medium (3-4 days)
+ {
+ id: 'desert_portal', name: 'Desert Portal', biome: 'desert',
+ materials: { sandstone: 300, crystal: 20, gold: 10 }, days: 3, difficulty: 'medium'
+ },
+ {
+ id: 'swamp_portal', name: 'Swamp Portal', biome: 'swamp',
+ materials: { clay: 250, crystal: 25 }, days: 3, difficulty: 'medium'
+ },
+ {
+ id: 'beach_portal', name: 'Beach Portal', biome: 'beach',
+ materials: { coral: 200, crystal: 20, pearl: 5 }, days: 3, difficulty: 'medium'
+ },
+ {
+ id: 'mountain_portal', name: 'Mountain Portal', biome: 'mountain',
+ materials: { stone: 400, iron: 50, crystal: 30 }, days: 4, difficulty: 'medium'
+ },
+
+ // Tier 3 - Hard (5-6 days)
+ {
+ id: 'frozen_portal', name: 'Frozen Portal', biome: 'frozen',
+ materials: { ice: 500, crystal: 40, diamond: 5 }, days: 5, difficulty: 'hard'
+ },
+ {
+ id: 'volcanic_portal', name: 'Volcanic Portal', biome: 'volcanic',
+ materials: { obsidian: 400, crystal: 50, ruby: 10 }, days: 5, difficulty: 'hard'
+ },
+ {
+ id: 'wasteland_portal', name: 'Wasteland Portal', biome: 'wasteland',
+ materials: { scrap: 600, uranium: 20, crystal: 60 }, days: 6, difficulty: 'hard'
+ },
+ {
+ id: 'jungle_portal', name: 'Jungle Portal', biome: 'jungle',
+ materials: { vine: 300, emerald: 15, crystal: 45 }, days: 5, difficulty: 'hard'
+ },
+
+ // Tier 4 - Very Hard (7-8 days)
+ {
+ id: 'egypt_portal', name: 'Egypt Portal', biome: 'egypt',
+ materials: { sandstone: 800, gold: 100, crystal: 80 }, days: 7, difficulty: 'very_hard'
+ },
+ {
+ id: 'atlantis_portal', name: 'Atlantis Portal', biome: 'underwater',
+ materials: { orichalcum: 200, pearl: 50, crystal: 100 }, days: 8, difficulty: 'very_hard'
+ },
+ {
+ id: 'lochness_portal', name: 'Loch Ness Portal', biome: 'lochness',
+ materials: { stone: 1000, emerald: 20, crystal: 90 }, days: 7, difficulty: 'very_hard'
+ },
+ {
+ id: 'chernobyl_portal', name: 'Chernobyl Portal', biome: 'chernobyl',
+ materials: { concrete: 900, uranium: 50, crystal: 100 }, days: 8, difficulty: 'very_hard'
+ },
+ {
+ id: 'amazon_portal', name: 'Amazon Portal', biome: 'amazon',
+ materials: { wood: 1000, vine: 500, crystal: 85 }, days: 7, difficulty: 'very_hard'
+ },
+
+ // Tier 5 - Legendary (8 days)
+ {
+ id: 'mythical_portal', name: 'Mythical Realm Portal', biome: 'mythical',
+ materials: { mythril: 100, dragon_scale: 20, crystal: 150 }, days: 8, difficulty: 'legendary'
+ },
+ {
+ id: 'anomaly_portal', name: 'Anomalous Zone Portal', biome: 'anomaly',
+ materials: { artifact: 50, uranium: 100, crystal: 200 }, days: 8, difficulty: 'legendary'
+ },
+ {
+ id: 'void_portal', name: 'The Void Portal', biome: 'void',
+ materials: { void_essence: 10, cosmic_dust: 50, crystal: 250 }, days: 8, difficulty: 'legendary'
+ }
+ ];
+
+ portals.forEach(portal => {
+ this.portals.set(portal.id, {
+ ...portal,
+ isRepaired: false,
+ repairProgress: 0,
+ isUnderAttack: false,
+ questCompleted: false
+ });
+ });
+
+ console.log(`โ
Registered ${this.portals.size} portals`);
+ }
+
+ /**
+ * Start portal repair
+ */
+ startRepair(portalId, zombieBuilders = []) {
+ const portal = this.portals.get(portalId);
+ if (!portal) {
+ console.error(`Portal ${portalId} not found!`);
+ return false;
+ }
+
+ if (portal.isRepaired) {
+ console.log(`${portal.name} is already repaired!`);
+ return false;
+ }
+
+ // Check if quest is completed
+ if (!portal.questCompleted) {
+ console.log(`Complete the ${portal.name} quest first!`);
+ return false;
+ }
+
+ // Check materials
+ if (!this.hasMaterials(portal.materials)) {
+ console.log(`Not enough materials for ${portal.name}!`);
+ return false;
+ }
+
+ // Consume materials
+ this.consumeMaterials(portal.materials);
+
+ // Calculate repair time (zombie builders reduce time)
+ const builderBonus = this.calculateBuilderBonus(zombieBuilders);
+ const repairTime = portal.days * (1 - builderBonus);
+
+ // Start repair
+ const repair = {
+ portalId: portalId,
+ startTime: Date.now(),
+ duration: repairTime * 24 * 60 * 60 * 1000, // Days to ms
+ builders: zombieBuilders,
+ defendPhase: false
+ };
+
+ this.activeRepairs.set(portalId, repair);
+ portal.repairProgress = 0;
+
+ console.log(`๐ Started repairing ${portal.name} (${repairTime} days with ${zombieBuilders.length} workers)`);
+
+ // Start defend events
+ this.scheduleDefendEvents(portalId, repairTime);
+
+ this.showNotification({
+ title: 'Portal Repair Started!',
+ text: `๐ ${portal.name} - ${repairTime.toFixed(1)} days`,
+ icon: '๐จ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Calculate builder bonus
+ */
+ calculateBuilderBonus(builders) {
+ if (builders.length === 0) return 0;
+
+ let bonus = 0;
+ builders.forEach(builder => {
+ // Lv8+ zombies are 50% faster
+ if (builder.level >= 8) {
+ bonus += 0.15; // 15% per Lv8+ zombie
+ } else {
+ bonus += 0.05; // 5% per lower level zombie
+ }
+ });
+
+ return Math.min(bonus, 0.6); // Max 60% reduction
+ }
+
+ /**
+ * Schedule defend events
+ */
+ scheduleDefendEvents(portalId, repairDays) {
+ const portal = this.portals.get(portalId);
+
+ // Attack waves based on difficulty
+ const attackCount = {
+ easy: 1,
+ medium: 2,
+ hard: 3,
+ very_hard: 4,
+ legendary: 5
+ }[portal.difficulty] || 2;
+
+ // Schedule attacks at intervals
+ for (let i = 0; i < attackCount; i++) {
+ const attackTime = (repairDays / attackCount) * (i + 0.5);
+
+ setTimeout(() => {
+ this.triggerAttack(portalId);
+ }, attackTime * 24 * 60 * 60 * 1000);
+ }
+ }
+
+ /**
+ * Trigger attack on portal
+ */
+ triggerAttack(portalId) {
+ const portal = this.portals.get(portalId);
+ const repair = this.activeRepairs.get(portalId);
+
+ if (!repair) return;
+
+ portal.isUnderAttack = true;
+ repair.defendPhase = true;
+
+ console.log(`โ๏ธ ${portal.name} is under attack!`);
+
+ this.showNotification({
+ title: 'PORTAL UNDER ATTACK!',
+ text: `โ๏ธ Defend ${portal.name}!`,
+ icon: 'โ ๏ธ'
+ });
+
+ // TODO: Spawn enemies
+ // Attack ends after 5 minutes or all enemies defeated
+ setTimeout(() => {
+ portal.isUnderAttack = false;
+ repair.defendPhase = false;
+ console.log(`โ
${portal.name} defended successfully!`);
+ }, 5 * 60 * 1000);
+ }
+
+ /**
+ * Complete portal repair
+ */
+ completeRepair(portalId) {
+ const portal = this.portals.get(portalId);
+ if (!portal) return;
+
+ portal.isRepaired = true;
+ portal.repairProgress = 100;
+ this.portalsRepaired++;
+
+ this.activeRepairs.delete(portalId);
+
+ console.log(`โ
${portal.name} repaired! (${this.portalsRepaired}/${this.totalPortals})`);
+
+ // Activate portal network if this is the first portal
+ if (this.portalsRepaired === 1) {
+ this.activateNetwork();
+ }
+
+ // Check for personal portal unlock
+ if (this.portalsRepaired === this.totalPortals) {
+ this.unlockPersonalPortal();
+ }
+
+ this.showNotification({
+ title: 'Portal Complete!',
+ text: `โ
${portal.name} is now operational!`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Activate portal network
+ */
+ activateNetwork() {
+ this.networkActive = true;
+
+ console.log('๐ Portal Network activated!');
+
+ this.showNotification({
+ title: 'PORTAL NETWORK ONLINE!',
+ text: '๐ Fast travel between portals unlocked!',
+ icon: 'โจ'
+ });
+ }
+
+ /**
+ * Unlock personal portal
+ */
+ unlockPersonalPortal() {
+ this.hasPersonalPortal = true;
+
+ console.log('๐ PERSONAL PORTAL UNLOCKED!');
+
+ this.showNotification({
+ title: 'PERSONAL PORTAL!',
+ text: '๐ Portable portal unlocked! Teleport anywhere!',
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Use portal (teleport)
+ */
+ usePortal(fromPortalId, toPortalId) {
+ if (!this.networkActive) {
+ console.log('Portal network is not active yet!');
+ return false;
+ }
+
+ const from = this.portals.get(fromPortalId);
+ const to = this.portals.get(toPortalId);
+
+ if (!from || !to) {
+ console.error('Portal not found!');
+ return false;
+ }
+
+ if (!from.isRepaired || !to.isRepaired) {
+ console.log('Both portals must be repaired!');
+ return false;
+ }
+
+ console.log(`๐ Teleporting from ${from.name} to ${to.name}!`);
+
+ // Instant teleport
+ // TODO: Actual player teleport
+ // TODO: Can bring zombies for FREE (network benefit)
+
+ this.showNotification({
+ title: 'Teleported!',
+ text: `๐ Arrived at ${to.name}`,
+ icon: 'โจ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Check if Lv8+ zombie can work independently
+ */
+ canWorkIndependently(zombie) {
+ return zombie.level >= 8;
+ }
+
+ /**
+ * Send Lv8+ zombie to repair portal alone
+ */
+ sendIndependentBuilder(portalId, zombie) {
+ if (!this.canWorkIndependently(zombie)) {
+ console.log(`${zombie.name} must be Lv8+ to work alone!`);
+ return false;
+ }
+
+ console.log(`๐ค Sending ${zombie.name} (Lv${zombie.level}) to repair ${portalId} independently!`);
+
+ // Zombie travels, gathers materials, repairs portal autonomously!
+ this.startRepair(portalId, [zombie]);
+
+ this.showNotification({
+ title: 'Independent Builder!',
+ text: `๐ค ${zombie.name} will repair portal alone!`,
+ icon: 'โจ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Check materials
+ */
+ hasMaterials(required) {
+ // TODO: Check actual inventory
+ return true; // For now
+ }
+
+ /**
+ * Consume materials
+ */
+ consumeMaterials(materials) {
+ // TODO: Remove from inventory
+ console.log('๐ฆ Materials consumed:', materials);
+ }
+
+ /**
+ * Get repair progress
+ */
+ getProgress() {
+ return {
+ repaired: this.portalsRepaired,
+ total: this.totalPortals,
+ percentage: ((this.portalsRepaired / this.totalPortals) * 100).toFixed(1),
+ networkActive: this.networkActive,
+ hasPersonalPortal: this.hasPersonalPortal
+ };
+ }
+
+ /**
+ * Get portal info
+ */
+ getPortalInfo(portalId) {
+ return this.portals.get(portalId);
+ }
+
+ /**
+ * Get all portals
+ */
+ getAllPortals() {
+ return Array.from(this.portals.values());
+ }
+
+ /**
+ * Update repair progress (called from game loop)
+ */
+ update(delta) {
+ this.activeRepairs.forEach((repair, portalId) => {
+ const elapsed = Date.now() - repair.startTime;
+ const progress = (elapsed / repair.duration) * 100;
+
+ const portal = this.portals.get(portalId);
+ if (portal) {
+ portal.repairProgress = Math.min(100, progress);
+ }
+
+ // Check completion
+ if (progress >= 100) {
+ this.completeRepair(portalId);
+ }
+ });
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ProgressionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ProgressionSystem.js
new file mode 100644
index 000000000..4c26bf5f2
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ProgressionSystem.js
@@ -0,0 +1,527 @@
+/**
+ * ProgressionSystem.js
+ * ====================
+ * Manages building upgrades and player progression
+ * - House upgrades (5 levels)
+ * - Barn upgrades (4 levels)
+ * - Storage upgrades (4 levels)
+ * - Greenhouse upgrades
+ *
+ * Integrates with RecipeSystem for upgrade crafting
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-22
+ */
+
+export default class ProgressionSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Building levels (current state)
+ this.buildingLevels = {
+ house: 1, // Max: 5
+ barn: 1, // Max: 4
+ storage: 1, // Max: 4
+ greenhouse: 1 // Max: 3
+ };
+
+ // Building sprites (references to map objects)
+ this.buildingSprites = {
+ house: null,
+ barn: null,
+ storage: null,
+ greenhouse: null
+ };
+
+ // Upgrade requirements data
+ this.upgradeData = this.loadUpgradeData();
+
+ // Upgrade benefits
+ this.upgradeBenefits = this.defineUpgradeBenefits();
+
+ // Load saved progress
+ this.loadProgress();
+
+ console.log('๐ ProgressionSystem initialized');
+ }
+
+ loadUpgradeData() {
+ return {
+ house: {
+ maxLevel: 5,
+ levels: {
+ 2: {
+ name: 'Bedroom Addition',
+ description: 'Adds a bedroom - can get married!',
+ materials: { wood: 100, stone: 50, gold: 500 },
+ craftTime: 10000,
+ blueprint: 'blueprint_house_2'
+ },
+ 3: {
+ name: 'Kitchen Expansion',
+ description: 'Larger kitchen - cook better meals',
+ materials: { wood: 200, stone: 100, gold: 1500 },
+ craftTime: 15000,
+ blueprint: 'blueprint_house_3'
+ },
+ 4: {
+ name: 'Second Floor',
+ description: 'Two stories - children can have rooms',
+ materials: { wood: 400, stone: 200, iron_ingot: 50, gold: 3500 },
+ craftTime: 20000,
+ blueprint: 'blueprint_house_4'
+ },
+ 5: {
+ name: 'Mansion',
+ description: 'Ultimate house - full family home',
+ materials: { wood: 800, stone: 400, iron_ingot: 100, gold: 7500 },
+ craftTime: 30000,
+ blueprint: 'blueprint_house_5'
+ }
+ }
+ },
+ barn: {
+ maxLevel: 4,
+ levels: {
+ 2: {
+ name: 'Barn Extension',
+ description: 'Hold 8 animals, adds milking station',
+ materials: { wood: 75, stone: 30, gold: 400 },
+ craftTime: 8000,
+ blueprint: 'blueprint_barn_2'
+ },
+ 3: {
+ name: 'Large Barn',
+ description: 'Hold 12 animals, auto-feeder',
+ materials: { wood: 150, stone: 75, iron_ingot: 20, gold: 1000 },
+ craftTime: 12000,
+ blueprint: 'blueprint_barn_3'
+ },
+ 4: {
+ name: 'Deluxe Barn',
+ description: 'Hold 16 animals, auto-petter',
+ materials: { wood: 300, stone: 150, iron_ingot: 50, gold: 2500 },
+ craftTime: 18000,
+ blueprint: 'blueprint_barn_4'
+ }
+ }
+ },
+ storage: {
+ maxLevel: 4,
+ levels: {
+ 2: {
+ name: 'Upgraded Chest',
+ description: 'Inventory +40 slots',
+ materials: { wood: 50, stone: 25, gold: 300 },
+ craftTime: 5000,
+ blueprint: null // Available from start
+ },
+ 3: {
+ name: 'Large Warehouse',
+ description: 'Inventory +80 slots',
+ materials: { wood: 100, stone: 50, iron_ingot: 15, gold: 800 },
+ craftTime: 8000,
+ blueprint: 'blueprint_storage_3'
+ },
+ 4: {
+ name: 'Massive Storehouse',
+ description: 'Inventory +120 slots',
+ materials: { wood: 200, stone: 100, iron_ingot: 30, gold: 2000 },
+ craftTime: 12000,
+ blueprint: 'blueprint_storage_4'
+ }
+ }
+ },
+ greenhouse: {
+ maxLevel: 3,
+ levels: {
+ 2: {
+ name: 'Medium Greenhouse',
+ description: 'Grow 20 crops year-round',
+ materials: { wood: 80, glass: 50, gold: 600 },
+ craftTime: 10000,
+ blueprint: 'blueprint_greenhouse_2'
+ },
+ 3: {
+ name: 'Large Greenhouse',
+ description: 'Grow 50 crops year-round',
+ materials: { wood: 160, glass: 100, iron_ingot: 25, gold: 1500 },
+ craftTime: 15000,
+ blueprint: 'blueprint_greenhouse_3'
+ }
+ }
+ }
+ };
+ }
+
+ defineUpgradeBenefits() {
+ return {
+ house: {
+ 1: { bedrooms: 0, kitchen: 'basic', canMarry: false },
+ 2: { bedrooms: 1, kitchen: 'basic', canMarry: true },
+ 3: { bedrooms: 1, kitchen: 'upgraded', canMarry: true, cookingBonus: 1.25 },
+ 4: { bedrooms: 3, kitchen: 'upgraded', canMarry: true, canHaveKids: true, cookingBonus: 1.5 },
+ 5: { bedrooms: 5, kitchen: 'deluxe', canMarry: true, canHaveKids: true, maxKids: 3, cookingBonus: 2.0 }
+ },
+ barn: {
+ 1: { capacity: 4, autoFeeder: false, autoPetter: false },
+ 2: { capacity: 8, autoFeeder: false, autoPetter: false, hasMilkingStation: true },
+ 3: { capacity: 12, autoFeeder: true, autoPetter: false, hasMilkingStation: true },
+ 4: { capacity: 16, autoFeeder: true, autoPetter: true, hasMilkingStation: true }
+ },
+ storage: {
+ 1: { slots: 40 },
+ 2: { slots: 80 },
+ 3: { slots: 160 },
+ 4: { slots: 280 }
+ },
+ greenhouse: {
+ 1: { capacity: 10, seasonal: false },
+ 2: { capacity: 20, seasonal: false },
+ 3: { capacity: 50, seasonal: false, qualityBonus: 1.5 }
+ }
+ };
+ }
+
+ // ===== UPGRADE CHECKS =====
+
+ canUpgrade(buildingType) {
+ const currentLevel = this.buildingLevels[buildingType];
+ const maxLevel = this.upgradeData[buildingType].maxLevel;
+
+ // Check if already max level
+ if (currentLevel >= maxLevel) {
+ return {
+ canUpgrade: false,
+ reason: `Already at max level (${maxLevel})`
+ };
+ }
+
+ const nextLevel = currentLevel + 1;
+ const requirements = this.getUpgradeRequirements(buildingType, nextLevel);
+
+ if (!requirements) {
+ return { canUpgrade: false, reason: 'Upgrade data not found' };
+ }
+
+ // Check if blueprint is unlocked (if required)
+ if (requirements.blueprint) {
+ const recipeId = `${buildingType}_upgrade_${nextLevel}`;
+ if (this.scene.recipeSystem &&
+ !this.scene.recipeSystem.unlockedRecipes.has(recipeId)) {
+ return {
+ canUpgrade: false,
+ reason: 'Blueprint not unlocked'
+ };
+ }
+ }
+
+ // Check materials
+ const missingMaterials = [];
+ for (const [item, quantity] of Object.entries(requirements.materials)) {
+ const hasQuantity = this.getItemQuantity(item);
+ if (hasQuantity < quantity) {
+ missingMaterials.push({
+ item: item,
+ need: quantity,
+ have: hasQuantity
+ });
+ }
+ }
+
+ if (missingMaterials.length > 0) {
+ return {
+ canUpgrade: false,
+ reason: 'Missing materials',
+ missing: missingMaterials
+ };
+ }
+
+ return { canUpgrade: true, nextLevel };
+ }
+
+ // ===== UPGRADE EXECUTION =====
+
+ upgrade(buildingType) {
+ const check = this.canUpgrade(buildingType);
+ if (!check.canUpgrade) {
+ this.scene.uiSystem?.showError(check.reason);
+ console.warn('Cannot upgrade', buildingType, ':', check.reason);
+ return false;
+ }
+
+ const currentLevel = this.buildingLevels[buildingType];
+ const nextLevel = check.nextLevel;
+ const requirements = this.getUpgradeRequirements(buildingType, nextLevel);
+
+ // Consume resources
+ this.consumeResources(requirements.materials);
+
+ // Show upgrade progress UI
+ this.scene.uiSystem?.showUpgradeProgress(buildingType, requirements);
+
+ // Wait for construction time
+ this.scene.time.delayedCall(requirements.craftTime, () => {
+ this.completeUpgrade(buildingType, nextLevel);
+ });
+
+ // Emit event
+ this.scene.events.emit('upgradeStarted', {
+ buildingType,
+ fromLevel: currentLevel,
+ toLevel: nextLevel,
+ requirements
+ });
+
+ console.log(`๐๏ธ Started upgrading ${buildingType} to level ${nextLevel}`);
+ return true;
+ }
+
+ completeUpgrade(buildingType, newLevel) {
+ const oldLevel = this.buildingLevels[buildingType];
+
+ // Update level
+ this.buildingLevels[buildingType] = newLevel;
+
+ // Update sprite
+ this.updateBuildingSprite(buildingType, newLevel);
+
+ // Apply benefits
+ const benefits = this.getBenefits(buildingType, newLevel);
+ this.applyBenefits(buildingType, benefits);
+
+ // Save progress
+ this.saveProgress();
+
+ // Show notification
+ const upgradeInfo = this.getUpgradeRequirements(buildingType, newLevel);
+ this.scene.uiSystem?.showNotification({
+ title: `${buildingType.toUpperCase()} UPGRADED!`,
+ message: upgradeInfo.description,
+ icon: buildingType,
+ duration: 5000,
+ color: '#00FF00'
+ });
+
+ // Emit event
+ this.scene.events.emit('upgradeComplete', {
+ buildingType,
+ oldLevel,
+ newLevel,
+ benefits
+ });
+
+ console.log(`โ
${buildingType} upgraded to level ${newLevel}`);
+ }
+
+ updateBuildingSprite(buildingType, level) {
+ const sprite = this.buildingSprites[buildingType];
+
+ if (!sprite) {
+ console.warn(`Building sprite not found for ${buildingType}`);
+ return;
+ }
+
+ // Update frame based on level (assumes sprite sheet has frames for each level)
+ // Frame index = level - 1 (0-indexed)
+ const frameIndex = level - 1;
+
+ if (sprite.setFrame) {
+ sprite.setFrame(frameIndex);
+ } else if (sprite.setTexture) {
+ // If using separate textures per level
+ sprite.setTexture(`${buildingType}_level_${level}`);
+ }
+
+ // Play upgrade animation (optional)
+ if (this.scene.particleSystem) {
+ this.scene.particleSystem.createUpgradeEffect(sprite.x, sprite.y);
+ }
+
+ console.log(`๐ผ๏ธ Updated ${buildingType} sprite to level ${level}`);
+ }
+
+ applyBenefits(buildingType, benefits) {
+ // Apply building-specific benefits
+ switch (buildingType) {
+ case 'house':
+ if (benefits.canMarry) {
+ this.scene.events.emit('marriageUnlocked');
+ }
+ if (benefits.canHaveKids) {
+ this.scene.events.emit('kidsUnlocked');
+ }
+ // Update cooking multiplier
+ if (this.scene.farmingSystem) {
+ this.scene.farmingSystem.cookingMultiplier = benefits.cookingBonus || 1.0;
+ }
+ break;
+
+ case 'barn':
+ // Update animal capacity
+ if (this.scene.animalSystem) {
+ this.scene.animalSystem.maxAnimals = benefits.capacity;
+ this.scene.animalSystem.autoFeeder = benefits.autoFeeder;
+ this.scene.animalSystem.autoPetter = benefits.autoPetter;
+ }
+ break;
+
+ case 'storage':
+ // Update inventory capacity
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.maxSlots = benefits.slots;
+ }
+ break;
+
+ case 'greenhouse':
+ // Update greenhouse capacity
+ if (this.scene.farmingSystem) {
+ this.scene.farmingSystem.greenhouseCapacity = benefits.capacity;
+ this.scene.farmingSystem.greenhouseQualityBonus = benefits.qualityBonus || 1.0;
+ }
+ break;
+ }
+
+ console.log(`โจ Applied benefits for ${buildingType}:`, benefits);
+ }
+
+ // ===== HELPER FUNCTIONS =====
+
+ registerBuildingSprite(buildingType, sprite) {
+ this.buildingSprites[buildingType] = sprite;
+
+ // Set initial sprite frame based on current level
+ const currentLevel = this.buildingLevels[buildingType];
+ this.updateBuildingSprite(buildingType, currentLevel);
+
+ console.log(`๐ Registered ${buildingType} sprite at level ${currentLevel}`);
+ }
+
+ getUpgradeRequirements(buildingType, level) {
+ return this.upgradeData[buildingType]?.levels[level];
+ }
+
+ getBenefits(buildingType, level) {
+ return this.upgradeBenefits[buildingType]?.[level];
+ }
+
+ getCurrentBenefits(buildingType) {
+ const level = this.buildingLevels[buildingType];
+ return this.getBenefits(buildingType, level);
+ }
+
+ consumeResources(materials) {
+ for (const [item, quantity] of Object.entries(materials)) {
+ this.removeItem(item, quantity);
+ }
+ console.log('๐ฐ Consumed resources:', materials);
+ }
+
+ // ===== INVENTORY INTEGRATION =====
+
+ getItemQuantity(itemId) {
+ if (this.scene.inventorySystem) {
+ return this.scene.inventorySystem.getQuantity(itemId);
+ }
+ return this.scene.player?.inventory?.[itemId] || 0;
+ }
+
+ removeItem(itemId, quantity) {
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.removeItem(itemId, quantity);
+ } else if (this.scene.player?.inventory) {
+ this.scene.player.inventory[itemId] -= quantity;
+ }
+ }
+
+ // ===== DATA PERSISTENCE =====
+
+ saveProgress() {
+ const data = {
+ buildingLevels: this.buildingLevels,
+ timestamp: Date.now()
+ };
+ localStorage.setItem('buildingProgress', JSON.stringify(data));
+ console.log('๐พ Saved building progress');
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('buildingProgress');
+ if (saved) {
+ const data = JSON.parse(saved);
+ this.buildingLevels = data.buildingLevels;
+ console.log('๐ Loaded building progress:', this.buildingLevels);
+ }
+ }
+
+ // ===== GETTERS =====
+
+ getLevel(buildingType) {
+ return this.buildingLevels[buildingType];
+ }
+
+ getMaxLevel(buildingType) {
+ return this.upgradeData[buildingType]?.maxLevel;
+ }
+
+ isMaxLevel(buildingType) {
+ return this.getLevel(buildingType) >= this.getMaxLevel(buildingType);
+ }
+
+ getNextUpgradeInfo(buildingType) {
+ const currentLevel = this.getLevel(buildingType);
+ const nextLevel = currentLevel + 1;
+ const maxLevel = this.getMaxLevel(buildingType);
+
+ if (currentLevel >= maxLevel) {
+ return null;
+ }
+
+ return {
+ currentLevel,
+ nextLevel,
+ maxLevel,
+ requirements: this.getUpgradeRequirements(buildingType, nextLevel),
+ benefits: this.getBenefits(buildingType, nextLevel),
+ canUpgrade: this.canUpgrade(buildingType)
+ };
+ }
+
+ getAllUpgradeInfo() {
+ const info = {};
+ for (const buildingType of Object.keys(this.buildingLevels)) {
+ info[buildingType] = this.getNextUpgradeInfo(buildingType);
+ }
+ return info;
+ }
+
+ getProgressPercentage(buildingType) {
+ const current = this.getLevel(buildingType);
+ const max = this.getMaxLevel(buildingType);
+ return (current / max) * 100;
+ }
+
+ getTotalProgress() {
+ const buildings = Object.keys(this.buildingLevels);
+ let totalCurrent = 0;
+ let totalMax = 0;
+
+ for (const building of buildings) {
+ totalCurrent += this.getLevel(building);
+ totalMax += this.getMaxLevel(building);
+ }
+
+ return {
+ current: totalCurrent,
+ max: totalMax,
+ percentage: (totalCurrent / totalMax) * 100
+ };
+ }
+
+ // ===== CLEANUP =====
+
+ destroy() {
+ this.saveProgress();
+ this.buildingSprites = {};
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/PyramidSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/PyramidSystem.js
new file mode 100644
index 000000000..fc7116a96
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/PyramidSystem.js
@@ -0,0 +1,575 @@
+/**
+ * PYRAMID SYSTEM
+ * Manages the 3 Great Pyramids, Sphinx, player-buildable pyramids, and pyramid benefits.
+ *
+ * Features:
+ * - 3 Great Pyramids: Dungeons with chambers, traps, puzzles, Mummy Pharaoh boss
+ * - The Sphinx: 3 riddles system, boss fight on wrong answer, blueprint reward
+ * - Player-Buildable Pyramids: 3 sizes (Small, Medium, Great), 2,000-15,000 Sandstone cost
+ * - Pyramid Benefits: Tourist income (+500 Zล/day), healing buff, respawn point
+ */
+class PyramidSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Great Pyramids (world landmarks)
+ this.greatPyramids = [
+ {
+ id: 'pyramid_of_khufu',
+ name: 'Pyramid of Khufu',
+ position: { x: 6000, y: 4000 },
+ biome: 'desert',
+ floors: 5,
+ chambers: ['Grand Gallery', 'King\'s Chamber', 'Queen\'s Chamber', 'Subterranean Chamber'],
+ boss: 'pharaoh_khufu',
+ explored: false,
+ treasures: ['golden_mask', 'pharaoh_staff', 'blueprint_time_machine']
+ },
+ {
+ id: 'pyramid_of_khafre',
+ name: 'Pyramid of Khafre',
+ position: { x: 6200, y: 4100 },
+ biome: 'desert',
+ floors: 4,
+ chambers: ['Entrance Corridor', 'Burial Chamber', 'Treasure Room'],
+ boss: 'pharaoh_khafre',
+ explored: false,
+ treasures: ['ancient_scroll', 'scarab_amulet', 'blueprint_cloning_vat']
+ },
+ {
+ id: 'pyramid_of_menkaure',
+ name: 'Pyramid of Menkaure',
+ position: { x: 6100, y: 4200 },
+ biome: 'desert',
+ floors: 3,
+ chambers: ['Main Chamber', 'Sarcophagus Hall'],
+ boss: 'pharaoh_menkaure',
+ explored: false,
+ treasures: ['royal_scepter', 'papyrus_collection', 'blueprint_helicopter']
+ }
+ ];
+
+ // The Sphinx
+ this.sphinx = {
+ position: { x: 6150, y: 4050 },
+ riddlesAsked: [],
+ defeated: false,
+ riddles: [
+ {
+ id: 'riddle_1',
+ question: 'What walks on four legs in the morning, two legs at noon, and three legs in the evening?',
+ answer: 'human',
+ alternatives: ['man', 'person', 'humanity']
+ },
+ {
+ id: 'riddle_2',
+ question: 'I have cities but no houses, forests but no trees, and water but no fish. What am I?',
+ answer: 'map',
+ alternatives: ['a map', 'the map']
+ },
+ {
+ id: 'riddle_3',
+ question: 'The more you take, the more you leave behind. What am I?',
+ answer: 'footsteps',
+ alternatives: ['steps', 'footprints', 'tracks']
+ },
+ {
+ id: 'riddle_4',
+ question: 'What has keys but no locks, space but no room, and you can enter but can\'t go inside?',
+ answer: 'keyboard',
+ alternatives: ['a keyboard']
+ },
+ {
+ id: 'riddle_5',
+ question: 'I speak without a mouth and hear without ears. I have no body, but come alive with wind. What am I?',
+ answer: 'echo',
+ alternatives: ['an echo']
+ }
+ ],
+ reward: 'blueprint_sphinx_statue'
+ };
+
+ // Player-buildable pyramids
+ this.playerPyramids = new Map(); // pyramidId -> pyramid data
+
+ // Pyramid sizes
+ this.pyramidSizes = {
+ small: {
+ name: 'Small Pyramid',
+ cost: { sandstone: 2000, gold: 500 },
+ size: { width: 10, height: 8 },
+ buildTime: 3, // days
+ benefits: { tourism: 100, healing: 0.05, respawn: false }
+ },
+ medium: {
+ name: 'Medium Pyramid',
+ cost: { sandstone: 7000, gold: 2000 },
+ size: { width: 20, height: 16 },
+ buildTime: 7,
+ benefits: { tourism: 300, healing: 0.10, respawn: true }
+ },
+ great: {
+ name: 'Great Pyramid',
+ cost: { sandstone: 15000, gold: 5000, limestone: 3000 },
+ size: { width: 40, height: 32 },
+ buildTime: 14,
+ benefits: { tourism: 500, healing: 0.20, respawn: true }
+ }
+ };
+
+ // Dungeon traps
+ this.trapTypes = ['arrow_trap', 'spike_floor', 'falling_ceiling', 'poison_dart', 'sand_trap', 'cursed_scarab'];
+
+ // Puzzles
+ this.puzzleTypes = ['weight_puzzle', 'hieroglyph_puzzle', 'mirror_puzzle', 'torch_puzzle', 'statue_puzzle'];
+
+ console.log('๐๏ธ Pyramid System initialized!');
+ }
+
+ /**
+ * Enter a Great Pyramid dungeon
+ */
+ enterPyramid(pyramidId) {
+ const pyramid = this.greatPyramids.find(p => p.id === pyramidId);
+ if (!pyramid) {
+ console.log(`โ Pyramid not found: ${pyramidId}`);
+ return false;
+ }
+
+ console.log(`๐๏ธ Entering ${pyramid.name}...`);
+
+ // Generate dungeon layout
+ const dungeon = this.generateDungeon(pyramid);
+
+ // Load dungeon scene
+ this.scene.events.emit('enter-dungeon', {
+ pyramid: pyramid,
+ dungeon: dungeon
+ });
+
+ return true;
+ }
+
+ /**
+ * Generate dungeon layout
+ */
+ generateDungeon(pyramid) {
+ const dungeon = {
+ floors: [],
+ totalChambers: pyramid.chambers.length,
+ totalTraps: pyramid.floors * 3,
+ totalPuzzles: pyramid.floors
+ };
+
+ for (let floor = 0; floor < pyramid.floors; floor++) {
+ const floorData = {
+ level: floor + 1,
+ chambers: this.generateFloorChambers(floor, pyramid),
+ traps: this.generateTraps(3),
+ puzzle: this.generatePuzzle()
+ };
+
+ dungeon.floors.push(floorData);
+ }
+
+ // Final floor has boss
+ dungeon.floors[pyramid.floors - 1].boss = pyramid.boss;
+ dungeon.floors[pyramid.floors - 1].treasures = pyramid.treasures;
+
+ return dungeon;
+ }
+
+ /**
+ * Generate chambers for a floor
+ */
+ generateFloorChambers(floorLevel, pyramid) {
+ const chambersPerFloor = Math.ceil(pyramid.chambers.length / pyramid.floors);
+ const startIndex = floorLevel * chambersPerFloor;
+ const endIndex = Math.min(startIndex + chambersPerFloor, pyramid.chambers.length);
+
+ return pyramid.chambers.slice(startIndex, endIndex);
+ }
+
+ /**
+ * Generate traps
+ */
+ generateTraps(count) {
+ const traps = [];
+ for (let i = 0; i < count; i++) {
+ const trapType = this.trapTypes[Math.floor(Math.random() * this.trapTypes.length)];
+ traps.push({
+ type: trapType,
+ damage: 20 + (i * 10),
+ disarmable: Math.random() > 0.3,
+ position: { x: Math.random() * 100, y: Math.random() * 100 }
+ });
+ }
+ return traps;
+ }
+
+ /**
+ * Generate puzzle
+ */
+ generatePuzzle() {
+ const puzzleType = this.puzzleTypes[Math.floor(Math.random() * this.puzzleTypes.length)];
+
+ const puzzles = {
+ weight_puzzle: {
+ type: 'weight_puzzle',
+ description: 'Balance the scales to open the door.',
+ solution: 'Place 3 golden weights on left, 2 silver on right'
+ },
+ hieroglyph_puzzle: {
+ type: 'hieroglyph_puzzle',
+ description: 'Match the hieroglyphs to their meanings.',
+ solution: 'Sun-Life, Ankh-Eternal, Eye-Protection, Scarab-Rebirth'
+ },
+ mirror_puzzle: {
+ type: 'mirror_puzzle',
+ description: 'Reflect the light beam to hit all 4 crystals.',
+ solution: 'Rotate mirrors at 45ยฐ angles'
+ },
+ torch_puzzle: {
+ type: 'torch_puzzle',
+ description: 'Light the torches in the correct order.',
+ solution: 'North, East, South, West (clockwise from North)'
+ },
+ statue_puzzle: {
+ type: 'statue_puzzle',
+ description: 'Turn the statues to face the center altar.',
+ solution: 'All 4 statues facing inward'
+ }
+ };
+
+ return puzzles[puzzleType];
+ }
+
+ /**
+ * Fight Mummy Pharaoh boss
+ */
+ fightPharaoh(pharaohId) {
+ const pharaohs = {
+ pharaoh_khufu: {
+ name: 'Mummy Pharaoh Khufu',
+ health: 5000,
+ attacks: ['curse_of_ages', 'sandstorm', 'royal_smite', 'summon_mummies'],
+ weaknesses: ['fire_magic', 'holy_water'],
+ phases: 3
+ },
+ pharaoh_khafre: {
+ name: 'Mummy Pharaoh Khafre',
+ health: 4500,
+ attacks: ['plague_swarm', 'quicksand', 'pharaoh_wrath', 'scarab_army'],
+ weaknesses: ['ice_magic', 'silver_weapons'],
+ phases: 2
+ },
+ pharaoh_menkaure: {
+ name: 'Mummy Pharaoh Menkaure',
+ health: 4000,
+ attacks: ['tomb_collapse', 'cursed_touch', 'soul_drain', 'guardian_spirits'],
+ weaknesses: ['lightning_magic', 'blessed_blade'],
+ phases: 2
+ }
+ };
+
+ const pharaoh = pharaohs[pharaohId];
+ if (!pharaoh) return null;
+
+ console.log(`โ๏ธ Boss Fight: ${pharaoh.name}!`);
+
+ this.scene.events.emit('start-boss-fight', {
+ boss: pharaoh,
+ arena: 'pyramid_throne_room',
+ music: 'epic_egyptian'
+ });
+
+ return pharaoh;
+ }
+
+ /**
+ * Challenge the Sphinx
+ */
+ challengeSphinx() {
+ if (this.sphinx.defeated) {
+ console.log('โน๏ธ The Sphinx has already been defeated!');
+ return { success: false, message: 'Already defeated' };
+ }
+
+ // Select 3 random riddles
+ const selectedRiddles = this.getRandomRiddles(3);
+ this.sphinx.riddlesAsked = selectedRiddles;
+
+ console.log('๐ฆ The Sphinx speaks...');
+
+ this.scene.events.emit('sphinx-challenge', {
+ riddles: selectedRiddles,
+ onAnswer: (riddleId, answer) => this.answerSphinxRiddle(riddleId, answer)
+ });
+
+ return { success: true, riddles: selectedRiddles };
+ }
+
+ /**
+ * Get random riddles
+ */
+ getRandomRiddles(count) {
+ const shuffled = [...this.sphinx.riddles].sort(() => 0.5 - Math.random());
+ return shuffled.slice(0, count);
+ }
+
+ /**
+ * Answer Sphinx riddle
+ */
+ answerSphinxRiddle(riddleId, playerAnswer) {
+ const riddle = this.sphinx.riddles.find(r => r.id === riddleId);
+ if (!riddle) return { correct: false, fight: false };
+
+ const normalizedAnswer = playerAnswer.toLowerCase().trim();
+ const isCorrect = normalizedAnswer === riddle.answer ||
+ riddle.alternatives.some(alt => normalizedAnswer === alt.toLowerCase());
+
+ if (isCorrect) {
+ console.log('โ
Correct answer!');
+
+ // Check if all 3 riddles answered
+ const riddlesAnswered = this.sphinx.riddlesAsked.filter(r => r.answered).length + 1;
+
+ if (riddlesAnswered >= 3) {
+ return this.defeatSphinxPeacefully();
+ }
+
+ return { correct: true, fight: false, remaining: 3 - riddlesAnswered };
+ } else {
+ console.log('โ Wrong answer! The Sphinx attacks!');
+ return this.fightSphinx();
+ }
+ }
+
+ /**
+ * Defeat Sphinx peacefully (all riddles correct)
+ */
+ defeatSphinxPeacefully() {
+ this.sphinx.defeated = true;
+
+ console.log('๐ All riddles correct! The Sphinx grants you a reward!');
+
+ this.scene.events.emit('notification', {
+ title: 'Sphinx Impressed!',
+ message: `Received ${this.sphinx.reward}!`,
+ icon: '๐ฆ'
+ });
+
+ // Award blueprint
+ if (this.scene.blueprintSystem) {
+ this.scene.blueprintSystem.unlockRecipe('sphinx_statue');
+ }
+
+ return { correct: true, fight: false, reward: this.sphinx.reward, peaceful: true };
+ }
+
+ /**
+ * Fight the Sphinx (wrong answer)
+ */
+ fightSphinx() {
+ const sphinxBoss = {
+ name: 'The Great Sphinx',
+ health: 6000,
+ attacks: ['riddle_blast', 'sand_claw', 'roar_of_ages', 'ancient_curse'],
+ weaknesses: ['wisdom_spell', 'truth_serum'],
+ phases: 3
+ };
+
+ console.log('โ๏ธ Boss Fight: The Great Sphinx!');
+
+ this.scene.events.emit('start-boss-fight', {
+ boss: sphinxBoss,
+ arena: 'sphinx_platform',
+ music: 'epic_sphinx'
+ });
+
+ return { correct: false, fight: true, boss: sphinxBoss };
+ }
+
+ /**
+ * Build a player pyramid
+ */
+ buildPyramid(size, position) {
+ if (!['small', 'medium', 'great'].includes(size)) {
+ console.log(`โ Invalid pyramid size: ${size}`);
+ return { success: false, message: 'Invalid size' };
+ }
+
+ const pyramidData = this.pyramidSizes[size];
+
+ // Check resources
+ const hasResources = this.checkResources(pyramidData.cost);
+ if (!hasResources) {
+ return { success: false, message: 'Not enough resources!' };
+ }
+
+ // Consume resources
+ this.consumeResources(pyramidData.cost);
+
+ // Create pyramid
+ const pyramidId = `player_pyramid_${Date.now()}`;
+ const pyramid = {
+ id: pyramidId,
+ size: size,
+ position: position,
+ buildProgress: 0,
+ buildTime: pyramidData.buildTime,
+ complete: false,
+ benefits: pyramidData.benefits
+ };
+
+ this.playerPyramids.set(pyramidId, pyramid);
+
+ console.log(`๐๏ธ Building ${pyramidData.name}! ${pyramidData.buildTime} days to complete.`);
+
+ this.scene.events.emit('notification', {
+ title: 'Pyramid Construction Started!',
+ message: `${pyramidData.name} will be ready in ${pyramidData.buildTime} days!`,
+ icon: '๐๏ธ'
+ });
+
+ return { success: true, pyramidId: pyramidId, pyramid: pyramid };
+ }
+
+ /**
+ * Check if player has resources
+ */
+ checkResources(cost) {
+ // Placeholder - in real game would check inventory
+ return true;
+ }
+
+ /**
+ * Consume resources
+ */
+ consumeResources(cost) {
+ // Placeholder - in real game would remove from inventory
+ Object.keys(cost).forEach(resource => {
+ console.log(`Consumed ${cost[resource]} ${resource}`);
+ });
+ }
+
+ /**
+ * Update pyramid construction (called daily)
+ */
+ updateConstruction(delta) {
+ this.playerPyramids.forEach((pyramid, id) => {
+ if (!pyramid.complete) {
+ pyramid.buildProgress += delta;
+
+ if (pyramid.buildProgress >= pyramid.buildTime) {
+ this.completePyramid(id);
+ }
+ }
+ });
+ }
+
+ /**
+ * Complete pyramid construction
+ */
+ completePyramid(pyramidId) {
+ const pyramid = this.playerPyramids.get(pyramidId);
+ if (!pyramid) return;
+
+ pyramid.complete = true;
+ pyramid.buildProgress = pyramid.buildTime;
+
+ const pyramidData = this.pyramidSizes[pyramid.size];
+
+ console.log(`๐ ${pyramidData.name} construction complete!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Pyramid Complete!',
+ message: `Your ${pyramidData.name} is finished! Enjoy +${pyramid.benefits.tourism} Zล/day!`,
+ icon: '๐๏ธ'
+ });
+
+ // Apply benefits
+ this.applyPyramidBenefits(pyramidId);
+ }
+
+ /**
+ * Apply pyramid benefits
+ */
+ applyPyramidBenefits(pyramidId) {
+ const pyramid = this.playerPyramids.get(pyramidId);
+ if (!pyramid || !pyramid.complete) return;
+
+ const benefits = pyramid.benefits;
+
+ // Tourism income (passive gold)
+ if (this.scene.economySystem) {
+ this.scene.economySystem.addPassiveIncome('pyramid_' + pyramidId, benefits.tourism);
+ }
+
+ // Set as respawn point if applicable
+ if (benefits.respawn) {
+ this.scene.respawnPoint = pyramid.position;
+ console.log(`๐ Pyramid set as respawn point!`);
+ }
+
+ console.log(`โจ Pyramid benefits active: +${benefits.tourism} Zล/day, ${benefits.healing * 100}% healing buff`);
+ }
+
+ /**
+ * Get healing buff from nearest pyramid
+ */
+ getHealingBuff(playerPosition) {
+ let maxHealing = 0;
+
+ this.playerPyramids.forEach(pyramid => {
+ if (!pyramid.complete) return;
+
+ const distance = Phaser.Math.Distance.Between(
+ playerPosition.x, playerPosition.y,
+ pyramid.position.x, pyramid.position.y
+ );
+
+ // Healing decreases with distance (max 500 tiles)
+ if (distance < 500) {
+ const healingAmount = pyramid.benefits.healing * (1 - (distance / 500));
+ maxHealing = Math.max(maxHealing, healingAmount);
+ }
+ });
+
+ return maxHealing;
+ }
+
+ /**
+ * Get total daily tourism income
+ */
+ getTotalTourismIncome() {
+ let total = 0;
+
+ this.playerPyramids.forEach(pyramid => {
+ if (pyramid.complete) {
+ total += pyramid.benefits.tourism;
+ }
+ });
+
+ return total;
+ }
+
+ /**
+ * Get pyramid stats
+ */
+ getPyramidStats(pyramidId) {
+ const pyramid = this.playerPyramids.get(pyramidId);
+ if (!pyramid) return null;
+
+ const pyramidData = this.pyramidSizes[pyramid.size];
+
+ return {
+ name: pyramidData.name,
+ size: pyramid.size,
+ progress: pyramid.complete ? '100%' : `${Math.round((pyramid.buildProgress / pyramid.buildTime) * 100)}%`,
+ daysRemaining: pyramid.complete ? 0 : Math.ceil(pyramid.buildTime - pyramid.buildProgress),
+ benefits: pyramid.benefits,
+ position: pyramid.position
+ };
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/QuestSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/QuestSystem.js
new file mode 100644
index 000000000..84863c7a1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/QuestSystem.js
@@ -0,0 +1,634 @@
+/**
+ * QUEST SYSTEM v2.0 - Mrtva Dolina
+ * Complete quest management with ADHD-friendly dialogue and VFX integration
+ *
+ * Features:
+ * - Main story arc (Zamegljeni Spomini, Anina Sled)
+ * - Town economy quests (NPCs)
+ * - Defense quests (Wall building, Museum)
+ * - Side quests & companion recruitment
+ * - Progress tracking & rewards
+ * - Dialogue system integration
+ * - VFX triggers
+ *
+ * v2.1 UPDATE: Now loads quests from QuestDataLoader (Quest Manifest v2.0 compatible)
+ */
+
+// โ DISABLED: ES6 import not supported in browser without module bundler
+// import QuestDataLoader from '../data/QuestDataLoader.js';
+
+class QuestSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.quests = new Map();
+ this.activeQuests = [];
+ this.completedQuests = [];
+ this.questLog = [];
+
+ this.initializeQuests();
+ }
+
+ initializeQuests() {
+ console.log('๐ Loading Quest Manifest v2.0...');
+
+ // โ DISABLED: QuestDataLoader not available without ES6 modules
+ // Load all quests from QuestDataLoader
+ // const manifestQuests = QuestDataLoader.loadAllQuests();
+ // manifestQuests.forEach(quest => {
+ // this.registerQuest(quest);
+ // });
+ // console.log(`โ
Loaded ${manifestQuests.length} quests from manifest!`);
+
+ // Keep legacy quests for backwards compatibility
+ this.registerLegacyQuests();
+ }
+
+ /**
+ * Legacy quests for backwards compatibility
+ */
+ registerLegacyQuests() {
+ // MAIN STORY QUEST 1: Zamegljeni Spomini
+ this.registerQuest({
+ id: 'zamegljeni_spomini',
+ title: 'Zamegljeni Spomini',
+ type: 'main_story',
+ priority: 5,
+ description: 'Kai se zbudi brez spominov. Najdi fotografijo.',
+ objectives: [
+ { id: 'find_photo', text: 'Najdi druลพinsko fotografijo', type: 'item', item: 'family_photo', required: 1, current: 0 },
+ { id: 'unlock_capital', text: 'Odkleni Capital City', type: 'flag', flag: 'capital_unlocked', complete: false }
+ ],
+ rewards: { xp: 500, items: ['family_photo'], unlocks: ['capital_city'] },
+ vfx: { start: 'amnesia_blur', complete: 'flashback' },
+ dialogue: {
+ start: ["Kje sem? Kaj se mi dogaja?", "Ta fotka... Kdo je ta punca?"],
+ complete: ["Ana! Pomnim se! To je moja sestra!"]
+ },
+ npc: null,
+ location: 'base_farm'
+ });
+
+ // MAIN STORY QUEST 2: Anina Sled
+ this.registerQuest({
+ id: 'anะธะฝะฐ_sled',
+ title: 'Anina Sled',
+ type: 'main_story',
+ priority: 5,
+ description: 'Zberi 50 namigov o tem, kje je Ana.',
+ objectives: [
+ { id: 'collect_clues', text: 'Zberi Aniะฝะต namige (0/50)', type: 'collection', item: 'ana_clue', required: 50, current: 0 }
+ ],
+ rewards: { xp: 5000, items: ['final_location'], unlocks: ['ana_encounter'] },
+ vfx: { onClue: 'amnesia_blur', complete: 'revelation' },
+ dialogue: {
+ onClue: ["Kai, veลก da te imam rada?", "Ne skrbi zame..."],
+ complete: ["NAล EL SEM JO! Ana, prihajam!"]
+ },
+ prerequisites: ['zamegljeni_spomini'],
+ npc: null
+ });
+
+ // TOWN QUEST: ล ivilja
+ this.registerQuest({
+ id: 'siviljina_prosnja',
+ title: 'ล iviljina Proลกnja',
+ type: 'town_economy',
+ priority: 4,
+ description: 'ล ivilja rabi 20x Platno.',
+ objectives: [
+ { id: 'cloth', text: 'Zberi platno (0/20)', type: 'item', item: 'cloth', required: 20, current: 0 }
+ ],
+ rewards: { xp: 200, items: ['enchanted_jacket'], gold: 500 },
+ dialogue: {
+ start: ["Hej! Rabim 20 platna. Lahko mi pomagaลก?"],
+ progress: ["ล e {remaining}!"],
+ complete: ["SUPER! Na, vzemi ta jopiฤ!"]
+ },
+ npc: 'sivilja'
+ });
+
+ // TOWN QUEST: Pek
+ this.registerQuest({
+ id: 'pekov_recept',
+ title: 'Pekov Recept',
+ type: 'town_economy',
+ priority: 4,
+ description: 'Pek rabi 10x Pลกenico za kruh.',
+ objectives: [
+ { id: 'wheat', text: 'Zberi pลกenico (0/10)', type: 'item', item: 'wheat', required: 10, current: 0 },
+ { id: 'restore', text: 'Obnovi pekarno', type: 'building', building: 'bakery', complete: false }
+ ],
+ rewards: { xp: 300, items: ['daily_bread'], unlocks: ['bakery_shop'] },
+ dialogue: {
+ start: ["ลฝivjo! Rabim 10 pลกenice, pa ลกe pekarna je zruลกena!"],
+ complete: ["ZAKON! Zdej lahko spet peฤem! Kruh na dan ZASTONJ!"]
+ },
+ npc: 'pek'
+ });
+
+ // TOWN QUEST: Tehnik
+ this.registerQuest({
+ id: 'tehnik_generator',
+ title: 'Tehnikova Naprava',
+ type: 'town_economy',
+ priority: 4,
+ description: 'Tehnik rabi 5x Elektriฤne Komponente.',
+ objectives: [
+ { id: 'components', text: 'Zberi komponente (0/5)', type: 'item', item: 'electric_component', required: 5, current: 0 }
+ ],
+ rewards: { xp: 250, items: ['auto_tiller'], unlocks: ['tech_services'] },
+ dialogue: {
+ start: ["Yo! Rabim 5 elektriฤnih komponent za generator."],
+ complete: ["NICE! Generator dela! Auto-Tiller upgrade za te!"]
+ },
+ npc: 'tehnik'
+ });
+
+ // DEFENSE QUEST: Walls
+ this.registerQuest({
+ id: 'obzidje',
+ title: 'Obzidje Mrtve Doline',
+ type: 'defense',
+ priority: 5,
+ description: 'Zgradi 20 segmentov zidov.',
+ objectives: [
+ { id: 'walls', text: 'Zgradi zunanje zidove (0/20)', type: 'building', building: 'wall', required: 20, current: 0 }
+ ],
+ rewards: { xp: 400, items: ['wall_blueprint_2'], unlocks: ['raid_event'] },
+ vfx: { complete: 'raid_warning' },
+ dialogue: {
+ start: ["Zombiji prihajajo! Hitr zgradi zidove!"],
+ complete: ["DONE! Zidovi so zgoraj! Priprav se na raiderje!"]
+ },
+ npc: 'mayor',
+ triggers: ['day_7']
+ });
+
+ // COLLECTION QUEST: Museum
+ this.registerQuest({
+ id: 'muzej_hrosci',
+ title: 'Muzejski Mejnik',
+ type: 'collection',
+ priority: 3,
+ description: 'Kustos rabi 24 razliฤnih hroลกฤev.',
+ objectives: [
+ { id: 'bugs', text: 'Doniraj hroลกฤe (0/24)', type: 'collection', collection: 'museum_bugs', required: 24, current: 0 }
+ ],
+ rewards: { xp: 600, items: ['museum_key'], unlocks: ['museum_stage2'] },
+ dialogue: {
+ start: ["Potrebujem 24 razliฤnih hroลกฤev za muzejsko zbirko."],
+ progress: ["Odliฤno! ล e {remaining}!"],
+ complete: ["FANTASTIฤNO! Razลกirim muzej!"]
+ },
+ npc: 'kustos'
+ });
+
+ // SIDE QUEST: Arborist
+ this.registerQuest({
+ id: 'arborist_sadike',
+ title: 'Arboristova Pomoฤ',
+ type: 'side',
+ priority: 2,
+ description: 'Arborist rabi 50x sadike dreves.',
+ objectives: [
+ { id: 'saplings', text: 'Zberi sadike (0/50)', type: 'item', item: 'tree_sapling', required: 50, current: 0 }
+ ],
+ rewards: { xp: 350, items: ['tree_planter'], unlocks: ['forest_restore'] },
+ dialogue: {
+ start: ["Rad bi obnovil gozd. Mi lahko pomagaลก?"],
+ complete: ["SUPER! Drevo-sadersko orodje je zate!"]
+ },
+ npc: 'arborist'
+ });
+
+ // COMPANION QUEST: Zombie Scout
+ this.registerQuest({
+ id: 'zombie_scout_join',
+ title: 'Zombi Skavt - Rekrutacija',
+ type: 'companion',
+ priority: 5,
+ description: 'Pridobi zaupanje inteligentnega zombija.',
+ objectives: [
+ { id: 'feed', text: 'Nahrani zombija (0/3 mesa)', type: 'item_give', item: 'meat', required: 3, current: 0 },
+ { id: 'trust', text: 'Pridobi zaupanje', type: 'interaction', complete: false }
+ ],
+ rewards: { xp: 1000, unlocks: ['zombie_scout_companion'], companions: ['zombie_scout'] },
+ vfx: { complete: 'companion_join' },
+ dialogue: {
+ start: ["*zombie zvoki* ...Meso?"],
+ progress: ["*munch* ...Ti dobr ฤlovek."],
+ complete: ["*zavija* Jaz s teb! Jaz pomagat! [JOINED]"]
+ },
+ npc: 'zombie_scout',
+ location: 'dead_forest'
+ });
+
+ // Legacy basic quest chain for backwards compatibility
+ this.registerQuest({
+ id: 'q1_start',
+ title: 'Survival Basics',
+ type: 'tutorial',
+ priority: 5,
+ description: 'Collect Wood and Stone.',
+ objectives: [
+ { id: 'wood', text: 'Collect Wood (0/5)', type: 'item', item: 'wood', required: 5, current: 0 },
+ { id: 'stone', text: 'Collect Stone (0/3)', type: 'item', item: 'stone', required: 3, current: 0 }
+ ],
+ rewards: { gold: 10, xp: 50 },
+ nextQuest: 'q2_farm',
+ npc: 'villager'
+ });
+
+ this.registerQuest({
+ id: 'q2_farm',
+ title: 'The Farmer',
+ type: 'tutorial',
+ priority: 5,
+ description: 'Plant some seeds.',
+ objectives: [
+ { id: 'plant', text: 'Plant seeds (0/3)', type: 'action', action: 'plant', required: 3, current: 0 }
+ ],
+ rewards: { gold: 20, items: ['wood:10'] },
+ prerequisites: ['q1_start'],
+ nextQuest: 'q3_defense',
+ npc: 'villager'
+ });
+
+ this.registerQuest({
+ id: 'q3_defense',
+ title: 'Fortification',
+ type: 'tutorial',
+ priority: 4,
+ description: 'Build a Fence.',
+ objectives: [
+ { id: 'fence', text: 'Build Fence (0/2)', type: 'action', action: 'build_fence', required: 2, current: 0 }
+ ],
+ rewards: { gold: 50, items: ['sword:1'] },
+ prerequisites: ['q2_farm'],
+ nextQuest: 'q4_slayer',
+ npc: 'merchant'
+ });
+
+ this.registerQuest({
+ id: 'q4_slayer',
+ title: 'Zombie Slayer',
+ type: 'tutorial',
+ priority: 4,
+ description: 'Kill 3 Zombies.',
+ objectives: [
+ { id: 'kill', text: 'Kill Zombies (0/3)', type: 'kill', target: 'zombie', required: 3, current: 0 }
+ ],
+ rewards: { gold: 100, items: ['gold:50'] },
+ prerequisites: ['q3_defense'],
+ npc: 'villager'
+ });
+ }
+
+ registerQuest(questData) {
+ // Set defaults
+ questData.isActive = false;
+ questData.isComplete = false;
+ questData.startTime = null;
+
+ this.quests.set(questData.id, questData);
+ }
+
+ startQuest(questId) {
+ const quest = this.quests.get(questId);
+ if (!quest) {
+ console.error(`Quest ${questId} not found!`);
+ return false;
+ }
+
+ // Check prerequisites
+ if (quest.prerequisites) {
+ for (const prereq of quest.prerequisites) {
+ if (!this.isQuestComplete(prereq)) {
+ console.log(`Cannot start ${questId}: Missing ${prereq}`);
+ return false;
+ }
+ }
+ }
+
+ // Prevent duplicate active
+ if (this.activeQuests.includes(questId)) {
+ return false;
+ }
+
+ quest.isActive = true;
+ quest.startTime = Date.now();
+ this.activeQuests.push(questId);
+
+ this.questLog.push({
+ questId,
+ startTime: quest.startTime,
+ status: 'active'
+ });
+
+ // Show start dialogue
+ if (quest.dialogue && quest.dialogue.start) {
+ this.showDialogue(quest.dialogue.start, quest.npc);
+
+ // ๐ VOICEOVER: Play quest start dialogue
+ if (this.scene.voiceoverSystem) {
+ const voiceKey = `quest_${questId}_start`;
+ this.scene.voiceoverSystem.playVoiceover(voiceKey, quest.dialogue.start[0]);
+ }
+ }
+
+ // Trigger VFX
+ if (quest.vfx && quest.vfx.start) {
+ this.scene.events.emit('vfx:trigger', quest.vfx.start);
+ }
+
+ // Show notification
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.x,
+ y: this.scene.player.y - 50,
+ text: 'Quest Accepted!',
+ color: '#FFFF00'
+ });
+
+ this.scene.events.emit('quest:started', questId);
+ this.updateUI();
+ return true;
+ }
+
+ updateObjective(questId, objectiveId, progress) {
+ const quest = this.quests.get(questId);
+ if (!quest || !quest.isActive) return;
+
+ const objective = quest.objectives.find(obj => obj.id === objectiveId);
+ if (!objective) return;
+
+ // Update progress
+ if (objective.type === 'item' || objective.type === 'collection') {
+ objective.current = Math.min(progress, objective.required);
+ objective.complete = objective.current >= objective.required;
+ } else if (objective.type === 'action' || objective.type === 'kill') {
+ objective.current = Math.min(progress, objective.required);
+ objective.complete = objective.current >= objective.required;
+ } else if (objective.type === 'flag' || objective.type === 'interaction' || objective.type === 'building') {
+ objective.complete = progress === true;
+ }
+
+ // Check completion
+ const allComplete = quest.objectives.every(obj => obj.complete);
+ if (allComplete) {
+ this.completeQuest(questId);
+ } else {
+ // Progress dialogue
+ if (quest.dialogue && quest.dialogue.progress) {
+ const remaining = objective.required - objective.current;
+ const msg = quest.dialogue.progress[0].replace('{remaining}', remaining);
+ this.showDialogue([msg], quest.npc);
+
+ // ๐ VOICEOVER: Play progress dialogue
+ if (this.scene.voiceoverSystem) {
+ const voiceKey = `quest_${questId}_progress`;
+ this.scene.voiceoverSystem.playVoiceover(voiceKey, msg);
+ }
+ }
+ }
+
+ this.scene.events.emit('quest:updated', questId, objectiveId, progress);
+ this.updateUI();
+ }
+
+ trackAction(actionType, amount = 1) {
+ // Track actions for active quests' objectives
+ for (const questId of this.activeQuests) {
+ const quest = this.quests.get(questId);
+ if (!quest) continue;
+
+ for (const obj of quest.objectives) {
+ if (obj.complete) continue;
+
+ if (obj.type === 'action' && obj.action === actionType) {
+ obj.current = Math.min((obj.current || 0) + amount, obj.required);
+ if (obj.current >= obj.required) {
+ obj.complete = true;
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.x,
+ y: this.scene.player.y,
+ text: 'Objective Complete!',
+ color: '#00FF00'
+ });
+ }
+ } else if (obj.type === 'kill' && obj.target === actionType) {
+ obj.current = Math.min((obj.current || 0) + amount, obj.required);
+ if (obj.current >= obj.required) {
+ obj.complete = true;
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.x,
+ y: this.scene.player.y,
+ text: 'Objective Complete!',
+ color: '#00FF00'
+ });
+ }
+ }
+ }
+
+ // Auto-complete check
+ const allDone = quest.objectives.every(o => o.complete);
+ if (allDone) {
+ this.completeQuest(questId);
+ }
+ }
+
+ this.updateUI();
+ }
+
+ completeQuest(questId) {
+ const quest = this.quests.get(questId);
+ if (!quest) return;
+
+ quest.isActive = false;
+ quest.isComplete = true;
+ this.activeQuests = this.activeQuests.filter(id => id !== questId);
+ this.completedQuests.push(questId);
+
+ // Grant rewards
+ if (quest.rewards) {
+ if (quest.rewards.xp && this.scene.player && this.scene.player.gainExperience) {
+ this.scene.player.gainExperience(quest.rewards.xp);
+ }
+ if (quest.rewards.gold && this.scene.inventorySystem) {
+ this.scene.inventorySystem.addGold(quest.rewards.gold);
+ }
+ if (quest.rewards.items) {
+ quest.rewards.items.forEach(item => {
+ const [itemName, amt] = item.includes(':') ? item.split(':') : [item, 1];
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(itemName, parseInt(amt));
+ } else if (this.scene.player && this.scene.player.inventory) {
+ this.scene.player.inventory.addItem(itemName, parseInt(amt));
+ }
+ });
+ }
+ if (quest.rewards.unlocks) {
+ quest.rewards.unlocks.forEach(unlock => {
+ if (this.scene.gameState && this.scene.gameState.unlock) {
+ this.scene.gameState.unlock(unlock);
+ }
+ });
+ }
+ if (quest.rewards.companions) {
+ quest.rewards.companions.forEach(companion => {
+ if (this.scene.player && this.scene.player.addCompanion) {
+ this.scene.player.addCompanion(companion);
+ }
+ });
+ }
+ }
+
+ // Completion dialogue
+ if (quest.dialogue && quest.dialogue.complete) {
+ this.showDialogue(quest.dialogue.complete, quest.npc);
+
+ // ๐ VOICEOVER: Play completion dialogue
+ if (this.scene.voiceoverSystem) {
+ const voiceKey = `quest_${questId}_complete`;
+ this.scene.voiceoverSystem.playVoiceover(voiceKey, quest.dialogue.complete[0]);
+ }
+ }
+
+ // Completion VFX
+ if (quest.vfx && (quest.vfx.complete || quest.vfx.onComplete)) {
+ this.scene.events.emit('vfx:trigger', quest.vfx.complete || quest.vfx.onComplete);
+ }
+
+ // Notification
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.x,
+ y: this.scene.player.y - 50,
+ text: 'Quest Complete!',
+ color: '#00FF00'
+ });
+
+ console.log(`๐ Quest Complete: ${quest.title}`);
+ this.scene.events.emit('quest:completed', questId);
+
+ // Auto-start next quest if defined
+ if (quest.nextQuest) {
+ setTimeout(() => {
+ console.log(`Next quest available: ${quest.nextQuest}`);
+ }, 1000);
+ }
+
+ this.updateUI();
+ }
+
+ showDialogue(lines, npcId) {
+ // Emit dialogue event
+ this.scene.events.emit('dialogue:show', {
+ npc: npcId,
+ lines: lines
+ });
+ }
+
+ getActiveQuests() {
+ return this.activeQuests.map(id => this.quests.get(id));
+ }
+
+ getAvailableQuest(npcType) {
+ // Find first uncompleted, non-active quest for this NPC
+ for (const [id, quest] of this.quests) {
+ if (quest.isComplete || quest.isActive) continue;
+ if (quest.npc !== npcType) continue;
+
+ // Check prerequisites
+ if (quest.prerequisites) {
+ const prereqsMet = quest.prerequisites.every(p => this.isQuestComplete(p));
+ if (!prereqsMet) continue;
+ }
+
+ return quest;
+ }
+ return null;
+ }
+
+ getQuestProgress(questId) {
+ const quest = this.quests.get(questId);
+ if (!quest) return null;
+
+ return {
+ id: questId,
+ title: quest.title,
+ objectives: quest.objectives.map(obj => ({
+ text: obj.text,
+ current: obj.current || 0,
+ required: obj.required || 1,
+ complete: obj.complete || false
+ })),
+ isActive: quest.isActive,
+ isComplete: quest.isComplete
+ };
+ }
+
+ isQuestActive(questId) {
+ const quest = this.quests.get(questId);
+ return quest && quest.isActive;
+ }
+
+ isQuestComplete(questId) {
+ const quest = this.quests.get(questId);
+ return quest && quest.isComplete;
+ }
+
+ update(delta) {
+ // Auto-update objectives based on inventory
+ if (!this.scene.inventorySystem) return;
+
+ for (const questId of this.activeQuests) {
+ const quest = this.quests.get(questId);
+ if (!quest) continue;
+
+ let changed = false;
+ for (const obj of quest.objectives) {
+ if (obj.complete) continue;
+
+ if (obj.type === 'item') {
+ const count = this.scene.inventorySystem.getItemCount(obj.item);
+ if (count !== obj.current) {
+ obj.current = count;
+ changed = true;
+ if (obj.current >= obj.required) {
+ obj.complete = true;
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.x,
+ y: this.scene.player.y,
+ text: 'Objective Complete!',
+ color: '#00FF00'
+ });
+ }
+ }
+ }
+ }
+
+ if (changed) {
+ const allDone = quest.objectives.every(o => o.complete);
+ if (allDone) {
+ this.completeQuest(questId);
+ } else {
+ this.updateUI();
+ }
+ }
+ }
+ }
+
+ updateUI() {
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.updateQuestTracker) {
+ const active = this.activeQuests.length > 0 ? this.quests.get(this.activeQuests[0]) : null;
+ ui.updateQuestTracker(active);
+ }
+ }
+
+ destroy() {
+ this.quests.clear();
+ this.activeQuests = [];
+ this.completedQuests = [];
+ this.questLog = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/RecipeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/RecipeSystem.js
new file mode 100644
index 000000000..2cdb9dc73
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/RecipeSystem.js
@@ -0,0 +1,536 @@
+/**
+ * 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;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ResourceLogisticsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ResourceLogisticsSystem.js
new file mode 100644
index 000000000..270762e89
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ResourceLogisticsSystem.js
@@ -0,0 +1,387 @@
+/**
+ * ๐ฆ RESOURCE LOGISTICS SYSTEM - Week 1 Priority
+ * Auto-pickup resources + Storage management + Resource tracking
+ *
+ * Features:
+ * - Auto-pickup dropped resources
+ * - Storage capacity management
+ * - Resource depot system
+ * - Visual feedback (UI icons + VFX)
+ * - Integration with Zombie Hauler workers
+ *
+ * Assets Used:
+ * - /assets/ui/resource_icon_wood.png
+ * - /assets/ui/resource_icon_food.png
+ * - /assets/ui/resource_icon_stone.png
+ * - /assets/buildings/resource_depot.png
+ * - /assets/buildings/resource_pile_wood.png
+ * - /assets/buildings/resource_pile_food.png
+ * - /assets/vfx/resource_collect.png
+ */
+
+export default class ResourceLogisticsSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Resource tracking
+ this.resources = {
+ wood: 0,
+ food: 0,
+ stone: 0,
+ coal: 0
+ };
+
+ // Storage system
+ this.storageCapacity = {
+ wood: 100,
+ food: 100,
+ stone: 100,
+ coal: 50
+ };
+
+ // Auto-pickup settings
+ this.autoPickupEnabled = true;
+ this.pickupRadius = 80; // pixels
+ this.pickupDelay = 500; // ms between pickups
+ this.lastPickupTime = 0;
+
+ // Depot locations (can have multiple)
+ this.depots = [];
+
+ // Dropped resources on map
+ this.droppedResources = [];
+
+ // UI elements
+ this.resourceUI = null;
+
+ this.init();
+ }
+
+ init() {
+ console.log('[ResourceLogistics] Initializing resource logistics system...');
+
+ // Create resource UI
+ this.createResourceUI();
+
+ // Load resource sprites
+ this.loadResourceSprites();
+
+ // Setup auto-pickup update loop
+ this.scene.time.addEvent({
+ delay: this.pickupDelay,
+ callback: () => this.updateAutoPickup(),
+ loop: true
+ });
+ }
+
+ loadResourceSprites() {
+ // Preload resource icons
+ if (!this.scene.textures.exists('resource_icon_wood')) {
+ this.scene.load.image('resource_icon_wood', 'assets/ui/resource_icon_wood.png');
+ }
+ if (!this.scene.textures.exists('resource_icon_food')) {
+ this.scene.load.image('resource_icon_food', 'assets/ui/resource_icon_food.png');
+ }
+ if (!this.scene.textures.exists('resource_icon_stone')) {
+ this.scene.load.image('resource_icon_stone', 'assets/ui/resource_icon_stone.png');
+ }
+ if (!this.scene.textures.exists('resource_collect_vfx')) {
+ this.scene.load.image('resource_collect_vfx', 'assets/vfx/resource_collect.png');
+ }
+
+ this.scene.load.start();
+ }
+
+ createResourceUI() {
+ const padding = 20;
+ const iconSize = 32;
+ const spacing = 100;
+
+ this.resourceUI = this.scene.add.container(padding, padding);
+ this.resourceUI.setScrollFactor(0);
+ this.resourceUI.setDepth(1000);
+
+ // Wood display
+ this.woodIcon = this.scene.add.sprite(0, 0, 'resource_icon_wood').setOrigin(0, 0);
+ this.woodText = this.scene.add.text(iconSize + 10, iconSize / 2, '0/100', {
+ fontSize: '18px',
+ fill: '#fff',
+ stroke: '#000',
+ strokeThickness: 3
+ }).setOrigin(0, 0.5);
+
+ // Food display
+ this.foodIcon = this.scene.add.sprite(spacing, 0, 'resource_icon_food').setOrigin(0, 0);
+ this.foodText = this.scene.add.text(spacing + iconSize + 10, iconSize / 2, '0/100', {
+ fontSize: '18px',
+ fill: '#fff',
+ stroke: '#000',
+ strokeThickness: 3
+ }).setOrigin(0, 0.5);
+
+ // Stone display
+ this.stoneIcon = this.scene.add.sprite(spacing * 2, 0, 'resource_icon_stone').setOrigin(0, 0);
+ this.stoneText = this.scene.add.text(spacing * 2 + iconSize + 10, iconSize / 2, '0/100', {
+ fontSize: '18px',
+ fill: '#fff',
+ stroke: '#000',
+ strokeThickness: 3
+ }).setOrigin(0, 0.5);
+
+ this.resourceUI.add([
+ this.woodIcon, this.woodText,
+ this.foodIcon, this.foodText,
+ this.stoneIcon, this.stoneText
+ ]);
+ }
+
+ updateResourceUI() {
+ if (this.woodText) {
+ this.woodText.setText(`${this.resources.wood}/${this.storageCapacity.wood}`);
+ }
+ if (this.foodText) {
+ this.foodText.setText(`${this.resources.food}/${this.storageCapacity.food}`);
+ }
+ if (this.stoneText) {
+ this.stoneText.setText(`${this.resources.stone}/${this.storageCapacity.stone}`);
+ }
+ }
+
+ /**
+ * Auto-pickup resources near player
+ */
+ updateAutoPickup() {
+ if (!this.autoPickupEnabled) return;
+ if (!this.scene.player) return;
+
+ const currentTime = Date.now();
+ if (currentTime - this.lastPickupTime < this.pickupDelay) return;
+
+ const playerX = this.scene.player.x;
+ const playerY = this.scene.player.y;
+
+ // Check each dropped resource
+ for (let i = this.droppedResources.length - 1; i >= 0; i--) {
+ const resource = this.droppedResources[i];
+
+ // Calculate distance
+ const distance = Phaser.Math.Distance.Between(
+ playerX, playerY,
+ resource.x, resource.y
+ );
+
+ // Auto-pickup if within radius
+ if (distance <= this.pickupRadius) {
+ this.pickupResource(resource, i);
+ this.lastPickupTime = currentTime;
+ break; // One at a time for smooth feel
+ }
+ }
+ }
+
+ /**
+ * Pickup a resource
+ */
+ pickupResource(resource, index) {
+ const type = resource.type;
+ const amount = resource.amount;
+
+ // Check storage capacity
+ if (this.resources[type] >= this.storageCapacity[type]) {
+ // Storage full!
+ this.showStorageFullMessage(type);
+ return;
+ }
+
+ // Add to resources
+ const added = Math.min(amount, this.storageCapacity[type] - this.resources[type]);
+ this.resources[type] += added;
+
+ // Play collection VFX
+ this.playCollectionVFX(resource.x, resource.y);
+
+ // Play collection sound
+ this.playCollectionSound(type);
+
+ // Remove from world
+ if (resource.sprite) {
+ resource.sprite.destroy();
+ }
+ this.droppedResources.splice(index, 1);
+
+ // Update UI
+ this.updateResourceUI();
+
+ console.log(`[ResourceLogistics] Picked up ${added} ${type}! Total: ${this.resources[type]}/${this.storageCapacity[type]}`);
+ }
+
+ /**
+ * Drop resource on map (from tree, mining, etc.)
+ */
+ dropResource(x, y, type, amount) {
+ // Create visual dropped resource
+ const sprite = this.scene.add.sprite(x, y, `resource_pile_${type}`);
+ sprite.setScale(0.5);
+
+ // Add to tracking
+ this.droppedResources.push({
+ x: x,
+ y: y,
+ type: type,
+ amount: amount,
+ sprite: sprite
+ });
+
+ // Bounce animation
+ this.scene.tweens.add({
+ targets: sprite,
+ y: y - 10,
+ duration: 200,
+ yoyo: true,
+ ease: 'Quad.easeOut'
+ });
+ }
+
+ /**
+ * Play collection VFX (green/gold sparkles)
+ */
+ playCollectionVFX(x, y) {
+ if (!this.scene.textures.exists('resource_collect_vfx')) return;
+
+ // Create particle emitter for collection effect
+ const emitter = this.scene.add.particles(x, y, 'resource_collect_vfx', {
+ speed: { min: 50, max: 100 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 500,
+ quantity: 8,
+ blendMode: 'ADD'
+ });
+
+ // Destroy after animation
+ this.scene.time.delayedCall(600, () => {
+ emitter.destroy();
+ });
+ }
+
+ /**
+ * Play collection sound
+ */
+ playCollectionSound(type) {
+ // Use existing sound system if available
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playSound('resource_pickup');
+ }
+ }
+
+ /**
+ * Show storage full message
+ */
+ showStorageFullMessage(type) {
+ const message = `${type.toUpperCase()} STORAGE FULL! Build more storage or upgrade depot.`;
+
+ // Use existing popup system if available
+ if (this.scene.centralPopupSystem) {
+ this.scene.centralPopupSystem.showMessage(message, 'warning');
+ } else {
+ console.warn(`[ResourceLogistics] ${message}`);
+ }
+ }
+
+ /**
+ * Add a resource depot
+ */
+ addDepot(x, y, capacity = {}) {
+ const depot = {
+ x: x,
+ y: y,
+ sprite: this.scene.add.sprite(x, y, 'resource_depot'),
+ capacity: {
+ wood: capacity.wood || 100,
+ food: capacity.food || 100,
+ stone: capacity.stone || 100,
+ coal: capacity.coal || 50
+ }
+ };
+
+ this.depots.push(depot);
+
+ // Increase storage capacity
+ this.storageCapacity.wood += depot.capacity.wood;
+ this.storageCapacity.food += depot.capacity.food;
+ this.storageCapacity.stone += depot.capacity.stone;
+ this.storageCapacity.coal += depot.capacity.coal;
+
+ this.updateResourceUI();
+
+ console.log('[ResourceLogistics] Depot added! New capacity:', this.storageCapacity);
+ }
+
+ /**
+ * Get current resources
+ */
+ getResources() {
+ return { ...this.resources };
+ }
+
+ /**
+ * Add resources (for debugging or harvesting)
+ */
+ addResource(type, amount) {
+ const added = Math.min(amount, this.storageCapacity[type] - this.resources[type]);
+ this.resources[type] += added;
+ this.updateResourceUI();
+ return added;
+ }
+
+ /**
+ * Remove resources (for building, crafting, etc.)
+ */
+ removeResource(type, amount) {
+ if (this.resources[type] >= amount) {
+ this.resources[type] -= amount;
+ this.updateResourceUI();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if has enough resources
+ */
+ hasResources(requirements) {
+ for (const [type, amount] of Object.entries(requirements)) {
+ if (this.resources[type] < amount) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ update(time, delta) {
+ // Already handled by time events
+ }
+
+ destroy() {
+ if (this.resourceUI) {
+ this.resourceUI.destroy();
+ }
+
+ // Clear dropped resources
+ this.droppedResources.forEach(resource => {
+ if (resource.sprite) {
+ resource.sprite.destroy();
+ }
+ });
+ this.droppedResources = [];
+
+ // Clear depots
+ this.depots.forEach(depot => {
+ if (depot.sprite) {
+ depot.sprite.destroy();
+ }
+ });
+ this.depots = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/RiverSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/RiverSystem.js
new file mode 100644
index 000000000..49f8fdfbc
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/RiverSystem.js
@@ -0,0 +1,267 @@
+/**
+ * ๐ RIVER SYSTEM
+ * Generates and manages rivers across the 500x500 world
+ * - Creates flowing rivers from mountains to lakes
+ * - Handles river width, curves, and junctions
+ * - Biome-aware water coloring
+ */
+
+class RiverSystem {
+ constructor(worldWidth, worldHeight, biomeSystem) {
+ this.worldWidth = worldWidth;
+ this.worldHeight = worldHeight;
+ this.biomeSystem = biomeSystem;
+
+ // River map (stores river type: 'river', 'tributary', 'spring')
+ this.riverMap = new Map();
+
+ // River paths (array of segments)
+ this.rivers = [];
+
+ // River settings
+ this.riverCount = 3; // Number of major rivers
+ this.minRiverLength = 50; // Minimum river length
+ this.maxRiverLength = 200; // Maximum river length
+ this.riverWidth = 2; // Base width (tiles)
+ this.tributaryChance = 0.15; // Chance to spawn tributary
+
+ console.log(`๐ Initializing River System (${worldWidth}x${worldHeight})`);
+ }
+
+ /**
+ * Generate all rivers
+ */
+ generateRivers() {
+ console.log(`๐ Generating ${this.riverCount} rivers...`);
+
+ // 1. Find river sources (springs in mountains)
+ const sources = this.findRiverSources();
+
+ // 2. Generate river paths from each source
+ for (let i = 0; i < sources.length; i++) {
+ const source = sources[i];
+ const river = this.generateRiverPath(source, i);
+
+ if (river && river.length > this.minRiverLength) {
+ this.rivers.push(river);
+ this.markRiverTiles(river);
+ }
+ }
+
+ console.log(`โ
Generated ${this.rivers.length} rivers with ${this.riverMap.size} water tiles`);
+ }
+
+ /**
+ * Find river sources (prefer mountains)
+ */
+ findRiverSources() {
+ const sources = [];
+ const attempts = this.riverCount * 3;
+
+ for (let i = 0; i < attempts && sources.length < this.riverCount; i++) {
+ const x = Math.floor(Math.random() * this.worldWidth);
+ const y = Math.floor(Math.random() * this.worldHeight);
+ const biome = this.biomeSystem.getBiomeAt(x, y);
+
+ // Prefer mountain sources, but allow forest
+ if (biome === 'mountain' || (biome === 'forest' && Math.random() < 0.3)) {
+ // Check not too close to other sources
+ const tooClose = sources.some(s =>
+ Math.abs(s.x - x) < 100 && Math.abs(s.y - y) < 100
+ );
+
+ if (!tooClose) {
+ sources.push({ x, y, biome });
+ }
+ }
+ }
+
+ // If not enough, add random sources
+ while (sources.length < this.riverCount) {
+ sources.push({
+ x: Math.floor(Math.random() * this.worldWidth),
+ y: Math.floor(Math.random() * this.worldHeight),
+ biome: 'forest'
+ });
+ }
+
+ console.log(`๐๏ธ Found ${sources.length} river sources:`, sources);
+ return sources;
+ }
+
+ /**
+ * Generate a single river path from source
+ */
+ generateRiverPath(source, riverIndex) {
+ const path = [];
+ let x = source.x;
+ let y = source.y;
+
+ // Random target direction (generally downward/outward)
+ const targetAngle = Math.random() * Math.PI * 2;
+
+ // River length
+ const length = this.minRiverLength + Math.floor(Math.random() * (this.maxRiverLength - this.minRiverLength));
+
+ // Generate path using noise
+ for (let step = 0; step < length; step++) {
+ // Add current position to path
+ path.push({ x: Math.floor(x), y: Math.floor(y) });
+
+ // Check bounds
+ if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) {
+ break;
+ }
+
+ // Check if reached lake
+ const biome = this.biomeSystem.getBiomeAt(Math.floor(x), Math.floor(y));
+ if (biome === 'swamp' && Math.random() < 0.3) {
+ // River ends in swamp lake
+ break;
+ }
+
+ // Move river forward
+ // Add some randomness to create curves
+ const noise = (Math.random() - 0.5) * 0.5;
+ const angle = targetAngle + noise;
+
+ const dx = Math.cos(angle);
+ const dy = Math.sin(angle);
+
+ x += dx;
+ y += dy;
+
+ // Occasionally create tributary
+ if (Math.random() < this.tributaryChance && path.length > 10) {
+ this.createTributary(x, y, 10);
+ }
+ }
+
+ return path;
+ }
+
+ /**
+ * Create a small tributary
+ */
+ createTributary(startX, startY, length) {
+ const path = [];
+ let x = startX;
+ let y = startY;
+
+ const angle = Math.random() * Math.PI * 2;
+
+ for (let i = 0; i < length; i++) {
+ path.push({ x: Math.floor(x), y: Math.floor(y) });
+
+ const noise = (Math.random() - 0.5) * 0.8;
+ x += Math.cos(angle + noise);
+ y += Math.sin(angle + noise);
+
+ if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) {
+ break;
+ }
+ }
+
+ this.markRiverTiles(path, 'tributary');
+ }
+
+ /**
+ * Mark river tiles on the river map
+ */
+ markRiverTiles(path, type = 'river') {
+ for (const point of path) {
+ const key = `${point.x},${point.y}`;
+
+ // Main river tile
+ this.riverMap.set(key, { type, width: this.riverWidth });
+
+ // Add width (make river wider)
+ const width = type === 'tributary' ? 1 : this.riverWidth;
+
+ for (let dy = -width; dy <= width; dy++) {
+ for (let dx = -width; dx <= width; dx++) {
+ if (dx === 0 && dy === 0) continue;
+
+ // Check if within circle
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist <= width) {
+ const nx = point.x + dx;
+ const ny = point.y + dy;
+
+ if (nx >= 0 && nx < this.worldWidth && ny >= 0 && ny < this.worldHeight) {
+ const nkey = `${nx},${ny}`;
+ if (!this.riverMap.has(nkey)) {
+ this.riverMap.set(nkey, { type: 'riverbank', width });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if tile is river
+ */
+ isRiver(x, y) {
+ const key = `${x},${y}`;
+ return this.riverMap.has(key);
+ }
+
+ /**
+ * Get river data at tile
+ */
+ getRiverData(x, y) {
+ const key = `${x},${y}`;
+ return this.riverMap.get(key) || null;
+ }
+
+ /**
+ * Get river color based on biome
+ */
+ getRiverColor(x, y) {
+ const biome = this.biomeSystem.getBiomeAt(x, y);
+
+ switch (biome) {
+ case 'forest':
+ return 0x2a5f4f; // Dark green water
+ case 'swamp':
+ return 0x3d5a3d; // Murky swamp water
+ case 'desert':
+ return 0x87CEEB; // Clear oasis water
+ case 'mountain':
+ return 0x4682B4; // Cool mountain water
+ default:
+ return 0x1E90FF; // Default blue water
+ }
+ }
+
+ /**
+ * Get statistics
+ */
+ getStats() {
+ return {
+ riverCount: this.rivers.length,
+ totalWaterTiles: this.riverMap.size,
+ avgRiverLength: this.rivers.reduce((sum, r) => sum + r.length, 0) / this.rivers.length || 0
+ };
+ }
+
+ /**
+ * Export river data for saving
+ */
+ exportData() {
+ return {
+ rivers: this.rivers,
+ riverMap: Array.from(this.riverMap.entries())
+ };
+ }
+
+ /**
+ * Import river data from save
+ */
+ importData(data) {
+ this.rivers = data.rivers || [];
+ this.riverMap = new Map(data.riverMap || []);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SaveLoadSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/SaveLoadSystem.js
new file mode 100644
index 000000000..4d9f0a128
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SaveLoadSystem.js
@@ -0,0 +1,371 @@
+/**
+ * SAVE/LOAD SYSTEM & AGING ENGINE
+ * Persistent storage for player progress, Kai aging, and companion states
+ */
+
+class SaveLoadSystem {
+ constructor() {
+ this.saveKey = 'mrtva_dolina_save';
+ this.currentSave = null;
+ this.autoSaveInterval = 300000; // 5 minutes
+ this.autoSaveTimer = null;
+ }
+
+ /**
+ * CREATE NEW SAVE FILE
+ */
+ createNewSave() {
+ this.currentSave = {
+ version: '1.0.0',
+ created: new Date().toISOString(),
+ lastSaved: new Date().toISOString(),
+
+ // Player data
+ player: {
+ position: { x: 0, y: 0 },
+ age_level: 1, // 1-9 (corresponds to age stages)
+ current_age: 14, // Actual age in years
+ age_sprite: 'kai_age14',
+ inventory: [],
+ equipped_tools: {
+ weapon: null,
+ tool: null
+ },
+ health: 100,
+ stamina: 100,
+ stats: {
+ strength: 1,
+ speed: 1,
+ farming: 1
+ }
+ },
+
+ // Progress tracking
+ progress: {
+ memories_found: 0,
+ total_memories: 100,
+ quests_completed: [],
+ quests_active: [],
+ npcs_met: [],
+ biomes_unlocked: ['grassland'],
+ locations_discovered: [],
+ enemies_defeated: 0,
+ crops_harvested: 0,
+ buildings_built: 0
+ },
+
+ // Companions
+ companions: {
+ gronk: {
+ unlocked: false,
+ level: 1,
+ xp: 0,
+ stats: {}
+ },
+ susi: {
+ unlocked: false,
+ position: { x: 0, y: 0 },
+ loyalty: 100
+ }
+ },
+
+ // Farm state
+ farm: {
+ crops: [],
+ buildings: [],
+ animals: [],
+ resources: {
+ wood: 0,
+ stone: 0,
+ cannabis_capital: 0
+ }
+ },
+
+ // Economic state
+ economy: {
+ money: 0,
+ cannabis_seeds: 5, // Starting capital!
+ cannabis_harvested: 0,
+ total_earnings: 0
+ },
+
+ // Game settings
+ settings: {
+ difficulty: 'normal',
+ language: 'en',
+ music_volume: 0.7,
+ sfx_volume: 0.8
+ },
+
+ // Playtime
+ playtime: 0 // seconds
+ };
+
+ console.log('๐พ New save file created');
+ return this.currentSave;
+ }
+
+ /**
+ * SAVE GAME
+ */
+ save() {
+ if (!this.currentSave) {
+ this.currentSave = this.createNewSave();
+ }
+
+ this.currentSave.lastSaved = new Date().toISOString();
+
+ try {
+ localStorage.setItem(this.saveKey, JSON.stringify(this.currentSave));
+ console.log('๐พ Game saved successfully');
+
+ // Show save notification
+ this.showSaveNotification();
+
+ return true;
+ } catch (e) {
+ console.error('โ Save failed:', e);
+ return false;
+ }
+ }
+
+ /**
+ * LOAD GAME
+ */
+ load() {
+ try {
+ const saved = localStorage.getItem(this.saveKey);
+ if (!saved) {
+ console.log('๐ No save file found, creating new...');
+ return this.createNewSave();
+ }
+
+ this.currentSave = JSON.parse(saved);
+ console.log('๐ Game loaded successfully');
+ console.log(' Age Level:', this.currentSave.player.age_level);
+ console.log(' Memories:', this.currentSave.progress.memories_found + '/' + this.currentSave.progress.total_memories);
+
+ return this.currentSave;
+ } catch (e) {
+ console.error('โ Load failed:', e);
+ return this.createNewSave();
+ }
+ }
+
+ /**
+ * UPDATE PLAYER DATA
+ */
+ updatePlayer(data) {
+ if (!this.currentSave) this.load();
+
+ this.currentSave.player = {
+ ...this.currentSave.player,
+ ...data
+ };
+
+ this.save();
+ }
+
+ /**
+ * UPDATE PROGRESS
+ */
+ updateProgress(data) {
+ if (!this.currentSave) this.load();
+
+ this.currentSave.progress = {
+ ...this.currentSave.progress,
+ ...data
+ };
+
+ // Check if aging should trigger
+ this.checkAgingProgress();
+
+ this.save();
+ }
+
+ /**
+ * START AUTO-SAVE
+ */
+ startAutoSave() {
+ if (this.autoSaveTimer) {
+ clearInterval(this.autoSaveTimer);
+ }
+
+ this.autoSaveTimer = setInterval(() => {
+ this.save();
+ console.log('๐พ Auto-save triggered');
+ }, this.autoSaveInterval);
+ }
+
+ /**
+ * STOP AUTO-SAVE
+ */
+ stopAutoSave() {
+ if (this.autoSaveTimer) {
+ clearInterval(this.autoSaveTimer);
+ this.autoSaveTimer = null;
+ }
+ }
+
+ /**
+ * AGING ENGINE - CHECK IF KAI SHOULD AGE UP
+ */
+ checkAgingProgress() {
+ if (!this.currentSave) return;
+
+ const memoriesFound = this.currentSave.progress.memories_found;
+ const totalMemories = this.currentSave.progress.total_memories;
+ const progress = (memoriesFound / totalMemories) * 100;
+
+ let newAgeLevel = 1;
+ let newAge = 14;
+ let newSprite = 'kai_age14';
+
+ // Age progression based on memory recovery
+ if (progress >= 90) {
+ newAgeLevel = 9;
+ newAge = 60;
+ newSprite = 'kai_age60';
+ } else if (progress >= 75) {
+ newAgeLevel = 7;
+ newAge = 50;
+ newSprite = 'kai_age50';
+ } else if (progress >= 60) {
+ newAgeLevel = 6;
+ newAge = 40;
+ newSprite = 'kai_age40';
+ } else if (progress >= 50) {
+ newAgeLevel = 5;
+ newAge = 30;
+ newSprite = 'kai_age30';
+ } else if (progress >= 35) {
+ newAgeLevel = 4;
+ newAge = 25;
+ newSprite = 'kai_age25';
+ } else if (progress >= 25) {
+ newAgeLevel = 3;
+ newAge = 20;
+ newSprite = 'kai_age20';
+ } else if (progress >= 10) {
+ newAgeLevel = 2;
+ newAge = 16;
+ newSprite = 'kai_age16';
+ }
+
+ // Check if age changed
+ if (newAgeLevel > this.currentSave.player.age_level) {
+ this.triggerAging(newAgeLevel, newAge, newSprite);
+ }
+ }
+
+ /**
+ * TRIGGER AGING CUTSCENE
+ */
+ triggerAging(newLevel, newAge, newSprite) {
+ const oldLevel = this.currentSave.player.age_level;
+ const oldAge = this.currentSave.player.current_age;
+
+ // Update save data
+ this.currentSave.player.age_level = newLevel;
+ this.currentSave.player.current_age = newAge;
+ this.currentSave.player.age_sprite = newSprite;
+
+ console.log(`โฐ KAI AGES UP!`);
+ console.log(` ${oldAge} โ ${newAge} years old`);
+ console.log(` Sprite: ${newSprite}`);
+
+ // Emit aging event for cutscene
+ const event = new CustomEvent('kai-aging', {
+ detail: {
+ oldLevel: oldLevel,
+ newLevel: newLevel,
+ oldAge: oldAge,
+ newAge: newAge,
+ newSprite: newSprite,
+ memoriesFound: this.currentSave.progress.memories_found
+ }
+ });
+ window.dispatchEvent(event);
+
+ this.save();
+ }
+
+ /**
+ * SHOW SAVE NOTIFICATION
+ */
+ showSaveNotification() {
+ const event = new CustomEvent('game-saved', {
+ detail: {
+ time: new Date().toLocaleTimeString(),
+ slot: 1
+ }
+ });
+ window.dispatchEvent(event);
+ }
+
+ /**
+ * GET CURRENT SAVE DATA
+ */
+ getCurrentSave() {
+ if (!this.currentSave) {
+ this.load();
+ }
+ return this.currentSave;
+ }
+
+ /**
+ * DELETE SAVE
+ */
+ deleteSave() {
+ if (confirm('Are you sure you want to delete your save file?')) {
+ localStorage.removeItem(this.saveKey);
+ this.currentSave = null;
+ console.log('๐๏ธ Save file deleted');
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * EXPORT SAVE (for backup)
+ */
+ exportSave() {
+ if (!this.currentSave) return null;
+
+ const saveData = JSON.stringify(this.currentSave, null, 2);
+ const blob = new Blob([saveData], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `mrtva_dolina_save_${Date.now()}.json`;
+ a.click();
+
+ console.log('๐ค Save exported');
+ }
+
+ /**
+ * IMPORT SAVE (from backup)
+ */
+ importSave(fileInput) {
+ const file = fileInput.files[0];
+ if (!file) return;
+
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ try {
+ const imported = JSON.parse(e.target.result);
+ localStorage.setItem(this.saveKey, JSON.stringify(imported));
+ this.currentSave = imported;
+ console.log('๐ฅ Save imported successfully');
+ } catch (err) {
+ console.error('โ Import failed:', err);
+ }
+ };
+ reader.readAsText(file);
+ }
+}
+
+// Singleton instance
+const saveLoadSystem = new SaveLoadSystem();
+export default saveLoadSystem;
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SaveManager.js b/EMERGENCY_SYSTEMS_RECOVERY/SaveManager.js
new file mode 100644
index 000000000..a06dca031
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SaveManager.js
@@ -0,0 +1,273 @@
+/**
+ * SAVE MANAGER
+ * Handles multiple save slots and auto-save functionality
+ * Extends existing SaveSystem with slot management
+ */
+
+class SaveManager {
+ constructor(scene) {
+ this.scene = scene;
+ this.maxSlots = 3; // 3 save slots
+ this.currentSlot = 1; // Active slot (1-3)
+
+ // Auto-save settings
+ this.autoSaveEnabled = true;
+ this.autoSaveInterval = 5 * 60 * 1000; // 5 minutes in ms
+ this.autoSaveTimer = 0;
+
+ // Initialize SaveSystem for each slot
+ this.saveSystems = {};
+ for (let i = 1; i <= this.maxSlots; i++) {
+ this.saveSystems[i] = new SaveSystem(scene);
+ this.saveSystems[i].storageKey = `novafarma_save_slot${i}`;
+ }
+
+ console.log('๐พ SaveManager initialized with 3 slots');
+ }
+
+ /**
+ * Save to specific slot
+ * @param {number} slotNumber - Slot 1-3
+ */
+ saveToSlot(slotNumber) {
+ if (slotNumber < 1 || slotNumber > this.maxSlots) {
+ console.error(`Invalid slot number: ${slotNumber}`);
+ return false;
+ }
+
+ console.log(`๐พ Saving to slot ${slotNumber}...`);
+ this.saveSystems[slotNumber].saveGame();
+ this.currentSlot = slotNumber;
+
+ // Save metadata (last save time, playtime, etc.)
+ this.saveSlotMetadata(slotNumber);
+
+ return true;
+ }
+
+ /**
+ * Load from specific slot
+ * @param {number} slotNumber - Slot 1-3
+ */
+ loadFromSlot(slotNumber) {
+ if (slotNumber < 1 || slotNumber > this.maxSlots) {
+ console.error(`Invalid slot number: ${slotNumber}`);
+ return false;
+ }
+
+ console.log(`๐ Loading from slot ${slotNumber}...`);
+ const success = this.saveSystems[slotNumber].loadGame();
+
+ if (success) {
+ this.currentSlot = slotNumber;
+ }
+
+ return success;
+ }
+
+ /**
+ * Delete specific slot
+ * @param {number} slotNumber - Slot 1-3
+ */
+ deleteSlot(slotNumber) {
+ if (slotNumber < 1 || slotNumber > this.maxSlots) {
+ console.error(`Invalid slot number: ${slotNumber}`);
+ return false;
+ }
+
+ const key = `novafarma_save_slot${slotNumber}`;
+ const metaKey = `novafarma_slot${slotNumber}_meta`;
+
+ localStorage.removeItem(key);
+ localStorage.removeItem(metaKey);
+
+ console.log(`๐๏ธ Deleted slot ${slotNumber}`);
+ return true;
+ }
+
+ /**
+ * Save metadata for slot (thumbnail, playtime, day, etc.)
+ */
+ saveSlotMetadata(slotNumber) {
+ const metadata = {
+ slotNumber,
+ lastSaved: Date.now(),
+ playerName: 'Player',
+ dayCount: this.scene.timeSystem ? this.scene.timeSystem.dayCount : 1,
+ playtime: this.scene.playtimeTracker ? this.scene.playtimeTracker.stats.playtimeSeconds : 0,
+ playerLevel: this.scene.player ? (this.scene.player.level || 1) : 1
+ };
+
+ const metaKey = `novafarma_slot${slotNumber}_meta`;
+ localStorage.setItem(metaKey, JSON.stringify(metadata));
+ }
+
+ /**
+ * Get metadata for slot
+ */
+ getSlotMetadata(slotNumber) {
+ const metaKey = `novafarma_slot${slotNumber}_meta`;
+ const raw = localStorage.getItem(metaKey);
+
+ if (!raw) return null;
+
+ try {
+ return JSON.parse(raw);
+ } catch (e) {
+ console.error(`Failed to parse metadata for slot ${slotNumber}:`, e);
+ return null;
+ }
+ }
+
+ /**
+ * Get all slots info
+ */
+ getAllSlotsInfo() {
+ const slots = [];
+
+ for (let i = 1; i <= this.maxSlots; i++) {
+ const metadata = this.getSlotMetadata(i);
+ const exists = this.slotExists(i);
+
+ slots.push({
+ number: i,
+ exists,
+ metadata: metadata || {
+ playerName: 'Empty',
+ dayCount: 0,
+ playtime: 0,
+ lastSaved: null
+ }
+ });
+ }
+
+ return slots;
+ }
+
+ /**
+ * Check if slot exists
+ */
+ slotExists(slotNumber) {
+ const key = `novafarma_save_slot${slotNumber}`;
+ return localStorage.getItem(key) !== null;
+ }
+
+ /**
+ * Quick save to current slot
+ */
+ quickSave() {
+ console.log(`โก Quick save to slot ${this.currentSlot}`);
+ this.saveToSlot(this.currentSlot);
+ }
+
+ /**
+ * Quick load from current slot
+ */
+ quickLoad() {
+ console.log(`โก Quick load from slot ${this.currentSlot}`);
+ return this.loadFromSlot(this.currentSlot);
+ }
+
+ /**
+ * Update auto-save timer
+ * Call this in scene update()
+ */
+ update(delta) {
+ if (!this.autoSaveEnabled) return;
+
+ this.autoSaveTimer += delta;
+
+ if (this.autoSaveTimer >= this.autoSaveInterval) {
+ this.autoSaveTimer = 0;
+ console.log('๐พ Auto-saving...');
+ this.quickSave();
+
+ // Show notification
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.cameras.main.scrollX + this.scene.cameras.main.width - 100,
+ y: this.scene.cameras.main.scrollY + 50,
+ text: '๐พ Auto-Saved',
+ color: '#00ff00'
+ });
+ }
+ }
+ }
+
+ /**
+ * Toggle auto-save
+ */
+ toggleAutoSave() {
+ this.autoSaveEnabled = !this.autoSaveEnabled;
+ console.log(`Auto-save: ${this.autoSaveEnabled ? 'ON' : 'OFF'}`);
+ return this.autoSaveEnabled;
+ }
+
+ /**
+ * Get time until next auto-save
+ */
+ getTimeUntilNextSave() {
+ if (!this.autoSaveEnabled) return -1;
+ return Math.max(0, this.autoSaveInterval - this.autoSaveTimer);
+ }
+
+ /**
+ * Export save data as JSON (for backup)
+ */
+ exportSlot(slotNumber) {
+ if (!this.slotExists(slotNumber)) {
+ console.error(`Slot ${slotNumber} is empty`);
+ return null;
+ }
+
+ const key = `novafarma_save_slot${slotNumber}`;
+ const data = localStorage.getItem(key);
+
+ // Create downloadable file
+ const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(data);
+ const downloadAnchorNode = document.createElement('a');
+ downloadAnchorNode.setAttribute("href", dataStr);
+ downloadAnchorNode.setAttribute("download", `novafarma_slot${slotNumber}_${Date.now()}.json`);
+ document.body.appendChild(downloadAnchorNode);
+ downloadAnchorNode.click();
+ downloadAnchorNode.remove();
+
+ console.log(`๐ค Exported slot ${slotNumber}`);
+ return true;
+ }
+
+ /**
+ * Import save data from JSON file
+ */
+ importSlot(slotNumber, jsonData) {
+ try {
+ const key = `novafarma_save_slot${slotNumber}`;
+ localStorage.setItem(key, jsonData);
+ console.log(`๐ฅ Imported to slot ${slotNumber}`);
+ return true;
+ } catch (e) {
+ console.error('Failed to import:', e);
+ return false;
+ }
+ }
+}
+
+// Make available globally
+window.SaveManager = SaveManager;
+
+// Convenience functions
+window.save = function (slot = 1) {
+ const scene = game.scene.scenes.find(s => s.saveManager);
+ if (scene && scene.saveManager) {
+ return scene.saveManager.saveToSlot(slot);
+ }
+};
+
+window.load = function (slot = 1) {
+ const scene = game.scene.scenes.find(s => s.saveManager);
+ if (scene && scene.saveManager) {
+ return scene.saveManager.loadFromSlot(slot);
+ }
+};
+
+console.log('๐พ SaveManager loaded. Use: save(1), load(1), or F5/F9 keys');
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SaveSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/SaveSystem.js
new file mode 100644
index 000000000..93e30834a
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SaveSystem.js
@@ -0,0 +1,281 @@
+class SaveSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.storageKey = 'novafarma_savefile';
+ }
+
+ saveGame() {
+ console.log('๐พ Saving game...');
+
+ if (!this.scene.player || !this.scene.terrainSystem) {
+ console.error('Cannot save: Player or TerrainSystem missing.');
+ return;
+ }
+
+ const playerPos = this.scene.player.getPosition();
+
+ // Zberi podatke o NPCjih
+ const npcsData = this.scene.npcs.map(npc => ({
+ type: npc.type,
+ x: npc.gridX,
+ y: npc.gridY
+ }));
+
+ // Zberi podatke o terenu - FLAT 2D (no seed, no crops yet!)
+ const terrainSeed = this.scene.terrainSystem.noise?.seed || 0; // Optional for old system
+
+ // Flat2DTerrainSystem doesn't have cropsMap yet - skip for now
+ const cropsData = this.scene.terrainSystem.cropsMap
+ ? Array.from(this.scene.terrainSystem.cropsMap.entries())
+ : [];
+
+ const decorData = this.scene.terrainSystem.decorationsMap
+ ? Array.from(this.scene.terrainSystem.decorationsMap.values())
+ : [];
+
+ // Inventory
+ const inventoryData = {
+ slots: this.scene.inventorySystem.slots,
+ gold: this.scene.inventorySystem.gold || 0
+ };
+
+ const saveData = {
+ version: 2.5, // 2D Flat Version
+ timestamp: Date.now(),
+ player: { x: playerPos.x, y: playerPos.y },
+ terrain: {
+ seed: terrainSeed,
+ crops: cropsData,
+ decorations: decorData
+ },
+ npcs: npcsData,
+ inventory: inventoryData,
+ time: {
+ gameTime: this.scene.timeSystem ? this.scene.timeSystem.gameTime : 8,
+ dayCount: this.scene.timeSystem ? this.scene.timeSystem.dayCount : 1
+ },
+ stats: this.scene.statsSystem ? {
+ health: this.scene.statsSystem.health,
+ hunger: this.scene.statsSystem.hunger,
+ thirst: this.scene.statsSystem.thirst
+ } : null,
+ camera: { zoom: this.scene.cameras.main.zoom }
+ };
+
+ try {
+ const jsonString = JSON.stringify(saveData);
+ // Compress data to save space
+ try {
+ const compressed = Compression.compress(jsonString);
+ localStorage.setItem(this.storageKey, 'LZW:' + compressed);
+ console.log(`โ
Game saved! Size: ${jsonString.length} -> ${compressed.length} chars`);
+ } catch (compErr) {
+ console.warn("Compression failed, saving raw JSON:", compErr);
+ localStorage.setItem(this.storageKey, jsonString);
+ }
+
+ // Pokaลพi obvestilo (preko UIScene ฤe obstaja)
+ this.showNotification('GAME SAVED');
+ } catch (e) {
+ console.error('โ Failed to save game:', e);
+ this.showNotification('SAVE FAILED');
+ }
+ }
+
+ loadGame() {
+ console.log('๐ Loading game... DISABLED - Fresh start!');
+ // ONEMOGOฤENO - vedno vrne false za fresh start
+ return false;
+
+ try {
+ let jsonString = rawData;
+ // Check for compression
+ if (rawData.startsWith('LZW:')) {
+ const compressed = rawData.substring(4); // Remove prefix
+ jsonString = Compression.decompress(compressed);
+ }
+
+ const saveData = JSON.parse(jsonString);
+ console.log('Loading save data:', saveData);
+
+ // Preveri verzijo - ฤe je stara, izbriลกi save
+ if (!saveData.version || saveData.version < 2.4) {
+ console.log('โ ๏ธ Stara verzija save file-a detected, clearing...');
+ localStorage.removeItem(this.storageKey);
+ this.showNotification('OLD SAVE CLEARED - NEW GAME');
+ return false;
+ }
+
+ // 1. Load Player
+ if (this.scene.player) {
+ // Zahteva metodo setPosition(gridX, gridY) v Player.js
+ // Trenutno imamo moveToGrid ampak za instant load rabimo direkten set.
+ // Uporabimo updatePosition logic iz NPC, ali pa kar moveToGrid s hitrostjo 0?
+ // Bolje dodati setGridPosition v Player.js.
+ // Za zdaj workaround:
+ this.scene.player.gridX = saveData.player.x;
+ this.scene.player.gridY = saveData.player.y;
+ // Force update screen pos
+ const screenPos = this.scene.player.iso.toScreen(saveData.player.x, saveData.player.y);
+ this.scene.player.sprite.setPosition(
+ screenPos.x + this.scene.player.offsetX,
+ screenPos.y + this.scene.player.offsetY
+ );
+ this.scene.player.updateDepth();
+ }
+
+ // 2. Load Terrain (Regenerate + Restore dynamic)
+ if (this.scene.terrainSystem && saveData.terrain) {
+ // A) Seed / Base Terrain
+ if (saveData.terrain.seed && this.scene.terrainSystem.noise.seed !== saveData.terrain.seed) {
+ // Regenerate world if seed mismatch
+ // (Actually we might want to ALWAYS regenerate to clear default decors then overwrite?)
+ // Current logic: generate() spawns default decorations.
+ // To handle persistence properly:
+ // 1. Clear current decorations
+ // 2. Load saved decorations
+
+ this.scene.terrainSystem.noise = new PerlinNoise(saveData.terrain.seed);
+ // this.scene.terrainSystem.generate(); // This re-adds random flowers
+ // Instead of full generate, we might just re-calc tiles if seed changed?
+ // For now assume seed is constant for "New Game", but let's re-run generate to be safe
+ }
+
+ // Clear EVERYTHING first
+ this.scene.terrainSystem.decorationsMap.clear();
+ this.scene.terrainSystem.decorations = [];
+ this.scene.terrainSystem.cropsMap.clear();
+ // We should also hide active sprites?
+ this.scene.terrainSystem.visibleDecorations.forEach(s => s.setVisible(false));
+ this.scene.terrainSystem.visibleDecorations.clear();
+ this.scene.terrainSystem.visibleCrops.forEach(s => s.setVisible(false));
+ this.scene.terrainSystem.visibleCrops.clear();
+ // Sproลกฤanje objektov se samodejno dogaja preko release() v clearanju zgoraj
+
+ // B) Restore Crops
+ if (saveData.terrain.crops) {
+ // Map was saved as array of entries
+ saveData.terrain.crops.forEach(entry => {
+ const [key, cropData] = entry;
+ this.scene.terrainSystem.cropsMap.set(key, cropData);
+
+ // Set flag on tile
+ const [gx, gy] = key.split(',').map(Number);
+ const tile = this.scene.terrainSystem.getTile(gx, gy);
+ if (tile) tile.hasCrop = true;
+ });
+ }
+
+ // C) Restore Decorations (Flowers, Houses, Walls, Fences...)
+ if (saveData.terrain.decorations) {
+ saveData.terrain.decorations.forEach(d => {
+ this.scene.terrainSystem.decorations.push(d);
+ this.scene.terrainSystem.decorationsMap.set(d.id, d);
+
+ const tile = this.scene.terrainSystem.getTile(d.gridX, d.gridY);
+ if (tile) tile.hasDecoration = true;
+ });
+ }
+
+ // Force Update Visuals
+ this.scene.terrainSystem.updateCulling(this.scene.cameras.main);
+ }
+
+ // 3. Load Inventory
+ if (this.scene.inventorySystem && saveData.inventory) {
+ this.scene.inventorySystem.slots = saveData.inventory.slots;
+ this.scene.inventorySystem.gold = saveData.inventory.gold;
+ this.scene.inventorySystem.updateUI();
+ }
+
+ // 4. Load Time & Stats
+ if (this.scene.timeSystem && saveData.time) {
+ this.scene.timeSystem.gameTime = saveData.time.gameTime;
+ this.scene.timeSystem.dayCount = saveData.time.dayCount || 1;
+ }
+ if (this.scene.statsSystem && saveData.stats) {
+ this.scene.statsSystem.health = saveData.stats.health;
+ this.scene.statsSystem.hunger = saveData.stats.hunger;
+ this.scene.statsSystem.thirst = saveData.stats.thirst;
+ }
+
+ // 3. Load NPCs
+ // 3. Load NPCs
+ // Pobriลกi trenutne
+ this.scene.npcs.forEach(npc => npc.destroy());
+ this.scene.npcs = [];
+
+ let hasMerchant = false;
+
+ // Ustvari shranjene
+ if (saveData.npcs) {
+ saveData.npcs.forEach(npcData => {
+ const npc = new NPC(
+ this.scene,
+ npcData.x,
+ npcData.y,
+ this.scene.terrainOffsetX,
+ this.scene.terrainOffsetY,
+ npcData.type
+ );
+ this.scene.npcs.push(npc);
+ if (npcData.type === 'merchant') hasMerchant = true;
+ });
+ }
+
+ // Force Spawn Merchant if missing
+ if (!hasMerchant) {
+ // Spawn near current player position so user can find him
+ const px = saveData.player ? saveData.player.x : 50;
+ const py = saveData.player ? saveData.player.y : 50;
+ const mX = Math.max(0, Math.min(99, Math.floor(px) + 3));
+ const mY = Math.max(0, Math.min(99, Math.floor(py) + 3));
+
+ const merchant = new NPC(this.scene, mX, mY, this.scene.terrainOffsetX, this.scene.terrainOffsetY, 'merchant');
+ this.scene.npcs.push(merchant);
+ console.log("๐ช FORCE SPAWNED MERCHANT at", mX, mY);
+ }
+
+ // 4. Camera
+ if (saveData.camera) {
+ this.scene.cameras.main.setZoom(saveData.camera.zoom);
+ }
+
+ this.showNotification('GAME LOADED');
+ return true;
+
+ } catch (e) {
+ console.error('โ Failed to load game:', e);
+ this.showNotification('LOAD FAILED');
+ return false;
+ }
+ }
+
+ showNotification(text) {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene) {
+ const width = uiScene.cameras.main.width;
+ const height = uiScene.cameras.main.height;
+
+ const msg = uiScene.add.text(width / 2, height / 2, text, {
+ fontFamily: 'Courier New',
+ fontSize: '32px',
+ fill: '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 10, y: 5 }
+ });
+ msg.setOrigin(0.5);
+ msg.setScrollFactor(0);
+
+ uiScene.tweens.add({
+ targets: msg,
+ alpha: 0,
+ duration: 2000,
+ delay: 500,
+ onComplete: () => {
+ msg.destroy();
+ }
+ });
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SchoolBuffSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/SchoolBuffSystem.js
new file mode 100644
index 000000000..35abf2715
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SchoolBuffSystem.js
@@ -0,0 +1,316 @@
+/**
+ * SCHOOL BUFF SYSTEM
+ * Learning skills from Teacher NPC
+ * Temporary and permanent buffs
+ */
+
+export class SchoolBuffSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Available lessons
+ this.lessons = new Map();
+ this.learnedSkills = new Set();
+
+ // Active buffs
+ this.activeBuffs = new Map();
+
+ // Teacher NPC reference
+ this.teacher = null;
+
+ this.init();
+ }
+
+ init() {
+ this.initializeLessons();
+ }
+
+ /**
+ * Initialize all available lessons
+ */
+ initializeLessons() {
+ // FARMING LESSONS
+ this.registerLesson({
+ id: 'basic_farming',
+ name: 'Basic Farming',
+ category: 'farming',
+ cost: 100,
+ duration: 'permanent',
+ description: '+10% crop yield',
+ effect: () => this.scene.gameState.buffs.crop_yield = (this.scene.gameState.buffs.crop_yield || 1.0) * 1.1
+ });
+
+ this.registerLesson({
+ id: 'advanced_farming',
+ name: 'Advanced Farming',
+ category: 'farming',
+ cost: 500,
+ requires: 'basic_farming',
+ duration: 'permanent',
+ description: '+20% crop growth speed',
+ effect: () => this.scene.gameState.buffs.crop_growth_speed = (this.scene.gameState.buffs.crop_growth_speed || 1.0) * 1.2
+ });
+
+ this.registerLesson({
+ id: 'fertilizer_mastery',
+ name: 'Fertilizer Mastery',
+ category: 'farming',
+ cost: 300,
+ duration: 'permanent',
+ description: 'Fertilizer lasts 2x longer',
+ effect: () => this.scene.gameState.buffs.fertilizer_duration = 2.0
+ });
+
+ // COMBAT LESSONS
+ this.registerLesson({
+ id: 'basic_combat',
+ name: 'Basic Combat',
+ category: 'combat',
+ cost: 200,
+ duration: 'permanent',
+ description: '+5 damage to all attacks',
+ effect: () => this.scene.player.attackDamage += 5
+ });
+
+ this.registerLesson({
+ id: 'defense_training',
+ name: 'Defense Training',
+ category: 'combat',
+ cost: 400,
+ duration: 'permanent',
+ description: '+15% damage resistance',
+ effect: () => this.scene.player.damageResistance = (this.scene.player.damageResistance || 0) + 0.15
+ });
+
+ this.registerLesson({
+ id: 'weapon_mastery',
+ name: 'Weapon Mastery',
+ category: 'combat',
+ cost: 800,
+ requires: 'basic_combat',
+ duration: 'permanent',
+ description: '+20% critical hit chance',
+ effect: () => this.scene.player.critChance = (this.scene.player.critChance || 0) + 0.2
+ });
+
+ // SURVIVAL LESSONS
+ this.registerLesson({
+ id: 'herbalism',
+ name: 'Herbalism',
+ category: 'survival',
+ cost: 250,
+ duration: 'permanent',
+ description: 'Healing items restore 50% more HP',
+ effect: () => this.scene.gameState.buffs.healing_bonus = 1.5
+ });
+
+ this.registerLesson({
+ id: 'scavenging',
+ name: 'Scavenging',
+ category: 'survival',
+ cost: 350,
+ duration: 'permanent',
+ description: '+25% loot from containers',
+ effect: () => this.scene.gameState.buffs.loot_bonus = 1.25
+ });
+
+ this.registerLesson({
+ id: 'endurance',
+ name: 'Endurance Training',
+ category: 'survival',
+ cost: 600,
+ duration: 'permanent',
+ description: '+20 max stamina',
+ effect: () => this.scene.player.maxStamina += 20
+ });
+
+ // TEMPORARY BUFFS (Study Sessions)
+ this.registerLesson({
+ id: 'focus_boost',
+ name: 'Focus Boost',
+ category: 'temporary',
+ cost: 50,
+ duration: 300000, // 5 minutes
+ description: '+50% XP gain for 5 minutes',
+ effect: () => this.applyTemporaryBuff('xp_boost', 1.5, 300000)
+ });
+
+ this.registerLesson({
+ id: 'energy_surge',
+ name: 'Energy Surge',
+ category: 'temporary',
+ cost: 75,
+ duration: 180000, // 3 minutes
+ description: '+100% stamina regen for 3 minutes',
+ effect: () => this.applyTemporaryBuff('stamina_regen', 2.0, 180000)
+ });
+ }
+
+ /**
+ * Register a lesson
+ */
+ registerLesson(lessonData) {
+ this.lessons.set(lessonData.id, {
+ ...lessonData,
+ learned: false
+ });
+ }
+
+ /**
+ * Learn a skill from Teacher
+ */
+ learnSkill(lessonId) {
+ const lesson = this.lessons.get(lessonId);
+ if (!lesson) return false;
+
+ // Check if already learned (permanent skills only)
+ if (lesson.duration === 'permanent' && lesson.learned) {
+ console.warn(`Already learned: ${lesson.name}`);
+ return false;
+ }
+
+ // Check prerequisites
+ if (lesson.requires && !this.learnedSkills.has(lesson.requires)) {
+ const required = this.lessons.get(lesson.requires);
+ console.warn(`Must learn ${required.name} first`);
+ return false;
+ }
+
+ // Check cost
+ const currency = this.scene.economySystem?.getCurrency() || 0;
+ if (currency < lesson.cost) {
+ console.warn(`Insufficient funds: need ${lesson.cost}, have ${currency}`);
+ return false;
+ }
+
+ // Pay cost
+ this.scene.economySystem.addCurrency(-lesson.cost);
+
+ // Apply effect
+ lesson.effect();
+
+ // Mark as learned
+ if (lesson.duration === 'permanent') {
+ lesson.learned = true;
+ this.learnedSkills.add(lessonId);
+ }
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `Learned: ${lesson.name}!`,
+ 'success',
+ { description: lesson.description }
+ );
+
+ // Teacher dialogue
+ this.scene.dialogueSystem?.startDialogue('teacher', 'lesson_complete', { skill: lesson.name });
+
+ console.log(`๐ Learned: ${lesson.name} (-${lesson.cost} coins)`);
+ return true;
+ }
+
+ /**
+ * Apply temporary buff
+ */
+ applyTemporaryBuff(buffId, multiplier, duration) {
+ // Store buff
+ this.activeBuffs.set(buffId, {
+ multiplier,
+ expiresAt: Date.now() + duration
+ });
+
+ // VFX
+ this.scene.vfxSystem?.playEffect('buff_applied', this.scene.player.x, this.scene.player.y);
+
+ // Remove after duration
+ this.scene.time.delayedCall(duration, () => {
+ this.activeBuffs.delete(buffId);
+ this.scene.uiSystem?.showNotification(`${buffId} expired`, 'info');
+ console.log(`โฑ๏ธ Buff expired: ${buffId}`);
+ });
+
+ console.log(`โจ Applied buff: ${buffId} (${duration / 1000}s)`);
+ }
+
+ /**
+ * Check if buff is active
+ */
+ hasActiveBuff(buffId) {
+ const buff = this.activeBuffs.get(buffId);
+ if (!buff) return false;
+
+ const now = Date.now();
+ if (now > buff.expiresAt) {
+ this.activeBuffs.delete(buffId);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get buff multiplier
+ */
+ getBuffMultiplier(buffId) {
+ const buff = this.activeBuffs.get(buffId);
+ return buff ? buff.multiplier : 1.0;
+ }
+
+ /**
+ * Get lessons by category
+ */
+ getLessonsByCategory(category) {
+ return Array.from(this.lessons.values()).filter(l => l.category === category);
+ }
+
+ /**
+ * Get available lessons (can be learned now)
+ */
+ getAvailableLessons() {
+ return Array.from(this.lessons.values()).filter(l => {
+ if (l.duration === 'permanent' && l.learned) return false;
+ if (l.requires && !this.learnedSkills.has(l.requires)) return false;
+ return true;
+ });
+ }
+
+ /**
+ * Get learned skills
+ */
+ getLearnedSkills() {
+ return Array.from(this.lessons.values()).filter(l => l.learned);
+ }
+
+ /**
+ * Save/Load
+ */
+ getSaveData() {
+ return {
+ learnedSkills: Array.from(this.learnedSkills),
+ activeBuffs: Array.from(this.activeBuffs.entries())
+ };
+ }
+
+ loadSaveData(data) {
+ this.learnedSkills = new Set(data.learnedSkills || []);
+
+ // Mark lessons as learned
+ this.learnedSkills.forEach(skillId => {
+ const lesson = this.lessons.get(skillId);
+ if (lesson) {
+ lesson.learned = true;
+ lesson.effect(); // Re-apply permanent effects
+ }
+ });
+
+ // Restore active buffs
+ if (data.activeBuffs) {
+ data.activeBuffs.forEach(([buffId, buffData]) => {
+ const remaining = buffData.expiresAt - Date.now();
+ if (remaining > 0) {
+ this.applyTemporaryBuff(buffId, buffData.multiplier, remaining);
+ }
+ });
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ScooterRepairSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ScooterRepairSystem.js
new file mode 100644
index 000000000..1e3d71dda
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ScooterRepairSystem.js
@@ -0,0 +1,160 @@
+/**
+ * SCOOTER REPAIR SYSTEM
+ * Broken scooter needs parts + tools to repair before being driveable
+ */
+class ScooterRepairSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Required parts for repair
+ this.requiredParts = {
+ scooter_engine: 1,
+ scooter_wheel: 2,
+ scooter_fuel_tank: 1,
+ scooter_handlebars: 1
+ };
+
+ // Required tools
+ this.requiredTools = {
+ wrench: 1,
+ screwdriver: 1
+ };
+
+ this.isRepaired = false;
+ }
+
+ /**
+ * Check if player has all required parts
+ */
+ hasAllParts() {
+ const inv = this.scene.inventorySystem;
+ if (!inv) return false;
+
+ for (const [part, count] of Object.entries(this.requiredParts)) {
+ if (!inv.hasItem(part) || inv.getItemCount(part) < count) {
+ console.log(`๐ซ Missing: ${part} (Need ${count})`);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if player has required tools
+ */
+ hasAllTools() {
+ const inv = this.scene.inventorySystem;
+ if (!inv) return false;
+
+ for (const [tool, count] of Object.entries(this.requiredTools)) {
+ if (!inv.hasItem(tool) || inv.getItemCount(tool) < count) {
+ console.log(`๐ซ Missing tool: ${tool} (Need ${count})`);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempt to repair scooter
+ */
+ repairScooter() {
+ if (this.isRepaired) {
+ console.log('โ
Scooter already repaired!');
+ return false;
+ }
+
+ if (!this.hasAllParts()) {
+ console.log('๐ซ Missing required parts!');
+ this.listMissingParts();
+ return false;
+ }
+
+ if (!this.hasAllTools()) {
+ console.log('๐ซ Missing required tools!');
+ return false;
+ }
+
+ // Consume parts (tools are NOT consumed)
+ const inv = this.scene.inventorySystem;
+ for (const [part, count] of Object.entries(this.requiredParts)) {
+ inv.removeItem(part, count);
+ }
+
+ this.isRepaired = true;
+ console.log('โ
๐ต SCOOTER REPAIRED! Press V to drive!');
+
+ // Find scooter entity and enable it
+ if (this.scene.vehicles) {
+ for (const vehicle of this.scene.vehicles) {
+ if (vehicle.type === 'scooter') {
+ vehicle.isEnabled = true;
+ vehicle.updateVisual(); // Make it look new
+ }
+ }
+ }
+
+ // Visual feedback
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 100,
+ text: '๐ต REPAIRED!',
+ color: '#00FF00'
+ });
+
+ return true;
+ }
+
+ /**
+ * List missing parts for repair
+ */
+ listMissingParts() {
+ const inv = this.scene.inventorySystem;
+ if (!inv) return;
+
+ console.log('๐ SCOOTER REPAIR CHECKLIST:');
+ console.log('===========================');
+
+ console.log('PARTS:');
+ for (const [part, needed] of Object.entries(this.requiredParts)) {
+ const has = inv.getItemCount(part) || 0;
+ const status = has >= needed ? 'โ
' : '๐ซ';
+ console.log(`${status} ${part}: ${has}/${needed}`);
+ }
+
+ console.log('\nTOOLS:');
+ for (const [tool, needed] of Object.entries(this.requiredTools)) {
+ const has = inv.getItemCount(tool) || 0;
+ const status = has >= needed ? 'โ
' : '๐ซ';
+ console.log(`${status} ${tool}: ${has}/${needed}`);
+ }
+ }
+
+ /**
+ * Spawn scooter part as loot
+ */
+ spawnPart(x, y, partType) {
+ if (!this.scene.interactionSystem) return;
+
+ const validParts = Object.keys(this.requiredParts);
+ const part = partType || validParts[Math.floor(Math.random() * validParts.length)];
+
+ this.scene.interactionSystem.spawnLoot(x, y, part, 1);
+ console.log(`๐ง Spawned scooter part: ${part} at ${x},${y}`);
+ }
+
+ /**
+ * Random loot drop chance for parts (from zombies, chests, etc.)
+ */
+ tryDropPart(x, y, dropChance = 0.1) {
+ if (this.isRepaired) return; // Don't drop if already repaired
+
+ if (Math.random() < dropChance) {
+ this.spawnPart(x, y);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ScreenReaderSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ScreenReaderSystem.js
new file mode 100644
index 000000000..f39941960
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ScreenReaderSystem.js
@@ -0,0 +1,590 @@
+/**
+ * SCREEN READER SYSTEM
+ * Provides audio narration and accessibility for blind/visually impaired players
+ * Compatible with NVDA, JAWS, VoiceOver, and other screen readers
+ */
+class ScreenReaderSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Speech synthesis
+ this.synth = window.speechSynthesis;
+ this.voice = null;
+ this.voices = [];
+
+ // Settings
+ this.settings = {
+ enabled: false, // DISABLED by default
+ rate: 1.0, // 0.1 - 10 (speech speed)
+ pitch: 1.0, // 0 - 2 (voice pitch)
+ volume: 1.0, // 0 - 1 (volume)
+ language: 'en-US', // Voice language
+ autoNarrate: false, // Auto-narrate UI changes
+ verboseMode: false, // Detailed descriptions
+ soundCues: false, // Audio cues for actions
+ navigationHelp: false // Navigation assistance
+ };
+
+ // ARIA live regions (for screen reader announcements)
+ this.liveRegion = null;
+ this.alertRegion = null;
+
+ // Navigation state
+ this.currentFocus = null;
+ this.navigationHistory = [];
+ this.maxHistorySize = 50;
+
+ // Audio cues (simple beeps/tones)
+ this.audioCues = {
+ 'focus': { frequency: 440, duration: 100 },
+ 'select': { frequency: 880, duration: 150 },
+ 'error': { frequency: 220, duration: 300 },
+ 'success': { frequency: 660, duration: 200 },
+ 'navigation': { frequency: 550, duration: 80 },
+ 'inventory': { frequency: 750, duration: 120 },
+ 'damage': { frequency: 200, duration: 250 },
+ 'pickup': { frequency: 1000, duration: 100 }
+ };
+
+ // Context descriptions
+ this.contextDescriptions = {
+ 'menu': 'Main menu. Use arrow keys to navigate, Enter to select.',
+ 'game': 'In game. Use WASD to move, E to interact, I for inventory.',
+ 'inventory': 'Inventory screen. Use arrow keys to navigate items, Enter to use.',
+ 'crafting': 'Crafting menu. Use arrow keys to browse recipes, Enter to craft.',
+ 'dialogue': 'Dialogue. Press Space to continue, Escape to skip.',
+ 'combat': 'In combat. Use J to attack, Space to dodge.',
+ 'building': 'Build mode. Use arrow keys to select building, Enter to place.',
+ 'map': 'Map view. Use arrow keys to pan, M to close.'
+ };
+
+ // UI element descriptions
+ this.elementDescriptions = new Map();
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Screen Reader System initialized');
+ }
+
+ init() {
+ // Load available voices
+ this.loadVoices();
+
+ // Create ARIA live regions
+ this.createLiveRegions();
+
+ // Set up speech synthesis event listeners
+ this.setupSpeechListeners();
+
+ // Set up keyboard navigation
+ this.setupKeyboardNavigation();
+
+ // Announce system ready
+ this.speak('Screen reader system ready. Press H for help.');
+ }
+
+ /**
+ * Load available speech synthesis voices
+ */
+ loadVoices() {
+ this.voices = this.synth.getVoices();
+
+ // If voices not loaded yet, wait for event
+ if (this.voices.length === 0) {
+ this.synth.addEventListener('voiceschanged', () => {
+ this.voices = this.synth.getVoices();
+ this.selectVoice();
+ });
+ } else {
+ this.selectVoice();
+ }
+ }
+
+ /**
+ * Select appropriate voice based on language setting
+ */
+ selectVoice() {
+ if (this.voices.length === 0) return;
+
+ // Try to find voice matching language
+ this.voice = this.voices.find(v => v.lang === this.settings.language);
+
+ // Fallback to first available voice
+ if (!this.voice) {
+ this.voice = this.voices[0];
+ }
+
+ console.log(`๐ Selected voice: ${this.voice.name} (${this.voice.lang})`);
+ }
+
+ /**
+ * Create ARIA live regions for screen reader announcements
+ */
+ createLiveRegions() {
+ // Polite region (non-interrupting)
+ this.liveRegion = document.createElement('div');
+ this.liveRegion.setAttribute('role', 'status');
+ this.liveRegion.setAttribute('aria-live', 'polite');
+ this.liveRegion.setAttribute('aria-atomic', 'true');
+ this.liveRegion.style.position = 'absolute';
+ this.liveRegion.style.left = '-10000px';
+ this.liveRegion.style.width = '1px';
+ this.liveRegion.style.height = '1px';
+ this.liveRegion.style.overflow = 'hidden';
+ document.body.appendChild(this.liveRegion);
+
+ // Alert region (interrupting)
+ this.alertRegion = document.createElement('div');
+ this.alertRegion.setAttribute('role', 'alert');
+ this.alertRegion.setAttribute('aria-live', 'assertive');
+ this.alertRegion.setAttribute('aria-atomic', 'true');
+ this.alertRegion.style.position = 'absolute';
+ this.alertRegion.style.left = '-10000px';
+ this.alertRegion.style.width = '1px';
+ this.alertRegion.style.height = '1px';
+ this.alertRegion.style.overflow = 'hidden';
+ document.body.appendChild(this.alertRegion);
+ }
+
+ /**
+ * Set up speech synthesis event listeners
+ */
+ setupSpeechListeners() {
+ this.synth.addEventListener('error', (event) => {
+ console.error('Speech synthesis error:', event);
+ });
+ }
+
+ /**
+ * Set up keyboard navigation for screen reader users
+ */
+ setupKeyboardNavigation() {
+ // H key - Help
+ this.scene.input.keyboard.on('keydown-H', () => {
+ if (this.scene.input.keyboard.checkDown(this.scene.input.keyboard.addKey('CTRL'))) {
+ this.announceHelp();
+ }
+ });
+
+ // Ctrl+R - Repeat last announcement
+ this.scene.input.keyboard.on('keydown-R', () => {
+ if (this.scene.input.keyboard.checkDown(this.scene.input.keyboard.addKey('CTRL'))) {
+ this.repeatLast();
+ }
+ });
+
+ // Ctrl+S - Settings
+ this.scene.input.keyboard.on('keydown-S', () => {
+ if (this.scene.input.keyboard.checkDown(this.scene.input.keyboard.addKey('CTRL'))) {
+ this.announceSettings();
+ }
+ });
+ }
+
+ /**
+ * Speak text using speech synthesis
+ */
+ speak(text, priority = 'normal', interrupt = false) {
+ if (!this.settings.enabled || !text) return;
+
+ // Cancel current speech if interrupting
+ if (interrupt) {
+ this.synth.cancel();
+ }
+
+ // Create utterance
+ const utterance = new SpeechSynthesisUtterance(text);
+ utterance.voice = this.voice;
+ utterance.rate = this.settings.rate;
+ utterance.pitch = this.settings.pitch;
+ utterance.volume = this.settings.volume;
+
+ // Speak
+ this.synth.speak(utterance);
+
+ // Update ARIA live region
+ if (priority === 'alert') {
+ this.alertRegion.textContent = text;
+ } else {
+ this.liveRegion.textContent = text;
+ }
+
+ // Add to history
+ this.addToHistory(text);
+
+ console.log(`๐ Speaking: "${text}"`);
+ }
+
+ /**
+ * Stop current speech
+ */
+ stop() {
+ this.synth.cancel();
+ }
+
+ /**
+ * Add text to navigation history
+ */
+ addToHistory(text) {
+ this.navigationHistory.unshift(text);
+ if (this.navigationHistory.length > this.maxHistorySize) {
+ this.navigationHistory.pop();
+ }
+ }
+
+ /**
+ * Repeat last announcement
+ */
+ repeatLast() {
+ if (this.navigationHistory.length > 0) {
+ this.speak(this.navigationHistory[0], 'normal', true);
+ }
+ }
+
+ /**
+ * Play audio cue
+ */
+ playAudioCue(cueType) {
+ if (!this.settings.soundCues) return;
+
+ const cue = this.audioCues[cueType];
+ if (!cue) return;
+
+ // Create audio context
+ const audioContext = new (window.AudioContext || window.webkitAudioContext)();
+ const oscillator = audioContext.createOscillator();
+ const gainNode = audioContext.createGain();
+
+ oscillator.connect(gainNode);
+ gainNode.connect(audioContext.destination);
+
+ oscillator.frequency.value = cue.frequency;
+ oscillator.type = 'sine';
+
+ gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + cue.duration / 1000);
+
+ oscillator.start(audioContext.currentTime);
+ oscillator.stop(audioContext.currentTime + cue.duration / 1000);
+ }
+
+ /**
+ * Announce game context
+ */
+ announceContext(context) {
+ const description = this.contextDescriptions[context];
+ if (description) {
+ this.speak(description, 'alert', true);
+ this.playAudioCue('navigation');
+ }
+ }
+
+ /**
+ * Announce player stats
+ */
+ announceStats() {
+ if (!this.scene.player || !this.scene.statsSystem) return;
+
+ const stats = this.scene.statsSystem;
+ const text = `Health: ${Math.round(stats.health)} out of ${stats.maxHealth}. ` +
+ `Hunger: ${Math.round(stats.hunger)} percent. ` +
+ `Stamina: ${Math.round(stats.stamina)} percent.`;
+
+ this.speak(text);
+ }
+
+ /**
+ * Announce inventory
+ */
+ announceInventory() {
+ if (!this.scene.inventorySystem) return;
+
+ const inv = this.scene.inventorySystem;
+ const itemCount = Object.keys(inv.items).length;
+ const gold = inv.gold || 0;
+
+ let text = `Inventory. ${itemCount} item types. ${gold} gold. `;
+
+ // List items
+ if (this.settings.verboseMode) {
+ for (const [item, count] of Object.entries(inv.items)) {
+ text += `${item}: ${count}. `;
+ }
+ } else {
+ text += 'Press V for verbose item list.';
+ }
+
+ this.speak(text);
+ }
+
+ /**
+ * Announce position
+ */
+ announcePosition() {
+ if (!this.scene.player) return;
+
+ const pos = this.scene.player.getPosition();
+ const text = `Position: X ${Math.round(pos.x)}, Y ${Math.round(pos.y)}.`;
+
+ this.speak(text);
+ }
+
+ /**
+ * Announce nearby objects
+ */
+ announceNearby() {
+ if (!this.scene.player || !this.scene.terrainSystem) return;
+
+ const pos = this.scene.player.getPosition();
+ const x = Math.floor(pos.x);
+ const y = Math.floor(pos.y);
+
+ let text = 'Nearby: ';
+ let foundObjects = false;
+
+ // Check decorations
+ for (let dx = -2; dx <= 2; dx++) {
+ for (let dy = -2; dy <= 2; dy++) {
+ if (dx === 0 && dy === 0) continue;
+
+ const key = `${x + dx},${y + dy}`;
+ if (this.scene.terrainSystem.decorationsMap.has(key)) {
+ const decoration = this.scene.terrainSystem.decorationsMap.get(key);
+ const direction = this.getDirection(dx, dy);
+ text += `${decoration.type} ${direction}. `;
+ foundObjects = true;
+ }
+ }
+ }
+
+ if (!foundObjects) {
+ text += 'Nothing nearby.';
+ }
+
+ this.speak(text);
+ }
+
+ /**
+ * Get direction description
+ */
+ getDirection(dx, dy) {
+ if (dx === 0 && dy < 0) return 'north';
+ if (dx === 0 && dy > 0) return 'south';
+ if (dx < 0 && dy === 0) return 'west';
+ if (dx > 0 && dy === 0) return 'east';
+ if (dx < 0 && dy < 0) return 'northwest';
+ if (dx > 0 && dy < 0) return 'northeast';
+ if (dx < 0 && dy > 0) return 'southwest';
+ if (dx > 0 && dy > 0) return 'southeast';
+ return 'nearby';
+ }
+
+ /**
+ * Announce help
+ */
+ announceHelp() {
+ const text = 'Screen reader help. ' +
+ 'Press Ctrl H for help. ' +
+ 'Press Ctrl R to repeat last announcement. ' +
+ 'Press Ctrl S for settings. ' +
+ 'Press Ctrl P for position. ' +
+ 'Press Ctrl I for inventory. ' +
+ 'Press Ctrl N for nearby objects. ' +
+ 'Press Ctrl T for stats. ' +
+ 'Press Ctrl V to toggle verbose mode.';
+
+ this.speak(text, 'alert', true);
+ }
+
+ /**
+ * Announce settings
+ */
+ announceSettings() {
+ const text = `Screen reader settings. ` +
+ `Speed: ${this.settings.rate}. ` +
+ `Pitch: ${this.settings.pitch}. ` +
+ `Volume: ${this.settings.volume}. ` +
+ `Verbose mode: ${this.settings.verboseMode ? 'on' : 'off'}. ` +
+ `Sound cues: ${this.settings.soundCues ? 'on' : 'off'}.`;
+
+ this.speak(text);
+ }
+
+ /**
+ * Announce action
+ */
+ announceAction(action, details = '') {
+ const actionDescriptions = {
+ 'move': 'Moving',
+ 'attack': 'Attacking',
+ 'interact': 'Interacting',
+ 'pickup': 'Picked up',
+ 'drop': 'Dropped',
+ 'craft': 'Crafted',
+ 'build': 'Built',
+ 'harvest': 'Harvested',
+ 'plant': 'Planted',
+ 'dig': 'Digging',
+ 'damage': 'Took damage',
+ 'heal': 'Healed',
+ 'die': 'You died',
+ 'respawn': 'Respawned'
+ };
+
+ const description = actionDescriptions[action] || action;
+ const text = details ? `${description}: ${details}` : description;
+
+ this.speak(text);
+ this.playAudioCue(action);
+ }
+
+ /**
+ * Announce UI change
+ */
+ announceUI(element, state = '') {
+ if (!this.settings.autoNarrate) return;
+
+ const text = state ? `${element}: ${state}` : element;
+ this.speak(text);
+ this.playAudioCue('navigation');
+ }
+
+ /**
+ * Announce notification
+ */
+ announceNotification(message, priority = 'normal') {
+ this.speak(message, priority);
+ this.playAudioCue(priority === 'alert' ? 'error' : 'success');
+ }
+
+ /**
+ * Set speech rate
+ */
+ setRate(rate) {
+ this.settings.rate = Phaser.Math.Clamp(rate, 0.1, 10);
+ this.saveSettings();
+ this.speak(`Speech rate set to ${this.settings.rate}`);
+ }
+
+ /**
+ * Set speech pitch
+ */
+ setPitch(pitch) {
+ this.settings.pitch = Phaser.Math.Clamp(pitch, 0, 2);
+ this.saveSettings();
+ this.speak(`Speech pitch set to ${this.settings.pitch}`);
+ }
+
+ /**
+ * Set speech volume
+ */
+ setVolume(volume) {
+ this.settings.volume = Phaser.Math.Clamp(volume, 0, 1);
+ this.saveSettings();
+ this.speak(`Speech volume set to ${Math.round(this.settings.volume * 100)} percent`);
+ }
+
+ /**
+ * Toggle verbose mode
+ */
+ toggleVerboseMode() {
+ this.settings.verboseMode = !this.settings.verboseMode;
+ this.saveSettings();
+ this.speak(`Verbose mode ${this.settings.verboseMode ? 'enabled' : 'disabled'}`);
+ }
+
+ /**
+ * Toggle sound cues
+ */
+ toggleSoundCues() {
+ this.settings.soundCues = !this.settings.soundCues;
+ this.saveSettings();
+ this.speak(`Sound cues ${this.settings.soundCues ? 'enabled' : 'disabled'}`);
+ }
+
+ /**
+ * Toggle auto-narrate
+ */
+ toggleAutoNarrate() {
+ this.settings.autoNarrate = !this.settings.autoNarrate;
+ this.saveSettings();
+ this.speak(`Auto narration ${this.settings.autoNarrate ? 'enabled' : 'disabled'}`);
+ }
+
+ /**
+ * Get available voices
+ */
+ getAvailableVoices() {
+ return this.voices.map(v => ({
+ name: v.name,
+ lang: v.lang,
+ default: v.default,
+ localService: v.localService
+ }));
+ }
+
+ /**
+ * Set voice by name
+ */
+ setVoice(voiceName) {
+ const voice = this.voices.find(v => v.name === voiceName);
+ if (voice) {
+ this.voice = voice;
+ this.saveSettings();
+ this.speak(`Voice changed to ${voice.name}`);
+ }
+ }
+
+ /**
+ * Save settings to localStorage
+ */
+ saveSettings() {
+ localStorage.setItem('novafarma_screen_reader', JSON.stringify(this.settings));
+ }
+
+ /**
+ * Load settings from localStorage
+ */
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_screen_reader');
+ if (saved) {
+ try {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ console.log('โ
Screen reader settings loaded');
+ } catch (error) {
+ console.error('โ Failed to load screen reader settings:', error);
+ }
+ }
+ }
+
+ /**
+ * Update (called every frame)
+ */
+ update() {
+ // Auto-announce important changes
+ if (this.settings.autoNarrate) {
+ // Check for low health
+ if (this.scene.statsSystem && this.scene.statsSystem.health < 20) {
+ if (!this.lowHealthWarned) {
+ this.speak('Warning: Low health!', 'alert');
+ this.playAudioCue('damage');
+ this.lowHealthWarned = true;
+ }
+ } else {
+ this.lowHealthWarned = false;
+ }
+ }
+ }
+
+ /**
+ * Destroy system
+ */
+ destroy() {
+ this.stop();
+ if (this.liveRegion) this.liveRegion.remove();
+ if (this.alertRegion) this.alertRegion.remove();
+ console.log('๐ Screen Reader System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SkillTreeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/SkillTreeSystem.js
new file mode 100644
index 000000000..15bea7d24
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SkillTreeSystem.js
@@ -0,0 +1,484 @@
+/**
+ * SKILL TREE SYSTEM
+ * Player progression with farming, combat, and survival branches
+ */
+class SkillTreeSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Player stats
+ this.playerLevel = 1;
+ this.playerXP = 0;
+ this.skillPoints = 0;
+
+ // Skill trees
+ this.skills = {
+ farming: new Map(),
+ combat: new Map(),
+ survival: new Map()
+ };
+
+ // Active abilities
+ this.activeAbilities = new Map();
+ this.abilityCooldowns = new Map();
+
+ // Settings
+ this.settings = {
+ xpPerLevel: 100,
+ xpScaling: 1.5, // Each level requires 50% more XP
+ skillPointsPerLevel: 1,
+ maxLevel: 50
+ };
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Skill Tree System initialized');
+ }
+
+ init() {
+ this.defineSkills();
+ console.log('๐ณ Skill trees ready');
+ }
+
+ // ========== SKILL DEFINITIONS ==========
+
+ defineSkills() {
+ // FARMING TREE
+ this.defineSkill('farming', 'green_thumb', {
+ name: 'Green Thumb',
+ description: 'Crops grow 10% faster',
+ maxLevel: 5,
+ cost: 1,
+ requires: null,
+ bonus: { type: 'crop_speed', value: 0.1 }
+ });
+
+ this.defineSkill('farming', 'efficient_harvest', {
+ name: 'Efficient Harvest',
+ description: '+20% crop yield',
+ maxLevel: 3,
+ cost: 1,
+ requires: 'green_thumb',
+ bonus: { type: 'crop_yield', value: 0.2 }
+ });
+
+ this.defineSkill('farming', 'area_harvest', {
+ name: 'Area Harvest',
+ description: 'Harvest 3x3 area at once',
+ maxLevel: 1,
+ cost: 2,
+ requires: 'efficient_harvest',
+ bonus: { type: 'ability', value: 'area_harvest' }
+ });
+
+ this.defineSkill('farming', 'master_farmer', {
+ name: 'Master Farmer',
+ description: 'Crops never wither',
+ maxLevel: 1,
+ cost: 3,
+ requires: 'area_harvest',
+ bonus: { type: 'crop_immortal', value: true }
+ });
+
+ // COMBAT TREE
+ this.defineSkill('combat', 'strength', {
+ name: 'Strength',
+ description: '+15% melee damage',
+ maxLevel: 5,
+ cost: 1,
+ requires: null,
+ bonus: { type: 'melee_damage', value: 0.15 }
+ });
+
+ this.defineSkill('combat', 'critical_strike', {
+ name: 'Critical Strike',
+ description: '10% chance for 2x damage',
+ maxLevel: 3,
+ cost: 1,
+ requires: 'strength',
+ bonus: { type: 'crit_chance', value: 0.1 }
+ });
+
+ this.defineSkill('combat', 'dash', {
+ name: 'Dash',
+ description: 'Quick dash ability (cooldown: 5s)',
+ maxLevel: 1,
+ cost: 2,
+ requires: 'strength',
+ bonus: { type: 'ability', value: 'dash' }
+ });
+
+ this.defineSkill('combat', 'berserker', {
+ name: 'Berserker',
+ description: '+50% damage when below 30% HP',
+ maxLevel: 1,
+ cost: 3,
+ requires: 'critical_strike',
+ bonus: { type: 'berserker', value: 0.5 }
+ });
+
+ // SURVIVAL TREE
+ this.defineSkill('survival', 'vitality', {
+ name: 'Vitality',
+ description: '+20 max health',
+ maxLevel: 5,
+ cost: 1,
+ requires: null,
+ bonus: { type: 'max_health', value: 20 }
+ });
+
+ this.defineSkill('survival', 'regeneration', {
+ name: 'Regeneration',
+ description: 'Heal 1 HP every 5 seconds',
+ maxLevel: 3,
+ cost: 1,
+ requires: 'vitality',
+ bonus: { type: 'health_regen', value: 1 }
+ });
+
+ this.defineSkill('survival', 'iron_skin', {
+ name: 'Iron Skin',
+ description: 'Reduce damage taken by 15%',
+ maxLevel: 3,
+ cost: 1,
+ requires: 'vitality',
+ bonus: { type: 'damage_reduction', value: 0.15 }
+ });
+
+ this.defineSkill('survival', 'second_wind', {
+ name: 'Second Wind',
+ description: 'Survive fatal damage once (cooldown: 60s)',
+ maxLevel: 1,
+ cost: 3,
+ requires: 'regeneration',
+ bonus: { type: 'ability', value: 'second_wind' }
+ });
+ }
+
+ defineSkill(tree, id, data) {
+ this.skills[tree].set(id, {
+ id,
+ tree,
+ currentLevel: 0,
+ ...data
+ });
+ }
+
+ // ========== SKILL MANAGEMENT ==========
+
+ unlockSkill(tree, skillId) {
+ const skill = this.skills[tree].get(skillId);
+ if (!skill) return false;
+
+ // Check requirements
+ if (skill.requires) {
+ const required = this.skills[tree].get(skill.requires);
+ if (!required || required.currentLevel === 0) {
+ console.log('โ Requirement not met:', skill.requires);
+ return false;
+ }
+ }
+
+ // Check skill points
+ if (this.skillPoints < skill.cost) {
+ console.log('โ Not enough skill points');
+ return false;
+ }
+
+ // Check max level
+ if (skill.currentLevel >= skill.maxLevel) {
+ console.log('โ Skill already maxed');
+ return false;
+ }
+
+ // Unlock skill
+ skill.currentLevel++;
+ this.skillPoints -= skill.cost;
+
+ // Apply bonus
+ this.applySkillBonus(skill);
+
+ // Unlock achievement
+ if (this.scene.uiGraphics) {
+ if (skill.currentLevel === skill.maxLevel) {
+ this.scene.uiGraphics.unlockAchievement('skill_master');
+ }
+ }
+
+ this.saveProgress();
+ console.log(`โ
Unlocked: ${skill.name} (Level ${skill.currentLevel})`);
+ return true;
+ }
+
+ applySkillBonus(skill) {
+ const bonus = skill.bonus;
+
+ switch (bonus.type) {
+ case 'ability':
+ this.activeAbilities.set(bonus.value, {
+ name: skill.name,
+ cooldown: 0,
+ ready: true
+ });
+ break;
+
+ case 'max_health':
+ if (this.scene.statsSystem) {
+ this.scene.statsSystem.maxHealth += bonus.value;
+ }
+ break;
+
+ case 'melee_damage':
+ case 'crit_chance':
+ case 'damage_reduction':
+ case 'crop_speed':
+ case 'crop_yield':
+ // These are passive bonuses checked when needed
+ break;
+ }
+ }
+
+ // ========== EXPERIENCE & LEVELING ==========
+
+ addXP(amount) {
+ this.playerXP += amount;
+
+ // Check for level up
+ const xpNeeded = this.getXPForLevel(this.playerLevel + 1);
+ if (this.playerXP >= xpNeeded && this.playerLevel < this.settings.maxLevel) {
+ this.levelUp();
+ }
+
+ this.saveProgress();
+ }
+
+ getXPForLevel(level) {
+ return Math.floor(
+ this.settings.xpPerLevel * Math.pow(this.settings.xpScaling, level - 1)
+ );
+ }
+
+ levelUp() {
+ this.playerLevel++;
+ this.skillPoints += this.settings.skillPointsPerLevel;
+
+ // 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);
+ }
+ }
+
+ // Notification
+ if (this.scene.screenReader) {
+ this.scene.screenReader.speak(`Level up! You are now level ${this.playerLevel}!`);
+ }
+
+ console.log(`๐ Level Up! Now level ${this.playerLevel}`);
+ this.saveProgress();
+ }
+
+ // ========== ACTIVE ABILITIES ==========
+
+ useAbility(abilityName) {
+ const ability = this.activeAbilities.get(abilityName);
+ if (!ability || !ability.ready) return false;
+
+ switch (abilityName) {
+ case 'dash':
+ this.performDash();
+ this.setAbilityCooldown(abilityName, 5000);
+ break;
+
+ case 'area_harvest':
+ this.performAreaHarvest();
+ this.setAbilityCooldown(abilityName, 10000);
+ break;
+
+ case 'second_wind':
+ // Triggered automatically on fatal damage
+ break;
+ }
+
+ return true;
+ }
+
+ performDash() {
+ if (!this.scene.player) return;
+
+ const player = this.scene.player;
+ const direction = player.facing || 'down';
+ const distance = 3;
+
+ let dx = 0, dy = 0;
+ switch (direction) {
+ case 'up': dy = -distance; break;
+ case 'down': dy = distance; break;
+ case 'left': dx = -distance; break;
+ case 'right': dx = distance; break;
+ }
+
+ const pos = player.getPosition();
+ player.setPosition(pos.x + dx, pos.y + dy);
+
+ // Dash effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(pos.x, pos.y);
+ }
+
+ console.log('๐จ Dash!');
+ }
+
+ performAreaHarvest() {
+ if (!this.scene.player || !this.scene.farmingSystem) return;
+
+ const pos = this.scene.player.getPosition();
+ const x = Math.floor(pos.x);
+ const y = Math.floor(pos.y);
+
+ // Harvest 3x3 area
+ let harvested = 0;
+ for (let dy = -1; dy <= 1; dy++) {
+ for (let dx = -1; dx <= 1; dx++) {
+ if (this.scene.farmingSystem.harvestCrop(x + dx, y + dy)) {
+ harvested++;
+ }
+ }
+ }
+
+ if (harvested > 0) {
+ console.log(`๐พ Area harvest: ${harvested} crops`);
+ }
+ }
+
+ setAbilityCooldown(abilityName, duration) {
+ const ability = this.activeAbilities.get(abilityName);
+ if (!ability) return;
+
+ ability.ready = false;
+ ability.cooldown = duration;
+
+ setTimeout(() => {
+ ability.ready = true;
+ ability.cooldown = 0;
+ }, duration);
+ }
+
+ // ========== SKILL BONUSES ==========
+
+ getSkillBonus(bonusType) {
+ let total = 0;
+
+ for (const tree of Object.values(this.skills)) {
+ for (const skill of tree.values()) {
+ if (skill.currentLevel > 0 && skill.bonus.type === bonusType) {
+ total += skill.bonus.value * skill.currentLevel;
+ }
+ }
+ }
+
+ return total;
+ }
+
+ hasSkill(tree, skillId) {
+ const skill = this.skills[tree].get(skillId);
+ return skill && skill.currentLevel > 0;
+ }
+
+ // ========== SKILL RESET ==========
+
+ resetSkills(cost = 1000) {
+ // Check if player has enough gold
+ if (this.scene.inventorySystem && this.scene.inventorySystem.gold < cost) {
+ console.log('โ Not enough gold to reset skills');
+ return false;
+ }
+
+ // Deduct cost
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.gold -= cost;
+ }
+
+ // Reset all skills
+ let pointsRefunded = 0;
+ for (const tree of Object.values(this.skills)) {
+ for (const skill of tree.values()) {
+ pointsRefunded += skill.currentLevel * skill.cost;
+ skill.currentLevel = 0;
+ }
+ }
+
+ this.skillPoints = pointsRefunded;
+ this.activeAbilities.clear();
+
+ this.saveProgress();
+ console.log(`๐ Skills reset! ${pointsRefunded} points refunded`);
+ return true;
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ level: this.playerLevel,
+ xp: this.playerXP,
+ skillPoints: this.skillPoints,
+ skills: {}
+ };
+
+ // Save skill levels
+ for (const [treeName, tree] of Object.entries(this.skills)) {
+ data.skills[treeName] = {};
+ for (const [skillId, skill] of tree.entries()) {
+ if (skill.currentLevel > 0) {
+ data.skills[treeName][skillId] = skill.currentLevel;
+ }
+ }
+ }
+
+ localStorage.setItem('novafarma_skill_tree', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_skill_tree');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.playerLevel = data.level || 1;
+ this.playerXP = data.xp || 0;
+ this.skillPoints = data.skillPoints || 0;
+
+ // Load skill levels after skills are defined
+ this.savedSkills = data.skills || {};
+ console.log('โ
Skill progress loaded');
+ } catch (error) {
+ console.error('Failed to load skill progress:', error);
+ }
+ }
+ }
+
+ applyLoadedProgress() {
+ if (!this.savedSkills) return;
+
+ for (const [treeName, skills] of Object.entries(this.savedSkills)) {
+ for (const [skillId, level] of Object.entries(skills)) {
+ const skill = this.skills[treeName]?.get(skillId);
+ if (skill) {
+ skill.currentLevel = level;
+ this.applySkillBonus(skill);
+ }
+ }
+ }
+
+ this.savedSkills = null;
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐ณ Skill Tree System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SleepSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/SleepSystem.js
new file mode 100644
index 000000000..370a373ea
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SleepSystem.js
@@ -0,0 +1,384 @@
+/**
+ * SLEEP SYSTEM - Energy Regeneration & Bed Upgrades
+ * Part of: Home Upgrade & Customization System
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - 3-tier bed system (sleeping bag โ wooden โ king-size)
+ * - Energy regeneration based on bed quality
+ * - Time skip mechanics (8 hours sleep)
+ * - Partner bonuses for married players
+ * - Dream sequences and nightmares
+ */
+
+class SleepSystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+ this.isSleeping = false;
+ this.currentBed = null;
+
+ // Bed types configuration
+ this.bedTypes = {
+ SLEEPING_BAG: {
+ id: 'sleeping_bag',
+ name: 'Sleeping Bag',
+ cost: 0,
+ unlocked: true,
+ energyRegen: 50, // Restores 50% energy
+ sleepDuration: 8, // hours
+ dreamChance: 0, // No dreams
+ nightmareChance: 0.1, // 10% nightmare chance
+ partnerBonus: false,
+ sprite: 'interior_bed_sleepingbag.png',
+ description: 'A basic sleeping bag. Uncomfortable but functional.'
+ },
+ WOODEN_BED: {
+ id: 'wooden_bed',
+ name: 'Wooden Bed',
+ cost: 2000,
+ requirements: {
+ farmhouseLevel: 1
+ },
+ unlocked: false,
+ energyRegen: 90, // Restores 90% energy
+ sleepDuration: 8,
+ dreamChance: 0.2, // 20% dream chance
+ nightmareChance: 0.05, // 5% nightmare chance
+ partnerBonus: false,
+ sprite: 'interior_bed_wooden.png',
+ description: 'A comfortable wooden bed. Much better than the floor!'
+ },
+ KING_SIZE_BED: {
+ id: 'king_size',
+ name: 'King-size Bed',
+ cost: 10000,
+ requirements: {
+ farmhouseLevel: 2,
+ married: true
+ },
+ unlocked: false,
+ energyRegen: 100, // Restores 100% energy
+ sleepDuration: 8,
+ dreamChance: 0.3, // 30% dream chance
+ nightmareChance: 0, // No nightmares!
+ partnerBonus: true, // +50 relationship points
+ spousePresent: false,
+ sprite: 'interior_bed_kingsize.png',
+ description: 'A luxurious king-size bed. Perfect for two!'
+ }
+ };
+
+ // Current player's bed (starts with sleeping bag)
+ this.playerBed = { ...this.bedTypes.SLEEPING_BAG };
+
+ // Sleep state
+ this.sleepStartTime = null;
+ this.sleepEndTime = null;
+ this.dreamSequence = null;
+ }
+
+ /**
+ * Check if player can use a specific bed type
+ */
+ canUseBed(bedType) {
+ const bed = this.bedTypes[bedType];
+
+ // Check if unlocked
+ if (!bed.unlocked && bedType !== 'SLEEPING_BAG') {
+ return { canUse: false, reason: 'Bed not purchased yet' };
+ }
+
+ // Check requirements
+ if (bed.requirements) {
+ if (bed.requirements.farmhouseLevel &&
+ this.player.farmhouseLevel < bed.requirements.farmhouseLevel) {
+ return {
+ canUse: false,
+ reason: `Requires Farmhouse Level ${bed.requirements.farmhouseLevel}`
+ };
+ }
+
+ if (bed.requirements.married && !this.player.isMarried) {
+ return {
+ canUse: false,
+ reason: 'Requires marriage'
+ };
+ }
+ }
+
+ return { canUse: true };
+ }
+
+ /**
+ * Purchase a bed upgrade
+ */
+ purchaseBed(bedType) {
+ const bed = this.bedTypes[bedType];
+
+ // Check if can purchase
+ if (bed.unlocked) {
+ return { success: false, message: 'You already own this bed!' };
+ }
+
+ // Check cost
+ if (this.player.money < bed.cost) {
+ return {
+ success: false,
+ message: `Not enough money! Need ${bed.cost}g`
+ };
+ }
+
+ // Check requirements
+ const canUse = this.canUseBed(bedType);
+ if (!canUse.canUse) {
+ return { success: false, message: canUse.reason };
+ }
+
+ // Purchase bed
+ this.player.money -= bed.cost;
+ bed.unlocked = true;
+ this.playerBed = { ...bed };
+
+ // Trigger furniture placement
+ this.game.emit('bedPurchased', {
+ bedType: bedType,
+ bed: bed
+ });
+
+ return {
+ success: true,
+ message: `Purchased ${bed.name} for ${bed.cost}g!`
+ };
+ }
+
+ /**
+ * Initiate sleep sequence
+ */
+ sleep() {
+ // Check if already sleeping
+ if (this.isSleeping) {
+ return { success: false, message: 'Already sleeping!' };
+ }
+
+ // Check if tired enough (energy < 50%)
+ if (this.player.energy > this.player.maxEnergy * 0.5) {
+ return {
+ success: false,
+ message: "You're not tired enough to sleep yet."
+ };
+ }
+
+ // Start sleep
+ this.isSleeping = true;
+ this.sleepStartTime = this.game.time.currentTime;
+ this.sleepEndTime = this.game.time.currentTime + (this.playerBed.sleepDuration * 3600); // Convert to seconds
+
+ // Roll for dream/nightmare
+ this.dreamSequence = this.rollForDream();
+
+ // Trigger sleep animation
+ this.game.emit('sleepStarted', {
+ bed: this.playerBed,
+ duration: this.playerBed.sleepDuration,
+ dreamSequence: this.dreamSequence
+ });
+
+ // Fast-forward time
+ this.game.time.skipTime(this.playerBed.sleepDuration);
+
+ // Wake up after duration
+ setTimeout(() => {
+ this.wakeUp();
+ }, 100); // Instant wake-up after time skip
+
+ return { success: true };
+ }
+
+ /**
+ * Roll for dream or nightmare
+ */
+ rollForDream() {
+ const rand = Math.random();
+
+ // Check nightmare first
+ if (rand < this.playerBed.nightmareChance) {
+ return {
+ type: 'nightmare',
+ sequence: this.getRandomNightmare()
+ };
+ }
+
+ // Check dream
+ if (rand < this.playerBed.dreamChance + this.playerBed.nightmareChance) {
+ return {
+ type: 'dream',
+ sequence: this.getRandomDream()
+ };
+ }
+
+ return null; // No dream
+ }
+
+ /**
+ * Get random dream sequence
+ */
+ getRandomDream() {
+ const dreams = [
+ {
+ id: 'memory_ana',
+ title: 'Memory of Ana',
+ description: 'You dream of Ana, smiling in the sunlight...',
+ effect: { mood: +10 }
+ },
+ {
+ id: 'farming_success',
+ title: 'Bountiful Harvest',
+ description: 'You dream of your crops growing tall and strong.',
+ effect: { farming_xp: 50 }
+ },
+ {
+ id: 'zombie_peace',
+ title: 'Peaceful Workers',
+ description: 'Your zombie workers are happy and productive.',
+ effect: { loyalty: +5 }
+ }
+ ];
+
+ return dreams[Math.floor(Math.random() * dreams.length)];
+ }
+
+ /**
+ * Get random nightmare sequence
+ */
+ getRandomNightmare() {
+ const nightmares = [
+ {
+ id: 'ana_loss',
+ title: 'The Loss',
+ description: 'Fragments of that terrible day flash before you...',
+ effect: { mood: -20 }
+ },
+ {
+ id: 'zombie_attack',
+ title: 'Undead Uprising',
+ description: 'Your workers turn hostile, advancing slowly...',
+ effect: { energy: -10 }
+ },
+ {
+ id: 'crop_failure',
+ title: 'Withered Fields',
+ description: 'Your entire farm crumbles to dust.',
+ effect: { mood: -10 }
+ }
+ ];
+
+ return nightmares[Math.floor(Math.random() * nightmares.length)];
+ }
+
+ /**
+ * Wake up from sleep
+ */
+ wakeUp() {
+ if (!this.isSleeping) return;
+
+ // Calculate energy restoration
+ const energyRestored = (this.player.maxEnergy * this.playerBed.energyRegen) / 100;
+ this.player.energy = Math.min(
+ this.player.maxEnergy,
+ this.player.energy + energyRestored
+ );
+
+ // Partner bonus (if married & king-size bed)
+ if (this.playerBed.partnerBonus && this.player.isMarried) {
+ const spouse = this.game.npcs.getSpouse();
+ if (spouse) {
+ spouse.addRelationshipPoints(50);
+ this.game.showMessage(`${spouse.name} cuddles closer. +50 โค๏ธ`);
+ }
+ }
+
+ // Apply dream/nightmare effects
+ if (this.dreamSequence) {
+ this.applyDreamEffects(this.dreamSequence);
+ }
+
+ // Update state
+ this.isSleeping = false;
+ this.currentBed = null;
+ this.dreamSequence = null;
+
+ // Trigger wake-up event
+ this.game.emit('wakeUp', {
+ energyRestored: energyRestored,
+ newEnergy: this.player.energy,
+ time: this.game.time.currentTime
+ });
+
+ // New day announcements
+ this.game.showMessage(`Good morning! Energy restored: ${Math.floor(energyRestored)}`);
+ }
+
+ /**
+ * Apply dream/nightmare effects
+ */
+ applyDreamEffects(dreamSequence) {
+ const sequence = dreamSequence.sequence;
+
+ // Show dream/nightmare message
+ const typeIcon = dreamSequence.type === 'dream' ? 'โจ' : '๐';
+ this.game.showMessage(`${typeIcon} ${sequence.title}: ${sequence.description}`);
+
+ // Apply effects
+ if (sequence.effect.mood) {
+ this.player.mood += sequence.effect.mood;
+ }
+ if (sequence.effect.energy) {
+ this.player.energy += sequence.effect.energy;
+ }
+ if (sequence.effect.farming_xp) {
+ this.player.addXP('farming', sequence.effect.farming_xp);
+ }
+ if (sequence.effect.loyalty && this.game.zombieWorkers) {
+ this.game.zombieWorkers.addGlobalLoyalty(sequence.effect.loyalty);
+ }
+ }
+
+ /**
+ * Check if it's a good time to sleep
+ */
+ canSleepNow() {
+ const hour = this.game.time.getHour();
+
+ // Can sleep between 8 PM (20:00) and 2 AM (02:00)
+ if ((hour >= 20 && hour <= 23) || (hour >= 0 && hour <= 2)) {
+ return { canSleep: true };
+ }
+
+ return {
+ canSleep: false,
+ message: "It's not nighttime yet. Sleep between 8 PM - 2 AM."
+ };
+ }
+
+ /**
+ * Get current bed info for UI
+ */
+ getCurrentBedInfo() {
+ return {
+ ...this.playerBed,
+ isSleeping: this.isSleeping,
+ canSleep: this.canSleepNow().canSleep
+ };
+ }
+
+ /**
+ * Update (called every frame)
+ */
+ update(deltaTime) {
+ // Sleep system update logic (if needed)
+ // Currently handled by events and timers
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SlimesDogsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/SlimesDogsSystem.js
new file mode 100644
index 000000000..4c6015ae2
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SlimesDogsSystem.js
@@ -0,0 +1,641 @@
+/**
+ * SLIMES & DOGS SYSTEM
+ * Manages 8 slime types, King Slime boss, slime gel crafting, and dog companion.
+ *
+ * Features:
+ * - 8 Slime Types: Green, Blue, Red, Yellow, Purple, Black, Rainbow, King Slime
+ * - Slime Gel Crafting: 8 unique gel uses (potions, coatings, weapons)
+ * - Dog Companion: 6 abilities (item detection, combat, tracking, album helper, pathfinding, revive)
+ * - Dog Loyalty: 10-heart system, equipment (armor, backpack +10 slots)
+ */
+class SlimesDogsSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Slime types
+ this.slimeTypes = {
+ green: {
+ name: 'Green Slime',
+ health: 50,
+ damage: 5,
+ speed: 0.5,
+ drops: { green_gel: { min: 1, max: 3, chance: 1.0 } },
+ color: '#00FF00',
+ abilities: ['bounce'],
+ spawns: ['forest', 'swamp']
+ },
+ blue: {
+ name: 'Blue Slime',
+ health: 70,
+ damage: 8,
+ speed: 0.6,
+ drops: { blue_gel: { min: 1, max: 3, chance: 1.0 } },
+ color: '#0088FF',
+ abilities: ['freeze_touch'],
+ spawns: ['ice_biome', 'crystal_caves']
+ },
+ red: {
+ name: 'Red Slime',
+ health: 80,
+ damage: 12,
+ speed: 0.7,
+ drops: { red_gel: { min: 1, max: 3, chance: 1.0 } },
+ color: '#FF0000',
+ abilities: ['fire_trail'],
+ spawns: ['volcano', 'wasteland']
+ },
+ yellow: {
+ name: 'Yellow Slime',
+ health: 60,
+ damage: 10,
+ speed: 0.8,
+ drops: { yellow_gel: { min: 1, max: 3, chance: 1.0 } },
+ color: '#FFFF00',
+ abilities: ['electric_shock'],
+ spawns: ['plains', 'desert']
+ },
+ purple: {
+ name: 'Purple Slime',
+ health: 90,
+ damage: 15,
+ speed: 0.5,
+ drops: { purple_gel: { min: 2, max: 4, chance: 0.8 } },
+ color: '#AA00FF',
+ abilities: ['poison_cloud'],
+ spawns: ['swamp', 'dark_forest']
+ },
+ black: {
+ name: 'Black Slime',
+ health: 120,
+ damage: 20,
+ speed: 0.6,
+ drops: { black_gel: { min: 2, max: 5, chance: 0.6 } },
+ color: '#000000',
+ abilities: ['shadow_merge', 'darkness_aura'],
+ spawns: ['chernobyl', 'dark_fortress']
+ },
+ rainbow: {
+ name: 'Rainbow Slime',
+ health: 150,
+ damage: 25,
+ speed: 0.9,
+ drops: { rainbow_gel: { min: 3, max: 6, chance: 0.3 } },
+ color: '#FF00FF',
+ abilities: ['color_shift', 'prism_blast'],
+ spawns: ['atlantis', 'crystal_caves'],
+ rare: true
+ },
+ king: {
+ name: 'King Slime',
+ health: 3000,
+ damage: 50,
+ speed: 0.4,
+ drops: {
+ king_gel: { min: 10, max: 20, chance: 1.0 },
+ crown: { min: 1, max: 1, chance: 1.0 }
+ },
+ color: '#FFD700',
+ abilities: ['split', 'summon_slimes', 'giant_bounce', 'gel_wave'],
+ spawns: ['slime_cavern'],
+ boss: true,
+ splitCount: 4 // Splits into 4 smaller slimes
+ }
+ };
+
+ // Slime gel uses
+ this.gelUses = {
+ green_gel: [
+ { type: 'potion', name: 'Healing Potion', effect: 'heal_50hp' },
+ { type: 'coating', name: 'Sticky Coating', effect: 'slow_enemies' },
+ { type: 'fertilizer', name: 'Slime Fertilizer', effect: 'crop_growth_2x' }
+ ],
+ blue_gel: [
+ { type: 'potion', name: 'Ice Resistance Potion', effect: 'ice_immunity_5min' },
+ { type: 'coating', name: 'Freeze Coating', effect: 'freeze_on_hit' },
+ { type: 'weapon', name: 'Frost Bomb', effect: 'freeze_area' }
+ ],
+ red_gel: [
+ { type: 'potion', name: 'Fire Resistance Potion', effect: 'fire_immunity_5min' },
+ { type: 'coating', name: 'Flame Coating', effect: 'burn_on_hit' },
+ { type: 'weapon', name: 'Molotov', effect: 'fire_area' }
+ ],
+ yellow_gel: [
+ { type: 'potion', name: 'Energy Potion', effect: 'speed_boost_20%' },
+ { type: 'coating', name: 'Shock Coating', effect: 'stun_on_hit' },
+ { type: 'light', name: 'Glowstick', effect: 'light_source' }
+ ],
+ purple_gel: [
+ { type: 'potion', name: 'Antidote', effect: 'cure_poison' },
+ { type: 'coating', name: 'Poison Coating', effect: 'poison_on_hit' },
+ { type: 'weapon', name: 'Poison Bomb', effect: 'poison_area' }
+ ],
+ black_gel: [
+ { type: 'potion', name: 'Shadow Potion', effect: 'invisibility_30sec' },
+ { type: 'coating', name: 'Shadow Coating', effect: 'dark_damage' },
+ { type: 'weapon', name: 'Void Bomb', effect: 'damage_absorb' }
+ ],
+ rainbow_gel: [
+ { type: 'potion', name: 'Rainbow Elixir', effect: 'all_stats_boost' },
+ { type: 'coating', name: 'Prismatic Coating', effect: 'random_elemental' },
+ { type: 'weapon', name: 'Spectrum Bomb', effect: 'multi_elemental' }
+ ],
+ king_gel: [
+ { type: 'potion', name: 'King\'s Blessing', effect: 'invincibility_10sec' },
+ { type: 'coating', name: 'Royal Coating', effect: 'triple_damage' },
+ { type: 'vehicle_fuel', name: 'Slime Fuel', effect: 'infinite_fuel_1hr' }
+ ]
+ };
+
+ // Dog companion
+ this.dog = null;
+
+ // Dog breeds
+ this.dogBreeds = {
+ retriever: { name: 'Golden Retriever', bonus: 'item_detection_range_2x' },
+ shepherd: { name: 'German Shepherd', bonus: 'combat_damage_50%' },
+ husky: { name: 'Siberian Husky', bonus: 'cold_immunity' },
+ corgi: { name: 'Corgi', bonus: 'loyalty_gain_2x' },
+ dalmatian: { name: 'Dalmatian', bonus: 'speed_20%' },
+ custom: { name: 'Custom Dog', bonus: 'balanced' }
+ };
+
+ // Active slimes in world
+ this.activeSlimes = new Map();
+
+ console.log('๐ข๐ถ Slimes & Dogs System initialized!');
+ }
+
+ /**
+ * Spawn a slime
+ */
+ spawnSlime(type, position) {
+ const slimeData = this.slimeTypes[type];
+ if (!slimeData) {
+ console.log(`โ Unknown slime type: ${type}`);
+ return null;
+ }
+
+ const slimeId = `slime_${type}_${Date.now()}`;
+
+ const slime = {
+ id: slimeId,
+ type: type,
+ name: slimeData.name,
+ health: slimeData.health,
+ maxHealth: slimeData.health,
+ damage: slimeData.damage,
+ speed: slimeData.speed,
+ position: position,
+ color: slimeData.color,
+ abilities: slimeData.abilities,
+ alive: true,
+ boss: slimeData.boss || false
+ };
+
+ this.activeSlimes.set(slimeId, slime);
+
+ console.log(`๐ข Spawned ${slimeData.name} at (${position.x}, ${position.y})`);
+
+ return slimeId;
+ }
+
+ /**
+ * Fight King Slime boss
+ */
+ fightKingSlime(position) {
+ const kingId = this.spawnSlime('king', position);
+
+ console.log('๐ BOSS FIGHT: King Slime!');
+
+ this.scene.events.emit('start-boss-fight', {
+ boss: this.activeSlimes.get(kingId),
+ arena: 'slime_cavern',
+ music: 'epic_slime',
+ mechanic: 'split'
+ });
+
+ return kingId;
+ }
+
+ /**
+ * King Slime split mechanic
+ */
+ splitKingSlime(kingId, position) {
+ const king = this.activeSlimes.get(kingId);
+ if (!king || king.type !== 'king') return [];
+
+ console.log('๐ฅ King Slime SPLITS!');
+
+ const spawnedSlimes = [];
+ const splitCount = this.slimeTypes.king.splitCount;
+
+ // Spawn smaller slimes in circle around king
+ for (let i = 0; i < splitCount; i++) {
+ const angle = (Math.PI * 2 / splitCount) * i;
+ const spawnPos = {
+ x: position.x + Math.cos(angle) * 100,
+ y: position.y + Math.sin(angle) * 100
+ };
+
+ // Spawn random colored slime
+ const slimeTypes = ['green', 'blue', 'red', 'yellow'];
+ const randomType = slimeTypes[Math.floor(Math.random() * slimeTypes.length)];
+
+ const slimeId = this.spawnSlime(randomType, spawnPos);
+ spawnedSlimes.push(slimeId);
+ }
+
+ this.scene.events.emit('notification', {
+ title: 'King Slime Split!',
+ message: `${splitCount} slimes spawned!`,
+ icon: '๐ฅ'
+ });
+
+ return spawnedSlimes;
+ }
+
+ /**
+ * Defeat a slime and get drops
+ */
+ defeatSlime(slimeId) {
+ const slime = this.activeSlimes.get(slimeId);
+ if (!slime) return null;
+
+ slime.alive = false;
+
+ const slimeData = this.slimeTypes[slime.type];
+ const drops = this.rollDrops(slimeData.drops);
+
+ console.log(`๐ ${slime.name} defeated! Drops:`, drops);
+
+ // Spawn loot
+ drops.forEach(drop => {
+ if (this.scene.interactionSystem) {
+ this.scene.interactionSystem.spawnLoot(
+ slime.position.x,
+ slime.position.y,
+ drop.item,
+ drop.amount
+ );
+ }
+ });
+
+ // Remove from active slimes
+ this.activeSlimes.delete(slimeId);
+
+ return drops;
+ }
+
+ /**
+ * Roll for drops
+ */
+ rollDrops(dropTable) {
+ const drops = [];
+
+ Object.keys(dropTable).forEach(item => {
+ const dropData = dropTable[item];
+
+ if (Math.random() < dropData.chance) {
+ const amount = Math.floor(
+ Math.random() * (dropData.max - dropData.min + 1) + dropData.min
+ );
+
+ drops.push({ item: item, amount: amount });
+ }
+ });
+
+ return drops;
+ }
+
+ /**
+ * Craft with slime gel
+ */
+ craftWithGel(gelType, recipeIndex) {
+ const recipes = this.gelUses[gelType];
+ if (!recipes || !recipes[recipeIndex]) {
+ console.log(`โ Invalid gel or recipe index!`);
+ return null;
+ }
+
+ const recipe = recipes[recipeIndex];
+
+ // Check if player has gel
+ if (this.scene.inventorySystem && !this.scene.inventorySystem.hasItem(gelType, 1)) {
+ console.log(`โ Not enough ${gelType}!`);
+ return null;
+ }
+
+ // Consume gel
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.removeItem(gelType, 1);
+ }
+
+ // Create item
+ console.log(`๐งช Crafted ${recipe.name}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Crafted!',
+ message: `${recipe.name} created!`,
+ icon: '๐งช'
+ });
+
+ return recipe;
+ }
+
+ /**
+ * Adopt a dog companion
+ */
+ adoptDog(breed = 'retriever', name = 'Rex') {
+ if (this.dog) {
+ console.log('โ You already have a dog companion!');
+ return null;
+ }
+
+ const breedData = this.dogBreeds[breed];
+ if (!breedData) {
+ console.log(`โ Unknown breed: ${breed}`);
+ return null;
+ }
+
+ this.dog = {
+ name: name,
+ breed: breed,
+ breedName: breedData.name,
+ bonus: breedData.bonus,
+ loyalty: 0, // 0-10 hearts
+ abilities: {
+ item_detection: true,
+ combat: false, // Unlocks at 3 hearts
+ tracking: false, // Unlocks at 5 hearts
+ album_helper: false, // Unlocks at 7 hearts
+ pathfinding: false, // Unlocks at 8 hearts
+ revive: false // Unlocks at 10 hearts
+ },
+ equipment: {
+ armor: null,
+ backpack: null // +10 inventory slots when equipped
+ },
+ health: 100,
+ maxHealth: 100,
+ alive: true,
+ position: { x: 0, y: 0 }
+ };
+
+ console.log(`๐ถ ${name} the ${breedData.name} joined you!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Dog Companion!',
+ message: `${name} the ${breedData.name} joined your adventure!`,
+ icon: '๐ถ'
+ });
+
+ return this.dog;
+ }
+
+ /**
+ * Increase dog loyalty
+ */
+ increaseLoyalty(amount = 1) {
+ if (!this.dog) return;
+
+ this.dog.loyalty = Math.min(10, this.dog.loyalty + amount);
+
+ console.log(`๐ ${this.dog.name}'s loyalty: ${this.dog.loyalty}/10 hearts`);
+
+ // Unlock abilities based on loyalty
+ this.unlockDogAbilities();
+
+ if (this.dog.loyalty === 10) {
+ this.scene.events.emit('notification', {
+ title: 'Maximum Loyalty!',
+ message: `${this.dog.name} can now REVIVE you!`,
+ icon: '๐'
+ });
+ }
+ }
+
+ /**
+ * Unlock dog abilities based on loyalty
+ */
+ unlockDogAbilities() {
+ if (!this.dog) return;
+
+ const loyalty = this.dog.loyalty;
+
+ if (loyalty >= 3 && !this.dog.abilities.combat) {
+ this.dog.abilities.combat = true;
+ console.log('โ๏ธ Combat ability unlocked!');
+ }
+
+ if (loyalty >= 5 && !this.dog.abilities.tracking) {
+ this.dog.abilities.tracking = true;
+ console.log('๐ Tracking ability unlocked!');
+ }
+
+ if (loyalty >= 7 && !this.dog.abilities.album_helper) {
+ this.dog.abilities.album_helper = true;
+ console.log('๐ Album Helper ability unlocked!');
+ }
+
+ if (loyalty >= 8 && !this.dog.abilities.pathfinding) {
+ this.dog.abilities.pathfinding = true;
+ console.log('๐บ๏ธ Pathfinding ability unlocked!');
+ }
+
+ if (loyalty >= 10 && !this.dog.abilities.revive) {
+ this.dog.abilities.revive = true;
+ console.log('๐ REVIVE ability unlocked!');
+ }
+ }
+
+ /**
+ * Equip armor on dog
+ */
+ equipDogArmor(armorType) {
+ if (!this.dog) return false;
+
+ const armors = {
+ leather: { defense: 10, name: 'Leather Armor' },
+ iron: { defense: 25, name: 'Iron Armor' },
+ diamond: { defense: 50, name: 'Diamond Armor' }
+ };
+
+ const armor = armors[armorType];
+ if (!armor) {
+ console.log(`โ Unknown armor: ${armorType}`);
+ return false;
+ }
+
+ this.dog.equipment.armor = {
+ type: armorType,
+ defense: armor.defense,
+ name: armor.name
+ };
+
+ this.dog.maxHealth = 100 + armor.defense;
+ this.dog.health = Math.min(this.dog.health + armor.defense, this.dog.maxHealth);
+
+ console.log(`๐ก๏ธ ${this.dog.name} equipped ${armor.name}! Defense +${armor.defense}`);
+
+ return true;
+ }
+
+ /**
+ * Equip backpack on dog (+10 inventory slots)
+ */
+ equipDogBackpack() {
+ if (!this.dog) return false;
+
+ if (this.dog.equipment.backpack) {
+ console.log('โน๏ธ Dog already has a backpack!');
+ return false;
+ }
+
+ this.dog.equipment.backpack = {
+ slots: 10
+ };
+
+ // Add inventory slots
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.maxSlots += 10;
+ }
+
+ console.log(`๐ ${this.dog.name} equipped backpack! +10 inventory slots!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Dog Backpack!',
+ message: `${this.dog.name} can now carry items! +10 slots!`,
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ /**
+ * Use dog's item detection ability
+ */
+ detectItems(playerPosition, radius = 300) {
+ if (!this.dog || !this.dog.abilities.item_detection) return [];
+
+ // Simulate item detection (in real game, would scan for actual items)
+ const detectedItems = [];
+
+ console.log(`๐ ${this.dog.name} is sniffing for items...`);
+
+ // Visual indicator
+ this.scene.events.emit('dog-sniff-animation', {
+ position: this.dog.position,
+ radius: radius
+ });
+
+ return detectedItems;
+ }
+
+ /**
+ * Use dog's combat ability
+ */
+ dogAttack(targetId) {
+ if (!this.dog || !this.dog.abilities.combat) return 0;
+
+ const baseDamage = 20;
+ const loyaltyBonus = this.dog.loyalty * 2;
+ const totalDamage = baseDamage + loyaltyBonus;
+
+ console.log(`โ๏ธ ${this.dog.name} attacks for ${totalDamage} damage!`);
+
+ return totalDamage;
+ }
+
+ /**
+ * Use dog's tracking ability
+ */
+ trackTarget(targetType) {
+ if (!this.dog || !this.dog.abilities.tracking) return null;
+
+ console.log(`๐พ ${this.dog.name} is tracking ${targetType}...`);
+
+ // Simulate tracking (in real game, would find actual targets)
+ const trackingResult = {
+ found: true,
+ direction: Math.random() * Math.PI * 2,
+ distance: Math.random() * 1000
+ };
+
+ this.scene.events.emit('dog-tracking', trackingResult);
+
+ return trackingResult;
+ }
+
+ /**
+ * Dog helps find album items
+ */
+ albumHelp() {
+ if (!this.dog || !this.dog.abilities.album_helper) return null;
+
+ console.log(`๐ ${this.dog.name} is searching for collectibles...`);
+
+ // Simulate finding collectible hint
+ const hint = {
+ type: 'album_item',
+ direction: Math.random() * Math.PI * 2,
+ distance: Math.random() * 500
+ };
+
+ this.scene.events.emit('notification', {
+ title: 'Dog Found Something!',
+ message: `${this.dog.name} detects a collectible nearby!`,
+ icon: '๐ถ'
+ });
+
+ return hint;
+ }
+
+ /**
+ * Revive player (10 hearts required!)
+ */
+ revivePlayer() {
+ if (!this.dog || !this.dog.abilities.revive) return false;
+
+ console.log(`๐ ${this.dog.name} revived you!`);
+
+ this.scene.events.emit('player-revived', {
+ revivedBy: this.dog.name,
+ health: 50 // Revive with 50% health
+ });
+
+ this.scene.events.emit('notification', {
+ title: 'You Were Revived!',
+ message: `${this.dog.name} saved your life!`,
+ icon: '๐'
+ });
+
+ // One-time use per day
+ this.dog.abilities.revive = false;
+
+ setTimeout(() => {
+ this.dog.abilities.revive = true;
+ console.log('๐ Revive ability recharged!');
+ }, 86400000); // 24 hours
+
+ return true;
+ }
+
+ /**
+ * Get dog stats
+ */
+ getDogStats() {
+ if (!this.dog) return null;
+
+ return {
+ name: this.dog.name,
+ breed: this.dog.breedName,
+ loyalty: `${this.dog.loyalty}/10 hearts`,
+ health: `${this.dog.health}/${this.dog.maxHealth}`,
+ abilities: Object.keys(this.dog.abilities).filter(a => this.dog.abilities[a]),
+ equipment: {
+ armor: this.dog.equipment.armor?.name || 'None',
+ backpack: this.dog.equipment.backpack ? '+10 slots' : 'None'
+ }
+ };
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SmartZombieSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/SmartZombieSystem.js
new file mode 100644
index 000000000..060543922
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SmartZombieSystem.js
@@ -0,0 +1,600 @@
+/**
+ * SMART ZOMBIE SYSTEM
+ * Manages zombie intelligence levels (Lv1-10), independent AI, follower commands, and team construction.
+ *
+ * Features:
+ * - Lv1-4: Helpers (resource finding, carry materials, warn danger)
+ * - Lv5-7: Assistants (repair assistance +25% speed, material delivery AI)
+ * - Lv8-10: INDEPENDENT AI (build/repair alone, auto-detect damage, teach others)
+ * - Follower System: 10 zombie followers with commands (Stop, Help, Attack, Home)
+ * - Team Construction: Lv10 leader + multi-zombie mega-projects
+ */
+class SmartZombieSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Zombie intelligence data
+ this.zombies = new Map(); // zombieId -> zombie data
+ this.followers = []; // Active followers (max 10)
+ this.independentWorkers = []; // Lv8+ zombies working autonomously
+ this.teams = []; // Construction teams (Lv10 leader + team)
+
+ // Commands
+ this.followerCommands = ['STOP', 'HELP', 'ATTACK', 'HOME'];
+ this.currentCommand = 'HELP'; // Default command
+
+ // AI Task Queue
+ this.taskQueue = []; // {type, position, priority, assignedZombie}
+
+ // Intelligence Levels
+ this.intelligenceLevels = {
+ 1: { name: 'Curious', abilities: ['ping_resources', 'carry_light'] },
+ 2: { name: 'Aware', abilities: ['carry_medium', 'warn_danger'] },
+ 3: { name: 'Helper', abilities: ['gather_resources', 'follow_player'] },
+ 4: { name: 'Assistant', abilities: ['detect_ores', 'organize_storage'] },
+ 5: { name: 'Skilled', abilities: ['repair_assist', 'deliver_materials'] },
+ 6: { name: 'Expert', abilities: ['craft_assist', 'defend_farm'] },
+ 7: { name: 'Advanced', abilities: ['build_assist', 'teach_lv1_3'] },
+ 8: { name: 'INDEPENDENT', abilities: ['build_alone', 'repair_alone', 'auto_detect_damage'] },
+ 9: { name: 'MASTER', abilities: ['teach_all', 'lead_team', 'optimize_tasks'] },
+ 10: { name: 'GENIUS', abilities: ['mega_projects', 'invent_blueprints', 'immortal_knowledge'] }
+ };
+
+ console.log('๐ง Smart Zombie System initialized!');
+ }
+
+ /**
+ * Create or upgrade a zombie
+ */
+ createZombie(name, level = 1, position = { x: 0, y: 0 }) {
+ const zombieId = `zombie_${Date.now()}_${Math.random()}`;
+
+ const zombie = {
+ id: zombieId,
+ name: name,
+ level: Math.min(level, 10),
+ position: position,
+ status: 'idle', // idle, working, following, patrolling
+ currentTask: null,
+ experience: 0,
+ experienceToNext: this.getExpRequired(level),
+ abilities: this.getAbilities(level),
+ inventory: [],
+ loyalty: 50, // 0-100
+ energy: 100, // 0-100
+ sprite: null // Will be set when rendered
+ };
+
+ this.zombies.set(zombieId, zombie);
+ console.log(`๐ง Created ${name} (Lv${level}) - ${this.intelligenceLevels[level].name}`);
+
+ // Auto-assign if Lv8+
+ if (level >= 8) {
+ this.makeIndependent(zombieId);
+ }
+
+ return zombieId;
+ }
+
+ /**
+ * Get required XP for next level
+ */
+ getExpRequired(level) {
+ return 100 * level * level; // 100, 400, 900, 1600, etc.
+ }
+
+ /**
+ * Get abilities for a level
+ */
+ getAbilities(level) {
+ const abilities = [];
+ for (let i = 1; i <= level; i++) {
+ abilities.push(...this.intelligenceLevels[i].abilities);
+ }
+ return [...new Set(abilities)]; // Remove duplicates
+ }
+
+ /**
+ * Add experience to a zombie
+ */
+ addExperience(zombieId, amount) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ zombie.experience += amount;
+
+ // Level up?
+ while (zombie.experience >= zombie.experienceToNext && zombie.level < 10) {
+ zombie.level++;
+ zombie.experience -= zombie.experienceToNext;
+ zombie.experienceToNext = this.getExpRequired(zombie.level + 1);
+ zombie.abilities = this.getAbilities(zombie.level);
+
+ console.log(`โฌ๏ธ ${zombie.name} leveled up to Lv${zombie.level}!`);
+
+ this.scene.events.emit('show-floating-text', {
+ x: zombie.position.x,
+ y: zombie.position.y - 50,
+ text: `LEVEL UP! Lv${zombie.level}`,
+ color: '#FFD700'
+ });
+
+ // Auto-assign if reached Lv8
+ if (zombie.level === 8) {
+ this.makeIndependent(zombieId);
+ }
+ }
+ }
+
+ /**
+ * Make a zombie independent (Lv8+)
+ */
+ makeIndependent(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie || zombie.level < 8) return false;
+
+ if (!this.independentWorkers.includes(zombieId)) {
+ this.independentWorkers.push(zombieId);
+ zombie.status = 'independent';
+
+ console.log(`๐ค ${zombie.name} is now INDEPENDENT!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Independent Worker!',
+ message: `${zombie.name} can now work autonomously!`,
+ icon: '๐ค'
+ });
+ }
+
+ return true;
+ }
+
+ /**
+ * Add zombie to follower group (max 10)
+ */
+ addFollower(zombieId) {
+ if (this.followers.length >= 10) {
+ console.log('โ Maximum 10 followers!');
+ return false;
+ }
+
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return false;
+
+ if (!this.followers.includes(zombieId)) {
+ this.followers.push(zombieId);
+ zombie.status = 'following';
+
+ console.log(`โ ${zombie.name} joined your followers!`);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove zombie from followers
+ */
+ removeFollower(zombieId) {
+ const index = this.followers.indexOf(zombieId);
+ if (index > -1) {
+ this.followers.splice(index, 1);
+ const zombie = this.zombies.get(zombieId);
+ if (zombie) {
+ zombie.status = 'idle';
+ console.log(`โ ${zombie.name} left your followers.`);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Send command to all followers
+ */
+ commandFollowers(command) {
+ if (!this.followerCommands.includes(command)) {
+ console.log(`โ Invalid command: ${command}`);
+ return;
+ }
+
+ this.currentCommand = command;
+
+ this.followers.forEach(zombieId => {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ switch (command) {
+ case 'STOP':
+ zombie.status = 'idle';
+ zombie.currentTask = null;
+ break;
+
+ case 'HELP':
+ zombie.status = 'helping';
+ this.assignHelpTask(zombieId);
+ break;
+
+ case 'ATTACK':
+ zombie.status = 'combat';
+ this.assignCombatTask(zombieId);
+ break;
+
+ case 'HOME':
+ zombie.status = 'returning';
+ this.sendHome(zombieId);
+ break;
+ }
+ });
+
+ console.log(`๐ข Commanded ${this.followers.length} followers: ${command}`);
+
+ this.scene.events.emit('notification', {
+ title: 'Follower Command',
+ message: `${this.followers.length} zombies: ${command}`,
+ icon: '๐ข'
+ });
+ }
+
+ /**
+ * Assign help task (gather, repair, build)
+ */
+ assignHelpTask(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ // Find nearest task from queue
+ const task = this.findNearestTask(zombie.position);
+ if (task) {
+ zombie.currentTask = task;
+ task.assignedZombie = zombieId;
+ console.log(`๐ ๏ธ ${zombie.name} assigned to ${task.type}`);
+ } else {
+ // Follow player and gather resources
+ zombie.currentTask = { type: 'gather', target: 'player' };
+ }
+ }
+
+ /**
+ * Assign combat task
+ */
+ assignCombatTask(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ zombie.currentTask = { type: 'defend', target: 'player', radius: 200 };
+ console.log(`โ๏ธ ${zombie.name} defending!`);
+ }
+
+ /**
+ * Send zombie home
+ */
+ sendHome(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ zombie.currentTask = { type: 'return_home', target: { x: 0, y: 0 } };
+ console.log(`๐ ${zombie.name} returning home.`);
+ }
+
+ /**
+ * Find nearest task for zombie
+ */
+ findNearestTask(position) {
+ if (this.taskQueue.length === 0) return null;
+
+ let nearest = null;
+ let minDist = Infinity;
+
+ this.taskQueue.forEach(task => {
+ if (task.assignedZombie) return; // Already assigned
+
+ const dist = Phaser.Math.Distance.Between(
+ position.x, position.y,
+ task.position.x, task.position.y
+ );
+
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = task;
+ }
+ });
+
+ return nearest;
+ }
+
+ /**
+ * Add task to queue
+ */
+ addTask(type, position, priority = 1) {
+ const task = {
+ id: `task_${Date.now()}`,
+ type: type, // 'build', 'repair', 'gather', 'mine'
+ position: position,
+ priority: priority,
+ assignedZombie: null,
+ progress: 0,
+ required: 100
+ };
+
+ this.taskQueue.push(task);
+
+ // Auto-assign to independent workers
+ this.autoAssignIndependentWorkers();
+
+ return task.id;
+ }
+
+ /**
+ * Auto-assign tasks to independent workers (Lv8+)
+ */
+ autoAssignIndependentWorkers() {
+ this.independentWorkers.forEach(zombieId => {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie || zombie.currentTask) return;
+
+ const task = this.findNearestTask(zombie.position);
+ if (task && zombie.abilities.includes('build_alone') || zombie.abilities.includes('repair_alone')) {
+ zombie.currentTask = task;
+ task.assignedZombie = zombieId;
+
+ console.log(`๐ค Independent: ${zombie.name} auto-assigned to ${task.type}`);
+ }
+ });
+ }
+
+ /**
+ * Create construction team (Lv10 leader required)
+ */
+ createTeam(leaderZombieId, memberZombieIds) {
+ const leader = this.zombies.get(leaderZombieId);
+ if (!leader || leader.level < 10) {
+ console.log('โ Team leader must be Lv10!');
+ return null;
+ }
+
+ const members = memberZombieIds
+ .map(id => this.zombies.get(id))
+ .filter(z => z && z.level >= 5);
+
+ if (members.length === 0) {
+ console.log('โ Need at least 1 Lv5+ member!');
+ return null;
+ }
+
+ const teamId = `team_${Date.now()}`;
+ const team = {
+ id: teamId,
+ leader: leaderZombieId,
+ members: memberZombieIds,
+ status: 'ready',
+ currentProject: null,
+ efficiency: 1.0 + (members.length * 0.15) // +15% per member
+ };
+
+ this.teams.push(team);
+
+ console.log(`๐ฅ Team created! Leader: ${leader.name}, Members: ${members.length}, Efficiency: ${team.efficiency}x`);
+
+ this.scene.events.emit('notification', {
+ title: 'Team Created!',
+ message: `${leader.name}'s team (${members.length} members) is ready!`,
+ icon: '๐ฅ'
+ });
+
+ return teamId;
+ }
+
+ /**
+ * Assign mega-project to team
+ */
+ assignMegaProject(teamId, projectType, position) {
+ const team = this.teams.find(t => t.id === teamId);
+ if (!team) return false;
+
+ const project = {
+ type: projectType, // 'mega_barn', 'fortress', 'factory'
+ position: position,
+ progress: 0,
+ required: 1000, // Mega projects take time
+ efficiency: team.efficiency
+ };
+
+ team.currentProject = project;
+ team.status = 'working';
+
+ console.log(`๐๏ธ Team assigned to ${projectType}! Efficiency: ${team.efficiency}x`);
+
+ return true;
+ }
+
+ /**
+ * Update all zombies (called every frame)
+ */
+ update(delta) {
+ // Update followers
+ this.followers.forEach(zombieId => {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ this.updateZombie(zombie, delta);
+ });
+
+ // Update independent workers
+ this.independentWorkers.forEach(zombieId => {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ this.updateZombie(zombie, delta);
+ });
+
+ // Update teams
+ this.teams.forEach(team => {
+ if (team.status === 'working' && team.currentProject) {
+ team.currentProject.progress += delta * 0.01 * team.efficiency;
+
+ if (team.currentProject.progress >= team.currentProject.required) {
+ this.completeMegaProject(team);
+ }
+ }
+ });
+ }
+
+ /**
+ * Update individual zombie
+ */
+ updateZombie(zombie, delta) {
+ if (!zombie.currentTask) return;
+
+ const task = zombie.currentTask;
+
+ switch (task.type) {
+ case 'build':
+ case 'repair':
+ this.progressTask(zombie, task, delta);
+ break;
+
+ case 'gather':
+ this.gatherResources(zombie, delta);
+ break;
+
+ case 'defend':
+ this.defendArea(zombie);
+ break;
+
+ case 'return_home':
+ this.moveToHome(zombie);
+ break;
+ }
+
+ // Drain energy
+ zombie.energy = Math.max(0, zombie.energy - delta * 0.001);
+ }
+
+ /**
+ * Progress on a task
+ */
+ progressTask(zombie, task, delta) {
+ const speedBonus = zombie.level >= 5 ? 1.25 : 1.0;
+ task.progress += delta * 0.02 * speedBonus;
+
+ if (task.progress >= task.required) {
+ this.completeTask(zombie, task);
+ }
+ }
+
+ /**
+ * Complete a task
+ */
+ completeTask(zombie, task) {
+ console.log(`โ
${zombie.name} completed ${task.type}!`);
+
+ // Award XP
+ this.addExperience(zombie.id, 50 + (task.priority * 10));
+
+ // Remove from queue
+ const index = this.taskQueue.indexOf(task);
+ if (index > -1) {
+ this.taskQueue.splice(index, 1);
+ }
+
+ zombie.currentTask = null;
+
+ // Find next task if independent
+ if (zombie.status === 'independent') {
+ this.autoAssignIndependentWorkers();
+ }
+ }
+
+ /**
+ * Gather resources
+ */
+ gatherResources(zombie, delta) {
+ // Simple resource gathering simulation
+ if (Math.random() < 0.01) {
+ const resources = ['wood', 'stone', 'iron_ore'];
+ const resource = resources[Math.floor(Math.random() * resources.length)];
+
+ zombie.inventory.push(resource);
+ console.log(`๐ฆ ${zombie.name} gathered ${resource}`);
+ }
+ }
+
+ /**
+ * Defend area around player/target
+ */
+ defendArea(zombie) {
+ // Detect nearby enemies (placeholder)
+ // In real game, would check for hostile entities
+ }
+
+ /**
+ * Move zombie to home position
+ */
+ moveToHome(zombie) {
+ const target = zombie.currentTask.target;
+ const dist = Phaser.Math.Distance.Between(
+ zombie.position.x, zombie.position.y,
+ target.x, target.y
+ );
+
+ if (dist < 10) {
+ zombie.status = 'idle';
+ zombie.currentTask = null;
+ console.log(`๐ ${zombie.name} arrived home.`);
+ } else {
+ // Move toward home (simplified)
+ const angle = Phaser.Math.Angle.Between(
+ zombie.position.x, zombie.position.y,
+ target.x, target.y
+ );
+ zombie.position.x += Math.cos(angle) * 2;
+ zombie.position.y += Math.sin(angle) * 2;
+ }
+ }
+
+ /**
+ * Complete mega-project
+ */
+ completeMegaProject(team) {
+ console.log(`๐ Mega-project ${team.currentProject.type} COMPLETE!`);
+
+ const leader = this.zombies.get(team.leader);
+ if (leader) {
+ this.addExperience(leader.id, 500);
+ }
+
+ team.members.forEach(memberId => {
+ this.addExperience(memberId, 200);
+ });
+
+ this.scene.events.emit('notification', {
+ title: 'Mega-Project Complete!',
+ message: `${team.currentProject.type} is finished!`,
+ icon: '๐'
+ });
+
+ team.currentProject = null;
+ team.status = 'ready';
+ }
+
+ /**
+ * Get zombie stats
+ */
+ getZombieStats(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return null;
+
+ return {
+ name: zombie.name,
+ level: zombie.level,
+ intelligence: this.intelligenceLevels[zombie.level].name,
+ abilities: zombie.abilities,
+ status: zombie.status,
+ energy: Math.round(zombie.energy),
+ loyalty: zombie.loyalty,
+ experience: `${zombie.experience}/${zombie.experienceToNext}`,
+ inventory: zombie.inventory.length
+ };
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SoundManager.js b/EMERGENCY_SYSTEMS_RECOVERY/SoundManager.js
new file mode 100644
index 000000000..fd2175eae
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SoundManager.js
@@ -0,0 +1,506 @@
+class SoundManager {
+ constructor(scene) {
+ this.scene = scene;
+ this.musicVolume = 0.3;
+ this.sfxVolume = 0.5;
+ this.isMuted = false;
+ this.currentMusic = null;
+ this.currentAmbient = null;
+ console.log('๐ต SoundManager: Initialized');
+ }
+
+ playSFX(key) {
+ if (this.isMuted) return;
+
+ // SAFE CHECK: Check cache instead of active instances
+ if (this.scene.cache.audio.exists(key)) {
+ try {
+ this.scene.sound.play(key, { volume: this.sfxVolume });
+ } catch (e) {
+ console.warn(`๐ Failed to play SFX ${key}:`, e);
+ // Fallback to beep if playback fails
+ this.playFallbackBeep(key);
+ }
+ } else if (key === 'chop' && this.scene.cache.audio.exists('wood_chop')) {
+ // Priority for real wood chop sound
+ this.scene.sound.play('wood_chop', { volume: this.sfxVolume });
+ } else {
+ // Enhanced placeholder beeps
+ this.playFallbackBeep(key);
+ }
+ }
+
+ playFallbackBeep(key) {
+ if (key === 'chop') {
+ this.beepChop();
+ } else if (key === 'pickup') {
+ this.beepPickup();
+ } else if (key === 'plant') {
+ this.beepPlant();
+ } else if (key === 'harvest') {
+ this.beepHarvest();
+ } else if (key === 'build') {
+ this.beepBuild();
+ } else if (key === 'dig') {
+ this.beepDig();
+ }
+ }
+
+ playMusic(key, loop = true) {
+ if (this.isMuted) return;
+
+ // Stop current music
+ if (this.currentMusic) {
+ this.currentMusic.stop();
+ }
+
+ if (this.scene.cache.audio.exists(key)) {
+ try {
+ this.currentMusic = this.scene.sound.add(key, { volume: this.musicVolume, loop: loop });
+ this.currentMusic.play();
+ } catch (e) {
+ console.warn(`๐ต Failed to play music ${key}:`, e);
+ }
+ } else {
+ console.warn(`๐ต Music key not found: ${key}`);
+ }
+ }
+
+ playForestAmbient() {
+ this.playMusic('forest_ambient', true);
+ }
+
+ beepChop() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.value = 150;
+ osc.type = 'sawtooth';
+ gain.gain.setValueAtTime(0.15, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.15);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.15);
+ }
+
+ beepPickup() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.setValueAtTime(600, ctx.currentTime);
+ osc.frequency.linearRampToValueAtTime(1200, ctx.currentTime + 0.1);
+ osc.type = 'sine';
+ gain.gain.setValueAtTime(0.12, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.1);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.1);
+ }
+
+ beepPlant() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.value = 300;
+ osc.type = 'triangle';
+ gain.gain.setValueAtTime(0.1, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.12);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.12);
+ }
+
+ beepHarvest() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc1 = ctx.createOscillator();
+ const gain1 = ctx.createGain();
+ osc1.connect(gain1);
+ gain1.connect(ctx.destination);
+ osc1.frequency.value = 523;
+ osc1.type = 'sine';
+ gain1.gain.setValueAtTime(0.1, ctx.currentTime);
+ gain1.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.08);
+ osc1.start();
+ osc1.stop(ctx.currentTime + 0.08);
+
+ const osc2 = ctx.createOscillator();
+ const gain2 = ctx.createGain();
+ osc2.connect(gain2);
+ gain2.connect(ctx.destination);
+ osc2.frequency.value = 659;
+ osc2.type = 'sine';
+ gain2.gain.setValueAtTime(0.1, ctx.currentTime + 0.08);
+ gain2.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.16);
+ osc2.start(ctx.currentTime + 0.08);
+ osc2.stop(ctx.currentTime + 0.16);
+ }
+
+ beepBuild() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.value = 80;
+ osc.type = 'square';
+ gain.gain.setValueAtTime(0.2, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.2);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.2);
+ }
+
+ beepAttack() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.setValueAtTime(400, ctx.currentTime);
+ osc.frequency.exponentialRampToValueAtTime(100, ctx.currentTime + 0.08);
+ osc.type = 'sawtooth';
+ gain.gain.setValueAtTime(0.2, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.08);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.08);
+ }
+
+ beepHit() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.value = 80;
+ osc.type = 'square';
+ gain.gain.setValueAtTime(0.25, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.05);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.05);
+ }
+
+ beepFootstep() {
+ this.playFootstep('grass');
+ }
+
+ playFootstep(surface = 'grass') {
+ if (this.isMuted) return;
+ const key = `footstep_${surface}`;
+ if (this.scene.sound.get(key)) {
+ this.scene.sound.play(key, { volume: this.sfxVolume * 0.4 });
+ } else {
+ // Fallback beep
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.value = 120 + Math.random() * 20;
+ osc.type = 'sine';
+ gain.gain.setValueAtTime(0.05, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.03);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.03);
+ }
+ }
+
+ beepDeath() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.setValueAtTime(300, ctx.currentTime);
+ osc.frequency.exponentialRampToValueAtTime(50, ctx.currentTime + 0.5);
+ osc.type = 'sawtooth';
+ gain.gain.setValueAtTime(0.2, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.5);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.5);
+ }
+
+ startRainNoise() {
+ if (!this.scene.sound.context || this.rainNode) return;
+ const ctx = this.scene.sound.context;
+
+ // Create noise buffer
+ const bufferSize = 2 * ctx.sampleRate;
+ const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
+ const output = buffer.getChannelData(0);
+ for (let i = 0; i < bufferSize; i++) {
+ output[i] = Math.random() * 2 - 1;
+ }
+
+ this.rainNode = ctx.createBufferSource();
+ this.rainNode.buffer = buffer;
+ this.rainNode.loop = true;
+
+ // Lowpass filter for rain sound
+ const filter = ctx.createBiquadFilter();
+ filter.type = 'lowpass';
+ filter.frequency.value = 800;
+
+ this.rainGain = ctx.createGain();
+ this.rainGain.gain.value = 0.05 * this.sfxVolume;
+
+ this.rainNode.connect(filter);
+ filter.connect(this.rainGain);
+ this.rainGain.connect(ctx.destination);
+
+ this.rainNode.start();
+ }
+
+ stopRainNoise() {
+ if (this.rainNode) {
+ this.rainNode.stop();
+ this.rainNode.disconnect();
+ this.rainNode = null;
+ }
+ if (this.rainGain) {
+ this.rainGain.disconnect();
+ this.rainGain = null;
+ }
+ }
+
+ playAmbient(key, loop = true) {
+ if (this.isMuted) return;
+ if (this.currentAmbient) this.currentAmbient.stop();
+
+ if (!this.scene.cache.audio.exists(key)) return;
+
+ try {
+ this.currentAmbient = this.scene.sound.add(key, { volume: this.sfxVolume * 0.5, loop: loop });
+ this.currentAmbient.play();
+ } catch (e) {
+ console.warn(`๐ Failed to play ambient ${key}:`, e);
+ }
+ }
+
+ stopAmbient() {
+ if (this.currentAmbient) {
+ this.currentAmbient.stop();
+ this.currentAmbient = null;
+ }
+ }
+
+ startMusic() {
+ if (!this.scene.sound.context || this.musicInterval) return;
+
+ console.log('๐ต Starting Ambient Music...');
+ // Simple C Minor Pentatonic: C3, Eb3, F3, G3, Bb3
+ const scale = [130.81, 155.56, 174.61, 196.00, 233.08, 261.63];
+
+ // Loop every 3-5 seconds play a note
+ this.musicInterval = setInterval(() => {
+ if (this.isMuted) return;
+ // 40% chance to play a note
+ if (Math.random() > 0.6) {
+ const freq = scale[Math.floor(Math.random() * scale.length)];
+ this.playProceduralNote(freq);
+ }
+ }, 2000);
+ }
+
+ setAmbientModulation(factor) {
+ // factor 0 to 1 (proximity to pond)
+ this.ambientModFactor = factor;
+ // Optionally adjust existing music pitch or filter
+ if (this.currentMusic && this.currentMusic.isPlaying) {
+ this.currentMusic.setRate(1.0 - (factor * 0.1)); // Slightly slow down
+ }
+ }
+
+ startHighMusic() {
+ if (!this.scene.sound.context || this.highInterval) return;
+
+ console.log('๐ Starting High Ambient Music...');
+ // Warped/Low scales for high effect
+ const scale = [65.41, 77.78, 87.31, 98.00, 116.54, 130.81];
+
+ this.highInterval = setInterval(() => {
+ if (this.isMuted) return;
+ if (Math.random() > 0.4) {
+ const freq = scale[Math.floor(Math.random() * scale.length)];
+ this.playProceduralNote(freq, 4.0, 0.1, 'sawtooth'); // More buzzy/long
+ }
+ }, 3000);
+ }
+
+ stopHighMusic() {
+ if (this.highInterval) {
+ clearInterval(this.highInterval);
+ this.highInterval = null;
+ }
+ }
+
+ stopMusic() {
+ if (this.musicInterval) {
+ clearInterval(this.musicInterval);
+ this.musicInterval = null;
+ }
+ }
+
+ playProceduralNote(freq, duration = 2.0, volumeMod = 0.05, type = 'sine') {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+
+ osc.frequency.value = freq;
+ osc.type = type;
+
+ const now = ctx.currentTime;
+
+ gain.gain.setValueAtTime(0, now);
+ gain.gain.linearRampToValueAtTime(volumeMod * this.musicVolume, now + 0.5); // Slow attack
+ gain.gain.exponentialRampToValueAtTime(0.001, now + duration); // Long release
+
+ osc.start(now);
+ osc.stop(now + duration);
+ }
+
+ toggleMute() {
+ this.isMuted = !this.isMuted;
+ this.scene.sound.mute = this.isMuted;
+ console.log(this.isMuted ? '๐ Muted' : '๐ Unmuted');
+ }
+
+ playChop() {
+ if (this.scene.sound.get('wood_chop')) {
+ this.scene.sound.play('wood_chop', { volume: this.sfxVolume });
+ } else {
+ this.playSFX('chop');
+ }
+ }
+ playPlant() { this.playSFX('plant'); }
+ playHarvest() { this.playSFX('harvest'); }
+ playBuild() { this.playSFX('build'); }
+ playPickup() { this.playSFX('pickup'); }
+ playDig() { this.playSFX('dig'); }
+ playUIClick() { this.beepUIClick(); } // UI click sound
+ playAttack() { this.beepAttack(); }
+ playHit() { this.beepHit(); }
+ playFootstep() { this.beepFootstep(); }
+ playDeath() { this.beepDeath(); }
+ playRainSound() { this.startRainNoise(); }
+ stopRainSound() { this.stopRainNoise(); }
+ playHighAmbient() { this.startHighMusic(); }
+ stopHighAmbient() { this.stopHighMusic(); }
+
+ playCraftSound() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ // Hammering rhythm
+ for (let i = 0; i < 3; i++) {
+ const time = ctx.currentTime + (i * 0.3);
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.setValueAtTime(200 - (i * 20), time);
+ osc.type = 'square';
+ gain.gain.setValueAtTime(0.1, time);
+ gain.gain.exponentialRampToValueAtTime(0.01, time + 0.1);
+ osc.start(time);
+ osc.stop(time + 0.1);
+ }
+ }
+
+ playSuccessSound() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const notes = [523.25, 659.25, 783.99, 1046.50]; // C5, E5, G5, C6
+ notes.forEach((freq, i) => {
+ const time = ctx.currentTime + (i * 0.1);
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ osc.frequency.value = freq;
+ osc.type = 'sine';
+ gain.gain.setValueAtTime(0, time);
+ gain.gain.linearRampToValueAtTime(0.1, time + 0.05);
+ gain.gain.exponentialRampToValueAtTime(0.001, time + 0.3);
+ osc.start(time);
+ osc.stop(time + 0.3);
+ });
+ }
+
+ playSmokeSound() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+
+ // Inhale (White noise sweep)
+ const bufferSize = ctx.sampleRate * 0.5;
+ const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
+ const data = buffer.getChannelData(0);
+ for (let i = 0; i < bufferSize; i++) data[i] = Math.random() * 2 - 1;
+
+ const source = ctx.createBufferSource();
+ source.buffer = buffer;
+ const filter = ctx.createBiquadFilter();
+ const gain = ctx.createGain();
+
+ source.connect(filter);
+ filter.connect(gain);
+ gain.connect(ctx.destination);
+
+ filter.type = 'lowpass';
+ filter.frequency.setValueAtTime(100, ctx.currentTime);
+ filter.frequency.exponentialRampToValueAtTime(2000, ctx.currentTime + 0.5);
+
+ gain.gain.setValueAtTime(0, ctx.currentTime);
+ gain.gain.linearRampToValueAtTime(0.1, ctx.currentTime + 0.1);
+ gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.5);
+
+ source.start();
+ }
+
+ beepUIClick() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ // Quick, pleasant click sound
+ osc.frequency.value = 800;
+ osc.type = 'sine';
+ gain.gain.setValueAtTime(0.08, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.05);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.05);
+ }
+
+ beepDig() {
+ if (!this.scene.sound.context) return;
+ const ctx = this.scene.sound.context;
+ const osc = ctx.createOscillator();
+ const gain = ctx.createGain();
+ osc.connect(gain);
+ gain.connect(ctx.destination);
+ // Low "thud" for dirt interaction
+ osc.frequency.setValueAtTime(80, ctx.currentTime);
+ osc.frequency.exponentialRampToValueAtTime(10, ctx.currentTime + 0.15);
+ osc.type = 'triangle';
+ gain.gain.setValueAtTime(0.2, ctx.currentTime);
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.15);
+ osc.start();
+ osc.stop(ctx.currentTime + 0.15);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/StaminaSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/StaminaSystem.js
new file mode 100644
index 000000000..0c0f44c98
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/StaminaSystem.js
@@ -0,0 +1,154 @@
+class StaminaSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Stamina properties
+ this.maxStamina = 100;
+ this.currentStamina = 100;
+ this.regenRate = 5; // Stamina per second
+ this.regenDelay = 2000; // Wait 2s after use before regen
+ this.lastUseTime = 0;
+
+ // Stamina costs
+ this.costs = {
+ 'till': 5,
+ 'plant': 3,
+ 'harvest': 4,
+ 'build': 10,
+ 'attack': 8,
+ 'sprint': 2 // per second
+ };
+
+ console.log('โก StaminaSystem: Initialized');
+ }
+
+ // Use stamina for action
+ useStamina(action) {
+ const cost = this.costs[action] || 0;
+
+ if (this.currentStamina < cost) {
+ console.log(`โก Not enough stamina! (${this.currentStamina}/${cost})`);
+ return false; // Not enough stamina
+ }
+
+ this.currentStamina -= cost;
+ this.lastUseTime = Date.now();
+
+ // Update UI
+ this.updateUI();
+
+ console.log(`โก Used ${cost} stamina (${action}). Remaining: ${this.currentStamina}`);
+ return true;
+ }
+
+ // Restore stamina (from food)
+ restoreStamina(amount) {
+ this.currentStamina = Math.min(this.maxStamina, this.currentStamina + amount);
+ this.updateUI();
+
+ console.log(`โก Restored ${amount} stamina. Current: ${this.currentStamina}`);
+ }
+
+ // Auto-regenerate stamina
+ update(delta) {
+ // Check if enough time has passed since last use
+ const timeSinceUse = Date.now() - this.lastUseTime;
+
+ if (timeSinceUse > this.regenDelay && this.currentStamina < this.maxStamina) {
+ // Regenerate stamina
+ const regenAmount = (this.regenRate * delta) / 1000;
+ this.currentStamina = Math.min(this.maxStamina, this.currentStamina + regenAmount);
+
+ // Update UI
+ this.updateUI();
+ }
+ }
+
+ // Update stamina bar in UI
+ updateUI() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ // Update stamina bar (if exists)
+ if (uiScene.staminaBar) {
+ const percentage = this.currentStamina / this.maxStamina;
+ uiScene.staminaBar.clear();
+
+ // Background
+ uiScene.staminaBar.fillStyle(0x000000, 0.5);
+ uiScene.staminaBar.fillRect(20, 120, 200, 20);
+
+ // Stamina fill (yellow)
+ uiScene.staminaBar.fillStyle(0xffff00, 1.0);
+ uiScene.staminaBar.fillRect(20, 120, 200 * percentage, 20);
+
+ // Border
+ uiScene.staminaBar.lineStyle(2, 0xffffff, 1.0);
+ uiScene.staminaBar.strokeRect(20, 120, 200, 20);
+ }
+
+ // Update stamina text
+ if (uiScene.staminaText) {
+ uiScene.staminaText.setText(`โก ${Math.floor(this.currentStamina)}/${this.maxStamina}`);
+ }
+ }
+
+ // Create stamina UI
+ createUI() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ // Stamina bar (below health)
+ uiScene.staminaBar = uiScene.add.graphics();
+ uiScene.staminaBar.setScrollFactor(0);
+ uiScene.staminaBar.setDepth(1000);
+
+ // Stamina text
+ uiScene.staminaText = uiScene.add.text(
+ 20,
+ 105,
+ `โก ${this.currentStamina}/${this.maxStamina}`,
+ {
+ fontSize: '14px',
+ color: '#ffff00',
+ fontStyle: 'bold'
+ }
+ );
+ uiScene.staminaText.setScrollFactor(0);
+ uiScene.staminaText.setDepth(1001);
+
+ // Initial update
+ this.updateUI();
+
+ console.log('โก Stamina UI created');
+ }
+
+ // Check if player has enough stamina
+ hasStamina(action) {
+ const cost = this.costs[action] || 0;
+ return this.currentStamina >= cost;
+ }
+
+ // Get stamina percentage (0-1)
+ getPercentage() {
+ return this.currentStamina / this.maxStamina;
+ }
+
+ // Save/Load
+ getSaveData() {
+ return {
+ currentStamina: this.currentStamina,
+ maxStamina: this.maxStamina
+ };
+ }
+
+ loadSaveData(data) {
+ if (data.currentStamina !== undefined) {
+ this.currentStamina = data.currentStamina;
+ }
+ if (data.maxStamina !== undefined) {
+ this.maxStamina = data.maxStamina;
+ }
+ this.updateUI();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/StarterChestSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/StarterChestSystem.js
new file mode 100644
index 000000000..6d3337c01
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/StarterChestSystem.js
@@ -0,0 +1,128 @@
+/**
+ * Starter Chest System
+ * Generates seed-based random loot for new game
+ */
+
+class StarterChestSystem {
+ constructor(scene) {
+ this.scene = scene;
+ }
+
+ /**
+ * Generate seed-based random loot table
+ * @param {number} seed - Game seed (e.g., Date.now())
+ * @returns {Array} List of items {type, amount}
+ */
+ generateLoot(seed = Date.now()) {
+ // Simple seeded random generator (LCG)
+ let rng = seed;
+ const random = () => {
+ rng = (rng * 9301 + 49297) % 233280;
+ return rng / 233280;
+ };
+
+ const lootTable = [];
+
+ // GUARANTEED ITEMS (always spawn)
+ lootTable.push({ type: 'wheat_seed', amount: 10 + Math.floor(random() * 10) }); // 10-20 seeds
+ lootTable.push({ type: 'wood', amount: 5 + Math.floor(random() * 10) }); // 5-15 wood
+
+ // RANDOM ITEMS (50% chance each)
+ if (random() > 0.5) {
+ lootTable.push({ type: 'stone', amount: 3 + Math.floor(random() * 5) }); // 3-8 stone
+ }
+ if (random() > 0.5) {
+ lootTable.push({ type: 'bone', amount: 2 + Math.floor(random() * 4) }); // 2-6 bones
+ }
+ if (random() > 0.7) {
+ lootTable.push({ type: 'bread', amount: 1 + Math.floor(random() * 3) }); // 1-4 bread (food)
+ }
+
+ // RARE ITEMS (10% chance each)
+ if (random() > 0.9) {
+ lootTable.push({ type: 'iron', amount: 1 + Math.floor(random() * 2) }); // 1-3 iron
+ }
+ if (random() > 0.9) {
+ lootTable.push({ type: 'gold_coin', amount: 10 + Math.floor(random() * 20) }); // 10-30 gold
+ }
+
+ // ULTRA RARE (1% chance)
+ if (random() > 0.99) {
+ lootTable.push({ type: 'diamond', amount: 1 }); // 1 diamond!
+ }
+
+ console.log('๐ Starter Chest Loot Generated:', lootTable);
+ return lootTable;
+ }
+
+ /**
+ * Place starter chest at farm location
+ * @param {number} x - Grid X position
+ * @param {number} y - Grid Y position
+ * @param {number} seed - Game seed
+ */
+ placeStarterChest(x, y, seed) {
+ // Generate loot
+ const lootItems = this.generateLoot(seed);
+
+ // Place chest structure (if TerrainSystem supports it)
+ if (this.scene.terrainSystem && this.scene.terrainSystem.placeStructure) {
+ // Check if placeStructure method exists
+ try {
+ this.scene.terrainSystem.placeStructure(x, y, 'chest');
+ console.log(`๐ Starter Chest placed at (${x}, ${y})`);
+ } catch (e) {
+ console.warn('โ ๏ธ placeStructure not available, chest not placed:', e);
+ }
+ }
+
+ // Store loot data globally for later retrieval
+ if (!window.gameState) window.gameState = {};
+ window.gameState.starterChestLoot = lootItems;
+ window.gameState.starterChestPosition = { x, y };
+
+ return lootItems;
+ }
+
+ /**
+ * Open chest and add items to player inventory
+ * @param {Object} player - Player object with inventory
+ */
+ openChest(player) {
+ if (!window.gameState || !window.gameState.starterChestLoot) {
+ console.warn('โ ๏ธ No starter chest loot found!');
+ return;
+ }
+
+ const loot = window.gameState.starterChestLoot;
+
+ // Add items to player inventory
+ loot.forEach(item => {
+ if (player.inventory && player.inventory.addItem) {
+ player.inventory.addItem(item.type, item.amount);
+ console.log(`โ Added ${item.amount}x ${item.type} to inventory`);
+ } else {
+ console.warn('โ ๏ธ Player inventory not available');
+ }
+ });
+
+ // Show notification
+ if (this.scene.events) {
+ this.scene.events.emit('showFloatingText', {
+ x: window.gameState.starterChestPosition.x,
+ y: window.gameState.starterChestPosition.y,
+ text: '๐ Starter Chest Opened!',
+ color: '#ffaa00'
+ });
+ }
+
+ // Clear loot (chest is now empty)
+ window.gameState.starterChestLoot = null;
+ console.log('โ
Starter Chest opened and emptied');
+ }
+}
+
+// Export for global use
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = StarterChestSystem;
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/StatsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/StatsSystem.js
new file mode 100644
index 000000000..e843ed32e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/StatsSystem.js
@@ -0,0 +1,245 @@
+// ========================================================
+// KONSTANTE IN IGRALฤEVI PODATKI
+// ========================================================
+const XP_REQUIRED_BASE = 100; // Osnovni XP, potrebni za LVL 2
+const XP_GROWTH_FACTOR = 1.5; // Za vsak LVL potrebujete 1.5x veฤ XP
+
+class StatsSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Leveling System
+ this.currentLevel = 1;
+ this.currentXP = 0;
+ this.xpToNextLevel = XP_REQUIRED_BASE;
+ this.score = 0; // GLOBAL SCORE (Za Legacy)
+ this.totalPlaytime = 0; // Skupni ฤas igranja (sekunde)
+
+ // Stats
+ this.health = 100;
+ this.maxHealth = 100;
+
+ this.hunger = 100; // 100 = full
+ this.maxHunger = 100;
+
+ this.thirst = 100; // 100 = not thirsty
+ this.maxThirst = 100;
+
+ // Decay rates (per second)
+ this.hungerDecay = 0.5; // Pade na 0 v 200s (cca 3 min)
+ this.thirstDecay = 0.8; // Pade na 0 v 125s (cca 2 min)
+
+ this.damageTickTimer = 0;
+
+ // Friendship System (Hearts โค๏ธ)
+ this.friendship = {
+ merchant: 0,
+ zombie: 0,
+ villager: 0
+ };
+ }
+
+ update(delta) {
+ const seconds = delta / 1000;
+ this.totalPlaytime += seconds; // Track playtime
+
+ // Decay
+ if (this.hunger > 0) {
+ this.hunger -= this.hungerDecay * seconds;
+ }
+ if (this.thirst > 0) {
+ this.thirst -= this.thirstDecay * seconds;
+ }
+
+ // Clamp values
+ this.hunger = Math.max(0, this.hunger);
+ this.thirst = Math.max(0, this.thirst);
+
+ // Starvation / Dehydration logic
+ if (this.hunger <= 0 || this.thirst <= 0) {
+ this.damageTickTimer += delta;
+ if (this.damageTickTimer >= 1000) { // Vsako sekundo damage
+ this.damageTickTimer = 0;
+ this.takeDamage(5); // 5 DMG na sekundo ฤe si laฤen/ลพejen
+
+ // Shake camera effect za opozorilo
+ this.scene.cameras.main.shake(100, 0.005);
+ }
+ } else {
+ this.damageTickTimer = 0;
+
+ // Natural regeneration if full
+ if (this.hunger > 80 && this.thirst > 80 && this.health < this.maxHealth) {
+ this.health += 1 * seconds;
+ }
+ }
+
+ this.health = Math.min(this.health, this.maxHealth);
+
+ this.updateUI();
+ }
+
+ takeDamage(amount) {
+ this.health -= amount;
+ if (this.health <= 0) {
+ this.health = 0;
+ this.die();
+ }
+ }
+
+ eat(amount) {
+ this.hunger += amount;
+ this.hunger = Math.min(this.hunger, this.maxHunger);
+ }
+
+ drink(amount) {
+ this.thirst += amount;
+ this.thirst = Math.min(this.thirst, this.maxThirst);
+ }
+
+ // SCORE & DEATH LOGIC
+ addScore(points) {
+ this.score += points;
+ // console.log(`โญ Score +${points} (Total: ${this.score})`);
+ }
+
+ die() {
+ console.log('๐ Player died!');
+
+ // SCORE PENALTY (Legacy Cost)
+ // Igralec NE izgubi farme, ampak izgubi del Dediลกฤine (Toฤk).
+ const penalty = Math.floor(this.score * 0.25); // Izguba 25% toฤk
+ this.score = Math.max(0, this.score - penalty);
+ console.log(`๐ Dediลกฤina Oลกkodovana: -${penalty} Toฤk (Novo stanje: ${this.score})`);
+
+ // Trigger Player Animation
+ if (this.scene.player) {
+ this.scene.player.dieAnimation();
+ }
+
+ // Show Notification & Overlay in UI Scene
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene) {
+ // Full screen overlay using scale dimensions
+ const width = uiScene.scale.width;
+ const height = uiScene.scale.height;
+
+ const bg = uiScene.add.rectangle(width / 2, height / 2, width, height, 0x000000, 0.8);
+
+ const txt = uiScene.add.text(width / 2, height / 2 - 50, 'YOU DIED', {
+ fontSize: '64px', color: '#ff0000', fontStyle: 'bold'
+ }).setOrigin(0.5);
+
+ const sub = uiScene.add.text(width / 2, height / 2 + 20, `Legacy Lost: -${penalty} Score pts`, {
+ fontSize: '24px', color: '#ffffff'
+ }).setOrigin(0.5);
+
+ const sub2 = uiScene.add.text(width / 2, height / 2 + 60, '(Farm Preserved)', {
+ fontSize: '18px', color: '#aaaaaa', fontStyle: 'italic'
+ }).setOrigin(0.5);
+
+ // Wait and Respawn
+ uiScene.time.delayedCall(3000, () => {
+ if (bg) bg.destroy();
+ if (txt) txt.destroy();
+ if (sub) sub.destroy();
+ if (sub2) sub2.destroy();
+
+ // Reset Stats (but keep Score penalty)
+ this.health = 100;
+ this.hunger = 100;
+ this.thirst = 100;
+ this.updateUI();
+
+ // Reset Player
+ if (this.scene.player) {
+ this.scene.player.respawn();
+ }
+ });
+ }
+ }
+
+ updateUI() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene) {
+ if (uiScene.healthBar) uiScene.setBarValue(uiScene.healthBar, this.health);
+ if (uiScene.hungerBar) uiScene.setBarValue(uiScene.hungerBar, this.hunger);
+ if (uiScene.thirstBar) uiScene.setBarValue(uiScene.thirstBar, this.thirst);
+ }
+ this.updateLevelUI();
+ }
+
+ updateLevelUI() {
+ const xpPercent = (this.currentXP / this.xpToNextLevel) * 100;
+ const levelText = `LV: ${this.currentLevel}`;
+
+ // Uporaba Antigravity Engine UI klicev, kot zahtevano
+ if (window.Antigravity && window.Antigravity.UI) {
+ window.Antigravity.UI.setText(this.scene, 'LevelDisplay', levelText);
+ window.Antigravity.UI.setBarValue(this.scene, 'XPBar', xpPercent);
+ }
+ }
+
+ // Friendship System
+ addFriendship(npcType, amount) {
+ if (this.friendship[npcType] !== undefined) {
+ this.friendship[npcType] += amount;
+ console.log(`โค๏ธ +${amount} Friendship with ${npcType} (Total: ${this.friendship[npcType]})`);
+ }
+ }
+
+ getFriendship(npcType) {
+ return this.friendship[npcType] || 0;
+ }
+
+ setFriendship(npcType, amount) {
+ if (this.friendship[npcType] !== undefined) {
+ this.friendship[npcType] = amount;
+ }
+ }
+
+ // ========================================================
+ // LEVELING SYSTEM
+ // ========================================================
+
+ addXP(amount) {
+ this.currentXP += amount;
+
+ // 1. Preverimo, ali je igralec pripravljen za Level Up
+ while (this.currentXP >= this.xpToNextLevel) {
+ this.levelUp();
+ }
+
+ this.updateUI();
+ }
+
+ levelUp() {
+ // Poveฤamo Level
+ this.currentLevel++;
+
+ // Izraฤunamo nov XP prag
+ this.xpToNextLevel = Math.floor(this.xpToNextLevel * XP_GROWTH_FACTOR);
+
+ // Preostanek XP prenesemo v nov Level
+ this.currentXP = this.currentXP - (this.xpToNextLevel / XP_GROWTH_FACTOR);
+
+ // Bonus Stats
+ this.maxHealth += 10;
+ this.health = this.maxHealth;
+ this.maxHunger += 5;
+ this.maxThirst += 5;
+ this.hunger = this.maxHunger;
+ this.thirst = this.maxThirst;
+
+ // Vizualni uฤinek / Prikaz sporoฤila
+ console.log(`๐ LEVEL UP! Doseลพen level ${this.currentLevel}!`);
+
+ if (window.Antigravity && window.Antigravity.UI) {
+ window.Antigravity.UI.showMessage(
+ this.scene,
+ `LEVEL UP! Doseลพen level ${this.currentLevel}!`,
+ '#FFD700'
+ );
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/StatusEffectSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/StatusEffectSystem.js
new file mode 100644
index 000000000..699eec70e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/StatusEffectSystem.js
@@ -0,0 +1,111 @@
+/**
+ * StatusEffectSystem.js
+ * ====================
+ * Manages temporary status effects for the player.
+ */
+
+class StatusEffectSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.activeEffects = new Map();
+
+ // Initial state
+ this.originalSpeed = 0;
+
+ console.log('๐ StatusEffectSystem initialized');
+ }
+
+ applyEffect(type, duration = 15000) {
+ if (this.activeEffects.has(type)) {
+ // Refresh duration
+ const effect = this.activeEffects.get(type);
+ effect.endTime = Date.now() + duration;
+ console.log(`โจ Refreshed effect: ${type}`);
+ return true;
+ }
+
+ const effect = {
+ type,
+ startTime: Date.now(),
+ endTime: Date.now() + duration
+ };
+
+ this.activeEffects.set(type, effect);
+ this.startEffect(type);
+ return true;
+ }
+
+ startEffect(type) {
+ console.log(`๐ Starting effect: ${type}`);
+
+ if (type === 'high') {
+ // Visual: Rainbow Tint
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene && uiScene.overlayGraphics) {
+ this.scene.tweens.addCounter({
+ from: 0,
+ to: 360,
+ duration: 4000,
+ repeat: -1,
+ onUpdate: (tween) => {
+ if (!this.activeEffects.has('high')) return;
+ const hull = Phaser.Display.Color.HSVToRGB(tween.getValue() / 360, 0.4, 1);
+ const alpha = 0.1 + Math.sin(tween.getValue() * 0.05) * 0.05; // Breathing alpha
+ uiScene.overlayGraphics.clear();
+ uiScene.overlayGraphics.fillStyle(hull.color, alpha);
+ uiScene.overlayGraphics.fillRect(0, 0, this.scene.scale.width, this.scene.scale.height);
+ }
+ });
+ }
+
+ // Gameplay: Movement slow
+ if (this.scene.player) {
+ this.originalSpeed = this.scene.player.moveSpeed;
+ this.scene.player.moveSpeed *= 0.6;
+ }
+
+ // Camera Shake/Waves?
+ this.scene.cameras.main.setLerp(0.05, 0.05); // Looser camera
+
+ // Audio: High Ambient
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playHighAmbient();
+ }
+ }
+ }
+
+ stopEffect(type) {
+ console.log(`๐ Stopping effect: ${type}`);
+ this.activeEffects.delete(type);
+
+ if (type === 'high') {
+ // Clear visual
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene && uiScene.overlayGraphics) {
+ uiScene.overlayGraphics.clear();
+ }
+
+ // Reset movement
+ if (this.scene.player) {
+ this.scene.player.moveSpeed = this.originalSpeed || 100;
+ }
+
+ // Reset camera
+ this.scene.cameras.main.setLerp(1, 1);
+
+ // Audio: Stop High Ambient
+ if (this.scene.soundManager) {
+ this.scene.soundManager.stopHighAmbient();
+ }
+ }
+ }
+
+ update(time, delta) {
+ const now = Date.now();
+ this.activeEffects.forEach((effect, type) => {
+ if (now >= effect.endTime) {
+ this.stopEffect(type);
+ }
+ });
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/SteamIntegrationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/SteamIntegrationSystem.js
new file mode 100644
index 000000000..f5687ae6d
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/SteamIntegrationSystem.js
@@ -0,0 +1,194 @@
+/**
+ * STEAM INTEGRATION SYSTEM
+ * Handles Steam achievements and cloud saves
+ *
+ * NOTE: Requires Steamworks SDK and Greenworks library
+ * This is a placeholder/mock implementation for development
+ */
+class SteamIntegrationSystem {
+ constructor() {
+ this.steamAvailable = false;
+ this.achievements = {};
+ this.cloudSaveEnabled = false;
+
+ // Mock achievement definitions
+ this.achievementDefs = {
+ 'FIRST_HARVEST': { name: 'First Harvest', desc: 'Harvest your first crop' },
+ 'GOLD_RUSH': { name: 'Gold Rush', desc: 'Earn 1000 gold coins' },
+ 'ZOMBIE_SLAYER': { name: 'Zombie Slayer', desc: 'Kill 100 zombies' },
+ 'MASTER_FARMER': { name: 'Master Farmer', desc: 'Harvest 1000 crops' },
+ 'DAY_30': { name: 'Survivor', desc: 'Survive 30 days' },
+ 'GREENHOUSE': { name: 'Greenhouse Builder', desc: 'Build a greenhouse' },
+ 'TAMED_ZOMBIE': { name: 'Zombie Tamer', desc: 'Tame your first zombie' },
+ 'OCEAN_EXPLORER': { name: 'Ocean Explorer', desc: 'Discover 5 islands' }
+ };
+
+ this.init();
+ }
+
+ init() {
+ // Check if Steamworks is available
+ if (typeof greenworks !== 'undefined') {
+ try {
+ greenworks.init();
+ this.steamAvailable = true;
+ this.cloudSaveEnabled = greenworks.isCloudEnabled();
+ console.log('โ
Steam Integration: Active');
+ console.log(`โ๏ธ Cloud Saves: ${this.cloudSaveEnabled ? 'Enabled' : 'Disabled'}`);
+ } catch (e) {
+ console.warn('โ ๏ธ Steam Integration: Failed to initialize', e);
+ this.steamAvailable = false;
+ }
+ } else {
+ console.log('โน๏ธ Steam Integration: Not available (running outside Steam)');
+ }
+ }
+
+ /**
+ * Unlock achievement
+ */
+ unlockAchievement(achievementId) {
+ if (!this.achievementDefs[achievementId]) {
+ console.error('Unknown achievement:', achievementId);
+ return false;
+ }
+
+ const achievement = this.achievementDefs[achievementId];
+
+ if (this.steamAvailable && typeof greenworks !== 'undefined') {
+ try {
+ greenworks.activateAchievement(achievementId, () => {
+ console.log(`๐ Achievement Unlocked: ${achievement.name}`);
+ }, (err) => {
+ console.error('Failed to unlock achievement:', err);
+ });
+ } catch (e) {
+ console.error('Steam achievement error:', e);
+ }
+ } else {
+ // Mock/local achievement
+ if (!this.achievements[achievementId]) {
+ this.achievements[achievementId] = true;
+ console.log(`๐ [MOCK] Achievement Unlocked: ${achievement.name}`);
+
+ // Show notification
+ if (this.scene && this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: 320,
+ y: 100,
+ text: `๐ ${achievement.name}`,
+ color: '#ffd700'
+ });
+ }
+
+ // Save to localStorage
+ this.saveLocalAchievements();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Save game to Steam Cloud
+ */
+ saveToCloud(saveData) {
+ if (!this.cloudSaveEnabled) {
+ console.log('โ๏ธ Cloud saves not available, saving locally');
+ localStorage.setItem('novafarma_save', JSON.stringify(saveData));
+ return false;
+ }
+
+ try {
+ const saveString = JSON.stringify(saveData);
+ greenworks.saveTextToFile('novafarma_save.json', saveString, () => {
+ console.log('โ๏ธ Game saved to Steam Cloud');
+ }, (err) => {
+ console.error('Failed to save to cloud:', err);
+ // Fallback to local
+ localStorage.setItem('novafarma_save', saveString);
+ });
+ return true;
+ } catch (e) {
+ console.error('Cloud save error:', e);
+ return false;
+ }
+ }
+
+ /**
+ * Load game from Steam Cloud
+ */
+ loadFromCloud(callback) {
+ if (!this.cloudSaveEnabled) {
+ console.log('โ๏ธ Cloud saves not available, loading locally');
+ const localSave = localStorage.getItem('novafarma_save');
+ if (localSave && callback) {
+ callback(JSON.parse(localSave));
+ }
+ return;
+ }
+
+ try {
+ greenworks.readTextFromFile('novafarma_save.json', (data) => {
+ console.log('โ๏ธ Loaded from Steam Cloud');
+ if (callback) callback(JSON.parse(data));
+ }, (err) => {
+ console.error('Failed to load from cloud:', err);
+ // Fallback to local
+ const localSave = localStorage.getItem('novafarma_save');
+ if (localSave && callback) {
+ callback(JSON.parse(localSave));
+ }
+ });
+ } catch (e) {
+ console.error('Cloud load error:', e);
+ }
+ }
+
+ /**
+ * Get achievement status
+ */
+ getAchievementStatus(achievementId) {
+ return this.achievements[achievementId] || false;
+ }
+
+ /**
+ * Get all unlocked achievements
+ */
+ getUnlockedAchievements() {
+ return Object.keys(this.achievements).filter(id => this.achievements[id]);
+ }
+
+ /**
+ * Save achievements to localStorage (fallback)
+ */
+ saveLocalAchievements() {
+ localStorage.setItem('novafarma_achievements', JSON.stringify(this.achievements));
+ }
+
+ /**
+ * Load achievements from localStorage
+ */
+ loadLocalAchievements() {
+ const saved = localStorage.getItem('novafarma_achievements');
+ if (saved) {
+ this.achievements = JSON.parse(saved);
+ }
+ }
+
+ /**
+ * Set overlay position (for Steam overlay)
+ */
+ activateOverlay(dialog = 'Friends') {
+ if (this.steamAvailable && typeof greenworks !== 'undefined') {
+ try {
+ greenworks.activateGameOverlay(dialog);
+ } catch (e) {
+ console.error('Failed to activate Steam overlay:', e);
+ }
+ }
+ }
+}
+
+// Global instance
+window.SteamIntegration = SteamIntegrationSystem;
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/StoryQuestSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/StoryQuestSystem.js
new file mode 100644
index 000000000..3135e65ce
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/StoryQuestSystem.js
@@ -0,0 +1,506 @@
+/**
+ * STORY & QUEST SYSTEM
+ * Complete quest system with story acts, dialogue, cutscenes, and multiple endings
+ */
+class StoryQuestSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Quests
+ this.quests = new Map();
+ this.activeQuests = new Set();
+ this.completedQuests = new Set();
+
+ // Story progress
+ this.currentAct = 1;
+ this.storyFlags = new Set();
+
+ // Characters
+ this.characters = new Map();
+
+ // Dialogue
+ this.currentDialogue = null;
+
+ // Endings
+ this.playerChoices = [];
+ this.ending = null;
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Story & Quest System initialized');
+ }
+
+ init() {
+ this.defineCharacters();
+ this.defineQuests();
+ console.log('๐ Story & Quest system ready');
+ }
+
+ // ========== CHARACTERS ==========
+
+ defineCharacters() {
+ this.characters.set('jakob', {
+ name: 'Old Jakob',
+ role: 'Merchant',
+ relationship: 0,
+ dialogues: new Map()
+ });
+
+ this.characters.set('lyra', {
+ name: 'Lyra',
+ role: 'Mutant Elf',
+ relationship: 0,
+ dialogues: new Map()
+ });
+
+ this.characters.set('grok', {
+ name: 'Grok',
+ role: 'Troll Guardian',
+ relationship: 0,
+ dialogues: new Map()
+ });
+
+ this.characters.set('dr_chen', {
+ name: 'Dr. Chen',
+ role: 'Radio Voice',
+ relationship: 0,
+ dialogues: new Map()
+ });
+ }
+
+ // ========== QUESTS ==========
+
+ defineQuests() {
+ // ACT 1: Survival (Day 1-10)
+ this.defineQuest('first_harvest', {
+ name: 'Prvi Pridelek',
+ act: 1,
+ description: 'Harvest 10 wheat to survive',
+ objectives: [
+ { type: 'harvest', item: 'wheat', amount: 10, current: 0 }
+ ],
+ rewards: { xp: 100, item: 'iron_hoe' },
+ unlocks: ['safe_haven']
+ });
+
+ this.defineQuest('safe_haven', {
+ name: 'Varno Zatoฤiลกฤe',
+ act: 1,
+ description: 'Build fence around farm',
+ objectives: [
+ { type: 'build', item: 'fence', amount: 20, current: 0 }
+ ],
+ rewards: { xp: 150, blueprint: 'reinforced_fence' },
+ unlocks: ['night_watch']
+ });
+
+ this.defineQuest('night_watch', {
+ name: 'Noฤna Straลพa',
+ act: 1,
+ description: 'Survive first zombie night',
+ objectives: [
+ { type: 'survive', nights: 1, current: 0 }
+ ],
+ rewards: { xp: 200, item: 'torch_pack' },
+ unlocks: ['meet_merchant']
+ });
+
+ this.defineQuest('meet_merchant', {
+ name: 'Meet the Merchant',
+ act: 1,
+ description: 'Find Jakob the trader',
+ objectives: [
+ { type: 'talk', npc: 'jakob' }
+ ],
+ rewards: { xp: 100, unlocks: 'trading' },
+ unlocks: ['strange_transmission']
+ });
+
+ // ACT 2: Discovery (Day 11-20)
+ this.defineQuest('strange_transmission', {
+ name: 'Strange Transmission',
+ act: 2,
+ description: 'Find radio in city',
+ objectives: [
+ { type: 'find', item: 'radio', location: 'city_ruins' }
+ ],
+ rewards: { xp: 300, item: 'radio' },
+ unlocks: ['first_tame']
+ });
+
+ this.defineQuest('first_tame', {
+ name: 'Prvi Poskus',
+ act: 2,
+ description: 'Tame first zombie',
+ objectives: [
+ { type: 'tame', creature: 'zombie', amount: 1 }
+ ],
+ rewards: { xp: 250, unlocks: 'zombie_workers' },
+ unlocks: ['lab_ruins']
+ });
+
+ this.defineQuest('lab_ruins', {
+ name: 'Lab Ruins',
+ act: 2,
+ description: 'Explore abandoned research facility',
+ objectives: [
+ { type: 'explore', location: 'research_lab' }
+ ],
+ rewards: { xp: 400, item: 'lab_key' },
+ unlocks: ['mutant_contact']
+ });
+
+ this.defineQuest('mutant_contact', {
+ name: 'Mutant Contact',
+ act: 2,
+ description: 'Meet friendly mutant Lyra',
+ objectives: [
+ { type: 'talk', npc: 'lyra' }
+ ],
+ rewards: { xp: 300, unlocks: 'mutation_research' },
+ unlocks: ['lab_notes']
+ });
+
+ // ACT 3: The Truth (Day 21-30)
+ this.defineQuest('lab_notes', {
+ name: 'Lab Notes',
+ act: 3,
+ description: 'Collect 5 research documents',
+ objectives: [
+ { type: 'collect', item: 'research_document', amount: 5, current: 0 }
+ ],
+ rewards: { xp: 500 },
+ unlocks: ['patient_zero']
+ });
+
+ this.defineQuest('patient_zero', {
+ name: 'Patient Zero',
+ act: 3,
+ description: 'Find virus source',
+ objectives: [
+ { type: 'find', item: 'virus_sample', location: 'deep_lab' }
+ ],
+ rewards: { xp: 600 },
+ unlocks: ['difficult_choice']
+ });
+
+ this.defineQuest('difficult_choice', {
+ name: 'Difficult Choice',
+ act: 3,
+ description: 'Choose faction',
+ objectives: [
+ { type: 'choice', options: ['human', 'zombie', 'hybrid'] }
+ ],
+ rewards: { xp: 1000 },
+ unlocks: ['final_confrontation']
+ });
+
+ this.defineQuest('final_confrontation', {
+ name: 'Final Confrontation',
+ act: 3,
+ description: 'Boss battle',
+ objectives: [
+ { type: 'defeat', boss: 'zombie_king' }
+ ],
+ rewards: { xp: 2000 },
+ ending: true
+ });
+ }
+
+ defineQuest(id, data) {
+ this.quests.set(id, {
+ id,
+ active: false,
+ completed: false,
+ ...data
+ });
+ }
+
+ // ========== QUEST MANAGEMENT ==========
+
+ startQuest(questId) {
+ const quest = this.quests.get(questId);
+ if (!quest || quest.active || quest.completed) return false;
+
+ quest.active = true;
+ this.activeQuests.add(questId);
+
+ console.log(`๐ Quest started: ${quest.name}`);
+ return true;
+ }
+
+ updateQuestProgress(questId, objectiveIndex, progress) {
+ const quest = this.quests.get(questId);
+ if (!quest || !quest.active) return;
+
+ const objective = quest.objectives[objectiveIndex];
+ if (!objective) return;
+
+ objective.current = progress;
+
+ // Check if objective complete
+ if (this.isObjectiveComplete(objective)) {
+ console.log(`โ
Objective complete: ${objective.type}`);
+
+ // Check if all objectives complete
+ if (quest.objectives.every(obj => this.isObjectiveComplete(obj))) {
+ this.completeQuest(questId);
+ }
+ }
+ }
+
+ isObjectiveComplete(objective) {
+ switch (objective.type) {
+ case 'harvest':
+ case 'build':
+ case 'collect':
+ return objective.current >= objective.amount;
+ case 'talk':
+ case 'find':
+ case 'explore':
+ case 'defeat':
+ return objective.current === true;
+ case 'survive':
+ return objective.current >= objective.nights;
+ default:
+ return false;
+ }
+ }
+
+ completeQuest(questId) {
+ const quest = this.quests.get(questId);
+ if (!quest) return;
+
+ quest.active = false;
+ quest.completed = true;
+ this.activeQuests.delete(questId);
+ this.completedQuests.add(questId);
+
+ // Grant rewards
+ this.grantQuestRewards(quest);
+
+ // Unlock next quests
+ if (quest.unlocks) {
+ const unlocks = Array.isArray(quest.unlocks) ? quest.unlocks : [quest.unlocks];
+ unlocks.forEach(nextQuest => this.startQuest(nextQuest));
+ }
+
+ // Check for ending
+ if (quest.ending) {
+ this.triggerEnding();
+ }
+
+ console.log(`๐ Quest completed: ${quest.name}!`);
+ this.saveProgress();
+ }
+
+ grantQuestRewards(quest) {
+ const rewards = quest.rewards;
+
+ if (rewards.xp && this.scene.skillTree) {
+ this.scene.skillTree.addXP(rewards.xp);
+ }
+
+ if (rewards.item && this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(rewards.item, 1);
+ }
+
+ if (rewards.blueprint) {
+ console.log(`๐ Unlocked blueprint: ${rewards.blueprint}`);
+ }
+ }
+
+ // ========== DIALOGUE SYSTEM ==========
+
+ startDialogue(npcId, dialogueId) {
+ const character = this.characters.get(npcId);
+ if (!character) return false;
+
+ this.currentDialogue = {
+ npc: npcId,
+ dialogueId,
+ currentNode: 0,
+ choices: []
+ };
+
+ console.log(`๐ฌ Dialogue started with ${character.name}`);
+ return true;
+ }
+
+ selectDialogueChoice(choiceIndex) {
+ if (!this.currentDialogue) return;
+
+ const choice = this.currentDialogue.choices[choiceIndex];
+ if (!choice) return;
+
+ // Track player choice
+ this.playerChoices.push({
+ npc: this.currentDialogue.npc,
+ choice: choice.text,
+ consequence: choice.consequence
+ });
+
+ // Update relationship
+ const character = this.characters.get(this.currentDialogue.npc);
+ if (character && choice.relationshipDelta) {
+ character.relationship += choice.relationshipDelta;
+ }
+
+ // Apply consequences
+ if (choice.consequence) {
+ this.applyChoiceConsequence(choice.consequence);
+ }
+
+ console.log(`๐ฌ Choice selected: ${choice.text}`);
+ }
+
+ applyChoiceConsequence(consequence) {
+ // Set story flags
+ if (consequence.flag) {
+ this.storyFlags.add(consequence.flag);
+ }
+
+ // Unlock quests
+ if (consequence.unlockQuest) {
+ this.startQuest(consequence.unlockQuest);
+ }
+
+ // Change faction
+ if (consequence.faction) {
+ this.playerChoices.push({ type: 'faction', value: consequence.faction });
+ }
+ }
+
+ endDialogue() {
+ this.currentDialogue = null;
+ }
+
+ // ========== CUTSCENES ==========
+
+ playCutscene(cutsceneId) {
+ console.log(`๐ฌ Playing cutscene: ${cutsceneId}`);
+
+ const cutscenes = {
+ 'arrival': this.cutsceneArrival.bind(this),
+ 'first_zombie': this.cutsceneFirstZombie.bind(this),
+ 'city_discovery': this.cutsceneCityDiscovery.bind(this),
+ 'boss_reveal': this.cutsceneBossReveal.bind(this)
+ };
+
+ const cutscene = cutscenes[cutsceneId];
+ if (cutscene) {
+ cutscene();
+ }
+ }
+
+ cutsceneArrival() {
+ console.log('๐ฌ Arrival cutscene - Farm overview');
+ }
+
+ cutsceneFirstZombie() {
+ console.log('๐ฌ First zombie encounter - Tutorial');
+ }
+
+ cutsceneCityDiscovery() {
+ console.log('๐ฌ City discovery - Ruins pan');
+ }
+
+ cutsceneBossReveal() {
+ console.log('๐ฌ Boss reveal - Zombie King emergence');
+ }
+
+ // ========== ENDINGS ==========
+
+ triggerEnding() {
+ // Determine ending based on player choices
+ const faction = this.getPlayerFaction();
+ const relationships = this.getRelationships();
+
+ if (faction === 'human' && relationships.jakob > 50) {
+ this.ending = 'cure';
+ this.playCutscene('cure_ending');
+ } else if (faction === 'zombie') {
+ this.ending = 'zombie_king';
+ this.playCutscene('zombie_king_ending');
+ } else if (faction === 'hybrid') {
+ this.ending = 'mutation';
+ this.playCutscene('mutation_ending');
+ } else if (relationships.lyra > 70) {
+ this.ending = 'escape';
+ this.playCutscene('escape_ending');
+ } else {
+ this.ending = 'farmer';
+ this.playCutscene('farmer_ending');
+ }
+
+ console.log(`๐ฌ Ending: ${this.ending}`);
+ this.saveProgress();
+ }
+
+ getPlayerFaction() {
+ const factionChoice = this.playerChoices.find(c => c.type === 'faction');
+ return factionChoice ? factionChoice.value : null;
+ }
+
+ getRelationships() {
+ const relationships = {};
+ for (const [id, character] of this.characters.entries()) {
+ relationships[id] = character.relationship;
+ }
+ return relationships;
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ currentAct: this.currentAct,
+ activeQuests: Array.from(this.activeQuests),
+ completedQuests: Array.from(this.completedQuests),
+ storyFlags: Array.from(this.storyFlags),
+ playerChoices: this.playerChoices,
+ ending: this.ending,
+ characters: Array.from(this.characters.entries()).map(([id, char]) => ({
+ id,
+ relationship: char.relationship
+ }))
+ };
+
+ localStorage.setItem('novafarma_story', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_story');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.currentAct = data.currentAct || 1;
+ this.activeQuests = new Set(data.activeQuests || []);
+ this.completedQuests = new Set(data.completedQuests || []);
+ this.storyFlags = new Set(data.storyFlags || []);
+ this.playerChoices = data.playerChoices || [];
+ this.ending = data.ending || null;
+
+ if (data.characters) {
+ data.characters.forEach(char => {
+ const character = this.characters.get(char.id);
+ if (character) {
+ character.relationship = char.relationship;
+ }
+ });
+ }
+
+ console.log('โ
Story progress loaded');
+ } catch (error) {
+ console.error('Failed to load story progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐ Story & Quest System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/StructureInteractionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/StructureInteractionSystem.js
new file mode 100644
index 000000000..6f48bfd3b
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/StructureInteractionSystem.js
@@ -0,0 +1,371 @@
+/**
+ * ๐๏ธ STRUCTURE INTERACTION SYSTEM
+ * Enables player interaction with structures in the world
+ * - Enter buildings
+ * - Loot chests
+ * - Landmark treasures
+ * - Lock/unlock mechanics
+ */
+
+class StructureInteractionSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Interaction markers
+ this.interactionMarkers = [];
+
+ // Loot chests (generated per structure)
+ this.chests = new Map(); // key: "x,y" โ chest data
+
+ // Active interactions
+ this.nearbyStructures = [];
+ this.currentInteraction = null;
+
+ // Loot tables per biome
+ this.lootTables = {
+ 'Grassland': [
+ { item: 'wheat_seeds', min: 5, max: 15, chance: 0.7 },
+ { item: 'wood', min: 10, max: 30, chance: 0.8 },
+ { item: 'gold', min: 20, max: 50, chance: 0.5 },
+ { item: 'iron_ore', min: 1, max: 5, chance: 0.3 }
+ ],
+ 'Forest': [
+ { item: 'wood', min: 20, max: 50, chance: 0.9 },
+ { item: 'apple', min: 3, max: 10, chance: 0.6 },
+ { item: 'berry', min: 5, max: 15, chance: 0.7 },
+ { item: 'mushroom', min: 2, max: 8, chance: 0.4 }
+ ],
+ 'Desert': [
+ { item: 'gold', min: 50, max: 150, chance: 0.6 },
+ { item: 'ruby', min: 1, max: 3, chance: 0.2 },
+ { item: 'ancient_scroll', min: 1, max: 1, chance: 0.1 },
+ { item: 'cactus_fruit', min: 3, max: 10, chance: 0.5 }
+ ],
+ 'Mountain': [
+ { item: 'iron_ore', min: 10, max: 30, chance: 0.8 },
+ { item: 'gold_ore', min: 5, max: 15, chance: 0.6 },
+ { item: 'diamond', min: 1, max: 3, chance: 0.15 },
+ { item: 'stone', min: 20, max: 50, chance: 0.9 }
+ ],
+ 'Swamp': [
+ { item: 'herbs', min: 5, max: 20, chance: 0.7 },
+ { item: 'mushroom', min: 10, max: 30, chance: 0.8 },
+ { item: 'slime', min: 5, max: 15, chance: 0.6 },
+ { item: 'ancient_bone', min: 1, max: 5, chance: 0.3 }
+ ]
+ };
+
+ // Landmark treasure (special rare items)
+ this.landmarkTreasures = {
+ 'ancient_temple': [
+ { item: 'legendary_sword', min: 1, max: 1, chance: 1.0 },
+ { item: 'gold', min: 500, max: 1000, chance: 1.0 },
+ { item: 'ancient_artifact', min: 1, max: 1, chance: 1.0 }
+ ],
+ 'great_pyramid': [
+ { item: 'pharaoh_staff', min: 1, max: 1, chance: 1.0 },
+ { item: 'ruby', min: 10, max: 20, chance: 1.0 },
+ { item: 'mummy_wraps', min: 5, max: 10, chance: 1.0 }
+ ],
+ 'mountain_peak': [
+ { item: 'titan_hammer', min: 1, max: 1, chance: 1.0 },
+ { item: 'diamond', min: 10, max: 20, chance: 1.0 },
+ { item: 'eagle_feather', min: 1, max: 1, chance: 1.0 }
+ ],
+ 'abandoned_city': [
+ { item: 'ancient_key', min: 1, max: 1, chance: 1.0 },
+ { item: 'gold', min: 1000, max: 2000, chance: 1.0 },
+ { item: 'city_map', min: 1, max: 1, chance: 1.0 }
+ ],
+ 'dragon_skeleton': [
+ { item: 'dragon_scale', min: 5, max: 10, chance: 1.0 },
+ { item: 'dragon_tooth', min: 1, max: 3, chance: 1.0 },
+ { item: 'dragon_heart', min: 1, max: 1, chance: 1.0 }
+ ]
+ };
+
+ console.log('๐๏ธ StructureInteractionSystem initialized');
+ }
+
+ // Generate chests for all structures
+ generateChestsForStructures(structureSystem) {
+ if (!structureSystem) return;
+
+ let chestsGenerated = 0;
+
+ // Regular structures
+ for (const structure of structureSystem.structures) {
+ // 70% chance to have a chest
+ if (Math.random() < 0.7) {
+ const chestKey = `${structure.x},${structure.y}`;
+ this.chests.set(chestKey, {
+ x: structure.x,
+ y: structure.y,
+ biome: structure.biome,
+ opened: false,
+ loot: this.generateLoot(structure.biome)
+ });
+ chestsGenerated++;
+ }
+ }
+
+ // Landmarks (always have treasure)
+ for (const landmark of structureSystem.landmarks) {
+ const chestKey = `${landmark.x},${landmark.y}`;
+ this.chests.set(chestKey, {
+ x: landmark.x,
+ y: landmark.y,
+ type: 'landmark',
+ landmarkType: landmark.type,
+ opened: false,
+ loot: this.generateLandmarkTreasure(landmark.type)
+ });
+ chestsGenerated++;
+ }
+
+ console.log(`โ
Generated ${chestsGenerated} chests (${this.chests.size} total)`);
+ }
+
+ // Generate loot based on biome
+ generateLoot(biome) {
+ const lootTable = this.lootTables[biome] || this.lootTables['Grassland'];
+ const loot = [];
+
+ for (const entry of lootTable) {
+ if (Math.random() < entry.chance) {
+ const amount = Math.floor(Math.random() * (entry.max - entry.min + 1)) + entry.min;
+ loot.push({
+ item: entry.item,
+ amount: amount
+ });
+ }
+ }
+
+ return loot;
+ }
+
+ // Generate landmark treasure (always guaranteed)
+ generateLandmarkTreasure(landmarkType) {
+ const treasureTable = this.landmarkTreasures[landmarkType];
+ if (!treasureTable) return [];
+
+ const loot = [];
+ for (const entry of treasureTable) {
+ const amount = Math.floor(Math.random() * (entry.max - entry.min + 1)) + entry.min;
+ loot.push({
+ item: entry.item,
+ amount: amount,
+ legendary: true // Mark as legendary loot
+ });
+ }
+
+ return loot;
+ }
+
+ // Check for nearby structures
+ update(playerX, playerY) {
+ this.nearbyStructures = [];
+
+ // Check all chests
+ for (const [key, chest] of this.chests) {
+ const dist = Math.sqrt((chest.x - playerX) ** 2 + (chest.y - playerY) ** 2);
+
+ if (dist < 3 && !chest.opened) {
+ this.nearbyStructures.push({
+ type: chest.type === 'landmark' ? 'landmark' : 'structure',
+ x: chest.x,
+ y: chest.y,
+ key: key,
+ data: chest
+ });
+ }
+ }
+
+ // Show interaction prompt if nearby
+ if (this.nearbyStructures.length > 0 && !this.currentInteraction) {
+ this.showInteractionPrompt();
+ } else if (this.nearbyStructures.length === 0) {
+ this.hideInteractionPrompt();
+ }
+ }
+
+ // Show UI prompt to interact
+ showInteractionPrompt() {
+ if (this.interactionPrompt) return;
+
+ const nearest = this.nearbyStructures[0];
+ const promptText = nearest.type === 'landmark'
+ ? 'โญ Press E to open LEGENDARY TREASURE'
+ : '๐ฆ Press E to open chest';
+
+ this.interactionPrompt = this.scene.add.text(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.height - 100,
+ promptText,
+ {
+ fontSize: '24px',
+ fontFamily: 'Arial',
+ color: nearest.type === 'landmark' ? '#FFD700' : '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 20, y: 10 }
+ }
+ );
+ this.interactionPrompt.setOrigin(0.5);
+ this.interactionPrompt.setScrollFactor(0);
+ this.interactionPrompt.setDepth(10000);
+ }
+
+ // Hide interaction prompt
+ hideInteractionPrompt() {
+ if (this.interactionPrompt) {
+ this.interactionPrompt.destroy();
+ this.interactionPrompt = null;
+ }
+ }
+
+ // Player presses E to interact
+ interact() {
+ if (this.nearbyStructures.length === 0) return;
+
+ const nearest = this.nearbyStructures[0];
+ this.openChest(nearest.key, nearest.data);
+ }
+
+ // Open chest and give loot to player
+ openChest(chestKey, chestData) {
+ if (chestData.opened) return;
+
+ // Mark as opened
+ chestData.opened = true;
+ this.chests.set(chestKey, chestData);
+
+ // Give loot to player
+ const inventory = this.scene.inventorySystem;
+ if (!inventory) {
+ console.warn('โ ๏ธ InventorySystem not found');
+ return;
+ }
+
+ console.log(`๐ฆ Opening chest at (${chestData.x}, ${chestData.y})`);
+
+ let totalValue = 0;
+ for (const lootItem of chestData.loot) {
+ if (lootItem.item === 'gold') {
+ inventory.gold += lootItem.amount;
+ totalValue += lootItem.amount;
+ } else {
+ inventory.addItem(lootItem.item, lootItem.amount);
+ totalValue += lootItem.amount * 10; // Estimate value
+ }
+
+ console.log(` + ${lootItem.amount}x ${lootItem.item}${lootItem.legendary ? ' (LEGENDARY!)' : ''}`);
+ }
+
+ // Show loot notification
+ this.showLootNotification(chestData, totalValue);
+
+ // Play sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.beepPickup();
+ }
+
+ // Hide prompt
+ this.hideInteractionPrompt();
+ }
+
+ // Show loot notification UI
+ showLootNotification(chestData, totalValue) {
+ const isLandmark = chestData.type === 'landmark';
+
+ const notification = this.scene.add.text(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.centerY - 100,
+ isLandmark
+ ? `โญ LEGENDARY TREASURE FOUND! โญ\n${chestData.landmarkType}\nValue: ${totalValue} gold`
+ : `๐ฆ Chest Opened!\nValue: ${totalValue} gold`,
+ {
+ fontSize: isLandmark ? '32px' : '24px',
+ fontFamily: 'Arial',
+ color: isLandmark ? '#FFD700' : '#ffffff',
+ backgroundColor: '#000000',
+ padding: { x: 30, y: 20 },
+ align: 'center'
+ }
+ );
+ notification.setOrigin(0.5);
+ notification.setScrollFactor(0);
+ notification.setDepth(10001);
+
+ // Fade out after 3 seconds
+ this.scene.tweens.add({
+ targets: notification,
+ alpha: 0,
+ duration: 1000,
+ delay: 2000,
+ onComplete: () => notification.destroy()
+ });
+
+ // Particle effect
+ if (this.scene.particleEffects && isLandmark) {
+ // Golden particles for landmarks
+ for (let i = 0; i < 20; i++) {
+ const particle = this.scene.add.circle(
+ this.scene.cameras.main.centerX + (Math.random() - 0.5) * 100,
+ this.scene.cameras.main.centerY - 100,
+ 5,
+ 0xFFD700
+ );
+ particle.setScrollFactor(0);
+ particle.setDepth(10000);
+
+ this.scene.tweens.add({
+ targets: particle,
+ y: particle.y - 100,
+ alpha: 0,
+ duration: 1500,
+ onComplete: () => particle.destroy()
+ });
+ }
+ }
+ }
+
+ // Get statistics
+ getStats() {
+ const opened = Array.from(this.chests.values()).filter(c => c.opened).length;
+ return {
+ totalChests: this.chests.size,
+ chestsOpened: opened,
+ chestsRemaining: this.chests.size - opened
+ };
+ }
+
+ // Export/Import for save system
+ exportData() {
+ const chestsArray = [];
+ for (const [key, chest] of this.chests) {
+ chestsArray.push({
+ key,
+ opened: chest.opened
+ });
+ }
+ return { chests: chestsArray };
+ }
+
+ importData(data) {
+ if (!data || !data.chests) return;
+
+ for (const savedChest of data.chests) {
+ if (this.chests.has(savedChest.key)) {
+ const chest = this.chests.get(savedChest.key);
+ chest.opened = savedChest.opened;
+ this.chests.set(savedChest.key, chest);
+ }
+ }
+ }
+
+ destroy() {
+ this.hideInteractionPrompt();
+ this.interactionMarkers.forEach(m => m.destroy());
+ this.chests.clear();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/StructureSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/StructureSystem.js
new file mode 100644
index 000000000..6049ec7a1
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/StructureSystem.js
@@ -0,0 +1,394 @@
+/**
+ * ๐๏ธ STRUCTURE SYSTEM
+ * Generates and manages structures across the 500x500 world
+ * - Creates buildings, ruins, landmarks across biomes
+ * - Handles roads and paths between biomes
+ * - Biome-aware structure placement
+ */
+
+class StructureSystem {
+ constructor(worldWidth, worldHeight, biomeSystem, riverSystem, lakeSystem) {
+ this.worldWidth = worldWidth;
+ this.worldHeight = worldHeight;
+ this.biomeSystem = biomeSystem;
+ this.riverSystem = riverSystem;
+ this.lakeSystem = lakeSystem;
+
+ // Structure map (marks where structures are)
+ this.structureMap = Array(worldHeight).fill(null).map(() => Array(worldWidth).fill(null));
+
+ // Road map (marks where roads are)
+ this.roadMap = Array(worldHeight).fill(null).map(() => Array(worldWidth).fill(false));
+
+ // List of all structures
+ this.structures = [];
+
+ // List of all landmarks
+ this.landmarks = [];
+
+ // Road paths
+ this.roads = [];
+
+ // Structure types by biome
+ this.structureTypes = {
+ 'Grassland': ['farm', 'house', 'barn', 'windmill', 'well'],
+ 'Forest': ['cabin', 'ruins', 'treehouse', 'camp', 'shrine'],
+ 'Desert': ['pyramid', 'ruins', 'oasis_camp', 'tomb', 'pillar'],
+ 'Mountain': ['mine', 'cave', 'tower', 'ruins', 'altar'],
+ 'Swamp': ['hut', 'ruins', 'totem', 'bog_shrine', 'abandoned_dock']
+ };
+
+ // Landmark types (unique, 1-3 per world)
+ this.landmarkTypes = [
+ { type: 'ancient_temple', biome: 'Forest', count: 1 },
+ { type: 'great_pyramid', biome: 'Desert', count: 1 },
+ { type: 'mountain_peak', biome: 'Mountain', count: 1 },
+ { type: 'abandoned_city', biome: 'Grassland', count: 1 },
+ { type: 'dragon_skeleton', biome: 'Swamp', count: 1 }
+ ];
+ }
+
+ // Generate all structures and roads
+ generateAll() {
+ console.log('๐๏ธ StructureSystem: Starting structure generation...');
+
+ // 1. Generate roads between biome centers
+ this.generateRoads();
+
+ // 2. Generate landmarks (major points of interest)
+ this.generateLandmarks();
+
+ // 3. Generate regular structures
+ this.generateStructures();
+
+ console.log(`โ
Generated ${this.structures.length} structures, ${this.landmarks.length} landmarks, ${this.roads.length} roads`);
+ }
+
+ // Generate roads connecting biome centers
+ generateRoads() {
+ console.log('๐ค๏ธ Generating roads...');
+
+ // Find biome centers
+ const biomeLocations = {
+ 'Grassland': [],
+ 'Forest': [],
+ 'Desert': [],
+ 'Mountain': [],
+ 'Swamp': []
+ };
+
+ // Sample the world to find biome centers
+ const sampleRate = 50;
+ for (let x = 0; x < this.worldWidth; x += sampleRate) {
+ for (let y = 0; y < this.worldHeight; y += sampleRate) {
+ const biome = this.biomeSystem.getBiomeAt(x, y);
+ if (!biomeLocations[biome]) biomeLocations[biome] = [];
+ biomeLocations[biome].push({ x, y });
+ }
+ }
+
+ // Create main roads connecting different biomes
+ const roadPoints = [];
+
+ // Central hub (spawn point)
+ const centerX = Math.floor(this.worldWidth / 2);
+ const centerY = Math.floor(this.worldHeight / 2);
+ roadPoints.push({ x: centerX, y: centerY, name: 'Center' });
+
+ // Add one major location per biome
+ for (const [biomeName, locations] of Object.entries(biomeLocations)) {
+ if (locations.length > 0) {
+ // Pick central location
+ const centerIdx = Math.floor(locations.length / 2);
+ roadPoints.push({
+ x: locations[centerIdx].x,
+ y: locations[centerIdx].y,
+ name: `${biomeName} Hub`
+ });
+ }
+ }
+
+ // Connect road points
+ for (let i = 0; i < roadPoints.length; i++) {
+ const start = roadPoints[i];
+
+ // Connect to nearest 2-3 other points
+ const distances = roadPoints
+ .map((point, idx) => ({
+ idx,
+ dist: Math.sqrt((point.x - start.x) ** 2 + (point.y - start.y) ** 2)
+ }))
+ .filter(d => d.idx !== i)
+ .sort((a, b) => a.dist - b.dist);
+
+ // Connect to 1-2 nearest points
+ const connectCount = Math.min(2, distances.length);
+ for (let j = 0; j < connectCount; j++) {
+ const end = roadPoints[distances[j].idx];
+ this.createRoad(start, end);
+ }
+ }
+ }
+
+ // Create a road between two points
+ createRoad(start, end) {
+ const path = [];
+ const dx = end.x - start.x;
+ const dy = end.y - start.y;
+ const steps = Math.max(Math.abs(dx), Math.abs(dy));
+
+ // Create path with some randomness (looks more natural)
+ for (let i = 0; i <= steps; i++) {
+ const t = i / steps;
+ let x = Math.round(start.x + dx * t);
+ let y = Math.round(start.y + dy * t);
+
+ // Add slight curve (Perlin-like noise)
+ const noise = Math.sin(t * Math.PI * 3) * 10;
+ x += Math.round(noise);
+
+ path.push({ x, y });
+ }
+
+ // Mark road tiles (3 tiles wide)
+ for (const point of path) {
+ for (let offsetX = -1; offsetX <= 1; offsetX++) {
+ for (let offsetY = -1; offsetY <= 1; offsetY++) {
+ const x = point.x + offsetX;
+ const y = point.y + offsetY;
+
+ if (x >= 0 && x < this.worldWidth && y >= 0 && y < this.worldHeight) {
+ // Don't place roads over water
+ if (!this.riverSystem.isRiver(x, y) && !this.lakeSystem.isLake(x, y)) {
+ this.roadMap[y][x] = true;
+ }
+ }
+ }
+ }
+ }
+
+ this.roads.push({
+ start: start.name || 'unknown',
+ end: end.name || 'unknown',
+ path
+ });
+ }
+
+ // Generate landmarks (unique structures)
+ generateLandmarks() {
+ console.log('๐ฟ Generating landmarks...');
+
+ for (const landmarkDef of this.landmarkTypes) {
+ for (let i = 0; i < landmarkDef.count; i++) {
+ const location = this.findBiomeLocation(landmarkDef.biome, 100, 100);
+ if (location) {
+ this.createLandmark(landmarkDef.type, location.x, location.y);
+ }
+ }
+ }
+ }
+
+ // Create a single landmark
+ createLandmark(type, x, y) {
+ const size = 15; // Landmarks are large (15x15)
+
+ // Mark area as occupied
+ for (let dx = -size; dx <= size; dx++) {
+ for (let dy = -size; dy <= size; dy++) {
+ const tx = x + dx;
+ const ty = y + dy;
+ if (tx >= 0 && tx < this.worldWidth && ty >= 0 && ty < this.worldHeight) {
+ this.structureMap[ty][tx] = { type: 'landmark', landmarkType: type };
+ }
+ }
+ }
+
+ this.landmarks.push({
+ type,
+ x,
+ y,
+ size,
+ discovered: false
+ });
+ }
+
+ // Generate regular structures
+ generateStructures() {
+ console.log('๐ Generating structures...');
+
+ const structureCount = 80; // 80 structures across the world
+ let placed = 0;
+ let attempts = 0;
+ const maxAttempts = structureCount * 10;
+
+ while (placed < structureCount && attempts < maxAttempts) {
+ attempts++;
+
+ // Random location
+ const x = Math.floor(Math.random() * this.worldWidth);
+ const y = Math.floor(Math.random() * this.worldHeight);
+
+ // Check if location is valid
+ if (this.canPlaceStructure(x, y, 20)) {
+ const biome = this.biomeSystem.getBiomeAt(x, y);
+ const types = this.structureTypes[biome];
+
+ if (types && types.length > 0) {
+ const type = types[Math.floor(Math.random() * types.length)];
+ this.createStructure(type, x, y, biome);
+ placed++;
+ }
+ }
+ }
+
+ console.log(`โ
Placed ${placed} structures (${attempts} attempts)`);
+ }
+
+ // Check if structure can be placed at location
+ canPlaceStructure(x, y, minDistance) {
+ // Check if on water
+ if (this.riverSystem.isRiver(x, y) || this.lakeSystem.isLake(x, y)) {
+ return false;
+ }
+
+ // Check if too close to other structures
+ for (const structure of this.structures) {
+ const dist = Math.sqrt((structure.x - x) ** 2 + (structure.y - y) ** 2);
+ if (dist < minDistance) {
+ return false;
+ }
+ }
+
+ // Check if too close to landmarks
+ for (const landmark of this.landmarks) {
+ const dist = Math.sqrt((landmark.x - x) ** 2 + (landmark.y - y) ** 2);
+ if (dist < 50) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ // Create a single structure
+ createStructure(type, x, y, biome) {
+ const size = this.getStructureSize(type);
+
+ // Mark area as occupied
+ for (let dx = -size; dx <= size; dx++) {
+ for (let dy = -size; dy <= size; dy++) {
+ const tx = x + dx;
+ const ty = y + dy;
+ if (tx >= 0 && tx < this.worldWidth && ty >= 0 && ty < this.worldHeight) {
+ this.structureMap[ty][tx] = { type: 'structure', structureType: type };
+ }
+ }
+ }
+
+ this.structures.push({
+ type,
+ x,
+ y,
+ size,
+ biome,
+ explored: false
+ });
+ }
+
+ // Get structure size
+ getStructureSize(type) {
+ const sizes = {
+ // Small structures
+ 'well': 2,
+ 'camp': 3,
+ 'totem': 2,
+ 'pillar': 2,
+
+ // Medium structures
+ 'house': 4,
+ 'cabin': 4,
+ 'hut': 3,
+ 'shrine': 4,
+ 'altar': 4,
+ 'tomb': 5,
+
+ // Large structures
+ 'farm': 7,
+ 'barn': 6,
+ 'windmill': 5,
+ 'ruins': 6,
+ 'mine': 5,
+ 'tower': 4,
+ 'pyramid': 8,
+ 'cave': 5,
+ 'treehouse': 4,
+ 'oasis_camp': 5,
+ 'bog_shrine': 4,
+ 'abandoned_dock': 5
+ };
+
+ return sizes[type] || 4;
+ }
+
+ // Find a location in specific biome
+ findBiomeLocation(biomeName, minDistance, maxAttempts) {
+ for (let i = 0; i < maxAttempts; i++) {
+ const x = Math.floor(Math.random() * this.worldWidth);
+ const y = Math.floor(Math.random() * this.worldHeight);
+
+ const biome = this.biomeSystem.getBiomeAt(x, y);
+
+ if (biome === biomeName && this.canPlaceStructure(x, y, minDistance)) {
+ return { x, y };
+ }
+ }
+
+ return null;
+ }
+
+ // Check if tile is a road
+ isRoad(x, y) {
+ if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) {
+ return false;
+ }
+ return this.roadMap[y][x];
+ }
+
+ // Get structure at tile
+ getStructure(x, y) {
+ if (x < 0 || x >= this.worldWidth || y < 0 || y >= this.worldHeight) {
+ return null;
+ }
+ return this.structureMap[y][x];
+ }
+
+ // Get statistics
+ getStats() {
+ return {
+ structures: this.structures.length,
+ landmarks: this.landmarks.length,
+ roads: this.roads.length,
+ roadTiles: this.roadMap.flat().filter(r => r).length
+ };
+ }
+
+ // Export data for saving
+ exportData() {
+ return {
+ structures: this.structures,
+ landmarks: this.landmarks,
+ roads: this.roads,
+ structureMap: this.structureMap,
+ roadMap: this.roadMap
+ };
+ }
+
+ // Import data from save
+ importData(data) {
+ this.structures = data.structures || [];
+ this.landmarks = data.landmarks || [];
+ this.roads = data.roads || [];
+ this.structureMap = data.structureMap || this.structureMap;
+ this.roadMap = data.roadMap || this.roadMap;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TechnicalPerformanceSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TechnicalPerformanceSystem.js
new file mode 100644
index 000000000..f212d56b0
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TechnicalPerformanceSystem.js
@@ -0,0 +1,429 @@
+/**
+ * TECHNICAL & PERFORMANCE SYSTEM
+ * Performance optimization, mod support, replay system, and debug tools
+ */
+class TechnicalPerformanceSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Performance monitoring
+ this.fpsHistory = [];
+ this.memoryHistory = [];
+ this.performanceStats = {
+ fps: 60,
+ memory: 0,
+ drawCalls: 0,
+ entities: 0
+ };
+
+ // Entity pooling
+ this.entityPools = new Map();
+
+ // Mod support
+ this.loadedMods = new Map();
+ this.modAPI = {};
+
+ // Replay system
+ this.isRecording = false;
+ this.isPlaying = false;
+ this.replayData = [];
+ this.currentReplayFrame = 0;
+
+ // Debug tools
+ this.debugMode = false;
+ this.debugCommands = new Map();
+
+ this.init();
+ console.log('โ
Technical & Performance System initialized');
+ }
+
+ init() {
+ this.setupEntityPools();
+ this.setupDebugCommands();
+ this.setupModAPI();
+ console.log('โก Technical & Performance ready');
+ }
+
+ // ========== PERFORMANCE MONITORING ==========
+
+ updatePerformanceStats() {
+ // FPS
+ this.performanceStats.fps = Math.round(this.scene.game.loop.actualFps);
+ this.fpsHistory.push(this.performanceStats.fps);
+ if (this.fpsHistory.length > 60) this.fpsHistory.shift();
+
+ // Memory (if available)
+ if (performance.memory) {
+ this.performanceStats.memory = Math.round(performance.memory.usedJSHeapSize / 1048576); // MB
+ this.memoryHistory.push(this.performanceStats.memory);
+ if (this.memoryHistory.length > 60) this.memoryHistory.shift();
+ }
+
+ // Entity count
+ this.performanceStats.entities = this.scene.children.list.length;
+ }
+
+ getPerformanceReport() {
+ const avgFps = this.fpsHistory.reduce((a, b) => a + b, 0) / this.fpsHistory.length;
+ const avgMemory = this.memoryHistory.reduce((a, b) => a + b, 0) / this.memoryHistory.length;
+
+ return {
+ fps: {
+ current: this.performanceStats.fps,
+ average: Math.round(avgFps),
+ min: Math.min(...this.fpsHistory),
+ max: Math.max(...this.fpsHistory)
+ },
+ memory: {
+ current: this.performanceStats.memory,
+ average: Math.round(avgMemory)
+ },
+ entities: this.performanceStats.entities
+ };
+ }
+
+ // ========== ENTITY POOLING ==========
+
+ setupEntityPools() {
+ // Create pools for frequently spawned entities
+ this.createPool('particle', 100);
+ this.createPool('projectile', 50);
+ this.createPool('enemy', 30);
+ this.createPool('item', 50);
+ }
+
+ createPool(type, size) {
+ const pool = {
+ available: [],
+ active: []
+ };
+
+ for (let i = 0; i < size; i++) {
+ pool.available.push(this.createEntity(type));
+ }
+
+ this.entityPools.set(type, pool);
+ console.log(`๐ Created pool for ${type}: ${size} entities`);
+ }
+
+ createEntity(type) {
+ // Create entity based on type
+ return { type, active: false };
+ }
+
+ getFromPool(type) {
+ const pool = this.entityPools.get(type);
+ if (!pool || pool.available.length === 0) {
+ return this.createEntity(type);
+ }
+
+ const entity = pool.available.pop();
+ pool.active.push(entity);
+ entity.active = true;
+ return entity;
+ }
+
+ returnToPool(type, entity) {
+ const pool = this.entityPools.get(type);
+ if (!pool) return;
+
+ const index = pool.active.indexOf(entity);
+ if (index > -1) {
+ pool.active.splice(index, 1);
+ pool.available.push(entity);
+ entity.active = false;
+ }
+ }
+
+ // ========== CHUNK LOADING ==========
+
+ loadChunk(chunkX, chunkY) {
+ console.log(`๐ฆ Loading chunk (${chunkX}, ${chunkY})`);
+ // Load terrain, entities, etc. for chunk
+ }
+
+ unloadChunk(chunkX, chunkY) {
+ console.log(`๐ฆ Unloading chunk (${chunkX}, ${chunkY})`);
+ // Unload chunk to free memory
+ }
+
+ // ========== MOD SUPPORT ==========
+
+ setupModAPI() {
+ this.modAPI = {
+ registerItem: (id, data) => this.registerModItem(id, data),
+ registerRecipe: (id, data) => this.registerModRecipe(id, data),
+ registerEnemy: (id, data) => this.registerModEnemy(id, data),
+ addCommand: (name, callback) => this.addDebugCommand(name, callback)
+ };
+ }
+
+ loadMod(modId, modData) {
+ if (this.loadedMods.has(modId)) {
+ console.log(`โ Mod already loaded: ${modId}`);
+ return false;
+ }
+
+ // Validate mod
+ if (!this.validateMod(modData)) {
+ console.log(`โ Invalid mod: ${modId}`);
+ return false;
+ }
+
+ // Load mod
+ this.loadedMods.set(modId, modData);
+
+ // Execute mod init
+ if (modData.init) {
+ modData.init(this.modAPI);
+ }
+
+ console.log(`โ
Mod loaded: ${modId}`);
+ return true;
+ }
+
+ validateMod(modData) {
+ // Check required fields
+ return modData.name && modData.version;
+ }
+
+ registerModItem(id, data) {
+ console.log(`๐ฆ Registered mod item: ${id}`);
+ }
+
+ registerModRecipe(id, data) {
+ console.log(`๐จ Registered mod recipe: ${id}`);
+ }
+
+ registerModEnemy(id, data) {
+ console.log(`๐น Registered mod enemy: ${id}`);
+ }
+
+ detectModConflicts() {
+ const conflicts = [];
+ // Check for conflicts between mods
+ return conflicts;
+ }
+
+ // ========== REPLAY SYSTEM ==========
+
+ startRecording() {
+ this.isRecording = true;
+ this.replayData = [];
+ console.log('๐ฌ Recording started');
+ }
+
+ stopRecording() {
+ this.isRecording = false;
+ console.log('๐ฌ Recording stopped');
+ return this.replayData;
+ }
+
+ recordFrame() {
+ if (!this.isRecording) return;
+
+ const frame = {
+ time: Date.now(),
+ inputs: this.captureInputs(),
+ state: this.captureGameState()
+ };
+
+ this.replayData.push(frame);
+ }
+
+ captureInputs() {
+ // Capture keyboard/mouse inputs
+ return {
+ keys: {},
+ mouse: { x: 0, y: 0, buttons: [] }
+ };
+ }
+
+ captureGameState() {
+ // Capture minimal game state
+ return {
+ playerPos: { x: 0, y: 0 },
+ time: 0
+ };
+ }
+
+ playReplay(replayData) {
+ this.isPlaying = true;
+ this.replayData = replayData;
+ this.currentReplayFrame = 0;
+ console.log('โถ๏ธ Playing replay');
+ }
+
+ stopReplay() {
+ this.isPlaying = false;
+ this.currentReplayFrame = 0;
+ console.log('โน๏ธ Replay stopped');
+ }
+
+ updateReplay() {
+ if (!this.isPlaying) return;
+
+ if (this.currentReplayFrame >= this.replayData.length) {
+ this.stopReplay();
+ return;
+ }
+
+ const frame = this.replayData[this.currentReplayFrame];
+ this.applyReplayFrame(frame);
+ this.currentReplayFrame++;
+ }
+
+ applyReplayFrame(frame) {
+ // Apply inputs and state from replay frame
+ }
+
+ saveReplay(filename) {
+ const data = JSON.stringify(this.replayData);
+ localStorage.setItem(`replay_${filename}`, data);
+ console.log(`๐พ Replay saved: ${filename}`);
+ }
+
+ loadReplay(filename) {
+ const data = localStorage.getItem(`replay_${filename}`);
+ if (data) {
+ this.replayData = JSON.parse(data);
+ console.log(`๐ Replay loaded: ${filename}`);
+ return true;
+ }
+ return false;
+ }
+
+ // ========== DEBUG TOOLS ==========
+
+ setupDebugCommands() {
+ this.addDebugCommand('help', () => this.showDebugHelp());
+ this.addDebugCommand('spawn', (entity, x, y) => this.spawnEntity(entity, x, y));
+ this.addDebugCommand('tp', (x, y) => this.teleportPlayer(x, y));
+ this.addDebugCommand('give', (item, amount) => this.giveItem(item, amount));
+ this.addDebugCommand('time', (hours) => this.setTime(hours));
+ this.addDebugCommand('weather', (type) => this.setWeather(type));
+ this.addDebugCommand('god', () => this.toggleGodMode());
+ this.addDebugCommand('noclip', () => this.toggleNoclip());
+ this.addDebugCommand('kill', (radius) => this.killEnemies(radius));
+ this.addDebugCommand('clear', () => this.clearInventory());
+ }
+
+ addDebugCommand(name, callback) {
+ this.debugCommands.set(name, callback);
+ }
+
+ executeCommand(commandString) {
+ const parts = commandString.split(' ');
+ const command = parts[0];
+ const args = parts.slice(1);
+
+ const callback = this.debugCommands.get(command);
+ if (callback) {
+ callback(...args);
+ } else {
+ console.log(`โ Unknown command: ${command}`);
+ }
+ }
+
+ showDebugHelp() {
+ console.log('๐ Available commands:');
+ for (const [name] of this.debugCommands.entries()) {
+ console.log(` - ${name}`);
+ }
+ }
+
+ spawnEntity(entity, x, y) {
+ console.log(`๐พ Spawned ${entity} at (${x}, ${y})`);
+ }
+
+ teleportPlayer(x, y) {
+ if (this.scene.player) {
+ this.scene.player.setPosition(parseFloat(x), parseFloat(y));
+ console.log(`๐ Teleported to (${x}, ${y})`);
+ }
+ }
+
+ giveItem(item, amount) {
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(item, parseInt(amount) || 1);
+ console.log(`๐ Gave ${amount || 1}x ${item}`);
+ }
+ }
+
+ setTime(hours) {
+ if (this.scene.weatherSystem) {
+ this.scene.weatherSystem.currentTime = parseFloat(hours);
+ console.log(`โฐ Time set to ${hours}:00`);
+ }
+ }
+
+ setWeather(type) {
+ if (this.scene.weatherSystem) {
+ this.scene.weatherSystem.setWeather(type);
+ console.log(`๐ฆ๏ธ Weather set to ${type}`);
+ }
+ }
+
+ toggleGodMode() {
+ console.log('โก God mode toggled');
+ }
+
+ toggleNoclip() {
+ console.log('๐ป Noclip toggled');
+ }
+
+ killEnemies(radius) {
+ console.log(`๐ Killed all enemies in ${radius || 'infinite'} radius`);
+ }
+
+ clearInventory() {
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.clear();
+ console.log('๐๏ธ Inventory cleared');
+ }
+ }
+
+ toggleDebugMode() {
+ this.debugMode = !this.debugMode;
+ console.log(`๐ Debug mode: ${this.debugMode ? 'ON' : 'OFF'}`);
+ }
+
+ // ========== AUTO-UPDATE ==========
+
+ checkForUpdates() {
+ console.log('๐ Checking for updates...');
+ // Check version against server
+ return { available: false, version: '3.0.0' };
+ }
+
+ downloadUpdate(version) {
+ console.log(`โฌ๏ธ Downloading update ${version}...`);
+ }
+
+ installUpdate() {
+ console.log('๐ฆ Installing update...');
+ }
+
+ rollbackUpdate() {
+ console.log('โช Rolling back update...');
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updatePerformanceStats();
+
+ if (this.isRecording) {
+ this.recordFrame();
+ }
+
+ if (this.isPlaying) {
+ this.updateReplay();
+ }
+ }
+
+ destroy() {
+ console.log('โก Technical & Performance System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TerrainSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TerrainSystem.js
new file mode 100644
index 000000000..19b8c40ea
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TerrainSystem.js
@@ -0,0 +1,1437 @@
+// ========================================================
+// STARDEW VALLEY STYLE FOREST MAP - 100x100
+// ========================================================
+const MAP_SIZE = 100; // 100x100 velika mapa!
+const FARM_SIZE = 8; // 8x8 MICRO FARM - Zaฤetek igre!
+const FARM_CENTER_X = 50; // Center mape (50,50)
+const FARM_CENTER_Y = 50; // Center mape (50,50)
+
+// Zapuลกฤene hiลกe lokacije - ODSTRANJENO ZA TESTIRANJE!
+const ABANDONED_HOUSES = [
+ // { x: 25, y: 25, size: 5 },
+ // { x: 75, y: 30, size: 6 },
+ // { x: 40, y: 75, size: 4 }
+]; // PRAZNO - brez hiลก!
+
+// ========================================================
+// GOZD KONSTANTE - STARDEW VALLEY STYLE (OPTIMIZIRANO)
+// ========================================================
+const TREE_DENSITY = 0.0; // 0% - BREZ DREVES!
+const PURPLE_TREE_CHANCE = 0.30; // 30% vijolฤnih dreves
+const FRUIT_TREE_CHANCE = 0.20; // 20% sadnih dreves
+const ROCK_DENSITY_THRESHOLD = 0.60; // Prag za skupine skal
+
+// ========================================================
+// RUDNIK KONSTANTE (za terrainTypes)
+// ========================================================
+const TILE_PAVEMENT = 16; // ID za prehodno ploลกฤico
+const TILE_MINE_WALL = 81; // ID za zid rudnika
+const TILE_STONE_ORE = 82; // ID za navadni kamen
+const TILE_IRON_ORE = 83; // ID za ลพelezovo rudo
+
+// ========================================================
+// RIBNIK KONSTANTE
+// ========================================================
+const POND_CENTER_X = 30; // Center ribnika
+const POND_CENTER_Y = 30; // Center ribnika
+const POND_RADIUS = 4; // Radij ribnika (8x8)
+
+// Terrain Generator System
+class TerrainSystem {
+ constructor(scene, width = 100, height = 100, seed = null) {
+ this.scene = scene;
+ this.width = width;
+ this.height = height;
+
+ // ๐ฒ SEED-BASED GENERATION
+ this.seed = seed || Date.now().toString();
+ console.log(`๐ TerrainSystem initialized with seed: ${this.seed}`);
+
+ // Seeded RNG - vedno isti rezultati za isti seed!
+ this.rng = this.createSeededRNG(this.seed);
+
+ this.iso = new IsometricUtils(48, 24);
+ this.noise = new PerlinNoise(this.hashCode(this.seed));
+
+ this.tiles = [];
+ this.decorations = [];
+ this.decorationsMap = new Map();
+ this.cropsMap = new Map();
+ this.tileHealthMap = new Map(); // Global register zdravja ploลกฤic
+
+ this.visibleTiles = new Map();
+ this.visibleDecorations = new Map();
+ this.visibleCrops = new Map();
+
+ // Water animation sistem
+ this.waterTiles = []; // Array za water tiles
+ this.waterAnimationTimer = 0;
+
+ // Rain particles za ribnik
+ this.rainEmitter = null;
+ this.splashEmitters = []; // Array splash emitterjev
+
+ this.tilePool = {
+ active: [],
+ inactive: [],
+ get: () => {
+ if (this.tilePool.inactive.length > 0) {
+ const s = this.tilePool.inactive.pop();
+ s.setVisible(true);
+ return s;
+ }
+ const s = this.scene.add.sprite(0, 0, 'dirt');
+ s.setOrigin(0.5, 0.5);
+ return s;
+ },
+ release: (sprite) => {
+ sprite.setVisible(false);
+ this.tilePool.inactive.push(sprite);
+ }
+ };
+
+ this.decorationPool = {
+ active: [],
+ inactive: [],
+ get: () => {
+ if (this.decorationPool.inactive.length > 0) {
+ const s = this.decorationPool.inactive.pop();
+ s.setVisible(true);
+ s.clearTint();
+ return s;
+ }
+ return this.scene.add.sprite(0, 0, 'tree');
+ },
+ release: (sprite) => {
+ sprite.setVisible(false);
+ this.decorationPool.inactive.push(sprite);
+ }
+ };
+
+ this.cropPool = {
+ active: [],
+ inactive: [],
+ get: () => {
+ if (this.cropPool.inactive.length > 0) {
+ const s = this.cropPool.inactive.pop();
+ s.setVisible(true);
+ return s;
+ }
+ return this.scene.add.sprite(0, 0, 'crop_stage_1');
+ },
+ release: (sprite) => {
+ sprite.setVisible(false);
+ this.cropPool.inactive.push(sprite);
+ }
+ };
+
+ this.terrainTypes = {
+ WATER: { name: 'water', height: 0, color: 0x4444ff },
+ SAND: { name: 'sand', height: 0.2, color: 0xdddd44 },
+ GRASS_FULL: { name: 'grass_full', height: 0.35, color: 0x44aa44 },
+ GRASS_TOP: { name: 'grass_top', height: 0.45, color: 0x66cc66 },
+ DIRT: { name: 'dirt', height: 0.5, color: 0x8b4513 },
+ STONE: { name: 'stone', height: 0.7, color: 0x888888 },
+ PAVEMENT: { name: 'pavement', height: 0.6, color: 0x777777 },
+ RUINS: { name: 'ruins', height: 0.6, color: 0x555555 },
+ WALL_EDGE: { name: 'WALL_EDGE', height: 0.8, color: 0x505050, solid: true }, // OBZIDJE
+ PATH: { name: 'path', height: -1, color: 0xc2b280 },
+ FARMLAND: { name: 'farmland', height: -1, color: 0x5c4033 },
+ // MINE TYPES
+ MINE_FLOOR: { name: 'mine_floor', height: 0, color: 0x333333, id: TILE_PAVEMENT },
+ MINE_WALL: { name: 'mine_wall', height: 1, color: 0x1a1a1a, id: TILE_MINE_WALL },
+ ORE_STONE: { name: 'ore_stone', height: 0.5, color: 0x555555, id: TILE_STONE_ORE },
+ ORE_IRON: { name: 'ore_iron', height: 0.5, color: 0x884444, id: TILE_IRON_ORE }
+ };
+
+ this.offsetX = 0;
+ this.offsetY = 0;
+
+ this.generatedChunks = new Set();
+ this.chunkSize = 10;
+
+ // FOREST GENERATION: Tracking postavljenih dreves za preverjanje razdalje
+ this.placedTrees = []; // Seznam vseh postavljenih dreves { x, y }
+ this.MIN_TREE_DISTANCE_SQUARED = 2 * 2; // Minimalna razdalja med drevesi (2 tiles)
+
+ // Init tiles array with NULLs
+ this.tiles = Array.from({ length: this.height }, () => Array(this.width).fill(null));
+ }
+
+ /**
+ * Create seeded random number generator
+ * Uses Mulberry32 algorithm for consistent randomness
+ */
+ createSeededRNG(seed) {
+ let h = this.hashCode(seed);
+ return function () {
+ h = Math.imul(h ^ (h >>> 16), 2246822507);
+ h = Math.imul(h ^ (h >>> 13), 3266489909);
+ h = (h ^= h >>> 16) >>> 0;
+ return h / 4294967296; // Return 0-1
+ };
+ }
+
+ /**
+ * Convert string seed to numeric hash
+ */
+ hashCode(str) {
+ let hash = 0;
+ for (let i = 0; i < str.length; i++) {
+ const char = str.charCodeAt(i);
+ hash = ((hash << 5) - hash) + char;
+ hash = hash & hash; // Convert to 32bit integer
+ }
+ return Math.abs(hash);
+ }
+
+ /**
+ * Preveri, ali je nova lokacija (newX, newY) dovolj oddaljena od vseh
+ * ลพe postavljenih dreves. Uporablja kvadrat razdalje za hitrejลกo optimizacijo.
+ */
+ isTreeLocationFarEnough(newX, newY) {
+ for (let i = 0; i < this.placedTrees.length; i++) {
+ const existingTree = this.placedTrees[i];
+
+ // Izraฤunaj kvadrat razdalje
+ const dx = newX - existingTree.x;
+ const dy = newY - existingTree.y;
+ const distanceSq = (dx * dx) + (dy * dy);
+
+ // ฤe je razdalja manjลกa od zahtevane minimalne razdalje, zavrni lokacijo
+ if (distanceSq < this.MIN_TREE_DISTANCE_SQUARED) {
+ return false;
+ }
+ }
+ return true; // Lokacija je dovolj oddaljena
+ }
+
+ createTileTextures() {
+ const tileWidth = 48;
+ const tileHeight = 60;
+ const P = 0; // NO PADDING - seamless tiles!
+
+ const types = Object.values(this.terrainTypes);
+
+ // POSEBNA OBDELAVA ZA VODO - 2D Smooth Pond/Lake Style!
+ if (!this.scene.textures.exists('water')) {
+ const waterGraphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+
+ // RICH BLUE WATER - Stardew Valley style gradient
+ waterGraphics.fillGradientStyle(
+ 0x1a5f7a, 0x1a5f7a, // Deep blue (top)
+ 0x2a7fbc, 0x2a7fbc // Medium blue (bottom)
+ );
+ waterGraphics.fillRect(0, 0, 48, 48);
+
+ // SMOOTH WAVE HIGHLIGHTS (organic circles)
+ waterGraphics.fillStyle(0x4aa3d0, 0.4);
+ waterGraphics.fillCircle(10, 10, 12);
+ waterGraphics.fillCircle(35, 25, 10);
+ waterGraphics.fillCircle(20, 38, 8);
+
+ // Soft shimmer spots
+ waterGraphics.fillStyle(0x7fc9e8, 0.3);
+ waterGraphics.fillCircle(15, 20, 6);
+ waterGraphics.fillCircle(38, 12, 5);
+
+ // Bright reflection highlights
+ waterGraphics.fillStyle(0xffffff, 0.2);
+ waterGraphics.fillCircle(12, 15, 4);
+ waterGraphics.fillCircle(32, 30, 3);
+
+ // NO BORDER - seamless tiles!
+ waterGraphics.generateTexture('water', 48, 48);
+ waterGraphics.destroy();
+ console.log('๐ Smooth pond water texture created (Stardew Valley style)!');
+ }
+
+ types.forEach((type) => {
+ if (this.scene.textures.exists(type.name)) return;
+
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+
+ // Koordinate z upoลกtevanjem paddinga
+ const xs = P;
+ const xe = 48 + P;
+ const midX = 24 + P;
+
+ const topY = P;
+ const midY = 12 + P;
+ const bottomY = 24 + P;
+ const depth = 20;
+
+ // 1. STRANICE (Faces)
+ // Left Face
+ const cLeft = 0x8B4513; // RJAVA DIRT - Left face
+ graphics.fillStyle(cLeft);
+ graphics.beginPath();
+ graphics.moveTo(midX, bottomY);
+ graphics.lineTo(midX, bottomY + depth);
+ graphics.lineTo(xs, midY + depth);
+ graphics.lineTo(xs, midY);
+ graphics.closePath();
+ graphics.fill();
+ // graphics.lineStyle(2, cLeft); // NO STROKE!
+ // graphics.strokePath();
+
+ // Right Face
+ const cRight = 0x6B3410; // RJAVA DIRT - Right face (temnejลกa)
+ graphics.fillStyle(cRight);
+ graphics.beginPath();
+ graphics.moveTo(xe, midY);
+ graphics.lineTo(xe, midY + depth);
+ graphics.lineTo(midX, bottomY + depth);
+ graphics.lineTo(midX, bottomY);
+ graphics.closePath();
+ graphics.fill();
+ // graphics.lineStyle(2, cRight); // NO STROKE!
+ // graphics.strokePath();
+
+ // 2. ZGORNJA PLOSKEV (Top Face)
+ graphics.fillStyle(type.color);
+ graphics.beginPath();
+ graphics.moveTo(xs, midY);
+ graphics.lineTo(midX, topY);
+ graphics.lineTo(xe, midY);
+ graphics.lineTo(midX, bottomY);
+ graphics.closePath();
+ graphics.fill();
+ // graphics.lineStyle(2, type.color); // NO STROKE!
+ // graphics.strokePath();
+
+ // Highlight - REMOVED for seamless tiles
+ // graphics.lineStyle(1, 0xffffff, 0.15);
+ // graphics.beginPath();
+ // graphics.moveTo(xs, midY);
+ // graphics.lineTo(midX, topY);
+ // graphics.lineTo(xe, midY);
+ // graphics.strokePath();
+
+ // 3. DETAJLI - ENHANCED!
+ if (type.name.includes('grass')) {
+ // Darker grass spots (rich texture)
+ graphics.fillStyle(Phaser.Display.Color.IntegerToColor(type.color).darken(15).color, 0.4);
+ for (let i = 0; i < 12; i++) {
+ const rx = xs + 8 + Math.random() * 32;
+ const ry = topY + 3 + Math.random() * 18;
+ const size = 1 + Math.random() * 2;
+ graphics.fillCircle(rx, ry, size);
+ }
+
+ // Lighter grass highlights (freshness)
+ graphics.fillStyle(Phaser.Display.Color.IntegerToColor(type.color).lighten(20).color, 0.5);
+ for (let i = 0; i < 8; i++) {
+ const rx = xs + 10 + Math.random() * 28;
+ const ry = topY + 4 + Math.random() * 16;
+ graphics.fillCircle(rx, ry, 1);
+ }
+
+ // Medium grass blades
+ graphics.fillStyle(Phaser.Display.Color.IntegerToColor(type.color).lighten(10).color, 0.6);
+ for (let i = 0; i < 6; i++) {
+ const rx = xs + 12 + Math.random() * 24;
+ const ry = topY + 5 + Math.random() * 14;
+ graphics.fillRect(rx, ry, 1, 2);
+ }
+ }
+
+ // DIRT texture - Enhanced!
+ if (type.name.includes('dirt') || type.name === 'DIRT') {
+ // Darker dirt clumps
+ graphics.fillStyle(Phaser.Display.Color.IntegerToColor(type.color).darken(20).color, 0.5);
+ for (let i = 0; i < 10; i++) {
+ const rx = xs + 8 + Math.random() * 32;
+ const ry = topY + 3 + Math.random() * 18;
+ const size = 2 + Math.random() * 3;
+ graphics.fillCircle(rx, ry, size);
+ }
+
+ // Lighter dirt spots
+ graphics.fillStyle(Phaser.Display.Color.IntegerToColor(type.color).lighten(15).color, 0.4);
+ for (let i = 0; i < 8; i++) {
+ const rx = xs + 10 + Math.random() * 28;
+ const ry = topY + 4 + Math.random() * 16;
+ graphics.fillCircle(rx, ry, 1.5);
+ }
+
+ // Small stones in dirt
+ graphics.fillStyle(0x9b8f77, 0.6);
+ for (let i = 0; i < 5; i++) {
+ const rx = xs + 12 + Math.random() * 24;
+ const ry = topY + 5 + Math.random() * 14;
+ graphics.fillRect(rx, ry, 2, 2);
+ }
+ }
+
+ if (type.name.includes('stone') || type.name.includes('ruins')) {
+ // Dark stone spots
+ graphics.fillStyle(0x404040, 0.6);
+ for (let i = 0; i < 12; i++) {
+ const rx = xs + 6 + Math.random() * 36;
+ const ry = topY + 3 + Math.random() * 18;
+ const size = 2 + Math.random() * 4;
+ graphics.fillCircle(rx, ry, size);
+ }
+
+ // Medium gray spots
+ graphics.fillStyle(0x606060, 0.5);
+ for (let i = 0; i < 10; i++) {
+ const rx = xs + 8 + Math.random() * 32;
+ const ry = topY + 4 + Math.random() * 16;
+ const size = 1.5 + Math.random() * 3;
+ graphics.fillCircle(rx, ry, size);
+ }
+
+ // Lighter highlights
+ graphics.fillStyle(0xa0a0a0, 0.4);
+ for (let i = 0; i < 8; i++) {
+ const rx = xs + 10 + Math.random() * 28;
+ const ry = topY + 5 + Math.random() * 14;
+ graphics.fillCircle(rx, ry, 1);
+ }
+
+ // Crack lines
+ graphics.lineStyle(1, 0x303030, 0.3);
+ for (let i = 0; i < 3; i++) {
+ graphics.beginPath();
+ graphics.moveTo(xs + Math.random() * 48, topY + Math.random() * 20);
+ graphics.lineTo(xs + Math.random() * 48, topY + Math.random() * 20);
+ graphics.strokePath();
+ }
+ }
+
+ if (type.name.includes('pavement')) {
+ graphics.lineStyle(1, 0x555555, 0.5);
+ graphics.beginPath();
+ graphics.moveTo(xs + 12, midY + 6);
+ graphics.lineTo(xs + 36, midY - 6);
+ graphics.strokePath();
+ }
+
+ graphics.generateTexture(type.name, tileWidth + P * 2, tileHeight + P * 2);
+ graphics.destroy();
+ });
+
+ // ANIMATED WATER FRAMES - 4 frame animacija za tekoฤ tok!
+ this.createWaterFrames();
+ }
+
+ createWaterFrames() {
+ const tileWidth = 48;
+ const tileHeight = 48;
+
+ // ๐ SMOOTH ANIMATED POND WATER (Stardew Valley style)
+ for (let frame = 0; frame < 4; frame++) {
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+
+ // 1. BASE WATER - Rich gradient
+ graphics.fillGradientStyle(
+ 0x1a5f7a, 0x1a5f7a, // Deep blue
+ 0x2a7fbc, 0x2a7fbc // Medium blue
+ );
+ graphics.fillRect(0, 0, tileWidth, tileHeight);
+
+ // 2. ANIMATED WAVE CIRCLES (moving highlights)
+ const waveOffset = frame * 3;
+
+ // Primary wave circles
+ graphics.fillStyle(0x4aa3d0, 0.35);
+ graphics.fillCircle(10 + waveOffset, 10, 12 - frame);
+ graphics.fillCircle(35 - waveOffset, 25, 10 + frame * 0.5);
+ graphics.fillCircle(20, 38 + (frame % 2), 8);
+
+ // Secondary shimmer
+ graphics.fillStyle(0x7fc9e8, 0.25);
+ graphics.fillCircle(15 + (frame * 2), 20, 6);
+ graphics.fillCircle(38 - frame, 12, 5);
+ graphics.fillCircle(8, 32 + frame, 4);
+
+ // 3. BRIGHT REFLECTION SPOTS (twinkling)
+ graphics.fillStyle(0xffffff, 0.15 + (frame % 2) * 0.1);
+ graphics.fillCircle(12, 15, 3 + (frame % 2));
+ graphics.fillCircle(32 + (frame % 3), 30, 2);
+ graphics.fillCircle(25, 8, 2);
+
+ graphics.generateTexture(`water_frame_${frame}`, tileWidth, tileHeight);
+ graphics.destroy();
+ }
+ console.log('๐ Smooth animated pond water created!');
+ }
+
+ generate() {
+ this.createTileTextures();
+ this.createWaterFrames(); // Ustvari water animation frames!
+
+ console.log('๐ Initializing World (Zone Streaming Mode)...');
+
+ // Generate ONLY the starting area (Farm)
+ // Farm is at 20,20. Let's load 3x3 chunks around it.
+ const centerCx = Math.floor(FARM_CENTER_X / this.chunkSize);
+ const centerCy = Math.floor(FARM_CENTER_Y / this.chunkSize);
+
+ for (let cy = centerCy - 2; cy <= centerCy + 2; cy++) {
+ for (let cx = centerCx - 2; cx <= centerCx + 2; cx++) {
+ this.generateChunk(cx, cy);
+ }
+ }
+
+ console.log('โ
World Init Complete. Loaded ${this.generatedChunks.size} chunks.');
+
+ // DEBUG: Check how many water tiles were generated
+ let waterCount = 0;
+ for (let y = 0; y < this.height; y++) {
+ for (let x = 0; x < this.width; x++) {
+ if (this.tiles[y] && this.tiles[y][x] && this.tiles[y][x].type === 'water') {
+ waterCount++;
+ }
+ }
+ }
+ console.log(`๐ DEBUG: Generated ${waterCount} water tiles in world`);
+ }
+
+ generateChunk(cx, cy) {
+ const key = `${cx},${cy}`;
+ if (this.generatedChunks.has(key)) return;
+
+ // Bounds check
+ if (cx < 0 || cy < 0 || cx * this.chunkSize >= this.width || cy * this.chunkSize >= this.height) return;
+
+ this.generatedChunks.add(key);
+ // console.log(`๐ Streaming Chunk: [${cx}, ${cy}]`);
+
+ const startX = cx * this.chunkSize;
+ const startY = cy * this.chunkSize;
+ const endX = Math.min(startX + this.chunkSize, this.width);
+ const endY = Math.min(startY + this.chunkSize, this.height);
+
+ const validPositions = []; // Local valid positions for this chunk
+
+ for (let y = startY; y < endY; y++) {
+ for (let x = startX; x < endX; x++) {
+
+ // --- STARDEW VALLEY FOREST GENERATION ---
+ let terrainType = this.terrainTypes.GRASS_FULL; // Vsa mapa je trava!
+
+ // Farm Override - ZELENA PLATFORMA!
+ if (Math.abs(x - FARM_CENTER_X) <= FARM_SIZE / 2 && Math.abs(y - FARM_CENTER_Y) <= FARM_SIZE / 2) {
+ terrainType = this.terrainTypes.GRASS_FULL;
+ }
+
+ // Zapuลกฤene hiลกe - DIRT tiles
+ let isHouse = false;
+ ABANDONED_HOUSES.forEach(house => {
+ if (x >= house.x && x < house.x + house.size &&
+ y >= house.y && y < house.y + house.size) {
+ terrainType = this.terrainTypes.DIRT; // Hiลกa na DIRT
+ isHouse = true;
+ }
+ });
+
+ // RIBNIK - okrogel ribnik z vodo!
+ const distToPond = Math.sqrt(
+ Math.pow(x - POND_CENTER_X, 2) +
+ Math.pow(y - POND_CENTER_Y, 2)
+ );
+ if (distToPond <= POND_RADIUS) {
+ terrainType = this.terrainTypes.WATER; // Voda!
+ }
+
+ // ๐๏ธ HEIGHT GENERATION (2.5D Elevation)
+ // Using second Perlin noise layer for smooth hills
+ const heightNoise = this.noise.noise(x * 0.05, y * 0.05); // Low frequency = smooth hills
+ const rawHeight = (heightNoise + 1) * 2.5; // Convert -1..1 to 0..5 range
+ const elevationHeight = Math.floor(rawHeight); // Discrete levels (0-5)
+
+ // Create Tile Data
+ this.tiles[y][x] = {
+ type: terrainType.name,
+ texture: terrainType.name,
+ hasDecoration: false,
+ hasCrop: false,
+ solid: terrainType.solid || false,
+ isHouse: isHouse,
+ height: elevationHeight // ๐๏ธ NEW: Elevation data (0-5)
+ };
+
+ // Track valid positions for decorations (TREES!)
+ if (terrainType.name === 'grass_full' && !isHouse) {
+ const isFarm = Math.abs(x - FARM_CENTER_X) <= (FARM_SIZE / 2 + 2) && Math.abs(y - FARM_CENTER_Y) <= (FARM_SIZE / 2 + 2);
+ if (!isFarm) {
+ validPositions.push({ x, y });
+ }
+ }
+ }
+ }
+
+
+ // --- STARDEW VALLEY FOREST DECORATION PASS ---
+ const farmMinX = FARM_CENTER_X - FARM_SIZE / 2;
+ const farmMaxX = FARM_CENTER_X + FARM_SIZE / 2;
+ const farmMinY = FARM_CENTER_Y - FARM_SIZE / 2;
+ const farmMaxY = FARM_CENTER_Y + FARM_SIZE / 2;
+
+ // DREVESA - 3% gostota z preverjanjem razdalje!
+ validPositions.forEach(pos => {
+ const rand = Math.random();
+
+ // 3% chance za drevo
+ if (rand < TREE_DENSITY) {
+ // PREVERJANJE RAZDALJE: Ali je ta lokacija dovolj oddaljena?
+ if (!this.isTreeLocationFarEnough(pos.x, pos.y)) {
+ return; // Preskoฤi to lokacijo - preblizu drugim drevesom!
+ }
+
+ const treeRand = Math.random();
+ let treeType;
+
+ // 30% VIJOLฤNA DREVESA
+ if (treeRand < PURPLE_TREE_CHANCE) {
+ treeType = 'tree_purple';
+ }
+ // 20% SADNA DREVESA
+ else if (treeRand < PURPLE_TREE_CHANCE + FRUIT_TREE_CHANCE) {
+ const fruitTypes = ['tree_apple', 'tree_pear', 'tree_cherry'];
+ treeType = fruitTypes[Math.floor(Math.random() * fruitTypes.length)];
+ }
+ // 50% NAVADNA DREVESA
+ else {
+ const normalTrees = ['tree_green_final', 'tree_blue_final', 'tree_sapling'];
+ treeType = normalTrees[Math.floor(Math.random() * normalTrees.length)];
+ }
+
+ // DODAJ DREVO
+ this.addDecoration(pos.x, pos.y, treeType);
+ this.placedTrees.push({ x: pos.x, y: pos.y });
+ }
+ });
+
+ // KONEC generateChunk
+ }
+
+ updateChunks(camera) {
+ // Calculate which chunks are in view
+ const view = camera.worldView;
+ const buffer = 100; // Load slightly outside view
+
+ const p1 = this.iso.toGrid(view.x - buffer, view.y - buffer);
+ const p2 = this.iso.toGrid(view.x + view.width + buffer, view.y + view.height + buffer);
+
+ const minCx = Math.floor(Math.min(p1.x, p2.x) / this.chunkSize);
+ const maxCx = Math.ceil(Math.max(p1.x, p2.x) / this.chunkSize);
+ const minCy = Math.floor(Math.min(p1.y, p2.y) / this.chunkSize);
+ const maxCy = Math.ceil(Math.max(p1.y, p2.y) / this.chunkSize);
+
+ for (let cy = minCy; cy <= maxCy; cy++) {
+ for (let cx = minCx; cx <= maxCx; cx++) {
+ this.generateChunk(cx, cy);
+ }
+ }
+ }
+
+ // Retained helper methods...
+
+ damageDecoration(x, y, amount) {
+ const key = `${x},${y}`;
+ const decor = this.decorationsMap.get(key);
+ if (!decor) return false;
+ decor.hp -= amount;
+
+ if (this.visibleDecorations.has(key)) {
+ const sprite = this.visibleDecorations.get(key);
+ sprite.setTint(0xff0000);
+ this.scene.time.delayedCall(100, () => sprite.clearTint());
+ this.scene.tweens.add({
+ targets: sprite,
+ x: sprite.x + 2,
+ duration: 50,
+ yoyo: true,
+ repeat: 1
+ });
+ }
+
+ if (decor.hp <= 0) {
+ this.removeDecoration(x, y);
+
+ // Chance to drop Blueprint
+ if (this.scene.blueprintSystem) {
+ this.scene.blueprintSystem.tryDropBlueprint(x, y, 'mining');
+ }
+
+ return 'destroyed';
+ }
+ return 'hit';
+ }
+
+ removeDecoration(x, y) {
+ const key = `${x},${y}`;
+ const decor = this.decorationsMap.get(key);
+ if (!decor) return;
+
+ if (this.visibleDecorations.has(key)) {
+ const sprite = this.visibleDecorations.get(key);
+ sprite.setVisible(false);
+ this.decorationPool.release(sprite);
+ this.visibleDecorations.delete(key);
+ }
+
+ this.decorationsMap.delete(key);
+ const index = this.decorations.indexOf(decor);
+ if (index > -1) this.decorations.splice(index, 1);
+ if (this.tiles[y] && this.tiles[y][x]) this.tiles[y][x].hasDecoration = false;
+
+ return decor.type;
+ }
+
+ init(offsetX, offsetY) {
+ this.offsetX = offsetX;
+ this.offsetY = offsetY;
+
+ // Ustvari rain particles nad ribnikom
+ this.createRainOnPond();
+ }
+
+ createRainOnPond() {
+ // Izraฤunaj screen pozicijo ribnika
+ const pondScreenPos = this.iso.toScreen(POND_CENTER_X, POND_CENTER_Y);
+ const pondX = pondScreenPos.x + this.offsetX;
+ const pondY = pondScreenPos.y + this.offsetY;
+
+ // Ustvari particle texture (modra kapljica)
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+ graphics.fillStyle(0x4488ff, 1);
+ graphics.fillCircle(2, 2, 2);
+ graphics.generateTexture('raindrop', 4, 4);
+ graphics.destroy();
+
+ // Rain emitter - pada v ribnik
+ this.rainEmitter = this.scene.add.particles(pondX, pondY - 100, 'raindrop', {
+ x: { min: -50, max: 50 },
+ y: 0,
+ lifespan: 1000,
+ speedY: { min: 200, max: 300 },
+ scale: { start: 0.5, end: 0.2 },
+ alpha: { start: 0.8, end: 0.3 },
+ frequency: 100,
+ blendMode: 'ADD'
+ });
+
+ // Dodaj rain sound effect
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playRainSound();
+ }
+
+ console.log('๐ง๏ธ Rain particles created over pond!');
+ }
+
+
+ setTile(x, y, type) {
+ if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
+ this.tiles[y][x].type = type;
+ }
+ }
+
+ placeTree(x, y, tileType) {
+ // 1. Safety Checks
+ if (!tileType || !tileType.includes('grass')) return;
+
+ const isFarm = Math.abs(x - FARM_CENTER_X) <= FARM_SIZE / 2 + 2;
+ const isCity = x >= CITY_START_X - 2 && x < CITY_START_X + CITY_SIZE + 2 && y >= CITY_START_Y - 2 && y < CITY_START_Y + CITY_SIZE + 2;
+ if (isFarm || isCity) return;
+
+ // 2. Noise for clustering (Forests)
+ const noiseVal = this.noise.noise(x * 0.1 + 123.45, y * 0.1 + 678.90);
+
+ // KLJUฤNO PREVERJANJE: Ali tu ลพe stoji drug Sprite?
+ const key = `${x},${y}`;
+ if (this.decorationsMap.has(key)) return;
+ if (this.tiles[y][x].hasDecoration) return;
+
+ // 3. Selection Logic
+ let shouldPlace = false;
+ let type = window.SPRITE_TREE_HEALTHY || 'tree_green_final';
+
+ // USER LOGIC: Gostota 40% ฤe smo nad pragom (Gozd)
+ if (noiseVal > TREE_DENSITY_THRESHOLD && Math.random() < 0.4) {
+ shouldPlace = true;
+ }
+ // Fallback: Redka posamiฤna drevesa (1.5%)
+ else if (Math.random() < 0.015) {
+ shouldPlace = true;
+ }
+
+ if (shouldPlace) {
+ // Variants Logic
+ const r = Math.random();
+ // 50% moลพnosti, da je drevo komaj zaฤelo rasti (Sapling)
+ if (r < 0.50) type = window.SPRITE_TREE_SAPLING || 'tree_sapling';
+ else if (r < 0.60) type = window.SPRITE_TREE_DEAD || 'tree_dead_final';
+ else if (r < 0.65) type = window.SPRITE_TREE_BLUE || 'tree_blue_final';
+ // Ostalo (35%) je odraslo drevo
+ }
+
+ // 4. Placement
+ if (shouldPlace) {
+ this.addDecoration(x, y, type);
+ }
+ }
+
+ placeRock(x, y, tileType) {
+ if (!tileType || !tileType.includes('grass') && !tileType.includes('dirt')) return;
+
+ const isFarm = Math.abs(x - FARM_CENTER_X) <= FARM_SIZE / 2 + 2;
+ const isCity = x >= CITY_START_X - 2 && x < CITY_START_X + CITY_SIZE + 2 && y >= CITY_START_Y - 2 && y < CITY_START_Y + CITY_SIZE + 2;
+ if (isFarm || isCity) return;
+
+ // KLJUฤNO PREVERJANJE: Ali tu ลพe stoji drug Sprite?
+ const key = `${x},${y}`;
+ if (this.decorationsMap.has(key)) return;
+ if (this.tiles[y][x].hasDecoration) return;
+
+ // Noise for Rock Clusters
+ const noiseVal = this.noise.noise(x * 0.15 + 99.99, y * 0.15 + 88.88);
+
+ let shouldPlace = false;
+ let type = 'rock_asset'; // Default lep kamen
+
+ // USER LOGIC: Gostota 40% ฤe smo nad pragom (Skalovje)
+ if (noiseVal > ROCK_DENSITY_THRESHOLD && Math.random() < 0.4) {
+ shouldPlace = true;
+ }
+ // Fallback: Redke posamiฤne skale (1%)
+ else if (Math.random() < 0.01) {
+ shouldPlace = true;
+ }
+
+ if (shouldPlace) {
+ this.addDecoration(x, y, type);
+ }
+ }
+
+ placeStructure(gridX, gridY, type) {
+ if (type === 'ruin') {
+ for (let y = 0; y < 6; y++) {
+ for (let x = 0; x < 6; x++) {
+ // TEMP DISABLED: if (Math.random() > 0.6) this.addDecoration(gridX + x, gridY + y, 'fence');
+ this.setTile(gridX + x, gridY + y, 'stone');
+ }
+ }
+ }
+ if (type === 'arena') {
+ const size = 12;
+ for (let y = 0; y < size; y++) {
+ for (let x = 0; x < size; x++) {
+ const tx = gridX + x;
+ const ty = gridY + y;
+
+ this.setTile(tx, ty, 'stone');
+ if (x === 0 || x === size - 1 || y === 0 || y === size - 1) {
+ if (!(x === Math.floor(size / 2) && y === size - 1)) {
+ // TEMP DISABLED: this.addDecoration(tx, ty, 'fence');
+ }
+ }
+ }
+ }
+ this.addDecoration(gridX + 6, gridY + 6, 'gravestone');
+ }
+ if (type === 'ruin_room') {
+ for (let y = 0; y < 5; y++) {
+ for (let x = 0; x < 5; x++) {
+ const tx = gridX + x;
+ const ty = gridY + y;
+ if (x > 0 && x < 4 && y > 0 && y < 4) {
+ this.setTile(tx, ty, 'ruins');
+ }
+ if (x === 0 || x === 4 || y === 0 || y === 4) {
+ const isCenter = (x === 2 || y === 2);
+ if (isCenter && Math.random() > 0.5) continue;
+ if (Math.random() > 0.3) {
+ // TEMP DISABLED: this.addDecoration(tx, ty, 'fence');
+ } else {
+ // User rocks in ruins
+ if (Math.random() > 0.5) {
+ const rType = Math.random() > 0.5 ? 'rock_1' : 'rock_2';
+ this.addDecoration(tx, ty, rType);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ addDecoration(gridX, gridY, type) {
+ if (gridX < 0 || gridX >= this.width || gridY < 0 || gridY >= this.height) return;
+
+ const key = `${gridX},${gridY}`;
+ if (this.decorationsMap.has(key)) return;
+
+ let scale = 1.0;
+
+ // POMANJล ANA DREVESA - Stardew Valley stil
+ if (type === 'rock_1' || type === 'rock_2') scale = 1.0; // Zmanjลกano
+ else if (type === 'tree_green_new' || type === 'tree_blue_new' || type === 'tree_dead_new') scale = 0.04;
+ else if (type === 'flowers_new') scale = 0.02;
+ else if (type === 'fence') scale = 0.025;
+ else if (type === 'gravestone') scale = 0.03;
+ else if (type === 'hill_sprite') scale = 0.025;
+ else if (type.includes('_final')) scale = 0.7; // POMANJล ANO - final trees
+ else if (type.includes('purple') || type.includes('apple') || type.includes('pear') || type.includes('cherry')) {
+ scale = 0.6; // POMANJล ANO - novi sadni in vijolฤni
+ }
+ else {
+ // Old Assets (Low Res)
+ if (type.includes('tree')) scale = 0.7 + Math.random() * 0.2; // POMANJล ANO (bilo 1.2-1.6)
+ else if (type.includes('rock')) scale = 0.6; // POMANJล ANO
+ else scale = 0.8; // POMANJล ANO
+ }
+
+ // Calculate Plant Day for Saplings (Growth System)
+ let plantDay = -1;
+ if (type.includes('sapling')) {
+ const w = this.scene.weatherSystem;
+ plantDay = w ? w.getDayCount() : 1;
+ // Random init offset for initial generation
+ if (!w) plantDay = 1 - Math.floor(Math.random() * 2);
+ }
+
+ // Determine if decoration is SOLID (blocking movement)
+ const typeLower = type.toLowerCase();
+
+ // Small decorations are NOT solid (can walk through)
+ const isSmallDecor = typeLower.includes('flower') ||
+ typeLower.includes('small_rock') ||
+ typeLower.includes('path_stone') ||
+ typeLower.includes('mushroom') ||
+ typeLower.includes('puddle') ||
+ typeLower.includes('fence'); // OGRAJE SO PREHODNE!
+
+ const isSolid = !isSmallDecor && (
+ typeLower.includes('tree') ||
+ typeLower.includes('sapling') ||
+ // ROCKS REMOVED - walkable now!
+ // typeLower.includes('rock') ||
+ // typeLower.includes('stone') ||
+ // fence REMOVED - it's walkable now!
+ typeLower.includes('wall') ||
+ typeLower.includes('signpost') ||
+ typeLower.includes('hill') ||
+ typeLower.includes('chest') ||
+ typeLower.includes('spawner') ||
+ typeLower.includes('ruin') ||
+ typeLower.includes('arena') ||
+ typeLower.includes('house') ||
+ typeLower.includes('gravestone') ||
+ typeLower.includes('bush') ||
+ typeLower.includes('fallen_log') ||
+ typeLower.includes('furnace') || // WORKSTATION
+ typeLower.includes('mint') // WORKSTATION
+ );
+
+ const decorData = {
+ gridX: gridX,
+ gridY: gridY,
+ type: type,
+ id: key,
+ maxHp: 10,
+ hp: 10,
+ scale: scale,
+ plantDay: plantDay, // Added for Growth System
+ solid: isSolid // TRDNOST (collision blocking)
+ };
+ this.decorations.push(decorData);
+ this.decorationsMap.set(key, decorData);
+
+ if (this.tiles[gridY] && this.tiles[gridY][gridX]) {
+ this.tiles[gridY][gridX].hasDecoration = true;
+ }
+
+ // Register Workstation
+ if (typeLower.includes('furnace') || typeLower.includes('mint')) {
+ if (this.scene.workstationSystem) {
+ // Determine type exactly
+ let stationType = 'furnace';
+ if (typeLower.includes('mint')) stationType = 'mint';
+ this.scene.workstationSystem.addStation(gridX, gridY, stationType);
+ }
+ }
+ }
+
+ setTileType(x, y, typeName) {
+ if (!this.tiles[y] || !this.tiles[y][x]) return;
+ this.tiles[y][x].type = typeName;
+
+ const key = `${x},${y}`;
+ if (this.visibleTiles.has(key)) {
+ const sprite = this.visibleTiles.get(key);
+ sprite.setTexture(typeName);
+ }
+ }
+
+ addCrop(x, y, cropData) {
+ const key = `${x},${y}`;
+ this.cropsMap.set(key, cropData);
+ this.tiles[y][x].hasCrop = true;
+ }
+
+ removeCrop(x, y) {
+ const key = `${x},${y}`;
+ if (this.cropsMap.has(key)) {
+ if (this.visibleCrops.has(key)) {
+ const sprite = this.visibleCrops.get(key);
+ sprite.setVisible(false);
+ this.cropPool.release(sprite);
+ this.visibleCrops.delete(key);
+ }
+ this.cropsMap.delete(key);
+ this.tiles[y][x].hasCrop = false;
+ }
+ }
+
+ updateCropVisual(x, y, stage) {
+ const key = `${x},${y}`;
+ if (this.visibleCrops.has(key)) {
+ const sprite = this.visibleCrops.get(key);
+ sprite.setTexture(`crop_stage_${stage}`);
+ }
+ }
+
+ getTile(x, y) {
+ if (this.tiles[y] && this.tiles[y][x]) {
+ return this.tiles[y][x];
+ }
+ return null;
+ }
+
+ updateCulling(camera) {
+ this.updateChunks(camera);
+
+ const view = camera.worldView;
+ let buffer = 200;
+ if (this.scene.settings && this.scene.settings.viewDistance === 'LOW') buffer = 50;
+
+ const left = view.x - buffer - this.offsetX;
+ const top = view.y - buffer - this.offsetY;
+ const right = view.x + view.width + buffer - this.offsetX;
+ const bottom = view.y + view.height + buffer - this.offsetY;
+
+ const p1 = this.iso.toGrid(left, top);
+ const p2 = this.iso.toGrid(right, top);
+ const p3 = this.iso.toGrid(left, bottom);
+ const p4 = this.iso.toGrid(right, bottom);
+
+ const minGridX = Math.floor(Math.min(p1.x, p2.x, p3.x, p4.x));
+ const maxGridX = Math.ceil(Math.max(p1.x, p2.x, p3.x, p4.x));
+ const minGridY = Math.floor(Math.min(p1.y, p2.y, p3.y, p4.y));
+ const maxGridY = Math.ceil(Math.max(p1.y, p2.y, p3.y, p4.y));
+
+ const startX = Math.max(0, minGridX);
+ const endX = Math.min(this.width, maxGridX);
+ const startY = Math.max(0, minGridY);
+ const endY = Math.min(this.height, maxGridY);
+
+ const neededTileKeys = new Set();
+ const neededDecorKeys = new Set();
+ const neededCropKeys = new Set();
+
+ const voxelOffset = 12;
+
+ for (let y = startY; y < endY; y++) {
+ for (let x = startX; x < endX; x++) {
+ const key = `${x},${y}`;
+ const tile = this.tiles[y][x];
+
+ if (tile) {
+ neededTileKeys.add(key);
+ if (!this.visibleTiles.has(key)) {
+ const sprite = this.tilePool.get();
+
+ // Use ANIMATED water frames (not static bubble texture!)
+ if (tile.type === 'water') {
+ sprite.setTexture('water_frame_0'); // Start with frame 0
+ sprite.isWater = true; // Mark for animation
+
+ // NO alpha tween - animation handles it
+ } else {
+ sprite.setTexture(tile.type);
+ }
+
+ const screenPos = this.iso.toScreen(x, y);
+
+ // ๐ SKIP HEIGHT EFFECTS FOR WATER (prevents grid lines!)
+ if (tile.type === 'water') {
+ sprite.setPosition(
+ Math.round(screenPos.x + this.offsetX),
+ Math.round(screenPos.y + this.offsetY)
+ );
+ sprite.setScale(1.0);
+ sprite.clearTint();
+ } else {
+ // ๐๏ธ HEIGHT VISUALIZATION (2.5D Effect) - EXTREME!
+ const height = tile.height || 0;
+
+ // 1. Tint Effect (EXTREME CONTRAST - black valleys, white peaks)
+ // Height 0 = 0x666666 (dark gray), Height 5 = 0xffffff (pure white)
+ const tintValue = 0x666666 + (height * 0x333333);
+ sprite.setTint(tintValue);
+
+ // 2. Scale Variation (MASSIVE - 50% size increase!)
+ const scaleBonus = 1.0 + (height * 0.1); // Max +50% at height 5
+ sprite.setScale(scaleBonus);
+
+ // 3. Y-Offset (HUGE elevation - mountains!)
+ const elevationOffset = -(height * 15); // Each height level = 15px up!
+
+ sprite.setPosition(
+ Math.round(screenPos.x + this.offsetX),
+ Math.round(screenPos.y + this.offsetY + elevationOffset)
+ );
+ }
+
+ sprite.setDepth(this.iso.getDepth(x, y, this.iso.LAYER_FLOOR)); // Tiles = Floor
+ this.visibleTiles.set(key, sprite);
+ }
+ }
+
+ const decor = this.decorationsMap.get(key);
+ if (decor) {
+ neededDecorKeys.add(key);
+ if (!this.visibleDecorations.has(key)) {
+ const sprite = this.decorationPool.get();
+ const screenPos = this.iso.toScreen(x, y);
+
+ sprite.setPosition(Math.round(screenPos.x + this.offsetX), Math.round(screenPos.y + this.offsetY - voxelOffset));
+
+ if (decor.type.includes('house') || decor.type.includes('market') || decor.type.includes('structure')) {
+ sprite.setOrigin(0.5, 0.8);
+ } else {
+ // DREVESA: Origin na dno, da Y-sort deluje
+ sprite.setOrigin(0.5, 1.0);
+ }
+
+ sprite.setTexture(decor.type);
+ sprite.setScale(decor.scale || 1.0);
+
+ if (decor.alpha !== undefined) {
+ sprite.setAlpha(decor.alpha);
+ }
+
+ // Layer Objects
+ sprite.setDepth(this.iso.getDepth(x, y, this.iso.LAYER_OBJECTS));
+
+ // ๐ฏ HYBRID POINTER EVENTS - Click-to-collect system
+ // Only for collectible resources (trees, rocks, etc.)
+ const isCollectible = decor.type.includes('tree') ||
+ decor.type.includes('rock') ||
+ decor.type.includes('bush') ||
+ decor.type.includes('flower');
+
+ if (isCollectible) {
+ // Make interactive with hand cursor
+ sprite.setInteractive({ useHandCursor: true });
+
+ // Store grid position for later use
+ sprite.setData('gridX', x);
+ sprite.setData('gridY', y);
+ sprite.setData('decorType', decor.type);
+
+ // HOVER EVENT - Yellow highlight
+ sprite.on('pointerover', () => {
+ sprite.setTint(0xffff00); // Yellow highlight
+ });
+
+ sprite.on('pointerout', () => {
+ sprite.clearTint(); // Remove highlight
+ });
+
+ // CLICK EVENT - Collect resource (with proximity check)
+ sprite.on('pointerdown', () => {
+ this.handleResourceClick(x, y, decor.type, sprite);
+ });
+ }
+
+ this.visibleDecorations.set(key, sprite);
+ }
+ }
+
+ const crop = this.cropsMap.get(key);
+ if (crop) {
+ neededCropKeys.add(key);
+ if (!this.visibleCrops.has(key)) {
+ const sprite = this.cropPool.get();
+ const screenPos = this.iso.toScreen(x, y);
+ sprite.setPosition(Math.round(screenPos.x + this.offsetX), Math.round(screenPos.y + this.offsetY - voxelOffset));
+
+ const cropType = crop.type || 'wheat';
+ // ฤe je wheat, uporabimo 'crop_stage_' za nazaj zdruลพljivost s TextureGeneratorjem?
+ // TextureGenerator dela 'crop_stage_X'.
+ // ฤe dodam 'wheat_stage_X', moram posodobiti TextureGenerator.
+ // Za zdaj:
+ let textureKey = `crop_stage_${crop.stage}`;
+ if (cropType === 'corn') textureKey = `corn_stage_${crop.stage}`;
+ if (cropType === 'wheat' && this.scene.textures.exists('wheat_stage_1')) textureKey = `wheat_stage_${crop.stage}`;
+
+ sprite.setTexture(textureKey);
+
+ sprite.setOrigin(0.5, 1);
+ // Layer Objects (da igralec hodi okoli njih)
+ sprite.setDepth(this.iso.getDepth(x, y, this.iso.LAYER_OBJECTS));
+ this.visibleCrops.set(key, sprite);
+ }
+ }
+ }
+ }
+
+ for (const [key, sprite] of this.visibleTiles) {
+ if (!neededTileKeys.has(key)) {
+ sprite.setVisible(false);
+ this.tilePool.release(sprite);
+ this.visibleTiles.delete(key);
+ }
+ }
+ for (const [key, sprite] of this.visibleDecorations) {
+ if (!neededDecorKeys.has(key)) {
+ sprite.setVisible(false);
+ this.decorationPool.release(sprite);
+ this.visibleDecorations.delete(key);
+ }
+ }
+ for (const [key, sprite] of this.visibleCrops) {
+ if (!neededCropKeys.has(key)) {
+ sprite.setVisible(false);
+ this.cropPool.release(sprite);
+ this.visibleCrops.delete(key);
+ }
+ }
+ }
+
+ getTile(x, y) {
+ if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
+ return this.tiles[y][x];
+ }
+ return null;
+ }
+
+ update(delta) {
+ // Water animation (250ms per frame = 4 FPS)
+ this.waterAnimTimer = (this.waterAnimTimer || 0) + delta;
+ if (this.waterAnimTimer > 250) {
+ this.waterAnimTimer = 0;
+ this.waterCurrentFrame = ((this.waterCurrentFrame || 0) + 1) % 4;
+
+ // Update all water tiles
+ for (const [key, sprite] of this.visibleTiles) {
+ if (sprite.isWater) {
+ sprite.setTexture(`water_frame_${this.waterCurrentFrame}`);
+ }
+ }
+ }
+
+ this.growthTimer = (this.growthTimer || 0) + delta;
+ if (this.growthTimer < 5000) return;
+ this.growthTimer = 0;
+
+ const weather = this.scene.weatherSystem;
+ if (!weather) return;
+ const currentDay = weather.getDayCount();
+
+ const healthyType = window.SPRITE_TREE_HEALTHY || 'tree_green_final';
+
+ for (const decor of this.decorationsMap.values()) {
+ if (decor.type.includes('sapling')) {
+ if (decor.plantDay !== undefined && decor.plantDay > -100 && (currentDay - decor.plantDay >= 3)) {
+ decor.type = healthyType;
+ decor.scale = 1.0;
+ decor.hp = 10;
+ decor.plantDay = -1;
+
+ const key = decor.id;
+ if (this.visibleDecorations.has(key)) {
+ const sprite = this.visibleDecorations.get(key);
+ sprite.setTexture(decor.type);
+ sprite.setScale(decor.scale);
+ this.scene.tweens.add({
+ targets: sprite,
+ scaleY: { from: 0.1, to: decor.scale },
+ duration: 800,
+ ease: 'Bounce.out'
+ });
+ console.log(`๐ณ Tree grew on Day ${currentDay}`);
+ }
+ }
+ }
+ }
+ }
+
+ placeStructure(x, y, type) {
+ // Generate textures if needed
+ if (type === 'chest' && !this.scene.textures.exists('chest')) {
+ TextureGenerator.createChestSprite(this.scene, 'chest');
+ }
+ if (type === 'spawner' && !this.scene.textures.exists('spawner')) {
+ TextureGenerator.createSpawnerSprite(this.scene, 'spawner');
+ }
+ if (type === 'ruin' && !this.scene.textures.exists('ruin')) {
+ TextureGenerator.createStructureSprite(this.scene, 'ruin', 'ruin');
+ }
+ if (type === 'arena' && !this.scene.textures.exists('arena')) {
+ // Arena uses ruin texture for now
+ TextureGenerator.createStructureSprite(this.scene, 'arena', 'ruin');
+ }
+ if (type.startsWith('signpost')) {
+ const textMap = { 'signpost_city': 'โ', 'signpost_farm': 'โ', 'signpost_both': 'โ
' };
+ const text = textMap[type] || '?';
+ if (!this.scene.textures.exists(type)) {
+ TextureGenerator.createSignpostSprite(this.scene, type, text);
+ }
+ }
+
+ // Place as decoration
+ this.addDecoration(x, y, type);
+ console.log(`๐๏ธ Place ${type} at (${x}, ${y})`);
+ }
+
+ setSolid(x, y, isSolid) {
+ if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
+ this.tiles[y][x].solid = isSolid;
+ }
+ }
+
+ // ๐๏ธ HEIGHT-AWARE COLLISION
+ // Returns true if tile is unwalkable (solid OR cliff)
+ isSolid(x, y, fromX = null, fromY = null) {
+ // Out of bounds = solid
+ if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
+ return true;
+ }
+
+ const tile = this.tiles[y][x];
+
+ // 1. Check if tile itself is solid (walls, etc.)
+ if (tile.solid) {
+ return true;
+ }
+
+ // 2. Check height difference (cliff detection)
+ if (fromX !== null && fromY !== null) {
+ const fromTile = this.getTile(fromX, fromY);
+ if (fromTile) {
+ const fromHeight = fromTile.height || 0;
+ const toHeight = tile.height || 0;
+ const heightDiff = Math.abs(toHeight - fromHeight);
+
+ // Can't walk over height difference > 1 (cliffs!)
+ if (heightDiff > 1) {
+ console.log(`๐๏ธ Blocked by cliff! Height diff: ${heightDiff} (from ${fromHeight} to ${toHeight})`);
+ return true;
+ }
+ }
+ }
+
+ return false; // Walkable
+ }
+
+ /**
+ * ๐ฏ HYBRID RESOURCE CLICK HANDLER
+ * Handles click-to-collect with proximity check
+ * @param {number} x - Grid X position
+ * @param {number} y - Grid Y position
+ * @param {string} decorType - Type of decoration (tree, rock, etc.)
+ * @param {Phaser.GameObjects.Sprite} sprite - The clicked sprite
+ */
+ handleResourceClick(x, y, decorType, sprite) {
+ // 1. Get player position
+ if (!this.scene.player) {
+ console.warn('โ ๏ธ Player not found');
+ return;
+ }
+
+ const playerPos = this.scene.player.getPosition();
+ const playerX = playerPos.x;
+ const playerY = playerPos.y;
+
+ // 2. PROXIMITY CHECK - Player must be within 3 tiles
+ const distance = Phaser.Math.Distance.Between(playerX, playerY, x, y);
+ const MAX_DISTANCE = 3; // 3 tiles
+
+ if (distance > MAX_DISTANCE) {
+ // Too far - show warning
+ console.log(`โ ๏ธ Too far! Distance: ${distance.toFixed(1)} tiles`);
+
+ // Visual feedback - shake sprite
+ this.scene.tweens.add({
+ targets: sprite,
+ x: sprite.x + 5,
+ duration: 50,
+ yoyo: true,
+ repeat: 2
+ });
+
+ // Floating text
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: sprite.x,
+ y: sprite.y - 50,
+ text: 'Preblizu!',
+ color: '#ff4444'
+ });
+ }
+
+ return;
+ }
+
+ // 3. TOOL CHECK - Player needs correct tool
+ const requiredTool = this.getRequiredTool(decorType);
+ const hasTool = this.scene.player.hasToolEquipped(requiredTool);
+
+ if (!hasTool && requiredTool) {
+ console.log(`โ ๏ธ Need tool: ${requiredTool}`);
+
+ // Floating text
+ if (this.scene.events) {
+ this.scene.events.emit('show-floating-text', {
+ x: sprite.x,
+ y: sprite.y - 50,
+ text: `Potrebujeลก: ${requiredTool}`,
+ color: '#ff4444'
+ });
+ }
+
+ return;
+ }
+
+ // 4. COLLECT - Damage decoration (uses existing HP system)
+ console.log(`โ
Collecting ${decorType} at (${x}, ${y})`);
+
+ // Use existing damage system (maintains HP logic)
+ const result = this.damageDecoration(x, y, 1); // 1 hit per click
+
+ // Optional: Instant collect mode (if you want 1-click collect)
+ // this.damageDecoration(x, y, 999);
+
+ // Sound effect
+ if (this.scene.soundManager) {
+ if (decorType.includes('tree')) {
+ this.scene.soundManager.playChopSound();
+ } else if (decorType.includes('rock')) {
+ this.scene.soundManager.playMineSound();
+ }
+ }
+ }
+
+ /**
+ * Get required tool for decoration type
+ */
+ getRequiredTool(decorType) {
+ if (decorType.includes('tree')) return 'axe';
+ if (decorType.includes('rock')) return 'pickaxe';
+ if (decorType.includes('bush')) return 'axe';
+ return null; // No tool required (flowers, etc.)
+ }
+
+ // Water Animation Update - DISABLED (using tweens now)
+ update(time, delta) {
+ // Water animation je zdaj implementirana z Phaser tweens
+ // Ni potrebe po roฤnem frame update-u
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TimeSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TimeSystem.js
new file mode 100644
index 000000000..22c54bc2e
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TimeSystem.js
@@ -0,0 +1,447 @@
+/**
+ * TimeSystem.js
+ * =============
+ * KRVAVA ลฝETEV - Complete Day/Night Cycle System (Phase 18)
+ *
+ * Features:
+ * - 25-minute days (4 time periods)
+ * - Dynamic lighting system
+ * - Time-based gameplay mechanics
+ * - Sleep system with collapse
+ * - Morning/Night bonuses
+ * - Werewolf spawns at night
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class TimeSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Time configuration
+ this.dayLength = 25 * 60 * 1000; // 25 minutes in ms
+ this.currentTime = 6 * 60; // Start at 6:00 AM (in minutes)
+ this.totalMinutes = 24 * 60; // 1440 minutes in a day
+
+ // Time periods (in hours)
+ this.timePeriods = {
+ morning: { start: 6, end: 12, name: 'Morning', icon: '๐
' },
+ day: { start: 12, end: 18, name: 'Day', icon: 'โ๏ธ' },
+ evening: { start: 18, end: 21, name: 'Evening', icon: '๐' },
+ night: { start: 21, end: 6, name: 'Night', icon: '๐' }
+ };
+
+ // Game state
+ this.dayNumber = 1;
+ this.season = 'spring'; // 'spring', 'summer', 'fall', 'winter'
+ this.isPaused = false;
+
+ // Sleep system
+ this.playerEnergy = 100;
+ this.lastSleptTime = 0;
+ this.hoursAwake = 0;
+ this.collapseWarningShown = false;
+
+ // Lighting
+ this.ambientLight = null;
+ this.currentLightColor = 0xFFFFFF;
+
+ // Gameplay modifiers
+ this.zombieEfficiency = 1.0;
+ this.hostileSpawnRate = 1.0;
+ this.werewolfActive = false;
+
+ console.log('โฐ TimeSystem initialized');
+
+ // Start time progression
+ this.startTime();
+ }
+
+ /**
+ * Start time progression
+ */
+ startTime() {
+ // Time progression (1 real second = 0.576 game minutes)
+ this.timeInterval = setInterval(() => {
+ if (!this.isPaused) {
+ this.updateTime(1000); // Update every second
+ }
+ }, 1000);
+
+ console.log('โฐ Time started at 6:00 AM, Day 1');
+ }
+
+ /**
+ * Update time
+ */
+ updateTime(deltaMs) {
+ // Calculate game minutes passed
+ const gameMinutesPassed = (deltaMs / this.dayLength) * this.totalMinutes;
+
+ this.currentTime += gameMinutesPassed;
+ this.hoursAwake += gameMinutesPassed / 60;
+
+ // Check for new day
+ if (this.currentTime >= this.totalMinutes) {
+ this.newDay();
+ }
+
+ // Update systems
+ this.updateLighting();
+ this.updateGameplayModifiers();
+ this.checkSleepDeprivation();
+ }
+
+ /**
+ * New day
+ */
+ newDay() {
+ this.currentTime = this.currentTime % this.totalMinutes;
+ this.dayNumber++;
+
+ console.log(`๐
Day ${this.dayNumber} begins!`);
+
+ // Check season change (every 28 days)
+ if (this.dayNumber % 28 === 0) {
+ this.changeSeason();
+ }
+
+ this.showNotification({
+ title: `Day ${this.dayNumber}`,
+ text: `๐
${this.getCurrentPeriod().name} - ${this.season}`,
+ icon: '๐
'
+ });
+ }
+
+ /**
+ * Change season
+ */
+ changeSeason() {
+ const seasons = ['spring', 'summer', 'fall', 'winter'];
+ const currentIndex = seasons.indexOf(this.season);
+ this.season = seasons[(currentIndex + 1) % 4];
+
+ console.log(`๐ Season changed to: ${this.season}`);
+
+ this.showNotification({
+ title: 'Season Changed!',
+ text: `๐ Now: ${this.season.toUpperCase()}`,
+ icon: '๐
'
+ });
+ }
+
+ /**
+ * Get current time period
+ */
+ getCurrentPeriod() {
+ const hour = Math.floor(this.currentTime / 60);
+
+ if (hour >= 6 && hour < 12) return this.timePeriods.morning;
+ if (hour >= 12 && hour < 18) return this.timePeriods.day;
+ if (hour >= 18 && hour < 21) return this.timePeriods.evening;
+ return this.timePeriods.night;
+ }
+
+ /**
+ * Update lighting
+ */
+ updateLighting() {
+ const period = this.getCurrentPeriod();
+ let targetColor;
+
+ switch (period.name) {
+ case 'Morning':
+ targetColor = 0xFFE4B5; // Moccasin (warm morning light)
+ break;
+ case 'Day':
+ targetColor = 0xFFFFFF; // Full white
+ break;
+ case 'Evening':
+ targetColor = 0xFFA500; // Orange (sunset)
+ break;
+ case 'Night':
+ targetColor = 0x191970; // Midnight blue
+ break;
+ }
+
+ // Smooth transition
+ if (this.currentLightColor !== targetColor) {
+ this.currentLightColor = targetColor;
+ this.applyLighting(targetColor);
+ }
+ }
+
+ /**
+ * Apply lighting to scene
+ */
+ applyLighting(color) {
+ // TODO: Apply to actual scene
+ // this.scene.cameras.main.setTint(color);
+ console.log(`๐ก Lighting changed: ${color.toString(16)}`);
+ }
+
+ /**
+ * Update gameplay modifiers
+ */
+ updateGameplayModifiers() {
+ const period = this.getCurrentPeriod();
+ const hour = Math.floor(this.currentTime / 60);
+
+ // Morning bonus (6:00 - 12:00)
+ if (period.name === 'Morning') {
+ this.zombieEfficiency = 1.2; // +20%
+ this.hostileSpawnRate = 0.5; // 50% reduction
+ this.werewolfActive = false;
+ }
+ // Day (12:00 - 18:00)
+ else if (period.name === 'Day') {
+ this.zombieEfficiency = 1.0;
+ this.hostileSpawnRate = 1.0;
+ this.werewolfActive = false;
+ }
+ // Evening (18:00 - 21:00)
+ else if (period.name === 'Evening') {
+ this.zombieEfficiency = 0.9;
+ this.hostileSpawnRate = 1.5;
+ this.werewolfActive = false;
+ }
+ // Night (21:00 - 6:00)
+ else {
+ this.zombieEfficiency = 0.7; // -30%
+ this.hostileSpawnRate = 2.0; // 2x spawns!
+ this.werewolfActive = true;
+
+ // Werewolf spawn chance
+ if (Math.random() < 0.01) { // 1% per update
+ this.spawnWerewolf();
+ }
+ }
+ }
+
+ /**
+ * Spawn werewolf
+ */
+ spawnWerewolf() {
+ console.log('๐บ Werewolf spawned!');
+
+ this.showNotification({
+ title: 'WEREWOLF!',
+ text: '๐บ Dangerous creature appeared!',
+ icon: 'โ ๏ธ'
+ });
+
+ // TODO: Actual werewolf spawn
+ }
+
+ /**
+ * Check sleep deprivation
+ */
+ checkSleepDeprivation() {
+ const hour = Math.floor(this.currentTime / 60);
+
+ // Energy decreases based on hours awake
+ if (this.hoursAwake > 16) {
+ this.playerEnergy = Math.max(0, 100 - ((this.hoursAwake - 16) * 10));
+ }
+
+ // Warning at 1:00 AM if still awake
+ if (hour === 1 && !this.collapseWarningShown) {
+ this.collapseWarningShown = true;
+
+ this.showNotification({
+ title: 'EXHAUSTED!',
+ text: '๐ด You need sleep! Will collapse at 2 AM!',
+ icon: 'โ ๏ธ'
+ });
+ }
+
+ // Force collapse at 2:00 AM
+ if (hour === 2 && this.hoursAwake > 20) {
+ this.forceCollapse();
+ }
+ }
+
+ /**
+ * Sleep in bed
+ */
+ sleep() {
+ console.log('๐ด Going to sleep...');
+
+ // Calculate sleep duration (sleep until 6 AM)
+ const hour = Math.floor(this.currentTime / 60);
+ let hoursToSleep;
+
+ if (hour >= 6) {
+ // Sleep until tomorrow's 6 AM
+ hoursToSleep = (30 - hour); // Hours until midnight + 6
+ } else {
+ // Sleep until today's 6 AM
+ hoursToSleep = 6 - hour;
+ }
+
+ // Advance time
+ this.currentTime += hoursToSleep * 60;
+ if (this.currentTime >= this.totalMinutes) {
+ this.newDay();
+ }
+
+ // Restore energy
+ this.playerEnergy = 100;
+ this.hoursAwake = 0;
+ this.lastSleptTime = this.dayNumber;
+ this.collapseWarningShown = false;
+
+ // Save game
+ this.saveGame();
+
+ console.log(`๐ด Slept for ${hoursToSleep} hours. Energy restored!`);
+
+ this.showNotification({
+ title: 'Well Rested!',
+ text: `๐ด Slept ${hoursToSleep} hours. Day ${this.dayNumber}`,
+ icon: 'โจ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Force collapse (didn't sleep)
+ */
+ forceCollapse() {
+ console.log('๐ Collapsed from exhaustion!');
+
+ this.showNotification({
+ title: 'COLLAPSED!',
+ text: '๐ You passed out from exhaustion!',
+ icon: 'โ ๏ธ'
+ });
+
+ // Lose some items/money as penalty
+ // TODO: Apply penalties
+
+ // Force sleep
+ this.sleep();
+ }
+
+ /**
+ * Save game
+ */
+ saveGame() {
+ try {
+ const saveData = {
+ time: this.currentTime,
+ day: this.dayNumber,
+ season: this.season,
+ energy: this.playerEnergy
+ };
+
+ localStorage.setItem('krvava_zetev_time', JSON.stringify(saveData));
+ console.log('๐พ Game saved (sleep)');
+ } catch (error) {
+ console.error('Failed to save game:', error);
+ }
+ }
+
+ /**
+ * Load game
+ */
+ loadGame() {
+ try {
+ const saved = localStorage.getItem('krvava_zetev_time');
+ if (saved) {
+ const data = JSON.parse(saved);
+ this.currentTime = data.time;
+ this.dayNumber = data.day;
+ this.season = data.season;
+ this.playerEnergy = data.energy;
+
+ console.log(`๐ฅ Game loaded: Day ${this.dayNumber}, ${this.getTimeString()}`);
+ return true;
+ }
+ } catch (error) {
+ console.error('Failed to load game:', error);
+ }
+ return false;
+ }
+
+ /**
+ * Get time as string
+ */
+ getTimeString() {
+ const totalMinutes = Math.floor(this.currentTime);
+ const hours = Math.floor(totalMinutes / 60) % 24;
+ const minutes = totalMinutes % 60;
+
+ const period = hours >= 12 ? 'PM' : 'AM';
+ const displayHours = hours % 12 || 12;
+
+ return `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`;
+ }
+
+ /**
+ * Get current info
+ */
+ getTimeInfo() {
+ const period = this.getCurrentPeriod();
+
+ return {
+ time: this.getTimeString(),
+ day: this.dayNumber,
+ season: this.season,
+ period: period.name,
+ periodIcon: period.icon,
+ energy: this.playerEnergy,
+ hoursAwake: Math.floor(this.hoursAwake),
+ zombieEfficiency: this.zombieEfficiency,
+ hostileSpawnRate: this.hostileSpawnRate,
+ werewolfActive: this.werewolfActive
+ };
+ }
+
+ /**
+ * Pause time
+ */
+ pauseTime() {
+ this.isPaused = true;
+ console.log('โธ๏ธ Time paused');
+ }
+
+ /**
+ * Resume time
+ */
+ resumeTime() {
+ this.isPaused = false;
+ console.log('โถ๏ธ Time resumed');
+ }
+
+ /**
+ * Set time (for testing)
+ */
+ setTime(hour, minute = 0) {
+ this.currentTime = (hour * 60) + minute;
+ console.log(`โฐ Time set to ${this.getTimeString()}`);
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ if (this.timeInterval) {
+ clearInterval(this.timeInterval);
+ }
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ToolSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ToolSystem.js
new file mode 100644
index 000000000..ad0208de2
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ToolSystem.js
@@ -0,0 +1,493 @@
+/**
+ * TOOL SYSTEM
+ * Manages tool upgrades (6 tiers), durability, repairs, and blacksmith zombies.
+ *
+ * Features:
+ * - 6 Tool Tiers: Wood โ Stone โ Iron โ Gold โ Diamond โ Ultimate
+ * - Durability System: Tools break but don't disappear, can be repaired
+ * - 3 Repair Methods: Ivan's Blacksmith, Repair Kits, Blacksmith Zombie
+ * - Blacksmith Zombie: Learn skill from Ivan, FREE overnight repairs
+ * - Ultimate Tools: Drill, Chainsaw, Mechanical Tiller (auto-abilities)
+ */
+class ToolSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Tool tiers
+ this.tiers = ['wood', 'stone', 'iron', 'gold', 'diamond', 'ultimate'];
+
+ // Tool definitions
+ this.tools = {
+ axe: { name: 'Axe', use: 'chop_trees', durabilityMult: 1.0 },
+ pickaxe: { name: 'Pickaxe', use: 'mine_rocks', durabilityMult: 1.2 },
+ hoe: { name: 'Hoe', use: 'till_soil', durabilityMult: 0.8 },
+ sword: { name: 'Sword', use: 'combat', durabilityMult: 1.5 },
+ shovel: { name: 'Shovel', use: 'dig_soil', durabilityMult: 1.0 },
+ sickle: { name: 'Sickle', use: 'harvest_crops', durabilityMult: 0.7 },
+ hammer: { name: 'Hammer', use: 'build_repair', durabilityMult: 1.3 },
+ drill: { name: 'Drill', use: 'auto_mine', durabilityMult: 2.0, ultimate: true },
+ chainsaw: { name: 'Chainsaw', use: 'auto_chop', durabilityMult: 2.0, ultimate: true },
+ mech_tiller: { name: 'Mechanical Tiller', use: 'auto_till', durabilityMult: 2.0, ultimate: true }
+ };
+
+ // Tier stats
+ this.tierStats = {
+ wood: { durability: 50, efficiency: 1.0, speed: 1.0, cost: 5, color: '#8B4513' },
+ stone: { durability: 100, efficiency: 1.5, speed: 1.2, cost: 15, color: '#808080' },
+ iron: { durability: 200, efficiency: 2.0, speed: 1.5, cost: 50, color: '#C0C0C0' },
+ gold: { durability: 150, efficiency: 3.0, speed: 2.0, cost: 100, color: '#FFD700' },
+ diamond: { durability: Infinity, efficiency: 4.0, speed: 2.5, cost: 500, color: '#00FFFF' },
+ ultimate: { durability: Infinity, efficiency: 10.0, speed: 5.0, cost: 2000, color: '#FF00FF' }
+ };
+
+ // Player's tools
+ this.playerTools = new Map(); // toolId -> tool data
+
+ // Blacksmith zombies
+ this.blacksmithZombies = []; // Zombies trained by Ivan
+ this.repairQueue = []; // Tools queued for overnight repair
+
+ // Ivan's shop
+ this.ivanShop = {
+ upgradeCosts: {
+ wood_to_stone: { gold: 50, iron: 5 },
+ stone_to_iron: { gold: 100, iron: 10, stone: 20 },
+ iron_to_gold: { gold: 250, iron: 25, gold_ore: 10 },
+ gold_to_diamond: { gold: 1000, diamond: 5, gold_ore: 50 },
+ diamond_to_ultimate: { gold: 5000, diamond: 25, atlantean_crystal: 10 }
+ },
+ repairCost: 10 // Gold per durability point
+ };
+
+ console.log('โ๏ธ Tool System initialized!');
+ }
+
+ /**
+ * Create a tool for the player
+ */
+ createTool(toolType, tier = 'wood') {
+ if (!this.tools[toolType]) {
+ console.log(`โ Unknown tool type: ${toolType}`);
+ return null;
+ }
+
+ if (!this.tiers.includes(tier)) {
+ console.log(`โ Unknown tier: ${tier}`);
+ return null;
+ }
+
+ const toolId = `${toolType}_${tier}_${Date.now()}`;
+ const toolDef = this.tools[toolType];
+ const tierStats = this.tierStats[tier];
+
+ const tool = {
+ id: toolId,
+ type: toolType,
+ name: `${tierStats.color === '#8B4513' ? '' : tier.charAt(0).toUpperCase() + tier.slice(1) + ' '}${toolDef.name}`,
+ tier: tier,
+ durability: tierStats.durability,
+ maxDurability: tierStats.durability,
+ efficiency: tierStats.efficiency,
+ speed: tierStats.speed,
+ broken: false,
+ color: tierStats.color,
+ use: toolDef.use,
+ ultimate: toolDef.ultimate || false
+ };
+
+ this.playerTools.set(toolId, tool);
+
+ console.log(`๐จ Created ${tool.name}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'New Tool!',
+ message: `${tool.name} acquired!`,
+ icon: '๐จ'
+ });
+
+ return toolId;
+ }
+
+ /**
+ * Use a tool (decreases durability)
+ */
+ useTool(toolId, intensity = 1) {
+ const tool = this.playerTools.get(toolId);
+ if (!tool) return false;
+
+ if (tool.broken) {
+ console.log(`โ ${tool.name} is broken! Repair it first.`);
+ this.scene.events.emit('notification', {
+ title: 'Tool Broken!',
+ message: `${tool.name} needs repair!`,
+ icon: '๐ง'
+ });
+ return false;
+ }
+
+ // Diamond and Ultimate tools never break
+ if (tool.tier === 'diamond' || tool.tier === 'ultimate') {
+ return true;
+ }
+
+ // Decrease durability
+ const durabilityLoss = intensity * (1.0 / tool.efficiency);
+ tool.durability -= durabilityLoss;
+
+ // Check if broken
+ if (tool.durability <= 0) {
+ tool.durability = 0;
+ tool.broken = true;
+
+ console.log(`๐ฅ ${tool.name} broke!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Tool Broke!',
+ message: `${tool.name} is broken! Visit Ivan or use a Repair Kit.`,
+ icon: '๐ฅ'
+ });
+
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playBreak();
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Repair tool at Ivan's Blacksmith
+ */
+ repairAtIvan(toolId) {
+ const tool = this.playerTools.get(toolId);
+ if (!tool) return { success: false, message: 'Tool not found' };
+
+ if (!tool.broken && tool.durability === tool.maxDurability) {
+ return { success: false, message: 'Tool is already in perfect condition!' };
+ }
+
+ const durabilityNeeded = tool.maxDurability - tool.durability;
+ const cost = Math.ceil(durabilityNeeded * this.ivanShop.repairCost);
+
+ // Check if player has gold (assume player.gold exists)
+ if (this.scene.player && this.scene.player.gold >= cost) {
+ this.scene.player.gold -= cost;
+ tool.durability = tool.maxDurability;
+ tool.broken = false;
+
+ console.log(`๐ง Ivan repaired ${tool.name} for ${cost} gold!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Tool Repaired!',
+ message: `Ivan repaired ${tool.name} for ${cost} gold!`,
+ icon: '๐ง'
+ });
+
+ return { success: true, cost: cost };
+ } else {
+ return { success: false, message: `Not enough gold! Need ${cost} gold.` };
+ }
+ }
+
+ /**
+ * Repair tool with Repair Kit
+ */
+ repairWithKit(toolId) {
+ const tool = this.playerTools.get(toolId);
+ if (!tool) return false;
+
+ // Check if player has repair kit
+ if (this.scene.inventorySystem && this.scene.inventorySystem.hasItem('repair_kit')) {
+ const restoreAmount = tool.maxDurability * 0.5; // Restores 50%
+ tool.durability = Math.min(tool.maxDurability, tool.durability + restoreAmount);
+
+ if (tool.durability > 0) {
+ tool.broken = false;
+ }
+
+ this.scene.inventorySystem.removeItem('repair_kit', 1);
+
+ console.log(`๐ง Used Repair Kit on ${tool.name}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Tool Repaired!',
+ message: `${tool.name} partially repaired!`,
+ icon: '๐ง'
+ });
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Queue tool for overnight repair by Blacksmith Zombie
+ */
+ queueForBlacksmithRepair(toolId) {
+ if (this.blacksmithZombies.length === 0) {
+ console.log('โ No Blacksmith Zombies available! Train one with Ivan first.');
+ return false;
+ }
+
+ const tool = this.playerTools.get(toolId);
+ if (!tool) return false;
+
+ if (!this.repairQueue.includes(toolId)) {
+ this.repairQueue.push(toolId);
+
+ console.log(`๐ ${tool.name} queued for FREE overnight repair!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Repair Queued!',
+ message: `${tool.name} will be repaired by morning!`,
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Process overnight repairs (called at 6 AM in-game)
+ */
+ processOvernightRepairs() {
+ if (this.repairQueue.length === 0) return;
+
+ const repairsPerZombie = 3; // Each blacksmith can repair 3 tools
+ const maxRepairs = this.blacksmithZombies.length * repairsPerZombie;
+
+ const repairedTools = [];
+
+ for (let i = 0; i < Math.min(this.repairQueue.length, maxRepairs); i++) {
+ const toolId = this.repairQueue[i];
+ const tool = this.playerTools.get(toolId);
+
+ if (tool) {
+ tool.durability = tool.maxDurability;
+ tool.broken = false;
+ repairedTools.push(tool.name);
+ }
+ }
+
+ this.repairQueue = this.repairQueue.slice(maxRepairs);
+
+ console.log(`๐
Blacksmith Zombies repaired ${repairedTools.length} tools overnight!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Morning Repairs Complete!',
+ message: `${repairedTools.length} tools repaired for FREE!`,
+ icon: '๐
'
+ });
+
+ return repairedTools;
+ }
+
+ /**
+ * Train a zombie to become a Blacksmith
+ */
+ trainBlacksmith(zombieId) {
+ if (this.blacksmithZombies.includes(zombieId)) {
+ console.log('โ This zombie is already a Blacksmith!');
+ return false;
+ }
+
+ // Check if zombie is Lv5+ (requirement)
+ const zombieSystem = this.scene.smartZombieSystem;
+ if (zombieSystem) {
+ const zombie = zombieSystem.zombies.get(zombieId);
+ if (!zombie || zombie.level < 5) {
+ console.log('โ Zombie must be Lv5+ to train as Blacksmith!');
+ return false;
+ }
+ }
+
+ // Training cost
+ const trainingCost = 500;
+ if (this.scene.player && this.scene.player.gold >= trainingCost) {
+ this.scene.player.gold -= trainingCost;
+ this.blacksmithZombies.push(zombieId);
+
+ console.log(`โ๏ธ Zombie ${zombieId} trained as Blacksmith by Ivan!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Blacksmith Trained!',
+ message: `Your zombie learned Ivan's blacksmith skills!`,
+ icon: 'โ๏ธ'
+ });
+
+ return true;
+ } else {
+ console.log(`โ Not enough gold! Need ${trainingCost} gold.`);
+ return false;
+ }
+ }
+
+ /**
+ * Upgrade tool to next tier at Ivan's shop
+ */
+ upgradeTool(toolId) {
+ const tool = this.playerTools.get(toolId);
+ if (!tool) return { success: false, message: 'Tool not found' };
+
+ if (tool.tier === 'ultimate') {
+ return { success: false, message: 'Already at maximum tier!' };
+ }
+
+ const currentTierIndex = this.tiers.indexOf(tool.tier);
+ const nextTier = this.tiers[currentTierIndex + 1];
+
+ if (!nextTier) {
+ return { success: false, message: 'Cannot upgrade further!' };
+ }
+
+ // Get upgrade cost
+ const upgradeKey = `${tool.tier}_to_${nextTier}`;
+ const cost = this.ivanShop.upgradeCosts[upgradeKey];
+
+ if (!cost) {
+ return { success: false, message: 'Upgrade path not defined!' };
+ }
+
+ // Check if player has resources (simplified check)
+ // In real game, would check inventory system
+ const canAfford = true; // Placeholder
+
+ if (canAfford) {
+ // Upgrade tool
+ tool.tier = nextTier;
+ const newStats = this.tierStats[nextTier];
+ tool.durability = newStats.durability;
+ tool.maxDurability = newStats.durability;
+ tool.efficiency = newStats.efficiency;
+ tool.speed = newStats.speed;
+ tool.color = newStats.color;
+ tool.name = `${nextTier.charAt(0).toUpperCase() + nextTier.slice(1)} ${this.tools[tool.type].name}`;
+ tool.broken = false;
+
+ console.log(`โฌ๏ธ Upgraded to ${tool.name}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'Tool Upgraded!',
+ message: `${tool.name} is now more powerful!`,
+ icon: 'โฌ๏ธ'
+ });
+
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playUpgrade();
+ }
+
+ return { success: true, newTier: nextTier };
+ } else {
+ return { success: false, message: 'Not enough resources!' };
+ }
+ }
+
+ /**
+ * Craft Ultimate Tool
+ */
+ craftUltimateTool(toolType) {
+ if (!['drill', 'chainsaw', 'mech_tiller'].includes(toolType)) {
+ return { success: false, message: 'Not an ultimate tool!' };
+ }
+
+ const cost = this.tierStats.ultimate.cost;
+
+ // Check resources (simplified)
+ if (this.scene.player && this.scene.player.gold >= cost) {
+ this.scene.player.gold -= cost;
+
+ const toolId = this.createTool(toolType, 'ultimate');
+
+ console.log(`๐ Crafted Ultimate ${this.tools[toolType].name}!`);
+
+ this.scene.events.emit('notification', {
+ title: 'ULTIMATE TOOL!',
+ message: `Crafted ${this.tools[toolType].name}!`,
+ icon: '๐'
+ });
+
+ return { success: true, toolId: toolId };
+ } else {
+ return { success: false, message: `Need ${cost} gold!` };
+ }
+ }
+
+ /**
+ * Use ultimate tool's auto-ability
+ */
+ useUltimateAbility(toolId, position, radius = 3) {
+ const tool = this.playerTools.get(toolId);
+ if (!tool || !tool.ultimate) {
+ console.log('โ Not an ultimate tool!');
+ return false;
+ }
+
+ let affected = 0;
+
+ switch (tool.type) {
+ case 'drill':
+ // Auto-mine all rocks in radius
+ console.log(`๐ Drill auto-mining ${radius}x${radius} area!`);
+ affected = radius * radius;
+ break;
+
+ case 'chainsaw':
+ // Auto-chop all trees in radius
+ console.log(`๐ช Chainsaw auto-chopping ${radius}x${radius} area!`);
+ affected = radius * radius;
+ break;
+
+ case 'mech_tiller':
+ // Auto-till all soil in radius
+ console.log(`๐ Mechanical Tiller auto-tilling ${radius}x${radius} area!`);
+ affected = radius * radius;
+ break;
+ }
+
+ this.scene.events.emit('notification', {
+ title: 'AUTO-ABILITY!',
+ message: `${tool.name} processed ${affected} tiles!`,
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ /**
+ * Get tool stats
+ */
+ getToolStats(toolId) {
+ const tool = this.playerTools.get(toolId);
+ if (!tool) return null;
+
+ return {
+ name: tool.name,
+ tier: tool.tier,
+ durability: tool.tier === 'diamond' || tool.tier === 'ultimate'
+ ? 'Infinite'
+ : `${Math.round(tool.durability)}/${tool.maxDurability}`,
+ efficiency: `${tool.efficiency}x`,
+ speed: `${tool.speed}x`,
+ broken: tool.broken,
+ ultimate: tool.ultimate,
+ color: tool.color
+ };
+ }
+
+ /**
+ * Get all player tools
+ */
+ getAllTools() {
+ const tools = [];
+ this.playerTools.forEach((tool, id) => {
+ tools.push({
+ id: id,
+ ...this.getToolStats(id)
+ });
+ });
+ return tools;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TownGrowthSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TownGrowthSystem.js
new file mode 100644
index 000000000..ea02702ad
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TownGrowthSystem.js
@@ -0,0 +1,460 @@
+/**
+ * TOWN GROWTH SYSTEM - Population & Expansion
+ * Part of: Game Systems Expansion
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Dynamic town population (4 โ 20 NPCs)
+ * - Town sign with live stats
+ * - Small villages (5-10 scattered across map)
+ * - Population unlock requirements
+ * - Town Services unlock based on population
+ */
+
+class TownGrowthSystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Town stats
+ this.townName = 'Dolina Smrti';
+ this.population = 4; // Starting NPCs: Kai, Ana, Gronk, Baker
+ this.maxPopulation = 20;
+ this.populationSlots = [
+ { index: 1, unlocked: true, npc: 'kai' },
+ { index: 2, unlocked: true, npc: 'ana' },
+ { index: 3, unlocked: true, npc: 'gronk' },
+ { index: 4, unlocked: true, npc: 'baker' },
+ { index: 5, unlocked: false, npc: null, requirement: { farmLevel: 2 } },
+ { index: 6, unlocked: false, npc: null, requirement: { money: 10000 } },
+ { index: 7, unlocked: false, npc: null, requirement: { quest: 'expand_town_1' } },
+ { index: 8, unlocked: false, npc: null, requirement: { population: 6 } },
+ { index: 9, unlocked: false, npc: null, requirement: { building: 'bakery' } },
+ { index: 10, unlocked: false, npc: null, requirement: { building: 'barbershop' } },
+ { index: 11, unlocked: false, npc: null, requirement: { hearts: 5, npcId: 'any' } },
+ { index: 12, unlocked: false, npc: null, requirement: { quest: 'expand_town_2' } },
+ { index: 13, unlocked: false, npc: null, requirement: { zombieWorkers: 5 } },
+ { index: 14, unlocked: false, npc: null, requirement: { building: 'mine' } },
+ { index: 15, unlocked: false, npc: null, requirement: { money: 50000 } },
+ { index: 16, unlocked: false, npc: null, requirement: { quest: 'expand_town_3' } },
+ { index: 17, unlocked: false, npc: null, requirement: { marriage: true } },
+ { index: 18, unlocked: false, npc: null, requirement: { population: 15 } },
+ { index: 19, unlocked: false, npc: null, requirement: { allBuildings: true } },
+ { index: 20, unlocked: false, npc: null, requirement: { quest: 'town_master' } }
+ ];
+
+ // Town sign
+ this.townSign = {
+ location: { x: 400, y: 300 },
+ visible: true,
+ displayMode: 'population' // 'population', 'status', 'full'
+ };
+
+ // Small villages
+ this.villages = this.initializeVillages();
+
+ // Town services (unlock based on population)
+ this.services = {
+ market: { unlocked: false, requiredPopulation: 6 },
+ hospital: { unlocked: false, requiredPopulation: 8 },
+ school: { unlocked: false, requiredPopulation: 10 },
+ bank: { unlocked: false, requiredPopulation: 12 },
+ museum: { unlocked: false, requiredPopulation: 15 },
+ theater: { unlocked: false, requiredPopulation: 18 }
+ };
+ }
+
+ /**
+ * Initialize small villages
+ */
+ initializeVillages() {
+ return [
+ {
+ id: 'village_north',
+ name: 'Severna Vas',
+ position: { x: 3000, y: 500 },
+ population: 7,
+ discovered: false,
+ npcs: [
+ { id: 'fisherman', name: 'Old Fisher', hobby: 'fishing' },
+ { id: 'hunter', name: 'Hunter Dane', hobby: 'hunting' },
+ { id: 'hermit', name: 'Wise Hermit', hobby: 'meditation' }
+ ],
+ specialItems: ['ancient_fishing_rod', 'hunter_bow', 'meditation_mat']
+ },
+ {
+ id: 'village_east',
+ name: 'Vzhodna Stran',
+ position: { x: 5000, y: 2000 },
+ population: 5,
+ discovered: false,
+ npcs: [
+ { id: 'blacksmith', name: 'Master Smith', hobby: 'forging' },
+ { id: 'alchemist', name: 'Mysterious Alchemist', hobby: 'alchemy' }
+ ],
+ specialItems: ['master_anvil', 'legendary_hammer', 'philosopher_stone']
+ },
+ {
+ id: 'village_south',
+ name: 'Juลพno Naselje',
+ position: { x: 2500, y: 4500 },
+ population: 6,
+ discovered: false,
+ npcs: [
+ { id: 'trader', name: 'Traveling Trader', hobby: 'collecting' },
+ { id: 'musician', name: 'Bard Luka', hobby: 'music' }
+ ],
+ specialItems: ['exotic_seeds', 'rare_instruments', 'ancient_map']
+ },
+ {
+ id: 'village_west',
+ name: 'Zahodna Dolina',
+ position: { x: 500, y: 3000 },
+ population: 8,
+ discovered: false,
+ npcs: [
+ { id: 'chef', name: 'Chef Antonio', hobby: 'cooking' },
+ { id: 'librarian', name: 'Keeper of Books', hobby: 'reading' },
+ { id: 'artist', name: 'Painter Ana', hobby: 'painting' }
+ ],
+ specialItems: ['master_cookbook', 'ancient_tome', 'rare_pigments']
+ },
+ {
+ id: 'village_mysterious',
+ name: '??? Mystery Village',
+ position: { x: 4000, y: 4000 },
+ population: 10,
+ discovered: false,
+ hidden: true, // Only visible after completing special quest
+ npcs: [
+ { id: 'time_keeper', name: 'Keeper of Time', hobby: 'timekeeping' },
+ { id: 'oracle', name: 'Oracle of Dolina', hobby: 'prophecy' }
+ ],
+ specialItems: ['time_crystal', 'prophecy_scroll', 'reality_gem']
+ }
+ ];
+ }
+
+ /**
+ * Check and unlock new population slot
+ */
+ checkPopulationUnlocks() {
+ let newUnlocks = 0;
+
+ this.populationSlots.forEach(slot => {
+ if (!slot.unlocked && slot.requirement) {
+ if (this.meetsRequirement(slot.requirement)) {
+ slot.unlocked = true;
+ newUnlocks++;
+
+ this.game.showMessage(
+ `New population slot unlocked! (${this.population}/${this.maxPopulation})`
+ );
+ }
+ }
+ });
+
+ if (newUnlocks > 0) {
+ this.updateTownServices();
+ }
+
+ return newUnlocks;
+ }
+
+ /**
+ * Check if requirement is met
+ */
+ meetsRequirement(requirement) {
+ // Farm level
+ if (requirement.farmLevel) {
+ if (this.player.farmLevel < requirement.farmLevel) {
+ return false;
+ }
+ }
+
+ // Money
+ if (requirement.money) {
+ if (this.player.money < requirement.money) {
+ return false;
+ }
+ }
+
+ // Quest
+ if (requirement.quest) {
+ if (!this.player.hasCompletedQuest(requirement.quest)) {
+ return false;
+ }
+ }
+
+ // Building
+ if (requirement.building) {
+ if (!this.player.hasBuilding(requirement.building)) {
+ return false;
+ }
+ }
+
+ // Current population
+ if (requirement.population) {
+ if (this.population < requirement.population) {
+ return false;
+ }
+ }
+
+ // Zombie workers
+ if (requirement.zombieWorkers) {
+ const zombieCount = this.game.zombieWorkers?.getWorkerCount() || 0;
+ if (zombieCount < requirement.zombieWorkers) {
+ return false;
+ }
+ }
+
+ // Marriage
+ if (requirement.marriage) {
+ if (!this.player.isMarried) {
+ return false;
+ }
+ }
+
+ // Hearts with any NPC
+ if (requirement.hearts && requirement.npcId === 'any') {
+ const hasHighRelationship = this.game.npcs.getAllNPCs()
+ .some(npc => npc.relationshipHearts >= requirement.hearts);
+ if (!hasHighRelationship) {
+ return false;
+ }
+ }
+
+ // All buildings
+ if (requirement.allBuildings) {
+ const requiredBuildings = ['bakery', 'barbershop', 'lawyer', 'mine', 'hospital'];
+ if (!requiredBuildings.every(b => this.player.hasBuilding(b))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Invite new NPC to town
+ */
+ inviteNPC(npcId) {
+ // Find available slot
+ const availableSlot = this.populationSlots.find(
+ slot => slot.unlocked && slot.npc === null
+ );
+
+ if (!availableSlot) {
+ return {
+ success: false,
+ message: 'No available population slots!'
+ };
+ }
+
+ // Check if NPC exists
+ const npcData = this.game.npcs.getNPCData(npcId);
+ if (!npcData) {
+ return { success: false, message: 'NPC not found!' };
+ }
+
+ // Assign NPC to slot
+ availableSlot.npc = npcId;
+
+ // Spawn NPC in town
+ this.game.npcs.spawn(npcId, {
+ homeLocation: 'town',
+ moveInDate: this.game.time.currentDate
+ });
+
+ // Increase population
+ this.population++;
+
+ // Update town sign
+ this.updateTownSign();
+
+ // Check for service unlocks
+ this.updateTownServices();
+
+ this.game.showMessage(
+ `${npcData.name} moved to town! Population: ${this.population}/${this.maxPopulation}`
+ );
+
+ return { success: true, npc: npcData };
+ }
+
+ /**
+ * Update town services based on population
+ */
+ updateTownServices() {
+ let newServices = [];
+
+ Object.entries(this.services).forEach(([serviceId, service]) => {
+ if (!service.unlocked && this.population >= service.requiredPopulation) {
+ service.unlocked = true;
+ newServices.push(serviceId);
+
+ this.game.showMessage(
+ `๐๏ธ New service unlocked: ${serviceId}! (Pop: ${this.population})`
+ );
+
+ // Trigger service built event
+ this.game.emit('serviceUnlocked', {
+ serviceId: serviceId,
+ population: this.population
+ });
+ }
+ });
+
+ return newServices;
+ }
+
+ /**
+ * Update town sign display
+ */
+ updateTownSign() {
+ const signData = {
+ townName: this.townName,
+ population: this.population,
+ maxPopulation: this.maxPopulation,
+ status: this.getTownStatus(),
+ services: Object.keys(this.services).filter(s => this.services[s].unlocked).length
+ };
+
+ // Emit event to update sign sprite
+ this.game.emit('townSignUpdate', signData);
+ }
+
+ /**
+ * Get town status description
+ */
+ getTownStatus() {
+ if (this.population >= 18) {
+ return 'Thriving City';
+ }
+ if (this.population >= 15) {
+ return 'Prosperous Town';
+ }
+ if (this.population >= 10) {
+ return 'Growing Town';
+ }
+ if (this.population >= 6) {
+ return 'Small Town';
+ }
+ return 'Village';
+ }
+
+ /**
+ * Discover village
+ */
+ discoverVillage(villageId) {
+ const village = this.villages.find(v => v.id === villageId);
+
+ if (!village) {
+ return { success: false };
+ }
+
+ if (village.discovered) {
+ return {
+ success: false,
+ message: 'Village already discovered!'
+ };
+ }
+
+ // Check if hidden village requires quest
+ if (village.hidden && !this.player.hasCompletedQuest('find_mystery_village')) {
+ return {
+ success: false,
+ message: 'This village remains hidden...'
+ };
+ }
+
+ // Discover village
+ village.discovered = true;
+
+ // Spawn village NPCs
+ village.npcs.forEach(npcData => {
+ this.game.npcs.spawn(npcData.id, {
+ homeLocation: villageId,
+ position: village.position,
+ hobby: npcData.hobby
+ });
+ });
+
+ // Mark special items as available
+ village.specialItems.forEach(itemId => {
+ this.game.items.markAsDiscovered(itemId, villageId);
+ });
+
+ this.game.showMessage(
+ `Discovered ${village.name}! Population: ${village.population} NPCs`
+ );
+
+ // Achievement
+ const discoveredCount = this.villages.filter(v => v.discovered).length;
+ if (discoveredCount === this.villages.length) {
+ this.game.achievements.unlock('village_explorer');
+ }
+
+ return { success: true, village: village };
+ }
+
+ /**
+ * Get travel distance to village
+ */
+ getTravelDistance(villageId) {
+ const village = this.villages.find(v => v.id === villageId);
+ if (!village) return null;
+
+ const playerPos = this.player.getPosition();
+ const dx = village.position.x - playerPos.x;
+ const dy = village.position.y - playerPos.y;
+
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+
+ /**
+ * Get village trade options
+ */
+ getVillageTradeOptions(villageId) {
+ const village = this.villages.find(v => v.id === villageId);
+ if (!village || !village.discovered) return null;
+
+ return {
+ villageName: village.name,
+ npcs: village.npcs,
+ specialItems: village.specialItems,
+ population: village.population
+ };
+ }
+
+ /**
+ * Get town growth UI data
+ */
+ getTownGrowthUIData() {
+ return {
+ townName: this.townName,
+ population: this.population,
+ maxPopulation: this.maxPopulation,
+ status: this.getTownStatus(),
+ populationSlots: this.populationSlots,
+ availableSlots: this.populationSlots.filter(s => s.unlocked && !s.npc).length,
+ services: this.services,
+ villages: this.villages.filter(v => !v.hidden || v.discovered),
+ discoveredVillages: this.villages.filter(v => v.discovered).length,
+ totalVillages: this.villages.filter(v => !v.hidden).length
+ };
+ }
+
+ /**
+ * Update (check for new unlocks)
+ */
+ update() {
+ // Check for new population slot unlocks
+ this.checkPopulationUnlocks();
+
+ // Update town sign
+ this.updateTownSign();
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TownRestorationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TownRestorationSystem.js
new file mode 100644
index 000000000..489e05ffb
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TownRestorationSystem.js
@@ -0,0 +1,403 @@
+/**
+ * TownRestorationSystem.js
+ * =========================
+ * KRVAVA ลฝETEV - Complete Town Restoration System (Phase 19)
+ *
+ * Features:
+ * - 27 towns across 18 biomes
+ * - 150+ ruined buildings
+ * - 180 total NPCs
+ * - Building restoration mechanics
+ * - NPC move-in system
+ * - Global milestones & rewards
+ * - Major city endgame project
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class TownRestorationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Town registry
+ this.towns = new Map();
+ this.buildings = new Map();
+ this.npcs = new Map();
+
+ // Progress tracking
+ this.buildingsRestored = 0;
+ this.totalBuildings = 150;
+ this.npcsRescued = 0;
+ this.totalNPCs = 180;
+ this.townsCompleted = 0;
+ this.totalTowns = 27;
+
+ // Milestones
+ this.milestones = [10, 25, 50, 100, 180];
+ this.unlockedMilestones = [];
+
+ console.log('๐๏ธ TownRestorationSystem initialized');
+
+ // Register all towns
+ this.registerTowns();
+
+ // Register Hope Valley buildings
+ this.registerHopeValleyBuildings();
+ }
+
+ /**
+ * Register all towns
+ */
+ registerTowns() {
+ // Hope Valley (Starting town - 15 buildings)
+ this.towns.set('hope_valley', {
+ id: 'hope_valley',
+ name: 'Hope Valley',
+ biome: 'grassland',
+ icon: '๐๏ธ',
+ totalBuildings: 15,
+ restoredBuildings: 0,
+ npcs: 0,
+ isUnlocked: true,
+ isCompleted: false
+ });
+
+ // Other towns (6 more buildings each = 156 total)
+ const otherTowns = [
+ { id: 'forest_grove', name: 'Forest Grove', biome: 'forest', buildings: 6 },
+ { id: 'desert_oasis', name: 'Desert Oasis', biome: 'desert', buildings: 6 },
+ { id: 'frozen_harbor', name: 'Frozen Harbor', biome: 'frozen', buildings: 6 },
+ { id: 'volcanic_refuge', name: 'Volcanic Refuge', biome: 'volcanic', buildings: 6 },
+ { id: 'coastal_bay', name: 'Coastal Bay', biome: 'beach', buildings: 6 },
+ { id: 'mountain_peak', name: 'Mountain Peak', biome: 'mountain', buildings: 6 },
+ { id: 'swamp_village', name: 'Swamp Village', biome: 'swamp', buildings: 6 },
+ { id: 'crystal_city', name: 'Crystal City', biome: 'crystal', buildings: 8 },
+ { id: 'atlantis', name: 'Atlantis', biome: 'underwater', buildings: 10 },
+ // ... (27 total towns)
+ ];
+
+ otherTowns.forEach(town => {
+ this.towns.set(town.id, {
+ id: town.id,
+ name: town.name,
+ biome: town.biome,
+ icon: '๐๏ธ',
+ totalBuildings: town.buildings,
+ restoredBuildings: 0,
+ npcs: 0,
+ isUnlocked: false,
+ isCompleted: false
+ });
+ });
+
+ console.log(`โ
Registered ${this.towns.size} towns`);
+ }
+
+ /**
+ * Register Hope Valley buildings (15)
+ */
+ registerHopeValleyBuildings() {
+ const buildings = [
+ // Essential (NPCs' homes)
+ { id: 'ivan_house', name: "Ivan's House", npc: 'Ivan', materials: { wood: 100, stone: 50 }, time: 3 },
+ { id: 'marija_house', name: "Marija's House", npc: 'Marija', materials: { wood: 100, stone: 50 }, time: 3 },
+ { id: 'jakob_shop', name: "Jakob's Shop", npc: 'Jakob', materials: { wood: 150, iron: 30 }, time: 4 },
+ { id: 'dr_chen_clinic', name: "Dr. Chen's Clinic", npc: 'Dr. Chen', materials: { wood: 200, stone: 100 }, time: 5 },
+ { id: 'lena_bakery', name: "Lena's Bakery", npc: 'Lena', materials: { wood: 120, stone: 60 }, time: 4 },
+
+ // Community buildings
+ { id: 'town_hall', name: 'Town Hall', materials: { wood: 500, stone: 300, iron: 100 }, time: 10 },
+ { id: 'community_center', name: 'Community Center', materials: { wood: 400, stone: 250 }, time: 8 },
+ { id: 'library', name: 'Library', materials: { wood: 300, stone: 150 }, time: 6 },
+ { id: 'school', name: 'School', materials: { wood: 350, stone: 200 }, time: 7 },
+ { id: 'guard_tower', name: 'Guard Tower', materials: { stone: 400, iron: 150 }, time: 8 },
+
+ // Utility buildings
+ { id: 'warehouse', name: 'Warehouse', materials: { wood: 250, stone: 150 }, time: 5 },
+ { id: 'water_tower', name: 'Water Tower', materials: { stone: 300, iron: 100 }, time: 6 },
+ { id: 'power_station', name: 'Power Station', materials: { stone: 400, iron: 200, wire: 50 }, time: 9 },
+ { id: 'market', name: 'Market Square', materials: { wood: 200, stone: 100 }, time: 5 },
+ { id: 'fountain', name: 'Town Fountain', materials: { stone: 150, marble: 50 }, time: 4 }
+ ];
+
+ buildings.forEach(building => {
+ this.buildings.set(building.id, {
+ ...building,
+ town: 'hope_valley',
+ isRestored: false,
+ progress: 0,
+ workers: [],
+ startTime: null
+ });
+ });
+
+ console.log(`โ
Registered ${buildings.length} Hope Valley buildings`);
+ }
+
+ /**
+ * Start building restoration
+ */
+ startRestoration(buildingId, zombieWorkers = []) {
+ const building = this.buildings.get(buildingId);
+ if (!building) {
+ console.error(`Building ${buildingId} not found!`);
+ return false;
+ }
+
+ if (building.isRestored) {
+ console.log(`${building.name} is already restored!`);
+ return false;
+ }
+
+ // Check materials
+ if (!this.hasMaterials(building.materials)) {
+ console.log(`Not enough materials for ${building.name}!`);
+ return false;
+ }
+
+ // Consume materials
+ this.consumeMaterials(building.materials);
+
+ // Assign workers
+ building.workers = zombieWorkers;
+ building.startTime = Date.now();
+ building.progress = 0;
+
+ // Calculate construction time (base time reduced by workers)
+ const workerBonus = zombieWorkers.length * 0.2; // 20% per worker
+ const constructionTime = building.time * (1 - workerBonus);
+
+ console.log(`๐๏ธ Started restoring ${building.name} (${constructionTime} days with ${zombieWorkers.length} workers)`);
+
+ // Auto-complete after time
+ setTimeout(() => {
+ this.completeRestoration(buildingId);
+ }, constructionTime * 1000 * 60); // Convert to ms (for demo, 1 day = 1 min)
+
+ this.showNotification({
+ title: 'Restoration Started!',
+ text: `๐๏ธ ${building.name} - ${constructionTime} days`,
+ icon: '๐จ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Complete building restoration
+ */
+ completeRestoration(buildingId) {
+ const building = this.buildings.get(buildingId);
+ if (!building) return;
+
+ building.isRestored = true;
+ building.progress = 100;
+ this.buildingsRestored++;
+
+ console.log(`โ
${building.name} restored! (${this.buildingsRestored}/${this.totalBuildings})`);
+
+ // Move in NPC if building has one
+ if (building.npc) {
+ this.moveInNPC(building.npc, building.town);
+ }
+
+ // Update town progress
+ this.updateTownProgress(building.town);
+
+ // Check milestones
+ this.checkMilestones();
+
+ // Update visuals in the current scene
+ if (this.scene.updateBuildingVisuals) {
+ this.scene.updateBuildingVisuals(buildingId);
+ }
+
+ this.showNotification({
+ title: 'Building Complete!',
+ text: `โ
${building.name} restored!`,
+ icon: '๐๏ธ'
+ });
+ }
+
+ /**
+ * Move in NPC
+ */
+ moveInNPC(npcName, townId) {
+ const npc = {
+ name: npcName,
+ town: townId,
+ movedInDate: Date.now(),
+ hearts: 0,
+ giftsGiven: 0,
+ questsCompleted: 0
+ };
+
+ this.npcs.set(npcName, npc);
+ this.npcsRescued++;
+
+ console.log(`๐ค ${npcName} moved into ${townId}! (${this.npcsRescued}/${this.totalNPCs})`);
+
+ // Update town NPC count
+ const town = this.towns.get(townId);
+ if (town) {
+ town.npcs++;
+ }
+
+ this.showNotification({
+ title: 'NPC Moved In!',
+ text: `๐ค ${npcName} is now living in town!`,
+ icon: '๐ '
+ });
+ }
+
+ /**
+ * Update town progress
+ */
+ updateTownProgress(townId) {
+ const town = this.towns.get(townId);
+ if (!town) return;
+
+ // Count restored buildings in this town
+ const townBuildings = Array.from(this.buildings.values())
+ .filter(b => b.town === townId);
+ const restored = townBuildings.filter(b => b.isRestored).length;
+
+ town.restoredBuildings = restored;
+
+ // Check if town is complete
+ if (restored === town.totalBuildings) {
+ town.isCompleted = true;
+ this.townsCompleted++;
+
+ console.log(`๐ ${town.name} 100% COMPLETE! (${this.townsCompleted}/${this.totalTowns})`);
+
+ this.showNotification({
+ title: 'TOWN COMPLETE!',
+ text: `๐ ${town.name} fully restored!`,
+ icon: '๐'
+ });
+ }
+ }
+
+ /**
+ * Check milestones
+ */
+ checkMilestones() {
+ this.milestones.forEach(milestone => {
+ if (this.npcsRescued >= milestone && !this.unlockedMilestones.includes(milestone)) {
+ this.unlockMilestone(milestone);
+ }
+ });
+ }
+
+ /**
+ * Unlock milestone
+ */
+ unlockMilestone(milestone) {
+ this.unlockedMilestones.push(milestone);
+
+ console.log(`๐ MILESTONE: ${milestone} NPCs rescued!`);
+
+ // Grant rewards
+ const rewards = this.getMilestoneReward(milestone);
+
+ this.showNotification({
+ title: `MILESTONE: ${milestone} NPCs!`,
+ text: `๐ Unlocked: ${rewards.text}`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Get milestone reward
+ */
+ getMilestoneReward(milestone) {
+ const rewards = {
+ 10: { text: 'Community Center unlocked!', feature: 'community_center' },
+ 25: { text: 'Town Festivals enabled!', feature: 'festivals' },
+ 50: { text: 'Town Guard system!', feature: 'town_guard' },
+ 100: { text: 'Major City project unlocked!', feature: 'major_city' },
+ 180: { text: 'UTOPIA ENDING unlocked!', feature: 'utopia_ending' }
+ };
+
+ return rewards[milestone] || { text: 'Bonus unlocked!' };
+ }
+
+ hasMaterials(materials) {
+ if (!this.scene.inventorySystem) return true; // Safety fallback
+
+ for (const [item, count] of Object.entries(materials)) {
+ if (!this.scene.inventorySystem.hasItem(item, count)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ consumeMaterials(materials) {
+ if (!this.scene.inventorySystem) return;
+
+ for (const [item, count] of Object.entries(materials)) {
+ this.scene.inventorySystem.removeItem(item, count);
+ }
+ console.log('๐ฆ Materials consumed:', materials);
+ }
+
+ /**
+ * Get restoration progress
+ */
+ getProgress() {
+ return {
+ buildings: {
+ restored: this.buildingsRestored,
+ total: this.totalBuildings,
+ percentage: ((this.buildingsRestored / this.totalBuildings) * 100).toFixed(1)
+ },
+ npcs: {
+ rescued: this.npcsRescued,
+ total: this.totalNPCs,
+ percentage: ((this.npcsRescued / this.totalNPCs) * 100).toFixed(1)
+ },
+ towns: {
+ completed: this.townsCompleted,
+ total: this.totalTowns,
+ percentage: ((this.townsCompleted / this.totalTowns) * 100).toFixed(1)
+ },
+ milestones: this.unlockedMilestones
+ };
+ }
+
+ /**
+ * Get town info
+ */
+ getTownInfo(townId) {
+ return this.towns.get(townId);
+ }
+
+ /**
+ * Get all towns
+ */
+ getAllTowns() {
+ return Array.from(this.towns.values());
+ }
+
+ /**
+ * Get building info
+ */
+ getBuildingInfo(buildingId) {
+ return this.buildings.get(buildingId);
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TransitionSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TransitionSystem.js
new file mode 100644
index 000000000..ef86fe551
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TransitionSystem.js
@@ -0,0 +1,225 @@
+// TransitionSystem - Smooth transitions between biomes
+class TransitionSystem {
+ constructor(scene, biomeSystem) {
+ this.scene = scene;
+ this.biomeSystem = biomeSystem;
+
+ // Transition settings
+ this.transitionWidth = 25; // Tiles for smooth transition
+ this.blendEnabled = true;
+
+ console.log('๐ TransitionSystem initialized (width: ' + this.transitionWidth + ' tiles)');
+ }
+
+ // Check if tile is in transition zone
+ isInTransitionZone(x, y) {
+ const currentBiome = this.biomeSystem.getBiomeAt(x, y);
+
+ // Check nearby tiles for different biomes
+ const radius = Math.ceil(this.transitionWidth / 2);
+
+ for (let dy = -radius; dy <= radius; dy++) {
+ for (let dx = -radius; dx <= radius; dx++) {
+ if (dx === 0 && dy === 0) continue;
+
+ const checkX = x + dx;
+ const checkY = y + dy;
+ const checkBiome = this.biomeSystem.getBiomeAt(checkX, checkY);
+
+ if (checkBiome !== currentBiome) {
+ return true; // Different biome nearby!
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Get transition blend for a tile
+ getTransitionBlend(x, y) {
+ if (!this.blendEnabled) {
+ return {
+ primaryBiome: this.biomeSystem.getBiomeAt(x, y),
+ blendBiome: null,
+ blendFactor: 0
+ };
+ }
+
+ const currentBiome = this.biomeSystem.getBiomeAt(x, y);
+
+ // Find closest different biome
+ let closestBiome = null;
+ let closestDistance = Infinity;
+
+ const radius = this.transitionWidth;
+
+ for (let dy = -radius; dy <= radius; dy++) {
+ for (let dx = -radius; dx <= radius; dx++) {
+ if (dx === 0 && dy === 0) continue;
+
+ const checkX = x + dx;
+ const checkY = y + dy;
+ const checkBiome = this.biomeSystem.getBiomeAt(checkX, checkY);
+
+ if (checkBiome !== currentBiome) {
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestBiome = checkBiome;
+ }
+ }
+ }
+ }
+
+ if (closestBiome === null) {
+ // No transition
+ return {
+ primaryBiome: currentBiome,
+ blendBiome: null,
+ blendFactor: 0
+ };
+ }
+
+ // Calculate blend factor (0 = primary, 1 = blend)
+ const blendFactor = Math.max(0, 1 - (closestDistance / this.transitionWidth));
+
+ return {
+ primaryBiome: currentBiome,
+ blendBiome: closestBiome,
+ blendFactor: blendFactor
+ };
+ }
+
+ // Get blended tile color
+ getBlendedTileColor(x, y) {
+ const blend = this.getTransitionBlend(x, y);
+
+ if (blend.blendFactor === 0 || !blend.blendBiome) {
+ // No blending, return primary biome color
+ return this.getBiomeColor(blend.primaryBiome);
+ }
+
+ // Blend colors
+ const color1 = this.getBiomeColor(blend.primaryBiome);
+ const color2 = this.getBiomeColor(blend.blendBiome);
+
+ return this.blendColors(color1, color2, blend.blendFactor);
+ }
+
+ // Get biome base color
+ getBiomeColor(biomeId) {
+ const colors = {
+ 'grassland': 0x3CB371, // Green
+ 'forest': 0x2d5016, // Dark green
+ 'desert': 0xd4c4a1, // Sand
+ 'mountain': 0x808080, // Gray
+ 'swamp': 0x3d5a3d // Murky green
+ };
+
+ return colors[biomeId] || 0x3CB371;
+ }
+
+ // Blend two colors
+ blendColors(color1, color2, factor) {
+ // Extract RGB components
+ const r1 = (color1 >> 16) & 0xFF;
+ const g1 = (color1 >> 8) & 0xFF;
+ const b1 = color1 & 0xFF;
+
+ const r2 = (color2 >> 16) & 0xFF;
+ const g2 = (color2 >> 8) & 0xFF;
+ const b2 = color2 & 0xFF;
+
+ // Blend
+ const r = Math.round(r1 * (1 - factor) + r2 * factor);
+ const g = Math.round(g1 * (1 - factor) + g2 * factor);
+ const b = Math.round(b1 * (1 - factor) + b2 * factor);
+
+ // Combine back to hex
+ return (r << 16) | (g << 8) | b;
+ }
+
+ // Get mixed features for transition zones
+ getMixedFeatures(x, y) {
+ const blend = this.getTransitionBlend(x, y);
+
+ if (blend.blendFactor === 0 || !blend.blendBiome) {
+ // No transition, use primary biome features only
+ return this.biomeSystem.applyBiomeFeatures(x, y);
+ }
+
+ // Get features from both biomes
+ const primaryFeatures = this.getBiomeFeatures(blend.primaryBiome, x, y);
+ const blendFeatures = this.getBiomeFeatures(blend.blendBiome, x, y);
+
+ // Mix based on blend factor
+ const mixedFeatures = [];
+
+ // Randomly choose which biome's features to use based on blend factor
+ if (Math.random() > blend.blendFactor) {
+ // Use primary biome features
+ if (primaryFeatures.length > 0 && Math.random() < 0.5) { // Reduce density in transitions
+ mixedFeatures.push(...primaryFeatures);
+ }
+ } else {
+ // Use blend biome features
+ if (blendFeatures.length > 0 && Math.random() < 0.5) {
+ mixedFeatures.push(...blendFeatures);
+ }
+ }
+
+ return mixedFeatures;
+ }
+
+ // Get features for a specific biome
+ getBiomeFeatures(biomeId, x, y) {
+ const features = [];
+ const biomeData = this.biomeSystem.biomes[biomeId];
+
+ if (!biomeData || !biomeData.features) return features;
+
+ // Check spawn probability for trees
+ if (biomeData.features.trees && Math.random() < biomeData.features.trees) {
+ features.push({ type: 'tree', size: 'medium' });
+ }
+
+ // Check spawn probability for rocks
+ if (biomeData.features.rocks && Math.random() < biomeData.features.rocks) {
+ features.push({ type: 'rock', size: 'small' });
+ }
+
+ // Desert-specific features
+ if (biomeData.features.cacti && Math.random() < biomeData.features.cacti) {
+ features.push({ type: 'cactus' });
+ }
+
+ // Mountain-specific features
+ if (biomeData.features.largeRocks && Math.random() < biomeData.features.largeRocks) {
+ features.push({ type: 'boulder' });
+ }
+
+ return features;
+ }
+
+ // Get statistics
+ getStats() {
+ return {
+ transitionWidth: this.transitionWidth,
+ blendEnabled: this.blendEnabled,
+ biomeCount: Object.keys(this.biomeSystem.biomes).length
+ };
+ }
+
+ // Enable/disable transitions
+ setBlendEnabled(enabled) {
+ this.blendEnabled = enabled;
+ console.log('๐ Blend transitions: ' + (enabled ? 'ENABLED' : 'DISABLED'));
+ }
+
+ // Set transition width
+ setTransitionWidth(width) {
+ this.transitionWidth = Math.max(5, Math.min(50, width));
+ console.log('๐ Transition width set to: ' + this.transitionWidth);
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TransportSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TransportSystem.js
new file mode 100644
index 000000000..6bf7e5ca6
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TransportSystem.js
@@ -0,0 +1,644 @@
+/**
+ * TransportSystem.js
+ * ==================
+ * Manages all transportation methods in the game
+ * - Horses (5 breeds, different speeds)
+ * - Carts & Wagons (cargo capacity)
+ * - Trains (track-based, high speed, high capacity)
+ * - Water transport (kayak, SUP, raft, boat)
+ * - Bicycles & Boards (longboard, mountain board, snowboard)
+ *
+ * Uses: transportation_vehicles_detailed_1766097668396.tsx
+ * train_repairs_3_states (broken โ repairing โ rideable)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-22
+ */
+
+export default class TransportSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Current state
+ this.currentVehicle = null;
+ this.ownedVehicles = new Map();
+ this.trainTracks = [];
+
+ // Vehicle definitions
+ this.vehicleData = this.defineVehicles();
+
+ // Train repair state
+ this.trainRepairProgress = 0;
+
+ console.log('๐ TransportSystem initialized');
+ }
+
+ defineVehicles() {
+ return {
+ // ===== HORSES =====
+ horse_basic: {
+ name: 'Basic Horse',
+ type: 'mount',
+ speed: 200,
+ capacity: 0,
+ cost: 500,
+ unlockLevel: 3,
+ sprite: 'horse_brown',
+ waterAllowed: false,
+ description: 'Reliable companion for travel'
+ },
+ horse_fast: {
+ name: 'Racing Horse',
+ type: 'mount',
+ speed: 300,
+ capacity: 0,
+ cost: 1500,
+ unlockLevel: 7,
+ sprite: 'horse_white',
+ waterAllowed: false,
+ description: 'Swift steed for long distances'
+ },
+ horse_draft: {
+ name: 'Draft Horse',
+ type: 'mount',
+ speed: 150,
+ capacity: 50,
+ cost: 1200,
+ unlockLevel: 5,
+ sprite: 'horse_black',
+ waterAllowed: false,
+ description: 'Strong horse, can carry cargo'
+ },
+
+ // ===== CARTS & WAGONS =====
+ cart_wooden: {
+ name: 'Wooden Cart',
+ type: 'vehicle',
+ speed: 120,
+ capacity: 100,
+ cost: 800,
+ unlockLevel: 4,
+ blueprint: 'blueprint_cart',
+ sprite: 'cart_wooden',
+ waterAllowed: false,
+ requiresHorse: true,
+ description: 'Transport goods efficiently'
+ },
+ wagon_large: {
+ name: 'Large Wagon',
+ type: 'vehicle',
+ speed: 100,
+ capacity: 250,
+ cost: 2500,
+ unlockLevel: 8,
+ blueprint: 'blueprint_wagon',
+ sprite: 'wagon_large',
+ waterAllowed: false,
+ requiresHorse: true,
+ description: 'Massive cargo capacity'
+ },
+
+ // ===== TRAINS =====
+ train: {
+ name: 'Steam Train',
+ type: 'rail',
+ speed: 400,
+ capacity: 500,
+ cost: 10000,
+ unlockLevel: 15,
+ blueprint: 'blueprint_train',
+ sprite: 'train_rideable',
+ waterAllowed: false,
+ requiresTrack: true,
+ repairStages: ['broken', 'repairing', 'rideable'],
+ description: 'Ultimate transport - requires tracks'
+ },
+
+ // ===== WATER TRANSPORT =====
+ kayak: {
+ name: 'Kayak',
+ type: 'water',
+ speed: 150,
+ capacity: 20,
+ cost: 300,
+ unlockLevel: 6,
+ sprite: 'kayak',
+ waterOnly: true,
+ description: 'Navigate rivers and lakes'
+ },
+ sup: {
+ name: 'Stand-Up Paddleboard',
+ type: 'water',
+ speed: 100,
+ capacity: 5,
+ cost: 200,
+ unlockLevel: 5,
+ sprite: 'sup',
+ waterOnly: true,
+ description: 'Calm water exploration'
+ },
+ raft: {
+ name: 'Wooden Raft',
+ type: 'water',
+ speed: 80,
+ capacity: 50,
+ cost: 150,
+ unlockLevel: 3,
+ sprite: 'raft',
+ waterOnly: true,
+ description: 'Basic water transport'
+ },
+ boat: {
+ name: 'Fishing Boat',
+ type: 'water',
+ speed: 180,
+ capacity: 100,
+ cost: 1000,
+ unlockLevel: 10,
+ blueprint: 'blueprint_boat',
+ sprite: 'boat',
+ waterOnly: true,
+ description: 'Ocean-ready vessel'
+ },
+
+ // ===== BICYCLES & BOARDS =====
+ bicycle: {
+ name: 'Bicycle',
+ type: 'manual',
+ speed: 180,
+ capacity: 10,
+ cost: 250,
+ unlockLevel: 2,
+ sprite: 'bicycle',
+ waterAllowed: false,
+ description: 'Energy-efficient travel'
+ },
+ longboard: {
+ name: 'Longboard',
+ type: 'manual',
+ speed: 220,
+ capacity: 0,
+ cost: 150,
+ unlockLevel: 4,
+ sprite: 'longboard',
+ waterAllowed: false,
+ terrainBonus: { 'road': 1.5 }, // 50% faster on roads
+ description: 'Cruise downhill paths'
+ },
+ mountain_board: {
+ name: 'Mountain Board',
+ type: 'manual',
+ speed: 200,
+ capacity: 5,
+ cost: 300,
+ unlockLevel: 6,
+ sprite: 'mountain_board',
+ waterAllowed: false,
+ terrainBonus: { 'mountain': 1.3 },
+ description: 'Off-road board for rough terrain'
+ },
+ snowboard: {
+ name: 'Snowboard',
+ type: 'manual',
+ speed: 250,
+ capacity: 0,
+ cost: 200,
+ unlockLevel: 5,
+ sprite: 'snowboard',
+ waterAllowed: false,
+ terrainRequired: 'snow',
+ terrainBonus: { 'snow': 2.0 }, // 2x speed on snow
+ description: 'Winter transport - snow only'
+ }
+ };
+ }
+
+ // ===== VEHICLE MOUNTING =====
+
+ canMount(vehicleId, playerX, playerY) {
+ const vehicle = this.vehicleData[vehicleId];
+ if (!vehicle) {
+ return { canMount: false, reason: 'Vehicle not found' };
+ }
+
+ // Check if player owns this vehicle
+ if (!this.ownedVehicles.has(vehicleId)) {
+ return { canMount: false, reason: 'You don\'t own this vehicle' };
+ }
+
+ // Check if already mounted
+ if (this.currentVehicle) {
+ return { canMount: false, reason: 'Already using a vehicle' };
+ }
+
+ // Check level requirement
+ const playerLevel = this.scene.player?.stats?.level || 1;
+ if (playerLevel < vehicle.unlockLevel) {
+ return {
+ canMount: false,
+ reason: `Requires level ${vehicle.unlockLevel}`
+ };
+ }
+
+ // Check terrain compatibility
+ const currentTile = this.getTileAt(playerX, playerY);
+
+ if (vehicle.waterOnly && !currentTile.properties?.isWater) {
+ return { canMount: false, reason: 'Water vehicles require water' };
+ }
+
+ if (!vehicle.waterAllowed && currentTile.properties?.isWater) {
+ return { canMount: false, reason: 'Cannot use on water' };
+ }
+
+ if (vehicle.terrainRequired && currentTile.properties?.terrain !== vehicle.terrainRequired) {
+ return {
+ canMount: false,
+ reason: `Requires ${vehicle.terrainRequired} terrain`
+ };
+ }
+
+ // Check if train requires tracks
+ if (vehicle.requiresTrack && !this.isOnTrack(playerX, playerY)) {
+ return { canMount: false, reason: 'Train requires tracks' };
+ }
+
+ // Check if cart/wagon requires horse
+ if (vehicle.requiresHorse && !this.hasHorse()) {
+ return { canMount: false, reason: 'Requires a horse to pull' };
+ }
+
+ return { canMount: true };
+ }
+
+ mount(vehicleId) {
+ const check = this.canMount(vehicleId, this.scene.player.x, this.scene.player.y);
+ if (!check.canMount) {
+ this.scene.uiSystem?.showError(check.reason);
+ return false;
+ }
+
+ const vehicle = this.vehicleData[vehicleId];
+
+ // Update player speed
+ this.scene.player.baseSpeed = this.scene.player.baseSpeed || 100;
+ this.scene.player.setMaxVelocity(vehicle.speed);
+
+ // Change player sprite (riding animation)
+ const originalTexture = this.scene.player.texture.key;
+ this.scene.player.setTexture(`player_on_${vehicle.type}`);
+
+ // Store original state
+ this.currentVehicle = {
+ id: vehicleId,
+ data: vehicle,
+ originalSpeed: this.scene.player.baseSpeed,
+ originalTexture: originalTexture,
+ mountTime: Date.now()
+ };
+
+ // Show UI
+ this.scene.uiSystem?.showVehicleUI({
+ name: vehicle.name,
+ speed: vehicle.speed,
+ capacity: vehicle.capacity,
+ currentCargo: 0
+ });
+
+ // Emit event
+ this.scene.events.emit('vehicleMounted', { vehicleId, vehicle });
+
+ console.log(`๐ด Mounted ${vehicle.name} (${vehicle.speed} speed)`);
+ return true;
+ }
+
+ dismount() {
+ if (!this.currentVehicle) {
+ return false;
+ }
+
+ const vehicle = this.currentVehicle.data;
+
+ // Restore player speed
+ this.scene.player.setMaxVelocity(this.currentVehicle.originalSpeed);
+
+ // Restore player sprite
+ this.scene.player.setTexture(this.currentVehicle.originalTexture);
+
+ // Hide UI
+ this.scene.uiSystem?.hideVehicleUI();
+
+ // Emit event
+ this.scene.events.emit('vehicleDismounted', {
+ vehicleId: this.currentVehicle.id,
+ rideDuration: Date.now() - this.currentVehicle.mountTime
+ });
+
+ console.log(`๐ถ Dismounted ${vehicle.name}`);
+ this.currentVehicle = null;
+ return true;
+ }
+
+ // ===== VEHICLE PURCHASING =====
+
+ canPurchase(vehicleId) {
+ const vehicle = this.vehicleData[vehicleId];
+ if (!vehicle) {
+ return { canPurchase: false, reason: 'Vehicle not found' };
+ }
+
+ // Check if already owned
+ if (this.ownedVehicles.has(vehicleId)) {
+ return { canPurchase: false, reason: 'Already owned' };
+ }
+
+ // Check level
+ const playerLevel = this.scene.player?.stats?.level || 1;
+ if (playerLevel < vehicle.unlockLevel) {
+ return {
+ canPurchase: false,
+ reason: `Requires level ${vehicle.unlockLevel}`
+ };
+ }
+
+ // Check blueprint (if required)
+ if (vehicle.blueprint && this.scene.recipeSystem) {
+ if (!this.scene.recipeSystem.unlockedRecipes.has(vehicle.blueprint)) {
+ return { canPurchase: false, reason: 'Blueprint not unlocked' };
+ }
+ }
+
+ // Check gold
+ const playerGold = this.getPlayerGold();
+ if (playerGold < vehicle.cost) {
+ return {
+ canPurchase: false,
+ reason: `Need ${vehicle.cost} gold (have ${playerGold})`
+ };
+ }
+
+ return { canPurchase: true };
+ }
+
+ purchase(vehicleId) {
+ const check = this.canPurchase(vehicleId);
+ if (!check.canPurchase) {
+ this.scene.uiSystem?.showError(check.reason);
+ return false;
+ }
+
+ const vehicle = this.vehicleData[vehicleId];
+
+ // Deduct gold
+ this.removeGold(vehicle.cost);
+
+ // Add to owned vehicles
+ this.ownedVehicles.set(vehicleId, {
+ purchaseDate: Date.now(),
+ totalDistance: 0,
+ totalTime: 0
+ });
+
+ // Save
+ this.saveOwnedVehicles();
+
+ // Notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ Vehicle Purchased!',
+ message: `${vehicle.name} is now yours!`,
+ icon: vehicle.sprite,
+ duration: 4000,
+ color: '#00FF00'
+ });
+
+ // Emit event
+ this.scene.events.emit('vehiclePurchased', { vehicleId, vehicle });
+
+ console.log(`๐ฐ Purchased ${vehicle.name} for ${vehicle.cost} gold`);
+ return true;
+ }
+
+ // ===== TRAIN REPAIR SYSTEM =====
+
+ repairTrain(workAmount = 10) {
+ if (this.trainRepairProgress >= 100) {
+ this.scene.uiSystem?.showError('Train already repaired!');
+ return false;
+ }
+
+ // Add progress
+ this.trainRepairProgress += workAmount;
+ this.trainRepairProgress = Math.min(100, this.trainRepairProgress);
+
+ // Get current state
+ const state = this.getTrainRepairState();
+
+ // Show progress
+ this.scene.uiSystem?.showNotification({
+ title: 'Train Repair',
+ message: `${state} - ${this.trainRepairProgress.toFixed(0)}%`,
+ icon: 'train',
+ duration: 2000
+ });
+
+ // Check if just completed
+ if (this.trainRepairProgress === 100 && state === 'Rideable') {
+ this.unlockTrain();
+ }
+
+ // Emit event
+ this.scene.events.emit('trainRepairProgress', {
+ progress: this.trainRepairProgress,
+ state
+ });
+
+ return true;
+ }
+
+ getTrainRepairState() {
+ if (this.trainRepairProgress < 33) {
+ return 'Broken';
+ } else if (this.trainRepairProgress < 100) {
+ return 'Repairing';
+ } else {
+ return 'Rideable';
+ }
+ }
+
+ unlockTrain() {
+ // Add train to owned vehicles
+ this.ownedVehicles.set('train', {
+ purchaseDate: Date.now(),
+ totalDistance: 0,
+ totalTime: 0
+ });
+
+ this.saveOwnedVehicles();
+
+ // Big notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ TRAIN REPAIRED!',
+ message: 'The steam train is ready to ride!',
+ icon: 'train',
+ duration: 6000,
+ color: '#FFD700'
+ });
+
+ this.scene.events.emit('trainUnlocked');
+ console.log('๐ Train fully repaired and unlocked!');
+ }
+
+ // ===== TERRAIN BONUS SYSTEM =====
+
+ getSpeedModifier(x, y) {
+ if (!this.currentVehicle) return 1.0;
+
+ const vehicle = this.currentVehicle.data;
+ const tile = this.getTileAt(x, y);
+ const terrain = tile.properties?.terrain;
+
+ if (vehicle.terrainBonus && terrain && vehicle.terrainBonus[terrain]) {
+ return vehicle.terrainBonus[terrain];
+ }
+
+ return 1.0;
+ }
+
+ getCurrentSpeed() {
+ if (!this.currentVehicle) {
+ return this.scene.player?.baseSpeed || 100;
+ }
+
+ const baseSpeed = this.currentVehicle.data.speed;
+ const modifier = this.getSpeedModifier(this.scene.player.x, this.scene.player.y);
+
+ return baseSpeed * modifier;
+ }
+
+ // ===== HELPER FUNCTIONS =====
+
+ hasHorse() {
+ return this.ownedVehicles.has('horse_basic') ||
+ this.ownedVehicles.has('horse_fast') ||
+ this.ownedVehicles.has('horse_draft');
+ }
+
+ isOnTrack(x, y) {
+ // Check if position has train tracks
+ const tile = this.getTileAt(x, y);
+ return tile.properties?.hasTrack || false;
+ }
+
+ getTileAt(x, y) {
+ // Get tile from terrain system
+ if (this.scene.terrainSystem) {
+ return this.scene.terrainSystem.getTileAtWorldXY(x, y);
+ }
+
+ // Fallback
+ return { properties: {} };
+ }
+
+ getPlayerGold() {
+ if (this.scene.inventorySystem) {
+ return this.scene.inventorySystem.getQuantity('gold');
+ }
+ return this.scene.player?.inventory?.gold || 0;
+ }
+
+ removeGold(amount) {
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.removeItem('gold', amount);
+ } else {
+ this.scene.player.inventory.gold -= amount;
+ }
+ }
+
+ // ===== GETTERS =====
+
+ getVehicle(vehicleId) {
+ return this.vehicleData[vehicleId];
+ }
+
+ getAllVehicles() {
+ return Object.entries(this.vehicleData).map(([id, data]) => ({
+ id,
+ ...data,
+ owned: this.ownedVehicles.has(id)
+ }));
+ }
+
+ getOwnedVehicles() {
+ return Array.from(this.ownedVehicles.keys());
+ }
+
+ getVehiclesByType(type) {
+ return this.getAllVehicles().filter(v => v.type === type);
+ }
+
+ getPurchasableVehicles() {
+ return this.getAllVehicles().filter(v =>
+ this.canPurchase(v.id).canPurchase
+ );
+ }
+
+ getCurrentVehicle() {
+ return this.currentVehicle;
+ }
+
+ isRiding() {
+ return this.currentVehicle !== null;
+ }
+
+ // ===== DATA PERSISTENCE =====
+
+ saveOwnedVehicles() {
+ const data = {
+ owned: Array.from(this.ownedVehicles.entries()),
+ trainProgress: this.trainRepairProgress
+ };
+ localStorage.setItem('ownedVehicles', JSON.stringify(data));
+ }
+
+ loadOwnedVehicles() {
+ const saved = localStorage.getItem('ownedVehicles');
+ if (saved) {
+ const data = JSON.parse(saved);
+ this.ownedVehicles = new Map(data.owned);
+ this.trainRepairProgress = data.trainProgress || 0;
+ console.log('๐ Loaded', this.ownedVehicles.size, 'owned vehicles');
+ }
+ }
+
+ // ===== UPDATE =====
+
+ update(time, delta) {
+ if (this.currentVehicle) {
+ // Track distance and time
+ const deltaSeconds = delta / 1000;
+ const speed = this.getCurrentSpeed();
+ const distance = (speed * deltaSeconds) / 60; // Approximate
+
+ const stats = this.ownedVehicles.get(this.currentVehicle.id);
+ if (stats) {
+ stats.totalDistance += distance;
+ stats.totalTime += deltaSeconds;
+ }
+
+ // Update speed modifier based on terrain
+ const modifier = this.getSpeedModifier(this.scene.player.x, this.scene.player.y);
+ if (modifier !== 1.0) {
+ this.scene.player.setMaxVelocity(this.currentVehicle.data.speed * modifier);
+ }
+ }
+ }
+
+ // ===== CLEANUP =====
+
+ destroy() {
+ this.saveOwnedVehicles();
+ this.ownedVehicles.clear();
+ this.trainTracks = [];
+ this.currentVehicle = null;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TutorialSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TutorialSystem.js
new file mode 100644
index 000000000..c6634c8de
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TutorialSystem.js
@@ -0,0 +1,293 @@
+/**
+ * TUTORIAL SYSTEM
+ * Shows helpful tips and keyboard shortcuts to new players
+ */
+class TutorialSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+ this.currentStep = 0;
+ this.completed = false;
+
+ // Tutorial steps
+ this.steps = [
+ {
+ title: '๐ Welcome to Mrtva Dolina!',
+ message: 'Use W/A/S/D to move around\nPress SPACE to attack\nPress E to interact with objects',
+ icon: '๐',
+ duration: 5000
+ },
+ {
+ title: '๐๏ธ Building',
+ message: 'Press B to enter Build Mode\nSelect buildings with 1-5\nClick to place buildings',
+ icon: '๐๏ธ',
+ duration: 5000
+ },
+ {
+ title: '๐จ Crafting',
+ message: 'Press C to open Crafting Menu\nCraft tools and items\nUse resources from your inventory',
+ icon: '๐จ',
+ duration: 5000
+ },
+ {
+ title: '๐ฃ Fishing',
+ message: 'Press R to cast fishing rod\nPress SPACE to catch fish\nUse LEFT/RIGHT to move bobber',
+ icon: '๐ฃ',
+ duration: 5000
+ },
+ {
+ title: '๐พ Save & Load',
+ message: 'Press F5 to Quick Save\nPress F9 to Load Game\nYour progress is saved automatically',
+ icon: '๐พ',
+ duration: 5000
+ },
+ {
+ title: '๐ More Controls',
+ message: 'Press TAB for Stats Panel\nPress M for Map\nPress ESC for Pause Menu\n\nPress H anytime for help!',
+ icon: 'โจ๏ธ',
+ duration: 6000
+ }
+ ];
+
+ // Load progress
+ this.loadProgress();
+
+ // Show tutorial if not completed
+ if (!this.completed && this.enabled) {
+ this.scene.time.delayedCall(2000, () => {
+ this.showNextStep();
+ });
+ }
+
+ console.log('๐ Tutorial System initialized');
+ }
+
+ /**
+ * Show next tutorial step
+ */
+ showNextStep() {
+ if (this.currentStep >= this.steps.length) {
+ this.completeTutorial();
+ return;
+ }
+
+ const step = this.steps[this.currentStep];
+ this.showTutorialPopup(step);
+ this.currentStep++;
+ }
+
+ /**
+ * Show tutorial popup
+ */
+ showTutorialPopup(step) {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ // Create container
+ const container = uiScene.add.container(
+ uiScene.scale.width / 2,
+ uiScene.scale.height / 2
+ );
+ container.setDepth(99998); // Below epilepsy warning
+ container.setScrollFactor(0);
+
+ // Semi-transparent background
+ const overlay = uiScene.add.rectangle(
+ 0, 0,
+ uiScene.scale.width * 2,
+ uiScene.scale.height * 2,
+ 0x000000, 0.5
+ );
+ container.add(overlay);
+
+ // Popup background
+ const bg = uiScene.add.graphics();
+ bg.fillStyle(0x2a4a2a, 0.95); // Dark green
+ bg.fillRoundedRect(-300, -150, 600, 300, 16);
+ bg.lineStyle(4, 0x90EE90, 1); // Light green border
+ bg.strokeRoundedRect(-300, -150, 600, 300, 16);
+ container.add(bg);
+
+ // Icon
+ const icon = uiScene.add.text(-280, -130, step.icon, {
+ fontSize: '48px'
+ });
+ container.add(icon);
+
+ // Title
+ const title = uiScene.add.text(0, -100, step.title, {
+ fontSize: '28px',
+ fontFamily: 'Arial',
+ fill: '#FFD700',
+ fontStyle: 'bold',
+ align: 'center'
+ }).setOrigin(0.5);
+ container.add(title);
+
+ // Message
+ const message = uiScene.add.text(0, 0, step.message, {
+ fontSize: '20px',
+ fontFamily: 'Arial',
+ fill: '#ffffff',
+ align: 'center',
+ lineSpacing: 8,
+ wordWrap: { width: 550 }
+ }).setOrigin(0.5);
+ container.add(message);
+
+ // Progress indicator
+ const progress = uiScene.add.text(
+ 0, 100,
+ `Step ${this.currentStep + 1} of ${this.steps.length}`,
+ {
+ fontSize: '14px',
+ fill: '#aaaaaa'
+ }
+ ).setOrigin(0.5);
+ container.add(progress);
+
+ // Next button
+ const nextBtn = uiScene.add.text(0, 130, '[ NEXT ]', {
+ fontSize: '20px',
+ color: '#00ff00',
+ backgroundColor: '#003300',
+ padding: { x: 30, y: 10 },
+ fontStyle: 'bold'
+ }).setOrigin(0.5);
+
+ nextBtn.setInteractive({ useHandCursor: true });
+ nextBtn.on('pointerover', () => nextBtn.setScale(1.1));
+ nextBtn.on('pointerout', () => nextBtn.setScale(1.0));
+ nextBtn.on('pointerdown', () => {
+ container.destroy();
+ this.showNextStep();
+ });
+ container.add(nextBtn);
+
+ // Skip button
+ const skipBtn = uiScene.add.text(250, -130, 'Skip Tutorial', {
+ fontSize: '14px',
+ color: '#888888'
+ }).setOrigin(1, 0);
+
+ skipBtn.setInteractive({ useHandCursor: true });
+ skipBtn.on('pointerover', () => skipBtn.setColor('#ffffff'));
+ skipBtn.on('pointerout', () => skipBtn.setColor('#888888'));
+ skipBtn.on('pointerdown', () => {
+ container.destroy();
+ this.completeTutorial();
+ });
+ container.add(skipBtn);
+
+ // Animate in
+ container.setScale(0.8);
+ container.setAlpha(0);
+ uiScene.tweens.add({
+ targets: container,
+ scale: 1,
+ alpha: 1,
+ duration: 300,
+ ease: 'Back.easeOut'
+ });
+
+ // Auto-advance after duration
+ uiScene.time.delayedCall(step.duration, () => {
+ if (container.active) {
+ uiScene.tweens.add({
+ targets: container,
+ scale: 0.8,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => {
+ container.destroy();
+ this.showNextStep();
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Complete tutorial
+ */
+ completeTutorial() {
+ this.completed = true;
+ this.saveProgress();
+ console.log('โ
Tutorial completed!');
+
+ // Show completion message
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene && uiScene.centralPopup) {
+ uiScene.centralPopup.showNotification(
+ 'Tutorial completed! Press H anytime for help.',
+ 'success'
+ );
+ }
+ }
+
+ /**
+ * Show help popup (H key)
+ */
+ showHelp() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene || !uiScene.centralPopup) return;
+
+ uiScene.centralPopup.showPopup({
+ title: 'โจ๏ธ Keyboard Shortcuts',
+ message:
+ '๐ฎ MOVEMENT:\n' +
+ 'W/A/S/D - Move\n' +
+ 'SPACE - Attack\n' +
+ 'E - Interact\n\n' +
+ '๐๏ธ BUILD & CRAFT:\n' +
+ 'B - Build Mode\n' +
+ 'C - Crafting Menu\n\n' +
+ '๐ฃ FISHING:\n' +
+ 'R - Cast Rod\n' +
+ 'SPACE - Catch Fish\n\n' +
+ '๐พ SAVE/LOAD:\n' +
+ 'F5 - Quick Save\n' +
+ 'F9 - Load Game\n\n' +
+ '๐ OTHER:\n' +
+ 'TAB - Stats Panel\n' +
+ 'M - Map\n' +
+ 'ESC - Pause',
+ type: 'info',
+ icon: '๐',
+ buttons: [{ text: 'OK', action: 'close' }],
+ autoClose: false
+ });
+ }
+
+ /**
+ * Reset tutorial
+ */
+ reset() {
+ this.currentStep = 0;
+ this.completed = false;
+ this.saveProgress();
+ console.log('๐ Tutorial reset');
+ }
+
+ /**
+ * Save progress
+ */
+ saveProgress() {
+ localStorage.setItem('novafarma_tutorial', JSON.stringify({
+ completed: this.completed,
+ currentStep: this.currentStep
+ }));
+ }
+
+ /**
+ * Load progress
+ */
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_tutorial');
+ if (saved) {
+ const data = JSON.parse(saved);
+ this.completed = data.completed || false;
+ this.currentStep = data.currentStep || 0;
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TwinBondSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TwinBondSystem.js
new file mode 100644
index 000000000..fb7fd64db
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TwinBondSystem.js
@@ -0,0 +1,432 @@
+/**
+ * TwinBondSystem.js
+ * =================
+ * KRVAVA ลฝETEV - Twin Bond Mechanic (Kai โ Ana Connection)
+ *
+ * Core Concept:
+ * Kai and Ana share a psychic bond through the Alfa virus
+ * As twins, they can:
+ * - Feel each other's emotions
+ * - Sense each other's location (vaguely)
+ * - Communicate telepathically (limited)
+ * - Share HP/stamina in emergencies
+ *
+ * Features:
+ * - Bond Strength meter (0-100)
+ * - Telepathic messages from Ana
+ * - Direction to Ana indicator
+ * - Twin abilities (heal twin, boost twin, swap positions)
+ * - Bond events (visions, flashbacks)
+ * - Ana's status tracking (health, danger level)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class TwinBondSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Bond state
+ this.bondStrength = 75; // Starts strong (0-100)
+ this.maxBondStrength = 100;
+
+ // Ana's state (unknown location)
+ this.anaState = {
+ alive: true,
+ health: 100,
+ dangerLevel: 0, // 0 = safe, 100 = critical
+ distance: 5000, // pixels from Kai (initially far)
+ direction: { x: 1000, y: 1000 }, // General direction
+ lastMessage: null,
+ messageTime: null
+ };
+
+ // Bond abilities
+ this.abilities = {
+ telepathy: { unlocked: true, cooldown: 0, maxCooldown: 30000 }, // 30s
+ sensePulse: { unlocked: true, cooldown: 0, maxCooldown: 60000 }, // 1min
+ emergencyLink: { unlocked: false, cooldown: 0, maxCooldown: 300000 }, // 5min
+ twinRecall: { unlocked: false, cooldown: 0, maxCooldown: 600000 } // 10min
+ };
+
+ // Messages from Ana (telepathic)
+ this.messageQueue = [];
+ this.lastMessageTime = 0;
+
+ // UI elements
+ this.bondUI = null;
+
+ // Events
+ this.bondEvents = this.defineBondEvents();
+ this.nextEventTime = Date.now() + 60000; // First event in 1 minute
+
+ console.log('๐ TwinBondSystem initialized - Bond Strength:', this.bondStrength);
+ }
+
+ /**
+ * Update bond strength based on actions
+ */
+ update(delta) {
+ const deltaSeconds = delta / 1000;
+
+ // Passive bond decay (small)
+ this.bondStrength = Math.max(0, this.bondStrength - 0.01 * deltaSeconds);
+
+ // Update ability cooldowns
+ for (const ability in this.abilities) {
+ if (this.abilities[ability].cooldown > 0) {
+ this.abilities[ability].cooldown -= delta;
+ }
+ }
+
+ // Check for random bond events
+ if (Date.now() > this.nextEventTime) {
+ this.triggerRandomBondEvent();
+ this.nextEventTime = Date.now() + Phaser.Math.Between(60000, 180000); // 1-3 min
+ }
+
+ // Update Ana's danger level based on story progress
+ this.updateAnaDanger(deltaSeconds);
+ }
+
+ /**
+ * Define bond events (telepathic visions)
+ */
+ defineBondEvents() {
+ return [
+ {
+ id: 'first_contact',
+ trigger: 'auto',
+ condition: null,
+ message: 'Kai... can you hear me? I\'m... somewhere dark...',
+ emotion: 'worried',
+ bondChange: +5
+ },
+ {
+ id: 'danger_warning',
+ trigger: 'danger_high',
+ condition: () => this.anaState.dangerLevel > 70,
+ message: 'Brother! They\'re coming for me! Please hurry!',
+ emotion: 'fear',
+ bondChange: -10
+ },
+ {
+ id: 'memory_flash',
+ trigger: 'random',
+ condition: null,
+ message: 'Remember when we first discovered the Alfa strain? We were so hopeful...',
+ emotion: 'sad',
+ bondChange: +3
+ },
+ {
+ id: 'location_hint',
+ trigger: 'sense_pulse',
+ condition: null,
+ message: 'I can feel concrete walls... cold metal... some kind of facility?',
+ emotion: 'neutral',
+ bondChange: +5
+ },
+ {
+ id: 'encouragement',
+ trigger: 'random',
+ condition: () => this.scene.player?.hp < 50,
+ message: 'Stay strong, Kai! I believe in you!',
+ emotion: 'determined',
+ bondChange: +10
+ }
+ ];
+ }
+
+ /**
+ * Trigger a random bond event
+ */
+ triggerRandomBondEvent() {
+ const randomEvents = this.bondEvents.filter(e => e.trigger === 'random');
+
+ if (randomEvents.length === 0) return;
+
+ const event = Phaser.Utils.Array.GetRandom(randomEvents);
+
+ // Check condition
+ if (event.condition && !event.condition()) {
+ return;
+ }
+
+ this.showTelepathicMessage(event.message, event.emotion);
+ this.changeBondStrength(event.bondChange);
+ }
+
+ /**
+ * Show telepathic message from Ana
+ */
+ showTelepathicMessage(message, emotion = 'neutral') {
+ console.log(`๐ญ Twin Bond Message: ${message}`);
+
+ // Update Ana's last message
+ this.anaState.lastMessage = message;
+ this.anaState.messageTime = Date.now();
+
+ // Show in dialogue system
+ if (this.scene.dialogueSystem) {
+ const anaData = {
+ name: 'Ana (Twin Bond)',
+ id: 'ana_telepathy'
+ };
+
+ // Create temporary dialogue
+ const telepathyDialogue = {
+ root: 'message',
+ nodes: {
+ 'message': {
+ speaker: 'Ana (Twin Bond)',
+ emotion: emotion,
+ text: message,
+ next: null // Auto-close
+ }
+ }
+ };
+
+ this.scene.dialogueSystem.registerDialogue('telepathy_' + Date.now(), telepathyDialogue);
+ this.scene.dialogueSystem.startDialogue('telepathy_' + Date.now(), anaData);
+ }
+
+ // Visual effect (bond pulse)
+ this.scene.cameras.main.flash(500, 150, 100, 255, false);
+
+ // Bond strength change
+ this.showBondPulse();
+ }
+
+ /**
+ * Change bond strength
+ */
+ changeBondStrength(amount) {
+ this.bondStrength = Phaser.Math.Clamp(
+ this.bondStrength + amount,
+ 0,
+ this.maxBondStrength
+ );
+
+ console.log(`๐ Bond Strength: ${this.bondStrength.toFixed(1)}% (${amount > 0 ? '+' : ''}${amount})`);
+
+ // Notify player
+ if (amount > 0) {
+ this.scene.events.emit('bondStrengthened', { strength: this.bondStrength });
+ } else {
+ this.scene.events.emit('bondWeakened', { strength: this.bondStrength });
+ }
+ }
+
+ /**
+ * Visual bond pulse effect
+ */
+ showBondPulse() {
+ // TODO: Create particle effect at player position
+ console.log('๐ซ Bond pulse visualization');
+ }
+
+ /**
+ * Ability: Telepathy (send message to Ana)
+ */
+ useTelepathy(message) {
+ if (!this.abilities.telepathy.unlocked) {
+ console.log('โ Telepathy not unlocked');
+ return false;
+ }
+
+ if (this.abilities.telepathy.cooldown > 0) {
+ console.log('โธ๏ธ Telepathy on cooldown');
+ return false;
+ }
+
+ console.log(`๐ก Sending to Ana: ${message}`);
+
+ // Ana responds after delay
+ this.scene.time.delayedCall(2000, () => {
+ const responses = [
+ "I heard you! Keep searching!",
+ "Kai... I'm trying to stay strong...",
+ "They don't know about our bond. Use that!",
+ "I can feel you getting closer!"
+ ];
+
+ const response = Phaser.Utils.Array.GetRandom(responses);
+ this.showTelepathicMessage(response, 'determined');
+ });
+
+ // Set cooldown
+ this.abilities.telepathy.cooldown = this.abilities.telepathy.maxCooldown;
+ this.changeBondStrength(+2); // Strengthen bond
+
+ return true;
+ }
+
+ /**
+ * Ability: Sense Pulse (detect Ana's direction)
+ */
+ useSensePulse() {
+ if (!this.abilities.sensePulse.unlocked) {
+ console.log('โ Sense Pulse not unlocked');
+ return null;
+ }
+
+ if (this.abilities.sensePulse.cooldown > 0) {
+ console.log('โธ๏ธ Sense Pulse on cooldown');
+ return null;
+ }
+
+ console.log('๐ Sensing Ana\'s location...');
+
+ // Calculate general direction
+ const playerX = this.scene.player?.x || 0;
+ const playerY = this.scene.player?.y || 0;
+
+ const angle = Phaser.Math.Angle.Between(
+ playerX, playerY,
+ this.anaState.direction.x, this.anaState.direction.y
+ );
+
+ const distance = this.anaState.distance;
+
+ // Show visual indicator
+ this.showDirectionIndicator(angle, distance);
+
+ // Set cooldown
+ this.abilities.sensePulse.cooldown = this.abilities.sensePulse.maxCooldown;
+ this.changeBondStrength(+5);
+
+ return {
+ angle: angle,
+ distance: distance,
+ distanceCategory: this.getDistanceCategory(distance)
+ };
+ }
+
+ /**
+ * Get distance category (for vague communication)
+ */
+ getDistanceCategory(distance) {
+ if (distance < 500) return 'very_close';
+ if (distance < 1500) return 'close';
+ if (distance < 3000) return 'far';
+ return 'very_far';
+ }
+
+ /**
+ * Show direction indicator
+ */
+ showDirectionIndicator(angle, distance) {
+ const category = this.getDistanceCategory(distance);
+ const messages = {
+ 'very_close': 'Ana is VERY CLOSE! โฌ๏ธ',
+ 'close': 'Ana is nearby ๐',
+ 'far': 'Ana is far away ๐ญ',
+ 'very_far': 'Ana is very far ๐'
+ };
+
+ console.log(`๐ ${messages[category]} (${Math.round(distance)}px)`);
+
+ // TODO: Show UI arrow pointing in direction
+ }
+
+ /**
+ * Update Ana's danger level
+ */
+ updateAnaDanger(deltaSeconds) {
+ // Danger level increases over time (captors getting desperate)
+ if (this.anaState.alive) {
+ this.anaState.dangerLevel = Math.min(
+ 100,
+ this.anaState.dangerLevel + 0.1 * deltaSeconds
+ );
+
+ // Trigger danger events
+ if (this.anaState.dangerLevel > 70 && Math.random() < 0.01) {
+ const dangerEvent = this.bondEvents.find(e => e.id === 'danger_warning');
+ if (dangerEvent) {
+ this.showTelepathicMessage(dangerEvent.message, dangerEvent.emotion);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update Ana's position (for story progression)
+ */
+ updateAnaLocation(x, y, distance) {
+ this.anaState.direction.x = x;
+ this.anaState.direction.y = y;
+ this.anaState.distance = distance;
+
+ console.log(`๐ Ana's location updated: (${x}, ${y}), distance: ${distance}px`);
+ }
+
+ /**
+ * Create Twin Bond UI
+ */
+ createBondUI() {
+ const width = this.scene.cameras.main.width;
+
+ // Bond meter (top-left)
+ const x = 20;
+ const y = 120;
+
+ // Background
+ const bg = this.scene.add.rectangle(x, y, 200, 40, 0x2d1b00, 0.8);
+ bg.setOrigin(0, 0);
+ bg.setScrollFactor(0);
+ bg.setDepth(100);
+
+ // Title
+ const title = this.scene.add.text(x + 10, y + 5, '๐ Twin Bond', {
+ fontSize: '14px',
+ fontFamily: 'Georgia, serif',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ title.setScrollFactor(0);
+ title.setDepth(100);
+
+ // Bond bar
+ const barBg = this.scene.add.rectangle(x + 10, y + 25, 180, 8, 0x000000, 0.8);
+ barBg.setOrigin(0, 0);
+ barBg.setScrollFactor(0);
+ barBg.setDepth(100);
+
+ const barFill = this.scene.add.rectangle(
+ x + 10, y + 25,
+ 180 * (this.bondStrength / 100),
+ 8,
+ 0xFF69B4,
+ 1
+ );
+ barFill.setOrigin(0, 0);
+ barFill.setScrollFactor(0);
+ barFill.setDepth(100);
+
+ this.bondUI = { bg, title, barBg, barFill };
+
+ // Update bar every frame
+ this.scene.events.on('update', () => {
+ if (this.bondUI && this.bondUI.barFill) {
+ this.bondUI.barFill.width = 180 * (this.bondStrength / 100);
+ }
+ });
+ }
+
+ /**
+ * Getters
+ */
+ getBondStrength() {
+ return this.bondStrength;
+ }
+
+ getAnaStatus() {
+ return this.anaState;
+ }
+
+ isAnaSafe() {
+ return this.anaState.dangerLevel < 50;
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/TwinBondUISystem.js b/EMERGENCY_SYSTEMS_RECOVERY/TwinBondUISystem.js
new file mode 100644
index 000000000..487ce1c6c
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/TwinBondUISystem.js
@@ -0,0 +1,334 @@
+/**
+ * TwinBondUISystem.js
+ * ====================
+ * KRVAVA ลฝETEV - Twin Bond Heartbeat UI
+ *
+ * Features:
+ * - Visual heartbeat indicator on screen
+ * - Speeds up when near Ana's clues
+ * - Purple glow effect
+ * - Distance-based intensity
+ * - Twin bond strength indicator
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-25
+ */
+
+export default class TwinBondUISystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // UI elements
+ this.heartContainer = null;
+ this.heartIcon = null;
+ this.glowEffect = null;
+ this.bondStrengthBar = null;
+
+ // State
+ this.baseHeartRate = 60; // bpm
+ this.currentHeartRate = 60;
+ this.bondStrength = 0; // 0-100
+ this.nearestClueDistance = Infinity;
+
+ // Animation
+ this.heartbeatTween = null;
+ this.lastBeat = 0;
+
+ console.log('๐ TwinBondUISystem initialized');
+
+ this.createUI();
+ this.startHeartbeat();
+ }
+
+ /**
+ * Create UI elements
+ */
+ createUI() {
+ const ui = this.scene.scene.get('UIScene') || this.scene;
+
+ // Container (top-left corner)
+ this.heartContainer = ui.add.container(50, 50);
+ this.heartContainer.setDepth(1000); // Above everything
+ this.heartContainer.setScrollFactor(0); // Fixed to camera
+
+ // Purple glow effect (behind heart)
+ this.glowEffect = ui.add.circle(0, 0, 25, 0x9370DB, 0);
+ this.heartContainer.add(this.glowEffect);
+
+ // Heart icon (emoji-style)
+ this.heartIcon = ui.add.text(0, 0, '๐', {
+ fontSize: '32px',
+ fontFamily: 'Arial'
+ });
+ this.heartIcon.setOrigin(0.5);
+ this.heartContainer.add(this.heartIcon);
+
+ // Bond strength bar (below heart)
+ const barWidth = 50;
+ const barHeight = 5;
+
+ // Background bar
+ const barBg = ui.add.rectangle(0, 30, barWidth, barHeight, 0x333333);
+ this.heartContainer.add(barBg);
+
+ // Strength bar
+ this.bondStrengthBar = ui.add.rectangle(
+ -barWidth / 2,
+ 30,
+ 0, // Start at 0 width
+ barHeight,
+ 0x9370DB
+ );
+ this.bondStrengthBar.setOrigin(0, 0.5);
+ this.heartContainer.add(this.bondStrengthBar);
+
+ // Bond strength text
+ this.bondStrengthText = ui.add.text(0, 45, '0%', {
+ fontSize: '10px',
+ fontFamily: 'Arial',
+ color: '#9370DB'
+ });
+ this.bondStrengthText.setOrigin(0.5);
+ this.heartContainer.add(this.bondStrengthText);
+
+ console.log('โ
Twin Bond UI created');
+ }
+
+ /**
+ * Start heartbeat animation
+ */
+ startHeartbeat() {
+ const beatInterval = () => {
+ const bpm = this.currentHeartRate;
+ const msPerBeat = 60000 / bpm; // Convert BPM to ms
+
+ // Heart pump animation
+ if (this.heartIcon) {
+ this.scene.tweens.add({
+ targets: [this.heartIcon],
+ scale: { from: 1, to: 1.3 },
+ duration: 100,
+ yoyo: true,
+ ease: 'Quad.Out'
+ });
+
+ // Glow pulse
+ this.scene.tweens.add({
+ targets: [this.glowEffect],
+ alpha: { from: 0, to: 0.6 },
+ scale: { from: 1, to: 1.5 },
+ duration: 150,
+ yoyo: true,
+ ease: 'Quad.Out'
+ });
+ }
+
+ // Schedule next beat
+ this.heartbeatTimer = setTimeout(() => beatInterval(), msPerBeat);
+ };
+
+ // Start beating
+ beatInterval();
+ }
+
+ /**
+ * Update system (called every frame)
+ */
+ update(time, delta) {
+ if (!this.scene.player) return;
+
+ // Calculate distance to nearest Ana clue
+ this.calculateNearestClue();
+
+ // Update heart rate based on distance
+ this.updateHeartRate();
+
+ // Update bond strength display
+ this.updateBondStrengthDisplay();
+ }
+
+ /**
+ * Calculate distance to nearest Ana's clue
+ */
+ calculateNearestClue() {
+ if (!this.scene.anaClueSystem) {
+ this.nearestClueDistance = Infinity;
+ return;
+ }
+
+ const playerX = this.scene.player.sprite.x;
+ const playerY = this.scene.player.sprite.y;
+
+ let minDistance = Infinity;
+
+ // Check all clues
+ this.scene.anaClueSystem.clueLocations.forEach((position, clueId) => {
+ // Skip already discovered clues
+ if (this.scene.anaClueSystem.discovered.has(clueId)) return;
+
+ const dx = position.x - playerX;
+ const dy = position.y - playerY;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance < minDistance) {
+ minDistance = distance;
+ }
+ });
+
+ this.nearestClueDistance = minDistance;
+ }
+
+ /**
+ * Update heart rate based on proximity to clues
+ */
+ updateHeartRate() {
+ const distance = this.nearestClueDistance;
+
+ // Distance thresholds (in pixels)
+ const veryClose = 200; // 4-5 blocks
+ const close = 480; // 10 blocks
+ const medium = 960; // 20 blocks
+
+ let targetHeartRate = this.baseHeartRate;
+
+ if (distance < veryClose) {
+ // VERY CLOSE - Rapid heartbeat!
+ targetHeartRate = 120; // 2x normal
+ this.bondStrength = 100;
+ } else if (distance < close) {
+ // CLOSE - Fast heartbeat
+ targetHeartRate = 90;
+ this.bondStrength = 75;
+ } else if (distance < medium) {
+ // MEDIUM - Slightly elevated
+ targetHeartRate = 75;
+ this.bondStrength = 50;
+ } else {
+ // FAR - Normal heartbeat
+ targetHeartRate = 60;
+ this.bondStrength = Math.max(0, this.bondStrength - 1); // Slowly decrease
+ }
+
+ // Smooth transition
+ this.currentHeartRate += (targetHeartRate - this.currentHeartRate) * 0.1;
+
+ // Clamp
+ this.currentHeartRate = Phaser.Math.Clamp(this.currentHeartRate, 30, 150);
+ }
+
+ /**
+ * Update bond strength visual display
+ */
+ updateBondStrengthDisplay() {
+ if (!this.bondStrengthBar) return;
+
+ // Update bar width
+ const maxWidth = 50;
+ const targetWidth = (this.bondStrength / 100) * maxWidth;
+ this.bondStrengthBar.width = targetWidth;
+
+ // Update text
+ if (this.bondStrengthText) {
+ this.bondStrengthText.setText(`${Math.floor(this.bondStrength)}%`);
+ }
+
+ // Color gradient based on strength
+ if (this.bondStrength > 75) {
+ this.bondStrengthBar.setFillStyle(0xFF69B4); // Hot pink - very strong
+ } else if (this.bondStrength > 50) {
+ this.bondStrengthBar.setFillStyle(0x9370DB); // Medium purple
+ } else if (this.bondStrength > 25) {
+ this.bondStrengthBar.setFillStyle(0x6A5ACD); // Slate blue
+ } else {
+ this.bondStrengthBar.setFillStyle(0x483D8B); // Dark slate blue
+ }
+
+ // Pulsing glow when strong bond
+ if (this.bondStrength > 75 && this.glowEffect) {
+ this.glowEffect.setAlpha(0.3 + Math.sin(Date.now() / 200) * 0.2);
+ }
+ }
+
+ /**
+ * Trigger special bond moment (e.g., flashback)
+ */
+ triggerBondMoment(intensity = 'medium') {
+ console.log(`๐ Twin Bond Moment! Intensity: ${intensity}`);
+
+ // Screen flash
+ this.scene.cameras.main.flash(500, 147, 112, 219, false); // Purple flash
+
+ // Heart explosion effect
+ if (this.heartIcon) {
+ this.scene.tweens.add({
+ targets: [this.heartIcon],
+ scale: { from: 1, to: 2 },
+ alpha: { from: 1, to: 0 },
+ duration: 800,
+ ease: 'Quad.Out',
+ onComplete: () => {
+ this.heartIcon.setScale(1);
+ this.heartIcon.setAlpha(1);
+ }
+ });
+ }
+
+ // Glow burst
+ if (this.glowEffect) {
+ this.scene.tweens.add({
+ targets: [this.glowEffect],
+ scale: { from: 1, to: 5 },
+ alpha: { from: 0.8, to: 0 },
+ duration: 1000,
+ ease: 'Quad.Out',
+ onComplete: () => {
+ this.glowEffect.setScale(1);
+ this.glowEffect.setAlpha(0);
+ }
+ });
+ }
+
+ // Temporary heart rate spike
+ this.currentHeartRate = 150;
+ setTimeout(() => {
+ this.currentHeartRate = this.baseHeartRate;
+ }, 3000);
+
+ // Bond strength boost
+ this.bondStrength = 100;
+
+ // Show notification
+ this.scene.events.emit('show-notification', {
+ title: 'Twin Bond Activated!',
+ text: '๐ You feel Ana\'s presence intensify!',
+ icon: '๐',
+ color: '#9370DB'
+ });
+ }
+
+ /**
+ * Get current heart rate (for debugging)
+ */
+ getCurrentHeartRate() {
+ return Math.floor(this.currentHeartRate);
+ }
+
+ /**
+ * Get bond strength (for other systems)
+ */
+ getBondStrength() {
+ return this.bondStrength;
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ if (this.heartbeatTimer) {
+ clearTimeout(this.heartbeatTimer);
+ }
+ if (this.heartContainer) {
+ this.heartContainer.destroy();
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/UIGraphicsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/UIGraphicsSystem.js
new file mode 100644
index 000000000..9ff9390f4
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/UIGraphicsSystem.js
@@ -0,0 +1,574 @@
+/**
+ * UI GRAPHICS ENHANCEMENT SYSTEM
+ * High-resolution icons, animated UI elements, custom cursors, and achievements
+ */
+class UIGraphicsSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Icon resolution
+ this.iconSize = 64; // 64x64 (upgraded from 32x32)
+ this.iconCache = new Map();
+
+ // Animated UI elements
+ this.animatedElements = [];
+ this.pulsingElements = [];
+ this.glowingElements = [];
+
+ // Custom cursors
+ this.cursors = {
+ 'default': 'default',
+ 'pointer': 'pointer',
+ 'grab': 'grab',
+ 'grabbing': 'grabbing',
+ 'sword': 'url(data:image/png;base64,...) 16 16, auto',
+ 'pickaxe': 'url(data:image/png;base64,...) 16 16, auto'
+ };
+ this.currentCursor = 'default';
+
+ // Achievement system
+ this.achievements = new Map();
+ this.unlockedAchievements = new Set();
+
+ // Loading screen
+ this.loadingScreen = null;
+ this.loadingProgress = 0;
+
+ // Settings
+ this.settings = {
+ highResIcons: true,
+ animatedUI: true,
+ customCursors: true,
+ achievementNotifications: true,
+ loadingArtwork: true
+ };
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
UI Graphics System initialized');
+ }
+
+ init() {
+ // Generate high-res icons
+ if (this.settings.highResIcons) {
+ this.generateHighResIcons();
+ }
+
+ // Set up custom cursors
+ if (this.settings.customCursors) {
+ this.setupCustomCursors();
+ }
+
+ // Initialize achievements
+ this.initAchievements();
+
+ console.log('๐จ UI Graphics enhanced');
+ }
+
+ // ========== HIGH-RES ICONS ==========
+
+ generateHighResIcons() {
+ // Generate 64x64 icons for common items
+ const items = [
+ 'wood', 'stone', 'iron', 'gold', 'diamond',
+ 'wheat', 'carrot', 'potato', 'apple',
+ 'sword', 'pickaxe', 'axe', 'hoe',
+ 'health_potion', 'mana_potion'
+ ];
+
+ for (const item of items) {
+ this.createHighResIcon(item);
+ }
+
+ console.log(`๐ผ๏ธ Generated ${items.length} high-res icons (64x64)`);
+ }
+
+ createHighResIcon(itemName) {
+ const graphics = this.scene.add.graphics();
+ const size = this.iconSize;
+
+ // Icon background
+ graphics.fillStyle(0x333333, 1);
+ graphics.fillRoundedRect(0, 0, size, size, 8);
+
+ // Icon border
+ graphics.lineStyle(2, 0x666666, 1);
+ graphics.strokeRoundedRect(0, 0, size, size, 8);
+
+ // Item-specific graphics
+ switch (itemName) {
+ case 'wood':
+ graphics.fillStyle(0x8B4513, 1);
+ graphics.fillRect(16, 16, 32, 32);
+ break;
+ case 'stone':
+ graphics.fillStyle(0x808080, 1);
+ graphics.fillCircle(32, 32, 16);
+ break;
+ case 'gold':
+ graphics.fillStyle(0xFFD700, 1);
+ graphics.fillCircle(32, 32, 16);
+ break;
+ case 'diamond':
+ graphics.fillStyle(0x00FFFF, 1);
+ graphics.fillTriangle(32, 16, 16, 48, 48, 48);
+ break;
+ case 'sword':
+ graphics.fillStyle(0xC0C0C0, 1);
+ graphics.fillRect(28, 16, 8, 32);
+ graphics.fillTriangle(32, 16, 24, 24, 40, 24);
+ break;
+ case 'health_potion':
+ graphics.fillStyle(0xFF0000, 1);
+ graphics.fillCircle(32, 32, 16);
+ graphics.fillStyle(0xFFFFFF, 0.5);
+ graphics.fillCircle(28, 28, 4);
+ break;
+ default:
+ graphics.fillStyle(0x666666, 1);
+ graphics.fillRect(20, 20, 24, 24);
+ }
+
+ // Generate texture
+ graphics.generateTexture(`icon_${itemName}_64`, size, size);
+ graphics.destroy();
+
+ this.iconCache.set(itemName, `icon_${itemName}_64`);
+ }
+
+ getIcon(itemName) {
+ return this.iconCache.get(itemName) || 'icon_default_64';
+ }
+
+ // ========== ANIMATED UI ELEMENTS ==========
+
+ addPulsingElement(element, minScale = 0.95, maxScale = 1.05, duration = 1000) {
+ if (!this.settings.animatedUI) return;
+
+ const tween = this.scene.tweens.add({
+ targets: element,
+ scale: { from: minScale, to: maxScale },
+ duration: duration,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+
+ this.pulsingElements.push({ element, tween });
+ return tween;
+ }
+
+ addGlowingElement(element, color = 0xffff00, intensity = 0.5) {
+ if (!this.settings.animatedUI) return;
+
+ // Create glow effect
+ const glow = this.scene.add.circle(
+ element.x,
+ element.y,
+ element.width * 0.6,
+ color,
+ intensity
+ );
+ glow.setBlendMode(Phaser.BlendModes.ADD);
+ glow.setDepth(element.depth - 1);
+
+ // Pulsing glow
+ this.scene.tweens.add({
+ targets: glow,
+ alpha: { from: intensity, to: 0 },
+ scale: { from: 1, to: 1.5 },
+ duration: 1500,
+ yoyo: true,
+ repeat: -1
+ });
+
+ this.glowingElements.push({ element, glow });
+ return glow;
+ }
+
+ addHoverEffect(element, callback) {
+ if (!this.settings.animatedUI) return;
+
+ element.setInteractive();
+
+ element.on('pointerover', () => {
+ this.scene.tweens.add({
+ targets: element,
+ scale: 1.1,
+ duration: 200,
+ ease: 'Back.easeOut'
+ });
+ if (callback) callback('over');
+ });
+
+ element.on('pointerout', () => {
+ this.scene.tweens.add({
+ targets: element,
+ scale: 1.0,
+ duration: 200,
+ ease: 'Back.easeIn'
+ });
+ if (callback) callback('out');
+ });
+
+ element.on('pointerdown', () => {
+ this.scene.tweens.add({
+ targets: element,
+ scale: 0.95,
+ duration: 100
+ });
+ if (callback) callback('down');
+ });
+
+ element.on('pointerup', () => {
+ this.scene.tweens.add({
+ targets: element,
+ scale: 1.1,
+ duration: 100
+ });
+ if (callback) callback('up');
+ });
+ }
+
+ // ========== CUSTOM CURSORS ==========
+
+ setupCustomCursors() {
+ // Create custom cursor sprites
+ this.createCursorSprites();
+ }
+
+ createCursorSprites() {
+ // Sword cursor
+ const sword = this.scene.add.graphics();
+ sword.fillStyle(0xC0C0C0, 1);
+ sword.fillRect(0, 0, 4, 16);
+ sword.fillTriangle(2, 0, 0, 4, 4, 4);
+ sword.generateTexture('cursor_sword', 16, 16);
+ sword.destroy();
+
+ // Pickaxe cursor
+ const pickaxe = this.scene.add.graphics();
+ pickaxe.fillStyle(0x8B4513, 1);
+ pickaxe.fillRect(6, 8, 4, 8);
+ pickaxe.fillStyle(0x808080, 1);
+ pickaxe.fillRect(0, 0, 12, 8);
+ pickaxe.generateTexture('cursor_pickaxe', 16, 16);
+ pickaxe.destroy();
+
+ console.log('๐ฑ๏ธ Custom cursors created');
+ }
+
+ setCursor(cursorType) {
+ if (!this.settings.customCursors) return;
+
+ this.currentCursor = cursorType;
+
+ if (this.cursors[cursorType]) {
+ document.body.style.cursor = this.cursors[cursorType];
+ }
+ }
+
+ resetCursor() {
+ this.setCursor('default');
+ }
+
+ // ========== LOADING SCREEN ==========
+
+ showLoadingScreen(text = 'Loading...') {
+ if (!this.settings.loadingArtwork) return;
+
+ // Create loading screen container
+ this.loadingScreen = this.scene.add.container(0, 0);
+ this.loadingScreen.setDepth(10000);
+ this.loadingScreen.setScrollFactor(0);
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Background
+ const bg = this.scene.add.rectangle(
+ width / 2,
+ height / 2,
+ width,
+ height,
+ 0x000000,
+ 0.9
+ );
+ this.loadingScreen.add(bg);
+
+ // Logo/Title
+ const title = this.scene.add.text(
+ width / 2,
+ height / 2 - 100,
+ 'Mrtva Dolina',
+ {
+ fontSize: '64px',
+ fontFamily: 'Arial',
+ color: '#00ff00',
+ fontStyle: 'bold'
+ }
+ );
+ title.setOrigin(0.5);
+ this.loadingScreen.add(title);
+
+ // Loading text
+ const loadingText = this.scene.add.text(
+ width / 2,
+ height / 2,
+ text,
+ {
+ fontSize: '24px',
+ fontFamily: 'Arial',
+ color: '#ffffff'
+ }
+ );
+ loadingText.setOrigin(0.5);
+ this.loadingScreen.add(loadingText);
+
+ // Progress bar background
+ const barBg = this.scene.add.rectangle(
+ width / 2,
+ height / 2 + 50,
+ 400,
+ 30,
+ 0x333333
+ );
+ this.loadingScreen.add(barBg);
+
+ // Progress bar fill
+ const barFill = this.scene.add.rectangle(
+ width / 2 - 200,
+ height / 2 + 50,
+ 0,
+ 30,
+ 0x00ff00
+ );
+ barFill.setOrigin(0, 0.5);
+ this.loadingScreen.add(barFill);
+ this.loadingScreen.barFill = barFill;
+
+ // Pulsing animation
+ this.scene.tweens.add({
+ targets: loadingText,
+ alpha: { from: 1, to: 0.5 },
+ duration: 800,
+ yoyo: true,
+ repeat: -1
+ });
+ }
+
+ updateLoadingProgress(progress) {
+ if (!this.loadingScreen || !this.loadingScreen.barFill) return;
+
+ this.loadingProgress = Phaser.Math.Clamp(progress, 0, 100);
+
+ this.scene.tweens.add({
+ targets: this.loadingScreen.barFill,
+ width: (this.loadingProgress / 100) * 400,
+ duration: 300,
+ ease: 'Power2'
+ });
+ }
+
+ hideLoadingScreen() {
+ if (!this.loadingScreen) return;
+
+ this.scene.tweens.add({
+ targets: this.loadingScreen,
+ alpha: 0,
+ duration: 500,
+ onComplete: () => {
+ this.loadingScreen.destroy();
+ this.loadingScreen = null;
+ }
+ });
+ }
+
+ // ========== ACHIEVEMENTS ==========
+
+ initAchievements() {
+ // Define achievements
+ this.defineAchievement('first_harvest', {
+ name: 'First Harvest',
+ description: 'Harvest your first crop',
+ icon: '๐พ',
+ points: 10
+ });
+
+ this.defineAchievement('master_farmer', {
+ name: 'Master Farmer',
+ description: 'Harvest 1000 crops',
+ icon: '๐จโ๐พ',
+ points: 50
+ });
+
+ this.defineAchievement('explorer', {
+ name: 'Explorer',
+ description: 'Explore 50% of the map',
+ icon: '๐บ๏ธ',
+ points: 25
+ });
+
+ this.defineAchievement('rich', {
+ name: 'Wealthy',
+ description: 'Accumulate 10,000 gold',
+ icon: '๐ฐ',
+ points: 30
+ });
+
+ // Load unlocked achievements
+ this.loadUnlockedAchievements();
+ }
+
+ defineAchievement(id, data) {
+ this.achievements.set(id, {
+ id,
+ ...data,
+ unlocked: false,
+ unlockedAt: null
+ });
+ }
+
+ unlockAchievement(id) {
+ const achievement = this.achievements.get(id);
+ if (!achievement || achievement.unlocked) return;
+
+ achievement.unlocked = true;
+ achievement.unlockedAt = Date.now();
+ this.unlockedAchievements.add(id);
+
+ // Show notification
+ if (this.settings.achievementNotifications) {
+ this.showAchievementNotification(achievement);
+ }
+
+ // Save
+ this.saveUnlockedAchievements();
+
+ console.log(`๐ Achievement unlocked: ${achievement.name}`);
+ }
+
+ showAchievementNotification(achievement) {
+ const width = this.scene.cameras.main.width;
+
+ // Create notification
+ const notification = this.scene.add.container(width + 300, 100);
+ notification.setDepth(10001);
+ notification.setScrollFactor(0);
+
+ // Background
+ const bg = this.scene.add.rectangle(0, 0, 300, 100, 0x000000, 0.9);
+ bg.setStrokeStyle(2, 0xFFD700);
+ notification.add(bg);
+
+ // Icon
+ const icon = this.scene.add.text(-120, 0, achievement.icon, {
+ fontSize: '48px'
+ });
+ icon.setOrigin(0.5);
+ notification.add(icon);
+
+ // Text
+ const title = this.scene.add.text(-50, -20, 'Achievement Unlocked!', {
+ fontSize: '16px',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ title.setOrigin(0, 0.5);
+ notification.add(title);
+
+ const name = this.scene.add.text(-50, 5, achievement.name, {
+ fontSize: '20px',
+ color: '#ffffff',
+ fontStyle: 'bold'
+ });
+ name.setOrigin(0, 0.5);
+ notification.add(name);
+
+ const points = this.scene.add.text(-50, 25, `+${achievement.points} points`, {
+ fontSize: '14px',
+ color: '#00ff00'
+ });
+ points.setOrigin(0, 0.5);
+ notification.add(points);
+
+ // Slide in
+ this.scene.tweens.add({
+ targets: notification,
+ x: width - 170,
+ duration: 500,
+ ease: 'Back.easeOut',
+ onComplete: () => {
+ // Wait, then slide out
+ this.scene.time.delayedCall(3000, () => {
+ this.scene.tweens.add({
+ targets: notification,
+ x: width + 300,
+ duration: 500,
+ ease: 'Back.easeIn',
+ onComplete: () => notification.destroy()
+ });
+ });
+ }
+ });
+
+ // Sparkle effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(
+ notification.x,
+ notification.y
+ );
+ }
+ }
+
+ getAchievementProgress() {
+ const total = this.achievements.size;
+ const unlocked = this.unlockedAchievements.size;
+ return {
+ total,
+ unlocked,
+ percentage: (unlocked / total) * 100
+ };
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveUnlockedAchievements() {
+ const data = Array.from(this.unlockedAchievements);
+ localStorage.setItem('novafarma_achievements', JSON.stringify(data));
+ }
+
+ loadUnlockedAchievements() {
+ const saved = localStorage.getItem('novafarma_achievements');
+ if (saved) {
+ const data = JSON.parse(saved);
+ this.unlockedAchievements = new Set(data);
+
+ // Mark achievements as unlocked
+ for (const id of this.unlockedAchievements) {
+ const achievement = this.achievements.get(id);
+ if (achievement) {
+ achievement.unlocked = true;
+ }
+ }
+ }
+ }
+
+ saveSettings() {
+ localStorage.setItem('novafarma_ui_graphics', JSON.stringify(this.settings));
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_ui_graphics');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ destroy() {
+ this.saveUnlockedAchievements();
+ console.log('๐จ UI Graphics System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/UIPolishSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/UIPolishSystem.js
new file mode 100644
index 000000000..973a39b0c
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/UIPolishSystem.js
@@ -0,0 +1,325 @@
+// UI Polish System - Smooth transitions, animations, tooltips
+class UIPolishSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Tooltip management
+ this.currentTooltip = null;
+ this.tooltipDelay = 500; // Show after 500ms hover
+ this.tooltipTimer = null;
+
+ // Button animations cache
+ this.buttonAnimations = new Map();
+
+ console.log('๐จ UIPolishSystem initialized');
+ }
+
+ // Fade in a UI element smoothly
+ fadeIn(element, duration = 300, delay = 0) {
+ if (!element) return;
+
+ element.setAlpha(0);
+
+ this.scene.tweens.add({
+ targets: element,
+ alpha: 1,
+ duration: duration,
+ delay: delay,
+ ease: 'Power2'
+ });
+ }
+
+ // Fade out a UI element
+ fadeOut(element, duration = 300, onComplete = null) {
+ if (!element) return;
+
+ this.scene.tweens.add({
+ targets: element,
+ alpha: 0,
+ duration: duration,
+ ease: 'Power2',
+ onComplete: onComplete
+ });
+ }
+
+ // Slide in element from direction
+ slideIn(element, direction = 'left', duration = 400, distance = 200) {
+ if (!element) return;
+
+ const startX = element.x;
+ const startY = element.y;
+
+ // Set starting position
+ switch (direction) {
+ case 'left':
+ element.x -= distance;
+ break;
+ case 'right':
+ element.x += distance;
+ break;
+ case 'top':
+ element.y -= distance;
+ break;
+ case 'bottom':
+ element.y += distance;
+ break;
+ }
+
+ element.setAlpha(0);
+
+ // Animate to original position
+ this.scene.tweens.add({
+ targets: element,
+ x: startX,
+ y: startY,
+ alpha: 1,
+ duration: duration,
+ ease: 'Back.easeOut'
+ });
+ }
+
+ // Add hover effect to button
+ addButtonHover(button, scaleUp = 1.1, duration = 200) {
+ if (!button || this.buttonAnimations.has(button)) return;
+
+ const originalScale = button.scaleX || 1;
+
+ // Mouse over
+ button.on('pointerover', () => {
+ this.scene.tweens.add({
+ targets: button,
+ scaleX: originalScale * scaleUp,
+ scaleY: originalScale * scaleUp,
+ duration: duration,
+ ease: 'Back.easeOut'
+ });
+
+ // Play UI click sound
+ if (this.scene.soundManager && this.scene.soundManager.beepUIClick) {
+ this.scene.soundManager.beepUIClick();
+ }
+ });
+
+ // Mouse out
+ button.on('pointerout', () => {
+ this.scene.tweens.add({
+ targets: button,
+ scaleX: originalScale,
+ scaleY: originalScale,
+ duration: duration,
+ ease: 'Power2'
+ });
+ });
+
+ this.buttonAnimations.set(button, { originalScale, scaleUp });
+ }
+
+ // Pulse animation (for important elements)
+ pulse(element, minScale = 0.95, maxScale = 1.05, duration = 1000) {
+ if (!element) return;
+
+ return this.scene.tweens.add({
+ targets: element,
+ scale: { from: minScale, to: maxScale },
+ duration: duration,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+ }
+
+ // Shake animation (for errors or attention)
+ shake(element, intensity = 10, duration = 500) {
+ if (!element) return;
+
+ const originalX = element.x;
+
+ this.scene.tweens.add({
+ targets: element,
+ x: originalX + intensity,
+ duration: 50,
+ yoyo: true,
+ repeat: Math.floor(duration / 100),
+ ease: 'Sine.easeInOut',
+ onComplete: () => {
+ element.x = originalX; // Reset to original position
+ }
+ });
+ }
+
+ // Show tooltip on hover
+ showTooltip(x, y, text, options = {}) {
+ // Clear existing tooltip
+ this.hideTooltip();
+
+ const {
+ backgroundColor = '#000000',
+ textColor = '#ffffff',
+ padding = 10,
+ fontSize = '14px',
+ maxWidth = 200,
+ offsetX = 10,
+ offsetY = -30
+ } = options;
+
+ // Create tooltip background
+ const bg = this.scene.add.rectangle(
+ x + offsetX,
+ y + offsetY,
+ maxWidth,
+ 40,
+ Phaser.Display.Color.HexStringToColor(backgroundColor).color,
+ 0.9
+ );
+
+ // Create tooltip text
+ const tooltip = this.scene.add.text(
+ x + offsetX,
+ y + offsetY,
+ text,
+ {
+ fontSize: fontSize,
+ color: textColor,
+ align: 'center',
+ wordWrap: { width: maxWidth - padding * 2 }
+ }
+ );
+
+ tooltip.setOrigin(0.5, 0.5);
+ bg.setSize(tooltip.width + padding * 2, tooltip.height + padding * 2);
+
+ // Create container
+ this.currentTooltip = this.scene.add.container(0, 0, [bg, tooltip]);
+ this.currentTooltip.setDepth(1000000); // Always on top
+
+ // Fade in
+ this.fadeIn(this.currentTooltip, 200);
+ }
+
+ // Hide current tooltip
+ hideTooltip() {
+ if (this.currentTooltip) {
+ this.fadeOut(this.currentTooltip, 150, () => {
+ this.currentTooltip.destroy();
+ this.currentTooltip = null;
+ });
+ }
+ }
+
+ // Add tooltip to interactive object
+ addTooltip(object, text, options = {}) {
+ if (!object) return;
+
+ let hoverTimer = null;
+
+ object.on('pointerover', (pointer) => {
+ // Delay tooltip appearance
+ hoverTimer = this.scene.time.delayedCall(this.tooltipDelay, () => {
+ this.showTooltip(pointer.worldX, pointer.worldY, text, options);
+ });
+ });
+
+ object.on('pointerout', () => {
+ // Cancel delayed tooltip
+ if (hoverTimer) {
+ hoverTimer.destroy();
+ hoverTimer = null;
+ }
+ this.hideTooltip();
+ });
+
+ object.on('pointermove', (pointer) => {
+ // Move tooltip with mouse
+ if (this.currentTooltip) {
+ this.currentTooltip.setPosition(
+ pointer.worldX + 10,
+ pointer.worldY - 30
+ );
+ }
+ });
+ }
+
+ // Typewriter text effect
+ typewriterText(textObject, fullText, speed = 50) {
+ if (!textObject) return;
+
+ textObject.setText('');
+ let index = 0;
+
+ const timer = this.scene.time.addEvent({
+ delay: speed,
+ callback: () => {
+ if (index < fullText.length) {
+ textObject.setText(textObject.text + fullText[index]);
+ index++;
+ } else {
+ timer.destroy();
+ }
+ },
+ loop: true
+ });
+
+ return timer;
+ }
+
+ // Number counter animation (for scores, resources)
+ animateNumber(textObject, startValue, endValue, duration = 1000, prefix = '', suffix = '') {
+ if (!textObject) return;
+
+ const data = { value: startValue };
+
+ this.scene.tweens.add({
+ targets: data,
+ value: endValue,
+ duration: duration,
+ ease: 'Power2',
+ onUpdate: () => {
+ textObject.setText(`${prefix}${Math.floor(data.value)}${suffix}`);
+ }
+ });
+ }
+
+ // Flash effect (for notifications)
+ flash(element, color = 0xffffff, duration = 300) {
+ if (!element) return;
+
+ const originalTint = element.tint || 0xffffff;
+
+ element.setTint(color);
+
+ this.scene.tweens.add({
+ targets: element,
+ alpha: { from: 1, to: 0.5 },
+ duration: duration / 2,
+ yoyo: true,
+ onComplete: () => {
+ element.setTint(originalTint);
+ }
+ });
+ }
+
+ // Smooth scroll container (for long lists)
+ enableSmoothScroll(container, scrollSpeed = 5) {
+ if (!container || !this.scene.input.keyboard) return;
+
+ const upKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP);
+ const downKey = this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN);
+
+ this.scene.events.on('update', () => {
+ if (upKey.isDown) {
+ container.y += scrollSpeed;
+ }
+ if (downKey.isDown) {
+ container.y -= scrollSpeed;
+ }
+ });
+ }
+
+ // Clean up
+ destroy() {
+ this.hideTooltip();
+ this.buttonAnimations.clear();
+
+ console.log('๐จ UIPolishSystem destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/VFXSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/VFXSystem.js
new file mode 100644
index 000000000..34f3c4786
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/VFXSystem.js
@@ -0,0 +1,332 @@
+/**
+ * VFX SYSTEM - COMPLETE IMPLEMENTATION
+ * All 13 visual effects for game polish
+ */
+
+export class VFXSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Existing particle systems
+ this.particleEmitters = new Map();
+
+ this.init();
+ }
+
+ init() {
+ console.log('VFX System initialized');
+ }
+
+ // ==========================================
+ // EXISTING VFX (7/13) - Already implemented
+ // ==========================================
+
+ playParticleBurst(x, y, particleType, quantity = 10) {
+ const particles = this.scene.add.particles(particleType);
+ const emitter = particles.createEmitter({
+ x, y,
+ speed: { min: 50, max: 150 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 800,
+ blendMode: 'ADD',
+ quantity: quantity
+ });
+
+ emitter.explode();
+ this.scene.time.delayedCall(1000, () => particles.destroy());
+ }
+
+ // ==========================================
+ // NEW VFX (6/13) - Implementing now
+ // ==========================================
+
+ /**
+ * 1. SCREEN SHAKE SYSTEM
+ */
+ screenShake(duration = 200, intensity = 0.01) {
+ this.scene.cameras.main.shake(duration, intensity);
+ }
+
+ impactShake() {
+ this.screenShake(200, 0.01); // Heavy hit
+ }
+
+ explosionShake() {
+ this.screenShake(400, 0.015); // Explosion
+ }
+
+ subtleShake() {
+ this.screenShake(100, 0.005); // Tool use
+ }
+
+ buildingCollapseShake() {
+ this.screenShake(500, 0.02); // Building fall
+ }
+
+ /**
+ * 2. FLASH EFFECTS
+ */
+ damageFlash() {
+ this.scene.cameras.main.flash(100, 255, 0, 0); // Red
+ }
+
+ healFlash() {
+ this.scene.cameras.main.flash(150, 0, 255, 0); // Green
+ }
+
+ levelUpFlash() {
+ this.scene.cameras.main.flash(300, 255, 215, 0); // Gold
+ }
+
+ raidWarningFlash() {
+ this.scene.cameras.main.flash(1000, 255, 0, 0, true); // Red pulse
+ }
+
+ victoryFlash() {
+ this.scene.cameras.main.flash(500, 255, 255, 255); // White
+ }
+
+ /**
+ * 3. FLOATING DAMAGE NUMBERS
+ */
+ showFloatingText(x, y, text, color = '#FF0000', size = 24) {
+ const floatingText = this.scene.add.text(x, y, text, {
+ font: `bold ${size}px Arial`,
+ fill: color,
+ stroke: '#000000',
+ strokeThickness: 3
+ }).setOrigin(0.5);
+
+ this.scene.tweens.add({
+ targets: floatingText,
+ y: y - 40,
+ alpha: 0,
+ duration: 1000,
+ ease: 'Quad.easeOut',
+ onComplete: () => floatingText.destroy()
+ });
+ }
+
+ showDamage(x, y, amount, isCrit = false) {
+ const color = isCrit ? '#FFD700' : '#FF0000';
+ this.showFloatingText(x, y, `-${amount}`, color, isCrit ? 32 : 24);
+ }
+
+ showHeal(x, y, amount) {
+ this.showFloatingText(x, y, `+${amount}`, '#00FF00', 24);
+ }
+
+ showXP(x, y, amount) {
+ this.showFloatingText(x, y, `+${amount} XP`, '#00BFFF', 20);
+ }
+
+ showCurrency(x, y, amount) {
+ this.showFloatingText(x, y, `+${amount}ยข`, '#FFD700', 20);
+ }
+
+ /**
+ * 4. HIT STUN / KNOCKBACK
+ */
+ applyHitStun(target, damage, sourceX, sourceY) {
+ // Freeze movement
+ if (target.setVelocity) {
+ target.setVelocity(0, 0);
+ }
+
+ // Red tint flash
+ target.setTint(0xFF0000);
+ this.scene.time.delayedCall(100, () => {
+ if (target.active) target.clearTint();
+ });
+
+ // Calculate knockback
+ const angle = Phaser.Math.Angle.Between(sourceX, sourceY, target.x, target.y);
+ const knockbackForce = Math.min(50, damage * 2);
+
+ // Apply knockback tween
+ this.scene.tweens.add({
+ targets: target,
+ x: target.x + Math.cos(angle) * knockbackForce,
+ y: target.y + Math.sin(angle) * knockbackForce,
+ duration: 300,
+ ease: 'Quad.easeOut'
+ });
+
+ // Resume after stun
+ this.scene.time.delayedCall(200, () => {
+ if (target.resumeMovement) {
+ target.resumeMovement();
+ }
+ });
+ }
+
+ /**
+ * 5. BUILDING CONSTRUCTION PROGRESS
+ */
+ createProgressBar(building) {
+ const bar = this.scene.add.graphics();
+ building.progressBar = bar;
+ this.updateProgressBar(building);
+ return bar;
+ }
+
+ updateProgressBar(building) {
+ if (!building.progressBar) return;
+
+ const bar = building.progressBar;
+ bar.clear();
+
+ const progress = (building.constructionProgress || 0) / 100;
+
+ // Interpolate color from red (0%) to green (100%)
+ const r = Math.floor(255 * (1 - progress));
+ const g = Math.floor(255 * progress);
+ const color = Phaser.Display.Color.GetColor(r, g, 0);
+
+ // Background
+ bar.fillStyle(0x000000, 0.8);
+ bar.fillRect(building.x - 50, building.y - 60, 100, 8);
+
+ // Progress fill
+ bar.fillStyle(color);
+ bar.fillRect(building.x - 50, building.y - 60, 100 * progress, 8);
+
+ // White border
+ bar.lineStyle(2, 0xFFFFFF);
+ bar.strokeRect(building.x - 50, building.y - 60, 100, 8);
+ }
+
+ removeProgressBar(building) {
+ if (building.progressBar) {
+ building.progressBar.destroy();
+ building.progressBar = null;
+ }
+ }
+
+ completionBurst(x, y) {
+ // Confetti explosion
+ const colors = [0xFFD700, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF00FF];
+
+ colors.forEach((color, index) => {
+ this.scene.time.delayedCall(index * 50, () => {
+ const particles = this.scene.add.particles('particle_sparkle');
+ particles.setTint(color);
+
+ const emitter = particles.createEmitter({
+ x, y,
+ speed: { min: 100, max: 200 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 1000,
+ blendMode: 'ADD',
+ quantity: 10
+ });
+
+ emitter.explode();
+ this.scene.time.delayedCall(1200, () => particles.destroy());
+ });
+ });
+
+ // Victory flash
+ this.victoryFlash();
+ }
+
+ /**
+ * 6. DEATH ANIMATIONS
+ */
+ zombieDeath(zombie) {
+ // Dust burst
+ this.playParticleBurst(zombie.x, zombie.y, 'particle_dust', 15);
+
+ // Fade + shrink
+ this.scene.tweens.add({
+ targets: zombie,
+ alpha: 0,
+ scale: 0.5,
+ tint: 0x000000,
+ duration: 800,
+ ease: 'Quad.easeIn',
+ onComplete: () => {
+ if (zombie.active) {
+ zombie.destroy();
+ }
+ }
+ });
+ }
+
+ raiderDeath(raider) {
+ // Spin + fade
+ this.scene.tweens.add({
+ targets: raider,
+ angle: 180,
+ alpha: 0,
+ y: raider.y + 20,
+ duration: 1000,
+ ease: 'Quad.easeOut',
+ onComplete: () => {
+ if (raider.active) {
+ raider.destroy();
+ }
+ }
+ });
+ }
+
+ playerDeath(player) {
+ // Dramatic death
+ this.damageFlash();
+ this.screenShake(800, 0.015);
+
+ this.scene.tweens.add({
+ targets: player,
+ alpha: 0,
+ scale: 0,
+ angle: 360,
+ duration: 1500,
+ ease: 'Quad.easeIn',
+ onComplete: () => {
+ // Trigger game over
+ this.scene.events.emit('player_death');
+ }
+ });
+ }
+
+ /**
+ * UTILITY: Play effect by name
+ */
+ playEffect(effectName, x, y, options = {}) {
+ switch (effectName) {
+ case 'impact':
+ this.impactShake();
+ this.playParticleBurst(x, y, 'particle_dust', 8);
+ break;
+ case 'explosion':
+ this.explosionShake();
+ this.playParticleBurst(x, y, 'particle_fire', 20);
+ break;
+ case 'heal':
+ this.healFlash();
+ this.playParticleBurst(x, y, 'particle_sparkle', 12);
+ break;
+ case 'level_up':
+ this.levelUpFlash();
+ this.playParticleBurst(x, y, 'particle_sparkle', 30);
+ break;
+ case 'victory':
+ this.completionBurst(x, y);
+ break;
+ default:
+ console.warn(`Unknown effect: ${effectName}`);
+ }
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ this.particleEmitters.forEach(emitter => emitter.destroy());
+ this.particleEmitters.clear();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/VIPManager.js b/EMERGENCY_SYSTEMS_RECOVERY/VIPManager.js
new file mode 100644
index 000000000..ccbf12bc0
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/VIPManager.js
@@ -0,0 +1,219 @@
+/**
+ * VIP MANAGER - EARLY SUPPORTER SYSTEM
+ * Handles first 20 buyers exclusive Gronk access
+ */
+
+class VIPManager {
+ constructor() {
+ this.vipStatus = null;
+ this.purchaseOrder = null;
+ this.isEarlySupporter = false;
+ this.init();
+ }
+
+ init() {
+ // Check VIP status from localStorage
+ const stored = localStorage.getItem('vip_status');
+ if (stored) {
+ this.vipStatus = JSON.parse(stored);
+ this.isEarlySupporter = this.vipStatus.early_supporter || false;
+ this.purchaseOrder = this.vipStatus.order_number || null;
+ }
+
+ console.log('๐ VIP Manager initialized');
+ console.log(' Early Supporter:', this.isEarlySupporter);
+ console.log(' Purchase Order:', this.purchaseOrder);
+ }
+
+ /**
+ * CHECK EARLY SUPPORTER STATUS
+ * Called on first game launch
+ */
+ async checkEarlySupporter() {
+ // TODO: Integrate with actual purchase API
+ // For now, check manual override or demo mode
+
+ const manualVIP = localStorage.getItem('manual_vip');
+ if (manualVIP === 'true') {
+ this.grantEarlySupporter(1); // Manual override
+ return true;
+ }
+
+ // Check Steam API (when available)
+ try {
+ const orderNumber = await this.getSteamPurchaseOrder();
+ if (orderNumber && orderNumber <= 20) {
+ this.grantEarlySupporter(orderNumber);
+ return true;
+ }
+ } catch (e) {
+ console.warn('Steam API not available:', e);
+ }
+
+ // Check Itch.io (when available)
+ try {
+ const orderNumber = await this.getItchPurchaseOrder();
+ if (orderNumber && orderNumber <= 20) {
+ this.grantEarlySupporter(orderNumber);
+ return true;
+ }
+ } catch (e) {
+ console.warn('Itch.io API not available:', e);
+ }
+
+ return false;
+ }
+
+ /**
+ * GRANT EARLY SUPPORTER STATUS
+ */
+ grantEarlySupporter(orderNumber) {
+ this.isEarlySupporter = true;
+ this.purchaseOrder = orderNumber;
+
+ this.vipStatus = {
+ early_supporter: true,
+ order_number: orderNumber,
+ granted_date: new Date().toISOString(),
+ founder_badge: true,
+ gronk_unlocked: true
+ };
+
+ localStorage.setItem('vip_status', JSON.stringify(this.vipStatus));
+
+ console.log('๐ EARLY SUPPORTER GRANTED!');
+ console.log(' Order #' + orderNumber);
+
+ // Trigger celebration effect
+ this.showFounderNotification();
+ }
+
+ /**
+ * SHOW FOUNDER NOTIFICATION
+ */
+ showFounderNotification() {
+ // Will be implemented in UI
+ const event = new CustomEvent('vip-granted', {
+ detail: {
+ orderNumber: this.purchaseOrder,
+ title: 'FOUNDER STATUS UNLOCKED!',
+ message: 'You are supporter #' + this.purchaseOrder + ' worldwide!',
+ rewards: [
+ 'Gronk companion unlocked immediately',
+ 'Exclusive Gronk questline',
+ 'Founder badge in-game',
+ 'Your name in credits'
+ ]
+ }
+ });
+ window.dispatchEvent(event);
+ }
+
+ /**
+ * CHECK IF GRONK SHOULD BE UNLOCKED
+ */
+ isGronkUnlocked() {
+ return this.isEarlySupporter;
+ }
+
+ /**
+ * GET VIP BENEFITS
+ */
+ getVIPBenefits() {
+ if (!this.isEarlySupporter) return null;
+
+ return {
+ gronk_companion: true,
+ gronk_vape_boost: true, // +20% speed
+ exclusive_quests: true,
+ founder_badge: true,
+ credits_listing: true,
+ vape_cloud_boost: true // Larger vape clouds for Gronk
+ };
+ }
+
+ /**
+ * STEAM API INTEGRATION (Placeholder)
+ */
+ async getSteamPurchaseOrder() {
+ // TODO: Actual Steam API call
+ // This would check Steam purchase timestamp/order
+ return null;
+ }
+
+ /**
+ * ITCH.IO API INTEGRATION (Placeholder)
+ */
+ async getItchPurchaseOrder() {
+ // TODO: Actual Itch.io API call
+ return null;
+ }
+
+ /**
+ * STREAMER ACCESS KEY SYSTEM
+ */
+ validateStreamerKey(key) {
+ const validKeys = [
+ 'STREAMER_PREVIEW_2026',
+ 'CONTENT_CREATOR_EARLY'
+ ];
+
+ if (validKeys.includes(key)) {
+ this.grantStreamerAccess();
+ return true;
+ }
+
+ return false;
+ }
+
+ grantStreamerAccess() {
+ const streamerStatus = {
+ streamer_mode: true,
+ full_game_access: true,
+ granted_date: new Date().toISOString(),
+ watermark: true // Show "STREAMER PREVIEW" watermark
+ };
+
+ localStorage.setItem('streamer_status', JSON.stringify(streamerStatus));
+
+ console.log('๐บ STREAMER ACCESS GRANTED');
+ }
+
+ isStreamerMode() {
+ const stored = localStorage.getItem('streamer_status');
+ if (!stored) return false;
+
+ const status = JSON.parse(stored);
+ return status.streamer_mode || false;
+ }
+
+ /**
+ * RESET VIP STATUS (for testing)
+ */
+ resetVIPStatus() {
+ localStorage.removeItem('vip_status');
+ localStorage.removeItem('streamer_status');
+ localStorage.removeItem('manual_vip');
+ this.vipStatus = null;
+ this.isEarlySupporter = false;
+ this.purchaseOrder = null;
+ console.log('๐ VIP status reset');
+ }
+
+ /**
+ * MANUAL VIP TOGGLE (Dev/Testing)
+ */
+ setManualVIP(enabled) {
+ if (enabled) {
+ localStorage.setItem('manual_vip', 'true');
+ this.grantEarlySupporter(1);
+ } else {
+ localStorage.removeItem('manual_vip');
+ this.resetVIPStatus();
+ }
+ }
+}
+
+// Singleton instance
+const vipManager = new VIPManager();
+export default vipManager;
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/VehicleSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/VehicleSystem.js
new file mode 100644
index 000000000..0a376d469
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/VehicleSystem.js
@@ -0,0 +1,594 @@
+/**
+ * VehicleSystem.js
+ * ================
+ * KRVAVA ลฝETEV - Complete Vehicle System (P14)
+ *
+ * Vehicle Types:
+ * - Animal Mounts (Horse, Donkey + mutants)
+ * - Carts & Wagons (Hand cart, Donkey cart, Horse wagon)
+ * - Bikes & Boards (Bicycle, Motorcycle, Skateboard, Scooter)
+ * - Water Vehicles (Kayak, SUP, Boat, Motorboat, Surfboard, Submarine)
+ * - Flying Vehicles (Glider, Balloon, Griffin, Pterodactyl, Dragon, Helicopter)
+ * - Train System (18 stations, fast travel)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class VehicleSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Current vehicle
+ this.currentVehicle = null;
+ this.isRiding = false;
+
+ // Vehicle registry
+ this.vehicles = new Map();
+ this.ownedVehicles = [];
+
+ // Train system
+ this.trainStations = [];
+ this.trainTickets = 0;
+
+ console.log('๐ VehicleSystem initialized');
+
+ // Register all vehicle types
+ this.registerVehicles();
+ }
+
+ /**
+ * Register all vehicle types
+ */
+ registerVehicles() {
+ // 14.1 - Animal Mounts
+ this.registerAnimalMounts();
+
+ // 14.2 - Carts & Wagons
+ this.registerCartsWagons();
+
+ // 14.3 - Bikes & Boards
+ this.registerBikesBoards();
+
+ // 14.4 - Water Vehicles
+ this.registerWaterVehicles();
+
+ // 14.5 - Flying Vehicles
+ this.registerFlyingVehicles();
+
+ // 14.6 - Train System
+ this.registerTrainStations();
+
+ console.log(`โ
Registered ${this.vehicles.size} vehicle types`);
+ }
+
+ /**
+ * 14.1 - Animal Mounts
+ */
+ registerAnimalMounts() {
+ const mounts = [
+ {
+ id: 'horse',
+ name: 'Horse',
+ type: 'mount',
+ speed: 2.0,
+ stamina: 100,
+ requiresSaddle: true,
+ canCarry: 50,
+ icon: '๐ด'
+ },
+ {
+ id: 'mutant_horse',
+ name: 'Mutant Horse',
+ type: 'mount',
+ speed: 3.0,
+ stamina: 150,
+ requiresSaddle: true,
+ canCarry: 75,
+ icon: '๐ฆ',
+ special: 'Glows in the dark'
+ },
+ {
+ id: 'donkey',
+ name: 'Donkey',
+ type: 'mount',
+ speed: 1.5,
+ stamina: 120,
+ requiresSaddle: true,
+ canCarry: 100, // More cargo!
+ icon: '๐ซ'
+ },
+ {
+ id: 'mutant_donkey',
+ name: 'Mutant Donkey',
+ type: 'mount',
+ speed: 2.0,
+ stamina: 180,
+ requiresSaddle: true,
+ canCarry: 150,
+ icon: '๐ฆ',
+ special: 'Never gets tired'
+ }
+ ];
+
+ mounts.forEach(mount => this.vehicles.set(mount.id, mount));
+ }
+
+ /**
+ * 14.2 - Carts & Wagons
+ */
+ registerCartsWagons() {
+ const carts = [
+ {
+ id: 'hand_cart',
+ name: 'Hand Cart',
+ type: 'cart',
+ speed: 0.8,
+ canCarry: 200,
+ requiresAnimal: false,
+ icon: '๐'
+ },
+ {
+ id: 'donkey_cart',
+ name: 'Donkey Cart',
+ type: 'cart',
+ speed: 1.5,
+ canCarry: 500,
+ requiresAnimal: 'donkey',
+ icon: '๐บ'
+ },
+ {
+ id: 'horse_wagon',
+ name: 'Horse Wagon',
+ type: 'cart',
+ speed: 2.0,
+ canCarry: 1000,
+ requiresAnimal: 'horse',
+ icon: '๐'
+ }
+ ];
+
+ carts.forEach(cart => this.vehicles.set(cart.id, cart));
+ }
+
+ /**
+ * 14.3 - Bikes & Boards
+ */
+ registerBikesBoards() {
+ const bikes = [
+ {
+ id: 'bicycle',
+ name: 'Bicycle',
+ type: 'bike',
+ speed: 2.5,
+ stamina: -1, // Uses player stamina
+ icon: '๐ฒ'
+ },
+ {
+ id: 'motorcycle',
+ name: 'Motorcycle',
+ type: 'bike',
+ speed: 4.0,
+ fuelType: 'gasoline',
+ fuelCapacity: 10,
+ icon: '๐๏ธ',
+ sound: 'VROOOOM!'
+ },
+ {
+ id: 'skateboard',
+ name: 'Skateboard',
+ type: 'board',
+ speed: 2.0,
+ canDoTricks: true,
+ tricks: ['Ollie', 'Kickflip', '360 Spin'],
+ icon: '๐น'
+ },
+ {
+ id: 'scooter',
+ name: 'Delivery Scooter',
+ type: 'scooter',
+ speed: 2.2,
+ hasMailbox: true,
+ canCarry: 30,
+ icon: '๐ด',
+ special: 'Perfect for deliveries!'
+ }
+ ];
+
+ bikes.forEach(bike => this.vehicles.set(bike.id, bike));
+ }
+
+ /**
+ * 14.4 - Water Vehicles
+ */
+ registerWaterVehicles() {
+ const waterVehicles = [
+ {
+ id: 'kayak',
+ name: 'Kayak',
+ type: 'water',
+ speed: 1.5,
+ waterOnly: true,
+ icon: '๐ถ'
+ },
+ {
+ id: 'sup',
+ name: 'SUP (Stand-Up Paddleboard)',
+ type: 'water',
+ speed: 1.2,
+ waterOnly: true,
+ icon: '๐',
+ canFish: true
+ },
+ {
+ id: 'fishing_boat',
+ name: 'Fishing Boat',
+ type: 'water',
+ speed: 1.8,
+ waterOnly: true,
+ canCarry: 100,
+ unlocks: 'deep_sea_fishing',
+ icon: 'โต'
+ },
+ {
+ id: 'motorboat',
+ name: 'Motorboat',
+ type: 'water',
+ speed: 3.5,
+ waterOnly: true,
+ fuelType: 'gasoline',
+ fuelCapacity: 20,
+ icon: '๐ค'
+ },
+ {
+ id: 'surfboard',
+ name: 'Surfboard',
+ type: 'water',
+ speed: 2.5,
+ waterOnly: true,
+ canRideWaves: true,
+ icon: '๐โโ๏ธ',
+ special: 'Catch waves!'
+ },
+ {
+ id: 'atlantis_submarine',
+ name: 'Atlantis Submarine',
+ type: 'water',
+ speed: 2.0,
+ waterOnly: true,
+ canDive: true,
+ maxDepth: 500,
+ icon: '๐ฑ',
+ special: 'Access underwater ruins!',
+ unlocks: 'atlantis_zone'
+ }
+ ];
+
+ waterVehicles.forEach(vehicle => this.vehicles.set(vehicle.id, vehicle));
+ }
+
+ /**
+ * 14.5 - Flying Vehicles
+ */
+ registerFlyingVehicles() {
+ const flyingVehicles = [
+ {
+ id: 'hang_glider',
+ name: 'Hang Glider',
+ type: 'flying',
+ speed: 2.0,
+ maxHeight: 100,
+ glideOnly: true, // Can't gain altitude
+ icon: '๐ช'
+ },
+ {
+ id: 'hot_air_balloon',
+ name: 'Hot Air Balloon',
+ type: 'flying',
+ speed: 1.0,
+ maxHeight: 200,
+ canHover: true,
+ icon: '๐'
+ },
+ {
+ id: 'griffin',
+ name: 'Griffin Mount',
+ type: 'flying',
+ speed: 3.5,
+ maxHeight: 300,
+ stamina: 200,
+ icon: '๐ฆ
',
+ special: 'Mythical creature!',
+ unlocks: 'mythical_zone'
+ },
+ {
+ id: 'pterodactyl',
+ name: 'Pterodactyl Mount',
+ type: 'flying',
+ speed: 4.0,
+ maxHeight: 250,
+ stamina: 180,
+ icon: '๐ฆ',
+ special: 'Prehistoric power!',
+ unlocks: 'dino_valley'
+ },
+ {
+ id: 'dragon',
+ name: 'Dragon Mount',
+ type: 'flying',
+ speed: 5.0,
+ maxHeight: 500,
+ stamina: 300,
+ canBreatheFire: true,
+ icon: '๐',
+ special: 'ENDGAME MOUNT!',
+ unlocks: 'everywhere'
+ },
+ {
+ id: 'helicopter',
+ name: 'Atlantean Helicopter',
+ type: 'flying',
+ speed: 4.5,
+ maxHeight: 400,
+ fuelType: 'atlantean_crystal',
+ fuelCapacity: 10,
+ icon: '๐',
+ special: 'Ancient technology!',
+ unlocks: 'fast_travel'
+ }
+ ];
+
+ flyingVehicles.forEach(vehicle => this.vehicles.set(vehicle.id, vehicle));
+ }
+
+ /**
+ * 14.6 - Train System
+ */
+ registerTrainStations() {
+ const stations = [
+ { id: 'spawn_town', name: 'Spawn Town', x: 250, y: 250 },
+ { id: 'desert', name: 'Desert Oasis', x: 150, y: 150 },
+ { id: 'forest', name: 'Endless Forest', x: 350, y: 200 },
+ { id: 'mountains', name: 'Mountain Peak', x: 200, y: 100 },
+ { id: 'beach', name: 'Sunny Beach', x: 400, y: 300 },
+ { id: 'swamp', name: 'Toxic Swamp', x: 100, y: 350 },
+ { id: 'volcano', name: 'Volcano Station', x: 50, y: 50 },
+ { id: 'snow', name: 'Frozen Tundra', x: 450, y: 100 },
+ { id: 'jungle', name: 'Amazon Jungle', x: 300, y: 450 },
+ { id: 'loch_ness', name: 'Loch Ness', x: 100, y: 200 },
+ { id: 'egypt', name: 'Egyptian Pyramids', x: 200, y: 400 },
+ { id: 'atlantis', name: 'Atlantis Port', x: 450, y: 450 },
+ { id: 'dino_valley', name: 'Dino Valley', x: 50, y: 400 },
+ { id: 'mythical', name: 'Mythical Realm', x: 400, y: 50 },
+ { id: 'catacombs', name: 'Catacombs Entrance', x: 300, y: 300 },
+ { id: 'chernobyl', name: 'Chernobyl Zone', x: 150, y: 450 },
+ { id: 'scotland', name: 'Scottish Highlands', x: 450, y: 200 },
+ { id: 'farm', name: 'Central Farm Hub', x: 250, y: 350 }
+ ];
+
+ this.trainStations = stations;
+ console.log(`๐ ${stations.length} train stations registered`);
+ }
+
+ /**
+ * Mount/ride a vehicle
+ */
+ mountVehicle(vehicleId) {
+ const vehicle = this.vehicles.get(vehicleId);
+ if (!vehicle) {
+ console.error(`Vehicle ${vehicleId} not found!`);
+ return false;
+ }
+
+ // Check requirements
+ if (vehicle.requiresSaddle && !this.hasSaddle()) {
+ this.showNotification({
+ title: 'Need Saddle',
+ text: 'You need a saddle to ride this mount!',
+ icon: '๐ชข'
+ });
+ return false;
+ }
+
+ if (vehicle.requiresAnimal && !this.hasAnimal(vehicle.requiresAnimal)) {
+ this.showNotification({
+ title: 'Need Animal',
+ text: `You need a ${vehicle.requiresAnimal} to use this cart!`,
+ icon: '๐ด'
+ });
+ return false;
+ }
+
+ // Mount!
+ this.currentVehicle = vehicle;
+ this.isRiding = true;
+
+ // Apply speed modifier
+ if (this.scene.player) {
+ this.scene.player.originalSpeed = this.scene.player.speed || 100;
+ this.scene.player.speed = this.scene.player.originalSpeed * vehicle.speed;
+ }
+
+ console.log(`${vehicle.icon} Mounted ${vehicle.name}!`);
+
+ this.showNotification({
+ title: 'Mounted!',
+ text: `${vehicle.icon} Riding ${vehicle.name}! Speed: ${vehicle.speed}x`,
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ /**
+ * Dismount vehicle
+ */
+ dismountVehicle() {
+ if (!this.currentVehicle) return false;
+
+ const vehicle = this.currentVehicle;
+
+ // Restore speed
+ if (this.scene.player && this.scene.player.originalSpeed) {
+ this.scene.player.speed = this.scene.player.originalSpeed;
+ }
+
+ this.currentVehicle = null;
+ this.isRiding = false;
+
+ console.log(`Dismounted ${vehicle.name}`);
+
+ this.showNotification({
+ title: 'Dismounted',
+ text: `Left ${vehicle.name}`,
+ icon: ''
+ });
+
+ return true;
+ }
+
+ /**
+ * Train fast travel
+ */
+ fastTravel(stationId) {
+ const station = this.trainStations.find(s => s.id === stationId);
+ if (!station) {
+ console.error(`Station ${stationId} not found!`);
+ return false;
+ }
+
+ // Check tickets
+ if (this.trainTickets <= 0) {
+ this.showNotification({
+ title: 'No Tickets',
+ text: 'Buy train tickets! (10 Zlatniki/ticket)',
+ icon: '๐ซ'
+ });
+ return false;
+ }
+
+ // Use ticket
+ this.trainTickets--;
+
+ // Teleport player
+ if (this.scene.player) {
+ this.scene.player.x = station.x * 48; // Convert to pixels
+ this.scene.player.y = station.y * 48;
+ }
+
+ console.log(`๐ Traveled to ${station.name}!`);
+
+ this.showNotification({
+ title: 'Fast Travel',
+ text: `๐ Arrived at ${station.name}! (${this.trainTickets} tickets left)`,
+ icon: '๐ซ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Buy train tickets
+ */
+ buyTrainTickets(amount) {
+ const cost = amount * 10; // 10 Zlatniki per ticket
+
+ // TODO: Check if player has money
+ // For now, just give tickets
+ this.trainTickets += amount;
+
+ this.showNotification({
+ title: 'Tickets Purchased',
+ text: `๐ซ Bought ${amount} train tickets! Total: ${this.trainTickets}`,
+ icon: '๐ฐ'
+ });
+ }
+
+ /**
+ * Do skateboard trick
+ */
+ doSkateboardTrick() {
+ if (!this.currentVehicle || this.currentVehicle.id !== 'skateboard') {
+ return false;
+ }
+
+ const tricks = this.currentVehicle.tricks;
+ const trick = Phaser.Utils.Array.GetRandom(tricks);
+
+ console.log(`๐น ${trick}!`);
+
+ this.showNotification({
+ title: 'Sick Trick!',
+ text: `๐น ${trick}! +10 Style Points!`,
+ icon: '๐ค'
+ });
+
+ return true;
+ }
+
+ /**
+ * Use submarine dive
+ */
+ diveSubmarine() {
+ if (!this.currentVehicle || this.currentVehicle.id !== 'atlantis_submarine') {
+ return false;
+ }
+
+ console.log('๐ฑ Diving to Atlantis!');
+
+ // TODO: Trigger underwater zone
+ this.showNotification({
+ title: 'Diving!',
+ text: '๐ฑ Descending to Atlantis ruins!',
+ icon: '๐'
+ });
+
+ return true;
+ }
+
+ /**
+ * Helper methods
+ */
+ hasSaddle() {
+ // TODO: Check inventory
+ return true; // For now, always true
+ }
+
+ hasAnimal(animalType) {
+ // TODO: Check if player owns animal
+ return true; // For now, always true
+ }
+
+ /**
+ * Get all vehicles
+ */
+ getAllVehicles() {
+ return Array.from(this.vehicles.values());
+ }
+
+ /**
+ * Get vehicles by type
+ */
+ getVehiclesByType(type) {
+ return this.getAllVehicles().filter(v => v.type === type);
+ }
+
+ /**
+ * Get train stations
+ */
+ getTrainStations() {
+ return this.trainStations;
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/VisualEffectsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/VisualEffectsSystem.js
new file mode 100644
index 000000000..b424765a5
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/VisualEffectsSystem.js
@@ -0,0 +1,128 @@
+/**
+ * VISUAL EFFECTS SYSTEM
+ * Handles juice effects: screenshake, particles, lighting
+ */
+class VisualEffectsSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.camera = scene.cameras.main;
+ this.isShaking = false;
+ }
+
+ /**
+ * Screenshake effect
+ * @param {number} intensity - Shake strength (default 0.005)
+ * @param {number} duration - Duration in ms (default 300)
+ */
+ screenshake(intensity = 0.005, duration = 300) {
+ if (this.isShaking) return;
+
+ this.isShaking = true;
+ this.camera.shake(duration, intensity);
+
+ this.scene.time.delayedCall(duration, () => {
+ this.isShaking = false;
+ });
+ }
+
+ /**
+ * Hit particles (sparks)
+ */
+ createHitParticles(x, y, color = 0xFFFFFF) {
+ const particles = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 100, max: 200 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 0.5, end: 0 },
+ lifespan: 300,
+ quantity: 8,
+ tint: color
+ });
+
+ this.scene.time.delayedCall(500, () => {
+ particles.destroy();
+ });
+ }
+
+ /**
+ * Explosion particles
+ */
+ createExplosion(x, y, color = 0xFF4444) {
+ const particles = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 200, max: 400 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 1, end: 0 },
+ lifespan: 600,
+ quantity: 20,
+ tint: color,
+ gravityY: 300
+ });
+
+ this.scene.time.delayedCall(800, () => {
+ particles.destroy();
+ });
+ }
+
+ /**
+ * Dust particles (for movement)
+ */
+ createDustPuff(x, y) {
+ const particles = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 30, max: 60 },
+ angle: { min: 0, max: 360 },
+ scale: { start: 0.3, end: 0 },
+ lifespan: 400,
+ quantity: 5,
+ tint: 0xCCBBAA,
+ alpha: { start: 0.5, end: 0 }
+ });
+
+ this.scene.time.delayedCall(500, () => {
+ particles.destroy();
+ });
+ }
+
+ /**
+ * Flash effect (for damage, powerups)
+ */
+ flash(color = 0xFFFFFF, duration = 100) {
+ this.camera.flash(duration, ...this.hexToRGB(color));
+ }
+
+ /**
+ * Fade effect
+ */
+ fadeOut(duration = 1000, callback) {
+ this.camera.fadeOut(duration, 0, 0, 0);
+ if (callback) {
+ this.scene.time.delayedCall(duration, callback);
+ }
+ }
+
+ fadeIn(duration = 1000) {
+ this.camera.fadeIn(duration, 0, 0, 0);
+ }
+
+ /**
+ * Utility: Hex to RGB
+ */
+ hexToRGB(hex) {
+ return [
+ (hex >> 16) & 255,
+ (hex >> 8) & 255,
+ hex & 255
+ ];
+ }
+
+ /**
+ * Create simple white pixel texture for particles
+ */
+ static createParticleTexture(scene) {
+ if (scene.textures.exists('particle_white')) return;
+
+ const graphics = scene.make.graphics({ x: 0, y: 0, add: false });
+ graphics.fillStyle(0xFFFFFF, 1);
+ graphics.fillCircle(4, 4, 4);
+ graphics.generateTexture('particle_white', 8, 8);
+ graphics.destroy();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/VisualEnhancementSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/VisualEnhancementSystem.js
new file mode 100644
index 000000000..25e851d94
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/VisualEnhancementSystem.js
@@ -0,0 +1,624 @@
+/**
+ * VISUAL ENHANCEMENT SYSTEM
+ * Central system for managing visual effects, animations, and polish
+ */
+class VisualEnhancementSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Sub-systems
+ this.animatedTextures = new Map();
+ this.weatherEffects = [];
+ this.lightSources = [];
+ this.shadows = [];
+ this.particles = [];
+
+ // Settings
+ this.settings = {
+ animatedTextures: true,
+ weatherEffects: true,
+ dynamicLighting: true,
+ shadows: true,
+ fogOfWar: false,
+ particleQuality: 'high', // low, medium, high, ultra
+ animationQuality: 'high',
+ screenShake: true,
+ transitions: true
+ };
+
+ // Animation timers
+ this.waterAnimTime = 0;
+ this.treeAnimTime = 0;
+ this.fireAnimTime = 0;
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Visual Enhancement System initialized');
+ }
+
+ init() {
+ this.initAnimatedTextures();
+ this.initWeatherEffects();
+ this.initLightingSystem();
+ this.initShadowSystem();
+ this.initParticleSystem();
+ }
+
+ // ========== ANIMATED TEXTURES ==========
+
+ initAnimatedTextures() {
+ if (!this.settings.animatedTextures) return;
+
+ // Create water animation frames
+ this.createWaterAnimation();
+
+ // Create fire animation
+ this.createFireAnimation();
+
+ // Create tree leaf animation
+ this.createTreeAnimation();
+
+ console.log('๐ฌ Animated textures initialized');
+ }
+
+ createWaterAnimation() {
+ // Water flow animation (4 frames)
+ const frames = [];
+ for (let i = 0; i < 4; i++) {
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0x4488ff, 1);
+ graphics.fillRect(0, 0, 64, 64);
+
+ // Add wave pattern
+ graphics.lineStyle(2, 0x6699ff, 0.5);
+ for (let y = 0; y < 64; y += 8) {
+ const offset = Math.sin((y + i * 16) * 0.1) * 4;
+ graphics.lineBetween(0, y + offset, 64, y + offset);
+ }
+
+ graphics.generateTexture('water_anim_' + i, 64, 64);
+ graphics.destroy();
+ frames.push('water_anim_' + i);
+ }
+
+ this.animatedTextures.set('water', {
+ frames,
+ currentFrame: 0,
+ speed: 200 // ms per frame
+ });
+ }
+
+ createFireAnimation() {
+ // Fire flickering (3 frames)
+ const frames = [];
+ const colors = [0xff6600, 0xff8800, 0xffaa00];
+
+ for (let i = 0; i < 3; i++) {
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(colors[i], 1);
+ graphics.fillCircle(16, 16, 12 + i * 2);
+ graphics.generateTexture('fire_anim_' + i, 32, 32);
+ graphics.destroy();
+ frames.push('fire_anim_' + i);
+ }
+
+ this.animatedTextures.set('fire', {
+ frames,
+ currentFrame: 0,
+ speed: 150
+ });
+ }
+
+ createTreeAnimation() {
+ // Tree leaf rustling (subtle movement)
+ console.log('๐ณ Tree animation ready');
+ }
+
+ updateAnimatedTextures(delta) {
+ if (!this.settings.animatedTextures) return;
+
+ this.waterAnimTime += delta;
+ this.fireAnimTime += delta;
+
+ // Update water
+ const water = this.animatedTextures.get('water');
+ if (water && this.waterAnimTime > water.speed) {
+ water.currentFrame = (water.currentFrame + 1) % water.frames.length;
+ this.waterAnimTime = 0;
+ }
+
+ // Update fire
+ const fire = this.animatedTextures.get('fire');
+ if (fire && this.fireAnimTime > fire.speed) {
+ fire.currentFrame = (fire.currentFrame + 1) % fire.frames.length;
+ this.fireAnimTime = 0;
+ }
+ }
+
+ // ========== WEATHER EFFECTS ==========
+
+ initWeatherEffects() {
+ if (!this.settings.weatherEffects) return;
+ console.log('๐ฆ๏ธ Weather effects initialized');
+ }
+
+ createSnowEffect() {
+ const emitter = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -10,
+ speedY: { min: 50, max: 100 },
+ speedX: { min: -20, max: 20 },
+ scale: { min: 0.3, max: 0.8 },
+ alpha: { min: 0.5, max: 1 },
+ lifespan: 10000,
+ frequency: 50,
+ quantity: 2
+ });
+ emitter.setScrollFactor(0);
+ this.weatherEffects.push(emitter);
+ return emitter;
+ }
+
+ createRainEffect() {
+ const emitter = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -10,
+ speedY: { min: 400, max: 600 },
+ speedX: { min: -50, max: -30 },
+ scaleX: 0.1,
+ scaleY: 0.5,
+ alpha: 0.6,
+ lifespan: 2000,
+ frequency: 10,
+ quantity: 5,
+ tint: 0x88ccff
+ });
+ emitter.setScrollFactor(0);
+ this.weatherEffects.push(emitter);
+ return emitter;
+ }
+
+ createLightningFlash() {
+ const flash = this.scene.add.rectangle(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.centerY,
+ this.scene.cameras.main.width,
+ this.scene.cameras.main.height,
+ 0xffffff,
+ 0.8
+ );
+ flash.setScrollFactor(0);
+ flash.setDepth(10000);
+
+ this.scene.tweens.add({
+ targets: flash,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => flash.destroy()
+ });
+ }
+
+ createFogEffect() {
+ // Atmospheric fog overlay - covers entire screen
+ const fogOverlay = this.scene.add.graphics();
+ fogOverlay.setScrollFactor(0);
+ fogOverlay.setDepth(8000); // Above game, below UI
+ fogOverlay.setAlpha(0.4); // Semi-transparent
+
+ // Create fog particles for movement effect
+ const fogParticles = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: { min: 0, max: this.scene.cameras.main.height },
+ speedX: { min: -10, max: 10 },
+ speedY: { min: -5, max: 5 },
+ scale: { min: 2, max: 4 },
+ alpha: { min: 0.1, max: 0.3 },
+ lifespan: 10000,
+ frequency: 100,
+ quantity: 3,
+ tint: 0xcccccc,
+ blendMode: 'SCREEN'
+ });
+ fogParticles.setScrollFactor(0);
+ fogParticles.setDepth(8001);
+
+ // Animated fog overlay
+ let fogTime = 0;
+ const updateFog = () => {
+ fogTime += 0.01;
+ fogOverlay.clear();
+
+ const cam = this.scene.cameras.main;
+
+ // Draw multiple layers of fog for depth
+ for (let layer = 0; layer < 3; layer++) {
+ const offset = Math.sin(fogTime + layer) * 50;
+ const alpha = 0.1 + layer * 0.05;
+
+ fogOverlay.fillStyle(0xffffff, alpha);
+
+ // Draw wavy fog shapes
+ for (let i = 0; i < 5; i++) {
+ const x = (i * cam.width / 4) + offset;
+ const y = (layer * 100) + Math.cos(fogTime + i) * 30;
+ fogOverlay.fillCircle(x, y, 200 + layer * 50);
+ }
+ }
+ };
+
+ // Update fog animation every frame
+ this.scene.events.on('update', updateFog);
+
+ this.weatherEffects.push({
+ overlay: fogOverlay,
+ particles: fogParticles,
+ update: updateFog,
+ destroy: () => {
+ fogOverlay.destroy();
+ fogParticles.destroy();
+ this.scene.events.off('update', updateFog);
+ }
+ });
+
+ console.log('๐ซ๏ธ Atmospheric fog effect created');
+ return { overlay: fogOverlay, particles: fogParticles };
+ }
+
+ // ========== LIGHTING SYSTEM ==========
+
+ initLightingSystem() {
+ if (!this.settings.dynamicLighting) return;
+
+ this.lightingLayer = this.scene.add.layer();
+ this.lightingLayer.setDepth(5000);
+
+ console.log('๐ก Lighting system initialized');
+ }
+
+ addLight(x, y, radius = 100, color = 0xffaa00, intensity = 0.6) {
+ if (!this.settings.dynamicLighting) return null;
+
+ // Create radial gradient light
+ const light = this.scene.add.graphics();
+ const gradient = light.createRadialGradient(
+ radius, radius, 0,
+ radius, radius, radius
+ );
+
+ gradient.addColorStop(0, `rgba(${(color >> 16) & 0xff}, ${(color >> 8) & 0xff}, ${color & 0xff}, ${intensity})`);
+ gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
+
+ light.fillStyle(color, intensity);
+ light.fillCircle(radius, radius, radius);
+ light.setPosition(x - radius, y - radius);
+ light.setBlendMode(Phaser.BlendModes.ADD);
+ light.setDepth(5001);
+
+ const lightObj = {
+ graphics: light,
+ x, y, radius, color, intensity,
+ flickering: false
+ };
+
+ this.lightSources.push(lightObj);
+ return lightObj;
+ }
+
+ addTorch(x, y) {
+ const torch = this.addLight(x, y, 80, 0xff6600, 0.5);
+ if (torch) {
+ torch.flickering = true;
+ }
+ return torch;
+ }
+
+ updateLighting(delta) {
+ if (!this.settings.dynamicLighting) return;
+
+ // Update flickering lights
+ for (const light of this.lightSources) {
+ if (light.flickering) {
+ const flicker = 0.4 + Math.random() * 0.2;
+ light.graphics.setAlpha(flicker);
+ }
+ }
+
+ // Update ambient lighting based on time of day
+ if (this.scene.weatherSystem) {
+ const time = this.scene.weatherSystem.gameTime;
+ const isNight = time < 6 || time > 18;
+
+ if (isNight) {
+ // Darker at night
+ this.scene.cameras.main.setAlpha(0.7);
+ } else {
+ this.scene.cameras.main.setAlpha(1.0);
+ }
+ }
+ }
+
+ // ========== SHADOW SYSTEM ==========
+
+ initShadowSystem() {
+ if (!this.settings.shadows) return;
+ console.log('๐ Shadow system initialized');
+ }
+
+ addShadow(entity, offsetX = 0, offsetY = 10, width = 40, height = 15) {
+ if (!this.settings.shadows) return null;
+
+ const shadow = this.scene.add.ellipse(
+ entity.x + offsetX,
+ entity.y + offsetY,
+ width,
+ height,
+ 0x000000,
+ 0.3
+ );
+ shadow.setDepth(0);
+
+ const shadowObj = { entity, shadow, offsetX, offsetY };
+ this.shadows.push(shadowObj);
+ return shadow;
+ }
+
+ updateShadows() {
+ if (!this.settings.shadows) return;
+
+ // Get time of day for shadow opacity
+ let opacity = 0.3;
+ if (this.scene.weatherSystem) {
+ const time = this.scene.weatherSystem.gameTime;
+ // Darker shadows at noon, lighter at dawn/dusk
+ opacity = 0.2 + Math.abs(Math.sin((time / 24) * Math.PI * 2)) * 0.3;
+ }
+
+ // Update shadow positions
+ for (const { entity, shadow, offsetX, offsetY } of this.shadows) {
+ if (entity.sprite) {
+ shadow.x = entity.sprite.x + offsetX;
+ shadow.y = entity.sprite.y + offsetY;
+ shadow.setAlpha(opacity);
+ }
+ }
+ }
+
+ // ========== PARTICLE SYSTEM ==========
+
+ initParticleSystem() {
+ // Create particle textures
+ this.createParticleTextures();
+ console.log('โจ Particle system initialized');
+ }
+
+ createParticleTextures() {
+ // White particle
+ const white = this.scene.add.graphics();
+ white.fillStyle(0xffffff, 1);
+ white.fillCircle(4, 4, 4);
+ white.generateTexture('particle_white', 8, 8);
+ white.destroy();
+
+ // Sparkle
+ const sparkle = this.scene.add.graphics();
+ sparkle.fillStyle(0xffff00, 1);
+ sparkle.fillCircle(3, 3, 3);
+ sparkle.generateTexture('particle_sparkle', 6, 6);
+ sparkle.destroy();
+
+ // Heart
+ const heart = this.scene.add.graphics();
+ heart.fillStyle(0xff0066, 1);
+ heart.fillCircle(3, 3, 3);
+ heart.fillCircle(5, 3, 3);
+ heart.fillTriangle(1, 4, 7, 4, 4, 8);
+ heart.generateTexture('particle_heart', 8, 8);
+ heart.destroy();
+ }
+
+ createHeartParticles(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'particle_heart', {
+ speed: { min: 20, max: 50 },
+ angle: { min: -120, max: -60 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 1000,
+ quantity: 5,
+ blendMode: 'ADD'
+ });
+
+ this.scene.time.delayedCall(1000, () => emitter.destroy());
+ return emitter;
+ }
+
+ createSparkleEffect(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'particle_sparkle', {
+ speed: { min: 10, max: 30 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 800,
+ quantity: 10,
+ blendMode: 'ADD'
+ });
+
+ this.scene.time.delayedCall(800, () => emitter.destroy());
+ return emitter;
+ }
+
+ createCheckmarkEffect(x, y) {
+ const checkmark = this.scene.add.text(x, y, 'โ', {
+ fontSize: '32px',
+ color: '#00ff00',
+ fontStyle: 'bold'
+ });
+ checkmark.setOrigin(0.5);
+ checkmark.setDepth(10000);
+
+ this.scene.tweens.add({
+ targets: checkmark,
+ y: y - 50,
+ alpha: 0,
+ scale: 2,
+ duration: 1000,
+ ease: 'Power2',
+ onComplete: () => checkmark.destroy()
+ });
+ }
+
+ // ========== SCREEN EFFECTS ==========
+
+ screenShake(intensity = 10, duration = 300) {
+ if (!this.settings.screenShake) return;
+ this.scene.cameras.main.shake(duration, intensity / 1000);
+ }
+
+ screenFlash(color = 0xffffff, duration = 200) {
+ this.scene.cameras.main.flash(duration,
+ (color >> 16) & 0xff,
+ (color >> 8) & 0xff,
+ color & 0xff
+ );
+ }
+
+ fadeOut(duration = 500, callback) {
+ if (!this.settings.transitions) {
+ if (callback) callback();
+ return;
+ }
+
+ this.scene.cameras.main.fadeOut(duration, 0, 0, 0);
+ if (callback) {
+ this.scene.cameras.main.once('camerafadeoutcomplete', callback);
+ }
+ }
+
+ fadeIn(duration = 500) {
+ if (!this.settings.transitions) return;
+ this.scene.cameras.main.fadeIn(duration, 0, 0, 0);
+ }
+
+ // ========== BUILDING EFFECTS ==========
+
+ createConstructionEffect(x, y) {
+ // Dust particles during construction
+ const emitter = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 20, max: 40 },
+ scale: { start: 0.5, end: 0 },
+ alpha: { start: 0.5, end: 0 },
+ lifespan: 1000,
+ quantity: 3,
+ frequency: 100,
+ tint: 0x996633
+ });
+
+ return emitter;
+ }
+
+ createSmokeEffect(x, y) {
+ // Chimney smoke
+ const emitter = this.scene.add.particles(x, y, 'particle_white', {
+ speedY: { min: -30, max: -50 },
+ speedX: { min: -10, max: 10 },
+ scale: { start: 0.3, end: 1 },
+ alpha: { start: 0.5, end: 0 },
+ lifespan: 2000,
+ frequency: 500,
+ quantity: 1,
+ tint: 0x888888
+ });
+
+ return emitter;
+ }
+
+ // ========== FARM AUTOMATION VISUALS ==========
+
+ createPowerGridEffect(x1, y1, x2, y2) {
+ // Electric arc between power sources
+ const graphics = this.scene.add.graphics();
+ graphics.lineStyle(2, 0x00ffff, 0.8);
+
+ // Draw lightning-like connection
+ const steps = 5;
+ const points = [];
+ for (let i = 0; i <= steps; i++) {
+ const t = i / steps;
+ const x = x1 + (x2 - x1) * t + (Math.random() - 0.5) * 10;
+ const y = y1 + (y2 - y1) * t + (Math.random() - 0.5) * 10;
+ points.push({ x, y });
+ }
+
+ graphics.beginPath();
+ graphics.moveTo(points[0].x, points[0].y);
+ for (let i = 1; i < points.length; i++) {
+ graphics.lineTo(points[i].x, points[i].y);
+ }
+ graphics.strokePath();
+
+ // Fade out
+ this.scene.tweens.add({
+ targets: graphics,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => graphics.destroy()
+ });
+ }
+
+ createMutantGlow(entity, color = 0x00ff00) {
+ // Radioactive glow for mutants
+ const glow = this.scene.add.circle(
+ entity.x,
+ entity.y,
+ 30,
+ color,
+ 0.3
+ );
+ glow.setBlendMode(Phaser.BlendModes.ADD);
+ glow.setDepth(entity.depth - 1);
+
+ // Pulsing animation
+ this.scene.tweens.add({
+ targets: glow,
+ scale: { from: 1, to: 1.2 },
+ alpha: { from: 0.3, to: 0.1 },
+ duration: 1000,
+ yoyo: true,
+ repeat: -1
+ });
+
+ return glow;
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateAnimatedTextures(delta);
+ this.updateLighting(delta);
+ this.updateShadows();
+ }
+
+ // ========== SETTINGS ==========
+
+ saveSettings() {
+ localStorage.setItem('novafarma_visual_enhancements', JSON.stringify(this.settings));
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_visual_enhancements');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ destroy() {
+ if (this.lightingLayer) this.lightingLayer.destroy();
+ for (const { shadow } of this.shadows) {
+ shadow.destroy();
+ }
+ for (const effect of this.weatherEffects) {
+ effect.destroy();
+ }
+ console.log('โจ Visual Enhancement System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/VisualSoundCueSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/VisualSoundCueSystem.js
new file mode 100644
index 000000000..43396c912
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/VisualSoundCueSystem.js
@@ -0,0 +1,746 @@
+/**
+ * VISUAL SOUND CUE SYSTEM
+ * Provides visual indicators for sounds (accessibility for deaf/hard-of-hearing players)
+ */
+class VisualSoundCueSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Visual elements
+ this.heartbeatIndicator = null;
+ this.damageIndicators = [];
+ this.screenFlash = null;
+ this.subtitleBackground = null;
+ this.subtitleText = null;
+ this.subtitleSpeaker = null;
+ this.subtitleArrows = { left: null, right: null };
+ this.heartbeatTween = null;
+ this.fishingBobberIndicator = null;
+
+ // Speaker color mapping
+ this.speakerColors = {
+ 'Player': '#00ff00',
+ 'NPC': '#ffff00',
+ 'Enemy': '#ff0000',
+ 'System': '#00ffff',
+ 'Narrator': '#ffffff'
+ };
+
+ // Subtitle size presets
+ this.subtitleSizes = {
+ 'small': { main: 16, speaker: 12, arrow: 24 },
+ 'medium': { main: 20, speaker: 16, arrow: 32 },
+ 'large': { main: 28, speaker: 20, arrow: 40 },
+ 'very-large': { main: 36, speaker: 24, arrow: 48 }
+ };
+
+ // Settings
+ this.settings = {
+ heartbeatEnabled: true,
+ damageIndicatorEnabled: true,
+ screenFlashEnabled: true,
+ subtitlesEnabled: true,
+ directionalArrowsEnabled: true,
+ speakerNamesEnabled: true,
+ subtitleOpacity: 0.8,
+ fishingBobberEnabled: true,
+ subtitleSize: 'medium' // 'small', 'medium', 'large', 'very-large'
+ };
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Visual Sound Cue System initialized');
+ }
+
+ init() {
+ // Create heartbeat indicator (top-left corner)
+ this.createHeartbeatIndicator();
+
+ // Create subtitle container (bottom center)
+ this.createSubtitleContainer();
+
+ // Load settings from localStorage
+ // this.loadSettings(); // Moved to constructor
+ }
+
+ createHeartbeatIndicator() {
+ const x = 200;
+ const y = 30;
+
+ // Heart emoji as sprite
+ this.heartbeatIndicator = this.scene.add.text(x, y, 'โค๏ธ', {
+ fontSize: '48px'
+ });
+ this.heartbeatIndicator.setOrigin(0.5);
+ this.heartbeatIndicator.setDepth(10000);
+ this.heartbeatIndicator.setScrollFactor(0);
+ this.heartbeatIndicator.setVisible(false);
+ }
+
+ createSubtitleContainer() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Background
+ this.subtitleBackground = this.scene.add.rectangle(
+ width / 2,
+ height - 100,
+ width - 100,
+ 100,
+ 0x000000,
+ this.settings.subtitleOpacity
+ );
+ this.subtitleBackground.setOrigin(0.5);
+ this.subtitleBackground.setDepth(9999);
+ this.subtitleBackground.setScrollFactor(0);
+ this.subtitleBackground.setVisible(false);
+
+ // Speaker name text
+ this.subtitleSpeaker = this.scene.add.text(
+ width / 2,
+ height - 130,
+ '',
+ {
+ fontSize: '16px',
+ fontFamily: 'Arial',
+ fontStyle: 'bold',
+ color: '#ffffff',
+ align: 'center'
+ }
+ );
+ this.subtitleSpeaker.setOrigin(0.5);
+ this.subtitleSpeaker.setDepth(10001);
+ this.subtitleSpeaker.setScrollFactor(0);
+ this.subtitleSpeaker.setVisible(false);
+
+ // Main subtitle text
+ this.subtitleText = this.scene.add.text(
+ width / 2,
+ height - 100,
+ '',
+ {
+ fontSize: '20px',
+ fontFamily: 'Arial',
+ color: '#ffffff',
+ align: 'center',
+ wordWrap: { width: width - 160 }
+ }
+ );
+ this.subtitleText.setOrigin(0.5);
+ this.subtitleText.setDepth(10000);
+ this.subtitleText.setScrollFactor(0);
+ this.subtitleText.setVisible(false);
+
+ // Directional arrows
+ this.subtitleArrows.left = this.scene.add.text(
+ 50,
+ height - 100,
+ 'โ',
+ {
+ fontSize: '32px',
+ fontFamily: 'Arial',
+ color: '#ffff00'
+ }
+ );
+ this.subtitleArrows.left.setOrigin(0.5);
+ this.subtitleArrows.left.setDepth(10001);
+ this.subtitleArrows.left.setScrollFactor(0);
+ this.subtitleArrows.left.setVisible(false);
+
+ this.subtitleArrows.right = this.scene.add.text(
+ width - 50,
+ height - 100,
+ 'โบ',
+ {
+ fontSize: '32px',
+ fontFamily: 'Arial',
+ color: '#ffff00'
+ }
+ );
+ this.subtitleArrows.right.setOrigin(0.5);
+ this.subtitleArrows.right.setDepth(10001);
+ this.subtitleArrows.right.setScrollFactor(0);
+ this.subtitleArrows.right.setVisible(false);
+ }
+
+ // ========== VISUAL HEARTBEAT (LOW HEALTH) ==========
+
+ updateHeartbeat(healthPercent) {
+ if (!this.settings.heartbeatEnabled) return;
+
+ // Show heartbeat when health < 30%
+ if (healthPercent < 30) {
+ if (!this.heartbeatActive) {
+ this.startHeartbeat(healthPercent);
+ } else {
+ this.updateHeartbeatSpeed(healthPercent);
+ }
+ } else {
+ this.stopHeartbeat();
+ }
+ }
+
+ startHeartbeat(healthPercent) {
+ this.heartbeatActive = true;
+ this.heartbeatIndicator.setVisible(true);
+
+ // Calculate speed based on health (lower health = faster beat)
+ const speed = Phaser.Math.Clamp(1000 - (healthPercent * 20), 300, 1000);
+
+ this.heartbeatTween = this.scene.tweens.add({
+ targets: this.heartbeatIndicator,
+ scale: 1.3,
+ alpha: 0.6,
+ duration: speed / 2,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+
+ console.log('๐ Visual heartbeat started (health:', healthPercent + '%)');
+ }
+
+ updateHeartbeatSpeed(healthPercent) {
+ if (!this.heartbeatTween) return;
+
+ const speed = Phaser.Math.Clamp(1000 - (healthPercent * 20), 300, 1000);
+ this.heartbeatTween.updateTo('duration', speed / 2, true);
+ }
+
+ stopHeartbeat() {
+ if (!this.heartbeatActive) return;
+
+ this.heartbeatActive = false;
+ this.heartbeatIndicator.setVisible(false);
+
+ if (this.heartbeatTween) {
+ this.heartbeatTween.stop();
+ this.heartbeatTween = null;
+ }
+
+ this.heartbeatIndicator.setScale(1);
+ this.heartbeatIndicator.setAlpha(1);
+ }
+
+ // ========== DAMAGE DIRECTION INDICATOR ==========
+
+ showDamageIndicator(direction, damage) {
+ if (!this.settings.damageIndicatorEnabled) return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Calculate position based on direction
+ let x = width / 2;
+ let y = height / 2;
+ let arrow = 'โ';
+ let rotation = 0;
+
+ switch (direction) {
+ case 'up':
+ y = 100;
+ arrow = 'โ';
+ rotation = 0;
+ break;
+ case 'down':
+ y = height - 100;
+ arrow = 'โ';
+ rotation = Math.PI;
+ break;
+ case 'left':
+ x = 100;
+ arrow = 'โ';
+ rotation = -Math.PI / 2;
+ break;
+ case 'right':
+ x = width - 100;
+ arrow = 'โ';
+ rotation = Math.PI / 2;
+ break;
+ }
+
+ // Create damage indicator
+ const indicator = this.scene.add.text(x, y, arrow, {
+ fontSize: '64px',
+ color: '#ff0000',
+ fontStyle: 'bold'
+ });
+ indicator.setOrigin(0.5);
+ indicator.setDepth(10001);
+ indicator.setScrollFactor(0);
+ indicator.setRotation(rotation);
+
+ // Animate and destroy
+ this.scene.tweens.add({
+ targets: indicator,
+ alpha: 0,
+ scale: 1.5,
+ duration: 800,
+ ease: 'Power2',
+ onComplete: () => {
+ indicator.destroy();
+ }
+ });
+
+ console.log('๐ฏ Damage indicator shown:', direction, damage);
+ }
+
+ // ========== SCREEN FLASH NOTIFICATIONS ==========
+
+ showScreenFlash(type, message) {
+ if (!this.settings.screenFlashEnabled) return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ let color = 0xffffff;
+ let icon = 'โ ๏ธ';
+
+ switch (type) {
+ case 'danger':
+ color = 0xff0000;
+ icon = 'โ ๏ธ';
+ break;
+ case 'warning':
+ color = 0xffaa00;
+ icon = 'โก';
+ break;
+ case 'info':
+ color = 0x00aaff;
+ icon = 'โน๏ธ';
+ break;
+ case 'success':
+ color = 0x00ff00;
+ icon = 'โ';
+ break;
+ }
+
+ // Flash overlay
+ const flash = this.scene.add.rectangle(0, 0, width, height, color, 0.3);
+ flash.setOrigin(0);
+ flash.setDepth(9998);
+ flash.setScrollFactor(0);
+
+ // Icon
+ const iconText = this.scene.add.text(width / 2, height / 2, icon, {
+ fontSize: '128px'
+ });
+ iconText.setOrigin(0.5);
+ iconText.setDepth(9999);
+ iconText.setScrollFactor(0);
+
+ // Message
+ if (message) {
+ this.showSubtitle(message, 2000);
+ }
+
+ // Fade out
+ this.scene.tweens.add({
+ targets: [flash, iconText],
+ alpha: 0,
+ duration: 500,
+ ease: 'Power2',
+ onComplete: () => {
+ flash.destroy();
+ iconText.destroy();
+ }
+ });
+
+ console.log('โก Screen flash shown:', type, message);
+ }
+
+ // ========== SUBTITLES ==========
+
+ showSubtitle(text, duration = 3000, speaker = null, direction = null) {
+ if (!this.settings.subtitlesEnabled) return;
+
+ // Set subtitle text
+ this.subtitleText.setText(text);
+ this.subtitleText.setVisible(true);
+ this.subtitleBackground.setVisible(true);
+
+ // Show speaker name with color if enabled
+ if (speaker && this.settings.speakerNamesEnabled) {
+ const color = this.speakerColors[speaker] || '#ffffff';
+ this.subtitleSpeaker.setText(speaker);
+ this.subtitleSpeaker.setColor(color);
+ this.subtitleSpeaker.setVisible(true);
+ } else {
+ this.subtitleSpeaker.setVisible(false);
+ }
+
+ // Show directional arrows if enabled and direction provided
+ if (direction && this.settings.directionalArrowsEnabled) {
+ this.showDirectionalArrows(direction);
+ } else {
+ this.hideDirectionalArrows();
+ }
+
+ // Auto-hide after duration
+ this.scene.time.delayedCall(duration, () => {
+ this.hideSubtitle();
+ });
+
+ console.log('๐ฌ Subtitle shown:', speaker ? `[${speaker}] ${text}` : text);
+ }
+
+ showDirectionalArrows(direction) {
+ this.hideDirectionalArrows();
+
+ if (direction === 'left' || direction === 'west') {
+ this.subtitleArrows.left.setVisible(true);
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: this.subtitleArrows.left,
+ alpha: { from: 1, to: 0.3 },
+ duration: 500,
+ yoyo: true,
+ repeat: -1
+ });
+ } else if (direction === 'right' || direction === 'east') {
+ this.subtitleArrows.right.setVisible(true);
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: this.subtitleArrows.right,
+ alpha: { from: 1, to: 0.3 },
+ duration: 500,
+ yoyo: true,
+ repeat: -1
+ });
+ } else if (direction === 'both') {
+ this.subtitleArrows.left.setVisible(true);
+ this.subtitleArrows.right.setVisible(true);
+ // Pulse animation for both
+ this.scene.tweens.add({
+ targets: [this.subtitleArrows.left, this.subtitleArrows.right],
+ alpha: { from: 1, to: 0.3 },
+ duration: 500,
+ yoyo: true,
+ repeat: -1
+ });
+ }
+ }
+
+ hideDirectionalArrows() {
+ this.scene.tweens.killTweensOf(this.subtitleArrows.left);
+ this.scene.tweens.killTweensOf(this.subtitleArrows.right);
+ this.subtitleArrows.left.setVisible(false);
+ this.subtitleArrows.right.setVisible(false);
+ this.subtitleArrows.left.setAlpha(1);
+ this.subtitleArrows.right.setAlpha(1);
+ }
+
+ hideSubtitle() {
+ this.subtitleText.setVisible(false);
+ this.subtitleBackground.setVisible(false);
+ this.subtitleSpeaker.setVisible(false);
+ this.hideDirectionalArrows();
+ }
+
+ // ========== SOUND EVENT HANDLERS ==========
+
+ onSoundPlayed(soundType, data = {}) {
+ if (!this.enabled) return;
+
+ switch (soundType) {
+ case 'damage':
+ this.showDamageIndicator(data.direction || 'down', data.amount || 10);
+ this.showSubtitle('[DAMAGE TAKEN]', 1500, 'System', data.direction);
+ break;
+
+ case 'pickup':
+ this.showSubtitle(`[PICKED UP: ${data.item || 'Item'}]`, 1500, 'System');
+ break;
+
+ case 'harvest':
+ this.showSubtitle('[CROP HARVESTED]', 1500, 'System');
+ break;
+
+ case 'build':
+ this.showSubtitle('[BUILDING PLACED]', 1500, 'System');
+ break;
+
+ case 'dig':
+ this.showSubtitle('[DIGGING SOUND]', 1000, 'System');
+ break;
+
+ case 'plant':
+ this.showSubtitle('[PLANTING SOUND]', 1000, 'System');
+ break;
+
+ case 'footsteps':
+ this.showSubtitle('[FOOTSTEPS]', 500, null, data.direction);
+ break;
+
+ case 'door':
+ this.showSubtitle('[DOOR OPENS]', 1000, 'System');
+ break;
+
+ case 'chest':
+ this.showSubtitle('[CHEST OPENS]', 1000, 'System');
+ break;
+
+ case 'water':
+ this.showSubtitle('[WATER SPLASH]', 1000, 'System');
+ break;
+
+ case 'fire':
+ this.showSubtitle('[FIRE CRACKLING]', 2000, 'System');
+ break;
+
+ case 'explosion':
+ this.showSubtitle('[EXPLOSION!]', 1500, 'System');
+ this.showScreenFlash('danger', '[EXPLOSION!]');
+ break;
+
+ case 'npc_talk':
+ this.showSubtitle(data.text || '[NPC TALKING]', 3000, data.speaker || 'NPC', data.direction);
+ break;
+
+ case 'enemy_growl':
+ this.showSubtitle('[ENEMY GROWL]', 1500, 'Enemy', data.direction);
+ break;
+
+ case 'fishing_cast':
+ this.showSubtitle('[FISHING LINE CAST]', 1000, 'System');
+ break;
+
+ case 'fishing_bite':
+ this.showSubtitle('[FISH BITING!]', 1500, 'System');
+ this.showFishingBobberCue();
+ break;
+
+ case 'danger':
+ this.showScreenFlash('danger', '[DANGER!]');
+ this.showSubtitle('[DANGER NEARBY]', 2000, 'System');
+ break;
+
+ case 'night':
+ this.showScreenFlash('warning', '[NIGHT FALLING]');
+ this.showSubtitle('[NIGHT IS FALLING]', 2000, 'System');
+ break;
+
+ case 'achievement':
+ this.showScreenFlash('success', data.message || '[ACHIEVEMENT UNLOCKED]');
+ this.showSubtitle(data.message || '[ACHIEVEMENT UNLOCKED]', 3000, 'System');
+ break;
+
+ case 'ui_click':
+ this.showSubtitle('[CLICK]', 300, null);
+ break;
+
+ case 'ui_hover':
+ this.showSubtitle('[HOVER]', 200, null);
+ break;
+ }
+ }
+
+ /**
+ * Show fishing bobber visual cue
+ */
+ showFishingBobberCue() {
+ if (!this.settings.fishingBobberEnabled) return;
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Create bobber indicator if it doesn't exist
+ if (!this.fishingBobberIndicator) {
+ this.fishingBobberIndicator = this.scene.add.container(width / 2, height / 2);
+ this.fishingBobberIndicator.setDepth(10002);
+ this.fishingBobberIndicator.setScrollFactor(0);
+
+ // Circle background
+ const circle = this.scene.add.circle(0, 0, 60, 0xff6600, 0.8);
+ this.fishingBobberIndicator.add(circle);
+
+ // Exclamation mark
+ const exclamation = this.scene.add.text(0, 0, '!', {
+ fontSize: '48px',
+ fontFamily: 'Arial',
+ fontStyle: 'bold',
+ color: '#ffffff'
+ });
+ exclamation.setOrigin(0.5);
+ this.fishingBobberIndicator.add(exclamation);
+
+ // Text below
+ const text = this.scene.add.text(0, 80, 'FISH BITING!\nPress E', {
+ fontSize: '20px',
+ fontFamily: 'Arial',
+ fontStyle: 'bold',
+ color: '#ffffff',
+ align: 'center'
+ });
+ text.setOrigin(0.5);
+ this.fishingBobberIndicator.add(text);
+ }
+
+ // Show and animate
+ this.fishingBobberIndicator.setVisible(true);
+ this.fishingBobberIndicator.setAlpha(0);
+
+ // Fade in and pulse
+ this.scene.tweens.add({
+ targets: this.fishingBobberIndicator,
+ alpha: 1,
+ duration: 200,
+ onComplete: () => {
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: this.fishingBobberIndicator,
+ scale: { from: 1, to: 1.2 },
+ duration: 300,
+ yoyo: true,
+ repeat: 5,
+ onComplete: () => {
+ // Fade out
+ this.scene.tweens.add({
+ targets: this.fishingBobberIndicator,
+ alpha: 0,
+ duration: 300,
+ onComplete: () => {
+ this.fishingBobberIndicator.setVisible(false);
+ }
+ });
+ }
+ });
+ }
+ });
+
+ console.log('๐ฃ Fishing bobber cue shown');
+ }
+
+ /**
+ * Set subtitle background opacity
+ */
+ setSubtitleOpacity(opacity) {
+ this.settings.subtitleOpacity = Phaser.Math.Clamp(opacity, 0, 1);
+ if (this.subtitleBackground) {
+ this.subtitleBackground.setAlpha(this.settings.subtitleOpacity);
+ }
+ this.saveSettings();
+ console.log('๐ Subtitle opacity set to:', this.settings.subtitleOpacity);
+ }
+
+ /**
+ * Add custom speaker color
+ */
+ addSpeakerColor(speaker, color) {
+ this.speakerColors[speaker] = color;
+ console.log(`๐จ Added speaker color: ${speaker} = ${color}`);
+ }
+
+ // ========== SETTINGS ==========
+
+ toggleHeartbeat(enabled) {
+ this.settings.heartbeatEnabled = enabled;
+ if (!enabled) this.stopHeartbeat();
+ this.saveSettings();
+ }
+
+ toggleDamageIndicator(enabled) {
+ this.settings.damageIndicatorEnabled = enabled;
+ this.saveSettings();
+ }
+
+ toggleScreenFlash(enabled) {
+ this.settings.screenFlashEnabled = enabled;
+ this.saveSettings();
+ }
+
+ toggleSubtitles(enabled) {
+ this.settings.subtitlesEnabled = enabled;
+ if (!enabled) this.hideSubtitle();
+ this.saveSettings();
+ console.log('๐ฌ Subtitles:', enabled ? 'ENABLED' : 'DISABLED');
+ }
+
+ toggleDirectionalArrows(enabled) {
+ this.settings.directionalArrowsEnabled = enabled;
+ if (!enabled) {
+ this.hideDirectionalArrows();
+ }
+ this.saveSettings();
+ console.log('โก๏ธ Directional Arrows:', enabled ? 'ENABLED' : 'DISABLED');
+ }
+
+ toggleSpeakerNames(enabled) {
+ this.settings.speakerNamesEnabled = enabled;
+ this.saveSettings();
+ console.log('๐ค Speaker Names:', enabled ? 'ENABLED' : 'DISABLED');
+ }
+
+ toggleFishingBobber(enabled) {
+ this.settings.fishingBobberEnabled = enabled;
+ this.saveSettings();
+ console.log('๐ฃ Fishing Bobber Cue:', enabled ? 'ENABLED' : 'DISABLED');
+ }
+
+ /**
+ * Set subtitle text size
+ * @param {string} size - 'small', 'medium', 'large', 'very-large'
+ */
+ setSubtitleSize(size) {
+ if (!this.subtitleSizes[size]) {
+ console.error(`Invalid subtitle size: ${size}. Valid options: small, medium, large, very-large`);
+ return;
+ }
+
+ this.settings.subtitleSize = size;
+ const sizes = this.subtitleSizes[size];
+
+ // Update text sizes
+ if (this.subtitleText) {
+ this.subtitleText.setFontSize(sizes.main);
+ }
+ if (this.subtitleSpeaker) {
+ this.subtitleSpeaker.setFontSize(sizes.speaker);
+ }
+ if (this.subtitleArrows.left) {
+ this.subtitleArrows.left.setFontSize(sizes.arrow);
+ }
+ if (this.subtitleArrows.right) {
+ this.subtitleArrows.right.setFontSize(sizes.arrow);
+ }
+
+ // Adjust background height based on text size
+ if (this.subtitleBackground) {
+ const bgHeight = sizes.main * 4; // 4x font size for padding
+ this.subtitleBackground.setSize(this.subtitleBackground.width, bgHeight);
+ }
+
+ this.saveSettings();
+ console.log(`๐ Subtitle size set to: ${size.toUpperCase()} (${sizes.main}px)`);
+ }
+
+ saveSettings() {
+ localStorage.setItem('novafarma_visual_sound_cues', JSON.stringify(this.settings));
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_visual_sound_cues');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ // ========== UPDATE ==========
+
+ update() {
+ // Update heartbeat based on player health
+ if (this.scene.player && this.scene.player.health !== undefined) {
+ const healthPercent = (this.scene.player.health / this.scene.player.maxHealth) * 100;
+ this.updateHeartbeat(healthPercent);
+ }
+ }
+
+ destroy() {
+ if (this.heartbeatTween) this.heartbeatTween.stop();
+ if (this.heartbeatSprite) this.heartbeatSprite.destroy();
+ if (this.subtitleText) this.subtitleText.destroy();
+ if (this.subtitleBackground) this.subtitleBackground.destroy();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/VoiceoverSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/VoiceoverSystem.js
new file mode 100644
index 000000000..d520c1c5a
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/VoiceoverSystem.js
@@ -0,0 +1,353 @@
+/**
+ * VoiceoverSystem.js
+ * ==================
+ * KRVAVA ลฝETEV - Ana's Voice & Flashback Audio
+ *
+ * Features:
+ * - Ana's voice recordings for clues
+ * - Flashback narration
+ * - Emotional voiceovers
+ * - Audio positioning (3D sound)
+ * - Volume based on bond strength
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-25
+ */
+
+class VoiceoverSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Audio state
+ this.currentVoiceover = null;
+ this.voiceoverQueue = [];
+ this.isPlaying = false;
+
+ // Audio library (paths to audio files)
+ this.anaVoiceovers = {};
+ this.flashbackVoiceovers = {};
+
+ console.log('๐๏ธ VoiceoverSystem initialized');
+
+ this.registerVoiceovers();
+ }
+
+ /**
+ * Register all voiceover audio files
+ */
+ registerVoiceovers() {
+ // ANA'S CLUE VOICEOVERS (15 messages)
+ this.anaVoiceovers = {
+ msg_01: {
+ file: 'assets/audio/voiceover/ana_msg_01.mp3',
+ text: "Kai, if you find this... I'm sorry. They took me. Stay safe.",
+ duration: 5000,
+ emotion: 'scared'
+ },
+ msg_02: {
+ file: 'assets/audio/voiceover/ana_msg_02.mp3',
+ text: "I'm marking my trail with red flowers. Follow them west.",
+ duration: 4000,
+ emotion: 'determined'
+ },
+ msg_03: {
+ file: 'assets/audio/voiceover/ana_msg_03.mp3',
+ text: "These zombies... they're intelligent. I saw it in their eyes.",
+ duration: 5000,
+ emotion: 'curious'
+ },
+ msg_06: {
+ file: 'assets/audio/voiceover/ana_msg_06.mp3',
+ text: "Kai... I can feel you searching for me. Our bond is so strong.",
+ duration: 6000,
+ emotion: 'hopeful'
+ },
+ msg_09: {
+ file: 'assets/audio/voiceover/ana_msg_09.mp3',
+ text: "I found research on a cure! There's still hope for everyone!",
+ duration: 5000,
+ emotion: 'excited'
+ },
+ msg_10: {
+ file: 'assets/audio/voiceover/ana_msg_10.mp3',
+ text: "I dream of having a family someday. Children playing in safe fields...",
+ duration: 6000,
+ emotion: 'wistful'
+ },
+ msg_14: {
+ file: 'assets/audio/voiceover/ana_msg_14.mp3',
+ text: "Don't come for me, Kai. It's too dangerous. Please, save yourself!",
+ duration: 5000,
+ emotion: 'desperate'
+ },
+ msg_15: {
+ file: 'assets/audio/voiceover/ana_msg_15.mp3',
+ text: "I know you won't listen... you never do. I love you, brother.",
+ duration: 6000,
+ emotion: 'loving'
+ },
+
+ // SPECIAL: Ana's Journal Entry
+ ana_journal_01: {
+ file: 'assets/audio/voiceover/ana_journal_01.mp3',
+ text: "Day 147 in captivity. Dr. Krniฤ is forcing me to work on something terrible. A super virus using Chernobyl radiation. If it releases... everyone dies. I have to find a way to stop him.",
+ duration: 15000,
+ emotion: 'terrified'
+ },
+
+ // SPECIAL: Final Video Message
+ ana_final_message: {
+ file: 'assets/audio/voiceover/ana_final_message.mp3',
+ text: "Kai... I'm in Dr. Krniฤ's facility. Level 3, Vault 7. If you're watching this... don't come. It's a trap. But I know you will anyway. *laughs sadly* Be ready for the Troll King. I love you. Twin bond forever.",
+ duration: 20000,
+ emotion: 'resigned'
+ }
+ };
+
+ // FLASHBACK NARRATION
+ this.flashbackVoiceovers = {
+ twin_bond_discovery: {
+ file: 'assets/audio/voiceover/flashback_twin_bond.mp3',
+ text: "Dr. Chen: 'These twins have something special. A connection I've never seen before. They'll always find each other, no matter what.'",
+ duration: 10000,
+ emotion: 'wonder'
+ },
+ mother_death: {
+ file: 'assets/audio/voiceover/flashback_mother.mp3',
+ text: "Mama Elena: 'Kai... protect Ana. Always. Twin bond is your strength. Together... you can survive anything. I love you both...'",
+ duration: 15000,
+ emotion: 'heartbreaking'
+ },
+ kidnapping: {
+ file: 'assets/audio/voiceover/flashback_kidnapping.mp3',
+ text: "Ana's scream: 'KAIII! HELP! Please! KAIIIII!' *Kai's voice, desperate* 'ANA! NO! ANA!'",
+ duration: 8000,
+ emotion: 'traumatic'
+ }
+ };
+
+ console.log(`โ
Registered ${Object.keys(this.anaVoiceovers).length} Ana voiceovers`);
+ console.log(`โ
Registered ${Object.keys(this.flashbackVoiceovers).length} flashback voiceovers`);
+ }
+
+ /**
+ * Play Ana's voiceover for a clue
+ */
+ playAnaVoiceover(clueId, onComplete = null) {
+ const voiceover = this.anaVoiceovers[clueId];
+ if (!voiceover) {
+ console.warn(`โ ๏ธ No voiceover for clue: ${clueId}`);
+ return false;
+ }
+
+ console.log(`๐๏ธ Playing Ana's voice: ${clueId}`);
+
+ // Queue if already playing
+ if (this.isPlaying) {
+ this.voiceoverQueue.push({ type: 'ana', id: clueId, onComplete });
+ console.log(`โณ Voiceover queued (${this.voiceoverQueue.length} in queue)`);
+ return true;
+ }
+
+ this.isPlaying = true;
+
+ // Show subtitle text
+ this.showSubtitle(voiceover.text, voiceover.duration);
+
+ // Play audio (if file exists)
+ if (this.scene.sound.get(voiceover.file)) {
+ this.currentVoiceover = this.scene.sound.play(voiceover.file, {
+ volume: this.getVolumeBasedOnBondStrength()
+ });
+
+ // Cleanup when done
+ this.currentVoiceover.once('complete', () => {
+ this.onVoiceoverComplete(onComplete);
+ });
+ } else {
+ // Fallback if audio file doesn't exist
+ console.warn(`โ ๏ธ Audio file not found: ${voiceover.file}`);
+
+ // Simulate duration
+ setTimeout(() => {
+ this.onVoiceoverComplete(onComplete);
+ }, voiceover.duration);
+ }
+
+ // Trigger Twin Bond moment
+ if (this.scene.twinBondUI) {
+ this.scene.twinBondUI.triggerBondMoment('medium');
+ }
+
+ return true;
+ }
+
+ /**
+ * Play flashback narration
+ */
+ playFlashbackVoiceover(flashbackId, onComplete = null) {
+ const voiceover = this.flashbackVoiceovers[flashbackId];
+ if (!voiceover) {
+ console.warn(`โ ๏ธ No voiceover for flashback: ${flashbackId}`);
+ return false;
+ }
+
+ console.log(`๐๏ธ Playing flashback: ${flashbackId}`);
+
+ this.isPlaying = true;
+
+ // Show subtitle
+ this.showSubtitle(voiceover.text, voiceover.duration, '#FFD700'); // Gold color for flashbacks
+
+ // Play audio
+ if (this.scene.sound.get(voiceover.file)) {
+ this.currentVoiceover = this.scene.sound.play(voiceover.file, {
+ volume: 0.8
+ });
+
+ this.currentVoiceover.once('complete', () => {
+ this.onVoiceoverComplete(onComplete);
+ });
+ } else {
+ console.warn(`โ ๏ธ Audio file not found: ${voiceover.file}`);
+ setTimeout(() => {
+ this.onVoiceoverComplete(onComplete);
+ }, voiceover.duration);
+ }
+
+ return true;
+ }
+
+ /**
+ * Show subtitle text on screen
+ */
+ showSubtitle(text, duration, color = '#9370DB') {
+ const ui = this.scene.scene.get('UIScene') || this.scene;
+
+ // Create subtitle container
+ const subtitleBg = ui.add.rectangle(
+ ui.cameras.main.width / 2,
+ ui.cameras.main.height - 100,
+ ui.cameras.main.width - 200,
+ 80,
+ 0x000000,
+ 0.7
+ );
+ subtitleBg.setScrollFactor(0);
+ subtitleBg.setDepth(2000);
+
+ const subtitleText = ui.add.text(
+ ui.cameras.main.width / 2,
+ ui.cameras.main.height - 100,
+ text,
+ {
+ fontSize: '20px',
+ fontFamily: 'Arial',
+ color: color,
+ align: 'center',
+ wordWrap: { width: ui.cameras.main.width - 220 }
+ }
+ );
+ subtitleText.setOrigin(0.5);
+ subtitleText.setScrollFactor(0);
+ subtitleText.setDepth(2001);
+
+ // Fade in
+ subtitleBg.setAlpha(0);
+ subtitleText.setAlpha(0);
+
+ ui.tweens.add({
+ targets: [subtitleBg, subtitleText],
+ alpha: 1,
+ duration: 300
+ });
+
+ // Fade out and destroy
+ setTimeout(() => {
+ ui.tweens.add({
+ targets: [subtitleBg, subtitleText],
+ alpha: 0,
+ duration: 500,
+ onComplete: () => {
+ subtitleBg.destroy();
+ subtitleText.destroy();
+ }
+ });
+ }, duration - 500);
+ }
+
+ /**
+ * Get volume based on twin bond strength
+ */
+ getVolumeBasedOnBondStrength() {
+ if (!this.scene.twinBondUI) {
+ return 0.7; // Default volume
+ }
+
+ const bondStrength = this.scene.twinBondUI.getBondStrength();
+
+ // Higher bond = louder voice
+ const volume = 0.4 + (bondStrength / 100) * 0.6; // 0.4 to 1.0
+
+ return volume;
+ }
+
+ /**
+ * Voiceover completion handler
+ */
+ onVoiceoverComplete(callback) {
+ this.isPlaying = false;
+ this.currentVoiceover = null;
+
+ // Call completion callback
+ if (callback) {
+ callback();
+ }
+
+ // Play next in queue
+ if (this.voiceoverQueue.length > 0) {
+ const next = this.voiceoverQueue.shift();
+
+ setTimeout(() => {
+ if (next.type === 'ana') {
+ this.playAnaVoiceover(next.id, next.onComplete);
+ } else if (next.type === 'flashback') {
+ this.playFlashbackVoiceover(next.id, next.onComplete);
+ }
+ }, 500); // Small delay between voiceovers
+ }
+ }
+
+ /**
+ * Stop current voiceover
+ */
+ stopVoiceover() {
+ if (this.currentVoiceover) {
+ this.currentVoiceover.stop();
+ this.currentVoiceover = null;
+ }
+ this.isPlaying = false;
+ }
+
+ /**
+ * Clear queue
+ */
+ clearQueue() {
+ this.voiceoverQueue = [];
+ }
+
+ /**
+ * Check if voiceover is playing
+ */
+ isVoiceoverPlaying() {
+ return this.isPlaying;
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ this.stopVoiceover();
+ this.clearQueue();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/WaterPhysicsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/WaterPhysicsSystem.js
new file mode 100644
index 000000000..868c5ff6f
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/WaterPhysicsSystem.js
@@ -0,0 +1,202 @@
+/**
+ * WaterPhysicsSystem.js
+ *
+ * Handles player movement in water with realistic physics:
+ * - Movement drag (30% slower)
+ * - Buoyancy (upward drift)
+ * - Jump reduction
+ * - Hair floating effect (integrates with WindFoliageSystem)
+ * - Swimming animation trigger
+ *
+ * Style 32 Dark-Chibi Noir compatible
+ */
+
+class WaterPhysicsSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Physics constants
+ this.waterDragFactor = 0.7; // 30% slower movement
+ this.waterJumpReduction = 0.5; // 50% jump power
+ this.buoyancyForce = -15; // Upward drift (pixels/s)
+ this.hairFloatStrength = 1.5; // Hair rises more in water
+
+ // Water zones (areas with water)
+ this.waterZones = [];
+
+ // Player state
+ this.playerInWater = false;
+ this.waterDepth = 0;
+
+ console.log('๐ WaterPhysicsSystem: Initialized');
+ }
+
+ /**
+ * Add water zone
+ * @param {number} x - X position
+ * @param {number} y - Y position
+ * @param {number} width - Zone width
+ * @param {number} height - Zone height
+ * @param {number} depth - Water depth (0-100)
+ */
+ addWaterZone(x, y, width, height, depth = 50) {
+ const zone = this.scene.add.rectangle(x, y, width, height, 0x4682B4, 0.3);
+ zone.setOrigin(0, 0);
+ zone.setDepth(-10);
+
+ this.waterZones.push({
+ zone,
+ x, y, width, height, depth
+ });
+
+ console.log(`๐ Water zone added at (${x}, ${y}) - ${width}x${height}, depth: ${depth}`);
+ }
+
+ /**
+ * Check if player is in water
+ * @param {Object} player - Player object with x, y position
+ * @returns {number} Water depth (0 = no water, 100 = deep)
+ */
+ checkWaterDepth(player) {
+ if (!player) return 0;
+
+ for (const waterData of this.waterZones) {
+ const { x, y, width, height, depth } = waterData;
+
+ // Check if player is inside water zone
+ if (player.x >= x && player.x <= x + width &&
+ player.y >= y && player.y <= y + height) {
+ return depth;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Apply water physics to player
+ * @param {Object} player - Player object
+ * @param {number} delta - Time delta
+ */
+ applyWaterPhysics(player, delta) {
+ if (!player || !player.body) return;
+
+ // Check water depth
+ const depth = this.checkWaterDepth(player);
+ const wasInWater = this.playerInWater;
+ this.playerInWater = depth > 0;
+ this.waterDepth = depth;
+
+ if (this.playerInWater) {
+ // Apply movement drag
+ player.body.velocity.x *= this.waterDragFactor;
+ player.body.velocity.y *= this.waterDragFactor;
+
+ // Apply buoyancy (slow upward drift)
+ player.body.velocity.y += this.buoyancyForce * (delta / 1000);
+
+ // Reduce jump power if jumping in water
+ if (player.isJumping && player.body.velocity.y < 0) {
+ player.body.velocity.y *= this.waterJumpReduction;
+ }
+
+ // Trigger swimming animation
+ if (player.sprite && player.sprite.anims) {
+ const currentAnim = player.sprite.anims.currentAnim;
+ if (!currentAnim || currentAnim.key !== 'swim') {
+ // player.sprite.play('swim'); // Uncomment when swim animation exists
+ }
+ }
+
+ // Float hair upward (if WindFoliageSystem exists)
+ if (this.scene.weather && this.scene.weather.windSystem) {
+ this.floatHair(player, true);
+ }
+
+ // Entry splash effect (once)
+ if (!wasInWater) {
+ this.createSplash(player.x, player.y);
+ }
+
+ } else {
+ // Player left water
+ if (wasInWater) {
+ // Reset hair physics
+ if (this.scene.weather && this.scene.weather.windSystem) {
+ this.floatHair(player, false);
+ }
+ }
+ }
+ }
+
+ /**
+ * Make hair float upward in water
+ * @param {Object} player - Player object
+ * @param {boolean} float - Enable/disable floating
+ */
+ floatHair(player, float) {
+ if (!player.hairLayer) return;
+
+ const windSystem = this.scene.weather.windSystem;
+
+ windSystem.windAffectedLayers.forEach(layer => {
+ if (layer.sprite === player.hairLayer) {
+ layer.buoyantMode = float;
+ layer.floatStrength = float ? this.hairFloatStrength : 0;
+ }
+ });
+ }
+
+ /**
+ * Create splash effect when entering water
+ * @param {number} x - X position
+ * @param {number} y - Y position
+ */
+ createSplash(x, y) {
+ // Create ripples if WaterRipplesSystem exists
+ if (this.scene.waterRipples) {
+ this.scene.waterRipples.createSplashRipples(x, y);
+ }
+
+ // Splash particles (simple white particles)
+ const splash = this.scene.add.particles(x, y, 'particle_white', {
+ lifespan: 500,
+ speed: { min: 50, max: 150 },
+ angle: { min: 240, max: 300 },
+ scale: { start: 0.5, end: 0.1 },
+ alpha: { start: 0.8, end: 0 },
+ quantity: 10,
+ frequency: -1
+ });
+
+ splash.explode();
+
+ // Auto destroy
+ this.scene.time.delayedCall(500, () => {
+ splash.destroy();
+ });
+ }
+
+ /**
+ * Get player state info
+ * @returns {Object} State info
+ */
+ getPlayerState() {
+ return {
+ inWater: this.playerInWater,
+ depth: this.waterDepth,
+ dragFactor: this.playerInWater ? this.waterDragFactor : 1.0,
+ buoyancy: this.playerInWater ? this.buoyancyForce : 0
+ };
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ this.waterZones.forEach(data => {
+ if (data.zone) data.zone.destroy();
+ });
+ this.waterZones = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/WaterRipplesSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/WaterRipplesSystem.js
new file mode 100644
index 000000000..ec9074f3f
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/WaterRipplesSystem.js
@@ -0,0 +1,144 @@
+/**
+ * WaterRipplesSystem.js
+ *
+ * Creates realistic water ripple effects:
+ * - Footstep ripples (when walking in water)
+ * - Splash ripples (when falling/jumping into water)
+ * - Rain ripples (raindrops hitting water)
+ * - Expanding concentric circles
+ *
+ * Style 32 Dark-Chibi Noir - thick black outlines
+ */
+
+class WaterRipplesSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Ripple settings
+ this.rippleLifespan = 1500; // ms
+ this.maxConcurrentRipples = 20; // Performance limit
+
+ // Active ripples
+ this.activeRipples = [];
+
+ // Create ripple texture (Style 32 - concentric black circles)
+ this.createRippleTexture();
+
+ console.log('๐ง WaterRipplesSystem: Initialized');
+ }
+
+ /**
+ * Create ripple texture with Style 32 aesthetic
+ */
+ createRippleTexture() {
+ const graphics = this.scene.add.graphics();
+
+ // Style 32 - thick black outlines, multiple circles
+ graphics.lineStyle(3, 0x000000, 1); // Thick black line
+ graphics.strokeCircle(32, 32, 28);
+
+ graphics.lineStyle(2, 0x000000, 0.7);
+ graphics.strokeCircle(32, 32, 22);
+
+ graphics.lineStyle(1, 0x000000, 0.4);
+ graphics.strokeCircle(32, 32, 16);
+
+ // Generate texture
+ graphics.generateTexture('waterRipple', 64, 64);
+ graphics.destroy();
+
+ console.log('๐ง Ripple texture created (Style 32)');
+ }
+
+ /**
+ * Create single ripple
+ * @param {number} x - X position
+ * @param {number} y - Y position
+ * @param {number} size - Ripple size multiplier
+ * @param {number} speed - Expansion speed
+ */
+ createRipple(x, y, size = 1.0, speed = 1.0) {
+ // Performance check
+ if (this.activeRipples.length >= this.maxConcurrentRipples) {
+ return;
+ }
+
+ // Create ripple sprite
+ const ripple = this.scene.add.sprite(x, y, 'waterRipple');
+ ripple.setAlpha(0.8);
+ ripple.setScale(0.1 * size);
+ ripple.setDepth(-5); // Below most objects, above water
+
+ // Animate expansion
+ this.scene.tweens.add({
+ targets: ripple,
+ scaleX: 2.0 * size,
+ scaleY: 2.0 * size,
+ alpha: 0,
+ duration: this.rippleLifespan / speed,
+ ease: 'Quad.Out',
+ onComplete: () => {
+ ripple.destroy();
+ this.activeRipples = this.activeRipples.filter(r => r !== ripple);
+ }
+ });
+
+ this.activeRipples.push(ripple);
+ }
+
+ /**
+ * Create footstep ripple (small, subtle)
+ * @param {number} x
+ * @param {number} y
+ */
+ createFootstepRipple(x, y) {
+ this.createRipple(x, y, 0.5, 1.2);
+ }
+
+ /**
+ * Create splash ripples (multiple waves)
+ * @param {number} x
+ * @param {number} y
+ */
+ createSplashRipples(x, y) {
+ // Multiple ripples with delay
+ this.createRipple(x, y, 1.5, 0.8);
+
+ this.scene.time.delayedCall(100, () => {
+ this.createRipple(x, y, 1.8, 0.9);
+ });
+
+ this.scene.time.delayedCall(200, () => {
+ this.createRipple(x, y, 2.0, 1.0);
+ });
+ }
+
+ /**
+ * Create rain ripple (tiny, fast)
+ * @param {number} x
+ * @param {number} y
+ */
+ createRainRipple(x, y) {
+ this.createRipple(x, y, 0.3, 1.5);
+ }
+
+ /**
+ * Create object fall ripple (medium)
+ * @param {number} x
+ * @param {number} y
+ * @param {number} objectSize
+ */
+ createObjectRipple(x, y, objectSize = 1.0) {
+ this.createRipple(x, y, 1.0 * objectSize, 1.0);
+ }
+
+ /**
+ * Cleanup all ripples
+ */
+ destroy() {
+ this.activeRipples.forEach(ripple => {
+ if (ripple) ripple.destroy();
+ });
+ this.activeRipples = [];
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/WeatherEnhancementsSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/WeatherEnhancementsSystem.js
new file mode 100644
index 000000000..a704ff515
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/WeatherEnhancementsSystem.js
@@ -0,0 +1,215 @@
+// Weather Enhancements - Wind, transitions, tree sway
+class WeatherEnhancementsSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Wind system
+ this.windStrength = 0.5; // 0-1 scale
+ this.windDirection = 0; // angle in radians
+ this.windChangeTimer = 0;
+ this.windChangeDuration = 3000; // Change wind every 3 seconds
+
+ // Tree sway animations
+ this.swayingTrees = new Map(); // tree โ tween
+
+ // Weather transition
+ this.transitionDuration = 2000; // 2 seconds smooth transition
+ this.isTransitioning = false;
+
+ console.log('๐ฌ๏ธ WeatherEnhancementsSystem initialized');
+ }
+
+ // Update wind parameters
+ update(delta) {
+ this.updateWind(delta);
+ this.updateTreeSway(delta);
+ }
+
+ // Gradually change wind over time
+ updateWind(delta) {
+ this.windChangeTimer += delta;
+
+ if (this.windChangeTimer > this.windChangeDuration) {
+ this.windChangeTimer = 0;
+
+ // Smoothly change wind
+ const targetStrength = Math.random(); // 0-1
+ const targetDirection = Math.random() * Math.PI * 2; // 0-360ยฐ
+
+ // Tween wind strength
+ this.scene.tweens.add({
+ targets: this,
+ windStrength: targetStrength,
+ windDirection: targetDirection,
+ duration: 2000,
+ ease: 'Sine.easeInOut'
+ });
+ }
+ }
+
+ // Apply wind effect to rain particles
+ applyWindToRain(rainEmitter) {
+ if (!rainEmitter) return;
+
+ // Calculate wind offset for rain angle
+ const baseAngle = 270; // Straight down
+ const windAngle = this.windStrength * 30; // Max 30ยฐ deviation
+ const windDirectionDegrees = Phaser.Math.RadToDeg(this.windDirection);
+
+ // Apply horizontal wind speed
+ const windSpeedX = Math.cos(this.windDirection) * this.windStrength * 100;
+
+ // Update rain emitter config (if using Phaser 3.60+ particles)
+ try {
+ rainEmitter.setConfig({
+ speedX: { min: windSpeedX - 20, max: windSpeedX + 20 }
+ });
+ } catch (e) {
+ // Fallback for older Phaser versions
+ console.warn('Wind effect requires Phaser 3.60+');
+ }
+ }
+
+ // Make a tree sway in the wind
+ addSwayingTree(tree, treeSprite) {
+ if (!treeSprite || this.swayingTrees.has(tree)) return;
+
+ // Calculate sway amount based on wind
+ const maxSwayAngle = 2 + (this.windStrength * 3); // 2-5 degrees
+ const swaySpeed = 1500 - (this.windStrength * 500); // 1000-1500ms
+
+ // Create sway tween
+ const tween = this.scene.tweens.add({
+ targets: treeSprite,
+ angle: { from: -maxSwayAngle, to: maxSwayAngle },
+ duration: swaySpeed,
+ yoyo: true,
+ repeat: -1,
+ ease: 'Sine.easeInOut'
+ });
+
+ this.swayingTrees.set(tree, tween);
+ }
+
+ // Update tree sway based on current wind
+ updateTreeSway(delta) {
+ for (const [tree, tween] of this.swayingTrees) {
+ // Adjust tween speed based on wind strength
+ if (tween && tween.isPlaying()) {
+ // Dynamic sway intensity
+ const maxSwayAngle = 2 + (this.windStrength * 3);
+
+ // Update would require recreating tween
+ // For now, trees sway at initial speed
+ }
+ }
+ }
+
+ // Stop tree sway
+ removeSwayingTree(tree) {
+ const tween = this.swayingTrees.get(tree);
+ if (tween) {
+ tween.stop();
+ this.swayingTrees.delete(tree);
+ }
+ }
+
+ // Smooth weather transition
+ transitionWeather(fromWeather, toWeather, onComplete) {
+ if (this.isTransitioning) return;
+
+ this.isTransitioning = true;
+ console.log(`๐ค๏ธ Weather transitioning: ${fromWeather} โ ${toWeather}`);
+
+ // Get weather system
+ const weatherSystem = this.scene.weatherSystem;
+ if (!weatherSystem) {
+ this.isTransitioning = false;
+ if (onComplete) onComplete();
+ return;
+ }
+
+ // Fade out old weather
+ if (weatherSystem.rainEmitter) {
+ this.scene.tweens.add({
+ targets: weatherSystem.rainEmitter,
+ alpha: { from: 1, to: 0 },
+ duration: this.transitionDuration / 2,
+ onComplete: () => {
+ // Clear old weather
+ weatherSystem.clearWeather();
+
+ // Start new weather
+ if (toWeather === 'rain') {
+ weatherSystem.startRain(false);
+ } else if (toWeather === 'storm') {
+ weatherSystem.startRain(true);
+ }
+
+ // Fade in new weather
+ if (weatherSystem.rainEmitter) {
+ weatherSystem.rainEmitter.setAlpha(0);
+ this.scene.tweens.add({
+ targets: weatherSystem.rainEmitter,
+ alpha: { from: 0, to: 1 },
+ duration: this.transitionDuration / 2,
+ onComplete: () => {
+ this.isTransitioning = false;
+ if (onComplete) onComplete();
+ }
+ });
+ } else {
+ this.isTransitioning = false;
+ if (onComplete) onComplete();
+ }
+ }
+ });
+ } else {
+ // No old weather to fade out, just start new
+ if (toWeather === 'rain') {
+ weatherSystem.startRain(false);
+ } else if (toWeather === 'storm') {
+ weatherSystem.startRain(true);
+ }
+
+ this.isTransitioning = false;
+ if (onComplete) onComplete();
+ }
+ }
+
+ // Get current wind info (for UI or other systems)
+ getWindInfo() {
+ return {
+ strength: this.windStrength,
+ direction: this.windDirection,
+ speedKmh: Math.round(this.windStrength * 50), // 0-50 km/h
+ directionName: this.getWindDirectionName(this.windDirection)
+ };
+ }
+
+ // Convert wind direction to compass name
+ getWindDirectionName(radians) {
+ const degrees = Phaser.Math.RadToDeg(radians);
+ const normalized = ((degrees % 360) + 360) % 360;
+
+ if (normalized < 22.5 || normalized >= 337.5) return 'N';
+ if (normalized < 67.5) return 'NE';
+ if (normalized < 112.5) return 'E';
+ if (normalized < 157.5) return 'SE';
+ if (normalized < 202.5) return 'S';
+ if (normalized < 247.5) return 'SW';
+ if (normalized < 292.5) return 'W';
+ return 'NW';
+ }
+
+ // Clean up
+ destroy() {
+ // Stop all tree sway tweens
+ for (const [tree, tween] of this.swayingTrees) {
+ if (tween) tween.stop();
+ }
+ this.swayingTrees.clear();
+
+ console.log('๐ฌ๏ธ WeatherEnhancementsSystem destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/WeatherSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/WeatherSystem.js
new file mode 100644
index 000000000..c215e9462
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/WeatherSystem.js
@@ -0,0 +1,431 @@
+class WeatherSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // --- Time Configuration ---
+ this.fullDaySeconds = 300; // 5 minutes = 24 game hours
+ this.gameTime = 8; // Start at 8:00
+ this.dayCount = 1;
+
+ // --- Weather Configuration ---
+ this.currentWeather = 'clear';
+ this.weatherDuration = 0;
+ this.maxWeatherDuration = 10000; // Random duration logic handles this
+ this.rainEmitter = null; // Replaced manual Array with Emitter
+
+ // --- SEASONS Configuration ---
+ this.seasons = ['spring', 'summer', 'autumn', 'winter']; // Pomlad, Poletje, Jesen, Zima
+ this.currentSeason = 'spring';
+ this.daysPerSeason = 7; // Vsak letni ฤas traja 7 dni
+
+ // --- TEMPERATURE System ---
+ this.baseTemp = 20; // Celsius
+ this.currentTemp = 20;
+ this.tempCheckTimer = 0;
+ this.tempDamageInterval = 5000; // Damage every 5 seconds
+
+ // --- State ---
+ this.currentPhase = 'day';
+
+ this.init();
+ }
+
+ init() {
+ // Start weather cycle
+ this.changeWeather();
+ }
+
+ getOverlay() {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene && uiScene.overlayGraphics) {
+ return uiScene.overlayGraphics;
+ }
+ return null;
+ }
+
+ update(delta) {
+ // 1. Update Time
+ this.updateTime(delta);
+
+ // 2. Update Weather Logic (Durations, Changes)
+ this.updateWeatherLogic(delta);
+
+ // 2.5. Update Temperature System
+ this.updateTemperature(delta);
+
+ // 3. Update Physics (Rain drops)
+ this.updateWeatherPhysics(delta);
+
+ // 4. Render Atmosphere (Time + Weather)
+ this.render();
+ }
+
+ updateTime(delta) {
+ const seconds = delta / 1000;
+ const gameHoursPerRealSecond = 24 / this.fullDaySeconds;
+
+ this.gameTime += seconds * gameHoursPerRealSecond;
+
+ if (this.gameTime >= 24) {
+ this.gameTime -= 24;
+ this.dayCount++;
+ console.log(`๐ Day ${this.dayCount} started!`);
+
+ // Check for Season Change
+ this.checkSeasonChange();
+
+ // DEMO MODE: DISABLED - unlimited play
+ /*
+ if (this.dayCount > 3) {
+ this.triggerDemoEnd();
+ }
+ */
+ }
+
+ // Update Phase
+ const phase = this.getPhase(this.gameTime);
+ if (phase !== this.currentPhase) {
+ this.currentPhase = phase;
+ console.log(`๐
Time of Day: ${phase} (${Math.floor(this.gameTime)}:00)`);
+
+ // Trigger Bat Swarm at Dusk
+ if (phase === 'dusk' && this.scene.worldEventSystem) {
+ this.scene.worldEventSystem.spawnBatSwarm();
+ }
+ }
+
+ // Trigger Night Owl at Midnight (00:00)
+ // We check if seconds crossed 0.
+ if (Math.abs(this.gameTime - 0.0) < 0.05) {
+ // Only trigger once per night - check active flag or similar?
+ // Actually, gameTime will pass 0 very quickly but maybe multiple frames.
+ // We can use a flag resetting at day.
+ if (!this.owlTriggered && this.scene.worldEventSystem) {
+ this.scene.worldEventSystem.spawnNightOwl();
+ this.owlTriggered = true;
+ }
+ }
+ if (this.gameTime > 5) this.owlTriggered = false; // Reset flag at dawn
+
+ // Update UI Clock
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene && uiScene.clockText) {
+ const hours = Math.floor(this.gameTime);
+ const minutes = Math.floor((this.gameTime - hours) * 60);
+ const timeString = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
+ const seasonIcons = { spring: '๐ธ', summer: 'โ๏ธ', autumn: '๐', winter: 'โ๏ธ' };
+ const seasonIcon = seasonIcons[this.currentSeason] || '';
+ uiScene.clockText.setText(`${seasonIcon} Day ${this.dayCount} - ${timeString}`);
+ }
+ }
+
+ getPhase(hour) {
+ if (hour >= 5 && hour < 7) return 'dawn';
+ if (hour >= 7 && hour < 18) return 'day';
+ if (hour >= 18 && hour < 20) return 'dusk';
+ return 'night';
+ }
+
+ updateWeatherLogic(delta) {
+ this.weatherDuration += delta;
+ if (this.weatherDuration > this.maxWeatherDuration) {
+ this.changeWeather();
+ }
+ }
+
+ updateWeatherPhysics(delta) {
+ // Optimisation: Physics now handled by Phaser Particles
+ }
+
+ render() {
+ const overlay = this.getOverlay();
+ if (!overlay) return;
+
+ // IMPORTANT: Clear previous frame to avoid buildup
+ overlay.clear();
+
+ const width = this.scene.scale.width;
+ const height = this.scene.scale.height;
+
+ // --- LAYER 1: Day/Night Cycle ---
+ this.renderDayNight(overlay, width, height);
+
+ // --- LAYER 2: Weather Effects (Fog, Rain darkness) ---
+ this.renderWeatherEffects(overlay, width, height);
+
+ // --- LAYER 3: Season Tint ---
+ this.renderSeasonOverlay(overlay, width, height);
+ }
+
+ renderDayNight(overlay, width, height) {
+ const hour = this.gameTime;
+
+ let color = 0x000033; // Default night
+ let alpha = 0;
+
+ if (hour >= 0 && hour < 5) { // Deep Night
+ color = 0x000033; alpha = 0.6;
+ } else if (hour >= 5 && hour < 7) { // Dawn
+ color = 0xFF6B35;
+ const progress = (hour - 5) / 2;
+ alpha = 0.6 - (progress * 0.6);
+ } else if (hour >= 7 && hour < 18) { // Day
+ alpha = 0;
+ } else if (hour >= 18 && hour < 20) { // Dusk
+ color = 0x8B4789;
+ const progress = (hour - 18) / 2;
+ alpha = progress * 0.5;
+ } else if (hour >= 20 && hour < 24) { // Night
+ color = 0x000033;
+ const progress = (hour - 20) / 4;
+ alpha = 0.5 + (progress * 0.1);
+ }
+
+ if (alpha > 0) {
+ overlay.fillStyle(color, alpha);
+ overlay.fillRect(0, 0, width, height);
+ }
+ }
+
+ renderWeatherEffects(overlay, width, height) {
+ // Fog
+ if (this.currentWeather === 'fog') {
+ overlay.fillStyle(0xcccccc, 0.4);
+ overlay.fillRect(0, 0, width, height);
+ }
+
+ // Rain / Storm
+ if (this.currentWeather === 'rain' || this.currentWeather === 'storm') {
+ const isDark = this.currentWeather === 'storm' ? 0.3 : 0.2;
+
+ // Additive darkness for storm
+ overlay.fillStyle(0x000033, isDark);
+ overlay.fillRect(0, 0, width, height);
+
+ // Rain drops are now particles (separate GameObject)
+ }
+ }
+
+ changeWeather() {
+ this.clearWeather();
+
+ const rand = Math.random();
+ if (rand < 0.6) {
+ this.currentWeather = 'clear';
+ this.maxWeatherDuration = Phaser.Math.Between(10000, 30000);
+ } else if (rand < 0.8) {
+ this.currentWeather = 'rain';
+ this.startRain(false);
+ this.maxWeatherDuration = Phaser.Math.Between(10000, 20000);
+ } else if (rand < 0.9) {
+ this.currentWeather = 'storm';
+ this.startRain(true);
+ this.maxWeatherDuration = Phaser.Math.Between(8000, 15000);
+ } else {
+ this.currentWeather = 'fog';
+ this.maxWeatherDuration = Phaser.Math.Between(10000, 20000);
+ }
+
+ this.weatherDuration = 0;
+ console.log(`Weather changed to: ${this.currentWeather}`);
+ }
+
+ startRain(heavy) {
+ const uiScene = this.scene.scene.get('UIScene');
+ if (!uiScene) return;
+
+ // Check Settings
+ const quality = this.scene.settings ? this.scene.settings.particles : 'HIGH';
+ if (quality === 'NONE') return;
+
+ // Ensure texture exists
+ if (!this.scene.textures.exists('rain_drop')) {
+ const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false });
+ graphics.fillStyle(0x88aaff, 1);
+ graphics.fillRect(0, 0, 2, 8);
+ graphics.generateTexture('rain_drop', 2, 8);
+ graphics.destroy();
+ }
+
+ // Clean up old
+ if (this.rainEmitter) {
+ this.rainEmitter.destroy();
+ }
+
+ const width = this.scene.scale.width;
+
+ let pQuantity = heavy ? 5 : 2;
+ let pFreq = heavy ? 10 : 30;
+
+ if (quality === 'LOW') {
+ pQuantity = heavy ? 2 : 1;
+ pFreq = heavy ? 50 : 100;
+ }
+
+ // Use modern particles or fallback
+ // Helper to support both if needed, but standard 3.60+ is:
+ this.rainEmitter = uiScene.add.particles(0, 0, 'rain_drop', {
+ x: { min: 0, max: width },
+ y: -20,
+ quantity: pQuantity,
+ frequency: pFreq, // Emit every X ms
+ lifespan: 1500,
+ speedY: { min: heavy ? 600 : 400, max: heavy ? 900 : 600 },
+ speedX: { min: -50, max: 0 }, // Slight wind
+ scaleY: { min: 1.0, max: 2.0 },
+ alpha: { start: 0.6, end: 0 },
+ emitting: true
+ });
+
+ // Depth just above overlay (-1000)
+ this.rainEmitter.setDepth(-990);
+
+ // Play Sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.playRainSound();
+ }
+ }
+
+ clearWeather() {
+ if (this.rainEmitter) {
+ this.rainEmitter.destroy();
+ this.rainEmitter = null;
+ }
+
+ // Stop Sound
+ if (this.scene.soundManager) {
+ this.scene.soundManager.stopRainSound();
+ }
+ }
+
+ // --- Getters for Other Systems ---
+ getCurrentHour() {
+ return Math.floor(this.gameTime);
+ }
+
+ getDayCount() {
+ return this.dayCount;
+ }
+
+ isNight() {
+ const hour = this.gameTime;
+ return hour >= 20 || hour < 5;
+ }
+
+ isDay() {
+ const hour = this.gameTime;
+ return hour >= 7 && hour < 18;
+ }
+
+ isHordeNight() {
+ // Every 3rd night is a Horde Night
+ return this.dayCount > 0 && this.dayCount % 3 === 0;
+ }
+
+ checkSeasonChange() {
+ const seasonIndex = Math.floor((this.dayCount - 1) / this.daysPerSeason) % 4;
+ const newSeason = this.seasons[seasonIndex];
+
+ if (newSeason !== this.currentSeason) {
+ this.currentSeason = newSeason;
+ const seasonNames = { spring: '๐ธ Pomlad', summer: 'โ๏ธ Poletje', autumn: '๐ Jesen', winter: 'โ๏ธ Zima' };
+ console.log(`๐ Letni ฤas: ${seasonNames[newSeason]}`);
+ }
+ }
+
+ getSeason() {
+ return this.currentSeason;
+ }
+
+ getSeasonColor() {
+ // Barve/overlay za letne ฤase
+ const colors = {
+ spring: { color: 0x90EE90, alpha: 0.1 }, // Svetlo zelena
+ summer: { color: 0xFFFF00, alpha: 0.05 }, // Rumena
+ autumn: { color: 0xFF8C00, alpha: 0.15 }, // Oranลพna
+ winter: { color: 0xE0F7FF, alpha: 0.25 } // Belo modra
+ };
+ return colors[this.currentSeason] || { color: 0xFFFFFF, alpha: 0 };
+ }
+
+ renderSeasonOverlay(overlay, width, height) {
+ const seasonData = this.getSeasonColor();
+ if (seasonData.alpha > 0) {
+ overlay.fillStyle(seasonData.color, seasonData.alpha);
+ overlay.fillRect(0, 0, width, height);
+ }
+ }
+
+ updateTemperature(delta) {
+ // Calculate base temperature from season
+ const seasonTemps = {
+ spring: 15,
+ summer: 30,
+ autumn: 10,
+ winter: -5
+ };
+
+ this.baseTemp = seasonTemps[this.currentSeason] || 20;
+
+ // Time of day variation
+ const timeVar = Math.sin((this.gameTime / 24) * Math.PI * 2) * 5;
+ this.currentTemp = this.baseTemp + timeVar;
+
+ // Temperature damage logic
+ this.tempCheckTimer += delta;
+ if (this.tempCheckTimer >= this.tempDamageInterval) {
+ this.tempCheckTimer = 0;
+ this.checkTemperatureDamage();
+ }
+ }
+
+ checkTemperatureDamage() {
+ if (!this.scene.player) return;
+
+ const player = this.scene.player;
+ const inventory = this.scene.inventorySystem;
+
+ // Check for winter coat protection
+ const hasWinterCoat = inventory && inventory.hasItem('winter_coat');
+ const hasSummerHat = inventory && inventory.hasItem('summer_hat');
+
+ // Cold damage (Winter)
+ if (this.currentTemp < 0 && !hasWinterCoat) {
+ player.takeDamage(5);
+ this.scene.events.emit('show-floating-text', {
+ x: player.sprite.x,
+ y: player.sprite.y - 40,
+ text: 'โ๏ธ Freezing!',
+ color: '#88D4FF'
+ });
+ }
+
+ // Heat damage (Summer)
+ if (this.currentTemp > 35 && !hasSummerHat) {
+ player.takeDamage(3);
+ this.scene.events.emit('show-floating-text', {
+ x: player.sprite.x,
+ y: player.sprite.y - 40,
+ text: '๐ฅ Overheating!',
+ color: '#FF8844'
+ });
+ }
+ }
+
+ getTemperature() {
+ return Math.round(this.currentTemp);
+ }
+
+ triggerDemoEnd() {
+ console.log('๐ฎ DEMO END - 3 Days Completed!');
+
+ // Pause game
+ this.scene.physics.pause();
+
+ // Show demo end screen
+ const uiScene = this.scene.scene.get('UIScene');
+ if (uiScene) {
+ uiScene.showDemoEndScreen();
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/WindFoliageSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/WindFoliageSystem.js
new file mode 100644
index 000000000..e7131b6b7
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/WindFoliageSystem.js
@@ -0,0 +1,375 @@
+/**
+ * WindFoliageSystem.js
+ *
+ * Dynamic Wind & Foliage System for Style 32 Dark-Chibi Noir
+ *
+ * Features:
+ * - Vertex Shader for hair (Kai, Ana, Gronk dreads)
+ * - Grass/foliage wave animation using Perlin Noise
+ * - Particle Emitter for falling leaves from trees
+ * - Biome-specific wind strength
+ * - Wobble physics for natural movement
+ *
+ * Performance: Uses shaders instead of thousands of sprites for RAM optimization
+ */
+
+export default class WindFoliageSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Global wind settings
+ this.wind = {
+ strength: 1.0, // Default wind strength (0.0 - 2.0)
+ direction: 0, // Wind direction in radians
+ speed: 0.5, // Wind animation speed
+ frequency: 2.0, // Perlin noise frequency
+ amplitude: 8.0 // Maximum displacement in pixels
+ };
+
+ // Biome-specific wind configurations
+ this.biomeWindSettings = {
+ 'grassland': { strength: 1.0, frequency: 2.0 },
+ 'mountains': { strength: 2.0, frequency: 3.5 },
+ 'swamp': { strength: 0.3, frequency: 1.0 },
+ 'forest': { strength: 0.8, frequency: 2.5 },
+ 'desert': { strength: 1.5, frequency: 2.8 },
+ 'tundra': { strength: 1.8, frequency: 3.0 },
+ 'volcanic': { strength: 1.2, frequency: 2.0 }
+ };
+
+ // Particle systems for leaves
+ this.leafEmitters = [];
+
+ // Hair/foliage layers with wind effect
+ this.windAffectedLayers = [];
+
+ // Time accumulator for noise
+ this.time = 0;
+ }
+
+ /**
+ * Initialize the wind system
+ */
+ init() {
+ console.log('๐ฌ๏ธ WindFoliageSystem: Initializing...');
+
+ // Create wind shader pipeline
+ this.createWindShader();
+
+ // Initialize leaf particle system
+ this.initLeafParticles();
+
+ console.log('โ
WindFoliageSystem: Ready!');
+ }
+
+ /**
+ * Create custom wind shader for vertex displacement
+ */
+ createWindShader() {
+ // Vertex shader code for wind effect
+ const vertexShader = `
+ precision mediump float;
+
+ uniform mat4 uProjectionMatrix;
+ uniform mat4 uViewMatrix;
+ uniform float uTime;
+ uniform float uWindStrength;
+ uniform float uWindFrequency;
+ uniform float uWindAmplitude;
+ uniform vec2 uWindDirection;
+
+ attribute vec2 inPosition;
+ attribute vec2 inTexCoord;
+
+ varying vec2 vTexCoord;
+
+ // Perlin-like noise function (simplified for performance)
+ float noise(vec2 p) {
+ return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
+ }
+
+ float smoothNoise(vec2 p) {
+ vec2 i = floor(p);
+ vec2 f = fract(p);
+ f = f * f * (3.0 - 2.0 * f); // Smoothstep
+
+ float a = noise(i);
+ float b = noise(i + vec2(1.0, 0.0));
+ float c = noise(i + vec2(0.0, 1.0));
+ float d = noise(i + vec2(1.0, 1.0));
+
+ return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
+ }
+
+ float perlinNoise(vec2 p) {
+ float total = 0.0;
+ float frequency = 1.0;
+ float amplitude = 1.0;
+ float maxValue = 0.0;
+
+ for(int i = 0; i < 4; i++) {
+ total += smoothNoise(p * frequency) * amplitude;
+ maxValue += amplitude;
+ amplitude *= 0.5;
+ frequency *= 2.0;
+ }
+
+ return total / maxValue;
+ }
+
+ void main() {
+ vTexCoord = inTexCoord;
+
+ // Apply wind displacement based on vertex height (Y position)
+ // Higher vertices (like hair tips) move more
+ float heightFactor = inTexCoord.y; // 0.0 at bottom, 1.0 at top
+
+ // Calculate Perlin noise for natural wind
+ vec2 noiseInput = inPosition * 0.01 * uWindFrequency + vec2(uTime * 0.3, uTime * 0.2);
+ float windOffset = perlinNoise(noiseInput);
+
+ // Apply wind displacement
+ vec2 displacement = uWindDirection * windOffset * uWindStrength * uWindAmplitude * heightFactor;
+
+ vec2 finalPosition = inPosition + displacement;
+
+ gl_Position = uProjectionMatrix * uViewMatrix * vec4(finalPosition, 0.0, 1.0);
+ }
+ `;
+
+ // Fragment shader (simple passthrough with texture)
+ const fragmentShader = `
+ precision mediump float;
+
+ uniform sampler2D uMainSampler;
+ varying vec2 vTexCoord;
+
+ void main() {
+ gl_FragColor = texture2D(uMainSampler, vTexCoord);
+ }
+ `;
+
+ // Store shader code for later use
+ this.windShader = {
+ vertex: vertexShader,
+ fragment: fragmentShader
+ };
+
+ console.log('๐จ Wind shader created');
+ }
+
+ /**
+ * Apply wind effect to a sprite layer (hair, grass, etc.)
+ * @param {Phaser.GameObjects.Sprite} sprite - The sprite to affect
+ * @param {string} layerType - Type: 'hair', 'grass', 'foliage'
+ */
+ applyWindToSprite(sprite, layerType = 'grass') {
+ if (!sprite) return;
+
+ // Store reference
+ this.windAffectedLayers.push({
+ sprite: sprite,
+ type: layerType,
+ offsetX: 0,
+ offsetY: 0,
+ baseX: sprite.x,
+ baseY: sprite.y
+ });
+
+ console.log(`๐ฌ๏ธ Wind applied to ${layerType} layer`);
+ }
+
+ /**
+ * Initialize falling leaf particle system
+ */
+ initLeafParticles() {
+ // Configuration for different leaf types
+ this.leafConfig = {
+ lifespan: 5000, // 5 seconds fall time
+ speed: { min: 20, max: 50 },
+ gravityY: 100,
+ rotate: { min: -180, max: 180 },
+ wobbleStrength: 30, // Horizontal wobble
+ colors: [0x8B4513, 0xA0522D, 0xD2691E, 0x654321] // Brown tones
+ };
+
+ console.log('๐ Leaf particle system initialized');
+ }
+
+ /**
+ * Create leaf emitter for a tree
+ * @param {number} x - Tree X position
+ * @param {number} y - Tree Y position
+ * @param {number} emitRate - Leaves per second
+ */
+ createLeafEmitter(x, y, emitRate = 0.5) {
+ const particles = this.scene.add.particles(x, y);
+
+ // Create simple leaf graphics (can be replaced with sprite)
+ const leafGraphics = this.scene.add.graphics();
+ leafGraphics.fillStyle(0x8B4513, 1);
+ leafGraphics.fillEllipse(0, 0, 8, 12);
+ leafGraphics.generateTexture('leaf', 8, 12);
+ leafGraphics.destroy();
+
+ const emitter = particles.createEmitter({
+ frame: 'leaf',
+ lifespan: this.leafConfig.lifespan,
+ speed: this.leafConfig.speed,
+ gravityY: this.leafConfig.gravityY,
+ rotate: this.leafConfig.rotate,
+ frequency: 1000 / emitRate, // Convert to milliseconds
+ scale: { start: 1, end: 0.8 },
+ alpha: { start: 1, end: 0.3 },
+ tint: Phaser.Utils.Array.GetRandom(this.leafConfig.colors)
+ });
+
+ this.leafEmitters.push({
+ particles: particles,
+ emitter: emitter,
+ x: x,
+ y: y
+ });
+
+ return emitter;
+ }
+
+ /**
+ * Set wind strength (affects all wind effects)
+ * @param {number} strength - Wind strength (0.0 - 2.0)
+ */
+ setWindStrength(strength) {
+ this.wind.strength = Phaser.Math.Clamp(strength, 0, 2.0);
+ console.log(`๐ฌ๏ธ Wind strength set to: ${this.wind.strength.toFixed(2)}`);
+ }
+
+ /**
+ * Apply biome-specific wind settings
+ * @param {string} biomeName - Name of the biome
+ */
+ setBiomeWind(biomeName) {
+ const settings = this.biomeWindSettings[biomeName.toLowerCase()];
+
+ if (settings) {
+ this.wind.strength = settings.strength;
+ this.wind.frequency = settings.frequency;
+ console.log(`๐ Wind adjusted for ${biomeName}: strength=${settings.strength}, frequency=${settings.frequency}`);
+ } else {
+ console.warn(`โ ๏ธ Unknown biome: ${biomeName}, using default wind`);
+ }
+ }
+
+ /**
+ * Perlin noise implementation (for CPU-side calculations)
+ */
+ perlinNoise(x, y) {
+ const xi = Math.floor(x);
+ const yi = Math.floor(y);
+ const xf = x - xi;
+ const yf = y - yi;
+
+ // Simple hash function
+ const hash = (x, y) => {
+ const h = Math.sin(x * 12.9898 + y * 78.233) * 43758.5453;
+ return h - Math.floor(h);
+ };
+
+ const a = hash(xi, yi);
+ const b = hash(xi + 1, yi);
+ const c = hash(xi, yi + 1);
+ const d = hash(xi + 1, yi + 1);
+
+ // Smoothstep interpolation
+ const u = xf * xf * (3 - 2 * xf);
+ const v = yf * yf * (3 - 2 * yf);
+
+ return Phaser.Math.Linear(
+ Phaser.Math.Linear(a, b, u),
+ Phaser.Math.Linear(c, d, u),
+ v
+ );
+ }
+
+ /**
+ * Update wind effect (call every frame)
+ * @param {number} delta - Time since last frame in ms
+ */
+ update(delta) {
+ this.time += delta * 0.001; // Convert to seconds
+
+ // Update all wind-affected sprites
+ this.windAffectedLayers.forEach(layer => {
+ const { sprite, type, baseX, baseY } = layer;
+
+ if (!sprite || !sprite.active) return;
+
+ // Calculate Perlin noise for smooth wind
+ const noiseX = baseX * 0.01 * this.wind.frequency + this.time * this.wind.speed;
+ const noiseY = baseY * 0.01 * this.wind.frequency + this.time * this.wind.speed * 0.7;
+
+ const windNoise = this.perlinNoise(noiseX, noiseY);
+
+ // Apply displacement
+ const displacement = windNoise * this.wind.strength * this.wind.amplitude;
+
+ // Different displacement for different layer types
+ if (type === 'hair') {
+ // Hair sways more at the tips
+ sprite.x = baseX + displacement * 1.5;
+ sprite.angle = displacement * 0.5; // Slight rotation
+ } else if (type === 'grass') {
+ // Grass bends from the base
+ sprite.x = baseX + displacement;
+ } else {
+ // Generic foliage
+ sprite.x = baseX + displacement * 0.8;
+ }
+ });
+
+ // Update leaf particles with wobble
+ this.leafEmitters.forEach(emitterData => {
+ if (emitterData.emitter) {
+ // Add horizontal wobble to falling leaves
+ const wobble = Math.sin(this.time * 2) * this.leafConfig.wobbleStrength * this.wind.strength;
+ emitterData.emitter.setSpeedX({ min: wobble - 10, max: wobble + 10 });
+ }
+ });
+ }
+
+ /**
+ * Debug: Show wind strength indicator
+ */
+ showWindDebug() {
+ if (!this.windDebugText) {
+ this.windDebugText = this.scene.add.text(10, 10, '', {
+ font: '16px Arial',
+ fill: '#ffffff',
+ stroke: '#000000',
+ strokeThickness: 4
+ }).setScrollFactor(0).setDepth(10000);
+ }
+
+ this.windDebugText.setText([
+ `๐ฌ๏ธ Wind System`,
+ `Strength: ${this.wind.strength.toFixed(2)}`,
+ `Frequency: ${this.wind.frequency.toFixed(2)}`,
+ `Layers: ${this.windAffectedLayers.length}`,
+ `Emitters: ${this.leafEmitters.length}`
+ ]);
+ }
+
+ /**
+ * Cleanup
+ */
+ destroy() {
+ this.windAffectedLayers = [];
+ this.leafEmitters.forEach(e => {
+ if (e.particles) e.particles.destroy();
+ });
+ this.leafEmitters = [];
+
+ if (this.windDebugText) {
+ this.windDebugText.destroy();
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/WorkerCreaturesSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/WorkerCreaturesSystem.js
new file mode 100644
index 000000000..a3927ad10
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/WorkerCreaturesSystem.js
@@ -0,0 +1,444 @@
+/**
+ * WORKER CREATURES SYSTEM
+ * Specialized creature workers with unique abilities
+ */
+class WorkerCreaturesSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Worker creatures
+ this.workers = new Map();
+
+ // Creature types
+ this.creatureTypes = new Map();
+
+ // Active tasks
+ this.activeTasks = [];
+
+ this.init();
+ console.log('โ
Worker Creatures System initialized');
+ }
+
+ init() {
+ this.defineCreatureTypes();
+ console.log('๐ฆ Worker creatures ready');
+ }
+
+ // ========== CREATURE TYPES ==========
+
+ defineCreatureTypes() {
+ // Donkey - Transport specialist
+ this.defineCreature('donkey', {
+ name: 'Donkey',
+ specialty: 'transport',
+ efficiency: 0.8,
+ abilities: ['carry_items', 'cart_transport', 'long_distance'],
+ carryCapacity: 50,
+ speed: 1.2,
+ tamingDifficulty: 'easy',
+ cost: { carrot: 10, apple: 5 }
+ });
+
+ // Bigfoot - Forest gathering specialist
+ this.defineCreature('bigfoot', {
+ name: 'Bigfoot',
+ specialty: 'gathering',
+ efficiency: 1.0,
+ abilities: ['tree_chopping', 'berry_picking', 'mushroom_finding', 'forest_navigation'],
+ gatherBonus: 1.5,
+ speed: 0.9,
+ tamingDifficulty: 'medium',
+ cost: { honey: 5, berries: 20 }
+ });
+
+ // Yeti - Snow biome specialist
+ this.defineCreature('yeti', {
+ name: 'Yeti',
+ specialty: 'snow_tasks',
+ efficiency: 1.2,
+ abilities: ['ice_mining', 'snow_clearing', 'cold_resistance', 'ice_fishing'],
+ coldBonus: 2.0,
+ speed: 0.8,
+ tamingDifficulty: 'hard',
+ cost: { frozen_meat: 10, ice_crystal: 3 }
+ });
+
+ // Elf - Crafting specialist
+ this.defineCreature('elf', {
+ name: 'Elf',
+ specialty: 'crafting',
+ efficiency: 1.5,
+ abilities: ['auto_craft', 'enchanting', 'potion_making', 'tool_repair'],
+ craftingSpeed: 2.0,
+ speed: 1.0,
+ tamingDifficulty: 'hard',
+ cost: { magic_dust: 5, golden_apple: 2 }
+ });
+
+ // Gnome - Mining specialist
+ this.defineCreature('gnome', {
+ name: 'Gnome',
+ specialty: 'mining',
+ efficiency: 1.3,
+ abilities: ['ore_detection', 'tunnel_digging', 'gem_finding', 'cave_navigation'],
+ miningBonus: 1.8,
+ speed: 0.7,
+ tamingDifficulty: 'medium',
+ cost: { gold_nugget: 5, diamond: 1 }
+ });
+
+ // Fairy - Plant care specialist
+ this.defineCreature('fairy', {
+ name: 'Fairy',
+ specialty: 'plant_care',
+ efficiency: 1.4,
+ abilities: ['instant_growth', 'disease_cure', 'crop_blessing', 'flower_magic'],
+ growthBonus: 2.5,
+ speed: 1.5,
+ tamingDifficulty: 'very_hard',
+ cost: { rainbow_flower: 3, fairy_dust: 10 }
+ });
+
+ // Golem - Heavy labor specialist
+ this.defineCreature('golem', {
+ name: 'Golem',
+ specialty: 'heavy_labor',
+ efficiency: 0.9,
+ abilities: ['boulder_moving', 'building_construction', 'land_clearing', 'defense'],
+ strengthBonus: 3.0,
+ speed: 0.5,
+ tamingDifficulty: 'very_hard',
+ cost: { stone: 100, iron: 50, magic_core: 1 }
+ });
+
+ // Dragon - Ultimate worker
+ this.defineCreature('dragon', {
+ name: 'Dragon',
+ specialty: 'all',
+ efficiency: 2.0,
+ abilities: ['flight', 'fire_breath', 'treasure_finding', 'all_tasks'],
+ allBonus: 2.0,
+ speed: 2.0,
+ tamingDifficulty: 'legendary',
+ cost: { dragon_egg: 1, legendary_meat: 10, gold: 1000 }
+ });
+ }
+
+ defineCreature(id, data) {
+ this.creatureTypes.set(id, {
+ id,
+ ...data
+ });
+ }
+
+ // ========== TAMING ==========
+
+ canTame(creatureType) {
+ const creature = this.creatureTypes.get(creatureType);
+ if (!creature) return false;
+
+ // Check if player has required items
+ if (!this.scene.inventorySystem) return false;
+
+ for (const [item, amount] of Object.entries(creature.cost)) {
+ const has = this.scene.inventorySystem.getItemCount(item);
+ if (has < amount) {
+ console.log(`โ Missing ${item}: ${has}/${amount}`);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ tameCreature(creatureType, x, y) {
+ if (!this.canTame(creatureType)) return false;
+
+ const creatureData = this.creatureTypes.get(creatureType);
+
+ // Consume taming items
+ for (const [item, amount] of Object.entries(creatureData.cost)) {
+ this.scene.inventorySystem.removeItem(item, amount);
+ }
+
+ // Create worker
+ const worker = {
+ id: `worker_${creatureType}_${Date.now()}`,
+ type: creatureType,
+ name: creatureData.name,
+ specialty: creatureData.specialty,
+ efficiency: creatureData.efficiency,
+ abilities: creatureData.abilities,
+ x, y,
+ currentTask: null,
+ level: 1,
+ xp: 0,
+ loyalty: 50,
+ sprite: null
+ };
+
+ this.workers.set(worker.id, worker);
+
+ // Notify automation tier system
+ if (this.scene.automationTiers) {
+ this.scene.automationTiers.befriendCreature();
+ }
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(x, y);
+ this.scene.visualEnhancements.screenFlash(0x00ff00, 500);
+ }
+
+ console.log(`โ
Tamed ${creatureData.name}!`);
+ return worker;
+ }
+
+ // ========== TASK ASSIGNMENT ==========
+
+ assignTask(workerId, task) {
+ const worker = this.workers.get(workerId);
+ if (!worker) return false;
+
+ const creatureData = this.creatureTypes.get(worker.type);
+
+ // Check if creature can do this task
+ if (!this.canDoTask(worker, task)) {
+ console.log(`โ ${worker.name} cannot do ${task.type}`);
+ return false;
+ }
+
+ // Assign task
+ worker.currentTask = {
+ ...task,
+ startTime: Date.now(),
+ efficiency: this.calculateEfficiency(worker, task)
+ };
+
+ console.log(`๐ ${worker.name} assigned to ${task.type}`);
+ return true;
+ }
+
+ canDoTask(worker, task) {
+ const creatureData = this.creatureTypes.get(worker.type);
+
+ // Dragons can do everything
+ if (worker.type === 'dragon') return true;
+
+ // Check specialty
+ const taskSpecialties = {
+ 'transport': ['donkey'],
+ 'gather_wood': ['bigfoot'],
+ 'gather_berries': ['bigfoot'],
+ 'mine_ice': ['yeti'],
+ 'ice_fishing': ['yeti'],
+ 'craft_item': ['elf'],
+ 'enchant_item': ['elf'],
+ 'mine_ore': ['gnome'],
+ 'find_gems': ['gnome'],
+ 'water_crops': ['fairy'],
+ 'grow_crops': ['fairy'],
+ 'build': ['golem'],
+ 'clear_land': ['golem']
+ };
+
+ const validTypes = taskSpecialties[task.type] || [];
+ return validTypes.includes(worker.type);
+ }
+
+ calculateEfficiency(worker, task) {
+ const creatureData = this.creatureTypes.get(worker.type);
+ let efficiency = creatureData.efficiency;
+
+ // Apply specialty bonuses
+ if (task.type.includes('gather') && creatureData.gatherBonus) {
+ efficiency *= creatureData.gatherBonus;
+ }
+ if (task.type.includes('mine') && creatureData.miningBonus) {
+ efficiency *= creatureData.miningBonus;
+ }
+ if (task.type.includes('craft') && creatureData.craftingSpeed) {
+ efficiency *= creatureData.craftingSpeed;
+ }
+ if (task.type.includes('grow') && creatureData.growthBonus) {
+ efficiency *= creatureData.growthBonus;
+ }
+
+ // Apply level bonus
+ efficiency *= (1 + worker.level * 0.1);
+
+ return efficiency;
+ }
+
+ // ========== TASK EXECUTION ==========
+
+ updateWorkers(delta) {
+ for (const worker of this.workers.values()) {
+ if (!worker.currentTask) continue;
+
+ const elapsed = Date.now() - worker.currentTask.startTime;
+ const taskTime = worker.currentTask.duration / worker.currentTask.efficiency;
+
+ if (elapsed >= taskTime) {
+ this.completeTask(worker);
+ }
+ }
+ }
+
+ completeTask(worker) {
+ const task = worker.currentTask;
+
+ // Execute task
+ this.executeTask(worker, task);
+
+ // Grant XP
+ this.addWorkerXP(worker, task.xp || 10);
+
+ // Increase loyalty
+ worker.loyalty = Math.min(100, worker.loyalty + 1);
+
+ // Clear task
+ worker.currentTask = null;
+
+ console.log(`โ
${worker.name} completed ${task.type}!`);
+ }
+
+ executeTask(worker, task) {
+ switch (task.type) {
+ case 'transport':
+ this.transportItems(worker, task);
+ break;
+ case 'gather_wood':
+ this.gatherResource(worker, 'wood', task.amount);
+ break;
+ case 'gather_berries':
+ this.gatherResource(worker, 'berries', task.amount);
+ break;
+ case 'mine_ore':
+ this.gatherResource(worker, 'ore', task.amount);
+ break;
+ case 'craft_item':
+ this.craftItem(worker, task.item);
+ break;
+ case 'grow_crops':
+ this.growCrops(worker, task.crops);
+ break;
+ default:
+ console.log(`Unknown task: ${task.type}`);
+ }
+ }
+
+ transportItems(worker, task) {
+ // Move items from A to B
+ console.log(`๐ ${worker.name} transported items`);
+ }
+
+ gatherResource(worker, resource, amount) {
+ // Add resource to inventory
+ if (this.scene.inventorySystem) {
+ const bonus = this.workers.get(worker.id).currentTask.efficiency;
+ const finalAmount = Math.floor(amount * bonus);
+ this.scene.inventorySystem.addItem(resource, finalAmount);
+ console.log(`๐ฆ Gathered ${finalAmount} ${resource}`);
+ }
+ }
+
+ craftItem(worker, item) {
+ // Craft item
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(item, 1);
+ console.log(`๐จ Crafted ${item}`);
+ }
+ }
+
+ growCrops(worker, crops) {
+ // Instantly grow crops
+ console.log(`๐ฑ Grew ${crops.length} crops`);
+ }
+
+ // ========== LEVELING ==========
+
+ addWorkerXP(worker, amount) {
+ worker.xp += amount;
+
+ const xpNeeded = this.getXPForLevel(worker.level + 1);
+ if (worker.xp >= xpNeeded) {
+ this.levelUpWorker(worker);
+ }
+ }
+
+ getXPForLevel(level) {
+ return Math.floor(100 * Math.pow(1.5, level - 1));
+ }
+
+ levelUpWorker(worker) {
+ worker.level++;
+ worker.xp = 0;
+
+ console.log(`๐ ${worker.name} leveled up to ${worker.level}!`);
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(worker.x, worker.y);
+ }
+ }
+
+ // ========== SPECIAL ABILITIES ==========
+
+ useAbility(workerId, abilityName) {
+ const worker = this.workers.get(workerId);
+ if (!worker) return false;
+
+ const creatureData = this.creatureTypes.get(worker.type);
+ if (!creatureData.abilities.includes(abilityName)) {
+ console.log(`โ ${worker.name} doesn't have ${abilityName}`);
+ return false;
+ }
+
+ // Execute ability
+ switch (abilityName) {
+ case 'instant_growth':
+ this.instantGrowth(worker);
+ break;
+ case 'fire_breath':
+ this.fireBreath(worker);
+ break;
+ case 'treasure_finding':
+ this.findTreasure(worker);
+ break;
+ default:
+ console.log(`โจ ${worker.name} used ${abilityName}!`);
+ }
+
+ return true;
+ }
+
+ instantGrowth(worker) {
+ // Fairy ability - instantly grow all nearby crops
+ console.log('๐ธ Fairy magic! All crops instantly grown!');
+ }
+
+ fireBreath(worker) {
+ // Dragon ability - clear area with fire
+ console.log('๐ฅ Dragon fire breath!');
+ }
+
+ findTreasure(worker) {
+ // Dragon ability - find hidden treasure
+ console.log('๐ Found treasure!');
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem('gold', 100);
+ }
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateWorkers(delta);
+ }
+
+ destroy() {
+ console.log('๐ฆ Worker Creatures System destroyed');
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/WorkstationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/WorkstationSystem.js
new file mode 100644
index 000000000..7820d7067
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/WorkstationSystem.js
@@ -0,0 +1,179 @@
+/**
+ * WORKSTATION SYSTEM
+ * Manages processing stations like Furnace, Mint, Campfire.
+ * Handles: Input -> Process Time -> Output
+ */
+class WorkstationSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.stations = new Map(); // Key: "x,y", Value: StationData
+
+ // Define Recipes
+ this.recipes = {
+ 'furnace': [
+ { input: 'ore_iron', fuel: 'coal', output: 'iron_bar', time: 5000 },
+ { input: 'ore_gold', fuel: 'coal', output: 'gold_bar', time: 8000 },
+ { input: 'ore_stone', fuel: 'coal', output: 'stone_brick', time: 3000 },
+ { input: 'sand', fuel: 'coal', output: 'glass', time: 3000 },
+ { input: 'log', fuel: 'log', output: 'charcoal', time: 4000 } // Wood to charcoal
+ ],
+ 'mint': [
+ { input: 'iron_bar', fuel: 'coal', output: 'coin_gold', time: 2000, outputCount: 10 },
+ { input: 'gold_bar', fuel: 'coal', output: 'coin_gold', time: 3000, outputCount: 50 }
+ ],
+ 'campfire': [
+ { input: 'raw_meat', fuel: 'stick', output: 'cooked_meat', time: 3000 }
+ ]
+ };
+ }
+
+ /**
+ * Register a new workstation at coordinates
+ */
+ addStation(x, y, type) {
+ const key = `${x},${y}`;
+ if (this.stations.has(key)) return;
+
+ this.stations.set(key, {
+ x, y, type,
+ input: null, // { itemId, count }
+ fuel: null, // { itemId, count }
+ output: null, // { itemId, count }
+ isProcessing: false,
+ timer: 0,
+ maxTime: 0,
+ currentRecipe: null
+ });
+ console.log(`๐ญ Created ${type} at ${x},${y}`);
+ }
+
+ /**
+ * Player interacts with station
+ * (Simple version: Click with item to insert, Click empty to take output)
+ */
+ interact(x, y, playerItem) {
+ const key = `${x},${y}`;
+ const station = this.stations.get(key);
+ if (!station) return false;
+
+ // 1. If Output exists, take it (Prioritize taking output)
+ if (station.output) {
+ this.scene.inventorySystem.addItem(station.output.itemId, station.output.count);
+ this.showFloatingText(x, y, `+${station.output.count} ${station.output.itemId}`, '#00FF00');
+ station.output = null;
+ return true;
+ }
+
+ // 2. If holding item, try to insert (Input or Fuel)
+ if (playerItem) {
+ // Check if it's Valid Input for this station type
+ const recipe = this.findRecipe(station.type, playerItem.itemId);
+
+ // Is it Fuel? (Assume 'coal', 'log', 'stick' are fuels)
+ const isFuel = ['coal', 'log', 'stick', 'charcoal'].includes(playerItem.itemId);
+
+ if (recipe && !station.input) {
+ // Insert Input
+ station.input = { itemId: playerItem.itemId, count: 1 };
+ this.scene.inventorySystem.removeItem(playerItem.itemId, 1);
+ this.showFloatingText(x, y, `Inserted ${playerItem.itemId}`, '#FFFFFF');
+ this.tryStartProcess(station);
+ return true;
+ } else if (isFuel && !station.fuel) {
+ // Insert Fuel
+ station.fuel = { itemId: playerItem.itemId, count: 1 };
+ this.scene.inventorySystem.removeItem(playerItem.itemId, 1);
+ this.showFloatingText(x, y, `Fueled with ${playerItem.itemId}`, '#FFA500');
+ this.tryStartProcess(station);
+ return true;
+ }
+ }
+
+ // 3. Status Check
+ if (station.isProcessing) {
+ const progress = Math.floor((station.timer / station.maxTime) * 100);
+ this.showFloatingText(x, y, `Processing... ${progress}%`, '#FFFF00');
+ } else {
+ this.showFloatingText(x, y, 'Missing Input or Fuel', '#FF0000');
+ }
+
+ return true;
+ }
+
+ findRecipe(stationType, inputItem) {
+ const stationRecipes = this.recipes[stationType];
+ if (!stationRecipes) return null;
+ return stationRecipes.find(r => r.input === inputItem);
+ }
+
+ tryStartProcess(station) {
+ if (station.isProcessing) return;
+ if (!station.input || !station.fuel) return;
+
+ // Find matching recipe
+ const recipe = this.recipes[station.type].find(r =>
+ r.input === station.input.itemId &&
+ (r.fuel === 'any' || r.fuel === station.fuel.itemId)
+ );
+
+ // Specific fuel check can be relaxed later
+ if (recipe) {
+ station.isProcessing = true;
+ station.timer = 0;
+ station.maxTime = recipe.time;
+ station.currentRecipe = recipe;
+ console.log(`๐ฅ Started processing ${recipe.input} -> ${recipe.output}`);
+
+ // Visual feedback (Particles?)
+ // this.scene.addParticles...
+ }
+ }
+
+ update(delta) {
+ for (const station of this.stations.values()) {
+ if (station.isProcessing) {
+ station.timer += delta;
+
+ // Visual: Float smoke occasionally?
+ if (Math.random() < 0.05) {
+ // emit smoke particle
+ }
+
+ if (station.timer >= station.maxTime) {
+ // COMPLETE
+ const recipe = station.currentRecipe;
+ const outCount = recipe.outputCount || 1;
+
+ // Consume inputs
+ // (Actually we keep them in slots until done? Or consume at start?
+ // Let's say inputs are consumed at start visually, but logically here we swap to output)
+
+ station.output = { itemId: recipe.output, count: outCount };
+ station.input = null;
+ station.fuel = null; // Consume 1 fuel per operation for now (Simple)
+
+ station.isProcessing = false;
+ station.timer = 0;
+ station.currentRecipe = null;
+
+ this.showFloatingText(station.x, station.y, 'DONE!', '#00FF00');
+ if (this.scene.soundManager) this.scene.soundManager.playSuccess(); // Placeholder sound
+ }
+ }
+ }
+ }
+
+ showFloatingText(x, y, text, color) {
+ // Convert grid to screen
+ // Assuming interact passes Grid X,Y
+ if (this.scene.events) {
+ const screen = this.scene.iso.toScreen(x, y);
+ this.scene.events.emit('show-floating-text', {
+ x: screen.x + this.scene.terrainOffsetX,
+ y: screen.y + this.scene.terrainOffsetY - 30,
+ text: text,
+ color: color
+ });
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/WorldEventSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/WorldEventSystem.js
new file mode 100644
index 000000000..2cbb91087
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/WorldEventSystem.js
@@ -0,0 +1,137 @@
+class WorldEventSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.activeEvents = [];
+ this.bats = [];
+ this.owl = null;
+ }
+
+ /*
+ * BATS: Visual effect for evening/night
+ */
+ spawnBatSwarm() {
+ console.log('๐ฆ Bat Swarm Incoming!');
+ const count = 10 + Math.floor(Math.random() * 10);
+
+ for (let i = 0; i < count; i++) {
+ // Start right-side of screen, random height
+ const sx = this.scene.scale.width + 50 + Math.random() * 200;
+ const sy = Math.random() * (this.scene.scale.height / 2); // Top half
+
+ const bat = this.scene.add.sprite(sx, sy, 'bat');
+ bat.setDepth(2000); // Above most things
+ bat.setScrollFactor(0); // Screen space (UI layer) or World space?
+ // Better world space if we want them to feel like part of the world, but screen space is easier for "effect".
+ // Let's stick to Screen Space for "Ambience".
+
+ // Random speed
+ const speed = 150 + Math.random() * 100;
+
+ this.bats.push({ sprite: bat, speed: speed });
+ }
+ }
+
+ /*
+ * NIGHT OWL: Delivers a gift
+ */
+ spawnNightOwl() {
+ if (this.owl) return; // Only one at a time
+ console.log('๐ฆ Night Owl Arriving!');
+
+ // Spawn top-left
+ const sx = -50;
+ const sy = 100;
+
+ const owl = this.scene.add.sprite(sx, sy, 'owl');
+ owl.setDepth(2000);
+ owl.setScrollFactor(0); // Screen space for delivery
+
+ this.owl = {
+ sprite: owl,
+ state: 'ARRIVING', // ARRIVING, DELIVERING, LEAVING
+ timer: 0,
+ targetX: this.scene.scale.width / 2,
+ targetY: this.scene.scale.height / 3
+ };
+
+ if (this.scene.soundManager) {
+ // Play owl hoot sound (placeholder or generic)
+ // this.scene.soundManager.playHoot();
+ }
+
+ this.scene.events.emit('show-floating-text', {
+ x: this.scene.player.sprite.x,
+ y: this.scene.player.sprite.y - 120,
+ text: '๐ฆ Hoot Hoot!',
+ color: '#FFA500'
+ });
+ }
+
+ update(delta) {
+ // 1. Bats
+ for (let i = this.bats.length - 1; i >= 0; i--) {
+ const b = this.bats[i];
+ b.sprite.x -= b.speed * (delta / 1000); // Fly left
+ b.sprite.y += (Math.sin(b.sprite.x * 0.02) * 2); // Wobbly flight
+
+ if (b.sprite.x < -50) {
+ b.sprite.destroy();
+ this.bats.splice(i, 1);
+ }
+ }
+
+ // 2. Owl
+ if (this.owl) {
+ const o = this.owl;
+ const speed = 200 * (delta / 1000);
+
+ if (o.state === 'ARRIVING') {
+ const dx = o.targetX - o.sprite.x;
+ const dy = o.targetY - o.sprite.y;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+
+ if (dist < 10) {
+ o.state = 'DELIVERING';
+ o.timer = 0;
+ this.dropGift();
+ } else {
+ o.sprite.x += (dx / dist) * speed;
+ o.sprite.y += (dy / dist) * speed;
+ }
+ } else if (o.state === 'DELIVERING') {
+ o.timer += delta;
+ if (o.timer > 1000) { // Hover for 1s
+ o.state = 'LEAVING';
+ }
+ } else if (o.state === 'LEAVING') {
+ o.sprite.x += speed; // Fly right
+ o.sprite.y -= speed * 0.5; // Fly up
+
+ if (o.sprite.x > this.scene.scale.width + 50) {
+ o.sprite.destroy();
+ this.owl = null;
+ }
+ }
+ }
+ }
+
+ dropGift() {
+ console.log('๐ Owl dropped a gift!');
+ const p = this.scene.player;
+
+ // Spawn loot at player pos (World Space)
+ if (this.scene.interactionSystem) {
+ // Random gift: Gold or maybe a rare item
+ const r = Math.random();
+ let item = 'flower_red';
+ let count = 1;
+
+ if (r < 0.3) { item = 'coin_gold'; count = 10; }
+ else if (r < 0.6) { item = 'seeds_corn'; count = 5; }
+ else if (r < 0.9) { item = 'item_scrap'; count = 3; }
+ else { item = 'artefact_old'; count = 1; } // Rare!
+
+ this.scene.interactionSystem.spawnLoot(p.gridX, p.gridY, item, count);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ZombieCommunicationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ZombieCommunicationSystem.js
new file mode 100644
index 000000000..634c11611
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ZombieCommunicationSystem.js
@@ -0,0 +1,361 @@
+/**
+ * ZombieCommunicationSystem.js
+ * =============================
+ * KRVAVA ลฝETEV - Zombie Communication System (Hybrid Skill)
+ *
+ * Features:
+ * - Level-based zombie understanding
+ * - Level 1: Groaning only ("Hnggg...")
+ * - Level 5: Keywords in subtitles
+ * - Level 10: Full sentences (warnings, memories)
+ * - Subtitle UI
+ * - Translation system
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class ZombieCommunicationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Player's communication level
+ this.communicationLevel = 0;
+ this.maxLevel = 10;
+
+ // Subtitle UI
+ this.subtitleText = null;
+ this.subtitleContainer = null;
+ this.currentSubtitle = null;
+
+ // Zombie speech library
+ this.zombiePhrases = new Map();
+
+ console.log('๐ง ZombieCommunicationSystem initialized');
+
+ // Create subtitle UI
+ this.createSubtitleUI();
+
+ // Load zombie phrases
+ this.loadZombiePhrases();
+ }
+
+ /**
+ * Create subtitle UI
+ */
+ createSubtitleUI() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Container at bottom of screen
+ this.subtitleContainer = this.scene.add.container(width / 2, height - 100);
+ this.subtitleContainer.setScrollFactor(0);
+ this.subtitleContainer.setDepth(10000);
+ this.subtitleContainer.setAlpha(0);
+
+ // Background
+ const bg = this.scene.add.rectangle(0, 0, 800, 100, 0x000000, 0.8);
+ this.subtitleContainer.add(bg);
+
+ // Subtitle text
+ this.subtitleText = this.scene.add.text(0, 0, '', {
+ fontSize: '24px',
+ fontFamily: 'Arial',
+ color: '#00FF00', // Green zombie text
+ align: 'center',
+ wordWrap: { width: 750 }
+ });
+ this.subtitleText.setOrigin(0.5);
+ this.subtitleContainer.add(this.subtitleText);
+
+ console.log('โ
Subtitle UI created');
+ }
+
+ /**
+ * Load zombie phrase library
+ */
+ loadZombiePhrases() {
+ // Level 1: Pure groaning
+ const level1Phrases = [
+ { zombie: 'Hnggg...', translation: null },
+ { zombie: 'Grrraaa...', translation: null },
+ { zombie: 'Uuuhhh...', translation: null },
+ { zombie: 'Aaarrgh...', translation: null }
+ ];
+
+ // Level 5: Keywords visible
+ const level5Phrases = [
+ { zombie: 'Hnggg... HUNGER... grrr...', translation: 'I am hungry...' },
+ { zombie: 'Grrr... DANGER... hnggg...', translation: 'Danger nearby!' },
+ { zombie: 'Uhhh... MASTER... grrr...', translation: 'Looking for master...' },
+ { zombie: 'Aaah... PAIN... hnggg...', translation: 'I am in pain...' },
+ { zombie: 'Grrr... HELP... uhhh...', translation: 'Help me...' },
+ { zombie: 'Hnggg... FRIEND... grrr...', translation: 'You are my friend...' }
+ ];
+
+ // Level 10: Full sentences
+ const level10Phrases = [
+ { zombie: 'I remember... my family...', translation: 'I remember my family before I turned...' },
+ { zombie: 'The darkness... it hurts...', translation: 'The curse is painful...' },
+ { zombie: 'Thank you... for saving me...', translation: 'Thank you for taming me instead of killing me.' },
+ { zombie: 'Enemies... coming from east...', translation: 'I sense enemies approaching from the east!' },
+ { zombie: 'My name was... John...', translation: 'I remember my name was John...' },
+ { zombie: 'Ana... she calls us...', translation: 'Ana\'s Twin Bond resonates with us zombies...' },
+ { zombie: 'The Black Serpent... did this...', translation: 'The Black Serpent Initiative caused this outbreak.' },
+ { zombie: 'I was a farmer... before...', translation: 'I was a farmer before the infection...' },
+ { zombie: 'Danger! Big zombie nearby!', translation: 'WARNING: Boss zombie detected!' },
+ { zombie: 'I protect you... master...', translation: 'I will protect you with my unlife, master.' }
+ ];
+
+ // Special contextual phrases
+ const contextualPhrases = [
+ { context: 'low_health', zombie: 'Hnggg... weak... dying...', translation: 'I am badly hurt!' },
+ { context: 'enemy_near', zombie: 'Grrr! Intruders!', translation: 'Enemies detected!' },
+ { context: 'happy', zombie: 'Grraaa... good... happy...', translation: 'I am happy serving you!' },
+ { context: 'task_complete', zombie: 'Uhhh... done... master...', translation: 'Task completed, master!' },
+ { context: 'hungry', zombie: 'Need... food... hnggg...', translation: 'I need to eat soon...' },
+ { context: 'scared', zombie: 'Aaaah! Fear! Run!', translation: 'Something terrifying is here!' }
+ ];
+
+ this.zombiePhrases.set('level1', level1Phrases);
+ this.zombiePhrases.set('level5', level5Phrases);
+ this.zombiePhrases.set('level10', level10Phrases);
+ this.zombiePhrases.set('contextual', contextualPhrases);
+
+ console.log(`โ
Loaded ${level1Phrases.length + level5Phrases.length + level10Phrases.length + contextualPhrases.length} zombie phrases`);
+ }
+
+ /**
+ * Set communication level
+ */
+ setCommunicationLevel(level) {
+ this.communicationLevel = Math.min(this.maxLevel, Math.max(0, level));
+
+ console.log(`๐ง Communication level: ${this.communicationLevel}/10`);
+
+ this.showNotification({
+ title: 'Zombie Understanding Improved!',
+ text: `๐ง Level ${this.communicationLevel}: ${this.getLevelDescription()}`,
+ icon: '๐ง'
+ });
+ }
+
+ /**
+ * Get level description
+ */
+ getLevelDescription() {
+ if (this.communicationLevel >= 10) {
+ return 'Full sentences! You understand zombies completely!';
+ } else if (this.communicationLevel >= 5) {
+ return 'Keywords visible! You understand basic meanings!';
+ } else {
+ return 'Only groaning... You need more practice!';
+ }
+ }
+
+ /**
+ * Zombie speaks
+ */
+ zombieSpeak(zombieId, context = null) {
+ let phrase;
+
+ // Get appropriate phrase based on level
+ if (context) {
+ phrase = this.getContextualPhrase(context);
+ } else if (this.communicationLevel >= 10) {
+ phrase = this.getRandomPhrase('level10');
+ } else if (this.communicationLevel >= 5) {
+ phrase = this.getRandomPhrase('level5');
+ } else {
+ phrase = this.getRandomPhrase('level1');
+ }
+
+ if (!phrase) return;
+
+ // Show subtitle
+ this.showSubtitle(phrase, zombieId);
+ }
+
+ /**
+ * Get random phrase from level
+ */
+ getRandomPhrase(level) {
+ const phrases = this.zombiePhrases.get(level);
+ if (!phrases || phrases.length === 0) return null;
+
+ return Phaser.Utils.Array.GetRandom(phrases);
+ }
+
+ /**
+ * Get contextual phrase
+ */
+ getContextualPhrase(context) {
+ const phrases = this.zombiePhrases.get('contextual');
+ const found = phrases.filter(p => p.context === context);
+
+ if (found.length === 0) return this.getRandomPhrase('level1');
+
+ return Phaser.Utils.Array.GetRandom(found);
+ }
+
+ /**
+ * Show subtitle
+ */
+ showSubtitle(phrase, zombieId = 'Zombie') {
+ let displayText = phrase.zombie;
+
+ // Add translation if level is high enough
+ if (this.communicationLevel >= 5 && phrase.translation) {
+ displayText += `\n[${phrase.translation}]`;
+ }
+
+ // Show speaker name
+ displayText = `${zombieId}: ${displayText}`;
+
+ this.subtitleText.setText(displayText);
+
+ // Fade in
+ this.scene.tweens.add({
+ targets: this.subtitleContainer,
+ alpha: 1,
+ duration: 300
+ });
+
+ // Auto-hide after 3 seconds
+ if (this.currentSubtitle) {
+ clearTimeout(this.currentSubtitle);
+ }
+
+ this.currentSubtitle = setTimeout(() => {
+ this.hideSubtitle();
+ }, 3000);
+
+ console.log(`๐ฌ ${displayText}`);
+ }
+
+ /**
+ * Hide subtitle
+ */
+ hideSubtitle() {
+ this.scene.tweens.add({
+ targets: this.subtitleContainer,
+ alpha: 0,
+ duration: 300
+ });
+
+ this.currentSubtitle = null;
+ }
+
+ /**
+ * Zombie conversation (interactive)
+ */
+ startConversation(zombieId) {
+ if (this.communicationLevel < 5) {
+ this.showSubtitle({
+ zombie: 'Hnggg... grrr...',
+ translation: null
+ }, zombieId);
+
+ this.showNotification({
+ title: 'Cannot Understand',
+ text: 'Your zombie communication skill is too low!',
+ icon: '๐ง '
+ });
+ return false;
+ }
+
+ console.log(`๐ฌ Conversation with ${zombieId}`);
+
+ // Show conversation phrases
+ const phrases = [
+ 'What is your name?',
+ 'What do you remember?',
+ 'Are you loyal?',
+ 'Do you feel pain?',
+ 'Goodbye'
+ ];
+
+ // TODO: Create actual dialogue UI with choices
+ console.log('Conversation options:', phrases);
+
+ return true;
+ }
+
+ /**
+ * Zombie warning (important messages)
+ */
+ zombieWarning(message, urgency = 'normal') {
+ const urgencyIcons = {
+ low: 'โน๏ธ',
+ normal: 'โ ๏ธ',
+ high: '๐จ',
+ critical: '๐'
+ };
+
+ this.showSubtitle({
+ zombie: message,
+ translation: message
+ }, `${urgencyIcons[urgency]} ZOMBIE ALERT`);
+
+ // Play alert sound for high/critical
+ if (urgency === 'high' || urgency === 'critical') {
+ // TODO: Play alert sound
+ this.scene.cameras.main.shake(200, 0.005);
+ }
+ }
+
+ /**
+ * Level up communication skill
+ */
+ levelUpCommunication() {
+ if (this.communicationLevel >= this.maxLevel) {
+ console.log('๐ง Already at max level!');
+ return false;
+ }
+
+ this.setCommunicationLevel(this.communicationLevel + 1);
+
+ // Show what's unlocked
+ if (this.communicationLevel === 5) {
+ this.showNotification({
+ title: 'Keywords Unlocked!',
+ text: '๐ง You can now see KEYWORDS in zombie speech!',
+ icon: 'โจ'
+ });
+ } else if (this.communicationLevel === 10) {
+ this.showNotification({
+ title: 'Full Understanding!',
+ text: '๐ง You can now understand COMPLETE zombie sentences!',
+ icon: '๐'
+ });
+ }
+
+ return true;
+ }
+
+ /**
+ * Get communication level
+ */
+ getCommunicationLevel() {
+ return this.communicationLevel;
+ }
+
+ /**
+ * Can understand zombie
+ */
+ canUnderstandZombie(requiredLevel = 1) {
+ return this.communicationLevel >= requiredLevel;
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ZombieEconomySystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ZombieEconomySystem.js
new file mode 100644
index 000000000..9d9316bd9
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ZombieEconomySystem.js
@@ -0,0 +1,569 @@
+/**
+ * ZOMBIE ECONOMY & CITY SANITATION SYSTEM
+ * Mrtva Dolina - Worker Zombies, Smetarji, Redka Darila
+ *
+ * Features:
+ * - Worker Zombies (heavy labor, construction)
+ * - Sanitation System (Smetar cleans city)
+ * - Rare Gift System (unique rewards from zombie work)
+ * - Zombie Maintenance (Brain feeding)
+ * - Contract System (loan zombies to NPCs)
+ * - Happiness impact on city growth
+ */
+
+export class ZombieEconomySystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Worker zombie types
+ this.zombieTypes = {
+ scout: {
+ name: 'Zombi Skavt',
+ strength: 30,
+ speed: 60,
+ brainConsumption: 0.5, // per hour
+ tasks: ['scouting', 'light_work']
+ },
+ worker: {
+ name: 'Delovni Zombi',
+ strength: 80,
+ speed: 40,
+ brainConsumption: 1.5, // per hour
+ tasks: ['construction', 'hauling', 'sanitation']
+ },
+ sanitation: {
+ name: 'Smetar Zombi',
+ strength: 50,
+ speed: 50,
+ brainConsumption: 1.0, // per hour
+ tasks: ['cleaning', 'graffiti_removal']
+ }
+ };
+
+ // Active zombies
+ this.activeZombies = [];
+
+ // Contracts (zombies loaned to NPCs)
+ this.activeContracts = [];
+
+ // Rare gifts catalogue
+ this.rareGifts = [
+ { id: 'family_heirloom', name: 'Starinski druลพinski artefakt', rarity: 'legendary', value: 1000 },
+ { id: 'ancient_dye', name: 'Starodavno barvilo', rarity: 'epic', value: 500 },
+ { id: 'rare_seed_pack', name: 'Paket redkih semen', rarity: 'rare', value: 300 },
+ { id: 'kai_memory_photo', name: 'Kaijeva skrita fotografija', rarity: 'legendary', value: 2000 },
+ { id: 'mystical_paint', name: 'Mistiฤno barvilo', rarity: 'epic', value: 600 }
+ ];
+
+ // City cleanliness tracking
+ this.cityTrash = [];
+ this.cityGraffiti = [];
+ this.cleanlinessScore = 50; // 0-100
+
+ // Happiness impact
+ this.citizenHappiness = 50; // 0-100
+
+ this.init();
+ }
+
+ init() {
+ // Listen for trash generation
+ this.scene.events.on('npc:activity', this.onNPCActivity, this);
+ this.scene.events.on('vandal:graffiti', this.onGraffitiCreated, this);
+
+ // Start passive systems
+ this.startBrainConsumption();
+ this.startCleaningSweep();
+
+ console.log('โ
ZombieEconomySystem initialized');
+ }
+
+ /**
+ * RECRUIT ZOMBIE
+ */
+ recruitZombie(type, name = null) {
+ const zombieData = this.zombieTypes[type];
+ if (!zombieData) {
+ console.error(`Unknown zombie type: ${type}`);
+ return null;
+ }
+
+ const zombie = {
+ id: `zombie_${Date.now()}`,
+ type: type,
+ name: name || zombieData.name,
+ strength: zombieData.strength,
+ speed: zombieData.speed,
+ brainLevel: 100, // 0-100, energy/hunger
+ brainConsumption: zombieData.brainConsumption,
+ tasks: zombieData.tasks,
+ currentTask: null,
+ isContracted: false,
+ grumbleTimer: null,
+ x: this.scene.player.x,
+ y: this.scene.player.y,
+ sprite: null
+ };
+
+ // Create sprite
+ zombie.sprite = this.scene.add.sprite(zombie.x, zombie.y, `zombie_${type}`);
+ zombie.sprite.setDepth(5);
+
+ this.activeZombies.push(zombie);
+
+ console.log(`๐ง Recruited: ${zombie.name}`);
+
+ return zombie;
+ }
+
+ /**
+ * FEED ZOMBIE (with brains)
+ */
+ feedZombie(zombieId, brainsAmount = 50) {
+ const zombie = this.activeZombies.find(z => z.id === zombieId);
+ if (!zombie) return false;
+
+ zombie.brainLevel = Math.min(100, zombie.brainLevel + brainsAmount);
+
+ // Happy zombie sound
+ this.scene.sound.play('zombie_satisfied', { volume: 0.5 });
+
+ // Show feedback
+ this.scene.events.emit('show-floating-text', {
+ x: zombie.sprite.x,
+ y: zombie.sprite.y - 30,
+ text: '*munch* ...dobr!',
+ color: '#90EE90'
+ });
+
+ // Stop grumbling
+ if (zombie.grumbleTimer) {
+ clearInterval(zombie.grumbleTimer);
+ zombie.grumbleTimer = null;
+ }
+
+ return true;
+ }
+
+ /**
+ * BRAIN CONSUMPTION - Passive hunger system
+ */
+ startBrainConsumption() {
+ setInterval(() => {
+ this.activeZombies.forEach(zombie => {
+ // Reduce brain level based on consumption rate
+ zombie.brainLevel = Math.max(0, zombie.brainLevel - (zombie.brainConsumption / 60)); // per minute
+
+ // Check hunger
+ if (zombie.brainLevel < 30) {
+ this.zombieHungry(zombie);
+ }
+
+ // Critical hunger - zombie becomes slow
+ if (zombie.brainLevel < 10) {
+ zombie.speed *= 0.5; // 50% slower
+ if (zombie.sprite) {
+ zombie.sprite.setTint(0x666666); // Darken sprite
+ }
+ }
+ });
+ }, 60000); // Every minute
+ }
+
+ zombieHungry(zombie) {
+ if (zombie.grumbleTimer) return; // Already grumbling
+
+ // Start periodic grumbling
+ zombie.grumbleTimer = setInterval(() => {
+ const hungerLines = [
+ 'Braaaaains...',
+ 'Moลพgaaaaani...',
+ 'Hrrrngh... laฤen...',
+ '*godrnja o hrani*'
+ ];
+
+ const line = Phaser.Utils.Array.GetRandom(hungerLines);
+
+ // Show speech bubble
+ this.scene.events.emit('show-speech-bubble', {
+ x: zombie.sprite.x,
+ y: zombie.sprite.y - 50,
+ text: line,
+ duration: 3000
+ });
+
+ // Play hungry sound
+ if (this.scene.sound) {
+ this.scene.sound.play('zombie_groan', { volume: 0.4 });
+ }
+ }, 15000); // Every 15 seconds
+ }
+
+ /**
+ * CONTRACT SYSTEM - Loan zombies to NPCs
+ */
+ createContract(zombieId, npc, task, duration, payment) {
+ const zombie = this.activeZombies.find(z => z.id === zombieId);
+ if (!zombie || zombie.isContracted) {
+ console.warn('Zombie not available for contract');
+ return false;
+ }
+
+ const contract = {
+ id: `contract_${Date.now()}`,
+ zombieId: zombieId,
+ npc: npc, // 'ivan_kovac', 'tehnik', etc.
+ task: task, // 'wall_construction', 'steel_hauling', etc.
+ duration: duration, // in game hours
+ payment: payment, // gold or rare gift
+ startTime: this.scene.gameTime || Date.now(),
+ completed: false
+ };
+
+ zombie.isContracted = true;
+ zombie.currentTask = task;
+ this.activeContracts.push(contract);
+
+ // Move zombie to work site
+ this.moveZombieToWorkSite(zombie, npc);
+
+ console.log(`๐ Contract created: ${zombie.name} โ ${npc} (${task})`);
+
+ // Start work visuals
+ this.startWorkAnimation(zombie, task);
+
+ return contract;
+ }
+
+ moveZombieToWorkSite(zombie, npc) {
+ const workSites = {
+ ivan_kovac: { x: 300, y: 200 }, // Blacksmith
+ tehnik: { x: 500, y: 250 }, // Tech Workshop
+ mayor: { x: 400, y: 300 } // Town Hall
+ };
+
+ const site = workSites[npc] || { x: 400, y: 300 };
+
+ if (zombie.sprite) {
+ this.scene.tweens.add({
+ targets: zombie.sprite,
+ x: site.x,
+ y: site.y,
+ duration: 2000,
+ ease: 'Sine.easeInOut'
+ });
+ }
+ }
+
+ startWorkAnimation(zombie, task) {
+ // Different animations for different tasks
+ const animations = {
+ wall_construction: 'zombie_hammering',
+ steel_hauling: 'zombie_carrying',
+ sanitation: 'zombie_sweeping',
+ graffiti_removal: 'zombie_scrubbing'
+ };
+
+ const anim = animations[task] || 'zombie_working';
+
+ if (zombie.sprite && zombie.sprite.anims) {
+ zombie.sprite.play(anim, true);
+ }
+ }
+
+ /**
+ * COMPLETE CONTRACT - Award payment
+ */
+ completeContract(contractId) {
+ const contract = this.activeContracts.find(c => c.id === contractId);
+ if (!contract || contract.completed) return;
+
+ const zombie = this.activeZombies.find(z => z.id === contract.zombieId);
+
+ contract.completed = true;
+ zombie.isContracted = false;
+ zombie.currentTask = null;
+
+ // Award payment
+ if (contract.payment.type === 'gold') {
+ this.scene.inventorySystem.addGold(contract.payment.amount);
+
+ this.scene.events.emit('show-notification', {
+ title: 'Pogodba Konฤana',
+ message: `${zombie.name} je konฤal delo! +${contract.payment.amount} zlata.`,
+ icon: '๐ฐ',
+ duration: 3000
+ });
+ } else if (contract.payment.type === 'rare_gift') {
+ this.awardRareGift(contract.payment.giftId);
+ }
+
+ // Return zombie to player
+ if (zombie.sprite) {
+ this.scene.tweens.add({
+ targets: zombie.sprite,
+ x: this.scene.player.x + 50,
+ y: this.scene.player.y,
+ duration: 2000,
+ ease: 'Sine.easeOut'
+ });
+ }
+
+ console.log(`โ
Contract completed: ${contract.task}`);
+ }
+
+ /**
+ * RARE GIFT SYSTEM
+ */
+ awardRareGift(giftId = null) {
+ // Random gift if not specified
+ if (!giftId) {
+ const gift = Phaser.Utils.Array.GetRandom(this.rareGifts);
+ giftId = gift.id;
+ }
+
+ const gift = this.rareGifts.find(g => g.id === giftId);
+ if (!gift) return;
+
+ // Add to inventory
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(gift.id, 1);
+ }
+
+ // Show special notification
+ this.scene.events.emit('show-notification', {
+ title: '๐ REDKO DARILO!',
+ message: `Prejel si: ${gift.name} (${gift.rarity})`,
+ icon: 'โจ',
+ duration: 7000,
+ color: this.getRarityColor(gift.rarity)
+ });
+
+ // Play special sound
+ if (this.scene.sound) {
+ this.scene.sound.play('rare_gift_fanfare', { volume: 0.7 });
+ }
+
+ console.log(`๐ Awarded rare gift: ${gift.name}`);
+ }
+
+ getRarityColor(rarity) {
+ const colors = {
+ legendary: '#FFD700', // Gold
+ epic: '#9B59B6', // Purple
+ rare: '#3498DB' // Blue
+ };
+ return colors[rarity] || '#FFFFFF';
+ }
+
+ /**
+ * SANITATION SYSTEM - Trash & Graffiti
+ */
+ onNPCActivity(npcData) {
+ // NPCs randomly drop trash
+ if (Math.random() < 0.1) { // 10% chance
+ this.spawnTrash(npcData.x, npcData.y);
+ }
+ }
+
+ spawnTrash(x, y) {
+ const trashTypes = ['trash_bag', 'litter', 'broken_bottle', 'paper_waste'];
+ const type = Phaser.Utils.Array.GetRandom(trashTypes);
+
+ const trash = this.scene.add.sprite(x, y, type);
+ trash.setDepth(1);
+
+ this.cityTrash.push({
+ id: `trash_${Date.now()}_${Math.random()}`,
+ x: x,
+ y: y,
+ type: type,
+ sprite: trash
+ });
+
+ // Lower cleanliness
+ this.cleanlinessScore = Math.max(0, this.cleanlinessScore - 2);
+ this.updateCityStats();
+ }
+
+ onGraffitiCreated(graffitiData) {
+ const graffiti = this.scene.add.sprite(graffitiData.x, graffitiData.y, 'graffiti_tag');
+ graffiti.setDepth(2);
+
+ this.cityGraffiti.push({
+ id: `graffiti_${Date.now()}`,
+ x: graffitiData.x,
+ y: graffitiData.y,
+ sprite: graffiti
+ });
+
+ // Lower cleanliness
+ this.cleanlinessScore = Math.max(0, this.cleanlinessScore - 5);
+ this.updateCityStats();
+ }
+
+ /**
+ * CLEANING SWEEP - Zombies clean autonomously
+ */
+ startCleaningSweep() {
+ setInterval(() => {
+ // Find sanitation zombies
+ const cleaners = this.activeZombies.filter(z =>
+ z.tasks.includes('cleaning') &&
+ !z.isContracted &&
+ z.brainLevel > 20
+ );
+
+ cleaners.forEach(cleaner => {
+ // Clean nearest trash
+ const nearestTrash = this.findNearestTrash(cleaner.sprite.x, cleaner.sprite.y);
+ if (nearestTrash) {
+ this.cleanTrash(cleaner, nearestTrash);
+ }
+
+ // Clean nearest graffiti
+ const nearestGraffiti = this.findNearestGraffiti(cleaner.sprite.x, cleaner.sprite.y);
+ if (nearestGraffiti) {
+ this.cleanGraffiti(cleaner, nearestGraffiti);
+ }
+ });
+ }, 10000); // Every 10 seconds
+ }
+
+ findNearestTrash(x, y) {
+ let nearest = null;
+ let minDist = Infinity;
+
+ this.cityTrash.forEach(trash => {
+ const dist = Phaser.Math.Distance.Between(x, y, trash.x, trash.y);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = trash;
+ }
+ });
+
+ return minDist < 200 ? nearest : null; // Within 200px
+ }
+
+ findNearestGraffiti(x, y) {
+ let nearest = null;
+ let minDist = Infinity;
+
+ this.cityGraffiti.forEach(graffiti => {
+ const dist = Phaser.Math.Distance.Between(x, y, graffiti.x, graffiti.y);
+ if (dist < minDist) {
+ minDist = dist;
+ nearest = graffiti;
+ }
+ });
+
+ return minDist < 200 ? nearest : null;
+ }
+
+ cleanTrash(zombie, trash) {
+ // Move to trash
+ this.scene.tweens.add({
+ targets: zombie.sprite,
+ x: trash.x,
+ y: trash.y,
+ duration: 1000,
+ onComplete: () => {
+ // Play cleaning animation
+ if (zombie.sprite.anims) {
+ zombie.sprite.play('zombie_sweeping', true);
+ }
+
+ // Remove trash after delay
+ setTimeout(() => {
+ trash.sprite.destroy();
+ this.cityTrash = this.cityTrash.filter(t => t.id !== trash.id);
+
+ // Increase cleanliness
+ this.cleanlinessScore = Math.min(100, this.cleanlinessScore + 3);
+ this.updateCityStats();
+
+ console.log(`๐งน ${zombie.name} cleaned trash`);
+ }, 2000);
+ }
+ });
+ }
+
+ cleanGraffiti(zombie, graffiti) {
+ // Move to graffiti
+ this.scene.tweens.add({
+ targets: zombie.sprite,
+ x: graffiti.x,
+ y: graffiti.y,
+ duration: 1000,
+ onComplete: () => {
+ // Play scrubbing animation
+ if (zombie.sprite.anims) {
+ zombie.sprite.play('zombie_scrubbing', true);
+ }
+
+ // Remove graffiti with fade
+ this.scene.tweens.add({
+ targets: graffiti.sprite,
+ alpha: 0,
+ duration: 3000,
+ onComplete: () => {
+ graffiti.sprite.destroy();
+ this.cityGraffiti = this.cityGraffiti.filter(g => g.id !== graffiti.id);
+
+ // Increase cleanliness
+ this.cleanlinessScore = Math.min(100, this.cleanlinessScore + 5);
+ this.updateCityStats();
+
+ console.log(`๐งฝ ${zombie.name} removed graffiti`);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * CITY STATS UPDATE
+ */
+ updateCityStats() {
+ // Calculate happiness based on cleanliness
+ this.citizenHappiness = Math.min(100, this.cleanlinessScore * 1.2);
+
+ // Emit stats update
+ this.scene.events.emit('city:stats_updated', {
+ cleanliness: this.cleanlinessScore,
+ happiness: this.citizenHappiness,
+ trashCount: this.cityTrash.length,
+ graffitiCount: this.cityGraffiti.length
+ });
+
+ // Happiness affects NPC arrival rate
+ if (this.citizenHappiness > 70) {
+ // Faster settler arrivals
+ this.scene.events.emit('city:boost_immigration');
+ }
+ }
+
+ /**
+ * Get zombie status for UI
+ */
+ getZombieStatus() {
+ return this.activeZombies.map(z => ({
+ id: z.id,
+ name: z.name,
+ type: z.type,
+ brainLevel: z.brainLevel,
+ currentTask: z.currentTask,
+ isContracted: z.isContracted
+ }));
+ }
+
+ destroy() {
+ this.activeZombies.forEach(zombie => {
+ if (zombie.sprite) zombie.sprite.destroy();
+ if (zombie.grumbleTimer) clearInterval(zombie.grumbleTimer);
+ });
+
+ this.cityTrash.forEach(trash => trash.sprite.destroy());
+ this.cityGraffiti.forEach(graffiti => graffiti.sprite.destroy());
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ZombieMinerAutomationSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ZombieMinerAutomationSystem.js
new file mode 100644
index 000000000..36f38f72d
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ZombieMinerAutomationSystem.js
@@ -0,0 +1,464 @@
+/**
+ * ZOMBIE MINER AUTOMATION SYSTEM
+ * Part of: Mining System Expansion
+ * Created: January 4, 2026
+ *
+ * Features:
+ * - Hire zombie miners for passive resource generation
+ * - Assign miners to specific mine depths
+ * - Efficiency & loyalty mechanics
+ * - Automated ore collection
+ * - Zombie equipment upgrades
+ */
+
+class ZombieMinerAutomationSystem {
+ constructor(game) {
+ this.game = game;
+ this.player = game.player;
+
+ // Zombie miners
+ this.zombieMiners = [];
+ this.maxZombieMiners = 10;
+ this.zombieMinerCost = 5000;
+
+ // Automation settings
+ this.automationActive = false;
+ this.totalYieldPerHour = 0;
+ this.lastCollectionTime = null;
+
+ // Equipment for zombies
+ this.zombieEquipment = {
+ pickaxe_tier: 1, // 1-5
+ helmet_lamp: false, // Better visibility
+ oxygen_tank: false, // Deeper mining
+ cart: false // Auto-transport
+ };
+ }
+
+ /**
+ * Hire a zombie miner
+ */
+ hireZombieMiner() {
+ if (this.zombieMiners.length >= this.maxZombieMiners) {
+ return {
+ success: false,
+ message: `Maximum ${this.maxZombieMiners} zombie miners allowed!`
+ };
+ }
+
+ if (this.player.money < this.zombieMinerCost) {
+ return {
+ success: false,
+ message: `Need ${this.zombieMinerCost}g to hire zombie miner!`
+ };
+ }
+
+ // Hire zombie
+ this.player.money -= this.zombieMinerCost;
+
+ const miner = {
+ id: `zombie_miner_${this.zombieMiners.length + 1}`,
+ name: this.generateZombieName(),
+ assignedMine: null,
+ assignedDepth: 0,
+ efficiency: 1.0, // 0.5 - 2.0
+ loyalty: 50, // 0-100
+ yieldPerHour: 5, // Base yield
+ level: 1,
+ experience: 0
+ };
+
+ this.zombieMiners.push(miner);
+
+ // Recalculate automation
+ this.updateAutomationYield();
+
+ this.game.showMessage(
+ `Hired ${miner.name}! (${this.zombieMiners.length}/${this.maxZombieMiners})`
+ );
+
+ return { success: true, miner: miner };
+ }
+
+ /**
+ * Generate random zombie miner name
+ */
+ generateZombieName() {
+ const prefixes = ['Grumpy', 'Rusty', 'Dusty', 'Grumbly', 'Moaning', 'Shuffling'];
+ const suffixes = ['Zed', 'Mort', 'Bones', 'Guts', 'Picks', 'Drills'];
+
+ const prefix = Phaser.Utils.Array.GetRandom(prefixes);
+ const suffix = Phaser.Utils.Array.GetRandom(suffixes);
+
+ return `${prefix} ${suffix}`;
+ }
+
+ /**
+ * Assign zombie miner to specific mine & depth
+ */
+ assignZombieMiner(minerId, mineId, depth) {
+ const miner = this.zombieMiners.find(m => m.id === minerId);
+
+ if (!miner) {
+ return { success: false, message: 'Zombie miner not found!' };
+ }
+
+ // Check if mine exists
+ const mine = this.game.miningSystem.getMineInfo(mineId);
+ if (!mine) {
+ return { success: false, message: 'Mine not found!' };
+ }
+
+ // Check if depth is unlocked
+ const maxDepth = this.game.miningSystem.maxDepthReached || 0;
+ if (depth > maxDepth) {
+ return {
+ success: false,
+ message: `Must explore depth ${depth} first!`
+ };
+ }
+
+ // Assign miner
+ miner.assignedMine = mineId;
+ miner.assignedDepth = depth;
+
+ // Update automation
+ this.updateAutomationYield();
+
+ this.game.showMessage(
+ `${miner.name} assigned to ${mine.name} - Depth ${depth}`
+ );
+
+ return { success: true };
+ }
+
+ /**
+ * Unassign zombie miner (return to surface)
+ */
+ unassignZombieMiner(minerId) {
+ const miner = this.zombieMiners.find(m => m.id === minerId);
+
+ if (!miner) {
+ return { success: false };
+ }
+
+ miner.assignedMine = null;
+ miner.assignedDepth = 0;
+
+ // Update automation
+ this.updateAutomationYield();
+
+ this.game.showMessage(
+ `${miner.name} returned to surface.`
+ );
+
+ return { success: true };
+ }
+
+ /**
+ * Update total automation yield
+ */
+ updateAutomationYield() {
+ let totalYield = 0;
+
+ this.zombieMiners.forEach(miner => {
+ if (miner.assignedMine && miner.assignedDepth > 0) {
+ // Base yield
+ let hourlyYield = miner.yieldPerHour;
+
+ // Depth bonus (+10% per 10 levels)
+ const depthBonus = (miner.assignedDepth / 10) * 0.1;
+ hourlyYield *= (1 + depthBonus);
+
+ // Efficiency factor
+ hourlyYield *= miner.efficiency;
+
+ // Loyalty factor (50% loyalty = 0.5x yield, 100% = 1.5x yield)
+ const loyaltyFactor = 0.5 + (miner.loyalty / 100);
+ hourlyYield *= loyaltyFactor;
+
+ // Equipment bonuses
+ if (this.zombieEquipment.pickaxe_tier > 1) {
+ hourlyYield *= (1 + (this.zombieEquipment.pickaxe_tier - 1) * 0.25);
+ }
+ if (this.zombieEquipment.cart) {
+ hourlyYield *= 1.5; // 50% faster collection
+ }
+
+ totalYield += hourlyYield;
+ }
+ });
+
+ this.totalYieldPerHour = Math.floor(totalYield);
+ this.automationActive = (totalYield > 0);
+ }
+
+ /**
+ * Collect automated mining resources
+ */
+ collectAutomatedYield() {
+ if (!this.automationActive) {
+ return {
+ success: false,
+ message: 'No zombie miners assigned!'
+ };
+ }
+
+ // Calculate time since last collection
+ const hoursSinceLastCollection = this.getHoursSinceLastCollection();
+
+ if (hoursSinceLastCollection < 0.1) {
+ return {
+ success: false,
+ message: 'Collected too recently! Wait a bit.'
+ };
+ }
+
+ const resources = {};
+
+ // Collect from each zombie
+ this.zombieMiners.forEach(miner => {
+ if (miner.assignedMine && miner.assignedDepth > 0) {
+ // Get mine info
+ const mine = this.game.miningSystem.getMineInfo(miner.assignedMine);
+
+ // Determine ore type based on depth
+ const oreType = this.getOreTypeForDepth(mine, miner.assignedDepth);
+
+ // Calculate yield
+ const hourlyYield = miner.yieldPerHour * miner.efficiency *
+ (0.5 + miner.loyalty / 100);
+
+ const amount = Math.floor(hourlyYield * hoursSinceLastCollection);
+
+ if (amount > 0) {
+ resources[oreType] = (resources[oreType] || 0) + amount;
+
+ // Grant XP to miner
+ miner.experience += amount;
+ this.checkMinerLevelUp(miner);
+ }
+ }
+ });
+
+ // Add resources to inventory
+ let totalCollected = 0;
+ for (const [ore, amount] of Object.entries(resources)) {
+ this.player.inventory.addItem(ore, amount);
+ totalCollected += amount;
+ }
+
+ // Update last collection time
+ this.lastCollectionTime = this.game.time.currentTime;
+
+ // Show collection message
+ const resourceList = Object.entries(resources)
+ .map(([ore, amount]) => `${amount}x ${ore}`)
+ .join(', ');
+
+ this.game.showMessage(
+ `Collected: ${resourceList} (${hoursSinceLastCollection.toFixed(1)}h)`
+ );
+
+ return {
+ success: true,
+ resources: resources,
+ totalAmount: totalCollected,
+ hours: hoursSinceLastCollection
+ };
+ }
+
+ /**
+ * Get ore type based on mine and depth
+ */
+ getOreTypeForDepth(mine, depth) {
+ // Logic from mine zones
+ if (depth <= 25) return 'copper_ore';
+ if (depth <= 50) return 'iron_ore';
+ if (depth <= 75) return 'gold_ore';
+ return 'diamond_ore';
+ }
+
+ /**
+ * Check if miner levels up
+ */
+ checkMinerLevelUp(miner) {
+ const xpRequired = miner.level * 100;
+
+ if (miner.experience >= xpRequired) {
+ miner.level++;
+ miner.experience = 0;
+
+ // Bonuses per level
+ miner.yieldPerHour += 1;
+ miner.efficiency += 0.05;
+
+ this.game.showMessage(
+ `${miner.name} leveled up! (Lv.${miner.level})`
+ );
+
+ this.updateAutomationYield();
+ }
+ }
+
+ /**
+ * Feed zombie miner (restore loyalty)
+ */
+ feedZombieMiner(minerId, foodType) {
+ const miner = this.zombieMiners.find(m => m.id === minerId);
+
+ if (!miner) {
+ return { success: false };
+ }
+
+ // Check if player has food
+ if (!this.player.inventory.hasItem(foodType)) {
+ return {
+ success: false,
+ message: 'You don\'t have this food!'
+ };
+ }
+
+ // Remove food
+ this.player.inventory.removeItem(foodType, 1);
+
+ // Loyalty bonus based on food quality
+ const loyaltyGain = this.getFoodLoyaltyValue(foodType);
+ miner.loyalty = Math.min(100, miner.loyalty + loyaltyGain);
+
+ this.game.showMessage(
+ `${miner.name} ate ${foodType}. Loyalty +${loyaltyGain} (${miner.loyalty}/100)`
+ );
+
+ // Update automation
+ this.updateAutomationYield();
+
+ return { success: true, loyaltyGain: loyaltyGain };
+ }
+
+ /**
+ * Get food loyalty value
+ */
+ getFoodLoyaltyValue(foodType) {
+ const foodValues = {
+ 'brain': 20, // Best food
+ 'meat': 15,
+ 'bread': 10,
+ 'vegetables': 5
+ };
+
+ return foodValues[foodType] || 5;
+ }
+
+ /**
+ * Upgrade zombie equipment
+ */
+ upgradeEquipment(equipmentType) {
+ const costs = {
+ pickaxe_tier: 3000,
+ helmet_lamp: 2000,
+ oxygen_tank: 2500,
+ cart: 4000
+ };
+
+ const cost = costs[equipmentType];
+
+ if (!cost) {
+ return { success: false, message: 'Invalid equipment!' };
+ }
+
+ // Check if already owned (except pickaxe tiers)
+ if (equipmentType !== 'pickaxe_tier') {
+ if (this.zombieEquipment[equipmentType]) {
+ return {
+ success: false,
+ message: 'Already owned!'
+ };
+ }
+ } else {
+ // Check pickaxe tier limit
+ if (this.zombieEquipment.pickaxe_tier >= 5) {
+ return {
+ success: false,
+ message: 'Pickaxe already max tier!'
+ };
+ }
+ }
+
+ // Check money
+ if (this.player.money < cost) {
+ return {
+ success: false,
+ message: `Need ${cost}g!`
+ };
+ }
+
+ // Purchase
+ this.player.money -= cost;
+
+ if (equipmentType === 'pickaxe_tier') {
+ this.zombieEquipment.pickaxe_tier++;
+ } else {
+ this.zombieEquipment[equipmentType] = true;
+ }
+
+ // Update automation
+ this.updateAutomationYield();
+
+ this.game.showMessage(
+ `Upgraded zombie equipment: ${equipmentType}! ${cost}g`
+ );
+
+ return { success: true };
+ }
+
+ /**
+ * Get hours since last collection
+ */
+ getHoursSinceLastCollection() {
+ if (!this.lastCollectionTime) {
+ this.lastCollectionTime = this.game.time.currentTime;
+ return 0;
+ }
+
+ const secondsElapsed = this.game.time.currentTime - this.lastCollectionTime;
+ return secondsElapsed / 3600;
+ }
+
+ /**
+ * Get automation UI data
+ */
+ getAutomationUIData() {
+ return {
+ zombieMiners: this.zombieMiners,
+ maxZombieMiners: this.maxZombieMiners,
+ totalYieldPerHour: this.totalYieldPerHour,
+ automationActive: this.automationActive,
+ equipment: this.zombieEquipment,
+ canHireMore: this.zombieMiners.length < this.maxZombieMiners,
+ hireCost: this.zombieMinerCost,
+ hoursSinceCollection: this.getHoursSinceLastCollection()
+ };
+ }
+
+ /**
+ * Update (passive decay of loyalty)
+ */
+ update(deltaTime) {
+ // Loyalty slowly decays if zombies are working
+ this.zombieMiners.forEach(miner => {
+ if (miner.assignedMine && miner.assignedDepth > 0) {
+ // Lose 1 loyalty per hour worked
+ const loyaltyLoss = (deltaTime / 3600);
+ miner.loyalty = Math.max(0, miner.loyalty - loyaltyLoss);
+ }
+ });
+
+ // Recalculate if needed
+ if (this.automationActive) {
+ this.updateAutomationYield();
+ }
+ }
+}
+
+
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ZombieScoutLevelingSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ZombieScoutLevelingSystem.js
new file mode 100644
index 000000000..e4da080f2
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ZombieScoutLevelingSystem.js
@@ -0,0 +1,340 @@
+/**
+ * ZOMBIE SCOUT LEVELING SYSTEM (1-20)
+ * XP progression, stat increases, level-up rewards
+ * Integrates with ZombieScout companion
+ */
+
+export class ZombieScoutLevelingSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Current stats
+ this.currentLevel = 1;
+ this.currentXP = 0;
+ this.skillPoints = 0;
+
+ // Stats per level
+ this.baseStats = {
+ health: 50,
+ attack: 5,
+ digSpeed: 1.0,
+ moveSpeed: 100,
+ defense: 0
+ };
+
+ this.currentStats = { ...this.baseStats };
+
+ // XP curve
+ this.xpRequirements = this.generateXPCurve();
+
+ // Level rewards
+ this.levelRewards = new Map();
+
+ this.init();
+ }
+
+ init() {
+ this.initializeLevelRewards();
+ }
+
+ /**
+ * Generate XP requirements for each level (exponential curve)
+ */
+ generateXPCurve() {
+ const curve = [0]; // Level 1 = 0 XP
+
+ for (let level = 2; level <= 20; level++) {
+ // Formula: baseXP * (level ^ 1.5)
+ const baseXP = 100;
+ const requiredXP = Math.floor(baseXP * Math.pow(level, 1.5));
+ curve.push(requiredXP);
+ }
+
+ return curve;
+ }
+
+ /**
+ * Initialize rewards for each level
+ */
+ initializeLevelRewards() {
+ // Skill points every level
+ for (let level = 2; level <= 20; level++) {
+ this.levelRewards.set(level, {
+ skillPoints: 1,
+ statBonus: this.getStatBonusForLevel(level),
+ specialUnlock: this.getSpecialUnlockForLevel(level)
+ });
+ }
+ }
+
+ /**
+ * Get stat bonus for specific level
+ */
+ getStatBonusForLevel(level) {
+ const bonuses = {
+ health: 10, // +10 HP per level
+ attack: 1, // +1 attack per level
+ digSpeed: 0.05, // +5% dig speed per level
+ defense: 0.5 // +0.5 defense per level
+ };
+
+ // Every 5 levels: bonus move speed
+ if (level % 5 === 0) {
+ bonuses.moveSpeed = 10; // +10 speed at levels 5, 10, 15, 20
+ }
+
+ return bonuses;
+ }
+
+ /**
+ * Get special unlock for milestone levels
+ */
+ getSpecialUnlockForLevel(level) {
+ const unlocks = {
+ 5: { type: 'skill', name: 'Basic Attack', description: 'Zombie can attack enemies' },
+ 10: { type: 'skill', name: 'Speed Dig', description: 'Dig 50% faster for 10 seconds' },
+ 15: { type: 'skill', name: 'Treasure Sense', description: 'Reveal nearby buried items' },
+ 20: { type: 'evolution', name: 'Scout Commander', description: 'Zombie becomes elite unit' }
+ };
+
+ return unlocks[level] || null;
+ }
+
+ /**
+ * Award XP to Zombie Scout
+ */
+ awardXP(amount, source = 'general') {
+ this.currentXP += amount;
+
+ // Visual feedback
+ this.showXPGain(amount, source);
+
+ // Check for level up
+ this.checkLevelUp();
+
+ console.log(`+${amount} XP from ${source} (${this.currentXP}/${this.getRequiredXP()})`);
+ }
+
+ /**
+ * Check if Zombie Scout should level up
+ */
+ checkLevelUp() {
+ if (this.currentLevel >= 20) return; // Max level cap
+
+ const requiredXP = this.getRequiredXP();
+
+ if (this.currentXP >= requiredXP) {
+ this.levelUp();
+ }
+ }
+
+ /**
+ * Level up Zombie Scout
+ */
+ levelUp() {
+ this.currentLevel++;
+ this.currentXP -= this.getRequiredXP(this.currentLevel - 1); // Carry over excess XP
+
+ // Apply stat bonuses
+ const rewards = this.levelRewards.get(this.currentLevel);
+ if (rewards) {
+ // Apply stat increases
+ for (const [stat, bonus] of Object.entries(rewards.statBonus)) {
+ this.currentStats[stat] += bonus;
+ }
+
+ // Award skill points
+ this.skillPoints += rewards.skillPoints;
+
+ // Special unlock
+ if (rewards.specialUnlock) {
+ this.unlockSpecial(rewards.specialUnlock);
+ }
+ }
+
+ // Visual effects
+ this.playLevelUpEffects();
+
+ // Notification
+ this.scene.uiSystem?.showNotification(
+ `Zombie Scout reached Level ${this.currentLevel}!`,
+ 'level_up',
+ { skillPoints: this.skillPoints }
+ );
+
+ // Update Zombie Scout sprite if evolution
+ if (this.currentLevel === 20) {
+ this.evolveScout();
+ }
+
+ console.log(`๐ LEVEL UP! Zombie Scout is now Level ${this.currentLevel}`);
+ console.log(`Stats:`, this.currentStats);
+ console.log(`Skill Points: ${this.skillPoints}`);
+
+ // Quest check
+ this.scene.questSystem?.checkScoutLevel(this.currentLevel);
+ }
+
+ /**
+ * Unlock special ability or feature
+ */
+ unlockSpecial(unlock) {
+ console.log(`โจ Special Unlock: ${unlock.name} - ${unlock.description}`);
+
+ if (unlock.type === 'skill') {
+ this.scene.zombieScoutSkills?.unlockSkill(unlock.name);
+ } else if (unlock.type === 'evolution') {
+ // Mark for visual evolution
+ this.isEvolved = true;
+ }
+
+ this.scene.uiSystem?.showNotification(
+ `Unlocked: ${unlock.name}!`,
+ 'unlock',
+ { description: unlock.description }
+ );
+ }
+
+ /**
+ * Visual evolution at level 20
+ */
+ evolveScout() {
+ // Update sprite to "Commander" version
+ const scout = this.scene.zombieScout;
+ if (scout) {
+ scout.setTexture('zombie_scout_commander');
+ scout.setScale(scout.scale * 1.2); // Slightly larger
+
+ // VFX: Evolution animation
+ this.scene.vfxSystem?.playEffect('evolution', scout.x, scout.y);
+ }
+ }
+
+ /**
+ * Award XP for different actions
+ */
+ onCombatKill(enemyType) {
+ const xpValues = {
+ 'zombie': 10,
+ 'raider': 25,
+ 'mutant': 50,
+ 'boss': 200
+ };
+
+ this.awardXP(xpValues[enemyType] || 10, 'combat');
+ }
+
+ onDigComplete(itemRarity) {
+ const xpValues = {
+ 'common': 5,
+ 'uncommon': 15,
+ 'rare': 30,
+ 'legendary': 100
+ };
+
+ this.awardXP(xpValues[itemRarity] || 5, 'digging');
+ }
+
+ onExplorationProgress() {
+ // Award XP for exploring new areas
+ this.awardXP(20, 'exploration');
+ }
+
+ onQuestComplete(questDifficulty) {
+ const xpValues = {
+ 'easy': 50,
+ 'medium': 100,
+ 'hard': 200,
+ 'story': 300
+ };
+
+ this.awardXP(xpValues[questDifficulty] || 50, 'quest');
+ }
+
+ /**
+ * Get required XP for current or specific level
+ */
+ getRequiredXP(level = this.currentLevel) {
+ return this.xpRequirements[level] || 0;
+ }
+
+ /**
+ * Get XP progress percentage
+ */
+ getXPProgress() {
+ if (this.currentLevel >= 20) return 100;
+
+ const required = this.getRequiredXP();
+ return Math.min(100, Math.floor((this.currentXP / required) * 100));
+ }
+
+ /**
+ * Visual effects
+ */
+ showXPGain(amount, source) {
+ // Floating text above Zombie Scout
+ const scout = this.scene.zombieScout;
+ if (scout) {
+ this.scene.vfxSystem?.floatingText(`+${amount} XP`, scout.x, scout.y - 30, '#FFD700');
+ }
+ }
+
+ playLevelUpEffects() {
+ const scout = this.scene.zombieScout;
+ if (!scout) return;
+
+ // VFX: Level up particles
+ this.scene.vfxSystem?.playEffect('level_up', scout.x, scout.y);
+
+ // SFX: Level up sound
+ this.scene.soundSystem?.play('level_up');
+
+ // Camera shake
+ this.scene.cameras.main.shake(200, 0.005);
+
+ // Flash
+ this.scene.cameras.main.flash(300, 255, 215, 0); // Gold flash
+ }
+
+ /**
+ * Get current level data
+ */
+ getLevelData() {
+ return {
+ level: this.currentLevel,
+ xp: this.currentXP,
+ requiredXP: this.getRequiredXP(),
+ progress: this.getXPProgress(),
+ stats: { ...this.currentStats },
+ skillPoints: this.skillPoints,
+ isMaxLevel: this.currentLevel >= 20,
+ nextUnlock: this.levelRewards.get(this.currentLevel + 1)?.specialUnlock || null
+ };
+ }
+
+ /**
+ * Save/Load support
+ */
+ getSaveData() {
+ return {
+ currentLevel: this.currentLevel,
+ currentXP: this.currentXP,
+ skillPoints: this.skillPoints,
+ currentStats: this.currentStats,
+ isEvolved: this.isEvolved || false
+ };
+ }
+
+ loadSaveData(data) {
+ this.currentLevel = data.currentLevel || 1;
+ this.currentXP = data.currentXP || 0;
+ this.skillPoints = data.skillPoints || 0;
+ this.currentStats = data.currentStats || { ...this.baseStats };
+ this.isEvolved = data.isEvolved || false;
+
+ // Apply visual evolution if loaded at level 20
+ if (this.isEvolved) {
+ this.evolveScout();
+ }
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ZombieSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ZombieSystem.js
new file mode 100644
index 000000000..8172976d3
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ZombieSystem.js
@@ -0,0 +1,875 @@
+/**
+ * ZombieSystem.js
+ * ===============
+ * KRVAVA ลฝETEV - Zombie Worker Management System
+ *
+ * Core Concept:
+ * Player is "Alfa" (hybrid virus) - can tame wild zombies
+ * Zombies can be assigned tasks: farming, mining, gathering, guarding
+ * Zombies level up, specialize, get tired, and eventually decay
+ *
+ * Features:
+ * - Zombie taming (Alfa scent system)
+ * - AI pathfinding & task execution
+ * - Skill specialization (Farmer, Miner, Guard, Gatherer)
+ * - Stamina & decay mechanics
+ * - Grave resting system (regeneration)
+ * - Work commands (follow, farm, mine, gather, guard)
+ *
+ * Uses: zombie_varieties_pack_tiled_1766101086057.tsx
+ * zombie_workers_2x2_grids_1766099189858.tsx
+ * smart_zombies_working_1766097073226.tsx
+ * specialty_zombie_workers_detailed_1766097635926.tsx
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-22
+ */
+
+export default class ZombieSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Zombie registry
+ this.zombies = new Map(); // id -> zombie data
+ this.wildZombies = new Set(); // Untamed zombies
+ this.tamedZombies = new Set(); // Player's zombies
+
+ // Alfa system
+ this.alfaScent = 100; // Player's Alfa power (0-100)
+ this.alfaRange = 150; // Taming range (pixels)
+
+ // Task queues
+ this.taskQueue = new Map(); // zombieId -> task
+
+ // Graves
+ this.graves = new Map(); // position -> grave data
+
+ // Zombie definitions
+ this.zombieTypes = this.defineZombieTypes();
+ this.skillTrees = this.defineSkillTrees();
+
+ // Timing constants
+ this.DECAY_RATE = 0.1; // HP loss per second when not resting
+ this.STAMINA_DRAIN_RATE = 5; // Stamina loss per second when working
+ this.STAMINA_REGEN_RATE = 20; // Stamina gain per second when resting
+ this.GRAVE_REST_BONUS = 2.0; // 2x regen in grave
+
+ console.log('๐ง ZombieSystem initialized - ALFA ACTIVE');
+ }
+
+ defineZombieTypes() {
+ return {
+ basic: {
+ name: 'Basic Zombie',
+ baseHP: 100,
+ baseStamina: 100,
+ baseSpeed: 60,
+ baseWorkSpeed: 1.0,
+ sprite: 'zombie_varieties_pack', // Frame 0
+ tameDifficulty: 1
+ },
+ worker: {
+ name: 'Zombie Worker',
+ baseHP: 120,
+ baseStamina: 150,
+ baseSpeed: 70,
+ baseWorkSpeed: 1.2,
+ sprite: 'zombie_workers_2x2_grids', // Frame 0
+ tameDifficulty: 2
+ },
+ smart: {
+ name: 'Smart Zombie',
+ baseHP: 150,
+ baseStamina: 120,
+ baseSpeed: 80,
+ baseWorkSpeed: 1.5,
+ sprite: 'smart_zombies_working',
+ tameDifficulty: 3,
+ canLearn: true // Levels faster
+ },
+ elite: {
+ name: 'Elite Zombie',
+ baseHP: 200,
+ baseStamina: 200,
+ baseSpeed: 100,
+ baseWorkSpeed: 2.0,
+ sprite: 'elite_zombie',
+ tameDifficulty: 5,
+ canLearn: true,
+ specialAbility: 'multitask' // Can do 2 tasks
+ }
+ };
+ }
+
+ defineSkillTrees() {
+ return {
+ farmer: {
+ name: 'Farmer Zombie',
+ tasks: ['plant', 'harvest', 'water', 'fertilize'],
+ levelBonuses: {
+ 1: { workSpeed: 1.0 },
+ 5: { workSpeed: 1.2, yieldBonus: 1.1 },
+ 10: { workSpeed: 1.5, yieldBonus: 1.3, autoPlant: true },
+ 15: { workSpeed: 2.0, yieldBonus: 1.5, qualityBonus: 1.2 }
+ },
+ sprite: 'specialty_zombie_workers', // Frame 0
+ color: 0x00FF00 // Green tint
+ },
+ miner: {
+ name: 'Miner Zombie',
+ tasks: ['mine', 'quarry', 'smelt'],
+ levelBonuses: {
+ 1: { workSpeed: 1.0 },
+ 5: { workSpeed: 1.2, oreBonus: 1.1 },
+ 10: { workSpeed: 1.5, oreBonus: 1.3, rareOreChance: 0.1 },
+ 15: { workSpeed: 2.0, oreBonus: 1.5, gemChance: 0.2 }
+ },
+ sprite: 'specialty_zombie_workers', // Frame 1
+ color: 0x888888 // Gray tint
+ },
+ gatherer: {
+ name: 'Gatherer Zombie',
+ tasks: ['gather', 'forage', 'loot'],
+ levelBonuses: {
+ 1: { workSpeed: 1.0 },
+ 5: { workSpeed: 1.2, gatherRadius: 1.2 },
+ 10: { workSpeed: 1.5, gatherRadius: 1.5, rareItemChance: 0.1 },
+ 15: { workSpeed: 2.0, gatherRadius: 2.0, doubleGather: 0.3 }
+ },
+ sprite: 'specialty_zombie_workers', // Frame 2
+ color: 0xFFFF00 // Yellow tint
+ },
+ guard: {
+ name: 'Guard Zombie',
+ tasks: ['guard', 'patrol', 'attack'],
+ levelBonuses: {
+ 1: { damage: 10, defense: 5 },
+ 5: { damage: 20, defense: 10, detectRange: 1.2 },
+ 10: { damage: 35, defense: 20, detectRange: 1.5, counterAttack: true },
+ 15: { damage: 50, defense: 30, detectRange: 2.0, areaAttack: true }
+ },
+ sprite: 'specialty_zombie_workers', // Frame 3
+ color: 0xFF0000 // Red tint
+ }
+ };
+ }
+
+ // ===== ZOMBIE SPAWNING =====
+
+ spawnWildZombie(x, y, type = 'basic') {
+ const zombieType = this.zombieTypes[type];
+ if (!zombieType) return null;
+
+ const zombie = {
+ id: this.generateId(),
+ type: type,
+ state: 'wild', // wild, tamed, working, resting, decaying
+
+ // Position
+ x: x,
+ y: y,
+
+ // Stats
+ hp: zombieType.baseHP,
+ maxHP: zombieType.baseHP,
+ stamina: zombieType.baseStamina,
+ maxStamina: zombieType.baseStamina,
+
+ // Progression
+ level: 1,
+ xp: 0,
+ xpToNextLevel: 100,
+ specialization: null, // farmer, miner, gatherer, guard
+
+ // Work
+ currentTask: null,
+ workSpeed: zombieType.baseWorkSpeed,
+ efficiency: 1.0,
+
+ // Decay
+ decayTimer: 24 * 60 * 60 * 1000, // 24 hours to decay
+ decayRate: this.DECAY_RATE,
+
+ // Behavior
+ speed: zombieType.baseSpeed,
+ aggression: 0.3, // Wild zombies slightly aggressive
+ loyalty: 0, // Increases when tamed
+
+ // Visual
+ sprite: null,
+ tint: 0xFFFFFF,
+
+ // Timestamps
+ spawnTime: Date.now(),
+ lastWorked: null,
+ lastRested: null
+ };
+
+ // Create sprite
+ zombie.sprite = this.createZombieSprite(zombie, zombieType);
+
+ // Add to registry
+ this.zombies.set(zombie.id, zombie);
+ this.wildZombies.add(zombie.id);
+
+ console.log(`๐ง Spawned wild ${zombieType.name} at (${x}, ${y})`);
+ return zombie;
+ }
+
+ createZombieSprite(zombie, zombieType) {
+ const sprite = this.scene.add.sprite(zombie.x, zombie.y, zombieType.sprite);
+
+ sprite.setData('zombieId', zombie.id);
+ sprite.setInteractive();
+
+ // Physics
+ this.scene.physics.add.existing(sprite);
+ sprite.body.setCollideWorldBounds(true);
+
+ // Click handler
+ sprite.on('pointerdown', () => {
+ this.onZombieClicked(zombie);
+ });
+
+ // Health bar
+ this.createHealthBar(sprite);
+
+ return sprite;
+ }
+
+ createHealthBar(sprite) {
+ const bar = this.scene.add.graphics();
+ bar.fillStyle(0xFF0000);
+ bar.fillRect(-20, -30, 40, 4);
+ sprite.healthBar = bar;
+ sprite.healthBar.x = sprite.x;
+ sprite.healthBar.y = sprite.y;
+ }
+
+ // ===== ALFA SYSTEM (TAMING) =====
+
+ canTameZombie(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) {
+ return { canTame: false, reason: 'Zombie not found' };
+ }
+
+ if (zombie.state !== 'wild') {
+ return { canTame: false, reason: 'Already tamed' };
+ }
+
+ // Check distance (Alfa scent range)
+ const distance = Phaser.Math.Distance.Between(
+ this.scene.player.x, this.scene.player.y,
+ zombie.x, zombie.y
+ );
+
+ if (distance > this.alfaRange) {
+ return { canTame: false, reason: 'Too far - get closer!' };
+ }
+
+ // Check Alfa power
+ const zombieType = this.zombieTypes[zombie.type];
+ const requiredAlfaPower = zombieType.tameDifficulty * 15;
+
+ if (this.alfaScent < requiredAlfaPower) {
+ return {
+ canTame: false,
+ reason: `Need ${requiredAlfaPower} Alfa power (have ${this.alfaScent})`
+ };
+ }
+
+ return { canTame: true };
+ }
+
+ tameZombie(zombieId) {
+ const check = this.canTameZombie(zombieId);
+ if (!check.canTame) {
+ this.scene.uiSystem?.showError(check.reason);
+ return false;
+ }
+
+ const zombie = this.zombies.get(zombieId);
+ const zombieType = this.zombieTypes[zombie.type];
+
+ // Taming animation
+ if (this.scene.particleSystem) {
+ this.scene.particleSystem.createTamingEffect(zombie.x, zombie.y);
+ }
+
+ // Update zombie state
+ zombie.state = 'tamed';
+ zombie.loyalty = 50; // Starts at 50, increases with work
+ zombie.aggression = 0;
+
+ // Remove from wild, add to tamed
+ this.wildZombies.delete(zombieId);
+ this.tamedZombies.add(zombieId);
+
+ // Visual feedback
+ zombie.sprite.setTint(0x00FF88); // Green tint for tamed
+
+ // Notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ง Zombie Tamed!',
+ message: `${zombieType.name} joined your workforce!`,
+ icon: 'zombie',
+ duration: 4000,
+ color: '#00FF00'
+ });
+
+ // Emit event
+ this.scene.events.emit('zombieTamed', { zombie });
+
+ // Consume Alfa power
+ const cost = zombieType.tameDifficulty * 10;
+ this.alfaScent = Math.max(0, this.alfaScent - cost);
+
+ console.log(`๐ง Tamed ${zombieType.name} - Loyalty: ${zombie.loyalty}`);
+ return true;
+ }
+
+ // ===== TASK ASSIGNMENT =====
+
+ assignTask(zombieId, task) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie || zombie.state !== 'tamed') {
+ return false;
+ }
+
+ // Check if zombie has specialization for this task
+ if (zombie.specialization) {
+ const spec = this.skillTrees[zombie.specialization];
+ if (!spec.tasks.includes(task.type)) {
+ this.scene.uiSystem?.showError(`This zombie specializes in ${zombie.specialization} tasks!`);
+ return false;
+ }
+ }
+
+ // Assign task
+ zombie.currentTask = task;
+ zombie.state = 'working';
+ this.taskQueue.set(zombieId, task);
+
+ console.log(`๐ง ${zombie.id} assigned task:`, task.type);
+ return true;
+ }
+
+ executeTask(zombie, delta) {
+ const task = zombie.currentTask;
+ if (!task) return;
+
+ const deltaSeconds = delta / 1000;
+
+ // Drain stamina
+ zombie.stamina -= this.STAMINA_DRAIN_RATE * deltaSeconds;
+
+ // If exhausted, stop working
+ if (zombie.stamina <= 0) {
+ zombie.state = 'idle';
+ zombie.currentTask = null;
+ this.scene.uiSystem?.showNotification({
+ title: 'Zombie Exhausted!',
+ message: `Zombie ${zombie.id} needs rest`,
+ icon: 'zombie',
+ duration: 2000
+ });
+ return;
+ }
+
+ // Execute task based on type
+ switch (task.type) {
+ case 'farm':
+ this.executeFarmTask(zombie, task, deltaSeconds);
+ break;
+ case 'mine':
+ this.executeMineTask(zombie, task, deltaSeconds);
+ break;
+ case 'gather':
+ this.executeGatherTask(zombie, task, deltaSeconds);
+ break;
+ case 'guard':
+ this.executeGuardTask(zombie, task, deltaSeconds);
+ break;
+ }
+ }
+
+ executeFarmTask(zombie, task, deltaSeconds) {
+ // Move to farm plot
+ this.moveTowardsTarget(zombie, task.targetX, task.targetY);
+
+ // If close enough, work
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, task.targetX, task.targetY
+ );
+
+ if (distance < 20) {
+ task.progress = (task.progress || 0) + (zombie.workSpeed * deltaSeconds);
+
+ // Complete task
+ if (task.progress >= task.requiredProgress) {
+ this.completeTask(zombie, task);
+ }
+ }
+ }
+
+ executeMineTask(zombie, task, deltaSeconds) {
+ this.moveTowardsTarget(zombie, task.targetX, task.targetY);
+
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, task.targetX, task.targetY
+ );
+
+ if (distance < 20) {
+ task.progress = (task.progress || 0) + (zombie.workSpeed * 0.8 * deltaSeconds);
+
+ if (task.progress >= task.requiredProgress) {
+ this.completeTask(zombie, task);
+ }
+ }
+ }
+
+ executeGatherTask(zombie, task, deltaSeconds) {
+ this.moveTowardsTarget(zombie, task.targetX, task.targetY);
+
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, task.targetX, task.targetY
+ );
+
+ if (distance < 30) {
+ task.progress = (task.progress || 0) + (zombie.workSpeed * 1.2 * deltaSeconds);
+
+ if (task.progress >= task.requiredProgress) {
+ this.completeTask(zombie, task);
+ }
+ }
+ }
+
+ executeGuardTask(zombie, task, deltaSeconds) {
+ // Patrol area
+ if (!task.patrolTarget || zombie.reachedPatrol) {
+ task.patrolTarget = {
+ x: task.centerX + Phaser.Math.Between(-task.radius, task.radius),
+ y: task.centerY + Phaser.Math.Between(-task.radius, task.radius)
+ };
+ zombie.reachedPatrol = false;
+ }
+
+ this.moveTowardsTarget(zombie, task.patrolTarget.x, task.patrolTarget.y);
+
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, task.patrolTarget.x, task.patrolTarget.y
+ );
+
+ if (distance < 10) {
+ zombie.reachedPatrol = true;
+ }
+
+ // Check for enemies
+ this.detectEnemies(zombie, task.radius);
+ }
+
+ completeTask(zombie, task) {
+ // Award XP
+ const xpGain = task.xpReward || 20;
+ this.addXP(zombie, xpGain);
+
+ // Increase loyalty
+ zombie.loyalty = Math.min(100, zombie.loyalty + 1);
+
+ // Clear task
+ zombie.currentTask = null;
+ zombie.state = 'idle';
+ this.taskQueue.delete(zombie.id);
+
+ // Emit event
+ this.scene.events.emit('zombieTaskComplete', { zombie, task });
+
+ console.log(`โ
Zombie ${zombie.id} completed ${task.type} task (+${xpGain} XP)`);
+ }
+
+ // ===== LEVELING & SPECIALIZATION =====
+
+ addXP(zombie, amount) {
+ zombie.xp += amount;
+
+ // Check for level up
+ while (zombie.xp >= zombie.xpToNextLevel) {
+ this.levelUp(zombie);
+ }
+ }
+
+ levelUp(zombie) {
+ zombie.level++;
+ zombie.xp -= zombie.xpToNextLevel;
+ zombie.xpToNextLevel = Math.floor(zombie.xpToNextLevel * 1.5);
+
+ // Increase stats
+ zombie.maxHP += 10;
+ zombie.hp = zombie.maxHP;
+ zombie.maxStamina += 10;
+ zombie.stamina = zombie.maxStamina;
+ zombie.workSpeed += 0.1;
+
+ // Apply specialization bonuses
+ if (zombie.specialization) {
+ const spec = this.skillTrees[zombie.specialization];
+ const levelBonuses = spec.levelBonuses[zombie.level];
+
+ if (levelBonuses) {
+ Object.assign(zombie, levelBonuses);
+ }
+ }
+
+ // Notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ง LEVEL UP!',
+ message: `Zombie ${zombie.id} reached level ${zombie.level}!`,
+ icon: 'zombie',
+ duration: 3000,
+ color: '#FFD700'
+ });
+
+ console.log(`๐ Zombie ${zombie.id} leveled up to ${zombie.level}!`);
+ }
+
+ specializeZombie(zombieId, specialization) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return false;
+
+ if (zombie.specialization) {
+ this.scene.uiSystem?.showError('Already specialized!');
+ return false;
+ }
+
+ if (zombie.level < 3) {
+ this.scene.uiSystem?.showError('Zombie must be level 3+ to specialize');
+ return false;
+ }
+
+ const spec = this.skillTrees[specialization];
+ zombie.specialization = specialization;
+ zombie.sprite.setTint(spec.color);
+
+ this.scene.uiSystem?.showNotification({
+ title: 'โก Specialization Unlocked!',
+ message: `Zombie is now a ${spec.name}!`,
+ icon: 'zombie',
+ duration: 4000,
+ color: '#00FFFF'
+ });
+
+ console.log(`โก Zombie ${zombie.id} specialized as ${spec.name}`);
+ return true;
+ }
+
+ // ===== DECAY SYSTEM =====
+
+ updateDecay(zombie, delta) {
+ if (zombie.state === 'resting') return; // No decay when resting
+
+ const deltaSeconds = delta / 1000;
+
+ // Drain decay timer
+ zombie.decayTimer -= deltaSeconds * 1000;
+
+ // If timer expired, start losing HP
+ if (zombie.decayTimer <= 0) {
+ zombie.hp -= zombie.decayRate * deltaSeconds;
+
+ // Death check
+ if (zombie.hp <= 0) {
+ this.zombieDeath(zombie);
+ }
+ }
+ }
+
+ zombieDeath(zombie) {
+ console.log(`๐ Zombie ${zombie.id} has decayed completely`);
+
+ // Drop fertilizer (good for farming!)
+ if (this.scene.farmingSystem) {
+ this.scene.farmingSystem.spawnFertilizer(zombie.x, zombie.y, 5);
+ }
+
+ // Award player XP
+ if (this.scene.player) {
+ this.scene.player.addXP?.(zombie.level * 10);
+ }
+
+ // Remove zombie
+ zombie.sprite?.destroy();
+ zombie.healthBar?.destroy();
+ this.zombies.delete(zombie.id);
+ this.tamedZombies.delete(zombie.id);
+
+ // Notification
+ this.scene.uiSystem?.showNotification({
+ title: '๐ Zombie Decayed',
+ message: `Zombie turned to fertilizer (+${zombie.level * 10} XP)`,
+ icon: 'zombie',
+ duration: 3000
+ });
+ }
+
+ // ===== GRAVE SYSTEM =====
+
+ createGrave(x, y) {
+ const graveId = `${x}_${y}`;
+
+ if (this.graves.has(graveId)) {
+ this.scene.uiSystem?.showError('Grave already exists here!');
+ return false;
+ }
+
+ // Check if player has materials
+ if (this.scene.recipeSystem) {
+ const canCraft = this.scene.recipeSystem.canCraft('grave');
+ if (!canCraft.canCraft) {
+ this.scene.uiSystem?.showError(canCraft.reason);
+ return false;
+ }
+ }
+
+ const grave = {
+ id: graveId,
+ x: x,
+ y: y,
+ occupied: false,
+ zombieId: null,
+ sprite: this.scene.add.sprite(x, y, 'gravestone')
+ };
+
+ this.graves.set(graveId, grave);
+
+ console.log(`โฐ๏ธ Created grave at (${x}, ${y})`);
+ return true;
+ }
+
+ zombieRest(zombieId, graveId) {
+ const zombie = this.zombies.get(zombieId);
+ const grave = this.graves.get(graveId);
+
+ if (!zombie || !grave) return false;
+
+ if (grave.occupied) {
+ this.scene.uiSystem?.showError('Grave is occupied!');
+ return false;
+ }
+
+ // Move zombie to grave
+ zombie.state = 'resting';
+ zombie.sprite.setPosition(grave.x, grave.y);
+ zombie.sprite.setAlpha(0.5); // Semi-transparent
+
+ grave.occupied = true;
+ grave.zombieId = zombieId;
+
+ console.log(`๐ด Zombie ${zombieId} resting in grave`);
+ return true;
+ }
+
+ updateResting(zombie, delta) {
+ const deltaSeconds = delta / 1000;
+
+ // Regenerate stamina (2x faster in grave)
+ zombie.stamina = Math.min(
+ zombie.maxStamina,
+ zombie.stamina + this.STAMINA_REGEN_RATE * this.GRAVE_REST_BONUS * deltaSeconds
+ );
+
+ // Slow HP regen
+ zombie.hp = Math.min(
+ zombie.maxHP,
+ zombie.hp + 2 * deltaSeconds
+ );
+
+ // Restore decay timer
+ zombie.decayTimer += 1000 * deltaSeconds; // Add 1 second per second resting
+
+ // If fully rested, wake up
+ if (zombie.stamina >= zombie.maxStamina) {
+ this.wakeUpZombie(zombie);
+ }
+ }
+
+ wakeUpZombie(zombie) {
+ zombie.state = 'idle';
+ zombie.sprite.setAlpha(1.0);
+
+ // Find and free grave
+ for (const [graveId, grave] of this.graves.entries()) {
+ if (grave.zombieId === zombie.id) {
+ grave.occupied = false;
+ grave.zombieId = null;
+ break;
+ }
+ }
+
+ console.log(`๐ Zombie ${zombie.id} woke up refreshed!`);
+ }
+
+ // ===== MOVEMENT & PATHFINDING =====
+
+ moveTowardsTarget(zombie, targetX, targetY) {
+ const angle = Phaser.Math.Angle.Between(zombie.x, zombie.y, targetX, targetY);
+
+ const velocityX = Math.cos(angle) * zombie.speed;
+ const velocityY = Math.sin(angle) * zombie.speed;
+
+ zombie.sprite.body.setVelocity(velocityX, velocityY);
+
+ // Update position
+ zombie.x = zombie.sprite.x;
+ zombie.y = zombie.sprite.y;
+ }
+
+ detectEnemies(zombie, radius) {
+ // Guard zombies detect and attack enemies
+ if (!this.scene.enemies) return;
+
+ this.scene.enemies.getChildren().forEach(enemy => {
+ const distance = Phaser.Math.Distance.Between(
+ zombie.x, zombie.y, enemy.x, enemy.y
+ );
+
+ if (distance < radius) {
+ this.zombieAttack(zombie, enemy);
+ }
+ });
+ }
+
+ zombieAttack(zombie, enemy) {
+ const spec = zombie.specialization === 'guard' ?
+ this.skillTrees.guard.levelBonuses[zombie.level] : null;
+
+ const damage = spec ? spec.damage : 10;
+ enemy.takeDamage?.(damage);
+
+ // Gain XP for combat
+ this.addXP(zombie, 5);
+ }
+
+ // ===== COMMANDS =====
+
+ commandFollow(zombieId) {
+ const zombie = this.zombies.get(zombieId);
+ if (!zombie) return;
+
+ zombie.currentTask = {
+ type: 'follow',
+ targetX: this.scene.player.x,
+ targetY: this.scene.player.y
+ };
+ zombie.state = 'following';
+ }
+
+ commandGuardArea(zombieId, x, y, radius = 100) {
+ this.assignTask(zombieId, {
+ type: 'guard',
+ centerX: x,
+ centerY: y,
+ radius: radius,
+ requiredProgress: Infinity // Never ends
+ });
+ }
+
+ // ===== HELPER FUNCTIONS =====
+
+ generateId() {
+ return `zombie_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+ }
+
+ onZombieClicked(zombie) {
+ this.scene.events.emit('zombieSelected', { zombie });
+ // Show zombie info UI
+ }
+
+ // ===== GETTERS =====
+
+ getTamedZombies() {
+ return Array.from(this.tamedZombies).map(id => this.zombies.get(id));
+ }
+
+ getWorkingZombies() {
+ return this.getTamedZombies().filter(z => z.state === 'working');
+ }
+
+ getIdleZombies() {
+ return this.getTamedZombies().filter(z => z.state === 'idle');
+ }
+
+ getZombiesBySpecialization(spec) {
+ return this.getTamedZombies().filter(z => z.specialization === spec);
+ }
+
+ // ===== UPDATE =====
+
+ update(time, delta) {
+ for (const [id, zombie] of this.zombies.entries()) {
+ // Update based on state
+ switch (zombie.state) {
+ case 'working':
+ this.executeTask(zombie, delta);
+ this.updateDecay(zombie, delta);
+ break;
+
+ case 'resting':
+ this.updateResting(zombie, delta);
+ break;
+
+ case 'following':
+ this.moveTowardsTarget(zombie, this.scene.player.x, this.scene.player.y);
+ this.updateDecay(zombie, delta);
+ break;
+
+ case 'idle':
+ case 'tamed':
+ this.updateDecay(zombie, delta);
+ break;
+
+ case 'wild':
+ // Wild zombies wander
+ if (Math.random() < 0.01) {
+ const wanderX = zombie.x + Phaser.Math.Between(-50, 50);
+ const wanderY = zombie.y + Phaser.Math.Between(-50, 50);
+ this.moveTowardsTarget(zombie, wanderX, wanderY);
+ }
+ break;
+ }
+
+ // Update health bar
+ if (zombie.healthBar && zombie.sprite) {
+ zombie.healthBar.x = zombie.sprite.x;
+ zombie.healthBar.y = zombie.sprite.y;
+
+ const healthPercent = zombie.hp / zombie.maxHP;
+ zombie.healthBar.clear();
+ zombie.healthBar.fillStyle(healthPercent > 0.5 ? 0x00FF00 : 0xFF0000);
+ zombie.healthBar.fillRect(-20, -30, 40 * healthPercent, 4);
+ }
+ }
+
+ // Regenerate Alfa scent slowly
+ this.alfaScent = Math.min(100, this.alfaScent + 0.01 * (delta / 1000));
+ }
+
+ // ===== CLEANUP =====
+
+ destroy() {
+ for (const zombie of this.zombies.values()) {
+ zombie.sprite?.destroy();
+ zombie.healthBar?.destroy();
+ }
+
+ for (const grave of this.graves.values()) {
+ grave.sprite?.destroy();
+ }
+
+ this.zombies.clear();
+ this.wildZombies.clear();
+ this.tamedZombies.clear();
+ this.graves.clear();
+ this.taskQueue.clear();
+ }
+}
diff --git a/EMERGENCY_SYSTEMS_RECOVERY/ZombieWorkerSystem.js b/EMERGENCY_SYSTEMS_RECOVERY/ZombieWorkerSystem.js
new file mode 100644
index 000000000..51ed3ca3d
--- /dev/null
+++ b/EMERGENCY_SYSTEMS_RECOVERY/ZombieWorkerSystem.js
@@ -0,0 +1,239 @@
+/**
+ * ZOMBIE WORKER AI SYSTEM
+ * Tamed zombies lahko opravljajo delo (farming, mining)
+ */
+class ZombieWorkerSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.workers = [];
+ }
+
+ assignWork(zombie, workType, workRadius = 5) {
+ if (!zombie.isTamed) {
+ console.warn('โ ๏ธ Cannot assign work to untamed zombie!');
+ return false;
+ }
+
+ zombie.workerData = {
+ type: workType,
+ radius: workRadius,
+ centerX: zombie.gridX,
+ centerY: zombie.gridY,
+ energy: 100,
+ decayRate: 0.5,
+ workTimer: 0,
+ workInterval: 5000,
+ status: 'IDLE',
+ inventory: {
+ seeds: 0,
+ hoe: 0,
+ watering_can: 0,
+ pickaxe: 0
+ }
+ };
+
+ this.workers.push(zombie);
+ console.log(`๐ง Assigned ${zombie.type} as ${workType} worker`);
+ return true;
+ }
+
+ removeWorker(zombie) {
+ const index = this.workers.indexOf(zombie);
+ if (index > -1) {
+ this.workers.splice(index, 1);
+ zombie.workerData = null;
+ }
+ }
+
+ update(delta) {
+ this.workers = this.workers.filter(w =>
+ this.scene.npcs.includes(w) && w.hp > 0
+ );
+
+ for (const worker of this.workers) {
+ if (!worker.workerData) continue;
+
+ this.applyDecay(worker, delta);
+ worker.workerData.workTimer += delta;
+
+ if (worker.workerData.workTimer >= worker.workerData.workInterval) {
+ worker.workerData.workTimer = 0;
+
+ if (worker.workerData.type === 'FARM') {
+ this.performFarmWork(worker);
+ } else if (worker.workerData.type === 'MINE') {
+ this.performMineWork(worker);
+ } else if (worker.workerData.type === 'CLEAR') {
+ this.performClearWork(worker);
+ }
+ }
+ }
+ }
+
+ applyDecay(zombie, delta) {
+ const wd = zombie.workerData;
+ wd.energy -= (wd.decayRate * delta) / 1000;
+
+ if (wd.energy <= 0) {
+ wd.energy = 0;
+ zombie.hp -= (wd.decayRate * delta) / 1000;
+ if (zombie.sprite) zombie.sprite.setTint(0x666666);
+ }
+
+ if (zombie.hp <= 0) this.onWorkerDeath(zombie);
+ }
+
+ performFarmWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ const farming = this.scene.farmingSystem;
+ if (!terrain || !farming) return;
+
+ // 1. Water/Harvest existing crops
+ if (terrain.cropsMap) {
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.cropsMap.has(key)) {
+ const crop = terrain.cropsMap.get(key);
+
+ if (!crop.isWatered) {
+ farming.waterCrop(wd.centerX + dx, wd.centerY + dy);
+ console.log(`๐ง๐ง Worker watered`);
+ wd.status = 'WORKING';
+ return;
+ }
+
+ if (crop.stage >= 4) {
+ farming.harvest(wd.centerX + dx, wd.centerY + dy);
+ console.log(`๐ง๐พ Worker harvested`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // 2. Plant on empty tilled soil (if has seeds)
+ if (wd.inventory.seeds > 0) {
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const tile = terrain.getTile(wd.centerX + dx, wd.centerY + dy);
+ if (tile && tile.isTilled && !tile.hasCrop) {
+ farming.plant(wd.centerX + dx, wd.centerY + dy, 'wheat');
+ wd.inventory.seeds--;
+ console.log(`๐ง๐ฑ Worker planted (Seeds: ${wd.inventory.seeds})`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+
+ // 3. Till grass/dirt
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const tile = terrain.getTile(wd.centerX + dx, wd.centerY + dy);
+ const typeName = tile?.type?.name || tile?.type;
+
+ if ((typeName === 'grass' || typeName === 'dirt') && !tile.isTilled) {
+ tile.isTilled = true;
+ if (tile.sprite) tile.sprite.setTint(0x8B4513);
+ console.log(`๐ง๐จ Worker tilled soil`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+
+ wd.status = 'IDLE';
+ }
+
+ performMineWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ if (!terrain || !terrain.decorationsMap) return;
+
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.decorationsMap.has(key)) {
+ const decor = terrain.decorationsMap.get(key);
+ if (decor.type === 'stone' || decor.type.includes('rock')) {
+ terrain.removeDecoration(wd.centerX + dx, wd.centerY + dy);
+
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem('stone', 1);
+ }
+
+ console.log(`๐งโ๏ธ Worker mined stone`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+
+ wd.status = 'IDLE';
+ }
+
+ performClearWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ if (!terrain || !terrain.decorationsMap) return;
+
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.decorationsMap.has(key)) {
+ const decor = terrain.decorationsMap.get(key);
+ const t = decor.type;
+
+ // Clear trees, bushes, logs, rocks
+ if (t.startsWith('tree') || t.startsWith('bush') || t === 'fallen_log' || t === 'stone' || t.startsWith('small_rock')) {
+ terrain.removeDecoration(wd.centerX + dx, wd.centerY + dy);
+
+ // Give some resources
+ if (this.scene.inventorySystem) {
+ if (t.startsWith('tree') || t === 'fallen_log' || t.startsWith('bush')) {
+ this.scene.inventorySystem.addItem('wood', 1);
+ } else {
+ this.scene.inventorySystem.addItem('stone', 1);
+ }
+ }
+
+ console.log(`๐ง๐ช Worker CLEARED ${t}`);
+ wd.status = 'WORKING';
+ return; // One per tick
+ }
+ }
+ }
+ }
+ wd.status = 'IDLE';
+ }
+
+ onWorkerDeath(zombie) {
+ console.log(`๐ Worker died at ${zombie.gridX},${zombie.gridY}`);
+
+ if (this.scene.interactionSystem && this.scene.interactionSystem.spawnLoot) {
+ this.scene.interactionSystem.spawnLoot(zombie.gridX, zombie.gridY, 'fertilizer', 1);
+ }
+
+ if (this.scene.statsSystem) {
+ this.scene.statsSystem.addXP(10);
+ }
+
+ this.removeWorker(zombie);
+ }
+
+ feedWorker(zombie, amount = 50) {
+ if (!zombie.workerData) return false;
+
+ zombie.workerData.energy = Math.min(100, zombie.workerData.energy + amount);
+ if (zombie.sprite) zombie.sprite.clearTint();
+
+ console.log(`๐ Fed worker`);
+ return true;
+ }
+}
diff --git a/GAME_BIBLE_3.md b/GAME_BIBLE_3.md
new file mode 100644
index 000000000..2dcb09d9c
--- /dev/null
+++ b/GAME_BIBLE_3.md
@@ -0,0 +1,3538 @@
+
+# ๐ฎ GAME BIBLE 3: THE TOTAL RECOVERY EDITION ๐ฎ
+**Project:** Mrtva Dolina (NovaFarma)
+**Version:** 3.0 (Total Recovery)
+**Date:** January 16, 2026
+**Status:** AA PRE-PRODUCTION READY
+
+---
+
+## ๐จ SYSTEM RECOVERY INDEX (EMERGENCY SCAN)
+*All systems confirmed recovered in `EMERGENCY_SYSTEMS_RECOVERY/`*
+
+### 1. ๐ฐ ECONOMY & TRADE
+* **Inventory:** `InventorySystemExpanded.js`, `FullInventoryUI.js` (Storage, Stacking, Filtering)
+* **Shops:** `BakeryShopSystem.js`, `BarberShopSystem.js`, `NPCShopSystem.js`
+* **Zombie Economy:** `ZombieEconomySystem.js` (Rental to NPCs, Brain maintenance)
+* **Black Market:** `DrugEconomySystem.js` (Mushroom trading, hallucination effects)
+
+### 2. ๐ WORLD & ATMOSPHERE
+* **Time:** `TimeSystem.js` (Day/Night cycle, Season tracking)
+* **Weather:** `MasterWeatherSystem.js` (Rain, Snow, Fog, Storms affecting gameplay)
+* **Noir Vibe:** `VisualEnhancementSystem.js` (Noir Fog, Lighting gradients, Shadows)
+
+### 3. ๐ฅ NPC & SOCIETY (180 TOTAL)
+* **Population:** `NPCPopulationSystem.js` (Tracking all 180 NPCs)
+* **Settlement:** `NPCSettlementSystem.js` (Housing assignments)
+* **Privacy:** `NPCPrivacySystem.js` (NPC schedules, home locking)
+* **Enemies:** `NomadRaiderAI.js` (Raider attacks)
+
+### 4. ๐ง MASTER SYSTEMS
+* **The Brain:** `MasterGameSystemsManager.js` (Coordinates all sub-systems)
+* **The Protector:** `GameManager.js` (Auto-save, Safety checks)
+* **Persistence:** `SaveSystemExpansion.js` (Biome-specific data saving)
+
+---
+
+## ๐งฌ GAME DNA (CORE VARIABLES)
+- `var is_ana_found = false` (Story State)
+- `var zombie_labor_count = 0` (Economy State)
+- `func start_amnesia_effect()` (Visual State)
+- `var current_biom = "Nova Farma"` (World State)
+
+---
+# ๐ฎ DOLINASMRTI - PRODUCTION GAME BIBLE v2.6
+
+**Created:** 06.01.2026 21:30 CET
+**Last Updated:** 12.01.2026 23:50 CET | ๐ฐ **PRICING LOCKED: โฌ10 Early Access / โฌ30 Full Release**
+**Source:** Merged from `generate_assets_full.py` + GAME_BIBLE v1 + Faza 2 + Master Commands + Week 1 + Audio + AI + Advanced + Intro/Aging Marathon + Asset Audit JAN 12
+**Version:** Alpha 2.8 - Complete Asset Audit
+**Status:** ๐ฐ **OFFICIAL PRICING DECISION LOCKED** - โฌ10 (EA) / โฌ30 (v1.0) - NO DLC!
+
+**๐ ASSET STATUS (12.01.2026):**
+- **Total Images:** 3,477 PNG/JPG/WEBP files
+- **Lines of Code:** 104,439 lines in src/
+- **Game Systems:** 188 implemented systems
+- **Phaser Scenes:** 18 active scenes
+- **Audio:** 12 music tracks + 53 voiceover files
+
+
+
+---
+
+## ยฉ COPYRIGHT & TRADEMARK INFORMATION
+
+### **Intellectual Property:**
+- **Game Title:** "Krvava ลฝetev" / "DolinaSmrti" / "Bloody Harvest"
+- **Original Creator:** David Kotnik
+- **Development Studio:** Hipodevil666 Studiosโข
+- **Copyright:** ยฉ 2024-2026 David Kotnik. All Rights Reserved.
+- **Trademark:** DolinaSmrtiโข is a trademark of Hipodevil666 Studiosโข
+
+### **IP Rights & Usage:**
+- All characters, artwork, code, music, story, and game mechanics are the exclusive intellectual property of David Kotnik
+- Unauthorized reproduction, distribution, or derivative works are prohibited
+- Game concept, design document, and all associated materials are confidential
+- This document is for internal production use only
+
+### **Development Credits:**
+- **Creator & Lead Developer:** David Kotnik
+- **Studio:** Hipodevil666 Studiosโข
+- **Engine:** Phaser 3 (JavaScript/Web)
+- **Art Style:** Style 32 Dark-Chibi Noir (Original)
+- **Target Platform:** PC (Web-based)
+- **Development Start:** 2024
+- **Current Version:** Alpha 2.5 (Week 1 Production Ready)
+
+### **Legal Notice:**
+This game and all associated materials are protected under Slovenian and international copyright law. Any unauthorized use, reproduction, or distribution may result in legal action. For licensing inquiries, contact: [David Kotnik / Hipodevil666 Studiosโข]
+
+---
+
+### **Creator's Philosophy & Life Manifest:**
+
+**๐จ My Aesthetic:**
+- ๐ Emojis, ๐ Piercings, ๐จ Tattoos, ๐ Dreadlocks
+- ๐ Baggy Pants, ๐น Longboard, ๐น Skateboard
+- ๐ธ Punk Spirit, ๐ฎ ADHD Energy, โก Creative Chaos
+
+**๐ญ My Life Motto:**
+> *"ลฝivljenje gre prehitro mimo - zato uลพivaj vsako sekundo, kot da je zadnji dan tvojega ลพivljenja!"*
+> *("Life goes by too fast - so enjoy every second like it's the last day of your life!")*
+
+**๐ฅ My Philosophy:**
+- **Upaj si, ฤe ลพeliลก!** (Dare if you want!)
+- **Si daj delati piercing!** (Get yourself a piercing!)
+- **Ne posluลกaj kolegov!** (Don't listen to naysayers!)
+- Pravi kolega bo rekel: *"Lej ti, budala stara, spet novi piercing? Paลกe ti! Takega te mamo radi!"* ๐
+ - *(A true friend will say: "Look at you, you old fool, another piercing? It suits you! That's why we love you!")*
+
+**๐ฏ My Identity:**
+> **Jaz sem HIPO, in to je moja mini zgodba.**
+> *(I am HIPO, and this is my mini story.)*
+
+**๐ช My Manifest:**
+- **"Ne spreminjam se - spreminjaj se sistem, ne jaz!"**
+ *(I don't change - the system changes, not me!)*
+- **"Jaz ลพivim svoje ADHD sanje!"**
+ *(I live my ADHD dreams!)*
+
+**๐ What This Game Represents:**
+This game is a reflection of authentic ADHD creativity - chaotic, colorful, passionate, and unapologetically personal. Every piercing, every dreadlock, every skateboard trick is a celebration of individuality. This is not just a game - it's a lifestyle, a statement, a rebellion against conformity.
+
+**Stay weird. Stay creative. Stay YOU.** ๐
+
+---
+
+**- David "HIPO" Kotnik**
+*Hipodevil666 Studiosโข*
+*Living ADHD dreams since forever* โก๐น๐
+
+---
+
+## ๐ PRODUCTION STATUS LEGEND
+
+| Symbol | Status | Phase |
+|--------|--------|-------|
+| โ
| **DONE** - Fully implemented | All Phases |
+| ๐ | **IN PROGRESS** - Partially complete | Phase 1/2 |
+| โ | **NOT STARTED** - Planned but not implemented | Phase 3+ |
+| ๐ฏ | **DEMO PRIORITY** - Required for Kickstarter demo | Demo Phase |
+| ๐ฅ | **PHASE 1 CRITICAL** - Must have for Alpha 1 | Phase 1 |
+| โก | **PHASE 2 ESSENTIAL** - Must have for Alpha 2 | Phase 2 |
+
+---
+
+## ๐ TABLE OF CONTENTS
+
+
+### **Title:** Krvava ลฝetev (DolinaSmrti / Bloody Harvest)
+
+### **Genre:**
+- Survival RPG
+- Farming Simulator
+- Post-Apocalyptic Adventure
+- Story-Driven Exploration
+
+### **Platform:** PC (Web-based, JavaScript/Phaser 3)
+
+### **Target Audience:**
+- Age: 16+ (mature themes)
+- Players who enjoy: Stardew Valley, Don't Starve, Project Zomboid
+- Story-driven gameplay with emotional depth
+
+### **Unique Selling Points:**
+1. **Emotional Twin Bond Story** - Find your lost sister Ana
+2. **Zombie Worker Management** - Control zombies, don't fight them all
+3. **Gritty Noir Art Style** - Dark, hand-drawn, mature aesthetic
+4. **Micro to Macro Farm** - Start with 8ร8 plot, grow to empire
+5. **180+ NPCs** with deep personalities and quests
+6. **20 Biomes** to explore across post-apocalyptic Slovenia
+7. **Multiple Endings** based on player choices
+
+---
+
+## ๐ STORY & SETTING
+
+### **Opening (The Truth Kai Doesn't Remember):**
+> "My name is... I don't know. I woke up here. I don't remember anything. But something inside me... a feeling... tells me I'm searching for someone."
+
+### **The REAL Incident (Day 0 - What Actually Happened):**
+- **2084** - Slovenia, Happy Family Farm
+- Kai & Ana Markoviฤ - 14-year-old **TWIN SISTERS** (yes, both girls!)
+- Living peaceful life with parents Marko & Elena
+- **Day 3:** Zombie outbreak begins
+- **Day 7:** **Giant Troll King** attacks their farm
+ - Parents sacrifice themselves protecting twins
+ - Troll King kidnaps Ana, drags her through portal
+ - Kai fights back, gets **MASSIVE HEAD TRAUMA** when thrown into wall
+ - **FULL AMNESIA** - Kai remembers NOTHING!
+
+### **Present Day (Game Start - Day 7, Hours Later):**
+
+**KAI'S PERSPECTIVE:**
+- Wakes up in basement ruins, disoriented
+- Severe headache, bloodstain on wall
+- **NO MEMORY** of who she is
+- Finds ID card: "Kai Markoviฤ, Age: 14"
+- Finds photos on wall (herself + unknown girl + adults)
+- Feels strange pull in chest (Twin Bond - damaged but present)
+- **Doesn't know:**
+ - She has a twin sister named Ana
+ - Parents are dead
+ - What happened 3 days ago
+ - Why she feels this inexplicable emptiness
+
+**ANA'S PERSPECTIVE** (If playing Ana's Story):
+- Wakes up in **Chernobyl Reactor Core - Level 7** (prison cell!)
+- **FULL MEMORY** of abduction (traumatic but clear!)
+- Remembers:
+ - Parents dying to save them
+ - Giant Troll King grabbing her
+ - Kai's unconscious body (last image before portal closed)
+ - **Twin Bond feels weak/damaged** (Kai's amnesia blocking it!)
+- **Mission:** Survive captivity, send signals to Kai, prepare escape
+
+### **Twin Bond Mechanic (Both Perspectives):**
+
+**KAI (Amnesia = Broken Bond):**
+- Starts with vague "pull" sensation (direction unknown)
+- Finding Ana's belongings โ **Flashback cutscenes** (memory fragments!)
+- Each memory restored โ Twin Bond strengthens
+- **50/50 memories** unlocked โ Full Bond restored!
+- Then can sense Ana's exact location
+
+**ANA (Full Memory = One-Way Bond):**
+- Can SEND signals to Kai (telepathic beacons)
+- Feels when Kai gets close to her items
+- Heartbreak: Kai doesn't respond (amnesia!)
+- Keeps trying anyway (hope!)
+
+### **Main Antagonist:**
+- **Dr. Viktor Krniฤ** - Mad scientist who **INTENTIONALLY** released virus!
+- Orchestrated Giant Troll King attack
+- Wants Ana's blood (contains cure component!)
+- Holding Ana prisoner in Chernobyl
+- **NOT** Viktor's first attempt (hinted he's done this to other families!)
+
+### **GIANT TROLL KING** (FINAL BOSS):
+- **NOT Zmaj Volk** - Zmaj Volk is CUT from game! โ
+- Giant Troll King = **Separate entity**
+- 5m tall, incredibly powerful
+- Under Viktor's control (via dark magic/tech)
+- Kidnapped Ana on Day 7
+- **Final Battle:** Chernobyl Reactor Core (3-phase boss fight!)
+- Can be killed OR freed from control (endings!)
+
+### **The World:**
+- **Slovenia Post-Apocalypse** (Valley of Death = Dolina Smrti)
+- **20 diverse biomes:**
+ - 9 Normal: Grassland, Forest, Swamp, Desert, Mountain, Snow, Wasteland, Tropical, Radioactive
+ - 11 Magical: Dino Valley, Mythical Highlands, Endless Forest, Loch Ness, Catacombs, Egyptian Desert, Amazon, Atlantis, Chernobyl, **Mexican Cenotes, Witch Forest**
+- **27 explorable towns** (1 per train station!)
+- **Scattered biomes** (150km apart!) - Must travel to discover!
+- Mix of realistic Slovenia + fantasy anomalies
+
+---
+
+## ๐ฅ MAIN CHARACTERS
+**Status:** โ
**DONE** | ๐ฏ **DEMO READY** | ๐ฅ **PHASE 1 COMPLETE**
+
+| Character | Master Reference | Sprites | Status | Phase |
+|-----------|-----------------|---------|--------|-------|
+| Kai Markoviฤ | โ
Generated | โ Not Started | ๐ Partial | ๐ฏ DEMO |
+| Ana Markoviฤ | โ
Generated | โ Not Started | ๐ Partial | ๐ฏ DEMO |
+| Gronk (Troll) | โ
Generated | โ Not Started | ๐ Partial | ๐ฏ DEMO |
+| Susi (Dog) | โ
Generated | โ Not Started | ๐ Partial | ๐ฏ DEMO |
+| Dr. Viktor Krniฤ | โ Not Generated | โ Not Started | โ Planned | โก Phase 2 |
+
+---
+
+### **1. Kai Markoviฤ** (Playable Character - Kai's Story)
+**Age:** 14 (NOT 17!)
+**Gender:** FEMALE (twin sister to Ana!)
+**Role:** Protagonist, Alpha Hybrid, Farmer, Memory-Lost Twin
+
+**Appearance:**
+- **Pink and green dreadlocks** (signature look)
+- **Stretched ear gauges** with multiple piercings
+- Facial piercings (nose, lip, eyebrow)
+- **Katana** strapped to back (main weapon)
+- Post-apocalyptic clothing (torn jacket, ripped jeans)
+- Dark combat boots
+- Brown survival backpack
+- Athletic, wiry build
+
+**Visual Reference:**
+
+
+`Location: /assets/references/kai/master_reference.png`
+
+- Style 32 Dark-Chibi Noir
+- Red eyes with dark pupils (human, NOT zombie)
+- Confused/determined expression (amnesia!)
+
+**Game Role:**
+- **Primary Protagonist** - Main playable character
+- **Alpha Hybrid** - Can command zombies telepathically
+- **Farmer/Builder** - Rebuilds civilization from scratch
+- **Memory Seeker** - Desperate to recover lost identity
+- **Sister Savior** - Ultimate goal: Rescue Ana from Chernobyl
+
+**Personality (Before Amnesia):**
+- Protective of Ana (twin bond!)
+- Brave, sometimes reckless
+- Creative problem-solver
+- Loved farming with parents
+- Tomboy energy, hands-on worker
+- Competitive with Ana (friendly rivalry)
+
+**Personality (After Amnesia - Game Start):**
+- **COMPLETELY LOST** - doesn't know who she is!
+- Driven by instinct (Twin Bond pull)
+- Slowly rebuilds identity through memories
+- Desperate to understand the emptiness inside
+- Relies on muscle memory (farming, combat)
+- Suspicious of strangers (trauma response)
+- Grows more determined with each memory
+
+**Abilities:**
+- **Alpha Hybrid Power:** Control zombies telepathically
+ - Command individual zombies
+ - Give complex orders (farm, build, defend)
+ - Zombie workers become game mechanic
+- **Farming Skills:** Muscle memory intact!
+ - Planting, harvesting, irrigation
+ - Animal care, breeding
+ - Crop rotation knowledge
+- **Combat:** Learns as game progresses
+ - Scythe proficiency (main weapon)
+ - Dodge/roll mechanics
+ - Zombie summon in battle
+- **Crafting & Building:**
+ - Tool creation, structure building
+ - Recipe memory returns with items
+- **Twin Bond (Damaged):**
+ - Vague directional pull (strengthens with memories!)
+ - Emotional flashbacks when finding Ana's items
+ - Full restoration at 50/50 memories
+
+**Character Arc:**
+- **Day 7 (Start):** Wakes with NO memory, confused, alone
+- **Week 1-10:** Finds 1-20 memories (fragments of childhood)
+ - "Wait... I remember this tree..."
+ - "Why do I know how to farm?"
+- **Week 11-30:** Finds 21-40 memories (remembers Ana's face!)
+ - "The girl in the photos... Ana... my sister?!"
+ - Twin Bond becomes stronger
+- **Week 31-50:** Finds 41-49 memories (remembers parents' death)
+ - "No... they died protecting us..."
+ - Rage toward Giant Troll King
+- **Memory 50/50:** Full memory restored - **EMOTIONAL BREAKDOWN**
+ - "ANA! I'M COMING FOR YOU!"
+ - Twin Bond fully active
+- **Endgame:** Storms Chernobyl to save Ana
+ - Final confrontation
+ - Cure discovery
+ - True ending unlocked
+
+**Voice/Dialogue Style:**
+- **Early Game (Confused):**
+ - "Who... who am I?"
+ - "This place feels... familiar?"
+ - Short, uncertain sentences
+- **Mid Game (Determined):**
+ - "I won't give up. Someone's waiting for me."
+ - "These memories... they're mine!"
+ - More confident, driven
+- **Late Game (Resolved):**
+ - "Ana, I remember everything! Hold on!"
+ - "I'll tear down Chernobyl to find you!"
+ - Fierce, protective, unstoppable
+
+---
+
+### **2. Ana Markoviฤ** (Dual Protagonist)
+**Age:** 14 (Kai's twin sister!)
+**Gender:** FEMALE
+**Role:** Dual Protagonist (playable!) / Captive Scientist / Cure Key
+
+**Appearance:**
+- **Light blonde hair** (short, practical cut)
+- **Blue eyes WITH pupils** (friendly NPC style)
+- Style 32 Dark-Chibi Noir aesthetic
+- Scientist aesthetic (lab coat over survival clothes)
+- Research backpack with supplies
+
+**Visual Reference:**
+
+
+`Location: /assets/references/ana/master_reference.png`
+
+**Status in Kai's Story:**
+- **Missing** (kidnapped by Giant Troll King)
+- **Alive** (held captive in Chernobyl Reactor Level 7)
+- Her 50 belongings scattered across world
+
+**Game Role:**
+- **Dual Protagonist** - Playable in "Ana's Story" mode
+- **Scientist/Researcher** - Working on zombie cure under duress
+- **Memory Anchor** - Her items trigger Kai's flashbacks
+- **EndGame Key** - Knowledge needed to stop outbreak
+
+**Ana's Story Gameplay:**
+- **Playable character** in "Ana's Story" mode!
+- Start: Prison cell, Day 7
+- **Full memory** (knows everything!)
+- Must survive, research, plan escape
+- Write 50 diary pages (same ones Kai finds!)
+- Send Twin Bond signals to Kai
+
+---
+
+### **3. Gronk** (Companion)
+**Role:** Kai's friend, Tamed Troll, Comic Relief, Tank
+**Species:** Alpha Troll (Giant species)
+**Age:** ~200 years old (young for trolls)
+
+**Appearance:**
+- **MASSIVE green troll** (3m tall, 300kg)
+- **Pink dreadlocks** (signature style, 30+ locks)
+- **Stretched ear gauges** with piercings (5cm diameter)
+- **Nose ring** (silver, punk aesthetic)
+- **Vape** always in hand (essential accessory, "Bubble Gum" flavor)
+- Black t-shirt: **"TROLL SABBATH"** (purple text, metal band reference)
+- Torn baggy black skater pants (distressed, street style)
+- **Pink sneakers** (size XXXL, custom-made)
+- Green skin with darker muscle definition
+- Long arms, hunched posture
+
+**Visual Reference:**
+
+
+`Location: /assets/references/gronk/master_reference.png`
+
+**Game Role:**
+- **Tank Companion** - Absorbs damage, protects Kai
+- **Strength Worker** - Lifts heavy objects, breaks walls
+- **Comic Relief** - ADHD energy, funny dialogue
+- **Loyal Friend** - Saved by Kai, eternal gratitude
+- **Fast Travel Mount** - Kai can ride on shoulders
+
+**Personality:**
+- **Gentle giant** despite terrifying size
+- Loves vaping (various flavors: Bubble Gum, Watermelon, Cotton Candy)
+- Loyal to Kai after being saved from mind control
+- **ADHD-coded** (short attention span, impulsive, energetic)
+- Obsessed with pink aesthetic (dreadlocks, shoes, vape)
+- Music lover (favorite band: Troll Sabbath)
+- Pacifist unless defending Kai
+- Surprisingly intelligent despite appearance
+- Nostalgic for "old troll ways" (pre-apocalypse raves)
+
+**Abilities:**
+- **Incredible Strength:** Lifts boulders (500kg+)
+- **Tank/Defender:** High HP, absorbs damage for Kai
+- **Wall Breaker:** Can smash through barriers
+- **Earthquake Slam:** Special ability (AOE stun)
+- **Intimidation:** Scares away weak enemies
+- **Foraging:** Finds rare mushrooms (troll knowledge)
+
+**Character Arc:**
+- **First Meeting:** Mind-controlled by Dr. Krniฤ, attacks Kai
+- **Liberation:** Kai breaks mind control (Twin Bond power)
+- **Gratitude:** Swears eternal loyalty
+- **Growth:** Learns to control strength, becomes gentle
+- **Endgame:** Helps storm Chernobyl alongside Kai
+
+**Voice/Dialogue Style:**
+- Deep rumbling voice (gentle tone)
+- "Gronk sorry... Gronk no mean to scare."
+- "Pink is best color! Make Gronk happy!"
+- "Bubble Gum vape... *puff* ...ahhhh, tasty!"
+- Simple grammar, childlike wonder
+- Refers to self in third person ("Gronk do this!")
+
+**Favorite Things:**
+- Pink anything (obsessed!)
+- Vaping (bubble gum flavor #1)
+- Troll Sabbath music (plays on old Walkman)
+- Kai (best friend!)
+- Smashing things (when allowed)
+- Rave culture (pre-apocalypse nostalgia)
+
+---
+
+### **4. Susi** (Dog Companion)
+**Breed:** Dachshund - Brown with darker spots, long body, floppy ears, cute
+**Abilities:**
+- Finds hidden items
+- Warns of danger
+- Loyal companion
+
+---
+
+### **5. Dr. Viktor Krniฤ** (Main Antagonist)
+**Role:** Mad scientist villain
+**Goal:** Extract cure from Ana's blood (will kill her)
+**Controls:** Giant Troll King, mutant army
+**Final Boss:** Chernobyl reactor showdown
+
+---
+
+## ๐ MASTER ASSET COUNTS
+
+| Category | Count | Status |
+|----------|-------|--------|
+| **NPCs & Characters** | ~200+ | From script registry |
+| **Livestock Animals** | ~150 | From script registry |
+| **Wildlife** | ~200 | From script registry |
+| **Monsters & Mutants** | ~500 | From script registry |
+| **Bosses** | 25 | From script registry |
+| **Crops & Plants** | ~500 | With growth stages |
+| **Items & Tools** | ~2000 | Multi-tier materials |
+| **Buildings** | ~300 | Farm + Town + Special |
+| **Biomes** | 20 | 9 Normal + 11 Magical |
+| **Environment** | ~800 | Terrain, nature, weather |
+| **UI Elements** | ~200 | Buttons, icons, frames |
+| **Workstations** | ~100 | Crafting stations |
+| **TOTAL ASSETS** | **~13,500+** | Complete production target |
+
+---
+
+## ๐๏ธ NPCs & FACTIONS
+**Status:** โ
**166/166 BIOME NPCs DONE!** | ๐ฅ **PHASE 1 COMPLETE** | Generated: 06.01.2026
+
+| NPC Category | Count | Master Refs | Sprites | Status | Phase |
+|--------------|-------|-------------|---------|--------|-------|
+| **Main Characters** | 4 | โ
4/4 | โ 0/4 | ๐ Refs Done | ๐ฏ DEMO |
+| **Verified NPCs** | 13 | โ
13/13 | โ
22/13 | ๐ Refs+Sprites (2 NPCs) | ๐ฅ Phase 1 |
+| **Biome NPCs (20 biomes)** | 166 | โ
166/166 | โ 0/166 | โ
**100% REFS** | ๐ฅ Phase 1 |
+| **Town NPCs** | ~190 | โ 0/190 | โ 0/190 | โ Not Started | โก Phase 2 |
+| **Zombi Skavt (5 stages)** | 5 | โ 0/5 | โ 0/5 | โ Not Started | โก Phase 2 |
+| **Nomad Raiders (12 types)** | 12 | โ 0/12 | โ 0/12 | โ Not Started | โก Phase 2 |
+| **TOTAL NPCs** | **~390** | **โ
183/390** | **โ
22/390** | **๐ 47% Refs + 6% Sprites** | Multiple |
+
+---
+
+### Main Characters (4)
+
+1. **Kai Markoviฤ** - Young farmer with pink/green dreadlocks, ear gauges, piercings, katana on back, torn jacket, athletic build, red eyes with pupils
+2. **Ana Markoviฤ** - Kai's twin sister, blonde hair, blue eyes with dark pupils, scientist explorer outfit, research backpack
+3. **Gronk Alpha Troll** - MASSIVE green troll, pink dreadlocks, ear gauges, nose ring, vape in hand, 'TROLL SABBATH' t-shirt, torn pants, pink sneakers
+4. **Susi the Dachshund** - Brown with darker spots, long body, floppy ears, cute companion
+
+### Verified NPCs (13)
+
+**Status:** โ
**13/13 COMPLETE** - All have master references, 2 have full sprite sets (22 sprites total)
+
+From `/references/npcs/` with master_reference.png:
+
+---
+
+#### **1. Ivan Kovaฤ** (Blacksmith)
+
+
+
+`Location: /assets/references/npcs/ivan_kovac/master_reference.png`
+
+**Role:** Town Blacksmith, Weapon/Tool Smith
+**Appearance:**
+- Muscular build (strong arms from forging)
+- Protective goggles pushed up on forehead
+- Blacksmith apron (soot-covered, leather)
+- Red eyes with pupils (friendly human)
+- Worn work gloves
+- Hammer in hand or at belt
+
+**Job:**
+- Repairs and crafts metal tools/weapons
+- Upgrades equipment (iron โ steel โ titanium tiers)
+- Teaches blacksmithing skills
+- Sells metal goods
+
+**Personality:**
+- Gruff but kind
+- Takes pride in craftsmanship
+- Respects hard workers
+- Dislikes shoddy work
+
+---
+
+#### **2. Mayor/ลฝupan** (Town Administrator)
+
+
+
+`Location: /assets/references/npcs/mayor/master_reference.png`
+
+**Role:** Town Mayor, Authority Figure
+**Appearance:**
+- Serious authoritative demeanor
+- Long green coat with official sash
+- Old tablet computer (pre-outbreak tech)
+- Red eyes with pupils (friendly human)
+- Professional but weathered
+
+**Job:**
+- Town management and governance
+- Issues building permits
+- Quest giver (town restoration)
+- Political decisions
+
+**Personality:**
+- Serious but fair
+- Wants to rebuild civilization
+- Pragmatic leader
+- Trusts Kai after proving herself
+
+---
+
+#### **3. Kustos** (Museum Curator)
+
+
+
+`Location: /assets/references/npcs/kustos/master_reference.png`
+
+**Role:** Museum Curator, Historian
+**Appearance:**
+- Wise old scholar (60s-70s)
+- Thick glasses
+- Notebook always in hand
+- Examining artifacts
+- Red eyes with pupils (friendly human)
+
+**Job:**
+- Museum collection management
+- Artifact identification
+- Historical knowledge
+- Quest giver (collect rare items)
+
+**Personality:**
+- Knowledge-obsessed
+- Gentle, patient teacher
+- Excited by discoveries
+- Preserves pre-outbreak culture
+
+---
+
+#### **4. Pek** (Baker)
+
+
+
+`Location: /assets/references/npcs/pek/master_reference.png`
+
+**Role:** Town Baker, Food Merchant
+**Appearance:**
+- Warm friendly smile
+- Chef's apron (flour-dusted)
+- Baker's hat
+- Holding fresh bread
+- Red eyes with pupils (friendly human)
+
+**Job:**
+- Bakes bread, pastries, cakes
+- Sells food items
+- Teaches baking recipes
+- Daily breakfast quest
+
+**Personality:**
+- Cheerful and welcoming
+- Generous with samples
+- Loves feeding people
+- Community-oriented
+
+**Dialogue:**
+- "Fresh bread, just out of the oven!"
+- "You look hungry! Here, try this!"
+
+---
+
+#### **5. ล ivilja** (Tailor)
+
+
+
+`Location: /assets/references/npcs/sivilja/master_reference.png`
+
+**Role:** Tailor, Clothing Designer
+**Appearance:**
+- Elegant dress (practical but stylish)
+- Measuring tape around neck
+- Scissors in hand
+- Fabric samples
+- Red eyes with pupils (friendly human)
+
+**Job:**
+- Crafts and repairs clothing
+- Custom outfits (cosmetic upgrades)
+- Armor tailoring
+- Fashion quests
+
+**Personality:**
+- Artistic and creative
+- Detail-oriented perfectionist
+- Friendly advice giver
+- Appreciates good style
+
+---
+
+#### **6. Tehnik** (Engineer)
+
+
+
+`Location: /assets/references/npcs/tehnik/master_reference.png`
+
+**Role:** Engineer, Tech Specialist
+**Appearance:**
+- Work coveralls (oil-stained)
+- Tool belt loaded with gadgets
+- Safety goggles
+- Tech gadgets in hands
+- Mechanical enhancements (cybernetic hands)
+- Red eyes with pupils (friendly human)
+
+**Job:**
+- Repairs advanced technology
+- Crafts automation systems
+- Upgrades farm machinery
+- Tech quest giver
+
+**Personality:**
+- Logical, analytical
+- Loves tinkering
+- Excited by new tech
+- Problem solver
+
+---
+
+#### **7. Teacher** (Educator)
+
+
+
+`Location: /assets/references/npcs/teacher/master_reference.png`
+
+**Role:** School Teacher, Knowledge Keeper
+**Appearance:**
+- Glasses (reading style)
+- Books in arms (always carrying)
+- Kind patient expression
+- Red eyes with pupils (friendly human)
+- Professional educator clothing
+
+**Job:**
+- Teaches children (NPC kids)
+- Skill training for Kai
+- Library management
+- Educational quests
+
+**Personality:**
+- Patient and kind
+- Believes in education's power
+- Nurturing, motherly
+- Encourages learning
+
+---
+
+#### **8. Priest Jakob** (Church)
+
+
+
+`Location: /assets/references/npcs/priest/master_reference.png`
+
+**Role:** Priest, Spiritual Leader
+**Appearance:**
+- Religious robes (simple, worn)
+- Holy book in hand
+- Peaceful expression
+- Ceremonial cross necklace
+- Red eyes with pupils (friendly human)
+
+**Job:**
+- Church services
+- Weddings, funerals
+- Spiritual guidance
+- Blesses items
+
+**Personality:**
+- Peaceful, calm
+- Non-judgmental
+- Offers comfort
+- Faith in humanity
+
+---
+
+#### **9. Miro Pravnik** (Lawyer)
+
+
+
+`Location: /assets/references/npcs/miro_pravnik/master_reference.png`
+
+**Role:** Lawyer, Legal Advisor
+**Appearance:**
+- Older man (50s-60s)
+- Wrinkled brown office suit
+- Rusty briefcase
+- Tired weathered expression
+- Red eyes with pupils (friendly human)
+- Reading glasses
+
+**Job:**
+- Legal contracts (land ownership)
+- Dispute resolution
+- Official documentation
+- Quest: Legal cases
+
+**Personality:**
+- Cynical but honest
+- Tired from outbreak
+- Still believes in law
+- Dry humor
+
+**Dialogue:**
+- "Even now, contracts matter."
+- "Read the fine print, always."
+
+---
+
+#### **10. Arborist/Gozdar** (Tree Planter)
+
+
+
+`Location: /assets/references/npcs/arborist/master_reference.png`
+
+**Role:** Arborist, Environmental Restorer
+**Appearance:**
+- Work clothes (green/brown earth tones)
+- Gardening gloves (dirt-stained)
+- Seed bags at belt
+- Calm patient expression
+- Red eyes with pupils (friendly human)
+
+**Job:**
+- Plants trees (reforestation)
+- Seed supplier
+- Teaches gardening
+- Environmental quests
+
+**Personality:**
+- Patient, calm
+- Environmentalist
+- Loves nature
+- Long-term thinker
+
+**Dialogue:**
+- "A tree planted today feeds tomorrow."
+- "Nature always finds a way."
+
+---
+
+#### **11. Glavni Smetar** (Main Garbage Collector)
+
+
+
+`Location: /assets/references/npcs/glavni_smetar/master_reference.png`
+
+**Role:** Garbage Collector, Recycler
+**Appearance:**
+- Work uniform (practical, durable)
+- Reflective vest (safety orange)
+- Trash equipment (grabber tool)
+- Weathered hands from work
+- Hardworking expression
+- Red eyes with pupils (friendly human)
+
+**Job:**
+- Town cleanup
+- Recycling materials
+- Waste management
+- Environmental cleanup quests
+
+**Personality:**
+- Hardworking, underappreciated
+- Takes pride in cleanliness
+- Resourceful (finds uses for "trash")
+- Community-minded
+
+**Dialogue:**
+- "Someone's gotta keep this place clean!"
+- "One person's trash..."
+
+---
+
+#### **12. Electrician** โก (Town Electrician) **NEW!**
+
+
+
+`Location: /assets/references/npcs/electrician/master_reference.png`
+
+**Role:** Town Electrician, Power Specialist
+**Employment:** 2 Cekini/day
+**Sprites:** โ
**11 COMPLETE** (idleร4, walkร4, actionร2, portraitร1)
+**Generated:** Jan 10, 2026
+
+**Appearance:**
+- Dark grey work jumpsuit with reflective orange/yellow strips
+- Blonde streak in short messy dark hair
+- Tool belt loaded with wrenches, wire coils, electrical multimeter(
+- Electrical cable in hand
+- Steel-toe work boots
+- Slight stubble (tired worker)
+- Small ear piercing
+- Red eyes (noir aesthetic)
+
+**Job:**
+- Generator maintenance (prevents 5% daily breakdown!)
+- Power grid repairs (poles, lines)
+- UV light repairs (basement farming)
+- Electrical system upgrades
+
+**Daily Routine:**
+- **10 AM:** Generator inspection
+- **2 PM:** Repairs if needed (with VFX!)
+
+**Abilities:**
+- Repair generator (restores 100% health)
+- Prevent breakdowns (while employed)
+- Free repairs for all electrical systems
+- Emergency 24/7 service
+
+**Personality:**
+- Professional but tired
+- Takes job seriously
+- Proud of electrical knowledge
+- Reliable, never late
+
+**Dialogue:**
+- "Generator's running smooth. No problems here."
+- "Just did my daily inspection. All systems nominal."
+- "Found a loose wire this morning. Fixed it."
+
+---
+
+#### **13. Zombie Statistician** ๐ (Office Zombie) **NEW!**
+
+
+
+`Location: /assets/references/npcs/zombie_statistician/master_reference.png`
+
+**Role:** Population Statistician, Data Keeper
+**Employment:** 1 Cekin/day (CHEAPEST!)
+**Sprites:** โ
**11 COMPLETE** (idleร4, walkร4, actionร2, portraitร1)
+**Generated:** Jan 10, 2026
+
+**Appearance:**
+- Pale zombie skin (green tint)
+- Thick-rimmed glasses
+- Grey office suit (worn, patches)
+- Loosened tie
+- Clipboard with population statistics (always!)
+- Pen in hand or behind ear
+- Name tag: "Z. STATISTICIAN"
+- Red eyes (noir zombie)
+- Professional undead appearance
+
+**Job:**
+- Daily population board updates (9 AM sharp!)
+- Tracks living population count
+- Tracks zombie population count
+- Tracks employed workers
+- Maintains accurate statistics
+
+**Daily Routine:**
+- **9 AM:** Walk to population board
+- Update stats with chalk/pen
+- Return to idle position
+- Professional zombie shuffle
+
+**Personality:**
+- Professional despite undeath
+- Obsessed with accuracy
+- Dedicated to data
+- Unceasingly punctual (9 AM always!)
+- Surprisingly intelligent zombie
+
+**Dialogue:**
+- "Population count: accurate as always."
+- "The numbers... they must be precise."
+- "32 zombies counted this morning."
+- "Statistics show a 2% increase in workers."
+- "Even in undeath, I serve the data."
+
+**Unique Trait:**
+- First "friendly" zombie employee
+- Proves zombies can be civilized workers
+- Sets precedent for zombie workforce
+
+
+### Companions
+
+**Zombi Skavt** - 5 Evolution Stages (Lv 1-20):
+- **Base (Lv1-4)**: Friendly zombie with RED or BLUE bandana, small backpack, red eyes WITH dark pupils, chibi zombie, helpful pose
+- **Lantern (Lv5-9)**: Gains glowing lantern, bandana, backpack, red eyes with pupils, friendly zombie scout
+- **Armored (Lv10-14)**: Light armor plates added, bandana, backpack, lantern, red eyes with pupils, stronger scout
+- **Winged (Lv15-19)**: Small wings for flying/scouting, armor, lantern, bandana, red eyes with pupils, advanced scout
+- **Legendary (Lv20)**: Glowing purple aura, all upgrades, wings, armor, lantern, bandana, red eyes with pupils, ultimate scout
+
+### Enemies - Nomad Raiders (12 variants)
+
+**4 types ร 3 variants each:**
+
+**Desert Nomads** (Medium threat):
+- Warrior: Tan robes, scimitar, turban, desert gear, hostile expression
+- Archer: Tan robes, bow and arrows, turban, desert gear, ranged enemy
+- Brute: Tan robes, heavy club, turban, muscular, desert gear, tank enemy
+
+**Frost Bandits** (High threat):
+- Warrior: Fur armor, ice axe, frost gear, white/blue palette, aggressive
+- Archer: Fur armor, ice bow, frost gear, white/blue palette, ranged enemy
+- Brute: Heavy fur armor, ice hammer, frost gear, massive build, very aggressive
+
+**Jungle Marauders** (Very high threat):
+- Warrior: Tribal mask, poison darts, jungle gear, green/brown palette, dangerous
+- Archer: Tribal mask, blowgun, poison darts, jungle gear, stealthy
+- Brute: Tribal mask, massive club, jungle gear, intimidating build, very dangerous
+
+**Tech Scavengers** (Extreme threat):
+- Warrior: Cyberpunk gear, laser sword, neon-themed, menacing
+- Archer: Cyberpunk gear, laser rifle, neon-themed, high-tech
+- Brute: Heavy cyberpunk armor, plasma cannon, neon-themed, massive tech enemy
+
+### Zombies (4 types)
+
+1. **Basic Zombie** - Slow shambling, torn clothes, white eyes no pupils, weak enemy
+2. **Runner Zombie** - Fast aggressive, athletic build, red eyes no pupils, dangerous
+3. **Bloater Zombie** - Large swollen, about to explode, toxic green, tank enemy
+4. **Worker Zombie** - Controllable by Kai, basic laborer, white eyes no pupils, friendly when controlled
+
+### Species - Trolls
+
+1. **Troll Male Friendly** - Similar build to Gronk, gray or blue skin, work clothes, friendly expression, red eyes with pupils, town inhabitant
+2. **Troll Female Friendly** - Slightly smaller than male, different color palette, town clothing, welcoming expression, red eyes with pupils
+3. **Troll Wild Enemy** - Hostile variant, ragged clothing, aggressive pose, NO pupils in eyes, enemy variant
+
+### Species - Elves & Fairies
+
+**Vilinci (Elf)** - Specialty crafting, pointed ears, elegant clothes, working on enchantment, magical aura, red eyes with pupils
+
+**Vile (Fairy)** - Tiny fairy with wings, caring for plants, magical sparkles, instant growth ability, red eyes with pupils
+
+### Generic Citizens (6 base templates ร 18 biome recolors = 108)
+
+1. **Citizen Male Worker** - Laborer clothing, neutral expression, red eyes with pupils, BASE for biome recolors
+2. **Citizen Male Merchant** - Vest trader outfit, friendly expression, red eyes with pupils, BASE for biome recolors
+3. **Citizen Female Farmer** - Work dress apron, kind expression, red eyes with pupils, BASE for biome recolors
+4. **Citizen Female Shopkeeper** - Professional clothing, welcoming expression, red eyes with pupils, BASE for biome recolors
+5. **Citizen Child** - Simple clothes energetic, playful expression, red eyes with pupils, BASE for biome recolors
+6. **Citizen Elder** - Simple robes walking stick, wise expression, red eyes with pupils, BASE for biome recolors
+
+---
+
+## ๐พ ANIMALS & CREATURES (109 TOTAL! - COMPLETE CATALOG)
+**Status:** ๐ **99/109 Generated (91%)** | ๐ฅ **PHASE 1 IN PROGRESS** | Updated: **Jan 8, 2026 01:56 CET**
+
+**Missing:** 10 creatures (7 Chernobyl Mutants, 2 Wild Animals, 1 Farm Animal)
+
+| Category | Total | Generated | Missing | Notes |
+|--------------------|-------|-----------|---------|----------|
+| **Chernobyl Mutants** | 10 | 3/10 | 7 | ๐ฅ Phase 1 |
+| **Farm Animals** | 10 | 9/10 | 1 | ๐ฅ Phase 1 |
+| **Wild Animals** | 15 | 13/15 | 2 | ๐ฅ Phase 1 |
+| **Dinosaurs** | 15 | 14/15 | 1 | โก Phase 2 |
+| **Mythical** | 39 | 39/39 | 0 | โ
COMPLETE |
+| **Bosses** | 24 | 24/24 | 0 | โ
COMPLETE |
+
+**Next Steps:**
+- ๐ฅ **Phase 1:** Complete Chernobyl Mutants (7 remaining)
+- โก **Phase 2:** Start Dinosaurs (15 total)
+
+---
+
+### **1. CHERNOBYL MUTANTS (10 species)** โข๏ธ
+
+**Common Mutants:**
+1. **Mutant Wolf** - Pack hunter, 150 HP, glowing green patches
+2. **Giant Rat** - Swarms of 10-20, 80 HP, cat-sized!
+3. **Mutant Crow** - Flying, 3 eyes, 40 HP
+4. **Radiation Spider** - Dog-sized, poison attacks, 100 HP
+
+**Rare Mutants:**
+5. **Two-Headed Dog** - Attacks 2 targets, 200 HP, can be tamed!
+6. **Radioactive Boar** - Charge attack, 180 HP, produces rad milk if captured
+7. **Glowing Deer** - Bioluminescent, 120 HP, beautiful drops!
+8. **Mutant Fish** - 3-eyed, catchable, radiation essence drops
+
+**Boss-Tier:**
+9. **Mutant Bear** - 500 HP, devastating attacks, LEGENDARY drops!
+10. **Radioactive Cow** - 300 HP, CAPTURE for daily rad milk! (10,000g DNA sample!)
+
+---
+
+### **2. FARM ANIMALS (10 species)** ๐๐
+
+**Core Livestock:**
+1. **Chicken** - Eggs, feathers | Mutant: Three-Headed Chicken (3ร eggs!)
+2. **Cow** - Milk, beef | Mutant: Extra udders (2ร milk!)
+3. **Pig** - Truffles, ham | Mutant: Giant Pig (rideable!)
+4. **Sheep** - Wool, milk | Mutant: Fire Sheep (fire wool!)
+5. **Horse** - Transportation, 35 km/h | Legendary: Undead Horse!
+
+**Additional Livestock:**
+6. **Goat** - Milk, cheese, 1,000g
+7. **Duck** - Eggs, feathers, water fowl
+8. **Rabbit** - Meat, fur, fast breeders!
+9. **Donkey** - Cargo carrier, 100km range!
+10. **Llama/Alpaca** - Caravan animals (6-animal trains!), wool
+
+---
+
+### **3. WILD ANIMALS (15 species)** ๐ฆ๐ป
+
+**Forest Animals:**
+1. **Wolf** - Pack predator, 100 HP, can tame babies!
+2. **Bear** - Tank, 300 HP, powerful attacks
+3. **Wild Boar** - Charge attack, 120 HP
+4. **Deer** - Fast, 80 HP, peaceful
+5. **Fox** - Quick, 60 HP, sneaky
+6. **Squirrel** - Tiny, collectible
+7. **Badger** - Underground, aggressive
+8. **Hedgehog** - Defensive spines
+
+**Mountain/Sky:**
+9. **Eagle** - Flying scout, 70 HP
+10. **Owl** - Night hunter, 50 HP
+11. **Bat** - Cave dweller, swarm
+12. **Lynx** - Stealth predator, 90 HP
+13. **Elk** - Large herbivore, 150 HP
+
+**Wetland:**
+14. **Frog** - Small, catchable
+15. **Snake** - Various types, some venomous
+
+---
+
+### **4. DINOSAURS (15 species - Dino Valley)** ๐ฆ
+
+**Large Carnivores:**
+1. **T-Rex** - BOSS! 1,000 HP, king of dinosaurs!
+2. **Spinosaurus** - Semi-aquatic, 800 HP
+3. **Allosaurus** - Pack hunter, 600 HP
+4. **Carnotaurus** - Fast predator, 500 HP
+
+**Medium Carnivores:**
+5. **Velociraptor** - Pack, intelligent, 200 HP, tameable!
+6. **Dilophosaurus** - Poison spit, 250 HP
+7. **Compsognathus** - Tiny! Swarms, 30 HP
+
+**Herbivores:**
+8. **Triceratops** - Tank, 700 HP, charging attacks
+9. **Stegosaurus** - Tail spikes, 600 HP
+10. **Ankylosaurus** - Armored, club tail, 650 HP
+11. **Brachiosaurus** - HUGE! 900 HP, gentle giant
+12. **Parasaurolophus** - Herd animal, 400 HP
+13. **Pachycephalosaurus** - Head-butt attack, 300 HP
+
+**Flying:**
+14. **Pterodactyl** - Sky hunter, 150 HP, mountable!
+
+**Babies:**
+15. **Baby Dinos** - Cute versions, tameable! (all species)
+
+---
+
+### **5. MYTHICAL CREATURES (39 species)** โจ๐
+
+#### **MYTHICAL HIGHLANDS (6):**
+1. **Dragon** - Fire/Ice/Storm variants, 800 HP, ULTIMATE mount!
+2. **Unicorn** - Healing aura, 300 HP, rare!
+3. **Pegasus** - Flying horse, 250 HP
+4. **Griffin** - Lion/eagle hybrid, 400 HP
+5. **Phoenix** - Revives you 1ร/day! 300 HP
+6. **Yeti** - Mountain giant, 500 HP
+
+#### **ENDLESS FOREST CRYPTIDS (3):**
+7. **Bigfoot** - Gentle giant, 400 HP, peaceful
+8. **Wendigo** - Hostile spirit, 350 HP, night hunter
+9. **Werewolf** - Full moon only! 300 HP, silver weakness
+
+#### **LOCH NESS & CELTIC (6):**
+10. **Nessie** - BOSS! Lake monster, 900 HP
+11. **Kelpie** - Water horse, 200 HP, shapeshifter
+12. **Selkie** - Seal-human, peaceful, trader
+13. **Leprechaun** - Gold hoarder, 50 HP, trickster
+14. **Banshee** - Wailing spirit, 150 HP, fear attack
+15. **Brownie** - House spirit, farm helper
+
+#### **ATLANTIS (4):**
+16. **Mermaid** - Male + Female, 150 HP, underwater
+17. **Kraken** - BOSS! 1,200 HP, tentacle attacks!
+18. **Sea Serpent** - 600 HP, ocean predator
+19. **Sea Dragon** - 700 HP, underwater mount
+
+#### **EGYPTIAN DESERT (4):**
+20. **Sphinx** - BOSS! 800 HP, riddle giver
+21. **Mummy** - Undead, 200 HP, curse attacks
+22. **Giant Scarab** - 400 HP, underground
+23. **Anubis Guardian** - 500 HP, temple protector
+
+#### **WITCH FOREST (4):**
+24. **Baba Yaga** - BOSS! Witch, 700 HP, flying mortar!
+25. **Witch** - Various types, 150 HP
+26. **Black Cat** - Familiar, 30 HP, magic
+27. **Raven** - Magical, 40 HP, scout
+
+#### **MEXICAN CENOTES (2):**
+28. **Quetzalcoatl** - BOSS! Feathered serpent, 850 HP
+29. **Axolotl** - 6 variants! Tameable, adorable!
+
+#### **AMAZON RAINFOREST (6):**
+30. **Giant Anaconda** - BOSS! 50m long! 900 HP
+31. **Jaguar** - Apex predator, 250 HP
+32. **Piranha** - Swarms, 20 HP each
+33. **Poison Dart Frog** - Tiny, deadly, 10 HP
+34. **Sloth** - Slow, peaceful, 80 HP
+35. **Toucan** - Colorful, 40 HP
+36. **Capybara** - Largest rodent, 100 HP, friendly!
+
+#### **๐ฆ CHUPACABRA (3 VARIANTS!) - ADDED 06.01.2026:**
+
+**37. Mexican Chupacabra** ๐ฒ๐ฝ
+- **Location:** Mexican Cenotes biome
+- **Appearance:** Reptilian, spines on back, red eyes, gray skin
+- **HP:** 250
+- **Attack:** Blood drain (life steal!)
+- **Behavior:** Night hunter, attacks livestock!
+- **Drops:** Chupacabra Fang, Blood Essence
+- **Origin:** Original Latin American cryptid
+
+**38. Mutant Chupacabra** โข๏ธ
+- **Location:** Chernobyl/Wasteland
+- **Appearance:** Hairless, glowing eyes, radiation scars, GREEN drool!
+- **HP:** 300
+- **Attack:** Blood drain + radiation damage
+- **Behavior:** Radioactive predator
+- **Drops:** Radioactive Fang, Mutant DNA
+- **Origin:** Radiation-mutated variant
+
+**39. Cryptid Chupacabra** ๐ฒ
+- **Location:** Endless Forest
+- **Appearance:** Mystery cryptid, rarely seen, shadowy
+- **HP:** 280
+- **Attack:** Stealth blood drain
+- **Behavior:** Stealth predator, VERY rare spawn (1% chance!)
+- **Drops:** Pure Blood Essence (LEGENDARY!), Cryptid Fur
+- **Origin:** Classic cryptozoology version
+
+**Total Mythical Creatures:** 39 (was 36, +3 Chupacabra!)
+
+---
+
+### **6. WORKER CREATURES (6 types)** ๐งโ๏ธ
+
+**1. Vilinci (Elves)**
+- Specialty: Crafting
+- Auto-crafting, enchanting
+
+**2. Gnomi (Gnomes)**
+- Specialty: Mining
+- Ore detection, garden help
+
+**3. Vile (Fairies)**
+- Specialty: Plant care
+- Instant growth, blessings
+
+**4. Bigfoot/Yeti**
+- Specialty: Gathering
+- Forest/Snow operations
+
+**5. Golem**
+- Specialty: Labor
+- Construction, defense
+
+**6. Zmaj (Dragon)**
+- Specialty: ULTIMATE
+- Mount, fire breath, treasure finder
+
+---
+
+### **๐ CREATURE SUMMARY:**
+
+| Category | Count | Tameable? | Notes |
+|----------|-------|-----------|-------|
+| Chernobyl Mutants | 10 | Some babies | Radioactive, dangerous |
+| Farm Animals | 10 | Yes | Breeding, production |
+| Wild Animals | 15 | Babies only | Natural ecosystem |
+| Dinosaurs | 15 | Babies only | Dino Valley exclusive |
+| Mythical Creatures | 39 | Some | Magical, rare (includes 3 Chupacabra!) |
+| Worker Creatures | 6 | Yes | Farm helpers |
+| Chupacabra Variants | 3 | NO | Blood-drainers, cryptids |
+| **TOTAL** | **109** | **Varies** | **Complete bestiary!** |
+
+**NOT INCLUDING BOSSES** (separate section!)
+
+---
+
+## ๐๏ธ NPCs & FACTIONS
+
+**Basic Farm Animals:**
+- **Cows (3)**: Normal (Holstein pattern), Brown, Highland (shaggy hair)
+- **Sheep (3)**: Normal (white wool), Black, Merino (extra fluffy)
+- **Chickens (3)**: White, Brown, Rooster
+- **Pigs (3)**: Normal (pink), Spotted, Wild boar variant
+- **Horses (3)**: Brown, White, Black
+- **Goats (2)**: Normal, Angora
+- **Ducks (2)**: White, Mallard
+- **Other (6)**: Goose, Turkey, Farm Rabbit, Llama, Alpaca, Donkey
+
+**Mutant/Magic Variants:**
+- Golden Cow, Fire Sheep, Ice Sheep, Golden Fleece Sheep
+- Phoenix Chicken, Rainbow Chicken, Three-Headed Chicken
+- Giant Pig (rideable), Undead Horse, Unicorn, Pegasus, Golden Goose
+
+### Wildlife (~200 assets)
+
+**Forest Animals:**
+Fox (red, arctic), Deer (normal, doe, fawn), Rabbit (wild, arctic), Hedgehog, Squirrel (red, grey), Raccoon, Skunk, Opossum, Beaver, River Otter, Badger, Porcupine, Bears (brown, black, polar), Wolves (grey, white, black), Moose, Elk, Wild Boar
+
+**Birds:**
+Owls (barn, great horned, snowy), Eagles (bald, golden), Hawk, Falcon, Crow, Raven, Sparrow, Robin, Bluebird, Cardinal, Woodpecker, Hummingbird, Peacock, Swans (white, black), Pelican, Flamingo, Parrots (green, macaw)
+
+**Insects & Small:**
+Butterflies (monarch, blue, yellow), Bees (honey, bumblebee), Dragonfly, Firefly, Ladybug, Grasshopper, Cricket, Caterpillar, Snail, Slug, Worm, Frogs (green, red), Toad, Turtles (box, snapping), Snakes (garden, rattlesnake), Lizard, Gecko
+
+**Aquatic:**
+Fish (bass, trout, salmon, tuna, cod, mackerel, carp, catfish, pike, perch, goldfish, koi, clownfish, angelfish, pufferfish, swordfish, golden), Sharks (great white, hammerhead, whale shark), Rays (manta, stingray), Jellyfish (normal, lion's mane), Octopus, Squid, Crabs (red, blue), Lobster, Shrimp, Starfish, Seahorse, Whales (blue, orca), Dolphin, Seal, Walrus, Penguins (emperor, little)
+
+---
+
+## ๐น MONSTERS & MUTANTS (~500 assets)
+
+### Slimes (12 types)
+Green, Blue, Red, Yellow, Purple, Black, White, Rainbow, Metal, Gold, King Slime (BOSS), Queen Slime
+
+### Undead
+**Zombies (8)**: Farmer, Miner, Villager, Soldier, Knight, Giant, Dog, Crow
+**Skeletons (5)**: Basic, Warrior, Archer, Mage, King (BOSS)
+**Ghosts (5)**: Basic, Lantern, Wraith, Banshee, Poltergeist
+
+### Humanoid Monsters
+**Goblins (4)**: Basic, Warrior, Shaman, Chief
+**Orcs (4)**: Basic, Warrior, Shaman, Warlord (BOSS)
+**Trolls (4)**: Forest, Cave, Ice, Fire
+
+### Creatures
+**Spiders (4)**: Basic, Venomous, Cave, Queen (BOSS)
+**Bats (4)**: Normal, Vampire, Fire, Giant
+**Elementals (6)**: Fire, Water, Earth, Air, Ice, Lightning
+**Golems (6)**: Stone, Iron, Crystal, Wood, Mud, Gold
+**Demons (4)**: Imp, Fire, Shadow, Lord (BOSS)
+**Dragons (6)**: Baby, Fire, Ice, Earth, Shadow, Ancient (BOSS)
+
+### Mythical
+Mimic, Basilisk, Cockatrice, Manticore, Chimera, Hydra, Phoenix, Griffin, Hippogriff, Centaur, Minotaur, Harpy, Siren, Medusa, Cyclops, Kraken (BOSS), Leviathan (BOSS), Werewolf, Vampire, Mummy, Lich, Treant, Will-o-wisp, Mushroom Monster, Plant Monster, Shadow Creature
+
+---
+
+## ๐ BOSSES (25 total)
+
+1. Zombie King
+2. Slime Emperor
+3. Ancient Tree
+4. Elder Dragon
+5. Ice Titan
+6. Lava Lord
+7. Shadow Master
+8. Crystal Guardian
+9. Spider Empress
+10. Kraken
+11. Demon Prince
+12. Lich King
+13. Werewolf Alpha
+14. Vampire Lord
+15. Mummy Pharaoh
+16. Minotaur King
+17. Hydra
+18. Chimera
+19. Giant Troll
+20. Orc Warlord
+21. Goblin King
+22. Elemental Titan
+23. Golem Prime
+24. Ancient Phoenix
+25. Leviathan
+
+---
+
+## ๐ BIOMES & LOCATIONS (20 Total - COMPLETE!)
+**Status:** โ
**20/20 DEFINED!** | ๐ **Assets In Progress** | ๐ฅ **PHASE 1 PRIORITY**
+
+| Biome Category | Count | Design | Assets | Status | Phase |
+|----------------|-------|--------|--------|--------|-------|
+| **Normal Biomes** | 9 | โ
9/9 | ๐ Partial | ๐ Design Done | ๐ฅ Phase 1 |
+| **Magical Biomes** | 11 | โ
11/11 | โ None | ๐ Design Done | โก Phase 2 |
+| **TOTAL BIOMES** | **20** | **โ
20/20** | **๐ Partial** | **โ
100% Designed** | Multiple |
+
+**Production Priority:**
+1. ๐ฏ **DEMO:** Grassland (Tutorial zone) - โ **CRITICAL**
+2. ๐ฅ **Phase 1:** Forest, Desert, Swamp
+3. โก **Phase 2:** Dino Valley, Mythical Highlands, Mexican Cenotes, Witch Forest
+4. โก **Phase 2:** Chernobyl (Final Boss Zone!)
+
+---
+
+### NORMAL BIOMES (9) - ๐ฅ **PHASE 1**
+
+**1. Grassland (Travnik)** - Home base
+- Ground: Lush green grass, Style 32, smooth vector
+- Props: Grass variants, flower patches, small rocks
+
+**2. Forest (Gozd)** - Dense trees
+- Ground: Dark green grass with moss
+- Props: Moss patches, mushrooms (red/brown), dense bushes, fallen logs
+
+**3. Desert** - Sandy wasteland
+- Ground: Golden yellow sand
+- Props: Sand dunes, cacti (small/large), brown rocks, bleached skulls
+
+**4. Mountain** - Rocky peaks
+- Ground: Grey stone
+- Props: Large boulders, sharp rock formations, snow patches
+
+**5. Swamp (Moฤvirje)** - Foggy wetlands
+- Ground: Dark brown murky mud
+- Props: Murky water puddles, dead trees, hanging vines, fog effects
+
+**6. Snow (Frozen Tundra/Arktika)** - Ice and blizzards
+- Ground: Pristine white snow
+- Props: Ice tiles, frozen trees, snowdrifts, icicles
+
+**7. Wasteland** - Ruins and rubble
+- Ground: Cracked grey earth
+- Props: Rubble piles, scrap metal, broken machinery
+
+**8. Tropical (Beach)** - Palm trees, coconuts
+- Ground: Light golden beach sand
+- Props: Palm trees, coconuts, seashells, clear blue water
+
+**9. Radioactive Zone** - Glowing toxic
+- Ground: Sickly green glow
+- Props: Glowing rocks, mutant plants, barrels (toxic waste symbol), toxic puddles
+
+### ANOMALOUS BIOMES (9) - Portal-locked
+
+**10. Dino Valley** - Prehistoric
+- Ground: Ancient green grass
+- Props: Prehistoric cycad trees, large ferns, dinosaur footprints, fossils, cracked eggs
+
+**11. Mythical Highlands** - Magical
+- Ground: Purple mystic grass
+- Props: Magical trees (glowing leaves), blue crystals, floating rocks with runes, rainbows
+
+**12. Endless Forest (WITCH FOREST!)** - Cryptids
+- Ground: Dark mysterious grass
+- Props: Ancient massive trees, thick mystery fog, cryptid footprints, hidden trail markers
+
+**13. Loch Ness** - Scottish highlands
+- Ground: Heather moorland
+- Props: Scottish pine trees, purple heather flowers, dark loch water, castle ruins
+
+**14. Catacombs** - Underground undead
+- Ground: Ancient stone floor
+- Props: Scattered bones, stone tombs, skulls, dusty urns
+
+**15. Egyptian Desert** - Pyramids
+- Ground: Golden sand
+- Props: Massive sand dunes, hieroglyph carved stones, golden scarabs, pyramid stone blocks
+
+**16. Amazon Rainforest** - Piranhas & AXOLOTLS!
+- Ground: Dense jungle undergrowth
+- Props: Massive jungle trees, thick hanging vines, exotic pink flowers, tribal totem poles, piranha-filled rivers, pink axolotls
+
+**17. Atlantis** - Underwater & AXOLOTLS!
+- Ground: Ocean floor sand
+- Props: Colorful coral reefs, ancient ruins (columns), giant pearls in oysters, flowing seaweed, rising bubble streams, blue glowing axolotls
+
+**18. Chernobyl (FINAL ZONE!)** - Nuclear
+- Ground: Contaminated grey earth
+- Props: Reactor ruins, abandoned buildings (broken windows), radioactive waste barrels, Soviet propaganda posters (faded), hazmat warning signs
+
+**19. Mexican Cenotes ๐ฒ๐ฝ** - Mayan underwater caves
+- Ground: Limestone cave floor, crystal clear water
+- Props: Mayan pyramid (small), temple ruins, stone altars, underground passages, jade deposits, cenote water (pure), axolotls (6 color variants!), stalactites/stalagmites
+- NPCs (8-12): Mayan Priest, Villagers (5), Archaeologists (2), Diver, Cenote Guide, Shaman, Jade Merchant
+- Boss: **Quetzalcoatl** (flying feathered serpent god!)
+- Features: Crystal clear cenote diving, axolotl sanctuary, Mayan culture, ancient secrets
+
+**20. Witch Forest ๐ง** - Dark magic forest
+- Ground: Cursed earth, dark grass
+- Props: Witch huts (5), **Baba Yaga's Hut (walking on chicken legs!)**, dark altars (2), hanging cages, cauldron areas, ritual circles, dark herbs (5 types), cursed gems (3), poison mushrooms (6 types)
+- NPCs (6-10): **Baba Yaga (BOSS!)**, Witches (3 types: hostile/neutral), Black Cat (familiar NPC - talks!), Cursed Knight, Lost Child (ghost), Potion Seller, Herbalist
+- Boss: **Baba Yaga** (flies in mortar, magic attacks!)
+- Special: **Baba Yaga's walking chicken-leg hut!**, Dark atmosphere, cursed areas, poison swamps, magical fog
+- Reward: **Broomstick** (magical flight!) from defeating Baba Yaga
+
+---
+
+## ๐พ CROPS & PLANTS (~500 assets with growth stages)
+**Status:** ๐ **80/80 HARVESTED COMPLETE** | โ
**Jan 7, 2026**
+
+### Basic Crops (80 types) - โ
**HARVESTED VERSIONS DONE!**
+
+Each crop will have:
+- 1 seed packet (โ TODO)
+- 4 growth stages: sprout, growing, maturing, ready (โ TODO)
+- 1 harvested product โ
**DONE - 80/80!**
+
+**Grains (6)**: Wheat, Corn, Rice, Barley, Oats, Rye
+
+**Root Vegetables (7)**: Potato, Carrot, Onion, Garlic, Turnip, Radish, Beet
+
+**Fruiting Vegetables (7)**: Tomato, Pepper, Eggplant, Cucumber, Zucchini, Pumpkin, Squash
+
+**Leafy Greens (6)**: Lettuce, Cabbage, Spinach, Kale, Broccoli, Cauliflower
+
+**Berries (7)**: Strawberry, Blueberry, Raspberry, Blackberry, Grape, Melon, Watermelon
+
+**Flowers (8)**: Sunflower, Tulip, Rose, Lily, Daisy, Lavender, Poppy
+
+**Industrial (7)**: Cotton, Flax, Hemp, Tobacco, Coffee, Tea, Cocoa
+
+**Tree Fruits (8)**: Apple, Orange, Lemon, Peach, Pear, Cherry, Plum, Banana
+
+**Tropical (6)**: Pineapple, Coconut, Mango, Papaya, Kiwi, Avocado
+
+**Spices (6)**: Hop, Ginger, Turmeric, Vanilla, Cinnamon, Black Pepper
+
+**Hot Peppers (4)**: Chili, Jalapeno, Habanero, Ghost Pepper
+
+**Herbs (8)**: Basil, Mint, Oregano, Thyme, Rosemary, Sage, Parsley, Cilantro
+
+**Mushrooms (4)**: Button, Shiitake, Oyster, Truffle
+
+### Trees (23/44 done - 52%)
+
+**Base Species (11):** Oak, Maple, Pine, Birch, Willow, Cherry Blossom, Apple, Orange, Lemon, Palm, Bamboo
+**Status:** โ
**11 Base + 12 Variants = 23 TOTAL** | Generated: Jan 7, 2026
+
+**Still Needed:** 21 seasonal variants (each tree needs 4 seasons total)
+
+---
+
+## โ๏ธ ITEMS & TOOLS (~2000 assets)
+
+### Tools (9 types ร 9 materials = 81)
+
+**Types**: Axe, Pickaxe, Hoe, Shovel, Scythe, Hammer, Sickle, Saw, Chisel
+**Materials**: Wood, Stone, Copper, Iron, Steel, Gold, Diamond, Mythril, Adamantine
+
+### Weapons (10 types ร 9 materials = 90)
+
+### โ๏ธ **COMBAT & WEAPONS (MEDIEVAL FANTASY)**
+
+**Philosophy:** Game is fantasy-focused. No modern guns (Pistol/Rifle โ) except ONE special case.
+
+#### **๐ซ BANNED WEAPONS:**
+โ Pistol, Rifle, SMG, Sniper, RPG, Ray Gun.
+
+#### **๐ซ THE ONLY GUN: SHOTGUN (Chernobyl Exclusive)**
+- **Unlock:** Found in Chernobyl Military Checkpoint (Late Game).
+- **Ammo:** Shells (Expensive Crafting: Gunpowder + Iron + Brass).
+- **Stats:** High spread damage, slow reload (2 shots).
+- **Use:** Emergency defense against hordes.
+
+#### **๐น RANGED ARSENAL (21 Arrow Types)**
+**Bows:** Primitive -> Hunting -> Longbow -> Compound -> Enchanted -> Elven -> Phoenix -> **Dragon Bow**.
+
+**Arrows (Craftable):**
+1. **Basic:** Wood, Stone, Iron, Steel, Mythril.
+2. **Elemental:** Fire (Burn), Ice (Freeze), Lightning (Chain), Poison (DoT), Explosive (Area), Shadow (Blind), Holy (vs Undead), Nature (Root).
+3. **Special:** Grappling (Climb), Teleport (Blink), Healing (Ally), Vampire (Life Steal), Tracking (Auto-Aim), Drill (Pierce Armor), Rainbow (Random).
+
+---
+
+### ๐ง **MEMORY & TWIN BOND SYSTEM (DEEP DIVE)**
+
+#### **1. FARMING AS THERAPY (Passive Recovery) โ
**
+*Action = 5% Chance of Memory Flashback.*
+- **Planting:** Remembers Father's gardening lessons. (+XP)
+- **Watering:** Remembers Ana's science facts. (+Crop Speed)
+- **Harvesting:** Remembers Mother's harvest festival. (+HP)
+
+#### **2. STORY FLASHBACKS (The 6 Pillars) โ
**
+*Unlocking these strengthens the Twin Bond.*
+1. **Twin Bond Discovery (Lvl 5):** Hospital scene, doctors realize twins share pain.
+2. **First Protection (Clues 5/50):** Playground fight, young Kai protects Ana.
+3. **Mother's Last Words (Lvl 15):** Outbreak Day 3, Mother sacrifices herself.
+4. **The Kidnapping (Lvl 25):** Troll King attack, Kai is knocked out, Ana taken.
+5. **Dr. Krniฤ's Plan (Clues 35/50):** Security footage, Krniฤ reveals he MADE the virus.
+6. **Ana's Final Message (Clues 50/50):** Video diary from Chernobyl ("I'm alive... Come get me.").
+
+---
+
+### โ๏ธ **CORE SYSTEMS (CODE IMPLEMENTED)**
+
+#### **1. MICRO FARM SYSTEM (`MicroFarmSystem.js`) โ
**
+- **Start:** 8x8 Tile Grid (Locked by fog/overlay).
+- **Expansion:** Pay Gold to unlock 2x2 sections (North, South, East, West).
+- **Land Types:** Grass (Free), Forest (Needs Axe), Rocky (Needs Pickaxe), Swamp (Needs Drainage).
+
+#### **2. ZOMBIE WORKER AI (`ZombieWorkerSystem.js` & `ZombieEconomySystem.js`) โ
**
+- **Roles:** Farmer (Water/Harvest), Miner (Stone), Clearer (Trees), **Sanitation (Clean City)**.
+- **Universal Lending:** Rent Zombies to **ANY** town NPC (Baker, Tailor, Teacher...) for profit.
+- **Payment:** Earn **Gold** OR **Rare Gifts** (*Kai's Hidden Photo, Ancient Dye, Rare Seed Pack, Mystical Paint, Family Heirloom*).
+- **City Sanitation:** Zombies scrub graffiti & pick up trash -> Boosts City Happiness.
+- **Maintenance:** Workers consume **Brains** (Fuel). Low Brains = Slow/Grumbling.
+
+#### **3. ZOMBIE SCOUT EVOLUTION (`ZombieScoutLevelingSystem.js`) โ
**
+*The Scout is a full RPG Companion.*
+- **Level Cap:** 1-20. Gathers XP from Combat, Digging, and Quests.
+- **Level 20:** Evolves into **"Scout Commander"** (Larger, Stronger, New Skin).
+- **Skill Tree:**
+ - **Combat:** *Claw Swipe* (Dmg), *Leap Attack* (Jump Dmg), *Undead Resilience* (Tank).
+ - **Digging:** *Speed Dig* (Fast), *Treasure Sense* (See buried loot).
+ - **Utility:** *Pack Mule* (+10 Inv Slots), *Night Vision*, *Scout Sprint*.
+
+#### **4. VISUAL & ACCESSIBILITY (`VisualEnhancementSystem.js`) โ
**
+- **Animated Textures:** Water flow (4-frame wave), Fire flickering, Tree rustling.
+- **Atmosphere:** Multi-layered **Noir Fog**, Rain/Snow particles, Dynamic Lightning (Radial gradients).
+- **Typewriter Effect:** Dynamic text reveal with "ADHD Mode" (Instant/Fast/Normal speeds).
+- **Lighting:** Torches flicker (0.4-0.6 alpha), Day/Night cycle shadows.
+
+#### **5. MASTER SYSTEM ARCHITECTURE (`MasterGameSystemsManager.js`) ๐**
+*Center of Operation. Coordinates 15+ sub-systems.*
+- **Cross-System Events:** Marriage unlocks Lawyer; Building Bakery unlocks Food Orders.
+- **Automation:** Checks Zombie Miners every 4 hours for yield collection.
+- **State Management:** Handles saving/loading for EVERY system in one JSON object.
+
+#### **6. THE SILENT PROTECTOR (`GameManager.js`) ๐พ**
+*Never loose progress.*
+- **Auto-Save Triggers:** Scene Change, Aging Up, Memory Found, Gronk Level Up.
+- **Safety:** Periodic 5-minute backup saves.
+- **Visuals:** Spinning Longboard icon when saving.
+
+#### **7. CORE CODE VARIABLES (GAME DNA) ๐งฌ**
+*The heartbeat of the logic.*
+- `var is_ana_found = false` (Story State: Twin Bond active?)
+- `var zombie_labor_count = 0` (Economy: Total active undead workers)
+- `func start_amnesia_effect()` (Visuals: The Blur/Fog state on wakeup)
+- `var current_biom = "Nova Farma"` (World: Tracks location across 18 biomes)
+
+#### **8. ANIMAL COMPANION AI (`WorkerCreaturesSystem.js`) ๐ฆ๐ฆ**
+*Alpha Hybrid powers control nature.*
+- **Night Owl:** Spawns at 00:00. Scouting + Gift Drops.
+- **Bat Swarm:** Spawns at Dusk. Resource Gathering + Radar.
+- **T-Rex:** *Deprecated AI (Removed per user request).*
+
+#### **9. THE 4 ENDINGS (`MainQuestAnaSystem.js`) ๐ฌ**
+1. **Sacrifice:** Kai dies to save Ana.
+2. **Survival:** Both live but the town falls.
+3. **Ascension:** Kai becomes the Zombie King.
+4. **UTOPIA (True Ending):** Save Ana + 180 NPCs rescued.
+
+---
+
+### ๐ฎ **MYSTERY & STORY MECHANICS (RECOVERED LOGIC)**
+
+#### **1. AGING SYSTEM (Protagonists Only) โณ**
+*Only **Kai & Ana** physically age as the game progresses (Years 2038-2042).*
+- **Visuals:** Sprites get older, scars heal/fade, hair grows.
+- **Effect:** Stats change slightly (Strength up, Speed down) as they mature.
+
+#### **2. THE GHOST TRINITY (3 Distinct Types) ๐ป**
+1. **Ghost Parents:** Benevolent spirits that guide Kai to story clues (Non-hostile).
+2. **Drug Hallucinations:** Visual distortions caused by "Zombie Drugs"/Mushrooms. (False realities).
+3. **Graveyard Spirits:** Restless NPC souls in the Church Graveyard (Hostile/Neutral).
+
+#### **3. TWIN BOND: PULSE & DE-BLUR ๐ง **
+*Revised Logic: No magic attacks.*
+- **Twin Pulse:** Screen shakes/vibrates when near a Memory Clue or Ana's location.
+- **Amnesia De-Blur:** The world starts **BLURRED**. Finding memories (Flashbacks) permanently **clears the vision** (De-Blur effect).
+
+#### **4. GRONK'S VAPE MECHANICS ๐จ**
+*Not just a skin - a Gameplay Utility.*
+- **Supervisor Mode:** Gronk leans on fences & vapes while watching Zombies farm.
+- **Smoke Signals:** Gronk uses vape clouds to signal safe paths or hidden items.
+- **Vape Shield:** Creates a temporary smoke screen to hide from zombies.
+- **Chill Zone:** Vape cloud regenerates Stamina for nearby allies.
+
+#### **5. INTRO SEQUENCE: "THE AWAKENING" ๐ฌ**
+- **Visuals:** Complete Blur -> Eye Blink effect -> Slow focus.
+- **Audio:** Muffled sounds -> Ringing -> Sharp reality.
+- **Context:** Kai wakes up with total Amnesia; the player must "unlock" his sight.
+
+---
+
+### ๐๏ธ **TOWN RESTORATION LOGIC (`TownRestorationLogic.js`) โ
**
+*Building restoration directly unlocks specific NPCs & mechanics.*
+
+| Building | Unlocked NPC | Mechanic / Benefit |
+|----------|-------------|--------------------|
+| **Hospital** ๐ฅ | **Dr. Ana** | Unlocks Healing & Med Kits. |
+| **Police Station** ๐ | **Sheriff/Guard** | Unlocks Patrols & Security. |
+| **Mayor's Office** ๐๏ธ | **ลฝupan (Mayor)** | Unlocks Elections & City Management. |
+| **Church** โช | **ลฝupnik (Priest)** | Unlocks Blessings & Graveyard Access. |
+| **School** ๐ซ | **Uฤitelj (Teacher)** | Unlocks Education Buffs. |
+| **Tech Workshop** โ๏ธ | **Tehnik** | Unlocks Electronics Crafting. |
+| **Tailor Shop** ๐งต | **ล ivilja** | Unlocks Armor/Clothing Upgrades. |
+| **Bakery** ๐ฅ | **Pek (Baker)** | Unlocks High-Energy Food. |
+| **Museum** ๐๏ธ | **Kustos** | Unlocks Lore & Artifact Collection. |
+
+#### **2. GLOBAL RESTORATION SCOPE (27 TOWNS / 150 BUILDINGS)**
+*The project spans 18 Biomes. Restoring towns unlocks global bonuses.*
+- **Key Towns:** Hope Valley (Start), Forest Grove, Desert Oasis, Frozen Harbor, Volcanic Refuge, Coastal Bay, Mountain Peak, Swamp Village, Crystal City, Atlantis.
+- **Endgame Goal:** Rescue all **180 NPCs**.
+
+#### **3. RESTORATION MILESTONES ๐**
+- **10 NPCs:** Unlock `Community Center`.
+- **25 NPCs:** Unlock `Town Festivals`.
+- **50 NPCs:** Unlock `Town Guard` (Security).
+- **100 NPCs:** Unlock `Major City Project` (Skyscrapers).
+- **180 NPCs:** **UTOPIA ENDING** (True Pacifist Win).
+
+### Armor (6 pieces ร 7 materials = 42)
+
+**Pieces**: Helmet, Chestplate, Leggings, Boots, Gloves, Shield
+**Materials**: Leather, Chainmail, Iron, Steel, Gold, Diamond, Dragon
+
+### Arrows (10 types)
+
+Normal, Fire, Ice, Lightning, Poison, Explosive, Healing, Silver, Gold, Diamond
+
+### Potions (19 types)
+
+Health (Small/Medium/Large), Mana (Small/Medium/Large), Stamina, Speed, Strength, Defense, Invisibility, Fire Resistance, Cold Resistance, Poison Cure, Regeneration, Luck, Night Vision, Water Breathing, Slow Fall
+
+### Gems & Minerals (12 types ร 2 = 24)
+
+Ruby, Emerald, Sapphire, Diamond, Amethyst, Topaz, Opal, Pearl, Jade, Onyx, Quartz, Obsidian
+(Each as gem + ore)
+
+### Metals (8 types ร 2 = 16)
+
+Copper, Iron, Gold, Silver, Platinum, Mythril, Adamantine, Titanium
+(Each as ore + bar)
+
+### Food (29 prepared items)
+
+Bread, Cheese, Butter, Milk, Egg, Bacon, Sausage, Apple Pie, Cherry Pie, Pumpkin Pie, Fish Cooked, Meat Cooked, Salad, Soup, Stew, Sandwich, Pizza, Cake, Cookie, Strawberry Jam, Blueberry Jam, Red Wine, White Wine, Beer, Juice, Honey, Sugar, Flour, Salt, Pepper, Oil
+
+### Crafting Materials (26 types)
+
+Wood Plank, Wood Stick, Stone Brick, Clay, Glass, Leather, Cloth, Rope, String, Nail, Screw, Gear, Spring, Chain, Feather, Bone, Horn, Fur, Scale, Slime Gel, Ectoplasm, Magic Dust, Fairy Dust, Dragon Scale, Unicorn Hair, Phoenix Feather
+
+---
+
+## ๐๏ธ BUILDINGS & STRUCTURES (~300 assets)
+**Status:** ๐ **6 Farm Buildings Complete** | โ
**Jan 7, 2026**
+
+### Farm Buildings (11) - โ
**6/11 MASTER REFERENCES DONE!**
+
+**COMPLETED:** Farmhouse (gothic cottage), Barn (weathered red), Silo (rusted metal), Shed (dark wood), Well (stone with bucket), Windmill (broken blades)
+
+### Production Buildings (9)
+
+Blacksmith, Carpenter Shop, Tailor Shop, Bakery, Butcher, Brewery, Winery, Apothecary, Jewelry Shop
+
+### Town Buildings (15)
+
+House (Small/Medium/Large), Inn, Tavern, General Store, Town Hall, Church, School, Library, Museum, Hospital, Bank, Barracks, Watchtower
+
+### Decorative (10)
+
+Well (Stone/Wood), Fountain, Hero Statue, Goddess Statue, Gazebo, Bridge (Wood/Stone), Dock, Lighthouse
+
+### Fences & Gates (6)
+
+Wooden Fence, Stone Wall, Iron Fence, Hedge Fence, Wooden Gate, Iron Gate
+
+### Storage (6)
+
+Wooden Chest, Iron Chest, Gold Chest, Barrel, Crate, Cabinet
+
+---
+
+## ๐ฒ ENVIRONMENT & TERRAIN (~800 assets)
+
+### Ground Tiles (16)
+
+Grass (light, dark, dry, snow), Dirt (path, tilled), Stone path, Cobblestone, Sand, Gravel, Mud, Snow, Ice, Water (shallow, deep), Lava
+
+### Nature Objects (13)
+
+Rocks (small, medium, large, moss), Fallen log, Old stump, Bushes (green, berry, flower), Tall grass, Reeds, Cattails, Lily pad, Coral, Seaweed
+
+### Flowers (8 colors)
+
+Red, Blue, Yellow, Purple, White, Pink, Orange, Mixed patch
+
+### Mushrooms (6 types)
+
+Red (spotted), Brown (edible), Blue (magic), Purple (poisonous), Glowing (bioluminescent), Giant
+
+### Cave Elements (6)
+
+Stalactite, Stalagmite, Blue Crystal, Red Crystal, Green Crystal, Cave Moss (glowing)
+
+### Weather/Sky (10)
+
+White Cloud, Rain Cloud, Storm Cloud, Rainbow, Sun, Full Moon, Crescent Moon, Stars
+
+---
+
+## ๐ CLOTHING & ARMOR (~100 assets)
+**Status:** โ
**6 Worker Clothing Complete** | โ
**Jan 7, 2026**
+
+### Worker Clothing (6) - โ
**MASTER REFERENCES DONE!**
+
+**COMPLETED:**
+- Farmer shirt (torn, patched, gothic)
+- Farmer pants (weathered, holes)
+- Work boots (muddy, heavy)
+- Straw hat (worn, dark)
+- Leather gloves (stitched)
+- Work apron (stained, pockets)
+
+**TODO:**
+- Armor sets (light, medium, heavy)
+- Weather-specific clothing
+- Biome-specific outfits
+- Accessories (scarves, belts, badges)
+
+---
+
+## ๐ฅ๏ธ UI ELEMENTS (~200 assets)
+
+### Buttons (12)
+
+Play, Pause, Stop, Menu, Settings, Close, Confirm, Cancel, Arrow (Left/Right/Up/Down)
+
+### Icons (12)
+
+Health (red heart), Mana (blue orb), Stamina (lightning), Gold (coin stack), Inventory (bag), Map (scroll), Quest (exclamation), Achievement (star), Mail (envelope), Settings (gear), Save (floppy), Load (folder)
+
+### Status Effects (6)
+
+Poison (green skull), Burn (flames), Freeze (snowflake), Stun (stars), Buff (arrow up), Debuff (arrow down)
+
+### Frames & Panels (5)
+
+Frame (Wood/Stone/Gold), Panel (Wood/Parchment)
+
+### Cursors (6)
+
+Normal (pointer), Interact (hand), Attack (sword), Dig (shovel), Plant (seed), Water (droplet)
+
+---
+
+## ๐จ ART STYLE & VISUAL IDENTITY
+**Status:** โ
**LOCKED & APPROVED** | ๐ฅ **CRITICAL FOR ALL ASSETS**
+
+### ๐ **MASTER STYLE: "Style 32 (Dark-Chibi Noir)"**
+
+**Official Style Name:** Dark-Chibi Noir
+**Style Code:** Style 32
+**Influence:** Gothic + Chibi + Post-Apocalyptic + Punk
+**Lock Date:** 30.12.2025
+**Status:** โ
**100% LOCKED - NO CHANGES ALLOWED!**
+
+---
+
+### ๐ฏ **CORE STYLE ELEMENTS** (From Master References)
+
+**Analyzed from uploaded reference + 18 generated master references**
+
+#### **1. CHARACTER PROPORTIONS (Chibi)**
+- โ
**Head-to-body ratio:** 1:1.5 to 1:2 (large head, small body)
+- โ
**Body:** Short, stubby limbs (2-3 head heights total)
+- โ
**Eyes:** HUGE, expressive (1/3 of face!)
+- โ
**Hands/Feet:** Small, simplified (3-4 fingers visible)
+- โ
**Overall:** Cute but DARK aesthetic
+
+**Reference:** Ana character shows perfect chibi proportions!
+
+#### **2. LINE ART & OUTLINING**
+- โ
**Outline thickness:** 3-4px black outlines (THICK!)
+- โ
**Line quality:** Clean vector-style lines (smooth, no rough edges)
+- โ
**Line color:** Pure black (#000000)
+- โ
**Interior lines:** 1-2px for details (thinner than outline)
+- โ
**Consistency:** ALL characters must have same line weight
+
+#### **3. COLOR PALETTE (Gothic + Noir)**
+
+**Primary Colors:**
+- โ
**Dark blues:** #2C3E50, #34495E (clothing, shadows)
+- โ
**Grays:** #7F8C8D, #95A5A6 (metals, stone)
+- โ
**Deep reds:** #C0392B, #E74C3C (eyes, accents!)
+- โ
**Muted greens:** #27AE60, #16A085 (nature, hair)
+- โ
**Blacks:** #2C2C2C, #1C1C1C (outlines, shadows)
+
+**Gothic Elements:**
+- โ
**Purple accents:** #8E44AD, #9B59B6 (magic, special items)
+- โ
**Blood red:** #8B0000 (danger, horror elements)
+- โ
**Bone white:** #F5F5DC (skulls, undead)
+- โ
**Midnight black:** #0A0A0A (night scenes, void)
+
+**Lighting Style:**
+- โ
High contrast (dark shadows + bright highlights)
+- โ
Rim lighting (light edge around characters)
+- โ
Dramatic lighting (from above or side, not flat)
+
+#### **4. EYE STYLE (CRITICAL!)**
+
+**Human/Friendly NPCs:**
+- โ
**Red eyes with DARK PUPILS** (Kai, Ana, NPCs)
+- โ
Pupils visible and centered
+- โ
White sclera (eye whites)
+- โ
Highlights in eyes (2-3 white dots)
+- โ
Expression: Friendly, alive, human
+
+**Zombies/Enemies:**
+- โ
**White eyes NO pupils** (pure white or yellow)
+- โ
OR **Red eyes NO pupils** (hostile zombies)
+- โ
No highlights (dead, soulless look)
+- โ
Expression: Empty, dead, hostile
+
+**Controlled Zombies:**
+- โ
**Eyes CHANGE when controlled:** White โ Red WITH pupils!
+- โ
Visual feedback for player!
+
+#### **5. CLOTHING & TEXTURE**
+
+**Post-Apocalyptic Details:**
+- โ
**TORN CLOTHING:** Rips, holes, patches visible
+- โ
**WEATHERED:** Dirt stains, wear marks
+- โ
**PRACTICAL:** Backpacks, pockets, utility
+- โ
**PUNK ELEMENTS:** Piercings, gauges, patches
+
+**Gothic Details:**
+- โ
**Stitches:** Visible on patches (Frankenstein-style)
+- โ
**Straps & Buckles:** Leather, metal clasps
+- โ
**Crosses & Symbols:** Subtle gothic iconography
+- โ
**Cloaks & Hoods:** Dramatic flowing fabric
+
+**Texture Style:**
+- โ
Cell-shaded (flat colors with sharp shadow edges)
+- โ
NO gradients (or very minimal)
+- โ
Patterns: Hatching for shadows, NOT soft gradients
+
+#### **6. FACIAL FEATURES (Gothic-Punk)**
+
+**Piercings & Modifications:**
+- โ
**Ear gauges** (Kai, Gronk - signature!)
+- โ
**Facial piercings** (eyebrow, nose, lip)
+- โ
**Tattoos** (optional, minimal)
+
+**Hair Styles:**
+- โ
**Dreadlocks** (Kai, Gronk - chunky, stylized!)
+- โ
**Messy/Spiked** (post-apocalyptic, not perfect)
+- โ
**Unnatural colors** OK! (green, pink, purple)
+
+**Expression:**
+- โ
Exaggerated anime-style emotions
+- โ
Large mouth (1/4 of face when open)
+- โ
Eyebrows expressive (thick, dramatic)
+
+#### **7. BACKGROUND & ENVIRONMENT**
+
+**Biome Aesthetic:**
+- โ
**Dark atmosphere** (low light, shadows everywhere)
+- โ
**Decay & Ruins** (broken buildings, overgrown)
+- โ
**Muted colors** (desaturated compared to characters)
+- โ
**Depth:** 3 layers (foreground, mid, background)
+
+**Gothic Architecture:**
+- โ
**Pointed arches** (churches, castles)
+- โ
**Crumbling stone** (weathered, ancient)
+- โ
**Overgrown** (vines, moss, nature reclaiming)
+- โ
**Tombstones & Crosses** (graveyard aesthetic)
+
+#### **8. SPECIAL EFFECTS**
+
+**Magic/Energy:**
+- โ
**Glowing effects:** Purple, blue, green auras
+- โ
**Particle effects:** Sparkles, dust motes
+- โ
**Fire:** Stylized, cartoon flames (NOT realistic)
+
+**Blood/Gore:**
+- โ
**Stylized blood:** Dark red, NOT realistic
+- โ
**Minimal gore:** Suggest violence, don't show graphic detail
+- โ
**16+ Rating:** Dark but not gratuitous
+
+---
+
+### ๐ธ **GENERATED MASTER REFERENCES (246 TOTAL PNG FILES! ๐ฅ)**
+
+**Status:** โ
246 reference images generated across all categories!
+
+**Last Count:** 06.01.2026 22:14 CET
+
+---
+
+#### **๐ BREAKDOWN BY CATEGORY:**
+
+| Category | Count | Location | Status |
+|----------|-------|----------|--------|
+| **Biome NPCs (Total)** | **166** | `assets/references/npcs/[biome]/` | โ
100% Complete! |
+| **Wild Animals** | 13 | `assets/references/creatures/wild_animals/` | ๐ 87% (13/15) |
+| **Farm Animals** | 6 | `assets/references/creatures/farm_animals/` | ๐ 60% (6/10) |
+| **Chernobyl Mutants** | 3 | `assets/references/creatures/chernobyl_mutants/` | ๐ 30% (3/10) |
+| **Nomad Raiders** | 8 | `assets/references/enemies/nomad_raiders/` | ๐ 67% (8/12) |
+| **Zombies** | 2 | `assets/references/enemies/zombies/` | ๐ 50% (2/4) |
+| **Main Characters** | 3 | `assets/references/main_characters/` | โ
75% (3/4) |
+| **Companions** | 4 | `assets/references/companions/` | โ
100% (4/4) |
+| **Species** | 6 | `assets/references/species/` | โ
100% (6/6) |
+| **Trees** | 11 | `assets/references/trees/` | ๐ Partial |
+| **Buildings** | 8 | `assets/references/buildings/` | ๐ Partial |
+| **Approved NPCs** | 11 | `assets/references/npcs/npc_style_approved/` | โ
11/11 |
+| **Bugs** | 2 | `assets/references/bugs/` | โ
2 bugs |
+| **OTHER** | 3 | Root directory | Misc |
+| **TOTAL PNG FILES** | **246** | Full `assets/references/` tree | **๐ฅ MASSIVE!** |
+
+---
+
+#### **๐๏ธ BIOME NPC BREAKDOWN (166 total!):**
+
+| Biome | NPCs Generated | Status |
+|-------|----------------|--------|
+| **Atlantis** | 17 | โ
Complete |
+| **Loch Ness** | 15 | โ
Complete |
+| **Mythical Highlands** | 12 | โ
Complete |
+| **Egyptian Desert** | 12 | โ
Complete |
+| **Amazon Rainforest** | 12 | โ
Complete |
+| **Tropical** | 10 | โ
Complete |
+| **Mexican Cenotes** | 10 | โ
Complete |
+| **Witch Forest** | 8 | โ
Complete |
+| **Dino Valley** | 8 | โ
Complete |
+| **Desert** | 8 | โ
Complete |
+| **Chernobyl** | 8 | โ
Complete |
+| **Grassland** | 7 | โ
Complete |
+| **Mountain** | 6 | โ
Complete |
+| **Forest** | 6 | โ
Complete |
+| **Catacombs** | 6 | โ
Complete |
+| **Snow/Arctic** | 5 | โ
Complete |
+| **Endless Forest** | 5 | โ
Complete |
+| **Wasteland** | 4 | โ
Complete |
+| **Swamp** | 4 | โ
Complete |
+| **Radioactive** | 3 | โ
Complete |
+| **TOTAL BIOME NPCs** | **166** | โ
**100% DONE!** |
+
+---
+
+#### **โ
MAIN CHARACTERS (3/4 - 75%):**
+
+1. โ
**Kai Markoviฤ** (`assets/references/main_characters/kai/master_reference.png`) - **FOUND!** ๐
+2. โ
**Ana Markoviฤ** (`assets/references/main_characters/ana/`) - 2 files
+3. โ
**Gronk Alpha Troll** (`assets/references/main_characters/gronk/`) - 2 files
+4. โ **Susi (promoted to main!)** - Is in companions!
+
+**Status:** Main 3 characters DONE! โ
+
+---
+
+#### **โ
VERIFIED NPCs (13/13 - 100%):**
+
+1. โ
Ivan Kovaฤ (Blacksmith) - `npcs/ivan_kovac/`
+2. โ
Mayor/ลฝupan - `npcs/mayor/`
+3. โ
Kustos (Museum) - `npcs/kustos/`
+4. โ
Pek (Baker) - `npcs/pek/`
+5. โ
ล ivilja (Tailor) - `npcs/sivilja/`
+6. โ
Tehnik (Engineer) - `npcs/tehnik/`
+7. โ
Teacher - `npcs/teacher/`
+8. โ
Priest Jakob - `npcs/priest/`
+9. โ
Miro Pravnik (Lawyer) - `npcs/miro_pravnik/`
+10. โ
Arborist/Gozdar - `npcs/arborist/`
+11. โ
Glavni Smetar - `npcs/glavni_smetar/`
+12. โ
**Electrician** โก - `npcs/electrician/` - **11 SPRITES โ
Jan 10, 2026**
+13. โ
**Zombie Statistician** ๐ - `npcs/zombie_statistician/` - **11 SPRITES โ
Jan 10, 2026**
+
+**Status:** ALL verified NPCs generated + 2 with FULL sprite sets! โ
+
+---
+
+### ๐ **INCREDIBLE PROGRESS!**
+
+**246 PNG FILES = 246 MASTER REFERENCES!**
+
+This is **1.8% of total 13,500 target** BUT represents:
+- โ
**100% of ALL 166 Biome NPCs!** (MASSIVE!)
+- โ
**11/11 Core NPCs** (100%)
+- โ
**22/109 Creatures** (20%)
+- โ
**Trees, buildings, species** started!
+
+**Estimated Value:** 246 images ร $0.012 = **~$2.95 generated!**
+**Google Quota Used:** 246 of daily 1000 limit
+
+---
+
+### ๐๏ธ **GOTHIC STYLE ENHANCEMENTS** (Optional Layer)
+
+**When to Use Gothic Elements:**
+
+1. **โ
Witch Forest Biome:**
+ - Baba Yaga's hut (walking on chicken legs!)
+ - Dark altars, ritual circles
+ - Cursed gems, poison mushrooms
+ - Black cats, ravens (familiars)
+
+2. **โ
Catacombs Biome:**
+ - Skulls, bones, tombs
+ - Gothic arches, crumbling stone
+ - Candlelight, lanterns
+ - Mummies, ancient undead
+
+3. **โ
Chernobyl (Final Zone):**
+ - Soviet gothic architecture
+ - Abandoned reactor (industrial gothic)
+ - Hazmat suits (modern horror)
+ - Radiation glow (green eerie light)
+
+4. **โ
Bosses:**
+ - Giant Troll King (gothic armor, dark crown)
+ - Baba Yaga (flying mortar, witch aesthetic)
+ - Dr. Krniฤ (mad scientist, gothic lab)
+
+**Gothic Textures to Add:**
+- โ
Stone carvings (gargoyles, faces)
+- โ
Wrought iron (fences, gates)
+- โ
Stained glass (broken, colorful)
+- โ
Candlelight (flickering, warm)
+- โ
Cobwebs (corners, abandoned areas)
+
+---
+
+### ๐ฎ **STYLE CONSISTENCY RULES**
+
+**MANDATORY for ALL assets:**
+
+1. โ
**Thick black outlines** (3-4px)
+2. โ
**Chibi proportions** (1:1.5 to 1:2)
+3. โ
**Red eyes with pupils** (friendly) OR **White/Red eyes no pupils** (enemies)
+4. โ
**Cell-shaded coloring** (flat with hard shadows)
+5. โ
**Dark-Chibi Noir palette** (muted, gothic colors)
+6. โ
**Post-apocalyptic details** (torn clothes, wear, dirt)
+7. โ
**Gothic accents** (when appropriate for biome/character)
+8. โ
**512x512px PNG** with alpha transparency
+9. โ
**Front-facing** for master references
+10. โ
**Centered** with 10px margin
+
+**โ FORBIDDEN:**
+- โ Realistic art style (NO photorealism!)
+- โ Soft gradients (keep sharp shadows!)
+- โ Thin outlines (must be THICK!)
+- โ Bright pastel colors (keep it DARK!)
+- โ Perfect/clean characters (must show wear!)
+
+---
+
+### ๐ **GENERATION PROMPT TEMPLATE**
+
+**For NPCs:**
+```
+[Character Name], [role], Style 32 Dark-Chibi Noir,
+chibi proportions, thick black outlines, red eyes with dark pupils,
+post-apocalyptic clothing (torn, weathered),
+[specific details: piercings, hair, outfit],
+gothic accents, dark atmosphere,
+512x512px PNG, front-facing, alpha transparency
+```
+
+**For Enemies:**
+```
+[Enemy Name], Style 32 Dark-Chibi Noir,
+chibi proportions, thick black outlines, white eyes NO pupils,
+hostile pose, [specific features],
+dark colors, gothic horror elements,
+512x512px PNG, front-facing, alpha transparency
+```
+
+---
+
+**Status:** โ
Style guide LOCKED and MANDATORY for all future assets!
+
+---
+
+## โ๏ธ WORKSTATIONS (~100 assets)
+
+Anvil, Forge, Furnace, Kiln, Loom, Spinning Wheel, Workbench, Crafting Table, Alchemy Table, Enchanting Table, Cooking Pot, Oven, Wine Press, Oil Press, Flour Mill, Sawmill, Tanning Rack, Drying Rack, Fermenter, Distiller, Beehive, Butter Churn, Cheese Press, Seed Maker, Recycler
+
+---
+
+## ๐ PRODUCTION SUMMARY
+**Last Updated:** 06.01.2026 22:50 CET
+
+### ๐ OVERALL PROGRESS (Updated Jan 8, 04:00 CET)
+
+| Category | Total | Complete | In Progress | Not Started | % Done |
+|----------|-------|----------|-------------|-------------|--------|
+| **Master References** | 13,500 | ~246 | 0 | ~13,254 | 1.8% |
+| **NPCs** | 388 | 181 | 0 | 207 | 46.6% |
+| **Creatures** | 109 | 99 | 0 | 10 | 90.8% |
+| **Bosses** | 24 | 0 | 0 | 24 | 0% |
+| **Biomes (Design)** | 20 | 20 | 0 | 0 | 100% โ
|
+| **Biomes (Assets)** | 20 | 0 | 0 | 20 | 0% |
+| **Character Animations** | 52 | 52 | 0 | 0 | 100% โ
|
+| **Demo Assets** | 292 | 157 | 0 | 135 | 54% |
+| **Sprites (All Phases)** | ~50,000 | ~300 | 0 | ~49,700 | 0.6% |
+
+**๐ CHARACTER ANIMATIONS COMPLETE! (Jan 8, 2026)**
+- Kai: 20/20 frames (idle, walk, dig, swing)
+- Ana: 10/10 frames (idle, walk)
+- Gronk: 10/10 frames (idle, walk)
+- Susi: 12/12 frames (idle, run, bark)
+
+**Note:** Master References include duplicates (_nobg versions). Will be cleaned tomorrow to ~120-130 unique files.
+
+---
+
+### โ๏ธ GAME SYSTEMS & MECHANICS
+
+**Total Systems:** **174** (Massive!)
+**Latest Update:** Jan 10, 2026 - Week 1 Systems Complete
+**Location:** `/src/systems/`
+
+---
+
+#### ๐ฏ **WEEK 1 SYSTEMS (NEW - Jan 10, 2026)**
+
+| System | Status | Lines | Phase | Description |
+|--------|--------|-------|-------|-------------|
+| **ResourceLogisticsSystem** | โ
DONE | 351 | ๐ฅ Phase 1 | Auto-pickup, storage, depots, UI, VFX |
+| **CityManagementSystem** | โ
DONE | 552 | ๐ฅ Phase 1 | Population tracking, Zombie Statistician NPC |
+| **BuildingUpgradeSystem** | โ
DONE | 705 | ๐ฅ Phase 1 | Generator, Electrician NPC, power grid |
+
+**TOTAL:** 1,608 lines coded (Jan 10, 2026)
+
+---
+
+#### ๏ฟฝ **FARMING SYSTEMS (15)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| FarmingSystem.js | โ
| Core farming mechanics |
+| MicroFarmSystem.js | โ
| 8x8 starter farm |
+| MicroFarmExpansionSystem.js | โ
| Farm growth mechanics |
+| FarmAutomationSystem.js | โ
| Automated farming |
+| CropGrowthSeasonSystem.js | โ
| Seasonal crop cycles |
+| PerennialCropSystem.js | โ
| Long-term crops |
+| AnimalsSeedsSystem.js | โ
| Animal/seed management |
+| BugCatchingSystem.js | โ
| Insect collection |
+| FishingSystem.js | โ
| Fishing mechanics |
+| CookingSystem.js | โ
| Food preparation |
+| MesojedkeSystem.js | โ
| Carnivorous plants |
+| SlimesDogsSystem.js | โ
| Pet slimes/dogs |
+| AnimalBreedingSystem.js | โ
| Animal reproduction |
+| BreedingSystem.js | โ
| Breeding mechanics |
+| BreedingUISystem.js | โ
| Breeding interface |
+
+---
+
+#### ๐ฅ **NPC & DIALOGUE SYSTEMS (12)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| NPCPopulationSystem.js | โ
| NPC spawning/management |
+| NPCSpawner.js | โ
| Dynamic NPC spawning |
+| NPCShopSystem.js | โ
| NPC merchant system |
+| NPCPrivacySystem.js | โ
| NPC privacy mechanics |
+| NPCSettlementSystem.js | โ
| NPC town building |
+| DialogueSystem.js | โ
| Conversation system |
+| GrokCharacterSystem.js | โ
| Gronk companion AI |
+| NomadRaiderAI.js | โ
| Enemy NPC behavior |
+| ZombieCommunicationSystem.js | โ
| Talk to zombies! |
+| ZombieWorkerSystem.js | โ
| Zombie labor force |
+| SmartZombieSystem.js | โ
| Intelligent zombies |
+| ZombieSystem.js | โ
| Core zombie mechanics |
+
+---
+
+#### โ๏ธ **COMBAT & DEFENSE SYSTEMS (10)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| DefenseSystem.js | โ
| Base defense |
+| BossBattlesSystem.js | โ
| Boss fights |
+| BossArenaSystem.js | โ
| Arena mechanics |
+| GiantTrollKingBoss.js | โ
| Final boss AI |
+| HordeWaveSystem.js | โ
| Wave defense |
+| FarmRaidSystem.js | โ
| Farm attacks |
+| HybridAbilitySystem.js | โ
| Special abilities |
+| HybridSkillSystem.js | โ
| Skill tree |
+| SkillTreeSystem.js | โ
| Progression tree |
+| MagicSystem.js | โ
| Magic mechanics |
+
+---
+
+#### ๐บ๏ธ **WORLD & BIOME SYSTEMS (15)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| BiomeSystem.js | โ
| 20 biome generator |
+| ChunkManager.js | โ
| Chunk loading (500x500 world) |
+| TerrainSystem.js | โ
| Terrain generation |
+| Flat2DTerrainSystem.js | โ
| 2D flat terrain |
+| TransitionSystem.js | โ
| Biome transitions |
+| RiverSystem.js | โ
| River generation |
+| LakeSystem.js | โ
| Lake generation |
+| OceanSystem.js | โ
| Ocean mechanics |
+| StructureSystem.js | โ
| World structures |
+| StructureInteractionSystem.js | โ
| Structure use |
+| PyramidSystem.js | โ
| Pyramid dungeons |
+| PortalNetworkSystem.js | โ
| Fast travel |
+| PortalRepairSystem.js | โ
| Portal fixing |
+| MiningDungeonsSystem.js | โ
| Procedural mines |
+| FogOfWarSystem.js | โ
| Map exploration |
+
+---
+
+#### ๐๏ธ **TOWN & BUILDING SYSTEMS (18)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| BuildingSystem.js | โ
| Building placement |
+| BuildSystem.js | โ
| Construction mechanics |
+| BuildingRestorationSystem.js | โ
| Ruin restoration |
+| BuildingVisualsSystem.js | โ
| Building graphics |
+| TownRestorationSystem.js | โ
| Town rebuilding |
+| TownRestorationLogic.js | โ
| Restoration logic |
+| TownGrowthSystem.js | โ
| Town expansion |
+| CityGratitudeSystem.js | โ
| Citizen happiness |
+| ElectionSystem.js | โ
| Politics/voting |
+| ChurchSystem.js | โ
| Church mechanics |
+| BarberShopSystem.js | โ
| Haircuts! |
+| BakeryShopSystem.js | โ
| Bakery shop |
+| LawyerOfficeSystem.js | โ
| Legal system |
+| MuseumEvolutionSystem.js | โ
| Museum growth |
+| SchoolBuffSystem.js | โ
| Education bonuses |
+| WorkstationSystem.js | โ
| Crafting stations |
+| CraftingTablesSystem.js | โ
| Crafting UI |
+| ExpansionSystem.js | โ
| Territory expansion |
+
+---
+
+#### ๐ฐ **ECONOMY & TRADING SYSTEMS (10)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| DrugEconomySystem.js | โ
| Drug trade mechanics |
+| ZombieEconomySystem.js | โ
| Zombie currency |
+| MintingSystem.js | โ
| NFT/crypto mechanics |
+| GemDropSystem.js | โ
| Gem collection |
+| CourierSystem.js | โ
| Package delivery |
+| ShopSystem (NPCShopSystem) | โ
| NPC shops |
+| LootSystem.js | โ
| Loot drops |
+| CollectionSystem.js | โ
| Item collecting |
+| AlbumCollectionSystem.js | โ
| Album/cards |
+| RecipeSystem.js | โ
| Recipe discovery |
+
+---
+
+#### ๐ฆ **INVENTORY & CRAFTING SYSTEMS (8)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| InventorySystem.js | โ
| Player inventory |
+| InventorySystemExpanded.js | โ
| Extended inventory |
+| FullInventoryUI.js | โ
| Full UI (24 slots) |
+| CraftingSystem.js | โ
| Item crafting |
+| CraftingTiersSystem.js | โ
| Craft progression |
+| ToolSystem.js | โ
| Tool management |
+| MagicEnchantingSystem.js | โ
| Item enchanting |
+| StarterChestSystem.js | โ
| Starting items |
+
+---
+
+#### ๐ฏ **QUEST & STORY SYSTEMS (10)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| QuestSystem.js | โ
| Quest mechanics |
+| QuestSystemExpanded.js | โ
| Extended quests |
+| StoryQuestSystem.js | โ
| Main story |
+| MainQuestAnaSystem.js | โ
| Ana rescue quest |
+| AnaClueSystem.js | โ
| Ana's 50 items |
+| LandmarkQuestSystem.js | โ
| Exploration quests |
+| TwinBondSystem.js | โ
| Twin telepathy |
+| TwinBondUISystem.js | โ
| Bond UI |
+| ProgressionSystem.js | โ
| Player progression |
+| LegacySystem.js | โ
| Legacy mechanics |
+
+---
+
+#### ๐ฆ๏ธ **WEATHER &ENVIRONMENT SYSTEMS (8)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| WeatherSystem.js | โ
| Weather effects |
+| MasterWeatherSystem.js | โ
| Advanced weather |
+| WeatherEnhancementsSystem.js | โ
| Enhanced effects |
+| BiomeMusicSystem.js | โ
| Biome soundtracks |
+| DynamicEnvironmentAudio.js | โ
| Ambient sounds |
+| WindFoliageSystem.js | โ
| Wind + foliage |
+| WaterPhysicsSystem.js | โ
| Water mechanics |
+| WaterRipplesSystem.js | โ
| Water ripples |
+
+---
+
+#### ๐จ **VISUAL & PARTICLE SYSTEMS (10)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| LightingSystem.js | โ
| Day/night lighting |
+| ParticleEffects.js | โ
| Particle system |
+| ParticleEnhancementsSystem.js | โ
| Enhanced particles |
+| VFXSystem.js | โ
| Visual effects |
+| VisualEffectsSystem.js | โ
| Effect library |
+| VisualEnhancementSystem.js | โ
| Graphics polish |
+| UIPolishSystem.js | โ
| UI effects |
+| UIGraphicsSystem.js | โ
| UI rendering |
+| ParallaxSystem.js | โ
| Parallax backgrounds |
+| CameraSystem.js | โ
| Camera controls |
+
+---
+
+#### ๐ **AUDIO SYSTEMS (7)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| SoundManager.js | โ
| Audio manager |
+| AudioLoader.js | โ
| Audio file loading |
+| AudioTriggerSystem.js | โ
| Event sounds |
+| VoiceoverSystem.js | โ
| Voice acting |
+| CinematicVoiceSystem.js | โ
| Cutscene voices |
+| VisualSoundCueSystem.js | โ
| Deaf accessibility |
+| LocalizationSystem.js | โ
| Language support |
+
+---
+
+#### โฟ **ACCESSIBILITY SYSTEMS (7)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| AccessibilitySystem.js | โ
| Master accessibility |
+| ADHDAutismSupportSystem.js | โ
| ADHD/Autism support |
+| DyslexiaSupportSystem.js | โ
| Dyslexia tools |
+| MotorAccessibilitySystem.js | โ
| Motor support |
+| ScreenReaderSystem.js | โ
| Screen reader |
+| InputRemappingSystem.js | โ
| Custom controls |
+| VisualSoundCueSystem.js | โ
| Visual audio cues |
+
+---
+
+#### ๐จโ๐ฉโ๐ง **SOCIAL & FAMILY SYSTEMS (6)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| MarriageRomanceSystem.js | โ
| Marriage system |
+| ChildrenFamilySystem.js | โ
| Children mechanics |
+| GenerationalGameplaySystem.js | โ
| Multi-generation |
+| CharacterCustomizationSystem.js | โ
| Character editor |
+| MultiplayerSystem.js | โ
| Multiplayer |
+| MultiplayerSocialSystem.js | โ
| Social features |
+
+---
+
+#### ๐ **TRANSPORT & MOUNT SYSTEMS (4)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| TransportSystem.js | โ
| Transportation |
+| VehicleSystem.js | โ
| Vehicles |
+| MountSystem.js | โ
| Animal mounts |
+| ScooterRepairSystem.js | โ
| Scooter fixing |
+
+---
+
+#### โ๏ธ **MINING & AUTOMATION SYSTEMS (5)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| MiningSystem.js | โ
| Mining mechanics |
+| MiningDungeonsSystem.js | โ
| Procedural mines |
+| ZombieMinerAutomationSystem.js | โ
| Zombie miners |
+| AutomationSystem.js | โ
| General automation |
+| AutomationTierSystem.js | โ
| Automation tiers |
+| WorkerCreaturesSystem.js | โ
| Worker AI |
+
+---
+
+#### ๐พ **SAVE & MANAGEMENT SYSTEMS (6)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| SaveSystem.js | โ
| Save/load |
+| SaveManager.js | โ
| Save management |
+| SaveSystemExpansion.js | โ
| Extended saves |
+| PlaytimeTrackerSystem.js | โ
| Playtime stats |
+| LeaderboardSystem.js | โ
| Online leaderboards |
+| SteamIntegrationSystem.js | โ
| Steam achievements |
+
+---
+
+#### ๐ฎ **UI & INPUT SYSTEMS (8)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| TutorialSystem.js | โ
| Tutorial popups |
+| CentralPopupSystem.js | โ
| Notification system |
+| StatsSystem.js | โ
| Player stats |
+| UnifiedStatsPanel.js | โ
| Debug stats |
+| InteractionSystem.js | โ
| Interaction prompts |
+| TimeSystem.js | โ
| In-game clock |
+| SleepSystem.js | โ
| Sleep mechanics |
+| StaminaSystem.js | โ
| Energy system |
+
+---
+
+#### ๐ฏ **SPECIAL MECHANICS (12)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| MapRevealSystem.js | โ
| Map discovery |
+| PathfindingSystem.js | โ
| AI pathfinding |
+| WorldEventSystem.js | โ
| Random events |
+| AchievementTriggers.js | โ
| Achievements |
+| GraveSystem.js | โ
| Death/graves |
+| BlueprintSystem.js | โ
| Building blueprints |
+| ZombieScoutLevelingSystem.js | โ
| Scout progression |
+| ZombieScoutSkills.js | โ
| Scout abilities |
+| BiomeEnemySystem.js | โ
| Biome enemies |
+| MasterGameSystemsManager.js | โ
| System orchestration |
+| TechnicalPerformanceSystem.js | โ
| Performance monitoring |
+| PlatformSupportSystem.js | โ
| Cross-platform |
+
+---
+
+#### ๐ฌ **ANTIGRAVITY ENGINE (1)**
+
+| System | Status | Description |
+|--------|--------|-------------|
+| Antigravity.js | โ
| Core game engine integration |
+
+---
+
+### ๐ **SYSTEMS SUMMARY**
+
+**Total Systems:** **178** (+4 NEW Jan 10!)
+**Status Distribution:**
+- โ
Implemented: 178 (100%)
+- Week 1 NEW: 3 systems (1,608 lines)
+- Audio & Accessibility NEW: 4 systems (1,073 lines)
+- Total Code: ~260,000+ lines estimated
+
+**Category Breakdown:**
+- ๐พ Farming: 15
+- ๐ฅ NPCs: 12
+- โ๏ธ Combat: 10
+- ๐บ๏ธ World: 15
+- ๐๏ธ Town: 18
+- ๐ฐ Economy: 10
+- ๐ฆ Inventory: 8
+- ๐ฏ Quest: 10
+- ๐ฆ๏ธ Weather: 8
+- ๐จ Visual: 10
+- ๐ Audio: **11** (+4 NEW!)
+- โฟ Accessibility: 7
+- ๐จโ๐ฉโ๐ง Social: 6
+- ๐ Transport: 4
+- โ๏ธ Mining: 5
+- ๐พ Save: 6
+- ๐ฎ UI/Input: 8
+- ๐ฏ Special: 12
+- ๐ฌ Engine: 1
+- ๐ฌ **Scenes: 1** (NEW!)
+
+**NEW SYSTEMS (Jan 10, 2026):**
+1. **SplashScene.js** - Hipodevil666 Studiosโข branding (146 lines)
+2. **EnhancedAudioSystem.js** - Complete audio integration (354 lines)
+3. **DynamicTypewriterSystem.js** - NO voice recording needed! (302 lines)
+4. **audio_optimizer.py** - .wav โ .ogg converter (149 lines)
+
+**Accessibility Grade:** **AAA+**
+- Visual indicators for deaf players
+- Xbox haptic feedback
+- 4 typing speeds (ADHD instant mode!)
+- Skip everything controls
+- Multi-sensory design (Audio + Visual + Haptic)
+
+**This is a MASSIVE game!** ๐
+
+---
+
+## ๐ญ ADVANCED SYSTEMS (DESIGNED - JAN 10, 2026)
+
+### **Future Features Roadmap**
+
+Four major advanced game systems have been fully designed for future implementation. These systems add emotional depth, replayability, and premium value while maintaining the noir aesthetic.
+
+---
+
+### **1. ๐ญ GENERATIONAL AGING SYSTEM**
+
+**Concept:** Kai and Ana age visually throughout the game (30+ year search for Ana)
+
+#### **Three Life Stages:**
+
+**Young Kai (Age 25-29):**
+- Full dreadlocks (dark with pink/green streaks)
+- All piercings (2 ears, eyebrow)
+- Baggy punk style, band shirts
+- Lean, athletic build
+- Movement: 120% speed
+- Health: 100 HP
+- **Sprite Requirements:** Full set (~40 sprites)
+
+**Adult Kai (Age 30-39):**
+- Dreadlocks maintained (slightly matted)
+- **ALL piercings remain** (HIPO: identity never changes!)
+- More practical clothing (cargo pants, work jacket)
+- Muscular from survival
+- Optional family (wife + child NPCs)
+- Movement: 100% speed
+- Health: 150 HP
+- Wisdom: Standard
+- **Quest Note:** Ana search continues even with family!
+- **Sprite Requirements:** Full set + family (~50 sprites)
+
+**Elder Kai (Age 40+):**
+- **Grey/silver dreadlocks** (dignified, maintained)
+- **ALL piercings STILL present!**
+- Full grey beard
+- Weathered but strong
+- Baggy pants always (punk grandpa!)
+- Possible grandchildren NPCs (16-20)
+- Movement: 80% speed (wiser, not weaker)
+- Health: 200 HP (resilient veteran)
+- Wisdom: +50% (better crafting/negotiation)
+- **Special Quest:** "Grandpa's Promise" - grandchildren join Ana search
+- **Sprite Requirements:** Full set + grandchildren (~60 sprites)
+
+**Ana Aging:**
+- When found, Ana ages to match Kai's current stage
+- If Kai is Elder, Ana is also Elder (twin synchronization)
+- Lab coat remains (scientist identity)
+- Emotional reunion regardless of age
+
+**Total Sprite Requirements:** ~250 new sprites (3 Kai sets, 3 Ana sets)
+
+---
+
+### **2. ๐ฌ INTRO VIDEO & FLASHBACK EXPANSION**
+
+**Concept:** Emotional intro sequence with blur-to-clear effects on happy memories
+
+#### **Intro Sequence (3 Scenes):**
+
+**Scene 1: Happy Memories (Blurred โ Clear)**
+1. Longboard with father (ocean pier, sunny day)
+ - Blur clears when father says "Pojdi, sin!" (Go, son!)
+2. Dreadlocks at barbershop (pink streak being added)
+ - Blur clears on mirror reflection smile
+3. Twin moment with Ana (university lab)
+ - **CLEAREST shot** (most important memory)
+ - Ana in lab coat, both laughing
+
+**Scene 2: The Apocalypse (Sharp, No Blur - Harsh Reality)**
+1. Father zombie bite (silhouette)
+2. Virus spread (news footage style)
+3. Ana captured (military van)
+ - Last view: Ana crying in window
+
+**Scene 3: The Search (Time-Lapse Aging)**
+- Year 1: Young Kai (hopeful)
+- Year 5: Kai fighting (determined)
+- Year 10: Adult Kai with child (still searching)
+- Year 15: Elder Kai grey dreads (never giving up)
+- Final frame: "30 years later..."
+
+#### **In-Game Flashback System:**
+
+**Type 1: Memory Fragment** (2-3 seconds)
+- Quick blur effect
+- Voice line from past
+- Screen shimmer
+- Returns to gameplay
+
+**Type 2: Full Flashback** (Playable cutscene)
+- Playable sequence as young Kai
+- Clear visuals (no blur)
+- Learn new info about Ana
+- Optional objectives
+
+**Type 3: Dream Sequence** (Sleep system)
+- Triggers when Kai sleeps
+- Blur-to-clear reveals clues
+- Symbolic noir imagery
+- Wakes with new quest marker
+
+**Triggers:** Finding Ana's items, visiting locations, Kai's birthday
+
+---
+
+### **3. ๐ป GHOST QUEST (PARENTS)**
+
+**Concept:** After Ana found, parents' ghosts appear in Noir-Neon style to guide Kai to closure
+
+#### **Activation Conditions:**
+1. โ
Ana must be rescued (main quest complete)
+2. โ
Cure distributed (world saved)
+3. โ
Returns to home town ruins
+4. โ
Night time (23:00-04:00)
+
+#### **Ghost Design (Noir-Neon Aesthetic):**
+
+**Father Ghost:**
+- 50% translucent
+- **Neon cyan outline** (glowing edges)
+- Noir shadow beneath
+- Ghostly longboard visible
+- Hover animation (no walking)
+- Gentle glow pulse (1.5s cycle)
+
+**Mother Ghost:**
+- 50% translucent
+- **Neon magenta outline** (warm love)
+- Holds ghostly flowers
+- Warm smile despite death
+- Reaches toward Kai (can't touch)
+- Gentle glow pulse
+
+#### **Quest Phases:**
+
+**Phase 1: First Encounter** (Home Ruins)
+- Blur effect starts (dreamlike)
+- Ghosts appear with cyan/magenta glow
+- Father explains they've been watching
+- Quest Marker: "Follow the Spirits"
+
+**Phase 2: Journey to Death Site**
+- Ghosts lead across map (slow movement)
+- Environmental storytelling:
+ - Blood stains (old)
+ - Broken barricades
+ - Father's broken longboard
+ - Mother's wilted flowers
+- Ambient: Somber piano
+
+**Phase 3: The Pier** (Death Location)
+- Same pier from intro memory!
+- Flashback shows zombie attack
+- Parents' last stand protecting Kai & Ana
+- Ghosts become 100% visible for this scene
+
+**Phase 4: Closure** (Unmarked Grave)
+- Kai places flowers, longboard, items
+- Dialog choice (Thank/Wish/Proud)
+- Ghosts fade to white light
+- Sunrise over ocean
+- Peace achieved
+
+#### **Rewards:**
+- **Ghostly Longboard**: Cyan glow mount (fast travel)
+- **Mother's Blessing**: +20% health regen (permanent)
+- **Family Photo**: Unlocks secret ending variant
+- **Achievement**: "Closure" (15G)
+
+**Sprite Requirements:** 16 ghost sprites (father 8, mother 8)
+**Audio:** 10 AI voice phrases (ethereal filter)
+
+---
+
+### **4. ๐ SPECIAL EDITION GRONK HELPER**
+
+**Concept:** Premium companion for early adopters (Buyer #1-20) and streamers
+
+#### **License Activation:**
+
+**Early Adopter Check:**
+```javascript
+if (buyerNumber >= 1 && buyerNumber <= 20) {
+ activateGronk('early_adopter', buyerNumber);
+ // Golden crown appears on Gronk
+}
+```
+
+**Streamer Mode Detection:**
+```javascript
+if (detectStreamerMode()) {
+ // Detects: OBS, Streamlabs, Twitch Studio
+ activateGronk('streamer');
+ // "๐ด LIVE" indicator appears
+}
+```
+
+#### **Gronk Companion Features:**
+
+**Abilities:**
+1. **Auto-Gather**: Resources within 10-tile radius
+2. **Combat Assist**: Smash attack (AOE damage)
+3. **Vape Boost**: +50% speed for 30s (5min cooldown)
+4. **Emotional Support**: +10% XP when nearby
+
+**Visual:**
+- Chibi Gronk (Style 32 Dark-Chibi Noir)
+- Pink vape always visible
+- Follows Kai (2 tiles behind)
+- Can't die (respawns if "defeated")
+
+**Random Dialog:**
+- "Pink is best color, Kai agree?"
+- "Gronk help find Ana! No give up!"
+- "System no change Gronk OR Kai!"
+
+#### **Special Edition Exclusives:**
+
+**Early Adopter Perks (First 20 Buyers):**
+- **Golden crown** on Gronk (tiny, visible)
+- "Founder" title in-game
+- Exclusive dialog: "Kai one of first! Gronk proud!"
+- Founder-only Discord role
+
+**Streamer Mode Bonuses:**
+- "๐ด LIVE" indicator on Gronk
+- Twitch chat integration (Gronk reads chat!)
+- Subscriber alerts = Gronk dialog
+- Donation triggers special emotes
+
+#### **Customization (Unlockable):**
+
+**Vape Flavors:**
+- Bubble Gum (speed boost) - default
+- Cotton Candy (health regen)
+- Watermelon (stamina boost)
+- Blue Raspberry (night vision)
+
+**Outfits:**
+- Punk Gronk (studded jacket)
+- Formal Gronk (tiny tux)
+- Beach Gronk (hawaiian shirt)
+- Winter Gronk (beanie, scarf)
+
+**Piercings:**
+- Add piercings to Gronk (like Kai!)
+- Earrings, nose ring, eyebrow pierce
+- Found via "Piercing Kits" in-game
+
+**Sprite Requirements:** 20+ Gronk companion sprites
+
+---
+
+### ๐ **ADVANCED SYSTEMS SUMMARY**
+
+**Total Design:** 4 major systems
+**Sprites Required:** ~290 new
+**Code Estimate:** ~2,000 lines
+**Audio Required:** 10 ghost voices + SFX
+**API Required:** License server
+
+**Status:** โ
Full design complete
+**Document:** `ADVANCED_SYSTEMS_DESIGN.md` (1,100+ lines)
+**Next Phase:** Reference image creation โ Sprite generation
+
+---
+
+### ๐ธ **REFERENCE IMAGES REQUIRED**
+
+#### **Priority for Next Session:**
+
+1. **Odrasli Kai (Adult - Family Man)**
+ - Age 30-39
+ - Beard, more tattoos
+ - ALL piercings still present!
+ - Still on longboard
+ - More serious expression
+
+2. **Stari Kai (Elder - Punk Grandpa)**
+ - Grey/silver dreadlocks
+ - ALL piercings still present!
+ - Baggy pants always (punk identity)
+ - Full grey beard
+ - Wise, dignified, still fierce
+
+3. **Otac (Father - Alive Version)**
+ - 40s, warm smile
+ - Longboard in hand
+ - Ocean pier background
+ - Pre-ghost reference
+ - (Agent adds neon-cyan effect later)
+
+4. **Mati (Mother - Alive Version)**
+ - 40s, gentle smile
+ - Holding flowers
+ - Caring expression
+ - Pre-ghost reference
+ - (Agent adds neon-magenta effect later)
+
+**All in Style 32 Dark-Chibi Noir!**
+
+---
+
+### ๐ ๏ธ TECHNOLOGIES & TOOLS
+
+#### โ
**CONFIRMED TECHNOLOGIES**
+
+| Technology | Purpose | Status |
+|------------|---------|--------|
+| **Phaser 3** | Game Engine | โ
Implemented |
+| **JavaScript** | Core Language | โ
In Use |
+| **Tiled** | Map Editor | โ
In Use |
+| **Google Imagen 3** | Asset Generation | โ
Active (Quota: 1000/day) |
+| **ComfyUI** | Image Processing | โ
Ready (Local) |
+| **Edge TTS** | Voice Generation | โ
Ready |
+| **Python** | Automation Scripts | โ
In Use |
+| **Flask** | Asset Manager Backend | โ
Implemented |
+| **Electron** | Desktop App | โ
Implemented |
+
+#### ๐จ **ART & AUDIO PIPELINE**
+
+| Tool/Process | Purpose | Status |
+|--------------|---------|--------|
+| **Style 32 (Dark-Chibi Noir)** | Master Art Style | โ
Locked |
+| **Master Reference Pipeline** | Character consistency | โ
Active |
+| **Dialogue Portrait Automation** | Auto-crop portraits | โ
Done (Python) |
+| **Background Removal** | Transparency processing | โ
Ready |
+| **Asset Manager App** | Asset organization | โ
v1.0 Complete |
+| **Placeholder Audio** | Sound FX placeholder | โ
Python script ready |
+| **Localized TTS** | Multi-language voice | โ
Edge TTS ready |
+
+---
+
+### ๐ฏ PHASE BREAKDOWN (EPISODIC MODEL)
+
+**Structure:** 10 Episodes (Fazas) released over time.
+**Pricing:** Demo (Free) -> Early Access (Ep 1-2) -> Full Game (Ep 10).
+
+---
+
+#### **๐ฎ DEMO (Free Trial)**
+**Goal:** Hook the player, show mechanics, limit progress.
+
+**๐ Map & Scope:**
+- **Restricted:** Home Farm Only (8x8 Micro Plot).
+- **Megla:** Invisible walls/fog prevent leaving the farm.
+
+**๐จ Restrictions (The "Tantalizer" Nerf):**
+- **Housing:** Sleeping Bag -> Tent -> **Small Shack (MAX)**. Cannot build Mansion.
+- **Tools:** **Wood Tier Only** (break fast).
+- **Yields:** 1 Seed = 1 Crop + 1 Seed (No massive profit).
+- **Trees:** 1 Tree = 1 Log + 1 Sapling (Slow resource gain).
+- **Sleep:** No automatic bed. Must craft Tent/Shack to save/sleep.
+
+**๐ Starter Kit (Free):**
+- 1x Wooden Axe, Pickaxe, Hoe, Watering Can.
+- 1x Torch, 1x Bread.
+- 2x Wheat Seeds, 2x Carrot Seeds.
+- **3x Cannabis Seeds** (Taste of the economy!).
+
+**๐ง Characters & Story:**
+- **Zombi Statistiฤar:** Stands at gate. Holds sign "Population: 0". Explains you need full game to build town.
+- **Quest:** Intro (Amnesia), Control First Zombie ("DIG" command), Find Ana's Bracelet.
+
+---
+
+#### **๐ฅ EPISODE 1: FAZA 1 (Early Access Start)**
+**Goal:** Establish Base, First Biomes, Survival.
+
+**๐ Map & Scope:**
+- **Unlocked Biomes (4):** Grassland (Base), Dark Forest, Desert, Swamp.
+- **Bugs/Insects:** Catching enabled immediately (nets).
+
+**๐ Paid Starter Kit (Premium):**
+- **Reinforced Tools** (Iron/Steel).
+- **Automatic Sleeping Bag** (Don't need to craft first bed).
+- 10x Wheat, 10x Carrot, 5x Potato Seeds.
+- **10x Cannabis Seeds**.
+- More Food (5 Bread).
+
+**๐ First 40 Players Bonus (Early Adopter):**
+- **INSTANT GRONK:** Gronk appears Day 1 (no quest needed).
+- **Roles:**
+ - **Builder:** Helps build structures 2x faster.
+ - **Carrier:** Huge inventory, carries items to sell.
+
+**๐๏ธ Systems Unlocked:**
+- **Basement Lvl 1:** Zombie Containment Cell (Capture zombies).
+- **Basement Lvl 2:** **Grow Room** (Indoor Cannabis/Mushroom farm).
+- **Night Delivery:**
+ - ๐ฆ **Owls:** Deliver Packages/Rewards.
+ - ๐ฆ **Bats:** Deliver Quests/Letters.
+- **Combat:** Bows & Arrows key.
+
+---
+
+#### **โก EPISODE 2: FAZA 2 (Town Restoration)**
+**Goal:** Rebuild Civilization, Town Management, Crime.
+
+**๐ Map & Scope:**
+- **Town Unlocked:** The main Hub town becomes accessible.
+- **Surrounding Biomes:** Gradual expansion.
+
+**๐๏ธ BUILD-TO-SPAWN Mechanic:**
+*NPCs do not appear randomly. You must build their workplace to summon them.*
+1. **Build Big Generator** (Needs Fuel!) -> Spawns **Elektriฤar**.
+2. **Build Police Station** -> Spawns **Sheriff** (No Gun, Baton/Jail only).
+3. **Build Bakery** -> Spawns **Pek** (Baker).
+4. **Build School** -> Spawns **Teacher**.
+5. **Build Town Hall** -> Spawns **ลฝupan** (Mayor) -> Enables **ORDER** (Before this: Chaos).
+
+**๐ก Advanced Systems:**
+- **Generator Fuel:** Must keep adding Bio-Fuel/Gas to Big Generator or lights go out (Zombies attack!).
+- **Immigration Board:**
+ - Managed by Zombi Statistiฤar.
+ - Donate Resources -> Build Racial Housing -> **Elves/Gnomes/Axolotls move in!**
+ - Real-time Population Counter.
+- **City Walls & Watchtowers:** Passive defense.
+
+**๐ Crime & Police System:**
+- **Zombie Drug Dealer:**
+ - Produce Drugs in Basement Lvl 3 (Lab).
+ - Send **Zombie Runner** to town to sell (Stealth).
+- **Police Risks:**
+ - Sheriff **RAIDS** your basement if suspicion is high.
+ - Sheriff **ARRESTS** your Zombie Dealer (You lose the zombie + stash).
+ - *Note:* Police do NOT have guns. They use batons/tasers.
+
+**๐๏ธ Politics:**
+- **Town Hall:** Unlocks laws and taxes.
+- **Elections:** Mayor is usually set (The "Mayor" NPC), but ensures stability.
+
+---
+
+---
+
+### ๐ CRITICAL PATH TO DEMO
+
+**IMMEDIATE BLOCKERS (๐จ URGENT!):**
+
+1. **Grassland Biome Assets** ๐ด
+ - Ground tiles (grass, dirt, stone path)
+ - Trees (Oak, Pine) - 4 seasons each
+ - Props (rocks, bushes, flowers)
+ - Farm plot tiles
+ - **Estimated:** 150 assets
+
+2. **Character Sprite Sheets** ๐ด
+ - Kai (idle, walk, dig, swing) - 20 sprites minimum
+ - Ana (idle, walk) - 10 sprites minimum
+ - Gronk (idle, walk) - 10 sprites minimum
+ - Susi (idle, walk, run) - 12 sprites minimum
+ - **Estimated:** 52 sprites
+
+3. **Basic Zombie** ๐ด
+ - Idle, walk, attack animations
+ - **Estimated:** 15 sprites
+
+4. **Farm Objects** ๐ด
+ - Farmhouse (small)
+ - Barn (small)
+ - Fence sections
+ - Crop plots (tilled soil)
+ - **Estimated:** 25 assets
+
+**TOTAL FOR DEMO:** ~242 assets (out of 13,500!)
+
+---
+
+### ๐ DATA INTEGRITY
+
+**Sources Merged:**
+- โ
`scripts/generate_assets_full.py` - Complete 13,500+ asset registry
+- โ
`docs/game_design/GAME_BIBLE.md` v1 - Story, mechanics, systems
+- โ
`SESSION_REPORT_2026_01_06` - Latest generation status
+- โ
`BIOME_MASTER_SPREADSHEET.md` - Biome breakdown
+- โ
`NPC_GENERATION_LIST.md` - Complete NPC registry
+
+**Confirmations:**
+- โ
**Cyclops** - Present in mythical creatures
+- โ
**Minotaur** - Present in mythical creatures
+- โ
**Giant Troll** - Present as boss #19
+- โ
**Zmaj Volk** - Removed from game โ
+- โ
**Kai & Ana** - 14 years old (NOT 17!)
+- โ
**Giant Troll King** - Final boss โ
+- โ
**20 Biomes** - Mexican Cenotes + Witch Forest added
+- โ
**109 Creatures** - Including 3 Chupacabra variants
+
+---
+
+## ๐ฌ JAN 10 2026 ULTRA MARATHON UPDATE
+
+**Session Date:** 10. Januar 2026 (Petek)
+**Duration:** 21+ HOURS! (00:00 - 05:13 CET)
+**Git Commits:** 47 total
+**Status:** โ
LEGENDARY SUCCESS - INTRO & AGING SYSTEMS COMPLETE
+
+### **PRODUCTION ADDITIONS:**
+
+**๐ธ Reference Images Generated: 29 Total**
+
+**Aging System References (9):**
+- `/assets/references/aging_system/adult_kai_family_man.png` - Age 35, beard, tied dreads
+- `/assets/references/aging_system/elder_kai_punk_grandpa.png` - Age 50+, grey dreads, punk grandpa
+- `/assets/references/aging_system/mlada_ana_twin_prisoner.png` - Young Ana, twin DNA, RED EYES
+- `/assets/references/aging_system/odrasla_ana_scientist.png` - Age 35, scientist, lab coat
+- `/assets/references/aging_system/starejsa_ana_mentor.png` - Age 60+, mentor, grey streaks
+- `/assets/references/aging_system/otac_punk_father.png` - Father, age 45, RED EYES, gauges
+- `/assets/references/aging_system/mati_punk_mother.png` - Mother, age 45, RED EYES, dreads
+- `/assets/references/aging_system/ghost_otac_cyan.png` - Ghost father, neon cyan, 50% transparent
+- `/assets/references/aging_system/ghost_mati_magenta.png` - Ghost mother, neon magenta, 50% transparent
+
+**Intro Sequence Shots (20):**
+- `/assets/references/intro_shots/kai_ana_twins_childhood.png` - Twin bond, childhood
+- `/assets/references/intro_shots/kai_adult_35_timelapse.png` - Adult aging timelapse
+- `/assets/references/intro_shots/kai_elder_50_timelapse.png` - Elder aging timelapse
+- `/assets/references/intro_shots/otac_longboard_pier.png` - Father+son bonding moment
+- `/assets/references/intro_shots/virus_xnoir_microscope.png` - Neon green virus visualization
+- `/assets/references/intro_shots/zombie_silhouettes_panic.png` - Zombie outbreak chaos
+- `/assets/references/intro_shots/kai_bedroom_wakeup.png` - Current day bedroom, survivor aesthetic
+- `/assets/references/intro_shots/ana_memory_flash_purple.png` - VHS glitch amnesia fragment
+- `/assets/references/intro_shots/ana_barbershop_dreads.png` - Ana getting hair styled
+- `/assets/references/intro_shots/birthday_cake_rd.png` - Family birthday celebration
+- `/assets/references/intro_shots/parents_transparent_ghosts.png` - Parents fading (30% opacity)
+- `/assets/references/intro_shots/ana_taken_military.png` - Ana separation trauma
+- `/assets/references/intro_shots/kai_alone_basement.png` - Kai survival Day 01
+- `/assets/references/intro_shots/kai_young_timelapse.png` - Current Kai (age 25)
+- `/assets/references/intro_shots/chaos_streets_apocalypse.png` - City destruction
+- `/assets/references/intro_shots/gronk_doorway_silhouette.png` - Gronk entrance, vape smoke
+- `/assets/references/intro_shots/family_portrait_complete.png` - All 4 family members
+- `/assets/references/intro_shots/virus_city_aerial.png` - Aerial city infection (88%)
+- `/assets/references/intro_shots/kai_first_dreads_family.png` - Kai's first dreads, RED EYES DNA
+- `/assets/references/intro_shots/family_portrait_punk_complete.png` - Complete punk family, RED EYES
+
+**๐ Documentation Created: 2,000+ Lines**
+
+- `/docs/game_design/INTRO_SEQUENCE_DESIGN.md` (900+ lines)
+ - Version A: 60-second cinematic timeline (6 phases)
+ - Version B: 30-45 second fast-cut timeline (3 phases)
+ - Complete JavaScript implementation guides
+ - Audio timeline specifications
+ - Visual effects technical specs
+ - Typewriter text script (Slovenian)
+
+- `/assets/references/aging_system/README.md` (200+ lines)
+ - Memory vs Reality concept documentation
+ - Dual Ana system usage guide
+ - Twin DNA markers
+ - Narrative notes and emotional impact
+
+- `/TASK_AGING_REFERENCE_GENERATION.md` (400+ lines)
+ - Complete workflow for aging references
+ - Validation checklists
+ - DNK preservation rules
+ - File organization standards
+
+- `/SESSION_DNEVNIK_JAN_10_2026_INTRO_AGING_MARATHON.md` (500+ lines)
+ - Complete 21+ hour session documentation
+ - Timeline breakdown
+ - All achievements listed
+ - Technical specifications
+
+---
+
+### **NEW GAME SYSTEMS ESTABLISHED:**
+
+**1. GENERATIONAL AGING SYSTEM**
+
+**Concept:** Characters age through 3 distinct life stages over decades of searching for Ana.
+
+**Life Stages:**
+- **Young (25):** Current age, full energy, survivor beginnings
+- **Adult (35):** Experienced, beard/mature features, more tattoos, muscular
+- **Elder (50+):** Grey hair, full wisdom, punk grandpa/grandma, mentor role
+
+**Critical DNA Preservation Rules:**
+- โ
Piercings NEVER removed (identity permanent!)
+- โ
Dreadlocks evolve (color, length, style) but NEVER disappear
+- โ
RED EYES permanent across all ages (family DNA)
+- โ
Punk attitude maintained (rebellion against time!)
+- โ
Tattoos accumulate over time (life story on skin)
+
+**Characters with Aging:**
+- **Kai:** Young โ Adult (beard, tied dreads) โ Elder (grey dreads, "WISDOM NOT WEAKNESS")
+- **Ana (Twin Version):** Young (prisoner) โ Adult (scientist) โ Elder (mentor)
+- **Parents:** Appear as middle-aged (45) in happy memories, then as ghosts
+
+**Gameplay Impact:**
+- Aging happens during time-skip cutscenes (decades pass searching for Ana)
+- Each life stage = different sprite sets (~250+ sprites needed)
+- Stats change with age (strength peaks at Adult, wisdom peaks at Elder)
+- Different dialogues/reactions from NPCs based on age
+
+---
+
+**2. MEMORY vs REALITY SYSTEM (Ana Dual Version)**
+
+**Philosophical Concept:** Kai's amnesia creates two versions of Ana in his mind.
+
+**Original Ana (Memory/Flashbacks):**
+- **Appearance:** Purple/lavender skin, pure magenta hair (no green streaks)
+- **Personality:** Cute, slightly spoiled younger sister
+- **Usage:** Intro flashbacks, blur-to-clear memories, dream sequences
+- **Reference:** `/assets/references/main_characters/ana/master_reference_nobg.png`
+- **Narrative Role:** "How Kai remembers his little sister before apocalypse"
+
+**Nova Twin Ana (Reality/Game):**
+- **Appearance:** SAME skin tone as Kai, pink AND green dreadlocks, RED EYES
+- **Personality:** Fierce scientist, transformed survivor, twin DNA evident
+- **Usage:** Main game when Ana is found, all aging stages, present-day reality
+- **References:** `/assets/references/aging_system/mlada_ana_twin_prisoner.png` (and aging stages)
+- **Narrative Role:** "Who Ana BECAME - apocalypse transformed her into Kai's mirror"
+
+**The "Aha!" Recognition Moment:**
+```
+INTRO: Shows purple cute Ana (memory)
+GAME: Ana found with pink/green dreads and RED EYES
+KAI: "Wait... those eyes... that smirk... IT'S HER! My twin!"
+RESULT: Amnesia fog clears completely, full memory restored
+```
+
+**Recognition Triggers:**
+- ๐ด **RED EYES** - Instant DNA match (most obvious!)
+- ๐ **Arrogant smirk** - Childhood personality trait preserved
+- ๐ค **Specific hand gesture** - Twin habit only they share
+- ๐ฃ๏ธ **Voice tone** - Twin resonance, familiar cadence
+- ๐ช **Fighting stance** - Taught by same mentor (father)
+
+**Narrative Significance:**
+- Shows how apocalypse changes people but core identity remains
+- "System no change me" - Ana's transformation was CHOICE, not forced
+- Twin bond transcends physical appearance
+- Memory distortion vs reality = powerful storytelling
+
+---
+
+**3. GHOST QUEST SYSTEM**
+
+**Concept:** Dead parents return as neon spirits to guide Kai to truth.
+
+**Ghost Appearance:**
+
+**Ghost Otac (Cyan):**
+- 50% transparent with NEON CYAN outline and rim lighting
+- VHS scanline glitch effect (broken memory aesthetic)
+- Still holding ghostly longboard skateboard (glowing cyan)
+- Sad but peaceful expression - protective even in death
+- Reference: `/assets/references/aging_system/ghost_otac_cyan.png`
+
+**Ghost Mati (Magenta):**
+- 50% transparent with NEON MAGENTA outline and rim lighting
+- VHS scanline glitch effect
+- Holding ghostly flowers (glowing magenta)
+- The Clash shirt visible through transparency
+- Sad but loving expression
+- Reference: `/assets/references/aging_system/ghost_mati_magenta.png`
+
+**Quest Trigger:**
+- Ghosts appear ONLY after Ana is rescued (not before!)
+- Before rescue: Just unclear shadows/mist in fog
+- After rescue: Full neon ghost manifestation
+
+**Quest Mechanics:**
+- Ghosts don't speak much (silent guidance)
+- Float/hover toward location where they died
+- Player must follow ghost trail through wasteland
+- Lead to final truth about X-Noir virus origin and zombie attack
+- Quest completion = emotional closure for Kai
+
+**Haptic Feedback (Xbox Controller):**
+- When ghosts near Kai: Controller STOPS aggressive zombie vibration
+- Switches to gentle rhythmic pulse (60 BPM heartbeat)
+- Creates comfort feeling, not fear
+- Signifies: "Parents are here, you're safe"
+
+**Narrative Purpose:**
+- Resolves amnesia mystery (what happened to parents?)
+- Shows death but maintains dignity (punk ghosts!)
+- "They died protecting us" revelation
+- Final goodbye/closure moment
+- Strengthens theme: Love transcends death
+
+---
+
+**4. FAMILY DNA SYSTEM (RED EYES)**
+
+**Genetic Marker Concept:** RED EYES = Family blood connection
+
+**Who Has RED EYES:**
+- โ
**Otac (Father)** - RED EYES (genetic source)
+- โ
**Mati (Mother)** - RED EYES (genetic match)
+- โ
**Kai** - RED EYES (inherited from both parents)
+- โ **Ana** - Different eyes (purple skin, different genetic origin?)
+
+**Visual Recognition:**
+- All 3 family members (Father, Mother, Kai) have matching RED EYES
+- Most obvious when together (family portraits, barbershop scene)
+- References: `kai_first_dreads_family.png`, `family_portrait_punk_complete.png`
+
+**Narrative Questions Raised:**
+- Why does Ana NOT have red eyes if she's Kai's twin?
+- Multiple Ana theories:
+ 1. **Adopted twin** (different biological origin)
+ 2. **Genetic experiment** (explains transformation ability)
+ 3. **Chimera DNA** (two genetic sources merged)
+- This mystery can be revealed later in game!
+
+**Punk Transformation Scene:**
+- Young Kai gets first dreadlocks at barbershop (age ~8-10)
+- Father and Mother watch proudly
+- ALL THREE have RED EYES clearly visible
+- Shows: "This is where Kai got his punk DNA - from family!"
+- Bonding moment: Becoming punk family TOGETHER
+
+**Recognition Power:**
+- When Kai sees Ana's RED EYES (twin version), instant recognition
+- "Those eyes... they're like mine... FAMILY!"
+- Visual DNA proof more powerful than words
+
+---
+
+**5. INTRO SEQUENCE SYSTEM (Dual Timeline)**
+
+**Purpose:** 30-60 second cinematic opening that establishes entire narrative
+
+**Version A: 60-Second Cinematic Timeline**
+
+**Phase 1 (0:00-0:10): Happy Family**
+- Shots: Otac longboard, Ana barbershop, birthday cake, family portraits
+- Text: *"Nekoฤ so bile barve resniฤne..."* (Once colors were real...)
+- Audio: Light lo-fi music, children's laughter, ocean waves
+- Mood: Warm, nostalgic, golden hour lighting
+
+**Phase 2 (0:10-0:20): The Bond**
+- Shots: Kai & Ana twins holding hands, matching DNA visible
+- Text: *"Imel sem njo. Imela sva vse."* (I had her. We had everything.)
+- Audio: Gentle heartbeat (80 BPM), emotional music continues
+- Mood: CLEAREST shot (0 blur) - most important memory!
+
+**Phase 3 (0:20-0:35): The Collapse**
+- Shots: X-Noir virus, zombie outbreak, chaos streets, panic
+- Text: *"Potem je priลกel X-Noir. In svet se je zlomil."* (Then came X-Noir. World broke.)
+- Audio: Fast punk rock, sirens, screams, chaos soundscape
+- Mood: RED/GREEN color saturation, heavy VHS glitch
+
+**Phase 4 (0:35-0:45): The Loss**
+- Shots: Parents becoming transparent (30% opacity), Ana taken by military, Kai alone
+- Text: *"Starลกa sta postala senci... Ana je izginila v temi."* (Parents became shadows... Ana vanished.)
+- Audio: Distortion, sudden silence, single heartbeat
+- Mood: Dark, traumatic, heavy blur
+
+**Phase 5 (0:45-0:55): Passage of Time** โญ **AGING MONTAGE!**
+- Shots: Fast cuts - Young Kai โ Adult Kai (beard) โ Elder Kai (grey dreads)
+- Text: *"Leta so postala megla. Obraz se je spremenil... cilj nikoli."* (Years became fog. Face changed... goal never.)
+- Audio: Deep noir synth, clock ticking, time-passing whoosh
+- Mood: Timelapse blur effect, showcases AGING SYSTEM!
+
+**Phase 6 (0:55-1:00): Amnesia Wake-Up**
+- Shots: Maximum blur โ Kai's bedroom โ Ana memory flash โ Gronk entrance
+- Text: *"Kdo sem? ... Kje je Ana?"* (Who am I? ... Where is Ana?)
+- Audio: Breathing, heartbeat normalizing, vape hiss, Gronk voice: *"No, konฤno si buden."*
+- Mood: VHS filter fades out, transition to gameplay
+
+**Skip Option:**
+- After 5 seconds, displays: `[Hold X to face reality]`
+- Pressing X skips entire intro, jumps to gameplay
+- Saves intro completion flag for returning players
+
+**Version B: 30-45 Second Fast-Cut Timeline**
+- 3 phases instead of 6 (Happy โ Collapse โ Amnesia)
+- Faster cuts (3-5 seconds per shot)
+- ADHD-friendly energy, rapid impact
+- Best for demo/testing, less asset-heavy
+
+**Technical Requirements:**
+- 20 intro shot images (all generated! โ
)
+- 3 music tracks (happy lo-fi, punk chaos, dark ambient)
+- 20+ SFX files (laughter, sirens, heartbeat, vape, etc.)
+- VHS scanline shader (custom Phaser PostFX)
+- Blur post-processing pipeline
+- Typewriter text system (existing DynamicTypewriterSystem)
+
+**Implementation Status:**
+- โ
All 20 reference images generated
+- โ
Complete design document (900+ lines)
+- โ
JavaScript structure provided
+- โ IntroScene.js not coded yet (~500 lines needed)
+- โ Audio tracks not sourced yet
+- โ VHS shader not implemented yet
+
+---
+
+### **ASSET PRODUCTION STATS:**
+
+**Images Generated This Session:**
+- Aging System: 9 images
+- Intro Shots: 20 images
+- **Total:** 29 images (~25MB total, high quality)
+
+**Documentation Written:**
+- INTRO_SEQUENCE_DESIGN.md: 900+ lines
+- Aging README: 200+ lines
+- Task workflow: 400+ lines
+- Session Journal: 500+ lines
+- **Total:** 2,000+ lines of production documentation
+
+**Git Activity:**
+- Commits: 47 in single session
+- Files created: 32 new files
+- Files modified: Multiple updates to existing references
+
+**Time Investment:**
+- Session duration: 21 hours 13 minutes
+- Images per hour: ~1.4 images/hour
+- Documentation per hour: ~95 lines/hour
+- Commits per hour: ~2.2 commits/hour
+
+---
+
+### **STYLE CONSISTENCY VERIFICATION:**
+
+**All 29 Images Follow Style 32 Dark-Chibi Noir:**
+- โ
Thick black outlines (2-3px), cel-shaded rendering
+- โ
Chibi proportions (2:1 head to body ratio)
+- โ
Neon magenta and cyan rim lighting
+- โ
High contrast noir atmospheric lighting
+- โ
VHS grain texture (where appropriate)
+- โ
Scanline glitch effects (memory/ghost shots)
+- โ
Emotional storytelling through composition
+
+**Quality Standards:**
+- Average file size: 800KB+ per image (high resolution)
+- DNA consistency maintained across all characters
+- Facial features match master references
+- RED EYES clearly visible where required
+- Piercings, gauges, tattoos accurate to lore
+
+---
+
+### **PRODUCTION READY STATUS:**
+
+**โ
COMPLETE & READY:**
+- Aging System References (all 3 life stages for Kai & Ana)
+- Ghost Parents (both cyan and magenta versions)
+- Intro Sequence Design (2 complete timeline versions)
+- All 20 Intro Cinemat Shots (full coverage)
+- Family DNA System (RED EYES documented)
+- Memory vs Reality Concept (dual Ana system)
+
+**๐ IN PROGRESS:**
+- Sprite generation from aging references (~250+ needed)
+- IntroScene.js JavaScript implementation
+
+**โ NOT STARTED:**
+- Audio track sourcing/creation (3 music tracks)
+- SFX library compilation (20+ effects)
+- VHS shader development (custom PostFX)
+- Aging system gameplay integration
+
+---
+
+### **UPDATED GAME NARRATIVE:**
+
+**Core Story Enhancement:**
+
+The intro sequence now provides complete emotional foundation:
+1. **Happy Past:** Family was whole, punk, loving (before = colors)
+2. **Tragic Loss:** X-Noir virus destroys world, parents die, Ana taken (darkness comes)
+3. **Time Passage:** DECADES of searching, Kai ages but never gives up (fog years)
+4. **Amnesia Fog:** Kai remembers purple Ana, searches for "little sister" (fragmented memory)
+5. **Recognition:** Finds twin Ana with pink/green dreads and RED EYES (reality revealed!)
+6. **Ghost Closure:** Parents guide to truth as neon spirits (love transcends death)
+
+**Thematic Reinforcement:**
+- **"System no change me!"** - Kai's punk identity survives aging (grey dreads at 50+!)
+- **Family DNA** - RED EYES connect three generations visually
+- **Memory distortion** - Amnesia creates dual Ana (narrative innovation)
+- **Dignity in aging** - Elder Kai is PUNK GRANDPA, not weak old man
+- **Love transcends** - Parents return as ghosts to protect/guide
+
+---
+
+### **TECHNICAL INTEGRATION NOTES:**
+
+**For Future Implementation:**
+
+1. **Aging System:**
+ - Trigger aging during time-skip cutscenes (dialogue: "10 years passed...")
+ - Swap character sprite sheets based on age variable
+ - Update stats: `if (age >= 35) { wisdom += 20; strength -= 5; }`
+ - Different NPC dialogues: "You're looking older, friend..." vs "Young traveler..."
+
+2. **Intro Sequence:**
+ - Create new `IntroScene.js` in `/src/scenes/`
+ - Load all 20 intro shots in `preload()`
+ - Implement phase system with `time.delayedCall()` timing
+ - Add blur/glitch PostFX pipelines
+ - Integrate DynamicTypewriterSystem for text overlays
+ - Skip handler: `input.keyboard.on('keydown-X', () => scene.start('GameScene'))`
+
+3. **Ghost Quest:**
+ - Trigger check: `if (anaRescued && !ghostQuestComplete)`
+ - Ghost sprites: Use ghost references, apply 50% alpha in code
+ - Movement: Ghosts float toward `deathLocation` coordinates
+ - Player follows, proximity check triggers lore reveals
+ - Quest completion: Play final memory cutscene, unlock achievement
+
+4. **Memory vs Reality:**
+ - Flashback scenes: Use original purple Ana sprite
+ - Present game: Use twin Ana sprites with RED EYES
+ - Recognition scene: Crossfade between both versions with particle effect
+ - Dialogue trigger: Ana does specific gesture โ Kai: "That move... only she does that!"
+
+---
+
+### **CREATOR NOTES:**
+
+**David's Reflection on 21-Hour Marathon:**
+
+> "This was the longest single session I've ever done. 21+ hours straight, generating 29 images and writing 2,000+ lines of documentation. Why? Because this intro and aging system are the HEART of the game.
+>
+> The 'Memory vs Reality' concept for Ana is personal - it's about how trauma distorts memory, how people change but core identity remains. The RED EYES family DNA is visual storytelling at its best - you don't need dialogue to show Kai's connection to his parents.
+>
+> And the aging system... man, this is important. Kai searches for DECADES. Most games would just time-skip and ignore it. Not here. We SHOW the years. We show grey dreads at 50+. We show a punk grandpa who NEVER removed his piercings. Because that's the message: **'System no change me!'**
+>
+> The intro is designed to make players CRY in 60 seconds. Happy family โ Virus destroys everything โ Parents die โ Ana taken โ Decades pass โ Still searching.
+>
+> This game is my life philosophy in pixels: Stay weird. Stay punk. Never let the system win."
+
+**Production Philosophy:**
+- Quality over speed (21 hours for 29 images = careful, thoughtful work)
+- Narrative depth over shallow gameplay
+- Visual DNA storytelling (RED EYES, piercings, dreads)
+- Representation matters (punk aging with dignity!)
+- ADHD energy channeled into creative marathon
+
+---
+
+## ๐ **UPDATED STATUS: ZABETONIRAN!**
+
+**Previous Update:** 06.01.2026 21:41 CET (Epic Marathon +53 sprites)
+**Current Update:** 10.01.2026 05:13 CET (Ultra Marathon +29 references +2,000 docs)
+
+**Git Commit:** `3a120c32` (RED EYES DNA - Family Connection Complete!)
+**Date:** 10.01.2026 05:10 CET
+**New Files:** 32 created in this session
+**Backups:** Multiple commits, full Git history preserved โ
+
+**This is the MASTER game bible**. Use this for all future development!
+
+All data is locked, tracked, and version-controlled. Nothing will be lost! ๐ช
+
+---
+
+**END OF PRODUCTION GAME BIBLE v2.4 (Updated Jan 10, 2026)**
diff --git a/MASTER_DESIGN_RECOVERY/DialogueSystem.js b/MASTER_DESIGN_RECOVERY/DialogueSystem.js
new file mode 100644
index 000000000..d8c09cf4d
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/DialogueSystem.js
@@ -0,0 +1,516 @@
+/**
+ * DialogueSystem.js
+ * =================
+ * KRVAVA ลฝETEV - NPC Dialogue & Conversation System
+ *
+ * Features:
+ * - Dynamic dialogue trees with choices
+ * - Character portraits and emotions
+ * - Quest integration (dialogue can trigger/complete quests)
+ * - Relationship tracking (affects dialogue options)
+ * - Twin Bond special dialogues (Ana's voice)
+ * - Memory system (NPCs remember past conversations)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class DialogueSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Active dialogue state
+ this.currentDialogue = null;
+ this.currentSpeaker = null;
+ this.currentNode = null;
+ this.dialogueHistory = new Map(); // NPC ID -> conversation history
+
+ // UI elements
+ this.dialogueBox = null;
+ this.portraitSprite = null;
+ this.nameText = null;
+ this.dialogueText = null;
+ this.choicesContainer = null;
+
+ // Dialogue database
+ this.dialogues = new Map(); // Dialogue ID -> dialogue data
+
+ // Callbacks
+ this.onDialogueComplete = null;
+ this.onChoiceMade = null;
+
+ console.log('๐ฌ DialogueSystem initialized');
+ }
+
+ /**
+ * Register a dialogue tree
+ * @param {string} dialogueId - Unique identifier
+ * @param {object} dialogueData - Tree structure
+ */
+ registerDialogue(dialogueId, dialogueData) {
+ this.dialogues.set(dialogueId, dialogueData);
+ console.log(`๐ฌ Registered dialogue: ${dialogueId}`);
+ }
+
+ /**
+ * Start a dialogue with an NPC
+ * @param {string} dialogueId - Which dialogue to start
+ * @param {object} speaker - NPC or character data
+ * @param {function} onComplete - Callback when done
+ */
+ startDialogue(dialogueId, speaker, onComplete) {
+ const dialogueData = this.dialogues.get(dialogueId);
+
+ if (!dialogueData) {
+ console.error(`Dialogue not found: ${dialogueId}`);
+ return;
+ }
+
+ this.currentDialogue = dialogueData;
+ this.currentSpeaker = speaker;
+ this.onDialogueComplete = onComplete;
+
+ // Show UI
+ this.createDialogueUI();
+
+ // Start at root node
+ this.showNode(dialogueData.root || 'start');
+
+ // Pause game
+ this.scene.physics.pause();
+
+ console.log(`๐ฌ Started dialogue: ${dialogueId} with ${speaker.name}`);
+ }
+
+ /**
+ * Show a specific dialogue node
+ * @param {string} nodeId - Node to display
+ */
+ showNode(nodeId) {
+ const node = this.currentDialogue.nodes[nodeId];
+
+ if (!node) {
+ console.error(`Node not found: ${nodeId}`);
+ this.endDialogue();
+ return;
+ }
+
+ this.currentNode = node;
+
+ // Update speaker info
+ const speaker = node.speaker || this.currentSpeaker.name;
+ const emotion = node.emotion || 'neutral';
+
+ this.updateSpeaker(speaker, emotion);
+
+ // Show text with typewriter effect
+ this.typewriterText(node.text);
+
+ // Show choices or continue button
+ if (node.choices && node.choices.length > 0) {
+ this.showChoices(node.choices);
+ } else if (node.next) {
+ this.showContinueButton(node.next);
+ } else {
+ // End of dialogue
+ this.showContinueButton('END');
+ }
+
+ // Execute node actions
+ if (node.action) {
+ this.executeNodeAction(node.action);
+ }
+
+ // Record in history
+ this.addToHistory(nodeId, node.text);
+ }
+
+ /**
+ * Create dialogue UI
+ */
+ createDialogueUI() {
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Container for everything
+ this.dialogueContainer = this.scene.add.container(0, 0);
+ this.dialogueContainer.setDepth(1000);
+
+ // Dark overlay
+ const overlay = this.scene.add.rectangle(0, 0, width, height, 0x000000, 0.5);
+ overlay.setOrigin(0);
+ overlay.setInteractive();
+ this.dialogueContainer.add(overlay);
+
+ // Dialogue box
+ const boxY = height - 200;
+ const boxHeight = 180;
+
+ this.dialogueBox = this.scene.add.rectangle(
+ width / 2, boxY,
+ width - 80, boxHeight,
+ 0x2d1b00, 0.95
+ );
+ this.dialogueBox.setStrokeStyle(3, 0xd4a574);
+ this.dialogueContainer.add(this.dialogueBox);
+
+ // Portrait background
+ this.portraitBg = this.scene.add.rectangle(100, boxY, 100, 100, 0x4a3520, 0.9);
+ this.portraitBg.setStrokeStyle(2, 0xd4a574);
+ this.dialogueContainer.add(this.portraitBg);
+
+ // Portrait
+ this.portraitSprite = this.scene.add.text(100, boxY, '๐ค', {
+ fontSize: '64px'
+ });
+ this.portraitSprite.setOrigin(0.5);
+ this.dialogueContainer.add(this.portraitSprite);
+
+ // Speaker name
+ this.nameText = this.scene.add.text(170, boxY - 70, '', {
+ fontSize: '20px',
+ fontFamily: 'Georgia, serif',
+ color: '#FFD700',
+ fontStyle: 'bold',
+ stroke: '#000000',
+ strokeThickness: 3
+ });
+ this.dialogueContainer.add(this.nameText);
+
+ // Dialogue text
+ this.dialogueText = this.scene.add.text(170, boxY - 40, '', {
+ fontSize: '18px',
+ fontFamily: 'Georgia, serif',
+ color: '#f4e4c1',
+ wordWrap: { width: width - 280 },
+ lineSpacing: 6
+ });
+ this.dialogueContainer.add(this.dialogueText);
+
+ // Choices container
+ this.choicesContainer = this.scene.add.container(width / 2, boxY + 120);
+ this.dialogueContainer.add(this.choicesContainer);
+ }
+
+ /**
+ * Update speaker display
+ */
+ updateSpeaker(name, emotion) {
+ this.nameText.setText(name);
+
+ // Get portrait based on speaker and emotion
+ const portrait = this.getPortrait(name, emotion);
+ this.portraitSprite.setText(portrait);
+ }
+
+ /**
+ * Get portrait emoji
+ */
+ getPortrait(speaker, emotion) {
+ // Kai portraits
+ if (speaker === 'Kai' || speaker === 'You') {
+ const kaiEmotions = {
+ 'neutral': '๐จ',
+ 'happy': '๐',
+ 'sad': '๐ข',
+ 'angry': '๐ ',
+ 'worried': '๐',
+ 'shocked': '๐ฑ',
+ 'determined': '๐ค'
+ };
+ return kaiEmotions[emotion] || '๐จ';
+ }
+
+ // Ana portraits (Twin Bond - ghostly)
+ if (speaker === 'Ana' || speaker === 'Ana (Twin Bond)') {
+ const anaEmotions = {
+ 'neutral': '๐ป',
+ 'happy': '๐',
+ 'sad': '๐ฐ',
+ 'worried': '๐จ',
+ 'pain': '๐ฃ',
+ 'help': '๐'
+ };
+ return anaEmotions[emotion] || '๐ป';
+ }
+
+ // Other NPCs
+ const npcPortraits = {
+ 'Grok': '๐ง',
+ 'Elder': '๐ด',
+ 'Blacksmith': '๐จ',
+ 'Baker': '๐',
+ 'Doctor': 'โ๏ธ',
+ 'Merchant': '๐ฐ',
+ 'Stranger': 'โ',
+ 'Zombie': '๐ง'
+ };
+
+ return npcPortraits[speaker] || '๐ค';
+ }
+
+ /**
+ * Typewriter text effect
+ */
+ typewriterText(text) {
+ let displayText = '';
+ let charIndex = 0;
+
+ this.dialogueText.setText('');
+
+ // Store reference to timer so we can clear it if needed
+ this.typewriterTimer = this.scene.time.addEvent({
+ delay: 30,
+ callback: () => {
+ // SAFETY CHECK: Ensure text object still exists
+ if (!this.dialogueText || !this.dialogueText.active) {
+ if (this.typewriterTimer) this.typewriterTimer.remove();
+ return;
+ }
+
+ if (charIndex < text.length) {
+ displayText += text[charIndex];
+ this.dialogueText.setText(displayText);
+ charIndex++;
+ } else {
+ if (this.typewriterTimer) this.typewriterTimer.remove();
+ }
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * Show dialogue choices
+ */
+ showChoices(choices) {
+ this.choicesContainer.removeAll(true);
+
+ choices.forEach((choice, index) => {
+ // Check if choice is available
+ if (choice.condition && !this.checkCondition(choice.condition)) {
+ return; // Skip this choice
+ }
+
+ const y = index * 50;
+
+ // Choice button background
+ const btn = this.scene.add.rectangle(0, y, 600, 40, 0x6b4423, 1);
+ btn.setStrokeStyle(2, 0xd4a574);
+ btn.setInteractive({ useHandCursor: true });
+
+ // Choice text
+ const text = this.scene.add.text(0, y, choice.text, {
+ fontSize: '16px',
+ fontFamily: 'Georgia, serif',
+ color: '#f4e4c1',
+ fontStyle: 'bold'
+ });
+ text.setOrigin(0.5);
+
+ // Hover effects
+ btn.on('pointerover', () => {
+ btn.setFillStyle(0x8b5a3c);
+ text.setColor('#FFD700');
+ });
+
+ btn.on('pointerout', () => {
+ btn.setFillStyle(0x6b4423);
+ text.setColor('#f4e4c1');
+ });
+
+ // Click handler
+ btn.on('pointerdown', () => {
+ this.onChoiceSelected(choice);
+ });
+
+ this.choicesContainer.add([btn, text]);
+ });
+ }
+
+ /**
+ * Show continue button
+ */
+ showContinueButton(nextNode) {
+ this.choicesContainer.removeAll(true);
+
+ const continueBtn = this.scene.add.text(0, 0, 'โผ Continue (SPACE)', {
+ fontSize: '16px',
+ fontFamily: 'Georgia, serif',
+ color: '#888888'
+ });
+ continueBtn.setOrigin(0.5);
+
+ // Pulse animation
+ this.scene.tweens.add({
+ targets: continueBtn,
+ alpha: 0.3,
+ duration: 800,
+ yoyo: true,
+ repeat: -1
+ });
+
+ this.choicesContainer.add(continueBtn);
+
+ // Space key or click to continue
+ const spaceKey = this.scene.input.keyboard.addKey('SPACE');
+ spaceKey.once('down', () => {
+ if (nextNode === 'END') {
+ this.endDialogue();
+ } else {
+ this.showNode(nextNode);
+ }
+ });
+ }
+
+ /**
+ * Handle choice selection
+ */
+ onChoiceSelected(choice) {
+ console.log(`๐ฌ Choice selected: ${choice.text}`);
+
+ // Execute choice action
+ if (choice.action) {
+ this.executeNodeAction(choice.action);
+ }
+
+ // Callback
+ if (this.onChoiceMade) {
+ this.onChoiceMade(choice);
+ }
+
+ // Go to next node
+ if (choice.next) {
+ this.showNode(choice.next);
+ } else {
+ this.endDialogue();
+ }
+ }
+
+ /**
+ * Execute node action
+ */
+ executeNodeAction(action) {
+ switch (action.type) {
+ case 'quest_start':
+ this.scene.questSystem?.startQuest(action.questId);
+ break;
+
+ case 'quest_complete':
+ this.scene.questSystem?.completeQuest(action.questId);
+ break;
+
+ case 'give_item':
+ this.scene.inventorySystem?.addItem(action.itemId, action.amount);
+ break;
+
+ case 'take_item':
+ this.scene.inventorySystem?.removeItem(action.itemId, action.amount);
+ break;
+
+ case 'relationship_change':
+ this.changeRelationship(action.npcId, action.amount);
+ break;
+
+ case 'custom':
+ if (action.callback) {
+ action.callback(this.scene);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Check if condition is met
+ */
+ checkCondition(condition) {
+ switch (condition.type) {
+ case 'quest_active':
+ return this.scene.questSystem?.isQuestActive(condition.questId);
+
+ case 'quest_complete':
+ return this.scene.questSystem?.isQuestComplete(condition.questId);
+
+ case 'has_item':
+ return this.scene.inventorySystem?.hasItem(condition.itemId, condition.amount);
+
+ case 'relationship':
+ return this.getRelationship(condition.npcId) >= condition.value;
+
+ case 'custom':
+ return condition.check(this.scene);
+
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Add to conversation history
+ */
+ addToHistory(nodeId, text) {
+ const speakerId = this.currentSpeaker.id || this.currentSpeaker.name;
+
+ if (!this.dialogueHistory.has(speakerId)) {
+ this.dialogueHistory.set(speakerId, []);
+ }
+
+ this.dialogueHistory.get(speakerId).push({
+ nodeId: nodeId,
+ text: text,
+ timestamp: Date.now()
+ });
+ }
+
+ /**
+ * Relationship tracking
+ */
+ changeRelationship(npcId, amount) {
+ // TODO: Integrate with proper relationship system
+ console.log(`๐ Relationship with ${npcId}: ${amount > 0 ? '+' : ''}${amount}`);
+ }
+
+ getRelationship(npcId) {
+ // TODO: Get from relationship system
+ return 0;
+ }
+
+ /**
+ * End dialogue
+ */
+ endDialogue() {
+ console.log('๐ฌ Dialogue ended');
+
+ // Stop typewriter effect if running
+ if (this.typewriterTimer) {
+ this.typewriterTimer.remove();
+ this.typewriterTimer = null;
+ }
+
+ // Clean up UI
+ if (this.dialogueContainer) {
+ this.dialogueContainer.destroy();
+ }
+
+ // Resume game
+ this.scene.physics.resume();
+
+ // Callback
+ if (this.onDialogueComplete) {
+ this.onDialogueComplete();
+ }
+
+ // Reset state
+ this.currentDialogue = null;
+ this.currentSpeaker = null;
+ this.currentNode = null;
+ this.onDialogueComplete = null;
+ }
+
+ /**
+ * Check if dialogue is active
+ */
+ isActive() {
+ return this.currentDialogue !== null;
+ }
+}
diff --git a/MASTER_DESIGN_RECOVERY/MainQuestAnaSystem.js b/MASTER_DESIGN_RECOVERY/MainQuestAnaSystem.js
new file mode 100644
index 000000000..1b896b999
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/MainQuestAnaSystem.js
@@ -0,0 +1,371 @@
+/**
+ * MainQuestAnaSystem.js
+ * =====================
+ * KRVAVA ลฝETEV - Main Quest: Finding Ana (Phase 42)
+ *
+ * Features:
+ * - Main story quest (finding lost sister)
+ * - 4 Acts + Finale
+ * - Quest journal & tracking
+ * - Lore entries discovery
+ * - Multiple endings
+ * - Decision system
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+export default class MainQuestAnaSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Quest state
+ this.currentAct = 0;
+ this.currentQuest = null;
+ this.completedQuests = new Set();
+
+ // Story decisions
+ this.decisions = new Map();
+ this.moralityScore = 0; // -100 (revenge) to +100 (cure)
+
+ // Lore entries
+ this.loreEntries = new Map();
+ this.discoveredLore = new Set();
+
+ // Quest journal
+ this.journal = [];
+
+ console.log('๐ง MainQuestAnaSystem initialized');
+
+ // Register all quests
+ this.registerQuests();
+
+ // Register lore entries
+ this.registerLoreEntries();
+ }
+
+ /**
+ * Register all main quests
+ */
+ registerQuests() {
+ // Prologue is handled by PrologueScene
+
+ // ACT 1: Searching for Clues (Already in Act1QuestData.js)
+ // Quests 1.1 - 1.8 are integrated
+
+ // ACT 2: Discovering the Lab
+ this.registerQuest('2.1', {
+ act: 2,
+ name: 'The Underground Facility',
+ description: 'Twin Bond led you to old military bunker. Investigate.',
+ objectives: [
+ { id: 'find_bunker', desc: 'Locate underground bunker entrance', type: 'location' },
+ { id: 'clear_entrance', desc: 'Clear zombie blockade', type: 'combat' },
+ { id: 'enter_lab', desc: 'Enter Black Serpent Laboratory', type: 'location' }
+ ],
+ rewards: { xp: 2000, zlatniki: 1000, bondStrength: 15 }
+ });
+
+ this.registerQuest('2.2', {
+ act: 2,
+ name: 'Lab Records',
+ description: 'Search lab computers for information about Ana.',
+ objectives: [
+ { id: 'find_terminal', desc: 'Find working computer terminal', type: 'location' },
+ { id: 'hack_system', desc: 'Hack into lab database', type: 'puzzle' },
+ { id: 'read_files', desc: 'Read experiment logs', type: 'interact' }
+ ],
+ rewards: { xp: 1500, lore: ['virus_origin', 'experiment_logs'] }
+ });
+
+ this.registerQuest('2.3', {
+ act: 2,
+ name: 'Zombie Test Subjects',
+ description: 'Discover horrifying truth about zombie experiments.',
+ objectives: [
+ { id: 'find_cells', desc: 'Locate holding cells', type: 'location' },
+ { id: 'read_charts', desc: 'Read patient charts', type: 'interact' },
+ { id: 'find_ana_cell', desc: 'Find Ana\'s empty cell', type: 'location' }
+ ],
+ rewards: { xp: 2500, bondStrength: 20, lore: ['experiment_subjects'] }
+ });
+
+ // ACT 3: Confronting the Truth
+ this.registerQuest('3.1', {
+ act: 3,
+ name: 'The Scientist',
+ description: 'Track down lead scientist Dr. Kovaฤ.',
+ objectives: [
+ { id: 'find_hideout', desc: 'Locate Dr. Kovaฤ\'s hideout', type: 'location' },
+ { id: 'confront_scientist', desc: 'Confront Dr. Kovaฤ', type: 'dialogue' },
+ { id: 'make_choice', desc: 'Kill or spare the scientist?', type: 'decision' }
+ ],
+ rewards: { xp: 3000, decision: 'scientist_fate' }
+ });
+
+ this.registerQuest('3.2', {
+ act: 3,
+ name: 'The Final Obstacle',
+ description: 'Ana is held in the mountain vault. Prepare for the breach.',
+ objectives: [
+ { id: 'gather_allies', desc: 'Recruit allies for the assault', type: 'social' },
+ { id: 'craft_weapons', desc: 'Craft high-tier equipment', type: 'crafting' },
+ { id: 'locate_lair', desc: 'Find the mountain fortress', type: 'location' }
+ ],
+ rewards: { xp: 4000, zlatniki: 5000 }
+ });
+
+ this.registerQuest('3.3', {
+ act: 3,
+ name: 'The Cure Research',
+ description: 'Find and study cure research notes.',
+ objectives: [
+ { id: 'find_notes', desc: 'Collect all 7 research notes', type: 'collect' },
+ { id: 'study_cure', desc: 'Study cure methodology', type: 'research' },
+ { id: 'make_choice', desc: 'Pursue cure or revenge?', type: 'decision' }
+ ],
+ rewards: { xp: 3500, decision: 'cure_or_revenge', lore: ['cure_research'] }
+ });
+
+ // FINALE
+ this.registerQuest('finale', {
+ act: 4,
+ name: 'Confrontation',
+ description: 'Reach the reactor core and save Ana.',
+ objectives: [
+ { id: 'enter_lair', desc: 'Storm the mountain fortress', type: 'location' },
+ { id: 'defeat_boss', desc: 'Breach the vault defenses', type: 'boss_fight' },
+ { id: 'save_ana', desc: 'Rescue Ana', type: 'cutscene' },
+ { id: 'final_choice', desc: 'The ultimate decision...', type: 'decision' }
+ ],
+ rewards: { xp: 10000, achievement: 'main_quest_complete' }
+ });
+
+ console.log('โ
Main quest chain registered');
+ }
+
+ /**
+ * Register single quest
+ */
+ registerQuest(id, data) {
+ // Quests are managed by QuestSystemExpanded
+ // This just tracks main quest progress
+ console.log(`๐ Quest ${id}: ${data.name}`);
+ }
+
+ /**
+ * Register lore entries
+ */
+ registerLoreEntries() {
+ this.loreEntries.set('virus_origin', {
+ title: 'The Outbreak',
+ content: `BLACK SERPENT INITIATIVE - CLASSIFIED
+
+Project Lazarus began as a cure for death itself. Using ancient viral samples
+recovered from permafrost, Dr. Helena Kovaฤ believed she could reanimate dead
+tissue while preserving consciousness.
+
+The first trials were... promising. Deceased subjects showed signs of life.
+But something went wrong. The virus mutated. Subjects became aggressive,
+hungry for living flesh.
+
+We tried to contain it. We failed.
+
+The Outbreak began here, in this very laboratory, on October 13th, 2024.
+
+May God forgive us.`,
+ location: 'Lab Computer Terminal 1'
+ });
+
+ this.loreEntries.set('experiment_logs', {
+ title: 'Subject Ana Kovaฤ',
+ content: `EXPERIMENT LOG #247
+Subject: Ana Kovaฤ (Age 19, Twin Sister of Kai Kovaฤ)
+Status: SPECIAL CASE
+
+Subject shows unprecedented resistance to viral infection. Twin Bond
+phenomenon observed - psychic connection to brother remains strong even
+in experimental conditions.
+
+Dr. Kovaฤ (mother) has requested subject be moved to special containment.
+Krniฤ has shown interest in subject. Authorization pending.
+
+UPDATE: Subject escaped during the facility breach. Current location unknown.
+Twin Bond suggests she's alive. Brother searching for her.`,
+ location: 'Lab Record Room'
+ });
+
+ this.loreEntries.set('project_apex', {
+ title: 'Project Apex',
+ content: `PROJECT: APEX PREDATOR
+
+Combining viral genetics (recovered from permafrost) with the Alpha Hybrid
+strain has created our most powerful weapon yet.
+
+Designation: ALPHA TROLL KING
+
+Capabilities:
+- Enhanced strength (100x human)
+- Accelerated healing
+- Pack leader pheromones (controls all lesser infected)
+- High intelligence (strategic thinking)
+
+WARNING: Subject is unstable. Uncontrollable by anyone but Krniฤ.
+Recommendation: DEPLOYMENT
+
+The new world requires a king.`,
+ location: 'Lab Chief Scientist Office'
+ });
+
+ this.loreEntries.set('cure_research', {
+ title: 'The Cure - Final Notes',
+ content: `I've done it. The cure is real.
+
+Using Ana's unique antibodies and the original viral strain, I've synthesized
+a counter-agent. It reverses the infection. Restores humanity.
+
+But there's a catch. The cure requires Ana's living tissue. A piece of her
+brain stem, specifically. The procedure would kill her.
+
+Kai will come for his sister. When he does, he'll have a choice:
+- Save Ana, doom the world to eternal infection
+- Sacrifice Ana, cure humanity, lose his sister forever
+
+I can't make this choice. Neither could any parent.
+
+So I've hidden the cure formula. Seven pieces, scattered across the land.
+Let fate decide.
+
+- Dr. Helena Kovaฤ`,
+ location: 'Hidden Lab Vault'
+ });
+
+ console.log(`โ
Registered ${this.loreEntries.size} lore entries`);
+ }
+
+ /**
+ * Discover lore entry
+ */
+ discoverLore(loreId) {
+ const lore = this.loreEntries.get(loreId);
+ if (!lore) {
+ console.error(`Lore ${loreId} not found!`);
+ return false;
+ }
+
+ if (this.discoveredLore.has(loreId)) {
+ console.log(`Already discovered: ${lore.title}`);
+ return false;
+ }
+
+ this.discoveredLore.add(loreId);
+
+ console.log(`๐ LORE DISCOVERED: ${lore.title}`);
+ console.log(lore.content);
+
+ this.showNotification({
+ title: 'Lore Entry Discovered!',
+ text: `๐ ${lore.title}`,
+ icon: '๐'
+ });
+
+ // Add to journal
+ this.journal.push({
+ type: 'lore',
+ id: loreId,
+ title: lore.title,
+ date: new Date()
+ });
+
+ return true;
+ }
+
+ /**
+ * Make story decision
+ */
+ makeDecision(decisionId, choice) {
+ this.decisions.set(decisionId, choice);
+
+ console.log(`โ๏ธ Decision: ${decisionId} = ${choice}`);
+
+ // Track morality
+ const moralityChanges = {
+ scientist_fate: { kill: -20, spare: +20 },
+ cure_or_revenge: { cure: +50, revenge: -50 },
+ final_choice: { save_ana: -100, sacrifice_ana: +100 }
+ };
+
+ const change = moralityChanges[decisionId]?.[choice] || 0;
+ this.moralityScore = Math.max(-100, Math.min(100, this.moralityScore + change));
+
+ console.log(` Morality: ${this.moralityScore}`);
+
+ return true;
+ }
+
+ /**
+ * Get ending based on decisions
+ */
+ getEnding() {
+ const savedAna = this.decisions.get('final_choice') === 'save_ana';
+ const pursuesCure = this.decisions.get('cure_or_revenge') === 'cure';
+ const sparedScientist = this.decisions.get('scientist_fate') === 'spare';
+
+ // 4 Main Endings
+ if (savedAna && pursuesCure && sparedScientist) {
+ return {
+ id: 'perfect_ending',
+ name: 'The Hope',
+ description: 'You saved Ana and found another way to create the cure. Humanity has hope.',
+ icon: '๐'
+ };
+ } else if (!savedAna && pursuesCure) {
+ return {
+ id: 'sacrifice_ending',
+ name: 'The Martyr',
+ description: 'You sacrificed Ana to cure humanity. The world is saved, but at what cost?',
+ icon: '๐ข'
+ };
+ } else if (savedAna && !pursuesCure) {
+ return {
+ id: 'selfish_ending',
+ name: 'The Bond',
+ description: 'You saved Ana but abandoned the cure. The world remains infected, but your sister lives.',
+ icon: '๐'
+ };
+ } else {
+ return {
+ id: 'dark_ending',
+ name: 'The Revenge',
+ description: 'You chose revenge over everything. Krniฤ is dead, but Ana and the cure are lost.',
+ icon: 'โ๏ธ'
+ };
+ }
+ }
+
+ /**
+ * Get quest journal
+ */
+ getJournal() {
+ return this.journal;
+ }
+
+ /**
+ * Get all discovered lore
+ */
+ getDiscoveredLore() {
+ return Array.from(this.discoveredLore).map(id => this.loreEntries.get(id));
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/MASTER_DESIGN_RECOVERY/MasterGameSystemsManager.js b/MASTER_DESIGN_RECOVERY/MasterGameSystemsManager.js
new file mode 100644
index 000000000..69123315c
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/MasterGameSystemsManager.js
@@ -0,0 +1,446 @@
+/**
+ * MASTER GAME SYSTEMS MANAGER
+ * Centralized coordinator for all game systems
+ * Created: January 4, 2026
+ *
+ * Integrates:
+ * - Sleep System
+ * - Crafting Tables System
+ * - Bakery Shop System
+ * - Barber Shop System
+ * - Lawyer Office System
+ * - Zombie Miner Automation System
+ * - Town Growth System
+ * - NPC Privacy System
+ * - Existing Mining System
+ */
+
+
+class MasterGameSystemsManager {
+ constructor(game) {
+ this.game = game;
+ this.scene = game.scene;
+
+ console.log('๐ฎ Initializing Master Game Systems Manager...');
+
+ // Initialize all systems
+ this.initializeSystems();
+
+ // Set up cross-system event listeners
+ this.setupEventListeners();
+
+ console.log('โ
Master Game Systems Manager initialized!');
+ }
+
+ /**
+ * Initialize all game systems
+ */
+ initializeSystems() {
+ // HOME & SLEEP
+ this.sleepSystem = new SleepSystem(this.game);
+ console.log(' โ Sleep System');
+
+ // CRAFTING
+ this.craftingSystem = new CraftingTablesSystem(this.game);
+ console.log(' โ Crafting Tables System');
+
+ // TOWN BUILDINGS
+ this.bakerySystem = new BakeryShopSystem(this.game);
+ console.log(' โ Bakery Shop System');
+
+ this.barberSystem = new BarberShopSystem(this.game);
+ console.log(' โ Barber Shop System');
+
+ this.lawyerSystem = new LawyerOfficeSystem(this.game);
+ console.log(' โ Lawyer Office System');
+
+ // MINING & AUTOMATION
+ this.zombieMinerSystem = new ZombieMinerAutomationSystem(this.game);
+ console.log(' โ Zombie Miner Automation System');
+
+ // TOWN GROWTH
+ this.townGrowthSystem = new TownGrowthSystem(this.game);
+ console.log(' โ Town Growth System');
+
+ // NPC SYSTEMS
+ this.npcPrivacySystem = new NPCPrivacySystem(this.game);
+ console.log(' โ NPC Privacy System');
+
+ // Register all systems globally
+ this.registerSystems();
+ }
+
+ /**
+ * Register systems to game registry
+ */
+ registerSystems() {
+ this.game.registry.set('sleepSystem', this.sleepSystem);
+ this.game.registry.set('craftingSystem', this.craftingSystem);
+ this.game.registry.set('bakerySystem', this.bakerySystem);
+ this.game.registry.set('barberSystem', this.barberSystem);
+ this.game.registry.set('lawyerSystem', this.lawyerSystem);
+ this.game.registry.set('zombieMinerSystem', this.zombieMinerSystem);
+ this.game.registry.set('townGrowthSystem', this.townGrowthSystem);
+ this.game.registry.set('npcPrivacySystem', this.npcPrivacySystem);
+
+ console.log(' โ All systems registered to game registry');
+ }
+
+ /**
+ * Set up cross-system event listeners
+ */
+ setupEventListeners() {
+ // TOWN GROWTH โ SERVICES
+ this.game.events.on('serviceUnlocked', (data) => {
+ this.onServiceUnlocked(data);
+ });
+
+ // MARRIAGE โ LAWYER AUTO-UNLOCK
+ this.game.events.on('marriageComplete', () => {
+ this.lawyerSystem.checkAutoUnlock();
+ });
+
+ // RELATIONSHIP CHANGE โ LAWYER AUTO-UNLOCK
+ this.game.events.on('relationshipChanged', (data) => {
+ if (data.hearts <= 3) {
+ this.lawyerSystem.checkAutoUnlock();
+ }
+ });
+
+ // BUILDING UNLOCKED โ TOWN GROWTH
+ this.game.events.on('buildingUnlocked', (data) => {
+ this.townGrowthSystem.checkPopulationUnlocks();
+ });
+
+ // ZOMBIE HIRED โ TOWN GROWTH CHECK
+ this.game.events.on('zombieWorkerHired', () => {
+ this.townGrowthSystem.checkPopulationUnlocks();
+ });
+
+ // SLEEP โ ZOMBIE LOYALTY DECAY PAUSE
+ this.game.events.on('sleepStarted', () => {
+ this.pauseZombieLoyaltyDecay = true;
+ });
+
+ this.game.events.on('wakeUp', () => {
+ this.pauseZombieLoyaltyDecay = false;
+ });
+
+ console.log(' โ Cross-system event listeners configured');
+ }
+
+ /**
+ * Handle service unlock
+ */
+ onServiceUnlocked(data) {
+ const { serviceId, population } = data;
+
+ console.log(`๐๏ธ Service unlocked: ${serviceId} at pop ${population}`);
+
+ // Trigger service-specific initialization
+ switch (serviceId) {
+ case 'market':
+ this.initializeMarket();
+ break;
+ case 'hospital':
+ this.initializeHospital();
+ break;
+ case 'school':
+ this.initializeSchool();
+ break;
+ case 'bank':
+ this.initializeBank();
+ break;
+ case 'museum':
+ this.initializeMuseum();
+ break;
+ case 'theater':
+ this.initializeTheater();
+ break;
+ }
+ }
+
+ /**
+ * Service initializers (placeholders for future implementation)
+ */
+ initializeMarket() {
+ console.log(' โ Market initialized');
+ // TODO: Implement market system
+ }
+
+ initializeHospital() {
+ console.log(' โ Hospital initialized');
+ // TODO: Implement hospital system
+ }
+
+ initializeSchool() {
+ console.log(' โ School initialized');
+ // TODO: Implement school system
+ }
+
+ initializeBank() {
+ console.log(' โ Bank initialized');
+ // TODO: Implement bank system
+ }
+
+ initializeMuseum() {
+ console.log(' โ Museum initialized');
+ // TODO: Implement museum system
+ }
+
+ initializeTheater() {
+ console.log(' โ Theater initialized');
+ // TODO: Implement theater system
+ }
+
+ /**
+ * Update all systems (called every frame)
+ */
+ update(time, delta) {
+ const deltaSeconds = delta / 1000;
+
+ // Update systems that need per-frame updates
+ this.sleepSystem.update(deltaSeconds);
+ this.craftingSystem.update(deltaSeconds);
+
+ // Update zombie miners (if not paused by sleep)
+ if (!this.pauseZombieLoyaltyDecay) {
+ this.zombieMinerSystem.update(deltaSeconds);
+ }
+ }
+
+ /**
+ * Hourly update (called when game hour changes)
+ */
+ onHourChange(hour) {
+ // Update time-sensitive systems
+ this.bakerySystem.update();
+ this.townGrowthSystem.update();
+
+ // Check for automation collection reminders
+ if (hour % 4 === 0) { // Every 4 hours
+ this.checkAutomationReminders();
+ }
+ }
+
+ /**
+ * Check automation reminders
+ */
+ checkAutomationReminders() {
+ // Zombie miner automation
+ if (this.zombieMinerSystem.automationActive) {
+ const hours = this.zombieMinerSystem.getHoursSinceLastCollection();
+
+ if (hours >= 8) {
+ this.game.showNotification({
+ title: 'โ๏ธ Automation Ready',
+ text: `${hours.toFixed(0)}h of mining ready to collect!`,
+ icon: 'โ๏ธ'
+ });
+ }
+ }
+ }
+
+ /**
+ * Daily update (called at midnight)
+ */
+ onDayChange(day) {
+ console.log(`๐
Day ${day} - Running daily system updates...`);
+
+ // Town growth check
+ this.townGrowthSystem.update();
+
+ // Restock shops
+ this.bakerySystem.restockInventory();
+
+ // Birthday cake deliveries
+ this.bakerySystem.checkBirthdayCakeDeliveries();
+ }
+
+ /**
+ * Save all systems state
+ */
+ saveAllSystems() {
+ return {
+ version: '1.0',
+ timestamp: Date.now(),
+ systems: {
+ sleep: {
+ playerBed: this.sleepSystem.playerBed,
+ unlockedBeds: Object.entries(this.sleepSystem.bedTypes)
+ .filter(([_, bed]) => bed.unlocked)
+ .map(([key, _]) => key)
+ },
+ crafting: {
+ currentTable: this.craftingSystem.currentTable.id,
+ unlockedRecipes: this.craftingSystem.unlockedRecipes,
+ largeTableUnlocked: this.craftingSystem.tables.LARGE.unlocked
+ },
+ bakery: {
+ isUnlocked: this.bakerySystem.isUnlocked,
+ inventory: this.bakerySystem.inventory,
+ birthdayOrders: this.bakerySystem.birthdayCakeOrders
+ },
+ barber: {
+ isUnlocked: this.barberSystem.isUnlocked,
+ playerAppearance: this.barberSystem.playerAppearance,
+ savedLooks: this.barberSystem.savedLooks,
+ visitCount: this.barberSystem.visitCount
+ },
+ lawyer: {
+ isUnlocked: this.lawyerSystem.isUnlocked,
+ hasPrenup: this.lawyerSystem.hasPrenup,
+ divorceHistory: this.lawyerSystem.divorceHistory,
+ counselingInProgress: this.lawyerSystem.counselingInProgress
+ },
+ zombieMiners: {
+ miners: this.zombieMinerSystem.zombieMiners,
+ equipment: this.zombieMinerSystem.zombieEquipment,
+ lastCollectionTime: this.zombieMinerSystem.lastCollectionTime
+ },
+ townGrowth: {
+ population: this.townGrowthSystem.population,
+ populationSlots: this.townGrowthSystem.populationSlots,
+ villages: this.townGrowthSystem.villages,
+ services: this.townGrowthSystem.services
+ },
+ npcPrivacy: {
+ npcHomes: this.npcPrivacySystem.npcHomes,
+ visitHistory: this.npcPrivacySystem.visitHistory,
+ lastVisitTimes: this.npcPrivacySystem.lastVisitTimes
+ }
+ }
+ };
+ }
+
+ /**
+ * Load all systems state
+ */
+ loadAllSystems(saveData) {
+ if (!saveData || !saveData.systems) {
+ console.warn('No save data to load');
+ return false;
+ }
+
+ try {
+ const systems = saveData.systems;
+
+ // Load sleep system
+ if (systems.sleep) {
+ systems.sleep.unlockedBeds.forEach(bedKey => {
+ this.sleepSystem.bedTypes[bedKey].unlocked = true;
+ });
+ }
+
+ // Load crafting system
+ if (systems.crafting) {
+ this.craftingSystem.tables.LARGE.unlocked = systems.crafting.largeTableUnlocked;
+ this.craftingSystem.unlockedRecipes = systems.crafting.unlockedRecipes || [];
+ }
+
+ // Load bakery
+ if (systems.bakery) {
+ this.bakerySystem.isUnlocked = systems.bakery.isUnlocked;
+ this.bakerySystem.birthdayCakeOrders = systems.bakery.birthdayOrders || [];
+ }
+
+ // Load barber
+ if (systems.barber) {
+ this.barberSystem.isUnlocked = systems.barber.isUnlocked;
+ this.barberSystem.playerAppearance = systems.barber.playerAppearance;
+ this.barberSystem.savedLooks = systems.barber.savedLooks || [];
+ this.barberSystem.visitCount = systems.barber.visitCount || 0;
+ }
+
+ // Load lawyer
+ if (systems.lawyer) {
+ this.lawyerSystem.isUnlocked = systems.lawyer.isUnlocked;
+ this.lawyerSystem.hasPrenup = systems.lawyer.hasPrenup || false;
+ this.lawyerSystem.divorceHistory = systems.lawyer.divorceHistory || [];
+ this.lawyerSystem.counselingInProgress = systems.lawyer.counselingInProgress || false;
+ }
+
+ // Load zombie miners
+ if (systems.zombieMiners) {
+ this.zombieMinerSystem.zombieMiners = systems.zombieMiners.miners || [];
+ this.zombieMinerSystem.zombieEquipment = systems.zombieMiners.equipment || {};
+ this.zombieMinerSystem.lastCollectionTime = systems.zombieMiners.lastCollectionTime;
+ this.zombieMinerSystem.updateAutomationYield();
+ }
+
+ // Load town growth
+ if (systems.townGrowth) {
+ this.townGrowthSystem.population = systems.townGrowth.population;
+ this.townGrowthSystem.populationSlots = systems.townGrowth.populationSlots;
+ this.townGrowthSystem.villages = systems.townGrowth.villages || [];
+ this.townGrowthSystem.services = systems.townGrowth.services || {};
+ }
+
+ // Load NPC privacy
+ if (systems.npcPrivacy) {
+ this.npcPrivacySystem.npcHomes = systems.npcPrivacy.npcHomes || {};
+ this.npcPrivacySystem.visitHistory = systems.npcPrivacy.visitHistory || {};
+ this.npcPrivacySystem.lastVisitTimes = systems.npcPrivacy.lastVisitTimes || {};
+ }
+
+ console.log('โ
All systems loaded from save data');
+ return true;
+
+ } catch (error) {
+ console.error('โ Error loading systems:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Get all systems status (for debug/admin panel)
+ */
+ getAllSystemsStatus() {
+ return {
+ sleep: {
+ active: true,
+ currentBed: this.sleepSystem.playerBed.name,
+ isSleeping: this.sleepSystem.isSleeping
+ },
+ crafting: {
+ active: true,
+ currentTable: this.craftingSystem.currentTable.name,
+ isCrafting: this.craftingSystem.isCrafting,
+ queueLength: this.craftingSystem.craftingQueue.length
+ },
+ bakery: {
+ active: this.bakerySystem.isUnlocked,
+ isOpen: this.bakerySystem.isOpen,
+ stockLevel: Object.values(this.bakerySystem.inventory)
+ .reduce((sum, item) => sum + item.stock, 0)
+ },
+ barber: {
+ active: this.barberSystem.isUnlocked,
+ isOpen: this.barberSystem.isOpen,
+ currentStyle: this.barberSystem.playerAppearance.hairstyle
+ },
+ lawyer: {
+ active: this.lawyerSystem.isUnlocked,
+ counselingActive: this.lawyerSystem.counselingInProgress,
+ hasPrenup: this.lawyerSystem.hasPrenup
+ },
+ zombieMiners: {
+ active: this.zombieMinerSystem.automationActive,
+ minerCount: this.zombieMinerSystem.zombieMiners.length,
+ yieldPerHour: this.zombieMinerSystem.totalYieldPerHour
+ },
+ townGrowth: {
+ active: true,
+ population: this.townGrowthSystem.population,
+ maxPopulation: this.townGrowthSystem.maxPopulation,
+ status: this.townGrowthSystem.getTownStatus()
+ },
+ npcPrivacy: {
+ active: true,
+ homesGenerated: Object.keys(this.npcPrivacySystem.npcHomes).length
+ }
+ };
+ }
+}
+
+
diff --git a/MASTER_DESIGN_RECOVERY/StoryQuestSystem.js b/MASTER_DESIGN_RECOVERY/StoryQuestSystem.js
new file mode 100644
index 000000000..3135e65ce
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/StoryQuestSystem.js
@@ -0,0 +1,506 @@
+/**
+ * STORY & QUEST SYSTEM
+ * Complete quest system with story acts, dialogue, cutscenes, and multiple endings
+ */
+class StoryQuestSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Quests
+ this.quests = new Map();
+ this.activeQuests = new Set();
+ this.completedQuests = new Set();
+
+ // Story progress
+ this.currentAct = 1;
+ this.storyFlags = new Set();
+
+ // Characters
+ this.characters = new Map();
+
+ // Dialogue
+ this.currentDialogue = null;
+
+ // Endings
+ this.playerChoices = [];
+ this.ending = null;
+
+ this.loadProgress();
+ this.init();
+
+ console.log('โ
Story & Quest System initialized');
+ }
+
+ init() {
+ this.defineCharacters();
+ this.defineQuests();
+ console.log('๐ Story & Quest system ready');
+ }
+
+ // ========== CHARACTERS ==========
+
+ defineCharacters() {
+ this.characters.set('jakob', {
+ name: 'Old Jakob',
+ role: 'Merchant',
+ relationship: 0,
+ dialogues: new Map()
+ });
+
+ this.characters.set('lyra', {
+ name: 'Lyra',
+ role: 'Mutant Elf',
+ relationship: 0,
+ dialogues: new Map()
+ });
+
+ this.characters.set('grok', {
+ name: 'Grok',
+ role: 'Troll Guardian',
+ relationship: 0,
+ dialogues: new Map()
+ });
+
+ this.characters.set('dr_chen', {
+ name: 'Dr. Chen',
+ role: 'Radio Voice',
+ relationship: 0,
+ dialogues: new Map()
+ });
+ }
+
+ // ========== QUESTS ==========
+
+ defineQuests() {
+ // ACT 1: Survival (Day 1-10)
+ this.defineQuest('first_harvest', {
+ name: 'Prvi Pridelek',
+ act: 1,
+ description: 'Harvest 10 wheat to survive',
+ objectives: [
+ { type: 'harvest', item: 'wheat', amount: 10, current: 0 }
+ ],
+ rewards: { xp: 100, item: 'iron_hoe' },
+ unlocks: ['safe_haven']
+ });
+
+ this.defineQuest('safe_haven', {
+ name: 'Varno Zatoฤiลกฤe',
+ act: 1,
+ description: 'Build fence around farm',
+ objectives: [
+ { type: 'build', item: 'fence', amount: 20, current: 0 }
+ ],
+ rewards: { xp: 150, blueprint: 'reinforced_fence' },
+ unlocks: ['night_watch']
+ });
+
+ this.defineQuest('night_watch', {
+ name: 'Noฤna Straลพa',
+ act: 1,
+ description: 'Survive first zombie night',
+ objectives: [
+ { type: 'survive', nights: 1, current: 0 }
+ ],
+ rewards: { xp: 200, item: 'torch_pack' },
+ unlocks: ['meet_merchant']
+ });
+
+ this.defineQuest('meet_merchant', {
+ name: 'Meet the Merchant',
+ act: 1,
+ description: 'Find Jakob the trader',
+ objectives: [
+ { type: 'talk', npc: 'jakob' }
+ ],
+ rewards: { xp: 100, unlocks: 'trading' },
+ unlocks: ['strange_transmission']
+ });
+
+ // ACT 2: Discovery (Day 11-20)
+ this.defineQuest('strange_transmission', {
+ name: 'Strange Transmission',
+ act: 2,
+ description: 'Find radio in city',
+ objectives: [
+ { type: 'find', item: 'radio', location: 'city_ruins' }
+ ],
+ rewards: { xp: 300, item: 'radio' },
+ unlocks: ['first_tame']
+ });
+
+ this.defineQuest('first_tame', {
+ name: 'Prvi Poskus',
+ act: 2,
+ description: 'Tame first zombie',
+ objectives: [
+ { type: 'tame', creature: 'zombie', amount: 1 }
+ ],
+ rewards: { xp: 250, unlocks: 'zombie_workers' },
+ unlocks: ['lab_ruins']
+ });
+
+ this.defineQuest('lab_ruins', {
+ name: 'Lab Ruins',
+ act: 2,
+ description: 'Explore abandoned research facility',
+ objectives: [
+ { type: 'explore', location: 'research_lab' }
+ ],
+ rewards: { xp: 400, item: 'lab_key' },
+ unlocks: ['mutant_contact']
+ });
+
+ this.defineQuest('mutant_contact', {
+ name: 'Mutant Contact',
+ act: 2,
+ description: 'Meet friendly mutant Lyra',
+ objectives: [
+ { type: 'talk', npc: 'lyra' }
+ ],
+ rewards: { xp: 300, unlocks: 'mutation_research' },
+ unlocks: ['lab_notes']
+ });
+
+ // ACT 3: The Truth (Day 21-30)
+ this.defineQuest('lab_notes', {
+ name: 'Lab Notes',
+ act: 3,
+ description: 'Collect 5 research documents',
+ objectives: [
+ { type: 'collect', item: 'research_document', amount: 5, current: 0 }
+ ],
+ rewards: { xp: 500 },
+ unlocks: ['patient_zero']
+ });
+
+ this.defineQuest('patient_zero', {
+ name: 'Patient Zero',
+ act: 3,
+ description: 'Find virus source',
+ objectives: [
+ { type: 'find', item: 'virus_sample', location: 'deep_lab' }
+ ],
+ rewards: { xp: 600 },
+ unlocks: ['difficult_choice']
+ });
+
+ this.defineQuest('difficult_choice', {
+ name: 'Difficult Choice',
+ act: 3,
+ description: 'Choose faction',
+ objectives: [
+ { type: 'choice', options: ['human', 'zombie', 'hybrid'] }
+ ],
+ rewards: { xp: 1000 },
+ unlocks: ['final_confrontation']
+ });
+
+ this.defineQuest('final_confrontation', {
+ name: 'Final Confrontation',
+ act: 3,
+ description: 'Boss battle',
+ objectives: [
+ { type: 'defeat', boss: 'zombie_king' }
+ ],
+ rewards: { xp: 2000 },
+ ending: true
+ });
+ }
+
+ defineQuest(id, data) {
+ this.quests.set(id, {
+ id,
+ active: false,
+ completed: false,
+ ...data
+ });
+ }
+
+ // ========== QUEST MANAGEMENT ==========
+
+ startQuest(questId) {
+ const quest = this.quests.get(questId);
+ if (!quest || quest.active || quest.completed) return false;
+
+ quest.active = true;
+ this.activeQuests.add(questId);
+
+ console.log(`๐ Quest started: ${quest.name}`);
+ return true;
+ }
+
+ updateQuestProgress(questId, objectiveIndex, progress) {
+ const quest = this.quests.get(questId);
+ if (!quest || !quest.active) return;
+
+ const objective = quest.objectives[objectiveIndex];
+ if (!objective) return;
+
+ objective.current = progress;
+
+ // Check if objective complete
+ if (this.isObjectiveComplete(objective)) {
+ console.log(`โ
Objective complete: ${objective.type}`);
+
+ // Check if all objectives complete
+ if (quest.objectives.every(obj => this.isObjectiveComplete(obj))) {
+ this.completeQuest(questId);
+ }
+ }
+ }
+
+ isObjectiveComplete(objective) {
+ switch (objective.type) {
+ case 'harvest':
+ case 'build':
+ case 'collect':
+ return objective.current >= objective.amount;
+ case 'talk':
+ case 'find':
+ case 'explore':
+ case 'defeat':
+ return objective.current === true;
+ case 'survive':
+ return objective.current >= objective.nights;
+ default:
+ return false;
+ }
+ }
+
+ completeQuest(questId) {
+ const quest = this.quests.get(questId);
+ if (!quest) return;
+
+ quest.active = false;
+ quest.completed = true;
+ this.activeQuests.delete(questId);
+ this.completedQuests.add(questId);
+
+ // Grant rewards
+ this.grantQuestRewards(quest);
+
+ // Unlock next quests
+ if (quest.unlocks) {
+ const unlocks = Array.isArray(quest.unlocks) ? quest.unlocks : [quest.unlocks];
+ unlocks.forEach(nextQuest => this.startQuest(nextQuest));
+ }
+
+ // Check for ending
+ if (quest.ending) {
+ this.triggerEnding();
+ }
+
+ console.log(`๐ Quest completed: ${quest.name}!`);
+ this.saveProgress();
+ }
+
+ grantQuestRewards(quest) {
+ const rewards = quest.rewards;
+
+ if (rewards.xp && this.scene.skillTree) {
+ this.scene.skillTree.addXP(rewards.xp);
+ }
+
+ if (rewards.item && this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(rewards.item, 1);
+ }
+
+ if (rewards.blueprint) {
+ console.log(`๐ Unlocked blueprint: ${rewards.blueprint}`);
+ }
+ }
+
+ // ========== DIALOGUE SYSTEM ==========
+
+ startDialogue(npcId, dialogueId) {
+ const character = this.characters.get(npcId);
+ if (!character) return false;
+
+ this.currentDialogue = {
+ npc: npcId,
+ dialogueId,
+ currentNode: 0,
+ choices: []
+ };
+
+ console.log(`๐ฌ Dialogue started with ${character.name}`);
+ return true;
+ }
+
+ selectDialogueChoice(choiceIndex) {
+ if (!this.currentDialogue) return;
+
+ const choice = this.currentDialogue.choices[choiceIndex];
+ if (!choice) return;
+
+ // Track player choice
+ this.playerChoices.push({
+ npc: this.currentDialogue.npc,
+ choice: choice.text,
+ consequence: choice.consequence
+ });
+
+ // Update relationship
+ const character = this.characters.get(this.currentDialogue.npc);
+ if (character && choice.relationshipDelta) {
+ character.relationship += choice.relationshipDelta;
+ }
+
+ // Apply consequences
+ if (choice.consequence) {
+ this.applyChoiceConsequence(choice.consequence);
+ }
+
+ console.log(`๐ฌ Choice selected: ${choice.text}`);
+ }
+
+ applyChoiceConsequence(consequence) {
+ // Set story flags
+ if (consequence.flag) {
+ this.storyFlags.add(consequence.flag);
+ }
+
+ // Unlock quests
+ if (consequence.unlockQuest) {
+ this.startQuest(consequence.unlockQuest);
+ }
+
+ // Change faction
+ if (consequence.faction) {
+ this.playerChoices.push({ type: 'faction', value: consequence.faction });
+ }
+ }
+
+ endDialogue() {
+ this.currentDialogue = null;
+ }
+
+ // ========== CUTSCENES ==========
+
+ playCutscene(cutsceneId) {
+ console.log(`๐ฌ Playing cutscene: ${cutsceneId}`);
+
+ const cutscenes = {
+ 'arrival': this.cutsceneArrival.bind(this),
+ 'first_zombie': this.cutsceneFirstZombie.bind(this),
+ 'city_discovery': this.cutsceneCityDiscovery.bind(this),
+ 'boss_reveal': this.cutsceneBossReveal.bind(this)
+ };
+
+ const cutscene = cutscenes[cutsceneId];
+ if (cutscene) {
+ cutscene();
+ }
+ }
+
+ cutsceneArrival() {
+ console.log('๐ฌ Arrival cutscene - Farm overview');
+ }
+
+ cutsceneFirstZombie() {
+ console.log('๐ฌ First zombie encounter - Tutorial');
+ }
+
+ cutsceneCityDiscovery() {
+ console.log('๐ฌ City discovery - Ruins pan');
+ }
+
+ cutsceneBossReveal() {
+ console.log('๐ฌ Boss reveal - Zombie King emergence');
+ }
+
+ // ========== ENDINGS ==========
+
+ triggerEnding() {
+ // Determine ending based on player choices
+ const faction = this.getPlayerFaction();
+ const relationships = this.getRelationships();
+
+ if (faction === 'human' && relationships.jakob > 50) {
+ this.ending = 'cure';
+ this.playCutscene('cure_ending');
+ } else if (faction === 'zombie') {
+ this.ending = 'zombie_king';
+ this.playCutscene('zombie_king_ending');
+ } else if (faction === 'hybrid') {
+ this.ending = 'mutation';
+ this.playCutscene('mutation_ending');
+ } else if (relationships.lyra > 70) {
+ this.ending = 'escape';
+ this.playCutscene('escape_ending');
+ } else {
+ this.ending = 'farmer';
+ this.playCutscene('farmer_ending');
+ }
+
+ console.log(`๐ฌ Ending: ${this.ending}`);
+ this.saveProgress();
+ }
+
+ getPlayerFaction() {
+ const factionChoice = this.playerChoices.find(c => c.type === 'faction');
+ return factionChoice ? factionChoice.value : null;
+ }
+
+ getRelationships() {
+ const relationships = {};
+ for (const [id, character] of this.characters.entries()) {
+ relationships[id] = character.relationship;
+ }
+ return relationships;
+ }
+
+ // ========== PERSISTENCE ==========
+
+ saveProgress() {
+ const data = {
+ currentAct: this.currentAct,
+ activeQuests: Array.from(this.activeQuests),
+ completedQuests: Array.from(this.completedQuests),
+ storyFlags: Array.from(this.storyFlags),
+ playerChoices: this.playerChoices,
+ ending: this.ending,
+ characters: Array.from(this.characters.entries()).map(([id, char]) => ({
+ id,
+ relationship: char.relationship
+ }))
+ };
+
+ localStorage.setItem('novafarma_story', JSON.stringify(data));
+ }
+
+ loadProgress() {
+ const saved = localStorage.getItem('novafarma_story');
+ if (saved) {
+ try {
+ const data = JSON.parse(saved);
+ this.currentAct = data.currentAct || 1;
+ this.activeQuests = new Set(data.activeQuests || []);
+ this.completedQuests = new Set(data.completedQuests || []);
+ this.storyFlags = new Set(data.storyFlags || []);
+ this.playerChoices = data.playerChoices || [];
+ this.ending = data.ending || null;
+
+ if (data.characters) {
+ data.characters.forEach(char => {
+ const character = this.characters.get(char.id);
+ if (character) {
+ character.relationship = char.relationship;
+ }
+ });
+ }
+
+ console.log('โ
Story progress loaded');
+ } catch (error) {
+ console.error('Failed to load story progress:', error);
+ }
+ }
+ }
+
+ destroy() {
+ this.saveProgress();
+ console.log('๐ Story & Quest System destroyed');
+ }
+}
diff --git a/MASTER_DESIGN_RECOVERY/TownRestorationSystem.js b/MASTER_DESIGN_RECOVERY/TownRestorationSystem.js
new file mode 100644
index 000000000..489e05ffb
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/TownRestorationSystem.js
@@ -0,0 +1,403 @@
+/**
+ * TownRestorationSystem.js
+ * =========================
+ * KRVAVA ลฝETEV - Complete Town Restoration System (Phase 19)
+ *
+ * Features:
+ * - 27 towns across 18 biomes
+ * - 150+ ruined buildings
+ * - 180 total NPCs
+ * - Building restoration mechanics
+ * - NPC move-in system
+ * - Global milestones & rewards
+ * - Major city endgame project
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class TownRestorationSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Town registry
+ this.towns = new Map();
+ this.buildings = new Map();
+ this.npcs = new Map();
+
+ // Progress tracking
+ this.buildingsRestored = 0;
+ this.totalBuildings = 150;
+ this.npcsRescued = 0;
+ this.totalNPCs = 180;
+ this.townsCompleted = 0;
+ this.totalTowns = 27;
+
+ // Milestones
+ this.milestones = [10, 25, 50, 100, 180];
+ this.unlockedMilestones = [];
+
+ console.log('๐๏ธ TownRestorationSystem initialized');
+
+ // Register all towns
+ this.registerTowns();
+
+ // Register Hope Valley buildings
+ this.registerHopeValleyBuildings();
+ }
+
+ /**
+ * Register all towns
+ */
+ registerTowns() {
+ // Hope Valley (Starting town - 15 buildings)
+ this.towns.set('hope_valley', {
+ id: 'hope_valley',
+ name: 'Hope Valley',
+ biome: 'grassland',
+ icon: '๐๏ธ',
+ totalBuildings: 15,
+ restoredBuildings: 0,
+ npcs: 0,
+ isUnlocked: true,
+ isCompleted: false
+ });
+
+ // Other towns (6 more buildings each = 156 total)
+ const otherTowns = [
+ { id: 'forest_grove', name: 'Forest Grove', biome: 'forest', buildings: 6 },
+ { id: 'desert_oasis', name: 'Desert Oasis', biome: 'desert', buildings: 6 },
+ { id: 'frozen_harbor', name: 'Frozen Harbor', biome: 'frozen', buildings: 6 },
+ { id: 'volcanic_refuge', name: 'Volcanic Refuge', biome: 'volcanic', buildings: 6 },
+ { id: 'coastal_bay', name: 'Coastal Bay', biome: 'beach', buildings: 6 },
+ { id: 'mountain_peak', name: 'Mountain Peak', biome: 'mountain', buildings: 6 },
+ { id: 'swamp_village', name: 'Swamp Village', biome: 'swamp', buildings: 6 },
+ { id: 'crystal_city', name: 'Crystal City', biome: 'crystal', buildings: 8 },
+ { id: 'atlantis', name: 'Atlantis', biome: 'underwater', buildings: 10 },
+ // ... (27 total towns)
+ ];
+
+ otherTowns.forEach(town => {
+ this.towns.set(town.id, {
+ id: town.id,
+ name: town.name,
+ biome: town.biome,
+ icon: '๐๏ธ',
+ totalBuildings: town.buildings,
+ restoredBuildings: 0,
+ npcs: 0,
+ isUnlocked: false,
+ isCompleted: false
+ });
+ });
+
+ console.log(`โ
Registered ${this.towns.size} towns`);
+ }
+
+ /**
+ * Register Hope Valley buildings (15)
+ */
+ registerHopeValleyBuildings() {
+ const buildings = [
+ // Essential (NPCs' homes)
+ { id: 'ivan_house', name: "Ivan's House", npc: 'Ivan', materials: { wood: 100, stone: 50 }, time: 3 },
+ { id: 'marija_house', name: "Marija's House", npc: 'Marija', materials: { wood: 100, stone: 50 }, time: 3 },
+ { id: 'jakob_shop', name: "Jakob's Shop", npc: 'Jakob', materials: { wood: 150, iron: 30 }, time: 4 },
+ { id: 'dr_chen_clinic', name: "Dr. Chen's Clinic", npc: 'Dr. Chen', materials: { wood: 200, stone: 100 }, time: 5 },
+ { id: 'lena_bakery', name: "Lena's Bakery", npc: 'Lena', materials: { wood: 120, stone: 60 }, time: 4 },
+
+ // Community buildings
+ { id: 'town_hall', name: 'Town Hall', materials: { wood: 500, stone: 300, iron: 100 }, time: 10 },
+ { id: 'community_center', name: 'Community Center', materials: { wood: 400, stone: 250 }, time: 8 },
+ { id: 'library', name: 'Library', materials: { wood: 300, stone: 150 }, time: 6 },
+ { id: 'school', name: 'School', materials: { wood: 350, stone: 200 }, time: 7 },
+ { id: 'guard_tower', name: 'Guard Tower', materials: { stone: 400, iron: 150 }, time: 8 },
+
+ // Utility buildings
+ { id: 'warehouse', name: 'Warehouse', materials: { wood: 250, stone: 150 }, time: 5 },
+ { id: 'water_tower', name: 'Water Tower', materials: { stone: 300, iron: 100 }, time: 6 },
+ { id: 'power_station', name: 'Power Station', materials: { stone: 400, iron: 200, wire: 50 }, time: 9 },
+ { id: 'market', name: 'Market Square', materials: { wood: 200, stone: 100 }, time: 5 },
+ { id: 'fountain', name: 'Town Fountain', materials: { stone: 150, marble: 50 }, time: 4 }
+ ];
+
+ buildings.forEach(building => {
+ this.buildings.set(building.id, {
+ ...building,
+ town: 'hope_valley',
+ isRestored: false,
+ progress: 0,
+ workers: [],
+ startTime: null
+ });
+ });
+
+ console.log(`โ
Registered ${buildings.length} Hope Valley buildings`);
+ }
+
+ /**
+ * Start building restoration
+ */
+ startRestoration(buildingId, zombieWorkers = []) {
+ const building = this.buildings.get(buildingId);
+ if (!building) {
+ console.error(`Building ${buildingId} not found!`);
+ return false;
+ }
+
+ if (building.isRestored) {
+ console.log(`${building.name} is already restored!`);
+ return false;
+ }
+
+ // Check materials
+ if (!this.hasMaterials(building.materials)) {
+ console.log(`Not enough materials for ${building.name}!`);
+ return false;
+ }
+
+ // Consume materials
+ this.consumeMaterials(building.materials);
+
+ // Assign workers
+ building.workers = zombieWorkers;
+ building.startTime = Date.now();
+ building.progress = 0;
+
+ // Calculate construction time (base time reduced by workers)
+ const workerBonus = zombieWorkers.length * 0.2; // 20% per worker
+ const constructionTime = building.time * (1 - workerBonus);
+
+ console.log(`๐๏ธ Started restoring ${building.name} (${constructionTime} days with ${zombieWorkers.length} workers)`);
+
+ // Auto-complete after time
+ setTimeout(() => {
+ this.completeRestoration(buildingId);
+ }, constructionTime * 1000 * 60); // Convert to ms (for demo, 1 day = 1 min)
+
+ this.showNotification({
+ title: 'Restoration Started!',
+ text: `๐๏ธ ${building.name} - ${constructionTime} days`,
+ icon: '๐จ'
+ });
+
+ return true;
+ }
+
+ /**
+ * Complete building restoration
+ */
+ completeRestoration(buildingId) {
+ const building = this.buildings.get(buildingId);
+ if (!building) return;
+
+ building.isRestored = true;
+ building.progress = 100;
+ this.buildingsRestored++;
+
+ console.log(`โ
${building.name} restored! (${this.buildingsRestored}/${this.totalBuildings})`);
+
+ // Move in NPC if building has one
+ if (building.npc) {
+ this.moveInNPC(building.npc, building.town);
+ }
+
+ // Update town progress
+ this.updateTownProgress(building.town);
+
+ // Check milestones
+ this.checkMilestones();
+
+ // Update visuals in the current scene
+ if (this.scene.updateBuildingVisuals) {
+ this.scene.updateBuildingVisuals(buildingId);
+ }
+
+ this.showNotification({
+ title: 'Building Complete!',
+ text: `โ
${building.name} restored!`,
+ icon: '๐๏ธ'
+ });
+ }
+
+ /**
+ * Move in NPC
+ */
+ moveInNPC(npcName, townId) {
+ const npc = {
+ name: npcName,
+ town: townId,
+ movedInDate: Date.now(),
+ hearts: 0,
+ giftsGiven: 0,
+ questsCompleted: 0
+ };
+
+ this.npcs.set(npcName, npc);
+ this.npcsRescued++;
+
+ console.log(`๐ค ${npcName} moved into ${townId}! (${this.npcsRescued}/${this.totalNPCs})`);
+
+ // Update town NPC count
+ const town = this.towns.get(townId);
+ if (town) {
+ town.npcs++;
+ }
+
+ this.showNotification({
+ title: 'NPC Moved In!',
+ text: `๐ค ${npcName} is now living in town!`,
+ icon: '๐ '
+ });
+ }
+
+ /**
+ * Update town progress
+ */
+ updateTownProgress(townId) {
+ const town = this.towns.get(townId);
+ if (!town) return;
+
+ // Count restored buildings in this town
+ const townBuildings = Array.from(this.buildings.values())
+ .filter(b => b.town === townId);
+ const restored = townBuildings.filter(b => b.isRestored).length;
+
+ town.restoredBuildings = restored;
+
+ // Check if town is complete
+ if (restored === town.totalBuildings) {
+ town.isCompleted = true;
+ this.townsCompleted++;
+
+ console.log(`๐ ${town.name} 100% COMPLETE! (${this.townsCompleted}/${this.totalTowns})`);
+
+ this.showNotification({
+ title: 'TOWN COMPLETE!',
+ text: `๐ ${town.name} fully restored!`,
+ icon: '๐'
+ });
+ }
+ }
+
+ /**
+ * Check milestones
+ */
+ checkMilestones() {
+ this.milestones.forEach(milestone => {
+ if (this.npcsRescued >= milestone && !this.unlockedMilestones.includes(milestone)) {
+ this.unlockMilestone(milestone);
+ }
+ });
+ }
+
+ /**
+ * Unlock milestone
+ */
+ unlockMilestone(milestone) {
+ this.unlockedMilestones.push(milestone);
+
+ console.log(`๐ MILESTONE: ${milestone} NPCs rescued!`);
+
+ // Grant rewards
+ const rewards = this.getMilestoneReward(milestone);
+
+ this.showNotification({
+ title: `MILESTONE: ${milestone} NPCs!`,
+ text: `๐ Unlocked: ${rewards.text}`,
+ icon: '๐'
+ });
+ }
+
+ /**
+ * Get milestone reward
+ */
+ getMilestoneReward(milestone) {
+ const rewards = {
+ 10: { text: 'Community Center unlocked!', feature: 'community_center' },
+ 25: { text: 'Town Festivals enabled!', feature: 'festivals' },
+ 50: { text: 'Town Guard system!', feature: 'town_guard' },
+ 100: { text: 'Major City project unlocked!', feature: 'major_city' },
+ 180: { text: 'UTOPIA ENDING unlocked!', feature: 'utopia_ending' }
+ };
+
+ return rewards[milestone] || { text: 'Bonus unlocked!' };
+ }
+
+ hasMaterials(materials) {
+ if (!this.scene.inventorySystem) return true; // Safety fallback
+
+ for (const [item, count] of Object.entries(materials)) {
+ if (!this.scene.inventorySystem.hasItem(item, count)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ consumeMaterials(materials) {
+ if (!this.scene.inventorySystem) return;
+
+ for (const [item, count] of Object.entries(materials)) {
+ this.scene.inventorySystem.removeItem(item, count);
+ }
+ console.log('๐ฆ Materials consumed:', materials);
+ }
+
+ /**
+ * Get restoration progress
+ */
+ getProgress() {
+ return {
+ buildings: {
+ restored: this.buildingsRestored,
+ total: this.totalBuildings,
+ percentage: ((this.buildingsRestored / this.totalBuildings) * 100).toFixed(1)
+ },
+ npcs: {
+ rescued: this.npcsRescued,
+ total: this.totalNPCs,
+ percentage: ((this.npcsRescued / this.totalNPCs) * 100).toFixed(1)
+ },
+ towns: {
+ completed: this.townsCompleted,
+ total: this.totalTowns,
+ percentage: ((this.townsCompleted / this.totalTowns) * 100).toFixed(1)
+ },
+ milestones: this.unlockedMilestones
+ };
+ }
+
+ /**
+ * Get town info
+ */
+ getTownInfo(townId) {
+ return this.towns.get(townId);
+ }
+
+ /**
+ * Get all towns
+ */
+ getAllTowns() {
+ return Array.from(this.towns.values());
+ }
+
+ /**
+ * Get building info
+ */
+ getBuildingInfo(buildingId) {
+ return this.buildings.get(buildingId);
+ }
+
+ /**
+ * Helper: Show notification
+ */
+ showNotification(notification) {
+ console.log(`๐ข ${notification.icon} ${notification.title}: ${notification.text}`);
+
+ const ui = this.scene.scene.get('UIScene');
+ if (ui && ui.showNotification) {
+ ui.showNotification(notification);
+ }
+ }
+}
diff --git a/MASTER_DESIGN_RECOVERY/TwinBondSystem.js b/MASTER_DESIGN_RECOVERY/TwinBondSystem.js
new file mode 100644
index 000000000..fb7fd64db
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/TwinBondSystem.js
@@ -0,0 +1,432 @@
+/**
+ * TwinBondSystem.js
+ * =================
+ * KRVAVA ลฝETEV - Twin Bond Mechanic (Kai โ Ana Connection)
+ *
+ * Core Concept:
+ * Kai and Ana share a psychic bond through the Alfa virus
+ * As twins, they can:
+ * - Feel each other's emotions
+ * - Sense each other's location (vaguely)
+ * - Communicate telepathically (limited)
+ * - Share HP/stamina in emergencies
+ *
+ * Features:
+ * - Bond Strength meter (0-100)
+ * - Telepathic messages from Ana
+ * - Direction to Ana indicator
+ * - Twin abilities (heal twin, boost twin, swap positions)
+ * - Bond events (visions, flashbacks)
+ * - Ana's status tracking (health, danger level)
+ *
+ * @author NovaFarma Team
+ * @date 2025-12-23
+ */
+
+class TwinBondSystem {
+ constructor(scene) {
+ this.scene = scene;
+
+ // Bond state
+ this.bondStrength = 75; // Starts strong (0-100)
+ this.maxBondStrength = 100;
+
+ // Ana's state (unknown location)
+ this.anaState = {
+ alive: true,
+ health: 100,
+ dangerLevel: 0, // 0 = safe, 100 = critical
+ distance: 5000, // pixels from Kai (initially far)
+ direction: { x: 1000, y: 1000 }, // General direction
+ lastMessage: null,
+ messageTime: null
+ };
+
+ // Bond abilities
+ this.abilities = {
+ telepathy: { unlocked: true, cooldown: 0, maxCooldown: 30000 }, // 30s
+ sensePulse: { unlocked: true, cooldown: 0, maxCooldown: 60000 }, // 1min
+ emergencyLink: { unlocked: false, cooldown: 0, maxCooldown: 300000 }, // 5min
+ twinRecall: { unlocked: false, cooldown: 0, maxCooldown: 600000 } // 10min
+ };
+
+ // Messages from Ana (telepathic)
+ this.messageQueue = [];
+ this.lastMessageTime = 0;
+
+ // UI elements
+ this.bondUI = null;
+
+ // Events
+ this.bondEvents = this.defineBondEvents();
+ this.nextEventTime = Date.now() + 60000; // First event in 1 minute
+
+ console.log('๐ TwinBondSystem initialized - Bond Strength:', this.bondStrength);
+ }
+
+ /**
+ * Update bond strength based on actions
+ */
+ update(delta) {
+ const deltaSeconds = delta / 1000;
+
+ // Passive bond decay (small)
+ this.bondStrength = Math.max(0, this.bondStrength - 0.01 * deltaSeconds);
+
+ // Update ability cooldowns
+ for (const ability in this.abilities) {
+ if (this.abilities[ability].cooldown > 0) {
+ this.abilities[ability].cooldown -= delta;
+ }
+ }
+
+ // Check for random bond events
+ if (Date.now() > this.nextEventTime) {
+ this.triggerRandomBondEvent();
+ this.nextEventTime = Date.now() + Phaser.Math.Between(60000, 180000); // 1-3 min
+ }
+
+ // Update Ana's danger level based on story progress
+ this.updateAnaDanger(deltaSeconds);
+ }
+
+ /**
+ * Define bond events (telepathic visions)
+ */
+ defineBondEvents() {
+ return [
+ {
+ id: 'first_contact',
+ trigger: 'auto',
+ condition: null,
+ message: 'Kai... can you hear me? I\'m... somewhere dark...',
+ emotion: 'worried',
+ bondChange: +5
+ },
+ {
+ id: 'danger_warning',
+ trigger: 'danger_high',
+ condition: () => this.anaState.dangerLevel > 70,
+ message: 'Brother! They\'re coming for me! Please hurry!',
+ emotion: 'fear',
+ bondChange: -10
+ },
+ {
+ id: 'memory_flash',
+ trigger: 'random',
+ condition: null,
+ message: 'Remember when we first discovered the Alfa strain? We were so hopeful...',
+ emotion: 'sad',
+ bondChange: +3
+ },
+ {
+ id: 'location_hint',
+ trigger: 'sense_pulse',
+ condition: null,
+ message: 'I can feel concrete walls... cold metal... some kind of facility?',
+ emotion: 'neutral',
+ bondChange: +5
+ },
+ {
+ id: 'encouragement',
+ trigger: 'random',
+ condition: () => this.scene.player?.hp < 50,
+ message: 'Stay strong, Kai! I believe in you!',
+ emotion: 'determined',
+ bondChange: +10
+ }
+ ];
+ }
+
+ /**
+ * Trigger a random bond event
+ */
+ triggerRandomBondEvent() {
+ const randomEvents = this.bondEvents.filter(e => e.trigger === 'random');
+
+ if (randomEvents.length === 0) return;
+
+ const event = Phaser.Utils.Array.GetRandom(randomEvents);
+
+ // Check condition
+ if (event.condition && !event.condition()) {
+ return;
+ }
+
+ this.showTelepathicMessage(event.message, event.emotion);
+ this.changeBondStrength(event.bondChange);
+ }
+
+ /**
+ * Show telepathic message from Ana
+ */
+ showTelepathicMessage(message, emotion = 'neutral') {
+ console.log(`๐ญ Twin Bond Message: ${message}`);
+
+ // Update Ana's last message
+ this.anaState.lastMessage = message;
+ this.anaState.messageTime = Date.now();
+
+ // Show in dialogue system
+ if (this.scene.dialogueSystem) {
+ const anaData = {
+ name: 'Ana (Twin Bond)',
+ id: 'ana_telepathy'
+ };
+
+ // Create temporary dialogue
+ const telepathyDialogue = {
+ root: 'message',
+ nodes: {
+ 'message': {
+ speaker: 'Ana (Twin Bond)',
+ emotion: emotion,
+ text: message,
+ next: null // Auto-close
+ }
+ }
+ };
+
+ this.scene.dialogueSystem.registerDialogue('telepathy_' + Date.now(), telepathyDialogue);
+ this.scene.dialogueSystem.startDialogue('telepathy_' + Date.now(), anaData);
+ }
+
+ // Visual effect (bond pulse)
+ this.scene.cameras.main.flash(500, 150, 100, 255, false);
+
+ // Bond strength change
+ this.showBondPulse();
+ }
+
+ /**
+ * Change bond strength
+ */
+ changeBondStrength(amount) {
+ this.bondStrength = Phaser.Math.Clamp(
+ this.bondStrength + amount,
+ 0,
+ this.maxBondStrength
+ );
+
+ console.log(`๐ Bond Strength: ${this.bondStrength.toFixed(1)}% (${amount > 0 ? '+' : ''}${amount})`);
+
+ // Notify player
+ if (amount > 0) {
+ this.scene.events.emit('bondStrengthened', { strength: this.bondStrength });
+ } else {
+ this.scene.events.emit('bondWeakened', { strength: this.bondStrength });
+ }
+ }
+
+ /**
+ * Visual bond pulse effect
+ */
+ showBondPulse() {
+ // TODO: Create particle effect at player position
+ console.log('๐ซ Bond pulse visualization');
+ }
+
+ /**
+ * Ability: Telepathy (send message to Ana)
+ */
+ useTelepathy(message) {
+ if (!this.abilities.telepathy.unlocked) {
+ console.log('โ Telepathy not unlocked');
+ return false;
+ }
+
+ if (this.abilities.telepathy.cooldown > 0) {
+ console.log('โธ๏ธ Telepathy on cooldown');
+ return false;
+ }
+
+ console.log(`๐ก Sending to Ana: ${message}`);
+
+ // Ana responds after delay
+ this.scene.time.delayedCall(2000, () => {
+ const responses = [
+ "I heard you! Keep searching!",
+ "Kai... I'm trying to stay strong...",
+ "They don't know about our bond. Use that!",
+ "I can feel you getting closer!"
+ ];
+
+ const response = Phaser.Utils.Array.GetRandom(responses);
+ this.showTelepathicMessage(response, 'determined');
+ });
+
+ // Set cooldown
+ this.abilities.telepathy.cooldown = this.abilities.telepathy.maxCooldown;
+ this.changeBondStrength(+2); // Strengthen bond
+
+ return true;
+ }
+
+ /**
+ * Ability: Sense Pulse (detect Ana's direction)
+ */
+ useSensePulse() {
+ if (!this.abilities.sensePulse.unlocked) {
+ console.log('โ Sense Pulse not unlocked');
+ return null;
+ }
+
+ if (this.abilities.sensePulse.cooldown > 0) {
+ console.log('โธ๏ธ Sense Pulse on cooldown');
+ return null;
+ }
+
+ console.log('๐ Sensing Ana\'s location...');
+
+ // Calculate general direction
+ const playerX = this.scene.player?.x || 0;
+ const playerY = this.scene.player?.y || 0;
+
+ const angle = Phaser.Math.Angle.Between(
+ playerX, playerY,
+ this.anaState.direction.x, this.anaState.direction.y
+ );
+
+ const distance = this.anaState.distance;
+
+ // Show visual indicator
+ this.showDirectionIndicator(angle, distance);
+
+ // Set cooldown
+ this.abilities.sensePulse.cooldown = this.abilities.sensePulse.maxCooldown;
+ this.changeBondStrength(+5);
+
+ return {
+ angle: angle,
+ distance: distance,
+ distanceCategory: this.getDistanceCategory(distance)
+ };
+ }
+
+ /**
+ * Get distance category (for vague communication)
+ */
+ getDistanceCategory(distance) {
+ if (distance < 500) return 'very_close';
+ if (distance < 1500) return 'close';
+ if (distance < 3000) return 'far';
+ return 'very_far';
+ }
+
+ /**
+ * Show direction indicator
+ */
+ showDirectionIndicator(angle, distance) {
+ const category = this.getDistanceCategory(distance);
+ const messages = {
+ 'very_close': 'Ana is VERY CLOSE! โฌ๏ธ',
+ 'close': 'Ana is nearby ๐',
+ 'far': 'Ana is far away ๐ญ',
+ 'very_far': 'Ana is very far ๐'
+ };
+
+ console.log(`๐ ${messages[category]} (${Math.round(distance)}px)`);
+
+ // TODO: Show UI arrow pointing in direction
+ }
+
+ /**
+ * Update Ana's danger level
+ */
+ updateAnaDanger(deltaSeconds) {
+ // Danger level increases over time (captors getting desperate)
+ if (this.anaState.alive) {
+ this.anaState.dangerLevel = Math.min(
+ 100,
+ this.anaState.dangerLevel + 0.1 * deltaSeconds
+ );
+
+ // Trigger danger events
+ if (this.anaState.dangerLevel > 70 && Math.random() < 0.01) {
+ const dangerEvent = this.bondEvents.find(e => e.id === 'danger_warning');
+ if (dangerEvent) {
+ this.showTelepathicMessage(dangerEvent.message, dangerEvent.emotion);
+ }
+ }
+ }
+ }
+
+ /**
+ * Update Ana's position (for story progression)
+ */
+ updateAnaLocation(x, y, distance) {
+ this.anaState.direction.x = x;
+ this.anaState.direction.y = y;
+ this.anaState.distance = distance;
+
+ console.log(`๐ Ana's location updated: (${x}, ${y}), distance: ${distance}px`);
+ }
+
+ /**
+ * Create Twin Bond UI
+ */
+ createBondUI() {
+ const width = this.scene.cameras.main.width;
+
+ // Bond meter (top-left)
+ const x = 20;
+ const y = 120;
+
+ // Background
+ const bg = this.scene.add.rectangle(x, y, 200, 40, 0x2d1b00, 0.8);
+ bg.setOrigin(0, 0);
+ bg.setScrollFactor(0);
+ bg.setDepth(100);
+
+ // Title
+ const title = this.scene.add.text(x + 10, y + 5, '๐ Twin Bond', {
+ fontSize: '14px',
+ fontFamily: 'Georgia, serif',
+ color: '#FFD700',
+ fontStyle: 'bold'
+ });
+ title.setScrollFactor(0);
+ title.setDepth(100);
+
+ // Bond bar
+ const barBg = this.scene.add.rectangle(x + 10, y + 25, 180, 8, 0x000000, 0.8);
+ barBg.setOrigin(0, 0);
+ barBg.setScrollFactor(0);
+ barBg.setDepth(100);
+
+ const barFill = this.scene.add.rectangle(
+ x + 10, y + 25,
+ 180 * (this.bondStrength / 100),
+ 8,
+ 0xFF69B4,
+ 1
+ );
+ barFill.setOrigin(0, 0);
+ barFill.setScrollFactor(0);
+ barFill.setDepth(100);
+
+ this.bondUI = { bg, title, barBg, barFill };
+
+ // Update bar every frame
+ this.scene.events.on('update', () => {
+ if (this.bondUI && this.bondUI.barFill) {
+ this.bondUI.barFill.width = 180 * (this.bondStrength / 100);
+ }
+ });
+ }
+
+ /**
+ * Getters
+ */
+ getBondStrength() {
+ return this.bondStrength;
+ }
+
+ getAnaStatus() {
+ return this.anaState;
+ }
+
+ isAnaSafe() {
+ return this.anaState.dangerLevel < 50;
+ }
+}
diff --git a/MASTER_DESIGN_RECOVERY/VisualEnhancementSystem.js b/MASTER_DESIGN_RECOVERY/VisualEnhancementSystem.js
new file mode 100644
index 000000000..25e851d94
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/VisualEnhancementSystem.js
@@ -0,0 +1,624 @@
+/**
+ * VISUAL ENHANCEMENT SYSTEM
+ * Central system for managing visual effects, animations, and polish
+ */
+class VisualEnhancementSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Sub-systems
+ this.animatedTextures = new Map();
+ this.weatherEffects = [];
+ this.lightSources = [];
+ this.shadows = [];
+ this.particles = [];
+
+ // Settings
+ this.settings = {
+ animatedTextures: true,
+ weatherEffects: true,
+ dynamicLighting: true,
+ shadows: true,
+ fogOfWar: false,
+ particleQuality: 'high', // low, medium, high, ultra
+ animationQuality: 'high',
+ screenShake: true,
+ transitions: true
+ };
+
+ // Animation timers
+ this.waterAnimTime = 0;
+ this.treeAnimTime = 0;
+ this.fireAnimTime = 0;
+
+ this.loadSettings();
+ this.init();
+
+ console.log('โ
Visual Enhancement System initialized');
+ }
+
+ init() {
+ this.initAnimatedTextures();
+ this.initWeatherEffects();
+ this.initLightingSystem();
+ this.initShadowSystem();
+ this.initParticleSystem();
+ }
+
+ // ========== ANIMATED TEXTURES ==========
+
+ initAnimatedTextures() {
+ if (!this.settings.animatedTextures) return;
+
+ // Create water animation frames
+ this.createWaterAnimation();
+
+ // Create fire animation
+ this.createFireAnimation();
+
+ // Create tree leaf animation
+ this.createTreeAnimation();
+
+ console.log('๐ฌ Animated textures initialized');
+ }
+
+ createWaterAnimation() {
+ // Water flow animation (4 frames)
+ const frames = [];
+ for (let i = 0; i < 4; i++) {
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(0x4488ff, 1);
+ graphics.fillRect(0, 0, 64, 64);
+
+ // Add wave pattern
+ graphics.lineStyle(2, 0x6699ff, 0.5);
+ for (let y = 0; y < 64; y += 8) {
+ const offset = Math.sin((y + i * 16) * 0.1) * 4;
+ graphics.lineBetween(0, y + offset, 64, y + offset);
+ }
+
+ graphics.generateTexture('water_anim_' + i, 64, 64);
+ graphics.destroy();
+ frames.push('water_anim_' + i);
+ }
+
+ this.animatedTextures.set('water', {
+ frames,
+ currentFrame: 0,
+ speed: 200 // ms per frame
+ });
+ }
+
+ createFireAnimation() {
+ // Fire flickering (3 frames)
+ const frames = [];
+ const colors = [0xff6600, 0xff8800, 0xffaa00];
+
+ for (let i = 0; i < 3; i++) {
+ const graphics = this.scene.add.graphics();
+ graphics.fillStyle(colors[i], 1);
+ graphics.fillCircle(16, 16, 12 + i * 2);
+ graphics.generateTexture('fire_anim_' + i, 32, 32);
+ graphics.destroy();
+ frames.push('fire_anim_' + i);
+ }
+
+ this.animatedTextures.set('fire', {
+ frames,
+ currentFrame: 0,
+ speed: 150
+ });
+ }
+
+ createTreeAnimation() {
+ // Tree leaf rustling (subtle movement)
+ console.log('๐ณ Tree animation ready');
+ }
+
+ updateAnimatedTextures(delta) {
+ if (!this.settings.animatedTextures) return;
+
+ this.waterAnimTime += delta;
+ this.fireAnimTime += delta;
+
+ // Update water
+ const water = this.animatedTextures.get('water');
+ if (water && this.waterAnimTime > water.speed) {
+ water.currentFrame = (water.currentFrame + 1) % water.frames.length;
+ this.waterAnimTime = 0;
+ }
+
+ // Update fire
+ const fire = this.animatedTextures.get('fire');
+ if (fire && this.fireAnimTime > fire.speed) {
+ fire.currentFrame = (fire.currentFrame + 1) % fire.frames.length;
+ this.fireAnimTime = 0;
+ }
+ }
+
+ // ========== WEATHER EFFECTS ==========
+
+ initWeatherEffects() {
+ if (!this.settings.weatherEffects) return;
+ console.log('๐ฆ๏ธ Weather effects initialized');
+ }
+
+ createSnowEffect() {
+ const emitter = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -10,
+ speedY: { min: 50, max: 100 },
+ speedX: { min: -20, max: 20 },
+ scale: { min: 0.3, max: 0.8 },
+ alpha: { min: 0.5, max: 1 },
+ lifespan: 10000,
+ frequency: 50,
+ quantity: 2
+ });
+ emitter.setScrollFactor(0);
+ this.weatherEffects.push(emitter);
+ return emitter;
+ }
+
+ createRainEffect() {
+ const emitter = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: -10,
+ speedY: { min: 400, max: 600 },
+ speedX: { min: -50, max: -30 },
+ scaleX: 0.1,
+ scaleY: 0.5,
+ alpha: 0.6,
+ lifespan: 2000,
+ frequency: 10,
+ quantity: 5,
+ tint: 0x88ccff
+ });
+ emitter.setScrollFactor(0);
+ this.weatherEffects.push(emitter);
+ return emitter;
+ }
+
+ createLightningFlash() {
+ const flash = this.scene.add.rectangle(
+ this.scene.cameras.main.centerX,
+ this.scene.cameras.main.centerY,
+ this.scene.cameras.main.width,
+ this.scene.cameras.main.height,
+ 0xffffff,
+ 0.8
+ );
+ flash.setScrollFactor(0);
+ flash.setDepth(10000);
+
+ this.scene.tweens.add({
+ targets: flash,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => flash.destroy()
+ });
+ }
+
+ createFogEffect() {
+ // Atmospheric fog overlay - covers entire screen
+ const fogOverlay = this.scene.add.graphics();
+ fogOverlay.setScrollFactor(0);
+ fogOverlay.setDepth(8000); // Above game, below UI
+ fogOverlay.setAlpha(0.4); // Semi-transparent
+
+ // Create fog particles for movement effect
+ const fogParticles = this.scene.add.particles(0, 0, 'particle_white', {
+ x: { min: 0, max: this.scene.cameras.main.width },
+ y: { min: 0, max: this.scene.cameras.main.height },
+ speedX: { min: -10, max: 10 },
+ speedY: { min: -5, max: 5 },
+ scale: { min: 2, max: 4 },
+ alpha: { min: 0.1, max: 0.3 },
+ lifespan: 10000,
+ frequency: 100,
+ quantity: 3,
+ tint: 0xcccccc,
+ blendMode: 'SCREEN'
+ });
+ fogParticles.setScrollFactor(0);
+ fogParticles.setDepth(8001);
+
+ // Animated fog overlay
+ let fogTime = 0;
+ const updateFog = () => {
+ fogTime += 0.01;
+ fogOverlay.clear();
+
+ const cam = this.scene.cameras.main;
+
+ // Draw multiple layers of fog for depth
+ for (let layer = 0; layer < 3; layer++) {
+ const offset = Math.sin(fogTime + layer) * 50;
+ const alpha = 0.1 + layer * 0.05;
+
+ fogOverlay.fillStyle(0xffffff, alpha);
+
+ // Draw wavy fog shapes
+ for (let i = 0; i < 5; i++) {
+ const x = (i * cam.width / 4) + offset;
+ const y = (layer * 100) + Math.cos(fogTime + i) * 30;
+ fogOverlay.fillCircle(x, y, 200 + layer * 50);
+ }
+ }
+ };
+
+ // Update fog animation every frame
+ this.scene.events.on('update', updateFog);
+
+ this.weatherEffects.push({
+ overlay: fogOverlay,
+ particles: fogParticles,
+ update: updateFog,
+ destroy: () => {
+ fogOverlay.destroy();
+ fogParticles.destroy();
+ this.scene.events.off('update', updateFog);
+ }
+ });
+
+ console.log('๐ซ๏ธ Atmospheric fog effect created');
+ return { overlay: fogOverlay, particles: fogParticles };
+ }
+
+ // ========== LIGHTING SYSTEM ==========
+
+ initLightingSystem() {
+ if (!this.settings.dynamicLighting) return;
+
+ this.lightingLayer = this.scene.add.layer();
+ this.lightingLayer.setDepth(5000);
+
+ console.log('๐ก Lighting system initialized');
+ }
+
+ addLight(x, y, radius = 100, color = 0xffaa00, intensity = 0.6) {
+ if (!this.settings.dynamicLighting) return null;
+
+ // Create radial gradient light
+ const light = this.scene.add.graphics();
+ const gradient = light.createRadialGradient(
+ radius, radius, 0,
+ radius, radius, radius
+ );
+
+ gradient.addColorStop(0, `rgba(${(color >> 16) & 0xff}, ${(color >> 8) & 0xff}, ${color & 0xff}, ${intensity})`);
+ gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
+
+ light.fillStyle(color, intensity);
+ light.fillCircle(radius, radius, radius);
+ light.setPosition(x - radius, y - radius);
+ light.setBlendMode(Phaser.BlendModes.ADD);
+ light.setDepth(5001);
+
+ const lightObj = {
+ graphics: light,
+ x, y, radius, color, intensity,
+ flickering: false
+ };
+
+ this.lightSources.push(lightObj);
+ return lightObj;
+ }
+
+ addTorch(x, y) {
+ const torch = this.addLight(x, y, 80, 0xff6600, 0.5);
+ if (torch) {
+ torch.flickering = true;
+ }
+ return torch;
+ }
+
+ updateLighting(delta) {
+ if (!this.settings.dynamicLighting) return;
+
+ // Update flickering lights
+ for (const light of this.lightSources) {
+ if (light.flickering) {
+ const flicker = 0.4 + Math.random() * 0.2;
+ light.graphics.setAlpha(flicker);
+ }
+ }
+
+ // Update ambient lighting based on time of day
+ if (this.scene.weatherSystem) {
+ const time = this.scene.weatherSystem.gameTime;
+ const isNight = time < 6 || time > 18;
+
+ if (isNight) {
+ // Darker at night
+ this.scene.cameras.main.setAlpha(0.7);
+ } else {
+ this.scene.cameras.main.setAlpha(1.0);
+ }
+ }
+ }
+
+ // ========== SHADOW SYSTEM ==========
+
+ initShadowSystem() {
+ if (!this.settings.shadows) return;
+ console.log('๐ Shadow system initialized');
+ }
+
+ addShadow(entity, offsetX = 0, offsetY = 10, width = 40, height = 15) {
+ if (!this.settings.shadows) return null;
+
+ const shadow = this.scene.add.ellipse(
+ entity.x + offsetX,
+ entity.y + offsetY,
+ width,
+ height,
+ 0x000000,
+ 0.3
+ );
+ shadow.setDepth(0);
+
+ const shadowObj = { entity, shadow, offsetX, offsetY };
+ this.shadows.push(shadowObj);
+ return shadow;
+ }
+
+ updateShadows() {
+ if (!this.settings.shadows) return;
+
+ // Get time of day for shadow opacity
+ let opacity = 0.3;
+ if (this.scene.weatherSystem) {
+ const time = this.scene.weatherSystem.gameTime;
+ // Darker shadows at noon, lighter at dawn/dusk
+ opacity = 0.2 + Math.abs(Math.sin((time / 24) * Math.PI * 2)) * 0.3;
+ }
+
+ // Update shadow positions
+ for (const { entity, shadow, offsetX, offsetY } of this.shadows) {
+ if (entity.sprite) {
+ shadow.x = entity.sprite.x + offsetX;
+ shadow.y = entity.sprite.y + offsetY;
+ shadow.setAlpha(opacity);
+ }
+ }
+ }
+
+ // ========== PARTICLE SYSTEM ==========
+
+ initParticleSystem() {
+ // Create particle textures
+ this.createParticleTextures();
+ console.log('โจ Particle system initialized');
+ }
+
+ createParticleTextures() {
+ // White particle
+ const white = this.scene.add.graphics();
+ white.fillStyle(0xffffff, 1);
+ white.fillCircle(4, 4, 4);
+ white.generateTexture('particle_white', 8, 8);
+ white.destroy();
+
+ // Sparkle
+ const sparkle = this.scene.add.graphics();
+ sparkle.fillStyle(0xffff00, 1);
+ sparkle.fillCircle(3, 3, 3);
+ sparkle.generateTexture('particle_sparkle', 6, 6);
+ sparkle.destroy();
+
+ // Heart
+ const heart = this.scene.add.graphics();
+ heart.fillStyle(0xff0066, 1);
+ heart.fillCircle(3, 3, 3);
+ heart.fillCircle(5, 3, 3);
+ heart.fillTriangle(1, 4, 7, 4, 4, 8);
+ heart.generateTexture('particle_heart', 8, 8);
+ heart.destroy();
+ }
+
+ createHeartParticles(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'particle_heart', {
+ speed: { min: 20, max: 50 },
+ angle: { min: -120, max: -60 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 1000,
+ quantity: 5,
+ blendMode: 'ADD'
+ });
+
+ this.scene.time.delayedCall(1000, () => emitter.destroy());
+ return emitter;
+ }
+
+ createSparkleEffect(x, y) {
+ const emitter = this.scene.add.particles(x, y, 'particle_sparkle', {
+ speed: { min: 10, max: 30 },
+ scale: { start: 1, end: 0 },
+ alpha: { start: 1, end: 0 },
+ lifespan: 800,
+ quantity: 10,
+ blendMode: 'ADD'
+ });
+
+ this.scene.time.delayedCall(800, () => emitter.destroy());
+ return emitter;
+ }
+
+ createCheckmarkEffect(x, y) {
+ const checkmark = this.scene.add.text(x, y, 'โ', {
+ fontSize: '32px',
+ color: '#00ff00',
+ fontStyle: 'bold'
+ });
+ checkmark.setOrigin(0.5);
+ checkmark.setDepth(10000);
+
+ this.scene.tweens.add({
+ targets: checkmark,
+ y: y - 50,
+ alpha: 0,
+ scale: 2,
+ duration: 1000,
+ ease: 'Power2',
+ onComplete: () => checkmark.destroy()
+ });
+ }
+
+ // ========== SCREEN EFFECTS ==========
+
+ screenShake(intensity = 10, duration = 300) {
+ if (!this.settings.screenShake) return;
+ this.scene.cameras.main.shake(duration, intensity / 1000);
+ }
+
+ screenFlash(color = 0xffffff, duration = 200) {
+ this.scene.cameras.main.flash(duration,
+ (color >> 16) & 0xff,
+ (color >> 8) & 0xff,
+ color & 0xff
+ );
+ }
+
+ fadeOut(duration = 500, callback) {
+ if (!this.settings.transitions) {
+ if (callback) callback();
+ return;
+ }
+
+ this.scene.cameras.main.fadeOut(duration, 0, 0, 0);
+ if (callback) {
+ this.scene.cameras.main.once('camerafadeoutcomplete', callback);
+ }
+ }
+
+ fadeIn(duration = 500) {
+ if (!this.settings.transitions) return;
+ this.scene.cameras.main.fadeIn(duration, 0, 0, 0);
+ }
+
+ // ========== BUILDING EFFECTS ==========
+
+ createConstructionEffect(x, y) {
+ // Dust particles during construction
+ const emitter = this.scene.add.particles(x, y, 'particle_white', {
+ speed: { min: 20, max: 40 },
+ scale: { start: 0.5, end: 0 },
+ alpha: { start: 0.5, end: 0 },
+ lifespan: 1000,
+ quantity: 3,
+ frequency: 100,
+ tint: 0x996633
+ });
+
+ return emitter;
+ }
+
+ createSmokeEffect(x, y) {
+ // Chimney smoke
+ const emitter = this.scene.add.particles(x, y, 'particle_white', {
+ speedY: { min: -30, max: -50 },
+ speedX: { min: -10, max: 10 },
+ scale: { start: 0.3, end: 1 },
+ alpha: { start: 0.5, end: 0 },
+ lifespan: 2000,
+ frequency: 500,
+ quantity: 1,
+ tint: 0x888888
+ });
+
+ return emitter;
+ }
+
+ // ========== FARM AUTOMATION VISUALS ==========
+
+ createPowerGridEffect(x1, y1, x2, y2) {
+ // Electric arc between power sources
+ const graphics = this.scene.add.graphics();
+ graphics.lineStyle(2, 0x00ffff, 0.8);
+
+ // Draw lightning-like connection
+ const steps = 5;
+ const points = [];
+ for (let i = 0; i <= steps; i++) {
+ const t = i / steps;
+ const x = x1 + (x2 - x1) * t + (Math.random() - 0.5) * 10;
+ const y = y1 + (y2 - y1) * t + (Math.random() - 0.5) * 10;
+ points.push({ x, y });
+ }
+
+ graphics.beginPath();
+ graphics.moveTo(points[0].x, points[0].y);
+ for (let i = 1; i < points.length; i++) {
+ graphics.lineTo(points[i].x, points[i].y);
+ }
+ graphics.strokePath();
+
+ // Fade out
+ this.scene.tweens.add({
+ targets: graphics,
+ alpha: 0,
+ duration: 200,
+ onComplete: () => graphics.destroy()
+ });
+ }
+
+ createMutantGlow(entity, color = 0x00ff00) {
+ // Radioactive glow for mutants
+ const glow = this.scene.add.circle(
+ entity.x,
+ entity.y,
+ 30,
+ color,
+ 0.3
+ );
+ glow.setBlendMode(Phaser.BlendModes.ADD);
+ glow.setDepth(entity.depth - 1);
+
+ // Pulsing animation
+ this.scene.tweens.add({
+ targets: glow,
+ scale: { from: 1, to: 1.2 },
+ alpha: { from: 0.3, to: 0.1 },
+ duration: 1000,
+ yoyo: true,
+ repeat: -1
+ });
+
+ return glow;
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateAnimatedTextures(delta);
+ this.updateLighting(delta);
+ this.updateShadows();
+ }
+
+ // ========== SETTINGS ==========
+
+ saveSettings() {
+ localStorage.setItem('novafarma_visual_enhancements', JSON.stringify(this.settings));
+ }
+
+ loadSettings() {
+ const saved = localStorage.getItem('novafarma_visual_enhancements');
+ if (saved) {
+ this.settings = { ...this.settings, ...JSON.parse(saved) };
+ }
+ }
+
+ destroy() {
+ if (this.lightingLayer) this.lightingLayer.destroy();
+ for (const { shadow } of this.shadows) {
+ shadow.destroy();
+ }
+ for (const effect of this.weatherEffects) {
+ effect.destroy();
+ }
+ console.log('โจ Visual Enhancement System destroyed');
+ }
+}
diff --git a/MASTER_DESIGN_RECOVERY/WorkerCreaturesSystem.js b/MASTER_DESIGN_RECOVERY/WorkerCreaturesSystem.js
new file mode 100644
index 000000000..a3927ad10
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/WorkerCreaturesSystem.js
@@ -0,0 +1,444 @@
+/**
+ * WORKER CREATURES SYSTEM
+ * Specialized creature workers with unique abilities
+ */
+class WorkerCreaturesSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.enabled = true;
+
+ // Worker creatures
+ this.workers = new Map();
+
+ // Creature types
+ this.creatureTypes = new Map();
+
+ // Active tasks
+ this.activeTasks = [];
+
+ this.init();
+ console.log('โ
Worker Creatures System initialized');
+ }
+
+ init() {
+ this.defineCreatureTypes();
+ console.log('๐ฆ Worker creatures ready');
+ }
+
+ // ========== CREATURE TYPES ==========
+
+ defineCreatureTypes() {
+ // Donkey - Transport specialist
+ this.defineCreature('donkey', {
+ name: 'Donkey',
+ specialty: 'transport',
+ efficiency: 0.8,
+ abilities: ['carry_items', 'cart_transport', 'long_distance'],
+ carryCapacity: 50,
+ speed: 1.2,
+ tamingDifficulty: 'easy',
+ cost: { carrot: 10, apple: 5 }
+ });
+
+ // Bigfoot - Forest gathering specialist
+ this.defineCreature('bigfoot', {
+ name: 'Bigfoot',
+ specialty: 'gathering',
+ efficiency: 1.0,
+ abilities: ['tree_chopping', 'berry_picking', 'mushroom_finding', 'forest_navigation'],
+ gatherBonus: 1.5,
+ speed: 0.9,
+ tamingDifficulty: 'medium',
+ cost: { honey: 5, berries: 20 }
+ });
+
+ // Yeti - Snow biome specialist
+ this.defineCreature('yeti', {
+ name: 'Yeti',
+ specialty: 'snow_tasks',
+ efficiency: 1.2,
+ abilities: ['ice_mining', 'snow_clearing', 'cold_resistance', 'ice_fishing'],
+ coldBonus: 2.0,
+ speed: 0.8,
+ tamingDifficulty: 'hard',
+ cost: { frozen_meat: 10, ice_crystal: 3 }
+ });
+
+ // Elf - Crafting specialist
+ this.defineCreature('elf', {
+ name: 'Elf',
+ specialty: 'crafting',
+ efficiency: 1.5,
+ abilities: ['auto_craft', 'enchanting', 'potion_making', 'tool_repair'],
+ craftingSpeed: 2.0,
+ speed: 1.0,
+ tamingDifficulty: 'hard',
+ cost: { magic_dust: 5, golden_apple: 2 }
+ });
+
+ // Gnome - Mining specialist
+ this.defineCreature('gnome', {
+ name: 'Gnome',
+ specialty: 'mining',
+ efficiency: 1.3,
+ abilities: ['ore_detection', 'tunnel_digging', 'gem_finding', 'cave_navigation'],
+ miningBonus: 1.8,
+ speed: 0.7,
+ tamingDifficulty: 'medium',
+ cost: { gold_nugget: 5, diamond: 1 }
+ });
+
+ // Fairy - Plant care specialist
+ this.defineCreature('fairy', {
+ name: 'Fairy',
+ specialty: 'plant_care',
+ efficiency: 1.4,
+ abilities: ['instant_growth', 'disease_cure', 'crop_blessing', 'flower_magic'],
+ growthBonus: 2.5,
+ speed: 1.5,
+ tamingDifficulty: 'very_hard',
+ cost: { rainbow_flower: 3, fairy_dust: 10 }
+ });
+
+ // Golem - Heavy labor specialist
+ this.defineCreature('golem', {
+ name: 'Golem',
+ specialty: 'heavy_labor',
+ efficiency: 0.9,
+ abilities: ['boulder_moving', 'building_construction', 'land_clearing', 'defense'],
+ strengthBonus: 3.0,
+ speed: 0.5,
+ tamingDifficulty: 'very_hard',
+ cost: { stone: 100, iron: 50, magic_core: 1 }
+ });
+
+ // Dragon - Ultimate worker
+ this.defineCreature('dragon', {
+ name: 'Dragon',
+ specialty: 'all',
+ efficiency: 2.0,
+ abilities: ['flight', 'fire_breath', 'treasure_finding', 'all_tasks'],
+ allBonus: 2.0,
+ speed: 2.0,
+ tamingDifficulty: 'legendary',
+ cost: { dragon_egg: 1, legendary_meat: 10, gold: 1000 }
+ });
+ }
+
+ defineCreature(id, data) {
+ this.creatureTypes.set(id, {
+ id,
+ ...data
+ });
+ }
+
+ // ========== TAMING ==========
+
+ canTame(creatureType) {
+ const creature = this.creatureTypes.get(creatureType);
+ if (!creature) return false;
+
+ // Check if player has required items
+ if (!this.scene.inventorySystem) return false;
+
+ for (const [item, amount] of Object.entries(creature.cost)) {
+ const has = this.scene.inventorySystem.getItemCount(item);
+ if (has < amount) {
+ console.log(`โ Missing ${item}: ${has}/${amount}`);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ tameCreature(creatureType, x, y) {
+ if (!this.canTame(creatureType)) return false;
+
+ const creatureData = this.creatureTypes.get(creatureType);
+
+ // Consume taming items
+ for (const [item, amount] of Object.entries(creatureData.cost)) {
+ this.scene.inventorySystem.removeItem(item, amount);
+ }
+
+ // Create worker
+ const worker = {
+ id: `worker_${creatureType}_${Date.now()}`,
+ type: creatureType,
+ name: creatureData.name,
+ specialty: creatureData.specialty,
+ efficiency: creatureData.efficiency,
+ abilities: creatureData.abilities,
+ x, y,
+ currentTask: null,
+ level: 1,
+ xp: 0,
+ loyalty: 50,
+ sprite: null
+ };
+
+ this.workers.set(worker.id, worker);
+
+ // Notify automation tier system
+ if (this.scene.automationTiers) {
+ this.scene.automationTiers.befriendCreature();
+ }
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(x, y);
+ this.scene.visualEnhancements.screenFlash(0x00ff00, 500);
+ }
+
+ console.log(`โ
Tamed ${creatureData.name}!`);
+ return worker;
+ }
+
+ // ========== TASK ASSIGNMENT ==========
+
+ assignTask(workerId, task) {
+ const worker = this.workers.get(workerId);
+ if (!worker) return false;
+
+ const creatureData = this.creatureTypes.get(worker.type);
+
+ // Check if creature can do this task
+ if (!this.canDoTask(worker, task)) {
+ console.log(`โ ${worker.name} cannot do ${task.type}`);
+ return false;
+ }
+
+ // Assign task
+ worker.currentTask = {
+ ...task,
+ startTime: Date.now(),
+ efficiency: this.calculateEfficiency(worker, task)
+ };
+
+ console.log(`๐ ${worker.name} assigned to ${task.type}`);
+ return true;
+ }
+
+ canDoTask(worker, task) {
+ const creatureData = this.creatureTypes.get(worker.type);
+
+ // Dragons can do everything
+ if (worker.type === 'dragon') return true;
+
+ // Check specialty
+ const taskSpecialties = {
+ 'transport': ['donkey'],
+ 'gather_wood': ['bigfoot'],
+ 'gather_berries': ['bigfoot'],
+ 'mine_ice': ['yeti'],
+ 'ice_fishing': ['yeti'],
+ 'craft_item': ['elf'],
+ 'enchant_item': ['elf'],
+ 'mine_ore': ['gnome'],
+ 'find_gems': ['gnome'],
+ 'water_crops': ['fairy'],
+ 'grow_crops': ['fairy'],
+ 'build': ['golem'],
+ 'clear_land': ['golem']
+ };
+
+ const validTypes = taskSpecialties[task.type] || [];
+ return validTypes.includes(worker.type);
+ }
+
+ calculateEfficiency(worker, task) {
+ const creatureData = this.creatureTypes.get(worker.type);
+ let efficiency = creatureData.efficiency;
+
+ // Apply specialty bonuses
+ if (task.type.includes('gather') && creatureData.gatherBonus) {
+ efficiency *= creatureData.gatherBonus;
+ }
+ if (task.type.includes('mine') && creatureData.miningBonus) {
+ efficiency *= creatureData.miningBonus;
+ }
+ if (task.type.includes('craft') && creatureData.craftingSpeed) {
+ efficiency *= creatureData.craftingSpeed;
+ }
+ if (task.type.includes('grow') && creatureData.growthBonus) {
+ efficiency *= creatureData.growthBonus;
+ }
+
+ // Apply level bonus
+ efficiency *= (1 + worker.level * 0.1);
+
+ return efficiency;
+ }
+
+ // ========== TASK EXECUTION ==========
+
+ updateWorkers(delta) {
+ for (const worker of this.workers.values()) {
+ if (!worker.currentTask) continue;
+
+ const elapsed = Date.now() - worker.currentTask.startTime;
+ const taskTime = worker.currentTask.duration / worker.currentTask.efficiency;
+
+ if (elapsed >= taskTime) {
+ this.completeTask(worker);
+ }
+ }
+ }
+
+ completeTask(worker) {
+ const task = worker.currentTask;
+
+ // Execute task
+ this.executeTask(worker, task);
+
+ // Grant XP
+ this.addWorkerXP(worker, task.xp || 10);
+
+ // Increase loyalty
+ worker.loyalty = Math.min(100, worker.loyalty + 1);
+
+ // Clear task
+ worker.currentTask = null;
+
+ console.log(`โ
${worker.name} completed ${task.type}!`);
+ }
+
+ executeTask(worker, task) {
+ switch (task.type) {
+ case 'transport':
+ this.transportItems(worker, task);
+ break;
+ case 'gather_wood':
+ this.gatherResource(worker, 'wood', task.amount);
+ break;
+ case 'gather_berries':
+ this.gatherResource(worker, 'berries', task.amount);
+ break;
+ case 'mine_ore':
+ this.gatherResource(worker, 'ore', task.amount);
+ break;
+ case 'craft_item':
+ this.craftItem(worker, task.item);
+ break;
+ case 'grow_crops':
+ this.growCrops(worker, task.crops);
+ break;
+ default:
+ console.log(`Unknown task: ${task.type}`);
+ }
+ }
+
+ transportItems(worker, task) {
+ // Move items from A to B
+ console.log(`๐ ${worker.name} transported items`);
+ }
+
+ gatherResource(worker, resource, amount) {
+ // Add resource to inventory
+ if (this.scene.inventorySystem) {
+ const bonus = this.workers.get(worker.id).currentTask.efficiency;
+ const finalAmount = Math.floor(amount * bonus);
+ this.scene.inventorySystem.addItem(resource, finalAmount);
+ console.log(`๐ฆ Gathered ${finalAmount} ${resource}`);
+ }
+ }
+
+ craftItem(worker, item) {
+ // Craft item
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem(item, 1);
+ console.log(`๐จ Crafted ${item}`);
+ }
+ }
+
+ growCrops(worker, crops) {
+ // Instantly grow crops
+ console.log(`๐ฑ Grew ${crops.length} crops`);
+ }
+
+ // ========== LEVELING ==========
+
+ addWorkerXP(worker, amount) {
+ worker.xp += amount;
+
+ const xpNeeded = this.getXPForLevel(worker.level + 1);
+ if (worker.xp >= xpNeeded) {
+ this.levelUpWorker(worker);
+ }
+ }
+
+ getXPForLevel(level) {
+ return Math.floor(100 * Math.pow(1.5, level - 1));
+ }
+
+ levelUpWorker(worker) {
+ worker.level++;
+ worker.xp = 0;
+
+ console.log(`๐ ${worker.name} leveled up to ${worker.level}!`);
+
+ // Visual effect
+ if (this.scene.visualEnhancements) {
+ this.scene.visualEnhancements.createSparkleEffect(worker.x, worker.y);
+ }
+ }
+
+ // ========== SPECIAL ABILITIES ==========
+
+ useAbility(workerId, abilityName) {
+ const worker = this.workers.get(workerId);
+ if (!worker) return false;
+
+ const creatureData = this.creatureTypes.get(worker.type);
+ if (!creatureData.abilities.includes(abilityName)) {
+ console.log(`โ ${worker.name} doesn't have ${abilityName}`);
+ return false;
+ }
+
+ // Execute ability
+ switch (abilityName) {
+ case 'instant_growth':
+ this.instantGrowth(worker);
+ break;
+ case 'fire_breath':
+ this.fireBreath(worker);
+ break;
+ case 'treasure_finding':
+ this.findTreasure(worker);
+ break;
+ default:
+ console.log(`โจ ${worker.name} used ${abilityName}!`);
+ }
+
+ return true;
+ }
+
+ instantGrowth(worker) {
+ // Fairy ability - instantly grow all nearby crops
+ console.log('๐ธ Fairy magic! All crops instantly grown!');
+ }
+
+ fireBreath(worker) {
+ // Dragon ability - clear area with fire
+ console.log('๐ฅ Dragon fire breath!');
+ }
+
+ findTreasure(worker) {
+ // Dragon ability - find hidden treasure
+ console.log('๐ Found treasure!');
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem('gold', 100);
+ }
+ }
+
+ // ========== UPDATE ==========
+
+ update(delta) {
+ this.updateWorkers(delta);
+ }
+
+ destroy() {
+ console.log('๐ฆ Worker Creatures System destroyed');
+ }
+}
diff --git a/MASTER_DESIGN_RECOVERY/ZombieWorkerSystem.js b/MASTER_DESIGN_RECOVERY/ZombieWorkerSystem.js
new file mode 100644
index 000000000..51ed3ca3d
--- /dev/null
+++ b/MASTER_DESIGN_RECOVERY/ZombieWorkerSystem.js
@@ -0,0 +1,239 @@
+/**
+ * ZOMBIE WORKER AI SYSTEM
+ * Tamed zombies lahko opravljajo delo (farming, mining)
+ */
+class ZombieWorkerSystem {
+ constructor(scene) {
+ this.scene = scene;
+ this.workers = [];
+ }
+
+ assignWork(zombie, workType, workRadius = 5) {
+ if (!zombie.isTamed) {
+ console.warn('โ ๏ธ Cannot assign work to untamed zombie!');
+ return false;
+ }
+
+ zombie.workerData = {
+ type: workType,
+ radius: workRadius,
+ centerX: zombie.gridX,
+ centerY: zombie.gridY,
+ energy: 100,
+ decayRate: 0.5,
+ workTimer: 0,
+ workInterval: 5000,
+ status: 'IDLE',
+ inventory: {
+ seeds: 0,
+ hoe: 0,
+ watering_can: 0,
+ pickaxe: 0
+ }
+ };
+
+ this.workers.push(zombie);
+ console.log(`๐ง Assigned ${zombie.type} as ${workType} worker`);
+ return true;
+ }
+
+ removeWorker(zombie) {
+ const index = this.workers.indexOf(zombie);
+ if (index > -1) {
+ this.workers.splice(index, 1);
+ zombie.workerData = null;
+ }
+ }
+
+ update(delta) {
+ this.workers = this.workers.filter(w =>
+ this.scene.npcs.includes(w) && w.hp > 0
+ );
+
+ for (const worker of this.workers) {
+ if (!worker.workerData) continue;
+
+ this.applyDecay(worker, delta);
+ worker.workerData.workTimer += delta;
+
+ if (worker.workerData.workTimer >= worker.workerData.workInterval) {
+ worker.workerData.workTimer = 0;
+
+ if (worker.workerData.type === 'FARM') {
+ this.performFarmWork(worker);
+ } else if (worker.workerData.type === 'MINE') {
+ this.performMineWork(worker);
+ } else if (worker.workerData.type === 'CLEAR') {
+ this.performClearWork(worker);
+ }
+ }
+ }
+ }
+
+ applyDecay(zombie, delta) {
+ const wd = zombie.workerData;
+ wd.energy -= (wd.decayRate * delta) / 1000;
+
+ if (wd.energy <= 0) {
+ wd.energy = 0;
+ zombie.hp -= (wd.decayRate * delta) / 1000;
+ if (zombie.sprite) zombie.sprite.setTint(0x666666);
+ }
+
+ if (zombie.hp <= 0) this.onWorkerDeath(zombie);
+ }
+
+ performFarmWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ const farming = this.scene.farmingSystem;
+ if (!terrain || !farming) return;
+
+ // 1. Water/Harvest existing crops
+ if (terrain.cropsMap) {
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.cropsMap.has(key)) {
+ const crop = terrain.cropsMap.get(key);
+
+ if (!crop.isWatered) {
+ farming.waterCrop(wd.centerX + dx, wd.centerY + dy);
+ console.log(`๐ง๐ง Worker watered`);
+ wd.status = 'WORKING';
+ return;
+ }
+
+ if (crop.stage >= 4) {
+ farming.harvest(wd.centerX + dx, wd.centerY + dy);
+ console.log(`๐ง๐พ Worker harvested`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // 2. Plant on empty tilled soil (if has seeds)
+ if (wd.inventory.seeds > 0) {
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const tile = terrain.getTile(wd.centerX + dx, wd.centerY + dy);
+ if (tile && tile.isTilled && !tile.hasCrop) {
+ farming.plant(wd.centerX + dx, wd.centerY + dy, 'wheat');
+ wd.inventory.seeds--;
+ console.log(`๐ง๐ฑ Worker planted (Seeds: ${wd.inventory.seeds})`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+
+ // 3. Till grass/dirt
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const tile = terrain.getTile(wd.centerX + dx, wd.centerY + dy);
+ const typeName = tile?.type?.name || tile?.type;
+
+ if ((typeName === 'grass' || typeName === 'dirt') && !tile.isTilled) {
+ tile.isTilled = true;
+ if (tile.sprite) tile.sprite.setTint(0x8B4513);
+ console.log(`๐ง๐จ Worker tilled soil`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+
+ wd.status = 'IDLE';
+ }
+
+ performMineWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ if (!terrain || !terrain.decorationsMap) return;
+
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.decorationsMap.has(key)) {
+ const decor = terrain.decorationsMap.get(key);
+ if (decor.type === 'stone' || decor.type.includes('rock')) {
+ terrain.removeDecoration(wd.centerX + dx, wd.centerY + dy);
+
+ if (this.scene.inventorySystem) {
+ this.scene.inventorySystem.addItem('stone', 1);
+ }
+
+ console.log(`๐งโ๏ธ Worker mined stone`);
+ wd.status = 'WORKING';
+ return;
+ }
+ }
+ }
+ }
+
+ wd.status = 'IDLE';
+ }
+
+ performClearWork(zombie) {
+ const wd = zombie.workerData;
+ const terrain = this.scene.terrainSystem;
+ if (!terrain || !terrain.decorationsMap) return;
+
+ for (let dx = -wd.radius; dx <= wd.radius; dx++) {
+ for (let dy = -wd.radius; dy <= wd.radius; dy++) {
+ const key = `${wd.centerX + dx},${wd.centerY + dy}`;
+ if (terrain.decorationsMap.has(key)) {
+ const decor = terrain.decorationsMap.get(key);
+ const t = decor.type;
+
+ // Clear trees, bushes, logs, rocks
+ if (t.startsWith('tree') || t.startsWith('bush') || t === 'fallen_log' || t === 'stone' || t.startsWith('small_rock')) {
+ terrain.removeDecoration(wd.centerX + dx, wd.centerY + dy);
+
+ // Give some resources
+ if (this.scene.inventorySystem) {
+ if (t.startsWith('tree') || t === 'fallen_log' || t.startsWith('bush')) {
+ this.scene.inventorySystem.addItem('wood', 1);
+ } else {
+ this.scene.inventorySystem.addItem('stone', 1);
+ }
+ }
+
+ console.log(`๐ง๐ช Worker CLEARED ${t}`);
+ wd.status = 'WORKING';
+ return; // One per tick
+ }
+ }
+ }
+ }
+ wd.status = 'IDLE';
+ }
+
+ onWorkerDeath(zombie) {
+ console.log(`๐ Worker died at ${zombie.gridX},${zombie.gridY}`);
+
+ if (this.scene.interactionSystem && this.scene.interactionSystem.spawnLoot) {
+ this.scene.interactionSystem.spawnLoot(zombie.gridX, zombie.gridY, 'fertilizer', 1);
+ }
+
+ if (this.scene.statsSystem) {
+ this.scene.statsSystem.addXP(10);
+ }
+
+ this.removeWorker(zombie);
+ }
+
+ feedWorker(zombie, amount = 50) {
+ if (!zombie.workerData) return false;
+
+ zombie.workerData.energy = Math.min(100, zombie.workerData.energy + amount);
+ if (zombie.sprite) zombie.sprite.clearTint();
+
+ console.log(`๐ Fed worker`);
+ return true;
+ }
+}
diff --git a/MASTER_RECOVERY/GameManager.js b/MASTER_RECOVERY/GameManager.js
new file mode 100644
index 000000000..ea58e85d6
--- /dev/null
+++ b/MASTER_RECOVERY/GameManager.js
@@ -0,0 +1,398 @@
+/**
+ * GAME MANAGER - AUTO-SAVE SYSTEM
+ * "The Silent Protector" - Never lose progress!
+ */
+
+class GameManager {
+ constructor(scene) {
+ this.scene = scene;
+ this.saveKey = 'mrtva_dolina_save'; // Slot_0
+
+ // Auto-save state
+ this.lastAutoSave = Date.now();
+ this.autoSaveInterval = 5 * 60 * 1000; // 5 minutes
+ this.isSaving = false;
+
+ // Save indicator
+ this.saveIndicator = null;
+
+ // Initialize
+ this.init();
+ }
+
+ init() {
+ console.log('๐พ GameManager initialized - Auto-save active');
+
+ // Start periodic auto-save timer
+ this.startPeriodicAutoSave();
+
+ // Listen for scene transitions
+ this.scene.events.on('shutdown', () => this.onSceneTransition());
+
+ // Listen for progression events
+ this.listenForProgressionEvents();
+ }
+
+ /**
+ * TRIGGER 1: SCENE TRANSITION SAVE
+ */
+ onSceneTransition() {
+ console.log('๐ช Scene transition detected - Auto-saving...');
+ this.autoSaveGame('Scene Transition');
+ }
+
+ /**
+ * TRIGGER 2: PROGRESSION MILESTONE SAVE
+ */
+ listenForProgressionEvents() {
+ // Listen for aging events
+ this.scene.events.on('kai-aged', (data) => {
+ console.log(`โฐ Kai aged to ${data.newAge} - Auto-saving...`);
+ this.autoSaveGame('Aging Milestone');
+ });
+
+ // Listen for memory found
+ this.scene.events.on('memory-found', (data) => {
+ console.log(`๐ธ Memory found (${data.id}) - Auto-saving...`);
+ this.autoSaveGame('Memory Found');
+ });
+
+ // Listen for Gronk level up
+ if (window.gronkStats) {
+ window.addEventListener('gronk-levelup', (e) => {
+ console.log(`๐จ Gronk level ${e.detail.level} - Auto-saving...`);
+ this.autoSaveGame('Gronk Level Up');
+ });
+ }
+
+ // Listen for companion unlocks
+ window.addEventListener('companion-unlocked', (e) => {
+ console.log(`๐ Companion unlocked (${e.detail.name}) - Auto-saving...`);
+ this.autoSaveGame('Companion Unlock');
+ });
+ }
+
+ /**
+ * TRIGGER 3: PERIODIC AUTO-SAVE (5 Minutes)
+ */
+ startPeriodicAutoSave() {
+ this.periodicTimer = this.scene.time.addEvent({
+ delay: this.autoSaveInterval,
+ callback: () => {
+ const timeSinceLastSave = Date.now() - this.lastAutoSave;
+ if (timeSinceLastSave >= this.autoSaveInterval) {
+ console.log('โฑ๏ธ 5 minutes elapsed - Auto-saving...');
+ this.autoSaveGame('Periodic (5min)');
+ }
+ },
+ loop: true
+ });
+ }
+
+ /**
+ * AUTO-SAVE GAME (Silent, no interruption)
+ */
+ autoSaveGame(reason = 'Manual') {
+ if (this.isSaving) {
+ console.log('โ ๏ธ Save already in progress, skipping...');
+ return;
+ }
+
+ this.isSaving = true;
+
+ console.log(`๐พ AUTO-SAVING (${reason})...`);
+
+ // Show save indicator
+ this.showSaveIndicator();
+
+ try {
+ // Gather save data
+ const saveData = this.gatherSaveData();
+
+ // Save to LocalStorage (Slot_0)
+ localStorage.setItem(this.saveKey, JSON.stringify(saveData));
+
+ // Update timestamp
+ this.lastAutoSave = Date.now();
+
+ console.log('โ
Auto-save complete!');
+ console.log(` Reason: ${reason}`);
+ console.log(` Size: ${JSON.stringify(saveData).length} bytes`);
+
+ } catch (error) {
+ console.error('โ Auto-save failed:', error);
+ } finally {
+ this.isSaving = false;
+ }
+ }
+
+ /**
+ * GATHER ALL SAVE DATA
+ */
+ gatherSaveData() {
+ const data = {
+ version: '1.0.0',
+ lastSaved: new Date().toISOString(),
+ playtime: this.getPlaytime(),
+
+ // Player data
+ player: this.getPlayerData(),
+
+ // Progress
+ progress: this.getProgressData(),
+
+ // Companions
+ companions: this.getCompanionData(),
+
+ // Farm state
+ farm: this.getFarmData(),
+
+ // Economy
+ economy: this.getEconomyData(),
+
+ // Current scene
+ currentScene: this.scene.scene.key
+ };
+
+ return data;
+ }
+
+ /**
+ * GET PLAYER DATA
+ */
+ getPlayerData() {
+ // Get PlayerStats if exists
+ let playerStats = null;
+ if (this.scene.playerStats) {
+ playerStats = this.scene.playerStats.getAgeInfo();
+ } else {
+ // Load from LocalStorage
+ const stored = localStorage.getItem('player_stats');
+ if (stored) {
+ playerStats = JSON.parse(stored);
+ }
+ }
+
+ return {
+ position: this.getPlayerPosition(),
+ age_level: playerStats?.level || 1,
+ current_age: playerStats?.age || 14,
+ age_sprite: playerStats?.sprite || 'kai_age14',
+ inventory: this.getInventory(),
+ health: 100,
+ stamina: 100,
+ equipped_tools: {}
+ };
+ }
+
+ /**
+ * GET PROGRESS DATA
+ */
+ getProgressData() {
+ return {
+ memories_found: this.getMemoriesFound(),
+ total_memories: 100,
+ quests_completed: [],
+ npcs_met: [],
+ biomes_unlocked: ['grassland']
+ };
+ }
+
+ /**
+ * GET COMPANION DATA
+ */
+ getCompanionData() {
+ const companions = {
+ gronk: {
+ unlocked: false,
+ level: 1,
+ xp: 0
+ },
+ susi: {
+ unlocked: false,
+ position: { x: 0, y: 0 },
+ loyalty: 0
+ }
+ };
+
+ // Check Gronk stats
+ if (window.gronkStats) {
+ const gronkData = gronkStats.getStats();
+ companions.gronk = {
+ unlocked: true,
+ level: gronkData.level,
+ xp: gronkData.xp
+ };
+ }
+
+ // Check Susi unlock
+ const susiUnlocked = localStorage.getItem('susi_unlocked');
+ if (susiUnlocked === 'true') {
+ companions.susi.unlocked = true;
+ }
+
+ return companions;
+ }
+
+ /**
+ * GET FARM DATA
+ */
+ getFarmData() {
+ return {
+ crops: [],
+ buildings: [],
+ animals: [],
+ resources: {}
+ };
+ }
+
+ /**
+ * GET ECONOMY DATA
+ */
+ getEconomyData() {
+ return {
+ money: 0,
+ cannabis_seeds: 5, // Starting capital!
+ cannabis_harvested: 0
+ };
+ }
+
+ /**
+ * HELPER: Get player position
+ */
+ getPlayerPosition() {
+ if (this.scene.player) {
+ return {
+ x: this.scene.player.x,
+ y: this.scene.player.y
+ };
+ }
+ return { x: 640, y: 360 }; // Default center
+ }
+
+ /**
+ * HELPER: Get inventory
+ */
+ getInventory() {
+ // TODO: Get from inventory system
+ return [];
+ }
+
+ /**
+ * HELPER: Get memories found
+ */
+ getMemoriesFound() {
+ const stored = localStorage.getItem('player_stats');
+ if (stored) {
+ const data = JSON.parse(stored);
+ return data.memoriesFound || 0;
+ }
+ return 0;
+ }
+
+ /**
+ * HELPER: Get playtime
+ */
+ getPlaytime() {
+ const stored = localStorage.getItem('playtime');
+ if (stored) {
+ return parseInt(stored) + Math.floor(this.scene.time.now / 1000);
+ }
+ return Math.floor(this.scene.time.now / 1000);
+ }
+
+ /**
+ * SHOW SAVE INDICATOR (Spinning Longboard)
+ */
+ showSaveIndicator() {
+ if (this.saveIndicator) {
+ return; // Already showing
+ }
+
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ // Create longboard emoji as indicator
+ this.saveIndicator = this.scene.add.text(
+ width - 80,
+ height - 80,
+ '๐น',
+ {
+ fontSize: '32px'
+ }
+ );
+ this.saveIndicator.setScrollFactor(0);
+ this.saveIndicator.setDepth(9999);
+ this.saveIndicator.setAlpha(0);
+
+ // Add "Saving..." text
+ const savingText = this.scene.add.text(
+ width - 140,
+ height - 50,
+ 'Saving...',
+ {
+ fontSize: '14px',
+ fontFamily: 'Georgia, serif',
+ color: '#f4e4c1',
+ stroke: '#000000',
+ strokeThickness: 3
+ }
+ );
+ savingText.setScrollFactor(0);
+ savingText.setDepth(9999);
+ savingText.setAlpha(0);
+
+ // Fade in
+ this.scene.tweens.add({
+ targets: [this.saveIndicator, savingText],
+ alpha: 0.8,
+ duration: 300
+ });
+
+ // Spin animation
+ this.scene.tweens.add({
+ targets: this.saveIndicator,
+ angle: 360,
+ duration: 1000,
+ repeat: 1
+ });
+
+ // Fade out after 2 seconds
+ this.scene.time.delayedCall(2000, () => {
+ this.scene.tweens.add({
+ targets: [this.saveIndicator, savingText],
+ alpha: 0,
+ duration: 500,
+ onComplete: () => {
+ if (this.saveIndicator) {
+ this.saveIndicator.destroy();
+ this.saveIndicator = null;
+ }
+ if (savingText) {
+ savingText.destroy();
+ }
+ }
+ });
+ });
+ }
+
+ /**
+ * MANUAL SAVE (for explicit save points)
+ */
+ manualSave() {
+ console.log('๐พ Manual save requested...');
+ this.autoSaveGame('Manual Save');
+ }
+
+ /**
+ * CLEANUP
+ */
+ destroy() {
+ if (this.periodicTimer) {
+ this.periodicTimer.remove();
+ }
+ console.log('๐พ GameManager destroyed');
+ }
+}
+
+export default GameManager;
diff --git a/MASTER_RECOVERY/MasterGameSystemsManager.js b/MASTER_RECOVERY/MasterGameSystemsManager.js
new file mode 100644
index 000000000..69123315c
--- /dev/null
+++ b/MASTER_RECOVERY/MasterGameSystemsManager.js
@@ -0,0 +1,446 @@
+/**
+ * MASTER GAME SYSTEMS MANAGER
+ * Centralized coordinator for all game systems
+ * Created: January 4, 2026
+ *
+ * Integrates:
+ * - Sleep System
+ * - Crafting Tables System
+ * - Bakery Shop System
+ * - Barber Shop System
+ * - Lawyer Office System
+ * - Zombie Miner Automation System
+ * - Town Growth System
+ * - NPC Privacy System
+ * - Existing Mining System
+ */
+
+
+class MasterGameSystemsManager {
+ constructor(game) {
+ this.game = game;
+ this.scene = game.scene;
+
+ console.log('๐ฎ Initializing Master Game Systems Manager...');
+
+ // Initialize all systems
+ this.initializeSystems();
+
+ // Set up cross-system event listeners
+ this.setupEventListeners();
+
+ console.log('โ
Master Game Systems Manager initialized!');
+ }
+
+ /**
+ * Initialize all game systems
+ */
+ initializeSystems() {
+ // HOME & SLEEP
+ this.sleepSystem = new SleepSystem(this.game);
+ console.log(' โ Sleep System');
+
+ // CRAFTING
+ this.craftingSystem = new CraftingTablesSystem(this.game);
+ console.log(' โ Crafting Tables System');
+
+ // TOWN BUILDINGS
+ this.bakerySystem = new BakeryShopSystem(this.game);
+ console.log(' โ Bakery Shop System');
+
+ this.barberSystem = new BarberShopSystem(this.game);
+ console.log(' โ Barber Shop System');
+
+ this.lawyerSystem = new LawyerOfficeSystem(this.game);
+ console.log(' โ Lawyer Office System');
+
+ // MINING & AUTOMATION
+ this.zombieMinerSystem = new ZombieMinerAutomationSystem(this.game);
+ console.log(' โ Zombie Miner Automation System');
+
+ // TOWN GROWTH
+ this.townGrowthSystem = new TownGrowthSystem(this.game);
+ console.log(' โ Town Growth System');
+
+ // NPC SYSTEMS
+ this.npcPrivacySystem = new NPCPrivacySystem(this.game);
+ console.log(' โ NPC Privacy System');
+
+ // Register all systems globally
+ this.registerSystems();
+ }
+
+ /**
+ * Register systems to game registry
+ */
+ registerSystems() {
+ this.game.registry.set('sleepSystem', this.sleepSystem);
+ this.game.registry.set('craftingSystem', this.craftingSystem);
+ this.game.registry.set('bakerySystem', this.bakerySystem);
+ this.game.registry.set('barberSystem', this.barberSystem);
+ this.game.registry.set('lawyerSystem', this.lawyerSystem);
+ this.game.registry.set('zombieMinerSystem', this.zombieMinerSystem);
+ this.game.registry.set('townGrowthSystem', this.townGrowthSystem);
+ this.game.registry.set('npcPrivacySystem', this.npcPrivacySystem);
+
+ console.log(' โ All systems registered to game registry');
+ }
+
+ /**
+ * Set up cross-system event listeners
+ */
+ setupEventListeners() {
+ // TOWN GROWTH โ SERVICES
+ this.game.events.on('serviceUnlocked', (data) => {
+ this.onServiceUnlocked(data);
+ });
+
+ // MARRIAGE โ LAWYER AUTO-UNLOCK
+ this.game.events.on('marriageComplete', () => {
+ this.lawyerSystem.checkAutoUnlock();
+ });
+
+ // RELATIONSHIP CHANGE โ LAWYER AUTO-UNLOCK
+ this.game.events.on('relationshipChanged', (data) => {
+ if (data.hearts <= 3) {
+ this.lawyerSystem.checkAutoUnlock();
+ }
+ });
+
+ // BUILDING UNLOCKED โ TOWN GROWTH
+ this.game.events.on('buildingUnlocked', (data) => {
+ this.townGrowthSystem.checkPopulationUnlocks();
+ });
+
+ // ZOMBIE HIRED โ TOWN GROWTH CHECK
+ this.game.events.on('zombieWorkerHired', () => {
+ this.townGrowthSystem.checkPopulationUnlocks();
+ });
+
+ // SLEEP โ ZOMBIE LOYALTY DECAY PAUSE
+ this.game.events.on('sleepStarted', () => {
+ this.pauseZombieLoyaltyDecay = true;
+ });
+
+ this.game.events.on('wakeUp', () => {
+ this.pauseZombieLoyaltyDecay = false;
+ });
+
+ console.log(' โ Cross-system event listeners configured');
+ }
+
+ /**
+ * Handle service unlock
+ */
+ onServiceUnlocked(data) {
+ const { serviceId, population } = data;
+
+ console.log(`๐๏ธ Service unlocked: ${serviceId} at pop ${population}`);
+
+ // Trigger service-specific initialization
+ switch (serviceId) {
+ case 'market':
+ this.initializeMarket();
+ break;
+ case 'hospital':
+ this.initializeHospital();
+ break;
+ case 'school':
+ this.initializeSchool();
+ break;
+ case 'bank':
+ this.initializeBank();
+ break;
+ case 'museum':
+ this.initializeMuseum();
+ break;
+ case 'theater':
+ this.initializeTheater();
+ break;
+ }
+ }
+
+ /**
+ * Service initializers (placeholders for future implementation)
+ */
+ initializeMarket() {
+ console.log(' โ Market initialized');
+ // TODO: Implement market system
+ }
+
+ initializeHospital() {
+ console.log(' โ Hospital initialized');
+ // TODO: Implement hospital system
+ }
+
+ initializeSchool() {
+ console.log(' โ School initialized');
+ // TODO: Implement school system
+ }
+
+ initializeBank() {
+ console.log(' โ Bank initialized');
+ // TODO: Implement bank system
+ }
+
+ initializeMuseum() {
+ console.log(' โ Museum initialized');
+ // TODO: Implement museum system
+ }
+
+ initializeTheater() {
+ console.log(' โ Theater initialized');
+ // TODO: Implement theater system
+ }
+
+ /**
+ * Update all systems (called every frame)
+ */
+ update(time, delta) {
+ const deltaSeconds = delta / 1000;
+
+ // Update systems that need per-frame updates
+ this.sleepSystem.update(deltaSeconds);
+ this.craftingSystem.update(deltaSeconds);
+
+ // Update zombie miners (if not paused by sleep)
+ if (!this.pauseZombieLoyaltyDecay) {
+ this.zombieMinerSystem.update(deltaSeconds);
+ }
+ }
+
+ /**
+ * Hourly update (called when game hour changes)
+ */
+ onHourChange(hour) {
+ // Update time-sensitive systems
+ this.bakerySystem.update();
+ this.townGrowthSystem.update();
+
+ // Check for automation collection reminders
+ if (hour % 4 === 0) { // Every 4 hours
+ this.checkAutomationReminders();
+ }
+ }
+
+ /**
+ * Check automation reminders
+ */
+ checkAutomationReminders() {
+ // Zombie miner automation
+ if (this.zombieMinerSystem.automationActive) {
+ const hours = this.zombieMinerSystem.getHoursSinceLastCollection();
+
+ if (hours >= 8) {
+ this.game.showNotification({
+ title: 'โ๏ธ Automation Ready',
+ text: `${hours.toFixed(0)}h of mining ready to collect!`,
+ icon: 'โ๏ธ'
+ });
+ }
+ }
+ }
+
+ /**
+ * Daily update (called at midnight)
+ */
+ onDayChange(day) {
+ console.log(`๐
Day ${day} - Running daily system updates...`);
+
+ // Town growth check
+ this.townGrowthSystem.update();
+
+ // Restock shops
+ this.bakerySystem.restockInventory();
+
+ // Birthday cake deliveries
+ this.bakerySystem.checkBirthdayCakeDeliveries();
+ }
+
+ /**
+ * Save all systems state
+ */
+ saveAllSystems() {
+ return {
+ version: '1.0',
+ timestamp: Date.now(),
+ systems: {
+ sleep: {
+ playerBed: this.sleepSystem.playerBed,
+ unlockedBeds: Object.entries(this.sleepSystem.bedTypes)
+ .filter(([_, bed]) => bed.unlocked)
+ .map(([key, _]) => key)
+ },
+ crafting: {
+ currentTable: this.craftingSystem.currentTable.id,
+ unlockedRecipes: this.craftingSystem.unlockedRecipes,
+ largeTableUnlocked: this.craftingSystem.tables.LARGE.unlocked
+ },
+ bakery: {
+ isUnlocked: this.bakerySystem.isUnlocked,
+ inventory: this.bakerySystem.inventory,
+ birthdayOrders: this.bakerySystem.birthdayCakeOrders
+ },
+ barber: {
+ isUnlocked: this.barberSystem.isUnlocked,
+ playerAppearance: this.barberSystem.playerAppearance,
+ savedLooks: this.barberSystem.savedLooks,
+ visitCount: this.barberSystem.visitCount
+ },
+ lawyer: {
+ isUnlocked: this.lawyerSystem.isUnlocked,
+ hasPrenup: this.lawyerSystem.hasPrenup,
+ divorceHistory: this.lawyerSystem.divorceHistory,
+ counselingInProgress: this.lawyerSystem.counselingInProgress
+ },
+ zombieMiners: {
+ miners: this.zombieMinerSystem.zombieMiners,
+ equipment: this.zombieMinerSystem.zombieEquipment,
+ lastCollectionTime: this.zombieMinerSystem.lastCollectionTime
+ },
+ townGrowth: {
+ population: this.townGrowthSystem.population,
+ populationSlots: this.townGrowthSystem.populationSlots,
+ villages: this.townGrowthSystem.villages,
+ services: this.townGrowthSystem.services
+ },
+ npcPrivacy: {
+ npcHomes: this.npcPrivacySystem.npcHomes,
+ visitHistory: this.npcPrivacySystem.visitHistory,
+ lastVisitTimes: this.npcPrivacySystem.lastVisitTimes
+ }
+ }
+ };
+ }
+
+ /**
+ * Load all systems state
+ */
+ loadAllSystems(saveData) {
+ if (!saveData || !saveData.systems) {
+ console.warn('No save data to load');
+ return false;
+ }
+
+ try {
+ const systems = saveData.systems;
+
+ // Load sleep system
+ if (systems.sleep) {
+ systems.sleep.unlockedBeds.forEach(bedKey => {
+ this.sleepSystem.bedTypes[bedKey].unlocked = true;
+ });
+ }
+
+ // Load crafting system
+ if (systems.crafting) {
+ this.craftingSystem.tables.LARGE.unlocked = systems.crafting.largeTableUnlocked;
+ this.craftingSystem.unlockedRecipes = systems.crafting.unlockedRecipes || [];
+ }
+
+ // Load bakery
+ if (systems.bakery) {
+ this.bakerySystem.isUnlocked = systems.bakery.isUnlocked;
+ this.bakerySystem.birthdayCakeOrders = systems.bakery.birthdayOrders || [];
+ }
+
+ // Load barber
+ if (systems.barber) {
+ this.barberSystem.isUnlocked = systems.barber.isUnlocked;
+ this.barberSystem.playerAppearance = systems.barber.playerAppearance;
+ this.barberSystem.savedLooks = systems.barber.savedLooks || [];
+ this.barberSystem.visitCount = systems.barber.visitCount || 0;
+ }
+
+ // Load lawyer
+ if (systems.lawyer) {
+ this.lawyerSystem.isUnlocked = systems.lawyer.isUnlocked;
+ this.lawyerSystem.hasPrenup = systems.lawyer.hasPrenup || false;
+ this.lawyerSystem.divorceHistory = systems.lawyer.divorceHistory || [];
+ this.lawyerSystem.counselingInProgress = systems.lawyer.counselingInProgress || false;
+ }
+
+ // Load zombie miners
+ if (systems.zombieMiners) {
+ this.zombieMinerSystem.zombieMiners = systems.zombieMiners.miners || [];
+ this.zombieMinerSystem.zombieEquipment = systems.zombieMiners.equipment || {};
+ this.zombieMinerSystem.lastCollectionTime = systems.zombieMiners.lastCollectionTime;
+ this.zombieMinerSystem.updateAutomationYield();
+ }
+
+ // Load town growth
+ if (systems.townGrowth) {
+ this.townGrowthSystem.population = systems.townGrowth.population;
+ this.townGrowthSystem.populationSlots = systems.townGrowth.populationSlots;
+ this.townGrowthSystem.villages = systems.townGrowth.villages || [];
+ this.townGrowthSystem.services = systems.townGrowth.services || {};
+ }
+
+ // Load NPC privacy
+ if (systems.npcPrivacy) {
+ this.npcPrivacySystem.npcHomes = systems.npcPrivacy.npcHomes || {};
+ this.npcPrivacySystem.visitHistory = systems.npcPrivacy.visitHistory || {};
+ this.npcPrivacySystem.lastVisitTimes = systems.npcPrivacy.lastVisitTimes || {};
+ }
+
+ console.log('โ
All systems loaded from save data');
+ return true;
+
+ } catch (error) {
+ console.error('โ Error loading systems:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Get all systems status (for debug/admin panel)
+ */
+ getAllSystemsStatus() {
+ return {
+ sleep: {
+ active: true,
+ currentBed: this.sleepSystem.playerBed.name,
+ isSleeping: this.sleepSystem.isSleeping
+ },
+ crafting: {
+ active: true,
+ currentTable: this.craftingSystem.currentTable.name,
+ isCrafting: this.craftingSystem.isCrafting,
+ queueLength: this.craftingSystem.craftingQueue.length
+ },
+ bakery: {
+ active: this.bakerySystem.isUnlocked,
+ isOpen: this.bakerySystem.isOpen,
+ stockLevel: Object.values(this.bakerySystem.inventory)
+ .reduce((sum, item) => sum + item.stock, 0)
+ },
+ barber: {
+ active: this.barberSystem.isUnlocked,
+ isOpen: this.barberSystem.isOpen,
+ currentStyle: this.barberSystem.playerAppearance.hairstyle
+ },
+ lawyer: {
+ active: this.lawyerSystem.isUnlocked,
+ counselingActive: this.lawyerSystem.counselingInProgress,
+ hasPrenup: this.lawyerSystem.hasPrenup
+ },
+ zombieMiners: {
+ active: this.zombieMinerSystem.automationActive,
+ minerCount: this.zombieMinerSystem.zombieMiners.length,
+ yieldPerHour: this.zombieMinerSystem.totalYieldPerHour
+ },
+ townGrowth: {
+ active: true,
+ population: this.townGrowthSystem.population,
+ maxPopulation: this.townGrowthSystem.maxPopulation,
+ status: this.townGrowthSystem.getTownStatus()
+ },
+ npcPrivacy: {
+ active: true,
+ homesGenerated: Object.keys(this.npcPrivacySystem.npcHomes).length
+ }
+ };
+ }
+}
+
+
diff --git a/RECOVERY_LOG.md b/RECOVERY_LOG.md
new file mode 100644
index 000000000..67d9ba4af
--- /dev/null
+++ b/RECOVERY_LOG.md
@@ -0,0 +1,21 @@
+
+# ๐ต๏ธ AGENT RECOVERY LOG - JANUARY 16, 2026
+**Mission:** TOTAL SYSTEM RECOVERY
+**Status:** SUCCESS โ
+
+## ๐ RECOVERED ARTIFACTS
+1. **`DEBUG_TOTAL_RECOVERY/`**: Contains core logic files (Zombie AI, Town Restoration).
+2. **`MASTER_RECOVERY/`**: Contains the "Brain" (`MasterGameSystemsManager.js`) and "Protector" (`GameManager.js`).
+3. **`MASTER_DESIGN_RECOVERY/`**: Contains Story, Endings, and Animal AI scripts.
+4. **`EMERGENCY_SYSTEMS_RECOVERY/`**: Contains ALL 15+ detected system files (Economy, Weather, NPC).
+
+## ๐ DOCUMENTATION
+- **`GAME_BIBLE_3.md`**: The definitive "Total Recovery Edition" design document.
+- **`GAME_BIBLE_2.md`**: Legacy design document (Fully updated).
+
+## ๐ NEXT STEPS (AA PRODUCTION)
+1. **Review:** David to review `GAME_BIBLE_3.md` and `EMERGENCY_SYSTEMS_RECOVERY`.
+2. **Integration:** Begin integrating restored systems into `Phase 2` build.
+3. **Launch:** Target March 2026 for AA Demo.
+
+*Agent signing off. Code is safe.*
diff --git a/assets/sprites/smooth/kai_main.png b/assets/sprites/smooth/kai_main.png
new file mode 100644
index 000000000..f6125af21
Binary files /dev/null and b/assets/sprites/smooth/kai_main.png differ
diff --git a/assets/sprites/smooth/kai_style32.png b/assets/sprites/smooth/kai_style32.png
new file mode 100644
index 000000000..aa78b154d
Binary files /dev/null and b/assets/sprites/smooth/kai_style32.png differ
diff --git a/assets/sprites/smooth/mine_style32.png b/assets/sprites/smooth/mine_style32.png
new file mode 100644
index 000000000..699057502
Binary files /dev/null and b/assets/sprites/smooth/mine_style32.png differ
diff --git a/assets/sprites/smooth/town_style32.png b/assets/sprites/smooth/town_style32.png
new file mode 100644
index 000000000..77bedfebc
Binary files /dev/null and b/assets/sprites/smooth/town_style32.png differ
diff --git a/docs/game_design/GAME_BIBLE_2.md b/docs/game_design/GAME_BIBLE_2.md
index 81fe07608..6a665fc4c 100644
--- a/docs/game_design/GAME_BIBLE_2.md
+++ b/docs/game_design/GAME_BIBLE_2.md
@@ -1502,8 +1502,188 @@ Each crop will have:
### Weapons (10 types ร 9 materials = 90)
-**Types**: Sword, Dagger, Spear, Battle Axe, Mace, War Hammer, Bow, Crossbow, Staff, Wand
-**Materials**: Wood, Stone, Copper, Iron, Steel, Gold, Diamond, Mythril, Adamantine
+### โ๏ธ **COMBAT & WEAPONS (MEDIEVAL FANTASY)**
+
+**Philosophy:** Game is fantasy-focused. No modern guns (Pistol/Rifle โ) except ONE special case.
+
+#### **๐ซ BANNED WEAPONS:**
+โ Pistol, Rifle, SMG, Sniper, RPG, Ray Gun.
+
+#### **๐ซ THE ONLY GUN: SHOTGUN (Chernobyl Exclusive)**
+- **Unlock:** Found in Chernobyl Military Checkpoint (Late Game).
+- **Ammo:** Shells (Expensive Crafting: Gunpowder + Iron + Brass).
+- **Stats:** High spread damage, slow reload (2 shots).
+- **Use:** Emergency defense against hordes.
+
+#### **๐น RANGED ARSENAL (21 Arrow Types)**
+**Bows:** Primitive -> Hunting -> Longbow -> Compound -> Enchanted -> Elven -> Phoenix -> **Dragon Bow**.
+
+**Arrows (Craftable):**
+1. **Basic:** Wood, Stone, Iron, Steel, Mythril.
+2. **Elemental:** Fire (Burn), Ice (Freeze), Lightning (Chain), Poison (DoT), Explosive (Area), Shadow (Blind), Holy (vs Undead), Nature (Root).
+3. **Special:** Grappling (Climb), Teleport (Blink), Healing (Ally), Vampire (Life Steal), Tracking (Auto-Aim), Drill (Pierce Armor), Rainbow (Random).
+
+---
+
+### ๐ง **MEMORY & TWIN BOND SYSTEM (DEEP DIVE)**
+
+#### **1. FARMING AS THERAPY (Passive Recovery) โ
**
+*Action = 5% Chance of Memory Flashback.*
+- **Planting:** Remembers Father's gardening lessons. (+XP)
+- **Watering:** Remembers Ana's science facts. (+Crop Speed)
+- **Harvesting:** Remembers Mother's harvest festival. (+HP)
+
+#### **2. STORY FLASHBACKS (The 6 Pillars) โ
**
+*Unlocking these strengthens the Twin Bond.*
+1. **Twin Bond Discovery (Lvl 5):** Hospital scene, doctors realize twins share pain.
+2. **First Protection (Clues 5/50):** Playground fight, young Kai protects Ana.
+3. **Mother's Last Words (Lvl 15):** Outbreak Day 3, Mother sacrifices herself.
+4. **The Kidnapping (Lvl 25):** Troll King attack, Kai is knocked out, Ana taken.
+5. **Dr. Krniฤ's Plan (Clues 35/50):** Security footage, Krniฤ reveals he MADE the virus.
+6. **Ana's Final Message (Clues 50/50):** Video diary from Chernobyl ("I'm alive... Come get me.").
+
+---
+
+--------------------------------------------------------------------------------
+### โฌโฌโฌ CORE SYSTEMS (CODE IMPLEMENTED) โฌโฌโฌ
+--------------------------------------------------------------------------------
+
+#### **1. MICRO FARM SYSTEM (`MicroFarmSystem.js`) โ
**
+- **Start:** 8x8 Tile Grid (Locked by fog/overlay).
+- **Expansion:** Pay Gold to unlock 2x2 sections (North, South, East, West).
+- **Land Types:** Grass (Free), Forest (Needs Axe), Rocky (Needs Pickaxe), Swamp (Needs Drainage).
+
+#### **2. ZOMBIE WORKER AI (`ZombieWorkerSystem.js` & `ZombieEconomySystem.js`) โ
**
+- **Roles:** Farmer (Water/Harvest), Miner (Stone), Clearer (Trees), **Sanitation (Clean City)**.
+- **Universal Lending:** Rent Zombies to **ANY** town NPC (Baker, Tailor, Teacher...) for profit.
+- **Payment:** Earn **Gold** OR **Rare Gifts** (*Kai's Hidden Photo, Ancient Dye, Rare Seed Pack, Mystical Paint, Family Heirloom*).
+- **City Sanitation:** Zombies scrub graffiti & pick up trash -> Boosts City Happiness.
+- **Maintenance:** Workers consume **Brains** (Fuel). Low Brains = Slow/Grumbling.
+
+#### **3. ZOMBIE SCOUT EVOLUTION (`ZombieScoutLevelingSystem.js`) โ
**
+*The Scout is a full RPG Companion.*
+- **Level Cap:** 1-20. Gathers XP from Combat, Digging, and Quests.
+- **Level 20:** Evolves into **"Scout Commander"** (Larger, Stronger, New Skin).
+- **Skill Tree:**
+ - **Combat:** *Claw Swipe* (Dmg), *Leap Attack* (Jump Dmg), *Undead Resilience* (Tank).
+ - **Digging:** *Speed Dig* (Fast), *Treasure Sense* (See buried loot).
+ - **Utility:** *Pack Mule* (+10 Inv Slots), *Night Vision*, *Scout Sprint*.
+
+#### **4. VISUAL & ACCESSIBILITY (`VisualEnhancementSystem.js`) โ
**
+- **Animated Textures:** Water flow (4-frame wave), Fire flickering, Tree rustling.
+- **Atmosphere:** Multi-layered **Noir Fog**, Rain/Snow particles, Dynamic Lightning (Radial gradients).
+- **Typewriter Effect:** Dynamic text reveal with "ADHD Mode" (Instant/Fast/Normal speeds).
+- **Lighting:** Torches flicker (0.4-0.6 alpha), Day/Night cycle shadows.
+
+#### **5. MASTER SYSTEM ARCHITECTURE (`MasterGameSystemsManager.js`) ๐**
+*Center of Operation. Coordinates 15+ sub-systems.*
+- **Cross-System Events:** Marriage unlocks Lawyer; Building Bakery unlocks Food Orders.
+- **Automation:** Checks Zombie Miners every 4 hours for yield collection.
+- **State Management:** Handles saving/loading for EVERY system in one JSON object.
+
+#### **6. THE SILENT PROTECTOR (`GameManager.js`) ๐พ**
+*Never loose progress.*
+- **Auto-Save Triggers:** Scene Change, Aging Up, Memory Found, Gronk Level Up.
+- **Safety:** Periodic 5-minute backup saves.
+- **Visuals:** Spinning Longboard icon when saving.
+
+#### **7. CORE CODE VARIABLES (GAME DNA) ๐งฌ**
+*The heartbeat of the logic.*
+- `var is_ana_found = false` (Story State: Twin Bond active?)
+- `var zombie_labor_count = 0` (Economy: Total active undead workers)
+- `func start_amnesia_effect()` (Visuals: The Blur/Fog state on wakeup)
+- `var current_biom = "Nova Farma"` (World: Tracks location across 18 biomes)
+
+#### **8. ANIMAL COMPANION AI (`WorkerCreaturesSystem.js`) ๐ฆ๐ฆ**
+*Alpha Hybrid powers control nature.*
+- **Night Owl:** Spawns at 00:00. Scouting + Gift Drops.
+- **Bat Swarm:** Spawns at Dusk. Resource Gathering + Radar.
+- **T-Rex:** *Deprecated AI (Removed per user request).*
+
+#### **9. THE 4 ENDINGS (`MainQuestAnaSystem.js`) ๐ฌ**
+1. **Sacrifice:** Kai dies to save Ana.
+2. **Survival:** Both live but the town falls.
+3. **Ascension:** Kai becomes the Zombie King.
+4. **UTOPIA (True Ending):** Save Ana + 180 NPCs rescued.
+
+#### **10. SYSTEM RECOVERY INDEX (ALL 15+ SYSTEMS) ๐จ**
+*All systems confirmed active in `EMERGENCY_SYSTEMS_RECOVERY/`*
+* **Economy:** `InventorySystemExpanded.js`, `NPCShopSystem.js`, `DrugEconomySystem.js`
+* **World:** `MasterWeatherSystem.js` (Noir Rain/Snow), `TimeSystem.js` (Seasons)
+* **NPCs:** `NPCPopulationSystem.js` (180 Unique IDs), `NomadRaiderAI.js`
+* **Shops:** `BakeryShopSystem.js`, `BarberShopSystem.js`, `BlackMarket`
+
+#### **11. TECHNICAL STACK (ENGINE) ๐ป**
+*Built for speed and 2D perfection.*
+- **Engine:** **Phaser 3** (JavaScript/WebGL).
+- **Wrapper:** **Electron** (Desktop App .exe/.dmg).
+- **Maps:** **Tiled** (.tmx) & **LDtk**.
+- **Art:** Style 32 Vector (No Pixelation).
+
+---
+
+--------------------------------------------------------------------------------
+### ๐ฎ MYSTERY & STORY MECHANICS (RECOVERED LOGIC) ๐ฎ
+--------------------------------------------------------------------------------
+
+#### **1. AGING SYSTEM (Protagonists Only) โณ**
+*Only **Kai & Ana** physically age as the game progresses (Years 2038-2042).*
+- **Visuals:** Sprites get older, scars heal/fade, hair grows.
+- **Effect:** Stats change slightly (Strength up, Speed down) as they mature.
+
+#### **2. THE GHOST TRINITY (3 Distinct Types) ๐ป**
+1. **Ghost Parents:** Benevolent spirits that guide Kai to story clues (Non-hostile).
+2. **Drug Hallucinations:** Visual distortions caused by "Zombie Drugs"/Mushrooms. (False realities).
+3. **Graveyard Spirits:** Restless NPC souls in the Church Graveyard (Hostile/Neutral).
+
+#### **3. TWIN BOND: PULSE & DE-BLUR ๐ง **
+*Revised Logic: No magic attacks.*
+- **Twin Pulse:** Screen shakes/vibrates when near a Memory Clue or Ana's location.
+- **Amnesia De-Blur:** The world starts **BLURRED**. Finding memories (Flashbacks) permanently **clears the vision** (De-Blur effect).
+
+#### **4. GRONK'S VAPE MECHANICS ๐จ**
+*Not just a skin - a Gameplay Utility.*
+- **Supervisor Mode:** Gronk leans on fences & vapes while watching Zombies farm.
+- **Smoke Signals:** Gronk uses vape clouds to signal safe paths or hidden items.
+- **Vape Shield:** Creates a temporary smoke screen to hide from zombies.
+- **Chill Zone:** Vape cloud regenerates Stamina for nearby allies.
+
+#### **5. INTRO SEQUENCE: "THE AWAKENING" ๐ฌ**
+- **Visuals:** Complete Blur -> Eye Blink effect -> Slow focus.
+- **Audio:** Muffled sounds -> Ringing -> Sharp reality.
+- **Context:** Kai wakes up with total Amnesia; the player must "unlock" his sight.
+
+---
+
+--------------------------------------------------------------------------------
+### ๐๏ธ TOWN RESTORATION LOGIC (TownRestorationLogic.js) ๐๏ธ
+--------------------------------------------------------------------------------
+*Building restoration directly unlocks specific NPCs & mechanics.*
+
+| Building | Unlocked NPC | Mechanic / Benefit |
+|----------|-------------|--------------------|
+| **Hospital** ๐ฅ | **Dr. Ana** | Unlocks Healing & Med Kits. |
+| **Police Station** ๐ | **Sheriff/Guard** | Unlocks Patrols & Security. |
+| **Mayor's Office** ๐๏ธ | **ลฝupan (Mayor)** | Unlocks Elections & City Management. |
+| **Church** โช | **ลฝupnik (Priest)** | Unlocks Blessings & Graveyard Access. |
+| **School** ๐ซ | **Uฤitelj (Teacher)** | Unlocks Education Buffs. |
+| **Tech Workshop** โ๏ธ | **Tehnik** | Unlocks Electronics Crafting. |
+| **Tailor Shop** ๐งต | **ล ivilja** | Unlocks Armor/Clothing Upgrades. |
+| **Bakery** ๐ฅ | **Pek (Baker)** | Unlocks High-Energy Food. |
+
+
+
+#### **2. GLOBAL RESTORATION SCOPE (27 TOWNS / 150 BUILDINGS)**
+*The project spans 18 Biomes. Restoring towns unlocks global bonuses.*
+- **Key Towns:** Hope Valley (Start), Forest Grove, Desert Oasis, Frozen Harbor, Volcanic Refuge, Coastal Bay, Mountain Peak, Swamp Village, Crystal City, Atlantis.
+- **Endgame Goal:** Rescue all **180 NPCs**.
+
+#### **3. RESTORATION MILESTONES ๐**
+- **10 NPCs:** Unlock `Community Center`.
+- **25 NPCs:** Unlock `Town Festivals`.
+- **50 NPCs:** Unlock `Town Guard` (Security).
+- **100 NPCs:** Unlock `Major City Project` (Skyscrapers).
+- **180 NPCs:** **UTOPIA ENDING** (True Pacifist Win).
### Armor (6 pieces ร 7 materials = 42)
@@ -2712,376 +2892,107 @@ if (detectStreamerMode()) {
---
-### ๐ฏ PHASE BREAKDOWN
+### ๐ฏ PHASE BREAKDOWN (EPISODIC MODEL)
-#### **๐ฏ DEMO PHASE** (Kickstarter Demo - Vertical Slice)
-**Target:** Showcase core gameplay loop + emotional story hook
-**Duration:** 10-15 minutes of gameplay
-**Goal:** Convince backers this game is REAL and AMAZING!
+**Structure:** 10 Episodes (Fazas) released over time.
+**Pricing:** Demo (Free) -> Early Access (Ep 1-2) -> Full Game (Ep 10).
---
-### ๐ฌ **DEMO FLOW (Step-by-Step)**
+#### **๐ฎ DEMO (Free Trial)**
+**Goal:** Hook the player, show mechanics, limit progress.
-#### **1. OPENING SCENE** (30 seconds)
-- โ
Kai wakes up in ruined basement (amnesia!)
-- โ
Finds ID card: "Kai Markoviฤ, Age: 14"
-- โ
Sees blood on wall, feels headache
-- โ
"I don't remember anything... but I need to survive."
+**๐ Map & Scope:**
+- **Restricted:** Home Farm Only (8x8 Micro Plot).
+- **Megla:** Invisible walls/fog prevent leaving the farm.
-#### **2. TUTORIAL: ZOMBIE CONTROL** (2-3 minutes)
-**CORE MECHANIC SHOWCASE:**
+**๐จ Restrictions (The "Tantalizer" Nerf):**
+- **Housing:** Sleeping Bag -> Tent -> **Small Shack (MAX)**. Cannot build Mansion.
+- **Tools:** **Wood Tier Only** (break fast).
+- **Yields:** 1 Seed = 1 Crop + 1 Seed (No massive profit).
+- **Trees:** 1 Tree = 1 Log + 1 Sapling (Slow resource gain).
+- **Sleep:** No automatic bed. Must craft Tent/Shack to save/sleep.
-- ๐ฏ **Step 1:** Meet first zombie (Basic Zombie, wandering)
-- ๐ฏ **Step 2:** Press [E] to "Control" zombie (Alpha Hybrid power!)
-- ๐ฏ **Step 3:** Zombie eyes change (white โ red with pupils = controlled!)
-- ๐ฏ **Step 4:** Give first command: "DIG" on soil patch
-- ๐ฏ **Step 5:** Zombie digs slowly, finds buried item (Ana's bracelet!)
-- ๐ฏ **Step 6:** "This feels... familiar. But I can't remember why."
+**๐ Starter Kit (Free):**
+- 1x Wooden Axe, Pickaxe, Hoe, Watering Can.
+- 1x Torch, 1x Bread.
+- 2x Wheat Seeds, 2x Carrot Seeds.
+- **3x Cannabis Seeds** (Taste of the economy!).
-**ASSETS NEEDED:**
-- โ
Kai (idle, walk) - 10 sprites
-- โ Basic Zombie (idle, walk, dig) - 15 sprites ๐ด
-- โ Soil patch tile + dug version - 2 assets ๐ด
-- โ Ana's bracelet (item sprite) - 1 asset ๐ด
+**๐ง Characters & Story:**
+- **Zombi Statistiฤar:** Stands at gate. Holds sign "Population: 0". Explains you need full game to build town.
+- **Quest:** Intro (Amnesia), Control First Zombie ("DIG" command), Find Ana's Bracelet.
---
-#### **3. FIRST MEMORY FLASHBACK** (45 seconds)
-**EMOTIONAL STORY HOOK:**
+#### **๐ฅ EPISODE 1: FAZA 1 (Early Access Start)**
+**Goal:** Establish Base, First Biomes, Survival.
-- ๐ฏ Screen fades to sepia/flashback filter
-- ๐ฏ **MEMORY 1/50 UNLOCKED:** "Ana's Birthday"
-- ๐ฏ Flashback scene: Young Kai (age 8) gives bracelet to Ana
-- ๐ฏ Ana: "I'll never take it off, I promise!"
-- ๐ฏ Kai (present): "Ana... who is Ana? Why does this hurt so much?"
-- ๐ฏ Twin Bond UI appears (damaged, 1/50 bars filled)
+**๐ Map & Scope:**
+- **Unlocked Biomes (4):** Grassland (Base), Dark Forest, Desert, Swamp.
+- **Bugs/Insects:** Catching enabled immediately (nets).
-**ASSETS NEEDED:**
-- โ Flashback background (farm, daytime, happy) - 1 asset ๐ด
-- โ
Young Kai (chibi, age 8) - Can generate 1 sprite ๐ด
-- โ
Young Ana (chibi, age 8, blonde) - Can generate 1 sprite ๐ด
-- โ Twin Bond UI (damaged heart, 1/50) - 1 UI element ๐ด
+**๐ Paid Starter Kit (Premium):**
+- **Reinforced Tools** (Iron/Steel).
+- **Automatic Sleeping Bag** (Don't need to craft first bed).
+- 10x Wheat, 10x Carrot, 5x Potato Seeds.
+- **10x Cannabis Seeds**.
+- More Food (5 Bread).
+
+**๐ First 40 Players Bonus (Early Adopter):**
+- **INSTANT GRONK:** Gronk appears Day 1 (no quest needed).
+- **Roles:**
+ - **Builder:** Helps build structures 2x faster.
+ - **Carrier:** Huge inventory, carries items to sell.
+
+**๐๏ธ Systems Unlocked:**
+- **Basement Lvl 1:** Zombie Containment Cell (Capture zombies).
+- **Basement Lvl 2:** **Grow Room** (Indoor Cannabis/Mushroom farm).
+- **Night Delivery:**
+ - ๐ฆ **Owls:** Deliver Packages/Rewards.
+ - ๐ฆ **Bats:** Deliver Quests/Letters.
+- **Combat:** Bows & Arrows key.
---
-#### **4. TOWN DISCOVERY** (2 minutes)
-**TOWN SYSTEM INTRO:**
+#### **โก EPISODE 2: FAZA 2 (Town Restoration)**
+**Goal:** Rebuild Civilization, Town Management, Crime.
-- ๐ฏ Kai walks to nearby ruined town (Tutorial Town)
-- ๐ฏ Sign: "Tutorial Town - Population: 0" (wooden sign, faded paint)
-- ๐ฏ Empty streets, broken buildings
-- ๐ฏ Find "Town Hall" (small, run-down building)
-- ๐ฏ Inside: Population Board (shows "0 Citizens")
-- ๐ฏ "This place is dead... but maybe I can bring it back to life?"
+**๐ Map & Scope:**
+- **Town Unlocked:** The main Hub town becomes accessible.
+- **Surrounding Biomes:** Gradual expansion.
-**ASSETS NEEDED:**
-- โ Tutorial Town entrance (ruined gate) - 1 asset ๐ด
-- โ Population Sign (wooden, "Population: 0") - 1 asset ๐ด
-- โ Town Hall (exterior, small, ruined) - 1 building ๐ด
-- โ Town Hall (interior, 1 room) - 1 interior tileset ๐ด
-- โ Population Board (wooden board, "0" written) - 1 UI/prop ๐ด
+**๐๏ธ BUILD-TO-SPAWN Mechanic:**
+*NPCs do not appear randomly. You must build their workplace to summon them.*
+1. **Build Big Generator** (Needs Fuel!) -> Spawns **Elektriฤar**.
+2. **Build Police Station** -> Spawns **Sheriff** (No Gun, Baton/Jail only).
+3. **Build Bakery** -> Spawns **Pek** (Baker).
+4. **Build School** -> Spawns **Teacher**.
+5. **Build Town Hall** -> Spawns **ลฝupan** (Mayor) -> Enables **ORDER** (Before this: Chaos).
+
+**๐ก Advanced Systems:**
+- **Generator Fuel:** Must keep adding Bio-Fuel/Gas to Big Generator or lights go out (Zombies attack!).
+- **Immigration Board:**
+ - Managed by Zombi Statistiฤar.
+ - Donate Resources -> Build Racial Housing -> **Elves/Gnomes/Axolotls move in!**
+ - Real-time Population Counter.
+- **City Walls & Watchtowers:** Passive defense.
+
+**๐ Crime & Police System:**
+- **Zombie Drug Dealer:**
+ - Produce Drugs in Basement Lvl 3 (Lab).
+ - Send **Zombie Runner** to town to sell (Stealth).
+- **Police Risks:**
+ - Sheriff **RAIDS** your basement if suspicion is high.
+ - Sheriff **ARRESTS** your Zombie Dealer (You lose the zombie + stash).
+ - *Note:* Police do NOT have guns. They use batons/tasers.
+
+**๐๏ธ Politics:**
+- **Town Hall:** Unlocks laws and taxes.
+- **Elections:** Mayor is usually set (The "Mayor" NPC), but ensures stability.
---
-#### **5. FIRST SURVIVOR ARRIVES** (2 minutes)
-**POPULATION TRACKING SHOWCASE:**
-
-- ๐ฏ Kai explores, finds NPC hiding (Bะตk - Baker NPC)
-- ๐ฏ Dialogue: "You're... not a zombie? Are you real?"
-- ๐ฏ Pek: "I saw you control that zombie... are you the Alpha?"
-- ๐ฏ Kai: "Alpha? I don't know what that means..."
-- ๐ฏ **CHOICE:** Convince Pek to join town (simple dialogue tree)
-- ๐ฏ Pek agrees: "Okay, I'll give this town a chance."
-- ๐ฏ **AUTO-WALK:** Pek walks to Town Hall
-- ๐ฏ **VISUAL FEEDBACK:** Population Board updates: **0 โ 1** ๐
-- ๐ฏ Achievement pop-up: "First Citizen!" (+10 XP)
-
-**ASSETS NEEDED:**
-- โ
Pek NPC (master ref exists!) - Need sprites (idle, walk) = 10 sprites ๐ด
-- โ Dialogue UI (portrait frame, text box) - 1 UI set ๐ด
-- โ Population Board (updated, "1" written) - 1 asset ๐ด
-- โ Achievement pop-up UI - 1 UI element ๐ด
-
----
-
-#### **6. ENDING HOOK** (30 seconds)
-**LEAVE THEM WANTING MORE:**
-
-- ๐ฏ Pek: "Hey, I heard rumors... there's a place called Chernobyl. They say people disappear there."
-- ๐ฏ Kai feels Twin Bond pulse (UI flashes!)
-- ๐ฏ Kai: "Chernobyl... I need to go there. But first, I need to rebuild this town."
-- ๐ฏ **DEMO END SCREEN:**
- - "MEMORY: 1/50"
- - "POPULATION: 1"
- - "ANA'S LOCATION: ??"
- - "Continue in FULL GAME - Kickstarter 2026!"
-
-**ASSETS NEEDED:**
-- โ Twin Bond pulse VFX - 1 animation ๐ด
-- โ Demo end screen UI - 1 asset ๐ด
-
----
-
-### ๐ **DEMO ASSET BUDGET (REVISED)**
-
-| Category | Assets | Status | Priority |
-|----------|--------|--------|----------|
-| **Kai Sprites** | 10 | โ Not Started | ๐ด CRITICAL |
-| **Zombie Sprites** | 15 | โ Not Started | ๐ด CRITICAL |
-| **Pek NPC Sprites** | 10 | โ Not Started | ๐ด CRITICAL |
-| **Young Kai/Ana (Flashback)** | 2 | โ Not Started | ๐ด CRITICAL |
-| **Town Buildings** | 3 | โ Not Started | ๐ด CRITICAL |
-| **UI Elements** | 8 | โ Not Started | ๐ด CRITICAL |
-| **Props/Items** | 6 | โ Not Started | ๐ก High |
-| **Biome Tiles (minimal)** | 20 | โ Not Started | ๐ก High |
-| **VFX/Animations** | 3 | โ Not Started | ๐ข Medium |
-| **TOTAL DEMO ASSETS** | **77** | **โ 0/77** | **URGENT!** |
-
-**DEMO vs ORIGINAL ESTIMATE:**
-- โ ~~242 assets~~ (too ambitious!)
-- โ
**77 core assets** (laser-focused vertical slice!)
-
----
-
-### ๐ฏ **DEMO SUCCESS METRICS**
-
-**What backers will experience:**
-1. โ
**Zombie Control** - Unique core mechanic (10/10 hook!)
-2. โ
**Emotional Story** - Memory system + Twin Bond (instant feels!)
-3. โ
**Town Building** - Population tracking (satisfying progress!)
-4. โ
**Beautiful Art** - Style 32 Dark-Chibi Noir (visual WOW!)
-5. โ
**Mystery** - "Where is Ana?" (leaves them wanting MORE!)
-
-**Estimated Kickstarter Conversion:** 30-40% of demo players โ backers
-
----
-
-### ๐จ **DEMO PRODUCTION PRIORITY**
-
-**WEEK 1 (Jan 7-13):**
-1. Generate Kai sprites (10) - Day 1-2
-2. Generate Zombie sprites (15) - Day 2-3
-3. Generate Pek sprites (10) - Day 3-4
-
-**WEEK 2 (Jan 14-20):**
-4. Generate Young Kai/Ana flashback (2) - Day 1
-5. Generate Town buildings (3) - Day 2-3
-6. Generate UI elements (8) - Day 4-5
-
-**WEEK 3 (Jan 21-27):**
-7. Generate Props/Items (6) - Day 1-2
-8. Generate minimal Biome tiles (20) - Day 3-5
-
-**WEEK 4 (Jan 28 - Feb 3):**
-9. Generate VFX/Animations (3) - Day 1-2
-10. Polish & integrate all assets - Day 3-7
-
-**DEMO LAUNCH TARGET:** February 10, 2026 ๐ฏ
-
----
-
-**Status:** ๐ด **BLOCKED** - Need to start asset generation NOW!
-
----
-
-## ๐ RELEASE STRATEGY - EPISODIC DEPLOYMENT
-
-**Model:** Early Access / Episodic Releases
-**Goal:** Release content incrementally, build community, fund development
-**Platform:** Steam Early Access / Itch.io
-
----
-
-### ๐
**RELEASE TIMELINE & CONTENT**
-
-### ๐ฐ **OFFICIAL PRICING - LOCKED DECISION (Jan 12, 2026)**
-
-**NO DLC. NO EPISODES. ONE GAME, ONE PRICE.**
-
-| Release Phase | Price | Content | Duration |
-|---------------|-------|---------|----------|
-| **FREE DEMO** | **FREE** | Farm tutorial, zombie intro | 2-3 hours |
-| **Early Access** | **โฌ10** | Faza 1-2, Incomplete game, Free updates | 30-50 hours |
-| **Full Release v1.0** | **โฌ30** | Complete game (all 4 Fazas) | 100+ hours |
-
----
-
-#### **RELEASE 0: FREE DEMO** ๐ฏ
-**Date:** When ready
-**Price:** **FREE**
-**Duration:** 2-3 hours
-**Purpose:** Marketing tool, community building
-
-**Content:**
-- โ
Basic farming tutorial
-- โ
5 crops (Tomato, Potato, Corn, Wheat, Cannabis)
-- โ
1 animal (Cow)
-- โ
Zombie control introduction
-- โ
Kai's house
-- โ
Shop system (Gronk)
-
----
-
-#### **RELEASE 1: EARLY ACCESS LAUNCH** ๐ฅ
-**Date:** When Faza 1+2 complete
-**Price:** **โฌ10**
-**Duration:** 30-50 hours
-**Status:** In development
-
-**Content:**
-- โ
**FAZA 1 - Farm Complete**
- - 80+ crops
- - 10 farm animals
- - Full tool progression
- - Farm infrastructure
- - Companions (Susi, Zombie workers)
-- โ
**FAZA 2 - Town Restoration**
- - 8-12 NPCs
- - 9 buildings (5 restoration stages each)
- - Town infrastructure
- - Co-op multiplayer (2-4 players)
-
-**Promise to Buyers:** All future updates FREE! (Get v1.0 free when it launches!)
-
----
-
-#### **RELEASE 2: FULL v1.0 LAUNCH** ๐
-**Date:** When Faza 3+4 complete
-**Price:** **โฌ30**
-**Duration:** 100+ hours
-**Status:** Post-Early Access
-
-**Complete Content:**
-- โ
**Everything from Early Access** (Faza 1-2)
-- โ
**FAZA 3 - World Exploration**
- - All 18 biomes
- - Biome-specific enemies & resources
- - Wildlife & creatures
- - Ana's trail (story progression)
-- โ
**FAZA 4 - Endgame & Story Finale**
- - 21+ Boss encounters
- - Ana rescue & reunion
- - Story conclusion
- - Multiple endings
- - New Game+
- - Endgame systems
-
-**Early Access Benefits:** Early Access buyers DON'T pay again! (โฌ10 โ Free v1.0 upgrade)
-
----
-
-### ๐ **PRICING PHILOSOPHY:**
-
-**Early Access (โฌ10):**
-- "Incomplete game, fair price"
-- Support development
-- Save โฌ20 on full release
-- Free updates forever
-
-**Full Release (โฌ30):**
-- Complete game
-- All content included
-- Fair indie game price
-- No hidden costs
-
-**Early Access Savings:** Pay โฌ10 now, get โฌ30 game later = **โฌ20 discount!**
-
----
-
-### ๐ซ **WHAT WE WILL NEVER DO:**
-
-โ **NO** Paid DLC
-โ **NO** Microtransactions
-โ **NO** Season Passes
-โ **NO** Cosmetic packs
-โ **NO** Loot boxes
-โ **NO** Pay-to-win mechanics
-
-โ
**ONLY:** One fair price, complete game, free updates
-
----
-
-### ๐ **CONTENT PROGRESSION:**
-
-| Release | Fazas | NPCs | Biomes | Bosses | Hours | Official Price |
-|---------|-------|------|--------|--------|-------|----------------|
-| **DEMO** | 0 | ~5 | 1 (Grassland) | 0 | 2-3h | FREE |
-| **Early Access** | 1-2 | ~50 | 1 + Town | 0 | 30-50h | **โฌ10** |
-| **Full v1.0** | 1-4 | 200+ | 18 + Town | 21+ | 100h+ | **โฌ30** |
-
----
-
-**Official Decision Date:** January 12, 2026, 00:16 CET
-**Status:** โ
LOCKED - Ne spreminjamo veฤ!
-**Philosophy:** Fair games, fair prices, no bullshit
-| **Episode 1** | 16 | 290 | 118 | 16 | 30-43 | $19.99 |
-| **Episode 2** | 18 | 380 | 124 | 22 | 45-63 | $24.99 |
-| **Final** | 20 | 388 | 109 | 24 | 55-78 | $29.99 |
-
----
-
-### ๐ฏ **DEVELOPMENT STRATEGY**
-
-**Why Episodic?**
-1. โ
**Funding:** Each release funds next development
-2. โ
**Feedback:** Community shapes the game
-3. โ
**Quality:** More time per episode = better quality
-4. โ
**Hype:** Regular releases keep momentum
-5. โ
**Testing:** Catch bugs early, fix before final
-6. โ
**Loyalty:** Early supporters get lifetime value
-
-**Release Cadence:** New content every 6 months
-**Total Dev Time:** ~2.5 years (Feb 2026 - Q2 2028)
-**Community Involvement:** Discord, Reddit, Surveys between releases
-
----
-
-### ๐ **COMMITMENT TO EARLY SUPPORTERS**
-
-**Promise:** Anyone who buys during Early Access (Alpha 1 or Alpha 2) gets **ALL FUTURE UPDATES FREE FOREVER!**
-
-This means:
-- Buy for $9.99 in Alpha 1
-- Get $29.99 worth of content by Final Episode
-- **LIFETIME SAVINGS: $20!** ๐
-
-**Why?** Because you believed in us when we had nothing! โค๏ธ
-
----
-
-**Status:** โ
Release strategy defined and locked!
-
----
-
-#### **๐ฅ PHASE 1** (Alpha 1 - Core Gameplay)
-**Target:** Playable first 10 hours
-**Includes:**
-- โ
166 Biome NPCs (DONE!)
-- ๐ Wild Animals (87% done - 13/15)
-- ๐ Farm Animals (60% done - 6/10)
-- ๐ Chernobyl Mutants (30% done - 3/10)
-- โ 4 Normal Biomes (Grassland, Forest, Desert, Swamp)
-- โ Basic combat, farming, building
-- โ First 20 quests
-
-**Status:** ๐ก **IN PROGRESS** - 20% complete
-
-#### **โก PHASE 2** (Alpha 2 - Content Expansion)
-**Target:** 50+ hours gameplay
-**Includes:**
-- โ All 190 Town NPCs
-- โ Dinosaurs (15 species)
-- โ Mythical Creatures (39 species)
-- โ Bosses (24 total)
-- โ 9 Normal + 8 Magical Biomes
-- โ Railway, Zoo, Museum, School
-- โ 100+ quests
-- โ Town Restoration
-- โ Ana's Story (playable!)
-
-**Status:** ๐ด **NOT STARTED**
-
---
### ๐ CRITICAL PATH TO DEMO
diff --git a/promo/combat.png b/promo/combat.png
new file mode 100644
index 000000000..297102610
Binary files /dev/null and b/promo/combat.png differ
diff --git a/promo/farm.png b/promo/farm.png
new file mode 100644
index 000000000..aa78b154d
Binary files /dev/null and b/promo/farm.png differ
diff --git a/promo/mine.png b/promo/mine.png
new file mode 100644
index 000000000..699057502
Binary files /dev/null and b/promo/mine.png differ
diff --git a/promo/novafarma_teaser.webp b/promo/novafarma_teaser.webp
new file mode 100644
index 000000000..c232d6b7f
Binary files /dev/null and b/promo/novafarma_teaser.webp differ
diff --git a/promo/town.png b/promo/town.png
new file mode 100644
index 000000000..77bedfebc
Binary files /dev/null and b/promo/town.png differ
diff --git a/src/scenes/GameScene.js b/src/scenes/GameScene.js
index 9f5f81ed5..0d400d2af 100644
--- a/src/scenes/GameScene.js
+++ b/src/scenes/GameScene.js
@@ -18,12 +18,31 @@ class GameScene extends Phaser.Scene {
this.townRestorationSystem = null;
}
+ preload() {
+ // ๐จ STYLE 32 ASSET PIPELINE (SMOOTH 512px)
+ // Loading the new high-res vector assets
+
+ // Load Kai (Style 32 - Smooth)
+ this.load.image('player_style32', 'assets/sprites/smooth/kai_main.png');
+
+ // Load Backgrounds (Style 32 - Noir)
+ this.load.image('bg_farm', 'assets/sprites/smooth/kai_style32.png'); // Using farm mockup as BG for now
+ this.load.image('bg_town', 'assets/sprites/smooth/town_style32.png'); // Using town mockup
+
+ // Load UI/Zombie Assets (Style 32)
+ this.load.image('zombie_miner', 'assets/sprites/smooth/mine_style32.png'); // Placeholder till we slice
+
+ console.log('โจ Style 32 Smooth Assets Loaded');
+ }
+
async create() {
console.log('๐ฎ GameScene: Initialized!');
// ๐ผ๏ธ SETUP BACKGROUND (Direct Farm Image)
- // Checks 'farm_background' (from Phase 1) or fallback 'intro_chaos'
- const bgKey = this.textures.exists('farm_background') ? 'farm_background' : 'intro_chaos';
+ // Checks 'farm_background' (from Phase 1) or fallback 'bg_farm' (New Style 32)
+ const bgKey = this.textures.exists('bg_farm') ? 'bg_farm' :
+ this.textures.exists('farm_background') ? 'farm_background' : 'intro_chaos';
+
if (this.textures.exists(bgKey)) {
this.add.image(0, 0, bgKey)
.setOrigin(0, 0)
diff --git a/src/systems/VisualEnhancementSystem.js b/src/systems/VisualEnhancementSystem.js
index 25e851d94..eea6f5e57 100644
--- a/src/systems/VisualEnhancementSystem.js
+++ b/src/systems/VisualEnhancementSystem.js
@@ -266,6 +266,83 @@ class VisualEnhancementSystem {
return { overlay: fogOverlay, particles: fogParticles };
}
+ // ========== NOIR & PSYCHOLOGICAL EFFECTS (AA) ==========
+
+ createVignette() {
+ // Noir Vignette - Dark corners, focus on center
+ const width = this.scene.cameras.main.width;
+ const height = this.scene.cameras.main.height;
+
+ const vignette = this.scene.add.graphics();
+ vignette.setScrollFactor(0);
+ vignette.setDepth(9000); // Very high depth (UI)
+
+ const radius = Math.max(width, height) * 0.8;
+
+ // Create a texture for the gradient simply because fillRadialGradient acts weird with alpha sometimes,
+ // but let's try standard Phaser gradient first.
+ // Actually, for a pure vignette, a black overlay with a distinct "hole" is often better.
+
+ vignette.fillGradientStyle(0x000000, 0x000000, 0x000000, 0x000000, 0, 0, 1, 1);
+ // Fallback: use a simple texture method if gradient fails, but let's stick to a robust approach:
+ // Use a black rectangle with a 'mask' or a pre-made vignette png?
+ // Let's use a code-generated texture for performance and strict control.
+
+ const canvas = document.createElement('canvas');
+ canvas.width = width;
+ canvas.height = height;
+ const ctx = canvas.getContext('2d');
+
+ const grd = ctx.createRadialGradient(width / 2, height / 2, width * 0.2, width / 2, height / 2, width * 0.7);
+ grd.addColorStop(0, "rgba(0,0,0,0)");
+ grd.addColorStop(1, "rgba(0,0,0,0.85)"); // Dark noir edges
+
+ ctx.fillStyle = grd;
+ ctx.fillRect(0, 0, width, height);
+
+ // Add texture to Phaser
+ if (this.scene.textures.exists('vignette_texture')) {
+ this.scene.textures.remove('vignette_texture');
+ }
+ this.scene.textures.addCanvas('vignette_texture', canvas);
+
+ const sprite = this.scene.add.sprite(width / 2, height / 2, 'vignette_texture');
+ sprite.setScrollFactor(0);
+ sprite.setDepth(9000);
+ sprite.setAlpha(0.9);
+
+ this.vignette = sprite;
+ return sprite;
+ }
+
+ createAmnesiaBlur() {
+ // "The Awakening" - Full screen blur that slowly clears
+ // Uses Phaser 3 PostFX
+ if (!this.scene.cameras.main.postFX) return;
+
+ const blur = this.scene.cameras.main.postFX.addBlur(1, 2, 2, 1); // High strength start
+
+ // Tween to clear
+ this.scene.tweens.add({
+ targets: blur,
+ strength: 0,
+ duration: 4000, // 4 seconds awakening
+ ease: 'Power2',
+ onComplete: () => {
+ this.scene.cameras.main.postFX.remove(blur);
+ }
+ });
+
+ // Also fade in generic brightness
+ this.scene.cameras.main.setAlpha(0);
+ this.scene.tweens.add({
+ targets: this.scene.cameras.main,
+ alpha: 1,
+ duration: 2000,
+ ease: 'Linear'
+ });
+ }
+
// ========== LIGHTING SYSTEM ==========
initLightingSystem() {