地图系统
This commit is contained in:
203
Assets/_Game/Scripts/UI/Inventory/QuestLogPanel.cs
Normal file
203
Assets/_Game/Scripts/UI/Inventory/QuestLogPanel.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
using TMPro;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
using BaseGames.Localization;
|
||||
using BaseGames.Quest;
|
||||
using QuestStateEnum = BaseGames.Core.Events.QuestState;
|
||||
|
||||
namespace BaseGames.UI.Inventory
|
||||
{
|
||||
/// <summary>
|
||||
/// 全屏任务日志 Tab(Journal/Quests 分栏)。
|
||||
/// 左侧列出进行中 / 已完成任务,右侧详情显示选中任务的名称、描述与目标进度。
|
||||
///
|
||||
/// 数据来源:ServiceLocator 获取 <see cref="IQuestManager"/>,读取 GetQuestsInState 快照。
|
||||
/// 与 HUD 的 <see cref="BaseGames.UI.HUD.QuestTrackerWidget"/> 不同:后者只显示当前追踪任务,
|
||||
/// 本面板提供完整列表浏览。本地化统一使用 Quest 表。
|
||||
/// </summary>
|
||||
public class QuestLogPanel : MonoBehaviour, IFocusable
|
||||
{
|
||||
[Header("任务列表")]
|
||||
[SerializeField] private Transform _listContainer;
|
||||
[Tooltip("任务行模板(含 Button + TMP_Text),kept inactive。")]
|
||||
[SerializeField] private Button _rowTemplate;
|
||||
|
||||
[Header("详情")]
|
||||
[SerializeField] private TMP_Text _detailTitle;
|
||||
[SerializeField] private TMP_Text _detailDesc;
|
||||
[SerializeField] private Transform _objectiveContainer;
|
||||
[Tooltip("目标行模板(TMP_Text),kept inactive。")]
|
||||
[SerializeField] private TMP_Text _objectiveRowTemplate;
|
||||
[Tooltip("无进行中任务时的提示节点。")]
|
||||
[SerializeField] private GameObject _emptyHint;
|
||||
|
||||
[Header("Event Channels - 刷新触发(任一即重建)")]
|
||||
[SerializeField] private QuestStateChangedEventChannel _onQuestStateChanged;
|
||||
|
||||
private IQuestManager _quests;
|
||||
private readonly List<string> _activeIds = new();
|
||||
private readonly List<Button> _rows = new();
|
||||
private readonly Queue<Button> _rowPool = new();
|
||||
private readonly List<TMP_Text> _objRows = new();
|
||||
private readonly Queue<TMP_Text> _objPool = new();
|
||||
private string _selectedQuestId;
|
||||
private readonly CompositeDisposable _subs = new();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_rowTemplate != null) _rowTemplate.gameObject.SetActive(false);
|
||||
if (_objectiveRowTemplate != null) _objectiveRowTemplate.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_quests = ServiceLocator.GetOrDefault<IQuestManager>();
|
||||
_onQuestStateChanged?.Subscribe(OnStateChanged).AddTo(_subs);
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_subs.Clear();
|
||||
RecycleRows();
|
||||
RecycleObjectives();
|
||||
}
|
||||
|
||||
private void OnStateChanged(QuestStateChangedEvent _) => Rebuild();
|
||||
|
||||
// ── 列表重建 ──────────────────────────────────────────────────────────
|
||||
private void Rebuild()
|
||||
{
|
||||
RecycleRows();
|
||||
if (_quests == null || _listContainer == null || _rowTemplate == null) return;
|
||||
|
||||
_activeIds.Clear();
|
||||
CollectInto(QuestStateEnum.Active);
|
||||
CollectInto(QuestStateEnum.Completed);
|
||||
|
||||
if (_emptyHint != null) _emptyHint.SetActive(_activeIds.Count == 0);
|
||||
|
||||
foreach (var id in _activeIds)
|
||||
{
|
||||
var row = SpawnRow();
|
||||
BindRow(row, id);
|
||||
_rows.Add(row);
|
||||
}
|
||||
|
||||
if (_activeIds.Count > 0) SelectQuest(_activeIds[0]);
|
||||
else ClearDetail();
|
||||
}
|
||||
|
||||
private void CollectInto(QuestStateEnum state)
|
||||
{
|
||||
var snapshot = _quests.GetQuestsInState(state);
|
||||
for (int i = 0; i < snapshot.Count; i++)
|
||||
if (!_activeIds.Contains(snapshot[i])) _activeIds.Add(snapshot[i]);
|
||||
}
|
||||
|
||||
private Button SpawnRow()
|
||||
{
|
||||
Button row = _rowPool.Count > 0 ? _rowPool.Dequeue() : Instantiate(_rowTemplate, _listContainer);
|
||||
row.transform.SetParent(_listContainer, false);
|
||||
row.gameObject.SetActive(true);
|
||||
return row;
|
||||
}
|
||||
|
||||
private void BindRow(Button row, string questId)
|
||||
{
|
||||
var label = row.GetComponentInChildren<TMP_Text>(includeInactive: true);
|
||||
if (label != null) label.text = ResolveQuestTitle(questId);
|
||||
row.onClick.RemoveAllListeners();
|
||||
string captured = questId;
|
||||
row.onClick.AddListener(() => SelectQuest(captured));
|
||||
}
|
||||
|
||||
private void RecycleRows()
|
||||
{
|
||||
foreach (var row in _rows)
|
||||
{
|
||||
if (row == null) continue;
|
||||
row.gameObject.SetActive(false);
|
||||
_rowPool.Enqueue(row);
|
||||
}
|
||||
_rows.Clear();
|
||||
}
|
||||
|
||||
// ── 详情 ──────────────────────────────────────────────────────────────
|
||||
private void SelectQuest(string questId)
|
||||
{
|
||||
_selectedQuestId = questId;
|
||||
if (_quests == null || !_quests.TryGetQuest(questId, out var quest) || quest == null)
|
||||
{
|
||||
ClearDetail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_detailTitle != null) _detailTitle.text = ResolveQuestTitle(questId);
|
||||
if (_detailDesc != null)
|
||||
{
|
||||
string loc = LocalizationManager.Get(quest.descriptionKey, LocalizationTable.Quest);
|
||||
_detailDesc.text = !string.IsNullOrEmpty(loc) && loc != quest.descriptionKey ? loc : string.Empty;
|
||||
}
|
||||
|
||||
RebuildObjectives(quest);
|
||||
}
|
||||
|
||||
private void RebuildObjectives(QuestSO quest)
|
||||
{
|
||||
RecycleObjectives();
|
||||
if (_objectiveContainer == null || _objectiveRowTemplate == null || quest.objectives == null) return;
|
||||
|
||||
foreach (var obj in quest.objectives)
|
||||
{
|
||||
if (obj == null) continue;
|
||||
var row = _objPool.Count > 0 ? _objPool.Dequeue() : Instantiate(_objectiveRowTemplate, _objectiveContainer);
|
||||
row.transform.SetParent(_objectiveContainer, false);
|
||||
row.gameObject.SetActive(true);
|
||||
string loc = LocalizationManager.Get(obj.displayTextKey, LocalizationTable.Quest);
|
||||
row.text = !string.IsNullOrEmpty(loc) && loc != obj.displayTextKey ? loc : obj.displayTextKey;
|
||||
_objRows.Add(row);
|
||||
}
|
||||
}
|
||||
|
||||
private void RecycleObjectives()
|
||||
{
|
||||
foreach (var row in _objRows)
|
||||
{
|
||||
if (row == null) continue;
|
||||
row.gameObject.SetActive(false);
|
||||
_objPool.Enqueue(row);
|
||||
}
|
||||
_objRows.Clear();
|
||||
}
|
||||
|
||||
private void ClearDetail()
|
||||
{
|
||||
if (_detailTitle != null) _detailTitle.text = string.Empty;
|
||||
if (_detailDesc != null) _detailDesc.text = string.Empty;
|
||||
RecycleObjectives();
|
||||
}
|
||||
|
||||
private string ResolveQuestTitle(string questId)
|
||||
{
|
||||
if (_quests != null && _quests.TryGetQuest(questId, out var quest) && quest != null
|
||||
&& !string.IsNullOrEmpty(quest.displayNameKey))
|
||||
{
|
||||
string loc = LocalizationManager.Get(quest.displayNameKey, LocalizationTable.Quest);
|
||||
return !string.IsNullOrEmpty(loc) && loc != quest.displayNameKey ? loc : questId;
|
||||
}
|
||||
return questId;
|
||||
}
|
||||
|
||||
// ── IFocusable ────────────────────────────────────────────────────────
|
||||
public void OnFocusRestored()
|
||||
{
|
||||
if (_rows.Count > 0 && _rows[0] != null)
|
||||
EventSystem.current?.SetSelectedGameObject(_rows[0].gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user