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

114 lines
5.1 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 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, IToolSlotService
{
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);
ServiceLocator.Register<IToolSlotService>(this);
}
private void OnDisable()
{
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
ServiceLocator.Unregister<IToolSlotService>(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;
// 持久化剩余使用次数(-1 = 无限,保持原值)
data.Tools.ToolStates["Slot0_Uses"] = Newtonsoft.Json.Linq.JObject.FromObject(new { uses = _remainingUses[0] });
data.Tools.ToolStates["Slot1_Uses"] = Newtonsoft.Json.Linq.JObject.FromObject(new { uses = _remainingUses[1] });
}
public void OnLoad(SaveData data)
{
_cooldowns[0] = _cooldowns[1] = 0f;
EquipTool(0, _toolCatalog.Find(data.Tools.ToolSlot0));
EquipTool(1, _toolCatalog.Find(data.Tools.ToolSlot1));
// 恢复剩余使用次数EquipTool 会重置为 maxUses此处覆盖还原
if (data.Tools.ToolStates.TryGetValue("Slot0_Uses", out var uses0))
_remainingUses[0] = (int?)uses0["uses"] ?? _remainingUses[0];
if (data.Tools.ToolStates.TryGetValue("Slot1_Uses", out var uses1))
_remainingUses[1] = (int?)uses1["uses"] ?? _remainingUses[1];
}
}
}