using System; using System.Collections.Generic; using System.Linq; using AibisDream.Framework; using AibisDream.SaveSystem; using UnityEngine; using Yarn; 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; } switch (GetVariableKind(variableName)) { case VariableKind.Stored: if (_variableDict.TryGetValue(variableName, out var value) && value is T target) { result = target; return true; } if (Program == null) { throw new InvalidOperationException( $"Can't get initial value for variable {variableName}, because {nameof(Program)} is not set"); } return Program.TryGetInitialValue(variableName, out result); case VariableKind.Smart: if (SmartVariableEvaluator == null) { throw new InvalidOperationException( $"Can't get value for smart variable {variableName}, because {nameof(SmartVariableEvaluator)} is not set"); } return SmartVariableEvaluator.TryGetSmartVariable(variableName, out result); default: 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; SnapshotRegistry.EnsureInitialized(); } #endregion } public enum VariableType { Local, Global, Illegal } public struct VariableItem { public string variableName; public object value; } }