From 29885d19924d171da6602bc9e323186116dee1e4 Mon Sep 17 00:00:00 2001 From: Pierre Wessman <4029607+pierrewessman@users.noreply.github.com> Date: Sat, 4 Oct 2025 16:12:21 +0200 Subject: [PATCH] Add pause-aware goal reset timer with proper scene cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 3-second countdown timer after goal that resets the scene - Timer pauses when game is paused, respecting pause state - Freeze all game updates (puck, players, goals) during countdown - Properly reset timer flags in create() to fix post-restart state - Add safety checks in Player and Puck update to handle destroyed entities - Remove player goal deceleration behavior (superseded by scene freeze) - Add destroy() method to Player for proper resource cleanup - Clean up goal event listeners before scene restart 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/entities/Player.ts | 38 +++++++++++++++++--------------------- src/entities/Puck.ts | 3 +++ src/game/GameScene.ts | 26 +++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/entities/Player.ts b/src/entities/Player.ts index 61fddc1..f12dc15 100644 --- a/src/entities/Player.ts +++ b/src/entities/Player.ts @@ -6,7 +6,6 @@ import { PLAYER_RADIUS_SKATER, SPEED_SCALE_FACTOR, MOVEMENT_STOP_THRESHOLD, - GOAL_DECELERATION_RATE, DEBUG, TACKLE_COOLDOWN, TACKLE_FALL_DURATION, @@ -97,8 +96,7 @@ export class Player extends Phaser.GameObjects.Container { // Home team (left side) faces right, Away team (right side) faces left this.currentAngle = this.team === 'home' ? 0 : Math.PI; - // Listen for goal events - this.scene.events.on('goal', this.onGoal, this); + // Note: Goal events are handled by GameScene (freezes game and resets after 3s) // Note: Player must be added to scene via scene.add.existing(player) and scene.physics.add.existing(player) // This is handled by GameScene.addPlayer() method, which also sets up the physics body @@ -182,28 +180,13 @@ export class Player extends Phaser.GameObjects.Container { this.targetY = targetY; } - /** - * Handle goal event - */ - private onGoal(_data: { team: string; goal: string }) { - // Gradually decelerate player after goal - const decelerate = () => { - this.speedMultiplier *= GOAL_DECELERATION_RATE; - - if (this.speedMultiplier > 0.01) { - this.scene.time.delayedCall(50, decelerate); - } else { - this.speedMultiplier = 0; - } - }; - - decelerate(); - } - /** * Update player movement each frame */ public update(delta: number) { + // Skip update if body doesn't exist (player being destroyed) + if (!this.body) return; + const deltaSeconds = delta / 1000; // Check if player is fallen and should stay still @@ -466,4 +449,17 @@ export class Player extends Phaser.GameObjects.Container { this.debugDetectionCircle.fillPath(); this.debugDetectionCircle.strokePath(); } + + /** + * Clean up resources when player is destroyed + */ + destroy(fromScene?: boolean) { + // Destroy detection sensor + if (this.detectionSensor) { + this.detectionSensor.destroy(); + } + + // Call parent destroy + super.destroy(fromScene); + } } diff --git a/src/entities/Puck.ts b/src/entities/Puck.ts index 8beefd7..b26da1f 100644 --- a/src/entities/Puck.ts +++ b/src/entities/Puck.ts @@ -112,6 +112,9 @@ export class Puck extends Phaser.GameObjects.Container { * Update puck game coordinates based on physics body position */ public update() { + // Skip update if body doesn't exist (puck being destroyed) + if (!this.body) return; + // Use body center position (body.x/y is top-left, need to account for that) const bodyX = this.body.x + this.body.width / 2; const bodyY = this.body.y + this.body.height / 2; diff --git a/src/game/GameScene.ts b/src/game/GameScene.ts index d53122e..3d3b8de 100644 --- a/src/game/GameScene.ts +++ b/src/game/GameScene.ts @@ -61,17 +61,28 @@ export class GameScene extends Phaser.Scene { private isPaused: boolean = false; private pauseButton!: Phaser.GameObjects.Text; + // Goal reset timer + private goalResetTimer: number = 0; + private isWaitingForReset: boolean = false; + constructor() { super({ key: 'GameScene' }); } create() { + console.log('[GameScene] Creating scene...'); + + // Reset goal timer flags + this.goalResetTimer = 0; + this.isWaitingForReset = false; + this.drawRink(); this.createGoals(); this.createPuck(); this.createPlayers(); this.setupEventListeners(); this.createPauseButton(); + console.log('[GameScene] Scene created. Players:', this.players.length); } private createPlayers() { @@ -210,7 +221,9 @@ export class GameScene extends Phaser.Scene { // Stop the puck (caught by net) this.puck.body.setVelocity(0, 0); - // Future: update score, trigger celebration, reset to faceoff, etc. + // Start 3-second countdown to reset + this.goalResetTimer = 3000; + this.isWaitingForReset = true; }); } @@ -327,6 +340,17 @@ export class GameScene extends Phaser.Scene { update(_time: number, delta: number) { if (this.isPaused) return; + // Handle goal reset timer + if (this.isWaitingForReset) { + this.goalResetTimer -= delta; + if (this.goalResetTimer <= 0) { + // Clean up before restart + this.events.off('goal'); + this.scene.restart(); + } + return; // Don't update anything while waiting for reset + } + this.updatePuck(); this.updatePlayers(delta); this.checkGoals();