Files
zeling_v2/Assets/_Game/Scripts/Equipment/ToolSlotManager.cs

96 lines
4.2 KiB
C#

using UnityEngine;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Core.Save;
using BaseGames.Player.States;
namespace BaseGames.Equipment
{
/// <summary>
/// 工具槽管理器(架构 09_ProgressionModule §7.5)。
/// 管理玩家的 2 个工具槽(装备、使用、冷却)。
/// 实现 ISaveable 以持久化槽位状态。
/// </summary>
public class ToolSlotManager : MonoBehaviour, ISaveable
{
private const int SlotCount = 2;
[SerializeField] private ToolCatalogSO _toolCatalog;
[SerializeField] private ToolSO[] _slots = new ToolSO[SlotCount];
[SerializeField] private int[] _remainingUses = new int[SlotCount]; // -1 = 无限
[SerializeField] private ToolUsedEventChannelSO _onToolUsed;
private readonly float[] _cooldowns = new float[SlotCount];
private void Awake()
{
Debug.Assert(_toolCatalog != null, "[ToolSlotManager] _toolCatalog 未赋值,请在 Inspector 中指定 ToolCatalogSO。", this);
}
private void OnEnable() => ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
private void OnDisable() => ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
// ── 装备 ─────────────────────────────────────────────────────────────
public void EquipTool(int slotIndex, ToolSO tool)
{
if (slotIndex < 0 || slotIndex >= SlotCount) return;
_slots[slotIndex] = tool;
_remainingUses[slotIndex] = tool != null ? tool.maxUses : 0;
_cooldowns[slotIndex] = 0f;
}
// ── 使用 ─────────────────────────────────────────────────────────────
public bool TryUseTool(int slotIndex, PlayerController player)
{
if (slotIndex < 0 || slotIndex >= SlotCount) return false;
var tool = _slots[slotIndex];
if (tool == null) return false;
if (_cooldowns[slotIndex] > 0f) return false;
if (_remainingUses[slotIndex] == 0) return false; // 已耗尽(-1 = 无限不触发)
tool.effect?.Use(player);
if (_remainingUses[slotIndex] > 0) _remainingUses[slotIndex]--;
_cooldowns[slotIndex] = tool is IToolCooldown tc ? tc.CooldownDuration : 0f;
_onToolUsed?.Raise(new ToolUsedPayload
{
SlotIndex = slotIndex,
ToolId = tool.toolId
});
return true;
}
private void Update()
{
for (int i = 0; i < SlotCount; i++)
if (_cooldowns[i] > 0f) _cooldowns[i] -= Time.deltaTime;
}
// ── 查询 ─────────────────────────────────────────────────────────────
public ToolSO GetTool(int slotIndex) => _slots[slotIndex];
public int GetRemainingUses(int slotIndex) => _remainingUses[slotIndex];
public float GetCooldownRatio(int slotIndex)
{
if (_slots[slotIndex] is IToolCooldown tc && tc.CooldownDuration > 0f)
return _cooldowns[slotIndex] / tc.CooldownDuration;
return 0f;
}
// ── ISaveable ────────────────────────────────────────────────────────
public void OnSave(SaveData data)
{
data.Tools.ToolSlot0 = _slots[0]?.toolId;
data.Tools.ToolSlot1 = _slots[1]?.toolId;
}
public void OnLoad(SaveData data)
{
_cooldowns[0] = _cooldowns[1] = 0f;
EquipTool(0, _toolCatalog.Find(data.Tools.ToolSlot0));
EquipTool(1, _toolCatalog.Find(data.Tools.ToolSlot1));
}
}
}