Relocate go encounter maintenance outside of pkhex.core

PKHeX.Core now accepts generated pkl binaries from PoGoEncounterTool (PGET).

Refer to pporg's repo. This will be treated like event data; occasionally updated.
This commit is contained in:
Kurt 2020-11-14 12:51:24 -08:00
parent 81110b4231
commit ad9e9ddfa0
10 changed files with 245 additions and 937 deletions

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.Species;
namespace PKHeX.Core
{
@ -10,65 +10,66 @@ namespace PKHeX.Core
/// </summary>
public sealed class EncounterArea7g : EncounterArea
{
private EncounterArea7g() : base(GameVersion.GO) { }
public int Species { get; }
public int Form { get; }
internal static EncounterArea7g[] GetArea(HashSet<int> raid15)
private EncounterArea7g(int species, int form) : base(GameVersion.GO)
{
var noForm = Enumerable.Range(1, 150).Concat(Enumerable.Range(808, 2)); // count : 152
var forms = new[]
{
(byte)Rattata,
(byte)Raticate,
(byte)Raichu,
(byte)Sandshrew,
(byte)Sandslash,
(byte)Vulpix,
(byte)Ninetales,
(byte)Diglett,
(byte)Dugtrio,
(byte)Meowth,
(byte)Persian,
(byte)Geodude,
(byte)Graveler,
(byte)Golem,
(byte)Grimer,
(byte)Muk,
(byte)Exeggutor,
(byte)Marowak,
};
Species = species;
Form = form;
Location = 50;
}
var area = new EncounterArea7g { Location = 50, Type = SlotType.GoPark };
EncounterSlot7GO GetSlot(EncounterArea7g a, int species, int form)
internal static EncounterArea7g[] GetArea(byte[][] pickle)
{
var areas = new EncounterArea7g[pickle.Length];
for (int i = 0; i < areas.Length; i++)
areas[i] = GetArea(pickle[i]);
return areas;
}
private const int entrySize = 2;
private static EncounterArea7g GetArea(byte[] data)
{
var sf = BitConverter.ToInt16(data, 0);
int species = sf & 0x7FF;
int form = sf >> 11;
// Files are padded to be multiples of 4 bytes. The last entry might actually be padding.
// Since we aren't saving a count up-front, just check if the last entry is valid.
int count = (data.Length - 2) / entrySize;
if (data[data.Length - 1] == 0) // type of "None" is not valid
count--;
var result = new EncounterSlot7GO[count];
var area = new EncounterArea7g(species, form) {Slots = result};
for (int i = 0; i < result.Length; i++)
{
var min = raid15.Contains(species | (form << 11)) ? 15 : 1;
return new EncounterSlot7GO(a, species, form, min, 40);
var offset = (i * entrySize) + 2;
var shiny = (Shiny)data[offset];
var type = (PogoType)data[offset + 1];
result[i] = new EncounterSlot7GO(area, species, form, shiny, type);
}
var regular = noForm.Select(z => GetSlot(area, z, 0));
var alolan = forms.Select(z => GetSlot(area, z, 1));
var slots = regular.Concat(alolan).ToArray();
area.Slots = slots;
return new[] { area };
return area;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
foreach (var slot in Slots)
bool exists = chain.Any(z => z.Species == Species && z.Form == Form);
if (!exists)
yield break;
foreach (var s in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (!slot.IsLevelWithinRange(pkm.Met_Level))
break;
if (slot.Form != evo.Form)
break;
yield return slot;
break;
}
var slot = (EncounterSlot7GO)s;
if (!slot.IsLevelWithinRange(pkm.Met_Level))
continue;
if (!slot.Shiny.IsValid(pkm))
continue;
yield return slot;
}
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
@ -9,82 +10,72 @@ namespace PKHeX.Core
/// </summary>
public sealed class EncounterArea8g : EncounterArea
{
private EncounterArea8g() : base(GameVersion.GO) { }
public int Species { get; }
public int Form { get; }
internal static EncounterArea8g[] GetArea(int maxSpecies, HashSet<int> banlist, IEnumerable<int> extras)
private EncounterArea8g(int species, int form) : base(GameVersion.GO)
{
var area = new EncounterArea8g { Location = Locations.GO8, Type = SlotType.GoPark };
var all = GetSlots(area, maxSpecies, banlist, extras);
area.Slots = all.ToArray();
return new[] { area };
Species = species;
Form = form;
Location = Locations.GO8;
}
private static EncounterSlot8GO GetSlot(EncounterArea8g area, int species, int form, GameVersion baseOrigin)
internal static EncounterArea8g[] GetArea(byte[][] pickle)
{
var min = EncountersGO.GetMinLevel(species, form);
return new EncounterSlot8GO(area, species, form, baseOrigin, min, 40);
var areas = new EncounterArea8g[pickle.Length];
for (int i = 0; i < areas.Length; i++)
areas[i] = GetArea(pickle[i]);
return areas;
}
private static IEnumerable<EncounterSlot> GetSlots(EncounterArea8g area, int maxSpecies, HashSet<int> banlist, IEnumerable<int> extras)
{
// Gen7: GO transfers to LGPE cannot send Mew.
// Gen8: GO transfers to HOME can send Mew. Iterate from here.
// However, Mew transfers with LGPE base moves. Because everything <= 151 uses LGPE level-up table. Handle manually!
yield return GetSlot(area, (int)Species.Mew, 0, GameVersion.GG);
const int start = 1;
var speciesList = Enumerable.Range(start, maxSpecies - start + 1).Concat(extras);
private const int entrySize = (2 * sizeof(int)) + 2;
var pt7 = PersonalTable.USUM;
private static EncounterArea8g GetArea(byte[] data)
{
var sf = BitConverter.ToInt16(data, 0);
int species = sf & 0x7FF;
int form = sf >> 11;
var group = GetGroup(species, form);
var result = new EncounterSlot8GO[(data.Length - 2) / entrySize];
var area = new EncounterArea8g(species, form) {Slots = result};
for (int i = 0; i < result.Length; i++)
{
var offset = (i * entrySize) + 2;
result[i] = ReadSlot(data, offset, area, species, form, group);
}
return area;
}
private static EncounterSlot8GO ReadSlot(byte[] data, int offset, EncounterArea8g area, int species, int form, GameVersion group)
{
int start = BitConverter.ToInt32(data, offset);
int end = BitConverter.ToInt32(data, offset + 4);
var shiny = (Shiny)data[offset + 8];
var type = (PogoType)data[offset + 9];
return new EncounterSlot8GO(area, species, form, group, type, shiny, start, end);
}
private static GameVersion GetGroup(int species, int form)
{
var pt8 = PersonalTable.SWSH;
var ptGG = PersonalTable.GG;
foreach (var specform in speciesList)
var pi8 = (PersonalInfoSWSH)pt8[species];
if (pi8.IsPresentInGame)
{
var species = specform & 0x7FF;
if (banlist.Contains(species))
continue;
var pi8 = (PersonalInfoSWSH)pt8[species];
if (pi8.IsPresentInGame)
{
for (int f = 0; f < pi8.FormeCount; f++)
{
var sf = species | (f << 11);
if (banlist.Contains(sf))
continue;
if (IsDisallowedDuplicateForm(species, f))
continue;
bool lgpe = (species <= 151 || species == 808 || species == 809) && (f == 0 || ptGG[species].HasForme(f));
var game = lgpe ? GameVersion.GG : GameVersion.SWSH;
yield return GetSlot(area, species, f, game);
}
}
else if (species <= Legal.MaxSpeciesID_7_USUM)
{
var pi7 = pt7[species];
for (int f = 0; f < pi7.FormeCount; f++)
{
var sf = species | (f << 11);
if (banlist.Contains(sf))
continue;
if (IsDisallowedDuplicateForm(species, f))
continue;
bool lgpe = species <= 151 && (f == 0 || ptGG[species].HasForme(f));
var game = lgpe ? GameVersion.GG : GameVersion.USUM;
yield return GetSlot(area, species, f, game);
}
}
bool lgpe = (species <= 151 || species == 808 || species == 809) && (form == 0 || ptGG[species].HasForme(form));
return lgpe ? GameVersion.GG : GameVersion.SWSH;
}
if (species <= Legal.MaxSpeciesID_7_USUM)
{
bool lgpe = species <= 151 && (form == 0 || ptGG[species].HasForme(form));
return lgpe ? GameVersion.GG : GameVersion.USUM;
}
}
private static bool IsDisallowedDuplicateForm(int species, int f)
{
if (AltFormInfo.IsBattleOnlyForm(species, f, 8))
return true;
if (AltFormInfo.IsFusedForm(species, f, 8))
return true;
if (species == (int) Species.Mothim)
return f != 0; // Reverts to Plant Cloak on transfer.
return false;
throw new ArgumentOutOfRangeException(nameof(species));
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
@ -92,21 +83,21 @@ public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyLi
if (pkm.TSV == 0) // HOME doesn't assign TSV=0 to accounts.
yield break;
foreach (var slot in Slots)
bool exists = chain.Any(z => z.Species == Species && z.Form == Form);
if (!exists)
yield break;
var ball = (Ball)pkm.Ball;
foreach (var s in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (!slot.IsLevelWithinRange(pkm.Met_Level))
break;
if (slot.Form != evo.Form)
break;
yield return slot;
break;
}
var slot = (EncounterSlot8GO)s;
if (!slot.IsLevelWithinRange(pkm.Met_Level))
continue;
if (!slot.IsBallValid(ball))
continue;
if (!slot.Shiny.IsValid(pkm))
continue;
yield return slot;
}
}
}

View File

@ -1,799 +1,15 @@
using System.Collections.Generic;
using static PKHeX.Core.Species;
namespace PKHeX.Core
namespace PKHeX.Core
{
/// <summary>
/// Encounter data from <see cref="GameVersion.GO"/>, which has multiple generations of origin.
/// </summary>
internal static class EncountersGO
{
/// <summary> Clamp for generating encounters; no species allowed above this value except for those in <see cref="ExtraSpecies"/>. </summary>
private const int MaxSpeciesID_GO_HOME = Legal.MaxSpeciesID_5;
internal const int MAX_LEVEL = 40;
/// <summary> Species beyond <see cref="MaxSpeciesID_GO_HOME"/> that are available for capture and transferring. </summary>
private static readonly int[] ExtraSpecies =
{
(int)Meltan,
(int)Melmetal,
internal static readonly EncounterArea7g[] SlotsGO_GG = EncounterArea7g.GetArea(Get("go_lgpe", "go"));
internal static readonly EncounterArea8g[] SlotsGO = EncounterArea8g.GetArea(Get("go_home", "go"));
(int)Obstagoon,
(int)Perrserker,
(int)Sirfetchd,
(int)Runerigus,
};
/// <summary> When generating encounters, these species will be skipped. </summary>
private static readonly HashSet<int> DisallowedSpecies = new HashSet<int>
{
// Unreleased Galarian Forms and Galarian Evolutions
(int)Slowpoke + (1 << 11), (int)Slowbro + (2 << 11), (int)Slowking + (1 << 11),
(int)MrMime + (1 << 11), (int)MrRime,
(int)Articuno + (1 << 11),
(int)Zapdos + (1 << 11),
(int)Moltres + (1 << 11),
(int)Corsola + (1 << 11), (int)Cursola,
// Unreleased Pokémon (Generations 6, 7, and 8 are all unavailable, with some exceptions)
(int)Kecleon,
(int)Phione,
(int)Manaphy,
(int)Shaymin,
(int)Arceus,
(int)Munna, (int)Musharna,
(int)Zorua, (int)Zoroark,
(int)Vanillite, (int)Vanillish, (int)Vanilluxe,
(int)Deerling, (int)Sawsbuck, // Spring
(int)Deerling + (1 << 11), (int)Sawsbuck + (1 << 11), // Summer
(int)Deerling + (3 << 11), (int)Sawsbuck + (3 << 11), // Winter
(int)Frillish, (int)Jellicent,
(int)Tynamo, (int)Eelektrik, (int)Eelektross,
(int)Mienfoo, (int)Mienshao,
(int)Druddigon,
(int)Larvesta, (int)Volcarona,
(int)Keldeo,
(int)Meloetta,
// Cannot be transferred to HOME
(int)Spinda,
};
private static readonly HashSet<int> UnavailableShiny = new HashSet<int>
{
// Unreleased Shiny Galarian Forms and Galarian Evolutions
(int)Meowth + (2 << 11), (int)Perrserker,
(int)Ponyta + (1 << 11), (int)Rapidash + (1 << 11),
(int)Slowpoke + (1 << 11), (int)Slowbro + (2 << 11), (int)Slowking + (1 << 11),
(int)Farfetchd + (1 << 11), (int)Sirfetchd,
(int)Weezing + (1 << 11),
(int)MrMime + (1 << 11), (int)MrRime,
(int)Articuno + (1 << 11),
(int)Zapdos + (1 << 11),
(int)Moltres + (1 << 11),
(int)Corsola + (1 << 11), (int)Cursola,
(int)Zigzagoon + (1 << 11), (int)Linoone + (1 << 11), (int)Obstagoon,
(int)Darumaka + (1 << 11), (int)Darmanitan + (2 << 11),
(int)Yamask + (1 << 11), (int)Runerigus,
(int)Stunfisk + (1 << 11),
// Unreleased Shiny Pokémon (Generations 6, 7, and 8 are all unavailable, with some exceptions)
(int)Spearow, (int)Fearow,
(int)Paras, (int)Parasect,
(int)Slowpoke, (int)Slowbro, (int)Slowking,
(int)Goldeen, (int)Seaking,
(int)Ditto,
(int)Mew,
(int)Hoothoot, (int)Noctowl,
(int)Spinarak, (int)Ariados,
(int)Hoppip, (int)Skiploom, (int)Jumpluff,
(int)Wooper, (int)Quagsire,
(int)Unown + (2 << 11), // B
(int)Unown + (3 << 11), // C
(int)Unown + (4 << 11), // D
(int)Unown + (5 << 11), // E
(int)Unown + (6 << 11), // F
(int)Unown + (8 << 11), // H
(int)Unown + (9 << 11), // I
(int)Unown + (10 << 11), // J
(int)Unown + (11 << 11), // K
(int)Unown + (13 << 11), // M
(int)Unown + (14 << 11), // N
(int)Unown + (16 << 11), // P
(int)Unown + (17 << 11), // Q
(int)Unown + (19 << 11), // S
(int)Unown + (22 << 11), // V
(int)Unown + (23 << 11), // W
(int)Unown + (24 << 11), // X
(int)Unown + (25 << 11), // Y
(int)Unown + (26 << 11), // Z
(int)Unown + (27 << 11), // !
(int)Unown + (28 << 11), // ?
(int)Girafarig,
(int)Heracross,
(int)Slugma, (int)Magcargo,
(int)Corsola,
(int)Remoraid, (int)Octillery,
(int)Phanpy, (int)Donphan,
(int)Smeargle,
(int)Tyrogue, (int)Hitmonlee, (int)Hitmonchan, (int)Hitmontop,
(int)Miltank,
(int)Celebi,
(int)Surskit, (int)Masquerain,
(int)Shroomish, (int)Breloom,
(int)Whismur, (int)Loudred, (int)Exploud,
(int)Nosepass, (int)Probopass,
(int)Gulpin, (int)Swalot,
(int)Numel, (int)Camerupt,
(int)Torkoal,
(int)Cacnea, (int)Cacturne,
(int)Corphish, (int)Crawdaunt,
(int)Kecleon,
(int)Tropius,
(int)Spheal, (int)Sealeo, (int)Walrein,
(int)Relicanth,
(int)Jirachi,
(int)Starly, (int)Staravia, (int)Staraptor,
(int)Bidoof, (int)Bibarel,
(int)Cranidos, (int)Rampardos,
(int)Shieldon, (int)Bastiodon,
(int)Combee, (int)Vespiquen,
(int)Pachirisu,
(int)Buizel, (int)Floatzel,
(int)Cherubi, (int)Cherrim,
(int)Shellos, (int)Gastrodon, // West Sea
(int)Shellos + (1 << 11), (int)Gastrodon + (1 << 11), // East Sea
(int)Chingling, (int)Chimecho,
(int)Stunky, (int)Skuntank,
(int)Chatot,
(int)Munchlax, (int)Snorlax,
(int)Carnivine,
(int)Finneon, (int)Lumineon,
(int)Mantyke, (int)Mantine,
(int)Rotom,
(int)Uxie,
(int)Mesprit,
(int)Azelf,
(int)Dialga,
(int)Palkia,
(int)Regigigas,
(int)Phione,
(int)Manaphy,
(int)Shaymin,
(int)Arceus,
(int)Victini,
(int)Snivy, (int)Servine, (int)Serperior,
(int)Tepig, (int)Pignite, (int)Emboar,
(int)Oshawott, (int)Dewott, (int)Samurott,
(int)Purrloin, (int)Liepard,
(int)Pansage, (int)Simisage,
(int)Pansear, (int)Simisear,
(int)Panpour, (int)Simipour,
(int)Munna, (int)Musharna,
(int)Blitzle, (int)Zebstrika,
(int)Drilbur, (int)Excadrill,
(int)Audino,
(int)Tympole, (int)Palpitoad, (int)Seismitoad,
(int)Throh,
(int)Sawk,
(int)Sewaddle, (int)Swadloon, (int)Leavanny,
(int)Venipede, (int)Whirlipede, (int)Scolipede,
(int)Cottonee, (int)Whimsicott,
(int)Petilil, (int)Lilligant,
(int)Basculin, // Red-Striped
(int)Basculin + (1 << 11), // Blue-Striped
(int)Sandile, (int)Krokorok, (int)Krookodile,
(int)Darumaka, (int)Darmanitan,
(int)Maractus,
(int)Scraggy, (int)Scrafty,
(int)Sigilyph,
(int)Tirtouga, (int)Carracosta,
(int)Archen, (int)Archeops,
(int)Trubbish, (int)Garbodor,
(int)Zorua, (int)Zoroark,
(int)Gothita, (int)Gothorita, (int)Gothitelle,
(int)Solosis, (int)Duosion, (int)Reuniclus,
(int)Ducklett, (int)Swanna,
(int)Vanillite, (int)Vanillish, (int)Vanilluxe,
(int)Deerling, (int)Sawsbuck, // Spring
(int)Deerling + (1 << 11), (int)Sawsbuck + (1 << 11), // Summer
(int)Deerling + (2 << 11), (int)Sawsbuck + (2 << 11), // Autumn
(int)Deerling + (3 << 11), (int)Sawsbuck + (3 << 11), // Winter
(int)Emolga,
(int)Karrablast, (int)Escavalier,
(int)Foongus, (int)Amoonguss,
(int)Frillish, (int)Jellicent,
(int)Alomomola,
(int)Joltik, (int)Galvantula,
(int)Tynamo, (int)Eelektrik, (int)Eelektross,
(int)Elgyem, (int)Beheeyem,
(int)Litwick, (int)Lampent, (int)Chandelure,
(int)Axew, (int)Fraxure, (int)Haxorus,
(int)Cubchoo, (int)Beartic,
(int)Cryogonal,
(int)Shelmet, (int)Accelgor,
(int)Stunfisk,
(int)Mienfoo, (int)Mienshao,
(int)Druddigon,
(int)Golett, (int)Golurk,
(int)Pawniard, (int)Bisharp,
(int)Bouffalant,
(int)Rufflet, (int)Braviary,
(int)Vullaby, (int)Mandibuzz,
(int)Larvesta, (int)Volcarona,
(int)Tornadus,
(int)Thundurus,
(int)Reshiram,
(int)Zekrom,
(int)Landorus,
(int)Kyurem,
(int)Keldeo,
(int)Meloetta,
};
private static readonly HashSet<int> Purified = new HashSet<int>
{
// Purified Pokémon
(int)Bulbasaur, (int)Ivysaur, (int)Venusaur,
(int)Charmander, (int)Charmeleon, (int)Charizard,
(int)Squirtle, (int)Wartortle, (int)Blastoise,
(int)Weedle, (int)Kakuna, (int)Beedrill,
(int)Rattata, (int)Raticate,
(int)Ekans, (int)Arbok,
(int)Sandshrew, (int)Sandslash,
(int)NidoranF, (int)Nidorina, (int)Nidoqueen,
(int)NidoranM, (int)Nidorino, (int)Nidoking,
(int)Vulpix, (int)Ninetales,
(int)Zubat, (int)Golbat, (int)Crobat,
(int)Oddish, (int)Gloom, (int)Vileplume, (int)Bellossom,
(int)Venonat, (int)Venomoth,
(int)Diglett, (int)Dugtrio,
(int)Meowth, (int)Persian,
(int)Psyduck, (int)Golduck,
(int)Growlithe, (int)Arcanine,
(int)Poliwag, (int)Poliwhirl, (int)Poliwrath, (int)Politoed,
(int)Abra, (int)Kadabra, (int)Alakazam,
(int)Machop, (int)Machoke, (int)Machamp,
(int)Bellsprout, (int)Weepinbell, (int)Victreebel,
(int)Slowpoke, (int)Slowbro, (int)Slowking,
(int)Magnemite, (int)Magneton, (int)Magnezone,
(int)Grimer, (int)Muk,
(int)Shellder, (int)Cloyster,
(int)Drowzee, (int)Hypno,
(int)Exeggcute, (int)Exeggutor,
(int)Cubone, (int)Marowak,
(int)Hitmonlee, (int)Hitmonchan,
(int)Koffing, (int)Weezing,
(int)Scyther, (int)Scizor,
(int)Electabuzz, (int)Electivire,
(int)Magmar, (int)Magmortar,
(int)Pinsir,
(int)Magikarp, (int)Gyarados,
(int)Lapras,
(int)Porygon, (int)Porygon2, (int)PorygonZ,
(int)Omanyte, (int)Omastar,
(int)Aerodactyl,
(int)Snorlax,
(int)Articuno,
(int)Zapdos,
(int)Moltres,
(int)Dratini, (int)Dragonair, (int)Dragonite,
(int)Mewtwo,
(int)Mareep, (int)Flaaffy, (int)Ampharos,
(int)Hoppip, (int)Skiploom, (int)Jumpluff,
(int)Misdreavus, (int)Mismagius,
(int)Wobbuffet,
(int)Pineco, (int)Forretress,
(int)Gligar, (int)Gliscor,
(int)Shuckle,
(int)Sneasel, (int)Weavile,
(int)Teddiursa, (int)Ursaring,
(int)Delibird,
(int)Skarmory,
(int)Houndour, (int)Houndoom,
(int)Stantler,
(int)Raikou,
(int)Entei,
(int)Suicune,
(int)Larvitar, (int)Pupitar, (int)Tyranitar,
(int)Mudkip, (int)Marshtomp, (int)Swampert,
(int)Seedot, (int)Nuzleaf, (int)Shiftry,
(int)Ralts, (int)Kirlia, (int)Gardevoir, (int)Gallade,
(int)Sableye,
(int)Mawile,
(int)Carvanha, (int)Sharpedo,
(int)Trapinch, (int)Vibrava, (int)Flygon,
(int)Cacnea, (int)Cacturne,
(int)Shuppet, (int)Banette,
(int)Duskull, (int)Dusclops, (int)Dusknoir,
(int)Absol,
(int)Bagon, (int)Shelgon, (int)Salamence,
(int)Beldum, (int)Metang, (int)Metagross,
(int)Turtwig, (int)Grotle, (int)Torterra,
(int)Stunky, (int)Skuntank,
(int)Snover, (int)Abomasnow,
};
private static readonly HashSet<int> PurifiedShiny = new HashSet<int>
{
(int)Ekans, (int)Arbok,
(int)Meowth, (int)Persian,
(int)Growlithe, (int)Arcanine,
(int)Grimer, (int)Muk,
(int)Drowzee, (int)Hypno,
(int)Koffing, (int)Weezing,
(int)Scyther, (int)Scizor,
(int)Pinsir,
(int)Lapras,
(int)Omanyte, (int)Omastar,
(int)Pineco, (int)Forretress,
(int)Sneasel, (int)Weavile,
(int)Stantler,
(int)Mawile,
(int)Absol,
(int)Bagon, (int)Shelgon, (int)Salamence,
(int)Beldum, (int)Metang, (int)Metagross,
};
/// <summary> Premier Ball Legality </summary>
private static readonly HashSet<int> AvailableAsRaids = new HashSet<int>(Purified)
{
// Base Species (Raids)
(int)Bulbasaur, (int)Ivysaur, (int)Venusaur,
(int)Charmander, (int)Charmeleon, (int)Charizard,
(int)Squirtle, (int)Wartortle, (int)Blastoise,
(int)Caterpie, (int)Metapod, (int)Butterfree,
(int)Beedrill,
(int)Pidgeot,
(int)Fearow,
(int)Ekans, (int)Arbok,
(int)Pikachu, (int)Raichu,
(int)Sandshrew, (int)Sandslash,
(int)Nidoqueen,
(int)NidoranM, (int)Nidorino, (int)Nidoking,
(int)Clefairy, (int)Clefable,
(int)Vulpix, (int)Ninetales,
(int)Jigglypuff, (int)Wigglytuff,
(int)Zubat, (int)Golbat, (int)Crobat,
(int)Gloom, (int)Vileplume, (int)Bellossom,
(int)Venonat, (int)Venomoth,
(int)Meowth, (int)Persian,
(int)Psyduck, (int)Golduck,
(int)Mankey, (int)Primeape,
(int)Growlithe, (int)Arcanine,
(int)Poliwag, (int)Poliwhirl, (int)Poliwrath, (int)Politoed,
(int)Abra, (int)Kadabra, (int)Alakazam,
(int)Machop, (int)Machoke, (int)Machamp,
(int)Victreebel,
(int)Tentacruel,
(int)Geodude, (int)Graveler, (int)Golem,
(int)Ponyta, (int)Rapidash,
(int)Slowpoke, (int)Slowbro, (int)Slowking,
(int)Magnemite, (int)Magneton, (int)Magnezone,
(int)Seel, (int)Dewgong,
(int)Grimer, (int)Muk,
(int)Shellder, (int)Cloyster,
(int)Haunter, (int)Gengar,
(int)Onix, (int)Steelix,
(int)Drowzee, (int)Hypno,
(int)Krabby, (int)Kingler,
(int)Voltorb, (int)Electrode,
(int)Exeggutor,
(int)Cubone, (int)Marowak,
(int)Hitmonlee, (int)Hitmonchan, (int)Hitmontop,
(int)Lickitung, (int)Lickilicky,
(int)Koffing, (int)Weezing,
(int)Rhydon, (int)Rhyperior,
(int)Chansey, (int)Blissey,
(int)Tangela, (int)Tangrowth,
(int)Horsea, (int)Seadra, (int)Kingdra,
(int)Staryu, (int)Starmie,
(int)Scyther, (int)Scizor,
(int)Jynx,
(int)Electabuzz, (int)Electivire,
(int)Magmar, (int)Magmortar,
(int)Pinsir,
(int)Magikarp, (int)Gyarados,
(int)Lapras,
(int)Eevee, (int)Vaporeon, (int)Jolteon, (int)Flareon, (int)Espeon, (int)Umbreon, (int)Leafeon, (int)Glaceon,
(int)Porygon, (int)Porygon2, (int)PorygonZ,
(int)Omanyte, (int)Omastar,
(int)Kabuto, (int)Kabutops,
(int)Aerodactyl,
(int)Snorlax,
(int)Articuno,
(int)Zapdos,
(int)Moltres,
(int)Dratini, (int)Dragonair, (int)Dragonite,
(int)Mewtwo,
(int)Chikorita, (int)Bayleef, (int)Meganium,
(int)Cyndaquil, (int)Quilava, (int)Typhlosion,
(int)Totodile, (int)Croconaw, (int)Feraligatr,
(int)Sentret, (int)Furret,
(int)Noctowl,
(int)Lanturn,
(int)Togetic, (int)Togekiss,
(int)Mareep, (int)Flaaffy, (int)Ampharos,
(int)Azumarill,
(int)Sudowoodo,
(int)Aipom, (int)Ambipom,
(int)Sunkern, (int)Sunflora,
(int)Yanma, (int)Yanmega,
(int)Murkrow, (int)Honchkrow,
(int)Misdreavus, (int)Mismagius,
(int)Unown + (1 << 11), // A
(int)Unown + (12 << 11), // L
(int)Unown + (18 << 11), // R
(int)Unown + (20 << 11), // T
(int)Unown + (21 << 11), // U
(int)Wobbuffet,
(int)Girafarig,
(int)Pineco, (int)Forretress,
(int)Gligar, (int)Gliscor,
(int)Snubbull, (int)Granbull,
(int)Qwilfish,
(int)Shuckle,
(int)Sneasel, (int)Weavile,
(int)Ursaring,
(int)Magcargo,
(int)Swinub, (int)Piloswine, (int)Mamoswine,
(int)Octillery,
(int)Delibird,
(int)Mantine,
(int)Skarmory,
(int)Houndour, (int)Houndoom,
(int)Donphan,
(int)Miltank,
(int)Raikou,
(int)Entei,
(int)Suicune,
(int)Larvitar, (int)Pupitar, (int)Tyranitar,
(int)Lugia,
(int)HoOh,
(int)Treecko, (int)Grovyle, (int)Sceptile,
(int)Torchic, (int)Combusken, (int)Blaziken,
(int)Mudkip, (int)Marshtomp, (int)Swampert,
(int)Mightyena,
(int)Wurmple, (int)Silcoon, (int)Beautifly, (int)Cascoon, (int)Dustox,
(int)Lotad, (int)Lombre, (int)Ludicolo,
(int)Nuzleaf, (int)Shiftry,
(int)Swellow,
(int)Wingull, (int)Pelipper,
(int)Ralts, (int)Kirlia, (int)Gardevoir, (int)Gallade,
(int)Masquerain,
(int)Breloom,
(int)Slakoth, (int)Vigoroth, (int)Slaking,
(int)Ninjask,
(int)Makuhita, (int)Hariyama,
(int)Nosepass, (int)Probopass,
(int)Sableye,
(int)Mawile,
(int)Aron, (int)Lairon, (int)Aggron,
(int)Meditite, (int)Medicham,
(int)Manectric,
(int)Plusle,
(int)Minun,
(int)Roselia, (int)Roserade,
(int)Swalot,
(int)Carvanha, (int)Sharpedo,
(int)Wailmer, (int)Wailord,
(int)Spoink, (int)Grumpig,
(int)Trapinch, (int)Vibrava, (int)Flygon,
(int)Cacnea, (int)Cacturne,
(int)Swablu, (int)Altaria,
(int)Lunatone,
(int)Solrock,
(int)Whiscash,
(int)Crawdaunt,
(int)Baltoy, (int)Claydol,
(int)Lileep, (int)Cradily,
(int)Anorith, (int)Armaldo,
(int)Feebas, (int)Milotic,
(int)Shuppet, (int)Banette,
(int)Duskull, (int)Dusclops, (int)Dusknoir,
(int)Absol,
(int)Snorunt, (int)Glalie, (int)Froslass,
(int)Walrein,
(int)Clamperl, (int)Huntail, (int)Gorebyss,
(int)Luvdisc,
(int)Bagon, (int)Shelgon, (int)Salamence,
(int)Beldum, (int)Metang, (int)Metagross,
(int)Regirock,
(int)Regice,
(int)Registeel,
(int)Latias,
(int)Latios,
(int)Kyogre,
(int)Groudon,
(int)Rayquaza,
(int)Deoxys,
(int)Turtwig, (int)Grotle, (int)Torterra,
(int)Chimchar, (int)Monferno, (int)Infernape,
(int)Piplup, (int)Prinplup, (int)Empoleon,
(int)Staraptor,
(int)Bidoof, (int)Bibarel,
(int)Kricketot, (int)Kricketune,
(int)Shinx, (int)Luxio, (int)Luxray,
(int)Cranidos, (int)Rampardos,
(int)Shieldon, (int)Bastiodon,
(int)Burmy, (int)Wormadam, (int)Mothim, // Plant Cloak
(int)Burmy + (1 << 11), (int)Wormadam + (1 << 11), (int)Mothim + (1 << 11), // Sandy Cloak
(int)Burmy + (2 << 11), (int)Wormadam + (2 << 11), (int)Mothim + (2 << 11), // Trash Cloak
(int)Combee, (int)Vespiquen,
(int)Buizel, (int)Floatzel,
(int)Drifloon, (int)Drifblim,
(int)Buneary, (int)Lopunny,
(int)Skuntank,
(int)Bronzor, (int)Bronzong,
(int)Hippopotas, (int)Hippowdon,
(int)Gible, (int)Gabite, (int)Garchomp,
(int)Skorupi, (int)Drapion,
(int)Croagunk, (int)Toxicroak,
(int)Lumineon,
(int)Abomasnow,
(int)Uxie,
(int)Mesprit,
(int)Azelf,
(int)Dialga,
(int)Palkia,
(int)Heatran,
(int)Regigigas,
(int)Giratina,
(int)Cresselia,
(int)Darkrai,
(int)Snivy, (int)Servine, (int)Serperior,
(int)Tepig, (int)Pignite, (int)Emboar,
(int)Oshawott, (int)Dewott, (int)Samurott,
(int)Patrat, (int)Watchog,
(int)Lillipup, (int)Herdier, (int)Stoutland,
(int)Liepard,
(int)Pidove, (int)Tranquill, (int)Unfezant,
(int)Blitzle, (int)Zebstrika,
(int)Roggenrola, (int)Boldore, (int)Gigalith,
(int)Woobat, (int)Swoobat,
(int)Excadrill,
(int)Timburr, (int)Gurdurr, (int)Conkeldurr,
(int)Tympole, (int)Palpitoad, (int)Seismitoad,
(int)Venipede, (int)Whirlipede, (int)Scolipede,
(int)Cottonee, (int)Whimsicott,
(int)Petilil, (int)Lilligant,
(int)Darumaka, (int)Darmanitan,
(int)Dwebble, (int)Crustle,
(int)Yamask, (int)Cofagrigus,
(int)Trubbish, (int)Garbodor,
(int)Minccino, (int)Cinccino,
(int)Gothita, (int)Gothorita, (int)Gothitelle,
(int)Solosis, (int)Duosion, (int)Reuniclus,
(int)Amoonguss,
(int)Joltik, (int)Galvantula,
(int)Ferroseed, (int)Ferrothorn,
(int)Klink, (int)Klang, (int)Klinklang,
(int)Elgyem, (int)Beheeyem,
(int)Litwick, (int)Lampent, (int)Chandelure,
(int)Cubchoo, (int)Beartic,
(int)Cryogonal,
(int)Stunfisk,
(int)Golurk,
(int)Cobalion,
(int)Terrakion,
(int)Virizion,
(int)Tornadus,
(int)Thundurus,
(int)Landorus,
(int)Reshiram,
(int)Zekrom,
(int)Kyurem,
// Alolan Forms (Raids)
(int)Raticate + (1 << 11),
(int)Raichu + (1 << 11),
(int)Vulpix + (1 << 11), (int)Ninetales + (1 << 11),
(int)Dugtrio + (1 << 11),
(int)Meowth + (1 << 11), (int)Persian + (1 << 11),
(int)Geodude + (1 << 11), (int)Graveler + (1 << 11), (int)Golem + (1 << 11),
(int)Grimer + (1 << 11), (int)Muk + (1 << 11),
(int)Exeggutor + (1 << 11),
(int)Marowak + (1 << 11),
// Galarian Forms (Raids)
(int)Ponyta + (1 << 11), (int)Rapidash + (1 << 11),
(int)Weezing + (1 << 11),
(int)Stunfisk + (1 << 11),
};
private static readonly HashSet<int> RequireLevelIV_Egg1_1 = new HashSet<int>
{
// Egg
// Minimum Lvl: 1
// Minimum GO IVs: 1/1/1
// Minimum Core Series IVs: 3/3/3/3/3/0
(int)Meowth + (2 << 11), (int)Perrserker,
(int)Ponyta + (1 << 11), (int)Rapidash + (1 << 11),
(int)Pichu,
(int)Cleffa,
(int)Igglybuff,
(int)Togepi,
(int)Tyrogue,
(int)Smoochum,
(int)Elekid,
(int)Magby,
(int)Zigzagoon + (1 << 11), (int)Linoone + (1 << 11), (int)Obstagoon,
(int)Azurill,
(int)Wynaut,
(int)Shinx, (int)Luxio, (int)Luxray,
(int)Budew,
(int)Chingling,
(int)Bonsly,
(int)Happiny,
(int)Munchlax,
(int)Riolu, (int)Lucario,
(int)Mantyke,
(int)Sandile, (int)Krokorok, (int)Krookodile,
(int)Darumaka, (int)Darmanitan,
(int)Darumaka + (1 << 11), (int)Darmanitan + (2 << 11),
(int)Klink, (int)Klang, (int)Klinklang,
(int)Pawniard, (int)Bisharp,
(int)Vullaby, (int)Mandibuzz,
};
private static readonly HashSet<int> RequireLevelIV_EggShiny1_1 = new HashSet<int>
{
// Shiny Baby Evolutions
// Minimum Lvl: 1
// Minimum GO IVs: 1/1/1
// Minimum Core Series IVs: 3/3/3/3/3/0
// The following Species can only be Shiny if hatched from an Egg.
// They can be encountered in the wild, but if they are Shiny, they must abide by Egg requirements.
(int)Togetic, (int)Togekiss,
(int)Jynx,
(int)Electabuzz, (int)Electivire,
(int)Magmar, (int)Magmortar,
};
private static readonly HashSet<int> RequireLevelIV_Raid15_1_LGPE = new HashSet<int>
{
// Raid Battle / Field Research
// Minimum Lvl: 15
// Minimum GO IVs: 1/1/1
// Minimum Core Series IVs: 3/3/3/3/3/0
// Pokémon from regular Raid Battles (Lv. 20) get moved down to Lv. 15 when Traded to low-level accounts.
(int)Raichu + (1 << 11),
(int)Articuno,
(int)Zapdos,
(int)Moltres,
(int)Mewtwo,
};
private static readonly HashSet<int> RequireLevelIV_Raid15_1 = new HashSet<int>(RequireLevelIV_Raid15_1_LGPE)
{
(int)Weezing + (1 << 11),
(int)Raikou,
(int)Entei,
(int)Suicune,
(int)Lugia,
(int)HoOh,
(int)Shedinja,
(int)Spinda,
(int)Regirock,
(int)Regice,
(int)Registeel,
(int)Latias,
(int)Latios,
(int)Kyogre,
(int)Groudon,
(int)Rayquaza,
(int)Spiritomb,
(int)Dialga,
(int)Palkia,
(int)Heatran,
(int)Regigigas,
(int)Giratina,
(int)Cresselia,
(int)Victini,
(int)Yamask + (1 << 11), (int)Runerigus,
(int)Cobalion,
(int)Terrakion,
(int)Virizion,
(int)Tornadus,
(int)Thundurus,
(int)Reshiram,
(int)Zekrom,
(int)Landorus,
(int)Kyurem,
};
private static readonly HashSet<int> RequireLevelIV_Field15_10 = new HashSet<int>
{
// Field Research (Mythical)
// Minimum Lvl: 15
// Minimum GO IVs: 10/10/10
// Minimum Core Series IVs: 21/21/21/21/21/0
(int)Mew,
(int)Celebi,
(int)Jirachi,
(int)Victini,
(int)Genesect,
};
private static readonly HashSet<int> RequireLevelIV_Raid20_10 = new HashSet<int>
{
// Raid Battle (Mythical)
// Minimum Lvl: 20
// Minimum GO IVs: 10/10/10
// Minimum Core Series IVs: 21/21/21/21/21/0
(int)Deoxys,
(int)Darkrai,
};
internal static readonly EncounterArea7g[] SlotsGO_GG = EncounterArea7g.GetArea(RequireLevelIV_Raid15_1_LGPE);
internal static readonly EncounterArea8g[] SlotsGO = EncounterArea8g.GetArea(MaxSpeciesID_GO_HOME, DisallowedSpecies, ExtraSpecies);
internal static bool IsBallValid(int species, int form, Ball ball)
{
if (ball == 0)
return false;
var sf = species | (form << 11);
if (ball == Ball.Premier && AvailableAsRaids.Contains(sf))
return true;
if (RequireLevelIV_Raid15_1.Contains(sf))
return ball == Ball.Premier;
if (RequireLevelIV_Raid20_10.Contains(sf))
return ball == Ball.Premier;
if (RequireLevelIV_Field15_10.Contains(sf))
return ball == Ball.Poke;
if (RequireLevelIV_Egg1_1.Contains(sf) || RequireLevelIV_EggShiny1_1.Contains(sf))
return ball == Ball.Poke;
return Ball.Ultra <= ball && ball <= Ball.Poke;
}
internal static int GetMinLevel(int species, int form)
{
var sf = species | (form << 11);
if (RequireLevelIV_Raid15_1.Contains(sf))
return 15;
if (RequireLevelIV_Field15_10.Contains(sf))
return 15;
if (RequireLevelIV_Raid20_10.Contains(sf))
return 20;
return 1;
}
internal static int GetMinIVs(int species, int form, Ball ball)
{
var sf = species | (form << 11);
if (ball == Ball.Premier)
{
if (RequireLevelIV_Raid15_1.Contains(sf))
return 1;
if (RequireLevelIV_Field15_10.Contains(sf))
return 10;
if (RequireLevelIV_Raid20_10.Contains(sf))
return 10;
return 0;
}
if (RequireLevelIV_Egg1_1.Contains(sf))
return 1;
if (RequireLevelIV_EggShiny1_1.Contains(sf))
return 1;
return 0;
}
public static bool IsShinyValid(int species, int form, Ball ball)
{
var sf = species | (form << 11);
if (ball == Ball.Premier)
{
if (PurifiedShiny.Contains(sf))
return true;
}
return !UnavailableShiny.Contains(sf);
}
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
}
}

View File

@ -1,17 +1,20 @@
namespace PKHeX.Core
{
public sealed class EncounterSlot7GO : EncounterSlot
public sealed class EncounterSlot7GO : EncounterSlot, IPogoSlot
{
public override int Generation => 7;
public PogoType Type { get; }
public Shiny Shiny { get; }
public EncounterSlot7GO(EncounterArea7g area, int species, int form, int min, int max) : base(area)
public EncounterSlot7GO(EncounterArea7g area, int species, int form, Shiny shiny, PogoType type) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
}
LevelMin = type.GetMinLevel();
LevelMax = EncountersGO.MAX_LEVEL;
public void ClampMinRaid(int level) => LevelMin = level;
Shiny = shiny;
Type = type;
}
}
}

