Files
Shigure/Runtime/StateBuilder.cs

216 lines
7.6 KiB
C#
Raw Permalink 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.Text.Json.Nodes;
namespace Shigure;
public sealed class StateBuilder : IRuntimeStateBuilder
{
private readonly ConfigService _config;
public StateBuilder(ConfigService config)
{
_config = config;
}
public GameState Build(
IReadOnlyDictionary<int, int> rowData,
IReadOnlyDictionary<int, int> barData,
IReadOnlyDictionary<int, int>? healAbsorbData = null)
{
var classId = rowData.TryGetValue(2, out var cid) ? cid : 0;
var specId = rowData.TryGetValue(3, out var sid) ? sid : 0;
var stateConfig = _config.BuildStateConfig(classId, specId);
var result = new Dictionary<string, object?>();
healAbsorbData ??= new Dictionary<int, int>();
var itemIds = new Dictionary<string, long>(StringComparer.Ordinal);
foreach (var (key, node) in stateConfig)
{
if (key is "group" or "spells" or "auras" || node is not JsonObject field || !field.ContainsKey("step"))
{
continue;
}
result[key] = ConvertRawValue(ResolveRaw(field, rowData, barData), JsonHelpers.GetString(JsonHelpers.Get(field, "type")));
var itemId = JsonHelpers.GetLong(JsonHelpers.Get(field, "itemId"));
if (itemId is > 0)
{
itemIds[key] = itemId.Value;
}
}
if (itemIds.Count > 0)
{
result["$itemIds"] = itemIds;
}
if (JsonHelpers.Get(stateConfig, "spells") is JsonObject spellsConfig)
{
result["spells"] = BuildFieldMap(spellsConfig, rowData, barData);
result["$spellDisplayTypes"] = BuildSpellDisplayTypes(spellsConfig);
}
if (JsonHelpers.Get(stateConfig, "auras") is JsonObject aurasConfig)
{
result["auras"] = BuildFieldMap(aurasConfig, rowData, barData);
}
if (JsonHelpers.Get(stateConfig, "group") is JsonObject groupConfig)
{
var group = BuildGroup(groupConfig, rowData, barData, healAbsorbData);
result["group"] = group;
}
return new GameState(result);
}
private static Dictionary<string, object?> BuildFieldMap(
JsonObject fieldsConfig,
IReadOnlyDictionary<int, int> rowData,
IReadOnlyDictionary<int, int> barData)
{
var values = new Dictionary<string, object?>();
foreach (var (fieldName, node) in fieldsConfig)
{
if (node is not JsonObject field || !field.ContainsKey("step"))
{
continue;
}
var value = ConvertRawValue(ResolveRaw(field, rowData, barData), JsonHelpers.GetString(JsonHelpers.Get(field, "type")));
values[fieldName] = value;
AddAuraAliases(values, field, value, includeScope: true);
}
return values;
}
private static Dictionary<string, string> BuildSpellDisplayTypes(JsonObject fieldsConfig)
{
var values = new Dictionary<string, string>();
foreach (var (fieldName, node) in fieldsConfig)
{
if (node is not JsonObject field)
{
continue;
}
var displayType = JsonHelpers.GetString(JsonHelpers.Get(field, "displayType"));
if (!string.IsNullOrWhiteSpace(displayType))
{
values[fieldName] = displayType;
}
}
return values;
}
private static Dictionary<string, IReadOnlyDictionary<string, object?>> BuildGroup(
JsonObject groupConfig,
IReadOnlyDictionary<int, int> rowData,
IReadOnlyDictionary<int, int> barData,
IReadOnlyDictionary<int, int> healAbsorbData)
{
var start = JsonHelpers.GetInt(JsonHelpers.Get(groupConfig, "start")) ?? 26;
var numParams = JsonHelpers.GetInt(JsonHelpers.Get(groupConfig, "num")) ?? 5;
var group = new Dictionary<string, IReadOnlyDictionary<string, object?>>();
for (var i = 1; i <= 30; i++)
{
var baseStep = start + (i - 1) * numParams;
var sub = new Dictionary<string, object?>();
foreach (var (fieldName, node) in groupConfig)
{
if (fieldName is "start" or "num" || node is not JsonObject field || !field.ContainsKey("step"))
{
continue;
}
int? raw;
var stepNode = JsonHelpers.Get(field, "step");
if (JsonHelpers.GetString(stepNode) == "bar")
{
raw = ResolveRaw(field, rowData, barData);
}
else
{
var relStep = JsonHelpers.GetInt(stepNode);
raw = relStep is null
? null
: rowData.TryGetValue(baseStep + relStep.Value, out var rawValue) ? rawValue : null;
}
var value = ConvertRawValue(raw, JsonHelpers.GetString(JsonHelpers.Get(field, "type")));
sub[fieldName] = value;
AddAuraAliases(sub, field, value, includeScope: false);
}
// 治疗吸收来自网格扫描:白块右侧像素的 B=单位编号G-1=吸收值。
// 插件像素里的生命值含吸收盾,这里折算为真实生命:生命值 -= 治疗吸收。
// 保留 0 和负数,供最低生命值选择器比较治疗吸收后的有效生命值。
var absorb = healAbsorbData.TryGetValue(i, out var absorbValue) ? absorbValue : 0;
sub["治疗吸收"] = absorb;
if (absorb != 0 && sub.TryGetValue("生命值", out var healthObj) && healthObj is int health)
{
sub["生命值"] = health - absorb;
}
group[i.ToString()] = sub;
}
return group;
}
private static void AddAuraAliases(
IDictionary<string, object?> target,
JsonObject field,
object? value,
bool includeScope)
{
var canonicalId = JsonHelpers.GetLong(JsonHelpers.Get(field, "spellId"));
var metric = JsonHelpers.GetString(JsonHelpers.Get(field, "metric"));
var scope = JsonHelpers.GetString(JsonHelpers.Get(field, "scope"));
if (canonicalId is null || string.IsNullOrWhiteSpace(metric)
|| JsonHelpers.Get(field, "spellIds") is not JsonArray aliases)
{
return;
}
foreach (var node in aliases)
{
var alias = JsonHelpers.GetLong(node);
if (alias is null || alias == canonicalId)
{
continue;
}
var key = includeScope
? $"{scope}.{alias}.{metric}"
: $"auras.{alias}.{metric}";
target[key] = value;
}
}
private static int? ResolveRaw(JsonObject field, IReadOnlyDictionary<int, int> rowData, IReadOnlyDictionary<int, int> barData)
{
var stepNode = JsonHelpers.Get(field, "step");
if (JsonHelpers.GetString(stepNode) == "bar")
{
var barIndex = JsonHelpers.GetInt(JsonHelpers.Get(field, "bar"));
return barIndex is not null && barData.TryGetValue(barIndex.Value, out var barValue) ? barValue : null;
}
var step = JsonHelpers.GetInt(stepNode);
return step is not null && rowData.TryGetValue(step.Value, out var value) ? value : null;
}
private static object ConvertRawValue(int? raw, string? type)
{
return type switch
{
"bool" => raw.GetValueOrDefault() != 0,
"string" => raw?.ToString() ?? string.Empty,
_ => raw.GetValueOrDefault()
};
}
}