using System; using System.Collections.Generic; using System.Linq; using static PKHeX.Core.Legal; namespace PKHeX.Core { public static class EncounterGenerator { public static IEnumerable getEncounters(PKM pkm, LegalInfo info) { switch (info.Generation) { case 1: case 2: foreach (var enc in getEncounters12(pkm, info)) yield return enc; yield break; case 3: // info.PIDIV = MethodFinder.Analyze(pkm); foreach (var enc in getEncounters3(pkm, info)) yield return enc; yield break; case 4: // info.PIDIV = MethodFinder.Analyze(pkm); foreach (var enc in getEncounters4(pkm, info)) yield return enc; yield break; default: foreach (var enc in GenerateRawEncounters(pkm)) yield return enc; yield break; } } private static IEnumerable getEncounters12(PKM pkm, LegalInfo info) { int baseSpecies = getBaseSpecies(pkm); bool g1 = pkm.VC1 || pkm.Format == 1; if (g1 && baseSpecies > MaxSpeciesID_1 || baseSpecies > MaxSpeciesID_2) yield break; foreach (var z in GenerateFilteredEncounters(pkm)) { info.Generation = z.Generation; info.Game = z.Game; yield return z.Encounter; } } private static IEnumerable getEncounters3(PKM pkm, LegalInfo info) { info.PIDIV = MethodFinder.Analyze(pkm); var deferred = new List(); foreach (var z in GenerateRawEncounters3(pkm)) { if (z is EncounterSlot w && pkm.Version == 15) info.PIDIV = MethodFinder.getPokeSpotSeeds(pkm, w.SlotNumber).FirstOrDefault() ?? info.PIDIV; if (info.PIDIV.Type.IsCompatible3(z, pkm)) yield return z; else deferred.Add(z); } info.PIDIVMatches = false; foreach (var z in deferred) yield return z; } private static IEnumerable getEncounters4(PKM pkm, LegalInfo info) { info.PIDIV = MethodFinder.Analyze(pkm); var deferred = new List(); foreach (var z in GenerateRawEncounters4(pkm)) { if (info.PIDIV.Type.IsCompatible4(z)) yield return z; else deferred.Add(z); } info.PIDIVMatches = false; foreach (var z in deferred) yield return z; } private static IEnumerable GenerateRawEncounters12(PKM pkm, GameVersion game) { var gen = game == GameVersion.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). // Favor special event move gifts as Static Encounters when applicable var maxspeciesorigin = game == GameVersion.GSC ? MaxSpeciesID_2 : MaxSpeciesID_1; DexLevel[] vs = getValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin).ToArray(); HashSet species = new HashSet(vs.Select(p => p.Species).ToList()); var deferred = new List(); foreach (var s in getValidStaticEncounter(pkm, game)) { // Valid stadium and non-stadium encounters, return only non-stadium encounters, they are less restrictive if (!species.Contains(s.Species)) continue; if (game == GameVersion.RBY && s.Species != 54 && s.Version == GameVersion.Stadium) { deferred.Add(s); continue; } yield return new GBEncounterData(pkm, gen, s, game); } foreach (var e in getValidWildEncounters(pkm, game)) { if (!species.Contains(e.Species)) continue; yield return new GBEncounterData(pkm, gen, e, game); } foreach (var t in getValidEncounterTrade(pkm, game)) { yield return new GBEncounterData(pkm, gen, t, game); } if (game == GameVersion.GSC) { bool WasEgg = !pkm.Gen1_NotTradeback && game == GameVersion.GSC && getWasEgg23(pkm) && !NoHatchFromEgg.Contains(pkm.Species); if (WasEgg) { // Further Filtering if (pkm.Format < 3) { WasEgg &= pkm.Met_Location == 0 || pkm.Met_Level == 1; // 2->1->2 clears met info WasEgg &= pkm.CurrentLevel >= 5; } } if (WasEgg) { int eggspec = getBaseEggSpecies(pkm); if (AllowGen2Crystal) yield return new GBEncounterData(eggspec, GameVersion.C); // gen2 egg yield return new GBEncounterData(eggspec, GameVersion.GS); // gen2 egg } } foreach (var d in deferred) yield return new GBEncounterData(pkm, gen, d, game); } private static IEnumerable GenerateFilteredEncounters(PKM pkm) { IEnumerable filter(IEnumerable data) => data .OrderBy(z => z.Type == GBEncounterType.EggEncounter).ThenBy(z => z.Species).ThenBy(z => z.Level); if (!pkm.Gen2_NotTradeback) foreach (var z in filter(GenerateRawEncounters12(pkm, GameVersion.RBY))) yield return z; if (!pkm.Gen1_NotTradeback && AllowGen2VCTransfer) foreach (var z in filter(GenerateRawEncounters12(pkm, GameVersion.GSC))) yield return z; } private static IEnumerable GenerateRawEncounters(PKM pkm) { int ctr = 0; if (pkm.WasLink) { foreach (var z in getValidLinkGifts(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; } if (pkm.WasEvent || pkm.WasEventEgg) { foreach (var z in getValidGifts(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; } if (pkm.WasEgg) { foreach (var z in generateEggs(pkm)) yield return z; } foreach (var z in getValidStaticEncounter(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; foreach (var z in getValidFriendSafari(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; foreach (var z in getValidWildEncounters(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; foreach (var z in getValidEncounterTrade(pkm)) { yield return z; ++ctr; } // if (ctr != 0) yield break; } private static IEnumerable GenerateRawEncounters4(PKM pkm) { int ctr = 0; bool wasEvent = pkm.WasEvent || pkm.WasEventEgg; // egg events? if (wasEvent) { foreach (var z in getValidGifts(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; } if (pkm.WasEgg) { foreach (var z in generateEggs(pkm)) { yield return z; ++ctr; } } bool safariSport = pkm.Ball == 0x05 || pkm.Ball == 0x18; // never static encounters if (!safariSport) foreach (var z in getValidStaticEncounter(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; foreach (var z in getValidFriendSafari(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; foreach (var z in getValidWildEncounters(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; foreach (var z in getValidEncounterTrade(pkm)) { yield return z; ++ctr; } if (ctr != 0) yield break; // do static encounters if they were deferred to end, spit out any possible encounters for invalid pkm if (safariSport) foreach (var z in getValidStaticEncounter(pkm)) yield return z; } private static IEnumerable GenerateRawEncounters3(PKM pkm) { foreach (var z in getValidGifts(pkm)) yield return z; bool safari = pkm.Ball == 0x05; // never static encounters if (!safari) foreach (var z in getValidStaticEncounter(pkm)) yield return z; foreach (var z in getValidFriendSafari(pkm)) yield return z; foreach (var z in getValidWildEncounters(pkm)) yield return z; foreach (var z in getValidEncounterTrade(pkm)) yield return z; // do static encounters if they were deferred to end, spit out any possible encounters for invalid pkm if (safari) foreach (var z in getValidStaticEncounter(pkm)) yield return z; if (pkm.Version == 15) yield break; // no eggs in C/XD foreach (var z in generateEggs(pkm)) yield return z; } // EncounterStatic private static bool IsEncounterTypeMatch(IEncounterable e, int type) { return type == 0 && !(e is EncounterStaticTyped) || e is EncounterStaticTyped t && t.TypeEncounter.Contains(type); } private static IEnumerable getValidStaticEncounter(PKM pkm, GameVersion gameSource = GameVersion.Any) { if (gameSource == GameVersion.Any) gameSource = (GameVersion)pkm.Version; // Get possible encounters IEnumerable poss = getStaticEncounters(pkm, gameSource: gameSource); int lvl = getMinLevelEncounter(pkm); if (lvl <= 0) yield break; // Back Check against pkm var enc = getMatchingStaticEncounters(pkm, poss, lvl).ToList(); // Filter for encounter types; type is cleared on 6->7 transfer if (!pkm.Gen4 || pkm.Format >= 7) { foreach (var e in enc) yield return e; yield break; } // Yield out if type matches, else defer to end if no matches were yielded int ctr = 0; int type = pkm.EncounterType; var pass = new List(); foreach (var e in enc) { if (IsEncounterTypeMatch(e, type)) { yield return e; ++ctr; } else pass.Add(e); } if (ctr != 0) yield break; foreach (var e in pass) yield return e; } private static IEnumerable getMatchingStaticEncounters(PKM pkm, IEnumerable poss, int lvl) { foreach (EncounterStatic e in poss) { if (e.Nature != Nature.Random && pkm.Nature != (int)e.Nature) continue; if (pkm.Gen3 && e.EggLocation != 0) // egg { if (pkm.Format == 3 && pkm.IsEgg && e.EggLocation != pkm.Met_Location) continue; } else if (e.EggLocation != pkm.Egg_Location) continue; if (pkm.HasOriginalMetLocation) { if (e.Location != 0 && e.Location != pkm.Met_Location) continue; if (e.Level != lvl) continue; } else if (e.Level > lvl) continue; if (e.Gender != -1 && e.Gender != pkm.Gender) continue; if (e.Form != pkm.AltForm && !e.SkipFormCheck && !getCanFormChange(pkm, e.Species)) continue; if (pkm is PK1 pk1 && pkm.Gen1_NotTradeback) { var catch_rate = pk1.Catch_Rate; var japanese = pk1.Japanese; // Pure gen 1, trades can be filter by catch rate if ((pkm.Species == 25 || pkm.Species == 26) && catch_rate == 190) // Red Blue Pikachu, is not a static encounter continue; if (e.Version == GameVersion.Stadium) { if (e.Species != 054 && !Stadium_CatchRate.Contains(catch_rate)) continue; // Amnesia Psyduck have different catch rate in japanese stadium and international stadium if (e.Species == 054 && japanese && catch_rate != 167) continue; if (e.Species == 054 && !japanese && catch_rate != 168) continue; } else if (catch_rate != PersonalTable.RB[e.Species].CatchRate && catch_rate != PersonalTable.Y[e.Species].CatchRate) continue; } // Defer to EC/PID check // if (e.Shiny != null && e.Shiny != pkm.IsShiny) // continue; // Defer ball check to later // if (e.Gift && pkm.Ball != 4) // PokéBall // continue; if (!AllowGBCartEra && GameVersion.GBCartEraOnly.Contains(e.Version)) continue; // disallow gb cart era encounters (as they aren't obtainable by Main/VC series) yield return e; } } private static IEnumerable getStaticEncounters(PKM pkm, int lvl = -1, GameVersion gameSource = GameVersion.Any) { if (gameSource == GameVersion.Any) gameSource = (GameVersion)pkm.Version; var table = getEncounterStaticTable(pkm, gameSource); switch (pkm.GenNumber) { case 1: return getStatic(pkm, table, maxspeciesorigin: MaxSpeciesID_1, lvl: lvl); case 2: return getStatic(pkm, table, maxspeciesorigin: MaxSpeciesID_2, lvl: lvl); default: return getStatic(pkm, table, lvl); } } private static IEnumerable getStatic(PKM pkm, IEnumerable table, int maxspeciesorigin = -1, int lvl = -1) { IEnumerable dl = getValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: lvl); return table.Where(e => dl.Any(d => d.Species == e.Species)); } // EncounterSlot private static IEnumerable getRawEncounterSlots(PKM pkm, GameVersion gameSource = GameVersion.Any) { return getEncounterAreas(pkm, gameSource).SelectMany(area => getValidEncounterSlots(pkm, area, DexNav: pkm.AO, gameSource: gameSource)); } private static IEnumerable getValidWildEncounters(PKM pkm, GameVersion gameSource = GameVersion.Any) { if (gameSource == GameVersion.Any) gameSource = (GameVersion)pkm.Version; var s = getRawEncounterSlots(pkm, gameSource); bool IsSafariBall = pkm.Ball == 5; bool IsSportsBall = pkm.Ball == 0x18; bool IsHidden = pkm.AbilityNumber == 4; // hidden Ability int gen = pkm.GenNumber; int species = pkm.Species; bool CheckEncounterType = gen == 4 && pkm.Format != 7; var deferred = new List(); foreach (EncounterSlot slot in s) { if (slot.Species == 265 && species != 265 && !getWurmpleEvoValid(pkm)) { } // bad wurmple evolution else if (IsHidden ^ IsHiddenAbilitySlot(slot)) { } // ability mismatch else if (IsSafariBall ^ IsSafariSlot(slot.Type)) { } // Safari Zone only ball else if (IsSportsBall ^ slot.Type == SlotType.BugContest) { } // BCC only ball else if (CheckEncounterType && !slot.TypeEncounter.Contains(pkm.EncounterType)) { } // mismatch else { yield return slot; continue; } deferred.Add(slot); } foreach (var d in deferred) yield return d; } private static IEnumerable getValidFriendSafari(PKM pkm) { if (!pkm.XY) yield break; if (pkm.Met_Location != 148) // Friend Safari yield break; if (pkm.Met_Level != 30) yield break; IEnumerable vs = getValidPreEvolutions(pkm); foreach (DexLevel d in vs.Where(d => FriendSafari.Contains(d.Species) && d.Level >= 30)) { yield return new EncounterSlot { Species = d.Species, LevelMin = 30, LevelMax = 30, Form = 0, Type = SlotType.FriendSafari, }; } } private static IEnumerable getValidEncounterSlots(PKM pkm, EncounterArea loc, bool DexNav, bool ignoreLevel = false, GameVersion gameSource = GameVersion.Any) { int fluteBoost = pkm.Format < 3 ? 0 : 4; const int dexnavBoost = 30; int df = DexNav ? fluteBoost : 0; int dn = DexNav ? fluteBoost + dexnavBoost : 0; List slotdata = new List(); var maxspeciesorigin = -1; if (gameSource == GameVersion.RBY) maxspeciesorigin = MaxSpeciesID_1; if (gameSource == GameVersion.GSC) maxspeciesorigin = MaxSpeciesID_2; // Get Valid levels IEnumerable vs = getValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: ignoreLevel ? 100 : -1, skipChecks: ignoreLevel); bool IsRGBKadabra = false; if (pkm.Format == 1 && pkm.Gen1_NotTradeback) { // Pure gen 1, slots can be filter by catch rate if ((pkm.Species == 25 || pkm.Species == 26) && (pkm as PK1).Catch_Rate == 163) // Yellow Pikachu, is not a wild encounter return slotdata; if ((pkm.Species == 64 || pkm.Species == 65) && (pkm as PK1).Catch_Rate == 96) // Yellow Kadabra, ignore Abra encounters vs = vs.Where(s => s.Species == 64); if ((pkm.Species == 148 || pkm.Species == 149) && (pkm as PK1).Catch_Rate == 27) // Yellow Dragonair, ignore Dratini encounters vs = vs.Where(s => s.Species == 148); else { IsRGBKadabra = (pkm.Species == 64 || pkm.Species == 65) && (pkm as PK1).Catch_Rate == 100; vs = vs.Where(s => (pkm as PK1).Catch_Rate == PersonalTable.RB[s.Species].CatchRate); } } // Get slots where pokemon can exist bool ignoreSlotLevel = ignoreLevel; IEnumerable slots = loc.Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species && (ignoreSlotLevel || evo.Level >= slot.LevelMin - df))); int lvl = getMinLevelEncounter(pkm); if (lvl <= 0) return slotdata; int gen = pkm.GenNumber; List encounterSlots; if (ignoreLevel) encounterSlots = slots.ToList(); else if (pkm.HasOriginalMetLocation) encounterSlots = slots.Where(slot => slot.LevelMin - df <= lvl && lvl <= slot.LevelMax + (slot.AllowDexNav ? dn : df)).ToList(); else // check for any less than current level encounterSlots = slots.Where(slot => slot.LevelMin <= lvl).ToList(); if (gen <= 2) { if (IsRGBKadabra) //Red Kadabra slots : Level 49 and 51 in RGB, but level 20 and 27 in Yellow encounterSlots = encounterSlots.Where(slot => slot.LevelMin >= 49).ToList(); // For gen 1 and 2 return Minimum level slot // Minimum level is needed to check available moves, because there is no move reminder in gen 1, // There are moves in the level up table that cant be legally obtained EncounterSlot slotMin = encounterSlots.OrderBy(slot => slot.LevelMin).FirstOrDefault(); if (slotMin != null) slotdata.Add(slotMin); return slotdata; } // Pressure Slot EncounterSlot slotMax = encounterSlots.OrderByDescending(slot => slot.LevelMax).FirstOrDefault(); if (slotMax != null) { slotMax = slotMax.Clone(); slotMax.Pressure = true; slotMax.Form = pkm.AltForm; } if (gen >= 6 && !DexNav) { // Filter for Form Specific slotdata.AddRange(WildForms.Contains(pkm.Species) ? encounterSlots.Where(slot => slot.Form == pkm.AltForm) : encounterSlots); if (slotMax != null) slotdata.Add(slotMax); return slotdata; } List eslots = encounterSlots.Where(slot => !WildForms.Contains(pkm.Species) || slot.Form == pkm.AltForm).ToList(); if (gen <= 5) { slotdata.AddRange(eslots); return slotdata; } if (slotMax != null) eslots.Add(slotMax); foreach (EncounterSlot s in eslots) { bool nav = s.AllowDexNav && (pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4); EncounterSlot slot = s.Clone(); slot.DexNav = nav; if (slot.LevelMin > lvl) slot.WhiteFlute = true; if (slot.LevelMax + 1 <= lvl && lvl <= slot.LevelMax + fluteBoost) slot.BlackFlute = true; if (slot.LevelMax != lvl && slot.AllowDexNav) slot.DexNav = true; slotdata.Add(slot); } return slotdata; } private static IEnumerable getEncounterSlots(PKM pkm, int lvl = -1, GameVersion gameSource = GameVersion.Any) { if (gameSource == GameVersion.Any) gameSource = (GameVersion)pkm.Version; return getSlots(pkm, getEncounterTable(pkm, gameSource), lvl); } private static IEnumerable getEncounterAreas(PKM pkm, GameVersion gameSource = GameVersion.Any) { if (gameSource == GameVersion.Any) gameSource = (GameVersion)pkm.Version; var slots = getEncounterSlots(pkm, gameSource: gameSource); bool noMet = !pkm.HasOriginalMetLocation; return noMet || pkm.Format <= 2 ? slots : slots.Where(area => area.Location == pkm.Met_Location); } private static IEnumerable getSlots(PKM pkm, IEnumerable tables, int lvl = -1) { IEnumerable vs = getValidPreEvolutions(pkm, lvl: lvl); foreach (var loc in tables) { IEnumerable slots = loc.Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species)); EncounterSlot[] es = slots.ToArray(); if (es.Length > 0) yield return new EncounterArea { Location = loc.Location, Slots = es }; } } // EncounterLink private static IEnumerable getValidLinkGifts(PKM pkm) { switch (pkm.GenNumber) { case 6: return LinkGifts6.Where(g => g.Species == pkm.Species && g.Level == pkm.Met_Level); default: return new EncounterLink[0]; } } // EncounterTrade private static EncounterTrade[] getEncounterTradeTable(PKM pkm) { switch (pkm.GenNumber) { case 3: return pkm.FRLG ? TradeGift_FRLG : TradeGift_RSE; case 4: return pkm.HGSS ? TradeGift_HGSS : TradeGift_DPPt; case 5: return pkm.B2W2 ? TradeGift_B2W2 : TradeGift_BW; case 6: return pkm.XY ? TradeGift_XY : TradeGift_AO; case 7: return pkm.SM ? TradeGift_SM : null; } return null; } private static IEnumerable getValidEncounterTradeVC(PKM pkm, GameVersion gameSource) { var p = getValidPreEvolutions(pkm).ToArray(); switch (gameSource) { case GameVersion.RBY: var table = !AllowGen1Tradeback ? TradeGift_RBY_NoTradeback : TradeGift_RBY_Tradeback; return getValidEncounterTradeVC1(pkm, p, table); case GameVersion.GSC: return getValidEncounterTradeVC2(pkm, p); default: return null; } } private static IEnumerable getValidEncounterTradeVC2(PKM pkm, DexLevel[] p) { // Check GSC trades. Reuse generic table fetch-match var possible = getValidEncounterTradeVC1(pkm, p, TradeGift_GSC); foreach (var z in possible) { // Filter Criteria if (z?.Gender != pkm.Gender) continue; if (z.TID != pkm.TID) continue; if (!z.IVs.SequenceEqual(pkm.IVs)) continue; if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != z.Location) continue; int index = Array.IndexOf(TradeGift_GSC, z); if (TradeGift_GSC_OTs[index].All(ot => ot != pkm.OT_Name)) continue; yield return z; } } private static IEnumerable getValidEncounterTradeVC1(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 && rate != r.Catch_Rate) continue; if (rate != PersonalTable.RB[z.Species].CatchRate && rate != PersonalTable.Y[z.Species].CatchRate) continue; yield return z; } } private static IEnumerable getValidEncounterTrade(PKM pkm, GameVersion gameSource = GameVersion.Any) { if (gameSource == GameVersion.Any) gameSource = (GameVersion)pkm.Version; if (pkm.VC || pkm.Format <= 2) { foreach (var z in getValidEncounterTradeVC(pkm, gameSource)) yield return z; yield break; } int lang = pkm.Language; if (lang == 0 || lang == 6) yield break; int lvl = getMinLevelEncounter(pkm); if (lvl <= 0) yield break; // 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 (getEncounterTradeValid(pkm, z, lvl)) yield return z; } } private static bool getEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl) { for (int i = 0; i < 6; i++) if (z.IVs[i] != -1 && z.IVs[i] != pkm.IVs[i]) return false; if (z.Shiny ^ pkm.IsShiny) // Are PIDs static? return false; if (z.TID != pkm.TID) return false; if (z.SID != pkm.SID) return false; if (pkm.HasOriginalMetLocation) { z.Location = z.Location > 0 ? z.Location : EncounterTrade.DefaultMetLocation[pkm.GenNumber - 3]; if (z.Location != pkm.Met_Location) return false; if (pkm.Format < 5) { if (z.Level > lvl) return false; } else if (z.Level != lvl) return false; } else if (z.Level > lvl) return false; if (z.Nature != Nature.Random && (int)z.Nature != pkm.Nature) return false; if (z.Gender != -1 && z.Gender != pkm.Gender) return false; if (z.OTGender != -1 && z.OTGender != pkm.OT_Gender) return false; // if (z.Ability == 4 ^ pkm.AbilityNumber == 4) // defer to Ability // countinue; return true; } // MysteryGift private static IEnumerable getValidGifts(PKM pkm) { switch (pkm.GenNumber) { 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); default: return new List(); } } private static IEnumerable getMatchingWC3(PKM pkm, IEnumerable DB) { if (DB == null) yield break; var validWC3 = new List(); var vs = getValidPreEvolutions(pkm, MaxSpeciesID_3).ToArray(); foreach (WC3 wc in DB.OfType().Where(wc => vs.Any(dl => dl.Species == wc.Species))) { // Gen3 Version MUST match. if (wc.Version != 0 && !((GameVersion)wc.Version).Contains((GameVersion)pkm.Version)) continue; if (!wc.IsEgg) { if (wc.SID != pkm.SID) continue; if (wc.TID != pkm.TID) continue; if (wc.OT_Name != pkm.OT_Name) continue; if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) continue; if (wc.Language != 0 && wc.Language != pkm.Language) continue; if (wc.Met_Location != pkm.Met_Location && pkm.HasOriginalMetLocation) continue; } else if (wc.IsEgg) { if (pkm.IsNative) { if (wc.Level != pkm.Met_Level) continue; } else { if (wc.Level > pkm.Met_Level) continue; if (pkm.IsEgg) continue; } } if (wc.Ball != pkm.Ball) continue; // Some checks are best performed separately as they are caused by users screwing up valid data. // if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality // RIBBONS: Defer to ribbon legality if (wc.Species == pkm.Species) // best match yield return wc; else validWC3.Add(wc); } foreach (var z in validWC3) yield return z; } private static IEnumerable getMatchingPCD(PKM pkm, IEnumerable DB) { if (DB == null) yield break; if (pkm.Species == 490 && (pkm.WasEgg || pkm.IsEgg)) // Manaphy { int loc = pkm.IsEgg ? pkm.Met_Location : pkm.Egg_Location; bool valid = loc == 2002; // Link Trade Egg valid |= loc == 3001 && !pkm.IsShiny; // Ranger & notShiny if (pkm.IsEgg && !pkm.IsNative) // transferred valid = false; if (valid) yield return new PGT { Data = { [0] = 7, [8] = 1 } }; yield break; } var validPCD = new List(); var vs = getValidPreEvolutions(pkm).ToArray(); foreach (PCD mg in DB.OfType().Where(wc => vs.Any(dl => dl.Species == wc.Species))) { var wc = mg.Gift.PK; if (pkm.Egg_Location == 0) // Not Egg { if (wc.SID != pkm.SID) continue; if (wc.TID != pkm.TID) continue; if (wc.OT_Name != pkm.OT_Name) continue; if (wc.OT_Gender != pkm.OT_Gender) continue; if (wc.Version != 0 && wc.Version != pkm.Version) continue; if (wc.Language != 0 && wc.Language != pkm.Language) continue; } if (wc.AltForm != pkm.AltForm && vs.All(dl => !getCanFormChange(pkm, dl.Species))) continue; if (wc.IsEgg) { if (wc.Egg_Location + 3000 != pkm.Egg_Location && pkm.Egg_Location != 2002) // traded continue; if (wc.CurrentLevel != pkm.Met_Level) continue; if (pkm.IsEgg && !pkm.IsNative) continue; } else { if (pkm.Format != 4) // transferred { // met location: deferred to general transfer check if (wc.CurrentLevel > pkm.Met_Level) continue; } else { if (wc.Egg_Location + 3000 != pkm.Met_Location) continue; if (wc.CurrentLevel != pkm.Met_Level) continue; } } if (wc.Ball != pkm.Ball) continue; if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) continue; if (wc.PID == 1 && pkm.IsShiny) continue; if (wc.Gender != 3 && wc.Gender != pkm.Gender) continue; if (wc.CNT_Cool > pkm.CNT_Cool) continue; if (wc.CNT_Beauty > pkm.CNT_Beauty) continue; if (wc.CNT_Cute > pkm.CNT_Cute) continue; if (wc.CNT_Smart > pkm.CNT_Smart) continue; if (wc.CNT_Tough > pkm.CNT_Tough) continue; if (wc.CNT_Sheen > pkm.CNT_Sheen) continue; // Some checks are best performed separately as they are caused by users screwing up valid data. // if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality // RIBBONS: Defer to ribbon legality if (wc.Species == pkm.Species) // best match yield return mg; else validPCD.Add(mg); } foreach (var z in validPCD) yield return z; } private static IEnumerable getMatchingPGF(PKM pkm, IEnumerable DB) { if (DB == null) yield break; var validPGF = new List(); var vs = getValidPreEvolutions(pkm).ToArray(); foreach (PGF wc in DB.OfType().Where(wc => vs.Any(dl => dl.Species == wc.Species))) { if (pkm.Egg_Location == 0) // Not Egg { if (wc.SID != pkm.SID) continue; if (wc.TID != pkm.TID) continue; if (wc.OT != pkm.OT_Name) continue; if (wc.PID != 0 && pkm.PID != wc.PID) continue; if (wc.PIDType == 0 && pkm.IsShiny) continue; if (wc.PIDType == 2 && !pkm.IsShiny) continue; if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) continue; if (wc.Language != 0 && wc.Language != pkm.Language) continue; } if (wc.Form != pkm.AltForm && vs.All(dl => !getCanFormChange(pkm, dl.Species))) continue; if (wc.IsEgg) { if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded continue; if (pkm.IsEgg && !pkm.IsNative) continue; } else { if (wc.EggLocation != pkm.Egg_Location) continue; if (wc.MetLocation != pkm.Met_Location) continue; } if (wc.Level != pkm.Met_Level) continue; if (wc.Ball != pkm.Ball) continue; if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) continue; if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) continue; if (wc.Gender != 2 && wc.Gender != pkm.Gender) continue; if (wc.CNT_Cool > pkm.CNT_Cool) continue; if (wc.CNT_Beauty > pkm.CNT_Beauty) continue; if (wc.CNT_Cute > pkm.CNT_Cute) continue; if (wc.CNT_Smart > pkm.CNT_Smart) continue; if (wc.CNT_Tough > pkm.CNT_Tough) continue; if (wc.CNT_Sheen > pkm.CNT_Sheen) continue; // Some checks are best performed separately as they are caused by users screwing up valid data. // if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality // RIBBONS: Defer to ribbon legality if (wc.Species == pkm.Species) // best match yield return wc; else validPGF.Add(wc); } foreach (var z in validPGF) yield return z; } private static IEnumerable getMatchingWC6(PKM pkm, IEnumerable DB) { if (DB == null) yield break; List validWC6 = new List(); var vs = getValidPreEvolutions(pkm).ToArray(); foreach (WC6 wc in DB.OfType().Where(wc => vs.Any(dl => dl.Species == wc.Species))) { if (pkm.Egg_Location == 0) // Not Egg { if (wc.CardID != pkm.SID) continue; if (wc.TID != pkm.TID) continue; if (wc.OT != pkm.OT_Name) continue; if (wc.OTGender != pkm.OT_Gender) continue; if (wc.PIDType == 0 && pkm.PID != wc.PID) continue; if (wc.PIDType == 2 && !pkm.IsShiny) continue; if (wc.PIDType == 3 && pkm.IsShiny) continue; if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) continue; if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) continue; if (wc.Language != 0 && wc.Language != pkm.Language) continue; } if (wc.Form != pkm.AltForm && vs.All(dl => !getCanFormChange(pkm, dl.Species))) continue; if (wc.IsEgg) { if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded continue; if (pkm.IsEgg && !pkm.IsNative) continue; } else { if (wc.EggLocation != pkm.Egg_Location) continue; if (wc.MetLocation != pkm.Met_Location) continue; } if (wc.Level != pkm.Met_Level) continue; if (wc.Ball != pkm.Ball) continue; if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) continue; if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) continue; if (wc.Gender != 3 && wc.Gender != pkm.Gender) continue; if (wc.CNT_Cool > pkm.CNT_Cool) continue; if (wc.CNT_Beauty > pkm.CNT_Beauty) continue; if (wc.CNT_Cute > pkm.CNT_Cute) continue; if (wc.CNT_Smart > pkm.CNT_Smart) continue; if (wc.CNT_Tough > pkm.CNT_Tough) continue; if (wc.CNT_Sheen > pkm.CNT_Sheen) continue; // Some checks are best performed separately as they are caused by users screwing up valid data. // if (!wc.RelearnMoves.SequenceEqual(pkm.RelearnMoves)) continue; // Defer to relearn legality // if (wc.OT.Length > 0 && pkm.CurrentHandler != 1) continue; // Defer to ownership legality // if (wc.OT.Length > 0 && pkm.OT_Friendship != PKX.getBaseFriendship(pkm.Species)) continue; // Friendship // if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality // RIBBONS: Defer to ribbon legality if (wc.Species == pkm.Species) // best match yield return wc; else validWC6.Add(wc); } foreach (var z in validWC6) yield return z; } private static IEnumerable getMatchingWC7(PKM pkm, IEnumerable DB) { if (DB == null) yield break; List validWC7 = new List(); var vs = getValidPreEvolutions(pkm).ToArray(); foreach (WC7 wc in DB.OfType().Where(wc => vs.Any(dl => dl.Species == wc.Species))) { if (pkm.Egg_Location == 0) // Not Egg { if (wc.OTGender != 3) { if (wc.SID != pkm.SID) continue; if (wc.TID != pkm.TID) continue; if (wc.OTGender != pkm.OT_Gender) continue; } if (!string.IsNullOrEmpty(wc.OT) && wc.OT != pkm.OT_Name) continue; if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) continue; if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) continue; if (wc.Language != 0 && wc.Language != pkm.Language) continue; } if (wc.Form != pkm.AltForm && vs.All(dl => !getCanFormChange(pkm, dl.Species))) continue; if (wc.IsEgg) { if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded continue; if (pkm.IsEgg && !pkm.IsNative) continue; } else { if (wc.EggLocation != pkm.Egg_Location) continue; if (wc.MetLocation != pkm.Met_Location) continue; } if (wc.MetLevel != pkm.Met_Level) continue; if (wc.Ball != pkm.Ball) continue; if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) continue; if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) continue; if (wc.Gender != 3 && wc.Gender != pkm.Gender) continue; if (wc.CNT_Cool > pkm.CNT_Cool) continue; if (wc.CNT_Beauty > pkm.CNT_Beauty) continue; if (wc.CNT_Cute > pkm.CNT_Cute) continue; if (wc.CNT_Smart > pkm.CNT_Smart) continue; if (wc.CNT_Tough > pkm.CNT_Tough) continue; if (wc.CNT_Sheen > pkm.CNT_Sheen) continue; if (wc.PIDType == 2 && !pkm.IsShiny) continue; if (wc.PIDType == 3 && pkm.IsShiny) continue; if ((pkm.SID << 16 | pkm.TID) == 0x79F57B49) // Greninja WC has variant PID and can arrive @ 36 or 37 { if (!pkm.IsShiny) validWC7.Add(wc); continue; } if (wc.PIDType == 0 && pkm.PID != wc.PID) continue; // Some checks are best performed separately as they are caused by users screwing up valid data. // if (!wc.RelearnMoves.SequenceEqual(pkm.RelearnMoves)) continue; // Defer to relearn legality // if (wc.OT.Length > 0 && pkm.CurrentHandler != 1) continue; // Defer to ownership legality // if (wc.OT.Length > 0 && pkm.OT_Friendship != PKX.getBaseFriendship(pkm.Species)) continue; // Friendship // if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality // RIBBONS: Defer to ribbon legality if (wc.Species == pkm.Species) // best match yield return wc; else validWC7.Add(wc); } foreach (var z in validWC7) yield return z; } // EncounterEgg private static IEnumerable generateEggs(PKM pkm) { if (NoHatchFromEgg.Contains(pkm.Species)) yield break; int lvl = pkm.GenNumber < 4 ? 5 : 1; var ver = (GameVersion) pkm.Version; // version is a true indicator for all generation 3+ origins yield return new EncounterEgg { Game = (GameVersion)pkm.Version, Level = lvl, Species = getBaseSpecies(pkm, 0) }; if (getSplitBreedGeneration(pkm).Contains(pkm.Species)) yield return new EncounterEgg { Game = ver, Level = lvl, Species = getBaseSpecies(pkm, 1), SplitBreed = true }; } // Utility internal static bool IsHiddenAbilitySlot(EncounterSlot slot) { return slot.DexNav || slot.Type == SlotType.FriendSafari || slot.Type == SlotType.Horde || slot.Type == SlotType.SOS; } internal static bool IsSafariSlot(SlotType t) { return t == SlotType.Grass_Safari || t == SlotType.Surf_Safari || t == SlotType.Rock_Smash_Safari || t == SlotType.Pokeradar_Safari || t == SlotType.Old_Rod_Safari || t == SlotType.Good_Rod_Safari || t == SlotType.Super_Rod_Safari; } internal static bool getDexNavValid(PKM pkm) { if (!pkm.AO || !pkm.InhabitedGeneration(6)) return false; IEnumerable locs = getDexNavAreas(pkm); return locs.Select(loc => getValidEncounterSlots(pkm, loc, DexNav: true)).Any(slots => slots.Any(slot => slot.AllowDexNav && slot.DexNav)); } internal static EncounterArea getCaptureLocation(PKM pkm) { return (from area in getEncounterSlots(pkm, 100) let slots = getValidEncounterSlots(pkm, area, pkm.AO, ignoreLevel: true).ToArray() where slots.Any() select new EncounterArea { Location = area.Location, Slots = slots, }).OrderBy(area => area.Slots.Min(x => x.LevelMin)).FirstOrDefault(); } internal static EncounterStatic getStaticLocation(PKM pkm, int species = -1) { switch (pkm.GenNumber) { case 1: return getRBYStaticTransfer(species); default: return getStaticEncounters(pkm, 100).OrderBy(s => s.Level).FirstOrDefault(); } } internal static EncounterStatic getRBYStaticTransfer(int species) { return new EncounterStatic { Species = species, Gift = true, // Forces Poké Ball Ability = TransferSpeciesDefaultAbility_1.Contains(species) ? 1 : 4, // Hidden by default, else first Shiny = species == 151 ? (bool?)false : null, Fateful = species == 151, Location = 30013, EggLocation = 0, IV3 = true, Version = GameVersion.RBY }; } internal static bool getEncounterTrade1Valid(PKM pkm) { string ot = pkm.OT_Name; string tr = pkm.Format <= 2 ? "TRAINER" : "Trainer"; // decaps on transfer return ot == "トレーナー" || ot == tr; } private static bool getWurmpleEvoValid(PKM pkm) { uint evoVal = PKX.getWurmpleEvoVal(pkm.GenNumber, pkm.EncryptionConstant); int wIndex = Array.IndexOf(WurmpleEvolutions, pkm.Species) / 2; return evoVal == wIndex; } } }