using UnityEngine; using BaseGames.Core; using BaseGames.Core.Events; using BaseGames.Core.Save; using BaseGames.Player.States; namespace BaseGames.Equipment { /// /// 工具槽管理器(架构 09_ProgressionModule §7.5)。 /// 管理玩家的 2 个工具槽(装备、使用、冷却)。 /// 实现 ISaveable 以持久化槽位状态。 /// 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()?.Register(this); private void OnDisable() => ServiceLocator.GetOrDefault()?.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; // 持久化剩余使用次数(-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]; } } }