diff --git a/PKHeX.Core/Legality/Encounters/Data/Gen3/EncountersWC3.cs b/PKHeX.Core/Legality/Encounters/Data/Gen3/EncountersWC3.cs index d4ef3a814..3d2a760c9 100644 --- a/PKHeX.Core/Legality/Encounters/Data/Gen3/EncountersWC3.cs +++ b/PKHeX.Core/Legality/Encounters/Data/Gen3/EncountersWC3.cs @@ -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; /// 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 = "ANA", 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 = "GW", 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 = "ANA", 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 = "GW", 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 = "AZUSA", OriginalTrainerGender = 1 }, // Swablu Egg with False Swipe - new(263, 5, Gen3, true) { Moves = new(033,045,039,245), Method = BACD_U, OriginalTrainerName = "AZUSA", OriginalTrainerGender = 1 }, // Zigzagoon Egg with Extreme Speed - new(300, 5, Gen3, true) { Moves = new(045,033,039,006), Method = BACD_U, OriginalTrainerName = "AZUSA", OriginalTrainerGender = 1 }, // Skitty Egg with Pay Day - new(172, 5, Gen3, true) { Moves = new(084,204,057,000), Method = BACD_U, OriginalTrainerName = "AZUSA", OriginalTrainerGender = 1 }, // Pichu Egg with Surf + new(333, 5, Gen3, true) { Moves = new(064,045,206,000), Method = BACD_U, OriginalTrainerName = "AZUSA", OriginalTrainerGender = Only1 }, // Swablu Egg with False Swipe + new(263, 5, Gen3, true) { Moves = new(033,045,039,245), Method = BACD_U, OriginalTrainerName = "AZUSA", OriginalTrainerGender = Only1 }, // Zigzagoon Egg with Extreme Speed + new(300, 5, Gen3, true) { Moves = new(045,033,039,006), Method = BACD_U, OriginalTrainerName = "AZUSA", OriginalTrainerGender = Only1 }, // Skitty Egg with Pay Day + new(172, 5, Gen3, true) { Moves = new(084,204,057,000), Method = BACD_U, OriginalTrainerName = "AZUSA", 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(); } diff --git a/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator3.cs b/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator3.cs index c5964b57e..315ebb678 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator3.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator3.cs @@ -64,7 +64,7 @@ public IEnumerable 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 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 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; diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterGift3Colo.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterGift3Colo.cs index 034a6b16e..99f33f597 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterGift3Colo.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterGift3Colo.cs @@ -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; diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterShadow3Colo.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterShadow3Colo.cs index f9e699395..1e96628c2 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterShadow3Colo.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterShadow3Colo.cs @@ -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() diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterStatic3Colo.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterStatic3Colo.cs index 8d82b965c..544061c1e 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterStatic3Colo.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterStatic3Colo.cs @@ -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; diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterSlot3.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterSlot3.cs index 3bd020104..c75343b53 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterSlot3.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterSlot3.cs @@ -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; diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterStatic3.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterStatic3.cs index f3632b0c5..c65302649 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterStatic3.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterStatic3.cs @@ -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) diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/Distribution3JPN.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/Distribution3JPN.cs index b8e5ad832..ca1e7e617 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/Distribution3JPN.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/Distribution3JPN.cs @@ -22,6 +22,34 @@ public static class Gen3PCJP private const string Fukuoka = "フクオカ"; private const string Sapporo = "サッポロ"; + private static ReadOnlySpan Distro1 => [252, 255, 258]; + private static ReadOnlySpan Distro2 => [152, 155, 158]; + private static ReadOnlySpan Distro3 => [023, 025, 052, 058, 069, 079, 090, 113, 115, 123, 125, 126, 128, 198, 200, 211, 215, 225, 226]; + private static ReadOnlySpan Distro4 => [1, 4, 7]; + private static ReadOnlySpan Distro5 => [025, 270, 273, 283, 300, 302, 303, 307, 311, 312, 315, 335, 336, 337, 338, 358]; + private static ReadOnlySpan 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 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, diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs index f7768ae47..fbca0f3ed 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs @@ -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 /// 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; /// /// Matched 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 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; + } +} + +/// +/// Trainer Gender for Gift Events in Gen3 +/// +/// If random, determined after the PID/IV (and potentially item). +public enum GiftGender3 : byte +{ + /// Match the recipient. Either 0 or 1. + Recipient, + /// Must be 0. + Only0, + /// Must be 1. + Only1, + + /// Determined by a separate algorithm and shouldn't be checked via regular logic. + RandAlgo, + + /// Divide by 3. + RandD3, + /// Shift by 3. + RandS3, + /// Shift by 7. + RandS7, + /// Shift by 15, after Item. + RandSG15, + + /// , but MUST be 0. Used for dual-OT events. + RandD3_0, + /// , but MUST be 1. Used for dual-OT events. + RandD3_1, } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3JPN.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3JPN.cs index 00de27bb2..e6df9ccab 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3JPN.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3JPN.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core; /// /// Specialized for the PCJP gift distribution machines. 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 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 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); } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3NY.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3NY.cs index 1157f363f..556720293 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3NY.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3NY.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core; /// /// Specialized for the PCNY gift distribution machines. 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 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 trainerName = stackalloc char[pk.TrashCharCountTrainer]; int len = pk.LoadString(pk.OriginalTrainerTrash, trainerName); @@ -139,4 +156,24 @@ internal static EncounterGift3NY[] GetArray(ReadOnlySpan 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); } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterShadow3XD.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterShadow3XD.cs index 8022df800..f62fc59fa 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterShadow3XD.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterShadow3XD.cs @@ -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; } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterSlot3XD.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterSlot3XD.cs index e8bdf9d3c..60a7f8152 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterSlot3XD.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterSlot3XD.cs @@ -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; } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterStatic3XD.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterStatic3XD.cs index d963e4915..c03c2e24d 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterStatic3XD.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterStatic3XD.cs @@ -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; diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterTrade3XD.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterTrade3XD.cs index ca54568fe..c6c787cee 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterTrade3XD.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterTrade3XD.cs @@ -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 trainer, int language) { diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterSlot4.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterSlot4.cs index 692dfd523..ce9ccaba5 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterSlot4.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterSlot4.cs @@ -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; } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4.cs index 59a0fd811..7114fdb24 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4.cs @@ -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; } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4Pokewalker.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4Pokewalker.cs index 75b88c25d..8864c5b98 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4Pokewalker.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4Pokewalker.cs @@ -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; } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Interfaces/Properties/IRandomCorrelation.cs b/PKHeX.Core/Legality/Encounters/Templates/Interfaces/Properties/IRandomCorrelation.cs index cdf175cf3..1c15c08d5 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Interfaces/Properties/IRandomCorrelation.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Interfaces/Properties/IRandomCorrelation.cs @@ -5,6 +5,30 @@ namespace PKHeX.Core; /// public interface IRandomCorrelation { - bool IsCompatible(PIDType val, PKM pk); + /// + /// Checks if the given is compatible with the encounter restrictions. + /// + /// Observed of the + /// Entity to compare against for other details + /// True if all details are compatible + bool IsCompatible(PIDType type, PKM pk); + + /// + /// Gets the suggested for the encounter. + /// PIDType GetSuggestedCorrelation(); } + +/// +/// Specialized interface for mutating a value to a derived type. +/// +public interface IRandomCorrelationEvent3 : IRandomCorrelation +{ + /// + /// Mutates the value to a derived type, if possible. + /// + /// Value to check and mutate + /// Entity to compare against + /// True if Compatible. Revision of the ref value is not returned. + bool IsCompatibleReviseReset(ref PIDIV value, PKM pk); +} diff --git a/PKHeX.Core/Legality/Formatting/LegalityFormatting.cs b/PKHeX.Core/Legality/Formatting/LegalityFormatting.cs index e9c7af06b..3bc7676f4 100644 --- a/PKHeX.Core/Legality/Formatting/LegalityFormatting.cs +++ b/PKHeX.Core/Legality/Formatting/LegalityFormatting.cs @@ -103,7 +103,19 @@ public static void AddEncounterInfoPIDIV(LegalityAnalysis la, List lines private static void AddEncounterInfoPIDIV(List 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 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); } } diff --git a/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs b/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs index 911062e43..f5ad420c8 100644 --- a/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs +++ b/PKHeX.Core/Legality/RNG/Algorithms/LCRNG.cs @@ -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; /// - /// Gets the next 16 bits of the next RNG seed. + /// Gets the upper 16 bits of the next RNG seed. /// /// Seed to advance one step. [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -99,9 +99,9 @@ public static uint Next16(ref uint seed) } /// - /// Gets the previous 16 bits of the previous RNG seed. + /// Gets the upper 16 bits of the previous RNG seed. /// - /// Seed to advance one step. + /// Seed to reverse one step. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev16(ref uint seed) { diff --git a/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs b/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs index d0d26d0a2..b5b75d674 100644 --- a/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs +++ b/PKHeX.Core/Legality/RNG/Algorithms/XDRNG.cs @@ -95,7 +95,7 @@ public static class XDRNG [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev12(uint seed) => (seed * rMult12) + rAdd12; /// - /// Gets the next 16 bits of the next RNG seed. + /// Gets the upper 16 bits of the next RNG seed. /// /// Seed to advance one step. [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -106,7 +106,7 @@ public static uint Next16(ref uint seed) } /// - /// Gets the next 16 bits of the next RNG seed. + /// Gets the upper 0x7FFF bits of the next RNG seed. /// /// Seed to advance one step. [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -117,9 +117,9 @@ public static uint Next15(ref uint seed) } /// - /// Gets the previous 16 bits of the previous RNG seed. + /// Gets the upper 16 bits of the previous RNG seed. /// - /// Seed to advance one step. + /// Seed to reverse one step. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Prev16(ref uint seed) { diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/CommonEvent3.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/CommonEvent3.cs new file mode 100644 index 000000000..36d2dc011 --- /dev/null +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/CommonEvent3.cs @@ -0,0 +1,240 @@ +namespace PKHeX.Core; + +/// +/// Logic for calculating and detecting Generation 3 Event PIDs. +/// +/// +/// 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. +/// +public static class CommonEvent3 +{ + /// + /// Standard calculation for the BA-CD PID generation. + /// + /// First rand16 call, placed in the upper bits of the PID. + /// Second rand16 call, placed in the lower bits of the PID. + /// 32bit PID + public static uint GetRegular(uint a16, uint b16) => (a16 << 16) | b16; + + /// + /// Alternate PID generation method for events, notably used in the PC-NY Gotta Catch 'Em All events. + /// + /// First rand16 call, placed in the upper bits of the PID. + /// Second rand16 call, placed in the lower bits of the PID. + /// Trainer ID xor value. + /// must be greater than 7. + /// 32bit PID + public static uint GetAntishiny(uint a16, uint b16, uint idXor) => ((a16 ^ (idXor ^ b16)) << 16) | b16; + + /// + /// Alternate PID generation method for events, notably used by the Berry Glitch Fix (Zigzagoon). + /// + /// First rand16 call, placed in the upper bits of the PID. + /// Second rand16 call, placed in the lower bits of the PID. + /// Trainer ID xor value. + /// instead of a16, this is 1 frame earlier. The a16 call is unused. + /// 32bit PID + public static uint GetForceShiny(uint x16, uint b16, uint idXor) => (x16 << 16) | (((idXor ^ x16) & 0xFFF8) | (b16 & 0b111)); + + /// + /// Regular PID generation method for events, that changes the PID to be non-shiny if it were shiny. + /// + /// Calculated 32-bit PID from the regular generation method. + /// Revised 32-bit PID + private static uint GetRegularAntishiny(uint pid) => (pid + 8) & 0xFFFFFFF8; + + /// + /// First rand16 call, placed in the upper bits of the PID. + /// Second rand16 call, placed in the lower bits of the PID. + /// Trainer ID xor value. + 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; + } + + /// + /// Seed to roll forward to generate the PID. + public static uint GetRegular(ref uint seed) + { + var a16 = LCRNG.Next16(ref seed); + var b16 = LCRNG.Next16(ref seed); + return GetRegular(a16, b16); + } + + /// + /// Seed to roll forward to generate the PID. + /// Trainer ID xor value. + 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); + } + + /// + /// Seed to roll forward to generate the PID. + /// Trainer ID xor value. + 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); + } + + /// + /// Seed to roll forward to generate the PID. + /// Trainer ID xor value. + 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); + } + + /// + /// Checks if the PID is a regular PID generated from the given a16 and b16 values. + /// + /// + public static bool IsRegular(uint pid, uint a16, uint b16) + { + uint result = GetRegular(a16, b16); + return result == pid; + } + + /// + /// Checks if the PID is an anti-shiny PID generated from the given a16 and b16 values. + /// + /// + 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; + } + + /// + /// Checks if the PID is an anti-shiny PID generated from the given a16 and b16 values. + /// + /// + 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; + } + + /// + /// Checks if the PID is a regular anti-shiny PID generated from the given a16 and b16 values. + /// + /// + public static bool IsRegularAntishiny(uint pid, uint a16, uint b16, uint idXor) + { + uint result = GetRegularAntishiny(a16, b16, idXor); + return result == pid; + } + + /// + /// Regular BA PID that is expected from the correlation, no corrections. + /// The actual PID of the Pokémon. + public static bool IsRegularAntishiny(uint actualPID, uint expectPID) + { + var result = GetRegularAntishiny(expectPID); + return result == actualPID; + } + + /// Checks for eggs hatched with the regular Anti-shiny correlation, but originating from an unknown ID xor. + /// + /// Only call this method when the Pokémon is SHINY. + /// Regular BA PID that is expected from the correlation, no corrections. + /// The actual PID of the Pokémon. + 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; + } + + /// Checks for eggs hatched with the Force Anti-shiny correlation, but originating from an unknown ID xor. + /// + /// Only call this method when the Pokémon is SHINY. + 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; + } + + /// Checks for eggs hatched with the Force Shiny correlation, but originating from an unknown ID xor. + /// + /// Only call this method when the Pokémon is NOT SHINY. + 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); + + /// + /// Gets a random restricted (16-bit) seed that respects the gender correlation. + /// + /// Seed that generates the PID/IV then gender. + /// Requested gender + 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); + } + } +} diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/CommonEvent3Checker.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/CommonEvent3Checker.cs new file mode 100644 index 000000000..3f8b16041 --- /dev/null +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/CommonEvent3Checker.cs @@ -0,0 +1,153 @@ +using static PKHeX.Core.PIDType; + +namespace PKHeX.Core; + +/// +/// Logic to revise a based on observed and constraining +/// +public static class CommonEvent3Checker +{ + /// + /// Checks if the observed type and seed value match the constraints of the encounter. + /// + /// Regular Antishiny Table constraint. + /// PID/IV observed + /// Observed PID Type + /// Encounter Species + /// Encounter moveset contains the move Wish + /// True if is match; mutation not indicated. + 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; + } + + /// Force Shiny Table constraint. + /// + 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; + } + + /// Mystry Mew constraint. + /// + 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; + } + + /// CHANNEL Jirachi constraint. + /// + 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; + } + + /// Regular BACD (WISHMKR Jirachi, Eggs) constraint. + /// + 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; + } + + /// Regular Antishiny without seed restriction constraint. + /// + 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; + } + + /// Regular Antishiny with seed restriction constraint. + /// + 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; + } + + /// Berry Fix Zigzagoon seed restriction constraint. + /// + 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; + } +} diff --git a/PKHeX.Core/Legality/Tables/MystryMew.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/MystryMew.cs similarity index 67% rename from PKHeX.Core/Legality/Tables/MystryMew.cs rename to PKHeX.Core/Legality/RNG/ClassicEra/Gen3/MystryMew.cs index 4ea4da3b7..c6128df66 100644 --- a/PKHeX.Core/Legality/Tables/MystryMew.cs +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/MystryMew.cs @@ -34,6 +34,22 @@ public static class MystryMew ]; private const int MewPerRestrictedSeed = 5; + private const int RestrictedIndex = 0; + + /// 01_34 of this set were released by the event organizer. + 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)) + + /// + /// Checks if the seed was unreleased. + /// + public static bool IsUnreleased(uint seed, int subIndex) => seed is ReleasedSeed && subIndex is not ReleasedSeedKeptIndex; + + /// + /// Checks if the seed index (of the returned tuple) is referring to the oldest (first) sibling of the 5-per-seed. + /// + public static bool IsRestrictedIndex0th(int index) => index == RestrictedIndex; /// /// Gets a random valid seed based on the input 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; } + /// + /// Gets the seed at (sorted) . + /// + public static uint GetSeed(int index) => Seeds[(uint)index >= Seeds.Length ? 0 : index]; + /// /// Checks if the seed is a known seed. /// - /// Origin seed (for the PID/IV) /// True if the seed is known. public static bool IsValidSeed(uint seed) => GetSeedIndex(seed) != -1; + /// Origin seed (for the PID/IV) + /// Sub-index (if not a restricted seed). + /// + public static bool IsValidSeed(uint seed, int subIndex) => !IsUnreleased(seed, subIndex); + /// /// Checks if the seed is a known seed. /// @@ -80,7 +107,7 @@ public static int GetSeedIndex(uint seed) } /// - /// 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. /// /// Origin seed (for the PID/IV) /// Tuple of indexes; -1 if not found. @@ -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) diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/PCJPFifthAnniversary.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/PCJPFifthAnniversary.cs new file mode 100644 index 000000000..eb5e5753b --- /dev/null +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/PCJPFifthAnniversary.cs @@ -0,0 +1,76 @@ +namespace PKHeX.Core; + +/// +/// Logic for determining the PCJP Fifth Anniversary event gift received. +/// +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); + } + + /// + /// Gets the Species index within the PCJP table. + /// + /// Species ID + /// 0-3 + 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; + } + + /// + /// Check if the given species, shiny, and wish moveset status match the given rand result. + /// + 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; + } + + /// + /// Gets a random 16-bit seed that will return the desired table result. + /// + 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); + } + } +} diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/WeightedTable3.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/WeightedTable3.cs new file mode 100644 index 000000000..31ff1d237 --- /dev/null +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/WeightedTable3.cs @@ -0,0 +1,37 @@ +using System.Diagnostics.CodeAnalysis; + +namespace PKHeX.Core; + +/// +/// Generation 3 Event Table Weighting Algorithm. +/// +public static class WeightedTable3 +{ + /// + /// Gets the random weight value, with a periodic frequency of repeating (*about* every 0x8000). + /// + /// 32-bit random number + /// Exclusive maximum value that can be returned + /// [0, max) + 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; + } + + /// + public static uint GetRandom32(uint seed) => GetRandom32(ref seed); + + /// + /// Gets a 32-bit random number from the given seed to use as the table random selector. + /// + public static uint GetRandom32(ref uint seed) + { + var rand1 = LCRNG.Next16(ref seed); + var rand2 = LCRNG.Next16(ref seed); + return (rand1 << 16) | rand2; + } +} diff --git a/PKHeX.Core/Legality/RNG/MethodFinder.cs b/PKHeX.Core/Legality/RNG/MethodFinder.cs index 7d456bb2c..7c920a8b5 100644 --- a/PKHeX.Core/Legality/RNG/MethodFinder.cs +++ b/PKHeX.Core/Legality/RNG/MethodFinder.cs @@ -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 temp = stackalloc uint[6]; for (int i = 0; i < 6; i++) temp[i] = (uint)pk.GetIV(i); - ReadOnlySpan IVs = temp; + return GetLCRNGMethod1Match(pk.EncryptionConstant, temp, out result); + } - const int maxResults = LCRNG.MaxCountSeedsIV; - Span seeds = stackalloc uint[maxResults]; - var count = LCRNGReversal.GetSeeds(seeds, bot, top); - var reg = seeds[..count]; + public static bool GetLCRNGMethod1Match(uint pid, ReadOnlySpan 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 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 seeds, PKM pk, uint pid, ReadOnlySpan IVs, out PIDIV pidiv) + private static bool GetBACDMatch(Span seeds, T pk, uint actualPID, ReadOnlySpan 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 seeds, PKM pk, uint top, uint bot, ReadOnlySpan IVs, out PIDIV pidiv) @@ -665,67 +671,8 @@ private static bool GetNonMatch(out PIDIV pidiv) pidiv = PIDIV.None; return false; } - - /// - /// Checks if the PID is a match. - /// - /// ^ - /// Full actual PID - /// Low portion of PID (B) - /// First RNG call - /// PID Type is updated if successful - /// True/False if the PID matches - /// First RNG call is unrolled once if the PID is valid with this correlation + [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; - } - - /// - /// Checks if the PID is a match. - /// - /// ^ - /// Full actual PID - /// Low portion of PID (B) - /// First RNG call - /// PID Type is updated if successful - /// True/False if the PID matches - [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. diff --git a/PKHeX.Core/Legality/RNG/PIDGenerator.cs b/PKHeX.Core/Legality/RNG/PIDGenerator.cs index 6021bbc8c..a190425ea 100644 --- a/PKHeX.Core/Legality/RNG/PIDGenerator.cs +++ b/PKHeX.Core/Legality/RNG/PIDGenerator.cs @@ -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 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 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 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 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 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 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; diff --git a/PKHeX.Core/Legality/RNG/PIDIV.cs b/PKHeX.Core/Legality/RNG/PIDIV.cs index 00b309a7b..748a0365b 100644 --- a/PKHeX.Core/Legality/RNG/PIDIV.cs +++ b/PKHeX.Core/Legality/RNG/PIDIV.cs @@ -13,23 +13,18 @@ namespace PKHeX.Core; internal static readonly PIDIV Pokewalker = new(PIDType.Pokewalker); internal static readonly PIDIV G5MGShiny = new(PIDType.G5MGShiny); - /// The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first) - [field: FieldOffset(0)] - public uint OriginSeed { get; } + /// The RNG seed which immediately generates the PID/IV (starting with PID or IVs, whichever comes first) + [field: FieldOffset(0)] public uint OriginSeed { get; } /// The RNG seed which starts the encounter generation routine. - [field: FieldOffset(4)] - public uint EncounterSeed { get; init; } + [field: FieldOffset(4)] public uint EncounterSeed { get; init; } - /// The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first) - [field: FieldOffset(0)] - public ulong Seed64 { get; } + /// The RNG seed which immediately generates the PID/IV (starting with PID or IVs, whichever comes first) + [field: FieldOffset(0)] public ulong Seed64 { get; } /// Type of PIDIV correlation - [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; } - /// Some PIDIVs may be generated without a single seed, but may follow a traceable pattern. + /// Some PID/IVs may be generated without a single seed, but may follow a traceable pattern. /// Indicates that there is no to refer to. 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 }; } diff --git a/PKHeX.Core/Legality/RNG/PIDType.cs b/PKHeX.Core/Legality/RNG/PIDType.cs index 48cf8b7cf..8d6c5ff7f 100644 --- a/PKHeX.Core/Legality/RNG/PIDType.cs +++ b/PKHeX.Core/Legality/RNG/PIDType.cs @@ -49,53 +49,48 @@ public enum PIDType : byte /// Method_1_Roamer, - /// - /// Event Reversed Order PID restricted to 16bit Origin Seed - /// - /// seed is clamped to 16bits. - BACD_R, + #region BACD Event - /// - /// Event Reversed Order PID without Origin Seed restrictions - /// - /// + #region Internal Matching Tagging + /// Event Reversed Order PID without Origin Seed restrictions + BACD, + /// Event Reversed Order PID without Origin Seed restrictions, anti-shiny (add). + BACD_A, + /// Event Reversed Order PID without Origin Seed restrictions, anti-shiny (xor) + BACD_AX, + /// Event Reversed Order PID without Origin Seed restrictions, shiny + BACD_S, + /// Event Reversed Order PID without Origin Seed restrictions, anti-shiny (add). + #endregion + + #region Egg-Only Internal Matching Tagging + BACD_EA, + /// Event Reversed Order PID without Origin Seed restrictions, anti-shiny (xor) + BACD_EAX, + /// Event Reversed Order PID without Origin Seed restrictions, shiny + BACD_ES, + #endregion + + /// Event Reversed Order PID without Origin Seed restrictions BACD_U, - - /// - /// Event Reversed Order PID restricted to 16bit Origin Seed, anti-shiny. - /// - /// seed is clamped to 16bits. - BACD_R_A, - - /// - /// Event Reversed Order PID without Origin Seed restrictions, anti-shiny. - /// - /// - BACD_U_A, - - /// - /// Event Reversed Order PID restricted to 8bit Origin Seed, shiny - /// - /// seed is clamped to 16bits. - BACD_R_S, - - /// - /// Event Reversed Order PID without Origin Seed restrictions, shiny - /// - /// - BACD_U_S, - - /// - /// Event Reversed Order PID restricted to 16bit Origin Seed, anti-shiny (nyx) - /// - /// seed is clamped to 16bits. - BACD_R_AX, - - /// - /// Event Reversed Order PID without Origin Seed restrictions, anti-shiny (nyx) - /// - /// + /// Event Reversed Order PID without Origin Seed restrictions, anti-shiny (xor) BACD_U_AX, + // BACD_U_A and BACD_U_S were never generated by any event. + + /// Event Reversed Order PID restricted to 16bit Origin Seed + BACD_R, + /// Event Reversed Order PID restricted to 16bit Origin Seed, anti-shiny. + BACD_R_A, + /// Event Reversed Order PID restricted to [0,213] Origin Seed, shiny + BACD_RBCD, + /// Event Reversed Order PID with Origin Seed restrictions, consuming 2 calls to the RNG before the PID is generated. + BACD_T2, + /// Event Reversed Order PID with Origin Seed restrictions, consuming 3 calls to the RNG before the PID is generated. + BACD_T3, + /// Event Reversed Order PID with Origin Seed restrictions, only using the Mystry Mew table. + BACD_M, + + #endregion /// /// Generation 4 Cute Charm PID, which is forced to an 8 bit PID value based on the gender & 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; } diff --git a/PKHeX.Core/Legality/Structures/LegalInfo.cs b/PKHeX.Core/Legality/Structures/LegalInfo.cs index 1777744d8..01dd00e8a 100644 --- a/PKHeX.Core/Legality/Structures/LegalInfo.cs +++ b/PKHeX.Core/Legality/Structures/LegalInfo.cs @@ -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; diff --git a/PKHeX.Core/Legality/Tables/FormInfo.cs b/PKHeX.Core/Legality/Tables/FormInfo.cs index 75fa53831..fe55c7eba 100644 --- a/PKHeX.Core/Legality/Tables/FormInfo.cs +++ b/PKHeX.Core/Legality/Tables/FormInfo.cs @@ -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 /// Entity form /// Current generation format /// True if it can only exist in a battle, false if it can exist outside of battle. - 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; + } + /// /// Reverts the Battle Form to the form it would have outside of Battle. /// @@ -131,7 +145,7 @@ public static bool IsFormChangeable(ushort species, byte oldForm, byte newForm, /// Species that can change between their forms, regardless of origin. /// /// Excludes Zygarde as it has special conditions. Check separately. - private static readonly HashSet FormChange = + private static ReadOnlySpan 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 BattleOnly = GetBattleFormSet(); - - private static HashSet GetBattleFormSet() - { - var reg = BattleForms; - var mega = BattleMegas; - var count = reg.Length + mega.Length + 2; - var hs = new HashSet(count); - foreach (var species in reg) - hs.Add(species); - foreach (var species in mega) - hs.Add(species); - return hs; - } - /// /// Species has a Totem form in Gen7 (S/M & US/UM) that can be captured and owned. /// diff --git a/PKHeX.Core/MysteryGifts/PCD.cs b/PKHeX.Core/MysteryGifts/PCD.cs index cd81a2240..57004ea4f 100644 --- a/PKHeX.Core/MysteryGifts/PCD.cs +++ b/PKHeX.Core/MysteryGifts/PCD.cs @@ -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) diff --git a/PKHeX.Core/MysteryGifts/PGT.cs b/PKHeX.Core/MysteryGifts/PGT.cs index b8e10c210..6e0353c60 100644 --- a/PKHeX.Core/MysteryGifts/PGT.cs +++ b/PKHeX.Core/MysteryGifts/PGT.cs @@ -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() diff --git a/Tests/PKHeX.Core.Tests/PKM/PIDIVTests.cs b/Tests/PKHeX.Core.Tests/PKM/PIDIVTests.cs index 0f21a92e7..afc4d8b5b 100644 --- a/Tests/PKHeX.Core.Tests/PKM/PIDIVTests.cs +++ b/Tests/PKHeX.Core.Tests/PKM/PIDIVTests.cs @@ -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]