diff --git a/PKHeX.Core/Legality/Encounters/Generator/Specific/EncounterEggGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/Specific/EncounterEggGenerator.cs index 13eb0a11e..aa02807d7 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/Specific/EncounterEggGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/Specific/EncounterEggGenerator.cs @@ -17,10 +17,12 @@ namespace PKHeX.Core public static IEnumerable GenerateEggs(PKM pkm, IReadOnlyList chain, int generation, bool all = false) { System.Diagnostics.Debug.Assert(generation >= 3); // if generating Gen2 eggs, use the other generator. - int species = pkm.Species; - if (!Breeding.CanHatchAsEgg(species)) + int currentSpecies = pkm.Species; + if (!Breeding.CanHatchAsEgg(currentSpecies)) yield break; - if (!Breeding.CanHatchAsEgg(species, pkm.Form, generation)) + + var currentForm = pkm.Form; + if (!Breeding.CanHatchAsEgg(currentSpecies, currentForm, generation)) yield break; // can't originate from eggs // version is a true indicator for all generation 3-5 origins @@ -31,26 +33,39 @@ namespace PKHeX.Core int lvl = EggStateLegality.GetEggLevel(generation); int max = GetMaxSpeciesOrigin(generation); - var e = EvoBase.GetBaseSpecies(chain, 0); - if (e.Species <= max && Breeding.CanHatchAsEgg(e.Species, e.Form, ver)) + var (species, form) = GetBaseSpecies(chain, 0); + if ((uint)species <= max) { - yield return new EncounterEgg(e.Species, e.Form, lvl, generation, ver); - if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver)) - yield return new EncounterEgg(e.Species, e.Form, lvl, generation, GetOtherTradePair(ver)); + // NOTE: THE SPLIT-BREED SECTION OF CODE SHOULD BE EXACTLY THE SAME AS THE BELOW SECTION + if (FormInfo.IsBattleOnlyForm(species, form, generation)) + form = FormInfo.GetOutOfBattleForm(species, form, generation); + if (Breeding.CanHatchAsEgg(species, form, ver)) + { + yield return new EncounterEgg(species, form, lvl, generation, ver); + if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver)) + yield return new EncounterEgg(species, form, lvl, generation, GetOtherTradePair(ver)); + } } - if (!Breeding.GetSplitBreedGeneration(generation).Contains(species)) + if (!Breeding.GetSplitBreedGeneration(generation).Contains(currentSpecies)) yield break; // no other possible species - var o = EvoBase.GetBaseSpecies(chain, 1); - if (o.Species == e.Species) + var otherSplit = species; + (species, form) = GetBaseSpecies(chain, 1); + if ((uint)species == otherSplit) yield break; - if (o.Species <= max && Breeding.CanHatchAsEgg(o.Species, o.Form, ver)) + if (species <= max) { - yield return new EncounterEgg(o.Species, o.Form, lvl, generation, ver); - if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver)) - yield return new EncounterEgg(o.Species, o.Form, lvl, generation, GetOtherTradePair(ver)); + // NOTE: THIS SECTION OF CODE SHOULD BE EXACTLY THE SAME AS THE ABOVE SECTION + if (FormInfo.IsBattleOnlyForm(species, form, generation)) + form = FormInfo.GetOutOfBattleForm(species, form, generation); + if (Breeding.CanHatchAsEgg(species, form, ver)) + { + yield return new EncounterEgg(species, form, lvl, generation, ver); + if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver)) + yield return new EncounterEgg(species, form, lvl, generation, GetOtherTradePair(ver)); + } } } @@ -66,5 +81,19 @@ namespace PKHeX.Core { return ver < GameVersion.GP; // lgpe and sw/sh don't have a sister pair } + + private static (int Species, int Form) GetBaseSpecies(IReadOnlyList evolutions, int skipOption) + { + int species = evolutions[0].Species; + if (species == (int)Species.Shedinja) // Shedinja + return ((int)Species.Nincada, 0); // Nincada + + // skip n from end, return empty if invalid index + int index = evolutions.Count - 1 - skipOption; + if ((uint)index >= evolutions.Count) + return (-1, 0); + var evo = evolutions[index]; + return (evo.Species, evo.Form); + } } } diff --git a/PKHeX.Core/Legality/Evolutions/DexLevel.cs b/PKHeX.Core/Legality/Evolutions/DexLevel.cs index d9ee1fa05..ae11362db 100644 --- a/PKHeX.Core/Legality/Evolutions/DexLevel.cs +++ b/PKHeX.Core/Legality/Evolutions/DexLevel.cs @@ -1,21 +1,14 @@ -namespace PKHeX.Core +namespace PKHeX.Core; + +/// +/// Small general purpose value passing object with misc data pertaining to an encountered Species. +/// +public record DexLevel(int Species, int Form) : ISpeciesForm { /// - /// Small general purpose value passing object with misc data pertaining to an encountered Species. + /// Maximum Level /// - public class DexLevel - { - public readonly int Species; - public readonly int Form; + public int Level { get; set; } - public int Level { get; set; } - - protected DexLevel(int species, int form) - { - Species = species; - Form = form; - } - - public override string ToString() => $"{(Species) Species} [{Level}]"; - } -} \ No newline at end of file + public override string ToString() => $"{(Species)Species}{(Form == 0 ? "" : $"-{Form}")} [{Level}]"; +} diff --git a/PKHeX.Core/Legality/Evolutions/EvoBase.cs b/PKHeX.Core/Legality/Evolutions/EvoBase.cs deleted file mode 100644 index 89c1f70c9..000000000 --- a/PKHeX.Core/Legality/Evolutions/EvoBase.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; - -namespace PKHeX.Core -{ - /// - /// Logic for determining the least-evolved species (baby/seed). - /// - internal static class EvoBase - { - internal static EvoCriteria GetBaseSpecies(PKM pkm, int skipOption = 0) - { - return GetBaseSpecies(pkm, -1, pkm.Format, skipOption); - } - - internal static EvoCriteria GetBaseSpecies(PKM pkm, int maxSpeciesOrigin, int generation, int skipOption = 0) - { - int tree = generation; - var table = EvolutionTree.GetEvolutionTree(pkm, tree); - var evos = table.GetValidPreEvolutions(pkm, maxLevel: 100, maxSpeciesOrigin: maxSpeciesOrigin, skipChecks: true); - return GetBaseSpecies(evos, skipOption); - } - - private static readonly EvoCriteria Nincada = new(290, 0) - { - Method = (int)EvolutionType.LevelUp, - MinLevel = 20, - Level = 20, - RequiresLvlUp = true, - }; - - private static readonly EvoCriteria EvoEmpty = new(0, 0) - { - Method = (int)EvolutionType.None, - }; - - internal static EvoCriteria GetBaseSpecies(IReadOnlyList evolutions, int skipOption = 0) - { - int species = evolutions[0].Species; - if (species == (int)Species.Shedinja) // Shedinja - return Nincada; // Nincada - - // skip n from end, return empty if invalid index - int index = evolutions.Count - 1 - skipOption; - return (uint)index >= evolutions.Count ? EvoEmpty : evolutions[index]; - } - } -} diff --git a/PKHeX.Core/Legality/Evolutions/EvoCriteria.cs b/PKHeX.Core/Legality/Evolutions/EvoCriteria.cs index 9be1e2bcf..73a31f513 100644 --- a/PKHeX.Core/Legality/Evolutions/EvoCriteria.cs +++ b/PKHeX.Core/Legality/Evolutions/EvoCriteria.cs @@ -1,17 +1,12 @@ -namespace PKHeX.Core +namespace PKHeX.Core; + +public sealed record EvoCriteria(int Species, int Form) : DexLevel(Species, Form) { - public sealed class EvoCriteria : DexLevel - { - public EvoCriteria(int species, int form) : base(species, form) - { - } + public int MinLevel { get; set; } + public bool RequiresLvlUp { get; set; } + public int Method { get; init; } = -1; - public int MinLevel { get; set; } - public bool RequiresLvlUp { get; set; } - public int Method { get; init; } = -1; + public bool IsTradeRequired => ((EvolutionType) Method).IsTrade(); - public bool IsTradeRequired => ((EvolutionType) Method).IsTrade(); - - public override string ToString() => $"{(Species) Species}{(Form != 0 ? $"-{Form}" : "")}}} [{MinLevel},{Level}] via {(EvolutionType) Method}"; - } + public override string ToString() => $"{(Species) Species}{(Form != 0 ? $"-{Form}" : "")}}} [{MinLevel},{Level}] via {(EvolutionType) Method}"; } diff --git a/PKHeX.Core/Legality/Restrictions/GBRestrictions.cs b/PKHeX.Core/Legality/Restrictions/GBRestrictions.cs index 241cd12dc..c17b8d015 100644 --- a/PKHeX.Core/Legality/Restrictions/GBRestrictions.cs +++ b/PKHeX.Core/Legality/Restrictions/GBRestrictions.cs @@ -235,7 +235,7 @@ namespace PKHeX.Core private static int GetRequiredMoveCount(PK1 pk, IReadOnlyList moves, IReadOnlyList[] learn, IReadOnlyList initialmoves, int originalSpecies) { if (SpecialMinMoveSlots.Contains(pk.Species)) - return GetRequiredMoveCountSpecial(pk, moves, learn); + return GetRequiredMoveCountSpecial(pk, moves, learn, originalSpecies); // A pokemon is captured with initial moves and can't forget any until have all 4 slots used // If it has learn a move before having 4 it will be in one of the free slots @@ -319,12 +319,12 @@ namespace PKHeX.Core return usedslots; } - private static int GetRequiredMoveCountSpecial(PKM pk, IReadOnlyList moves, IReadOnlyList[] learn) + private static int GetRequiredMoveCountSpecial(PKM pk, IReadOnlyList moves, IReadOnlyList[] learn, int originalSpecies) { // Species with few mandatory slots, species with stone evolutions that could evolve at lower level and do not learn any more moves // and Pikachu and Nidoran family, those only have mandatory the initial moves and a few have one level up moves, // every other move could be avoided switching game or evolving - var mandatory = GetRequiredMoveCountLevel(pk); + var mandatory = GetRequiredMoveCountLevel(pk, originalSpecies); switch (pk.Species) { case (int)Exeggutor when pk.CurrentLevel >= 28: // Exeggutor @@ -346,10 +346,9 @@ namespace PKHeX.Core return mandatory.Distinct().Count(z => z != 0) + moves.Where(m => m != 0).Count(m => !mandatory.Contains(m) && learn[1].Contains(m)); } - private static List GetRequiredMoveCountLevel(PKM pk) + private static List GetRequiredMoveCountLevel(PKM pk, int basespecies) { int species = pk.Species; - int basespecies = EvoBase.GetBaseSpecies(pk).Species; int maxlevel = 1; int minlevel = 1; diff --git a/PKHeX.Core/PKM/PK1.cs b/PKHeX.Core/PKM/PK1.cs index 4047a6f64..04b64fe14 100644 --- a/PKHeX.Core/PKM/PK1.cs +++ b/PKHeX.Core/PKM/PK1.cs @@ -107,7 +107,9 @@ namespace PKHeX.Core if (value == (int)Core.Species.Pikachu && rate == 0xA3) // Light Ball (starter) return; - int baseSpecies = EvoBase.GetBaseSpecies(this).Species; + var table = EvolutionTree.GetEvolutionTree(1); + var evos = table.GetValidPreEvolutions(this, maxLevel: 100, maxSpeciesOrigin: -1, skipChecks: true); + var baseSpecies = evos[^1].Species; if (IsCatchRatePreEvolutionRate(baseSpecies, value, rate)) return;