feat(game/enemies): 所有怪物子类支持 speedMultiplier/hpMultiplier 参数
This commit is contained in:
@@ -10,18 +10,17 @@ export class BossVP extends EnemyBase {
|
|||||||
constructor(
|
constructor(
|
||||||
scene: Phaser.Scene,
|
scene: Phaser.Scene,
|
||||||
pathPoints: PathPoint[],
|
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
|
this.onDestroyTower = onDestroyTower
|
||||||
// BOSS 放大到 1.3 个格子(跨格的威压感)
|
|
||||||
const bossSize = this.cellW * 1.3
|
const bossSize = this.cellW * 1.3
|
||||||
this.imageSprite.setDisplaySize(bossSize, bossSize)
|
this.imageSprite.setDisplaySize(bossSize, bossSize)
|
||||||
this.imageSprite.setDepth(12)
|
this.imageSprite.setDepth(12)
|
||||||
// BOSS 出现特效
|
|
||||||
scene.cameras.main.flash(800, 255, 0, 0, false)
|
scene.cameras.main.flash(800, 255, 0, 0, false)
|
||||||
this.showBossAlert()
|
this.showBossAlert()
|
||||||
// BOSS 名字标签
|
|
||||||
this.bossLabel = scene.add.text(this.x, this.y + this.cellH * 0.5, '空降VP', {
|
this.bossLabel = scene.add.text(this.x, this.y + this.cellH * 0.5, '空降VP', {
|
||||||
fontFamily: 'VT323, monospace', fontSize: '14px',
|
fontFamily: 'VT323, monospace', fontSize: '14px',
|
||||||
color: '#FBBF24', backgroundColor: '#7c2d12', padding: { x: 4, y: 1 },
|
color: '#FBBF24', backgroundColor: '#7c2d12', padding: { x: 4, y: 1 },
|
||||||
@@ -30,7 +29,7 @@ export class BossVP extends EnemyBase {
|
|||||||
|
|
||||||
private showBossAlert(): void {
|
private showBossAlert(): void {
|
||||||
const alert = this.scene.add
|
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',
|
fontFamily: 'VT323, monospace', fontSize: '36px',
|
||||||
color: '#FBBF24', backgroundColor: '#7F1D1D', padding: { x: 16, y: 8 },
|
color: '#FBBF24', backgroundColor: '#7F1D1D', padding: { x: 16, y: 8 },
|
||||||
}).setOrigin(0.5, 0.5).setDepth(50)
|
}).setOrigin(0.5, 0.5).setDepth(50)
|
||||||
@@ -48,11 +47,9 @@ export class BossVP extends EnemyBase {
|
|||||||
this.skillTimer = 20000
|
this.skillTimer = 20000
|
||||||
this.triggerOrgRestructure()
|
this.triggerOrgRestructure()
|
||||||
}
|
}
|
||||||
// 更新名字标签位置
|
|
||||||
if (this.bossLabel) {
|
if (this.bossLabel) {
|
||||||
this.bossLabel.setPosition(this.x, this.y + this.cellH * 0.5)
|
this.bossLabel.setPosition(this.x, this.y + this.cellH * 0.5)
|
||||||
}
|
}
|
||||||
// BOSS 金色发光边框
|
|
||||||
this.imageSprite.setTint(this.skillTimer < 3000 ? 0xff6600 : 0xfbbf24)
|
this.imageSprite.setTint(this.skillTimer < 3000 ? 0xff6600 : 0xfbbf24)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import type Phaser from 'phaser'
|
import type Phaser from 'phaser'
|
||||||
import { GameManager } from '../GameManager'
|
import { GameManager } from '../GameManager'
|
||||||
import { HUD_HEIGHT, PATH_WAYPOINTS } from '../constants'
|
import { HUD_HEIGHT } from '../constants'
|
||||||
import { getCellSize } from '../mapRenderer'
|
import { getCellSize } from '../mapRenderer'
|
||||||
|
import { ALL_MAPS } from '../data/mapConfigs'
|
||||||
|
|
||||||
export interface PathPoint { x: number; y: number }
|
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 { cellW, cellH } = getCellSize()
|
||||||
|
const pts = waypoints ?? ALL_MAPS[0].waypoints
|
||||||
const points: PathPoint[] = []
|
const points: PathPoint[] = []
|
||||||
for (let i = 0; i < PATH_WAYPOINTS.length - 1; i++) {
|
for (let i = 0; i < pts.length - 1; i++) {
|
||||||
const from = gridToPixel(PATH_WAYPOINTS[i].x, PATH_WAYPOINTS[i].y, cellW, cellH)
|
const from = gridToPixel(pts[i].x, pts[i].y, cellW, cellH)
|
||||||
const to = gridToPixel(PATH_WAYPOINTS[i + 1].x, PATH_WAYPOINTS[i + 1].y, cellW, cellH)
|
const to = gridToPixel(pts[i + 1].x, pts[i + 1].y, cellW, cellH)
|
||||||
points.push(from)
|
points.push(from)
|
||||||
points.push(to)
|
points.push(to)
|
||||||
}
|
}
|
||||||
@@ -31,7 +39,6 @@ export interface DotEffect { damage: number; duration: number; timer: number }
|
|||||||
export abstract class EnemyBase {
|
export abstract class EnemyBase {
|
||||||
protected scene: Phaser.Scene
|
protected scene: Phaser.Scene
|
||||||
protected imageSprite!: Phaser.GameObjects.Image
|
protected imageSprite!: Phaser.GameObjects.Image
|
||||||
// expose for tower targeting & health bar
|
|
||||||
public x: number = 0
|
public x: number = 0
|
||||||
public y: number = 0
|
public y: number = 0
|
||||||
protected healthBar!: Phaser.GameObjects.Graphics
|
protected healthBar!: Phaser.GameObjects.Graphics
|
||||||
@@ -67,13 +74,15 @@ export abstract class EnemyBase {
|
|||||||
speed: number,
|
speed: number,
|
||||||
kpiDamage: number,
|
kpiDamage: number,
|
||||||
hcReward: number,
|
hcReward: number,
|
||||||
spriteKey: string
|
spriteKey: string,
|
||||||
|
speedMultiplier: number = 1.0,
|
||||||
|
hpMultiplier: number = 1.0
|
||||||
) {
|
) {
|
||||||
this.scene = scene
|
this.scene = scene
|
||||||
this.pathPoints = pathPoints
|
this.pathPoints = pathPoints
|
||||||
this.maxHp = maxHp
|
this.maxHp = Math.ceil(maxHp * hpMultiplier)
|
||||||
this.hp = maxHp
|
this.hp = this.maxHp
|
||||||
this.speed = speed
|
this.speed = speed * speedMultiplier
|
||||||
this.kpiDamage = kpiDamage
|
this.kpiDamage = kpiDamage
|
||||||
this.hcReward = hcReward
|
this.hcReward = hcReward
|
||||||
this.spriteKey = spriteKey
|
this.spriteKey = spriteKey
|
||||||
@@ -87,7 +96,6 @@ export abstract class EnemyBase {
|
|||||||
this.y = pathPoints[0].y
|
this.y = pathPoints[0].y
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用 AI 图片精灵,精确设为格子尺寸的 75%(怪物比塔小一点)
|
|
||||||
const enemySize = cellW * 0.75
|
const enemySize = cellW * 0.75
|
||||||
this.imageSprite = scene.add.image(this.x, this.y, spriteKey)
|
this.imageSprite = scene.add.image(this.x, this.y, spriteKey)
|
||||||
this.imageSprite.setDisplaySize(enemySize, enemySize)
|
this.imageSprite.setDisplaySize(enemySize, enemySize)
|
||||||
@@ -108,7 +116,6 @@ export abstract class EnemyBase {
|
|||||||
.setAlpha(0)
|
.setAlpha(0)
|
||||||
|
|
||||||
this.drawHealthBar()
|
this.drawHealthBar()
|
||||||
// 出生后 0.5s 显示语录
|
|
||||||
scene.time.delayedCall(500, () => { if (!this.isDead) this.showQuote() })
|
scene.time.delayedCall(500, () => { if (!this.isDead) this.showQuote() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +161,6 @@ export abstract class EnemyBase {
|
|||||||
this.x += (dx / dist) * distance
|
this.x += (dx / dist) * distance
|
||||||
this.y += (dy / dist) * distance
|
this.y += (dy / dist) * distance
|
||||||
}
|
}
|
||||||
// 减速时图片变色
|
|
||||||
if (this.slowEffect > 0) {
|
if (this.slowEffect > 0) {
|
||||||
this.imageSprite.setTint(0x93c5fd)
|
this.imageSprite.setTint(0x93c5fd)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -3,8 +3,13 @@ import { EnemyBase, type PathPoint } from './EnemyBase'
|
|||||||
import { getRandomQuote } from '../data/quotes'
|
import { getRandomQuote } from '../data/quotes'
|
||||||
|
|
||||||
export class FreshGraduate extends EnemyBase {
|
export class FreshGraduate extends EnemyBase {
|
||||||
constructor(scene: Phaser.Scene, pathPoints: PathPoint[]) {
|
constructor(
|
||||||
super(scene, pathPoints, 30, 120, 2, 10, 'enemy-fresh')
|
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') }
|
getQuote(): string { return getRandomQuote('FreshGraduate') }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,14 @@ import { EnemyBase, type PathPoint } from './EnemyBase'
|
|||||||
import { getRandomQuote } from '../data/quotes'
|
import { getRandomQuote } from '../data/quotes'
|
||||||
|
|
||||||
export class OldEmployee extends EnemyBase {
|
export class OldEmployee extends EnemyBase {
|
||||||
constructor(scene: Phaser.Scene, pathPoints: PathPoint[]) {
|
constructor(
|
||||||
super(scene, pathPoints, 150, 50, 8, 30, 'enemy-old')
|
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
|
this.shieldCount = 3
|
||||||
// 老员工比普通怪略大(0.85 个格子)
|
|
||||||
this.imageSprite.setDisplaySize(this.cellW * 0.85, this.cellW * 0.85)
|
this.imageSprite.setDisplaySize(this.cellW * 0.85, this.cellW * 0.85)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,13 @@ import { GameManager } from '../GameManager'
|
|||||||
import { getRandomQuote } from '../data/quotes'
|
import { getRandomQuote } from '../data/quotes'
|
||||||
|
|
||||||
export class TroubleMaker extends EnemyBase {
|
export class TroubleMaker extends EnemyBase {
|
||||||
constructor(scene: Phaser.Scene, pathPoints: PathPoint[]) {
|
constructor(
|
||||||
super(scene, pathPoints, 80, 80, 5, 20, 'enemy-trouble')
|
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 {
|
protected override onDeath(): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user