# 🎯 MRTVA DOLINA v1.0 - IMPLEMENTATION PLAN **Generated**: 2026-01-04 18:52 CET **Mode**: DIRECTOR MODE (Autonomous) **Target**: v1.0 Release Build **Status**: 🟢 READY TO EXECUTE --- ## 📅 TIMELINE **Total Estimated Time**: 6-8 hours **Phases**: 5 **Autonomous**: YES (// turbo-all) --- ## 🔢 PHASE 1: ASSET GENERATION (Style 32) **Duration**: 2 hours **Priority**: HIGH ### Tasks: #### 1.1 Political NPCs - [ ] `lawyer_wrinkled_suit_style32.png` - Odvetnik z aktovko in debelimi očali - [ ] `mayor_post_apo_sash_style32.png` - Župan z lento, post-apo stil #### 1.2 Animation Spritesheets (4 frames each) - [ ] `milking_cow_4frame_style32.png` - Molža krave - [ ] `blacksmithing_4frame_style32.png` - Kovaštvo z iskrami - [ ] `chopping_wood_4frame_style32.png` - Sekanje drv - [ ] `mining_4frame_style32.png` - Rudarjenje #### 1.3 Zombie Workers - [ ] `zombie_worker_purple_eyes_style32.png` - Zombie delavec z vijolično očmi - [ ] `zombie_worker_carrying_style32.png` - Zombie nosi material - [ ] `zombie_worker_mining_style32.png` - Zombie rudari **Output Location**: `/assets/images/STYLE_32_SESSION_JAN_04/` --- ## 🎮 PHASE 2: GAME SYSTEMS IMPLEMENTATION **Duration**: 2 hours **Priority**: HIGH ### Tasks: #### 2.1 Lawyer Service System **File**: `/src/systems/LawyerService.js` ```javascript export class LawyerService { constructor(scene) { this.scene = scene; this.divorceFee = 50000; this.lawyerCut = 0.25; // 25% this.cooldownDays = 7; this.lastDivorce = null; } processDivorce(player, partner) { if (player.gold < this.divorceFee) { return { success: false, reason: 'insufficient_funds' }; } const lawyerCut = this.divorceFee * this.lawyerCut; player.gold -= this.divorceFee; this.scene.economy.lawyerGold += lawyerCut; // Trigger town gossip this.scene.gossipSystem.spread({ type: 'divorce', source: player.name, partner: partner.name, severity: 'high' }); this.lastDivorce = this.scene.time.getCurrentDay(); return { success: true, fee: this.divorceFee, lawyerCut }; } canProcess() { if (!this.lastDivorce) return true; const daysSince = this.scene.time.getCurrentDay() - this.lastDivorce; return daysSince >= this.cooldownDays; } } ``` #### 2.2 Mayor Service System **File**: `/src/systems/MayorService.js` ```javascript export class MayorService { constructor(scene) { this.scene = scene; this.biomePermits = { desert: 10000, mountains: 15000, jungle: 20000, swamp: 12000, arctic: 18000 }; this.taxRate = 0.10; // 10% this.taxInterval = 30; // days this.lastTaxCollection = 0; } issuePermit(player, biome) { const cost = this.biomePermits[biome]; if (!cost) return { success: false, reason: 'invalid_biome' }; if (player.gold < cost) return { success: false, reason: 'insufficient_funds' }; player.gold -= cost; player.biomeAccess[biome] = true; this.scene.economy.mayorGold += cost; return { success: true, biome, cost }; } collectTax(player) { const daysSince = this.scene.time.getCurrentDay() - this.lastTaxCollection; if (daysSince < this.taxInterval) return { success: false, reason: 'too_soon' }; const taxAmount = Math.floor(player.gold * this.taxRate); player.gold -= taxAmount; this.scene.economy.mayorGold += taxAmount; this.lastTaxCollection = this.scene.time.getCurrentDay(); return { success: true, amount: taxAmount }; } } ``` #### 2.3 Zombie Worker Manager **File**: `/src/systems/ZombieWorkerManager.js` ```javascript export class ZombieWorkerManager { constructor(scene) { this.scene = scene; this.workers = []; this.efficiency = 0.75; // 75% of human speed this.maintenanceCostPerDay = 50; } assignTask(zombie, task) { zombie.currentTask = task; zombie.workProgress = 0; zombie.isWorking = true; // Purple eye glow effect zombie.eyes.setTint(0x9D4EDD); // violet zombie.eyes.alpha = 0.8; } update(delta) { this.workers.forEach(zombie => { if (!zombie.isWorking) return; const task = zombie.currentTask; const progressRate = (this.efficiency * delta) / 1000; zombie.workProgress += progressRate; if (zombie.workProgress >= task.duration) { this.completeTask(zombie, task); } }); } completeTask(zombie, task) { // Auto-store resources in base storage this.scene.storage.add(task.resource, task.amount); zombie.isWorking = false; zombie.currentTask = null; // Find next task const nextTask = this.scene.taskQueue.getNext(); if (nextTask) { this.assignTask(zombie, nextTask); } } } ``` #### 2.4 Work Animation System **File**: `/src/systems/WorkAnimationSystem.js` ```javascript export class WorkAnimationSystem { constructor(scene) { this.scene = scene; this.animations = {}; } create() { // Milking animation this.scene.anims.create({ key: 'milking', frames: this.scene.anims.generateFrameNumbers('milking_4frame', { start: 0, end: 3 }), frameRate: 8, repeat: -1 }); // Blacksmithing with sparks this.scene.anims.create({ key: 'blacksmithing', frames: this.scene.anims.generateFrameNumbers('blacksmith_4frame', { start: 0, end: 3 }), frameRate: 6, repeat: -1 }); // Chopping wood this.scene.anims.create({ key: 'chopping', frames: this.scene.anims.generateFrameNumbers('chop_wood_4frame', { start: 0, end: 3 }), frameRate: 7, repeat: -1 }); // Mining this.scene.anims.create({ key: 'mining', frames: this.scene.anims.generateFrameNumbers('mining_4frame', { start: 0, end: 3 }), frameRate: 8, repeat: -1 }); } playAnimation(sprite, animKey) { sprite.play(animKey); // Add particle effects for specific frames if (animKey === 'blacksmithing') { sprite.on('animationupdate', (anim, frame) => { if (frame.index === 2) { // Impact frame this.scene.particles.emitSparks(sprite.x, sprite.y); } }); } } } ``` --- ## 🎬 PHASE 3: CINEMATIC INTRO SYSTEM **Duration**: 2 hours **Priority**: MEDIUM ### Tasks: #### 3.1 Intro Scene with Ken Burns Effect **File**: `/src/scenes/IntroScene.js` ```javascript export class IntroScene extends Phaser.Scene { constructor() { super({ key: 'IntroScene' }); } create() { this.cameras.main.setBackgroundColor('#000000'); // Scene 1: Abandoned farm with Ken Burns zoom this.showScene1(); } showScene1() { const bg = this.add.image(0, 0, 'intro_farm_abandoned').setOrigin(0); bg.setScale(1.2); // Start zoomed in // Ken Burns: slow zoom out + pan this.tweens.add({ targets: bg, scale: 1.0, x: -50, y: -30, duration: 5000, ease: 'Sine.easeInOut' }); // Typewriter text this.showTypewriter( "Leta 2084, Dolina se ni več obranila...", 100, 500, () => this.crossFadeToScene2() ); } showTypewriter(text, x, y, onComplete) { const textObj = this.add.text(x, y, '', { fontSize: '24px', fontFamily: 'Georgia', color: '#FFFFFF', stroke: '#000000', strokeThickness: 4 }); let charIndex = 0; const typeSpeed = 60; // ms per character const timer = this.time.addEvent({ delay: typeSpeed, repeat: text.length - 1, callback: () => { textObj.text += text[charIndex]; charIndex++; if (charIndex === text.length && onComplete) { this.time.delayedCall(2000, onComplete); } } }); } crossFadeToScene2() { this.cameras.main.fadeOut(1500, 0, 0, 0); this.cameras.main.once('camerafadeoutcomplete', () => { this.showScene2(); this.cameras.main.fadeIn(1500, 0, 0, 0); }); } showScene2() { this.children.removeAll(); const bg = this.add.image(400, 300, 'intro_lab_flash').setScale(1.0); this.showTypewriter( "Incident je pustil posledice, ki jih nihče ni pričakoval...", 100, 500, () => this.blurToGameplay() ); } blurToGameplay() { // Blur-to-clear transition const blur = this.cameras.main.postFX.addBlur(0, 2, 2, 10); this.cameras.main.fadeOut(2000, 0, 0, 0); this.tweens.add({ targets: blur, strength: 0, duration: 2000, onComplete: () => { this.scene.start('GameScene'); // Start gameplay } }); } } ``` #### 3.2 Voice Cloning Pipeline **File**: `/tools/voice_clone.py` ```python import edge_tts import asyncio from pydub import AudioSegment from pydub.effects import low_pass_filter, high_pass_filter async def clone_voice_with_radio_filter(text, lang='sl', output_path='narration.mp3'): """ Clone user's voice and apply radio filter """ # Use Edge TTS for voice generation communicate = edge_tts.Communicate(text, voice=f"{lang}-SL-PetraNeural") await communicate.save(output_path) # Apply radio filter (post-apocalyptic aesthetic) audio = AudioSegment.from_file(output_path) # Radio effect: bandpass filter + compression radio_audio = high_pass_filter(audio, 300) # Cut below 300Hz radio_audio = low_pass_filter(radio_audio, 3000) # Cut above 3kHz radio_audio = radio_audio + 3 # Increase volume slightly radio_audio.export(output_path, format="mp3") print(f"✅ Voice generated with radio filter: {output_path}") # Generate all languages languages = { 'sl': 'sl-SL-PetraNeural', # Slovenian (use as base) 'en': 'en-US-GuyNeural', # English 'de': 'de-DE-ConradNeural', # German 'it': 'it-IT-DiegoNeural', # Italian 'zh': 'zh-CN-YunxiNeural' # Chinese } async def generate_all_narrations(): script = "Leta 2084, Dolina se ni več obranila..." for lang_code, voice in languages.items(): await clone_voice_with_radio_filter( script, lang_code, f"assets/audio/intro_{lang_code}.mp3" ) asyncio.run(generate_all_narrations()) ``` --- ## 🖼️ PHASE 4: ACCESSIBILITY & ASSET GALLERY **Duration**: 1.5 hours **Priority**: MEDIUM ### Tasks: #### 4.1 Asset Gallery (Thumbnail Grid) **File**: `/tools/asset_gallery.html` ```html