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

@@ -50,6 +50,16 @@ namespace BaseGames.Editor.Dialogue
$"▸ nameKey 解析预览:{resolved}",
s_previewStyle);
}
// ── 跳转到本地化文件 ────────────────────────────────────────────
EditorGUILayout.Space(4);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("跳转到本地化文件UI 表)", GUILayout.Width(200)))
{
PingLocalizationFile("UI");
}
EditorGUILayout.EndHorizontal();
}
/// <summary>
@@ -69,5 +79,32 @@ namespace BaseGames.Editor.Dialogue
return null;
}
}
/// <summary>
/// 在 Project 窗口中 Ping 指定表名对应的本地化 JSON 文件Resources/Localization/…/{tableName}.json
/// 遍历所有语言目录,以第一个找到的文件为准。
/// </summary>
private static void PingLocalizationFile(string tableName)
{
string[] guids = AssetDatabase.FindAssets(
$"t:TextAsset {tableName}",
new[] { "Assets/Resources/Localization" });
foreach (var guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
// 文件名(不含扩展名)必须完全匹配 tableName
if (!path.EndsWith($"/{tableName}.json", System.StringComparison.OrdinalIgnoreCase)) continue;
var asset = AssetDatabase.LoadAssetAtPath<TextAsset>(path);
if (asset == null) continue;
EditorGUIUtility.PingObject(asset);
Selection.activeObject = asset;
return;
}
Debug.LogWarning($"[NpcSOEditor] 未找到本地化表文件Resources/Localization/…/{tableName}.json");
}
}
}

View File

@@ -31,6 +31,11 @@ namespace BaseGames.Editor.Modules
// playModeStateChanged 订阅的字段引用,便于在重建 ActionBar 时退订旧订阅,避免内存泄漏
private System.Action<UnityEditor.PlayModeStateChange> _playModeHandler;
// 依赖关系图中 FindAll<QuestSO>() 的静态缓存,同一编辑器会话内复用,避免重复扫描磁盘
private static QuestSO[] s_allQuestCache;
private static double s_allQuestCacheTime;
private const double k_AllQuestCacheTtl = 5.0; // 秒;超时后下次打开 foldout 时刷新
public void Initialize()
{
_listPane = new SoListPane<QuestSO>(
@@ -421,7 +426,14 @@ namespace BaseGames.Editor.Modules
private static void PopulateDependencyGraph(VisualElement container, QuestSO s)
{
var allQuests = AssetOperations.FindAll<QuestSO>();
// 静态 TTL 缓存5 秒内复用上次 FindAll 结果,避免每次展开 foldout 都扫描全量资产
if (s_allQuestCache == null ||
EditorApplication.timeSinceStartup - s_allQuestCacheTime > k_AllQuestCacheTtl)
{
s_allQuestCache = AssetOperations.FindAll<QuestSO>();
s_allQuestCacheTime = EditorApplication.timeSinceStartup;
}
var allQuests = s_allQuestCache;
// ── 前置任务(上游)────────────────────────────────────────────────
bool hasPrereqs = s.prerequisiteQuests != null && s.prerequisiteQuests.Length > 0;