diff --git a/game/enemies/BossVP.ts b/game/enemies/BossVP.ts index ea531a1..c0fbacc 100644 --- a/game/enemies/BossVP.ts +++ b/game/enemies/BossVP.ts @@ -10,18 +10,17 @@ export class BossVP extends EnemyBase { constructor( scene: Phaser.Scene, pathPoints: PathPoint[], - onDestroyTower?: () => void + onDestroyTower?: () => void, + speedMultiplier: number = 1.0, + hpMultiplier: number = 1.0 ) { - super(scene, pathPoints, 800, 40, 30, 150, 'enemy-boss') + super(scene, pathPoints, 800, 40, 30, 150, 'enemy-boss', speedMultiplier, hpMultiplier) this.onDestroyTower = onDestroyTower - // BOSS 放大到 1.3 个格子(跨格的威压感) const bossSize = this.cellW * 1.3 this.imageSprite.setDisplaySize(bossSize, bossSize) this.imageSprite.setDepth(12) - // BOSS 出现特效 scene.cameras.main.flash(800, 255, 0, 0, false) this.showBossAlert() - // BOSS 名字标签 this.bossLabel = scene.add.text(this.x, this.y + this.cellH * 0.5, '空降VP', { fontFamily: 'VT323, monospace', fontSize: '14px', color: '#FBBF24', backgroundColor: '#7c2d12', padding: { x: 4, y: 1 }, @@ -30,7 +29,7 @@ export class BossVP extends EnemyBase { private showBossAlert(): void { const alert = this.scene.add - .text(this.scene.scale.width / 2, this.scene.scale.height / 2, '⚠ 空降VP来袭 ⚠', { + .text(this.scene.scale.width / 2, this.scene.scale.height / 2, '空降VP来袭', { fontFamily: 'VT323, monospace', fontSize: '36px', color: '#FBBF24', backgroundColor: '#7F1D1D', padding: { x: 16, y: 8 }, }).setOrigin(0.5, 0.5).setDepth(50) @@ -48,11 +47,9 @@ export class BossVP extends EnemyBase { this.skillTimer = 20000 this.triggerOrgRestructure() } - // 更新名字标签位置 if (this.bossLabel) { this.bossLabel.setPosition(this.x, this.y + this.cellH * 0.5) } - // BOSS 金色发光边框 this.imageSprite.setTint(this.skillTimer < 3000 ? 0xff6600 : 0xfbbf24) } diff --git a/game/enemies/EnemyBase.ts b/game/enemies/EnemyBase.ts index 17a2315..436a4b6 100644 --- a/game/enemies/EnemyBase.ts +++ b/game/enemies/EnemyBase.ts @@ -1,7 +1,8 @@ import type Phaser from 'phaser' import { GameManager } from '../GameManager' -import { HUD_HEIGHT, PATH_WAYPOINTS } from '../constants' +import { HUD_HEIGHT } from '../constants' import { getCellSize } from '../mapRenderer' +import { ALL_MAPS } from '../data/mapConfigs' export interface PathPoint { x: number; y: number } @@ -12,12 +13,19 @@ function gridToPixel(gx: number, gy: number, cellW: number, cellH: number): Path } } -export function buildFullPath(): PathPoint[] { +/** + * 将地图路径折线坐标转换为像素路径点序列 + * @param waypoints 折线关键坐标点(默认使用地图1) + */ +export function buildFullPath( + waypoints?: readonly { x: number; y: number }[] +): PathPoint[] { const { cellW, cellH } = getCellSize() + const pts = waypoints ?? ALL_MAPS[0].waypoints const points: PathPoint[] = [] - for (let i = 0; i < PATH_WAYPOINTS.length - 1; i++) { - const from = gridToPixel(PATH_WAYPOINTS[i].x, PATH_WAYPOINTS[i].y, cellW, cellH) - const to = gridToPixel(PATH_WAYPOINTS[i + 1].x, PATH_WAYPOINTS[i + 1].y, cellW, cellH) + for (let i = 0; i < pts.length - 1; i++) { + const from = gridToPixel(pts[i].x, pts[i].y, cellW, cellH) + const to = gridToPixel(pts[i + 1].x, pts[i + 1].y, cellW, cellH) points.push(from) points.push(to) } @@ -31,7 +39,6 @@ export interface DotEffect { damage: number; duration: number; timer: number } export abstract class EnemyBase { protected scene: Phaser.Scene protected imageSprite!: Phaser.GameObjects.Image - // expose for tower targeting & health bar public x: number = 0 public y: number = 0 protected healthBar!: Phaser.GameObjects.Graphics @@ -67,13 +74,15 @@ export abstract class EnemyBase { speed: number, kpiDamage: number, hcReward: number, - spriteKey: string + spriteKey: string, + speedMultiplier: number = 1.0, + hpMultiplier: number = 1.0 ) { this.scene = scene this.pathPoints = pathPoints - this.maxHp = maxHp - this.hp = maxHp - this.speed = speed + this.maxHp = Math.ceil(maxHp * hpMultiplier) + this.hp = this.maxHp + this.speed = speed * speedMultiplier this.kpiDamage = kpiDamage this.hcReward = hcReward this.spriteKey = spriteKey @@ -87,7 +96,6 @@ export abstract class EnemyBase { this.y = pathPoints[0].y } - // 用 AI 图片精灵,精确设为格子尺寸的 75%(怪物比塔小一点) const enemySize = cellW * 0.75 this.imageSprite = scene.add.image(this.x, this.y, spriteKey) this.imageSprite.setDisplaySize(enemySize, enemySize) @@ -108,7 +116,6 @@ export abstract class EnemyBase { .setAlpha(0) this.drawHealthBar() - // 出生后 0.5s 显示语录 scene.time.delayedCall(500, () => { if (!this.isDead) this.showQuote() }) } @@ -154,7 +161,6 @@ export abstract class EnemyBase { this.x += (dx / dist) * distance this.y += (dy / dist) * distance } - // 减速时图片变色 if (this.slowEffect > 0) { this.imageSprite.setTint(0x93c5fd) } else { diff --git a/game/enemies/FreshGraduate.ts b/game/enemies/FreshGraduate.ts index de59131..a741a95 100644 --- a/game/enemies/FreshGraduate.ts +++ b/game/enemies/FreshGraduate.ts @@ -3,8 +3,13 @@ import { EnemyBase, type PathPoint } from './EnemyBase' import { getRandomQuote } from '../data/quotes' export class FreshGraduate extends EnemyBase { - constructor(scene: Phaser.Scene, pathPoints: PathPoint[]) { - super(scene, pathPoints, 30, 120, 2, 10, 'enemy-fresh') + constructor( + scene: Phaser.Scene, + pathPoints: PathPoint[], + speedMultiplier: number = 1.0, + hpMultiplier: number = 1.0 + ) { + super(scene, pathPoints, 30, 120, 2, 10, 'enemy-fresh', speedMultiplier, hpMultiplier) } getQuote(): string { return getRandomQuote('FreshGraduate') } } diff --git a/game/enemies/OldEmployee.ts b/game/enemies/OldEmployee.ts index fd2bc2e..d88db60 100644 --- a/game/enemies/OldEmployee.ts +++ b/game/enemies/OldEmployee.ts @@ -3,10 +3,14 @@ import { EnemyBase, type PathPoint } from './EnemyBase' import { getRandomQuote } from '../data/quotes' export class OldEmployee extends EnemyBase { - constructor(scene: Phaser.Scene, pathPoints: PathPoint[]) { - super(scene, pathPoints, 150, 50, 8, 30, 'enemy-old') + constructor( + scene: Phaser.Scene, + pathPoints: PathPoint[], + speedMultiplier: number = 1.0, + hpMultiplier: number = 1.0 + ) { + super(scene, pathPoints, 150, 50, 8, 30, 'enemy-old', speedMultiplier, hpMultiplier) this.shieldCount = 3 - // 老员工比普通怪略大(0.85 个格子) this.imageSprite.setDisplaySize(this.cellW * 0.85, this.cellW * 0.85) } diff --git a/game/enemies/TroubleMaker.ts b/game/enemies/TroubleMaker.ts index 31f03e1..1835b23 100644 --- a/game/enemies/TroubleMaker.ts +++ b/game/enemies/TroubleMaker.ts @@ -4,8 +4,13 @@ import { GameManager } from '../GameManager' import { getRandomQuote } from '../data/quotes' export class TroubleMaker extends EnemyBase { - constructor(scene: Phaser.Scene, pathPoints: PathPoint[]) { - super(scene, pathPoints, 80, 80, 5, 20, 'enemy-trouble') + constructor( + scene: Phaser.Scene, + pathPoints: PathPoint[], + speedMultiplier: number = 1.0, + hpMultiplier: number = 1.0 + ) { + super(scene, pathPoints, 80, 80, 5, 20, 'enemy-trouble', speedMultiplier, hpMultiplier) } protected override onDeath(): void {