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 LoadSpeciesData(Stream stream) { var obj = JsonSerializer.Deserialize(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(JsonOptions.DefaultOptions)); } 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, IEnumerable, IReadOnlyList, IEnumerable, 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 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(), ToSpecies = evolution.Species, }, "levelfemale" => new LevelGenderEvolution { Level = evolution.Data.GetValue(), Gender = Gender.Female, ToSpecies = evolution.Species, }, "levelmale" => new LevelGenderEvolution { Level = evolution.Data.GetValue(), Gender = Gender.Male, ToSpecies = evolution.Species, }, "happiness" => new HappinessEvolution { Happiness = evolution.Data.GetValue(), ToSpecies = evolution.Species, }, "happinessday" => new HappinessDayEvolution() { Happiness = evolution.Data.GetValue(), ToSpecies = evolution.Species, }, "happinessnight" => new HappinessNightEvolution() { Happiness = evolution.Data.GetValue(), ToSpecies = evolution.Species, }, "item" => new ItemUseEvolution() { Item = evolution.Data.GetValue() ?? throw new InvalidDataException("Item is null."), ToSpecies = evolution.Species, }, "itemmale" => new ItemGenderEvolution { Item = evolution.Data.GetValue() ?? throw new InvalidDataException("Item is null."), ToSpecies = evolution.Species, }, "itemfemale" => new ItemGenderEvolution { Item = evolution.Data.GetValue() ?? throw new InvalidDataException("Item is null."), ToSpecies = evolution.Species, }, "holditem" => new HoldItemEvolution() { Item = evolution.Data.GetValue() ?? throw new InvalidDataException("Item is null."), ToSpecies = evolution.Species, }, "dayholditem" => new DayHoldItemEvolution() { Item = evolution.Data.GetValue() ?? throw new InvalidDataException("Item is null."), ToSpecies = evolution.Species, }, "nightholditem" => new NightHoldItemEvolution() { Item = evolution.Data.GetValue() ?? throw new InvalidDataException("Item is null."), ToSpecies = evolution.Species, }, "hasmove" => new HasMoveEvolution { MoveName = evolution.Data.GetValue() ?? throw new InvalidDataException("Move is null."), ToSpecies = evolution.Species, }, "trade" => new TradeEvolution { ToSpecies = evolution.Species, }, "tradespecies" => new TradeSpeciesEvolution { WithSpecies = evolution.Data.GetValue() ?? throw new InvalidDataException("Species is null."), ToSpecies = evolution.Species, }, "tradeitem" => new TradeItemEvolution { Item = evolution.Data.GetValue() ?? throw new InvalidDataException("Item is null."), ToSpecies = evolution.Species, }, "location" => new CustomEvolution { Name = "location", Parameters = new Dictionary { ["location"] = evolution.Data.ToString() ?? throw new InvalidDataException("Location is null."), }, ToSpecies = evolution.Species, }, "custom" => new CustomEvolution { Name = evolution.Data.AsObject()["type"]?.GetValue() ?? 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."), }; } }