Files
pixelheros/assets/script/game/hero/BuffSystem.ts
panFD e8c6a7ca6a feat: 实现DoT灼烧技能与debuff图标系统
本次提交完成了以下核心改动:
1.  调整fb-1~fb-6技能预制件的攻击偏移坐标,优化技能弹道位置
2.  新增DoT伤害机制,支持周期伤害/治疗的独立结算
3.  为Buff系统添加施法者属性快照,让DoT暴击、控制判定更准确
4.  新增顶栏debuff图标显示功能,支持灼烧等负面状态可视化
5.  为破晓先知等英雄替换为新的灼烧火球技能6104
6.  修复fb-2预制件的配置字段顺序与多余属性问题
2026-08-15 14:36:38 +08:00

118 lines
4.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* BuffSystem — 计时性 buff/debuff 的核心结算系统。
*
* 使用 oops-framework 的 Timer 实现固定 0.1s 步长 tick
* - 遍历 BuffComp.buffs每层 remaining -= 步长
* - 若配置了 tick 效果DoT/HoT累计 tick_acc 并按 interval 结算伤害/治疗
* - 到期的层从数组中移除;数组空则清理 key
*
* 控制类 buff冰冻/眩晕)不在此处做特殊处理:层存活期间 hasControl 判定
* 即生效,到期移除后控制自然解除。旧 HeroBuffSystem 已下线。
*/
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { Timer } from "db://oops-framework/core/common/timer/Timer";
import { BuffList } from "../common/config/BuffSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { BuffComp, ActiveBuff } from "./BuffComp";
import { DamageQueueHelper } from "./DamageQueueComp";
import { mLogger } from "../common/Logger";
/** Buff 模块调试日志开关 */
const DEBUG = false;
/** 固定步长(秒) */
const TICK_STEP = 0.1;
@ecs.register('BuffSystem')
export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private timer = new Timer(TICK_STEP);
filter(): ecs.IMatcher {
return ecs.allOf(BuffComp);
}
update(e: ecs.Entity): void {
if (!this.timer.update(this.dt)) return;
const buffComp = e.get(BuffComp);
if (!buffComp) return;
// HeroAttrsComp 可选,用于 DoT/HoT 的血量结算
const attrsComp = e.has(HeroAttrsComp) ? e.get(HeroAttrsComp) : null;
// ---- 处理所有 buff 层 ----
let anyChanged = false;
buffComp.buffs.forEach((layers, buffId) => {
const cfg = BuffList[buffId];
if (!cfg) return;
// 从后往前遍历,安全 splice 移除到期层
for (let i = layers.length - 1; i >= 0; i--) {
const layer = layers[i];
// 周期效果结算(先于到期判断,避免到期帧丢失末次 tick
if (cfg.tick && attrsComp) {
this.processTick(e, buffId, layer, cfg.tick, attrsComp);
}
// 永久 buffduration<=0不递减 remaining
if (cfg.duration > 0) {
layer.remaining -= TICK_STEP;
}
// 到期移除
if (cfg.duration > 0 && layer.remaining <= 0) {
layers.splice(i, 1);
anyChanged = true;
mLogger.log(DEBUG, "BuffSystem", `buff 到期移除: ${cfg.name}(${buffId}) eid=${e.eid} 剩余层数=${layers.length}`);
}
}
// 数组空则清理 key避免空 Map 项残留
if (layers.length === 0) {
buffComp.buffs.delete(buffId);
anyChanged = true;
}
});
if (anyChanged) {
buffComp.dirty_buffs = true;
}
}
/**
* 处理单层的周期效果DoT/HoT
* 累计 tick_acc 到 interval 时结算一次伤害/治疗。
*
* DoT负值转为 DamageEvent 入队,由 HeroAtkSystem.doAttack 走完整结算链
* (护盾抵扣 / 暴击 / 冰冻 / 击晕 / 飘字 / 死亡链Attrs 取 buff 层的施法者属性快照;
* HoT正值保持直接回血不走伤害链。
*
* @param target 被作用者实体DoT 入队用)
* @param buffId buff 配置 idDoT 事件借 s_uuid 字段携带,仅作日志追溯)
* @param layer 当前 buff 层实例
* @param tick 周期效果配置
* @param attrs 被作用者属性组件
*/
private processTick(target: ecs.Entity, buffId: number, layer: ActiveBuff, tick: { interval: number; damage_or_heal: number; scale_by_ap_pct: number }, attrs: HeroAttrsComp): void {
// interval 非正值守卫,避免死循环
if (tick.interval <= 0) return;
layer.tick_acc += TICK_STEP;
while (layer.tick_acc >= tick.interval) {
layer.tick_acc -= tick.interval;
// 按 ap 缩放后取整floor(dmg * (1 + ap_snapshot * scale_pct / 10000))
// damage_or_heal 优先取药水卡覆写值
const baseAmount = layer.value_override !== undefined ? layer.value_override : tick.damage_or_heal;
const scale = 1 + layer.source_ap_snapshot * tick.scale_by_ap_pct / 10000;
const amount = Math.floor(baseAmount * scale);
if (amount < 0) {
// DoT 走伤害队列dot_dmg 传正值doAttack 内识别并走固定伤害分支
DamageQueueHelper.addDamageToEntity(target, layer.source_attrs_snapshot ?? null, layer.source_uuid, buffId, 0, 1, -amount);
} else if (amount > 0) {
attrs.add_hp(amount);
}
}
}
}