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:
2026-05-25 00:34:59 +08:00
parent 9c1e70fdeb
commit 48f018f4b8
6 changed files with 181 additions and 43 deletions

View File

@@ -310,12 +310,14 @@ namespace BaseGames.Quest
[Tooltip("条件任务:该任务已 Completed 时走本分支(留空 = 默认分支,总是满足)。")]
public QuestSO conditionQuest;
[Tooltip("世界状态标志条件判断逻辑:\n" +
" And默认= 全部 conditionFlags 均满足才走本分支\n" +
" Or = 任意一个 conditionFlag 满足即可走本分支")]
" And默认= 全部 conditionFlagEntries 均满足才走本分支\n" +
" Or = 任意一个 conditionFlagEntry 满足即可走本分支")]
public BaseGames.Core.WorldStateFlagLogic conditionFlagsLogic = BaseGames.Core.WorldStateFlagLogic.And;
[Tooltip("世界状态标志条件(可选)。按 conditionFlagsLogic 逻辑与 conditionQuest 共同决定分支是否激活。\n" +
"标志由 ISaveService.GetFlag(flagId) 查询,通常由 SetFlagAction 或其他系统写入。")]
[WorldStateFlag]
[Tooltip("世界状态标志条件(支持 invert 取反)。按 conditionFlagsLogic 逻辑与 conditionQuest 共同决定分支是否激活。\n" +
"优先使用此字段;若为空则自动回退到旧版 conditionFlags 以保证兼容性。")]
public BranchFlagEntry[] conditionFlagEntries;
[Tooltip("(旧版兼容字段,已被 conditionFlagEntries 取代。如 conditionFlagEntries 不为空则本字段被忽略。)")]
[HideInInspector]
public string[] conditionFlags;
[Tooltip("本分支解锁的后续任务。满足所有条件后,此任务将被设为 Available。")]
public QuestSO nextQuest;
@@ -327,6 +329,19 @@ namespace BaseGames.Quest
public string npcDialogueKey;
}
/// <summary>
/// 任务分支中单个世界状态标志条件支持取反NOT逻辑。
/// </summary>
[Serializable]
public struct BranchFlagEntry
{
[Tooltip("世界状态标志 ID由 ISaveService.GetFlag 查询)。")]
[BaseGames.Core.WorldStateFlag]
public string flagId;
[Tooltip("若勾选,则该标志为 false 时才满足条件NOT 取反逻辑)。")]
public bool invert;
}
/// <summary>任务分类,供日志 UI 分区和 DataHub 过滤使用。</summary>
public enum QuestCategory
{