Files
zeling_v2/Assets/_Game/Scripts/Equipment/EquipmentManager.cs
2026-05-25 11:54:37 +08:00

167 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// 装备管理器(架构 09_ProgressionModule §6
/// 挂在 Player 上,管理护符的装备/卸下及 Notch 容量。
/// 实现 ISaveable 以持久化装备状态。
/// </summary>
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<CharmSO> _equipped = new(4);
private readonly List<CharmSO> _collected = new(32);
private int _currentNotchCapacity;
private int _usedNotches;
private EquipmentContext _ctx;
private readonly CompositeDisposable _subs = new();
// ── 生命周期 ──────────────────────────────────────────────────────────
private void OnEnable()
{
ServiceLocator.Register<IEquipmentService>(this);
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
_onAchievementNotchGranted?.Subscribe(() => IncreaseNotches(1)).AddTo(_subs);
}
private void OnDisable()
{
ServiceLocator.Unregister<IEquipmentService>(this);
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
_subs.Clear();
}
private void Awake()
{
_ctx = new EquipmentContext
{
Stats = GetComponent<PlayerStats>(),
Feedback = GetComponent<PlayerFeedback>(),
Events = ServiceLocator.GetOrDefault<IEventChannelRegistry>(),
SkillMods = GetComponent<SkillModifierRegistry>(),
WeaponMgr = GetComponent<WeaponManager>(),
};
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<CharmSO> Equipped => _equipped;
public IReadOnlyList<CharmSO> Collected => _collected;
// ── 装备操作 ─────────────────────────────────────────────────────────
/// <summary>
/// 装备护符。返回 null 表示成功;返回错误字符串表示失败原因。
/// </summary>
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();
}
/// <summary>将护符加入收藏(拾取时调用)。</summary>
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();
}
}
}