feat(game): 新增3个角色、修复实习生攻击特效、加速开发子弹、每波结束自动进入下一波

This commit is contained in:
Cloud Bot
2026-03-24 07:47:41 +00:00
parent 300f4c432f
commit c8b8c7109f
9 changed files with 392 additions and 66 deletions

View File

@@ -1,21 +1,27 @@
import type Phaser from 'phaser'
import { GameManager } from '../GameManager'
import { TILE_SIZE, HUD_HEIGHT, COFFEE_COST } from '../constants'
import { PATH_TILES } from '../mapRenderer'
import { getCellSize } from '../mapRenderer'
import { COFFEE_COST } from '../constants'
import type { EnemyBase } from '../enemies/EnemyBase'
import { TowerBase } from './TowerBase'
import { InternTower } from './InternTower'
import { SeniorDevTower } from './SeniorDevTower'
import { PPTMasterTower } from './PPTMasterTower'
import { HRBPTower } from './HRBPTower'
import { PMTower } from './PMTower'
import { OpsTower } from './OpsTower'
import { OutsourceTower } from './OutsourceTower'
export type TowerType = 'intern' | 'senior' | 'ppt' | 'hrbp'
export type TowerType = 'intern' | 'senior' | 'ppt' | 'hrbp' | 'pm' | 'ops' | 'outsource'
export const TOWER_COSTS: Record<TowerType, number> = {
intern: 50,
senior: 120,
ppt: 100,
hrbp: 80,
outsource: 30,
intern: 50,
hrbp: 80,
ops: 90,
ppt: 100,
senior: 120,
pm: 160,
}
export class TowerManager {
@@ -30,11 +36,22 @@ export class TowerManager {
canPlace(gridX: number, gridY: number): boolean {
const key = `${gridX},${gridY}`
return !PATH_TILES.has(key) && !this.occupiedCells.has(key)
// 用 occupiedCells 判断,路径判断由 GameScene 传入的 currentPathTiles 负责
return !this.occupiedCells.has(key)
}
setCurrentPathTiles(tiles: Set<string>): void {
this._pathTiles = tiles
}
private _pathTiles: Set<string> = new Set()
canPlaceWithPath(gridX: number, gridY: number): boolean {
const key = `${gridX},${gridY}`
return !this._pathTiles.has(key) && !this.occupiedCells.has(key)
}
placeTower(gridX: number, gridY: number, type: TowerType): boolean {
if (!this.canPlace(gridX, gridY)) return false
if (!this.canPlaceWithPath(gridX, gridY)) return false
const cost = TOWER_COSTS[type]
const manager = GameManager.getInstance()
@@ -54,14 +71,13 @@ export class TowerManager {
private createTower(type: TowerType, gridX: number, gridY: number): TowerBase {
switch (type) {
case 'senior':
return new SeniorDevTower(this.scene, gridX, gridY)
case 'ppt':
return new PPTMasterTower(this.scene, gridX, gridY)
case 'hrbp':
return new HRBPTower(this.scene, gridX, gridY)
default:
return new InternTower(this.scene, gridX, gridY)
case 'senior': return new SeniorDevTower(this.scene, gridX, gridY)
case 'ppt': return new PPTMasterTower(this.scene, gridX, gridY)
case 'hrbp': return new HRBPTower(this.scene, gridX, gridY)
case 'pm': return new PMTower(this.scene, gridX, gridY)
case 'ops': return new OpsTower(this.scene, gridX, gridY)
case 'outsource': return new OutsourceTower(this.scene, gridX, gridY)
default: return new InternTower(this.scene, gridX, gridY)
}
}
@@ -104,11 +120,12 @@ export class TowerManager {
return
}
const { cellW } = getCellSize()
const hasTarget = enemies.some((e) => {
if (e.isDead) return false
const dx = e.x - tower['px']
const dy = e.y - tower['py']
return Math.sqrt(dx * dx + dy * dy) <= tower.attackRange * TILE_SIZE
return Math.sqrt(dx * dx + dy * dy) <= tower.attackRange * cellW
})
if (hasTarget && tower['attackCooldown'] <= 0) {
@@ -149,14 +166,14 @@ export class TowerManager {
private showTowerInfo(tower: TowerBase): void {
this.infoLabel?.destroy()
const px = tower.gridX * TILE_SIZE + TILE_SIZE / 2
const py = tower.gridY * TILE_SIZE + TILE_SIZE / 2 + HUD_HEIGHT
const { cellW, cellH } = getCellSize()
const { x: cx, y: cy } = tower.getPixelCenter()
void cx // suppress unused warning (used in label position below)
const label = this.scene.add
.text(
px,
py - 40,
cx,
cy - 40,
`精力: ${Math.floor(tower.stamina)}% [点击购买咖啡 ${COFFEE_COST}HC]`,
{
fontFamily: 'VT323, monospace',