Move data and data loading to plugin libraries.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
56
PkmnLib.Dynamic/Libraries/DataLoaders/AbilityDataLoader.cs
Normal file
56
PkmnLib.Dynamic/Libraries/DataLoaders/AbilityDataLoader.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
using PkmnLib.Static.Libraries;
|
||||
using PkmnLib.Static.Species;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
public static class AbilityDataLoader
|
||||
{
|
||||
private static Dictionary<string, SerializedAbility> LoadAbilitiesData(Stream stream)
|
||||
{
|
||||
var obj = JsonSerializer.Deserialize<JsonObject>(stream, JsonOptions.DefaultOptions);
|
||||
if (obj == null)
|
||||
throw new InvalidDataException("Ability data is empty.");
|
||||
obj.Remove("$schema");
|
||||
var cleanedString = obj.ToJsonString();
|
||||
|
||||
var objects =
|
||||
JsonSerializer.Deserialize<Dictionary<string, SerializedAbility>>(cleanedString,
|
||||
JsonOptions.DefaultOptions);
|
||||
if (objects == null)
|
||||
throw new InvalidDataException("Ability data is empty.");
|
||||
return objects;
|
||||
}
|
||||
|
||||
public static AbilityLibrary LoadAbilities(Stream stream,
|
||||
Action<Dictionary<string, SerializedAbility>>? action = null)
|
||||
{
|
||||
var library = new AbilityLibrary();
|
||||
var objects = LoadAbilitiesData(stream);
|
||||
if (objects == null)
|
||||
throw new InvalidDataException("Ability data is empty.");
|
||||
action?.Invoke(objects);
|
||||
|
||||
var abilities = objects.Select(x => DeserializeAbility(x.Key, x.Value));
|
||||
foreach (var a in abilities)
|
||||
library.Add(a);
|
||||
return library;
|
||||
}
|
||||
|
||||
private static AbilityImpl DeserializeAbility(string name, SerializedAbility serialized)
|
||||
{
|
||||
var effect = serialized.Effect;
|
||||
var parameters = serialized.Parameters.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter());
|
||||
|
||||
StringKey? effectName = effect == null ? null! : new StringKey(effect);
|
||||
|
||||
var flags = serialized.Flags.Select(x => new StringKey(x)).ToImmutableHashSet();
|
||||
|
||||
var ability = new AbilityImpl(name, effectName, parameters, flags);
|
||||
return ability;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
using PkmnLib.Static.Moves;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
internal static class CommonDataLoaderHelper
|
||||
{
|
||||
internal static ISecondaryEffect? ParseEffect(this SerializedMoveEffect? effect)
|
||||
{
|
||||
if (effect == null)
|
||||
return null;
|
||||
var name = effect.Name;
|
||||
var chance = effect.Chance ?? -1;
|
||||
var parameters = effect.Parameters?.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter()) ??
|
||||
new Dictionary<StringKey, object?>();
|
||||
return new SecondaryEffectImpl(chance, name, parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Text.Json;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Libraries;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
public static class GrowthRateDataLoader
|
||||
{
|
||||
public static GrowthRateLibrary LoadGrowthRates(Stream stream, Action<List<IGrowthRate>>? action = null)
|
||||
{
|
||||
var objects = JsonSerializer.Deserialize<Dictionary<string, uint[]>>(stream, JsonOptions.DefaultOptions)!;
|
||||
var growthRates = objects.Select(x => new LookupGrowthRate(x.Key, x.Value)).Cast<IGrowthRate>().ToList();
|
||||
action?.Invoke(growthRates);
|
||||
|
||||
var library = new GrowthRateLibrary();
|
||||
foreach (var growthRate in growthRates)
|
||||
{
|
||||
library.Add(growthRate);
|
||||
}
|
||||
return library;
|
||||
}
|
||||
}
|
||||
51
PkmnLib.Dynamic/Libraries/DataLoaders/ItemDataLoader.cs
Normal file
51
PkmnLib.Dynamic/Libraries/DataLoaders/ItemDataLoader.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using JetBrains.Annotations;
|
||||
using PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Libraries;
|
||||
using PkmnLib.Static.Moves;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
public static class ItemDataLoader
|
||||
{
|
||||
public static ItemLibrary LoadItems(Stream stream, Action<List<SerializedItem>>? onAfterLoad = null)
|
||||
{
|
||||
var library = new ItemLibrary();
|
||||
var obj = JsonSerializer.Deserialize<List<SerializedItem>>(stream, JsonOptions.DefaultOptions);
|
||||
if (obj == null)
|
||||
throw new InvalidDataException("Item data is empty.");
|
||||
onAfterLoad?.Invoke(obj);
|
||||
var items = obj.Select(DeserializeItem);
|
||||
foreach (var i in items)
|
||||
library.Add(i);
|
||||
return library;
|
||||
}
|
||||
|
||||
public delegate IItem ItemFactoryDelegate(SerializedItem serialized, StringKey name, ItemCategory type,
|
||||
BattleItemCategory battleType, int price, ImmutableHashSet<StringKey> flags, ISecondaryEffect? effect,
|
||||
ISecondaryEffect? battleTriggerEffect, Dictionary<StringKey, object?> additionalData);
|
||||
|
||||
[PublicAPI]
|
||||
public static ItemFactoryDelegate ItemConstructor { get; set; } =
|
||||
(_, name, type, battleType, price, flags, effect, battleTriggerEffect, additionalData) => new ItemImpl(name,
|
||||
type, battleType, price, flags, effect, battleTriggerEffect, additionalData);
|
||||
|
||||
private static IItem DeserializeItem(SerializedItem serialized)
|
||||
{
|
||||
if (!Enum.TryParse<ItemCategory>(serialized.ItemType, true, out var itemType))
|
||||
throw new InvalidDataException($"Item type {serialized.ItemType} is not valid for item {serialized.Name}.");
|
||||
Enum.TryParse(serialized.BattleType, true, out BattleItemCategory battleType);
|
||||
var effect = serialized.Effect?.ParseEffect();
|
||||
var battleTriggerEffect = serialized.BattleEffect?.ParseEffect();
|
||||
var additionalData =
|
||||
serialized.AdditionalData?.ToDictionary(x => (StringKey)x.Key, x => x.Value.ToParameter()) ??
|
||||
new Dictionary<StringKey, object?>();
|
||||
|
||||
return ItemConstructor(serialized, serialized.Name, itemType, battleType, serialized.Price,
|
||||
serialized.Flags.Select(x => (StringKey)x).ToImmutableHashSet(), effect, battleTriggerEffect,
|
||||
additionalData);
|
||||
}
|
||||
}
|
||||
13
PkmnLib.Dynamic/Libraries/DataLoaders/JsonOptions.cs
Normal file
13
PkmnLib.Dynamic/Libraries/DataLoaders/JsonOptions.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
internal static class JsonOptions
|
||||
{
|
||||
public static JsonSerializerOptions DefaultOptions => new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
AllowTrailingCommas = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
||||
};
|
||||
}
|
||||
38
PkmnLib.Dynamic/Libraries/DataLoaders/JsonParameterLoader.cs
Normal file
38
PkmnLib.Dynamic/Libraries/DataLoaders/JsonParameterLoader.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
internal static class JsonParameterLoader
|
||||
{
|
||||
internal static object? ToParameter(this JsonNode node)
|
||||
{
|
||||
switch (node.GetValueKind())
|
||||
{
|
||||
case JsonValueKind.Undefined:
|
||||
throw new InvalidOperationException("Undefined value.");
|
||||
case JsonValueKind.Object:
|
||||
return node.AsObject().ToDictionary(x => (StringKey)x.Key, x => x.Value?.ToParameter());
|
||||
case JsonValueKind.Array:
|
||||
return node.AsArray().Select(x => x?.ToParameter()).ToList();
|
||||
case JsonValueKind.String:
|
||||
return node.GetValue<string>();
|
||||
case JsonValueKind.Number:
|
||||
var element = node.GetValue<JsonElement>();
|
||||
if (element.TryGetInt32(out var v))
|
||||
return v;
|
||||
if (element.TryGetSingle(out var f))
|
||||
return f;
|
||||
throw new InvalidOperationException("Number is not an integer or a float.");
|
||||
case JsonValueKind.True:
|
||||
return true;
|
||||
case JsonValueKind.False:
|
||||
return false;
|
||||
case JsonValueKind.Null:
|
||||
return null;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
|
||||
public class SerializedAbility
|
||||
{
|
||||
public string? Effect { get; set; }
|
||||
public Dictionary<string, JsonNode> Parameters { get; set; } = new();
|
||||
public string[] Flags { get; set; } = [];
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
|
||||
public class SerializedItem
|
||||
{
|
||||
public string Name { get; set; } = null!;
|
||||
public string ItemType { get; set; } = null!;
|
||||
public string BattleType { get; set; } = null!;
|
||||
public string[] Flags { get; set; } = null!;
|
||||
public int Price { get; set; }
|
||||
public SerializedMoveEffect? Effect { get; set; }
|
||||
public SerializedMoveEffect? BattleEffect { get; set; }
|
||||
|
||||
public Dictionary<string, JsonNode>? AdditionalData { get; set; } = null!;
|
||||
|
||||
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
|
||||
public class SerializedMoveDataWrapper
|
||||
{
|
||||
public SerializedMove[] Data { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class SerializedMove
|
||||
{
|
||||
public string Name { get; set; } = null!;
|
||||
public string Type { get; set; } = null!;
|
||||
public byte Power { get; set; }
|
||||
public byte PP { get; set; }
|
||||
public byte Accuracy { get; set; }
|
||||
public sbyte Priority { get; set; }
|
||||
public string Target { get; set; } = null!;
|
||||
public string Category { get; set; } = null!;
|
||||
public string[] Flags { get; set; } = null!;
|
||||
public SerializedMoveEffect? Effect { get; set; }
|
||||
|
||||
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
}
|
||||
|
||||
public class SerializedMoveEffect
|
||||
{
|
||||
public string Name { get; set; } = null!;
|
||||
public float? Chance { get; set; }
|
||||
public Dictionary<string, JsonNode>? Parameters { get; set; } = null!;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
|
||||
public class SerializedSpecies
|
||||
{
|
||||
public string Species { get; set; } = null!;
|
||||
public ushort Id { get; set; }
|
||||
public float GenderRatio { get; set; }
|
||||
public string GrowthRate { get; set; } = null!;
|
||||
public byte BaseHappiness { get; set; }
|
||||
public byte CatchRate { get; set; }
|
||||
public string Color { get; set; } = null!;
|
||||
public bool GenderDifference { get; set; }
|
||||
public string[] EggGroups { get; set; } = null!;
|
||||
public int EggCycles { get; set; }
|
||||
public string[] Flags { get; set; } = [];
|
||||
public Dictionary<string, SerializedForm> Formes { get; set; } = null!;
|
||||
public SerializedEvolution[] Evolutions { get; set; } = [];
|
||||
|
||||
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
}
|
||||
|
||||
public class SerializedForm
|
||||
{
|
||||
public string[] Abilities { get; set; } = null!;
|
||||
public string[] HiddenAbilities { get; set; } = [];
|
||||
public SerializedStats BaseStats { get; set; } = null!;
|
||||
public SerializedStats EVReward { get; set; } = null!;
|
||||
public string[] Types { get; set; } = null!;
|
||||
public float Height { get; set; }
|
||||
public float Weight { get; set; }
|
||||
public uint BaseExp { get; set; }
|
||||
public bool IsMega { get; set; }
|
||||
public SerializedMoves Moves { get; set; } = null!;
|
||||
public string[] Flags { get; set; } = [];
|
||||
|
||||
[JsonExtensionData] public Dictionary<string, JsonElement>? ExtensionData { get; set; }
|
||||
}
|
||||
|
||||
public class SerializedEvolution
|
||||
{
|
||||
public string Species { get; set; } = null!;
|
||||
public string Method { get; set; } = null!;
|
||||
public JsonNode Data { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class SerializedStats
|
||||
{
|
||||
public ushort Hp { get; set; }
|
||||
public ushort Attack { get; set; }
|
||||
public ushort Defense { get; set; }
|
||||
public ushort SpecialAttack { get; set; }
|
||||
public ushort SpecialDefense { get; set; }
|
||||
public ushort Speed { get; set; }
|
||||
}
|
||||
|
||||
public class SerializedLevelMove
|
||||
{
|
||||
public string Name { get; set; } = null!;
|
||||
public uint Level { get; set; }
|
||||
}
|
||||
|
||||
public class SerializedMoves
|
||||
{
|
||||
public SerializedLevelMove[]? LevelMoves { get; set; } = null!;
|
||||
public string[]? EggMoves { get; set; } = null!;
|
||||
public string[]? TutorMoves { get; set; } = null!;
|
||||
public string[]? Machine { get; set; } = null!;
|
||||
}
|
||||
64
PkmnLib.Dynamic/Libraries/DataLoaders/MoveDataLoader.cs
Normal file
64
PkmnLib.Dynamic/Libraries/DataLoaders/MoveDataLoader.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using JetBrains.Annotations;
|
||||
using PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Libraries;
|
||||
using PkmnLib.Static.Moves;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
public static class MoveDataLoader
|
||||
{
|
||||
public static MoveLibrary LoadMoves(Stream stream, TypeLibrary typeLibrary,
|
||||
Action<SerializedMoveDataWrapper>? onAfterLoad = null)
|
||||
{
|
||||
var library = new MoveLibrary();
|
||||
var objects = JsonSerializer.Deserialize<SerializedMoveDataWrapper>(stream, JsonOptions.DefaultOptions);
|
||||
if (objects == null)
|
||||
throw new InvalidDataException("Move data is empty.");
|
||||
onAfterLoad?.Invoke(objects);
|
||||
var moves = objects.Data.Select(x => DeserializeMove(x, typeLibrary));
|
||||
foreach (var m in moves)
|
||||
library.Add(m);
|
||||
return library;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static
|
||||
Func<SerializedMove, StringKey, TypeIdentifier, MoveCategory, byte, byte, byte, MoveTarget, sbyte,
|
||||
ISecondaryEffect?
|
||||
, IEnumerable<StringKey>, MoveDataImpl> MoveConstructor =
|
||||
(_, name, moveType, category, basePower, accuracy, baseUsages, target, priority, secondaryEffect, flags) =>
|
||||
new MoveDataImpl(name, moveType, category, basePower, accuracy, baseUsages, target, priority,
|
||||
secondaryEffect, flags);
|
||||
|
||||
private static MoveDataImpl DeserializeMove(SerializedMove serialized, TypeLibrary typeLibrary)
|
||||
{
|
||||
var type = serialized.Type;
|
||||
var power = serialized.Power;
|
||||
var pp = serialized.PP;
|
||||
var accuracy = serialized.Accuracy;
|
||||
var priority = serialized.Priority;
|
||||
var target = serialized.Target;
|
||||
var category = serialized.Category;
|
||||
var flags = serialized.Flags;
|
||||
var effect = serialized.Effect;
|
||||
|
||||
if (target.Equals("self", StringComparison.InvariantCultureIgnoreCase))
|
||||
target = "selfUse";
|
||||
|
||||
if (!typeLibrary.TryGetTypeIdentifier(type, out var typeIdentifier))
|
||||
throw new InvalidDataException($"Type {type} is not a valid type.");
|
||||
if (!Enum.TryParse<MoveCategory>(category, true, out var categoryEnum))
|
||||
throw new InvalidDataException($"Category {category} is not a valid category.");
|
||||
if (!Enum.TryParse<MoveTarget>(target, true, out var targetEnum))
|
||||
throw new InvalidDataException($"Target {target} is not a valid target.");
|
||||
var secondaryEffect = effect.ParseEffect();
|
||||
|
||||
var move = MoveConstructor(serialized, serialized.Name, typeIdentifier, categoryEnum, power, accuracy, pp,
|
||||
targetEnum, priority, secondaryEffect, flags.Select(x => (StringKey)x).ToImmutableHashSet());
|
||||
return move;
|
||||
}
|
||||
}
|
||||
57
PkmnLib.Dynamic/Libraries/DataLoaders/NatureDataLoader.cs
Normal file
57
PkmnLib.Dynamic/Libraries/DataLoaders/NatureDataLoader.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Libraries;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
public static class NatureDataLoader
|
||||
{
|
||||
private static readonly char[] CommonCsvDelimiters = ['|', ','];
|
||||
|
||||
public static NatureLibrary LoadNatureLibrary(Stream stream)
|
||||
{
|
||||
var library = new NatureLibrary();
|
||||
|
||||
using var reader = new StreamReader(stream);
|
||||
var header = reader.ReadLine();
|
||||
if (header == null)
|
||||
throw new InvalidDataException("Type data is empty.");
|
||||
var delimiter = CommonCsvDelimiters.FirstOrDefault(header.Contains);
|
||||
if (delimiter == default)
|
||||
throw new InvalidDataException("No valid delimiter found in type data.");
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line == null)
|
||||
break;
|
||||
var values = line.Split(delimiter)!;
|
||||
var nature = values[0];
|
||||
var increasedStat = values[1];
|
||||
var decreasedStat = values[2];
|
||||
|
||||
var increasedModifier = 1.1f;
|
||||
var decreasedModifier = 0.9f;
|
||||
|
||||
if (increasedStat == string.Empty)
|
||||
{
|
||||
increasedStat = "Hp";
|
||||
increasedModifier = 1.0f;
|
||||
}
|
||||
|
||||
if (decreasedStat == string.Empty)
|
||||
{
|
||||
decreasedStat = "Hp";
|
||||
decreasedModifier = 1.0f;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse<Statistic>(increasedStat, out var increasedStatEnum))
|
||||
throw new InvalidDataException($"Increased stat {increasedStat} is not a valid stat.");
|
||||
if (!Enum.TryParse<Statistic>(decreasedStat, out var decreasedStatEnum))
|
||||
throw new InvalidDataException($"Decreased stat {decreasedStat} is not a valid stat.");
|
||||
|
||||
library.Add(new Nature(nature, increasedStatEnum, decreasedStatEnum, increasedModifier, decreasedModifier));
|
||||
}
|
||||
|
||||
return library;
|
||||
}
|
||||
}
|
||||
227
PkmnLib.Dynamic/Libraries/DataLoaders/SpeciesDataLoader.cs
Normal file
227
PkmnLib.Dynamic/Libraries/DataLoaders/SpeciesDataLoader.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using PkmnLib.Dynamic.Libraries.DataLoaders.Models;
|
||||
using PkmnLib.Static;
|
||||
using PkmnLib.Static.Libraries;
|
||||
using PkmnLib.Static.Species;
|
||||
using PkmnLib.Static.Utils;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
public static class SpeciesDataLoader
|
||||
{
|
||||
private static Dictionary<string, SerializedSpecies> LoadSpeciesData(Stream stream)
|
||||
{
|
||||
var obj = JsonSerializer.Deserialize<JsonObject>(stream, JsonOptions.DefaultOptions);
|
||||
if (obj == null)
|
||||
throw new InvalidDataException("Species data is empty.");
|
||||
return obj.Where(x => x.Key != "$schema").ToDictionary(x => x.Key,
|
||||
x => x.Value.Deserialize<SerializedSpecies>(JsonOptions.DefaultOptions))!;
|
||||
}
|
||||
|
||||
public static SpeciesLibrary LoadSpecies(Stream stream, IReadOnlyTypeLibrary typeLibrary,
|
||||
Action<Dictionary<string, SerializedSpecies>>? action = null)
|
||||
{
|
||||
var library = new SpeciesLibrary();
|
||||
var objects = LoadSpeciesData(stream);
|
||||
if (objects == null)
|
||||
throw new InvalidDataException("Species data is empty.");
|
||||
action?.Invoke(objects);
|
||||
var species = objects.Select(x => DeserializeSpecies(x.Value, typeLibrary));
|
||||
foreach (var s in species)
|
||||
library.Add(s);
|
||||
return library;
|
||||
}
|
||||
|
||||
public static
|
||||
Func<SerializedSpecies, ushort, StringKey, float, StringKey, byte, byte, IReadOnlyDictionary<StringKey, IForm>,
|
||||
IEnumerable<StringKey>, IReadOnlyList<IEvolution>, IEnumerable<StringKey>, SpeciesImpl> SpeciesConstructor =
|
||||
(_, id, name, genderRate, growthRate, captureRate, baseHappiness, forms, flags, evolutionData, eggGroups) =>
|
||||
{
|
||||
return new SpeciesImpl(id, name, genderRate, growthRate, captureRate, baseHappiness, forms, flags,
|
||||
evolutionData, eggGroups);
|
||||
};
|
||||
|
||||
private static SpeciesImpl DeserializeSpecies(SerializedSpecies serialized, IReadOnlyTypeLibrary typeLibrary)
|
||||
{
|
||||
var id = serialized.Id;
|
||||
var genderRate = serialized.GenderRatio;
|
||||
if (genderRate < -1.0 || genderRate > 100.0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Gender rate for species {id} is invalid: {genderRate}. Must be between -1.0 and 100.0.");
|
||||
}
|
||||
|
||||
if (serialized.EggCycles < 0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Egg cycles for species {id} is invalid: {serialized.EggCycles}. Must be greater than or equal to 0.");
|
||||
}
|
||||
|
||||
var forms = serialized.Formes.ToDictionary(x => (StringKey)x.Key,
|
||||
x => DeserializeForm(x.Key, x.Value, typeLibrary));
|
||||
var evolutions = serialized.Evolutions.Select(DeserializeEvolution).ToList();
|
||||
|
||||
var species = SpeciesConstructor(serialized, serialized.Id, serialized.Species, genderRate,
|
||||
serialized.GrowthRate, serialized.CatchRate, serialized.BaseHappiness, forms,
|
||||
serialized.Flags.Select(x => new StringKey(x)), evolutions, serialized.EggGroups.Select(x => (StringKey)x));
|
||||
return species;
|
||||
}
|
||||
|
||||
private static IForm DeserializeForm(string name, SerializedForm form, IReadOnlyTypeLibrary typeLibrary)
|
||||
{
|
||||
if (form == null)
|
||||
throw new ArgumentException("Form data is null.", nameof(form));
|
||||
if (form.Height < 0.0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Height for form {name} is invalid: {form.Height}. Must be greater than or equal to 0.0.");
|
||||
}
|
||||
if (form.Weight < 0.0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Weight for form {name} is invalid: {form.Weight}. Must be greater than or equal to 0.0.");
|
||||
}
|
||||
var types = form.Types.Select(x =>
|
||||
typeLibrary.TryGetTypeIdentifier(new StringKey(x), out var t)
|
||||
? t
|
||||
: throw new InvalidDataException($"Type {x} for form {name} is invalid.")).ToList();
|
||||
|
||||
return new FormImpl(name, form.Height, form.Weight, form.BaseExp, types, DeserializeStats(form.BaseStats),
|
||||
form.Abilities.Select(x => new StringKey(x)).ToList(),
|
||||
form.HiddenAbilities.Select(x => new StringKey(x)).ToList(), DeserializeMoves(form.Moves),
|
||||
form.Flags.Select(x => new StringKey(x)).ToImmutableHashSet());
|
||||
}
|
||||
|
||||
private static ILearnableMoves DeserializeMoves(SerializedMoves moves)
|
||||
{
|
||||
var learnableMoves = new LearnableMovesImpl();
|
||||
if (moves.LevelMoves != null)
|
||||
{
|
||||
foreach (var levelMove in moves.LevelMoves)
|
||||
{
|
||||
learnableMoves.AddLevelMove((byte)levelMove.Level, new StringKey(levelMove.Name));
|
||||
}
|
||||
}
|
||||
|
||||
if (moves.EggMoves != null)
|
||||
{
|
||||
foreach (var eggMove in moves.EggMoves)
|
||||
{
|
||||
learnableMoves.AddEggMove(new StringKey(eggMove));
|
||||
}
|
||||
}
|
||||
|
||||
return learnableMoves;
|
||||
}
|
||||
|
||||
private static ImmutableStatisticSet<ushort> DeserializeStats(SerializedStats stats) =>
|
||||
new(stats.Hp, stats.Attack, stats.Defense, stats.SpecialAttack, stats.SpecialDefense, stats.Speed);
|
||||
|
||||
private static IEvolution DeserializeEvolution(SerializedEvolution evolution)
|
||||
{
|
||||
return evolution.Method.ToLowerInvariant() switch
|
||||
{
|
||||
"level" => new LevelEvolution
|
||||
{
|
||||
Level = evolution.Data.GetValue<byte>(),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"levelfemale" => new LevelGenderEvolution
|
||||
{
|
||||
Level = evolution.Data.GetValue<byte>(),
|
||||
Gender = Gender.Female,
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"levelmale" => new LevelGenderEvolution
|
||||
{
|
||||
Level = evolution.Data.GetValue<byte>(),
|
||||
Gender = Gender.Male,
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"happiness" => new HappinessEvolution
|
||||
{
|
||||
Happiness = evolution.Data.GetValue<byte>(),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"happinessday" => new HappinessDayEvolution
|
||||
{
|
||||
Happiness = evolution.Data.GetValue<byte>(),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"happinessnight" => new HappinessNightEvolution
|
||||
{
|
||||
Happiness = evolution.Data.GetValue<byte>(),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"item" => new ItemUseEvolution
|
||||
{
|
||||
Item = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Item is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"itemmale" => new ItemGenderEvolution
|
||||
{
|
||||
Item = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Item is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"itemfemale" => new ItemGenderEvolution
|
||||
{
|
||||
Item = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Item is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"holditem" => new HoldItemEvolution
|
||||
{
|
||||
Item = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Item is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"dayholditem" => new DayHoldItemEvolution
|
||||
{
|
||||
Item = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Item is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"nightholditem" => new NightHoldItemEvolution
|
||||
{
|
||||
Item = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Item is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"hasmove" => new HasMoveEvolution
|
||||
{
|
||||
MoveName = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Move is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"trade" => new TradeEvolution
|
||||
{
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"tradespecies" => new TradeSpeciesEvolution
|
||||
{
|
||||
WithSpecies = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Species is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"tradeitem" => new TradeItemEvolution
|
||||
{
|
||||
Item = evolution.Data.GetValue<string>() ?? throw new InvalidDataException("Item is null."),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"location" => new CustomEvolution
|
||||
{
|
||||
Name = "location",
|
||||
Parameters = new Dictionary<StringKey, object?>
|
||||
{
|
||||
["location"] = evolution.Data.ToString() ?? throw new InvalidDataException("Location is null."),
|
||||
},
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
"custom" => new CustomEvolution
|
||||
{
|
||||
Name = evolution.Data.AsObject()["type"]?.GetValue<string>() ??
|
||||
throw new InvalidDataException("Type is null."),
|
||||
Parameters = evolution.Data.AsObject().Where(x => x.Key != "type")
|
||||
.ToDictionary(x => new StringKey(x.Key), x => x.Value!.ToParameter()),
|
||||
ToSpecies = evolution.Species,
|
||||
},
|
||||
_ => throw new InvalidDataException($"Evolution type {evolution.Method} is invalid."),
|
||||
};
|
||||
}
|
||||
}
|
||||
52
PkmnLib.Dynamic/Libraries/DataLoaders/TypeDataLoader.cs
Normal file
52
PkmnLib.Dynamic/Libraries/DataLoaders/TypeDataLoader.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using PkmnLib.Static.Libraries;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
|
||||
public static class TypeDataLoader
|
||||
{
|
||||
private static readonly char[] CommonCsvDelimiters = ['|', ','];
|
||||
|
||||
public static TypeLibrary LoadTypeLibrary(Stream stream)
|
||||
{
|
||||
var library = new TypeLibrary();
|
||||
|
||||
using var reader = new StreamReader(stream);
|
||||
var header = reader.ReadLine();
|
||||
if (header == null)
|
||||
throw new InvalidDataException("Type data is empty.");
|
||||
var delimiter = CommonCsvDelimiters.FirstOrDefault(header.Contains);
|
||||
if (delimiter == default)
|
||||
throw new InvalidDataException("No valid delimiter found in type data.");
|
||||
|
||||
var types = header.Split(delimiter, StringSplitOptions.RemoveEmptyEntries)!;
|
||||
if (!types.Any())
|
||||
throw new InvalidDataException("No types found in type data.");
|
||||
|
||||
foreach (var type in types.Skip(1))
|
||||
library.RegisterType(type);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line == null)
|
||||
break;
|
||||
var values = line.Split(delimiter, StringSplitOptions.RemoveEmptyEntries)!;
|
||||
var type = values[0];
|
||||
if (!library.TryGetTypeIdentifier(type, out var typeId))
|
||||
throw new InvalidDataException($"Type {type} not found in type library.");
|
||||
for (var i = 1; i < values.Length; i++)
|
||||
{
|
||||
var effectiveness = float.Parse(values[i]);
|
||||
if (effectiveness < 0.0)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Effectiveness for {type} against {types[i]} is invalid: {effectiveness}. Must be greater than or equal to 0.0.");
|
||||
}
|
||||
library.TryGetTypeIdentifier(types[i], out var defendingTypeId);
|
||||
library.SetEffectiveness(typeId, defendingTypeId, effectiveness);
|
||||
}
|
||||
}
|
||||
|
||||
return library;
|
||||
}
|
||||
}
|
||||
@@ -50,24 +50,12 @@ public class DynamicLibraryImpl : IDynamicLibrary
|
||||
/// Initializes a new instance of the <see cref="DynamicLibraryImpl"/> class, with the given
|
||||
/// plugins and static library.
|
||||
/// </summary>
|
||||
public static IDynamicLibrary Create(IStaticLibrary staticLibrary, IEnumerable<Plugin> plugins)
|
||||
public static IDynamicLibrary Create(IEnumerable<Plugin> plugins)
|
||||
{
|
||||
var registry = new ScriptRegistry();
|
||||
foreach (var plugin in plugins.OrderBy(x => x.LoadOrder))
|
||||
{
|
||||
plugin.Register(registry);
|
||||
}
|
||||
if (registry.DamageCalculator is null)
|
||||
throw new InvalidOperationException("Damage calculator not found in plugins.");
|
||||
if (registry.BattleStatCalculator is null)
|
||||
throw new InvalidOperationException("Stat calculator not found in plugins.");
|
||||
if (registry.MiscLibrary is null)
|
||||
throw new InvalidOperationException("Misc library not found in plugins.");
|
||||
if (registry.CaptureLibrary is null)
|
||||
throw new InvalidOperationException("Capture library not found in plugins.");
|
||||
var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes);
|
||||
return new DynamicLibraryImpl(staticLibrary, registry.BattleStatCalculator, registry.DamageCalculator,
|
||||
registry.MiscLibrary, registry.CaptureLibrary, scriptResolver);
|
||||
var load = LibraryLoader.LoadPlugins(plugins);
|
||||
|
||||
return new DynamicLibraryImpl(load.staticLibrary, load.registry.BattleStatCalculator!,
|
||||
load.registry.DamageCalculator!, load.registry.MiscLibrary!, load.registry.CaptureLibrary!, load.resolver);
|
||||
}
|
||||
|
||||
private DynamicLibraryImpl(IStaticLibrary staticLibrary, IBattleStatCalculator statCalculator,
|
||||
|
||||
87
PkmnLib.Dynamic/Libraries/LibraryLoader.cs
Normal file
87
PkmnLib.Dynamic/Libraries/LibraryLoader.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using PkmnLib.Dynamic.Libraries.DataLoaders;
|
||||
using PkmnLib.Dynamic.ScriptHandling;
|
||||
using PkmnLib.Dynamic.ScriptHandling.Registry;
|
||||
using PkmnLib.Static.Libraries;
|
||||
|
||||
namespace PkmnLib.Dynamic.Libraries;
|
||||
|
||||
public static class LibraryLoader
|
||||
{
|
||||
public record LoadResult(ScriptRegistry registry, ScriptResolver resolver, IStaticLibrary staticLibrary);
|
||||
|
||||
public static LoadResult LoadPlugins(IEnumerable<Plugin> plugins)
|
||||
{
|
||||
var registry = new ScriptRegistry();
|
||||
var orderedPlugins = plugins.OrderBy(x => x.LoadOrder).ToList();
|
||||
var staticLibrary = CreateStaticLibrary(orderedPlugins);
|
||||
foreach (var plugin in orderedPlugins)
|
||||
{
|
||||
plugin.Register(registry);
|
||||
}
|
||||
if (registry.DamageCalculator is null)
|
||||
throw new InvalidOperationException("Damage calculator not found in plugins.");
|
||||
if (registry.BattleStatCalculator is null)
|
||||
throw new InvalidOperationException("Stat calculator not found in plugins.");
|
||||
if (registry.MiscLibrary is null)
|
||||
throw new InvalidOperationException("Misc library not found in plugins.");
|
||||
if (registry.CaptureLibrary is null)
|
||||
throw new InvalidOperationException("Capture library not found in plugins.");
|
||||
var scriptResolver = new ScriptResolver(registry.ScriptTypes, registry.ItemScriptTypes);
|
||||
return new LoadResult(registry, scriptResolver, staticLibrary);
|
||||
}
|
||||
|
||||
private static StaticLibraryImpl CreateStaticLibrary(IReadOnlyList<Plugin> plugins)
|
||||
{
|
||||
var resourceProviders = plugins.OfType<IResourceProvider>().ToList();
|
||||
var settings = resourceProviders.Select(x => x.Settings).LastOrDefault(x => x != null);
|
||||
if (settings == null)
|
||||
throw new InvalidOperationException("Settings not found.");
|
||||
var typesResult = resourceProviders.Select(x => x.GetResource(ResourceFileType.Types))
|
||||
.LastOrDefault(x => x != null);
|
||||
if (typesResult == null)
|
||||
throw new InvalidOperationException("Types resource not found.");
|
||||
var naturesResult = resourceProviders.Select(x => x.GetResource(ResourceFileType.Natures))
|
||||
.LastOrDefault(x => x != null);
|
||||
if (naturesResult == null)
|
||||
throw new InvalidOperationException("Natures resource not found.");
|
||||
var movesResult = resourceProviders.Select(x => x.GetResource(ResourceFileType.Moves))
|
||||
.LastOrDefault(x => x != null);
|
||||
if (movesResult == null)
|
||||
throw new InvalidOperationException("Moves resource not found.");
|
||||
var itemsResult = resourceProviders.Select(x => x.GetResource(ResourceFileType.Items))
|
||||
.LastOrDefault(x => x != null);
|
||||
if (itemsResult == null)
|
||||
throw new InvalidOperationException("Items resource not found.");
|
||||
var abilitiesResult = resourceProviders.Select(x => x.GetResource(ResourceFileType.Abilities))
|
||||
.LastOrDefault(x => x != null);
|
||||
if (abilitiesResult == null)
|
||||
throw new InvalidOperationException("Abilities resource not found.");
|
||||
var growthRatesResult = resourceProviders.Select(x => x.GetResource(ResourceFileType.GrowthRates))
|
||||
.LastOrDefault(x => x != null);
|
||||
if (growthRatesResult == null)
|
||||
throw new InvalidOperationException("Growth rates resource not found.");
|
||||
var speciesResult = resourceProviders.Select(x => x.GetResource(ResourceFileType.Species))
|
||||
.LastOrDefault(x => x != null);
|
||||
if (speciesResult == null)
|
||||
throw new InvalidOperationException("Species resource not found.");
|
||||
|
||||
// ReSharper disable once SuspiciousTypeConversion.Global
|
||||
var mutators = plugins.OfType<IPluginDataMutator>().ToList();
|
||||
|
||||
var typesLibrary = TypeDataLoader.LoadTypeLibrary(typesResult);
|
||||
var naturesLibrary = NatureDataLoader.LoadNatureLibrary(naturesResult);
|
||||
var movesLibrary = MoveDataLoader.LoadMoves(movesResult, typesLibrary,
|
||||
wrapper => mutators.ForEach(x => x.MutateMoveData(wrapper)));
|
||||
var itemsLibrary = ItemDataLoader.LoadItems(itemsResult,
|
||||
items => mutators.ForEach(x => x.MutateItemData(items)));
|
||||
var abilitiesLibrary = AbilityDataLoader.LoadAbilities(abilitiesResult,
|
||||
abilities => mutators.ForEach(x => x.MutateAbilityData(abilities)));
|
||||
var growthRatesLibrary = GrowthRateDataLoader.LoadGrowthRates(growthRatesResult,
|
||||
growthRates => mutators.ForEach(x => x.MutateGrowthRateData(growthRates)));
|
||||
var speciesLibrary = SpeciesDataLoader.LoadSpecies(speciesResult, typesLibrary,
|
||||
map => mutators.ForEach(x => x.MutateSpeciesData(map)));
|
||||
|
||||
return new StaticLibraryImpl(settings, speciesLibrary, movesLibrary, abilitiesLibrary, typesLibrary,
|
||||
naturesLibrary, growthRatesLibrary, itemsLibrary);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user