/** * BuffManager — buff/debuff 的应用、驱散、查询管理器。 * * 设计说明: * - 非 ECS 组件,是普通 class 单例(参考 smc 的单例暴露模式)。 * - 对外提供 applyBuff / dispel / getStack / hasControl 四组核心能力。 * - 控制类 buff(冰冻/眩晕)统一写入 BuffComp,由 BuffSystem 计时到期; * 旧字段 frost_end_time/stun_end_time 已随 HeroBuffSystem 一并下线。 */ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; import { BuffCategory, BuffList } from "../common/config/BuffSet"; import { BuffComp, ActiveBuff } from "./BuffComp"; import { mLogger } from "../common/Logger"; /** Buff 模块调试日志开关 */ const DEBUG = false; /** applyBuff 的覆盖参数(预留按需覆写持续时间 / 修饰数值) */ export interface BuffApplyOverrides { duration?: number; /** 覆写计时 buff 的修饰数值(modifiers.value / tick.damage_or_heal),由药水卡 buff_value 自定义档位 */ value?: number; } class BuffManagerImpl { /** * 对目标施加一个 buff 实例。 * * 叠层规则: * - max_stack === 0:无限叠加,始终 push 新层 * - existing.length < max_stack:push 新层 * - 已满:移除最老层(数组首部)后追加新层,保持 FIFO 刷新语义 * * @param target 目标实体 * @param buffId BuffList 配置 id * @param sourceUuid 施法者 uuid(0=系统/无主) * @param sourceAp 施法者攻击力快照(用于 DoT 缩放) * @param overrides 可选覆写参数 */ applyBuff(target: ecs.Entity, buffId: number, sourceUuid: number = 0, sourceAp: number = 0, overrides?: BuffApplyOverrides): void { const cfg = BuffList[buffId]; if (!cfg) { mLogger.warn(true, "BuffManager", `applyBuff: buffId=${buffId} 配置不存在,跳过`); return; } mLogger.log(DEBUG, "BuffManager", `applyBuff: buff=${cfg.name}(${buffId}) target_eid=${target.eid} duration=${overrides?.duration ?? cfg.duration} value_override=${overrides?.value} source_ap=${sourceAp}`); const buffComp = this.ensureBuffComp(target); const duration = overrides?.duration ?? cfg.duration; let layers = buffComp.buffs.get(buffId); if (!layers) { layers = []; buffComp.buffs.set(buffId, layers); } const newLayer = new ActiveBuff(buffId, duration, sourceUuid, sourceAp, overrides?.value); if (cfg.max_stack === 0 || layers.length < cfg.max_stack) { layers.push(newLayer); } else { layers.shift(); layers.push(newLayer); } buffComp.dirty_buffs = true; } /** * 驱散目标身上匹配的 buff。 * * @param target 目标实体 * @param category 仅驱散该类别(不传=所有类别) * @param excludeIds 排除的 buff id 列表 * @returns 移除的 buff 种类数 */ dispel(target: ecs.Entity, category?: BuffCategory, excludeIds?: number[]): number { if (!target.has(BuffComp)) return 0; const buffComp = target.get(BuffComp); let removedKinds = 0; buffComp.buffs.forEach((layers, buffId) => { const cfg = BuffList[buffId]; if (!cfg) return; if (excludeIds && excludeIds.includes(buffId)) return; if (category !== undefined && cfg.category !== category) return; if (layers.length > 0) removedKinds++; buffComp.buffs.delete(buffId); }); buffComp.dirty_buffs = true; return removedKinds; } /** * 获取目标身上指定 buff 的当前叠层数。 * @returns 层数(未命中返回 0) */ getStack(target: ecs.Entity, buffId: number): number { if (!target.has(BuffComp)) return 0; const buffComp = target.get(BuffComp); return buffComp.buffs.get(buffId)?.length ?? 0; } /** * 判断目标是否处于指定类型的控制状态下。 * 唯一判定来源:BuffComp 中是否存在对应 control_kind 的活跃控制 buff。 */ hasControl(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; } // ==================== 私有方法 ==================== /** * 确保目标实体拥有 BuffComp,没有则添加。 */ private ensureBuffComp(target: ecs.Entity): BuffComp { if (!target.has(BuffComp)) { target.add(BuffComp); } return target.get(BuffComp); } } /** Buff 管理器单例(全局唯一实例) */ export const buffManager = new BuffManagerImpl();