diff --git a/packages/boop-game/src/scenes/GameScene.ts b/packages/boop-game/src/scenes/GameScene.ts index f327174..c05f6bc 100644 --- a/packages/boop-game/src/scenes/GameScene.ts +++ b/packages/boop-game/src/scenes/GameScene.ts @@ -15,6 +15,12 @@ export class GameScene extends GameHostScene { private turnText!: Phaser.GameObjects.Text; private infoText!: Phaser.GameObjects.Text; private winnerOverlay?: Phaser.GameObjects.Container; + private whiteSupplyText!: Phaser.GameObjects.Text; + private blackSupplyText!: Phaser.GameObjects.Text; + private pieceTypeSelector!: Phaser.GameObjects.Container; + private selectedPieceType: PieceType = 'kitten'; + private kittenButton!: Phaser.GameObjects.Container; + private catButton!: Phaser.GameObjects.Container; constructor() { super('GameScene'); @@ -26,6 +32,7 @@ export class GameScene extends GameHostScene { this.boardContainer = this.add.container(0, 0); this.gridGraphics = this.add.graphics(); this.drawGrid(); + this.createSupplyUI(); this.disposables.add(spawnEffect(new BoopPartSpawner(this, this.gameHost.state))); @@ -42,8 +49,11 @@ export class GameScene extends GameHostScene { this.watch(() => { const currentPlayer = this.state.currentPlayer; this.updateTurnText(currentPlayer); + this.updateSupplyUI(); + this.updatePieceTypeSelector(); }); + this.createPieceTypeSelector(); this.setupInput(); } @@ -59,7 +69,7 @@ export class GameScene extends GameHostScene { if (this.state.winner) return; if (this.isCellOccupied(row, col)) return; - const cmd = commands.play(this.state.currentPlayer, row, col, 'kitten'); + const cmd = commands.play(this.state.currentPlayer, row, col, this.selectedPieceType); const error = this.gameHost.onInput(cmd); if (error) { console.warn('Invalid move:', error); @@ -97,8 +107,8 @@ export class GameScene extends GameHostScene { }).setOrigin(0.5); this.turnText = this.add.text( - BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2, - BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE + 30, + BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2, + BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE + 30, '', { fontSize: '22px', @@ -121,6 +131,149 @@ export class GameScene extends GameHostScene { this.updateTurnText(this.state.currentPlayer); } + private createSupplyUI(): void { + const boardCenterX = BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2; + const uiY = BOARD_OFFSET.y - 20; + + // 白色玩家储备 + this.whiteSupplyText = this.add.text( + boardCenterX - 150, + uiY, + '', + { + fontSize: '16px', + fontFamily: 'Arial', + color: '#ffffff', + backgroundColor: '#000000', + padding: { x: 10, y: 5 }, + } + ).setOrigin(0.5).setDepth(100); + + // 黑色玩家储备 + this.blackSupplyText = this.add.text( + boardCenterX + 150, + uiY, + '', + { + fontSize: '16px', + fontFamily: 'Arial', + color: '#ffffff', + backgroundColor: '#333333', + padding: { x: 10, y: 5 }, + } + ).setOrigin(0.5).setDepth(100); + + this.updateSupplyUI(); + } + + private updateSupplyUI(): void { + const white = this.state.players.white; + const black = this.state.players.black; + + this.whiteSupplyText.setText( + `⚪ WHITE\n🐾 ${white.kitten.supply} | 🐱 ${white.cat.supply}` + ); + + this.blackSupplyText.setText( + `⚫ BLACK\n🐾 ${black.kitten.supply} | 🐱 ${black.cat.supply}` + ); + + // 高亮当前玩家 + const isWhiteTurn = this.state.currentPlayer === 'white'; + this.whiteSupplyText.setStyle({ backgroundColor: isWhiteTurn ? '#fbbf24' : '#000000' }); + this.blackSupplyText.setStyle({ backgroundColor: !isWhiteTurn ? '#fbbf24' : '#333333' }); + } + + private createPieceTypeSelector(): void { + const boardCenterX = BOARD_OFFSET.x + (BOARD_SIZE * CELL_SIZE) / 2; + const selectorY = BOARD_OFFSET.y + BOARD_SIZE * CELL_SIZE + 100; + + this.pieceTypeSelector = this.add.container(boardCenterX, selectorY); + + // 标签文字 + const label = this.add.text(-120, 0, '放置:', { + fontSize: '18px', + fontFamily: 'Arial', + color: '#4b5563', + }).setOrigin(0.5, 0.5); + + // 小猫按钮 + this.kittenButton = this.createPieceButton('kitten', '🐾 小猫', -30); + this.catButton = this.createPieceButton('cat', '🐱 大猫', 50); + + this.pieceTypeSelector.add([label, this.kittenButton, this.catButton]); + this.updatePieceTypeSelector(); + } + + private createPieceButton(type: PieceType, text: string, xOffset: number): Phaser.GameObjects.Container { + const container = this.add.container(xOffset, 0); + + const bg = this.add.rectangle(0, 0, 100, 40, 0xe5e7eb) + .setStrokeStyle(2, 0x9ca3af); + + const textObj = this.add.text(0, 0, text, { + fontSize: '16px', + fontFamily: 'Arial', + color: '#1f2937', + }).setOrigin(0.5); + + container.add([bg, textObj]); + + // 使按钮可交互 + bg.setInteractive({ useHandCursor: true }); + bg.on('pointerdown', () => { + this.selectedPieceType = type; + this.updatePieceTypeSelector(); + }); + + // 存储引用以便后续更新 + if (type === 'kitten') { + this.kittenButton = container; + } else { + this.catButton = container; + } + + return container; + } + + private updatePieceTypeSelector(): void { + if (!this.kittenButton || !this.catButton) return; + + const white = this.state.players.white; + const black = this.state.players.black; + const currentPlayer = this.state.players[this.state.currentPlayer]; + const isWhiteTurn = this.state.currentPlayer === 'white'; + + // 更新按钮状态 + const kittenAvailable = currentPlayer.kitten.supply > 0; + const catAvailable = currentPlayer.cat.supply > 0; + + // 更新小猫按钮 + const kittenBg = this.kittenButton.list[0] as Phaser.GameObjects.Rectangle; + const kittenText = this.kittenButton.list[1] as Phaser.GameObjects.Text; + const isKittenSelected = this.selectedPieceType === 'kitten'; + + kittenBg.setFillStyle(isKittenSelected ? 0xfbbf24 : (kittenAvailable ? 0xe5e7eb : 0xd1d5db)); + kittenText.setText(`🐾 小猫 (${currentPlayer.kitten.supply})`); + + // 更新大猫按钮 + const catBg = this.catButton.list[0] as Phaser.GameObjects.Rectangle; + const catText = this.catButton.list[1] as Phaser.GameObjects.Text; + const isCatSelected = this.selectedPieceType === 'cat'; + + catBg.setFillStyle(isCatSelected ? 0xfbbf24 : (catAvailable ? 0xe5e7eb : 0xd1d5db)); + catText.setText(`🐱 大猫 (${currentPlayer.cat.supply})`); + + // 如果选中的类型不可用,切换到可用的 + if (!kittenAvailable && this.selectedPieceType === 'kitten' && catAvailable) { + this.selectedPieceType = 'cat'; + this.updatePieceTypeSelector(); + } else if (!catAvailable && this.selectedPieceType === 'cat' && kittenAvailable) { + this.selectedPieceType = 'kitten'; + this.updatePieceTypeSelector(); + } + } + private updateTurnText(player: PlayerType): void { if (this.turnText) { const whitePieces = this.state.players.white; @@ -201,8 +354,15 @@ class BoopPartSpawner implements Spawner const [row, col] = part.position; const x = BOARD_OFFSET.x + col * CELL_SIZE + CELL_SIZE / 2; const y = BOARD_OFFSET.y + row * CELL_SIZE + CELL_SIZE / 2; - obj.x = x; - obj.y = y; + + // 使用 tween 动画平滑移动棋子 + this.scene.tweens.add({ + targets: obj, + x: x, + y: y, + duration: 200, + ease: 'Power2', + }); } onSpawn(part: BoopPart) {