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

@@ -244,6 +244,7 @@ namespace BaseGames.Quest
public void AcceptQuest(string questId)
{
if (string.IsNullOrEmpty(questId)) return;
// CanAccept 内部已通过 GetState() != Available 检查,防止重复接取 Active/Completed 任务产生重复事件
if (!CanAccept(questId)) return;
var oldState = GetState(questId);
_questStates[questId] = QuestStateEnum.Active;
@@ -361,7 +362,13 @@ namespace BaseGames.Quest
Chan_QuestCompleted?.Raise(questId);
OnQuestCompleted?.Invoke(questId);
quest.reward?.Apply(rewardTarget);
// 奖励发放:用 try-catch 包裹,防止 Apply 异常导致好感度/对话解锁等后续逻辑中断
try { quest.reward?.Apply(rewardTarget); }
catch (System.Exception ex)
{
Debug.LogError(
$"[QuestManager] 任务 '{questId}' 奖励发放时抛出异常(任务状态已提交为 Completed{ex.Message}\n{ex.StackTrace}");
}
ApplyAffinity(quest);
UnlockDialogueKey(quest);
UnlockBranches(questId, quest);
@@ -425,38 +432,62 @@ namespace BaseGames.Quest
if (!conditionMet) continue;
// 世界状态标志条件And/Or 由 conditionFlagsLogic 决定)
// 优先用新版 conditionFlagEntries支持 invert/NOT 取反),若为空则回退到旧版 conditionFlags
// saveService 未注入时降级:跳过标志检查,仅由 conditionQuest 决定分支
if (branch.conditionFlags != null && branch.conditionFlags.Length > 0
&& saveService != null)
bool hasFlagEntries = branch.conditionFlagEntries != null && branch.conditionFlagEntries.Length > 0;
bool hasLegacyFlags = branch.conditionFlags != null && branch.conditionFlags.Length > 0;
bool hasFlagConds = hasFlagEntries || hasLegacyFlags;
if (hasFlagConds && saveService != null)
{
if (branch.conditionFlagsLogic == BaseGames.Core.WorldStateFlagLogic.Or)
{
conditionMet = false;
foreach (var flag in branch.conditionFlags)
if (hasFlagEntries)
{
if (!string.IsNullOrEmpty(flag) && saveService.GetFlag(flag))
foreach (var entry in branch.conditionFlagEntries)
{
conditionMet = true;
break;
if (string.IsNullOrEmpty(entry.flagId)) continue;
bool raw = saveService.GetFlag(entry.flagId);
if (entry.invert ? !raw : raw) { conditionMet = true; break; }
}
}
else
{
foreach (var flag in branch.conditionFlags)
{
if (!string.IsNullOrEmpty(flag) && saveService.GetFlag(flag))
{ conditionMet = true; break; }
}
}
}
else
{
// AND默认全部标志均须满足
foreach (var flag in branch.conditionFlags)
// AND默认全部标志均须满足(支持 invert 取反)
if (hasFlagEntries)
{
if (string.IsNullOrEmpty(flag)) continue;
if (!saveService.GetFlag(flag)) { conditionMet = false; break; }
foreach (var entry in branch.conditionFlagEntries)
{
if (string.IsNullOrEmpty(entry.flagId)) continue;
bool raw = saveService.GetFlag(entry.flagId);
if (entry.invert ? raw : !raw) { conditionMet = false; break; }
}
}
else
{
foreach (var flag in branch.conditionFlags)
{
if (string.IsNullOrEmpty(flag)) continue;
if (!saveService.GetFlag(flag)) { conditionMet = false; break; }
}
}
}
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
else if (branch.conditionFlags != null && branch.conditionFlags.Length > 0
&& saveService == null)
else if (hasFlagConds && saveService == null)
{
Debug.LogWarning(
$"[QuestManager] 任务 '{questId}' 分支配置了 conditionFlags,但 ISaveService 未注册," +
$"[QuestManager] 任务 '{questId}' 分支配置了标志条件,但 ISaveService 未注册," +
"标志条件已跳过(降级为仅 conditionQuest 判断)。");
}
#endif