From 0f335e77227b2fadfed1ea943f18f4c5f99ee183 Mon Sep 17 00:00:00 2001 From: Cloud Bot Date: Sat, 21 Mar 2026 09:45:32 +0000 Subject: [PATCH] =?UTF-8?q?refactor(game/GameScene):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=B8=BA=E5=A4=9A=E5=9C=B0=E5=9B=BE=E6=B5=81=E7=A8=8B=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=9C=B0=E5=9B=BE=E5=88=87=E6=8D=A2=E5=92=8C?= =?UTF-8?q?=E8=83=8C=E6=99=AF/=E8=A3=85=E9=A5=B0=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/GameScene.ts | 153 ++++++++++++++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 54 deletions(-) diff --git a/game/GameScene.ts b/game/GameScene.ts index 7216ffe..f13b608 100644 --- a/game/GameScene.ts +++ b/game/GameScene.ts @@ -2,10 +2,12 @@ import type Phaser from 'phaser' import { MAP_COLS, MAP_ROWS, HUD_HEIGHT, GAME_HEIGHT, GAME_WIDTH } from './constants' import { GameManager } from './GameManager' import { - PATH_TILES, + buildPathTiles, getCellSize, drawAllTiles, renderMapLabels, + renderMapBackground, + renderDecorations, renderHUD, updateKPIBar, BAR_X, BAR_Y, BAR_W, BAR_H, @@ -14,10 +16,10 @@ import { TowerManager, type TowerType } from './towers/TowerManager' import { WaveManager } from './enemies/WaveManager' import { HUD } from './ui/HUD' import { WeeklyReportModal } from './ui/WeeklyReportModal' +import { MapTransitionModal } from './ui/MapTransitionModal' +import { ALL_MAPS, type MapConfig } from './data/mapConfigs' -// 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 +void MAP_ROWS; void GAME_HEIGHT; void GAME_WIDTH; void BAR_X; void BAR_Y; void BAR_W; void BAR_H /** 所有游戏精灵的 key → public path 映射 */ const SPRITE_ASSETS: Record = { @@ -29,6 +31,9 @@ const SPRITE_ASSETS: Record = { 'enemy-old': '/game-assets/enemy-old.png', 'enemy-trouble': '/game-assets/enemy-trouble.png', 'enemy-boss': '/game-assets/enemy-boss.png', + 'deco-coffee': '/game-assets/deco-coffee.png', + 'deco-monitor': '/game-assets/deco-monitor.png', + 'deco-desk': '/game-assets/deco-desk.png', } export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { @@ -43,51 +48,62 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { private waveManager!: WaveManager private hud!: HUD private weeklyModal!: WeeklyReportModal + private mapTransitionModal!: MapTransitionModal private selectedTowerType: TowerType | null = null private buildModeGraphics!: Phaser.GameObjects.Graphics private isWaveRunning: boolean = false + // 多地图状态 + private currentPathTiles: Set = new Set() + private decorationObjects: Phaser.GameObjects.Image[] = [] + private labelObjects: Phaser.GameObjects.Text[] = [] + private bgObject: Phaser.GameObjects.Image | null = null + private mapInTransition: boolean = false + constructor() { super({ key: 'GameScene' }) } preload(): void { - // 加载所有 AI 生成的角色图片 for (const [key, path] of Object.entries(SPRITE_ASSETS)) { this.load.image(key, path) } + // 预加载所有地图背景 + for (const map of ALL_MAPS) { + if (!this.textures.exists(map.bgKey)) { + this.load.image(map.bgKey, map.bgPath) + } + } } create(): void { this.manager = GameManager.getInstance() this.manager.reset() + + // 读取难度(React 层通过 window.__gameDifficulty 传入) + if (typeof window !== 'undefined') { + const diff = (window as any).__gameDifficulty + if (diff === 'easy' || diff === 'normal' || diff === 'hard') { + this.manager.setDifficulty(diff) + } + } + this.manager.gameState = 'playing' this.tileGraphics = this.add.graphics() - drawAllTiles(this.tileGraphics, null) - renderMapLabels(this) - + this.buildModeGraphics = this.add.graphics().setDepth(5) const hudObjs = renderHUD(this) this.kpiBar = hudObjs.kpiBar this.kpiText = hudObjs.kpiText this.hcText = hudObjs.hcText - this.buildModeGraphics = this.add.graphics().setDepth(5) - this.towerManager = new TowerManager(this) - this.weeklyModal = new WeeklyReportModal({ - onBossInspection: () => this.freezeAllTowers(3000), - onFullStamina: () => this.refillAllStamina(), + this.weeklyModal = new WeeklyReportModal({ onBossInspection: () => this.freezeAllTowers(3000), + onFullStamina: () => this.refillAllStamina(), }) - this.waveManager = new WaveManager( - this, - () => this.onWeeklyReport(), - () => this.onAllWavesComplete(), - () => this.towerManager.removeRandomTower() - ) + this.mapTransitionModal = new MapTransitionModal() this.hud = new HUD(this) this.hud.createWaveButton(() => this.onWaveButtonClick()) - - // 接收 React 层传来的选塔事件 + this.loadMap(ALL_MAPS[0]) if (typeof window !== 'undefined') { ;(window as any).__gameSelectTower = (type: TowerType | null) => { this.selectedTowerType = type @@ -96,34 +112,51 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { } this.setupInteraction() + this.setupManagerCallbacks() + if (typeof window !== 'undefined') { ;(window as any).__gameReady?.() } + } - // HC/KPI 变化时同步到 React HUD + private setupManagerCallbacks(): void { this.manager.onHCChange.push((hc: number) => { this.hcText.setText(`HC: ${hc}`) - // 通知 React 层更新塔面板可用状态 - if (typeof window !== 'undefined') { - ;(window as any).__gameOnHCChange?.(hc) - } + if (typeof window !== 'undefined') { ;(window as any).__gameOnHCChange?.(hc) } }) this.manager.onKPIChange.push((kpi: number) => { updateKPIBar(this.kpiBar, kpi) this.kpiText.setText(`${kpi}%`) }) - this.manager.onGameOver.push(() => { - this.hud.showGameOver() - }) - this.manager.onVictory.push(() => { - this.hud.showVictory() - }) + this.manager.onGameOver.push(() => { this.hud.showGameOver() }) + this.manager.onVictory.push(() => { this.hud.showVictory() }) + } - // 通知 React 层游戏已就绪 - if (typeof window !== 'undefined') { - ;(window as any).__gameReady?.() - } + /** + * 加载指定地图:渲染背景、路径、装饰物,重置 WaveManager + */ + private loadMap(mapConfig: MapConfig): void { + this.currentPathTiles = buildPathTiles(mapConfig.waypoints) + this.bgObject?.destroy() + this.bgObject = renderMapBackground(this, mapConfig.bgKey) + drawAllTiles(this.tileGraphics, null, this.currentPathTiles, mapConfig) + this.labelObjects.forEach(l => l.destroy()) + this.labelObjects = renderMapLabels(this, mapConfig.labels) + this.decorationObjects.forEach(d => d.destroy()) + this.decorationObjects = renderDecorations(this, mapConfig.decorations, this.currentPathTiles) + this.waveManager = new WaveManager( + this, mapConfig.waves, this.manager.difficulty, + { + onWaveComplete: () => this.onWeeklyReport(), + onAllWavesComplete: () => this.onMapCleared(), + onDestroyRandomTower: () => this.towerManager.removeRandomTower(), + }, + mapConfig.waypoints + ) + this.mapInTransition = false } update(_time: number, delta: number): void { if (this.manager.gameState !== 'playing' && this.manager.gameState !== 'idle') return + if (this.mapInTransition) return + this.towerManager.update(delta, this.waveManager.getAllActiveEnemies()) this.waveManager.update(delta) @@ -139,7 +172,7 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { } private onWaveButtonClick(): void { - if (!this.waveManager.hasMoreWaves() || this.isWaveRunning) return + if (!this.waveManager.hasMoreWaves() || this.isWaveRunning || this.mapInTransition) return this.isWaveRunning = true this.hud.disableWaveButton() this.hud.setWaveButtonText('波次进行中...') @@ -153,7 +186,28 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { this.time.delayedCall(600, () => { this.weeklyModal.show() }) } - /** 禁锢全场塔(老板视察效果) */ + private onMapCleared(): void { + this.mapInTransition = true + this.isWaveRunning = false + this.hud.disableWaveButton() + this.hud.setWaveButtonText('关卡完成!') + const currentMap = ALL_MAPS[this.manager.currentMapIndex] + const nextIndex = this.manager.currentMapIndex + 1 + if (nextIndex >= ALL_MAPS.length) { + this.manager.totalWaveCleared += currentMap.waveCount + this.mapTransitionModal.show(currentMap, () => { this.manager.triggerVictory() }) + return + } + this.mapTransitionModal.show(currentMap, () => { + this.manager.totalWaveCleared += currentMap.waveCount + this.manager.currentMapIndex = nextIndex + this.waveManager.clearAllEnemies() + this.loadMap(ALL_MAPS[nextIndex]) + this.hud.enableWaveButton() + this.hud.setWaveButtonText('▶ 召唤下一波') + }) + } + freezeAllTowers(duration: number = 3000): void { this.towerManager.getAllTowers().forEach(tower => { tower.isFrozen = true @@ -161,7 +215,6 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { }) } - /** 补满全场塔精力 */ refillAllStamina(): void { this.towerManager.getAllTowers().forEach(tower => { tower.stamina = tower.maxStamina @@ -169,27 +222,23 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { }) } - private onAllWavesComplete(): void { - this.manager.triggerVictory() - this.hud.disableWaveButton() - } - private setupInteraction(): void { const { cellW, cellH } = getCellSize() this.input.on('pointermove', (pointer: Phaser.Input.Pointer) => { const col = Math.floor(pointer.x / cellW) const row = Math.floor((pointer.y - HUD_HEIGHT) / cellH) - if (col >= 0 && col < MAP_COLS && row >= 0 && row < MAP_ROWS && !PATH_TILES.has(`${col},${row}`)) { + const inBounds = col >= 0 && col < MAP_COLS && row >= 0 && row < MAP_ROWS + if (inBounds && !this.currentPathTiles.has(`${col},${row}`)) { if (!this.hoveredTile || this.hoveredTile.col !== col || this.hoveredTile.row !== row) { this.hoveredTile = { col, row } - drawAllTiles(this.tileGraphics, this.hoveredTile) + drawAllTiles(this.tileGraphics, this.hoveredTile, this.currentPathTiles, ALL_MAPS[this.manager.currentMapIndex]) } if (this.selectedTowerType) this.drawBuildPreview(col, row, cellW, cellH) return } if (this.hoveredTile !== null) { this.hoveredTile = null - drawAllTiles(this.tileGraphics, null) + drawAllTiles(this.tileGraphics, null, this.currentPathTiles, ALL_MAPS[this.manager.currentMapIndex]) this.buildModeGraphics.clear() } }) @@ -209,29 +258,25 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { const y = HUD_HEIGHT + row * cellH this.buildModeGraphics.lineStyle(2, 0xa78bfa, 0.9) this.buildModeGraphics.strokeRect(x + 2, y + 2, cellW - 4, cellH - 4) - // 填充半透明预览 this.buildModeGraphics.fillStyle(0xa78bfa, 0.15) this.buildModeGraphics.fillRect(x + 2, y + 2, cellW - 4, cellH - 4) } private handleTileClick(col: number, row: number, cellW: number, cellH: number): void { if (this.selectedTowerType) { - if (PATH_TILES.has(`${col},${row}`)) return + if (this.currentPathTiles.has(`${col},${row}`)) return const placed = this.towerManager.placeTower(col, row, this.selectedTowerType) if (placed) { this.buildModeGraphics.clear() this.selectedTowerType = null - // 通知 React 取消选中状态 - if (typeof window !== 'undefined') { - ;(window as any).__gameOnTowerDeselect?.() - } + if (typeof window !== 'undefined') { ;(window as any).__gameOnTowerDeselect?.() } } else { this.showTip(col, row, cellW, cellH, 'HC不足或格子已占用', '#EF4444') } return } const hasTower = this.towerManager.handleTileClick(col, row) - if (!hasTower && !PATH_TILES.has(`${col},${row}`)) { + if (!hasTower && !this.currentPathTiles.has(`${col},${row}`)) { this.showTip(col, row, cellW, cellH, '请先从底部选择塔', '#A78BFA') } }