From aff9f3f276f0ef95304cb3e7c3d7b4832a4822bb Mon Sep 17 00:00:00 2001 From: Admin Date: Sun, 20 Sep 2026 07:54:25 +0000 Subject: [PATCH] Enable pirate friendly fire --- src/game-v4.js | 16 +++++++++++++--- tests/wiring.test.mjs | 11 +++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/game-v4.js b/src/game-v4.js index 31ab38e..5a6ea48 100644 --- a/src/game-v4.js +++ b/src/game-v4.js @@ -120,6 +120,7 @@ class GameScene extends Phaser.Scene { this.createEnvironmentPhysics(); this.physics.add.overlap(this.playerShots, this.pirates, this.hitPirate, null, this); + this.physics.add.overlap(this.pirateShots, this.pirates, this.hitPirate, null, this); this.physics.add.overlap(this.pirateShots, this.player, this.hitPlayer, null, this); registerDebugSnapshot(() => ({ @@ -798,6 +799,7 @@ class GameScene extends Phaser.Scene { shot.setData('bornAt', this.time.now); shot.setData('damage', projectileDamage); shot.setData('projectileOwner', actor); + shot.setData('sourceShip', ship); if (actor === 'pirate') shot.setData('playerHitQueued', false); const shotWind = environmentWindAt({ x, @@ -817,10 +819,16 @@ class GameScene extends Phaser.Scene { hitPirate(first, second) { if (this.gameState !== 'playing') return; - const shot = resolveTaggedProjectile(first, second, 'player'); + const playerShot = resolveTaggedProjectile(first, second, 'player'); + const pirateShot = playerShot ? null : resolveTaggedProjectile(first, second, 'pirate'); + const shot = playerShot || pirateShot; const pirate = shot === first ? second : first; if (!shot?.active || !pirate?.active || pirate === this.player) return; + const projectileOwner = shot.getData('projectileOwner'); + const sourceShip = shot.getData('sourceShip'); + if (projectileOwner === 'pirate' && sourceShip === pirate) return; + const damage = shot.getData('damage') || 10; const { x, y } = shot; shot.destroy(); @@ -834,12 +842,14 @@ class GameScene extends Phaser.Scene { damage, hullBefore: previousHull, hullAfter: hull, + projectileOwner, + friendlyFire: projectileOwner === 'pirate', }); if (hull <= 0) { this.spawnExplosion(pirate.x, pirate.y, 1.25); pirate.destroy(); - this.defeated += 1; + if (projectileOwner === 'player') this.defeated += 1; if (this.pirates.countActive(true) === 0) { this.wave += 1; @@ -852,7 +862,7 @@ class GameScene extends Phaser.Scene { return; } - this.flashShip(pirate, 0xffcccc); + this.flashShip(pirate, projectileOwner === 'pirate' ? 0xffd29a : 0xffcccc); } hitPlayer(first, second) { diff --git a/tests/wiring.test.mjs b/tests/wiring.test.mjs index 5434acd..62f4d77 100644 --- a/tests/wiring.test.mjs +++ b/tests/wiring.test.mjs @@ -194,6 +194,17 @@ test('player and pirate broadsides use the cannonball image asset', async () => assert.doesNotMatch(source, /generateTexture\('pirate-shot'/); }); +test('pirate cannonballs damage other pirates but ignore their source ship', async () => { + const source = await read('src/game-v4.js'); + + assert.match(source, /this\.physics\.add\.overlap\(this\.pirateShots, this\.pirates, this\.hitPirate/); + assert.match(source, /shot\.setData\('sourceShip', ship\)/); + assert.match(source, /const pirateShot = playerShot \? null : resolveTaggedProjectile\(first, second, 'pirate'\)/); + assert.match(source, /if \(projectileOwner === 'pirate' && sourceShip === pirate\) return/); + assert.match(source, /friendlyFire: projectileOwner === 'pirate'/); + assert.match(source, /if \(projectileOwner === 'player'\) this\.defeated \+= 1/); +}); + test('player uses the four-frame north-facing sheet as a forward propulsion animation', async () => { const source = await read('src/game-v4.js');