Rework Gen3 Event PIDIV detection & checks

Detect base algorithm (regular, regular-anti, anti, shiny) and check for sub-patterns that differ per encounter (such as OT gender).
Implement PIDIV generators within the Gift3* template, remove old branches
Add Table Weight detection (still need to test) for PCJP 5anniv
Add PCJP GCEA campaign 1-6 templates
Differentiate Mystry Mew seeds; disallow Mews that were deleted b4 cart cloning
Need to double check some gen3 events (esp hadou titans), but is 99% there

Thanks Sabresite, for your continued support
This commit is contained in:
Kurt 2024-10-16 00:01:10 -05:00
parent 71c9c43eef
commit b389cd3acd
36 changed files with 1282 additions and 599 deletions

View File

@ -2,6 +2,7 @@
using static PKHeX.Core.PIDType;
using static PKHeX.Core.Shiny;
using static PKHeX.Core.LanguageID;
using static PKHeX.Core.GiftGender3;
namespace PKHeX.Core;
@ -14,236 +15,240 @@ namespace PKHeX.Core;
/// </remarks>
internal static class EncountersWC3
{
// Higher frequency of being checked/requested.
private static readonly EncounterGift3[] Common =
[
new(151, 10, R) { Moves = new(001,144,000,000), Method = BACD_U, ID32 = 06930, Shiny = Never, OriginalTrainerName = "MYSTRY", OriginalTrainerGender = 3, Language = (int)English, FatefulEncounter = true }, // Mew
new(151, 10, R) { Moves = new(001,144,000,000), Method = BACD_R, ID32 = 06930, Shiny = Never, OriginalTrainerName = "MYSTRY", OriginalTrainerGender = 3, Language = (int)English, FatefulEncounter = true }, // Mew
new(385, 05, R) { Moves = new(273,093,156,000), Method = BACD_R, ID32 = 20043, Shiny = Random, OriginalTrainerName = "WISHMKR", OriginalTrainerGender = 0, Language = (int)English },
new(385, 05, RS, false, met: 0) { Moves = new(273,093,156,000), Method = Channel, TID16 = 40122, Shiny = Random, OriginalTrainerName = "CHANNEL", OriginalTrainerGender = 3 },
new(151, 10, R) { Moves = new(001,144,000,000), Method = BACD_M, ID32 = 06930, Shiny = Never, OriginalTrainerName = "MYSTRY", OriginalTrainerGender = RandD3, Language = (int)English, FatefulEncounter = true }, // Mew
new(385, 05, R) { Moves = new(273,093,156,000), Method = BACD_R, ID32 = 20043, Shiny = Random, OriginalTrainerName = "WISHMKR", OriginalTrainerGender = Only0, Language = (int)English },
new(385, 05, RS, false, met: 0) { Moves = new(273,093,156,000), Method = Channel, TID16 = 40122, Shiny = Random, OriginalTrainerName = "CHANNEL", OriginalTrainerGender = RandAlgo },
];
private static readonly EncounterGift3[] Japan =
[
new(385, 05, R) { Moves = new(001,144,000,000), Method = BACD_R, ID32 = 30719, Shiny = Never, OriginalTrainerName = "ネガイボシ", OriginalTrainerGender = 0, Language = (int)Japanese }, // Negai Boshi Jirachi
new(385, 05, RS){ Moves = new(001,144,000,000), Method = BACD_U_AX, ID32 = 30719, Shiny = Never, OriginalTrainerName = "ネガイボシ", OriginalTrainerGender = 3, Language = (int)Japanese }, // Negai Boshi Jirachi (Match Recipient)
new(263, 05, S) { Moves = new(033,045,039,000), Method = BACD_R_S, ID32 = 21121, Shiny = Always, OriginalTrainerName = "ルビー", OriginalTrainerGender = 1, Language = (int)Japanese }, // Berry Fix Ruby
new(263, 05, S) { Moves = new(033,045,039,000), Method = BACD_R_S, ID32 = 21121, Shiny = Always, OriginalTrainerName = "サファイア", OriginalTrainerGender = 0, Language = (int)Japanese }, // Berry Fix Sapphire
new(385, 05, R) { Moves = new(001,144,000,000), Method = BACD_R, ID32 = 40707, Shiny = Never, OriginalTrainerName = "タナバタ", OriginalTrainerGender = 1, Language = (int)Japanese }, // Tanabata Jirachi (2004)
new(025, 10, R) { Moves = new(019,084,039,086), Method = BACD_R, ID32 = 41205, Shiny = Never, OriginalTrainerName = "", OriginalTrainerGender = 0, Language = (int)Japanese }, // ANA Pikachu
new(052, 05, R) { Moves = new(010,045,000,000), Method = BACD_R, ID32 = 50318, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, Language = (int)Japanese }, // PokéPark Meowth
new(025, 10, R) { Moves = new(084,045,086,057), Method = BACD_R, ID32 = 50319, Shiny = Never, OriginalTrainerName = "ヨコハマ", OriginalTrainerGender = 0, Language = (int)Japanese }, // Yokohama Pikachu
new(151, 10, R) { Moves = new(001,144,000,000), Method = BACD_R, ID32 = 50716, Shiny = Never, OriginalTrainerName = "ハドウ", OriginalTrainerGender = 3, Language = (int)Japanese, FatefulEncounter = true }, // Hadou Mew
new(025, 10, R) { Moves = new(045,039,086,019), Method = BACD_R, ID32 = 50425, Shiny = Never, OriginalTrainerName = "", OriginalTrainerGender = 3, Language = (int)Japanese }, // GW Pikachu
new(025, 10, R) { Moves = new(045,039,086,019), Method = BACD_R, ID32 = 50701, Shiny = Never, OriginalTrainerName = "サッポロ", OriginalTrainerGender = 0, Language = (int)Japanese }, // Sapporo Pikachu
new(385, 05, R) { Moves = new(001,144,000,000), Method = BACD_R, ID32 = 50707, Shiny = Never, OriginalTrainerName = "タナバタ", OriginalTrainerGender = 1, Language = (int)Japanese }, // Tanabata Jirachi (2005)
new(375, 30, R) { Moves = new(036,093,232,287), Method = BACD_R, ID32 = 02005, Shiny = Never, OriginalTrainerName = "フェスタ", OriginalTrainerGender = 0, Language = (int)Japanese, RibbonNational = true }, // Festa Metang
new(202, 05, R) { Moves = new(068,243,219,194), Method = BACD_R, ID32 = 50701, Shiny = Never, OriginalTrainerName = "サンデー", OriginalTrainerGender = 3, Language = (int)Japanese }, // Sunday Wobbuffet
new(377, 40, R) { Moves = new(174,276,246,063), Method = BACD_R, ID32 = 50901, Shiny = Never, OriginalTrainerName = "ハドウ", OriginalTrainerGender = 3, Language = (int)Japanese }, // Regirock
new(378, 40, R) { Moves = new(174,276,246,063), Method = BACD_R, ID32 = 50901, Shiny = Never, OriginalTrainerName = "ハドウ", OriginalTrainerGender = 3, Language = (int)Japanese }, // Regice
new(379, 40, R) { Moves = new(174,276,246,063), Method = BACD_R, ID32 = 50901, Shiny = Never, OriginalTrainerName = "ハドウ", OriginalTrainerGender = 3, Language = (int)Japanese }, // Registeel
new(151, 30, R) { Moves = new(001,144,005,118), Method = BACD_R, ID32 = 60510, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 3, Language = (int)Japanese, FatefulEncounter = true }, // PokéPark Mew
new(251, 30, R) { Moves = new(215,219,246,248), Method = BACD_R, ID32 = 60623, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 3, Language = (int)Japanese }, // PokéPark Celebi
new(385, 05, R) { Moves = new(001,144,000,000), Method = BACD_R, ID32 = 60707, Shiny = Never, OriginalTrainerName = "タナバタ", OriginalTrainerGender = 3, Language = (int)Japanese }, // Tanabata Jirachi (2006)
new(251, 10, R) { Moves = new(073,105,215,219), Method = BACD_R, ID32 = 60720, Shiny = Never, OriginalTrainerName = "ミツリン", OriginalTrainerGender = 3, Language = (int)Japanese }, // Mitsurin Celebi (2006)
new(385, 30, R) { Moves = new(273,094,270,156), Method = BACD_R, ID32 = 60731, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, Language = (int)Japanese }, // PokéPark Jirachi (2006)
new(385, 30, R) { Moves = new(273,094,270,156), Method = BACD_R, ID32 = 60830, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, Language = (int)Japanese }, // PokéPark Jirachi (2006)
new(263, 05, S) { Moves = new(033,045,039,000), Language = (int)Japanese, Method = BACD_RBCD, ID32 = 21121, Shiny = Always, OriginalTrainerName = "ルビー", OriginalTrainerGender = RandD3_1 }, // Berry Fix Ruby
new(263, 05, S) { Moves = new(033,045,039,000), Language = (int)Japanese, Method = BACD_RBCD, ID32 = 21121, Shiny = Always, OriginalTrainerName = "サファイア", OriginalTrainerGender = RandD3_0 }, // Berry Fix Sapphire
new(385, 05, R) { Moves = new(001,144,000,000), Language = (int)Japanese, Method = BACD_T2, ID32 = 30719, Shiny = Never, OriginalTrainerName = "ネガイボシ", OriginalTrainerGender = Only0 }, // Negai Boshi Jirachi
new(385, 05, RS){ Moves = new(001,144,000,000), Language = (int)Japanese, Method = BACD_U_AX, ID32 = 30719, Shiny = Never, OriginalTrainerName = "ネガイボシ", OriginalTrainerGender = Recipient }, // Negai Boshi Jirachi (Match Recipient)
new(385, 05, R) { Moves = new(001,144,000,000), Language = (int)Japanese, Method = BACD_R_A, ID32 = 40707, Shiny = Never, OriginalTrainerName = "タナバタ", OriginalTrainerGender = Only1 }, // Tanabata Jirachi (2004)
new(025, 10, R) { Moves = new(019,084,039,086), Language = (int)Japanese, Method = BACD_R_A, ID32 = 41205, Shiny = Never, OriginalTrainerName = "", OriginalTrainerGender = Only0 }, // ANA Pikachu
new(052, 05, R) { Moves = new(010,045,000,000), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50318, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0 }, // PokéPark Meowth
new(025, 10, R) { Moves = new(084,045,086,057), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50319, Shiny = Never, OriginalTrainerName = "ヨコハマ", OriginalTrainerGender = Only0 }, // Yokohama Pikachu
new(151, 10, R) { Moves = new(001,144,000,000), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50716, Shiny = Never, OriginalTrainerName = "ハドウ", OriginalTrainerGender = RandD3, FatefulEncounter = true }, // Hadou Mew
new(025, 10, R) { Moves = new(045,039,086,019), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50425, Shiny = Never, OriginalTrainerName = "", OriginalTrainerGender = RandS3 }, // GW Pikachu
new(025, 10, R) { Moves = new(045,039,086,019), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50701, Shiny = Never, OriginalTrainerName = "サッポロ", OriginalTrainerGender = Only0 }, // Sapporo Pikachu
new(385, 05, R) { Moves = new(001,144,000,000), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50707, Shiny = Never, OriginalTrainerName = "タナバタ", OriginalTrainerGender = Only1 }, // Tanabata Jirachi (2005)
new(375, 30, R) { Moves = new(036,093,232,287), Language = (int)Japanese, Method = BACD_R_A, ID32 = 02005, Shiny = Never, OriginalTrainerName = "フェスタ", OriginalTrainerGender = Only0, RibbonNational = true }, // Festa Metang
new(202, 05, R) { Moves = new(068,243,219,194), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50701, Shiny = Never, OriginalTrainerName = "サンデー", OriginalTrainerGender = RandS3 }, // Sunday Wobbuffet
new(377, 40, R) { Moves = new(174,276,246,063), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50901, Shiny = Never, OriginalTrainerName = "ハドウ", OriginalTrainerGender = RandSG15 }, // Regirock
new(378, 40, R) { Moves = new(174,276,246,063), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50901, Shiny = Never, OriginalTrainerName = "ハドウ", OriginalTrainerGender = RandSG15 }, // Regice
new(379, 40, R) { Moves = new(174,276,246,063), Language = (int)Japanese, Method = BACD_R_A, ID32 = 50901, Shiny = Never, OriginalTrainerName = "ハドウ", OriginalTrainerGender = RandSG15 }, // Registeel
new(151, 30, R) { Moves = new(001,144,005,118), Language = (int)Japanese, Method = BACD_R_A, ID32 = 60510, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = RandD3, FatefulEncounter = true }, // PokéPark Mew
new(251, 30, R) { Moves = new(215,219,246,248), Language = (int)Japanese, Method = BACD_R_A, ID32 = 60623, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = RandS7 }, // PokéPark Celebi
new(385, 05, R) { Moves = new(001,144,000,000), Language = (int)Japanese, Method = BACD_R_A, ID32 = 60707, Shiny = Never, OriginalTrainerName = "タナバタ", OriginalTrainerGender = RandS7 }, // Tanabata Jirachi (2006)
new(251, 10, R) { Moves = new(073,105,215,219), Language = (int)Japanese, Method = BACD_R_A, ID32 = 60720, Shiny = Never, OriginalTrainerName = "ミツリン", OriginalTrainerGender = RandS7 }, // Mitsurin Celebi (2006)
new(385, 30, R) { Moves = new(273,094,270,156), Language = (int)Japanese, Method = BACD_R_A, ID32 = 60731, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = RandD3 }, // PokéPark Jirachi (2006)
new(385, 30, R) { Moves = new(273,094,270,156), Language = (int)Japanese, Method = BACD_R_A, ID32 = 60830, Shiny = Never, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = RandD3 }, // PokéPark Jirachi (2006)
];
private static readonly EncounterGift3[] International =
[
// EBGames/GameStop (March 1, 2004, to April 22, 2007), also via multi-game discs
new(263, 5, S) { Moves = new(033,045,039,000), Method = BACD_R_S, ID32 = 30317, Shiny = Always, OriginalTrainerName = "RUBY", OriginalTrainerGender = 1, Language = (int)English },
new(263, 5, S) { Moves = new(033,045,039,000), Method = BACD_R_S, ID32 = 30317, Shiny = Always, OriginalTrainerName = "SAPHIRE", OriginalTrainerGender = 0, Language = (int)English },
new(263, 5, S) { Moves = new(033,045,039,000), Method = BACD_RBCD, ID32 = 30317, Shiny = Always, OriginalTrainerName = "RUBY", OriginalTrainerGender = RandD3_1, Language = (int)English }, // Berry Fix Ruby
new(263, 5, S) { Moves = new(033,045,039,000), Method = BACD_RBCD, ID32 = 30317, Shiny = Always, OriginalTrainerName = "SAPHIRE", OriginalTrainerGender = RandD3_0, Language = (int)English }, // Berry Fix Sapphire
// English
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)English }, // Latios
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Latios
// French
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = 3, Language = (int)French }, // Latios
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNIV", OriginalTrainerGender = RandS7, Language = (int)French }, // Latios
// German
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = 3, Language = (int)German }, // Latios
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10JAHRE", OriginalTrainerGender = RandS7, Language = (int)German }, // Latios
// Italian
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = 3, Language = (int)Italian }, // Latios
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANNI", OriginalTrainerGender = RandS7, Language = (int)Italian }, // Latios
// Spanish
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = 3, Language = (int)Spanish }, // Latios
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Charizard
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Pikachu
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Articuno
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Suicune
new(249, 70, R) { Moves = new(105,056,240,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Lugia
new(250, 70, R) { Moves = new(105,126,241,129), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Ho-Oh
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R_A, ID32 = 06227, Shiny = Never, OriginalTrainerName = "10ANIV", OriginalTrainerGender = RandS7, Language = (int)Spanish }, // Latios
// Aura Mew
new(151, 10, R) { Moves = new(001,144,000,000), Method = BACD_R, ID32 = 20078, Shiny = Never, OriginalTrainerName = "Aura", OriginalTrainerGender = 3, FatefulEncounter = true }, // Mew
new(151, 10, R) { Moves = new(001,144,000,000), Method = BACD_R_A, ID32 = 20078, Shiny = Never, OriginalTrainerName = "Aura", OriginalTrainerGender = RandS7, FatefulEncounter = true }, // Mew
// English Events
new(375, 30, R) { Moves = new(036,093,232,287), Method = BACD_R, ID32 = 02005, Shiny = Never, OriginalTrainerName = "ROCKS", OriginalTrainerGender = 0, Language = (int)English, RibbonNational = true }, // Metang
new(386, 70, R) { Moves = new(322,105,354,063), Method = BACD_R, ID32 = 28606, Shiny = Never, OriginalTrainerName = "DOEL", OriginalTrainerGender = 3, Language = (int)English, FatefulEncounter = true }, // Deoxys
new(386, 70, R) { Moves = new(322,105,354,063), Method = BACD_R, ID32 = 00010, Shiny = Never, OriginalTrainerName = "SPACE C", OriginalTrainerGender = 3, Language = (int)English, FatefulEncounter = true }, // Deoxys
new(375, 30, R) { Moves = new(036,093,232,287), Method = BACD_R_A, ID32 = 02005, Shiny = Never, OriginalTrainerName = "ROCKS", OriginalTrainerGender = Only0, Language = (int)English, RibbonNational = true }, // Metang
new(386, 70, R) { Moves = new(322,105,354,063), Method = BACD_R_A, ID32 = 28606, Shiny = Never, OriginalTrainerName = "DOEL", OriginalTrainerGender = RandS7, Language = (int)English, FatefulEncounter = true }, // Deoxys
new(386, 70, R) { Moves = new(322,105,354,063), Method = BACD_R_A, ID32 = 00010, Shiny = Never, OriginalTrainerName = "SPACE C", OriginalTrainerGender = RandS7, Language = (int)English, FatefulEncounter = true }, // Deoxys
// Party of the Decade
new(001, 70, R) { Moves = new(230,074,076,235), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Bulbasaur
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Charizard
new(009, 70, R) { Moves = new(182,240,130,056), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Blastoise
new(025, 70, R) { Moves = new(085,087,113,019), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Pikachu (Fly)
new(065, 70, R) { Moves = new(248,347,094,271), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Alakazam
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Articuno
new(145, 70, R) { Moves = new(097,197,065,268), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Zapdos
new(146, 70, R) { Moves = new(097,203,053,219), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Moltres
new(149, 70, R) { Moves = new(097,219,017,200), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Dragonite
new(157, 70, R) { Moves = new(098,172,129,053), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Typhlosion
new(196, 70, R) { Moves = new(060,244,094,234), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Espeon
new(197, 70, R) { Moves = new(185,212,103,236), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Umbreon
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Suicune
new(248, 70, R) { Moves = new(037,184,242,089), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Tyranitar
new(257, 70, R) { Moves = new(299,163,119,327), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Blaziken
new(359, 70, R) { Moves = new(104,163,248,195), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Absol
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = 3, Language = (int)English }, // Latios
new(001, 70, R) { Moves = new(230,074,076,235), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Bulbasaur
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Charizard
new(009, 70, R) { Moves = new(182,240,130,056), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Blastoise
new(025, 70, R) { Moves = new(085,087,113,019), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Pikachu (Fly)
new(065, 70, R) { Moves = new(248,347,094,271), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Alakazam
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Articuno
new(145, 70, R) { Moves = new(097,197,065,268), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Zapdos
new(146, 70, R) { Moves = new(097,203,053,219), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Moltres
new(149, 70, R) { Moves = new(097,219,017,200), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Dragonite
new(157, 70, R) { Moves = new(098,172,129,053), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Typhlosion
new(196, 70, R) { Moves = new(060,244,094,234), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Espeon
new(197, 70, R) { Moves = new(185,212,103,236), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Umbreon
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Suicune
new(248, 70, R) { Moves = new(037,184,242,089), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Tyranitar
new(257, 70, R) { Moves = new(299,163,119,327), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Blaziken
new(359, 70, R) { Moves = new(104,163,248,195), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Absol
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R_A, ID32 = 06808, Shiny = Never, OriginalTrainerName = "10 ANIV", OriginalTrainerGender = RandS7, Language = (int)English }, // Latios
// Journey Across America
new(001, 70, R) { Moves = new(230,074,076,235), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Bulbasaur
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Charizard
new(009, 70, R) { Moves = new(182,240,130,056), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Blastoise
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Pikachu (No Fly)
new(065, 70, R) { Moves = new(248,347,094,271), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Alakazam
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Articuno
new(145, 70, R) { Moves = new(097,197,065,268), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Zapdos
new(146, 70, R) { Moves = new(097,203,053,219), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Moltres
new(149, 70, R) { Moves = new(097,219,017,200), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Dragonite
new(157, 70, R) { Moves = new(098,172,129,053), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Typhlosion
new(196, 70, R) { Moves = new(060,244,094,234), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Espeon
new(197, 70, R) { Moves = new(185,212,103,236), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Umbreon
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Suicune
new(248, 70, R) { Moves = new(037,184,242,089), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Tyranitar
new(251, 70, R) { Moves = new(246,248,226,195), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Celebi
new(257, 70, R) { Moves = new(299,163,119,327), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Blaziken
new(359, 70, R) { Moves = new(104,163,248,195), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Absol
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = 3, Language = (int)English }, // Latios
new(001, 70, R) { Moves = new(230,074,076,235), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Bulbasaur
new(006, 70, R) { Moves = new(017,163,082,083), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Charizard
new(009, 70, R) { Moves = new(182,240,130,056), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Blastoise
new(025, 70, R) { Moves = new(085,097,087,113), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Pikachu (No Fly)
new(065, 70, R) { Moves = new(248,347,094,271), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Alakazam
new(144, 70, R) { Moves = new(097,170,058,115), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Articuno
new(145, 70, R) { Moves = new(097,197,065,268), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Zapdos
new(146, 70, R) { Moves = new(097,203,053,219), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Moltres
new(149, 70, R) { Moves = new(097,219,017,200), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Dragonite
new(157, 70, R) { Moves = new(098,172,129,053), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Typhlosion
new(196, 70, R) { Moves = new(060,244,094,234), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Espeon
new(197, 70, R) { Moves = new(185,212,103,236), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Umbreon
new(243, 70, R) { Moves = new(098,209,115,242), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Raikou
new(244, 70, R) { Moves = new(083,023,053,207), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Entei
new(245, 70, R) { Moves = new(016,062,054,243), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Suicune
new(248, 70, R) { Moves = new(037,184,242,089), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Tyranitar
new(251, 70, R) { Moves = new(246,248,226,195), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Celebi
new(257, 70, R) { Moves = new(299,163,119,327), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Blaziken
new(359, 70, R) { Moves = new(104,163,248,195), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Absol
new(380, 70, R) { Moves = new(296,094,105,204), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Latias
new(381, 70, R) { Moves = new(295,094,105,349), Method = BACD_R_A, ID32 = 00010, OriginalTrainerName = "10 ANIV", Shiny = Never, OriginalTrainerGender = RandS7, Language = (int)English }, // Latios
];
public const string PCJPEggTrainerName = "オヤNAME";
private const string M2WishEggOT = "";
private static readonly EncounterGift3[] Eggs =
[
// Pokémon Box -- Recipient
new(333, 5, Gen3, true) { Moves = new(064,045,206,000), Method = BACD_U, OriginalTrainerName = "", OriginalTrainerGender = 1 }, // Swablu Egg with False Swipe
new(263, 5, Gen3, true) { Moves = new(033,045,039,245), Method = BACD_U, OriginalTrainerName = "", OriginalTrainerGender = 1 }, // Zigzagoon Egg with Extreme Speed
new(300, 5, Gen3, true) { Moves = new(045,033,039,006), Method = BACD_U, OriginalTrainerName = "", OriginalTrainerGender = 1 }, // Skitty Egg with Pay Day
new(172, 5, Gen3, true) { Moves = new(084,204,057,000), Method = BACD_U, OriginalTrainerName = "", OriginalTrainerGender = 1 }, // Pichu Egg with Surf
new(333, 5, Gen3, true) { Moves = new(064,045,206,000), Method = BACD_U, OriginalTrainerName = "", OriginalTrainerGender = Only1 }, // Swablu Egg with False Swipe
new(263, 5, Gen3, true) { Moves = new(033,045,039,245), Method = BACD_U, OriginalTrainerName = "", OriginalTrainerGender = Only1 }, // Zigzagoon Egg with Extreme Speed
new(300, 5, Gen3, true) { Moves = new(045,033,039,006), Method = BACD_U, OriginalTrainerName = "", OriginalTrainerGender = Only1 }, // Skitty Egg with Pay Day
new(172, 5, Gen3, true) { Moves = new(084,204,057,000), Method = BACD_U, OriginalTrainerName = "", OriginalTrainerGender = Only1 }, // Pichu Egg with Surf
// PCJP - Pokémon Center 5th Anniversary Eggs (April 25 to May 18, 2003)
new(172, 5, R, true) { Moves = new(084,204,298,000), Method = BACD_R_S, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Pichu with Teeter Dance
new(172, 5, R, true) { Moves = new(084,204,273,000), Method = BACD_R_S, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Pichu with Wish
new(172, 5, R, true) { Moves = new(084,204,298,000), Method = BACD_R, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Pichu with Teeter Dance
new(172, 5, R, true) { Moves = new(084,204,273,000), Method = BACD_R, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Pichu with Wish
new(280, 5, R, true) { Moves = new(045,204,000,000), Method = BACD_R, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Ralts with Charm
new(280, 5, R, true) { Moves = new(045,273,000,000), Method = BACD_R, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Ralts with Wish
new(359, 5, R, true) { Moves = new(010,043,180,000), Method = BACD_R, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Absol with Spite
new(359, 5, R, true) { Moves = new(010,043,273,000), Method = BACD_R, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Absol with Wish
new(371, 5, R, true) { Moves = new(099,044,334,000), Method = BACD_R, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Bagon with Iron Defense
new(371, 5, R, true) { Moves = new(099,044,273,000), Method = BACD_R, OriginalTrainerName = "オヤNAME", OriginalTrainerGender = 0 }, // Bagon with Wish
new(172, 5, R, true) { Moves = new(084,204,298,000), Method = BACD_T3, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0, Shiny = Always }, // Pichu with Teeter Dance
new(172, 5, R, true) { Moves = new(084,204,273,000), Method = BACD_T3, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0, Shiny = Always }, // Pichu with Wish
new(172, 5, R, true) { Moves = new(084,204,298,000), Method = BACD_T2, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0 }, // Pichu with Teeter Dance
new(172, 5, R, true) { Moves = new(084,204,273,000), Method = BACD_T2, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0 }, // Pichu with Wish
new(280, 5, R, true) { Moves = new(045,204,000,000), Method = BACD_T2, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0 }, // Ralts with Charm
new(280, 5, R, true) { Moves = new(045,273,000,000), Method = BACD_T2, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0 }, // Ralts with Wish
new(359, 5, R, true) { Moves = new(010,043,180,000), Method = BACD_T2, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0 }, // Absol with Spite
new(359, 5, R, true) { Moves = new(010,043,273,000), Method = BACD_T2, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0 }, // Absol with Wish
new(371, 5, R, true) { Moves = new(099,044,334,000), Method = BACD_T2, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0 }, // Bagon with Iron Defense
new(371, 5, R, true) { Moves = new(099,044,273,000), Method = BACD_T2, OriginalTrainerName = PCJPEggTrainerName, OriginalTrainerGender = Only0 }, // Bagon with Wish
// Wondercard gifts call the same give egg script as the Hot Springs Wynaut, but the game has a vblank interrupt between PID and IVs (potentially because the script does not lock when starting).
// Removing the vblank interrupt via ROM patch results in the scripts yielding a Method 1 PID/IV, so hopefully it's consistent enough to assume all are just Method 2 and no splits.
// PCJP Egg Pokémon Present Eggs - Wondercard (March 21 to April 4, 2004)
new(043, 5, FRLG, true) { Moves = new(071,073,000,000), Method = Method_2, FatefulEncounter = true }, // Oddish with Leech Seed
new(052, 5, FRLG, true) { Moves = new(010,045,080,000), Method = Method_2, FatefulEncounter = true }, // Meowth with Petal Dance
new(060, 5, FRLG, true) { Moves = new(145,186,000,000), Method = Method_2, FatefulEncounter = true }, // Poliwag with Sweet Kiss
new(069, 5, FRLG, true) { Moves = new(022,298,000,000), Method = Method_2, FatefulEncounter = true }, // Bellsprout with Teeter Dance
new(043, 5, FRLG, true) { Moves = new(071,073,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Oddish with Leech Seed
new(052, 5, FRLG, true) { Moves = new(010,045,080,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Meowth with Petal Dance
new(060, 5, FRLG, true) { Moves = new(145,186,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Poliwag with Sweet Kiss
new(069, 5, FRLG, true) { Moves = new(022,298,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Bellsprout with Teeter Dance
// PCNY Wish Eggs - Wondercard (December 16, 2004, to January 2, 2005)
new(083, 5, FRLG, true) { Moves = new(281,273,000,000), Method = Method_2, FatefulEncounter = true }, // Farfetch'd with Wish & Yawn
new(096, 5, FRLG, true) { Moves = new(187,273,000,000), Method = Method_2, FatefulEncounter = true }, // Drowzee with Wish & Belly Drum
new(102, 5, FRLG, true) { Moves = new(230,273,000,000), Method = Method_2, FatefulEncounter = true }, // Exeggcute with Wish & Sweet Scent
new(108, 5, FRLG, true) { Moves = new(215,273,000,000), Method = Method_2, FatefulEncounter = true }, // Lickitung with Wish & Heal Bell
new(113, 5, FRLG, true) { Moves = new(230,273,000,000), Method = Method_2, FatefulEncounter = true }, // Chansey with Wish & Sweet Scent
new(115, 5, FRLG, true) { Moves = new(281,273,000,000), Method = Method_2, FatefulEncounter = true }, // Kangaskhan with Wish & Yawn
new(083, 5, FRLG, true) { Moves = new(281,273,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Farfetch'd with Wish & Yawn
new(096, 5, FRLG, true) { Moves = new(187,273,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Drowzee with Wish & Belly Drum
new(102, 5, FRLG, true) { Moves = new(230,273,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Exeggcute with Wish & Sweet Scent
new(108, 5, FRLG, true) { Moves = new(215,273,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Lickitung with Wish & Heal Bell
new(113, 5, FRLG, true) { Moves = new(230,273,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Chansey with Wish & Sweet Scent
new(115, 5, FRLG, true) { Moves = new(281,273,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Kangaskhan with Wish & Yawn
// PokéPark Eggs - Wondercard (March 12 to May 8, 2005)
new(054, 5, EFL, true) { Moves = new(346,010,039,300), Method = Method_2, FatefulEncounter = true }, // Psyduck with Mud Sport
new(172, 5, EFL, true) { Moves = new(084,204,266,000), Method = Method_2, FatefulEncounter = true }, // Pichu with Follow me
new(174, 5, EFL, true) { Moves = new(047,204,111,321), Method = Method_2, FatefulEncounter = true }, // Igglybuff with Tickle
new(222, 5, EFL, true) { Moves = new(033,300,000,000), Method = Method_2, FatefulEncounter = true }, // Corsola with Mud Sport
new(276, 5, EFL, true) { Moves = new(064,045,116,297), Method = Method_2, FatefulEncounter = true }, // Taillow with Feather Dance
new(283, 5, EFL, true) { Moves = new(145,300,000,000), Method = Method_2, FatefulEncounter = true }, // Surskit with Mud Sport
new(293, 5, EFL, true) { Moves = new(001,253,298,000), Method = Method_2, FatefulEncounter = true }, // Whismur with Teeter Dance
new(300, 5, EFL, true) { Moves = new(045,033,039,205), Method = Method_2, FatefulEncounter = true }, // Skitty with Rollout
new(311, 5, EFL, true) { Moves = new(045,086,346,000), Method = Method_2, FatefulEncounter = true }, // Plusle with Water Sport
new(312, 5, EFL, true) { Moves = new(045,086,300,000), Method = Method_2, FatefulEncounter = true }, // Minun with Mud Sport
new(325, 5, EFL, true) { Moves = new(150,253,000,000), Method = Method_2, FatefulEncounter = true }, // Spoink with Uproar
new(327, 5, EFL, true) { Moves = new(033,253,047,000), Method = Method_2, FatefulEncounter = true }, // Spinda with Sing
new(331, 5, EFL, true) { Moves = new(040,043,071,227), Method = Method_2, FatefulEncounter = true }, // Cacnea with Encore
new(341, 5, EFL, true) { Moves = new(145,346,000,000), Method = Method_2, FatefulEncounter = true }, // Corphish with Water Sport
new(360, 5, EFL, true) { Moves = new(150,204,227,321), Method = Method_2, FatefulEncounter = true }, // Wynaut with Tickle
new(054, 5, EFL, true) { Moves = new(346,010,039,300), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Psyduck with Mud Sport
new(172, 5, EFL, true) { Moves = new(084,204,266,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Pichu with Follow me
new(174, 5, EFL, true) { Moves = new(047,204,111,321), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Igglybuff with Tickle
new(222, 5, EFL, true) { Moves = new(033,300,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Corsola with Mud Sport
new(276, 5, EFL, true) { Moves = new(064,045,116,297), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Taillow with Feather Dance
new(283, 5, EFL, true) { Moves = new(145,300,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Surskit with Mud Sport
new(293, 5, EFL, true) { Moves = new(001,253,298,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Whismur with Teeter Dance
new(300, 5, EFL, true) { Moves = new(045,033,039,205), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Skitty with Rollout
new(311, 5, EFL, true) { Moves = new(045,086,346,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Plusle with Water Sport
new(312, 5, EFL, true) { Moves = new(045,086,300,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Minun with Mud Sport
new(325, 5, EFL, true) { Moves = new(150,253,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Spoink with Uproar
new(327, 5, EFL, true) { Moves = new(033,253,047,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Spinda with Sing
new(331, 5, EFL, true) { Moves = new(040,043,071,227), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Cacnea with Encore
new(341, 5, EFL, true) { Moves = new(145,346,000,000), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Corphish with Water Sport
new(360, 5, EFL, true) { Moves = new(150,204,227,321), Method = Method_2, FatefulEncounter = true, OriginalTrainerName = M2WishEggOT }, // Wynaut with Tickle
// PokéPark Eggs - DS Download Play (March 12 to May 8, 2005)
new(054, 5, R, true, 5) { Moves = new(346,010,039,300), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Psyduck with Mud Sport
new(172, 5, R, true, 5) { Moves = new(084,204,266,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Pichu with Follow Me
new(174, 5, R, true, 5) { Moves = new(047,204,111,321), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Igglybuff with Tickle
new(222, 5, R, true, 5) { Moves = new(033,300,000,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Corsola with Mud Sport
new(276, 5, R, true, 5) { Moves = new(064,045,116,297), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Taillow with Feather Dance
new(283, 5, R, true, 5) { Moves = new(145,300,000,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Surskit with Mud Sport
new(293, 5, R, true, 5) { Moves = new(001,253,298,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Whismur with Teeter Dance
new(300, 5, R, true, 5) { Moves = new(045,033,039,205), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Skitty with Rollout
new(311, 5, R, true, 5) { Moves = new(045,086,346,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Plusle with Water Sport
new(312, 5, R, true, 5) { Moves = new(045,086,300,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Minun with Mud Sport
new(325, 5, R, true, 5) { Moves = new(150,253,000,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Spoink with Uproar
new(327, 5, R, true, 5) { Moves = new(033,253,047,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Spinda with Sing
new(331, 5, R, true, 5) { Moves = new(040,043,071,227), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Cacnea with Encore
new(341, 5, R, true, 5) { Moves = new(145,346,000,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Corphish with Water Sport
new(360, 5, R, true, 5) { Moves = new(150,204,227,321), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = 0, ID32 = 50318 }, // Wynaut with Tickle
new(054, 5, R, true, 5) { Moves = new(346,010,039,300), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Psyduck with Mud Sport
new(172, 5, R, true, 5) { Moves = new(084,204,266,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Pichu with Follow Me
new(174, 5, R, true, 5) { Moves = new(047,204,111,321), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Igglybuff with Tickle
new(222, 5, R, true, 5) { Moves = new(033,300,000,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Corsola with Mud Sport
new(276, 5, R, true, 5) { Moves = new(064,045,116,297), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Taillow with Feather Dance
new(283, 5, R, true, 5) { Moves = new(145,300,000,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Surskit with Mud Sport
new(293, 5, R, true, 5) { Moves = new(001,253,298,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Whismur with Teeter Dance
new(300, 5, R, true, 5) { Moves = new(045,033,039,205), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Skitty with Rollout
new(311, 5, R, true, 5) { Moves = new(045,086,346,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Plusle with Water Sport
new(312, 5, R, true, 5) { Moves = new(045,086,300,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Minun with Mud Sport
new(325, 5, R, true, 5) { Moves = new(150,253,000,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Spoink with Uproar
new(327, 5, R, true, 5) { Moves = new(033,253,047,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Spinda with Sing
new(331, 5, R, true, 5) { Moves = new(040,043,071,227), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Cacnea with Encore
new(341, 5, R, true, 5) { Moves = new(145,346,000,000), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Corphish with Water Sport
new(360, 5, R, true, 5) { Moves = new(150,204,227,321), Method = BACD_R, OriginalTrainerName = "ポケパーク", OriginalTrainerGender = Only0, ID32 = 50318 }, // Wynaut with Tickle
];
internal static readonly EncounterGift3[] Encounter_WC3 = [..Common, ..International, .. Japan, ..Eggs];
internal static readonly EncounterGift3NY[] PCNY = EncounterGift3NY.GetArray(EncounterUtil.Get("pcny"));
internal static readonly EncounterGift3JPN[] PCJP = [];
internal static readonly EncounterGift3JPN[] PCJP = Gen3PCJP.GetArray();
}

View File

@ -64,7 +64,7 @@ public IEnumerable<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, Le
foreach (var enc in iterator)
{
var e = enc.Encounter;
if (!IsTypeCompatible(e, pk, info.PIDIV.Type))
if (!IsTypeCompatible(e, pk, ref info.GetPIDIVRef()))
{
defer.Update(DeferralType.PIDIV, e);
continue;
@ -76,26 +76,6 @@ public IEnumerable<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, Le
}
if (e is not EncounterSlot3 slot)
{
if (e is EncounterGift3 gift)
{
if (gift.TID16 == 40122) // CHANNEL Jirachi
{
var chk = ChannelJirachi.GetPossible(info.PIDIV.OriginSeed);
if (chk.Pattern is not ChannelJirachiRandomResult.None)
info.PIDIV = info.PIDIV.AsEncounteredVia(new(chk.Seed, LeadRequired.None));
else
info.ManualFlag = EncounterYieldFlag.InvalidPIDIV;
yield return gift;
yield break;
}
if (gift.TID16 == 06930) // MYSTRY Mew
{
if (!MystryMew.IsValidSeed(info.PIDIV.OriginSeed))
info.ManualFlag = EncounterYieldFlag.InvalidPIDIV;
yield return gift;
yield break;
}
}
yield return e;
continue;
}
@ -134,8 +114,11 @@ public IEnumerable<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, Le
_ => pk.Ball is not (byte)Ball.Safari,
};
private static bool IsTypeCompatible(IEncounterTemplate enc, PKM pk, PIDType type)
private static bool IsTypeCompatible(IEncounterTemplate enc, PKM pk, ref PIDIV pidiv)
{
if (enc is IRandomCorrelationEvent3 revise)
return revise.IsCompatibleReviseReset(ref pidiv, pk);
var type = pidiv.Type;
if (enc is IRandomCorrelation r)
return r.IsCompatible(type, pk);
return type == PIDType.None;

View File

@ -157,7 +157,7 @@ private bool IsMatchPartial(PKM pk)
}
#endregion
public bool IsCompatible(PIDType val, PKM pk) => val is PIDType.CXD;
public bool IsCompatible(PIDType type, PKM pk) => type is PIDType.CXD;
public PIDType GetSuggestedCorrelation() => PIDType.CXD;

View File

@ -196,11 +196,11 @@ private bool IsMatchLocation(PKM pk)
#endregion
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
if (IsEReader)
return true;
return val is PIDType.CXD;
return type is PIDType.CXD;
}
public PIDType GetSuggestedCorrelation()

View File

@ -142,11 +142,11 @@ private bool IsMatchPartial(PKM pk)
}
#endregion
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
if (IsColoStarter)
return val is PIDType.CXD_ColoStarter;
return val is PIDType.CXD;
return type is PIDType.CXD_ColoStarter;
return type is PIDType.CXD;
}
public PIDType GetSuggestedCorrelation() => PIDType.CXD;

View File

@ -120,11 +120,11 @@ public EncounterMatchRating GetMatchRating(PKM pk)
private bool IsDeferredSafari3(bool IsSafariBall) => IsSafariBall != Locations.IsSafariZoneLocation3(Location);
#endregion
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
if (Species != (int)Core.Species.Unown)
return val is (Method_1 or Method_2 or Method_3 or Method_4);
return val is (Method_1_Unown or Method_2_Unown or Method_3_Unown or Method_4_Unown);
return type is (Method_1 or Method_2 or Method_3 or Method_4);
return type is (Method_1_Unown or Method_2_Unown or Method_3_Unown or Method_4_Unown);
}
public PIDType GetSuggestedCorrelation() => Species == (int)Core.Species.Unown ? Method_1_Unown : Method_1;

View File

@ -176,15 +176,15 @@ private bool IsMatchPartial(PKM pk)
}
#endregion
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
var version = pk.Version;
if (version is GameVersion.E)
return val is PIDType.Method_1;
return type is PIDType.Method_1;
if (version is GameVersion.FR or GameVersion.LG)
return Roaming ? IsRoamerPIDIV(val, pk) : val is PIDType.Method_1;
return Roaming ? IsRoamerPIDIV(type, pk) : type is PIDType.Method_1;
// RS, roamer glitch && RSBox s/w emulation => method 4 available
return Roaming ? IsRoamerPIDIV(val, pk) : val is (PIDType.Method_1 or PIDType.Method_4);
return Roaming ? IsRoamerPIDIV(type, pk) : type is (PIDType.Method_1 or PIDType.Method_4);
}
private static bool IsRoamerPIDIV(PIDType val, PKM pk)

View File

@ -22,6 +22,34 @@ public static class Gen3PCJP
private const string Fukuoka = "フクオカ";
private const string Sapporo = "サッポロ";
private static ReadOnlySpan<ushort> Distro1 => [252, 255, 258];
private static ReadOnlySpan<ushort> Distro2 => [152, 155, 158];
private static ReadOnlySpan<ushort> Distro3 => [023, 025, 052, 058, 069, 079, 090, 113, 115, 123, 125, 126, 128, 198, 200, 211, 215, 225, 226];
private static ReadOnlySpan<ushort> Distro4 => [1, 4, 7];
private static ReadOnlySpan<ushort> Distro5 => [025, 270, 273, 283, 300, 302, 303, 307, 311, 312, 315, 335, 336, 337, 338, 358];
private static ReadOnlySpan<ushort> Distro6 => [025, 163, 179, 190, 191, 202, 204, 207, 209, 213, 216, 228, 234, 235];
public static EncounterGift3JPN[] GetArray()
{
var size = Distro1.Length + Distro2.Length + Distro3.Length + Distro4.Length + Distro5.Length + Distro6.Length;
var arr = new EncounterGift3JPN[size];
var i = 0;
AddDistro(arr, ref i, Distro1, First);
AddDistro(arr, ref i, Distro2, Second);
AddDistro(arr, ref i, Distro3, Third);
AddDistro(arr, ref i, Distro4, Fourth);
AddDistro(arr, ref i, Distro5, Fifth);
AddDistro(arr, ref i, Distro6, Sixth);
return arr;
static void AddDistro(EncounterGift3JPN[] arr, ref int i, ReadOnlySpan<ushort> Distro, Distribution3JPN dist)
{
foreach (var species in Distro)
arr[i++] = new EncounterGift3JPN(species, dist);
}
}
public static ushort GetTrainerID(this Distribution3JPN dist) => dist switch
{
First => 51126,

View File

@ -1,4 +1,7 @@
using System;
using static PKHeX.Core.PIDType;
using static PKHeX.Core.CommonEvent3;
using static PKHeX.Core.CommonEvent3Checker;
namespace PKHeX.Core;
@ -6,7 +9,7 @@ namespace PKHeX.Core;
/// Generation 3 Event Gift
/// </summary>
public sealed class EncounterGift3 : IEncounterable, IEncounterMatch, IMoveset, IFatefulEncounterReadOnly,
IRibbonSetEvent3, IRandomCorrelation, IFixedTrainer, IMetLevel
IRibbonSetEvent3, IRandomCorrelationEvent3, IFixedTrainer, IMetLevel
{
public ushort Species { get; }
public byte Form => 0;
@ -16,7 +19,6 @@ public sealed class EncounterGift3 : IEncounterable, IEncounterMatch, IMoveset,
public bool IsEgg { get; }
private const ushort UnspecifiedID = ushort.MaxValue;
private const byte UnspecifiedTrainerGender = 3;
/// <summary>
/// Matched <see cref="PIDIV"/> Type
@ -24,8 +26,8 @@ public sealed class EncounterGift3 : IEncounterable, IEncounterMatch, IMoveset,
public required PIDType Method { get; init; }
public required Moveset Moves { get; init; }
public string OriginalTrainerName { get; init; } = string.Empty;
public byte OriginalTrainerGender { get; init; } = UnspecifiedTrainerGender;
public required string OriginalTrainerName { get; init; }
public GiftGender3 OriginalTrainerGender { get; init; }
public uint ID32 { get => (uint)(SID16 << 16 | TID16); init => (SID16, TID16) = ((ushort)(value >> 16), (ushort)value); }
public ushort TID16 { get; init; } = UnspecifiedID;
public ushort SID16 { get; init; } = UnspecifiedID;
@ -67,17 +69,6 @@ public EncounterGift3(ushort species, byte level, GameVersion version, bool egg,
MetLevel = met;
}
public bool IsCompatible(PIDType type, PKM pk)
{
if (type == Method)
return true;
// forced shiny eggs, when hatched, can lose their detectable correlation.
if (!IsEgg || pk.IsEgg)
return false;
return type is PIDType.BACD_R_S or PIDType.BACD_U_S;
}
PKM IEncounterConvertible.ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr, EncounterCriteria.Unrestricted);
PKM IEncounterConvertible.ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) => ConvertToPKM(tr, criteria);
@ -108,6 +99,8 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.SetMoves(Moves);
pk.SetMaximumPPCurrent(Moves);
// Generate PIDIV
var seed = SetPINGA(pk, criteria, pi);
bool hatchedEgg = IsEgg && tr.Generation != 3;
if (hatchedEgg)
{
@ -115,12 +108,12 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
}
else
{
pk.OriginalTrainerGender = OriginalTrainerGender != 3 ? (byte)(OriginalTrainerGender & 1): tr.Gender;
pk.ID32 = TID16;
pk.SID16 = SID16;
pk.Language = (int)GetSafeLanguage((LanguageID)tr.Language);
pk.OriginalTrainerName = !string.IsNullOrWhiteSpace(OriginalTrainerName) ? OriginalTrainerName : tr.OT;
pk.OriginalTrainerGender = OriginalTrainerGender == GiftGender3.Recipient ? tr.Gender : (byte)GetGender(seed);
if (IsEgg)
{
pk.IsEgg = true; // lang should be set to japanese already
@ -131,13 +124,28 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, 3); // will be set to Egg nickname if appropriate by PK3 setter
pk.OriginalTrainerFriendship = pk.IsEgg ? pi.HatchCycles : pi.BaseFriendship;
// Generate PIDIV
SetPINGA(pk, criteria);
pk.RefreshChecksum();
return pk;
}
private uint GetGender(uint seed) => OriginalTrainerGender switch
{
GiftGender3.Only0 or GiftGender3.RandD3_0 => 0,
GiftGender3.Only1 or GiftGender3.RandD3_1 => 1,
GiftGender3.RandD3 => GetGenderBit0(LCRNG.Next(seed) >> 16),
GiftGender3.RandS3 => GetGenderBit3(LCRNG.Next(seed) >> 16),
GiftGender3.RandS7 => GetGenderBit7(LCRNG.Next(seed) >> 16),
GiftGender3.RandSG15 => GetGenderBit15(LCRNG.Next2(seed) >> 16),
_ => 0,
};
private bool IsGenderSpecificMatch(byte value) => OriginalTrainerGender switch
{
GiftGender3.Only0 or GiftGender3.RandD3_0 => value == 0,
GiftGender3.Only1 or GiftGender3.RandD3_1 => value == 1,
_ => true, // Algorithmic check later.
};
private void SetForceHatchDetails(PK3 pk, ITrainerInfo sav)
{
pk.Language = (int)GetSafeLanguageNotEgg((LanguageID)sav.Language);
@ -168,20 +176,47 @@ private GameVersion GetVersion(ITrainerInfo tr)
return GetRandomVersion(Version);
}
private void SetPINGA(PK3 pk, EncounterCriteria _)
private uint SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
{
var seed = Util.Rand32();
seed = TID16 == 06930 ? MystryMew.GetSeed(seed, Method) : GetSaneSeed(seed);
PIDGenerator.SetValuesFromSeed(pk, Method, seed);
pk.RefreshAbility((int)(pk.EncryptionConstant & 1));
var gr = pi.Gender;
uint idXor = pk.TID16 ^ (uint)pk.SID16;
while (true)
{
uint seed = Util.Rand32();
seed = GetSaneSeed(seed);
var pid = Shiny switch
{
Shiny.Never when Method is BACD_U_AX => GetAntishiny(ref seed, idXor),
Shiny.Never => GetRegularAntishiny(ref seed, idXor),
Shiny.Always => GetForceShiny(ref seed, idXor),
_ => GetRegular(ref seed),
};
if (criteria.IsSpecifiedNature() && criteria.Nature != (Nature)(pid % 25))
continue; // try again
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
if (!criteria.IsGenderSatisfied(gender))
continue;
pk.PID = pid;
PIDGenerator.SetIVsFromSeedSequentialLCRNG(ref seed, pk);
pk.RefreshAbility((int)(pk.PID & 1));
return seed;
}
}
private uint GetSaneSeed(uint seed) => Method switch
{
PIDType.BACD_R => seed & 0x0000FFFF, // u16
PIDType.BACD_R_S => seed & 0x000000FF, // u8
PIDType.Channel => ChannelJirachi.SkipToPIDIV(seed),
_ => seed,
BACD_RBCD => Math.Clamp(seed, 3, 213), // BCD digit sum
Channel => ChannelJirachi.SkipToPIDIV(seed),
BACD_T2 when Species is not (ushort)Core.Species.Jirachi
=> PCJPFifthAnniversary.GetSeedForResult(Species, Shiny == Shiny.Always, Moves.Contains((ushort)Move.Wish), seed),
BACD_T3
=> PCJPFifthAnniversary.GetSeedForResult(Species, Shiny == Shiny.Always, Moves.Contains((ushort)Move.Wish), seed),
BACD_M => MystryMew.GetSeed(seed),
_ when OriginalTrainerGender is GiftGender3.RandD3_0 => GetRandomRestrictedGenderBit0(seed, 0),
_ when OriginalTrainerGender is GiftGender3.RandD3_1 => GetRandomRestrictedGenderBit0(seed, 1),
_ => Method.IsRestricted() ? seed & 0x0000FFFF : seed,
};
private LanguageID GetSafeLanguage(LanguageID hatchLang)
@ -227,7 +262,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
{
if (SID16 != UnspecifiedID && SID16 != pk.SID16) return false;
if (TID16 != UnspecifiedID && TID16 != pk.TID16) return false;
if (OriginalTrainerGender < 3 && OriginalTrainerGender != pk.OriginalTrainerGender) return false;
if (!IsGenderSpecificMatch(pk.OriginalTrainerGender)) return false;
var wcOT = OriginalTrainerName;
if (!string.IsNullOrEmpty(wcOT) && wcOT != pk.OriginalTrainerName)
return false;
@ -274,4 +309,71 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
public EncounterMatchRating GetMatchRating(PKM pk) => EncounterMatchRating.Match;
public bool IsTrainerMatch(PKM pk, ReadOnlySpan<char> trainer, int language) => true; // checked in explicit match
public bool IsCompatible(PIDType type, PKM pk) => type == Method;
public bool IsCompatibleReviseReset(ref PIDIV value, PKM pk)
{
var prev = value.Mutated; // if previously revised, use that instead.
var type = prev is 0 ? value.Type : prev;
if (type is BACD_EA or BACD_ES && !IsEgg)
return false;
if (OriginalTrainerGender is not GiftGender3.Recipient && (!IsEgg || pk.IsEgg) && !IsMatchGender(pk, value.OriginSeed))
return false;
return Method switch
{
BACD_U => true,
BACD_R => IsRestrictedSimple(ref value, type),
BACD_R_A => IsRestrictedAnti(ref value, type),
BACD_T2 => IsRestrictedTable2(ref value, type, Species, Moves.Contains((ushort)Move.Wish)),
BACD_T3 => IsRestrictedTable3(ref value, type, Species, Moves.Contains((ushort)Move.Wish)),
BACD_RBCD => IsBerryFixShiny(ref value, type),
BACD_M => IsMystryMew(ref value, type),
Channel => IsChannelJirachi(ref value, type),
_ => false,
};
}
private bool IsMatchGender(PKM pk, uint seed)
{
var expect = OriginalTrainerGender is GiftGender3.RandD3_0 or GiftGender3.RandD3_1
? GetGenderBit0(LCRNG.Next6(seed) >> 16)
: GetGender(LCRNG.Next4(seed)); // another implicit advance inside the method
return pk.OriginalTrainerGender == expect;
}
}
/// <summary>
/// Trainer Gender for Gift Events in Gen3
/// </summary>
/// <remarks>If random, determined after the PID/IV (and potentially item).</remarks>
public enum GiftGender3 : byte
{
/// <summary> Match the recipient. Either 0 or 1. </summary>
Recipient,
/// <summary> Must be 0. </summary>
Only0,
/// <summary> Must be 1. </summary>
Only1,
/// <summary> Determined by a separate algorithm and shouldn't be checked via regular logic. </summary>
RandAlgo,
/// <summary> Divide by 3. </summary>
RandD3,
/// <summary> Shift by 3. </summary>
RandS3,
/// <summary> Shift by 7. </summary>
RandS7,
/// <summary> Shift by 15, after Item. </summary>
RandSG15,
/// <summary> <see cref="RandD3"/>, but MUST be 0. Used for dual-OT events. </summary>
RandD3_0,
/// <summary> <see cref="RandD3"/>, but MUST be 1. Used for dual-OT events. </summary>
RandD3_1,
}

View File

@ -7,7 +7,7 @@ namespace PKHeX.Core;
/// </summary>
/// <remarks>Specialized for the PCJP gift distribution machines.</remarks>
public sealed class EncounterGift3JPN(ushort Species, Distribution3JPN Distribution)
: IEncounterable, IEncounterMatch, IRandomCorrelation, IFixedTrainer
: IEncounterable, IEncounterMatch, IRandomCorrelationEvent3, IFixedTrainer
{
public ushort Species { get; } = Species;
public Distribution3JPN Distribution { get; } = Distribution;
@ -15,6 +15,7 @@ public sealed class EncounterGift3JPN(ushort Species, Distribution3JPN Distribut
private const PIDType Method = PIDType.BACD_U_AX;
public PIDType GetSuggestedCorrelation() => Method;
public byte Form => 0;
public GameVersion Version => GameVersion.R;
public byte Generation => 3;
@ -29,10 +30,9 @@ public sealed class EncounterGift3JPN(ushort Species, Distribution3JPN Distribut
public EntityContext Context => EntityContext.Gen3;
public bool IsEgg => false;
public bool IsFixedTrainer => true;
public string Name => "PCNY Gift";
public string Name => "PCJP Gift";
public string LongName => Name;
public bool IsCompatible(PIDType val, PKM pk) => val is Method;
public EncounterMatchRating GetMatchRating(PKM pk) => EncounterMatchRating.Match; // checked in explicit match
public bool IsTrainerMatch(PKM pk, ReadOnlySpan<char> trainer, int language) => true; // checked in explicit match
@ -63,17 +63,33 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
};
// Generate PIDIV
SetPINGA(pk, criteria);
SetPINGA(pk, criteria, pi);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.RefreshChecksum();
return pk;
}
private static void SetPINGA(PK3 pk, EncounterCriteria _)
private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
{
var seed = Util.Rand32();
PIDGenerator.SetValuesFromSeed(pk, Method, seed);
pk.RefreshAbility((int)(pk.EncryptionConstant & 1));
uint seed = Util.Rand32();
var gr = pi.Gender;
var idXor = pk.TID16; // no SID
while (true)
{
var pid = CommonEvent3.GetAntishiny(ref seed, idXor);
if (criteria.IsSpecifiedNature() && criteria.Nature != (Nature)(pid % 25))
continue; // try again
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
if (!criteria.IsGenderSatisfied(gender))
continue;
PIDGenerator.SetIVsFromSeedSequentialLCRNG(ref seed, pk);
pk.PID = pid;
pk.RefreshAbility((int)(pid & 1));
pk.OriginalTrainerGender = (byte)GetGender(LCRNG.Next16(ref seed));
return;
}
}
#endregion
@ -85,8 +101,10 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
if (Version != 0 && !Version.Contains(pk.Version))
return false;
if (pk.SID16 != 0) return false;
if (!IsValidTrainerID(pk.TID16)) return false;
if (pk.SID16 != 0)
return false;
if (!IsValidTrainerID(pk.TID16))
return false;
Span<char> trainerName = stackalloc char[pk.TrashCharCountTrainer];
int len = pk.LoadString(pk.OriginalTrainerTrash, trainerName);
@ -99,7 +117,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
if (Form != evo.Form && !FormInfo.IsFormChangeable(Species, Form, pk.Form, Context, pk.Context))
return false;
if (pk.Language == (int)LanguageID.Japanese)
if (pk.Language != (int)LanguageID.Japanese)
return false;
if ((byte)FixedBall != pk.Ball)
@ -123,4 +141,24 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
}
return true;
}
public bool IsCompatible(PIDType type, PKM pk) => type is Method;
public bool IsCompatibleReviseReset(ref PIDIV value, PKM pk)
{
var prev = value.Mutated; // if previously revised, use that instead.
var type = prev is 0 ? value.Type : prev;
if (type is not PIDType.BACD_AX)
return false;
var seed = value.OriginSeed;
var rand5 = LCRNG.Next5(seed) >> 16;
var expect = GetGender(rand5);
if (pk.OriginalTrainerGender != expect)
return false;
return true; // Table weight -> gift selection is a separate RNG, nothing to check!
}
private static uint GetGender(uint rand16) => CommonEvent3.GetGenderBit7(rand16);
}

View File

@ -8,7 +8,7 @@ namespace PKHeX.Core;
/// </summary>
/// <remarks>Specialized for the PCNY gift distribution machines.</remarks>
public sealed class EncounterGift3NY(ushort Species, Distribution3NY Distribution, byte Level, Moveset Moves)
: IEncounterable, IEncounterMatch, IRandomCorrelation, IFixedTrainer, IMoveset
: IEncounterable, IEncounterMatch, IRandomCorrelationEvent3, IFixedTrainer, IMoveset
{
public ushort Species { get; } = Species;
public Distribution3NY Distribution { get; } = Distribution;
@ -34,7 +34,6 @@ public sealed class EncounterGift3NY(ushort Species, Distribution3NY Distributio
public string Name => "PCNY Gift";
public string LongName => Name;
public bool IsCompatible(PIDType val, PKM pk) => val is Method;
public EncounterMatchRating GetMatchRating(PKM pk) => EncounterMatchRating.Match; // checked in explicit match
public bool IsTrainerMatch(PKM pk, ReadOnlySpan<char> trainer, int language) => true; // checked in explicit match
@ -65,17 +64,33 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
};
// Generate PIDIV
SetPINGA(pk, criteria);
SetPINGA(pk, criteria, pi);
pk.SetMoves(Moves);
pk.RefreshChecksum();
return pk;
}
private static void SetPINGA(PK3 pk, EncounterCriteria _)
private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
{
var seed = Util.Rand32();
PIDGenerator.SetValuesFromSeed(pk, Method, seed);
pk.RefreshAbility((int)(pk.EncryptionConstant & 1));
uint seed = Util.Rand32();
var gr = pi.Gender;
var idXor = pk.TID16; // no SID
while (true)
{
var pid = CommonEvent3.GetAntishiny(ref seed, idXor);
if (criteria.IsSpecifiedNature() && criteria.Nature != (Nature)(pid % 25))
continue; // try again
var gender = EntityGender.GetFromPIDAndRatio(pid, gr);
if (!criteria.IsGenderSatisfied(gender))
continue;
PIDGenerator.SetIVsFromSeedSequentialLCRNG(ref seed, pk);
pk.PID = pid;
pk.RefreshAbility((int)(pid & 1));
pk.OriginalTrainerGender = (byte)GetGender(LCRNG.Next16(ref seed));
return;
}
}
#endregion
@ -85,8 +100,10 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
if (Version != 0 && !Version.Contains(pk.Version))
return false;
if (pk.SID16 != 0) return false;
if (!Distribution.IsValidTrainerID(pk.TID16)) return false;
if (pk.SID16 != 0)
return false;
if (!Distribution.IsValidTrainerID(pk.TID16))
return false;
Span<char> trainerName = stackalloc char[pk.TrashCharCountTrainer];
int len = pk.LoadString(pk.OriginalTrainerTrash, trainerName);
@ -139,4 +156,24 @@ internal static EncounterGift3NY[] GetArray(ReadOnlySpan<byte> readOnlySpan)
}
return result;
}
public bool IsCompatible(PIDType type, PKM pk) => type is Method;
public bool IsCompatibleReviseReset(ref PIDIV value, PKM pk)
{
var prev = value.Mutated; // if previously revised, use that instead.
var type = prev is 0 ? value.Type : prev;
if (type is not PIDType.BACD_AX)
return false;
var seed = value.OriginSeed;
var rand5 = LCRNG.Next5(seed) >> 16;
var expect = GetGender(rand5);
if (pk.OriginalTrainerGender != expect)
return false;
return true; // Table weight -> gift selection is a separate RNG, nothing to check!
}
private static uint GetGender(uint rand16) => CommonEvent3.GetGenderBit7(rand16);
}

View File

@ -166,6 +166,6 @@ private bool IsMatchLocation(PKM pk)
#endregion
public bool IsCompatible(PIDType val, PKM pk) => val is PIDType.CXD or PIDType.CXDAnti;
public bool IsCompatible(PIDType type, PKM pk) => type is PIDType.CXD or PIDType.CXDAnti;
public PIDType GetSuggestedCorrelation() => PIDType.CXD;
}

View File

@ -72,6 +72,6 @@ private void SetPINGA(XK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
public EncounterMatchRating GetMatchRating(PKM pk) => EncounterMatchRating.Match;
#endregion
public bool IsCompatible(PIDType val, PKM pk) => val == PIDType.PokeSpot;
public bool IsCompatible(PIDType type, PKM pk) => type == PIDType.PokeSpot;
public PIDType GetSuggestedCorrelation() => PIDType.PokeSpot;
}

View File

@ -143,11 +143,11 @@ private bool IsMatchPartial(PKM pk)
}
#endregion
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
if (val is PIDType.CXD)
if (type is PIDType.CXD)
return true;
return val is PIDType.CXDAnti && FatefulEncounter;
return type is PIDType.CXDAnti && FatefulEncounter;
}
public PIDType GetSuggestedCorrelation() => PIDType.CXD;

View File

@ -171,7 +171,7 @@ private bool IsMatchPartial(PKM pk)
}
#endregion
public bool IsCompatible(PIDType val, PKM pk) => val is PIDType.CXD;
public bool IsCompatible(PIDType type, PKM pk) => type is PIDType.CXD;
public PIDType GetSuggestedCorrelation() => PIDType.CXD;
public bool IsTrainerMatch(PKM pk, ReadOnlySpan<char> trainer, int language)
{

View File

@ -176,15 +176,15 @@ public EncounterMatchRating GetMatchRating(PKM pk)
private bool IsDeferredWurmple(PKM pk) => Species == (int)Core.Species.Wurmple && pk.Species != (int)Core.Species.Wurmple && !WurmpleUtil.IsWurmpleEvoValid(pk);
#endregion
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
if (val is PIDType.Method_1)
if (type is PIDType.Method_1)
return true;
// Chain shiny with Poké Radar is only possible in D/P/Pt, in grass.
// Safari Zone does not allow using the Poké Radar
if (val is PIDType.ChainShiny)
if (type is PIDType.ChainShiny)
return pk.IsShiny && CanUseRadar;
if (val is PIDType.CuteCharm)
if (type is PIDType.CuteCharm)
return MethodFinder.IsCuteCharm4Valid(this, pk);
return false;
}

View File

@ -275,15 +275,15 @@ public static bool IsMatchRoamerLocation([ConstantExpected] uint permit, ushort
#endregion
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
if (Species == (int)Core.Species.Pichu)
return val == PIDType.Pokewalker;
return type == PIDType.Pokewalker;
if (Shiny == Shiny.Always)
return val == PIDType.ChainShiny;
if (val is PIDType.Method_1)
return type == PIDType.ChainShiny;
if (type is PIDType.Method_1)
return true;
if (val is PIDType.CuteCharm)
if (type is PIDType.CuteCharm)
return MethodFinder.IsCuteCharm4Valid(this, pk);
return false;
}

View File

@ -183,13 +183,13 @@ public EncounterMatchRating GetMatchRating(PKM pk)
private bool IsMatchPartial(PKM pk) => pk.Ball != (byte)Ball.Poke || !IsMatchSeed(pk);
#endregion
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
if (val is PIDType.Pokewalker)
if (type is PIDType.Pokewalker)
return true;
// Pokewalker can sometimes be confused with CuteCharm due to the PID creation routine. Double check if it is okay.
if (val is PIDType.CuteCharm)
if (type is PIDType.CuteCharm)
return MethodFinder.IsCuteCharm(pk, pk.EncryptionConstant) && MethodFinder.IsCuteCharm4Valid(this, pk);
return false;
}

View File

@ -5,6 +5,30 @@ namespace PKHeX.Core;
/// </summary>
public interface IRandomCorrelation
{
bool IsCompatible(PIDType val, PKM pk);
/// <summary>
/// Checks if the given <see cref="PIDType"/> is compatible with the encounter restrictions.
/// </summary>
/// <param name="type">Observed <see cref="PIDType"/> of the <see cref="pk"/></param>
/// <param name="pk">Entity to compare against for other details</param>
/// <returns>True if all details are compatible</returns>
bool IsCompatible(PIDType type, PKM pk);
/// <summary>
/// Gets the suggested <see cref="PIDType"/> for the encounter.
/// </summary>
PIDType GetSuggestedCorrelation();
}
/// <summary>
/// Specialized interface for mutating a <see cref="PIDIV"/> value to a derived type.
/// </summary>
public interface IRandomCorrelationEvent3 : IRandomCorrelation
{
/// <summary>
/// Mutates the <see cref="PIDIV"/> value to a derived type, if possible.
/// </summary>
/// <param name="value">Value to check and mutate</param>
/// <param name="pk">Entity to compare against</param>
/// <returns>True if Compatible. Revision of the ref value is not returned.</returns>
bool IsCompatibleReviseReset(ref PIDIV value, PKM pk);
}

View File

@ -103,7 +103,19 @@ public static void AddEncounterInfoPIDIV(LegalityAnalysis la, List<string> lines
private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
{
var pidiv = info.PIDIV;
lines.Add(string.Format(L_FPIDType_0, pidiv.Type));
var type = string.Format(L_FPIDType_0, pidiv.Type);
if (info is { EncounterMatch: IRandomCorrelationEvent3 enc})
{
var mainType = enc.GetSuggestedCorrelation();
if (mainType != pidiv.Type)
type += $" [{mainType}]";
if (enc is EncounterGift3 { Method: PIDType.BACD_M } && info.PIDIVMatches) // mystry
{
var detail = MystryMew.GetSeedIndexes(pidiv.OriginSeed);
type += $" ({detail.Index}-{detail.SubIndex})";
}
}
lines.Add(type);
if (pidiv.NoSeed)
return;
@ -139,6 +151,8 @@ private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
{
var seed = pidiv.OriginSeed;
var line = string.Format(L_FOriginSeed_0, seed.ToString("X8"));
if (info is { EncounterMatch: IRandomCorrelationEvent3 } && pidiv.Mutated is not 0 && pidiv.OriginSeed != pidiv.EncounterSeed)
line += $" [{pidiv.EncounterSeed:X8}]";
lines.Add(line);
}
}

View File

@ -70,7 +70,7 @@ public static class LCRNG
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next (uint seed) => (seed * Mult ) + Add ;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next2(uint seed) => (seed * Mult2) + Add2;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next3(uint seed) => (seed * Mult3) + Add3;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next4(uint seed) => (seed * Mult4) + Add5;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next4(uint seed) => (seed * Mult4) + Add4;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next5(uint seed) => (seed * Mult5) + Add5;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next6(uint seed) => (seed * Mult6) + Add6;
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Next7(uint seed) => (seed * Mult7) + Add7;
@ -88,7 +88,7 @@ public static class LCRNG
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev9(uint seed) => (seed * rMult9) + rAdd9;
/// <summary>
/// Gets the next 16 bits of the next RNG seed.
/// Gets the upper 16 bits of the next RNG seed.
/// </summary>
/// <param name="seed">Seed to advance one step.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -99,9 +99,9 @@ public static uint Next16(ref uint seed)
}
/// <summary>
/// Gets the previous 16 bits of the previous RNG seed.
/// Gets the upper 16 bits of the previous RNG seed.
/// </summary>
/// <param name="seed">Seed to advance one step.</param>
/// <param name="seed">Seed to reverse one step.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Prev16(ref uint seed)
{

View File

@ -95,7 +95,7 @@ public static class XDRNG
[MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev12(uint seed) => (seed * rMult12) + rAdd12;
/// <summary>
/// Gets the next 16 bits of the next RNG seed.
/// Gets the upper 16 bits of the next RNG seed.
/// </summary>
/// <param name="seed">Seed to advance one step.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -106,7 +106,7 @@ public static uint Next16(ref uint seed)
}
/// <summary>
/// Gets the next 16 bits of the next RNG seed.
/// Gets the upper 0x7FFF bits of the next RNG seed.
/// </summary>
/// <param name="seed">Seed to advance one step.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -117,9 +117,9 @@ public static uint Next15(ref uint seed)
}
/// <summary>
/// Gets the previous 16 bits of the previous RNG seed.
/// Gets the upper 16 bits of the previous RNG seed.
/// </summary>
/// <param name="seed">Seed to advance one step.</param>
/// <param name="seed">Seed to reverse one step.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Prev16(ref uint seed)
{

View File

@ -0,0 +1,240 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for calculating and detecting Generation 3 Event PIDs.
/// </summary>
/// <remarks>
/// Commonly referred to as B-A-C-D, the actual call order of the PID may be different.
/// We still call them B-A-C-D just to keep them lumped together.
/// </remarks>
public static class CommonEvent3
{
/// <summary>
/// Standard calculation for the BA-CD PID generation.
/// </summary>
/// <param name="a16">First rand16 call, placed in the upper bits of the PID.</param>
/// <param name="b16">Second rand16 call, placed in the lower bits of the PID.</param>
/// <returns>32bit PID</returns>
public static uint GetRegular(uint a16, uint b16) => (a16 << 16) | b16;
/// <summary>
/// Alternate PID generation method for events, notably used in the PC-NY Gotta Catch 'Em All events.
/// </summary>
/// <param name="a16">First rand16 call, placed in the upper bits of the PID.</param>
/// <param name="b16">Second rand16 call, placed in the lower bits of the PID.</param>
/// <param name="idXor">Trainer ID xor value.</param>
/// <remarks><see cref="a16"/> must be greater than 7.</remarks>
/// <returns>32bit PID</returns>
public static uint GetAntishiny(uint a16, uint b16, uint idXor) => ((a16 ^ (idXor ^ b16)) << 16) | b16;
/// <summary>
/// Alternate PID generation method for events, notably used by the Berry Glitch Fix (Zigzagoon).
/// </summary>
/// <param name="x16">First rand16 call, placed in the upper bits of the PID.</param>
/// <param name="b16">Second rand16 call, placed in the lower bits of the PID.</param>
/// <param name="idXor">Trainer ID xor value.</param>
/// <remarks><see cref="x16"/> instead of a16, this is 1 frame earlier. The a16 call is unused.</remarks>
/// <returns>32bit PID</returns>
public static uint GetForceShiny(uint x16, uint b16, uint idXor) => (x16 << 16) | (((idXor ^ x16) & 0xFFF8) | (b16 & 0b111));
/// <summary>
/// Regular PID generation method for events, that changes the PID to be non-shiny if it were shiny.
/// </summary>
/// <param name="pid">Calculated 32-bit PID from the regular generation method.</param>
/// <returns>Revised 32-bit PID</returns>
private static uint GetRegularAntishiny(uint pid) => (pid + 8) & 0xFFFFFFF8;
/// <inheritdoc cref="GetRegularAntishiny(uint)"/>
/// <param name="a16">First rand16 call, placed in the upper bits of the PID.</param>
/// <param name="b16">Second rand16 call, placed in the lower bits of the PID.</param>
/// <param name="idXor">Trainer ID xor value.</param>
public static uint GetRegularAntishiny(uint a16, uint b16, uint idXor)
{
var pid = GetRegular(a16, b16);
if (ShinyUtil.GetIsShiny(idXor, pid, 8))
pid = GetRegularAntishiny(pid);
return pid;
}
/// <inheritdoc cref="GetRegular(uint, uint)"/>
/// <param name="seed">Seed to roll forward to generate the PID.</param>
public static uint GetRegular(ref uint seed)
{
var a16 = LCRNG.Next16(ref seed);
var b16 = LCRNG.Next16(ref seed);
return GetRegular(a16, b16);
}
/// <inheritdoc cref="GetAntishiny(uint, uint, uint)"/>
/// <param name="seed">Seed to roll forward to generate the PID.</param>
/// <param name="idXor">Trainer ID xor value.</param>
public static uint GetAntishiny(ref uint seed, uint idXor)
{
uint a16;
do a16 = LCRNG.Next16(ref seed); // If it were, it would result in a shiny. We need any bits that won't yield a shiny.
while ((a16 & ~0b111) == 0);
var b16 = LCRNG.Next16(ref seed);
return GetAntishiny(a16, b16, idXor);
}
/// <inheritdoc cref="GetForceShiny(uint, uint, uint)"/>
/// <param name="seed">Seed to roll forward to generate the PID.</param>
/// <param name="idXor">Trainer ID xor value.</param>
public static uint GetForceShiny(ref uint seed, uint idXor)
{
var x16 = LCRNG.Next16(ref seed);
_ = LCRNG.Next16(ref seed); // A (ends up unused)
var b16 = LCRNG.Next16(ref seed);
return GetForceShiny(x16, b16, idXor);
}
/// <inheritdoc cref="GetRegularAntishiny(uint, uint, uint)"/>
/// <param name="seed">Seed to roll forward to generate the PID.</param>
/// <param name="idXor">Trainer ID xor value.</param>
public static uint GetRegularAntishiny(ref uint seed, uint idXor)
{
var a16 = LCRNG.Next16(ref seed);
var b16 = LCRNG.Next16(ref seed);
return GetRegularAntishiny(a16, b16, idXor);
}
/// <summary>
/// Checks if the PID is a regular PID generated from the given a16 and b16 values.
/// </summary>
/// <inheritdoc cref="GetRegular(uint,uint)"/>
public static bool IsRegular(uint pid, uint a16, uint b16)
{
uint result = GetRegular(a16, b16);
return result == pid;
}
/// <summary>
/// Checks if the PID is an anti-shiny PID generated from the given a16 and b16 values.
/// </summary>
/// <inheritdoc cref="IsForceAntishiny(uint,uint,uint,uint)"/>
public static bool IsForceAntishiny(uint pid, uint a16, uint b16, uint idXor)
{
// First RNG call should be sought to be above 7 to ensure a shiny is not generated.
if ((a16 & ~0b111) == 0)
return false; // If it were, it would result in a shiny. We need any bits that won't yield a shiny.
// 0-Origin
// A-PIDH component (>= 8)
// B-PIDL
uint result = GetAntishiny(a16, b16, idXor);
return result == pid;
}
/// <summary>
/// Checks if the PID is an anti-shiny PID generated from the given a16 and b16 values.
/// </summary>
/// <inheritdoc cref="GetForceShiny(uint,uint,uint)"/>
public static bool IsForceShiny(uint pid, uint x16, uint b16, uint idXor)
{
// The mutation algorithm doesn't end up using the 'B' portion of the PID.
if (x16 != pid >> 16)
return false; // eager check to avoid the more expensive calculation.
// 0-Origin
// X-PIDH
// A-PIDL (ends up unused)
// B-FORCEBITS
uint result = GetForceShiny(x16, b16, idXor);
return result == pid;
}
/// <summary>
/// Checks if the PID is a regular anti-shiny PID generated from the given a16 and b16 values.
/// </summary>
/// <inheritdoc cref="GetRegularAntishiny(uint,uint,uint)"/>
public static bool IsRegularAntishiny(uint pid, uint a16, uint b16, uint idXor)
{
uint result = GetRegularAntishiny(a16, b16, idXor);
return result == pid;
}
/// <inheritdoc cref="IsRegularAntishiny(uint,uint,uint,uint)"/>
/// <param name="expectPID">Regular BA PID that is expected from the correlation, no corrections.</param>
/// <param name="actualPID">The actual PID of the Pokémon.</param>
public static bool IsRegularAntishiny(uint actualPID, uint expectPID)
{
var result = GetRegularAntishiny(expectPID);
return result == actualPID;
}
/// <summary>Checks for eggs hatched with the regular Anti-shiny correlation, but originating from an unknown ID xor. </summary>
/// <inheritdoc cref="IsRegularAntishiny(uint,uint,uint,uint)"/>
/// <remarks>Only call this method when the Pokémon is SHINY.</remarks>
/// <param name="expectPID">Regular BA PID that is expected from the correlation, no corrections.</param>
/// <param name="actualPID">The actual PID of the Pokémon.</param>
public static bool IsRegularAntishinyDifferentOT(uint actualPID, uint expectPID)
{
// The mutation algorithm basically adds until it is non-shiny.
if ((actualPID & 0b111) != 0)
return false; // eager check to avoid the more expensive calculation.
var delta = actualPID - expectPID;
return delta is (not 0) and < 8;
}
/// <summary>Checks for eggs hatched with the Force Anti-shiny correlation, but originating from an unknown ID xor. </summary>
/// <inheritdoc cref="IsForceAntishiny"/>
/// <remarks>Only call this method when the Pokémon is SHINY.</remarks>
public static bool IsForceAntishinyDifferentOT(uint pid, uint a16, uint b16, uint idXor)
{
// First RNG call should be sought to be above 7 to ensure a shiny is not generated.
if ((a16 & ~0b111) == 0)
return false; // If it were, it would result in a shiny. We need any bits that won't yield a shiny.
if ((pid & 0xFFFF) != b16)
return false;
// The ID xor is used in the top half. Since we need a different OT, these must be different.
// Be sure to ignore the 3 bits of TSV (bit-shift 19 bits instead of 16).
uint result = GetAntishiny(a16, b16, idXor);
return result >> 19 != pid >> 19;
}
/// <summary>Checks for eggs hatched with the Force Shiny correlation, but originating from an unknown ID xor. </summary>
/// <inheritdoc cref="IsForceShiny"/>
/// <remarks>Only call this method when the Pokémon is NOT SHINY.</remarks>
public static bool IsForceShinyDifferentOT(uint pid, uint x16, uint b16, uint idXor)
{
if (x16 != pid >> 16)
return false; // eager check to avoid the more expensive calculation.
// Due to the nature of the Force Shiny algorithm, bits 0-2 are from b16, and x16 is the high bits.
if ((pid & 0b111) != (b16 & 0b111))
return false;
// Bits 3-15 are from the ID xor, which is x16 ^ idXor. But we don't know the original.
// However, it must not be the same as the hatching OT xor. Because this method is only called when not shiny.
var low = (idXor ^ x16) & 0xFFF8;
var actual = (pid & 0xFFF8);
return low != actual;
}
public static uint GetGenderBit0(uint rand16) => (rand16 / 3) & 1;
public static uint GetGenderBit3(uint rand16) => (rand16 >> 3) & 1;
public static uint GetGenderBit7(uint rand16) => (rand16 >> 7) & 1;
public static uint GetGenderBit15(uint rand16) => (rand16 >> 15) & 1;
public static uint GetGenderBit0(ref uint seed) => GetGenderBit0(LCRNG.Next16(ref seed));
public static uint GetGenderBit3(ref uint seed) => GetGenderBit3(LCRNG.Next16(ref seed));
public static uint GetGenderBit7(ref uint seed) => GetGenderBit7(LCRNG.Next16(ref seed));
public static uint GetGenderBit15(ref uint seed) => GetGenderBit15(LCRNG.Next2(seed) >> 15);
/// <summary>
/// Gets a random restricted (16-bit) seed that respects the gender correlation.
/// </summary>
/// <param name="seed">Seed that generates the PID/IV then gender.</param>
/// <param name="gender5">Requested gender</param>
public static uint GetRandomRestrictedGenderBit0(uint seed, byte gender5)
{
while (true)
{
var u16 = seed & 0xFFFF;
var rand5 = LCRNG.Next5(seed) >> 16;
if (GetGenderBit0(rand5) == gender5)
return u16;
seed = LCRNG.Next(seed);
}
}
}

View File

@ -0,0 +1,153 @@
using static PKHeX.Core.PIDType;
namespace PKHeX.Core;
/// <summary>
/// Logic to revise a <see cref="PIDIV"/> based on observed <see cref="PIDType"/> and constraining <see cref="PIDType"/>
/// </summary>
public static class CommonEvent3Checker
{
/// <summary>
/// Checks if the observed type and seed value match the constraints of the encounter.
/// </summary>
/// <remarks>Regular Antishiny Table constraint.</remarks>
/// <param name="value">PID/IV observed</param>
/// <param name="observed">Observed PID Type</param>
/// <param name="species">Encounter Species</param>
/// <param name="isWish">Encounter moveset contains the move Wish</param>
/// <returns>True if is match; mutation not indicated.</returns>
public static bool IsRestrictedTable2(ref PIDIV value, PIDType observed, ushort species, bool isWish)
{
if (observed is not (BACD or BACD_A or BACD_EA))
return false;
var seed = value.OriginSeed;
var prev2 = LCRNG.Prev2(seed);
if (prev2 > ushort.MaxValue)
return false;
if (species is (ushort)Species.Jirachi)
return true;
var index = PCJPFifthAnniversary.GetIndexPCJP(species);
var combined = WeightedTable3.GetRandom32(prev2);
var result = PCJPFifthAnniversary.GetResultPCJP(combined);
if (result.Shiny)
return false;
if (result.Index != index)
return false;
if (result.Wish != isWish)
return false;
value = value.AsMutated(BACD_R, prev2);
return true;
}
/// <remarks>Force Shiny Table constraint.</remarks>
/// <inheritdoc cref="IsRestrictedTable2"/>
public static bool IsRestrictedTable3(ref PIDIV value, PIDType observed, ushort species, bool isWish)
{
if (observed is not (BACD_S or BACD_ES))
return false;
var seed = value.OriginSeed;
var prev3 = LCRNG.Prev2(seed);
if (prev3 > ushort.MaxValue)
return false;
if (species is (ushort)Species.Jirachi)
return true;
var index = PCJPFifthAnniversary.GetIndexPCJP(species);
seed = prev3;
var rand1 = LCRNG.Next16(ref seed);
var rand2 = LCRNG.Next16(ref seed);
var combined = (rand1 << 16) | rand2;
var result = PCJPFifthAnniversary.GetResultPCJP(combined);
if (result.Shiny)
return false;
if (result.Index != index)
return false;
if (result.Wish != isWish)
return false;
value = value.AsMutated(BACD_R, prev3);
return true;
}
/// <remarks>Mystry Mew constraint.</remarks>
/// <inheritdoc cref="IsRestrictedTable2"/>
public static bool IsMystryMew(ref PIDIV value, PIDType observed)
{
if (observed is not BACD)
return false;
var seed = value.OriginSeed;
// Reverse back to origin seed.
var info = MystryMew.GetSeedIndexes(seed);
if (info.Index == -1)
return false;
seed = MystryMew.IsRestrictedIndex0th(info.SubIndex) ? seed : MystryMew.GetSeed(info.Index);
if (!MystryMew.IsValidSeed(seed, info.SubIndex))
return false;
value = value.AsMutated(BACD_R, seed);
return true;
}
/// <remarks>CHANNEL Jirachi constraint.</remarks>
/// <inheritdoc cref="IsRestrictedTable2"/>
public static bool IsChannelJirachi(ref PIDIV value, PIDType observed)
{
if (observed is not Channel)
return false;
var seed = value.OriginSeed;
var chk = ChannelJirachi.GetPossible(seed);
if (chk.Pattern is ChannelJirachiRandomResult.None)
return false;
value = value.AsMutated(Channel, chk.Seed);
return true;
}
/// <remarks>Regular BACD (WISHMKR Jirachi, Eggs) constraint.</remarks>
/// <inheritdoc cref="IsRestrictedTable2"/>
public static bool IsRestrictedSimple(ref PIDIV value, PIDType observed)
{
if (observed is not BACD)
return false;
if (value.OriginSeed > ushort.MaxValue)
return false;
value = value.AsMutated(BACD_R);
return true;
}
/// <remarks>Regular Antishiny without seed restriction constraint.</remarks>
/// <inheritdoc cref="IsRestrictedTable2"/>
public static bool IsUnrestrictedAnti(ref PIDIV value, PIDType observed)
{
if (observed is not (BACD or BACD_A or BACD_EA))
return false;
if (value.OriginSeed > ushort.MaxValue)
return false;
value = value.AsMutated(BACD_U);
return true;
}
/// <remarks>Regular Antishiny with seed restriction constraint.</remarks>
/// <inheritdoc cref="IsRestrictedTable2"/>
public static bool IsRestrictedAnti(ref PIDIV value, PIDType observed)
{
if (observed is not (BACD or BACD_A or BACD_EA))
return false;
if (value.OriginSeed > ushort.MaxValue)
return false;
value = value.AsMutated(BACD_R);
return true;
}
/// <remarks>Berry Fix Zigzagoon seed restriction constraint.</remarks>
/// <inheritdoc cref="IsRestrictedTable2"/>
public static bool IsBerryFixShiny(ref PIDIV value, PIDType observed)
{
if (observed is not BACD_S)
return false;
if (value.OriginSeed is < 3 or > 213) // Binary Coded Decimal sum of timestamp must be possible.
return false;
value = value.AsMutated(BACD_R);
return true;
}
}

View File

@ -34,6 +34,22 @@ public static class MystryMew
];
private const int MewPerRestrictedSeed = 5;
private const int RestrictedIndex = 0;
/// <summary> 01_34 of this set were released by the event organizer. </summary>
private const ushort ReleasedSeed = 0x6065;
private const byte ReleasedSeedKeptIndex = 2; // 2nd sibling is the only valid one.
private const uint ReleasedSeedKeptSeed = 0xE8D1A0BF; // Next5(Next5(ReleasedSeed))
/// <summary>
/// Checks if the seed was unreleased.
/// </summary>
public static bool IsUnreleased(uint seed, int subIndex) => seed is ReleasedSeed && subIndex is not ReleasedSeedKeptIndex;
/// <summary>
/// Checks if the seed index (of the returned tuple) is referring to the oldest (first) sibling of the 5-per-seed.
/// </summary>
public static bool IsRestrictedIndex0th(int index) => index == RestrictedIndex;
/// <summary>
/// Gets a random valid seed based on the input <see cref="random"/> value.
@ -43,6 +59,8 @@ public static uint GetSeed(uint random, PIDType type = PIDType.BACD_U)
var seeds = Seeds;
uint restricted = random % (uint)seeds.Length;
var seed = (uint)seeds[(int)restricted];
if (seed is ReleasedSeed)
return ReleasedSeedKeptSeed;
if (type == PIDType.BACD_R)
return seed;
@ -53,13 +71,22 @@ public static uint GetSeed(uint random, PIDType type = PIDType.BACD_U)
return seed;
}
/// <summary>
/// Gets the seed at (sorted) <see cref="index"/>.
/// </summary>
public static uint GetSeed(int index) => Seeds[(uint)index >= Seeds.Length ? 0 : index];
/// <summary>
/// Checks if the seed is a known seed.
/// </summary>
/// <param name="seed">Origin seed (for the PID/IV)</param>
/// <returns>True if the seed is known.</returns>
public static bool IsValidSeed(uint seed) => GetSeedIndex(seed) != -1;
/// <param name="seed">Origin seed (for the PID/IV)</param>
/// <param name="subIndex">Sub-index (if not a restricted seed).</param>
/// <inheritdoc cref="IsValidSeed(uint)"/>
public static bool IsValidSeed(uint seed, int subIndex) => !IsUnreleased(seed, subIndex);
/// <summary>
/// Checks if the seed is a known seed.
/// </summary>
@ -80,7 +107,7 @@ public static int GetSeedIndex(uint seed)
}
/// <summary>
/// Get the index and subindex of the seed in the known seed list.
/// Get the index and sub-index of the seed in the known seed list.
/// </summary>
/// <param name="seed">Origin seed (for the PID/IV)</param>
/// <returns>Tuple of indexes; -1 if not found.</returns>
@ -90,9 +117,9 @@ public static (int Index, int SubIndex) GetSeedIndexes(uint seed)
if (seed <= ushort.MaxValue)
{
var index = seeds.BinarySearch((ushort)seed);
return (index, -1);
return (index, RestrictedIndex);
}
for (int i = 0; i < MewPerRestrictedSeed; i++)
for (int i = RestrictedIndex + 1; i < RestrictedIndex + MewPerRestrictedSeed; i++)
{
seed = LCRNG.Prev5(seed); // BACD{?}
if (seed <= ushort.MaxValue)

View File

@ -0,0 +1,76 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for determining the PCJP Fifth Anniversary event gift received.
/// </summary>
public static class PCJPFifthAnniversary
{
private const uint MaxTableWeight = 1000;
private const uint EntryWeight = 125;
// Table:
// Pichu 125 Teeter Dance (100-124 shiny)
// Pichu 125 Wish (100-124 shiny)
// Bagon 125 Iron Defense
// Bagon 125 Wish
// Absol 125 Spite
// Absol 125 Wish
// Ralts 125 Charm
// Ralts 125 Wish
// As we can see from the above table, there are 4 different species, with Wish being the second set of moves.
// From a rand(1000) result, we can determine the species and the moveset with the following branch-less operations:
// Species index: (result / 250)
// Moveset index: (result / 125) % 2
// Shiny: if Pichu, (result % 125) >= 100
public static (uint Index, bool Wish, bool Shiny) GetResultPCJP(uint rand)
{
var result = WeightedTable3.GetPeriodicWeight(rand, MaxTableWeight);
var eighth = result / EntryWeight;
var wish = (eighth & 1) == 1;
var index = eighth >> 1;
var shiny = index == 0 && (result % EntryWeight) >= 100;
return (index, wish, shiny);
}
/// <summary>
/// Gets the Species index within the PCJP table.
/// </summary>
/// <param name="species">Species ID</param>
/// <returns>0-3</returns>
public static uint GetIndexPCJP(ushort species)
{
// Pichu: 172 = 0_10_10_11_00
// Bagon: 371 = 1_01_11_00_11
// Absol: 359 = 1_01_10_01_11
// Ralts: 280 = 1_00_01_10_00
// To get the index of the species (0-3) from the above table, we can do some bitwise magic:
// Bits 2 & 3 are different across species, and conveniently in sequential order! We can just shift right by 2 add 1, then clamp to 0-3.
return (((uint)species >> 2) + 1) & 3u;
}
/// <summary>
/// Check if the given species, shiny, and wish moveset status match the given rand result.
/// </summary>
public static bool IsMatch(ushort species, bool shiny, bool wish, uint rand)
{
var index = GetIndexPCJP(species);
var result = GetResultPCJP(rand);
return index == result.Index && wish == result.Wish && shiny == result.Shiny;
}
/// <summary>
/// Gets a random 16-bit seed that will return the desired table result.
/// </summary>
public static uint GetSeedForResult(ushort species, bool shiny, bool wish, uint seed)
{
while (true)
{
var u16 = seed & 0xFFFF; // restricted
var u32 = WeightedTable3.GetRandom32(u16);
if (IsMatch(species, shiny, wish, u32))
return u16;
seed = LCRNG.Next(seed);
}
}
}

View File

@ -0,0 +1,37 @@
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <summary>
/// Generation 3 Event Table Weighting Algorithm.
/// </summary>
public static class WeightedTable3
{
/// <summary>
/// Gets the random weight value, with a periodic frequency of repeating (*about* every 0x8000).
/// </summary>
/// <param name="rand">32-bit random number</param>
/// <param name="max">Exclusive maximum value that can be returned</param>
/// <returns>[0, max)</returns>
public static uint GetPeriodicWeight(uint rand, [ConstantExpected] uint max)
{
var high = rand >> 16;
var first = ((high << 2) & 0xFFFF) + high;
var second = ((rand & 0xFFFF) << 1) + (first >> 16);
second += high + (second >> 16);
return (max * (second & 0xFFFF)) >> 16;
}
/// <inheritdoc cref="GetRandom32(ref uint)"/>
public static uint GetRandom32(uint seed) => GetRandom32(ref seed);
/// <summary>
/// Gets a 32-bit random number from the given seed to use as the table random selector.
/// </summary>
public static uint GetRandom32(ref uint seed)
{
var rand1 = LCRNG.Next16(ref seed);
var rand2 = LCRNG.Next16(ref seed);
return (rand1 << 16) | rand2;
}
}

View File

@ -86,32 +86,42 @@ private static bool GetModified8BitMatch(PKM pk, uint pid, out PIDIV pidiv)
public static bool GetLCRNGMethod1Match(PKM pk, out uint result)
{
var pid = pk.EncryptionConstant;
var top = pid & 0xFFFF0000;
var bot = pid << 16;
Span<uint> temp = stackalloc uint[6];
for (int i = 0; i < 6; i++)
temp[i] = (uint)pk.GetIV(i);
ReadOnlySpan<uint> IVs = temp;
return GetLCRNGMethod1Match(pk.EncryptionConstant, temp, out result);
}
const int maxResults = LCRNG.MaxCountSeedsIV;
Span<uint> seeds = stackalloc uint[maxResults];
var count = LCRNGReversal.GetSeeds(seeds, bot, top);
var reg = seeds[..count];
public static bool GetLCRNGMethod1Match(uint pid, ReadOnlySpan<uint> IVs, out uint result)
{
var iv1 = GetIVChunk(IVs[..3]);
var iv2 = GetIVChunk(IVs[3..]);
return GetLCRNGMethod1Match(pid, iv1, iv2, out result);
}
public static bool GetLCRNGMethod1Match(uint pid, uint iv1, uint iv2, out uint result)
{
var bot = pid << 16;
var top = pid & 0xFFFF0000;
return GetLCRNGMethod1Match(bot, top, iv1, iv2, out result);
}
public static bool GetLCRNGMethod1Match(uint a, uint b, uint c, uint d, out uint result)
{
const int maxResults = LCRNG.MaxCountSeedsIV;
Span<uint> seeds = stackalloc uint[maxResults];
var count = LCRNGReversal.GetSeeds(seeds, a, b);
var reg = seeds[..count];
foreach (var seed in reg)
{
var C = LCRNG.Next3(seed);
var ivC = C >> 16 & 0x7FFF;
if (iv1 != ivC)
if (c != ivC)
continue;
var D = LCRNG.Next(C);
var ivD = D >> 16 & 0x7FFF;
if (iv2 != ivD)
if (d != ivD)
continue;
// ABCD
result = seed;
@ -521,75 +531,71 @@ public static bool IsChainShinyValid(ITrainerID32 pk, uint pid, uint seed, out u
return true;
}
private static bool GetBACDMatch(Span<uint> seeds, PKM pk, uint pid, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
private static bool GetBACDMatch<T>(Span<uint> seeds, T pk, uint actualPID, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
where T : ITrainerID32
{
var bot = GetIVChunk(IVs[..3]) << 16;
var top = GetIVChunk(IVs[3..]) << 16;
var count = LCRNGReversal.GetSeedsIVs(seeds, bot, top);
var reg = seeds[..count];
PIDType type = BACD_U;
foreach (var seed in reg)
PIDType type = BACD;
uint idXor = (uint)(pk.TID16 ^ pk.SID16);
foreach (var x in reg)
{
var B = seed;
var A = LCRNG.Prev(B);
var low = B >> 16;
// Check for the expected BA(CD) pattern -- the expected PID.
var seed = x;
var b16 = seed >> 16;
var a16 = LCRNG.Prev16(ref seed);
var PID = (A & 0xFFFF0000) | low;
if (PID != pid)
var expectPID = CommonEvent3.GetRegular(a16, b16);
if (expectPID != actualPID)
{
uint idxor = (uint)(pk.TID16 ^ pk.SID16);
bool isShiny = (idxor ^ PID >> 16 ^ (PID & 0xFFFF)) < 8;
if (!isShiny)
// Check for the alternate event types that force shiny state.
bool isShiny = ShinyUtil.GetIsShiny(idXor, actualPID, 8);
if (!isShiny) // most likely, branch prediction!
{
if (!pk.IsShiny) // check for nyx antishiny
{
if (!IsBACD_U_AX(idxor, pid, low, A, ref type))
continue;
}
else // check for force shiny pk
{
if (!IsBACD_U_S(idxor, pid, low, ref A, ref type))
continue;
}
}
else if (!IsBACD_U_AX(idxor, pid, low, A, ref type))
{
if ((PID + 8 & 0xFFFFFFF8) != pid)
if (CommonEvent3.IsRegularAntishiny(actualPID, expectPID))
type = BACD_A;
else if (CommonEvent3.IsForceAntishiny(actualPID, a16, b16, idXor))
type = BACD_AX;
else if (CommonEvent3.IsForceShinyDifferentOT(actualPID, LCRNG.Prev16(ref seed), b16, idXor))
type = BACD_ES; // was shiny, hatched by different OT.
else
continue;
}
else // Shiny
{
if (CommonEvent3.IsRegularAntishinyDifferentOT(actualPID, expectPID))
type = BACD_EA; // was not-shiny, hatched by different OT.
else if (CommonEvent3.IsForceAntishinyDifferentOT(actualPID, a16, b16, idXor))
type = BACD_EAX; // was not-shiny, hatched by different OT.
else if (CommonEvent3.IsForceShiny(actualPID, LCRNG.Prev16(ref seed), b16, idXor))
type = BACD_S;
else
continue;
type = BACD_U_A;
}
}
var s = LCRNG.Prev(A);
// Check for prior Restricted seed
var sn = s;
for (int i = 0; i < 3; i++, sn = LCRNG.Prev(sn))
{
if ((sn & 0xFFFF0000) != 0)
continue;
// shift from unrestricted enum val to restricted enum val
pidiv = new PIDIV(--type, sn);
return true;
}
// no restricted seed found, thus unrestricted
pidiv = new PIDIV(type, s);
// Unroll one final time to get the origin seed.
var origin = LCRNG.Prev(seed);
pidiv = new PIDIV(type, origin);
return true;
}
return GetNonMatch(out pidiv);
}
private static bool GetPokewalkerMatch(PKM pk, uint pid, out PIDIV pidiv)
private static bool GetPokewalkerMatch(PKM pk, uint actualPID, out PIDIV pidiv)
{
// check surface compatibility
// Bits 8-24 must all be zero or all be one.
const uint midMask = 0x00FFFF00;
var mid = pid & midMask;
var mid = actualPID & midMask;
if (mid is not (0 or midMask))
return GetNonMatch(out pidiv);
// Quirky Nature is not possible with the algorithm.
var nature = pid % 25;
var nature = actualPID % 25;
if (nature == 24)
return GetNonMatch(out pidiv);
@ -599,10 +605,10 @@ private static bool GetPokewalkerMatch(PKM pk, uint pid, out PIDIV pidiv)
var gr = pk.PersonalInfo.Gender;
if (pk.Species == (int)Species.Froslass)
gr = 0x7F; // Snorunt
var expect = PokewalkerRNG.GetPID(pk.TID16, pk.SID16, nature, gender, gr);
if (expect != pid)
var expectPID = PokewalkerRNG.GetPID(pk.TID16, pk.SID16, nature, gender, gr);
if (expectPID != actualPID)
{
if (!(gender == 0 && IsAzurillEdgeCaseM(pk, nature, pid)))
if (!(gender == 0 && IsAzurillEdgeCaseM(pk, nature, actualPID)))
return GetNonMatch(out pidiv);
}
pidiv = PIDIV.Pokewalker;
@ -610,7 +616,7 @@ private static bool GetPokewalkerMatch(PKM pk, uint pid, out PIDIV pidiv)
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsAzurillEdgeCaseM(PKM pk, uint nature, uint oldpid)
private static bool IsAzurillEdgeCaseM(PKM pk, uint nature, uint actualPID)
{
// check for Azurill evolution edge case... 75% F-M is now 50% F-M; was this a Female->Male bend?
ushort species = pk.Species;
@ -618,12 +624,12 @@ private static bool IsAzurillEdgeCaseM(PKM pk, uint nature, uint oldpid)
return false;
const byte AzurillGenderRatio = 0xBF;
var gender = EntityGender.GetFromPIDAndRatio(pk.EncryptionConstant, AzurillGenderRatio);
var gender = EntityGender.GetFromPIDAndRatio(actualPID, AzurillGenderRatio);
if (gender != 1)
return false;
var pid = PokewalkerRNG.GetPID(pk.TID16, pk.SID16, nature, 1, AzurillGenderRatio);
return pid == oldpid;
return pid == actualPID;
}
private static bool GetColoStarterMatch(Span<uint> seeds, PKM pk, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
@ -665,67 +671,8 @@ private static bool GetNonMatch(out PIDIV pidiv)
pidiv = PIDIV.None;
return false;
}
/// <summary>
/// Checks if the PID is a <see cref="PIDType.BACD_U_S"></see> match.
/// </summary>
/// <param name="idXor"><see cref="PKM.TID16"/> ^ <see cref="PKM.SID16"/></param>
/// <param name="pid">Full actual PID</param>
/// <param name="low">Low portion of PID (B)</param>
/// <param name="A">First RNG call</param>
/// <param name="type">PID Type is updated if successful</param>
/// <returns>True/False if the PID matches</returns>
/// <remarks>First RNG call is unrolled once if the PID is valid with this correlation</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsBACD_U_S(uint idXor, uint pid, uint low, ref uint A, ref PIDType type)
{
// 0-Origin
// 1-PIDH
// 2-PIDL (ends up unused)
// 3-FORCEBITS
// PID = PIDH << 16 | (SID16 ^ TID16 ^ PIDH)
var X = LCRNG.Prev(A); // unroll once as there's 3 calls instead of 2
uint PID = (X & 0xFFFF0000) | (idXor ^ X >> 16);
PID &= 0xFFFFFFF8;
PID |= low & 0x7; // lowest 3 bits
if (PID != pid)
return false;
A = X; // keep the unrolled seed
type = BACD_U_S;
return true;
}
/// <summary>
/// Checks if the PID is a <see cref="PIDType.BACD_U_AX"></see> match.
/// </summary>
/// <param name="idxor"><see cref="PKM.TID16"/> ^ <see cref="PKM.SID16"/></param>
/// <param name="pid">Full actual PID</param>
/// <param name="low">Low portion of PID (B)</param>
/// <param name="A">First RNG call</param>
/// <param name="type">PID Type is updated if successful</param>
/// <returns>True/False if the PID matches</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsBACD_U_AX(uint idxor, uint pid, uint low, uint A, ref PIDType type)
{
if ((pid & 0xFFFF) != low)
return false;
// 0-Origin
// 1-ushort rnd, do until >8
// 2-PIDL
uint rnd = A >> 16;
if (rnd < 8)
return false;
uint PID = ((rnd ^ idxor ^ low) << 16) | low;
if (PID != pid)
return false;
type = BACD_U_AX;
return true;
}
private static PIDIV AnalyzeGB(PKM _)
{
// not implemented; correlation between IVs and RNG hasn't been converted to code.

View File

@ -1,4 +1,5 @@
using System;
using static PKHeX.Core.PIDType;
namespace PKHeX.Core;
@ -11,72 +12,60 @@ private static void SetValuesFromSeedLCRNG(PKM pk, PIDType type, uint seed)
{
var A = LCRNG.Next(seed);
var B = LCRNG.Next(A);
var skipBetweenPID = type is PIDType.Method_3 or PIDType.Method_3_Unown;
var skipBetweenPID = type is Method_3 or Method_3_Unown;
if (skipBetweenPID) // VBlank skip between PID rand() [RARE]
B = LCRNG.Next(B);
var swappedPIDHalves = type is >= PIDType.Method_1_Unown and <= PIDType.Method_4_Unown;
var swappedPIDHalves = type is >= Method_1_Unown and <= Method_4_Unown;
if (swappedPIDHalves) // switched order of PID halves, "BA**"
pk.PID = (A & 0xFFFF0000) | (B >> 16);
else
pk.PID = (B & 0xFFFF0000) | (A >> 16);
var C = LCRNG.Next(B);
var skipIV1Frame = type is PIDType.Method_2 or PIDType.Method_2_Unown;
var skipIV1Frame = type is Method_2 or Method_2_Unown;
if (skipIV1Frame) // VBlank skip after PID
C = LCRNG.Next(C);
var D = LCRNG.Next(C);
var skipIV2Frame = type is PIDType.Method_4 or PIDType.Method_4_Unown;
var skipIV2Frame = type is Method_4 or Method_4_Unown;
if (skipIV2Frame) // VBlank skip between IVs
D = LCRNG.Next(D);
Span<int> IVs = stackalloc int[6];
MethodFinder.GetIVsInt32(IVs, C >> 16, D >> 16);
if (type == PIDType.Method_1_Roamer)
if (type == Method_1_Roamer)
{
// Only store lowest 8 bits of IV data; zero out the other bits.
IVs[1] &= 7;
for (int i = 2; i < 6; i++)
IVs[i] = 0;
IVs[2..].Clear();
}
pk.SetIVs(IVs);
}
private static void SetValuesFromSeedBACD(PKM pk, PIDType type, uint seed)
{
bool shiny = type is PIDType.BACD_R_S or PIDType.BACD_U_S;
uint X = shiny ? LCRNG.Next(seed) : seed;
var A = LCRNG.Next(X);
var B = LCRNG.Next(A);
var C = LCRNG.Next(B);
var D = LCRNG.Next(C);
uint idxor = pk.TID16 ^ (uint)pk.SID16;
pk.PID = GetPIDFromSeedBACD(ref seed, type, idxor);
SetIVsFromSeedSequentialLCRNG(ref seed, pk);
pk.RefreshAbility((int)(pk.PID & 1));
}
if (shiny)
{
uint PID = (X & 0xFFFF0000) | ((X >> 16) ^ pk.TID16 ^ pk.SID16);
PID &= 0xFFFFFFF8;
PID |= (B >> 16) & 0x7; // lowest 3 bits
pk.PID = PID;
}
else if (type is PIDType.BACD_R_AX or PIDType.BACD_U_AX)
{
uint low = B >> 16;
pk.PID = ((A & 0xFFFF0000) ^ ((low ^ pk.TID16 ^ pk.SID16) << 16)) | low;
}
else
{
pk.PID = (A & 0xFFFF0000) | (B >> 16);
}
private static uint GetPIDFromSeedBACD(ref uint seed, PIDType type, uint idXor) => type switch
{
BACD => CommonEvent3.GetRegular(ref seed),
BACD_S => CommonEvent3.GetForceShiny(ref seed, idXor),
BACD_AX => CommonEvent3.GetAntishiny(ref seed, idXor),
_ => CommonEvent3.GetRegularAntishiny(ref seed, idXor),
};
public static void SetIVsFromSeedSequentialLCRNG(ref uint seed, PKM pk)
{
var c16 = LCRNG.Next16(ref seed);
var d16 = LCRNG.Next16(ref seed);
Span<int> IVs = stackalloc int[6];
MethodFinder.GetIVsInt32(IVs, C >> 16, D >> 16);
MethodFinder.GetIVsInt32(IVs, c16, d16);
pk.SetIVs(IVs);
bool antishiny = type is PIDType.BACD_R_A or PIDType.BACD_U_A;
while (antishiny && pk.IsShiny)
pk.PID = unchecked(pk.PID + 1);
}
private static void SetValuesFromSeedXDRNG(PKM pk, uint seed)
@ -96,26 +85,27 @@ private static void SetValuesFromSeedXDRNG(PKM pk, uint seed)
seed = XDRNG.Next2(seed); // fake PID
break;
}
var A = XDRNG.Next(seed); // IV1
var B = XDRNG.Next(A); // IV2
var C = XDRNG.Next(B); // Ability
var a16 = XDRNG.Next15(ref seed); // IV1
var b16 = XDRNG.Next15(ref seed); // IV2
Span<int> IVs = stackalloc int[6];
MethodFinder.GetIVsInt32(IVs, a16, b16);
pk.SetIVs(IVs);
_ = XDRNG.Next16(ref seed); // Ability
if (species is (int)Species.Umbreon or (int)Species.Espeon)
{
// Reuse existing logic.
pk.PID = LockFinder.GenerateStarterPID(ref C, pk.TID16, pk.SID16);
pk.PID = LockFinder.GenerateStarterPID(ref seed, pk.TID16, pk.SID16);
}
else
{
// Generate PID.
var D = XDRNG.Next(C); // PID
var E = XDRNG.Next(D); // PID
pk.PID = (D & 0xFFFF0000) | (E >> 16);
var d16 = XDRNG.Next16(ref seed); // PID
var e16 = XDRNG.Next16(ref seed); // PID
pk.PID = (d16 << 16) | e16;
}
Span<int> IVs = stackalloc int[6];
MethodFinder.GetIVsInt32(IVs, A >> 16, B >> 16);
pk.SetIVs(IVs);
}
public static void SetValuesFromSeedXDRNG_EReader(PKM pk, uint seed)
@ -128,27 +118,21 @@ public static void SetValuesFromSeedXDRNG_EReader(PKM pk, uint seed)
private static void SetValuesFromSeedChannel(PKM pk, uint seed)
{
var O = XDRNG.Next(seed); // SID16
var A = XDRNG.Next(O); // PID
var B = XDRNG.Next(A); // PID
var C = XDRNG.Next(B); // Held Item
var D = XDRNG.Next(C); // Version
var E = XDRNG.Next(D); // OT Gender
const ushort TID16 = 40122;
pk.ID32 = (O & 0xFFFF0000) | TID16;
var SID16 = O >> 16;
var pid1 = A >> 16;
var pid2 = B >> 16;
var sid = XDRNG.Next16(ref seed);
pk.ID32 = (sid << 16) | TID16;
var pid1 = XDRNG.Next16(ref seed);
var pid2 = XDRNG.Next16(ref seed);
var pid = (pid1 << 16) | pid2;
if ((pid2 > 7 ? 0 : 1) != (pid1 ^ SID16 ^ TID16))
if ((pid2 > 7 ? 0 : 1) != (pid1 ^ sid ^ TID16))
pid ^= 0x80000000;
pk.PID = pid;
pk.HeldItem = (ushort)(C >> 31) + 169; // 0-Ganlon, 1-Salac
pk.Version = GameVersion.S + (byte)(D >> 31); // 0-Sapphire, 1-Ruby
pk.OriginalTrainerGender = (byte)(E >> 31);
pk.HeldItem = (ushort)(XDRNG.Next16(ref seed) >> 15) + 169; // 0-Ganlon, 1-Salac
pk.Version = GameVersion.S + (byte)(XDRNG.Next16(ref seed) >> 15); // 0-Sapphire, 1-Ruby
pk.OriginalTrainerGender = (byte)(XDRNG.Next16(ref seed) >> 15);
Span<int> ivs = stackalloc int[6];
XDRNG.GetSequentialIVsInt32(E, ivs);
XDRNG.GetSequentialIVsInt32(seed, ivs);
pk.SetIVs(ivs);
}
@ -162,34 +146,29 @@ public static void SetValuesFromSeed(PKM pk, PIDType type, uint seed)
{
switch (t)
{
case PIDType.Channel:
case Channel:
return SetValuesFromSeedChannel;
case PIDType.CXD:
case CXD:
return SetValuesFromSeedXDRNG;
case PIDType.Method_1 or PIDType.Method_2 or PIDType.Method_3 or PIDType.Method_4:
case PIDType.Method_1_Unown or PIDType.Method_2_Unown or PIDType.Method_3_Unown or PIDType.Method_4_Unown:
case PIDType.Method_1_Roamer:
case Method_1 or Method_2 or Method_3 or Method_4:
case Method_1_Unown or Method_2_Unown or Method_3_Unown or Method_4_Unown:
case Method_1_Roamer:
return (pk, seed) => SetValuesFromSeedLCRNG(pk, t, seed);
case PIDType.BACD_R:
case PIDType.BACD_R_A:
case PIDType.BACD_R_S:
case PIDType.BACD_R_AX:
return (pk, seed) => SetValuesFromSeedBACD(pk, t, seed & 0xFFFF);
case PIDType.BACD_U:
case PIDType.BACD_U_A:
case PIDType.BACD_U_S:
case PIDType.BACD_U_AX:
case BACD:
case BACD_S:
case BACD_A:
case BACD_AX:
return (pk, seed) => SetValuesFromSeedBACD(pk, t, seed);
case PIDType.PokeSpot:
case PokeSpot:
return SetRandomPokeSpotPID;
case PIDType.G5MGShiny:
case G5MGShiny:
return SetValuesFromSeedMG5Shiny;
case PIDType.Pokewalker:
case Pokewalker:
return (pk, seed) =>
{
var pid = pk.PID = PokewalkerRNG.GetPID(pk.TID16, pk.SID16, seed % 24, pk.Gender, pk.PersonalInfo.Gender);
@ -197,11 +176,11 @@ public static void SetValuesFromSeed(PKM pk, PIDType type, uint seed)
};
// others: unimplemented
case PIDType.CuteCharm:
case CuteCharm:
break;
case PIDType.ChainShiny:
case ChainShiny:
return SetRandomChainShinyPID;
case PIDType.G4MGAntiShiny:
case G4MGAntiShiny:
break;
}
return (_, _) => { };
@ -210,12 +189,7 @@ public static void SetValuesFromSeed(PKM pk, PIDType type, uint seed)
public static void SetRandomChainShinyPID(PKM pk, uint seed)
{
pk.PID = ClassicEraRNG.GetChainShinyPID(ref seed, pk.ID32);
Span<int> IVs = stackalloc int[6];
var rand1 = LCRNG.Next16(ref seed);
var rand2 = LCRNG.Next16(ref seed);
MethodFinder.GetIVsInt32(IVs, rand1, rand2);
pk.SetIVs(IVs);
SetIVsFromSeedSequentialLCRNG(ref seed, pk);
}
private static void SetRandomPokeSpotPID(PKM pk, uint seed)
@ -291,7 +265,7 @@ public static bool IsValidCriteria4(PKM pk, Nature nature, int ability, byte gen
return true;
}
public static void SetRandomWildPID5(PKM pk, Nature nature, int ability, byte gender, PIDType specific = PIDType.None)
public static void SetRandomWildPID5(PKM pk, Nature nature, int ability, byte gender, PIDType specific = None)
{
var tidbit = (pk.TID16 ^ pk.SID16) & 1;
pk.RefreshAbility(ability);
@ -305,7 +279,7 @@ public static void SetRandomWildPID5(PKM pk, Nature nature, int ability, byte ge
while (true)
{
uint seed = rnd.Rand32();
if (specific == PIDType.G5MGShiny)
if (specific == G5MGShiny)
{
SetValuesFromSeedMG5Shiny(pk, seed);
seed = pk.PID;

View File

@ -13,23 +13,18 @@ namespace PKHeX.Core;
internal static readonly PIDIV Pokewalker = new(PIDType.Pokewalker);
internal static readonly PIDIV G5MGShiny = new(PIDType.G5MGShiny);
/// <summary>The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first)</summary>
[field: FieldOffset(0)]
public uint OriginSeed { get; }
/// <summary>The RNG seed which immediately generates the PID/IV (starting with PID or IVs, whichever comes first)</summary>
[field: FieldOffset(0)] public uint OriginSeed { get; }
/// <summary>The RNG seed which starts the encounter generation routine.</summary>
[field: FieldOffset(4)]
public uint EncounterSeed { get; init; }
[field: FieldOffset(4)] public uint EncounterSeed { get; init; }
/// <summary>The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first)</summary>
[field: FieldOffset(0)]
public ulong Seed64 { get; }
/// <summary>The RNG seed which immediately generates the PID/IV (starting with PID or IVs, whichever comes first)</summary>
[field: FieldOffset(0)] public ulong Seed64 { get; }
/// <summary>Type of PIDIV correlation</summary>
[field: FieldOffset(8)]
public PIDType Type { get; }
[field: FieldOffset(9)]
public LeadRequired Lead { get; init; }
[field: FieldOffset(8)] public PIDType Type { get; }
[field: FieldOffset(9)] public LeadRequired Lead { get; init; }
[field: FieldOffset(9)] public PIDType Mutated { get; init; }
public PIDIV(PIDType type, uint seed = 0)
{
@ -43,7 +38,7 @@ public PIDIV(PIDType type, ulong seed)
Seed64 = seed;
}
/// <remarks> Some PIDIVs may be generated without a single seed, but may follow a traceable pattern. </remarks>
/// <remarks> Some PID/IVs may be generated without a single seed, but may follow a traceable pattern. </remarks>
/// <summary> Indicates that there is no <see cref="OriginSeed"/> to refer to. </summary>
public bool NoSeed => Type is PIDType.None or PIDType.Pokewalker or PIDType.G5MGShiny;
@ -54,4 +49,6 @@ public PIDIV(PIDType type, ulong seed)
public bool IsSeed64() => Type is PIDType.Xoroshiro;
public PIDIV AsEncounteredVia(LeadSeed condition) => this with { Lead = condition.Lead, EncounterSeed = condition.Seed };
public PIDIV AsMutated(PIDType type, uint Origin) => this with { Mutated = type, EncounterSeed = Origin };
public PIDIV AsMutated(PIDType type) => this with { Mutated = type, EncounterSeed = OriginSeed };
}

View File

@ -49,53 +49,48 @@ public enum PIDType : byte
/// <remarks><see cref="LCRNG"/></remarks>
Method_1_Roamer,
/// <summary>
/// Event Reversed Order PID restricted to 16bit Origin Seed
/// </summary>
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
BACD_R,
#region BACD Event
/// <summary>
/// Event Reversed Order PID without Origin Seed restrictions
/// </summary>
/// <remarks><see cref="LCRNG"/></remarks>
#region Internal Matching Tagging
/// <summary> Event Reversed Order PID without Origin Seed restrictions </summary>
BACD,
/// <summary> Event Reversed Order PID without Origin Seed restrictions, anti-shiny (add). </summary>
BACD_A,
/// <summary> Event Reversed Order PID without Origin Seed restrictions, anti-shiny (xor) </summary>
BACD_AX,
/// <summary> Event Reversed Order PID without Origin Seed restrictions, shiny </summary>
BACD_S,
/// <summary> Event Reversed Order PID without Origin Seed restrictions, anti-shiny (add). </summary>
#endregion
#region Egg-Only Internal Matching Tagging
BACD_EA,
/// <summary> Event Reversed Order PID without Origin Seed restrictions, anti-shiny (xor) </summary>
BACD_EAX,
/// <summary> Event Reversed Order PID without Origin Seed restrictions, shiny </summary>
BACD_ES,
#endregion
/// <summary> Event Reversed Order PID without Origin Seed restrictions </summary>
BACD_U,
/// <summary>
/// Event Reversed Order PID restricted to 16bit Origin Seed, anti-shiny.
/// </summary>
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
BACD_R_A,
/// <summary>
/// Event Reversed Order PID without Origin Seed restrictions, anti-shiny.
/// </summary>
/// <remarks><see cref="LCRNG"/></remarks>
BACD_U_A,
/// <summary>
/// Event Reversed Order PID restricted to 8bit Origin Seed, shiny
/// </summary>
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
BACD_R_S,
/// <summary>
/// Event Reversed Order PID without Origin Seed restrictions, shiny
/// </summary>
/// <remarks><see cref="LCRNG"/></remarks>
BACD_U_S,
/// <summary>
/// Event Reversed Order PID restricted to 16bit Origin Seed, anti-shiny (nyx)
/// </summary>
/// <remarks><see cref="LCRNG"/> seed is clamped to 16bits.</remarks>
BACD_R_AX,
/// <summary>
/// Event Reversed Order PID without Origin Seed restrictions, anti-shiny (nyx)
/// </summary>
/// <remarks><see cref="LCRNG"/></remarks>
/// <summary> Event Reversed Order PID without Origin Seed restrictions, anti-shiny (xor) </summary>
BACD_U_AX,
// BACD_U_A and BACD_U_S were never generated by any event.
/// <summary> Event Reversed Order PID restricted to 16bit Origin Seed </summary>
BACD_R,
/// <summary> Event Reversed Order PID restricted to 16bit Origin Seed, anti-shiny. </summary>
BACD_R_A,
/// <summary> Event Reversed Order PID restricted to [0,213] Origin Seed, shiny </summary>
BACD_RBCD,
/// <summary> Event Reversed Order PID with Origin Seed restrictions, consuming 2 calls to the RNG before the PID is generated. </summary>
BACD_T2,
/// <summary> Event Reversed Order PID with Origin Seed restrictions, consuming 3 calls to the RNG before the PID is generated. </summary>
BACD_T3,
/// <summary> Event Reversed Order PID with Origin Seed restrictions, only using the Mystry Mew table. </summary>
BACD_M,
#endregion
/// <summary>
/// Generation 4 Cute Charm PID, which is forced to an 8 bit PID value based on the gender &amp; gender ratio value.
@ -206,4 +201,6 @@ public static class PIDTypeExtensions
public static bool IsClassicMethod(this PIDType type) => type
is Method_1 or Method_2 or Method_3 or Method_4
or Method_1_Unown or Method_2_Unown or Method_3_Unown or Method_4_Unown;
public static bool IsRestricted(this PIDType type) => type is >= BACD_R and <= BACD_M;
}

View File

@ -61,6 +61,7 @@ internal set
public bool PIDParsed { get; private set; }
private PIDIV _pidiv;
internal ref PIDIV GetPIDIVRef() => ref _pidiv;
public EncounterYieldFlag ManualFlag { get; internal set; }
public bool FrameMatches => ManualFlag != EncounterYieldFlag.InvalidFrame;

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.Species;
namespace PKHeX.Core;
@ -16,11 +15,19 @@ public static class FormInfo
/// <param name="form">Entity form</param>
/// <param name="format">Current generation format</param>
/// <returns>True if it can only exist in a battle, false if it can exist outside of battle.</returns>
public static bool IsBattleOnlyForm(ushort species, byte form, byte format) => BattleOnly.Contains(species) && species switch
public static bool IsBattleOnlyForm(ushort species, byte form, byte format)
{
if (BattleMegas.Contains(species))
return IsBattleMegaForm(species, form);
if (BattleForms.Contains(species))
return IsBattleForm(species, form);
return false;
}
private static bool IsBattleForm(ushort species, byte form) => species switch
{
// Only continue checking if the species is in the list of Battle Only forms.
// Some species have battle only forms as well as out-of-battle forms (other than base form).
(ushort)Slowbro => form == 1, // Mega
(ushort)Darmanitan => (form & 1) == 1, // Zen
(ushort)Zygarde => form == 4, // Zygarde Complete
(ushort)Minior => form < 7, // Minior Shields-Down
@ -30,6 +37,13 @@ public static class FormInfo
_ => form != 0,
};
private static bool IsBattleMegaForm(ushort species, byte form)
{
if (species is (ushort)Slowbro)
return form == 1; // Mega
return form != 0;
}
/// <summary>
/// Reverts the Battle Form to the form it would have outside of Battle.
/// </summary>
@ -131,7 +145,7 @@ public static bool IsFormChangeable(ushort species, byte oldForm, byte newForm,
/// Species that can change between their forms, regardless of origin.
/// </summary>
/// <remarks>Excludes Zygarde as it has special conditions. Check separately.</remarks>
private static readonly HashSet<ushort> FormChange =
private static ReadOnlySpan<ushort> FormChange =>
[
(int)Burmy,
(int)Furfrou,
@ -225,21 +239,6 @@ public static bool IsFormChangeable(ushort species, byte oldForm, byte newForm,
(int)Audino, (int)Diancie,
];
private static readonly HashSet<ushort> BattleOnly = GetBattleFormSet();
private static HashSet<ushort> GetBattleFormSet()
{
var reg = BattleForms;
var mega = BattleMegas;
var count = reg.Length + mega.Length + 2;
var hs = new HashSet<ushort>(count);
foreach (var species in reg)
hs.Add(species);
foreach (var species in mega)
hs.Add(species);
return hs;
}
/// <summary>
/// Species has a Totem form in Gen7 (S/M &amp; US/UM) that can be captured and owned.
/// </summary>

View File

@ -101,7 +101,7 @@ public override string CardTitle
public override ushort Location { get => (ushort)(IsEgg ? 0 : Gift.EggLocation + 3000); set { } }
public override ushort EggLocation { get => (ushort)(IsEgg ? Gift.EggLocation + 3000 : 0); set { } }
public bool IsCompatible(PIDType val, PKM pk) => Gift.IsCompatible(val, pk);
public bool IsCompatible(PIDType type, PKM pk) => Gift.IsCompatible(type, pk);
public PIDType GetSuggestedCorrelation() => Gift.GetSuggestedCorrelation();
public bool GiftEquals(PGT pgt)

View File

@ -293,13 +293,13 @@ public static bool IsRangerManaphy(PKM pk)
public bool RibbonChampionWorld { get => PK.RibbonChampionWorld; set => PK.RibbonChampionWorld = value; }
public bool RibbonSouvenir { get => PK.RibbonSouvenir; set => PK.RibbonSouvenir = value; }
public bool IsCompatible(PIDType val, PKM pk)
public bool IsCompatible(PIDType type, PKM pk)
{
if (IsManaphyEgg)
return IsG4ManaphyPIDValid(val, pk);
if (PK.PID != 1 && val == PIDType.G5MGShiny)
return IsG4ManaphyPIDValid(type, pk);
if (PK.PID != 1 && type == PIDType.G5MGShiny)
return true;
return val == PIDType.None;
return type == PIDType.None;
}
public PIDType GetSuggestedCorrelation()

View File

@ -83,35 +83,39 @@ public void PIDIVMatchingTest3Event()
{
// Restricted: TID16/SID16 are zero.
var pkR = new PK3 {PID = 0x0000E97E, IVs = [17, 19, 20, 16, 13, 12]};
MethodFinder.Analyze(pkR).Type.Should().Be(PIDType.BACD_R);
var result = MethodFinder.Analyze(pkR);
(result is { Type: PIDType.BACD, OriginSeed: <= ushort.MaxValue }).Should().BeTrue();
// Restricted Antishiny: PID is incremented 2 times to lose shininess.
var pkRA = new PK3 {PID = 0x0000E980, IVs = [17, 19, 20, 16, 13, 12], TID16 = 01337, SID16 = 60486};
MethodFinder.Analyze(pkRA).Type.Should().Be(PIDType.BACD_R_A);
result = MethodFinder.Analyze(pkRA);
(result is { Type: PIDType.BACD_A, OriginSeed: <= ushort.MaxValue }).Should().BeTrue();
// Unrestricted: TID16/SID16 are zero.
var pkU = new PK3 {PID = 0x67DBFC33, IVs = [12, 25, 27, 30, 02, 31]};
MethodFinder.Analyze(pkU).Type.Should().Be(PIDType.BACD_U);
result = MethodFinder.Analyze(pkU);
(result is { Type: PIDType.BACD }).Should().BeTrue();
// Unrestricted Antishiny: PID is incremented 5 times to lose shininess.
var pkUA = new PK3 {PID = 0x67DBFC38, IVs = [12, 25, 27, 30, 02, 31], TID16 = 01337, SID16 = 40657};
MethodFinder.Analyze(pkUA).Type.Should().Be(PIDType.BACD_U_A);
result = MethodFinder.Analyze(pkUA);
(result is { Type: PIDType.BACD_A }).Should().BeTrue();
// berry fix zigzagoon: seed 0x0020
const ushort bfix = 0x20;
var pkRS = new PK3 {PID = 0x38CA4EA0, IVs = [00, 20, 28, 11, 19, 00], TID16 = 30317, SID16 = 00000};
var a_pkRS = MethodFinder.Analyze(pkRS);
a_pkRS.Type.Should().Be(PIDType.BACD_R_S);
a_pkRS.OriginSeed.Should().Be(0x0020);
result = MethodFinder.Analyze(pkRS);
(result is { Type: PIDType.BACD_S, OriginSeed: bfix }).Should().BeTrue();
var gkRS = new PK3 { TID16 = 30317, SID16 = 00000 };
PIDGenerator.SetValuesFromSeed(gkRS, PIDType.BACD_R_S, a_pkRS.OriginSeed);
PIDGenerator.SetValuesFromSeed(gkRS, PIDType.BACD_S, bfix);
gkRS.PID.Should().Be(pkRS.PID);
gkRS.IVs.SequenceEqual(pkRS.IVs).Should().BeTrue();
// Unrestricted Antishiny nyx
var nyxUA = new PK3 {PID = 0xBD3DF676, IVs = [00, 15, 05, 04, 21, 05], TID16 = 80, SID16 = 0};
var nyx_pkUA = MethodFinder.Analyze(nyxUA);
nyx_pkUA.Type.Should().Be(PIDType.BACD_U_AX);
var nyxUA = new PK3 {PID = 0xBD3DF676, IVs = [00, 15, 05, 04, 21, 05], TID16 = 00080, SID16 = 00000};
result = MethodFinder.Analyze(nyxUA);
(result is { Type: PIDType.BACD_AX }).Should().BeTrue();
}
[Fact]