Files
novafarma/src/systems/VFXSystem.js

333 lines
8.8 KiB
JavaScript

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