Files
zeling_v2/Assets/_Game/Scripts/UI/Menus/SaveSlotUI.cs
2026-06-05 18:41:33 +08:00

144 lines
6.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;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using BaseGames.Core.Save;
using BaseGames.Localization;
using BaseGames.World.Map;
namespace BaseGames.UI.Menus
{
/// <summary>
/// 单个存档槽卡片组件,负责显示存档摘要或空槽提示。
///
/// ⚠ 必须独立成文件Unity 仅为与文件名同名的类生成 MonoScript 资产,
/// 若与 SaveSlotController 合并在同一文件AddComponent 后脚本引用会丢失Missing Script
/// </summary>
public class SaveSlotUI : MonoBehaviour
{
[Header("基本信息")]
[SerializeField] private TMP_Text _playtimeText;
[SerializeField] private TMP_Text _regionText;
[SerializeField] private TMP_Text _lastSavedText;
[SerializeField] private Image _formIcon;
[Header("扩展信息(可选)")]
[Tooltip("货币(灵珠)持有量文本。")]
[SerializeField] private TMP_Text _lingZhuText;
[Tooltip("生命(面具)上限文本。")]
[SerializeField] private TMP_Text _hpText;
[Tooltip("钢铁之魂徽章根节点,仅一命模式存档显示。")]
[SerializeField] private GameObject _steelSoulBadge;
[Header("区域背景图")]
[Tooltip("RegionRegistry.asset用于根据 SceneName 查找区域背景图。")]
[SerializeField] private RegionRegistrySO _regionRegistry;
[Tooltip("存档点不在任何已注册区域时显示的默认背景图。")]
[SerializeField] private Sprite _fallbackBackground;
[Tooltip("显示区域背景图的 Image 组件。")]
[SerializeField] private Image _backgroundImage;
[Header("槽位状态")]
[SerializeField] private GameObject _emptyIndicator; // 空槽时显示的"新游戏"提示
[SerializeField] private GameObject _dataIndicator; // 有数据时显示的内容根
[SerializeField] private Button _selectButton;
[SerializeField] private Button _deleteButton;
private int _slotIndex;
private SaveSlotController _controller;
/// <summary>由 SaveSlotController 在 Awake 时调用以完成按钮绑定。</summary>
public void Init(int slotIndex, SaveSlotController controller)
{
_slotIndex = slotIndex;
_controller = controller;
if (_selectButton != null)
{
_selectButton.onClick.RemoveAllListeners();
_selectButton.onClick.AddListener(() => _controller.OnSlotSelected(_slotIndex));
}
if (_deleteButton != null)
{
_deleteButton.onClick.RemoveAllListeners();
// 删除统一走 Controller → 通用确认对话框(不再使用每卡内联确认)
_deleteButton.onClick.AddListener(() => _controller.OnSlotDeleteRequested(_slotIndex));
}
}
/// <summary>用摘要数据刷新显示summary 为 null 表示空槽。</summary>
/// <param name="summary">槽位摘要null 为空槽。</param>
/// <param name="mode">当前面板模式:继续模式下空槽不可选。</param>
public void Refresh(SlotSummary summary, SaveSlotPanelMode mode)
{
bool hasData = summary != null;
if (_emptyIndicator != null) _emptyIndicator.SetActive(!hasData);
if (_dataIndicator != null) _dataIndicator.SetActive(hasData);
if (_deleteButton != null) _deleteButton.gameObject.SetActive(hasData);
// 继续模式下空槽不可选;新游戏模式下空槽可选(建新档)
if (_selectButton != null)
_selectButton.interactable = hasData || mode == SaveSlotPanelMode.NewGame;
if (_steelSoulBadge != null)
_steelSoulBadge.SetActive(hasData && summary.IsSteelSoul);
if (!hasData) return;
if (_playtimeText != null) _playtimeText.text = FormatPlaytime(summary.Playtime);
if (_regionText != null)
{
string key = summary.SceneName ?? string.Empty;
string loc = !string.IsNullOrEmpty(key)
? LocalizationManager.Get(key, LocalizationTable.UI)
: null;
_regionText.text = !string.IsNullOrEmpty(loc) && loc != key ? loc : key;
}
if (_lastSavedText != null) _lastSavedText.text = FormatDateTime(summary.LastSaved);
if (_lingZhuText != null) _lingZhuText.text = summary.CurrentLingZhu.ToString();
if (_hpText != null) _hpText.text = summary.MaxHP.ToString();
RefreshBackground(summary);
}
private void RefreshBackground(SlotSummary summary)
{
if (_backgroundImage == null) return;
Sprite bg = null;
if (summary != null && !string.IsNullOrEmpty(summary.SceneName))
bg = _regionRegistry?.FindBySceneName(summary.SceneName)?.saveSlotBackground;
_backgroundImage.sprite = bg != null ? bg : _fallbackBackground;
_backgroundImage.enabled = _backgroundImage.sprite != null;
}
// ── 格式化工具 ────────────────────────────────────────────────────────
private static string FormatPlaytime(float seconds)
{
int h = (int)(seconds / 3600);
int m = (int)((seconds % 3600) / 60);
int s = (int)(seconds % 60);
return $"{h:D2}:{m:D2}:{s:D2}";
}
private static string FormatDateTime(string iso8601)
{
if (string.IsNullOrEmpty(iso8601)) return string.Empty;
if (DateTime.TryParse(iso8601,
System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.RoundtripKind,
out DateTime dt))
{
return dt.ToLocalTime().ToString("yyyy-MM-dd HH:mm");
}
return iso8601;
}
}
}