diff --git a/Assets/Prefabs/FixSystemPrefabs/维修面板.prefab b/Assets/Prefabs/FixSystemPrefabs/维修面板.prefab index 2e5c8011e..d1f363213 100644 --- a/Assets/Prefabs/FixSystemPrefabs/维修面板.prefab +++ b/Assets/Prefabs/FixSystemPrefabs/维修面板.prefab @@ -3329,6 +3329,8 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 895638590458225172} + - {fileID: 910000000000000002} + - {fileID: 910000000000000004} - {fileID: 2161354415544340870} - {fileID: 930245164819552547} - {fileID: 1768036090393189231} @@ -3347,7 +3349,74 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: taskRoot: {fileID: 895638590458225172} + hiddenAnchor: {fileID: 910000000000000002} + shownAnchor: {fileID: 910000000000000004} taskListParent: {fileID: 2078109273994026669} + taskItemPrefab: {fileID: 910000000000000005, guid: 63913c6ee20b38644bf16a505dd300f2, type: 3} + animationDuration: 1 + animationEase: 6 +--- !u!1 &910000000000000001 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 910000000000000002} + m_Layer: 0 + m_Name: TaskPanelHiddenAnchor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &910000000000000002 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 910000000000000001} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -10.32, y: 3.6511626, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4912059846133439537} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &910000000000000003 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 910000000000000004} + m_Layer: 0 + m_Name: TaskPanelShownAnchor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &910000000000000004 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 910000000000000003} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -7.12, y: 3.6511626, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4912059846133439537} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2080789105181032567 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Prefabs/UIprefab/TaskItem.prefab b/Assets/Prefabs/UIprefab/TaskItem.prefab index 115f25811..1ff5b5afc 100644 --- a/Assets/Prefabs/UIprefab/TaskItem.prefab +++ b/Assets/Prefabs/UIprefab/TaskItem.prefab @@ -11,6 +11,7 @@ GameObject: - component: {fileID: 4156240678385147197} - component: {fileID: 3152720343440710739} - component: {fileID: 351969066121265812} + - component: {fileID: 910000000000000005} m_Layer: 0 m_Name: TaskItem m_TagString: Untagged @@ -171,3 +172,16 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_renderer: {fileID: 3152720343440710739} m_maskType: 0 +--- !u!114 &910000000000000005 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5370176113195622641} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4fe106c891d8f8b448b72b805e707075, type: 3} + m_Name: + m_EditorClassIdentifier: + label: {fileID: 351969066121265812} diff --git a/Assets/Scripts/AssetRefs/ConstRef.cs b/Assets/Scripts/AssetRefs/ConstRef.cs index 3c1912efa..c8597eac8 100644 --- a/Assets/Scripts/AssetRefs/ConstRef.cs +++ b/Assets/Scripts/AssetRefs/ConstRef.cs @@ -56,7 +56,6 @@ namespace AibisDream.Utility public const string CutLinePrefabName = "Prefab/CutLine"; - public const string TaskItemName = "Prefab/TaskItem"; public const string ActorPrefabName = "Prefab/ActorAnima"; public const string SpriteActorPrefabName = "Prefab/SpriteActor"; diff --git a/Assets/Scripts/FixSystemNew/Screen System/Core/FixPanelSystem.cs b/Assets/Scripts/FixSystemNew/Screen System/Core/FixPanelSystem.cs index 8d320af29..1392ed60d 100644 --- a/Assets/Scripts/FixSystemNew/Screen System/Core/FixPanelSystem.cs +++ b/Assets/Scripts/FixSystemNew/Screen System/Core/FixPanelSystem.cs @@ -102,7 +102,7 @@ namespace AibisDream.FixSystem isStartupPrepared = true; targetLightState = LightState.Normal; ApplySystemOnImmediate(false); - taskPanel?.HidePanelImmediate(); + taskPanel?.SetVisible(false, immediate: true); } /// @@ -205,7 +205,7 @@ namespace AibisDream.FixSystem if (taskPanel != null) { - taskPanel.CaptureSnapshot(out dto.isTaskPanelVisible, dto.tasks); + taskPanel.CaptureSnapshot(out dto.isTaskPanelRequestedVisible, dto.tasks); } return dto; @@ -235,7 +235,7 @@ namespace AibisDream.FixSystem GetRepairPanel()?.ApplyCableSnapshot(dto.isCableRetracted, dto.pluggedModuleName); - taskPanel?.ApplySnapshot(dto.isTaskPanelVisible, dto.tasks, immediate: true); + taskPanel?.ApplySnapshot(dto.isTaskPanelRequestedVisible, dto.tasks); } /// 读档终态:跳过 StartAllSystems 动画,直接写面板壳层状态。 diff --git a/Assets/Scripts/FixSystemNew/Screen System/FixPanel/TaskPanel.cs b/Assets/Scripts/FixSystemNew/Screen System/FixPanel/TaskPanel.cs index ecdf3dee3..21d4f7aa5 100644 --- a/Assets/Scripts/FixSystemNew/Screen System/FixPanel/TaskPanel.cs +++ b/Assets/Scripts/FixSystemNew/Screen System/FixPanel/TaskPanel.cs @@ -1,121 +1,153 @@ -using System.Collections.Generic; -using AibisDream.Framework; +using System.Collections.Generic; using AibisDream.SaveSystem; -using AibisDream.Utility; -using TMPro; +using DG.Tweening; using UnityEngine; using UnityEngine.UI; -using DG.Tweening; namespace AibisDream.FixSystem { public class TaskPanel : MonoBehaviour, ILineView { - private const float HiddenLocalX = -10.32f; - private const float ShownLocalX = -7.12f; - - #region 索引 - + [Header("面板位置")] [SerializeField] private Transform taskRoot; - [SerializeField] private Transform taskListParent; - private GameObject _taskItemPrefab; - private readonly Dictionary _taskDict = new(); + [SerializeField] private Transform hiddenAnchor; + [SerializeField] private Transform shownAnchor; - #endregion + [Header("任务列表")] + [SerializeField] private RectTransform taskListParent; + [SerializeField] private TaskItem taskItemPrefab; + + [Header("动画")] + [SerializeField, Min(0f)] private float animationDuration = 1f; + [SerializeField] private Ease animationEase = Ease.OutQuad; + + private readonly List _tasks = new(); + private Tween _moveTween; + private bool _requestedVisible; + private bool _temporarilyHidden; + + public bool RequestedVisible => _requestedVisible; + public bool EffectiveVisible => _requestedVisible && !_temporarilyHidden; + + private sealed class TaskEntry + { + public string LineId; + public string Text; + public TaskItem View; + } private void Awake() { - _taskItemPrefab = ResourceKit.LoadAssetSync(ConstRef.TaskItemName); + ValidateConfiguration(); } - public bool IsPanelVisible => Mathf.Approximately(taskRoot.localPosition.x, ShownLocalX); - - public void ShowPanel() + private void OnEnable() { - taskRoot.DOLocalMoveX(ShownLocalX, 1f); + RefreshVisual(immediate: true); } - /// 动画收起面板(与 ShowPanel 对称)。 - public void HidePanel() + private void OnDisable() { - taskRoot.DOKill(); - taskRoot.DOLocalMoveX(HiddenLocalX, 1f); + KillMoveTween(); } - public void HidePanelImmediate() + public void SetVisible(bool visible, bool immediate = false) { - taskRoot.DOKill(); - SetPanelVisibleImmediate(false); + if (_requestedVisible == visible) + { + if (immediate) + { + RefreshVisual(immediate: true); + } + + return; + } + + var wasEffectivelyVisible = EffectiveVisible; + _requestedVisible = visible; + + if (wasEffectivelyVisible != EffectiveVisible || immediate) + { + RefreshVisual(immediate); + } } - private bool _restoreVisibleAfterCollapse; - - /// 临时收起面板(如进入 Memory 模式),记录之前是否展开,供 RestoreCollapsedPanel 恢复。 - public void CollapsePanelTemporary(bool immediate = false) + public void SetTemporarilyHidden(bool hidden, bool immediate = false) { - _restoreVisibleAfterCollapse = IsPanelVisible; - if (!_restoreVisibleAfterCollapse) return; - if (immediate) HidePanelImmediate(); - else HidePanel(); - } + if (_temporarilyHidden == hidden) + { + if (immediate) + { + RefreshVisual(immediate: true); + } - /// 按 CollapsePanelTemporary 记录的状态恢复面板(之前未展开则不做任何事)。 - public void RestoreCollapsedPanel() - { - if (!_restoreVisibleAfterCollapse) return; - _restoreVisibleAfterCollapse = false; - ShowPanel(); + return; + } + + var wasEffectivelyVisible = EffectiveVisible; + _temporarilyHidden = hidden; + + if (wasEffectivelyVisible != EffectiveVisible || immediate) + { + RefreshVisual(immediate); + } } public void CompleteTask(string lineId) { - if (_taskDict.Remove(lineId, out var taskItem)) + var index = FindTaskIndex(lineId); + if (index < 0) { - Destroy(taskItem); + return; } + + DestroyTaskView(_tasks[index].View); + _tasks.RemoveAt(index); + MarkTaskLayoutDirty(); } public void ClearTasks() { - foreach (var task in _taskDict) + foreach (var task in _tasks) { - Destroy(task.Value); + DestroyTaskView(task.View); } - _taskDict.Clear(); + + _tasks.Clear(); + MarkTaskLayoutDirty(); } public void RunLine(LineSyncToken token) { - if (_taskDict.ContainsKey(token.lineInfo.lineId)) + var lineId = token.lineInfo.lineId; + if (string.IsNullOrWhiteSpace(lineId)) { - EnumEventSystem.Global.Send(DialogEventEnum.LineShown); - token.ForceAdvance(); - return; + Debug.LogWarning($"[{nameof(TaskPanel)}] 忽略缺少 lineId 的任务文本。", this); + } + else if (FindTaskIndex(lineId) < 0) + { + AddTaskItem(lineId, token.lineInfo.lineText); } - AddTaskItem(token.lineInfo.lineId, $"\u25cf {token.lineInfo.lineText}"); - token.TextStart(); - token.TextShown(); - token.ForceAdvance(); + CompleteLineImmediately(token); } - public void CaptureSnapshot(out bool isPanelVisible, List tasks) + public void CaptureSnapshot(out bool requestedVisible, List tasks) { - isPanelVisible = IsPanelVisible; + requestedVisible = RequestedVisible; tasks.Clear(); - foreach (var kv in _taskDict) + foreach (var task in _tasks) { - var text = kv.Value.GetComponent()?.text ?? string.Empty; tasks.Add(new TaskEntrySnapshotDto { - lineId = kv.Key, - text = text + lineId = task.LineId, + text = task.Text }); } } - public void ApplySnapshot(bool isPanelVisible, List tasks, bool immediate) + public void ApplySnapshot(bool requestedVisible, List tasks) { ClearTasks(); @@ -123,40 +155,208 @@ namespace AibisDream.FixSystem { foreach (var entry in tasks) { - if (string.IsNullOrEmpty(entry.lineId)) + if (entry == null || string.IsNullOrWhiteSpace(entry.lineId)) { + Debug.LogWarning($"[{nameof(TaskPanel)}] 跳过缺少 lineId 的任务快照。", this); continue; } - AddTaskItem(entry.lineId, entry.text); + if (FindTaskIndex(entry.lineId) >= 0) + { + Debug.LogWarning($"[{nameof(TaskPanel)}] 跳过重复任务快照 '{entry.lineId}'。", this); + continue; + } + + AddTaskItem(entry.lineId, entry.text ?? string.Empty); } } - if (immediate) + SetVisible(requestedVisible, immediate: true); + } + + private void RefreshVisual(bool immediate) + { + KillMoveTween(); + + var visible = EffectiveVisible; + if (visible) { - taskRoot.DOKill(); - SetPanelVisibleImmediate(isPanelVisible); + SetTaskTextVisible(true); + MarkTaskLayoutDirty(); } - else if (isPanelVisible) + + if (taskRoot == null || hiddenAnchor == null || shownAnchor == null) { - ShowPanel(); + if (!visible) + { + SetTaskTextVisible(false); + } + + return; } + + var targetPosition = visible + ? shownAnchor.localPosition + : hiddenAnchor.localPosition; + + if (immediate || animationDuration <= 0f) + { + taskRoot.localPosition = targetPosition; + if (!visible) + { + SetTaskTextVisible(false); + } + + return; + } + + var tween = taskRoot + .DOLocalMove(targetPosition, animationDuration) + .SetEase(animationEase); + + _moveTween = tween; + tween.OnComplete(() => + { + if (_moveTween != tween) + { + return; + } + + _moveTween = null; + if (!EffectiveVisible) + { + SetTaskTextVisible(false); + } + }); } private void AddTaskItem(string lineId, string text) { - var taskItem = Instantiate(_taskItemPrefab, taskListParent); - var task = taskItem.GetComponent(); - task.text = text; - _taskDict[lineId] = taskItem; - LayoutRebuilder.ForceRebuildLayoutImmediate(taskListParent.GetComponent()); + TaskItem view = null; + if (taskItemPrefab == null || taskListParent == null) + { + Debug.LogError( + $"[{nameof(TaskPanel)}] 无法创建任务 '{lineId}':TaskItem Prefab 或任务列表父节点未配置。", + this); + } + else + { + view = Instantiate(taskItemPrefab, taskListParent); + view.SetText(text); + } + + _tasks.Add(new TaskEntry + { + LineId = lineId, + Text = text, + View = view + }); + + MarkTaskLayoutDirty(); } - private void SetPanelVisibleImmediate(bool visible) + private int FindTaskIndex(string lineId) { - var pos = taskRoot.localPosition; - pos.x = visible ? ShownLocalX : HiddenLocalX; - taskRoot.localPosition = pos; + if (string.IsNullOrEmpty(lineId)) + { + return -1; + } + + for (var i = 0; i < _tasks.Count; i++) + { + if (_tasks[i].LineId == lineId) + { + return i; + } + } + + return -1; + } + + private static void CompleteLineImmediately(LineSyncToken token) + { + token.TextStart(); + token.TextShown(); + token.ForceAdvance(); + } + + private static void DestroyTaskView(TaskItem view) + { + if (view == null) + { + return; + } + + view.gameObject.SetActive(false); + Destroy(view.gameObject); + } + + private void SetTaskTextVisible(bool visible) + { + if (taskListParent != null && taskListParent.gameObject.activeSelf != visible) + { + taskListParent.gameObject.SetActive(visible); + } + } + + private void MarkTaskLayoutDirty() + { + if (taskListParent != null) + { + LayoutRebuilder.MarkLayoutForRebuild(taskListParent); + } + } + + private void KillMoveTween() + { + if (_moveTween == null) + { + return; + } + + var tween = _moveTween; + _moveTween = null; + tween.Kill(); + } + + private void ValidateConfiguration() + { + if (taskRoot == null) + { + Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置 Task Root。", this); + } + + if (hiddenAnchor == null) + { + Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置 Hidden Anchor。", this); + } + + if (shownAnchor == null) + { + Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置 Shown Anchor。", this); + } + + if (taskRoot != null + && hiddenAnchor != null + && shownAnchor != null + && (taskRoot.parent != hiddenAnchor.parent || taskRoot.parent != shownAnchor.parent)) + { + Debug.LogError($"[{nameof(TaskPanel)}] {name} 的 Task Root 与两个 Anchor 必须拥有同一个父节点。", this); + } + + if (taskListParent == null) + { + Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置任务列表父节点。", this); + } + + if (taskItemPrefab == null) + { + Debug.LogError($"[{nameof(TaskPanel)}] {name} 未配置 TaskItem Prefab。", this); + } + else if (!taskItemPrefab.IsConfigured) + { + Debug.LogError($"[{nameof(TaskPanel)}] {name} 的 TaskItem Prefab 未绑定文本组件。", this); + } } } } diff --git a/Assets/Scripts/FixSystemNew/Screen System/ScreenYarnCommand.cs b/Assets/Scripts/FixSystemNew/Screen System/ScreenYarnCommand.cs index a7672371b..f6015e7fd 100644 --- a/Assets/Scripts/FixSystemNew/Screen System/ScreenYarnCommand.cs +++ b/Assets/Scripts/FixSystemNew/Screen System/ScreenYarnCommand.cs @@ -37,7 +37,7 @@ namespace AibisDream [YarnCommand("show_task_panel")] public static void ShowTaskPanel() { - TaskPanel.ShowPanel(); + TaskPanel?.SetVisible(true); } #endregion diff --git a/Assets/Scripts/FixSystemNew/Screen System/TaskItem.cs b/Assets/Scripts/FixSystemNew/Screen System/TaskItem.cs index e5bccdbcb..7350193eb 100644 --- a/Assets/Scripts/FixSystemNew/Screen System/TaskItem.cs +++ b/Assets/Scripts/FixSystemNew/Screen System/TaskItem.cs @@ -1,13 +1,23 @@ using TMPro; using UnityEngine; -namespace AibisDream +namespace AibisDream.FixSystem { public class TaskItem : MonoBehaviour { + [SerializeField] private TMP_Text label; + + public bool IsConfigured => label != null; + public void SetText(string value) { - GetComponent().text = $"\u25cf {value}"; + if (label == null) + { + Debug.LogError($"[{nameof(TaskItem)}] {name} 未绑定文本组件。", this); + return; + } + + label.text = $"\u25cf {value}"; } } } diff --git a/Assets/Scripts/FixSystemNew/States/PeipeiFixCues.cs b/Assets/Scripts/FixSystemNew/States/PeipeiFixCues.cs index 028b913e4..a71fb5793 100644 --- a/Assets/Scripts/FixSystemNew/States/PeipeiFixCues.cs +++ b/Assets/Scripts/FixSystemNew/States/PeipeiFixCues.cs @@ -106,7 +106,7 @@ namespace AibisDream.FixSystem float duration = 0.5f; var memorySystem = FixSystemCenter.SystemDic.Get(); // 进入Memory前收起Task面板 - FixPanelSystem.Instance.TaskPanel?.CollapsePanelTemporary(); + FixPanelSystem.Instance.TaskPanel?.SetTemporarilyHidden(true); // 先切BodyModule if (!CameraKit.Instance.EqualToCameraState(CameraEnum.BodyModule)) { @@ -126,7 +126,7 @@ namespace AibisDream.FixSystem { var memorySystem = FixSystemCenter.SystemDic.Get(); - FixPanelSystem.Instance.TaskPanel?.CollapsePanelTemporary(immediate: true); + FixPanelSystem.Instance.TaskPanel?.SetTemporarilyHidden(true, immediate: true); yield return memorySystem.DropMemoryProjector(); FixPanelSystem.GetRepairPanel()?.PullUpCable(); yield return CameraKit.Instance.SwitchCamera(CameraEnum.Memory, true); @@ -145,7 +145,7 @@ namespace AibisDream.FixSystem yield return CameraKit.Instance.SwitchCamera(CameraEnum.BodyModule); memorySystem.PullMemoryProjector(); // 退出Memory后恢复Task面板 - FixPanelSystem.Instance.TaskPanel?.RestoreCollapsedPanel(); + FixPanelSystem.Instance.TaskPanel?.SetTemporarilyHidden(false); } } } diff --git a/Assets/Scripts/SaveSystem/README.md b/Assets/Scripts/SaveSystem/README.md index c0c7b793d..59d32be19 100644 --- a/Assets/Scripts/SaveSystem/README.md +++ b/Assets/Scripts/SaveSystem/README.md @@ -146,7 +146,7 @@ Postflight 校验 Provider 使用 `ISyncSnapshotProvider` 或 `IAsyncSnapshotProvider` 显式声明同步/异步恢复;异步 Provider 必须把等待过程返回给编排层,禁止内部 fire-and-forget。 -## 快照 JSON 结构(schemaVersion = 1) +## 快照 JSON 结构(schemaVersion = 2) | 字段 | 含义 | | --- | --- | @@ -191,11 +191,11 @@ Fix 场景各 State 的 `Enter()` 通常包含相机过渡、Timeline 播放、F | --- | --- | --- | --- | | `punchTape` | `PunchTapeSnapshotDto` | 63 | 打孔带收藏 | | `fix` | `FixSnapshotDto` | 65 | FixSceneDirector 宏观模式(state + args) | -| `fixPanel` | `FixPanelSnapshotDto` | 66 | FixPanel 壳层 + 线缆/插头 + TaskPanel(展开状态与任务列表) | +| `fixPanel` | `FixPanelSnapshotDto` | 66 | FixPanel 壳层 + 线缆/插头 + TaskPanel(请求展开状态与任务列表) | | `bodyModule` | `BodyModuleSnapshotDto` | 67 | 插线模块物理态 | | `eye` | `EyeSnapshotDto` | 68 | Eye 叙事阶段 | -**还原顺序**:punchTape → fix(cue)→ fixPanel → bodyModule → eye → showcase → day2SleepPresentation → playTool → screen。`fixPanel` 须在 `fix` 之后,以覆盖 Cue `EnterImmediate` 中的 `ResetPlug`;D2 动态表现须在通用 Showcase 图片之后恢复,才能叠加虚焦、缩放或场景专属动画。 +**还原顺序**:punchTape → fix(cue)→ fixPanel → bodyModule → eye → showcase → day2SleepPresentation → playTool → screen。`fixPanel` 须在 `fix` 之后,以覆盖 Cue `EnterImmediate` 中的 `ResetPlug`,并让 Memory Cue 先建立 TaskPanel 临时隐藏状态;D2 动态表现须在通用 Showcase 图片之后恢复,才能叠加虚焦、缩放或场景专属动画。 **维修场景门控**(`FixSceneSnapshotHelper`):上述 section 仅在 `FixSystemCenter.Instance != null` 时 Capture/Restore。 diff --git a/Assets/Scripts/SaveSystem/SaveSnapshot.cs b/Assets/Scripts/SaveSystem/SaveSnapshot.cs index daba7a69b..7e9f7d46b 100644 --- a/Assets/Scripts/SaveSystem/SaveSnapshot.cs +++ b/Assets/Scripts/SaveSystem/SaveSnapshot.cs @@ -9,7 +9,7 @@ namespace AibisDream.SaveSystem /// public static class SaveSnapshotSchema { - public const int CurrentVersion = 1; + public const int CurrentVersion = 2; } /// @@ -269,7 +269,7 @@ namespace AibisDream.SaveSystem /// Yarn 行 id,如 line:01dade5 public string lineId; - /// 存档时 UI 显示的完整文本(含 前缀)。 + /// 存档时已完成 substitutions 的本地化文本,不含 UI 圆点前缀。 public string text; } @@ -290,8 +290,8 @@ namespace AibisDream.SaveSystem /// 插头插入的 BodyModule 名(data.moduleName);null/空 = 未插入。 public string pluggedModuleName; - /// TaskPanel 是否处于展开位(show_task_panel 后)。 - public bool isTaskPanelVisible; + /// 剧情是否要求 TaskPanel 展开;Memory 等临时隐藏状态不写入存档。 + public bool isTaskPanelRequestedVisible; /// TaskPanel 当前未完成任务列表(保持插入顺序)。 public List tasks = new();