250 lines
8.9 KiB
C#
250 lines
8.9 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using GameCult.Aetheria.State.Unity;
|
|
using Unity.Mathematics;
|
|
|
|
public interface IRuntimeItemProjectionReader
|
|
{
|
|
AetheriaRuntimeCatalogItem GetRuntimeItem(Guid guid);
|
|
IReadOnlyList<BehaviorData> GetBehaviorProjections(Guid guid);
|
|
}
|
|
|
|
public sealed class AetheriaRuntimeItemCatalog : IRuntimeItemProjectionReader
|
|
{
|
|
private readonly Dictionary<Guid, AetheriaRuntimeCatalogItem> _typedItems;
|
|
private static readonly IReadOnlyDictionary<int, Type> BehaviorTypesByUnionKey = new Dictionary<int, Type>
|
|
{
|
|
{ 0, typeof(GuidedWeaponData) },
|
|
{ 1, typeof(LauncherData) },
|
|
{ 2, typeof(ReactorData) },
|
|
{ 3, typeof(RadiatorData) },
|
|
{ 4, typeof(StatModifierData) },
|
|
{ 5, typeof(SensorData) },
|
|
{ 6, typeof(ReflectorData) },
|
|
{ 7, typeof(ShieldData) },
|
|
{ 8, typeof(ThrusterData) },
|
|
{ 9, typeof(WearData) },
|
|
{ 10, typeof(VelocityConversionData) },
|
|
{ 11, typeof(VelocityLimitData) },
|
|
{ 12, typeof(AetherDriveData) },
|
|
{ 15, typeof(CooldownData) },
|
|
{ 16, typeof(HeatData) },
|
|
{ 18, typeof(ItemUsageData) },
|
|
{ 20, typeof(SwitchData) },
|
|
{ 21, typeof(TriggerData) },
|
|
{ 22, typeof(VisibilityData) },
|
|
{ 23, typeof(ThermotoggleData) },
|
|
{ 24, typeof(EnergyDrawData) },
|
|
{ 26, typeof(MiningToolData) },
|
|
{ 28, typeof(ResourceScannerData) },
|
|
{ 31, typeof(CapacitorData) },
|
|
{ 32, typeof(CockpitData) },
|
|
{ 33, typeof(HeatStorageData) },
|
|
{ 34, typeof(TurretControllerData) },
|
|
{ 35, typeof(InstantWeaponData) },
|
|
{ 36, typeof(ConstantWeaponData) },
|
|
{ 37, typeof(ChargedWeaponData) },
|
|
{ 38, typeof(AutoWeaponData) }
|
|
};
|
|
|
|
public AetheriaRuntimeItemCatalog(AetheriaRuntimeCatalogSnapshot catalog)
|
|
{
|
|
if (catalog == null) throw new ArgumentNullException(nameof(catalog));
|
|
|
|
_typedItems = catalog.Items
|
|
.Where(item => Guid.TryParse(item.LegacyId, out _))
|
|
.ToDictionary(item => Guid.Parse(item.LegacyId), item => item);
|
|
}
|
|
|
|
public AetheriaRuntimeCatalogItem GetRuntimeItem(Guid guid)
|
|
{
|
|
_typedItems.TryGetValue(guid, out var item);
|
|
return item;
|
|
}
|
|
|
|
public IReadOnlyList<BehaviorData> GetBehaviorProjections(Guid guid)
|
|
{
|
|
var item = GetRuntimeItem(guid);
|
|
return item == null
|
|
? Array.Empty<BehaviorData>()
|
|
: ProjectBehaviors(item);
|
|
}
|
|
|
|
private static BehaviorData[] ProjectBehaviors(AetheriaRuntimeCatalogItem item)
|
|
{
|
|
return item.BehaviorPayloads
|
|
.Select(ProjectBehavior)
|
|
.Where(behavior => behavior != null)
|
|
.ToArray();
|
|
}
|
|
|
|
private static PerformanceStat ProjectPerformanceStat(AetheriaRuntimePerformanceStat stat)
|
|
{
|
|
return new PerformanceStat
|
|
{
|
|
Min = (float)stat.Min,
|
|
Max = (float)stat.Max,
|
|
HeatExponentMultiplier = (float)stat.HeatExponentMultiplier,
|
|
DurabilityExponentMultiplier = (float)stat.DurabilityExponentMultiplier,
|
|
QualityExponent = (float)stat.QualityExponent
|
|
};
|
|
}
|
|
|
|
private static BehaviorData ProjectBehavior(AetheriaRuntimeBehaviorPayload payload)
|
|
{
|
|
if (!BehaviorTypesByUnionKey.TryGetValue(payload.UnionKey, out var behaviorType))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var behavior = Activator.CreateInstance(behaviorType) as BehaviorData;
|
|
if (behavior == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
behavior.Group = payload.Group;
|
|
|
|
foreach (var field in GetKeyedFields(behaviorType))
|
|
{
|
|
var payloadField = payload.Fields.FirstOrDefault(candidate => candidate.Key == field.key);
|
|
if (payloadField == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
field.field.SetValue(behavior, ConvertValue(payloadField.Value, field.field.FieldType));
|
|
}
|
|
|
|
return behavior;
|
|
}
|
|
|
|
private static IEnumerable<(int key, FieldInfo field)> GetKeyedFields(Type type)
|
|
{
|
|
return type
|
|
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
|
.Select(field => (attribute: field.GetCustomAttribute<LegacyPayloadKeyAttribute>(), field))
|
|
.Where(entry => entry.attribute != null)
|
|
.Select(entry => (entry.attribute.Key, entry.field));
|
|
}
|
|
|
|
private static object ConvertValue(AetheriaRuntimeBehaviorValue value, Type targetType)
|
|
{
|
|
if (targetType == typeof(string)) return value.StringValue ?? "";
|
|
if (targetType == typeof(bool)) return value.BoolValue;
|
|
if (targetType == typeof(float)) return (float)value.NumberValue;
|
|
if (targetType == typeof(double)) return value.NumberValue;
|
|
if (targetType == typeof(int)) return checked((int)value.NumberValue);
|
|
if (targetType == typeof(uint)) return checked((uint)value.NumberValue);
|
|
if (targetType == typeof(Guid)) return ParseGuid(value.LegacyIdValue);
|
|
if (targetType == typeof(float2)) return ConvertFloat2(value);
|
|
if (targetType == typeof(float3)) return ConvertFloat3(value);
|
|
if (targetType == typeof(float4)) return ConvertFloat4(value);
|
|
if (targetType == typeof(int2)) return ConvertInt2(value);
|
|
if (targetType.IsEnum) return ParseEnumValue(value, targetType);
|
|
|
|
if (targetType.IsArray)
|
|
{
|
|
return ConvertArray(value, targetType.GetElementType());
|
|
}
|
|
|
|
if (typeof(IList).IsAssignableFrom(targetType) && targetType.IsGenericType)
|
|
{
|
|
return ConvertList(value, targetType);
|
|
}
|
|
|
|
if (GetKeyedFields(targetType).Any())
|
|
{
|
|
return ConvertKeyedObject(value, targetType);
|
|
}
|
|
|
|
return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
|
|
}
|
|
|
|
private static object ConvertArray(AetheriaRuntimeBehaviorValue value, Type elementType)
|
|
{
|
|
var array = Array.CreateInstance(elementType, value.Children.Count);
|
|
for (var index = 0; index < value.Children.Count; index++)
|
|
{
|
|
array.SetValue(ConvertValue(value.Children[index], elementType), index);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
private static object ConvertList(AetheriaRuntimeBehaviorValue value, Type targetType)
|
|
{
|
|
var elementType = targetType.GetGenericArguments()[0];
|
|
var list = Activator.CreateInstance(targetType) as IList;
|
|
foreach (var child in value.Children)
|
|
{
|
|
list.Add(ConvertValue(child, elementType));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private static object ConvertKeyedObject(AetheriaRuntimeBehaviorValue value, Type targetType)
|
|
{
|
|
var instance = Activator.CreateInstance(targetType);
|
|
var children = value.Children;
|
|
foreach (var field in GetKeyedFields(targetType))
|
|
{
|
|
if (field.key >= 0 && field.key < children.Count)
|
|
{
|
|
field.field.SetValue(instance, ConvertValue(children[field.key], field.field.FieldType));
|
|
}
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
private static object ParseEnumValue(AetheriaRuntimeBehaviorValue value, Type targetType)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(value.StringValue))
|
|
{
|
|
return Enum.Parse(targetType, value.StringValue, true);
|
|
}
|
|
|
|
return Enum.ToObject(targetType, checked((int)value.NumberValue));
|
|
}
|
|
|
|
private static Guid ParseGuid(string value)
|
|
{
|
|
return Guid.TryParse(value, out var result) ? result : Guid.Empty;
|
|
}
|
|
|
|
private static float2 ConvertFloat2(AetheriaRuntimeBehaviorValue value)
|
|
{
|
|
return new float2(
|
|
value.Children.Count > 0 ? (float)value.Children[0].NumberValue : 0,
|
|
value.Children.Count > 1 ? (float)value.Children[1].NumberValue : 0);
|
|
}
|
|
|
|
private static float3 ConvertFloat3(AetheriaRuntimeBehaviorValue value)
|
|
{
|
|
return new float3(
|
|
value.Children.Count > 0 ? (float)value.Children[0].NumberValue : 0,
|
|
value.Children.Count > 1 ? (float)value.Children[1].NumberValue : 0,
|
|
value.Children.Count > 2 ? (float)value.Children[2].NumberValue : 0);
|
|
}
|
|
|
|
private static float4 ConvertFloat4(AetheriaRuntimeBehaviorValue value)
|
|
{
|
|
return new float4(
|
|
value.Children.Count > 0 ? (float)value.Children[0].NumberValue : 0,
|
|
value.Children.Count > 1 ? (float)value.Children[1].NumberValue : 0,
|
|
value.Children.Count > 2 ? (float)value.Children[2].NumberValue : 0,
|
|
value.Children.Count > 3 ? (float)value.Children[3].NumberValue : 0);
|
|
}
|
|
|
|
private static int2 ConvertInt2(AetheriaRuntimeBehaviorValue value)
|
|
{
|
|
return new int2(
|
|
value.Children.Count > 0 ? checked((int)value.Children[0].NumberValue) : 0,
|
|
value.Children.Count > 1 ? checked((int)value.Children[1].NumberValue) : 0);
|
|
}
|
|
}
|