You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
81 lines
2.9 KiB
81 lines
2.9 KiB
|
3 weeks ago
|
import type { World, EntityId } from 'db://assets/mx/module/ecs';
|
||
|
|
import type { ISystem } from 'db://assets/mx/module/ecs';
|
||
|
|
import { BattlePhase } from '../core/BattlePhase';
|
||
|
|
import { BattleTeamId } from '../ecs/BattleTeamId';
|
||
|
|
import { MovementComp, TeamComp } from '../ecs/Components';
|
||
|
|
import type { PlayerSlot } from '../player/PlayerSlot';
|
||
|
|
import type { BattleEventBus } from '../events/BattleEventBus';
|
||
|
|
import type { LifecycleSystem } from './LifecycleSystem';
|
||
|
|
|
||
|
|
/** 怪物到达攻击点:对手营地 -1 血并移除该怪 */
|
||
|
|
export class ReachGoalBaseDamageSystem implements ISystem {
|
||
|
|
/** 调度优先级(Movement 50 之后) */
|
||
|
|
readonly priority = 55;
|
||
|
|
/** 是否启用 */
|
||
|
|
enabled = true;
|
||
|
|
/** 本帧待结算漏怪 */
|
||
|
|
private readonly _leakIds: EntityId[] = [];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param _world ECS 世界
|
||
|
|
* @param _players 玩家槽
|
||
|
|
* @param _events 事件总线
|
||
|
|
* @param _lifecycle 实体生命周期
|
||
|
|
* @param _getPhase 当前战斗阶段
|
||
|
|
*/
|
||
|
|
constructor(
|
||
|
|
private readonly _world: World,
|
||
|
|
private readonly _players: PlayerSlot[],
|
||
|
|
private readonly _events: BattleEventBus,
|
||
|
|
private readonly _lifecycle: LifecycleSystem,
|
||
|
|
private readonly _getPhase: () => BattlePhase,
|
||
|
|
) {}
|
||
|
|
|
||
|
|
/** 每帧收集已到达攻击点的敌怪并结算漏怪 */
|
||
|
|
tick(_dt: number): void {
|
||
|
|
if (this._getPhase() !== BattlePhase.COMBAT) return;
|
||
|
|
this._leakIds.length = 0;
|
||
|
|
this._world.view2(MovementComp, TeamComp).forEach(this._collectLeak);
|
||
|
|
for (let i = 0; i < this._leakIds.length; i++) {
|
||
|
|
this._applyLeak(this._leakIds[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 收集本帧到达攻击点的敌怪。
|
||
|
|
* @param id 实体
|
||
|
|
* @param move 移动组件
|
||
|
|
* @param team 阵营
|
||
|
|
*/
|
||
|
|
private readonly _collectLeak = (id: EntityId, move: MovementComp, team: TeamComp): void => {
|
||
|
|
if (!BattleTeamId.isEnemyTeam(team.team)) return;
|
||
|
|
if (!move.reachedGoal || !move.hasAttackGoal) return;
|
||
|
|
if (move.spawnProtected || move.presentLocked || move.imprisoned) return;
|
||
|
|
if (move.bajiaoBlowRetreating || move.forcePathSpeed > 0) return;
|
||
|
|
this._leakIds.push(id);
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param id 到达营地的敌怪
|
||
|
|
*/
|
||
|
|
private _applyLeak(id: EntityId): void {
|
||
|
|
if (!this._world.isAlive(id)) return;
|
||
|
|
const team = this._world.getComponent(id, TeamComp);
|
||
|
|
if (!team) {
|
||
|
|
console.log('[ReachGoal] 漏怪无 TeamComp', id);
|
||
|
|
this._lifecycle.despawn(id);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const slot = this._players[team.ownerId];
|
||
|
|
if (!slot) {
|
||
|
|
console.log('[ReachGoal] 无对应玩家槽', team.ownerId, id);
|
||
|
|
this._lifecycle.despawn(id);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (slot.wallHp > 0) {
|
||
|
|
slot.damageWall(1, this._events);
|
||
|
|
}
|
||
|
|
this._lifecycle.despawn(id);
|
||
|
|
}
|
||
|
|
}
|