PKHeX/PKHeX.Core/Legality/Encounters/EncounterGenerator.cs
javierhimura f889a6ce7f Fix generation 1/2 encounters for pokemon with different initial moves in yellow/crystal than r/b/g/s
The encounter generator was returning always RBY and GSC and that make the ParseMovesGenGB to check only for rb and gs learnset for initial moves, the game returned should be the game for the encounter to make ParseMovesGenGB use the correct learnset table for the initial moves
Closes #1417
2017-08-25 16:25:59 +02:00

1411 lines
58 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.Legal;
namespace PKHeX.Core
{
public static class EncounterGenerator
{
public static IEnumerable<IEncounterable> 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<IEncounterable> 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<IEncounterable> GetEncounters3(PKM pkm, LegalInfo info)
{
info.PIDIV = MethodFinder.Analyze(pkm);
var deferred = new List<IEncounterable>();
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<IEncounterable> GetEncounters4(PKM pkm, LegalInfo info)
{
info.PIDIV = MethodFinder.Analyze(pkm);
var deferred = new List<IEncounterable>();
foreach (var z in GenerateRawEncounters4(pkm))
{
if (info.PIDIV.Type.IsCompatible4(z, pkm))
yield return z;
else
deferred.Add(z);
}
info.PIDIVMatches = false;
foreach (var z in deferred)
yield return z;
}
private static IEnumerable<GBEncounterData> GenerateRawEncounters12(PKM pkm, GameVersion game)
{
var gen = game == GameVersion.RBY ? 1 : 2;
// 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<int> species = new HashSet<int>(vs.Select(p => p.Species).ToList());
var deferred = new List<IEncounterable>();
foreach (var t in GetValidEncounterTrades(pkm, game))
{
if (pkm.Format >= 7)
{
deferred.Add(t);
continue;
}
yield return new GBEncounterData(pkm, gen, t, t.Version);
}
foreach (var s in GetValidStaticEncounter(pkm, game).Where(z => species.Contains(z.Species)))
{
// Valid stadium and non-stadium encounters, return only non-stadium encounters, they are less restrictive
if (s.Version == GameVersion.Stadium || s.Version == GameVersion.Stadium2)
{
deferred.Add(s);
continue;
}
if (s.Version == GameVersion.EventsGBGen2 && s.Species != 251)
{
// no Gen2 events outside of Japan besides Celebi
var jp = (pkm as PK2)?.Japanese ?? (pkm as PK1)?.Japanese;
if (jp == true)
deferred.Add(s);
continue;
}
if (game == GameVersion.GSC && !s.EggEncounter && s.Version == GameVersion.C && !pkm.HasOriginalMetLocation)
continue;
yield return new GBEncounterData(pkm, gen, s, s.Version);
}
foreach (var e in GetValidWildEncounters(pkm, game).OfType<EncounterSlot1>())
{
if (!species.Contains(e.Species))
continue;
yield return new GBEncounterData(pkm, gen, e, e.Version);
}
if (game == GameVersion.GSC || game == GameVersion.C)
{
bool WasEgg = !pkm.Gen1_NotTradeback && 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<GBEncounterData> GenerateFilteredEncounters(PKM pkm)
{
bool crystal = pkm.Format == 2 && pkm.Met_Location != 0;
var g1i = new PeekEnumerator<GBEncounterData>(get1().GetEnumerator());
var g2i = new PeekEnumerator<GBEncounterData>(get2().GetEnumerator());
var deferred = new List<GBEncounterData>();
while (g2i.PeekIsNext() || g1i.PeekIsNext())
{
PeekEnumerator<GBEncounterData> move;
if (g1i.PeekIsNext())
{
if (g2i.PeekIsNext())
move = g1i.Peek().Type > g2i.Peek().Type ? g1i : g2i;
else
move = g1i;
}
else
move = g2i;
var obj = move.Peek();
if (obj.Generation == 1 && obj.Encounter is EncounterTrade && !IsEncounterTrade1Valid(pkm))
deferred.Add(obj);
else
yield return obj;
move.MoveNext();
}
foreach (var z in deferred)
yield return z;
IEnumerable<GBEncounterData> get1()
{
if (!pkm.Gen2_NotTradeback && !crystal)
foreach (var z in GenerateRawEncounters12(pkm, GameVersion.RBY))
yield return z;
}
IEnumerable<GBEncounterData> get2()
{
if (!pkm.Gen1_NotTradeback && AllowGen2VCTransfer)
foreach (var z in GenerateRawEncounters12(pkm, crystal ? GameVersion.C : GameVersion.GSC))
yield return z;
}
}
private static IEnumerable<IEncounterable> 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; ++ctr; }
}
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 GetValidEncounterTrades(pkm))
{ yield return z; ++ctr; }
// if (ctr != 0) yield break;
}
private static IEnumerable<IEncounterable> 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; }
}
var deferred = new List<IEncounterable>();
bool safariSport = pkm.Ball == 0x05 || pkm.Ball == 0x18; // never static encounters
if (!safariSport)
foreach (var z in GetValidStaticEncounter(pkm))
{
if (z.Gift && pkm.Ball != 4)
deferred.Add(z);
else
{
yield return z; ++ctr;
}
}
// if (ctr != 0) yield break;
foreach (var z in GetValidWildEncounters(pkm))
{ yield return z; ++ctr; }
if (ctr != 0 && pkm.HasOriginalMetLocation) yield break; // EncounterTrade abra/gengar will match wild slots
foreach (var z in GetValidEncounterTrades(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;
foreach (var z in deferred)
yield return z;
}
private static IEnumerable<IEncounterable> 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 GetValidWildEncounters(pkm))
yield return z;
foreach (var z in GetValidEncounterTrades(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<EncounterStatic> GetValidStaticEncounter(PKM pkm, GameVersion gameSource = GameVersion.Any)
{
if (gameSource == GameVersion.Any)
gameSource = (GameVersion)pkm.Version;
// Get possible encounters
IEnumerable<EncounterStatic> 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<EncounterStatic>();
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<EncounterStatic> GetMatchingStaticEncounters(PKM pkm, IEnumerable<EncounterStatic> poss, int lvl)
{
// check for petty rejection scenarios that will be flagged by other legality checks
var deferred = new List<EncounterStatic>();
foreach (EncounterStatic e in poss)
{
if (e.Nature != Nature.Random && pkm.Nature != (int)e.Nature)
continue;
if (pkm.WasEgg ^ e.EggEncounter && pkm.Egg_Location == 0 && pkm.Format > 3)
{
if (!pkm.IsEgg)
continue;
}
if (pkm.Gen3 && e.EggLocation != 0) // Gen3 Egg
{
if (pkm.Format == 3 && pkm.IsEgg && e.EggLocation != pkm.Met_Location)
continue;
}
else if (pkm.VC || e.EggLocation != 0) // Gen2 Egg
{
if (pkm.Format <= 2)
{
if (pkm.IsEgg)
{
if (pkm.Met_Location != 0 && pkm.Met_Level != 0)
continue;
}
else
{
switch (pkm.Met_Level)
{
case 0:
if (pkm.Met_Location != 0)
continue;
break;
case 1:
if (pkm.Met_Location == 0)
continue;
break;
default:
if (pkm.Met_Location == 0)
continue;
break;
}
}
lvl = 5; // met @ 1, hatch @ 5.
}
}
else if (e.EggLocation != pkm.Egg_Location)
{
switch (pkm.GenNumber)
{
case 4:
if (pkm.Egg_Location != 2002) // Link Trade
continue;
break;
default:
if (pkm.Egg_Location != 30002) // Link Trade
continue;
break;
}
}
if (pkm.HasOriginalMetLocation)
{
if (!e.EggEncounter && e.Location != 0 && e.Location != pkm.Met_Location)
continue;
if (e.Level != lvl)
{
if (!(pkm.Format == 3 && e.EggEncounter && lvl == 0))
continue;
}
}
else if (e.Level > lvl)
continue;
if (e.Gender != -1 && e.Gender != pkm.Gender)
continue;
if (e.Form != pkm.AltForm && !e.SkipFormCheck && !IsFormChangeable(pkm, e.Species))
continue;
if (e.EggLocation == 60002 && e.Relearn[0] == 0 && pkm.RelearnMoves.Any(z => z != 0)) // gen7 eevee edge case
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;
}
// Encounters with different catch rates in yellow and redblue are duplicated with different gameverion
else if (e.Version == GameVersion.YW && catch_rate != PersonalTable.Y[e.Species].CatchRate)
continue;
else if (e.Version != GameVersion.YW && catch_rate != PersonalTable.RB[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)
if (pkm.FatefulEncounter ^ e.Fateful)
deferred.Add(e);
else
yield return e;
}
foreach (var e in deferred)
yield return e;
}
private static IEnumerable<EncounterStatic> 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<EncounterStatic> GetStatic(PKM pkm, IEnumerable<EncounterStatic> table, int maxspeciesorigin = -1, int lvl = -1)
{
IEnumerable<DexLevel> dl = GetValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: lvl);
return table.Where(e => dl.Any(d => d.Species == e.Species));
}
// EncounterSlot
private static IEnumerable<EncounterSlot> GetRawEncounterSlots(PKM pkm, int lvl, GameVersion gameSource = GameVersion.Any)
{
return GetEncounterAreas(pkm, gameSource).SelectMany(area => GetValidEncounterSlots(pkm, area, DexNav: pkm.AO, lvl: lvl, gameSource: gameSource));
}
private static IEnumerable<EncounterSlot> GetValidWildEncounters(PKM pkm, GameVersion gameSource = GameVersion.Any)
{
if (gameSource == GameVersion.Any)
gameSource = (GameVersion)pkm.Version;
int lvl = GetMinLevelEncounter(pkm);
if (lvl <= 0)
yield break;
var s = GetRawEncounterSlots(pkm, lvl, 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<EncounterSlot>();
foreach (EncounterSlot slot in s)
{
// check for petty rejection scenarios that will be flagged by other legality checks
// defer these edge case scenarios in the event that a later encounter ends up passing
if (slot.Species == 265 && species != 265 && !IsWurmpleEvoValid(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)) { } // incorrect encounter type
else
{
yield return slot;
continue;
}
deferred.Add(slot);
}
foreach (var d in deferred)
yield return d;
}
private static IEnumerable<EncounterSlot> 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<DexLevel> 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<EncounterSlot> GetValidEncounterSlots(PKM pkm, EncounterArea loc, bool DexNav, int lvl = -1, bool ignoreLevel = false, GameVersion gameSource = GameVersion.Any)
{
if (lvl < 0)
lvl = GetMinLevelEncounter(pkm);
if (lvl <= 0)
yield break;
int gen = pkm.GenNumber;
int fluteBoost = gen < 3 ? 0 : 4;
const int dexnavBoost = 30;
int df = DexNav ? fluteBoost : 0;
int dn = DexNav ? fluteBoost + dexnavBoost : 0;
var maxspeciesorigin = -1;
if (gameSource == GameVersion.RBY) maxspeciesorigin = MaxSpeciesID_1;
else if (GameVersion.GSC.Contains(gameSource)) maxspeciesorigin = MaxSpeciesID_2;
// Get Valid levels
IEnumerable<DexLevel> vs = GetValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: ignoreLevel ? 100 : -1, skipChecks: ignoreLevel);
if (!FilterGBSlotsCatchRate(pkm, ref vs, out GameVersion Gen1Version, out bool RBDragonair))
yield break;
// Get slots where pokemon can exist with respect to the evolution chain
IEnumerable<EncounterSlot> slots = loc.Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species && (ignoreLevel || evo.Level >= slot.LevelMin - df)));
List<EncounterSlot> encounterSlots;
if (ignoreLevel)
encounterSlots = slots.ToList();
else if (pkm.HasOriginalMetLocation)
encounterSlots = slots.Where(slot => slot.LevelMin - df <= lvl && lvl <= slot.LevelMax + (slot.Permissions.AllowDexNav ? dn : df)).ToList();
else // check for any less than current level
encounterSlots = slots.Where(slot => slot.LevelMin <= lvl).ToList();
if (gen <= 2)
{
var gbslots = FilterGBSlots(pkm, gen, Gen1Version, encounterSlots, RBDragonair);
foreach (var s in gbslots.OrderBy(slot => slot.LevelMin))
yield return s;
yield break;
}
// Pressure Slot
EncounterSlot slotMax = encounterSlots.OrderByDescending(slot => slot.LevelMax).FirstOrDefault();
if (gen >= 6 && !DexNav)
{
var slotdata = WildForms.Contains(pkm.Species)
? encounterSlots.Where(slot => slot.Form == pkm.AltForm)
: encounterSlots;
foreach (var z in slotdata)
yield return z;
// Filter for Form Specific
if (slotMax != null)
yield return getPressureSlot(slotMax);
yield break;
}
IEnumerable<EncounterSlot> formMatchSlots = encounterSlots.Where(slot => !WildForms.Contains(pkm.Species) || slot.Form == pkm.AltForm);
if (gen <= 5)
{
foreach (var z in formMatchSlots)
yield return z;
yield break;
}
foreach (EncounterSlot s in formMatchSlots)
{
bool nav = s.Permissions.AllowDexNav && (pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4);
EncounterSlot slot = s.Clone();
slot.Permissions.DexNav = nav;
if (slot.LevelMin > lvl)
slot.Permissions.WhiteFlute = true;
if (slot.LevelMax + 1 <= lvl && lvl <= slot.LevelMax + fluteBoost)
slot.Permissions.BlackFlute = true;
if (slot.LevelMax != lvl && slot.Permissions.AllowDexNav)
slot.Permissions.DexNav = true;
yield return slot;
}
if (slotMax != null)
yield return getPressureSlot(slotMax);
EncounterSlot getPressureSlot(EncounterSlot s)
{
var max = s.Clone();
max.Permissions.Pressure = true;
max.Form = pkm.AltForm;
return max;
}
}
private static bool FilterGBSlotsCatchRate(PKM pkm, ref IEnumerable<DexLevel> vs, out GameVersion Gen1Version, out bool RBDragonair)
{
RBDragonair = false;
Gen1Version = GameVersion.RBY;
if (!(pkm is PK1 pk1) || !pkm.Gen1_NotTradeback)
return true;
// Pure gen 1, slots can be filter by catch rate
switch (pkm.Species)
{
// Pikachu
case 25 when pk1.Catch_Rate == 163:
case 26 when pk1.Catch_Rate == 163:
return false; // Yellow Pikachu is not a wild encounter
// Kadabra (YW)
case 64 when pk1.Catch_Rate == 96:
case 65 when pk1.Catch_Rate == 96:
vs = vs.Where(s => s.Species == 64);
Gen1Version = GameVersion.YW;
return true;
// Kadabra (RB)
case 64 when pk1.Catch_Rate == 100:
case 65 when pk1.Catch_Rate == 100:
vs = vs.Where(s => s.Species == 64);
Gen1Version = GameVersion.RB;
return true;
// Dragonair (YW)
case 148 when pk1.Catch_Rate == 27:
case 149 when pk1.Catch_Rate == 27:
vs = vs.Where(s => s.Species == 148); // Yellow Dragonair, ignore Dratini encounters
Gen1Version = GameVersion.YW;
return true;
// Dragonair (RB)
case 148:
case 149:
// Red blue dragonair have the same catch rate as dratini, it could also be a dratini from any game
vs = vs.Where(s => pk1.Catch_Rate == PersonalTable.RB[s.Species].CatchRate);
RBDragonair = true;
return true;
default:
vs = vs.Where(s => pk1.Catch_Rate == PersonalTable.RB[s.Species].CatchRate);
return true;
}
}
private static IEnumerable<EncounterSlot> FilterGBSlots(PKM pkm, int gen, GameVersion Gen1Version, IEnumerable<EncounterSlot> slots, bool RBDragonair)
{
switch (gen)
{
case 1:
if (Gen1Version != GameVersion.RBY)
slots = slots.Where(slot => Gen1Version.Contains(((EncounterSlot1)slot).Version));
// Red Blue dragonair or dratini from any gen 1 games
if (RBDragonair)
return slots.Where(slot => GameVersion.RB.Contains(((EncounterSlot1)slot).Version) || slot.Species == 147);
return slots;
case 2:
if (pkm is PK2 pk2 && pk2.Met_Day != 0)
slots = slots.Where(slot => ((EncounterSlot1)slot).Time.Contains(pk2.Met_Day));
return slots;
default:
return slots;
}
}
private static IEnumerable<EncounterArea> 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<EncounterArea> 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 || pkm.Format == 2 && gameSource != GameVersion.C;
return noMet ? slots : slots.Where(area => area.Location == pkm.Met_Location);
}
private static IEnumerable<EncounterArea> GetSlots(PKM pkm, IEnumerable<EncounterArea> tables, int lvl = -1)
{
IEnumerable<DexLevel> vs = GetValidPreEvolutions(pkm, lvl: lvl);
foreach (var loc in tables)
{
IEnumerable<EncounterSlot> 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<EncounterLink> GetValidLinkGifts(PKM pkm)
{
switch (pkm.GenNumber)
{
case 6:
return Encounters6.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 ? 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 : null;
}
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[0] >= 0 && !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);
if (Encounters2.TradeGift_GSC_OTs[index].All(ot => ot != pkm.OT_Name))
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;
}
}
private static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, GameVersion gameSource = GameVersion.Any)
{
if (gameSource == GameVersion.Any)
gameSource = (GameVersion)pkm.Version;
if (pkm.VC || pkm.Format <= 2)
{
foreach (var z in GetValidEncounterTradesVC(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<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;
}
}
private static bool IsEncounterTradeValid(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)
{
var loc = z.Location > 0 ? z.Location : EncounterTrade.DefaultMetLocation[pkm.GenNumber - 1];
if (loc != 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<MysteryGift> 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<MysteryGift>();
}
}
private static IEnumerable<MysteryGift> GetMatchingWC3(PKM pkm, IEnumerable<MysteryGift> DB)
{
if (DB == null)
yield break;
var validWC3 = new List<MysteryGift>();
var vs = GetValidPreEvolutions(pkm, MaxSpeciesID_3).ToArray();
var enumerable = DB.OfType<WC3>().Where(wc => vs.Any(dl => dl.Species == wc.Species));
foreach (WC3 wc in enumerable)
{
if (!GetIsMatchWC3(pkm, wc))
continue;
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<MysteryGift> GetMatchingPCD(PKM pkm, IEnumerable<MysteryGift> DB)
{
if (DB == null || pkm.IsEgg && pkm.Format != 4) // transferred
yield break;
if (IsRangerManaphy(pkm))
{
yield return new PGT { Data = { [0] = 7, [8] = 1 } };
yield break;
}
var validPCD = new List<MysteryGift>();
var vs = GetValidPreEvolutions(pkm).ToArray();
var enumerable = DB.OfType<PCD>().Where(wc => vs.Any(dl => dl.Species == wc.Species));
foreach (PCD mg in enumerable)
{
var wc = mg.Gift.PK;
if (!GetIsMatchPCD(pkm, wc, vs))
continue;
bool receivable = mg.CanBeReceivedBy(pkm.Version);
if (wc.Species == pkm.Species && receivable) // best match
yield return mg;
else
validPCD.Add(mg);
}
foreach (var z in validPCD)
yield return z;
}
private static IEnumerable<MysteryGift> GetMatchingPGF(PKM pkm, IEnumerable<MysteryGift> DB)
{
if (DB == null)
yield break;
var validPGF = new List<MysteryGift>();
var vs = GetValidPreEvolutions(pkm).ToArray();
var enumerable = DB.OfType<PGF>().Where(wc => vs.Any(dl => dl.Species == wc.Species));
foreach (PGF wc in enumerable)
{
if (!GetIsMatchPGF(pkm, wc, vs))
continue;
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<MysteryGift> GetMatchingWC6(PKM pkm, IEnumerable<MysteryGift> DB)
{
if (DB == null)
yield break;
List<MysteryGift> validWC6 = new List<MysteryGift>();
var vs = GetValidPreEvolutions(pkm).ToArray();
var enumerable = DB.OfType<WC6>().Where(wc => vs.Any(dl => dl.Species == wc.Species));
foreach (WC6 wc in enumerable)
{
if (!GetIsMatchWC6(pkm, wc, vs))
continue;
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<MysteryGift> GetMatchingWC7(PKM pkm, IEnumerable<MysteryGift> DB)
{
if (DB == null)
yield break;
List<MysteryGift> validWC7 = new List<MysteryGift>();
var vs = GetValidPreEvolutions(pkm).ToArray();
var enumerable = DB.OfType<WC7>().Where(wc => vs.Any(dl => dl.Species == wc.Species));
foreach (WC7 wc in enumerable)
{
if (!GetIsMatchWC7(pkm, wc, vs))
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;
if (wc.Species == pkm.Species) // best match
yield return wc;
else
validWC7.Add(wc);
}
foreach (var z in validWC7)
yield return z;
}
private static bool GetIsMatchWC3(PKM pkm, WC3 wc)
{
// Gen3 Version MUST match.
if (wc.Version != 0 && !((GameVersion)wc.Version).Contains((GameVersion)pkm.Version))
return false;
bool hatchedEgg = wc.IsEgg && !pkm.IsEgg;
if (!hatchedEgg)
{
if (wc.SID != -1 && wc.SID != pkm.SID) return false;
if (wc.TID != -1 && wc.TID != pkm.TID) return false;
if (wc.OT_Name != null && wc.OT_Name != pkm.OT_Name) return false;
if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) return false;
}
if (wc.Language != -1 && wc.Language != pkm.Language) return false;
if (wc.Ball != pkm.Ball) return false;
if (wc.Fateful != pkm.FatefulEncounter)
{
// XD Gifts only at level 20 get flagged after transfer
bool valid = wc.Level == 20 && pkm is XK3;
if (!valid)
return false;
}
if (pkm.IsNative)
{
if (wc.Met_Level != pkm.Met_Level)
return false;
if (wc.Met_Location != pkm.Met_Location && (!wc.IsEgg || pkm.IsEgg))
return false;
}
else
{
if (pkm.IsEgg)
return false;
if (wc.Level > pkm.Met_Level)
return false;
}
return true;
}
private static bool GetIsMatchPCD(PKM pkm, PKM wc, IEnumerable<DexLevel> vs)
{
if (!wc.IsEgg)
{
if (wc.TID != pkm.TID) return false;
if (wc.SID != pkm.SID) return false;
if (wc.OT_Name != pkm.OT_Name) return false;
if (wc.OT_Gender != pkm.OT_Gender) return false;
if (wc.Language != 0 && wc.Language != pkm.Language) return false;
if (pkm.Format != 4) // transferred
{
// met location: deferred to general transfer check
if (wc.CurrentLevel > pkm.Met_Level) return false;
}
else
{
if (wc.Egg_Location + 3000 != pkm.Met_Location) return false;
if (wc.CurrentLevel != pkm.Met_Level) return false;
}
}
else // Egg
{
if (wc.Egg_Location + 3000 != pkm.Egg_Location && pkm.Egg_Location != 2002) // traded
return false;
if (wc.CurrentLevel != pkm.Met_Level)
return false;
if (pkm.IsEgg && !pkm.IsNative)
return false;
}
if (wc.AltForm != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species)))
return false;
if (wc.Ball != pkm.Ball) return false;
if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) return false;
if (wc.PID == 1 && pkm.IsShiny) return false;
if (wc.Gender != 3 && wc.Gender != pkm.Gender) return false;
if (wc.CNT_Cool > pkm.CNT_Cool) return false;
if (wc.CNT_Beauty > pkm.CNT_Beauty) return false;
if (wc.CNT_Cute > pkm.CNT_Cute) return false;
if (wc.CNT_Smart > pkm.CNT_Smart) return false;
if (wc.CNT_Tough > pkm.CNT_Tough) return false;
if (wc.CNT_Sheen > pkm.CNT_Sheen) return false;
return false;
}
private static bool GetIsMatchPGF(PKM pkm, PGF wc, IEnumerable<DexLevel> vs)
{
if (!wc.IsEgg)
{
if (wc.SID != pkm.SID) return false;
if (wc.TID != pkm.TID) return false;
if (wc.OT != pkm.OT_Name) return false;
if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) return false;
if (wc.PID != 0 && pkm.PID != wc.PID) return false;
if (wc.PIDType == 0 && pkm.IsShiny) return false;
if (wc.PIDType == 2 && !pkm.IsShiny) return false;
if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) return false;
if (wc.Language != 0 && wc.Language != pkm.Language) return false;
if (wc.EggLocation != pkm.Egg_Location) return false;
if (wc.MetLocation != pkm.Met_Location) return false;
}
else
{
if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30003) // traded
return false;
if (pkm.IsEgg && !pkm.IsNative)
return false;
}
if (wc.Form != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) return false;
if (wc.Level != pkm.Met_Level) return false;
if (wc.Ball != pkm.Ball) return false;
if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) return false;
if (wc.Gender != 2 && wc.Gender != pkm.Gender) return false;
if (wc.CNT_Cool > pkm.CNT_Cool) return false;
if (wc.CNT_Beauty > pkm.CNT_Beauty) return false;
if (wc.CNT_Cute > pkm.CNT_Cute) return false;
if (wc.CNT_Smart > pkm.CNT_Smart) return false;
if (wc.CNT_Tough > pkm.CNT_Tough) return false;
if (wc.CNT_Sheen > pkm.CNT_Sheen) return false;
return true;
}
private static bool GetIsMatchWC6(PKM pkm, WC6 wc, IEnumerable<DexLevel> vs)
{
if (pkm.Egg_Location == 0) // Not Egg
{
if (wc.CardID != pkm.SID) return false;
if (wc.TID != pkm.TID) return false;
if (wc.OT != pkm.OT_Name) return false;
if (wc.OTGender != pkm.OT_Gender) return false;
if (wc.PIDType == 0 && pkm.PID != wc.PID) return false;
if (wc.PIDType == 2 && !pkm.IsShiny) return false;
if (wc.PIDType == 3 && pkm.IsShiny) return false;
if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) return false;
if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) return false;
if (wc.Language != 0 && wc.Language != pkm.Language) return false;
}
if (wc.Form != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) return false;
if (wc.IsEgg)
{
if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded
return false;
if (pkm.IsEgg && !pkm.IsNative)
return false;
}
else
{
if (wc.EggLocation != pkm.Egg_Location) return false;
if (wc.MetLocation != pkm.Met_Location) return false;
}
if (wc.Level != pkm.Met_Level) return false;
if (wc.Ball != pkm.Ball) return false;
if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) return false;
if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) return false;
if (wc.Gender != 3 && wc.Gender != pkm.Gender) return false;
if (wc.CNT_Cool > pkm.CNT_Cool) return false;
if (wc.CNT_Beauty > pkm.CNT_Beauty) return false;
if (wc.CNT_Cute > pkm.CNT_Cute) return false;
if (wc.CNT_Smart > pkm.CNT_Smart) return false;
if (wc.CNT_Tough > pkm.CNT_Tough) return false;
if (wc.CNT_Sheen > pkm.CNT_Sheen) return false;
return true;
}
private static bool GetIsMatchWC7(PKM pkm, WC7 wc, IEnumerable<DexLevel> vs)
{
if (pkm.Egg_Location == 0) // Not Egg
{
if (wc.OTGender != 3)
{
if (wc.SID != pkm.SID) return false;
if (wc.TID != pkm.TID) return false;
if (wc.OTGender != pkm.OT_Gender) return false;
}
if (!string.IsNullOrEmpty(wc.OT) && wc.OT != pkm.OT_Name) return false;
if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) return false;
if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) return false;
if (wc.Language != 0 && wc.Language != pkm.Language) return false;
}
if (wc.Form != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) return false;
if (wc.IsEgg)
{
if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded
return false;
if (pkm.IsEgg && !pkm.IsNative)
return false;
}
else
{
if (wc.EggLocation != pkm.Egg_Location) return false;
if (wc.MetLocation != pkm.Met_Location) return false;
}
if (wc.MetLevel != pkm.Met_Level) return false;
if (wc.Ball != pkm.Ball) return false;
if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) return false;
if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) return false;
if (wc.Gender != 3 && wc.Gender != pkm.Gender) return false;
if (wc.CNT_Cool > pkm.CNT_Cool) return false;
if (wc.CNT_Beauty > pkm.CNT_Beauty) return false;
if (wc.CNT_Cute > pkm.CNT_Cute) return false;
if (wc.CNT_Smart > pkm.CNT_Smart) return false;
if (wc.CNT_Tough > pkm.CNT_Tough) return false;
if (wc.CNT_Sheen > pkm.CNT_Sheen) return false;
if (wc.PIDType == 2 && !pkm.IsShiny) return false;
if (wc.PIDType == 3 && pkm.IsShiny) return false;
return true;
}
// EncounterEgg
private static IEnumerable<EncounterEgg> 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
int max = GetMaxSpeciesOrigin(pkm.GenNumber);
var baseSpecies = GetBaseSpecies(pkm, 0);
if (baseSpecies <= max)
yield return new EncounterEgg { Game = ver, Level = lvl, Species = baseSpecies };
if (!GetSplitBreedGeneration(pkm).Contains(pkm.Species))
yield break;
baseSpecies = GetBaseSpecies(pkm, 1);
if (baseSpecies <= max)
yield return new EncounterEgg { Game = ver, Level = lvl, Species = baseSpecies, SplitBreed = true };
}
// Utility
private static bool IsRangerManaphy(PKM pkm)
{
var egg = pkm.Egg_Location;
const int ranger = 3001;
const int linkegg = 2002;
if (!pkm.IsEgg) // Link Trade Egg or Ranger
return egg == linkegg || egg == ranger;
if (egg != ranger)
return false;
var met = pkm.Met_Location;
return met == linkegg || met == 0;
}
private static bool IsHiddenAbilitySlot(EncounterSlot slot)
{
return slot.Permissions.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 IsDexNavValid(PKM pkm)
{
if (!pkm.AO || !pkm.InhabitedGeneration(6))
return false;
IEnumerable<EncounterArea> locs = GetDexNavAreas(pkm);
var d_areas = locs.Select(loc => GetValidEncounterSlots(pkm, loc, DexNav: true));
return d_areas.Any(slots => slots.Any(slot => slot.Permissions.AllowDexNav && slot.Permissions.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, pkm.Met_Level);
case 2:
return GetGSStaticTransfer(species, pkm.Met_Level);
default:
return GetStaticEncounters(pkm, 100).OrderBy(s => s.Level).FirstOrDefault();
}
}
internal static EncounterStatic GetRBYStaticTransfer(int species, int pkmMetLevel)
{
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,
Level = pkmMetLevel,
Version = GameVersion.RBY
};
}
internal static EncounterStatic GetGSStaticTransfer(int species, int pkmMetLevel)
{
return new EncounterStatic
{
Species = species,
Gift = true, // Forces Poké Ball
Ability = TransferSpeciesDefaultAbility_2.Contains(species) ? 1 : 4, // Hidden by default, else first
Shiny = species == 151 || species == 251 ? (bool?)false : null,
Fateful = species == 151 || species == 251,
Location = 30004, // todo
EggLocation = 0,
IV3 = true,
Level = pkmMetLevel,
Version = GameVersion.GS
};
}
internal static bool IsEncounterTrade1Valid(PKM pkm)
{
string ot = pkm.OT_Name;
string tr = pkm.Format <= 2 ? "TRAINER" : "Trainer"; // decaps on transfer
return ot == "トレーナー" || ot == tr;
}
private static bool IsWurmpleEvoValid(PKM pkm)
{
uint evoVal = PKX.GetWurmpleEvoVal(pkm.EncryptionConstant);
int wIndex = Array.IndexOf(WurmpleEvolutions, pkm.Species) / 2;
return evoVal == wIndex;
}
}
}