feat(game): GameScene 集成周报弹窗、全场塔禁锢和精力补满功能
This commit is contained in:
@@ -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) => {
|
||||
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
|
||||
) {
|
||||
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)
|
||||
}
|
||||
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(
|
||||
'pointerdown',
|
||||
(pointer: Phaser.Input.Pointer) => {
|
||||
})
|
||||
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
|
||||
) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user