/** * BuffSystem — 计时性 buff/debuff 的核心结算系统。 * * 使用 oops-framework 的 Timer 实现固定 0.1s 步长 tick: * - 遍历 BuffComp.buffs,每层 remaining -= 步长 * - 若配置了 tick 效果(DoT/HoT),累计 tick_acc 并按 interval 结算伤害/治疗 * - 到期的层从数组中移除;数组空则清理 key * - 兼容期:同时递减旧字段 frost_end_time / stun_end_time * * 注意:当前 HeroBuffSystem 仍在运行相同的 frost/stun 递减逻辑, * 待旧系统完全下线后可移除此处兼容代码。 */ 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"; /** 固定步长(秒) */ 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; // ---- 兼容期:递减旧字段 frost_end_time / stun_end_time ---- if (attrsComp) { if (attrsComp.frost_end_time > 0) { attrsComp.frost_end_time -= TICK_STEP; if (attrsComp.frost_end_time <= 0) { attrsComp.frost_end_time = 0; } } if (attrsComp.stun_end_time > 0) { attrsComp.stun_end_time -= TICK_STEP; if (attrsComp.stun_end_time <= 0) { attrsComp.stun_end_time = 0; } } } // ---- 处理所有 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]; // 永久 buff(duration<=0)不递减 remaining if (cfg.duration > 0) { layer.remaining -= TICK_STEP; } // 周期效果结算(仅在未到期时) if (cfg.tick && layer.remaining > 0 && attrsComp) { this.processTick(layer, cfg.tick, attrsComp); } // 到期移除 if (cfg.duration > 0 && layer.remaining <= 0) { layers.splice(i, 1); anyChanged = true; } } // 数组空则清理 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)) const scale = 1 + layer.source_ap_snapshot * tick.scale_by_ap_pct / 10000; const amount = Math.floor(tick.damage_or_heal * scale); attrs.add_hp(amount); } } }