Files
pixelheros/docs/superpowers/specs/2026-07-23-timed-buff-system-design.md
pan c4e7f2584b feat(hero): 新增完整计时性Buff/Debuff系统
本次提交实现了游戏内完整的计时性增益/减益系统,包含:
1. 新增Buff配置表与运行时数据组件
2. 实现属性修饰器与最终属性计算逻辑
3. 完成Buff管理、计时结算与周期效果处理
4. 兼容旧有控制状态系统,支持平滑过渡
5. 附带完整的系统设计文档

同时关闭了两个闲置的任务界面节点。
2026-07-23 15:48:59 +08:00

5.2 KiB
Raw Blame History

计时性 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 — 静态配置

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 — 运行时实例

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

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 改空或删除