feat: Round 49 narrative systems improvements

QuestManager: extract CheckQuestDepsAndFlags shared method, simplify GetQuestLockInfo/CanAccept/MeetsPrerequisites; add GetQuestsInState+FilterQuests implementations; fix extra brace compile bug; add _pauseTimestamps logging; use actualDelta in ApplyAffinity event.

QuestSO: add depth>32 guard to HasPrerequisiteCycle and HasBranchCycle to prevent editor freeze on deep chains.

EventChainModule: replace FindObjectOfType with ServiceLocator.GetOrDefault in ForceExecute; add self-trigger flag detection (check 6) in ValidateAllChains using reflection.

DialogueVariantPreviewWindow: add matrix analysis section enumerating all 2^N flag combinations (N<=10) with table showing winning variant per combination.

WorldStateRegistry: LoadFromSave null guard on data.World sub-collections (P0 fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-25 00:17:27 +08:00
parent 6eaa83dc71
commit 3c3ea1ead6
6 changed files with 279 additions and 118 deletions

View File

@@ -431,10 +431,10 @@ namespace BaseGames.Editor.Modules
var capturedC = c;
var forceBtn = new Button(() =>
{
var mgr = UnityEngine.Object.FindObjectOfType<EventChainManager>();
var mgr = BaseGames.Core.ServiceLocator.GetOrDefault<EventChainManager>();
if (mgr == null)
{
Debug.LogWarning("[EventChainModule] 场景中未找到 EventChainManager无法强制触发。");
Debug.LogWarning("[EventChainModule] ServiceLocator 中未找到 EventChainManager无法强制触发。请确认场景中已挂载并注册。");
return;
}
Debug.Log($"[EventChainModule] 强制触发链:{capturedC.chainId}");
@@ -519,6 +519,48 @@ namespace BaseGames.Editor.Modules
AddError($"{c.chainId}: actions[{i}] 为 null运行时将触发 NullReferenceException。", c);
}
// ⑥ 自触发检测:某条件检查的标志由同一链的 Action 写入(可能造成链被自身条件阻断或无限反复触发)
var flagFieldFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
foreach (var c in allChains)
{
if (string.IsNullOrWhiteSpace(c.chainId)) continue;
var writtenFlags = new System.Collections.Generic.HashSet<string>(StringComparer.Ordinal);
var readFlags = new System.Collections.Generic.HashSet<string>(StringComparer.Ordinal);
if (c.actions != null)
foreach (var action in c.actions)
{
if (action == null) continue;
foreach (var field in action.GetType().GetFields(flagFieldFlags))
if ((field.Name.Contains("flag", System.StringComparison.OrdinalIgnoreCase) ||
field.Name.Contains("Flag", System.StringComparison.OrdinalIgnoreCase))
&& field.FieldType == typeof(string))
{
var val = field.GetValue(action) as string;
if (!string.IsNullOrEmpty(val)) writtenFlags.Add(val);
}
}
if (c.conditions != null)
foreach (var cond in c.conditions)
{
if (cond == null) continue;
foreach (var field in cond.GetType().GetFields(flagFieldFlags))
if ((field.Name.Contains("flag", System.StringComparison.OrdinalIgnoreCase) ||
field.Name.Contains("Flag", System.StringComparison.OrdinalIgnoreCase))
&& field.FieldType == typeof(string))
{
var val = field.GetValue(cond) as string;
if (!string.IsNullOrEmpty(val)) readFlags.Add(val);
}
}
foreach (var flagId in readFlags)
if (writtenFlags.Contains(flagId))
AddWarn($"{c.chainId}: 条件读取的标志 '{flagId}' 同时被本链的 Action 写入。" +
"若该标志在触发前已被设置,链将永远无法执行或会产生意外的循环行为。", c);
}
Debug.Log($"[EventChainModule] 验证完成:{allChains.Count} 条事件链,{errorCount} 个错误,{warnCount} 个警告。");
QuestValidationResultWindow.Show(issues, errorCount, warnCount, allChains.Count, "事件链批量验证结果", "事件链");
}