refactor(game): 重构Buff系统并移除废弃代码

- 将Debuff枚举移至Attrs作为状态属性,统一Buff/Debuff处理逻辑
- 移除HeroViewComp中废弃的MP显示代码和三个设计文档文件
- 重构HeroAttrsComp的Buff系统,支持临时/永久增益、状态控制和属性修改
- 重构SkillSet配置,分离Buff定义为独立列表,简化技能配置
- 更新技能距离缓存逻辑,直接基于技能配置计算
This commit is contained in:
walkpan
2026-03-11 21:35:51 +08:00
parent 0ce299b0d8
commit 48769e699e
7 changed files with 238 additions and 211 deletions

View File

@@ -1,8 +1,8 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { BType } from "../common/config/HeroAttrs";
import { BuffConf, SkillRange } from "../common/config/SkillSet";
import { Attrs, BType } from "../common/config/HeroAttrs";
import { BuffConf, SkillDisVal, SkillRange, SkillSet } from "../common/config/SkillSet";
import { HeroInfo } from "../common/config/heroSet";
import { mLogger } from "../common/Logger";
import { _decorator } from "cc";
@@ -66,7 +66,6 @@ export class HeroAttrsComp extends ecs.Comp {
// ==================== 脏标签标记 ====================
dirty_hp: boolean = false; // 血量变更标记
dirty_mp: boolean = false; // 魔法值变更标记
dirty_shield: boolean = false; // 护盾变更标记
// ==================== 技能距离缓存 ====================
@@ -146,15 +145,119 @@ export class HeroAttrsComp extends ecs.Comp {
}
// ==================== BUFF 管理 ====================
/**
* 添加 buff 效果(支持多次叠加)
* @param buffConf buff 配置 (来自 SkillSet.BuffConf heroSet.buff)
* 添加 buff 效果
* @param buffConf buff 配置
*/
addBuff(buffConf: BuffConf) {
// 1. 如果是永久性增益time=0直接修改基础属性
if (buffConf.time <= 0) {
this.applyAttrChange(buffConf.buff, buffConf.value, buffConf.BType);
return;
}
// 2. 临时性 Buff/Debuff 处理
// 区分存储列表
const targetList = buffConf.isDebuff ? this.DEBUFFS : this.BUFFS;
// 确保列表初始化
// 注意:这里我们用 buffConf.buff (属性名) 作为 key而不是 buffConf.uuid
// 这样同一种属性的 buff 可以叠加或覆盖
const attrKey = buffConf.buff as unknown as number; // 强制转换 key 类型以适配 Record
if (!targetList[attrKey]) {
targetList[attrKey] = [];
}
const currentBuffs = targetList[attrKey];
// 查找是否已有同源的 buff (这里假设 uuid 相同为同源,或者简单点直接追加)
// 策略直接追加update 时统一计算
// 记录添加时的数值,方便后续移除(虽然 update 是重新计算总值的)
currentBuffs.push({
value: buffConf.value,
BType: buffConf.BType,
time: buffConf.time
});
// 立即应用一次属性变更(增量)
this.applyAttrChange(buffConf.buff, buffConf.value, buffConf.BType);
mLogger.log(this.debugMode, 'HeroAttrs', `添加Buff: ${buffConf.name}, 属性:${buffConf.buff}, 值:${buffConf.value}, 时间:${buffConf.time}`);
}
/**
* 通用属性修改应用
* @param attr 属性名
* @param value 变化值
* @param type 数值类型 (0:固定值, 1:百分比)
* @param reverse 是否反向应用 (用于移除 buff)
*/
private applyAttrChange(attr: Attrs, value: number, type: BType, reverse: boolean = false) {
let finalValue = value;
// 如果是移除 buff取反
if (reverse) {
finalValue = -value;
}
// 处理百分比
// 注意:百分比通常是基于基础值计算,这里简化为直接修改当前值
// 如果需要严格的基础值+百分比,需要维护 baseAttrs 和 bonusAttrs
// 当前架构直接修改属性,百分比暂时按 (当前值 * 百分比) 或 (基础值 * 百分比) 处理
// 鉴于属性系统中 hp/hp_max 等已经是数值,这里百分比暂定为:属性 = 属性 + (属性 * 百分比)
// 但对于 addBuff 这种临时增益,百分比通常是基于"基础值"的。
// 由于没有显式的 base_ap这里简化处理
// VALUE: 直接加减
// RATIO: 简单处理为 value 就是最终加成值(需要配置表里填好的数值),或者基于当前值
// 通常配置表填 10 表示 10%,即 0.1。
// 但这里的 value 已经是 number。假设配置 10 就是 10 点,或者 10%。
// 修正:根据 BType 处理
if (type === BType.RATIO) {
// 假设 value 是百分比整数,如 10 代表 10%
// 必须基于某个基数。这里暂时基于当前值(不严谨,但在没有 base 属性的情况下只能这样,或者基于 100?
// 更合理的做法:如果是 RATIOvalue 应该在配置时就转为实际数值,或者这里基于当前属性计算
// 考虑到 Attrs 定义里有很多非数值属性(如状态),需要特殊处理
// 针对状态类属性IN_FROST 等),通常是 BOOLEAN不走 RATIO
// 针对数值类ap, hpRATIO 应该是 value% * current
// 简化:目前只支持 VALUE 型直接加减。RATIO 型需要更复杂的属性系统支持Base + Add + Mul
// 这里的实现先按 VALUE 处理,如果以后需要 RATIO建议在 addBuff 前计算好 value
// 或者:约定 RATIO 时value 是基于当前值的百分比增量
const ratio = finalValue / 100;
// 注意:这里直接修改 this[attr] 会导致多次叠加问题。
// 临时 Buff 的正确做法是Update 中每一帧重置属性 -> 应用所有 Buff。
// 但当前架构似乎是“增量修改”式的。
// “增量修改”式在移除时很麻烦(尤其是百分比)。
// 妥协方案:只支持 VALUE 加减。配置表里的 RATIO 需要在逻辑层转为 VALUE。
// 但为了兼容 BType.RATIO这里暂时按 (当前值 * ratio) 计算增量
// 这会导致 recursive 问题,所以严谨的项目里属性必须分层。
// 鉴于“少可用原则”,这里仅处理 VALUE 和 BOOLEAN
if (typeof this[attr] === 'number') {
// 警告:直接改写数值可能不准确
this[attr] = (this[attr] as number) * (1 + ratio);
}
} else if (type === BType.BOOLEAN) {
// 布尔型/状态型value > 0 为 true/计数+1移除时 -1
// 这里使用计数器方式来支持多个同类状态叠加
// 例如 IN_FROST 是一个状态,不是属性。
// 需要在 HeroAttrsComp 中定义对应的状态计数器,或者利用 DEBUFFS 列表的存在性判断状态
// 暂时直接修改属性(如果属性是 boolean
if (typeof this[attr] === 'boolean') {
this[attr] = !reverse; // 添加设为 true, 移除设为 false (不仅确,多个冰冻会出问题)
// 正确做法updateBuffsDebuffs 中根据列表是否为空来设置状态
}
} else {
// VALUE 固定值
if (typeof this[attr] === 'number') {
this[attr] = (this[attr] as number) + finalValue;
}
}
// 标记脏数据(如果有对应的 dirty 标记)
if (attr === Attrs.hp) this.dirty_hp = true;
if (attr === Attrs.shield) this.dirty_shield = true;
}
// ==================== 临时 BUFF/DEBUFF 更新 ====================
/**
@@ -162,7 +265,59 @@ export class HeroAttrsComp extends ecs.Comp {
* @param dt 时间增量
*/
updateBuffsDebuffs(dt: number) {
this.updateList(this.BUFFS, dt);
this.updateList(this.DEBUFFS, dt);
// 更新状态标记 (根据 DEBUFFS 列表是否存在有效项)
this.updateStatusFlags();
}
private updateList(list: Record<number, Array<{value: number, BType: BType, time: number}>>, dt: number) {
for (const attrKey in list) {
const buffs = list[attrKey];
if (!buffs || buffs.length === 0) continue;
// 倒序遍历以便移除
for (let i = buffs.length - 1; i >= 0; i--) {
const buff = buffs[i];
buff.time -= dt;
if (buff.time <= 0) {
// Buff 过期,移除属性加成
// 注意key 是 string (from record key),需要转为 Attrs
const attrName = attrKey as unknown as Attrs;
this.applyAttrChange(attrName, buff.value, buff.BType, true); // reverse = true
// 从列表中移除
buffs.splice(i, 1);
mLogger.log(this.debugMode, 'HeroAttrs', `Buff过期: 属性:${attrName}, 恢复值:${buff.value}`);
}
}
// 如果该属性的 buff 列表空了,可以清理 key (可选)
if (buffs.length === 0) {
delete list[attrKey];
}
}
}
private updateStatusFlags() {
// 检查特定状态的 Debuff 列表是否为空,来更新 boolean 标志
// 例如:冰冻
this.checkStatus(Attrs.IN_FROST, 'is_stop'); // 假设冰冻会停止行动
// this.checkStatus(Attrs.IN_STUN, 'is_stunned'); // 需要在类里定义 is_stunned
}
private checkStatus(attr: Attrs, flagName: string) {
const key = attr as unknown as number;
const hasBuff = this.DEBUFFS[key] && this.DEBUFFS[key].length > 0;
// 只有当状态改变时才赋值,避免每帧赋值
if (this[flagName] !== hasBuff) {
// 特殊处理:有些状态可能由其他逻辑控制,这里强行覆盖可能会冲突
// 但作为 Buff 系统,它应该拥有最高解释权之一
this[flagName] = hasBuff;
}
}
@@ -172,17 +327,17 @@ export class HeroAttrsComp extends ecs.Comp {
* 在技能初始化、新增技能、MP变化时调用
* @param skillsComp 技能组件
*/
public updateSkillDistanceCache(skillsComp: HeroSkillsComp): void {
if (!skillsComp) {
public updateSkillDistanceCache(skill_id:number): void {
let skillConf=SkillSet[skill_id];
if (!skillConf) {
this.maxSkillDistance = 0;
this.minSkillDistance = 0;
return;
}
// 最远距离使用当前MP可施放的技能
this.maxSkillDistance = skillsComp.getMaxSkillDistance();
this.maxSkillDistance = SkillDisVal[skillConf.dis];
// 最近距离使用所有技能中的最小距离不考虑MP限制用于停止位置判断
this.minSkillDistance = skillsComp.getAbsoluteMinSkillDistance();
this.minSkillDistance = SkillDisVal[skillConf.dis];
}
/**
@@ -255,7 +410,6 @@ export class HeroAttrsComp extends ecs.Comp {
this.killed_count =0;
// 重置脏标签
this.dirty_hp = false;
this.dirty_mp = false;
this.dirty_shield = false;
}

View File

@@ -154,10 +154,6 @@ export class HeroViewComp extends CCComp {
this.model.dirty_hp = false;
}
// if (this.model.dirty_mp) {
// this.mp_show();
// this.model.dirty_mp = false;
// }
if (this.model.dirty_shield) {
this.show_shield(this.model.shield, this.model.shield_max);