using UnityEngine;
using UnityEngine.Serialization;
using TMPro;
using System.Collections.Generic;
using BaseGames.Core;
using BaseGames.Core.Events;
namespace BaseGames.World.Map
{
///
/// 探索进度百分比 UI 组件。
/// 显示全局探索进度和当前区域探索进度,随 OnExplorationChanged 实时更新。
/// 挂在 HUD 或地图面板下;需配置两个 TMP_Text 字段和可选的 StringEventChannelSO。
///
public class MapProgressDisplay : MonoBehaviour
{
[Header("文本组件")]
[Tooltip("全局探索进度文本,例如 '已探索 42%'。留空则不更新。")]
[SerializeField] private TMP_Text _globalProgressText;
[Tooltip("当前区域进度文本,例如 '区域:67%'。留空则不更新。")]
[SerializeField] private TMP_Text _regionProgressText;
[Header("格式字符串({0:P0} = 百分比,{1} = 区域显示名)")]
[SerializeField] private string _globalFormat = "已探索 {0:P0}";
[SerializeField] private string _regionFormat = "{1}:{0:P0}";
[Header("区域名映射(与 RegionNameDisplay 共用同一机制)")]
[Tooltip("RegionId → 本地化显示名映射。未配置时直接显示 RegionId。")]
[FormerlySerializedAs("_regionNameEntries")]
[SerializeField] private RegionNameEntry[] _regionNames;
[Header("Event Channels")]
[Tooltip("区域切换事件;订阅后区域切换时自动刷新区域进度。")]
[SerializeField] private StringEventChannelSO _onRegionChanged;
private IMapService _mapSvc;
private string _currentRegionId;
private readonly CompositeDisposable _subs = new();
// R15-N2 RegionId → Entry 字典,将 ResolveRegionDisplayName 从 O(N) 降至 O(1)
private Dictionary _regionDict;
private void Awake()
{
BuildRegionDict();
}
private void OnValidate() => BuildRegionDict();
private void OnEnable()
{
_mapSvc = ServiceLocator.GetOrDefault();
if (_mapSvc != null)
{
_mapSvc.OnExplorationChanged += Refresh;
_currentRegionId = _mapSvc.CurrentRegionId;
}
_onRegionChanged?.Subscribe(OnRegionChanged).AddTo(_subs);
Refresh();
}
private void OnDisable()
{
if (_mapSvc != null)
{
_mapSvc.OnExplorationChanged -= Refresh;
_mapSvc = null;
}
_subs.Clear();
}
private void OnRegionChanged(string regionId)
{
_currentRegionId = regionId;
Refresh();
}
/// 立即刷新进度文本。可由外部调用(例如地图面板打开时)。
public void Refresh()
{
if (_mapSvc == null)
_mapSvc = ServiceLocator.GetOrDefault();
if (_mapSvc == null) return;
// 全局探索进度
if (_globalProgressText != null)
{
float progress = _mapSvc.GetExplorationProgress();
try
{
_globalProgressText.text = string.Format(_globalFormat, progress);
}
catch (System.FormatException)
{
_globalProgressText.text = $"{progress:P0}";
Debug.LogWarning($"[MapProgressDisplay] _globalFormat 格式字符串配置有误:'{_globalFormat}',已回退到默认显示。", this);
}
}
// 当前区域进度
if (_regionProgressText != null && !string.IsNullOrEmpty(_currentRegionId))
{
var rooms = _mapSvc.GetRoomsByRegion(_currentRegionId);
if (rooms != null && rooms.Length > 0)
{
int exploredCount = 0;
foreach (var r in rooms)
if (r != null && _mapSvc.IsExplored(r.RoomId)) exploredCount++;
float regionProgress = (float)exploredCount / rooms.Length;
// R15-N2 解析区域显示名(本地化 Key → DisplayName → RegionId 回退)
string regionDisplayName = ResolveRegionDisplayName(_currentRegionId);
try
{
_regionProgressText.text = string.Format(_regionFormat, regionProgress, regionDisplayName);
}
catch (System.FormatException)
{
_regionProgressText.text = $"{regionDisplayName}:{regionProgress:P0}";
Debug.LogWarning($"[MapProgressDisplay] _regionFormat 格式字符串配置有误:'{_regionFormat}',已回退到默认显示。", this);
}
}
}
}
// ── 辅助方法 ──────────────────────────────────────────────────────────
/// 预建 RegionId → Entry 字典,O(1) 查询。
private void BuildRegionDict()
=> _regionDict = MapServiceExtensions.BuildRegionDict(_regionNames);
///
/// 将 regionId 解析为玩家可读的显示名。
/// 优先读 LocKey(本地化),其次 DisplayName,最后回退到 regionId 本身。
///
private string ResolveRegionDisplayName(string regionId)
=> MapServiceExtensions.ResolveRegionDisplayName(_regionDict, regionId);
}
}