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/BattleBuffDamageHook.ts

121 lines
4.0 KiB

import type { AbilityContext } from 'db://assets/mx/module/battle/combat/AbilityContext';
import {
setDamagePreCalcHook,
type DamagePreCalcState,
} from 'db://assets/mx/module/battle/combat/DamagePipeline';
import { AttributeComp } from 'db://assets/mx/module/battle/ecs/Components';
import type { EntityId } from 'db://assets/mx/module/ecs';
import { getBattleBuffDigitStatHost } from './BattleBuffDigitStatHost';
import { getPassiveSkillHost } from './PassiveSkillRuntime';
/**
* 从 Ability.id 解析 Skill.id。
* @param abilityId Ability id
*/
export function parseSkillIdFromAbilityId(abilityId: string): number {
const raw = String(abilityId ?? '');
const m = /(?:ability_(?:norm|hero|weapon|monster|buffskill)_|passive_)(\d+)/.exec(raw);
if (!m) return 0;
return Math.floor(Number(m[1]) || 0);
}
/**
* 列表是否含 skillId。
* @param ids id 列表
* @param skillId Skill.id
*/
function listHasSkill(ids: readonly number[], skillId: number): boolean {
for (let i = 0; i < ids.length; i++) {
if (ids[i] === skillId) return true;
}
return false;
}
/**
* 目标是否处于减速(move_speed_mul&lt;1 或 debuff_3 / slow 标签)。
* @param ctx 伤害上下文
* @param target 目标
*/
function targetIsSlowed(ctx: AbilityContext, target: EntityId): boolean {
const world = ctx.services.world;
const attr = world.getComponent(target, AttributeComp);
if (attr) {
for (let i = 0; i < attr.modifiers.length; i++) {
const m = attr.modifiers[i];
if (m.attr === 'move_speed_mul' && m.op === 'Mul' && m.value < 1) return true;
}
}
if (ctx.services.buffs.hasTag(target, 'debuff_3')) return true;
if (ctx.services.buffs.hasTag(target, 'slow')) return true;
return false;
}
/**
* 局内数字 buff 伤害预调:107/110/111/112。
*/
export class BattleBuffDamageHook {
/** 注册到 DamagePipeline */
static install(): void {
setDamagePreCalcHook((ctx, params, state) => {
BattleBuffDamageHook.apply(ctx, params, state);
});
}
/** 清除注册 */
static uninstall(): void {
setDamagePreCalcHook(null);
}
/**
* @param ctx 伤害上下文
* @param params deal 参数
* @param state 可调状态
*/
static apply(ctx: AbilityContext, params: Record<string, unknown>, state: DamagePreCalcState): void {
const flags = getBattleBuffDigitStatHost().getFlags(ctx.caster);
if (!flags) return;
const skillId = parseSkillIdFromAbilityId(ctx.abilityId);
const tableSkillId = Math.floor(Number(params.skillId) || 0);
const sid = skillId > 0 ? skillId : tableSkillId;
for (let i = 0; i < flags.skillMuls.length; i++) {
const row = flags.skillMuls[i];
if (sid > 0 && listHasSkill(row.skillIds, sid)) {
state.atk *= row.mul;
}
}
for (let i = 0; i < flags.forceCritSkills.length; i++) {
const row = flags.forceCritSkills[i];
if (sid > 0 && listHasSkill(row.skillIds, sid)) {
state.forceCrit = true;
if (row.convert) state.critRateToCritDmg = true;
}
}
if (flags.chanceMul && flags.chanceMul.wan > 0 && flags.chanceMul.mul > 1) {
const roll = ctx.rng.next() * 10000;
if (roll < flags.chanceMul.wan) {
state.atk *= flags.chanceMul.mul;
}
}
const target = ctx.targets[0];
if (!(target > 0)) return;
const mode = flags.condCrit;
if (!mode) return;
if (mode === 'always_convert') {
state.forceCrit = true;
state.critRateToCritDmg = true;
return;
}
if (mode === 'poison') {
if (getPassiveSkillHost().hasPoison(target)) state.forceCrit = true;
return;
}
if (mode === 'slow') {
if (targetIsSlowed(ctx, target)) state.forceCrit = true;
}
}
}