using System; using System.Collections.Generic; using AibisDream.FixSystem; using Newtonsoft.Json; namespace AibisDream.SaveSystem { /// /// 当前快照 schema 的版本号。结构变更时递增。 /// public static class SaveSnapshotSchema { public const int CurrentVersion = 2; } /// /// 存档快照根对象:描述「进入可存节点那一刻」的完整游戏状态。 /// /// 与槽位/文件路径解耦;序列化后写入 JSON。对应需求§3 四分类: /// anchor(恢复锚点)、sections(表现类 + 维修子模块)、yarnVariables。 /// /// /// 宏观阶段(场景 / 章节 SO / YarnProject)不单独冗余存储,统一以 sections["scene"] / sections["macro"] /// 为唯一来源;槽位 UI 等需要摘要时通过 现算派生,避免重复字段漂移。 /// /// [Serializable] public class SaveSnapshot { /// 快照结构版本,对应 public int schemaVersion = SaveSnapshotSchema.CurrentVersion; /// 保存时的 Application.version public string gameVersion; /// 保存时间(本地),格式 yyyy-MM-dd HH:mm:ss public string savedAt; /// 当前场景(恢复的第一步)。 public SceneSnapshotDto scene = new(); /// 恢复锚点:章节、YarnProject、来源节点与显式恢复动作。 public AnchorSnapshot anchor = new(); /// 全部 Yarn 运行时变量(来自 )。 public YarnVariablesSnapshot yarnVariables = new(); /// /// 各子系统快照,key 为 中的稳定 id。 /// 表现层与维修模块(含 BlockPuzzle / Memory 等)均在此注册,按 RestoreOrder 还原。 /// public Dictionary sections = new(); } /// /// 恢复锚点:节点名始终记录存档来源;是否重新进入节点由 /// 独立决定。 /// [Serializable] public class AnchorSnapshot { /// 当前章节 TalkSceneSO 名称,由读档预检解析为目标章节。 public string sceneSoName; public string yarnProjectId; public string nodeName; [JsonProperty(Required = Required.Always)] public bool startDialogueOnRestore; } /// Yarn 变量三分组,与 Yarn Spinner 支持的类型一致。 [Serializable] public class YarnVariablesSnapshot { public Dictionary floats = new(); public Dictionary strings = new(); public Dictionary bools = new(); } #region Provider DTOs /// sections["scene"]:当前加载的场景。 [Serializable] public class SceneSnapshotDto { public string sceneName; } /// sections["env"]:环境时间/天气。 [Serializable] public class EnvironmentSnapshotDto { /// 枚举名。 public string curTime; /// 枚举名。 public string curWeather; public bool isInitialized; } /// sections["actor"]:场景中所有角色的逐项状态。 [Serializable] public class ActorSnapshotDto { public List actors = new(); } /// 单个角色的快照条目。 [Serializable] public class ActorEntrySnapshotDto { public string actorName; public string slotName; public string actorType; public string stateName; /// Anima:Animator layer 0 当前状态的 normalizedTime。 public float stateNormalizedTime; public float alpha; /// AnimaEx:最后一次 set_actor_face 的表情名。 public string faceName; /// AnimaEx:Animator "Is Talking" 参数。 public bool isTalking; } /// /// sections["audio"]:FMOD 音乐/SFX 终态、AMB 状态与 Yarn 全局参数 finalValue /// (不记 Event 播放进度)。 /// [Serializable] public class AudioSnapshotDto { /// key → FMOD event path。 public Dictionary musicPaths = new(); public Dictionary sfxPaths = new(); /// 枚举名。 public string ambState; /// 由 Yarn set_global_param 修改过的参数名 → 捕获时的 FMOD finalValue。 public Dictionary yarnGlobalParameters = new(); } /// /// Timeline 终/初态标记(决策 D1.1:不按时间轴逐帧还原)。 /// 序列化进 JSON 时使用字符串形式存入 。 /// public enum TimelinePlaybackPhase { AtStart, AtEnd, /// 从未 Evaluate 过 Timeline 绑定(untouched);非播放中途 Stop。 Stopped } /// sections["timeline"]:各 PlayableDirector 的终/初态。 [Serializable] public class TimelineSnapshotDto { public List entries = new(); } [Serializable] public class TimelineEntrySnapshotDto { public string directorName; public bool isActive; /// 的字符串名。 public string phase; /// 若由 Addressables 加载 Timeline,记录 key;否则为空。 public string loadedAddressableKey; /// 是否曾通过 Play/Reset/RestoreAtEnd 等路径 Evaluate 过 Timeline。 public bool hasEverAppliedPlayback; } /// sections["screen"]:屏幕饱和度与演出淡入淡出遮罩。 [Serializable] public class ScreenSnapshotDto { /// Post-process Volume weight(0=正常,1=完全褪色)。 public float saturationWeight; /// /// 全局遮罩终态:true = 画面被完全盖住; /// false = 画面可见(FadeOut 完成或遮罩未启用)。不记录转场中间 alpha。 /// public bool isFadeScreenCovered; } /// sections["playTool"]:常驻演出 UI 中的物品图与全屏图终态。 [Serializable] public class PlayToolSnapshotDto { public bool isObjVisible; public string objPicName; public bool objIsLocalized; public bool isFullScreenVisible; public string fullScreenPicName; public bool fullScreenIsLocalized; } public enum ShowcaseDisplayMode { None, Small, Large } /// sections["showcase"]:梦境通用静态图片与背景终态。 [Serializable] public class ShowcaseSnapshotDto { public string displayMode; public string picName; public bool isBackgroundVisible; } public enum Day2SleepPresentationMode { None, SmallMotion, Kite, LargeEffects } /// sections["day2SleepPresentation"]:D2 Sleep 动态表现语义终态。 [Serializable] public class Day2SleepPresentationSnapshotDto { public string mode; public string picName; public string motionProfile; public float kiteHeight; public int kiteWind; public float kiteTurbulence; public bool largeAnimationActive; public string largeAnimationPrefix; public int largeAnimationFrameCount; public float largeAnimationSecondsPerFrame; public float largeBlurAmount; public float largeBlurSize; public float largeScaleMultiplier = 1f; public float largeAlpha = 1f; } /// 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 { /// 枚举名。 public string state; /// 状态参数(如 BodyModule 的 ModuleState 字符串)。 public string args; } /// TaskPanel 单条任务条目快照。 [Serializable] public class TaskEntrySnapshotDto { /// Yarn 行 id,如 line:01dade5 public string lineId; /// 存档时已完成 substitutions 的本地化文本,不含 UI 圆点前缀。 public string text; } /// sections["fixPanel"]:FixPanel 壳层 + 线缆/插头 + TaskPanel 状态。 [Serializable] public class FixPanelSnapshotDto { public bool isSystemOn; /// 当前目标灯态;旧存档缺失时按 Normal 处理。 public string lightState; /// RepairSystemManager 当前激活系统类型名;空则默认 BodyModuleSystem。 public string currentRepairSystemType; public bool isCableRetracted; /// 插头插入的 BodyModule 名(data.moduleName);null/空 = 未插入。 public string pluggedModuleName; /// 剧情是否要求 TaskPanel 展开;Memory 等临时隐藏状态不写入存档。 public bool isTaskPanelRequestedVisible; /// TaskPanel 当前未完成任务列表(保持插入顺序)。 public List tasks = new(); } /// sections["bodyModule"]:插线模块持久物理状态。 [Serializable] public class BodyModuleSnapshotDto { /// 枚举名(Body / Head)。 public string moduleState; public bool isUFCoverRemoved; public bool isUFRemoved; public bool isColorRemoved; public bool isChipRemoved; /// 高亮中的模块名(data.moduleName)。 public List highlightedModules = new(); /// 报警中的模块名(data.moduleName)。 public List alarmedModules = new(); /// 模块名 → 枚举名;旧存档缺失时按 Normal 处理。 public Dictionary moduleConditions = new(); } /// sections["eye"]:Eye 维修叙事阶段。 [Serializable] public class EyeSnapshotDto { /// Eye 颜色阶段枚举名。 public string eyeColorState; } /// sections["punchTape"]:打孔带收藏列表。 [Serializable] public class PunchTapeSnapshotDto { public List favorites = new(); } #endregion /// /// 从 根对象直接读取的宏观摘要(场景 / 章节 SO / YarnProject)。 /// /// scene 与 anchor 已提升到根对象,无需再从 sections 字典派生。 /// 槽位 / 「继续游戏」等 UI 在需要展示时调用 即可。 /// /// public readonly struct SaveSnapshotSummary { /// Addressable 场景 key(来自 )。 public readonly string SceneName; /// 当前 TalkSceneSO 的 asset 名称(来自 )。 public readonly string SceneSoName; /// 当前 YarnProject 的 name(来自 )。 public readonly string YarnProjectId; public SaveSnapshotSummary(string sceneName, string sceneSoName, string yarnProjectId) { SceneName = sceneName; SceneSoName = sceneSoName; YarnProjectId = yarnProjectId; } /// 从快照根对象直接读取宏观摘要。 public static SaveSnapshotSummary From(SaveSnapshot snapshot) { if (snapshot == null) { return new SaveSnapshotSummary(null, null, null); } var sceneName = snapshot.scene?.sceneName; var sceneSoName = snapshot.anchor?.sceneSoName; var yarnProjectId = snapshot.anchor?.yarnProjectId; return new SaveSnapshotSummary(sceneName, sceneSoName, yarnProjectId); } } }