diff --git a/game/GameScene.ts b/game/GameScene.ts index 6762800..35ca9c4 100644 --- a/game/GameScene.ts +++ b/game/GameScene.ts @@ -8,24 +8,17 @@ import { renderMapLabels, renderHUD, updateKPIBar, - BAR_X, - BAR_Y, - BAR_W, - BAR_H, + BAR_X, BAR_Y, BAR_W, BAR_H, } from './mapRenderer' import { TowerManager, type TowerType } from './towers/TowerManager' import { WaveManager } from './enemies/WaveManager' import { TowerPanel } from './ui/TowerPanel' import { HUD } from './ui/HUD' +import { WeeklyReportModal } from './ui/WeeklyReportModal' // 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 export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { class GameScene extends PhaserLib.Scene { @@ -35,64 +28,56 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { private hcText!: Phaser.GameObjects.Text private hoveredTile: { col: number; row: number } | null = null private tileGraphics!: Phaser.GameObjects.Graphics - - // New systems private towerManager!: TowerManager private waveManager!: WaveManager private towerPanel!: TowerPanel private hud!: HUD + private weeklyModal!: WeeklyReportModal private selectedTowerType: TowerType | null = null private buildModeGraphics!: Phaser.GameObjects.Graphics private isWaveRunning: boolean = false - constructor() { - super({ key: 'GameScene' }) - } + constructor() { super({ key: 'GameScene' }) } create(): void { this.manager = GameManager.getInstance() this.manager.reset() this.manager.gameState = 'playing' - // Render map this.tileGraphics = this.add.graphics() drawAllTiles(this.tileGraphics, null) renderMapLabels(this) - // Render HUD const hudObjs = renderHUD(this) this.kpiBar = hudObjs.kpiBar this.kpiText = hudObjs.kpiText this.hcText = hudObjs.hcText - // Build mode preview overlay - this.buildModeGraphics = this.add.graphics() - this.buildModeGraphics.setDepth(5) + this.buildModeGraphics = this.add.graphics().setDepth(5) - // Initialize game systems this.towerManager = new TowerManager(this) + this.weeklyModal = new WeeklyReportModal({ + onBossInspection: () => this.freezeAllTowers(3000), + onFullStamina: () => this.refillAllStamina(), + }) this.waveManager = new WaveManager( this, - () => this.hud.showWeeklyReportAlert(), + () => this.onWeeklyReport(), () => 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() - // GameManager callbacks → update HUD this.manager.onKPIChange.push((kpi: number) => { updateKPIBar(this.kpiBar, kpi) this.kpiText.setText(`${kpi}%`) @@ -112,15 +97,10 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { } update(_time: number, delta: number): void { - if ( - this.manager.gameState !== 'playing' && - this.manager.gameState !== 'idle' - ) return - + 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 && @@ -137,12 +117,32 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { 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 onWeeklyReport(): void { + this.hud.showWeeklyReportAlert() + this.time.delayedCall(600, () => { this.weeklyModal.show() }) + } + + /** 禁锢全场塔(老板视察效果) */ + private freezeAllTowers(duration: number = 3000): void { + this.towerManager.getAllTowers().forEach(tower => { + tower.isFrozen = true + setTimeout(() => { tower.isFrozen = false }, duration) + }) + } + + /** 补满全场塔精力 */ + private refillAllStamina(): void { + this.towerManager.getAllTowers().forEach(tower => { + tower.stamina = tower.maxStamina + tower['isActive'] = true + }) + } + private onAllWavesComplete(): void { this.manager.triggerVictory() this.hud.disableWaveButton() @@ -150,64 +150,33 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { 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}`) - ) { - if ( - !this.hoveredTile || - this.hoveredTile.col !== col || - this.hoveredTile.row !== row - ) { - 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) { - this.hoveredTile = null - drawAllTiles(this.tileGraphics, null) - this.buildModeGraphics.clear() + 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}`)) { + if (!this.hoveredTile || this.hoveredTile.col !== col || this.hoveredTile.row !== row) { + this.hoveredTile = { col, row } + drawAllTiles(this.tileGraphics, this.hoveredTile) } + if (this.selectedTowerType) this.drawBuildPreview(col, row, cellW, cellH) + return } - ) - - this.input.on( - 'pointerdown', - (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 - ) { - this.handleTileClick(col, row, cellW, cellH) - } + if (this.hoveredTile !== null) { + this.hoveredTile = null + drawAllTiles(this.tileGraphics, null) + this.buildModeGraphics.clear() } - ) + }) + this.input.on('pointerdown', (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) { + this.handleTileClick(col, row, cellW, cellH) + } + }) } - private drawBuildPreview( - col: number, - row: number, - cellW: number, - cellH: number - ): void { + private drawBuildPreview(col: number, row: number, cellW: number, cellH: number): void { this.buildModeGraphics.clear() if (!this.towerManager.canPlace(col, row)) return const x = col * cellW @@ -216,13 +185,7 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { 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 + private handleTileClick(col: number, row: number, cellW: number, cellH: number): void { if (this.selectedTowerType) { if (PATH_TILES.has(`${col},${row}`)) return const placed = this.towerManager.placeTower(col, row, this.selectedTowerType) @@ -231,57 +194,23 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene { this.towerPanel.deselect() this.selectedTowerType = null } else { - this.showPlaceFail(col, row, cellW, cellH) + this.showTip(col, row, cellW, cellH, 'HC不足或格子已占用', '#EF4444') } 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) + this.showTip(col, row, cellW, cellH, '从底部面板选择塔', '#A78BFA') } } - private showBuildHint( - col: number, - row: number, - cellW: number, - cellH: number - ): void { + private showTip(col: number, row: number, cellW: number, cellH: number, msg: string, color: string): void { const x = col * cellW + cellW / 2 const y = HUD_HEIGHT + row * cellH + cellH / 2 - const tip = this.add - .text(x, y - 20, '从底部面板选择塔', { - fontFamily: 'VT323, monospace', - fontSize: '16px', - color: '#A78BFA', - backgroundColor: '#0a1628', - padding: { x: 8, y: 4 }, - }) - .setOrigin(0.5, 1) - .setDepth(20) - this.time.delayedCall(1200, () => tip.destroy()) - } - - private showPlaceFail( - 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) + const tip = this.add.text(x, y - 20, msg, { + fontFamily: 'VT323, monospace', fontSize: '16px', + color, backgroundColor: '#0a1628', padding: { x: 8, y: 4 }, + }).setOrigin(0.5, 1).setDepth(20) this.time.delayedCall(1200, () => tip.destroy()) } }