mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-05-24 01:46:12 -05:00
shifts encounter data from Legal's split tables to individual files for easier maintenance and initialization. Legal Core's init is so much simpler now. fix resource name typo
1337 lines
56 KiB
C#
1337 lines
56 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))
|
|
{
|
|
yield return new GBEncounterData(pkm, gen, t, game);
|
|
}
|
|
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;
|
|
}
|
|
if (game == GameVersion.GSC && !s.EggEncounter && s.Version == GameVersion.C && !pkm.HasOriginalMetLocation)
|
|
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);
|
|
}
|
|
|
|
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; }
|
|
}
|
|
|
|
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 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;
|
|
|
|
// 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<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)
|
|
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();
|
|
foreach (WC3 wc in DB.OfType<WC3>().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;
|
|
|
|
bool hatchedEgg = wc.IsEgg && !pkm.IsEgg;
|
|
if (!hatchedEgg)
|
|
{
|
|
if (wc.SID != -1 && wc.SID != pkm.SID) continue;
|
|
if (wc.TID != -1 && wc.TID != pkm.TID) continue;
|
|
if (wc.OT_Name != null && wc.OT_Name != pkm.OT_Name) continue;
|
|
if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) continue;
|
|
}
|
|
|
|
if (wc.Language != -1 && wc.Language != pkm.Language) continue;
|
|
if (wc.Ball != pkm.Ball) continue;
|
|
if (wc.Fateful != pkm.FatefulEncounter) continue;
|
|
|
|
if (pkm.IsNative)
|
|
{
|
|
if (wc.Met_Level != pkm.Met_Level)
|
|
continue;
|
|
if (wc.Met_Location != pkm.Met_Location && (!wc.IsEgg || pkm.IsEgg))
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (pkm.IsEgg)
|
|
break;
|
|
if (wc.Level > pkm.Met_Level)
|
|
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();
|
|
foreach (PCD mg in DB.OfType<PCD>().Where(wc => vs.Any(dl => dl.Species == wc.Species)))
|
|
{
|
|
var wc = mg.Gift.PK;
|
|
if (!wc.IsEgg)
|
|
{
|
|
if (wc.TID != pkm.TID) continue;
|
|
if (wc.SID != pkm.SID) continue;
|
|
if (wc.OT_Name != pkm.OT_Name) continue;
|
|
if (wc.OT_Gender != pkm.OT_Gender) continue;
|
|
if (wc.Language != 0 && wc.Language != pkm.Language) continue;
|
|
|
|
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;
|
|
}
|
|
}
|
|
else // Egg
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (wc.AltForm != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) 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;
|
|
|
|
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();
|
|
foreach (PGF wc in DB.OfType<PGF>().Where(wc => vs.Any(dl => dl.Species == wc.Species)))
|
|
{
|
|
if (!wc.IsEgg)
|
|
{
|
|
if (wc.SID != pkm.SID) continue;
|
|
if (wc.TID != pkm.TID) continue;
|
|
if (wc.OT != pkm.OT_Name) continue;
|
|
if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) 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.EggLocation != pkm.Egg_Location) continue;
|
|
if (wc.MetLocation != pkm.Met_Location) continue;
|
|
}
|
|
else
|
|
{
|
|
if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded
|
|
continue;
|
|
if (pkm.IsEgg && !pkm.IsNative)
|
|
continue;
|
|
}
|
|
|
|
if (wc.Form != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) continue;
|
|
|
|
if (wc.Level != pkm.Met_Level) continue;
|
|
if (wc.Ball != pkm.Ball) 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;
|
|
|
|
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();
|
|
foreach (WC6 wc in DB.OfType<WC6>().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 => !IsFormChangeable(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;
|
|
|
|
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();
|
|
foreach (WC7 wc in DB.OfType<WC7>().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 => !IsFormChangeable(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;
|
|
|
|
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<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) && (baseSpecies = GetBaseSpecies(pkm, 1)) <= 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);
|
|
return locs.Select(loc => GetValidEncounterSlots(pkm, loc, DexNav: true)).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;
|
|
}
|
|
}
|
|
}
|