feat(GameScene): 集成防御塔系统与波次系统,替换占位提示逻辑

This commit is contained in:
Cloud Bot
2026-03-21 08:04:25 +00:00
parent ff3fb0536f
commit 3bd9a02acd

View File

@@ -13,11 +13,20 @@ import {
BAR_W, BAR_W,
BAR_H, BAR_H,
} from './mapRenderer' } from './mapRenderer'
import { TowerManager, type TowerType } from './towers/TowerManager'
import { WaveManager } from './enemies/WaveManager'
import { TowerPanel } from './ui/TowerPanel'
import { HUD } from './ui/HUD'
// Suppress unused-variable warnings for exported constants
void MAP_ROWS
void GAME_HEIGHT
void GAME_WIDTH
void BAR_X
void BAR_Y
void BAR_W
void BAR_H
/**
* 创建主游戏场景类(工厂函数,接收动态导入的 Phaser 实例)
* 这样可以保证 SSR 安全(只在客户端执行)
*/
export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
class GameScene extends PhaserLib.Scene { class GameScene extends PhaserLib.Scene {
private manager!: GameManager private manager!: GameManager
@@ -27,6 +36,15 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
private hoveredTile: { col: number; row: number } | null = null private hoveredTile: { col: number; row: number } | null = null
private tileGraphics!: Phaser.GameObjects.Graphics private tileGraphics!: Phaser.GameObjects.Graphics
// New systems
private towerManager!: TowerManager
private waveManager!: WaveManager
private towerPanel!: TowerPanel
private hud!: HUD
private selectedTowerType: TowerType | null = null
private buildModeGraphics!: Phaser.GameObjects.Graphics
private isWaveRunning: boolean = false
constructor() { constructor() {
super({ key: 'GameScene' }) super({ key: 'GameScene' })
} }
@@ -36,33 +54,100 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
this.manager.reset() this.manager.reset()
this.manager.gameState = 'playing' this.manager.gameState = 'playing'
// 渲染地图 // Render map
this.tileGraphics = this.add.graphics() this.tileGraphics = this.add.graphics()
drawAllTiles(this.tileGraphics, null) drawAllTiles(this.tileGraphics, null)
// 地图装饰标签
renderMapLabels(this) renderMapLabels(this)
// HUD在地图之上 // Render HUD
const hud = renderHUD(this) const hudObjs = renderHUD(this)
this.kpiBar = hud.kpiBar this.kpiBar = hudObjs.kpiBar
this.kpiText = hud.kpiText this.kpiText = hudObjs.kpiText
this.hcText = hud.hcText this.hcText = hudObjs.hcText
// 鼠标交互 // Build mode preview overlay
this.buildModeGraphics = this.add.graphics()
this.buildModeGraphics.setDepth(5)
// Initialize game systems
this.towerManager = new TowerManager(this)
this.waveManager = new WaveManager(
this,
() => this.hud.showWeeklyReportAlert(),
() => this.onAllWavesComplete(),
() => this.towerManager.removeRandomTower()
)
// HUD helper
this.hud = new HUD(this)
this.hud.createWaveButton(() => this.onWaveButtonClick())
// DOM tower panel
this.towerPanel = new TowerPanel('game-container')
this.towerPanel.onSelect((type) => {
this.selectedTowerType = type
if (!type) this.buildModeGraphics.clear()
})
// Mouse interaction
this.setupInteraction() this.setupInteraction()
// GameManager 事件回调 → 更新 HUD // GameManager callbacks → update HUD
this.manager.onKPIChange.push((kpi: number) => { this.manager.onKPIChange.push((kpi: number) => {
updateKPIBar(this.kpiBar, kpi) updateKPIBar(this.kpiBar, kpi)
this.kpiText.setText(`${kpi}%`) this.kpiText.setText(`${kpi}%`)
}) })
this.manager.onHCChange.push((hc: number) => { this.manager.onHCChange.push((hc: number) => {
this.hcText.setText(`HC: ${hc}`) this.hcText.setText(`HC: ${hc}`)
this.towerPanel.refreshCardStates()
})
this.manager.onGameOver.push(() => {
this.hud.showGameOver()
this.towerPanel.destroy()
})
this.manager.onVictory.push(() => {
this.hud.showVictory()
this.towerPanel.destroy()
}) })
} }
/** 注册鼠标悬停 + 点击事件 */ update(_time: number, delta: number): void {
if (
this.manager.gameState !== 'playing' &&
this.manager.gameState !== 'idle'
) return
this.towerManager.update(delta, this.waveManager.getAllActiveEnemies())
this.waveManager.update(delta)
// Re-enable wave button when wave clears
if (
this.isWaveRunning &&
this.waveManager.getAllActiveEnemies().length === 0 &&
this.waveManager.hasMoreWaves()
) {
this.isWaveRunning = false
this.hud.enableWaveButton()
this.hud.setWaveButtonText('▶ 召唤下一波')
}
}
private onWaveButtonClick(): void {
if (!this.waveManager.hasMoreWaves() || this.isWaveRunning) return
this.isWaveRunning = true
this.hud.disableWaveButton()
this.hud.setWaveButtonText('波次进行中...')
this.waveManager.startNextWave()
const waveNum = this.waveManager.getCurrentWaveNumber()
this.hud.showWaveBanner(waveNum, this.waveManager.totalWaves)
}
private onAllWavesComplete(): void {
this.manager.triggerVictory()
this.hud.disableWaveButton()
}
private setupInteraction(): void { private setupInteraction(): void {
const { cellW, cellH } = getCellSize() const { cellW, cellH } = getCellSize()
@@ -72,22 +157,31 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
const col = Math.floor(pointer.x / cellW) const col = Math.floor(pointer.x / cellW)
const row = Math.floor((pointer.y - HUD_HEIGHT) / cellH) const row = Math.floor((pointer.y - HUD_HEIGHT) / cellH)
if (col >= 0 && col < MAP_COLS && row >= 0 && row < MAP_ROWS) { if (
if (!PATH_TILES.has(`${col},${row}`)) { col >= 0 && col < MAP_COLS &&
if ( row >= 0 && row < MAP_ROWS &&
!this.hoveredTile || !PATH_TILES.has(`${col},${row}`)
this.hoveredTile.col !== col || ) {
this.hoveredTile.row !== row if (
) { !this.hoveredTile ||
this.hoveredTile = { col, row } this.hoveredTile.col !== col ||
drawAllTiles(this.tileGraphics, this.hoveredTile) this.hoveredTile.row !== row
} ) {
return this.hoveredTile = { col, row }
drawAllTiles(this.tileGraphics, this.hoveredTile)
} }
// Show build preview if in build mode
if (this.selectedTowerType) {
this.drawBuildPreview(col, row, cellW, cellH)
}
return
} }
if (this.hoveredTile !== null) { if (this.hoveredTile !== null) {
this.hoveredTile = null this.hoveredTile = null
drawAllTiles(this.tileGraphics, null) drawAllTiles(this.tileGraphics, null)
this.buildModeGraphics.clear()
} }
} }
) )
@@ -100,17 +194,56 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
if ( if (
col >= 0 && col < MAP_COLS && col >= 0 && col < MAP_COLS &&
row >= 0 && row < MAP_ROWS && row >= 0 && row < MAP_ROWS
!PATH_TILES.has(`${col},${row}`)
) { ) {
this.showBuildPrompt(col, row, cellW, cellH) this.handleTileClick(col, row, cellW, cellH)
} }
} }
) )
} }
/** 点击可建格子时的占位提示Phase 1 */ private drawBuildPreview(
private showBuildPrompt( col: number,
row: number,
cellW: number,
cellH: number
): void {
this.buildModeGraphics.clear()
if (!this.towerManager.canPlace(col, row)) return
const x = col * cellW
const y = HUD_HEIGHT + row * cellH
this.buildModeGraphics.lineStyle(2, 0xa78bfa, 0.9)
this.buildModeGraphics.strokeRect(x + 2, y + 2, cellW - 4, cellH - 4)
}
private handleTileClick(
col: number,
row: number,
cellW: number,
cellH: number
): void {
// If in build mode, try to place tower
if (this.selectedTowerType) {
if (PATH_TILES.has(`${col},${row}`)) return
const placed = this.towerManager.placeTower(col, row, this.selectedTowerType)
if (placed) {
this.buildModeGraphics.clear()
this.towerPanel.deselect()
this.selectedTowerType = null
} else {
this.showPlaceFail(col, row, cellW, cellH)
}
return
}
// Otherwise check if clicking an existing tower
const hasTower = this.towerManager.handleTileClick(col, row)
if (!hasTower && !PATH_TILES.has(`${col},${row}`)) {
this.showBuildHint(col, row, cellW, cellH)
}
}
private showBuildHint(
col: number, col: number,
row: number, row: number,
cellW: number, cellW: number,
@@ -119,30 +252,39 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
const x = col * cellW + cellW / 2 const x = col * cellW + cellW / 2
const y = HUD_HEIGHT + row * cellH + cellH / 2 const y = HUD_HEIGHT + row * cellH + cellH / 2
const tip = this.add const tip = this.add
.text(x, y - 20, '建塔 (即将开放)', { .text(x, y - 20, '从底部面板选择塔', {
fontFamily: 'VT323, monospace', fontFamily: 'VT323, monospace',
fontSize: '18px', fontSize: '16px',
color: '#F43F5E', color: '#A78BFA',
backgroundColor: '#0a1628', backgroundColor: '#0a1628',
padding: { x: 8, y: 4 }, padding: { x: 8, y: 4 },
}) })
.setOrigin(0.5, 1) .setOrigin(0.5, 1)
.setDepth(20) .setDepth(20)
this.time.delayedCall(1200, () => tip.destroy())
}
this.time.delayedCall(1500, () => { private showPlaceFail(
tip.destroy() col: number,
}) row: number,
cellW: number,
cellH: number
): void {
const x = col * cellW + cellW / 2
const y = HUD_HEIGHT + row * cellH + cellH / 2
const tip = this.add
.text(x, y - 20, 'HC不足或格子已占用', {
fontFamily: 'VT323, monospace',
fontSize: '16px',
color: '#EF4444',
backgroundColor: '#0a1628',
padding: { x: 8, y: 4 },
})
.setOrigin(0.5, 1)
.setDepth(20)
this.time.delayedCall(1200, () => tip.destroy())
} }
} }
// 避免 unused variable 警告
void MAP_ROWS
void GAME_HEIGHT
void GAME_WIDTH
void BAR_X
void BAR_Y
void BAR_W
void BAR_H
return GameScene return GameScene
} }