diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator.cs index 852775c19..9b9e5764a 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator.cs @@ -46,12 +46,12 @@ private static IEnumerable GetEncounters12(PKM pkm, LegalInfo in if ((pkm.Format == 1 && baseSpecies > MaxSpeciesID_1) || baseSpecies > MaxSpeciesID_2) yield break; - foreach (var z in GenerateFilteredEncounters(pkm)) + foreach (var z in GenerateFilteredEncounters12(pkm)) { - pkm.WasEgg = z.Encounter.EggEncounter; - info.Generation = z.Generation; - info.Game = z.Game; - yield return z.Encounter; + pkm.WasEgg = z.EggEncounter; + info.Generation = z is IGeneration g ? g.Generation : 2; + info.Game = ((IVersion)z).Version; + yield return z; } } private static IEnumerable GetEncounters3(PKM pkm, LegalInfo info) @@ -100,10 +100,10 @@ private static IEnumerable GetEncounters4(PKM pkm, LegalInfo inf foreach (var z in deferredPIDIV) yield return z; } - private static IEnumerable GenerateRawEncounters12(PKM pkm, GameVersion game) + + private static IEnumerable GenerateRawEncounters12(PKM pkm, GameVersion game) { bool gsc = GameVersion.GSC.Contains(game); - var gen = gsc ? 2 : 1; // Since encounter matching is super weak due to limited stored data in the structure // Calculate all 3 at the same time and pick the best result (by species). @@ -120,7 +120,7 @@ private static IEnumerable GenerateRawEncounters12(PKM pkm, Gam deferred.Add(t); continue; } - yield return new GBEncounterData(pkm, gen, t, t.Version); + yield return t; } foreach (var s in GetValidStaticEncounter(pkm, game).Where(z => species.Contains(z.Species))) { @@ -144,17 +144,17 @@ private static IEnumerable GenerateRawEncounters12(PKM pkm, Gam continue; break; } - yield return new GBEncounterData(pkm, gen, s, s.Version); + yield return s; } // clear egg flag // necessary for static egg gifts which appear in wild, level 8 GS clefairy // GetValidWildEncounters immediately returns empty otherwise pkm.WasEgg = false; - foreach (var e in GetValidWildEncounters(pkm, game).OfType()) + foreach (var e in GetValidWildEncounters(pkm, game)) { if (!species.Contains(e.Species)) continue; - yield return new GBEncounterData(pkm, gen, e, e.Version); + yield return e; } if (gsc) @@ -173,65 +173,97 @@ private static IEnumerable GenerateRawEncounters12(PKM pkm, Gam { int eggspec = GetBaseEggSpecies(pkm); if (AllowGen2Crystal(pkm)) - yield return new GBEncounterData(eggspec, GameVersion.C); // gen2 egg - yield return new GBEncounterData(eggspec, GameVersion.GS); // gen2 egg + yield return new EncounterEgg { Species = eggspec, Version = GameVersion.C, Level = 5 }; // gen2 egg + yield return new EncounterEgg { Species = eggspec, Version = GameVersion.GS, Level = 5 }; // gen2 egg } } foreach (var d in deferred) - yield return new GBEncounterData(pkm, gen, d, game); + yield return d; } - private static IEnumerable GenerateFilteredEncounters(PKM pkm) + private static IEnumerable GenerateFilteredEncounters12(PKM pkm) { bool crystal = pkm.Format == 2 && pkm.Met_Location != 0 || pkm.Format >= 7 && pkm.OT_Gender == 1; bool kadabra = pkm.Species == 64 && pkm is PK1 pk1 && (pk1.Catch_Rate == PersonalTable.RB[64].CatchRate || pk1.Catch_Rate == PersonalTable.Y[64].CatchRate); // catch rate outsider, return gen1 first always - var g1i = new PeekEnumerator(get1()); - var g2i = new PeekEnumerator(get2()); - var deferred = new List(); + // iterate over both games, consuming from one list at a time until the other list has higher priority encounters + var get1 = GenerateRawEncounters1(pkm, crystal); + var get2 = GenerateRawEncounters2(pkm, crystal); + var g1i = new PeekEnumerator(get1); + var g2i = new PeekEnumerator(get2); + + var deferred = new List(); while (g2i.PeekIsNext() || g1i.PeekIsNext()) { - var move = GetPreferredGBIterator(g1i, g2i); + var move = GetPreferredGBIterator(pkm, g1i, g2i); var obj = move.Peek(); + int gen = obj is IGeneration g ? g.Generation : 2; // only eggs don't implement interface - if ((obj.Generation == 1 && (pkm.Korean || (obj.Encounter is EncounterTrade t && !IsEncounterTrade1Valid(pkm, t)))) - || (obj.Generation == 2 && (pkm.Korean && (obj.Encounter is IVersion v && v.Version == GameVersion.C) || kadabra))) + if (gen == 1 && (pkm.Korean || (obj is EncounterTrade t && !IsEncounterTrade1Valid(pkm, t)))) + deferred.Add(obj); + else if (gen == 2 && ((pkm.Korean && (((IVersion)obj).Version == GameVersion.C)) || kadabra)) deferred.Add(obj); else yield return obj; + move.MoveNext(); } foreach (var z in deferred) yield return z; - - IEnumerable get1() - { - if (!pkm.Gen2_NotTradeback && !crystal) - foreach (var z in GenerateRawEncounters12(pkm, GameVersion.RBY)) - yield return z; - } - IEnumerable get2() - { - if (!pkm.Gen1_NotTradeback) - foreach (var z in GenerateRawEncounters12(pkm, crystal ? GameVersion.C : GameVersion.GSC)) - yield return z; - } } - /// - /// Gets the preferred iterator from a pair of iterators based on the highest value . - /// - /// Generation 1 Iterator - /// Generation 2 Iterator - /// Preferred iterator - private static PeekEnumerator GetPreferredGBIterator(PeekEnumerator g1i, PeekEnumerator g2i) + private static IEnumerable GenerateRawEncounters1(PKM pkm, bool crystal) + { + return pkm.Gen2_NotTradeback || crystal + ? Enumerable.Empty() + : GenerateRawEncounters12(pkm, GameVersion.RBY); + } + private static IEnumerable GenerateRawEncounters2(PKM pkm, bool crystal) + { + return pkm.Gen1_NotTradeback + ? Enumerable.Empty() + : GenerateRawEncounters12(pkm, crystal ? GameVersion.C : GameVersion.GSC); + } + private static PeekEnumerator GetPreferredGBIterator(PKM pkm, PeekEnumerator g1i, PeekEnumerator g2i) { if (!g1i.PeekIsNext()) return g2i; if (!g2i.PeekIsNext()) return g1i; - return g1i.Peek().Type > g2i.Peek().Type ? g1i : g2i; + var p1 = GetGBEncounterPriority(pkm, g1i.Current); + var p2 = GetGBEncounterPriority(pkm, g2i.Current); + return p1 > p2 ? g1i : g2i; + } + private static GBEncounterPriority GetGBEncounterPriority(PKM pkm, IEncounterable Encounter) + { + switch (Encounter) + { + case EncounterTrade t: + return t.Generation == 2 ? GBEncounterPriority.TradeEncounterG2 : GBEncounterPriority.TradeEncounterG1; + case EncounterStatic s: + if (s.Moves != null && s.Moves[0] != 0 && pkm.Moves.Contains(s.Moves[0])) + return GBEncounterPriority.SpecialEncounter; + return GBEncounterPriority.StaticEncounter; + case EncounterSlot _: + return GBEncounterPriority.WildEncounter; + + default: + return GBEncounterPriority.EggEncounter; + } + } + + /// + /// Generation 1/2 Encounter Data type, which serves as a 'best match' priority rating when returning from a list. + /// + private enum GBEncounterPriority + { + EggEncounter, + WildEncounter, + StaticEncounter, + SpecialEncounter, + TradeEncounterG1, + TradeEncounterG2, } private static IEnumerable GenerateRawEncounters(PKM pkm) diff --git a/PKHeX.Core/Legality/Encounters/Information/GBEncounterData.cs b/PKHeX.Core/Legality/Encounters/Information/GBEncounterData.cs deleted file mode 100644 index e3e423fd7..000000000 --- a/PKHeX.Core/Legality/Encounters/Information/GBEncounterData.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Linq; - -namespace PKHeX.Core -{ - /// - /// Generation 1/2 Encounter Data type, which serves as a 'best match' priority rating when returning from a list. - /// - internal enum GBEncounterType - { - EggEncounter, - WildEncounter, - StaticEncounter, - SpecialEncounter, - TradeEncounterG1, - TradeEncounterG2, - } - - /// - /// Generation 1/2 Encounter Data wrapper for storing supplemental information about the encounter. - /// - internal class GBEncounterData - { - private readonly int Level; - public readonly GameVersion Game; - public readonly int Generation; - internal readonly GBEncounterType Type; - public readonly IEncounterable Encounter; - - // Egg encounter - public GBEncounterData(int species, GameVersion game) - { - Generation = 2; - Game = game; - Encounter = new EncounterEgg { Species = species, Version = game, Level = 5 }; - Type = GBEncounterType.EggEncounter; - } - - public GBEncounterData(PKM pkm, int gen, IEncounterable enc, GameVersion game) - { - Game = game; - Generation = gen; - Encounter = enc; - switch (Encounter) - { - case EncounterTrade t: - if (pkm.HasOriginalMetLocation && t.Level < pkm.Met_Level) - Level = pkm.Met_Level; // Crystal - else - Level = t.Level; - Type = Generation == 2 - ? GBEncounterType.TradeEncounterG2 - : GBEncounterType.TradeEncounterG1; - break; - case EncounterStatic s: - Level = s.Level; - Type = s.Moves != null && s.Moves[0] != 0 && pkm.Moves.Contains(s.Moves[0]) - ? GBEncounterType.SpecialEncounter - : GBEncounterType.StaticEncounter; - break; - case EncounterSlot w: - Level = pkm.HasOriginalMetLocation && w.LevelMin >= pkm.Met_Level && pkm.Met_Level <= w.LevelMax - ? pkm.Met_Level // Crystal - : w.LevelMin; - Type = GBEncounterType.WildEncounter; - break; - } - } - } -}