import type { World, EntityId } from 'db://assets/mx/module/ecs'; import { BattleEventFactory } from '../events/BattleEvents'; import type { BattleEventBus } from '../events/BattleEventBus'; import type { GridMap } from '../battlefield/GridMap'; import type { SpatialGrid } from '../battlefield/SpatialGrid'; import type { AbilitySystem } from './AbilitySystem'; /** 暂缓 kill 判定(还魂丹等:死亡特效播完再复活或真死) */ export type TryDeferEntityKillFn = (victim: EntityId, killer: EntityId, hpBeforeKill: number) => boolean; /** 实体生命周期 */ export class LifecycleSystem { private _tryDeferKill: TryDeferEntityKillFn | null = null; constructor( private readonly _world: World, private readonly _grid: GridMap, private readonly _spatial: SpatialGrid, private readonly _events: BattleEventBus, private readonly _abilities: AbilitySystem, ) {} /** 注册暂缓 kill 回调(BattleSession.tryDeferEntityKill 桥接) */ setTryDeferKillPredicate(fn: TryDeferEntityKillFn | null): void { this._tryDeferKill = fn; } /** * @param victim 被击杀实体 ID * @param killer 击杀者实体 ID * @param hpBeforeKill 致死前血量(还魂丹等复活继承用) * @param damageType 致死伤害类型 * @param fromPassiveResult 是否来自被动 result */ kill(victim: EntityId, killer: EntityId, hpBeforeKill = 0, damageType?: string, fromPassiveResult = false): void { if (!this._world.isAlive(victim)) return; if (this._tryDeferKill?.(victim, killer, hpBeforeKill)) return; // 先解绑触发器,避免后续 fireEvent('OnKill') 仍把 victim 当作施法者 this._abilities.unbindEntity(victim); this._grid.clearOccupant(victim); this._spatial.removeEntity(victim); // 立即派发:金币/飞桃需在 destroy 前读到 UnitMeta / 怪物节点 this._events.emit(BattleEventFactory.entityKilled(killer, victim, damageType, fromPassiveResult), true); this._world.destroy(victim); this._abilities.fireEvent('OnKill', { killer, victim }); } /** 无击杀结算移除(复活回收上场怪等) */ despawn(entity: EntityId): void { if (!this._world.isAlive(entity)) return; this._abilities.unbindEntity(entity); this._grid.clearOccupant(entity); this._spatial.removeEntity(entity); this._events.emit(BattleEventFactory.unitDespawned(entity), true); this._world.destroy(entity); } }