View File

@ -1,17 +1,118 @@
namespace PKHeX.Core
{
public sealed class EncounterSlot8GO : EncounterSlot
public sealed class EncounterSlot8GO : EncounterSlot, IPogoSlotTime
{
public override int Generation => 8;
public GameVersion OriginGroup { get; }
public EncounterSlot8GO(EncounterArea8g area, int species, int form, GameVersion gameVersion, int min, int max) : base(area)
public PogoType Type { get; }
public Shiny Shiny { get; }
public int Start { get; }
public int End { get; }
public override string LongName
{
get
{
var init = $"{Name} ({Type})";
if (Start == 0 && End == 0)
return init;
return $"{init}: {GetDate(Start)}-{GetDate(End)}";
}
}
private static string GetDate(int time)
{
var d = time & 0xFF;
var m = (time >> 8) & 0xFF;
var y = time >> 16;
return $"{y:0000}.{m:00}.{d:00}";
}
public bool IsWithinStartEnd(int y, int m, int d)
{
var stamp = GetTimeStamp(y, m, d);
return IsWithinStartEnd(stamp);
}
public bool IsWithinStartEnd(int stamp)
{
if (End == 0)
return stamp >= Start;
if (Start == 0)
return stamp <= Start;
return Start <= stamp && stamp <= End;
}
public static int GetTimeStamp(int y, int m, int d) => (y << 16) | (m << 8) | d;
public EncounterSlot8GO(EncounterArea8g area, int species, int form, GameVersion gameVersion, PogoType type, Shiny shiny, int start, int end) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
LevelMin = type.GetMinLevel();
LevelMax = 40;
Start = start;
End = end;
Shiny = shiny;
Type = type;
OriginGroup = gameVersion;
}
public int GetMinIV() => Type.GetMinIV();
public bool IsBallValid(Ball ball) => Type.IsBallValid(ball);
}
public interface IPogoSlot
{
Shiny Shiny { get; }
PogoType Type { get; }
}
public interface IPogoSlotTime : IPogoSlot
{
int Start { get; }
int End { get; }
}
public enum PogoType : byte
{
None, // Don't use this.
Wild,
Egg,
/// <summary> requires level 15 and IV=1 </summary>
Raid15 = 10,
/// <summary> requires level 20 and IV=10 </summary>
Raid20,
/// <summary> requires level 15 and IV=10 </summary>
Field15 = 20,
Shadow = 30,
}
public static class PogoTypeExtensions
{
public static int GetMinLevel(this PogoType t) => t switch
{
_ => 1,
};
public static int GetMinIV(this PogoType t) => t switch
{
_ => 1,
};
public static bool IsBallValid(this PogoType t, Ball b) => t switch
{
PogoType.Egg => b == Ball.Poke,
PogoType.Raid15 => b == Ball.Premier,
PogoType.Raid20 => b == Ball.Premier,
_ => b != Ball.Premier,
};
}
}

