256 lines
9.5 KiB
C#
256 lines
9.5 KiB
C#
using System.Text.Json.Nodes;
|
|
|
|
namespace Sand.Core;
|
|
|
|
public static class ParticleLibraryLoader
|
|
{
|
|
private static readonly HashSet<string> StaticTypes =
|
|
[
|
|
"wall",
|
|
"stone",
|
|
"rock",
|
|
"iron",
|
|
"gold",
|
|
"copper",
|
|
"wood",
|
|
"brass",
|
|
"glass",
|
|
];
|
|
|
|
public static ParticleLibrary LoadFromDirectory(string partRootPath)
|
|
{
|
|
var directories = new[]
|
|
{
|
|
Path.Combine(partRootPath, "coreparts"),
|
|
Path.Combine(partRootPath, "mods"),
|
|
};
|
|
|
|
var rawDefinitions = new Dictionary<string, JsonObject>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (var directory in directories)
|
|
{
|
|
if (!Directory.Exists(directory))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var filePath in Directory.EnumerateFiles(directory, "*.json", SearchOption.AllDirectories))
|
|
{
|
|
var rootNode = JsonNode.Parse(File.ReadAllText(filePath)) as JsonObject;
|
|
if (rootNode is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (var pair in rootNode)
|
|
{
|
|
if (pair.Value is JsonObject obj)
|
|
{
|
|
rawDefinitions[pair.Key.ToLowerInvariant()] = obj;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var orderedIds = rawDefinitions.Keys.OrderBy(static id => id, StringComparer.Ordinal).ToArray();
|
|
var definitions = new List<ParticleDef>(orderedIds.Length);
|
|
|
|
foreach (var id in orderedIds)
|
|
{
|
|
definitions.Add(ParseDefinition(id, rawDefinitions[id]));
|
|
}
|
|
|
|
var knownIds = new HashSet<string>(orderedIds, StringComparer.OrdinalIgnoreCase);
|
|
foreach (var definition in definitions)
|
|
{
|
|
ValidateTarget(knownIds, definition.Id, definition.Melt, nameof(definition.Melt));
|
|
ValidateTarget(knownIds, definition.Id, definition.Evaporate, nameof(definition.Evaporate));
|
|
ValidateTarget(knownIds, definition.Id, definition.Solidify, nameof(definition.Solidify));
|
|
ValidateTarget(knownIds, definition.Id, definition.Freeze, nameof(definition.Freeze));
|
|
ValidateTarget(knownIds, definition.Id, definition.Broken, nameof(definition.Broken));
|
|
ValidateTarget(knownIds, definition.Id, definition.Produces, nameof(definition.Produces));
|
|
ValidateTarget(knownIds, definition.Id, definition.ProducesOnDeath, nameof(definition.ProducesOnDeath));
|
|
}
|
|
|
|
return new ParticleLibrary(definitions);
|
|
}
|
|
|
|
private static ParticleDef ParseDefinition(string id, JsonObject source)
|
|
{
|
|
var isGas = GetBool(source, "is_gas");
|
|
var isLiquid = GetBool(source, "liquid");
|
|
var isSolid = GetBool(source, "solid", true);
|
|
var isSpecial = !isGas && !isLiquid && !isSolid;
|
|
|
|
return new ParticleDef
|
|
{
|
|
Id = id,
|
|
Name = GetString(source, "name") ?? id,
|
|
Kind = isGas ? ParticleKind.Gas : isLiquid ? ParticleKind.Liquid : ParticleKind.Solid,
|
|
IsStatic = StaticTypes.Contains(id) || GetBool(source, "static"),
|
|
IsSpecial = isSpecial,
|
|
Mass = GetFloat(source, "mass", 1f),
|
|
Hardness = GetFloat(source, "hardness", 0.5f),
|
|
Velocity = GetFloat(source, "velocity", 0f),
|
|
Conductivity = GetFloat(source, "conductivity", 0f),
|
|
Conductive = GetBool(source, "conductive"),
|
|
Flamability = GetFloat(source, "flamability", 0f),
|
|
Durability = GetFloat(source, "durability", 100f),
|
|
HeatCapacity = GetFloat(source, "heat_capacity", 1f),
|
|
Friction = GetFloat(source, "friction", 0f),
|
|
Viscosity = GetFloat(source, "viscosity", 0f),
|
|
Pressure = GetFloat(source, "pressure", 0f),
|
|
Temperature = GetFloat(source, "temperature", 22f),
|
|
Melt = GetLowerString(source, "melt"),
|
|
MeltTemperature = GetNullableFloat(source, "melt_temperature"),
|
|
Evaporate = GetLowerString(source, "evaporate"),
|
|
EvaporateTemperature = GetNullableFloat(source, "evaporate_temperature"),
|
|
Solidify = GetLowerString(source, "solidify"),
|
|
SolidifyTemperature = GetNullableFloat(source, "solidify_temperature"),
|
|
Freeze = GetLowerString(source, "freeze"),
|
|
FreezeTemperature = GetNullableFloat(source, "freeze_temperature"),
|
|
BurnDuration = GetFloat(source, "burn_duration", 0f),
|
|
BurnTemperature = GetFloat(source, "burn_temperature", 0f),
|
|
BurnRate = GetFloat(source, "burn_rate", 1f),
|
|
Burning = GetBool(source, "burning"),
|
|
Lifetime = GetNullableFloat(source, "lifetime"),
|
|
Explosive = GetBool(source, "explosive"),
|
|
ExplosionRadius = GetInt(source, "explosion_radius", 0),
|
|
ExplosionColor = GetOptionalColor(source, "explosion_color"),
|
|
ExplosionForce = GetFloat(source, "explosion_force", 6f),
|
|
ExplosionDuration = GetInt(source, "explosion_duration", 1),
|
|
PressureResistance = GetFloat(source, "pressure_resistance", 0f),
|
|
PressureTolerance = GetFloat(source, "pressure_tolerance", 0f),
|
|
PressureThreshold = GetFloat(source, "pressure_threshold", 0f),
|
|
PressureThresholdDuration = GetInt(source, "pressure_threshold_duration", 0),
|
|
Broken = GetLowerString(source, "Broken") ?? GetLowerString(source, "broken"),
|
|
Produces = GetLowerString(source, "produces"),
|
|
ProducesOnDeath = GetLowerString(source, "produces_on_death"),
|
|
HeatEmission = GetFloat(source, "heat_emission", 0f),
|
|
EnergyTransfer = GetFloat(source, "energy_transfer", 0f),
|
|
Radius = GetFloat(source, "radius", 0f),
|
|
ForceFalloff = GetFloat(source, "force_falloff", 1f),
|
|
Turbulence = GetFloat(source, "turbulence", 0f),
|
|
Affects = GetStringArray(source, "affects"),
|
|
IsWind = GetBool(source, "is_wind"),
|
|
WindStrength = GetFloat(source, "wind_strength", 0f),
|
|
WindDirection = GetFloatArray(source, "wind_direction"),
|
|
IsGravity = GetBool(source, "is_gravity"),
|
|
GravityStrength = GetFloat(source, "gravity_strength", 0f),
|
|
PullDirection = GetLowerString(source, "pull_direction"),
|
|
IsRepulsor = GetBool(source, "is_repulsor"),
|
|
RepulsionStrength = GetFloat(source, "repulsion_strength", 0f),
|
|
PushDirection = GetLowerString(source, "push_direction"),
|
|
Color = GetColor(source),
|
|
};
|
|
}
|
|
|
|
private static void ValidateTarget(HashSet<string> knownIds, string id, string? target, string propertyName)
|
|
{
|
|
if (target is not null && !knownIds.Contains(target))
|
|
{
|
|
throw new InvalidDataException($"Particle '{id}' references unknown {propertyName} target '{target}'.");
|
|
}
|
|
}
|
|
|
|
private static string? GetString(JsonObject source, string key) => source[key]?.GetValue<string>();
|
|
|
|
private static string? GetLowerString(JsonObject source, string key)
|
|
{
|
|
var value = GetString(source, key);
|
|
return value?.ToLowerInvariant();
|
|
}
|
|
|
|
private static bool GetBool(JsonObject source, string key, bool defaultValue = false)
|
|
{
|
|
if (source[key] is null)
|
|
{
|
|
return defaultValue;
|
|
}
|
|
|
|
return source[key]!.GetValue<bool>();
|
|
}
|
|
|
|
private static float GetFloat(JsonObject source, string key, float defaultValue)
|
|
{
|
|
if (source[key] is null)
|
|
{
|
|
return defaultValue;
|
|
}
|
|
|
|
return source[key]!.GetValue<float>();
|
|
}
|
|
|
|
private static int GetInt(JsonObject source, string key, int defaultValue)
|
|
{
|
|
if (source[key] is null)
|
|
{
|
|
return defaultValue;
|
|
}
|
|
|
|
return source[key]!.GetValue<int>();
|
|
}
|
|
|
|
private static float? GetNullableFloat(JsonObject source, string key)
|
|
{
|
|
if (source[key] is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return source[key]!.GetValue<float>();
|
|
}
|
|
|
|
private static Rgb24 GetColor(JsonObject source)
|
|
{
|
|
if (source["color"] is not JsonArray array || array.Count < 3)
|
|
{
|
|
return new Rgb24(255, 255, 255);
|
|
}
|
|
|
|
return new Rgb24(
|
|
(byte)array[0]!.GetValue<int>(),
|
|
(byte)array[1]!.GetValue<int>(),
|
|
(byte)array[2]!.GetValue<int>());
|
|
}
|
|
|
|
private static Rgb24? GetOptionalColor(JsonObject source, string key)
|
|
{
|
|
if (source[key] is not JsonArray array || array.Count < 3)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new Rgb24(
|
|
(byte)array[0]!.GetValue<int>(),
|
|
(byte)array[1]!.GetValue<int>(),
|
|
(byte)array[2]!.GetValue<int>());
|
|
}
|
|
|
|
private static string[] GetStringArray(JsonObject source, string key)
|
|
{
|
|
if (source[key] is not JsonArray array)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
return array
|
|
.Where(static item => item is not null)
|
|
.Select(static item => item!.GetValue<string>().ToLowerInvariant())
|
|
.ToArray();
|
|
}
|
|
|
|
private static float[]? GetFloatArray(JsonObject source, string key)
|
|
{
|
|
if (source[key] is not JsonArray array || array.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return array
|
|
.Where(static item => item is not null)
|
|
.Select(static item => item!.GetValue<float>())
|
|
.ToArray();
|
|
}
|
|
}
|