110 lines
3.2 KiB
TypeScript
110 lines
3.2 KiB
TypeScript
|
|
import Phaser from 'phaser';
|
||
|
|
import type { BoopState, PlayerType } from '@/game/boop';
|
||
|
|
import type { ReadonlySignal } from '@preact/signals-core';
|
||
|
|
|
||
|
|
const BOARD_SIZE = 6;
|
||
|
|
const CELL_SIZE = 80;
|
||
|
|
const BOARD_OFFSET = { x: 80, y: 100 };
|
||
|
|
|
||
|
|
export { BOARD_SIZE, CELL_SIZE, BOARD_OFFSET };
|
||
|
|
|
||
|
|
export class BoardRenderer {
|
||
|
|
private container: Phaser.GameObjects.Container;
|
||
|
|
private gridGraphics: Phaser.GameObjects.Graphics;
|
||
|
|
private turnText: Phaser.GameObjects.Text;
|
||
|
|
private infoText: Phaser.GameObjects.Text;
|
||
|
|
|
||
|
|
constructor(private scene: Phaser.Scene) {
|
||
|
|
this.container = this.scene.add.container(0, 0);
|
||
|
|
this.gridGraphics = this.scene.add.graphics();
|
||
|
|
this.drawGrid();
|
||
|
|
|
||
|
|
this.turnText = this.scene.add.text(
|
||
|
|
BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2,
|
||
|
|
BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE + 30,
|
||
|
|
'',
|
||
|
|
{
|
||
|
|
fontSize: '22px',
|
||
|
|
fontFamily: 'Arial',
|
||
|
|
color: '#4b5563',
|
||
|
|
}
|
||
|
|
).setOrigin(0.5);
|
||
|
|
|
||
|
|
this.infoText = this.scene.add.text(
|
||
|
|
BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2,
|
||
|
|
BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE + 60,
|
||
|
|
'Click to place kitten. Cats win with 3 in a row!',
|
||
|
|
{
|
||
|
|
fontSize: '16px',
|
||
|
|
fontFamily: 'Arial',
|
||
|
|
color: '#6b7280',
|
||
|
|
}
|
||
|
|
).setOrigin(0.5);
|
||
|
|
}
|
||
|
|
|
||
|
|
private drawGrid(): void {
|
||
|
|
const g = this.gridGraphics;
|
||
|
|
g.lineStyle(2, 0x6b7280);
|
||
|
|
|
||
|
|
for (let i = 1; i < BOARD_SIZE; i++) {
|
||
|
|
g.lineBetween(
|
||
|
|
BOARD_OFFSET.x + i * CELL_SIZE,
|
||
|
|
BOARD_OFFSET.y,
|
||
|
|
BOARD_OFFSET.x + i * CELL_SIZE,
|
||
|
|
BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE,
|
||
|
|
);
|
||
|
|
g.lineBetween(
|
||
|
|
BOARD_OFFSET.x,
|
||
|
|
BOARD_OFFSET.y + i * CELL_SIZE,
|
||
|
|
BOARD_OFFSET.x + BOARD_SIZE * CELL_SIZE,
|
||
|
|
BOARD_OFFSET.y + i * CELL_SIZE,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
g.strokePath();
|
||
|
|
|
||
|
|
this.scene.add.text(BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2, BOARD_OFFSET.y - 50, 'Boop Game', {
|
||
|
|
fontSize: '32px',
|
||
|
|
fontFamily: 'Arial',
|
||
|
|
color: '#1f2937',
|
||
|
|
}).setOrigin(0.5);
|
||
|
|
}
|
||
|
|
|
||
|
|
updateTurnText(player: PlayerType, state: BoopState): void {
|
||
|
|
const current = player === 'white' ? state.players.white : state.players.black;
|
||
|
|
const catsAvailable = current.catPool.remaining() + current.graduatedCount;
|
||
|
|
|
||
|
|
this.turnText.setText(
|
||
|
|
`${player.toUpperCase()}'s turn | Kittens: ${current.kittenPool.remaining()} | Cats: ${catsAvailable}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
setupInput(
|
||
|
|
state: ReadonlySignal<BoopState>,
|
||
|
|
onCellClick: (row: number, col: number) => void
|
||
|
|
): void {
|
||
|
|
for (let row = 0; row < BOARD_SIZE; row++) {
|
||
|
|
for (let col = 0; col < BOARD_SIZE; col++) {
|
||
|
|
const x = BOARD_OFFSET.x + col * CELL_SIZE + CELL_SIZE / 2;
|
||
|
|
const y = BOARD_OFFSET.y + row * CELL_SIZE + CELL_SIZE / 2;
|
||
|
|
|
||
|
|
const zone = this.scene.add.zone(x, y, CELL_SIZE, CELL_SIZE).setInteractive();
|
||
|
|
|
||
|
|
zone.on('pointerdown', () => {
|
||
|
|
const isOccupied = !!state.value.board.partMap[`${row},${col}`];
|
||
|
|
if (!isOccupied && !state.value.winner) {
|
||
|
|
onCellClick(row, col);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
destroy(): void {
|
||
|
|
this.container.destroy();
|
||
|
|
this.gridGraphics.destroy();
|
||
|
|
this.turnText.destroy();
|
||
|
|
this.infoText.destroy();
|
||
|
|
}
|
||
|
|
}
|