mirror of
https://github.com/project-redbud/FunGame-Core.git
synced 2025-04-23 20:49:33 +08:00
添加视觉小说引擎
This commit is contained in:
parent
ca1247cbf5
commit
e623a1809c
181
Api/Utility/NovelConfig.cs
Normal file
181
Api/Utility/NovelConfig.cs
Normal file
@ -0,0 +1,181 @@
|
||||
using Milimoe.FunGame.Core.Library.Constant;
|
||||
using Milimoe.FunGame.Core.Model;
|
||||
|
||||
namespace Milimoe.FunGame.Core.Api.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// 视觉小说文本配置器<para/>
|
||||
/// <para/>文件会保存为:程序目录/<see cref="RootPath"/>(通常是 novels)/<see cref="NovelName"/>/<see cref="FileName"/>.json
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 新建一个配置文件,文件会保存为:程序目录/<see cref="RootPath"/>(通常是 novels)/<paramref name="novel_name"/>/<paramref name="file_name"/>.json
|
||||
/// </remarks>
|
||||
/// <param name="novel_name"></param>
|
||||
/// <param name="file_name"></param>
|
||||
public class NovelConfig(string novel_name, string file_name) : Dictionary<string, NovelNode>
|
||||
{
|
||||
/// <summary>
|
||||
/// 配置文件存放的根目录
|
||||
/// </summary>
|
||||
public static string RootPath { get; set; } = "novels";
|
||||
|
||||
/// <summary>
|
||||
/// 模组的名称
|
||||
/// </summary>
|
||||
public string NovelName { get; set; } = novel_name;
|
||||
|
||||
/// <summary>
|
||||
/// 配置文件的名称(后缀将是.json)
|
||||
/// </summary>
|
||||
public string FileName { get; set; } = file_name;
|
||||
|
||||
/// <summary>
|
||||
/// 使用索引器给指定key赋值,不存在key会新增
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public new NovelNode this[string key]
|
||||
{
|
||||
get => base[key];
|
||||
set
|
||||
{
|
||||
if (value != null) Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定key的value
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public NovelNode? Get(string key)
|
||||
{
|
||||
if (TryGetValue(key, out NovelNode? value) && value != null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个配置,如果已存在key会覆盖
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
public new void Add(string key, NovelNode value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
if (TryGetValue(key, out _)) base[key] = value;
|
||||
else base.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从配置文件中读取配置。
|
||||
/// </summary>
|
||||
/// <param name="Predicates">传入定义好的条件字典</param>
|
||||
public void LoadConfig(Dictionary<string, Func<bool>>? Predicates = null)
|
||||
{
|
||||
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}{RootPath}/{NovelName}";
|
||||
string fpath = $@"{dpath}/{FileName}.json";
|
||||
if (Directory.Exists(dpath) && File.Exists(fpath))
|
||||
{
|
||||
string json = File.ReadAllText(fpath, General.DefaultEncoding);
|
||||
Dictionary<string, NovelNode> dict = NetworkUtility.JsonDeserialize<Dictionary<string, NovelNode>>(json) ?? [];
|
||||
Clear();
|
||||
foreach (string key in dict.Keys)
|
||||
{
|
||||
NovelNode obj = dict[key];
|
||||
base.Add(key, obj);
|
||||
if (obj.Values.TryGetValue(nameof(NovelNode.NextNodes), out object? value) && value is List<string> nextKeys)
|
||||
{
|
||||
foreach (string nextKey in nextKeys)
|
||||
{
|
||||
if (dict.TryGetValue(nextKey, out NovelNode? node) && node != null)
|
||||
{
|
||||
obj.NextNodes.Add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Predicates != null)
|
||||
{
|
||||
if (obj.Values.TryGetValue(nameof(NovelNode.AndPredicates), out object? value2) && value2 is List<string> aps)
|
||||
{
|
||||
foreach (string ap in aps)
|
||||
{
|
||||
if (Predicates.TryGetValue(ap, out Func<bool>? value3) && value3 != null)
|
||||
{
|
||||
obj.AndPredicates[ap] = value3;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj.Values.TryGetValue(nameof(NovelNode.OrPredicates), out value2) && value2 is List<string> ops)
|
||||
{
|
||||
foreach (string op in ops)
|
||||
{
|
||||
if (Predicates.TryGetValue(op, out Func<bool>? value3) && value3 != null)
|
||||
{
|
||||
obj.OrPredicates[op] = value3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (NovelOption option in obj.Options)
|
||||
{
|
||||
if (option.Values.TryGetValue(nameof(NovelOption.Targets), out object? value2) && value2 is List<string> targets)
|
||||
{
|
||||
foreach (string targetKey in targets)
|
||||
{
|
||||
if (dict.TryGetValue(targetKey, out NovelNode? node) && node != null)
|
||||
{
|
||||
option.Targets.Add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Predicates != null)
|
||||
{
|
||||
if (option.Values.TryGetValue(nameof(NovelNode.AndPredicates), out object? value3) && value3 is List<string> aps)
|
||||
{
|
||||
foreach (string ap in aps)
|
||||
{
|
||||
if (Predicates.TryGetValue(ap, out Func<bool>? value4) && value4 != null)
|
||||
{
|
||||
option.AndPredicates[ap] = value4;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (option.Values.TryGetValue(nameof(NovelNode.OrPredicates), out value3) && value3 is List<string> ops)
|
||||
{
|
||||
foreach (string op in ops)
|
||||
{
|
||||
if (Predicates.TryGetValue(op, out Func<bool>? value4) && value4 != null)
|
||||
{
|
||||
option.OrPredicates[op] = value4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将配置保存到配置文件。调用此方法会覆盖原有的.json,请注意备份
|
||||
/// </summary>
|
||||
public void SaveConfig()
|
||||
{
|
||||
string json = NetworkUtility.JsonSerialize((Dictionary<string, NovelNode>)this);
|
||||
string dpath = $@"{AppDomain.CurrentDomain.BaseDirectory}{RootPath}/{NovelName}";
|
||||
string fpath = $@"{dpath}/{FileName}.json";
|
||||
if (!Directory.Exists(dpath))
|
||||
{
|
||||
Directory.CreateDirectory(dpath);
|
||||
}
|
||||
using StreamWriter writer = new(fpath, false, General.DefaultEncoding);
|
||||
writer.WriteLine(json);
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
}
|
85
Library/Common/JsonConverter/NovelNodeConverter.cs
Normal file
85
Library/Common/JsonConverter/NovelNodeConverter.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using System.Text.Json;
|
||||
using Milimoe.FunGame.Core.Api.Utility;
|
||||
using Milimoe.FunGame.Core.Library.Common.Architecture;
|
||||
using Milimoe.FunGame.Core.Model;
|
||||
|
||||
namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
|
||||
{
|
||||
public class NovelNodeConverter : BaseEntityConverter<NovelNode>
|
||||
{
|
||||
public override NovelNode NewInstance()
|
||||
{
|
||||
return new NovelNode();
|
||||
}
|
||||
|
||||
public override void ReadPropertyName(ref Utf8JsonReader reader, string propertyName, JsonSerializerOptions options, ref NovelNode result, Dictionary<string, object> convertingContext)
|
||||
{
|
||||
switch (propertyName)
|
||||
{
|
||||
case nameof(NovelNode.Key):
|
||||
result.Key = reader.GetString() ?? "";
|
||||
break;
|
||||
case nameof(NovelNode.Priority):
|
||||
result.Priority = reader.GetInt32();
|
||||
break;
|
||||
case nameof(NovelNode.Previous):
|
||||
result.Values[nameof(NovelNode.Previous)] = reader.GetString() ?? "";
|
||||
break;
|
||||
case nameof(NovelNode.NextNodes):
|
||||
result.Values[nameof(NovelNode.NextNodes)] = NetworkUtility.JsonDeserialize<List<string>>(ref reader, options) ?? [];
|
||||
break;
|
||||
case nameof(NovelNode.Options):
|
||||
result.Options = NetworkUtility.JsonDeserialize<List<NovelOption>>(ref reader, options) ?? [];
|
||||
break;
|
||||
case nameof(NovelNode.Name):
|
||||
result.Name = reader.GetString() ?? "";
|
||||
break;
|
||||
case nameof(NovelNode.Name2):
|
||||
result.Name2 = reader.GetString() ?? "";
|
||||
break;
|
||||
case nameof(NovelNode.Content):
|
||||
result.Content = reader.GetString() ?? "";
|
||||
break;
|
||||
case nameof(NovelNode.PortraitImagePath):
|
||||
result.PortraitImagePath = reader.GetString() ?? "";
|
||||
break;
|
||||
case nameof(NovelNode.AndPredicates):
|
||||
result.Values[nameof(NovelNode.AndPredicates)] = NetworkUtility.JsonDeserialize<List<string>>(ref reader, options) ?? [];
|
||||
break;
|
||||
case nameof(NovelNode.OrPredicates):
|
||||
result.Values[nameof(NovelNode.OrPredicates)] = NetworkUtility.JsonDeserialize<List<string>>(ref reader, options) ?? [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, NovelNode value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString(nameof(NovelNode.Key), value.Key);
|
||||
writer.WriteNumber(nameof(NovelNode.Priority), value.Priority);
|
||||
if (value.Previous != null) writer.WriteString(nameof(NovelNode.Previous), value.Previous.Key);
|
||||
writer.WritePropertyName(nameof(NovelNode.NextNodes));
|
||||
JsonSerializer.Serialize(writer, value.NextNodes.Select(n => n.Key).ToList(), options);
|
||||
if (value.Options.Count > 0)
|
||||
{
|
||||
writer.WritePropertyName(nameof(NovelNode.Options));
|
||||
JsonSerializer.Serialize(writer, value.Options, options);
|
||||
}
|
||||
writer.WriteString(nameof(NovelNode.Name), value.Name);
|
||||
if (value.Name2 != "") writer.WriteString(nameof(NovelNode.Name2), value.Name2);
|
||||
writer.WriteString(nameof(NovelNode.Content), value.Content);
|
||||
if (value.PortraitImagePath != "") writer.WriteString(nameof(NovelNode.PortraitImagePath), value.PortraitImagePath);
|
||||
if (value.AndPredicates.Count > 0)
|
||||
{
|
||||
writer.WritePropertyName(nameof(NovelNode.AndPredicates));
|
||||
JsonSerializer.Serialize(writer, value.AndPredicates.Keys.ToList(), options);
|
||||
}
|
||||
if (value.OrPredicates.Count > 0)
|
||||
{
|
||||
writer.WritePropertyName(nameof(NovelNode.OrPredicates));
|
||||
JsonSerializer.Serialize(writer, value.OrPredicates.Keys.ToList(), options);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
57
Library/Common/JsonConverter/NovelOptionConverter.cs
Normal file
57
Library/Common/JsonConverter/NovelOptionConverter.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System.Text.Json;
|
||||
using Milimoe.FunGame.Core.Api.Utility;
|
||||
using Milimoe.FunGame.Core.Library.Common.Architecture;
|
||||
using Milimoe.FunGame.Core.Model;
|
||||
|
||||
namespace Milimoe.FunGame.Core.Library.Common.JsonConverter
|
||||
{
|
||||
public class NovelOptionConverter : BaseEntityConverter<NovelOption>
|
||||
{
|
||||
public override NovelOption NewInstance()
|
||||
{
|
||||
return new NovelOption();
|
||||
}
|
||||
|
||||
public override void ReadPropertyName(ref Utf8JsonReader reader, string propertyName, JsonSerializerOptions options, ref NovelOption result, Dictionary<string, object> convertingContext)
|
||||
{
|
||||
switch (propertyName)
|
||||
{
|
||||
case nameof(NovelOption.Key):
|
||||
result.Key = reader.GetString() ?? "";
|
||||
break;
|
||||
case nameof(NovelOption.Name):
|
||||
result.Name = reader.GetString() ?? "";
|
||||
break;
|
||||
case nameof(NovelOption.Targets):
|
||||
result.Values[nameof(NovelOption.Targets)] = NetworkUtility.JsonDeserialize<List<string>>(ref reader, options) ?? [];
|
||||
break;
|
||||
case nameof(NovelNode.AndPredicates):
|
||||
result.Values[nameof(NovelNode.AndPredicates)] = NetworkUtility.JsonDeserialize<List<string>>(ref reader, options) ?? [];
|
||||
break;
|
||||
case nameof(NovelNode.OrPredicates):
|
||||
result.Values[nameof(NovelNode.OrPredicates)] = NetworkUtility.JsonDeserialize<List<string>>(ref reader, options) ?? [];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, NovelOption value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString(nameof(NovelOption.Key), value.Key);
|
||||
writer.WriteString(nameof(NovelOption.Name), value.Name);
|
||||
writer.WritePropertyName(nameof(NovelOption.Targets));
|
||||
JsonSerializer.Serialize(writer, value.Targets.Select(n => n.Key).ToList(), options);
|
||||
if (value.AndPredicates.Count > 0)
|
||||
{
|
||||
writer.WritePropertyName(nameof(NovelNode.AndPredicates));
|
||||
JsonSerializer.Serialize(writer, value.AndPredicates.Keys.ToList(), options);
|
||||
}
|
||||
if (value.OrPredicates.Count > 0)
|
||||
{
|
||||
writer.WritePropertyName(nameof(NovelNode.OrPredicates));
|
||||
JsonSerializer.Serialize(writer, value.OrPredicates.Keys.ToList(), options);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
}
|
48
Model/NovelNode.cs
Normal file
48
Model/NovelNode.cs
Normal file
@ -0,0 +1,48 @@
|
||||
namespace Milimoe.FunGame.Core.Model
|
||||
{
|
||||
public class NovelNode
|
||||
{
|
||||
public string Key { get; set; } = "";
|
||||
public int Priority { get; set; } = 0;
|
||||
public NovelNode? Previous { get; set; } = null;
|
||||
public List<NovelNode> NextNodes { get; set; } = [];
|
||||
public NovelNode? Next => NextNodes.OrderByDescending(n => n.Priority).Where(n => n.ShowNode).FirstOrDefault();
|
||||
public List<NovelOption> Options { get; set; } = [];
|
||||
public List<NovelOption> AvailableOptions => [.. Options.Where(o => o.ShowOption)];
|
||||
public string Name { get; set; } = "";
|
||||
public string Name2 { get; set; } = "";
|
||||
public string Content { get; set; } = "";
|
||||
public string PortraitImagePath { get; set; } = "";
|
||||
public Dictionary<string, Func<bool>> AndPredicates { get; set; } = [];
|
||||
public Dictionary<string, Func<bool>> OrPredicates { get; set; } = [];
|
||||
public bool ShowNode
|
||||
{
|
||||
get
|
||||
{
|
||||
bool andResult = AndPredicates.Values.All(predicate => predicate());
|
||||
bool orResult = OrPredicates.Values.Any(predicate => predicate());
|
||||
return andResult && (OrPredicates.Count == 0 || orResult);
|
||||
}
|
||||
}
|
||||
internal Dictionary<string, object> Values { get; set; } = [];
|
||||
}
|
||||
|
||||
public class NovelOption
|
||||
{
|
||||
public string Key { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public List<NovelNode> Targets { get; set; } = [];
|
||||
public Dictionary<string, Func<bool>> AndPredicates { get; set; } = [];
|
||||
public Dictionary<string, Func<bool>> OrPredicates { get; set; } = [];
|
||||
public bool ShowOption
|
||||
{
|
||||
get
|
||||
{
|
||||
bool andResult = AndPredicates.Values.All(predicate => predicate());
|
||||
bool orResult = OrPredicates.Values.Any(predicate => predicate());
|
||||
return andResult && (OrPredicates.Count == 0 || orResult);
|
||||
}
|
||||
}
|
||||
internal Dictionary<string, object> Values { get; set; } = [];
|
||||
}
|
||||
}
|
@ -21,7 +21,8 @@ namespace Milimoe.FunGame.Core.Service
|
||||
ReferenceHandler = ReferenceHandler.IgnoreCycles,
|
||||
Converters = { new DateTimeConverter(), new DataTableConverter(), new DataSetConverter(), new UserConverter(), new RoomConverter(),
|
||||
new CharacterConverter(), new MagicResistanceConverter(), new EquipSlotConverter(), new SkillConverter(), new EffectConverter(), new ItemConverter(),
|
||||
new InventoryConverter(), new NormalAttackConverter(), new ClubConverter(), new GoodsConverter(), new StoreConverter()
|
||||
new InventoryConverter(), new NormalAttackConverter(), new ClubConverter(), new GoodsConverter(), new StoreConverter(),
|
||||
new NovelOptionConverter(), new NovelNodeConverter()
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user