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,159 @@
# 计时性 Buff/Debuff 系统设计
> 日期2026-07-23
> 状态:已批准,待实现
## 1. 目标与范围
为英雄/怪物添加通用的计时性 buff/debuff 机制,覆盖四类需求:
- 限时的属性加成/削弱(如"10秒内攻击+30%"
- 周期性跳伤害/治疗 DoT/HoT如中毒、灼烧、再生
- 统一现有冰冻/击晕的计时框架,便于扩展更多控制效果
- 数据层完整记录 buff 状态UI 展示本期不做,预留脏标记)
## 2. 核心架构决策
| 维度 | 决策 |
|---|---|
| 属性计算模型 | 修饰器模型(读取时重算,无残留风险) |
| 叠加规则 | 可叠层,每层独立计时 |
| 配置组织 | 独立 `BuffSet.ts` 配置表 |
| 架构方案 | 方案 A原字段=base + 修饰器表 |
## 3. 模块划分
```
assets/script/game/
├── common/config/
│ ├── BuffSet.ts 新增buff 静态配置表id → 定义)
│ └── HeroAttrs.ts 复用现有 Attrs 枚举
└── hero/
├── HeroAttrsComp.ts 改造:新增 getFinalXxx() getter
├── BuffComp.ts 新增ECS Comp持有 buffs 运行时实例
├── BuffSystem.ts 新增ECS Systemtick 驱动
└── BuffManager.ts 新增:对外 APIapply/dispel/查询)
```
职责边界:
- `BuffSet.ts`:纯静态配置,无运行时状态
- `BuffComp.ts`:挂实体上的运行时数据,不写逻辑
- `BuffSystem.ts`:固定 tick 推进 buff倒计时/DoT/到期)
- `BuffManager.ts`:对外门面,屏蔽 ECS 细节
## 4. 数据模型
### BuffSet.ts — 静态配置
```ts
export enum BuffCategory {
Buff = 0, // 正面增益
Debuff = 1, // 负面减益
Control = 2, // 控制(冰冻/击晕/减速)
}
export enum ModOp {
Flat = 0, // 固定值
PercentAdd = 1, // 百分比(加算)
PercentMul = 2, // 百分比(乘算,预留)
}
export interface BuffModifier {
attr: Attrs;
op: ModOp;
value: number;
}
export interface BuffTickEffect {
interval: number; // 触发间隔(秒)
damage_or_heal: number; // 正=治疗,负=伤害
scale_by_ap_pct: number; // 按施法者 ap 缩放百分比0=不吃加成
}
export interface BuffConfig {
id: number;
name: string;
icon: string;
category: BuffCategory;
duration: number; // 单层持续时间0=永久
max_stack: number; // 默认 0=无限叠加1=不可叠层N=最多N层
modifiers?: BuffModifier[];
tick?: BuffTickEffect;
is_control?: boolean;
control_kind?: 'frost' | 'stun' | 'none';
info: string;
}
export const BuffList: Record<number, BuffConfig> = { /* 配置项 */ };
```
### BuffComp.ts — 运行时实例
```ts
export class ActiveBuff {
config_id: number;
remaining: number;
source_uuid: number;
source_ap_snapshot: number; // 施法时锁定
tick_acc: number = 0;
}
@ecs.register('Buff')
export class BuffComp extends ecs.Comp {
buffs: Map<number, ActiveBuff[]> = new Map();
dirty_buffs: boolean = false; // 预留 UI 消费
}
```
## 5. 修饰器模型与最终值计算
HeroAttrsComp 原字段ap/hp_max/critical 等)保持原语义(基础值 + 永久加成),新增 getter
```
final = (base + Σ_flat_timed) × (1 + Σ_pctAdd_timed / 100)
```
- flat 优先于 pct先加固定值再乘百分比
- 驻场加成并入 getter避免"忘记乘驻场"
- `add_ap`/`add_hp_max`/`add_special_attr` 语义不变,继续改 base
- getter 不缓存,避免脏数据;实测有热点再加 dirty
## 6. BuffSystem 驱动逻辑
- 固定步长 0.1s(与现有 HeroBuffSystem 一致),用 Timer(0.1)
- 每层独立倒计时,到期 splice 移除
- DoT/HoT每层独立 tick_acc到 interval 跳一次,`tick_acc -= interval` 保留余数
- DoT 数值按施法时 ap 快照缩放,不在结算时重读施法者
- 控制类 buff 双写兼容:施加时同时调用现有 toFrost/toStun现有字段继续由 BuffSystem.tickLegacyControl 驱动
- 永久 buffduration=0不递减 remaining只能被 dispel 清除
## 7. BuffManager API
```ts
applyBuff(target, buffId, sourceUuid?, sourceAp?, overrides?): void
dispel(target, category?, excludeIds?): number
getStack(target, buffId): number
hasControl(target, kind): boolean
```
叠层规则max_stack 默认 0=无限叠加):
- max_stack=0每次施加都新增层
- 未满:新增层
- 已满≥1替换最老层刷新
驱散后控制类需 recomputeControlState同 kind 还有任意层则保持,否则清除。
## 8. 迁移策略
- Step 1新增 buff 基础设施(纯加法,零风险)
- Step 2HeroAttrsComp 新增 getter不改旧字段
- Step 3SkillSet 新增 timed_buff_idSCastSystem 新旧路径并存,消费方逐个改用 getter
- Step 4控制类 buff 迁移(双写),最后删除 HeroBuffSystem 与旧字段
## 9. 风险与兜底
| 风险 | 兜底 |
|---|---|
| getter 未覆盖所有读取点 | grep 全量排查 |
| max_stack=0 无限叠加内存泄漏 | 超阈值打 warning |
| BuffComp 未 reset 残留 | reset() 清空,英雄回收时调用 |
| 双 System 冲突 | 兼容期 BuffSystem 接管HeroBuffSystem 改空或删除 |