/** * 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 { 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(layer, cfg.tick, attrsComp); } // 永久 buff(duration<=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 时结算一次伤害/治疗。 * * @param layer 当前 buff 层实例 * @param tick 周期效果配置 * @param attrs 被作用者属性组件(调用 add_hp) */ private processTick(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); attrs.add_hp(amount); } } }