using System;
using UnityEngine;
using Yarn.Unity;
namespace AibisDream.SaveSystem
{
///
/// 从运行时组装 (纯内存,不写盘)。
///
/// 流程:元数据 → 场景 → 锚点(含宏观章节)→ Yarn 变量 → 各 Provider.Capture。
/// scene / anchor 由框架直管;其余子系统通过 Provider 写入 sections。
///
///
public static class SnapshotCapture
{
/// 捕获当前完整快照。调用前需已注册全部 provider。
///
/// 触发存档时的 Yarn 节点名(通常为 OnNodeStart 传入值)。
/// 非 null 时作为 anchor,并与 Capture 时刻的当前节点比较;不一致时打 warning,不阻断写盘。
///
///
/// 为 true 时写入空 (显式 <<save>> 默认行为)。
///
public static SaveSnapshot Capture(
YarnVariableStorage storage,
string triggerNodeName = null,
bool omitAnchor = false)
{
SnapshotRegistry.ValidateRequiredProviders();
var snapshot = new SaveSnapshot
{
gameVersion = Application.version,
savedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
};
CaptureScene(snapshot);
CaptureAnchor(snapshot, triggerNodeName, omitAnchor);
CaptureYarnVariables(storage, snapshot);
foreach (var provider in SnapshotRegistry.GetOrderedProviders())
{
var dto = provider.Capture();
if (dto == null) continue;
snapshot.sections[provider.SaveId] = dto;
}
snapshot.schemaVersion = SaveSnapshotSchema.CurrentVersion;
return snapshot;
}
private static void CaptureScene(SaveSnapshot snapshot)
{
var sceneLoader = SceneLoader.Instance;
snapshot.scene = new SceneSnapshotDto
{
sceneName = sceneLoader != null ? sceneLoader.CurrentSceneName : null
};
}
private static void CaptureAnchor(SaveSnapshot snapshot, string triggerNodeName, bool omitAnchor)
{
var dialog = DialogController.Instance;
if (dialog == null) return;
string anchorNodeName;
if (omitAnchor)
{
anchorNodeName = string.Empty;
}
else
{
var (currentNodeName, _) = dialog.GetCurrentNodeContext();
anchorNodeName = triggerNodeName ?? currentNodeName ?? string.Empty;
if (triggerNodeName != null
&& !string.Equals(
NormalizeNodeName(triggerNodeName),
NormalizeNodeName(currentNodeName),
StringComparison.Ordinal))
{
Debug.LogWarning(
$"[SnapshotCapture] 存档锚点漂移:触发时「{FormatNodeName(triggerNodeName)}」," +
$"Capture 时「{FormatNodeName(currentNodeName)}」。anchor 使用触发节点。");
}
}
var yarnProject = dialog.DialogueRunner?.YarnProject;
var projectId = yarnProject != null ? yarnProject.name : string.Empty;
var gameManager = GameManager.Instance;
var sceneSoName = gameManager != null ? gameManager.GetSceneSoName() : null;
snapshot.anchor = new AnchorSnapshot
{
sceneSoName = sceneSoName,
yarnProjectId = projectId,
nodeName = anchorNodeName
};
}
private static string NormalizeNodeName(string nodeName)
{
return string.IsNullOrEmpty(nodeName) ? string.Empty : nodeName;
}
private static string FormatNodeName(string nodeName)
{
return string.IsNullOrEmpty(nodeName) ? "(无节点)" : nodeName;
}
private static void CaptureYarnVariables(YarnVariableStorage storage, SaveSnapshot snapshot)
{
var (floats, strings, bools) = storage.GetAllVariables();
snapshot.yarnVariables = new YarnVariablesSnapshot
{
floats = floats ?? new(),
strings = strings ?? new(),
bools = bools ?? new()
};
}
}
}