152 lines
6.9 KiB
C#
152 lines
6.9 KiB
C#
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
|
||
{
|
||
[Header("配置")]
|
||
[SerializeField] private EquipmentConfigSO _config;
|
||
[SerializeField] private CharmCatalogSO _charmCatalog;
|
||
|
||
[Header("Event Channels")]
|
||
[SerializeField] private CharmEventChannelSO _onCharmEquipped;
|
||
[SerializeField] private CharmEventChannelSO _onCharmUnequipped;
|
||
[SerializeField] private VoidEventChannelSO _onEquipmentChanged;
|
||
|
||
private readonly List<CharmSO> _equipped = new(4);
|
||
private readonly List<CharmSO> _collected = new(32);
|
||
private int _currentNotchCapacity;
|
||
private int _usedNotches;
|
||
|
||
private EquipmentContext _ctx;
|
||
|
||
// ── 生命周期 ──────────────────────────────────────────────────────────
|
||
private void OnEnable() => ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
|
||
private void OnDisable() => ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
|
||
|
||
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();
|
||
}
|
||
}
|
||
}
|