diff --git a/Assets/Scripts/Dialog System/BaseYarnCommand.cs b/Assets/Scripts/Dialog System/BaseYarnCommand.cs index efd9e26d8..faf3ab03b 100644 --- a/Assets/Scripts/Dialog System/BaseYarnCommand.cs +++ b/Assets/Scripts/Dialog System/BaseYarnCommand.cs @@ -15,7 +15,7 @@ namespace AibisDream public static void NextYarn(string exitName = null) { EnumEventSystem.Global.Send(EventEnum.NextYarn); - StorageSystem.Instance.ClearLocal(); + YarnVariableStorage.Instance.ClearLocal(); DialogController.Instance.StartCoroutine(GameManager.Instance.NextSceneSo(exitName)); } diff --git a/Assets/Scripts/Dialog System/DialogController.cs b/Assets/Scripts/Dialog System/DialogController.cs index cd2dd40ce..816eae864 100644 --- a/Assets/Scripts/Dialog System/DialogController.cs +++ b/Assets/Scripts/Dialog System/DialogController.cs @@ -83,7 +83,7 @@ namespace AibisDream public void StopDialog() { _ = _dialogueRunner.Stop(); - StorageSystem.Instance.Clear(); + YarnVariableStorage.Instance.Clear(); DialogUIManager.Instance.HideDialog(); } diff --git a/Assets/Scripts/FixSystemNew/BodyModule/CableSystem.cs b/Assets/Scripts/FixSystemNew/BodyModule/CableSystem.cs index 46cfb6c71..a9e3aa142 100644 --- a/Assets/Scripts/FixSystemNew/BodyModule/CableSystem.cs +++ b/Assets/Scripts/FixSystemNew/BodyModule/CableSystem.cs @@ -67,7 +67,7 @@ namespace AibisDream.FixSystem private void LoadCableState() { - if (StorageSystem.Instance.TryGetValue(CableRetractedKey, out bool savedRetractedState)) + if (YarnVariableStorage.Instance.TryGetValue(CableRetractedKey, out bool savedRetractedState)) { if (isCableRetracted) { @@ -89,7 +89,7 @@ namespace AibisDream.FixSystem private void SaveCableState() { Debug.Log("Save cableRetractedstate" + isCableRetracted); - StorageSystem.Instance.SetValue(CableRetractedKey, isCableRetracted); + YarnVariableStorage.Instance.SetValue(CableRetractedKey, isCableRetracted); } private void Update() diff --git a/Assets/Scripts/FixSystemNew/Screen System/FixPanel/CablePanel.cs b/Assets/Scripts/FixSystemNew/Screen System/FixPanel/CablePanel.cs index dce151fbe..9e3c1b7d8 100644 --- a/Assets/Scripts/FixSystemNew/Screen System/FixPanel/CablePanel.cs +++ b/Assets/Scripts/FixSystemNew/Screen System/FixPanel/CablePanel.cs @@ -95,7 +95,7 @@ namespace AibisDream.FixSystem } // 从存储系统读取 - if (!StorageSystem.Instance.TryGetValue(CableRetractedKey, out bool savedRetractedState)) + if (!YarnVariableStorage.Instance.TryGetValue(CableRetractedKey, out bool savedRetractedState)) return; if (savedRetractedState) @@ -112,7 +112,7 @@ namespace AibisDream.FixSystem private void SaveCableState() { Debug.Log("Save cableRetractedstate" + isCableRetracted); - StorageSystem.Instance.SetValue(CableRetractedKey, isCableRetracted); + YarnVariableStorage.Instance.SetValue(CableRetractedKey, isCableRetracted); } private void Update() diff --git a/Assets/Scripts/FixSystemNew/WhackMole/WhackMoleSystem.cs b/Assets/Scripts/FixSystemNew/WhackMole/WhackMoleSystem.cs index b57ac866c..59dc3e6b0 100644 --- a/Assets/Scripts/FixSystemNew/WhackMole/WhackMoleSystem.cs +++ b/Assets/Scripts/FixSystemNew/WhackMole/WhackMoleSystem.cs @@ -58,17 +58,17 @@ namespace AibisDream.FixSystem _whackMoleHandler.EndWhackGame(); var whackRes = _whackMoleHandler.res; Debug.Log($"结果:共计{whackRes.totalMoleNum}个,成功击中{whackRes.hitMoleNum}个"); - StorageSystem.Instance.SetValue("$whackRes.totalMoleNum", whackRes.totalMoleNum); - StorageSystem.Instance.SetValue("$whackRes.hitMoleNum", whackRes.hitMoleNum); + YarnVariableStorage.Instance.SetValue("$whackRes.totalMoleNum", whackRes.totalMoleNum); + YarnVariableStorage.Instance.SetValue("$whackRes.hitMoleNum", whackRes.hitMoleNum); // 流水线结果 if (_data.HasPipeline()) { var pipelineRes = pipelineSystem.res; Debug.Log( $"流水线结果:共计{pipelineRes.totalProductNum}个,成功{pipelineRes.successNum}个,失败{pipelineRes.failNum}个"); - StorageSystem.Instance.SetValue("$whackRes.totalProductNum", pipelineRes.totalProductNum); - StorageSystem.Instance.SetValue("$whackRes.successNum", pipelineRes.successNum); - StorageSystem.Instance.SetValue("$whackRes.failNum", pipelineRes.failNum); + YarnVariableStorage.Instance.SetValue("$whackRes.totalProductNum", pipelineRes.totalProductNum); + YarnVariableStorage.Instance.SetValue("$whackRes.successNum", pipelineRes.successNum); + YarnVariableStorage.Instance.SetValue("$whackRes.failNum", pipelineRes.failNum); } // 结束 DialogController.Instance?.StartDialogNode("打地鼠结束"); diff --git a/Assets/Scripts/Game Loop/YarnVariableStorage.cs b/Assets/Scripts/Game Loop/YarnVariableStorage.cs new file mode 100644 index 000000000..35b2dfe04 --- /dev/null +++ b/Assets/Scripts/Game Loop/YarnVariableStorage.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AibisDream.Framework; +using AibisDream.SaveSystem; +using UnityEngine; +using Yarn.Unity; + +namespace AibisDream +{ + /// + /// Yarn 运行时变量存储(VariableStorageBehaviour)。 + /// 存档快照、落盘、深度维修容器见 SaveSystem 下各类。 + /// + public class YarnVariableStorage : VariableStorageBehaviour + { + private const string VariablePrefix = "$"; + private const string GlobalPrefix = "$global_"; + + private readonly Dictionary _variableDict = new(); + + #region 变量设置 + + public override void SetValue(string variableName, bool boolValue) + { + SetValue(variableName, boolValue); + } + + public override void SetValue(string variableName, string stringValue) + { + SetValue(variableName, stringValue); + } + + public override void SetValue(string variableName, float floatValue) + { + SetValue(variableName, floatValue); + } + + private void SetValue(string variableName, T value) + { + var variableType = ValidateVariableName(variableName); + + if (variableType == VariableType.Illegal) + { + Debug.LogError($"{variableName}: 缺少$前缀"); + return; + } + + _variableDict[variableName] = value; + + var variableItem = new VariableItem + { + variableName = variableName, + value = value + }; + EnumEventSystem.Global.Send(StorageEvent.VariableSet, variableItem); + } + + public override void Clear() + { + _variableDict.Clear(); + } + + public void ClearLocal() + { + var locals = _variableDict.Keys.Where(item => ValidateVariableName(item) == VariableType.Local).ToArray(); + foreach (var localKey in locals) + { + _variableDict.Remove(localKey); + } + } + + #endregion + + #region 变量获取 + + public override bool Contains(string variableName) + { + var variableType = ValidateVariableName(variableName); + if (variableType == VariableType.Illegal) + { + return false; + } + + return _variableDict.ContainsKey(variableName); + } + + public override bool TryGetValue(string variableName, out T result) + { + var variableType = ValidateVariableName(variableName); + if (variableType == VariableType.Illegal) + { + result = default; + return false; + } + + if (_variableDict.TryGetValue(variableName, out var value)) + { + if (value is T target) + { + result = target; + return true; + } + } + + result = default; + return false; + } + + #endregion + + #region 序列化(供 SnapshotCapture / SnapshotRestore 使用) + + public override void SetAllVariables(Dictionary floats, Dictionary strings, + Dictionary bools, bool clear = true) + { + if (clear) + { + Clear(); + } + + if (floats != null) + { + foreach (var value in floats) + { + SetValue(value.Key, value.Value); + } + } + + if (strings != null) + { + foreach (var value in strings) + { + SetValue(value.Key, value.Value); + } + } + + if (bools != null) + { + foreach (var value in bools) + { + SetValue(value.Key, value.Value); + } + } + } + + public override (Dictionary FloatVariables, Dictionary StringVariables, + Dictionary BoolVariables) GetAllVariables() + { + var floatDict = new Dictionary(); + var stringDict = new Dictionary(); + var boolDict = new Dictionary(); + + foreach (var variablePair in _variableDict) + { + switch (variablePair.Value) + { + case float: + { + var value = Convert.ToSingle(variablePair.Value); + floatDict.Add(variablePair.Key, value); + break; + } + case string: + { + var value = Convert.ToString(variablePair.Value); + stringDict.Add(variablePair.Key, value); + break; + } + case bool: + { + var value = Convert.ToBoolean(variablePair.Value); + boolDict.Add(variablePair.Key, value); + break; + } + default: + Debug.Log($"{variablePair.Key} is not a valid type"); + break; + } + } + + return (floatDict, stringDict, boolDict); + } + + #endregion + + private static VariableType ValidateVariableName(string variableName) + { + if (string.IsNullOrWhiteSpace(variableName) || !variableName.StartsWith(VariablePrefix)) + { + return VariableType.Illegal; + } + + if (variableName.StartsWith(GlobalPrefix)) + { + return VariableType.Global; + } + + return VariableType.Local; + } + + #region 单例 + + public static YarnVariableStorage Instance { get; private set; } + + private void Awake() + { + if (Instance != null) + { + Destroy(gameObject); + } + + Instance = this; + SnapshotBootstrap.EnsureInitialized(); + } + + #endregion + } + + public enum VariableType + { + Local, + Global, + Illegal + } + + public struct VariableItem + { + public string variableName; + public object value; + } +} diff --git a/Assets/Scripts/Game Loop/YarnVariableStorage.cs.meta b/Assets/Scripts/Game Loop/YarnVariableStorage.cs.meta new file mode 100644 index 000000000..6af3a5a80 --- /dev/null +++ b/Assets/Scripts/Game Loop/YarnVariableStorage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd2f44edfbd942b49bb7a37f80d7a5eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleSystem.cs b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleSystem.cs index 42e62b7e1..95e77202a 100644 --- a/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleSystem.cs +++ b/Assets/Scripts/MiniGame/BlockPuzzle/Class/BlockPuzzleSystem.cs @@ -123,7 +123,7 @@ namespace AibisDream var validateResult = validator.Validate(grid.GetShapes()); var shapesInGrid = grid.GetShapes().Select(s => s.BlockShapeData.blockShapeInfo.shapeName).ToArray(); - StorageSystem.Instance.SetValue("$shapesInGrid", string.Join(",", shapesInGrid)); + YarnVariableStorage.Instance.SetValue("$shapesInGrid", string.Join(",", shapesInGrid)); // 如果验证通过, 则设置变量并弹出对话 if (validateResult.isPass) @@ -236,7 +236,7 @@ namespace AibisDream // 点击形状 blockPuzzlePanel.ShowShapeInScreen(blockShape.BlockShapeData.blockShapeInfo); // 播放事件节点 - StorageSystem.Instance.SetValue("$selectedShape", blockShape.BlockShapeData.blockShapeInfo.shapeName); + YarnVariableStorage.Instance.SetValue("$selectedShape", blockShape.BlockShapeData.blockShapeInfo.shapeName); SingleCastEventSystem.Global.Trigger(DialogEventEnum.StartNode, "ShapeSelected"); } diff --git a/Assets/Scripts/MiniGame/HuoShan/SalesSystem/KnobController.cs b/Assets/Scripts/MiniGame/HuoShan/SalesSystem/KnobController.cs index c719e1211..f39bbfc05 100644 --- a/Assets/Scripts/MiniGame/HuoShan/SalesSystem/KnobController.cs +++ b/Assets/Scripts/MiniGame/HuoShan/SalesSystem/KnobController.cs @@ -707,7 +707,7 @@ namespace AibisDream.MiniGame.HuoShan private void TriggerFailure() { _failCount++; - StorageSystem.Instance?.SetValue("$knobFailCount", (float)_failCount); + YarnVariableStorage.Instance?.SetValue("$knobFailCount", (float)_failCount); _hasFailed = true; Debug.Log($"[KnobController] Failure #{_failCount} at {_currentPower:F1}%, waiting for snapback..."); diff --git a/Assets/Scripts/MiniGame/HuoShan/SalesSystem/SalesManager.cs b/Assets/Scripts/MiniGame/HuoShan/SalesSystem/SalesManager.cs index 02c735180..423f85f11 100644 --- a/Assets/Scripts/MiniGame/HuoShan/SalesSystem/SalesManager.cs +++ b/Assets/Scripts/MiniGame/HuoShan/SalesSystem/SalesManager.cs @@ -307,7 +307,7 @@ namespace AibisDream.MiniGame.Language if (_currentTurnIndex > totalTurns) return; // 滑片开始转动前,先触发 ChipModule 解释当前步骤(Stage3 等配合) - StorageSystem.Instance?.SetValue("$salesTurnIndex", (float)_currentTurnIndex); + YarnVariableStorage.Instance?.SetValue("$salesTurnIndex", (float)_currentTurnIndex); if (!string.IsNullOrEmpty(crankSlideStartNode)) DialogController.Instance?.StartDialogNode(crankSlideStartNode); @@ -327,7 +327,7 @@ namespace AibisDream.MiniGame.Language { if (string.IsNullOrEmpty(crankCompleteNode)) return; - StorageSystem.Instance?.SetValue("$salesTurnIndex", (float)_currentTurnIndex); + YarnVariableStorage.Instance?.SetValue("$salesTurnIndex", (float)_currentTurnIndex); DialogController.Instance?.StartDialogNode(crankCompleteNode); } diff --git a/Assets/Scripts/MiniGame/Tarot/TarotManager.cs b/Assets/Scripts/MiniGame/Tarot/TarotManager.cs index 5d6560e22..f15cdd0df 100644 --- a/Assets/Scripts/MiniGame/Tarot/TarotManager.cs +++ b/Assets/Scripts/MiniGame/Tarot/TarotManager.cs @@ -164,7 +164,7 @@ namespace AibisDream.MiniGame.Tarot { _isCardSelected = true; _selectedIndex = card.Index; - StorageSystem.Instance?.SetValue("$tarotSelectedIndex", (float)card.Index); + YarnVariableStorage.Instance?.SetValue("$tarotSelectedIndex", (float)card.Index); } private void OnDestroy() diff --git a/Assets/Scripts/UI/DialogUI/VariableBar.cs b/Assets/Scripts/UI/DialogUI/VariableBar.cs index a35c4700e..d2aa9c728 100644 --- a/Assets/Scripts/UI/DialogUI/VariableBar.cs +++ b/Assets/Scripts/UI/DialogUI/VariableBar.cs @@ -19,7 +19,7 @@ namespace AibisDream.UI { // 读取存储中所有变量并打印 - var variables = StorageSystem.Instance.GetAllVariables(); + var variables = YarnVariableStorage.Instance.GetAllVariables(); PrintAllVariables(variables); EnumEventSystem.Global.Register(StorageEvent.VariableSet, OnVariableUpdate); } diff --git a/Assets/Scripts/Utility/GlobalVariableKit.cs b/Assets/Scripts/Utility/GlobalVariableKit.cs index 0c83ca7a0..24fbe65b3 100644 --- a/Assets/Scripts/Utility/GlobalVariableKit.cs +++ b/Assets/Scripts/Utility/GlobalVariableKit.cs @@ -4,14 +4,14 @@ { public static bool IsPlugHighLight { - get => !(StorageSystem.Instance.TryGetValue("$global.IsPlugHighLight", out bool res) && res); - set => StorageSystem.Instance.SetValue("$global.IsPlugHighLight", !value); + get => !(YarnVariableStorage.Instance.TryGetValue("$global.IsPlugHighLight", out bool res) && res); + set => YarnVariableStorage.Instance.SetValue("$global.IsPlugHighLight", !value); } public static bool IsCordHighLight { - get => !(StorageSystem.Instance.TryGetValue("$global.IsCurdHighLight", out bool res) && res); - set => StorageSystem.Instance.SetValue("$global.IsCurdHighLight", !value); + get => !(YarnVariableStorage.Instance.TryGetValue("$global.IsCurdHighLight", out bool res) && res); + set => YarnVariableStorage.Instance.SetValue("$global.IsCurdHighLight", !value); } } } \ No newline at end of file