/** * Drives WHEN boost visual effects play (state machine over the player's * power-up state). All actual particle allocation is delegated to the pooled * ParticleManager (scene.particles), so this class never creates GameObjects. */ export class EffectsManager { constructor(scene) { this.scene = scene; this.speedLineTimer = 0; this.lastState = 'normal'; this.springTrailUntil = 0; } get pm() { return this.scene.particles; } update(player, delta) { const pm = this.pm; if (!pm) return; if (!player || !player.active || player.state === 'dead') { pm.stopFlow(); return; } const state = player.state; const now = this.scene.time.now; const feetY = player.y + player.displayHeight / 2 - 6; if (state === 'rocket' && Math.abs(player.body.velocity.y) > 150) { pm.flameAt(player.x, feetY); this._speedLines(delta); } else if (state === 'propeller' && Math.abs(player.body.velocity.y) > 150) { pm.windAt(player.x, player.y); this._speedLines(delta); } else if (state === 'normal' && now < this.springTrailUntil && player.body.velocity.y < -500) { pm.springAt(player.x, feetY); } else { pm.stopFlow(); } if (this.lastState !== state) { if ((this.lastState === 'rocket' || this.lastState === 'propeller') && state === 'normal') { pm.puff(player.x, player.y); } this.lastState = state; } } _speedLines(delta) { this.speedLineTimer += delta; if (this.speedLineTimer > 65) { this.speedLineTimer = 0; this.pm.speedLine(); } } startBoost(player, type) { if (this.pm) this.pm.boostBurst(player.x, player.y, type); if (type === 'spring') { this.springTrailUntil = this.scene.time.now + 700; } else { this.lastState = type; } } }