1. 为护盾技能添加duration配置,支持永久/限时护盾 2. 新增护盾倒计时逻辑,到期自动清空护盾 3. 调整英雄6301魔法盾的冷却时间为10秒,新增7秒持续时长配置 4. 重构HeroAttrsComp,新增护盾持续时间字段与清空护盾方法
137 lines
6.3 KiB
TypeScript
137 lines
6.3 KiB
TypeScript
/**
|
||
* 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 = true;
|
||
|
||
/** 固定步长(秒) */
|
||
const TICK_STEP = 0.1;
|
||
|
||
@ecs.register('BuffSystem')
|
||
export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||
private timer = new Timer(TICK_STEP);
|
||
|
||
// HeroAttrsComp 可选,用于 DoT/HoT 的血量结算与护盾倒计时
|
||
private processShieldTimer(attrsComp: HeroAttrsComp | null): void {
|
||
if (!attrsComp) return;
|
||
// 永久护盾(duration<=0)不参与倒计时
|
||
if (attrsComp.shield_duration <= 0) return;
|
||
attrsComp.shield_duration -= TICK_STEP;
|
||
if (attrsComp.shield_duration <= 0) {
|
||
attrsComp.clear_shield();
|
||
mLogger.log(DEBUG, "BuffSystem", `护盾到期: ${attrsComp.hero_name}(eid:${attrsComp.ent?.eid ?? "-"}) 护盾已清空`);
|
||
}
|
||
}
|
||
|
||
// filter 改为 allOf(HeroAttrsComp):护盾倒计时挂在 HeroAttrsComp 上,
|
||
// 若按 BuffComp 过滤,无 buff 的实体不会被遍历,护盾到期清空会失效
|
||
filter(): ecs.IMatcher {
|
||
return ecs.allOf(HeroAttrsComp);
|
||
}
|
||
|
||
update(e: ecs.Entity): void {
|
||
if (!this.timer.update(this.dt)) return;
|
||
|
||
// HeroAttrsComp 必有(filter 保证),用于 DoT/HoT 血量结算与护盾倒计时
|
||
const attrsComp = e.get(HeroAttrsComp);
|
||
this.processShieldTimer(attrsComp);
|
||
|
||
// BuffComp 可选:无 buff 时仅做护盾倒计时
|
||
const buffComp = e.has(BuffComp) ? e.get(BuffComp) : null;
|
||
if (!buffComp) return;
|
||
|
||
// ---- 处理所有 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);
|
||
}
|
||
|
||
// 永久 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 时结算一次伤害/治疗。
|
||
*
|
||
* DoT(负值):转为 DamageEvent 入队,由 HeroAtkSystem.doAttack 走完整结算链
|
||
* (护盾抵扣 / 暴击 / 冰冻 / 击晕 / 飘字 / 死亡链),Attrs 取 buff 层的施法者属性快照;
|
||
* HoT(正值):保持直接回血,不走伤害链。
|
||
*
|
||
* @param target 被作用者实体(DoT 入队用)
|
||
* @param buffId buff 配置 id(DoT 事件借 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);
|
||
const buffName = BuffList[buffId]?.name ?? String(buffId);
|
||
if (amount < 0) {
|
||
// DoT 走伤害队列:dot_dmg 传正值,doAttack 内识别并走固定伤害分支
|
||
mLogger.log(DEBUG, "BuffSystem", `[debuff伤害] 「${buffName}」(buffId:${buffId}) tick 结算 -> ${attrs.hero_name}(eid:${target.eid}) 基础:${baseAmount} ap缩放:${scale.toFixed(3)} 入队伤害:${-amount} 剩余:${layer.remaining.toFixed(1)}s`);
|
||
DamageQueueHelper.addDamageToEntity(target, layer.source_attrs_snapshot ?? null, layer.source_uuid, buffId, 0, 1, -amount);
|
||
} else if (amount > 0) {
|
||
mLogger.log(DEBUG, "BuffSystem", `[buff治疗] 「${buffName}」(buffId:${buffId}) tick 结算 -> ${attrs.hero_name}(eid:${target.eid}) 基础:${baseAmount} ap缩放:${scale.toFixed(3)} 回血:${amount}`);
|
||
attrs.add_hp(amount);
|
||
}
|
||
}
|
||
}
|
||
}
|