using System.Collections.Generic; using System.Linq; using UnityEngine; using BaseGames.Core; using BaseGames.Player; using BaseGames.Core.Events; using BaseGames.Core.Save; using BaseGames.Skills; using BaseGames.Feedback; namespace BaseGames.Equipment { /// /// 装备管理器(架构 09_ProgressionModule §6)。 /// 挂在 Player 上,管理护符的装备/卸下及 Notch 容量。 /// 实现 ISaveable 以持久化装备状态。 /// public class EquipmentManager : MonoBehaviour, ISaveable, IEquipmentService { [Header("配置")] [SerializeField] private EquipmentConfigSO _config; [SerializeField] private CharmCatalogSO _charmCatalog; [Header("Event Channels")] [SerializeField] private CharmEventChannelSO _onCharmEquipped; [SerializeField] private CharmEventChannelSO _onCharmUnequipped; [SerializeField] private VoidEventChannelSO _onEquipmentChanged; [Tooltip("AchievementManager 解锁授予凹槽成就时发布;订阅后调用 IncreaseNotches(1)。")] [SerializeField] private VoidEventChannelSO _onAchievementNotchGranted; private readonly List _equipped = new(4); private readonly List _collected = new(32); private int _currentNotchCapacity; private int _usedNotches; private EquipmentContext _ctx; private readonly CompositeDisposable _subs = new(); // ── 生命周期 ────────────────────────────────────────────────────────── private void OnEnable() { ServiceLocator.Register(this); ServiceLocator.GetOrDefault()?.Register(this); _onAchievementNotchGranted?.Subscribe(() => IncreaseNotches(1)).AddTo(_subs); } private void OnDisable() { ServiceLocator.Unregister(this); ServiceLocator.GetOrDefault()?.Unregister(this); _subs.Clear(); } private void Awake() { _ctx = new EquipmentContext { Stats = GetComponent(), Feedback = GetComponent(), Events = ServiceLocator.GetOrDefault(), SkillMods = GetComponent(), WeaponMgr = GetComponent(), }; Debug.Assert(_config != null, "[EquipmentManager] _config 未赋值,请在 Inspector 中指定 EquipmentConfigSO。", this); Debug.Assert(_charmCatalog != null, "[EquipmentManager] _charmCatalog 未赋值,请在 Inspector 中指定 CharmCatalogSO。", this); _currentNotchCapacity = _config.initialNotchCount; Debug.Assert(_ctx.Stats != null, "[EquipmentManager] 缺少 PlayerStats,护符效果无法修改属性。", this); Debug.Assert(_ctx.Feedback != null, "[EquipmentManager] 缺少 PlayerFeedback,护符效果无法触发反馈。", this); Debug.Assert(_ctx.SkillMods != null, "[EquipmentManager] 缺少 SkillModifierRegistry。", this); } // ── 查询属性 ───────────────────────────────────────────────────────── public int UsedNotches => _usedNotches; // 缓存值,避免每次调用 LINQ Sum public int TotalNotches => _currentNotchCapacity; public IReadOnlyList Equipped => _equipped; public IReadOnlyList Collected => _collected; // ── 装备操作 ───────────────────────────────────────────────────────── /// /// 装备护符。返回 null 表示成功;返回错误字符串表示失败原因。 /// public string TryEquipCharm(CharmSO charm) { if (charm == null) return "护符不存在"; if (_equipped.Contains(charm)) return "已经装备"; if (!_collected.Contains(charm)) return "尚未收集此护符"; int remaining = _currentNotchCapacity - UsedNotches; if (charm.notchCost > remaining) return $"笔记不足(需要 {charm.notchCost},剩余 {remaining})"; _equipped.Add(charm); _usedNotches += charm.notchCost; foreach (var fx in charm.effects) fx?.OnEquip(_ctx); _onCharmEquipped?.Raise(charm); _onEquipmentChanged?.Raise(); return null; } public void UnequipCharm(CharmSO charm) { if (charm == null || !_equipped.Remove(charm)) return; _usedNotches -= charm.notchCost; foreach (var fx in charm.effects) fx?.OnUnequip(_ctx); _onCharmUnequipped?.Raise(charm); _onEquipmentChanged?.Raise(); } /// 将护符加入收藏(拾取时调用)。 public void AddToCollection(string charmId) { var charm = _charmCatalog.Find(charmId); if (charm == null) { Debug.LogWarning($"[EquipmentManager] 找不到护符: {charmId}"); return; } if (!_collected.Contains(charm)) _collected.Add(charm); } public void IncreaseNotches(int amount) { _currentNotchCapacity += amount; _onEquipmentChanged?.Raise(); } // ── ISaveable ──────────────────────────────────────────────────────── public void OnSave(SaveData data) { data.Equipment.EquippedCharmIds.Clear(); data.Equipment.EquippedCharmIds.AddRange(_equipped.Select(c => c.charmId)); data.Equipment.OwnedCharmIds.Clear(); data.Equipment.OwnedCharmIds.AddRange(_collected.Select(c => c.charmId)); data.Equipment.MaxNotches = _currentNotchCapacity; } public void OnLoad(SaveData data) { // 卸下所有当前护符(不触发事件,逆序遍历避免 ToList() GC 分配) for (int i = _equipped.Count - 1; i >= 0; i--) foreach (var fx in _equipped[i].effects) fx?.OnUnequip(_ctx); _equipped.Clear(); _usedNotches = 0; // 恢复 Notch 上限(若存档值为 0 则使用初始默认值) _currentNotchCapacity = data.Equipment.MaxNotches > 0 ? data.Equipment.MaxNotches : _config.initialNotchCount; // 从 CharmCatalog 按 ID 恢复收藏并重新装备 _collected.Clear(); foreach (var id in data.Equipment.OwnedCharmIds) { var charm = _charmCatalog.Find(id); if (charm != null && !_collected.Contains(charm)) _collected.Add(charm); } foreach (var id in data.Equipment.EquippedCharmIds) { var charm = _charmCatalog.Find(id); if (charm != null) TryEquipCharm(charm); } _onEquipmentChanged?.Raise(); } } }