feat(hero): 新增完整计时性Buff/Debuff系统

本次提交实现了游戏内完整的计时性增益/减益系统,包含:
1. 新增Buff配置表与运行时数据组件
2. 实现属性修饰器与最终属性计算逻辑
3. 完成Buff管理、计时结算与周期效果处理
4. 兼容旧有控制状态系统,支持平滑过渡
5. 附带完整的系统设计文档

同时关闭了两个闲置的任务界面节点。
This commit is contained in:
pan
2026-07-23 15:48:59 +08:00
parent 708ca0827a
commit c4e7f2584b
11 changed files with 714 additions and 2 deletions

View File

@@ -0,0 +1,115 @@
/**
* 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];
// 永久 buffduration<=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);
}
}
}