Files
zeling_v2/Assets/_Game/Scripts/UI/FormSkillPanel.cs
2026-06-08 11:26:17 +08:00

213 lines
9.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 System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Skills;
using BaseGames.Localization;
namespace BaseGames.UI
{
/// <summary>
/// 形态技能一览面板(架构 10_UIModule §Panel
/// 只读面板:按形态展示各技能槽的技能资料(名称、描述、消耗、冷却),不含任何解锁/技能点交互。
/// 支持左右翻页浏览各形态,并高亮当前实际使用的形态。
///
/// 数据驱动:技能数据读 <see cref="FormSkillDatabaseSO"/>(与 SkillManager 共享的权威源),不再复制配置。
/// Inspector 必填:
/// _database — FormSkillDatabaseSO与玩家 SkillManager 同一资产)
/// _formTitleText — 显示当前形态标签名的文本
/// _prevFormBtn / _nextFormBtn — 翻页按钮
/// _activeFormIndicator — 高亮"当前形态"的标志(可选,如小圆点)
/// _skillSlots — 3 个 SkillSlotView 引用Soul / Spirit1 / Spirit2
/// _onFormChanged — IntEventChannelSOFormController 广播当前形态 index
/// </summary>
public class FormSkillPanel : MonoBehaviour
{
// ── 子数据结构 ────────────────────────────────────────────────────────
[System.Serializable]
public struct SkillSlotView
{
[Tooltip("技能图标")]
public Image icon;
[Tooltip("技能名称文本")]
public TMP_Text nameText;
[Tooltip("技能描述文本")]
public TMP_Text descText;
[Tooltip("消耗数值文本(如 \"魂力 25\"")]
public TMP_Text costText;
[Tooltip("冷却数值文本(如 \"CD 3.5s\"")]
public TMP_Text cooldownText;
[Tooltip("无技能时隐藏的根 GameObject")]
public GameObject root;
}
// ── Inspector 字段 ────────────────────────────────────────────────────
[Header("形态技能数据库(权威源,与玩家 SkillManager 共享同一资产)")]
[SerializeField] private FormSkillDatabaseSO _database;
[Header("UI 导航")]
[SerializeField] private TMP_Text _formTitleText;
[SerializeField] private Button _prevFormBtn;
[SerializeField] private Button _nextFormBtn;
[Tooltip("高亮当前实际装备形态的指示器(可选)")]
[SerializeField] private GameObject _activeFormIndicator;
[Header("技能卡槽Soul / Spirit1 / Spirit2")]
[SerializeField] private SkillSlotView _soulSlot;
[SerializeField] private SkillSlotView _spiritSlot1;
[SerializeField] private SkillSlotView _spiritSlot2;
[Header("关闭按钮")]
[SerializeField] private Button _btnClose;
[Header("Event Channels")]
[Tooltip("FormController 广播当前形态切换payload = FormType 的整数序号0=天魂,1=地魂,2=命魂)")]
[SerializeField] private IntEventChannelSO _onFormChanged;
// ── 私有状态 ──────────────────────────────────────────────────────────
private int _viewIndex = 0; // 当前正在查看的形态
private int _activeIndex = 0; // 玩家当前实际使用的形态
private readonly CompositeDisposable _subs = new();
private FormSkillDatabaseSO.Entry[] Entries => _database != null ? _database.Entries : null;
// ── 生命周期 ──────────────────────────────────────────────────────────
private void Awake()
{
_prevFormBtn?.onClick.AddListener(PrevForm);
_nextFormBtn?.onClick.AddListener(NextForm);
_btnClose?.onClick.AddListener(OnCloseClicked);
}
private void OnEnable()
{
_onFormChanged?.Subscribe(OnFormChanged).AddTo(_subs);
// 打开时自动跳到当前形态
_viewIndex = _activeIndex;
Refresh();
}
private void OnDisable()
{
_subs.Clear();
}
// ── 事件处理 ──────────────────────────────────────────────────────────
private void OnFormChanged(int formIndex)
{
_activeIndex = formIndex;
if (_activeFormIndicator != null)
_activeFormIndicator.SetActive(_viewIndex == _activeIndex);
}
// ── 导航 ──────────────────────────────────────────────────────────────
private void PrevForm()
{
var entries = Entries;
if (entries == null || entries.Length == 0) return;
_viewIndex = (_viewIndex - 1 + entries.Length) % entries.Length;
Refresh();
}
private void NextForm()
{
var entries = Entries;
if (entries == null || entries.Length == 0) return;
_viewIndex = (_viewIndex + 1) % entries.Length;
Refresh();
}
// ── 显示刷新 ──────────────────────────────────────────────────────────
private void Refresh()
{
var entries = Entries;
if (entries == null || entries.Length == 0) return;
_viewIndex = Mathf.Clamp(_viewIndex, 0, entries.Length - 1);
var cfg = entries[_viewIndex];
// 标题走本地化管道fallback 为 key 本身)
if (_formTitleText != null)
{
string label = !string.IsNullOrEmpty(cfg.formLabelKey)
? LocalizationManager.Get(cfg.formLabelKey, LocalizationTable.UI)
: string.Empty;
_formTitleText.text = string.IsNullOrEmpty(label) || label == cfg.formLabelKey
? cfg.formLabelKey // fallback直接显示 Key
: label;
}
// 当前形态高亮指示器
if (_activeFormIndicator != null)
_activeFormIndicator.SetActive(_viewIndex == _activeIndex);
// 翻页按钮可见性(只有一种形态时隐藏)
bool multiForm = entries.Length > 1;
if (_prevFormBtn != null) _prevFormBtn.gameObject.SetActive(multiForm);
if (_nextFormBtn != null) _nextFormBtn.gameObject.SetActive(multiForm);
// 技能卡槽
FillSlot(ref _soulSlot, cfg.soulSkill);
FillSlot(ref _spiritSlot1, cfg.spiritSkill1);
FillSlot(ref _spiritSlot2, cfg.spiritSkill2);
}
private static void FillSlot(ref SkillSlotView slot, FormSkillSO skill)
{
bool hasSkill = skill != null;
if (slot.root != null) slot.root.SetActive(hasSkill);
if (!hasSkill) return;
if (slot.icon != null)
{
slot.icon.sprite = skill.icon;
slot.icon.enabled = skill.icon != null;
}
if (slot.nameText != null)
{
string name = LocalizationManager.Get(skill.displayNameKey, LocalizationTable.UI);
slot.nameText.text = string.IsNullOrEmpty(name) || name == skill.displayNameKey
? skill.skillId
: name;
}
if (slot.descText != null)
{
string desc = LocalizationManager.Get(skill.descriptionKey, LocalizationTable.UI);
slot.descText.text = string.IsNullOrEmpty(desc) || desc == skill.descriptionKey
? string.Empty
: desc;
}
if (slot.costText != null)
slot.costText.text = skill.baseCost > 0
? $"{skill.resourceType} {skill.baseCost}"
: string.Empty;
if (slot.cooldownText != null)
slot.cooldownText.text = skill.cooldown > 0f
? $"CD {skill.cooldown:0.#}s"
: string.Empty;
}
// ── 关闭 ──────────────────────────────────────────────────────────────
private void OnCloseClicked()
{
var uiMgr = ServiceLocator.GetOrDefault<UIManager>();
if (uiMgr != null) uiMgr.CloseTopPanel();
else gameObject.SetActive(false);
}
}
}