Files
ichni_Official/Assets/Scripts/NewStorySystem/Data/StoryVariables.cs
SoulliesOfficial 810d019619 剧情+对话完善
2026-07-21 15:24:42 -04:00

214 lines
9.9 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;
namespace Ichni.Story
{
/// <summary>
/// 当前章节剧情变量的统一数据枢纽。
/// 所有外部系统(包括 Yarn、UI、小游戏读写剧情变量的唯一官方入口。
/// 自动桥接至 GameSaveManager 的 StorySaveModule 中当前章节的持久化容器。
/// </summary>
public static class StoryVariables
{
// 应对游戏尚未初始化(或者单独测试场景)的内存兜底
private static readonly StoryVariablesSave _fallback = new StoryVariablesSave();
private static StoryVariablesSave Variables
{
get
{
if (GameSaveManager.instance != null && GameSaveManager.instance.StorySaveModule != null)
return GameSaveManager.instance.StorySaveModule.GetActiveChapterVariables();
return _fallback;
}
}
// ── 读取 ────────────────────────────────────────────────────────────────
public static float GetFloat(string key, float defaultValue = 0f)
{
StoryVariablesSave vars = Variables;
if (vars.floatVariables.TryGetValue(key, out float f)) return f;
if (vars.boolVariables.TryGetValue(key, out bool b)) return b ? 1f : 0f;
if (vars.stringVariables.TryGetValue(key, out string s) && float.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out float p)) return p;
if (TryGetInitialVariable(key, out StoryVariableDefinition definition))
{
return definition.type switch
{
StoryVariableType.Bool => definition.boolValue ? 1f : 0f,
StoryVariableType.Int => definition.intValue,
StoryVariableType.Float => definition.floatValue,
StoryVariableType.String when float.TryParse(definition.stringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsed) => parsed,
_ => defaultValue
};
}
return defaultValue;
}
public static int GetInt(string key, int defaultValue = 0)
{
StoryVariablesSave vars = Variables;
if (vars.floatVariables.TryGetValue(key, out float f)) return Mathf.RoundToInt(f);
if (vars.boolVariables.TryGetValue(key, out bool b)) return b ? 1 : 0;
if (vars.stringVariables.TryGetValue(key, out string s) && float.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out float p)) return Mathf.RoundToInt(p);
if (TryGetInitialVariable(key, out StoryVariableDefinition definition))
{
return definition.type switch
{
StoryVariableType.Bool => definition.boolValue ? 1 : 0,
StoryVariableType.Int => definition.intValue,
StoryVariableType.Float => Mathf.RoundToInt(definition.floatValue),
StoryVariableType.String when float.TryParse(definition.stringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out float parsed) => Mathf.RoundToInt(parsed),
_ => defaultValue
};
}
return defaultValue;
}
public static bool GetBool(string key, bool defaultValue = false)
{
StoryVariablesSave vars = Variables;
if (vars.boolVariables.TryGetValue(key, out bool b)) return b;
if (vars.floatVariables.TryGetValue(key, out float f)) return f != 0f;
if (vars.stringVariables.TryGetValue(key, out string s) && bool.TryParse(s, out bool p)) return p;
if (TryGetInitialVariable(key, out StoryVariableDefinition definition))
{
return definition.type switch
{
StoryVariableType.Bool => definition.boolValue,
StoryVariableType.Int => definition.intValue != 0,
StoryVariableType.Float => !Mathf.Approximately(definition.floatValue, 0f),
StoryVariableType.String when bool.TryParse(definition.stringValue, out bool parsed) => parsed,
_ => defaultValue
};
}
return defaultValue;
}
public static string GetString(string key, string defaultValue = "")
{
StoryVariablesSave vars = Variables;
if (vars.stringVariables.TryGetValue(key, out string s)) return s;
if (vars.floatVariables.TryGetValue(key, out float f)) return f.ToString(CultureInfo.InvariantCulture);
if (vars.boolVariables.TryGetValue(key, out bool b)) return b.ToString();
if (TryGetInitialVariable(key, out StoryVariableDefinition definition))
{
return definition.type switch
{
StoryVariableType.Bool => definition.boolValue.ToString(),
StoryVariableType.Int => definition.intValue.ToString(CultureInfo.InvariantCulture),
StoryVariableType.Float => definition.floatValue.ToString(CultureInfo.InvariantCulture),
StoryVariableType.String => definition.stringValue ?? string.Empty,
_ => defaultValue
};
}
return defaultValue;
}
public static bool HasVariable(string key)
{
StoryVariablesSave vars = Variables;
return vars.floatVariables.ContainsKey(key) || vars.boolVariables.ContainsKey(key) || vars.stringVariables.ContainsKey(key) ||
TryGetInitialVariable(key, out _);
}
// ── 写入 ────────────────────────────────────────────────────────────────
public static void SetFloat(string key, float value)
{
RemoveStaleEntries(key, keepFloat: true, keepString: false, keepBool: false);
Variables.floatVariables[key] = value;
}
public static void SetInt(string key, int value)
{
// 在 Yarn/存档底层中数字统一存为 float避免双字典冗余
SetFloat(key, value);
}
public static void SetBool(string key, bool value)
{
RemoveStaleEntries(key, keepFloat: false, keepString: false, keepBool: true);
Variables.boolVariables[key] = value;
}
public static void SetString(string key, string value)
{
RemoveStaleEntries(key, keepFloat: false, keepString: true, keepBool: false);
Variables.stringVariables[key] = value;
}
/// <summary>
/// 删除指定剧情变量在三种底层字典中的所有残留值。
/// 仅删除内存值;需要持久化和故事树刷新时,应调用 <see cref="StoryProgress"/> 的统一接口。
/// </summary>
public static void Remove(string key)
{
if (string.IsNullOrWhiteSpace(key)) return;
StoryVariablesSave vars = Variables;
vars.floatVariables.Remove(key);
vars.stringVariables.Remove(key);
vars.boolVariables.Remove(key);
}
public static void ClearAll()
{
StoryVariablesSave vars = Variables;
vars.floatVariables.Clear();
vars.stringVariables.Clear();
vars.boolVariables.Clear();
}
private static 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);
}
/// <summary>
/// 从当前 StoryTree 的 StoryData 查询变量默认值。没有章节上下文时不虚构默认值,
/// 由调用方传入的 defaultValue 作为独立测试场景的回退。
/// </summary>
private static bool TryGetInitialVariable(string key, out StoryVariableDefinition definition)
{
StoryData storyData = StoryManager.instance?.treeController?.ActiveStoryData;
if (storyData != null)
return storyData.TryGetInitialVariable(key, out definition);
definition = null;
return false;
}
// ── 字典批量操作(内部供 Yarn Adapter 使用) ─────────────────────────────
internal static 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 (var kv in floats) vars.floatVariables[kv.Key] = kv.Value;
if (strings != null) foreach (var kv in strings) vars.stringVariables[kv.Key] = kv.Value;
if (bools != null) foreach (var kv in bools) vars.boolVariables[kv.Key] = kv.Value;
}
internal static (Dictionary<string, float> floats, Dictionary<string, string> strings, Dictionary<string, bool> bools) GetAllVariables()
{
StoryVariablesSave vars = Variables;
return (
new Dictionary<string, float>(vars.floatVariables),
new Dictionary<string, string>(vars.stringVariables),
new Dictionary<string, bool>(vars.boolVariables)
);
}
}
}