View File

@ -87,7 +87,7 @@ private CheckResult VerifyBallWild(LegalityAnalysis data, EncounterSlot w)
private CheckResult VerifyBallGOHOME(LegalityAnalysis data, EncounterSlot8GO enc)
{
var valid = EncountersGO.IsBallValid(enc.Species, enc.Form, (Ball)data.pkm.Ball);
var valid = enc.IsBallValid((Ball)data.pkm.Ball);
return GetResult(valid);
}

View File

@ -116,18 +116,10 @@ private void VerifyIVsGoTransfer(LegalityAnalysis data)
}
else if (data.EncounterMatch is EncounterSlot8GO g)
{
var minIV = EncountersGO.GetMinIVs(g.Species, g.Form, (Ball) pkm.Ball);
var minIV = g.GetMinIV();
if (!IsGoIVSetValid(pkm, minIV))
data.AddLine(GetInvalid(LIVNotCorrect));
}
if (!pkm.IsShiny)
return;
var enc = data.EncounterMatch;
bool shinyValid = EncountersGO.IsShinyValid(enc.Species, enc.Form, (Ball)pkm.Ball);
if (!shinyValid)
data.AddLine(GetInvalid(LEncStaticPIDShiny, CheckIdentifier.PID));
}
private static bool IsGoIVSetValid(PKM pkm)

View File

@ -35,6 +35,8 @@
<None Remove="Resources\byte\encounter_e.pkl" />
<None Remove="Resources\byte\encounter_fr.pkl" />
<None Remove="Resources\byte\encounter_ge.pkl" />
<None Remove="Resources\byte\encounter_go_home.pkl" />
<None Remove="Resources\byte\encounter_go_lgpe.pkl" />
<None Remove="Resources\byte\encounter_gold.pkl" />
<None Remove="Resources\byte\encounter_gp.pkl" />
<None Remove="Resources\byte\encounter_hg.pkl" />
@ -877,6 +879,8 @@
<EmbeddedResource Include="Resources\byte\encounter_e.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_fr.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_ge.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_go_home.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_go_lgpe.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_gold.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_gp.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_hg.pkl" />

Binary file not shown.

Binary file not shown.