mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-16 09:08:02 +00:00
f889a6ce7f
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
1411 lines
58 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|