import Phaser from 'phaser'; import { SCALE, GOAL_WIDTH, GOAL_DEPTH } from '../config/constants'; export class Goal extends Phaser.GameObjects.Container { private leftPost!: Phaser.Physics.Arcade.Image; private rightPost!: Phaser.Physics.Arcade.Image; private backBar!: Phaser.Physics.Arcade.Image; private scoringZone!: Phaser.GameObjects.Zone; private isLeftGoal: boolean; constructor(scene: Phaser.Scene, x: number, y: number, isLeftGoal: boolean = true) { super(scene, x, y); this.isLeftGoal = isLeftGoal; // Add to scene scene.add.existing(this); this.createGoalStructure(); } private createGoalStructure() { const postThickness = 0.2 * SCALE; // Post thickness const goalWidth = GOAL_WIDTH * SCALE; // Width of goal opening (top to bottom) const goalDepth = GOAL_DEPTH * SCALE; // Depth extending into zone const barThickness = 0.2 * SCALE; // Create graphics for visual representation const graphics = this.scene.add.graphics(); // Draw goal outline (visual only) // Left goal extends leftward (negative X), right goal extends rightward (positive X) const offsetX = this.isLeftGoal ? -goalDepth : 0; // Fill the scoring zone with semi-transparent color graphics.fillStyle(0x00ff00, 0.2); // Green, 20% opacity graphics.fillRect( offsetX + barThickness, -goalWidth / 2 + postThickness, goalDepth - barThickness * 2, goalWidth - postThickness * 2 ); // Draw goal posts (filled red) graphics.fillStyle(0xff0000, 1); // Top post (horizontal bar across top) graphics.fillRect(offsetX, -goalWidth / 2, goalDepth, postThickness); // Bottom post (horizontal bar across bottom) graphics.fillRect(offsetX, goalWidth / 2 - postThickness, goalDepth, postThickness); // Back bar (vertical bar at the back) const backX = this.isLeftGoal ? offsetX : offsetX + goalDepth - barThickness; graphics.fillRect(backX, -goalWidth / 2, barThickness, goalWidth); this.add(graphics); // Create physics bodies for collision (in world coordinates, not container-relative) const physics = this.scene.physics; // Top post - rigid body (invisible, collision only) this.leftPost = this.scene.add.rectangle( this.x + offsetX + goalDepth / 2, this.y - goalWidth / 2 + postThickness / 2, goalDepth, postThickness ) as any; this.leftPost.setVisible(false); physics.add.existing(this.leftPost, true); // true = static body // Bottom post - rigid body (invisible, collision only) this.rightPost = this.scene.add.rectangle( this.x + offsetX + goalDepth / 2, this.y + goalWidth / 2 - postThickness / 2, goalDepth, postThickness ) as any; this.rightPost.setVisible(false); physics.add.existing(this.rightPost, true); // Back bar (invisible, collision only) this.backBar = this.scene.add.rectangle( this.x + backX + barThickness / 2, this.y, barThickness, goalWidth ) as any; this.backBar.setVisible(false); physics.add.existing(this.backBar, true); // Scoring zone - sensor (no physical collision, just detection) this.scoringZone = this.scene.add.zone( this.x + offsetX + goalDepth / 2, this.y, goalDepth - barThickness * 2, goalWidth - postThickness * 2 ); physics.add.existing(this.scoringZone, true); } public getLeftPost(): Phaser.Physics.Arcade.Image { return this.leftPost; } public getRightPost(): Phaser.Physics.Arcade.Image { return this.rightPost; } public getBackBar(): Phaser.Physics.Arcade.Image { return this.backBar; } public getScoringZone(): Phaser.GameObjects.Zone { return this.scoringZone; } }