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.
 
 
 
 
AdLike2048/assets/script/battle/combat/BattleBuffDigitStatHost.ts

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;
}