import type { BattleSession } from 'db://assets/mx/module/battle'; import { AttributeComp, CombatComp } from 'db://assets/mx/module/battle/ecs/Components'; import type { EntityId } from 'db://assets/mx/module/ecs'; import { BattleBuffCarryStore } from '../../manager/battleBuff/BattleBuffCarryStore'; import { resolveCarriedBothAttackSpeed, resolveCarriedUnitAttackSpeedWan, } from '../../manager/battleBuff/BattleBuffPassiveSkillUtil'; import type { ProjectBattleHandle } from '../runtime/BattleRuntime'; import { AI_PLAYER_ID } from '../ai/runtime/AiCampArea'; /** 被动攻击单位攻速 modifier 分组 */ const UNIT_ASPD_MOD_GROUP = 'battle_buff_unit_aspd'; /** 单玩家攻速万分比累加 */ type PlayerAspd = { /** playerId */ playerId: number; /** 万分比 */ bonusWan: number; }; /** * 被动 functiontype=3 攻速符 + functiontype=7 鏖战册:携带方场上单位攻速。 */ export class BattleBuffUnitAttackSpeedHost { private _battle: ProjectBattleHandle | null = null; private readonly _bonuses: PlayerAspd[] = []; /** 绑定本局并对已有攻击单位施加攻速加成 */ bind(battle: ProjectBattleHandle): void { this.unbind(); this._battle = battle; this._bonuses.length = 0; this._accumulateCarrier(battle.localPlayerId); this._accumulateCarrier(AI_PLAYER_ID); if (this._bonuses.length <= 0) return; for (let i = 0; i < this._bonuses.length; i++) { const player = battle.getPlayer(this._bonuses[i].playerId); if (!player) continue; const deployed = player.deployedUnits; for (let u = 0; u < deployed.length; u++) { this.refreshEntity(battle.session, deployed[u], this._bonuses[i].playerId); } } } /** 解除绑定 */ unbind(): void { this._battle = null; this._bonuses.length = 0; } /** * 塔部署/升级后补施加被动攻速。 * @param session 会话 * @param entityId 实体 * @param ownerPlayerId 塔归属;缺省用本机 */ refreshEntity(session: BattleSession, entityId: EntityId, ownerPlayerId?: number): void { if (this._bonuses.length <= 0) return; const pid = ownerPlayerId != null ? Math.max(0, Math.floor(Number(ownerPlayerId) || 0)) : (this._battle?.localPlayerId ?? 0); for (let i = 0; i < this._bonuses.length; i++) { if (this._bonuses[i].playerId !== pid) continue; this._applyModifier(session, entityId, this._bonuses[i].bonusWan, pid); return; } } /** * 汇总携带方攻速符与鏖战册(己方/对方分量)。 * @param carrierPlayerId 携带方 playerId */ private _accumulateCarrier(carrierPlayerId: number): void { const passives = BattleBuffCarryStore.getPassives(carrierPlayerId); const unitAspd = resolveCarriedUnitAttackSpeedWan(passives); if (unitAspd.active && unitAspd.bonusWan > 0) { this._addBonusWan(carrierPlayerId, unitAspd.bonusWan); } const both = resolveCarriedBothAttackSpeed(passives); if (!both.active) return; if (both.selfWan !== 0) { this._addBonusWan(carrierPlayerId, both.selfWan); } const opponentId = this._resolveOpponentPlayerId(carrierPlayerId); if (opponentId >= 0 && both.enemyWan !== 0) { this._addBonusWan(opponentId, both.enemyWan); } } /** * 双人对战对手 id。 * @param playerId 当前玩家 */ private _resolveOpponentPlayerId(playerId: number): number { const battle = this._battle; if (!battle) return -1; if (playerId === battle.localPlayerId) return AI_PLAYER_ID; if (playerId === AI_PLAYER_ID) return battle.localPlayerId; return playerId === 0 ? AI_PLAYER_ID : 0; } /** * 累加某 player 槽位攻速万分比。 * @param playerId 玩家 * @param wan 万分比 */ private _addBonusWan(playerId: number, wan: number): void { if (wan === 0) return; for (let i = 0; i < this._bonuses.length; i++) { if (this._bonuses[i].playerId !== playerId) continue; this._bonuses[i].bonusWan += wan; return; } this._bonuses.push({ playerId, bonusWan: wan }); } private _applyModifier(session: BattleSession, entityId: EntityId, bonusWan: number, playerId: number): void { if (bonusWan === 0) return; const world = session.world; if (!world.isAlive(entityId)) return; if (!world.getComponent(entityId, CombatComp)) return; if (!world.getComponent(entityId, AttributeComp)) { world.addComponent(entityId, new AttributeComp()); } session.attributes.addModifier(entityId, { attr: 'aspd_add', op: 'Add', value: bonusWan, source: `${UNIT_ASPD_MOD_GROUP}_${playerId}`, group: UNIT_ASPD_MOD_GROUP, }); } } let _host: BattleBuffUnitAttackSpeedHost | null = null; /** 全局攻击单位攻速被动 Host(单局复用) */ export function getBattleBuffUnitAttackSpeedHost(): BattleBuffUnitAttackSpeedHost { if (!_host) _host = new BattleBuffUnitAttackSpeedHost(); return _host; }