import type { BattleSession } from 'db://assets/mx/module/battle'; import { AttrId, AttributeComp, CombatComp } from 'db://assets/mx/module/battle/ecs/Components'; import type { EntityId } from 'db://assets/mx/module/ecs'; import { TableNames } from '../../game/ConfigTableData'; import { BattleBuffCarryStore } from '../../manager/battleBuff/BattleBuffCarryStore'; import { BattleBuffFunctionType } from '../../manager/battleBuff/BattleBuffTypes'; import { BattleBuffTargetParse, parseFunctionParNums } from '../../manager/battleBuff/BattleBuffParamParse'; import { AI_PLAYER_ID } from '../ai/runtime/AiCampArea'; import { getTowerLoadoutService } from './TowerLoadoutService'; /** modifier source 前缀 */ const DIGIT_STAT_SOURCE = 'battle_buff_digit_'; /** 绑定所需最小战斗句柄(避免 import BattleRuntime 造成循环) */ interface DigitStatBattleRef { /** 本机 playerId */ localPlayerId: number; /** 取玩家 */ getPlayer(playerId: number): { deployedUnits: readonly EntityId[] } | null | undefined; /** 会话 */ session: BattleSession; } /** * 局内 functiontype 101~104:按 target/targetpar 给数字塔加伤/暴击/爆伤/攻速。 */ export class BattleBuffDigitStatHost { private _battle: DigitStatBattleRef | null = null; /** 绑定本局并刷新已部署单位 */ bind(battle: DigitStatBattleRef): void { this._battle = battle; this.reapplyPlayer(battle.localPlayerId); this.reapplyPlayer(AI_PLAYER_ID); } /** 解除绑定 */ unbind(): void { this._battle = null; } /** * 某玩家携带变更后重刷其场上单位。 * @param playerId 玩家 */ reapplyPlayer(playerId: number): void { const battle = this._battle; if (!battle) return; const pid = Math.max(0, Math.floor(Number(playerId) || 0)); const player = battle.getPlayer(pid); if (!player) return; const deployed = player.deployedUnits; for (let i = 0; i < deployed.length; i++) { this.refreshEntity(battle.session, deployed[i], pid); } } /** * 单塔刷新 101~104 修正。 * @param session 会话 * @param entityId 实体 * @param ownerPlayerId 归属 */ refreshEntity(session: BattleSession, entityId: EntityId, ownerPlayerId: number): void { this._clearDigitMods(session, entityId); const pid = Math.max(0, Math.floor(Number(ownerPlayerId) || 0)); const loadout = getTowerLoadoutService().getLoadout(entityId); if (!loadout) return; const hero = gg.data.table.getTableData(TableNames.Hero, loadout.weapon1Id); if (!hero) return; const heroType = Math.floor(Number(hero.herotype) || 0); const level = Math.floor(Number(loadout.level) || 0); const passives = BattleBuffCarryStore.getPassives(pid); for (let i = 0; i < passives.length; i++) { const row = passives[i].row as ITableBattlebuff | undefined; if (!row) continue; const ft = Math.floor(Number(row.functiontype) || 0); if ( ft !== BattleBuffFunctionType.DigitDamage && ft !== BattleBuffFunctionType.DigitCritRate && ft !== BattleBuffFunctionType.DigitCritDamage && ft !== BattleBuffFunctionType.DigitAttackSpeed ) { continue; } if (!BattleBuffTargetParse.matches(row.target, row.targetpar, heroType, level)) continue; const nums = parseFunctionParNums(row.functionpar); const wan = nums.length > 0 ? Math.floor(nums[0]) : 0; if (wan === 0) { console.warn('[BattleBuffDigitStat] functionpar 无效', row.id, row.functionpar); continue; } this._applyOne(session, entityId, ft, wan, `${DIGIT_STAT_SOURCE}${row.id}_${i}`); } } private _clearDigitMods(session: BattleSession, entityId: EntityId): void { const world = session.world; if (!world.isAlive(entityId)) return; const attr = world.getComponent(entityId, AttributeComp); if (!attr) return; const next = []; for (let i = 0; i < attr.modifiers.length; i++) { const src = String(attr.modifiers[i].source ?? ''); if (src.indexOf(DIGIT_STAT_SOURCE) === 0) continue; next.push(attr.modifiers[i]); } attr.modifiers = next; } private _applyOne(session: BattleSession, entityId: EntityId, ft: number, wan: number, source: string): void { 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()); } const attrSvc = session.attributes; if (ft === BattleBuffFunctionType.DigitDamage) { attrSvc.addModifier(entityId, { attr: AttrId.Attack, op: 'Mul', value: 1 + wan / 10000, source, }); return; } if (ft === BattleBuffFunctionType.DigitCritRate) { attrSvc.addModifier(entityId, { attr: AttrId.CritRate, op: 'Add', value: wan / 10000, source, }); return; } if (ft === BattleBuffFunctionType.DigitCritDamage) { attrSvc.addModifier(entityId, { attr: AttrId.CritDamage, op: 'Add', value: wan / 10000, source, }); return; } if (ft === BattleBuffFunctionType.DigitAttackSpeed) { attrSvc.addModifier(entityId, { attr: 'aspd_add', op: 'Add', value: wan, source, }); } } } let _host: BattleBuffDigitStatHost | null = null; /** 局内数字属性 Host */ export function getBattleBuffDigitStatHost(): BattleBuffDigitStatHost { if (!_host) _host = new BattleBuffDigitStatHost(); return _host; }