using System; using System.Collections.Generic; using UnityEngine; namespace BaseGames.Combat.StatusEffects { /// /// 状态效果管理器。 /// /// 架构 06_CombatModule §11: /// - 双结构:List(Update 遍历)+ Dictionary(O(1) 类型查找) /// - 实现 IStatusEffectable,接受来自 HurtBox 的 DamageType 并映射到具体效果 /// - DealDotDamage:StatusEffect 子类通过 Owner 调用,绕过无敌帧造成 DoT /// - CleanseEffect:净化指定类型效果(道具/技能使用) /// [RequireComponent(typeof(SpriteRenderer))] public class StatusEffectManager : MonoBehaviour, IStatusEffectable { [Header("事件频道(可选)")] [SerializeField] private StatusEffectEventChannelSO _onStatusEffectApplied; [SerializeField] private StatusEffectEventChannelSO _onStatusEffectExpired; // ── 双结构 ───────────────────────────────────────────────────────── private readonly List _activeList = new(); private readonly Dictionary _activeIndex = new(); // ── Shader 渲染(MaterialPropertyBlock,不修改共享材质)───────── private SpriteRenderer _renderer; private MaterialPropertyBlock _propBlock; // 缓存 Shader 属性 ID,避免每次调用 SetShaderParam 都做字符串哈希查找 private readonly Dictionary _shaderPropIds = new(); // ── DoT 伤害代理(由 StatusEffect.OnTick 通过 Owner 调用)────────── private IDamageable _damageable; // ── 效果工厂字典(可在 Awake 后动态注册)───────────────────── private readonly Dictionary> _effectFactories = new(); private void Awake() { _renderer = GetComponent(); _propBlock = new MaterialPropertyBlock(); _damageable = GetComponentInParent(); // 默认标准效果注册(子类或外部模块可调用 RegisterEffectFactory 覆盖或扩展) RegisterEffectFactory(DamageType.Fire, () => new FireEffect()); RegisterEffectFactory(DamageType.Poison, () => new PoisonEffect()); } private void Update() { float delta = Time.deltaTime; // 逆序遍历,避免移除时索引错位 for (int i = _activeList.Count - 1; i >= 0; i--) { StatusEffect effect = _activeList[i]; effect.Update(delta); if (effect.IsExpired) RemoveAt(i, effect); } } // ── IStatusEffectable 实现 ───────────────────────────────────────── /// /// HurtBox 调用入口:将 DamageType 映射为具体 StatusEffect 实例并施加。 /// public void ApplyStatusEffect(DamageType type) { StatusEffect effect = CreateEffect(type); if (effect != null) ApplyEffect(effect); } /// /// 注册或覆盖一个 DamageType 对应的效果工厂。 /// Boss 或特殊游玩法式可在运行时注册自定义效果。 /// public void RegisterEffectFactory(DamageType type, Func factory) => _effectFactories[type] = factory; // ── 公开 API ─────────────────────────────────────────────────────── /// 直接施加一个具体效果(供技能/Boss 使用)。 public void ApplyEffect(StatusEffect effect) { // 检查是否被现有效果阻断 foreach (var blockerType in effect.BlockedBy) if (_activeIndex.ContainsKey(blockerType)) return; // 净化与新效果互斥的现有效果 foreach (var excludedType in effect.MutualExclusions) if (_activeIndex.ContainsKey(excludedType)) CleanseEffect(excludedType); if (_activeIndex.TryGetValue(effect.EffectType, out StatusEffect existing)) { existing.OnStack(); BroadcastApplied(existing); } else { effect.OnApply(this); _activeList.Add(effect); _activeIndex[effect.EffectType] = effect; BroadcastApplied(effect); } } /// 净化指定类型效果(净化道具/技能调用)。 public void CleanseEffect(StatusEffectType type) { if (!_activeIndex.TryGetValue(type, out StatusEffect effect)) return; effect.OnExpire(); _activeIndex.Remove(type); _activeList.Remove(effect); BroadcastExpired(effect); } /// 查询是否存在指定类型效果(供状态机/UI 轮询)。 public bool HasEffect(StatusEffectType type) => _activeIndex.ContainsKey(type); /// 获取指定类型效果(可为 null)。 public StatusEffect GetEffect(StatusEffectType type) => _activeIndex.TryGetValue(type, out var e) ? e : null; /// /// DoT 伤害代理(架构 06 §10)。StatusEffect.OnTick() 调用此方法,传入已构建好的 DamageInfo。 /// public void ApplyDirectDamage(DamageInfo info) { _damageable?.TakeDamage(info); } /// 设置 Sprite Shader 参数(MaterialPropertyBlock,不修改共享材质)。 public void SetShaderParam(string param, float value) { if (_renderer == null) return; if (!_shaderPropIds.TryGetValue(param, out int propId)) { propId = Shader.PropertyToID(param); _shaderPropIds[param] = propId; } _renderer.GetPropertyBlock(_propBlock); _propBlock.SetFloat(propId, value); _renderer.SetPropertyBlock(_propBlock); } /// 净化所有状态效果(存档点激活 / 返回城镇等调用)。 public void CleanseAll() { foreach (var e in _activeList) e.OnExpire(); _activeList.Clear(); _activeIndex.Clear(); } // ── 私有辅助 ─────────────────────────────────────────────────────── private StatusEffect CreateEffect(DamageType type) => _effectFactories.TryGetValue(type, out var factory) ? factory() : null; private void RemoveAt(int index, StatusEffect effect) { effect.OnExpire(); _activeList.RemoveAt(index); _activeIndex.Remove(effect.EffectType); BroadcastExpired(effect); } private void BroadcastApplied(StatusEffect effect) { _onStatusEffectApplied?.Raise(new StatusEffectEvent { EffectType = effect.EffectType, StackCount = effect.StackCount, RemainingDuration = effect.Duration, }); } private void BroadcastExpired(StatusEffect effect) { _onStatusEffectExpired?.Raise(new StatusEffectEvent { EffectType = effect.EffectType, StackCount = 0, RemainingDuration = 0f, }); } } }