diff --git a/src/game-v4.js b/src/game-v4.js index 67d0a7b..caf5acb 100644 --- a/src/game-v4.js +++ b/src/game-v4.js @@ -431,23 +431,32 @@ class GameScene extends Phaser.Scene { hitPlayer(shot) { if (!shot?.active || this.gameState !== 'playing') return; - const damage = shot.getData('damage') || PIRATE_SHOT_DAMAGE; + const rawDamage = Number(shot.getData('damage')); + const damage = clamp(Number.isFinite(rawDamage) ? rawDamage : PIRATE_SHOT_DAMAGE, 1, 25); const { x, y } = shot; - shot.destroy(); - // Always show projectile impact feedback. Damage is still rate-limited so - // one three-shot broadside cannot register as three hull hits. - this.spawnExplosion(x, y, 0.32); - this.flashShip(this.player, 0xffcccc); + // Do not destroy an Arcade body from inside the overlap callback. Disable + // it immediately, then clean it up and render impact FX on the next tick. + if (shot.body) shot.disableBody(true, true); + else shot.setActive(false).setVisible(false); + + this.time.delayedCall(0, () => { + if (shot?.scene) shot.destroy(); + try { + this.spawnExplosion(x, y, 0.32); + this.flashShip(this.player, 0xffcccc); + } catch (error) { + console.warn('Player impact FX failed', error); + } + }); if (this.time.now - this.lastPlayerHitAt < PLAYER_HIT_COOLDOWN_MS) return; this.lastPlayerHitAt = this.time.now; - this.playerHull = Math.max(0, this.playerHull - damage); + const currentHull = Number.isFinite(this.playerHull) ? this.playerHull : PLAYER_MAX_HULL; + this.playerHull = Math.max(0, currentHull - damage); - if (this.playerHull <= 0) { - this.finishGame(); - } + if (this.playerHull <= 0) this.finishGame(); } spawnExplosion(x, y, scale = 1) {