using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Equipment;
namespace BaseGames.UI
{
///
/// 工具栏 HUD(架构 09_ProgressionModule §7.5)。
/// 显示 2 个工具槽的图标 + 剩余次数 + 冷却遮罩。
///
public class ToolHUD : MonoBehaviour
{
[SerializeField] private ToolSlotUI[] _slots; // 2 个 ToolSlotUI 组件
[SerializeField] private ToolUsedEventChannelSO _onToolUsed;
// 通过 ServiceLocator 解析:避免 UI 直接持有 ToolSlotManager 具体类型引用
private IToolSlotService _slotManager;
private readonly CompositeDisposable _subs = new();
// 每个槽独立的冷却追踪协程;仅在冷却期间运行,避免每帧全槽轮询
private Coroutine[] _cooldownCoroutines;
private void Awake()
{
_cooldownCoroutines = _slots != null
? new Coroutine[_slots.Length]
: System.Array.Empty();
}
private void OnEnable()
{
_slotManager = ServiceLocator.GetOrDefault();
_onToolUsed?.Subscribe(RefreshSlot).AddTo(_subs);
}
private void OnDisable()
{
_subs.Clear();
StopAllCoroutines();
if (_cooldownCoroutines != null)
for (int i = 0; i < _cooldownCoroutines.Length; i++)
_cooldownCoroutines[i] = null;
}
private void RefreshSlot(ToolUsedPayload payload)
{
int i = payload.SlotIndex;
if (_slots == null || i < 0 || i >= _slots.Length) return;
if (_slotManager == null) return; // ToolSlotManager 尚未注册
_slots[i].Refresh(
_slotManager.GetTool(i),
_slotManager.GetRemainingUses(i),
_slotManager.GetCooldownRatio(i));
// 启动(或重启)该槽的冷却追踪协程
if (_cooldownCoroutines[i] != null) StopCoroutine(_cooldownCoroutines[i]);
_cooldownCoroutines[i] = StartCoroutine(TrackCooldown(i));
}
///
/// 仅在该槽冷却未结束时每帧更新填充值;冷却归零后自动停止,不消耗额外 CPU。
///
private System.Collections.IEnumerator TrackCooldown(int slotIndex)
{
while (_slotManager != null && _slotManager.GetCooldownRatio(slotIndex) > 0f)
{
_slots[slotIndex].SetCooldownFill(_slotManager.GetCooldownRatio(slotIndex));
yield return null;
}
// 冷却结束,确保填充归零
if (slotIndex < _slots.Length)
_slots[slotIndex].SetCooldownFill(0f);
_cooldownCoroutines[slotIndex] = null;
}
}
///
/// 单个工具槽 UI:图标 + 剩余次数文本 + 冷却遮罩 Image(FillAmount)。
///
public class ToolSlotUI : MonoBehaviour
{
[SerializeField] private Image _icon;
[SerializeField] private TMP_Text _usesText;
[SerializeField] private Image _cooldownMask; // type = Filled, Image Type = Filled
public void Refresh(BaseGames.Equipment.ToolSO tool, int remainingUses, float cooldownRatio)
{
if (_icon != null)
_icon.sprite = tool != null ? tool.icon : null;
if (_usesText != null)
_usesText.text = remainingUses < 0 ? "∞" : remainingUses.ToString();
SetCooldownFill(cooldownRatio);
}
public void SetCooldownFill(float ratio)
{
if (_cooldownMask != null)
_cooldownMask.fillAmount = Mathf.Clamp01(ratio);
}
}
}