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.
151 lines
5.9 KiB
151 lines
5.9 KiB
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<ITableHero>(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;
|
|
}
|
|
|