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