import Phaser from 'phaser'; import { SCALE, MAX_PUCK_VELOCITY } from '../config/constants'; import { CoordinateUtils } from '../utils/coordinates'; import type { PuckState, TeamSide, Position } from '../types/game'; export class Puck extends Phaser.GameObjects.Container { declare body: Phaser.Physics.Arcade.Body; // Position in game coordinates (meters) public gameX: number; public gameY: number; // Puck state public state: PuckState; public possession: TeamSide | null; public carrier: string | null; // PlayerID constructor(scene: Phaser.Scene, gameX: number = 0, gameY: number = 0) { // Convert game coordinates to screen coordinates const screenPos = CoordinateUtils.gameToScreen(scene, gameX, gameY); super(scene, screenPos.x, screenPos.y); this.gameX = gameX; this.gameY = gameY; this.state = 'loose'; this.possession = null; this.carrier = null; // Add to scene scene.add.existing(this); scene.physics.add.existing(this); this.body = this.body as Phaser.Physics.Arcade.Body; // Set physics body size to match the puck visual (10px diameter) this.body.setSize(10, 10); this.body.setOffset(-5, -5); // Center the body on the container // Set max velocity (allow up to x m/s) this.body.setMaxVelocity(MAX_PUCK_VELOCITY * SCALE, MAX_PUCK_VELOCITY * SCALE); // Set initial velocity to 0 (stationary) this.body.setVelocity(0, 0); // Add bounce to keep it moving this.body.setBounce(1, 1); this.body.setCollideWorldBounds(true); // Enable high-velocity collision detection this.body.setCircle(5); // Use circular body instead of rectangle for better collision this.createSprite(); } private createSprite() { const graphics = this.scene.add.graphics(); // Draw puck as black circle (5px radius) graphics.fillStyle(0x000000, 1); graphics.fillCircle(0, 0, 5); // Add white highlight for visibility on ice graphics.fillStyle(0xffffff, 0.3); graphics.fillCircle(-1, -1, 2); this.add(graphics); } /** * Update puck position in game coordinates (meters) */ public setGamePosition(gameX: number, gameY: number) { this.gameX = gameX; this.gameY = gameY; // Convert to screen coordinates const screenPos = CoordinateUtils.gameToScreen(this.scene, gameX, gameY); this.setPosition(screenPos.x, screenPos.y); } /** * Set puck to be carried by a player */ public setCarrier(playerId: string, team: TeamSide) { this.carrier = playerId; this.possession = team; this.state = 'carried'; } /** * Set puck to loose state */ public setLoose() { this.carrier = null; this.state = 'loose'; } /** * Get puck position in game coordinates */ public getGamePosition(): Position { return { x: this.gameX, y: this.gameY }; } /** * Update puck game coordinates based on physics body position */ public update() { // 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; const gamePos = CoordinateUtils.screenToGame(this.scene, bodyX, bodyY); this.gameX = gamePos.x; this.gameY = gamePos.y; } }