# 🎬 VFX & INTERACTIVE EFFECTS SYSTEM **Project:** Mrtva Dolina **Purpose:** Visual feedback, emotional storytelling, player satisfaction **Style:** ADHD-friendly, instant feedback, satisfying --- ## ✨ **1. AMNESIA EFFECT (Kai's Memory System)** ### **Concept:** When Kai finds family heirlooms or Ana's belongings, trigger a **blur-to-clear memory flashback** with audio cues. ### **Visual Effect:** **Stage 1: Discovery** ```javascript // Player finds item (photo, Ana's necklace, family object) onItemFound(familyItem) { // Show blurred image overlay showImage({ src: familyItem.memoryImage, blur: 20, // Heavy gaussian blur opacity: 0.8, fadeIn: 500ms }); // Play mysterious sound playSound('memory_echo.mp3', volume: 0.5); // Show prompt: "E - Remember" (simple text) showPrompt("Pritisni E za spomin"); } ``` **Stage 2: Memory Restoration** ```javascript // Player presses E to "remember" onRememberPressed() { // Blur clears gradually (2 seconds) animateBlur({ from: 20, to: 0, duration: 2000ms, easing: 'easeOut' }); // Play Ana's voice (short clip) playVoice('ana_flashback_01.mp3'); // Example: "Kai, pomnš tisti dan...?" (short, 2-3 sec) // OR play emotional music sting playMusic('flashback_theme.mp3', fadeIn: 500ms); // Show caption (simple text) showCaption({ text: "Spomin odklenjn...", // ADHD-friendly: simple, typo intentional for domestic feel duration: 3000ms, color: '#FFD700' // Gold }); // Update quest log questLog.add('family_memory_' + itemID); } ``` **Stage 3: Reward** ```javascript // After memory clears: - Unlock new location on map (e.g., Capital City) - Add clue to Ana's trail counter (X/50) - Small stat boost (Hope +5%) - Journal entry auto-saved ``` ### **Audio Assets Needed:** - `memory_echo.mp3` - Mysterious hum/echo - `flashback_theme.mp3` - Short emotional music (10-15 sec) - `ana_voice_01-10.mp3` - 10 short voice clips from Ana ### **Visual Assets Needed:** - Family photo (blurred + clear versions) - Ana's necklace (blurred + clear) - Old diary page (blurred + clear) - Kai's childhood toy (blurred + clear) --- ## 🌾 **2. HARVESTING VFX (Stardew Valley Style)** ### **Concept:** When player harvests crops, items **bounce up with sparkles** and fly into inventory. **Instant satisfaction feedback.** ### **Animation Sequence:** **Step 1: Harvest Action** ```javascript onCropHarvested(cropTile) { // 1. Crop disappears from tile cropTile.sprite.fadeOut(200ms); // 2. Item sprite bounces UP let itemSprite = createSprite(crop.harvestedItem); itemSprite.position = cropTile.position; // Bounce animation (arc trajectory) itemSprite.animate({ y: cropTile.y - 40, // Jump up 40px duration: 300ms, easing: 'easeOutQuad' }); // 3. Sparkle particles emitParticles({ position: cropTile.position, count: 8, sprite: 'sparkle_star.png', color: '#FFD700', // Gold sparkles velocity: random(-50, 50), lifetime: 800ms, fadeOut: true }); // 4. Sound effect playSound('harvest_pop.mp3', volume: 0.7); // Happy "pop" or "ding" sound // 5. Fly to inventory (UI corner) itemSprite.animate({ x: inventoryIcon.x, y: inventoryIcon.y, duration: 600ms, easing: 'easeInQuad', onComplete: () => { // Add to inventory inventory.add(crop.harvestedItem); itemSprite.destroy(); // Inventory icon pulse inventoryIcon.pulse(); } }); } ``` ### **Optional: Quality Tier Effects** Different sparkle colors for quality: ```javascript qualityColors = { basic: '#FFFFFF', // White sparkles silver: '#C0C0C0', // Silver sparkles gold: '#FFD700', // Gold sparkles iridium: '#9D00FF' // Purple sparkles (legendary) } ``` ### **Audio Assets Needed:** - `harvest_pop.mp3` - Satisfying "pop" sound - `sparkle_ting.mp3` - Optional twinkle sound ### **Visual Assets Needed:** - `sparkle_star.png` - 8x8px star particle - `glow_particle.png` - 4x4px glow dot --- ## 🐟 **3. WATER LIFE (Fish Jump Animation)** ### **Concept:** Fish occasionally **jump out of water** with splash effect. **Visual hint** for good fishing spots. ### **Animation Sequence:** **Random Fish Jump:** ```javascript // Every 5-15 seconds at fishing spots setInterval(() => { if (Math.random() < 0.3) { // 30% chance spawnFishJump(); } }, randomRange(5000, 15000)); function spawnFishJump() { let waterTile = getRandomWaterTile(); // 1. Fish sprite jumps out let fish = createSprite('fish_jump.png', waterTile.position); fish.animate({ // Arc jump animation keyframes: [ { y: waterTile.y, time: 0 }, { y: waterTile.y - 32, time: 300 }, // Peak of jump { y: waterTile.y, time: 600 } // Back to water ], rotation: [0, 180, 360], // Flip in air easing: 'easeInOutQuad' }); // 2. Splash effect (start + end) createSplash(waterTile.position, scale: 1.0); setTimeout(() => { createSplash(waterTile.position, scale: 1.2); // Bigger splash on landing fish.destroy(); }, 600); // 3. Sound effect playSound('water_splash.mp3', volume: 0.6); // 4. Ripple effect createRipple(waterTile.position, { radius: [0, 48], duration: 1000ms, opacity: [0.8, 0] }); } ``` **Splash Particle Effect:** ```javascript function createSplash(position, scale) { emitParticles({ position: position, count: 12, sprite: 'water_drop.png', color: '#4DB8FF', // Water blue velocity: randomRange(-80, 80), gravity: 200, // Drops fall down lifetime: 500ms, scale: scale, rotation: random(0, 360) }); } ``` ### **Gameplay Integration:** - **Fish jump frequency** indicates fish abundance - More jumps = better fishing spot - Rare fish have unique jump animations (golden sparkle) ### **Audio Assets Needed:** - `water_splash.mp3` - Splash sound - `fish_jump.mp3` - Optional fish "plop" ### **Visual Assets Needed:** - `fish_jump.png` - 16x16px fish mid-jump (Style 32) - `water_drop.png` - 4x4px water droplet particle - `ripple.png` - 32x32px circular ripple ring --- ## 🎥 **4. DYNAMIC VISUALS (Cutscenes & Story Moments)** ### **Cross-Fade Transitions:** ```javascript // Scene transitions (smooth, cinematic) function transitionScene(fromScene, toScene) { // Fade out current scene fromScene.fadeOut({ duration: 1000ms, color: '#000000' // Fade to black }); // Wait for fade setTimeout(() => { // Switch scenes game.scene.stop(fromScene); game.scene.start(toScene); // Fade in new scene toScene.fadeIn({ duration: 1000ms }); }, 1000); } ``` ### **Vignette Effect (Important Moments):** ```javascript // Adds dark edge vignette for dramatic moments function applyVignette(intensity = 0.5) { let vignetteShader = { type: 'radialGradient', center: [screenWidth/2, screenHeight/2], radius: screenWidth * 0.6, colors: [ { offset: 0, color: 'rgba(0,0,0,0)' }, { offset: 1, color: `rgba(0,0,0,${intensity})` } ] }; camera.applyPostFXShader(vignetteShader); } // Use during: - Intro sequence (heavy vignette) - Ana clue discoveries (medium vignette) - Boss fights (light vignette) - Emotional cutscenes (heavy vignette) ``` ### **Blur Effect (Dream/Memory Sequences):** ```javascript // Full-screen blur for dream states function applyDreamBlur() { camera.applyPostFXShader({ type: 'gaussianBlur', strength: 8, quality: 'medium' }); // Desaturate colors slightly camera.applyColorMatrix({ saturation: 0.5, // 50% saturation brightness: 1.1 // Slightly brighter }); } ``` ### **Slow-Motion Effect (Epic Moments):** ```javascript // Slow-motion for dramatic moments function applySlowMotion(duration = 2000, speed = 0.3) { game.time.timeScale = speed; // 30% speed setTimeout(() => { // Return to normal speed tweenValue(game.time.timeScale, 1.0, { duration: 500ms, easing: 'easeOut' }); }, duration); // Optional: Add motion blur camera.applyMotionBlur(strength: 0.5); } // Use during: - Boss defeated moment - Ana rescue cutscene - Major discovery moments ``` --- ## 🎨 **PARTICLE SYSTEM LIBRARY** ### **Reusable Particle Effects:** ```javascript particlePresets = { // Sparkle (harvest, treasure) sparkle: { sprite: 'sparkle_star.png', count: 8, color: '#FFD700', velocity: random(-50, 50), lifetime: 800ms, fadeOut: true }, // Smoke (campfire, forge) smoke: { sprite: 'smoke_puff.png', count: 3, color: '#888888', velocity: { x: random(-10, 10), y: -30 }, lifetime: 2000ms, fadeOut: true, scaleUp: true }, // Magic (enchanting, portals) magic: { sprite: 'glow_particle.png', count: 20, color: '#9D00FF', // Purple velocity: spiral(radius: 40, speed: 2), lifetime: 1500ms, fadeOut: true, glow: true }, // Blood (combat - optional, can be green "zombie goo") blood: { sprite: 'blood_splat.png', count: 6, color: '#00FF00', // Green (zombie blood) velocity: random(-60, 60), gravity: 150, lifetime: 800ms, fadeOut: true }, // Coins (quest rewards, sales) coins: { sprite: 'coin_spin.png', count: 10, color: '#FFD700', velocity: { x: random(-40, 40), y: -80 }, gravity: 200, lifetime: 1200ms, rotation: true, bounce: true } } ``` --- ## 📊 **VFX IMPLEMENTATION PRIORITY** | Effect | Priority | Complexity | Impact | |--------|----------|------------|--------| | **Harvest Sparkles** | ⭐⭐⭐⭐⭐ | Low | High satisfaction | | **Amnesia Blur** | ⭐⭐⭐⭐⭐ | Medium | Emotional storytelling | | **Fish Jump** | ⭐⭐⭐⭐ | Low | World feels alive | | **Cross-Fade** | ⭐⭐⭐⭐ | Low | Professional polish | | **Vignette** | ⭐⭐⭐ | Low | Dramatic moments | | **Slow-Motion** | ⭐⭐ | Medium | Epic boss moments | --- ## 🎯 **PHASER 3 IMPLEMENTATION NOTES** ### **Particle Manager:** ```javascript // src/systems/ParticleManager.js class ParticleManager { constructor(scene) { this.scene = scene; this.emitters = {}; } emit(preset, position, customParams = {}) { let params = { ...particlePresets[preset], ...customParams }; let emitter = this.scene.add.particles(position.x, position.y, params.sprite, params); return emitter; } sparkle(position) { return this.emit('sparkle', position); } smoke(position) { return this.emit('smoke', position); } magic(position) { return this.emit('magic', position); } } ``` ### **Post-FX Pipeline:** ```javascript // Phaser 3 built-in post-processing scene.cameras.main.setPostPipeline('BlurPostFX'); scene.cameras.main.setPostPipeline('VignettePostFX'); ``` --- ## 🎬 **CUTSCENE SYSTEM** ### **Simple Dialogue + VFX:** ```javascript // src/systems/CutsceneManager.js class CutsceneManager { playMemoryFlashback(memoryData) { // 1. Freeze player player.freeze(); // 2. Apply vignette applyVignette(0.7); // 3. Show blurred image showBlurredImage(memoryData.image); // 4. Play Ana's voice playVoice(memoryData.audioClip); // 5. Clear blur after 2 seconds setTimeout(() => { clearBlur(duration: 2000ms); }, 2000); // 6. Show dialogue setTimeout(() => { showDialogue({ text: memoryData.caption, speaker: "Ana", duration: 4000ms }); }, 4000); // 7. Resume game setTimeout(() => { removeVignette(); player.unfreeze(); }, 8000); } } ``` --- ## 🚀 **ASSETS TO GENERATE** ### **Particle Sprites:** - `sparkle_star.png` - 8x8px gold star - `glow_particle.png` - 4x4px white glow - `smoke_puff.png` - 16x16px gray smoke - `water_drop.png` - 4x4px blue droplet - `blood_splat.png` - 8x8px green splat (zombie blood) - `coin_spin.png` - 8x8px gold coin ### **VFX Animations:** - `fish_jump.png` - 16x16px fish sprite - `ripple.png` - 32x32px water ripple ring ### **Audio:** - `memory_echo.mp3` - `flashback_theme.mp3` - `ana_voice_01.mp3` through `ana_voice_10.mp3` - `harvest_pop.mp3` - `sparkle_ting.mp3` - `water_splash.mp3` --- **Status:** 🟢 **READY FOR IMPLEMENTATION** **Estimated Time:** 6-8 hours coding + 2 hours audio/visual assets **Emotional Impact:** 🚀 **MASSIVE** - Game feels alive and responsive!