/** * 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()); } }