Add minimally filtered encounter generators

Filters by species at most, resulting in something that can be scanned
for a preferred encounter.
This commit is contained in:
Kurt 2018-03-11 00:29:31 -08:00
parent 94811d9052
commit 5148fd2c4e
6 changed files with 222 additions and 181 deletions

View file

@ -25,7 +25,7 @@ namespace PKHeX.Core
{
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 @@ namespace PKHeX.Core
{
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;
}
}
}

View file

@ -5,13 +5,15 @@ namespace PKHeX.Core
{
public static class EncounterLinkGenerator
{
// EncounterLink
public static IEnumerable<EncounterLink> GetValidLinkGifts(PKM pkm)
public static IEnumerable<EncounterLink> GetPossible(PKM pkm)
{
if (pkm.GenNumber != 6)
return Enumerable.Empty<EncounterLink>();
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<EncounterLink> GetValidLinkGifts(PKM pkm)
{
return GetPossible(pkm).Where(g => g.Level == pkm.Met_Level);
}
}
}

View file

@ -8,16 +8,30 @@ namespace PKHeX.Core
{
public static class EncounterSlotGenerator
{
public static IEnumerable<EncounterSlot> 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<EncounterSlot> 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<EncounterSlot> GetValidWildEncounters(PKM pkm, GameVersion gameSource = GameVersion.Any)
@ -57,7 +71,7 @@ namespace PKHeX.Core
return vs.SelectMany(z => Encounters6.FriendSafari[z.Species]);
}
public static IEnumerable<EncounterSlot> GetValidEncounterSlots(PKM pkm, EncounterArea loc, IEnumerable<DexLevel> vs, bool DexNav = false, int lvl = -1, bool ignoreLevel = false)
private static IEnumerable<EncounterSlot> GetValidEncounterSlots(PKM pkm, EncounterArea loc, IEnumerable<DexLevel> vs, bool DexNav = false, int lvl = -1, bool ignoreLevel = false)
{
if (pkm.WasEgg)
return Enumerable.Empty<EncounterSlot>();

View file

@ -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<EncounterStatic> GetValidStaticEncounter(PKM pkm, GameVersion gameSource = GameVersion.Any)
public static IEnumerable<EncounterStatic> GetPossible(PKM pkm, GameVersion gameSource = GameVersion.Any)
{
if (gameSource == GameVersion.Any)
gameSource = (GameVersion)pkm.Version;
// Get possible encounters
IEnumerable<EncounterStatic> poss = GetStaticEncounters(pkm, gameSource: gameSource);
return GetStaticEncounters(pkm, gameSource: gameSource);
}
public static IEnumerable<EncounterStatic> GetValidStaticEncounter(PKM pkm, GameVersion gameSource = GameVersion.Any)
{
var poss = GetPossible(pkm, gameSource: gameSource);
int lvl = GetMinLevelEncounter(pkm);
if (lvl < 0)
@ -50,6 +27,7 @@ namespace PKHeX.Core
foreach (var z in enc)
yield return z;
}
private static IEnumerable<EncounterStatic> GetMatchingStaticEncounters(PKM pkm, IEnumerable<EncounterStatic> poss, int lvl)
{
// check for petty rejection scenarios that will be flagged by other legality checks
@ -210,8 +188,7 @@ namespace PKHeX.Core
return GetStatic(pkm, table);
}
}
public static IEnumerable<EncounterStatic> GetStatic(PKM pkm, IEnumerable<EncounterStatic> table, int maxspeciesorigin = -1, int lvl = -1, bool skip = false)
private static IEnumerable<EncounterStatic> GetStatic(PKM pkm, IEnumerable<EncounterStatic> table, int maxspeciesorigin = -1, int lvl = -1, bool skip = false)
{
IEnumerable<DexLevel> dl = GetValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: lvl, skipChecks: skip);
return table.Where(e => dl.Any(d => d.Species == e.Species));
@ -277,5 +254,30 @@ namespace PKHeX.Core
{
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;
}
}
}

View file

@ -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<EncounterTrade> 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<EncounterTrade> 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<EncounterTrade> GetValidEncounterTradesVC1(PKM pkm, DexLevel[] p, IEnumerable<EncounterTrade> 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<EncounterTrade> GetValidEncounterTrades(PKM pkm, GameVersion gameSource = GameVersion.Any)
public static IEnumerable<EncounterTrade> 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<EncounterTrade> 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<EncounterTrade>();
}
private static IEnumerable<EncounterTrade> 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<EncounterTrade> 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<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<EncounterTrade> 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<EncounterTrade> 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<EncounterTrade>();
// Get valid pre-evolutions
IEnumerable<DexLevel> 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 @@ namespace PKHeX.Core
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);
}
}
}

View file

@ -7,16 +7,36 @@ namespace PKHeX.Core
{
public static class MysteryGiftGenerator
{
// MysteryGift
public static IEnumerable<MysteryGift> 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<MysteryGift> 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<MysteryGift>();
}
}
private static IEnumerable<MysteryGift> 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<MysteryGift>();
}
}
@ -49,7 +69,7 @@ namespace PKHeX.Core
if (IsRangerManaphy(pkm))
{
if (pkm.Language != (int)LanguageID.Korean) // never korean
yield return new PGT { Data = { [0] = 7, [8] = 1 } };
yield return RangerManaphy;
yield break;
}
@ -409,6 +429,7 @@ namespace PKHeX.Core
}
// Utility
private static readonly PGT RangerManaphy = new PGT {Data = {[0] = 7, [8] = 1}};
private static bool IsRangerManaphy(PKM pkm)
{
var egg = pkm.Egg_Location;