Files
ichni_Official/Assets/Scripts/NewStorySystem/Dialogue/StoryVariableStorage.cs
2026-07-05 16:08:23 -04:00

208 lines
8.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
using Yarn.Unity;
namespace Ichni.Story.Dialogue
{
/// <summary>
/// 自定义 Yarn 变量存储。直接读写 <see cref="StorySaveModule.variables"/>(全局剧情变量),
/// 让 Yarn 对话中的 <c>&lt;&lt;set $var to ...&gt;&gt;</c> 与故事树解锁条件共享同一份数据来源,
/// 并借助存档模块的 ES3 序列化持久化。
/// <para>变量名沿用 Yarn 约定(带 <c>$</c> 前缀);解锁条件中的变量名也应保持一致。</para>
/// </summary>
public class StoryVariableStorage : VariableStorageBehaviour
{
// GameSaveManager 尚未就绪(编辑器早期 / 场景未初始化)时的兜底容器
private readonly StoryVariablesSave _fallback = new StoryVariablesSave();
/// <summary>当前生效的变量容器:优先取存档模块,缺失时用本地兜底容器。</summary>
private StoryVariablesSave Variables
{
get
{
if (GameSaveManager.instance != null && GameSaveManager.instance.StorySaveModule != null)
return GameSaveManager.instance.StorySaveModule.variables;
return _fallback;
}
}
// ── IVariableStorage 实现 ────────────────────────────────────────────────
public override bool TryGetValue<T>(string variableName, out T result)
{
StoryVariablesSave vars = Variables;
if (vars.floatVariables.TryGetValue(variableName, out float f))
return TryConvert(f, out result);
if (vars.boolVariables.TryGetValue(variableName, out bool b))
return TryConvert(b, out result);
if (vars.stringVariables.TryGetValue(variableName, out string s))
return TryConvert(s, out result);
result = default;
return false;
}
public override void SetValue(string variableName, string stringValue)
{
RemoveStaleEntries(variableName, keepFloat: false, keepString: true, keepBool: false);
Variables.stringVariables[variableName] = stringValue;
NotifyVariableChanged(variableName, stringValue);
}
public override void SetValue(string variableName, float floatValue)
{
RemoveStaleEntries(variableName, keepFloat: true, keepString: false, keepBool: false);
Variables.floatVariables[variableName] = floatValue;
NotifyVariableChanged(variableName, floatValue);
}
public override void SetValue(string variableName, bool boolValue)
{
RemoveStaleEntries(variableName, keepFloat: false, keepString: false, keepBool: true);
Variables.boolVariables[variableName] = boolValue;
NotifyVariableChanged(variableName, boolValue);
}
public override bool Contains(string variableName)
{
StoryVariablesSave vars = Variables;
return vars.floatVariables.ContainsKey(variableName)
|| vars.boolVariables.ContainsKey(variableName)
|| vars.stringVariables.ContainsKey(variableName);
}
public override void Clear()
{
StoryVariablesSave vars = Variables;
vars.floatVariables.Clear();
vars.stringVariables.Clear();
vars.boolVariables.Clear();
}
public override void SetAllVariables(
Dictionary<string, float> floats,
Dictionary<string, string> strings,
Dictionary<string, bool> bools,
bool clear = true)
{
StoryVariablesSave vars = Variables;
if (clear)
{
vars.floatVariables.Clear();
vars.stringVariables.Clear();
vars.boolVariables.Clear();
}
if (floats != null)
foreach (KeyValuePair<string, float> kv in floats) vars.floatVariables[kv.Key] = kv.Value;
if (strings != null)
foreach (KeyValuePair<string, string> kv in strings) vars.stringVariables[kv.Key] = kv.Value;
if (bools != null)
foreach (KeyValuePair<string, bool> kv in bools) vars.boolVariables[kv.Key] = kv.Value;
}
public override (Dictionary<string, float> FloatVariables,
Dictionary<string, string> StringVariables,
Dictionary<string, bool> BoolVariables) GetAllVariables()
{
StoryVariablesSave vars = Variables;
return (
new Dictionary<string, float>(vars.floatVariables),
new Dictionary<string, string>(vars.stringVariables),
new Dictionary<string, bool>(vars.boolVariables)
);
}
// ── 供故事树解锁条件复用的便捷读取 ────────────────────────────────────────
/// <summary>读取变量的整数近似值float 四舍五入、bool 取 0/1不存在返回 0。</summary>
public int GetIntVariable(string variableName)
{
StoryVariablesSave vars = Variables;
if (vars.floatVariables.TryGetValue(variableName, out float f))
return Mathf.RoundToInt(f);
if (vars.boolVariables.TryGetValue(variableName, out bool b))
return b ? 1 : 0;
return 0;
}
/// <summary>该变量是否存在于任一类型的存储中。</summary>
public bool HasVariable(string variableName) => Contains(variableName);
// ── 内部 ────────────────────────────────────────────────────────────────
// 当变量被重新赋值为其它类型时,从旧类型字典移除,避免同名变量残留多份
private void RemoveStaleEntries(string name, bool keepFloat, bool keepString, bool keepBool)
{
StoryVariablesSave vars = Variables;
if (!keepFloat) vars.floatVariables.Remove(name);
if (!keepString) vars.stringVariables.Remove(name);
if (!keepBool) vars.boolVariables.Remove(name);
}
// 将存储中的原始值转换为 Yarn 请求的类型float / bool / string / int 之间互转)
private static bool TryConvert<T>(object value, out T result)
{
if (value is T typed)
{
result = typed;
return true;
}
try
{
if (typeof(T) == typeof(float) || typeof(T) == typeof(double) || typeof(T) == typeof(int))
{
float f = value switch
{
float fv => fv,
bool bv => bv ? 1f : 0f,
string sv => float.TryParse(sv, NumberStyles.Any, CultureInfo.InvariantCulture, out float p) ? p : 0f,
_ => 0f
};
result = (T)System.Convert.ChangeType(f, typeof(T), CultureInfo.InvariantCulture);
return true;
}
if (typeof(T) == typeof(bool))
{
bool b = value switch
{
bool bv => bv,
float fv => fv != 0f,
string sv => bool.TryParse(sv, out bool p) && p,
_ => false
};
result = (T)(object)b;
return true;
}
if (typeof(T) == typeof(string))
{
string s = value switch
{
string sv => sv,
float fv => fv.ToString(CultureInfo.InvariantCulture),
bool bv => bv.ToString(),
_ => value != null ? value.ToString() : string.Empty
};
result = (T)(object)s;
return true;
}
}
catch
{
// 转换失败时落到下方失败分支
}
result = default;
return false;
}
}
}