feat(save): 添加可存点判定器与节点事件触发机制
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
namespace AibisDream.SaveSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 可存点被拒绝的原因,供日志与 UI 提示使用。
|
||||
/// </summary>
|
||||
public enum SavePointRejectReason
|
||||
{
|
||||
None,
|
||||
Restoring,
|
||||
GamePaused,
|
||||
NoActiveNode,
|
||||
StartNode,
|
||||
FunctionNode,
|
||||
NoAutoSave
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可存点判定器。
|
||||
/// <para>
|
||||
/// P3 只搭框架:规则不是最终版,后续随 Yarn 节点 tags 整理和章节策略扩展而更新。
|
||||
/// 当前所有 FixState 均放行,不可存状态由 Yarn 节点流程控制。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public static class SavePointEvaluator
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前是否可以自动存档。
|
||||
/// </summary>
|
||||
public static bool CanAutoSave(out SavePointRejectReason reason)
|
||||
{
|
||||
if (SaveRestoreOrchestrator.IsRestoring)
|
||||
{
|
||||
reason = SavePointRejectReason.Restoring;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GameManager.Instance != null && GameManager.Instance.state.isInPause)
|
||||
{
|
||||
reason = SavePointRejectReason.GamePaused;
|
||||
return false;
|
||||
}
|
||||
|
||||
var (nodeName, tags) = DialogController.Instance != null
|
||||
? DialogController.Instance.GetCurrentNodeContext()
|
||||
: (null, null);
|
||||
|
||||
if (string.IsNullOrEmpty(nodeName))
|
||||
{
|
||||
reason = SavePointRejectReason.NoActiveNode;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nodeName == "Start")
|
||||
{
|
||||
reason = SavePointRejectReason.StartNode;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tags != null && IsTagPresent(tags, "function"))
|
||||
{
|
||||
reason = SavePointRejectReason.FunctionNode;
|
||||
return false;
|
||||
}
|
||||
|
||||
reason = SavePointRejectReason.None;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前是否可以手动存档。
|
||||
/// 手动档 = 复制最近自动档,因此需要当前可自动存档且自动档存在。
|
||||
/// </summary>
|
||||
public static bool CanManualSave(out SavePointRejectReason reason)
|
||||
{
|
||||
if (!CanAutoSave(out reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SlotDirectory.Exists(SlotIndex.Auto))
|
||||
{
|
||||
reason = SavePointRejectReason.NoAutoSave;
|
||||
return false;
|
||||
}
|
||||
|
||||
reason = SavePointRejectReason.None;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsTagPresent(System.Collections.Generic.IReadOnlyList<string> tags, string tag)
|
||||
{
|
||||
if (tags == null) return false;
|
||||
|
||||
foreach (var t in tags)
|
||||
{
|
||||
if (t == tag)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 512f62f0cf333514384484ef1ab1cb3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -11,7 +11,7 @@ namespace AibisDream.SaveSystem
|
||||
/// <summary>
|
||||
/// 存读档流程编排层:连接 UI、<see cref="SnapshotService"/> 与槽位系统。
|
||||
/// <para>
|
||||
/// 对外入口:<see cref="SaveToAutoSlot"/>(<<auto_save>>)、
|
||||
/// 对外入口:<see cref="TryAutoSave"/>(节点进入事件触发)、
|
||||
/// <see cref="CreateManualSlot"/>(手动存档)、
|
||||
/// <see cref="RestoreFromSlot"/>(读档)、
|
||||
/// <see cref="RestoreFromFile"/>(旧档/调试)。
|
||||
@@ -19,8 +19,11 @@ namespace AibisDream.SaveSystem
|
||||
/// </summary>
|
||||
public static class SaveRestoreOrchestrator
|
||||
{
|
||||
/// <summary>是否正在读档还原中;为 true 时禁止自动存档覆盖 slot_0。</summary>
|
||||
public static bool IsRestoring { get; private set; }
|
||||
|
||||
/// <summary>捕获快照并写入自动档(slot 0);含保存 Loading UI。</summary>
|
||||
public static void SaveToAutoSlot()
|
||||
public static void TryAutoSave()
|
||||
{
|
||||
var host = YarnVariableStorage.Instance;
|
||||
if (host == null)
|
||||
@@ -29,6 +32,12 @@ namespace AibisDream.SaveSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SavePointEvaluator.CanAutoSave(out var reason))
|
||||
{
|
||||
Debug.LogWarning($"[SaveRestoreOrchestrator] 当前不是可存点,跳过自动存档:{reason}");
|
||||
return;
|
||||
}
|
||||
|
||||
ActionKit.Sequence()
|
||||
.Callback(() => UIManager.Instance.GetPanel<InfoPanel>().ShowSaveLoading())
|
||||
.Delay(1f)
|
||||
@@ -46,39 +55,61 @@ namespace AibisDream.SaveSystem
|
||||
/// <summary>将当前自动档复制到指定手动档。</summary>
|
||||
public static void CreateManualSlot(int slotIndex)
|
||||
{
|
||||
if (!SavePointEvaluator.CanManualSave(out var reason))
|
||||
{
|
||||
Debug.LogWarning($"[SaveRestoreOrchestrator] 当前不可手动存档:{reason}");
|
||||
return;
|
||||
}
|
||||
|
||||
SlotManager.CopyAutoToManual(slotIndex);
|
||||
}
|
||||
|
||||
/// <summary>从指定槽位读档并还原。</summary>
|
||||
public static IEnumerator RestoreFromSlot(int slotIndex)
|
||||
{
|
||||
var snapshot = SlotManager.LoadSnapshot(slotIndex);
|
||||
if (snapshot == null)
|
||||
IsRestoring = true;
|
||||
try
|
||||
{
|
||||
Debug.LogError($"[SaveRestoreOrchestrator] 槽位 {slotIndex} 不存在或读取失败");
|
||||
yield break;
|
||||
}
|
||||
var snapshot = SlotManager.LoadSnapshot(slotIndex);
|
||||
if (snapshot == null)
|
||||
{
|
||||
Debug.LogError($"[SaveRestoreOrchestrator] 槽位 {slotIndex} 不存在或读取失败");
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return RestoreSnapshot(snapshot);
|
||||
yield return RestoreSnapshot(snapshot);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsRestoring = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>从文件读档并还原;自动区分新快照格式与 legacy 格式。</summary>
|
||||
public static IEnumerator RestoreFromFile(string savePath)
|
||||
{
|
||||
if (SnapshotPersistence.IsLegacyFormat(savePath))
|
||||
IsRestoring = true;
|
||||
try
|
||||
{
|
||||
Debug.LogWarning("[SaveRestoreOrchestrator] 检测到旧格式存档,请使用新快照格式重新保存。");
|
||||
yield return RestoreLegacy(savePath);
|
||||
yield break;
|
||||
}
|
||||
if (SnapshotPersistence.IsLegacyFormat(savePath))
|
||||
{
|
||||
Debug.LogWarning("[SaveRestoreOrchestrator] 检测到旧格式存档,请使用新快照格式重新保存。");
|
||||
yield return RestoreLegacy(savePath);
|
||||
yield break;
|
||||
}
|
||||
|
||||
SaveSnapshot snapshot;
|
||||
using (new CodeTimer("LoadSnapshot"))
|
||||
SaveSnapshot snapshot;
|
||||
using (new CodeTimer("LoadSnapshot"))
|
||||
{
|
||||
snapshot = SnapshotPersistence.Load(savePath);
|
||||
}
|
||||
|
||||
yield return RestoreSnapshot(snapshot);
|
||||
}
|
||||
finally
|
||||
{
|
||||
snapshot = SnapshotPersistence.Load(savePath);
|
||||
IsRestoring = false;
|
||||
}
|
||||
|
||||
yield return RestoreSnapshot(snapshot);
|
||||
}
|
||||
|
||||
/// <summary>旧档:Yarn 变量 + DeepRepairDataRegistry systemData。</summary>
|
||||
|
||||
Reference in New Issue
Block a user