From 5148fd2c4ed26b6e5b99ccf495fb73dcaa4e936a Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 11 Mar 2018 00:29:31 -0800 Subject: [PATCH] Add minimally filtered encounter generators Filters by species at most, resulting in something that can be scanned for a preferred encounter. --- .../Generator/EncounterEggGenerator.cs | 22 +- .../Generator/EncounterLinkGenerator.cs | 10 +- .../Generator/EncounterSlotGenerator.cs | 28 +- .../Generator/EncounterStaticGenerator.cs | 64 ++--- .../Generator/EncounterTradeGenerator.cs | 242 +++++++++--------- .../Generator/MysteryGiftGenerator.cs | 37 ++- 6 files changed, 222 insertions(+), 181 deletions(-) diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterEggGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterEggGenerator.cs index db1935396..c8ad365ce 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterEggGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterEggGenerator.cs @@ -25,7 +25,7 @@ public static IEnumerable GenerateEggs(PKM pkm) { yield return new EncounterEgg { Game = ver, Level = lvl, Species = baseSpecies }; if (gen > 5 && pkm.WasTradedEgg) - yield return new EncounterEgg { Game = tradePair(), Level = lvl, Species = baseSpecies }; + yield return new EncounterEgg { Game = GetOtherTradePair(ver), Level = lvl, Species = baseSpecies }; } if (!GetSplitBreedGeneration(pkm).Contains(pkm.Species)) @@ -36,18 +36,18 @@ public static IEnumerable GenerateEggs(PKM pkm) { yield return new EncounterEgg { Game = ver, Level = lvl, Species = baseSpecies, SplitBreed = true }; if (gen > 5 && pkm.WasTradedEgg) - yield return new EncounterEgg { Game = tradePair(), Level = lvl, Species = baseSpecies, SplitBreed = true }; + yield return new EncounterEgg { Game = GetOtherTradePair(ver), Level = lvl, Species = baseSpecies, SplitBreed = true }; } + } - // Gen6+ update the origin game when hatched. Quick manip for X.Y<->A.O | S.M<->US.UM, ie X->A - GameVersion tradePair() - { - if (ver <= GameVersion.OR) // gen6 - return (GameVersion)((int)ver ^ 2); - if (ver <= GameVersion.MN) // gen7 - return ver + 2; - return ver - 2; - } + // Gen6+ update the origin game when hatched. Quick manip for X.Y<->A.O | S.M<->US.UM, ie X->A + private static GameVersion GetOtherTradePair(GameVersion ver) + { + if (ver <= GameVersion.OR) // gen6 + return (GameVersion)((int)ver ^ 2); + if (ver <= GameVersion.MN) // gen7 + return ver + 2; + return ver - 2; } } } diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterLinkGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterLinkGenerator.cs index 860a5a6ba..fc9a1f26a 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterLinkGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterLinkGenerator.cs @@ -5,13 +5,15 @@ namespace PKHeX.Core { public static class EncounterLinkGenerator { - // EncounterLink - public static IEnumerable GetValidLinkGifts(PKM pkm) + public static IEnumerable GetPossible(PKM pkm) { if (pkm.GenNumber != 6) return Enumerable.Empty(); - var gifts = Encounters6.LinkGifts6.Where(g => g.Species == pkm.Species); - return gifts.Where(g => g.Level == pkm.Met_Level); + return Encounters6.LinkGifts6.Where(g => g.Species == pkm.Species); + } + public static IEnumerable GetValidLinkGifts(PKM pkm) + { + return GetPossible(pkm).Where(g => g.Level == pkm.Met_Level); } } } diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterSlotGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterSlotGenerator.cs index 24f568f85..a45bb806e 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterSlotGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterSlotGenerator.cs @@ -8,16 +8,30 @@ namespace PKHeX.Core { public static class EncounterSlotGenerator { + public static IEnumerable GetPossible(PKM pkm, GameVersion gameSource = GameVersion.Any) + { + int maxspeciesorigin = GetMaxSpecies(gameSource); + var vs = GetValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin); - // EncounterSlot + var possibleAreas = GetEncounterSlots(pkm, gameSource); + return possibleAreas.SelectMany(area => area.Slots).Where(z => vs.Any(v => v.Species == z.Species)); + } private static IEnumerable GetRawEncounterSlots(PKM pkm, int lvl, GameVersion gameSource = GameVersion.Any) { - int maxspeciesorigin = -1; - if (gameSource == GameVersion.RBY) maxspeciesorigin = MaxSpeciesID_1; - else if (GameVersion.GSC.Contains(gameSource)) maxspeciesorigin = MaxSpeciesID_2; - + int maxspeciesorigin = GetMaxSpecies(gameSource); var vs = GetValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin); - return GetEncounterAreas(pkm, gameSource).SelectMany(area => GetValidEncounterSlots(pkm, area, vs, DexNav: pkm.AO, lvl: lvl)); + + var possibleAreas = GetEncounterAreas(pkm, gameSource); + return possibleAreas.SelectMany(area => GetValidEncounterSlots(pkm, area, vs, DexNav: pkm.AO, lvl: lvl)); + } + + private static int GetMaxSpecies(GameVersion gameSource) + { + if (gameSource == GameVersion.RBY) + return MaxSpeciesID_1; + if (GameVersion.GSC.Contains(gameSource)) + return MaxSpeciesID_2; + return -1; } public static IEnumerable GetValidWildEncounters(PKM pkm, GameVersion gameSource = GameVersion.Any) @@ -57,7 +71,7 @@ public static IEnumerable GetValidFriendSafari(PKM pkm) return vs.SelectMany(z => Encounters6.FriendSafari[z.Species]); } - public static IEnumerable GetValidEncounterSlots(PKM pkm, EncounterArea loc, IEnumerable vs, bool DexNav = false, int lvl = -1, bool ignoreLevel = false) + private static IEnumerable GetValidEncounterSlots(PKM pkm, EncounterArea loc, IEnumerable vs, bool DexNav = false, int lvl = -1, bool ignoreLevel = false) { if (pkm.WasEgg) return Enumerable.Empty(); diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterStaticGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterStaticGenerator.cs index 501411409..0bca58d4c 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterStaticGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterStaticGenerator.cs @@ -7,39 +7,16 @@ namespace PKHeX.Core { public static class EncounterStaticGenerator { - private static bool IsValidCatchRatePK1(EncounterStatic e, PK1 pk1) - { - var catch_rate = pk1.Catch_Rate; - // Pure gen 1, trades can be filter by catch rate - if (pk1.Species == 25 || pk1.Species == 26) - { - if (catch_rate == 190) // Red Blue Pikachu, is not a static encounter - return false; - if (catch_rate == 163 && e.Level == 5) // Light Ball (Yellow) starter - return true; - } - - if (e.Version == GameVersion.Stadium) - { - // Amnesia Psyduck has different catch rates depending on language - if (e.Species == 054) - return catch_rate == (pk1.Japanese ? 167 : 168); - return Stadium_CatchRate.Contains(catch_rate); - } - - // Encounters can have different Catch Rates (RBG vs Y) - var table = e.Version == GameVersion.Y ? PersonalTable.Y : PersonalTable.RB; - var rate = table[e.Species].CatchRate; - return catch_rate == rate; - } - - public static IEnumerable GetValidStaticEncounter(PKM pkm, GameVersion gameSource = GameVersion.Any) + public static IEnumerable GetPossible(PKM pkm, GameVersion gameSource = GameVersion.Any) { if (gameSource == GameVersion.Any) gameSource = (GameVersion)pkm.Version; - // Get possible encounters - IEnumerable poss = GetStaticEncounters(pkm, gameSource: gameSource); + return GetStaticEncounters(pkm, gameSource: gameSource); + } + public static IEnumerable GetValidStaticEncounter(PKM pkm, GameVersion gameSource = GameVersion.Any) + { + var poss = GetPossible(pkm, gameSource: gameSource); int lvl = GetMinLevelEncounter(pkm); if (lvl < 0) @@ -50,6 +27,7 @@ public static IEnumerable GetValidStaticEncounter(PKM pkm, Game foreach (var z in enc) yield return z; } + private static IEnumerable GetMatchingStaticEncounters(PKM pkm, IEnumerable poss, int lvl) { // check for petty rejection scenarios that will be flagged by other legality checks @@ -210,8 +188,7 @@ private static IEnumerable GetStaticEncounters(PKM pkm, GameVer return GetStatic(pkm, table); } } - - public static IEnumerable GetStatic(PKM pkm, IEnumerable table, int maxspeciesorigin = -1, int lvl = -1, bool skip = false) + private static IEnumerable GetStatic(PKM pkm, IEnumerable table, int maxspeciesorigin = -1, int lvl = -1, bool skip = false) { IEnumerable dl = GetValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: lvl, skipChecks: skip); return table.Where(e => dl.Any(d => d.Species == e.Species)); @@ -277,5 +254,30 @@ internal static bool IsVCStaticTransferEncounterValid(PKM pkm, EncounterStatic e { return pkm.Met_Location == e.Location && pkm.Egg_Location == e.EggLocation; } + private static bool IsValidCatchRatePK1(EncounterStatic e, PK1 pk1) + { + var catch_rate = pk1.Catch_Rate; + // Pure gen 1, trades can be filter by catch rate + if (pk1.Species == 25 || pk1.Species == 26) + { + if (catch_rate == 190) // Red Blue Pikachu, is not a static encounter + return false; + if (catch_rate == 163 && e.Level == 5) // Light Ball (Yellow) starter + return true; + } + + if (e.Version == GameVersion.Stadium) + { + // Amnesia Psyduck has different catch rates depending on language + if (e.Species == 054) + return catch_rate == (pk1.Japanese ? 167 : 168); + return Stadium_CatchRate.Contains(catch_rate); + } + + // Encounters can have different Catch Rates (RBG vs Y) + var table = e.Version == GameVersion.Y ? PersonalTable.Y : PersonalTable.RB; + var rate = table[e.Species].CatchRate; + return catch_rate == rate; + } } } diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs index 563c83b53..686a2d987 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs @@ -7,137 +7,128 @@ namespace PKHeX.Core { public static class EncounterTradeGenerator { - private static EncounterTrade[] GetEncounterTradeTable(PKM pkm) - { - switch (pkm.GenNumber) - { - case 3: - return pkm.FRLG ? Encounters3.TradeGift_FRLG : Encounters3.TradeGift_RSE; - case 4: - return pkm.HGSS ? Encounters4.TradeGift_HGSS : Encounters4.TradeGift_DPPt; - case 5: - return pkm.B2W2 ? Encounters5.TradeGift_B2W2 : Encounters5.TradeGift_BW; - case 6: - return pkm.XY ? Encounters6.TradeGift_XY : Encounters6.TradeGift_AO; - case 7: - return pkm.SM ? Encounters7.TradeGift_SM : Encounters7.TradeGift_USUM; - } - return null; - } - private static IEnumerable GetValidEncounterTradesVC(PKM pkm, GameVersion gameSource) - { - var p = GetValidPreEvolutions(pkm).ToArray(); - - switch (gameSource) - { - case GameVersion.RBY: - var table = !AllowGen1Tradeback ? Encounters1.TradeGift_RBY_NoTradeback : Encounters1.TradeGift_RBY_Tradeback; - return GetValidEncounterTradesVC1(pkm, p, table); - case GameVersion.GSC: - case GameVersion.C: - return GetValidEncounterTradesVC2(pkm, p); - default: - return null; - } - } - private static IEnumerable GetValidEncounterTradesVC2(PKM pkm, DexLevel[] p) - { - // Check GSC trades. Reuse generic table fetch-match - var possible = GetValidEncounterTradesVC1(pkm, p, Encounters2.TradeGift_GSC); - - foreach (var z in possible) - { - // Filter Criteria - if (z.TID != pkm.TID) - continue; - if (z.Gender >= 0 && z.Gender != pkm.Gender && pkm.Format <= 2) - continue; - if (z.IVs != null && !z.IVs.SequenceEqual(pkm.IVs) && pkm.Format <= 2) - continue; - if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126) - continue; - - int index = Array.IndexOf(Encounters2.TradeGift_GSC, z); - int otIndex = Encounters2.TradeGift_GSC.Length + index; - bool valid; - if (pkm.Japanese) - valid = Encounters2.TradeGift_GSC_OTs[(int)LanguageID.Japanese][otIndex] == pkm.OT_Name; - else if (pkm.Korean) - valid = Encounters2.TradeGift_GSC_OTs[(int)LanguageID.Korean][otIndex] == pkm.OT_Name; - else - valid = Array.FindIndex(Encounters2.TradeGift_GSC_OTs, 2, 6, arr => arr.Length > index && arr[otIndex] == pkm.OT_Name) >= 0; - if (!valid) - continue; - - yield return z; - } - } - private static IEnumerable GetValidEncounterTradesVC1(PKM pkm, DexLevel[] p, IEnumerable table) - { - var possible = table.Where(f => p.Any(r => r.Species == f.Species)); - foreach (var z in possible) - { - if (z == null) - continue; - if (z.Level > pkm.CurrentLevel) // minimum required level - continue; - if (pkm.Format != 1 || !pkm.Gen1_NotTradeback) - yield return z; - - // Even if the in game trade uses the tables with source pokemon allowing generation 2 games, the traded pokemon could be a non-tradeback pokemon - var rate = (pkm as PK1)?.Catch_Rate; - if (z is EncounterTradeCatchRate r) - { - if (rate != r.Catch_Rate) - continue; - } - else - { - if (z.Version == GameVersion.YW && rate != PersonalTable.Y[z.Species].CatchRate) - continue; - if (z.Version != GameVersion.YW && rate != PersonalTable.RB[z.Species].CatchRate) - continue; - } - - yield return z; - } - } - - public static IEnumerable GetValidEncounterTrades(PKM pkm, GameVersion gameSource = GameVersion.Any) + public static IEnumerable GetPossible(PKM pkm, GameVersion gameSource = GameVersion.Any) { if (gameSource == GameVersion.Any) gameSource = (GameVersion)pkm.Version; if (pkm.VC || pkm.Format <= 2) + return GetPossibleVC(pkm, gameSource); + return GetPossibleNonVC(pkm, gameSource); + } + private static IEnumerable GetPossibleNonVC(PKM pkm, GameVersion gameSource = GameVersion.Any) + { + if (gameSource == GameVersion.Any) + gameSource = (GameVersion)pkm.Version; + + if (pkm.VC || pkm.Format <= 2) + return GetValidEncounterTradesVC(pkm, gameSource); + + var p = GetValidPreEvolutions(pkm); + var table = GetEncounterTradeTable(pkm); + return table?.Where(f => p.Any(r => r.Species == f.Species)) ?? Enumerable.Empty(); + } + private static IEnumerable GetPossibleVC(PKM pkm, GameVersion gameSource = GameVersion.Any) + { + var table = GetEncounterTradeTableVC(gameSource); + var p = GetValidPreEvolutions(pkm); + return table.Where(f => p.Any(r => r.Species == f.Species)); + } + private static IEnumerable GetEncounterTradeTableVC(GameVersion gameSource) + { + switch (gameSource) { - foreach (var z in GetValidEncounterTradesVC(pkm, gameSource)) - yield return z; - yield break; + case GameVersion.RBY: + return !AllowGen1Tradeback ? Encounters1.TradeGift_RBY_NoTradeback : Encounters1.TradeGift_RBY_Tradeback; + case GameVersion.GSC: + case GameVersion.C: + return Encounters2.TradeGift_GSC; + default: + return null; } + } + private static IEnumerable GetEncounterTradeTable(PKM pkm) + { + switch (pkm.GenNumber) + { + case 3: return pkm.FRLG ? Encounters3.TradeGift_FRLG : Encounters3.TradeGift_RSE; + case 4: return pkm.HGSS ? Encounters4.TradeGift_HGSS : Encounters4.TradeGift_DPPt; + case 5: return pkm.B2W2 ? Encounters5.TradeGift_B2W2 : Encounters5.TradeGift_BW; + case 6: return pkm.XY ? Encounters6.TradeGift_XY : Encounters6.TradeGift_AO; + case 7: return pkm.SM ? Encounters7.TradeGift_SM : Encounters7.TradeGift_USUM; + } + return null; + } + private static IEnumerable GetValidEncounterTradesVC(PKM pkm, GameVersion gameSource) + { + var poss = GetPossibleVC(pkm, gameSource); + if (gameSource == GameVersion.RBY) + return poss.Where(z => GetIsValidTradeVC1(pkm, z)); + return poss.Where(z => GetIsValidTradeVC2(pkm, z)); + } + private static bool GetIsValidTradeVC1(PKM pkm, EncounterTrade z) + { + if (z.Level > pkm.CurrentLevel) // minimum required level + return false; + if (pkm.Format != 1 || !pkm.Gen1_NotTradeback) + return true; - int lang = pkm.Language; - if (lang == (int)LanguageID.UNUSED_6) // invalid language - yield break; - if (lang == (int)LanguageID.Hacked && (pkm.Format != 5 || !pkm.BW)) // Japanese trades in BW have no language ID - yield break; + // Even if the in game trade uses the tables with source pokemon allowing generation 2 games, the traded pokemon could be a non-tradeback pokemon + var rate = (pkm as PK1)?.Catch_Rate; + if (z is EncounterTradeCatchRate r) + { + if (rate != r.Catch_Rate) + return false; + } + else + { + if (z.Version == GameVersion.YW && rate != PersonalTable.Y[z.Species].CatchRate) + return false; + if (z.Version != GameVersion.YW && rate != PersonalTable.RB[z.Species].CatchRate) + return false; + } + return true; + } + private static bool GetIsValidTradeVC2(PKM pkm, EncounterTrade z) + { + if (z.Level > pkm.CurrentLevel) // minimum required level + return false; + if (z.TID != pkm.TID) + return false; + if (z.Gender >= 0 && z.Gender != pkm.Gender && pkm.Format <= 2) + return false; + if (z.IVs != null && !z.IVs.SequenceEqual(pkm.IVs) && pkm.Format <= 2) + return false; + if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126) + return false; - int lvl = GetMinLevelEncounter(pkm); + int index = Array.IndexOf(Encounters2.TradeGift_GSC, z); + int otIndex = Encounters2.TradeGift_GSC.Length + index; + bool valid; + if (pkm.Japanese) + valid = Encounters2.TradeGift_GSC_OTs[(int)LanguageID.Japanese][otIndex] == pkm.OT_Name; + else if (pkm.Korean) + valid = Encounters2.TradeGift_GSC_OTs[(int)LanguageID.Korean][otIndex] == pkm.OT_Name; + else + valid = Array.FindIndex(Encounters2.TradeGift_GSC_OTs, 2, 6, arr => arr.Length > index && arr[otIndex] == pkm.OT_Name) >= 0; + if (!valid) + return false; + + return true; + } + + private static bool GetIsFromGB(PKM pkm) => pkm.VC || pkm.Format <= 2; + public static IEnumerable GetValidEncounterTrades(PKM pkm, GameVersion gameSource = GameVersion.Any) + { + if (GetIsFromGB(pkm)) + return GetValidEncounterTradesVC(pkm, gameSource); + + int lvl = IsNotTrade(pkm); if (lvl <= 0) - yield break; + return Enumerable.Empty(); - // Get valid pre-evolutions - IEnumerable p = GetValidPreEvolutions(pkm); - - EncounterTrade[] table = GetEncounterTradeTable(pkm); - if (table == null) - yield break; - var poss = table.Where(f => p.Any(r => r.Species == f.Species) && f.Version.Contains((GameVersion)pkm.Version)); - - foreach (var z in poss) - { - if (IsEncounterTradeValid(pkm, z, lvl)) - yield return z; - } + var poss = GetPossibleNonVC(pkm); + return poss.Where(z => IsEncounterTradeValid(pkm, z, lvl)); } private static bool IsEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl) { @@ -201,5 +192,16 @@ private static bool IsEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl) return true; } + + private static int IsNotTrade(PKM pkm) + { + int lang = pkm.Language; + if (lang == (int)LanguageID.UNUSED_6) // invalid language + return 0; + if (lang == (int)LanguageID.Hacked && (pkm.Format != 5 || !pkm.BW)) // Japanese trades in BW have no language ID + return 0; + + return GetMinLevelEncounter(pkm); + } } } diff --git a/PKHeX.Core/Legality/Encounters/Generator/MysteryGiftGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/MysteryGiftGenerator.cs index 84c99b8fd..b778515e6 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/MysteryGiftGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/MysteryGiftGenerator.cs @@ -7,16 +7,36 @@ namespace PKHeX.Core { public static class MysteryGiftGenerator { - // MysteryGift + public static IEnumerable GetPossible(PKM pkm) + { + int maxSpecies = GetMaxSpeciesOrigin(pkm.Format); + var vs = GetValidPreEvolutions(pkm, maxSpecies).ToArray(); + var table = GetTable(pkm.GenNumber); + return table.Where(wc => vs.Any(dl => dl.Species == wc.Species)); + } public static IEnumerable GetValidGifts(PKM pkm) { - switch (pkm.GenNumber) + int gen = pkm.GenNumber; + var table = GetTable(gen); + switch (gen) { - case 3: return GetMatchingWC3(pkm, MGDB_G3); - case 4: return GetMatchingPCD(pkm, MGDB_G4); - case 5: return GetMatchingPGF(pkm, MGDB_G5); - case 6: return GetMatchingWC6(pkm, MGDB_G6); - case 7: return GetMatchingWC7(pkm, MGDB_G7); + case 3: return GetMatchingWC3(pkm, table); + case 4: return GetMatchingPCD(pkm, table); + case 5: return GetMatchingPGF(pkm, table); + case 6: return GetMatchingWC6(pkm, table); + case 7: return GetMatchingWC7(pkm, table); + default: return Enumerable.Empty(); + } + } + private static IEnumerable GetTable(int generation) + { + switch (generation) + { + case 3: return MGDB_G3; + case 4: return MGDB_G4; + case 5: return MGDB_G5; + case 6: return MGDB_G6; + case 7: return MGDB_G7; default: return Enumerable.Empty(); } } @@ -49,7 +69,7 @@ private static IEnumerable GetMatchingPCD(PKM pkm, IEnumerable vs) } // Utility + private static readonly PGT RangerManaphy = new PGT {Data = {[0] = 7, [8] = 1}}; private static bool IsRangerManaphy(PKM pkm) { var egg = pkm.Egg_Location;