PkmnLib.NET/PkmnLib.Dataloader/SpeciesDataLoader.cs

238 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using PkmnLib.Dataloader.Models;
using PkmnLib.Static;
using PkmnLib.Static.Libraries;
using PkmnLib.Static.Species;
using PkmnLib.Static.Utils;
namespace PkmnLib.Dataloader;
public static class SpeciesDataLoader
{
private static Dictionary<string, SerializedSpecies> LoadSpeciesData(Stream stream)
{
var obj = JsonSerializer.Deserialize<JsonObject>(stream);
if (obj == null)
throw new InvalidDataException("Species data is empty.");
var jsonConfig = new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true,
};
return obj.Where(x => x.Key != "$schema")
.ToDictionary(x => x.Key, x => x.Value.Deserialize<SerializedSpecies>(jsonConfig));
}
public static SpeciesLibrary LoadSpecies(Stream[] streams, IReadOnlyTypeLibrary typeLibrary)
{
var library = new SpeciesLibrary();
var objects = streams.SelectMany(LoadSpeciesData);
if (objects == null)
throw new InvalidDataException("Species data is empty.");
var species = objects.Select(x => DeserializeSpecies(x.Value, typeLibrary));
foreach (var s in species)
library.Add(s);
return library;
}
public static SpeciesLibrary LoadSpecies(Stream stream, IReadOnlyTypeLibrary typeLibrary)
{
var library = new SpeciesLibrary();
var objects = LoadSpeciesData(stream);
if (objects == null)
throw new InvalidDataException("Species data is empty.");
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)
{
return new ImmutableStatisticSet<ushort>(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."),
};
}
}