using System.Collections; using Newtonsoft.Json.Linq; using UnityEngine; namespace AibisDream.SaveSystem { /// /// 将 逐项写回运行时(纯内存,不读盘)。 /// /// 顺序:Yarn 变量 → 场景加载 → 设置章节 → 各 Provider(按 RestoreOrder)→ 锚点加载对话并重进节点。 /// 完整淡入淡出时序由 P4 在 扩展。 /// /// public static class SnapshotRestore { /// /// 还原 Yarn 变量、场景、章节与各 section;不包含锚点重进(由 或上层编排)。 /// public static IEnumerator Restore(YarnVariableStorage storage, SaveSnapshot snapshot) { if (snapshot == null) { Debug.LogError("[SnapshotRestore] snapshot 为 null"); yield break; } RestoreYarnVariables(storage, snapshot); yield return RestoreScene(snapshot); RestoreSceneSo(snapshot); foreach (var provider in SnapshotRegistry.GetOrderedProviders()) { if (!snapshot.sections.TryGetValue(provider.SaveId, out var dto) || dto == null) { continue; } dto = CoerceSectionDto(provider.SaveId, dto); yield return provider.Restore(dto); } if (snapshot.deepRepair != null && snapshot.deepRepair.sections != null && snapshot.deepRepair.sections.Count > 0) { Debug.LogWarning("[SnapshotRestore] DeepRepair 段尚未实现,已跳过(P5)。"); } } /// /// 按 加载对话工程并重新进入 Yarn 节点,实现「所见即所存」。 /// 调用前应已完成场景加载与各 provider 还原。 /// public static IEnumerator RestoreAnchor(SaveSnapshot snapshot) { if (snapshot?.anchor == null || string.IsNullOrEmpty(snapshot.anchor.nodeName)) { yield break; } var dialog = DialogController.Instance; if (dialog == null) yield break; var runner = dialog.DialogueRunner; if (runner == null) yield break; // 若当前 Runner 未加载对应 YarnProject,则先加载 if (!string.IsNullOrEmpty(snapshot.anchor.yarnProjectId)) { var sceneSo = GameManager.Instance?.GetCurrentTalkSceneSo(); if (sceneSo?.yarnProject != null && sceneSo.yarnProject.name == snapshot.anchor.yarnProjectId) { if (runner.YarnProject == null || runner.YarnProject.name != snapshot.anchor.yarnProjectId) { dialog.LoadDialog(sceneSo.yarnProject); } } else if (runner.YarnProject != null && runner.YarnProject.name != snapshot.anchor.yarnProjectId) { Debug.LogWarning( $"[SnapshotRestore] YarnProject 不匹配:当前 {runner.YarnProject.name},存档 {snapshot.anchor.yarnProjectId}"); } } if (runner.IsDialogueRunning) { yield return runner.Stop(); } yield return runner.StartDialogue(snapshot.anchor.nodeName); } private static void RestoreYarnVariables(YarnVariableStorage storage, SaveSnapshot snapshot) { var vars = snapshot.yarnVariables; if (vars == null) return; storage.SetAllVariables(vars.floats, vars.strings, vars.bools); } private static IEnumerator RestoreScene(SaveSnapshot snapshot) { if (snapshot.scene == null || string.IsNullOrEmpty(snapshot.scene.sceneName)) { yield break; } var sceneLoader = SceneLoader.Instance; if (sceneLoader == null) { Debug.LogError("[SnapshotRestore] SceneLoader 未初始化,无法加载场景"); yield break; } yield return sceneLoader.LoadSceneAsync(snapshot.scene.sceneName); } private static void RestoreSceneSo(SaveSnapshot snapshot) { if (snapshot.anchor == null || string.IsNullOrEmpty(snapshot.anchor.sceneSoName)) { return; } var gameManager = GameManager.Instance; if (gameManager == null) { Debug.LogWarning("[SnapshotRestore] GameManager 未初始化,无法设置章节 SO"); return; } gameManager.SetSceneSoByName(snapshot.anchor.sceneSoName); } /// /// 读盘后 中的值常为 ; /// 按 SaveId 转回各 Provider 所需的强类型 DTO。 /// private static object CoerceSectionDto(string saveId, object dto) { if (dto is not JObject jobj) { return dto; } return saveId switch { SnapshotProviderIds.Environment => jobj.ToObject(), SnapshotProviderIds.Actor => jobj.ToObject(), SnapshotProviderIds.Audio => jobj.ToObject(), SnapshotProviderIds.Timeline => jobj.ToObject(), SnapshotProviderIds.Fix => jobj.ToObject(), SnapshotProviderIds.Screen => jobj.ToObject(), _ => jobj }; } } }