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)
{
SetStoredValue(variableName, boolValue);
}
public override void SetValue(string variableName, string stringValue)
{
SetStoredValue(variableName, stringValue);
}
public override void SetValue(string variableName, float floatValue)
{
SetStoredValue(variableName, floatValue);
}
private void SetStoredValue(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();
EnumEventSystem.Global.Send(StorageEvent.VariablesCleared);
}
public void ClearLocal()
{
var locals = _variableDict.Keys.Where(item => ValidateVariableName(item) == VariableType.Local).ToArray();
foreach (var localKey in locals)
{
_variableDict.Remove(localKey);
}
if (locals.Length > 0)
{
EnumEventSystem.Global.Send(StorageEvent.VariablesCleared);
}
}
#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;
}
}