using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using BaseGames.Core;
using BaseGames.Spells;
namespace BaseGames.UI.HUD
{
///
/// 法术槽 HUD 小组件。
///
/// 通过 ServiceLocator 获取 ISpellService,显示:
/// · 已装备法术图标(无法术时显示空槽占位)
/// · 冷却进度环(_cooldownFill.fillAmount = CooldownFraction,0 = 冷却完毕,1 = 刚施放)
///
/// 图标刺新:订阅 ISpellService.OnSpellChanged 事件,装备切换实时响应(零延迟)。
/// 冷却进度:协程专职追踪,冷却中每帧更新,待机时每 0.1 秒轮询。
///
/// Inspector 必填:
/// _iconImage — 法术图标 Image
/// _cooldownFill — 覆盖在图标上的冷却遮罩 Image(fillMethod = Radial360 推荐)
/// _emptySlot — 无法术时显示的占位对象
///
public class SpellSlotWidget : MonoBehaviour
{
// ── Inspector 字段 ────────────────────────────────────────────────────
[SerializeField] private Image _iconImage;
[Tooltip("冷却进度覆盖图(填充方式建议设为 Radial360 + 顺时针)。")]
[SerializeField] private Image _cooldownFill;
[Tooltip("无法术装备时显示的空槽占位对象。")]
[SerializeField] private GameObject _emptySlot;
// ── 私有状态 ──────────────────────────────────────────────────────────
private ISpellService _spellService;
private SpellSO _cachedSpell; // 延迟绑定期间班本记录,避免重复 SetSprite
// ── 生命周期 ──────────────────────────────────────────────────────────
private void OnEnable()
{
_cachedSpell = null;
StartCoroutine(TrackSpell());
}
private void OnDisable()
{
StopAllCoroutines();
if (_spellService != null)
{
_spellService.OnSpellChanged -= RefreshIcon;
_spellService = null;
}
_cachedSpell = null;
}
// ── 协程 ─────────────────────────────────────────────────────────────
///
/// 阶段一:延迟绑定——等到 ISpellService 就绪,订阅图标事件并同步初始状态。
/// 阶段二:冷却追踪——事件驱动图标(零延迟),协程专职更新进度环。
///
private IEnumerator TrackSpell()
{
var pollWait = new WaitForSeconds(0.1f);
// 阶段一:延迟绑定(SpellManager 可能晚于此组件初始化)
while (_spellService == null)
{
_spellService = ServiceLocator.GetOrDefault();
if (_spellService == null)
{
ShowEmpty();
yield return pollWait;
continue;
}
// 服务就绪:订阅装备变更事件(零延迟图标刷新)+ 同步当前状态
_spellService.OnSpellChanged += RefreshIcon;
RefreshIcon(_spellService.EquippedSpell);
}
// 阶段二:冷却进度追踪(图标已由事件驱动,协程仅负责冷却环)
while (true)
{
float f = _spellService.CooldownFraction;
if (_cooldownFill != null) _cooldownFill.fillAmount = f;
yield return f > 0f ? null : (object)pollWait;
}
}
// ── 视觉刷新 ──────────────────────────────────────────────────────────
private void RefreshIcon(SpellSO spell)
{
_cachedSpell = spell;
bool hasSpell = spell != null;
if (_emptySlot != null)
_emptySlot.SetActive(!hasSpell);
if (_iconImage != null)
{
_iconImage.gameObject.SetActive(hasSpell);
if (hasSpell) _iconImage.sprite = spell.icon;
}
if (_cooldownFill != null)
{
_cooldownFill.fillAmount = 0f;
_cooldownFill.gameObject.SetActive(hasSpell);
}
}
private void ShowEmpty()
{
if (_emptySlot != null) _emptySlot.SetActive(true);
if (_iconImage != null) _iconImage.gameObject.SetActive(false);
if (_cooldownFill != null)
{
_cooldownFill.fillAmount = 0f;
_cooldownFill.gameObject.SetActive(false);
}
}
}
}