diff --git a/Assets/Scripts/SaveSystem/FixSceneSnapshotHelper.cs b/Assets/Scripts/SaveSystem/FixSceneSnapshotHelper.cs new file mode 100644 index 000000000..9c60568d4 --- /dev/null +++ b/Assets/Scripts/SaveSystem/FixSceneSnapshotHelper.cs @@ -0,0 +1,13 @@ +namespace AibisDream.SaveSystem +{ + /// + /// Fix 维修场景门控:fix / punchTape / bodyModule / eye 等 section 仅在维修场景 Capture / Restore。 + /// + public static class FixSceneSnapshotHelper + { + public static bool IsFixSceneActive() + { + return FixSystem.FixSystemCenter.Instance != null; + } + } +} diff --git a/Assets/Scripts/SaveSystem/FixSceneSnapshotHelper.cs.meta b/Assets/Scripts/SaveSystem/FixSceneSnapshotHelper.cs.meta new file mode 100644 index 000000000..92ab200a8 --- /dev/null +++ b/Assets/Scripts/SaveSystem/FixSceneSnapshotHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76df7c860ec032240a7278a7767154b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/SaveSystem/Providers/BodyModuleSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/BodyModuleSnapshotProvider.cs new file mode 100644 index 000000000..e384d4057 --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/BodyModuleSnapshotProvider.cs @@ -0,0 +1,54 @@ +using UnityEngine; + +namespace AibisDream.SaveSystem +{ + /// + /// sections["bodyModule"]:插线模块持久物理状态。RestoreOrder=66,在 fix 之后。 + /// + public class BodyModuleSnapshotProvider : ISyncSnapshotProvider + { + public string SaveId => SnapshotProviderIds.BodyModule; + public int RestoreOrder => 67; + + public object Capture() + { + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + return null; + } + + var bodyModule = FixSystem.FixSystemCenter.SystemDic.Get(); + if (bodyModule == null) + { + Debug.LogWarning("[BodyModuleSnapshotProvider] BodyModuleSystem 未初始化,跳过捕获。"); + return null; + } + + return bodyModule.CaptureBodyModuleSnapshot(); + } + + public void Restore(object dto, SnapshotRestoreContext context) + { + if (dto is not BodyModuleSnapshotDto bodyModuleDto) + { + context.Warn("[BodyModuleSnapshotProvider] DTO 类型不匹配,跳过还原。"); + return; + } + + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + context.Log("[BodyModuleSnapshotProvider] 当前非维修场景,跳过还原。"); + return; + } + + var bodyModule = FixSystem.FixSystemCenter.SystemDic.Get(); + if (bodyModule == null) + { + context.Warn("[BodyModuleSnapshotProvider] BodyModuleSystem 未初始化,跳过还原。"); + return; + } + + bodyModule.ApplyBodyModuleSnapshot(bodyModuleDto); + } + } +} diff --git a/Assets/Scripts/SaveSystem/Providers/BodyModuleSnapshotProvider.cs.meta b/Assets/Scripts/SaveSystem/Providers/BodyModuleSnapshotProvider.cs.meta new file mode 100644 index 000000000..8989f3e61 --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/BodyModuleSnapshotProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d9b525cdc20474840b754d942b7fb1cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/SaveSystem/Providers/EyeSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/EyeSnapshotProvider.cs new file mode 100644 index 000000000..1d117668f --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/EyeSnapshotProvider.cs @@ -0,0 +1,55 @@ +using AibisDream; +using UnityEngine; + +namespace AibisDream.SaveSystem +{ + /// + /// sections["eye"]:Eye 维修叙事阶段。RestoreOrder=67,在 bodyModule 之后。 + /// + public class EyeSnapshotProvider : ISyncSnapshotProvider + { + public string SaveId => SnapshotProviderIds.Eye; + public int RestoreOrder => 68; + + public object Capture() + { + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + return null; + } + + var eyeSystem = FixSystem.FixSystemCenter.SystemDic.Get(); + if (eyeSystem == null) + { + Debug.LogWarning("[EyeSnapshotProvider] EyeSystem 未初始化,跳过捕获。"); + return null; + } + + return eyeSystem.CaptureEyeSnapshot(); + } + + public void Restore(object dto, SnapshotRestoreContext context) + { + if (dto is not EyeSnapshotDto eyeDto) + { + context.Warn("[EyeSnapshotProvider] DTO 类型不匹配,跳过还原。"); + return; + } + + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + context.Log("[EyeSnapshotProvider] 当前非维修场景,跳过还原。"); + return; + } + + var eyeSystem = FixSystem.FixSystemCenter.SystemDic.Get(); + if (eyeSystem == null) + { + context.Warn("[EyeSnapshotProvider] EyeSystem 未初始化,跳过还原。"); + return; + } + + eyeSystem.ApplyEyeSnapshot(eyeDto); + } + } +} diff --git a/Assets/Scripts/SaveSystem/Providers/EyeSnapshotProvider.cs.meta b/Assets/Scripts/SaveSystem/Providers/EyeSnapshotProvider.cs.meta new file mode 100644 index 000000000..b499e01d6 --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/EyeSnapshotProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3207faffc75fae44d802cb9af0d73dcf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/SaveSystem/Providers/FixPanelSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/FixPanelSnapshotProvider.cs new file mode 100644 index 000000000..ebdb5dc2e --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/FixPanelSnapshotProvider.cs @@ -0,0 +1,52 @@ +using UnityEngine; + +namespace AibisDream.SaveSystem +{ + /// + /// sections["fixPanel"]:FixPanel 壳层 + 线缆/插头。RestoreOrder=66,在 fix 之后、bodyModule 之前。 + /// + public class FixPanelSnapshotProvider : ISyncSnapshotProvider + { + public string SaveId => SnapshotProviderIds.FixPanel; + public int RestoreOrder => 66; + + public object Capture() + { + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + return null; + } + + if (FixSystem.FixPanelSystem.Instance == null) + { + Debug.LogWarning("[FixPanelSnapshotProvider] FixPanelSystem 未初始化,跳过捕获。"); + return null; + } + + return FixSystem.FixPanelSystem.Instance.CapturePanelSnapshot(); + } + + public void Restore(object dto, SnapshotRestoreContext context) + { + if (dto is not FixPanelSnapshotDto panelDto) + { + context.Warn("[FixPanelSnapshotProvider] DTO 类型不匹配,跳过还原。"); + return; + } + + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + context.Log("[FixPanelSnapshotProvider] 当前非维修场景,跳过还原。"); + return; + } + + if (FixSystem.FixPanelSystem.Instance == null) + { + context.Warn("[FixPanelSnapshotProvider] FixPanelSystem 未初始化,跳过还原。"); + return; + } + + FixSystem.FixPanelSystem.Instance.ApplyPanelSnapshot(panelDto); + } + } +} diff --git a/Assets/Scripts/SaveSystem/Providers/FixPanelSnapshotProvider.cs.meta b/Assets/Scripts/SaveSystem/Providers/FixPanelSnapshotProvider.cs.meta new file mode 100644 index 000000000..cdb1a026d --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/FixPanelSnapshotProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 479311b005ae10a45a963b7826cf2da7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/SaveSystem/Providers/FixSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/FixSnapshotProvider.cs index 01bc3572c..33da68a59 100644 --- a/Assets/Scripts/SaveSystem/Providers/FixSnapshotProvider.cs +++ b/Assets/Scripts/SaveSystem/Providers/FixSnapshotProvider.cs @@ -14,6 +14,11 @@ namespace AibisDream.SaveSystem public object Capture() { + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + return null; + } + if (FixSystem.FixSystemCenter.Instance == null) { Debug.LogWarning("[FixSnapshotProvider] FixSystemCenter 未初始化,跳过捕获。"); @@ -31,6 +36,12 @@ namespace AibisDream.SaveSystem yield break; } + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + context.Log("[FixSnapshotProvider] 当前非维修场景,跳过还原。"); + yield break; + } + if (FixSystem.FixSystemCenter.Instance == null) { context.Warn("[FixSnapshotProvider] FixSystemCenter 未初始化,跳过还原。"); diff --git a/Assets/Scripts/SaveSystem/Providers/PunchTapeSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/PunchTapeSnapshotProvider.cs new file mode 100644 index 000000000..b23128f63 --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/PunchTapeSnapshotProvider.cs @@ -0,0 +1,52 @@ +using UnityEngine; + +namespace AibisDream.SaveSystem +{ + /// + /// sections["punchTape"]:打孔带收藏列表。RestoreOrder=63,在 fix 之前。 + /// + public class PunchTapeSnapshotProvider : ISyncSnapshotProvider + { + public string SaveId => SnapshotProviderIds.PunchTape; + public int RestoreOrder => 63; + + public object Capture() + { + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + return null; + } + + if (FixSystem.PunchTapeManager.Instance == null) + { + Debug.LogWarning("[PunchTapeSnapshotProvider] PunchTapeManager 未初始化,跳过捕获。"); + return null; + } + + return FixSystem.PunchTapeManager.Instance.CapturePunchTapeSnapshot(); + } + + public void Restore(object dto, SnapshotRestoreContext context) + { + if (dto is not PunchTapeSnapshotDto punchTape) + { + context.Warn("[PunchTapeSnapshotProvider] DTO 类型不匹配,跳过还原。"); + return; + } + + if (!FixSceneSnapshotHelper.IsFixSceneActive()) + { + context.Log("[PunchTapeSnapshotProvider] 当前非维修场景,跳过还原。"); + return; + } + + if (FixSystem.PunchTapeManager.Instance == null) + { + context.Warn("[PunchTapeSnapshotProvider] PunchTapeManager 未初始化,跳过还原。"); + return; + } + + FixSystem.PunchTapeManager.Instance.ApplyPunchTapeSnapshot(punchTape); + } + } +} diff --git a/Assets/Scripts/SaveSystem/Providers/PunchTapeSnapshotProvider.cs.meta b/Assets/Scripts/SaveSystem/Providers/PunchTapeSnapshotProvider.cs.meta new file mode 100644 index 000000000..dfaccf20e --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/PunchTapeSnapshotProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b81daacbefa4094b85fa03aaf2202c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/SaveSystem/Providers/SubwaySnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/SubwaySnapshotProvider.cs new file mode 100644 index 000000000..eca3f4048 --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/SubwaySnapshotProvider.cs @@ -0,0 +1,43 @@ +using System.Collections; +using UnityEngine; + +namespace AibisDream.SaveSystem +{ + /// + /// sections["subway"]:地铁场景宏观状态(SubwayCenter)。 + /// RestoreOrder=64,在 timeline(60) 之后、fix(65) 之前。 + /// + public class SubwaySnapshotProvider : IAsyncSnapshotProvider + { + public string SaveId => SnapshotProviderIds.Subway; + public int RestoreOrder => 64; + + public object Capture() + { + if (SubwayCenter.Instance == null) + { + Debug.LogWarning("[SubwaySnapshotProvider] SubwayCenter 未初始化,跳过捕获。"); + return null; + } + + return SubwayCenter.Instance.CaptureSnapshot(); + } + + public IEnumerator RestoreAsync(object dto, SnapshotRestoreContext context) + { + if (dto is not SubwaySnapshotDto subway) + { + context.Warn("[SubwaySnapshotProvider] DTO 类型不匹配,跳过还原。"); + yield break; + } + + if (SubwayCenter.Instance == null) + { + context.Warn("[SubwaySnapshotProvider] SubwayCenter 未初始化,跳过还原。"); + yield break; + } + + yield return SubwayCenter.Instance.RestoreSnapshot(subway); + } + } +} diff --git a/Assets/Scripts/SaveSystem/Providers/SubwaySnapshotProvider.cs.meta b/Assets/Scripts/SaveSystem/Providers/SubwaySnapshotProvider.cs.meta new file mode 100644 index 000000000..c151f6377 --- /dev/null +++ b/Assets/Scripts/SaveSystem/Providers/SubwaySnapshotProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c4e8f1a23b5d6e7094ac8210d3f7b2e1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/SaveSystem/SaveSnapshot.cs b/Assets/Scripts/SaveSystem/SaveSnapshot.cs index 8f0eb7f96..c02ee0f69 100644 --- a/Assets/Scripts/SaveSystem/SaveSnapshot.cs +++ b/Assets/Scripts/SaveSystem/SaveSnapshot.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using AibisDream.FixSystem; namespace AibisDream.SaveSystem { @@ -15,7 +16,7 @@ namespace AibisDream.SaveSystem /// 存档快照根对象:描述「进入可存节点那一刻」的完整游戏状态。 /// /// 与槽位/文件路径解耦;序列化后写入 JSON。对应需求§3 四分类: - /// anchor(恢复锚点)、sections(宏观 scene/macro + 表现类)、yarnVariables、deepRepair(深度维修,P5)。 + /// anchor(恢复锚点)、sections(表现类 + 维修子模块)、yarnVariables。 /// /// /// 宏观阶段(场景 / 章节 SO / YarnProject)不单独冗余存储,统一以 sections["scene"] / sections["macro"] @@ -44,14 +45,10 @@ namespace AibisDream.SaveSystem public YarnVariablesSnapshot yarnVariables = new(); /// - /// 各子系统表现类快照,key 为 中的稳定 id。 + /// 各子系统快照,key 为 中的稳定 id。 + /// 表现层与维修模块(含 BlockPuzzle / Memory 等)均在此注册,按 RestoreOrder 还原。 /// public Dictionary sections = new(); - - /// - /// 深度维修段。P1 捕获时不写入(null);P5 按模块策略填充。 - /// - public DeepRepairSnapshot deepRepair; } /// @@ -112,8 +109,13 @@ namespace AibisDream.SaveSystem public string slotName; public string actorType; public string stateName; + /// Anima:Animator layer 0 当前状态的 normalizedTime。 + public float stateNormalizedTime; public float alpha; - public float faceParam; + /// AnimaEx:最后一次 set_actor_face 的表情名。 + public string faceName; + /// AnimaEx:Animator "Is Talking" 参数。 + public bool isTalking; } /// sections["audio"]:FMOD 音乐/SFX 终态与 AMB 状态(不记播放进度)。 @@ -174,7 +176,21 @@ namespace AibisDream.SaveSystem public bool isFadeScreenCovered; } - /// sections["fix"]:Fix 场景宏观模式(FixSceneDirector)。 + /// sections["subway"]:地铁场景宏观状态(SubwayCenter / OutSideState)。 + [Serializable] + public class SubwaySnapshotDto + { + /// 枚举名。 + public string state; + + /// 状态参数(预留,当前各 State 未使用)。 + public string args; + + /// 当前新闻图片 Addressable id(Sprite/News[{id}] 中的 id)。 + public string newsSpriteId; + } + + /// sections["fix"]:Fix 场景宏观 Cue(FixSceneDirector),不含面板壳层。 [Serializable] public class FixSnapshotDto { @@ -183,18 +199,48 @@ namespace AibisDream.SaveSystem /// 状态参数(如 BodyModule 的 ModuleState 字符串)。 public string args; + } - /// BodyModule 当前分组(Body/Head)。 - public string moduleState; - - /// EyeSystem 当前颜色阶段。 - public string eyeColorState; - - /// FixPanelSystem 是否已启动。 + /// sections["fixPanel"]:FixPanel 壳层 + 线缆/插头状态。 + [Serializable] + public class FixPanelSnapshotDto + { public bool isSystemOn; - /// RepairSystemManager 当前激活的系统类型名。 + /// RepairSystemManager 当前激活系统类型名;空则默认 BodyModuleSystem。 public string currentRepairSystemType; + + public bool isCableRetracted; + + /// 插头插入的 BodyModule 名(data.moduleName);null/空 = 未插入。 + public string pluggedModuleName; + } + + /// sections["bodyModule"]:插线模块持久物理状态。 + [Serializable] + public class BodyModuleSnapshotDto + { + /// 枚举名(Body / Head)。 + public string moduleState; + public bool isUFCoverRemoved; + public bool isUFRemoved; + public bool isColorRemoved; + public bool isChipRemoved; + } + + /// sections["eye"]:Eye 维修叙事阶段。 + [Serializable] + public class EyeSnapshotDto + { + /// Eye 颜色阶段枚举名。 + public string eyeColorState; + } + + /// sections["punchTape"]:打孔带收藏列表。 + [Serializable] + public class PunchTapeSnapshotDto + { + public List favorites = new(); } #endregion diff --git a/Assets/Scripts/SaveSystem/SnapshotRegistry.cs b/Assets/Scripts/SaveSystem/SnapshotRegistry.cs index eaedfc5c0..1851ee4d3 100644 --- a/Assets/Scripts/SaveSystem/SnapshotRegistry.cs +++ b/Assets/Scripts/SaveSystem/SnapshotRegistry.cs @@ -15,7 +15,12 @@ namespace AibisDream.SaveSystem public const string Audio = "audio"; public const string Timeline = "timeline"; public const string Screen = "screen"; + public const string Subway = "subway"; + public const string PunchTape = "punchTape"; public const string Fix = "fix"; + public const string FixPanel = "fixPanel"; + public const string BodyModule = "bodyModule"; + public const string Eye = "eye"; /// /// P1 应参与 Capture 的 provider 清单。 @@ -24,7 +29,7 @@ namespace AibisDream.SaveSystem /// public static readonly string[] RequiredForCapture = { - Environment, Actor, Audio, Timeline, Fix, Screen + Environment, Actor, Audio, Timeline, Subway, Fix, Screen }; } @@ -48,7 +53,12 @@ namespace AibisDream.SaveSystem Register(new ActorSnapshotProvider()); Register(new AudioSnapshotProvider()); Register(new TimelineSnapshotProvider()); + Register(new SubwaySnapshotProvider()); + Register(new PunchTapeSnapshotProvider()); Register(new FixSnapshotProvider()); + Register(new FixPanelSnapshotProvider()); + Register(new BodyModuleSnapshotProvider()); + Register(new EyeSnapshotProvider()); Register(new ScreenSnapshotProvider()); } diff --git a/Assets/Scripts/SaveSystem/SnapshotRestore.cs b/Assets/Scripts/SaveSystem/SnapshotRestore.cs index d1e012328..91902f622 100644 --- a/Assets/Scripts/SaveSystem/SnapshotRestore.cs +++ b/Assets/Scripts/SaveSystem/SnapshotRestore.cs @@ -36,12 +36,6 @@ namespace AibisDream.SaveSystem context.Log("Phase 2: Restore providers (ordered by RestoreOrder)"); yield return RestoreProvidersInOrder(snapshot, context); - - if (snapshot.deepRepair != null && snapshot.deepRepair.sections != null - && snapshot.deepRepair.sections.Count > 0) - { - context.Warn("DeepRepair 段尚未实现,已跳过(P5)。"); - } } /// @@ -209,7 +203,12 @@ namespace AibisDream.SaveSystem SnapshotProviderIds.Actor => jobj.ToObject(), SnapshotProviderIds.Audio => jobj.ToObject(), SnapshotProviderIds.Timeline => jobj.ToObject(), + SnapshotProviderIds.Subway => jobj.ToObject(), + SnapshotProviderIds.PunchTape => jobj.ToObject(), SnapshotProviderIds.Fix => jobj.ToObject(), + SnapshotProviderIds.FixPanel => jobj.ToObject(), + SnapshotProviderIds.BodyModule => jobj.ToObject(), + SnapshotProviderIds.Eye => jobj.ToObject(), SnapshotProviderIds.Screen => jobj.ToObject(), _ => jobj };