feat: Round 51 narrative systems improvements
- SaveData: update QuestState.Status comment to include Paused state - QuestManager: add inline comment on AcceptQuest duplicate-accept guard - QuestManager: wrap reward.Apply() in try-catch so exceptions don't corrupt already-committed Completed state - QuestManager.UnlockBranches: support new conditionFlagEntries (invert/ NOT logic) with graceful fallback to legacy conditionFlags - QuestGiver: cache IQuestManager field in OnEnable; subscribe to OnQuestStateChanged for automatic cache invalidation instead of manual _cacheDirty = true after each Interact; remove per-call SL.GetOrDefault - QuestGiver: replace hardcoded Chinese prompt strings with LocalizationManager.Get(key, 'UI') + inline fallback via GetPrompt() - QuestSO: add BranchFlagEntry struct (flagId + invert) for NOT-logic branch conditions; add conditionFlagEntries to QuestBranch with HideInInspector on legacy conditionFlags for backward compat - QuestModule: add static TTL cache (5 s) for FindAll<QuestSO>() in PopulateDependencyGraph to avoid re-scanning disk on every foldout open - NpcSOEditor: add 'jump to localization file' button that pings and selects the UI table JSON in the Project window Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,7 @@ using BaseGames.Dialogue;
|
||||
using BaseGames.Player;
|
||||
using QuestStateEnum = BaseGames.Core.Events.QuestState;
|
||||
using SL = BaseGames.Core.ServiceLocator;
|
||||
using L10n = BaseGames.Localization.LocalizationManager;
|
||||
|
||||
namespace BaseGames.Quest
|
||||
{
|
||||
@@ -38,25 +39,68 @@ namespace BaseGames.Quest
|
||||
private QuestStateEnum _cachedState;
|
||||
private bool _cacheDirty = true;
|
||||
|
||||
// 本地化 Key 常量 — 对应 UI 本地化表中的条目;
|
||||
// 如本地化表未配置该 Key,GetPrompt 会降级为内联的中文默认文本。
|
||||
private const string K_Accept = "QUEST_PROMPT_ACCEPT";
|
||||
private const string K_Submit = "QUEST_PROMPT_SUBMIT";
|
||||
private const string K_InProgress = "QUEST_PROMPT_IN_PROGRESS";
|
||||
private const string K_Paused = "QUEST_PROMPT_PAUSED";
|
||||
private const string K_Talk = "QUEST_PROMPT_TALK";
|
||||
|
||||
// 缓存 IQuestManager + IQuestEventSource 引用,避免每次访问 InteractPrompt 调用 SL.GetOrDefault
|
||||
private IQuestManager _questManager;
|
||||
private IQuestEventSource _questEvents;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
_cacheDirty = true;
|
||||
_questManager = SL.GetOrDefault<IQuestManager>();
|
||||
_questEvents = _questManager as IQuestEventSource;
|
||||
if (_questEvents != null)
|
||||
_questEvents.OnQuestStateChanged += HandleQuestStateChanged;
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
if (_questEvents != null)
|
||||
{
|
||||
_questEvents.OnQuestStateChanged -= HandleQuestStateChanged;
|
||||
_questEvents = null;
|
||||
}
|
||||
_questManager = null;
|
||||
}
|
||||
|
||||
// 任务状态变化时自动标记缓存失效(无需再手动设 _cacheDirty)
|
||||
private void HandleQuestStateChanged(string questId, QuestStateEnum from, QuestStateEnum to)
|
||||
{
|
||||
if (_offeredQuests == null) return;
|
||||
foreach (var q in _offeredQuests)
|
||||
if (q != null && q.questId == questId) { _cacheDirty = true; return; }
|
||||
}
|
||||
|
||||
// 本地化提示词辅助:如 Key 在表中找不到(返回值等于 Key 自身),回退到内联默认文本
|
||||
private static string GetPrompt(string key, string fallback)
|
||||
{
|
||||
var v = L10n.Get(key, "UI");
|
||||
return v != key ? v : fallback;
|
||||
}
|
||||
|
||||
public override string InteractPrompt
|
||||
{
|
||||
get
|
||||
{
|
||||
var qm = SL.GetOrDefault<IQuestManager>();
|
||||
var quest = GetCachedQuest(qm);
|
||||
if (quest == null || qm == null) return base.InteractPrompt;
|
||||
var quest = GetCachedQuest();
|
||||
if (quest == null || _questManager == null) return base.InteractPrompt;
|
||||
return _cachedState switch
|
||||
{
|
||||
QuestStateEnum.Available => "接受任务",
|
||||
QuestStateEnum.Active => qm.IsReadyToComplete(quest.questId) ? "提交任务" : "进行中…",
|
||||
QuestStateEnum.Paused => "暂停中…",
|
||||
QuestStateEnum.Completed => "对话",
|
||||
QuestStateEnum.Available => GetPrompt(K_Accept, "接受任务"),
|
||||
QuestStateEnum.Active => _questManager.IsReadyToComplete(quest.questId)
|
||||
? GetPrompt(K_Submit, "提交任务")
|
||||
: GetPrompt(K_InProgress, "进行中…"),
|
||||
QuestStateEnum.Paused => GetPrompt(K_Paused, "暂停中…"),
|
||||
QuestStateEnum.Completed => GetPrompt(K_Talk, "对话"),
|
||||
_ => base.InteractPrompt,
|
||||
};
|
||||
}
|
||||
@@ -64,34 +108,32 @@ namespace BaseGames.Quest
|
||||
|
||||
protected override void Interact_Internal(Transform player)
|
||||
{
|
||||
var qm = SL.GetOrDefault<IQuestManager>();
|
||||
var quest = GetCachedQuest(qm);
|
||||
if (quest == null || qm == null) return;
|
||||
var quest = GetCachedQuest();
|
||||
if (quest == null || _questManager == null) return;
|
||||
|
||||
if (_cachedState == QuestStateEnum.Available)
|
||||
{
|
||||
qm.AcceptQuest(quest.questId);
|
||||
_cacheDirty = true; // 状态已变更,下次访问重新查询
|
||||
_questManager.AcceptQuest(quest.questId);
|
||||
// OnQuestStateChanged 事件会自动触发 HandleQuestStateChanged → _cacheDirty = true
|
||||
}
|
||||
else if (_cachedState == QuestStateEnum.Active && qm.IsReadyToComplete(quest.questId))
|
||||
else if (_cachedState == QuestStateEnum.Active && _questManager.IsReadyToComplete(quest.questId))
|
||||
{
|
||||
// 直接从 player 获取 PlayerStats,避免对 PlayerController 的程序集依赖
|
||||
var stats = player.GetComponentInParent<PlayerStats>();
|
||||
qm.CompleteQuest(quest.questId, stats);
|
||||
_cacheDirty = true; // 状态已变更,下次访问重新查询
|
||||
_questManager.CompleteQuest(quest.questId, stats);
|
||||
// OnQuestStateChanged 事件会自动触发 HandleQuestStateChanged → _cacheDirty = true
|
||||
}
|
||||
}
|
||||
|
||||
protected override DialogueSequenceSO GetCurrentDialogue()
|
||||
{
|
||||
var qm = SL.GetOrDefault<IQuestManager>();
|
||||
var quest = GetCachedQuest(qm);
|
||||
if (quest == null || qm == null) return base.GetCurrentDialogue();
|
||||
var quest = GetCachedQuest();
|
||||
if (quest == null || _questManager == null) return base.GetCurrentDialogue();
|
||||
|
||||
return _cachedState switch
|
||||
{
|
||||
QuestStateEnum.Available => _availableDialogue,
|
||||
QuestStateEnum.Active => qm.IsReadyToComplete(quest.questId)
|
||||
QuestStateEnum.Active => _questManager.IsReadyToComplete(quest.questId)
|
||||
? _readyDialogue : _activeDialogue,
|
||||
QuestStateEnum.Paused => _activeDialogue, // 暂停中显示"催促"对话,不触发任何状态推进
|
||||
QuestStateEnum.Completed => _completedDialogue,
|
||||
@@ -104,13 +146,14 @@ namespace BaseGames.Quest
|
||||
/// <summary>
|
||||
/// 返回缓存的当前任务(处于 Available/Active/Paused 的第一个,或最后一个已完成任务)。
|
||||
/// 若缓存不脏,直接返回上次结果,避免每帧遍历 _offeredQuests。
|
||||
/// 调用 Interact_Internal 后将 _cacheDirty 置 true,确保下次交互状态是最新的。
|
||||
/// 任务状态事件(HandleQuestStateChanged)或 OnEnable 会自动将 _cacheDirty 置 true,
|
||||
/// 确保下次访问状态是最新的。
|
||||
/// </summary>
|
||||
private QuestSO GetCachedQuest(IQuestManager qm = null)
|
||||
private QuestSO GetCachedQuest()
|
||||
{
|
||||
if (!_cacheDirty && _cachedQuest != null) return _cachedQuest;
|
||||
|
||||
qm ??= SL.GetOrDefault<IQuestManager>();
|
||||
var qm = _questManager;
|
||||
if (_offeredQuests == null || qm == null) { _cacheDirty = false; return null; }
|
||||
|
||||
QuestSO lastCompleted = null;
|
||||
|
||||
Reference in New Issue
Block a user