fix(game): 修复底部塔面板不可见问题,改用React层实现,并用AI素材替换Graphics像素块
This commit is contained in:
@@ -1,45 +1,24 @@
|
||||
import type Phaser from 'phaser'
|
||||
import { TowerBase } from './TowerBase'
|
||||
import type { EnemyBase } from '../enemies/EnemyBase'
|
||||
import type { TowerBase as TowerBaseType } from './TowerBase'
|
||||
|
||||
const BUFF_ATTACK_SPEED_BONUS = 0.2
|
||||
|
||||
export class HRBPTower extends TowerBase {
|
||||
private buffCooldown: number = 0
|
||||
private readonly BUFF_INTERVAL = 500
|
||||
private nearbyTowersBuff: Set<TowerBaseType> = new Set()
|
||||
private nearbyTowersBuff: Set<TowerBase> = new Set()
|
||||
|
||||
constructor(scene: Phaser.Scene, gridX: number, gridY: number) {
|
||||
super(scene, gridX, gridY, 80, 1, 0, 0)
|
||||
this.drawSprite()
|
||||
}
|
||||
|
||||
drawSprite(): void {
|
||||
if (!this.sprite) return
|
||||
this.sprite.clear()
|
||||
// 菱形(粉色)
|
||||
this.sprite.fillStyle(0xec4899, 1)
|
||||
this.sprite.fillTriangle(0, -16, 16, 0, 0, 16)
|
||||
this.sprite.fillTriangle(0, -16, -16, 0, 0, 16)
|
||||
this.sprite.setPosition(this.px, this.py)
|
||||
this.sprite.setDepth(10)
|
||||
super(scene, gridX, gridY, 80, 1, 0, 0, 'tower-hrbp')
|
||||
}
|
||||
|
||||
override update(delta: number, enemies: EnemyBase[]): void {
|
||||
// HRBP 没有攻击逻辑,只做 BUFF
|
||||
void enemies
|
||||
|
||||
if (!this.isActive) {
|
||||
this.stamina = Math.min(
|
||||
this.maxStamina,
|
||||
this.stamina + (this.staminaRegen * delta) / 1000
|
||||
)
|
||||
this.stamina = Math.min(this.maxStamina, this.stamina + (this.staminaRegen * delta) / 1000)
|
||||
if (this.stamina > 20) this.isActive = true
|
||||
this.updateStaminaBar()
|
||||
return
|
||||
}
|
||||
|
||||
this.buffCooldown -= delta
|
||||
if (this.buffCooldown <= 0) {
|
||||
this.buffCooldown = this.BUFF_INTERVAL
|
||||
@@ -47,44 +26,30 @@ export class HRBPTower extends TowerBase {
|
||||
}
|
||||
}
|
||||
|
||||
setNearbyTowers(towers: TowerBaseType[]): void {
|
||||
setNearbyTowers(towers: TowerBase[]): void {
|
||||
this.nearbyTowersBuff = new Set(towers)
|
||||
}
|
||||
|
||||
private applyBuffToNearby(): void {
|
||||
if (this.nearbyTowersBuff.size === 0) return
|
||||
if (this.stamina < 5) {
|
||||
this.isActive = false
|
||||
return
|
||||
}
|
||||
if (this.stamina < 5) { this.isActive = false; return }
|
||||
this.stamina -= 5
|
||||
this.updateStaminaBar()
|
||||
// BUFF 效果通过 attackSpeedMultiplier 外部读取
|
||||
// 这里显示一个粉色光圈效果
|
||||
this.showBuffEffect()
|
||||
}
|
||||
|
||||
private showBuffEffect(): void {
|
||||
const g = this.scene.add.graphics()
|
||||
g.lineStyle(2, 0xec4899, 0.6)
|
||||
g.strokeCircle(this.px, this.py, 90)
|
||||
g.strokeCircle(this.px, this.py, this.cellW * 1.5)
|
||||
g.setDepth(8)
|
||||
this.scene.tweens.add({
|
||||
targets: g,
|
||||
alpha: 0,
|
||||
duration: 400,
|
||||
targets: g, alpha: 0, duration: 400,
|
||||
onComplete: () => g.destroy(),
|
||||
})
|
||||
}
|
||||
|
||||
getBuffedTowers(): Set<TowerBaseType> {
|
||||
return this.nearbyTowersBuff
|
||||
}
|
||||
|
||||
getAttackSpeedBonus(): number {
|
||||
return BUFF_ATTACK_SPEED_BONUS
|
||||
}
|
||||
|
||||
// HRBP 无直接攻击
|
||||
getBuffedTowers(): Set<TowerBase> { return this.nearbyTowersBuff }
|
||||
getAttackSpeedBonus(): number { return 0.2 }
|
||||
attack(_target: EnemyBase): void {}
|
||||
}
|
||||
|
||||
@@ -10,37 +10,18 @@ export class InternTower extends TowerBase {
|
||||
public onSelfDestroy?: (tower: InternTower) => void
|
||||
|
||||
constructor(scene: Phaser.Scene, gridX: number, gridY: number) {
|
||||
super(scene, gridX, gridY, 50, 1, 15, 1.5)
|
||||
this.drawSprite()
|
||||
}
|
||||
|
||||
drawSprite(): void {
|
||||
if (!this.sprite) return
|
||||
this.sprite.clear()
|
||||
// 绿色小人(圆头+十字身体)
|
||||
this.sprite.fillStyle(0x22c55e, 1)
|
||||
this.sprite.fillCircle(0, -12, 8)
|
||||
// 身体
|
||||
this.sprite.fillRect(-3, -4, 6, 14)
|
||||
// 手臂
|
||||
this.sprite.fillRect(-12, -2, 24, 4)
|
||||
this.sprite.setPosition(this.px, this.py)
|
||||
this.sprite.setDepth(10)
|
||||
super(scene, gridX, gridY, 50, 1, 15, 1.5, 'tower-intern')
|
||||
}
|
||||
|
||||
override update(delta: number, enemies: EnemyBase[]): void {
|
||||
if (this.destroyed) return
|
||||
|
||||
super.update(delta, enemies)
|
||||
|
||||
// 被动:每秒1%概率离场
|
||||
this.selfDestroyTimer += delta
|
||||
if (this.selfDestroyTimer >= this.SELF_DESTROY_INTERVAL) {
|
||||
this.selfDestroyTimer -= this.SELF_DESTROY_INTERVAL
|
||||
if (Math.random() < 0.01) {
|
||||
// 退还 25 HC
|
||||
GameManager.getInstance().addHC(25)
|
||||
this.showMessage('实习生跑路!+25HC')
|
||||
this.showMessage('实习生跑路!+25HC', '#22C55E')
|
||||
this.destroyed = true
|
||||
this.onSelfDestroy?.(this)
|
||||
this.destroy()
|
||||
@@ -50,42 +31,32 @@ export class InternTower extends TowerBase {
|
||||
}
|
||||
|
||||
attack(target: EnemyBase): void {
|
||||
// 整顿职场:5% 概率秒杀 HP < 500 的怪物
|
||||
if (Math.random() < 0.05 && target.hp < 500) {
|
||||
target.takeDamage(9999)
|
||||
this.showMessage('整顿职场!秒杀!')
|
||||
this.showMessage('整顿职场!秒杀!', '#A3E635')
|
||||
} else {
|
||||
target.takeDamage(this.attackDamage)
|
||||
}
|
||||
// 近战效果(闪光)
|
||||
this.showMeleeEffect(target)
|
||||
}
|
||||
|
||||
private showMeleeEffect(target: EnemyBase): void {
|
||||
const g = this.scene.add.graphics()
|
||||
g.fillStyle(0x22c55e, 0.7)
|
||||
g.fillCircle(target.sprite.x, target.sprite.y, 10)
|
||||
g.fillStyle(0x22c55e, 0.6)
|
||||
g.fillCircle(target.x, target.y, 12)
|
||||
g.setDepth(15)
|
||||
this.scene.time.delayedCall(150, () => g.destroy())
|
||||
}
|
||||
|
||||
private showMessage(msg: string): void {
|
||||
private showMessage(msg: string, color: string): void {
|
||||
const txt = this.scene.add
|
||||
.text(this.px, this.py - 30, msg, {
|
||||
fontFamily: 'VT323, monospace',
|
||||
fontSize: '14px',
|
||||
color: '#22C55E',
|
||||
backgroundColor: '#14532D',
|
||||
padding: { x: 4, y: 2 },
|
||||
})
|
||||
.setOrigin(0.5, 1)
|
||||
.setDepth(20)
|
||||
fontFamily: 'VT323, monospace', fontSize: '14px',
|
||||
color, backgroundColor: '#052e16', padding: { x: 4, y: 2 },
|
||||
}).setOrigin(0.5, 1).setDepth(20)
|
||||
this.scene.tweens.add({
|
||||
targets: txt,
|
||||
y: this.py - 55,
|
||||
alpha: 0,
|
||||
duration: 1200,
|
||||
onComplete: () => txt.destroy(),
|
||||
targets: txt, y: this.py - 55, alpha: 0,
|
||||
duration: 1200, onComplete: () => txt.destroy(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,57 +4,37 @@ import type { EnemyBase } from '../enemies/EnemyBase'
|
||||
|
||||
export class PPTMasterTower extends TowerBase {
|
||||
constructor(scene: Phaser.Scene, gridX: number, gridY: number) {
|
||||
super(scene, gridX, gridY, 100, 3, 5, 1.5)
|
||||
this.drawSprite()
|
||||
super(scene, gridX, gridY, 100, 3, 5, 1.5, 'tower-ppt')
|
||||
}
|
||||
|
||||
drawSprite(): void {
|
||||
if (!this.sprite) return
|
||||
this.sprite.clear()
|
||||
// 橙色圆形
|
||||
this.sprite.fillStyle(0xf59e0b, 1)
|
||||
this.sprite.fillCircle(0, 0, 16)
|
||||
// 圆心白点
|
||||
this.sprite.fillStyle(0xffffff, 0.9)
|
||||
this.sprite.fillCircle(0, 0, 5)
|
||||
this.sprite.setPosition(this.px, this.py)
|
||||
this.sprite.setDepth(10)
|
||||
attack(_target: EnemyBase): void {
|
||||
// AOE 攻击由 TowerManager.updatePPTTower 直接调用 attackAoe
|
||||
}
|
||||
|
||||
attack(target: EnemyBase): void {
|
||||
// AOE 攻击:对射程内所有怪物造成伤害 + 减速
|
||||
this.showAoeEffect()
|
||||
}
|
||||
|
||||
/** 该方法由 TowerManager 调用,传入全体敌人 */
|
||||
attackAoe(enemies: EnemyBase[]): void {
|
||||
const rangePx = this.attackRange * 80 // TILE_SIZE
|
||||
this.showAoeEffect()
|
||||
const rangePx = this.attackRange * this.cellW
|
||||
this.showAoeEffect(rangePx)
|
||||
for (const e of enemies) {
|
||||
if (e.isDead) continue
|
||||
const dx = e.sprite.x - this.px
|
||||
const dy = e.sprite.y - this.py
|
||||
const dist = Math.sqrt(dx * dx + dy * dy)
|
||||
if (dist <= rangePx) {
|
||||
const dx = e.x - this.px
|
||||
const dy = e.y - this.py
|
||||
if (Math.sqrt(dx * dx + dy * dy) <= rangePx) {
|
||||
e.takeDamage(this.attackDamage)
|
||||
// 黑话领域:减速40%持续2秒
|
||||
e.addSlow(0.4, 2000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private showAoeEffect(): void {
|
||||
private showAoeEffect(rangePx: number): void {
|
||||
const g = this.scene.add.graphics()
|
||||
g.lineStyle(2, 0xf59e0b, 0.8)
|
||||
g.strokeCircle(this.px, this.py, this.attackRange * 80)
|
||||
g.setDepth(12)
|
||||
g.lineStyle(2, 0xf59e0b, 0.7)
|
||||
g.strokeCircle(this.px, this.py, rangePx)
|
||||
g.fillStyle(0xf59e0b, 0.07)
|
||||
g.fillCircle(this.px, this.py, rangePx)
|
||||
g.setDepth(8)
|
||||
this.scene.tweens.add({
|
||||
targets: g,
|
||||
alpha: 0,
|
||||
scaleX: 1.2,
|
||||
scaleY: 1.2,
|
||||
duration: 400,
|
||||
onComplete: () => g.destroy(),
|
||||
targets: g, alpha: 0, scaleX: 1.15, scaleY: 1.15,
|
||||
duration: 450, onComplete: () => g.destroy(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,71 +4,43 @@ import type { EnemyBase } from '../enemies/EnemyBase'
|
||||
|
||||
export class SeniorDevTower extends TowerBase {
|
||||
constructor(scene: Phaser.Scene, gridX: number, gridY: number) {
|
||||
super(scene, gridX, gridY, 120, 5, 30, 1.0)
|
||||
this.drawSprite()
|
||||
}
|
||||
|
||||
drawSprite(): void {
|
||||
if (!this.sprite) return
|
||||
this.sprite.clear()
|
||||
// 深蓝色方块
|
||||
this.sprite.fillStyle(0x3b82f6, 1)
|
||||
this.sprite.fillRect(-12, -12, 24, 24)
|
||||
this.sprite.lineStyle(1, 0x93c5fd, 1)
|
||||
this.sprite.strokeRect(-12, -12, 24, 24)
|
||||
this.sprite.setPosition(this.px, this.py)
|
||||
this.sprite.setDepth(10)
|
||||
|
||||
// 顶部 </> 符号文字
|
||||
if (this.scene) {
|
||||
const existing = this.scene.children.getByName(`dev_label_${this.gridX}_${this.gridY}`)
|
||||
if (!existing) {
|
||||
this.scene.add
|
||||
.text(this.px, this.py, '</>', {
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '10px',
|
||||
color: '#DBEAFE',
|
||||
})
|
||||
.setOrigin(0.5, 0.5)
|
||||
.setDepth(11)
|
||||
.setName(`dev_label_${this.gridX}_${this.gridY}`)
|
||||
}
|
||||
}
|
||||
super(scene, gridX, gridY, 120, 5, 30, 1.0, 'tower-senior')
|
||||
}
|
||||
|
||||
attack(target: EnemyBase): void {
|
||||
// 发射绿色代码块子弹
|
||||
this.fireBullet(target)
|
||||
}
|
||||
|
||||
private fireBullet(target: EnemyBase): void {
|
||||
const bullet = this.scene.add.graphics()
|
||||
bullet.fillStyle(0x22c55e, 1)
|
||||
bullet.fillRect(-4, -4, 8, 8)
|
||||
bullet.fillRoundedRect(-5, -5, 10, 10, 2)
|
||||
// 添加绿色发光
|
||||
bullet.lineStyle(1, 0x86efac, 0.8)
|
||||
bullet.strokeRoundedRect(-6, -6, 12, 12, 3)
|
||||
bullet.setPosition(this.px, this.py)
|
||||
bullet.setDepth(13)
|
||||
|
||||
const startX = this.px
|
||||
const startY = this.py
|
||||
const targetX = target.sprite.x
|
||||
const targetY = target.sprite.y
|
||||
|
||||
const dx = targetX - startX
|
||||
const dy = targetY - startY
|
||||
const dx = target.x - this.px
|
||||
const dy = target.y - this.py
|
||||
const dist = Math.sqrt(dx * dx + dy * dy)
|
||||
const duration = (dist / 400) * 1000
|
||||
const duration = (dist / 500) * 1000
|
||||
|
||||
this.scene.tweens.add({
|
||||
targets: bullet,
|
||||
x: targetX,
|
||||
y: targetY,
|
||||
x: target.x, y: target.y,
|
||||
duration,
|
||||
onComplete: () => {
|
||||
bullet.destroy()
|
||||
if (!target.isDead) {
|
||||
target.takeDamage(this.attackDamage)
|
||||
// 代码屎山:附加 DOT
|
||||
target.addDOT(10, 3000)
|
||||
// DOT 命中效果
|
||||
const fx = this.scene.add.graphics()
|
||||
fx.lineStyle(2, 0x22c55e, 0.8)
|
||||
fx.strokeCircle(target.x, target.y, 14)
|
||||
fx.setDepth(15)
|
||||
this.scene.time.delayedCall(300, () => fx.destroy())
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import type Phaser from 'phaser'
|
||||
import { GameManager } from '../GameManager'
|
||||
import { TILE_SIZE, HUD_HEIGHT, COFFEE_COST, STAMINA_MAX, STAMINA_REGEN } from '../constants'
|
||||
import { HUD_HEIGHT, COFFEE_COST, STAMINA_MAX, STAMINA_REGEN } from '../constants'
|
||||
import { getCellSize } from '../mapRenderer'
|
||||
import type { EnemyBase } from '../enemies/EnemyBase'
|
||||
|
||||
export abstract class TowerBase {
|
||||
protected scene: Phaser.Scene
|
||||
public gridX: number
|
||||
public gridY: number
|
||||
protected sprite!: Phaser.GameObjects.Graphics
|
||||
protected imageSprite!: Phaser.GameObjects.Image
|
||||
protected spriteKey: string
|
||||
protected staminaBar!: Phaser.GameObjects.Graphics
|
||||
private frozenOverlay!: Phaser.GameObjects.Graphics
|
||||
|
||||
@@ -23,9 +25,11 @@ export abstract class TowerBase {
|
||||
protected staminaRegen: number = STAMINA_REGEN
|
||||
protected isActive: boolean = true
|
||||
|
||||
// Pixel center position
|
||||
// Pixel center position (computed from actual cell size)
|
||||
protected px: number
|
||||
protected py: number
|
||||
protected cellW: number
|
||||
protected cellH: number
|
||||
|
||||
constructor(
|
||||
scene: Phaser.Scene,
|
||||
@@ -34,7 +38,8 @@ export abstract class TowerBase {
|
||||
cost: number,
|
||||
attackRange: number,
|
||||
attackDamage: number,
|
||||
attackSpeed: number
|
||||
attackSpeed: number,
|
||||
spriteKey: string
|
||||
) {
|
||||
this.scene = scene
|
||||
this.gridX = gridX
|
||||
@@ -43,41 +48,44 @@ export abstract class TowerBase {
|
||||
this.attackRange = attackRange
|
||||
this.attackDamage = attackDamage
|
||||
this.attackSpeed = attackSpeed
|
||||
this.spriteKey = spriteKey
|
||||
|
||||
this.px = gridX * TILE_SIZE + TILE_SIZE / 2
|
||||
this.py = gridY * TILE_SIZE + TILE_SIZE / 2 + HUD_HEIGHT
|
||||
const { cellW, cellH } = getCellSize()
|
||||
this.cellW = cellW
|
||||
this.cellH = cellH
|
||||
this.px = gridX * cellW + cellW / 2
|
||||
this.py = HUD_HEIGHT + gridY * cellH + cellH / 2
|
||||
|
||||
// 用 AI 图片作为精灵
|
||||
this.imageSprite = scene.add.image(this.px, this.py, spriteKey)
|
||||
const scale = Math.min(cellW, cellH) / 128 * 0.85
|
||||
this.imageSprite.setScale(scale)
|
||||
this.imageSprite.setDepth(10)
|
||||
|
||||
this.sprite = scene.add.graphics()
|
||||
this.staminaBar = scene.add.graphics()
|
||||
this.frozenOverlay = scene.add.graphics()
|
||||
|
||||
this.drawSprite()
|
||||
this.updateStaminaBar()
|
||||
}
|
||||
|
||||
update(delta: number, enemies: EnemyBase[]): void {
|
||||
// 禁锢状态:跳过攻击,显示灰色覆盖
|
||||
if (this.isFrozen) {
|
||||
this.drawFrozenOverlay()
|
||||
return
|
||||
}
|
||||
// 解除禁锢时清除覆盖层
|
||||
this.clearFrozenOverlay()
|
||||
this.attackCooldown -= delta
|
||||
|
||||
if (this.stamina <= 0) {
|
||||
this.isActive = false
|
||||
}
|
||||
if (this.stamina <= 0) this.isActive = false
|
||||
|
||||
if (!this.isActive) {
|
||||
this.stamina = Math.min(
|
||||
this.maxStamina,
|
||||
this.stamina + (this.staminaRegen * delta) / 1000
|
||||
)
|
||||
this.stamina = Math.min(this.maxStamina, this.stamina + (this.staminaRegen * delta) / 1000)
|
||||
if (this.stamina > 20) this.isActive = true
|
||||
this.updateStaminaBar()
|
||||
// 摸鱼时图片半透明
|
||||
this.imageSprite.setAlpha(0.5)
|
||||
return
|
||||
}
|
||||
this.imageSprite.setAlpha(1)
|
||||
|
||||
const target = this.findTarget(enemies)
|
||||
if (target && this.attackCooldown <= 0) {
|
||||
@@ -86,29 +94,23 @@ export abstract class TowerBase {
|
||||
this.attackCooldown = 1000 / this.attackSpeed
|
||||
this.updateStaminaBar()
|
||||
} else if (!target) {
|
||||
this.stamina = Math.min(
|
||||
this.maxStamina,
|
||||
this.stamina + (this.staminaRegen * delta) / 1000
|
||||
)
|
||||
this.stamina = Math.min(this.maxStamina, this.stamina + (this.staminaRegen * delta) / 1000)
|
||||
this.updateStaminaBar()
|
||||
}
|
||||
}
|
||||
|
||||
protected findTarget(enemies: EnemyBase[]): EnemyBase | null {
|
||||
const rangePx = this.attackRange * TILE_SIZE
|
||||
const rangePx = this.attackRange * this.cellW
|
||||
let best: EnemyBase | null = null
|
||||
let bestProgress = -1
|
||||
|
||||
for (const e of enemies) {
|
||||
if (e.isDead) continue
|
||||
const dx = e.sprite.x - this.px
|
||||
const dy = e.sprite.y - this.py
|
||||
const dx = e.x - this.px
|
||||
const dy = e.y - this.py
|
||||
const dist = Math.sqrt(dx * dx + dy * dy)
|
||||
if (dist <= rangePx) {
|
||||
// 优先选取路径进度最深的(模拟血量最多/威胁最大)
|
||||
const progress = e.pathProgress
|
||||
if (progress > bestProgress) {
|
||||
bestProgress = progress
|
||||
if (e.pathProgress > bestProgress) {
|
||||
bestProgress = e.pathProgress
|
||||
best = e
|
||||
}
|
||||
}
|
||||
@@ -118,15 +120,16 @@ export abstract class TowerBase {
|
||||
|
||||
protected updateStaminaBar(): void {
|
||||
this.staminaBar.clear()
|
||||
const bw = 40
|
||||
const bh = 4
|
||||
const bw = this.cellW * 0.7
|
||||
const bh = 5
|
||||
const bx = this.px - bw / 2
|
||||
const by = this.py + TILE_SIZE / 2 - 8
|
||||
|
||||
this.staminaBar.fillStyle(0x374151, 1)
|
||||
const by = this.py + this.cellH / 2 - 10
|
||||
this.staminaBar.fillStyle(0x1f2937, 1)
|
||||
this.staminaBar.fillRect(bx, by, bw, bh)
|
||||
this.staminaBar.fillStyle(0xf59e0b, 1)
|
||||
this.staminaBar.fillRect(bx, by, bw * (this.stamina / this.maxStamina), bh)
|
||||
const ratio = this.stamina / this.maxStamina
|
||||
const color = ratio > 0.5 ? 0xf59e0b : ratio > 0.25 ? 0xfb923c : 0xef4444
|
||||
this.staminaBar.fillStyle(color, 1)
|
||||
this.staminaBar.fillRect(bx, by, bw * ratio, bh)
|
||||
this.staminaBar.setDepth(11)
|
||||
}
|
||||
|
||||
@@ -135,6 +138,7 @@ export abstract class TowerBase {
|
||||
if (manager.spendHC(COFFEE_COST)) {
|
||||
this.stamina = this.maxStamina
|
||||
this.isActive = true
|
||||
this.imageSprite.setAlpha(1)
|
||||
this.updateStaminaBar()
|
||||
return true
|
||||
}
|
||||
@@ -145,30 +149,25 @@ export abstract class TowerBase {
|
||||
return { x: this.px, y: this.py }
|
||||
}
|
||||
|
||||
/** 绘制禁锢状态灰色覆盖层 */
|
||||
protected drawFrozenOverlay(): void {
|
||||
this.frozenOverlay.clear()
|
||||
const half = TILE_SIZE / 2
|
||||
this.frozenOverlay.fillStyle(0x6b7280, 0.6)
|
||||
this.frozenOverlay.fillRect(
|
||||
this.px - half,
|
||||
this.py - half,
|
||||
TILE_SIZE,
|
||||
TILE_SIZE
|
||||
)
|
||||
const hw = this.cellW / 2
|
||||
const hh = this.cellH / 2
|
||||
this.frozenOverlay.fillStyle(0x6b7280, 0.55)
|
||||
this.frozenOverlay.fillRect(this.px - hw, this.py - hh, this.cellW, this.cellH)
|
||||
this.frozenOverlay.setDepth(13)
|
||||
this.imageSprite.setTint(0x9ca3af)
|
||||
}
|
||||
|
||||
/** 清除禁锢覆盖层 */
|
||||
protected clearFrozenOverlay(): void {
|
||||
this.frozenOverlay.clear()
|
||||
this.imageSprite.clearTint()
|
||||
}
|
||||
|
||||
abstract attack(target: EnemyBase): void
|
||||
abstract drawSprite(): void
|
||||
|
||||
destroy(): void {
|
||||
this.sprite?.destroy()
|
||||
this.imageSprite?.destroy()
|
||||
this.staminaBar?.destroy()
|
||||
this.frozenOverlay?.destroy()
|
||||
}
|
||||
|
||||
@@ -106,8 +106,8 @@ export class TowerManager {
|
||||
|
||||
const hasTarget = enemies.some((e) => {
|
||||
if (e.isDead) return false
|
||||
const dx = e.sprite.x - tower['px']
|
||||
const dy = e.sprite.y - tower['py']
|
||||
const dx = e.x - tower['px']
|
||||
const dy = e.y - tower['py']
|
||||
return Math.sqrt(dx * dx + dy * dy) <= tower.attackRange * TILE_SIZE
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user