From f5b35fd87f9bafcd8b00fac52cc10433731e7cd0 Mon Sep 17 00:00:00 2001 From: Admin Date: Fri, 18 Sep 2026 01:10:17 +0000 Subject: [PATCH] Apply player damage once per pirate broadside --- src/game-v4.js | 59 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/src/game-v4.js b/src/game-v4.js index a37c621..4d8d771 100644 --- a/src/game-v4.js +++ b/src/game-v4.js @@ -46,6 +46,8 @@ class GameScene extends Phaser.Scene { this.lastPlayerShot = 0; this.lastPlayerHitAt = -Infinity; this.pendingPlayerHits = []; + this.nextPirateVolleyId = 1; + this.damagedPlayerVolleys = new Set(); this.lastDiagnosticsAt = -Infinity; this.wave = 1; this.defeated = 0; @@ -459,6 +461,7 @@ class GameScene extends Phaser.Scene { fireBroadside(ship, side, group, texture, damage, velocityScale) { if (!ship?.body) return; const actor = group === this.pirateShots ? 'pirate' : 'player'; + const volleyId = actor === 'pirate' ? this.nextPirateVolleyId++ : null; const base = headingRad(ship) + side * Math.PI / 2; airshipLog('combat:broadside-fired', { @@ -468,6 +471,7 @@ class GameScene extends Phaser.Scene { x: Math.round(ship.x), y: Math.round(ship.y), angle: Math.round(ship.angle), + volleyId, }, 'debug'); const offsets = [-7, 0, 7]; @@ -480,6 +484,7 @@ class GameScene extends Phaser.Scene { shot.body.setAllowGravity(false); shot.setData('bornAt', this.time.now); shot.setData('damage', damage); + if (volleyId !== null) shot.setData('volleyId', volleyId); shot.setVelocity( Math.cos(angle) * SHOT_SPEED * velocityScale + ship.body.velocity.x * 0.25, Math.sin(angle) * SHOT_SPEED * velocityScale + ship.body.velocity.y * 0.25, @@ -536,12 +541,14 @@ class GameScene extends Phaser.Scene { this.pendingPlayerHits.push({ shot, damage, + volleyId: shot.getData('volleyId'), x: shot.x, y: shot.y, }); airshipLog('combat:player-hit-queued', { damage, + volleyId: shot.getData('volleyId'), pendingHits: this.pendingPlayerHits.length, shotAgeMs: this.time.now - (shot.getData('bornAt') || this.time.now), hull: this.playerHull, @@ -564,20 +571,33 @@ class GameScene extends Phaser.Scene { gameState: this.gameState, }; - const result = processQueuedPlayerHits({ - hull: this.playerHull, - lastHitAt: this.lastPlayerHitAt, - now: this.time.now, - hits, - cooldownMs: PLAYER_HIT_COOLDOWN_MS, - maxHull: PLAYER_MAX_HULL, - }); + const acceptedHits = []; + const ignoredHits = []; - this.playerHull = result.hull; - this.lastPlayerHitAt = result.lastHitAt; + for (const hit of hits) { + const volleyId = hit.volleyId; + if (volleyId != null && this.damagedPlayerVolleys.has(volleyId)) { + ignoredHits.push(hit); + continue; + } - if (result.appliedHits > 0) { - const impact = hits[0]; + if (volleyId != null) { + this.damagedPlayerVolleys.add(volleyId); + if (this.damagedPlayerVolleys.size > 128) { + const oldestVolleyId = this.damagedPlayerVolleys.values().next().value; + this.damagedPlayerVolleys.delete(oldestVolleyId); + } + } + + acceptedHits.push(hit); + } + + const totalDamage = acceptedHits.reduce((sum, hit) => sum + normalizePlayerDamage(hit.damage, PIRATE_SHOT_DAMAGE), 0); + this.playerHull = Math.max(0, this.playerHull - totalDamage); + const gameOver = this.playerHull <= 0; + + if (acceptedHits.length > 0) { + const impact = acceptedHits[0]; try { this.spawnExplosion(impact.x, impact.y, 0.32); this.flashShip(this.player, 0xffcccc); @@ -585,6 +605,7 @@ class GameScene extends Phaser.Scene { x: Math.round(impact.x), y: Math.round(impact.y), scale: 0.32, + volleyId: impact.volleyId ?? null, }, 'debug'); } catch (error) { airshipLog('combat:player-hit-fx-error', { @@ -595,12 +616,14 @@ class GameScene extends Phaser.Scene { airshipLog('combat:player-hit-processed', { queuedHits: hits.length, - appliedHits: result.appliedHits, - ignoredHits: result.ignoredHits, - damage: result.totalDamage, + appliedHits: acceptedHits.length, + ignoredHits: ignoredHits.length, + acceptedVolleyIds: acceptedHits.map((hit) => hit.volleyId ?? null), + ignoredVolleyIds: ignoredHits.map((hit) => hit.volleyId ?? null), + damage: totalDamage, hullBefore: beforeHull, hullAfter: this.playerHull, - gameOver: result.gameOver, + gameOver, now: this.time.now, playerStateBefore, playerStateAfter: { @@ -611,9 +634,9 @@ class GameScene extends Phaser.Scene { velocityY: this.player?.body?.velocity?.y ?? null, gameState: this.gameState, }, - }, result.gameOver ? 'warn' : 'info'); + }, gameOver ? 'warn' : 'info'); - if (result.gameOver) this.finishGame(); + if (gameOver) this.finishGame(); } spawnExplosion(x, y, scale = 1) {