refactor(hero): 移除旧版控制buff计时系统,统一用buff管理

完成全量buff系统迁移,删除HeroBuffSystem和旧的计时字段,
所有冰冻/眩晕控制效果改为通过BuffManager管理,
简化了控制状态判定逻辑,移除了新旧系统兼容代码
This commit is contained in:
panFD
2026-07-23 21:07:38 +08:00
parent ee7c0ea69c
commit 2876108970
4 changed files with 30 additions and 141 deletions

View File

@@ -4,14 +4,12 @@
* 设计说明:
* - 非 ECS 组件,是普通 class 单例(参考 smc 的单例暴露模式)。
* - 对外提供 applyBuff / dispel / getStack / hasControl 四组核心能力。
* - 控制类 buff 采用"双写"策略:既写入 BuffComp新框架也同步调用
* HeroAttrsComp.toFrost/toStun旧框架保证过渡期表现一致
* - 控制类 buff(冰冻/眩晕)统一写入 BuffComp由 BuffSystem 计时到期;
* 旧字段 frost_end_time/stun_end_time 已随 HeroBuffSystem 一并下线
*/
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { BuffCategory, BuffConfig, BuffList } from "../common/config/BuffSet";
import { FightSet } from "../common/config/GameSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { BuffCategory, BuffList } from "../common/config/BuffSet";
import { BuffComp, ActiveBuff } from "./BuffComp";
/** applyBuff 的覆盖参数(预留按需覆写持续时间) */
@@ -26,9 +24,7 @@ class BuffManagerImpl {
* 叠层规则:
* - max_stack === 0无限叠加始终 push 新层
* - existing.length < max_stackpush 新层
* - 已满:替换最老层(数组末尾)
*
* 控制类is_control额外双写旧字段frost/stun
* - 已满:移除最老层(数组首部)后追加新层,保持 FIFO 刷新语义
*
* @param target 目标实体
* @param buffId BuffList 配置 id
@@ -54,16 +50,10 @@ class BuffManagerImpl {
if (cfg.max_stack === 0 || layers.length < cfg.max_stack) {
layers.push(newLayer);
} else {
// 已满,移除最老层(数组首部)后追加新层,保持 FIFO 刷新语义
layers.shift();
layers.push(newLayer);
}
// 控制类双写旧字段
if (cfg.is_control) {
this.applyControlEffect(target, cfg, duration);
}
buffComp.dirty_buffs = true;
}
@@ -80,8 +70,6 @@ class BuffManagerImpl {
const buffComp = target.get(BuffComp);
let removedKinds = 0;
const controlKindsCleared: ('frost' | 'stun')[] = [];
buffComp.buffs.forEach((layers, buffId) => {
const cfg = BuffList[buffId];
if (!cfg) return;
@@ -90,18 +78,8 @@ class BuffManagerImpl {
if (layers.length > 0) removedKinds++;
buffComp.buffs.delete(buffId);
// 记录被驱散的控制类型,用于后续重算
if (cfg.is_control && cfg.control_kind && cfg.control_kind !== 'none') {
controlKindsCleared.push(cfg.control_kind);
}
});
// 控制类驱散后重算旧字段
if (controlKindsCleared.length > 0) {
this.recomputeControlState(target, controlKindsCleared);
}
buffComp.dirty_buffs = true;
return removedKinds;
}
@@ -118,23 +96,15 @@ class BuffManagerImpl {
/**
* 判断目标是否处于指定类型的控制状态下。
* 同时检查新框架 BuffComp 和旧字段,兼容过渡期
* 唯一判定来源:BuffComp 中是否存在对应 control_kind 的活跃控制 buff
*/
hasControl(target: ecs.Entity, kind: 'frost' | 'stun'): boolean {
// 新框架:检查 BuffComp 中是否有对应控制类 buff
if (target.has(BuffComp)) {
const buffComp = target.get(BuffComp);
for (const [buffId, layers] of buffComp.buffs) {
if (layers.length === 0) continue;
const cfg = BuffList[buffId];
if (cfg?.is_control && cfg.control_kind === kind) return true;
}
}
// 旧字段兜底
if (target.has(HeroAttrsComp)) {
const attrs = target.get(HeroAttrsComp);
if (kind === 'frost') return attrs.isFrost();
if (kind === 'stun') return attrs.isStun();
if (!target.has(BuffComp)) return false;
const buffComp = target.get(BuffComp);
for (const [buffId, layers] of buffComp.buffs) {
if (layers.length === 0) continue;
const cfg = BuffList[buffId];
if (cfg?.is_control && cfg.control_kind === kind) return true;
}
return false;
}
@@ -150,59 +120,6 @@ class BuffManagerImpl {
}
return target.get(BuffComp);
}
/**
* 控制效果双写:将控制类 buff 同步写入旧字段。
* duration 会被换算成 toFrost/toStun 所需的倍数参数。
*/
private applyControlEffect(target: ecs.Entity, cfg: BuffConfig, duration: number): void {
if (!target.has(HeroAttrsComp)) return;
const attrs = target.get(HeroAttrsComp);
if (cfg.control_kind === 'frost') {
// toFrost(time) 内部会乘以 FROST_TIME这里反除得到倍数
attrs.toFrost(duration / FightSet.FROST_TIME);
} else if (cfg.control_kind === 'stun') {
attrs.toStun(duration / FightSet.STUN_TIME);
}
}
/**
* 驱散控制 buff 后重算旧字段。
* 如果对应类型的控制 buff 已全部清除,则立即将旧字段归零。
*
* @param target 目标实体
* @param clearedKinds 本次被驱散的控制类型列表
*/
private recomputeControlState(target: ecs.Entity, clearedKinds: ('frost' | 'stun')[]): void {
if (!target.has(HeroAttrsComp)) return;
const attrs = target.get(HeroAttrsComp);
// 检查是否还有同类型控制 buff 残留
const hasFrostInBuff = this.hasControlInBuff(target, 'frost');
const hasStunInBuff = this.hasControlInBuff(target, 'stun');
if (clearedKinds.includes('frost') && !hasFrostInBuff) {
attrs.frost_end_time = 0;
}
if (clearedKinds.includes('stun') && !hasStunInBuff) {
attrs.stun_end_time = 0;
}
}
/**
* 仅检查 BuffComp 中是否存在指定控制类型(不查旧字段)。
*/
private hasControlInBuff(target: ecs.Entity, kind: 'frost' | 'stun'): boolean {
if (!target.has(BuffComp)) return false;
const buffComp = target.get(BuffComp);
for (const [buffId, layers] of buffComp.buffs) {
if (layers.length === 0) continue;
const cfg = BuffList[buffId];
if (cfg?.is_control && cfg.control_kind === kind) return true;
}
return false;
}
}
/** Buff 管理器单例(全局唯一实例) */

View File

@@ -5,10 +5,9 @@
* - 遍历 BuffComp.buffs每层 remaining -= 步长
* - 若配置了 tick 效果DoT/HoT累计 tick_acc 并按 interval 结算伤害/治疗
* - 到期的层从数组中移除;数组空则清理 key
* - 兼容期:同时递减旧字段 frost_end_time / stun_end_time
*
* 注意:当前 HeroBuffSystem 仍在运行相同的 frost/stun 递减逻辑,
* 待旧系统完全下线后可移除此处兼容代码
* 控制类 buff冰冻/眩晕)不在此处做特殊处理:层存活期间 hasControl 判定
* 即生效,到期移除后控制自然解除。旧 HeroBuffSystem 已下线
*/
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";

View File

@@ -200,14 +200,14 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
targetView.playEnd(skillConf.endAnm);
if (isFrost) {
TAttrsComp.toFrost();
targetView.in_iced(TAttrsComp.frost_end_time);
targetView.in_iced(FightSet.FROST_TIME);
if (damageEvent.Attrs.fac === FacSet.HERO) {
smc.vmdata.scores.freeze_count++;
}
}
if (isStun) {
TAttrsComp.toStun();
targetView.in_stun(TAttrsComp.stun_end_time);
targetView.in_stun(FightSet.STUN_TIME);
if (damageEvent.Attrs.fac === FacSet.HERO) {
smc.vmdata.scores.stun_count++;
}

View File

@@ -1,7 +1,6 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType } from "../common/config/heroSet";
import { mLogger } from "../common/Logger";
import { Timer } from "db://oops-framework/core/common/timer/Timer";
import { FacSet, FightSet } from "../common/config/GameSet";
import { FieldSkillSet, FieldSkillType, SkillOverrides } from "../common/config/SkillSet";
import { smc } from "../common/SingletonModuleComp";
@@ -9,6 +8,11 @@ import { Attrs } from "../common/config/HeroAttrs";
import { FieldSkillHelper } from "./FieldSkillHelper";
import { BuffComp } from "./BuffComp";
import { BuffList, ModOp } from "../common/config/BuffSet";
import { buffManager } from "./BuffManager";
/** 控制类 buff 配置 id对应 BuffList */
const BUFF_ID_FROST = 7003;
const BUFF_ID_STUN = 7004;
@ecs.register('HeroAttrs')
export class HeroAttrsComp extends ecs.Comp {
public debugMode: boolean = false;
@@ -64,9 +68,6 @@ export class HeroAttrsComp extends ecs.Comp {
invincible_time: number = 0;// 无敌时间
frost_end_time: number = 0;
stun_end_time: number = 0;
boom: boolean = false; // 自爆怪
// ==================== 脏标签标记 ====================
@@ -106,8 +107,6 @@ export class HeroAttrsComp extends ecs.Comp {
* 从 HeroInfo 读取初始配置,建立属性系统
*/
initAttrs() {
this.frost_end_time = 0;
this.stun_end_time = 0;
}
/*******************基础属性管理********************/
@@ -167,14 +166,16 @@ export class HeroAttrsComp extends ecs.Comp {
}
/** 施加冰冻:委托 buffManager 写入 BuffCompduration = FROST_TIME * time */
toFrost(time: number = 1) {
const frostTime = FightSet.FROST_TIME * time;
this.frost_end_time = Math.max(this.frost_end_time, frostTime);
if (!this.ent) return;
buffManager.applyBuff(this.ent, BUFF_ID_FROST, 0, 0, { duration: FightSet.FROST_TIME * time });
}
/** 施加击晕:委托 buffManager 写入 BuffComp并清零技能 CD */
toStun(time: number = 1) {
const stunTime = FightSet.STUN_TIME * time;
this.stun_end_time = Math.max(this.stun_end_time, stunTime);
if (!this.ent) return;
buffManager.applyBuff(this.ent, BUFF_ID_STUN, 0, 0, { duration: FightSet.STUN_TIME * time });
// 击晕时 CD 清零
for (const key in this.skills) {
@@ -215,11 +216,13 @@ export class HeroAttrsComp extends ecs.Comp {
skill.ccd = Math.min(actualCd, skill.ccd + dt);
}
}
/** 是否处于冰冻状态(统一走 BuffComp 判定) */
isFrost(): boolean {
return this.frost_end_time > 0
return !!this.ent && buffManager.hasControl(this.ent, 'frost');
}
/** 是否处于击晕状态(统一走 BuffComp 判定) */
isStun(): boolean {
return this.stun_end_time > 0
return !!this.ent && buffManager.hasControl(this.ent, 'stun');
}
getSkillLevel(skillId: number): number {
if (!skillId) return 0;
@@ -547,9 +550,6 @@ export class HeroAttrsComp extends ecs.Comp {
this.wfuny = 0;
this.boom = false;
this.frost_end_time = 0;
this.stun_end_time = 0;
// 重置技能距离缓存
this.maxSkillDistance = 0;
this.minSkillDistance = 0;
@@ -578,32 +578,5 @@ export class HeroAttrsComp extends ecs.Comp {
}
}
@ecs.register('HeroBuffSystem')
export class HeroBuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private timer = new Timer(0.1)
filter(): ecs.IMatcher {
return ecs.allOf(HeroAttrsComp);
}
update(e: ecs.Entity): void {
if (this.timer.update(this.dt)) {
const attrsComp = e.get(HeroAttrsComp);
if (attrsComp.frost_end_time > 0) {
attrsComp.frost_end_time -= 0.1;
if (attrsComp.frost_end_time <= 0) {
attrsComp.frost_end_time = 0;
}
}
if (attrsComp.stun_end_time > 0) {
attrsComp.stun_end_time -= 0.1;
if (attrsComp.stun_end_time <= 0) {
attrsComp.stun_end_time = 0;
}
}
}
void e;
}
}