From fa34292a2c9e7ae7fcbe4f0d852717487603856a Mon Sep 17 00:00:00 2001 From: Admin Date: Sat, 19 Sep 2026 04:31:04 +0000 Subject: [PATCH] Fix player smoke world-space emission --- src/game-v4.js | 25 ++++++++++++++----------- tests/wiring.test.mjs | 13 ++++++++----- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/game-v4.js b/src/game-v4.js index 8a979c6..6c956b4 100644 --- a/src/game-v4.js +++ b/src/game-v4.js @@ -186,11 +186,13 @@ class GameScene extends Phaser.Scene { smokeTexture.destroy(); } - this.playerSmoke = this.add.particles(this.player.x, this.player.y, 'player-smoke', { - lifespan: { min: 520, max: 920 }, - scale: { start: 0.34, end: 1.15 }, - alpha: { start: 0.24, end: 0 }, + this.playerSmoke = this.add.particles(0, 0, 'player-smoke', { + lifespan: { min: 620, max: 980 }, + scale: { start: 0.5, end: 1.45 }, + alpha: { start: 0.46, end: 0 }, rotate: { min: 0, max: 360 }, + speedX: 0, + speedY: 0, frequency: -1, quantity: 1, }); @@ -214,25 +216,26 @@ class GameScene extends Phaser.Scene { const sternX = this.player.x - forwardX * PLAYER_SMOKE_REAR_OFFSET; const sternY = this.player.y - forwardY * PLAYER_SMOKE_REAR_OFFSET; - this.playerSmoke.setPosition(sternX, sternY); - const emissionIntervalMs = Phaser.Math.Linear(175, 58, forwardThrust); + const emissionIntervalMs = Phaser.Math.Linear(150, 48, forwardThrust); this.playerSmokeAccumulatorMs += deltaMs; while (this.playerSmokeAccumulatorMs >= emissionIntervalMs) { const spread = Phaser.Math.FloatBetween(-0.16, 0.16); const trailHeading = heading + Math.PI + spread; const trailSpeed = Phaser.Math.Linear(16, 46, forwardThrust) * Phaser.Math.FloatBetween(0.82, 1.16); - this.playerSmoke.setParticleSpeed( - Math.cos(trailHeading) * trailSpeed, - Math.sin(trailHeading) * trailSpeed, - ); + const velocityX = Math.cos(trailHeading) * trailSpeed; + const velocityY = Math.sin(trailHeading) * trailSpeed; for (const engineSide of [-1, 1]) { const jitter = Phaser.Math.FloatBetween(-1.5, 1.5); const engineX = sternX + sideX * (PLAYER_SMOKE_ENGINE_OFFSET * engineSide + jitter); const engineY = sternY + sideY * (PLAYER_SMOKE_ENGINE_OFFSET * engineSide + jitter); - this.playerSmoke.emitParticleAt(engineX, engineY, 1); + const particle = this.playerSmoke.emitParticleAt(engineX, engineY, 1); + if (particle) { + particle.velocityX = velocityX; + particle.velocityY = velocityY; + } } this.playerSmokeAccumulatorMs -= emissionIntervalMs; diff --git a/tests/wiring.test.mjs b/tests/wiring.test.mjs index 9327dab..1d87f5f 100644 --- a/tests/wiring.test.mjs +++ b/tests/wiring.test.mjs @@ -210,11 +210,11 @@ test('player uses the four-frame north-facing sheet as a forward propulsion anim assert.doesNotMatch(source, /key: 'player-idle'/); }); -test('player propulsion smoke is a separate world-space Phaser particle effect', async () => { +test('player propulsion smoke emits at world engine positions with per-particle trailing velocity', async () => { const source = await read('src/game-v4.js'); assert.match(source, /this\.createPlayerSmoke\(\)/); - assert.match(source, /this\.add\.particles\(this\.player\.x, this\.player\.y, 'player-smoke'/); + assert.match(source, /this\.add\.particles\(0, 0, 'player-smoke'/); assert.match(source, /frequency: -1/); assert.match(source, /this\.updatePlayerSmoke\(deltaMs\)/); assert.match(source, /const forwardThrust = clamp\(this\.playerSpeed \/ PLAYER_MAX_SPEED, 0, 1\)/); @@ -222,9 +222,12 @@ test('player propulsion smoke is a separate world-space Phaser particle effect', assert.match(source, /const sternX = this\.player\.x - forwardX \* PLAYER_SMOKE_REAR_OFFSET/); assert.match(source, /const sternY = this\.player\.y - forwardY \* PLAYER_SMOKE_REAR_OFFSET/); assert.match(source, /PLAYER_SMOKE_ENGINE_OFFSET \* engineSide/); - assert.match(source, /this\.playerSmoke\.setParticleSpeed\(/); - assert.match(source, /this\.playerSmoke\.emitParticleAt\(engineX, engineY, 1\)/); - assert.match(source, /Phaser\.Math\.Linear\(175, 58, forwardThrust\)/); + assert.match(source, /const particle = this\.playerSmoke\.emitParticleAt\(engineX, engineY, 1\)/); + assert.match(source, /particle\.velocityX = velocityX/); + assert.match(source, /particle\.velocityY = velocityY/); + assert.match(source, /Phaser\.Math\.Linear\(150, 48, forwardThrust\)/); + assert.doesNotMatch(source, /this\.playerSmoke\.setPosition\(sternX, sternY\)/); + assert.doesNotMatch(source, /this\.playerSmoke\.setParticleSpeed\(/); }); test('mobile HUD skips hidden Phaser rendering and throttles DOM state publishing', async () => {