119 lines
3.2 KiB
TypeScript
119 lines
3.2 KiB
TypeScript
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;
|
|
}
|
|
}
|