2
0
Fork 0
mirror of https://github.com/kwsch/PKHeX synced 2025-02-24 11:27:08 +00:00

Offload gen1/2 event templates to pickles

Thanks @ShadowMario3 for doing the raw data entry for these templates -- see PKHeX.EncounterSlotDumper for the csv -> pkl conversion logic.

Reduces object size by classifying groups of events with specific features, and reduces the explicitness of defining each single encounter.

Hard-code the span of Gen1 VC mew to not need to grab the resource, and add an overload to iterate a single template (skips an array creation).

Co-Authored-By: ShadowMario3 <36941677+ShadowMario3@users.noreply.github.com>
This commit is contained in:
Kurt 2024-02-24 17:53:46 -06:00
parent 8206427d21
commit b435f8c258
15 changed files with 355 additions and 486 deletions

View file

@ -1,38 +1,21 @@
using System;
namespace PKHeX.Core;
internal static class Encounters1GBEra
{
private static readonly IndividualValueSet Yoshira = new(5, 10, 1, 12, 5, 5);
private static readonly string[] YoshiOT = ["YOSHIRA", "YOSHIRB", "YOSHIBA", "YOSHIBB"];
private static readonly string[] TourOT = ["LINKE", "LINKW", "LUIGE", "LUIGW", "LUIGIC", "YOSHIC"];
private static readonly string[] StadiumOT_Int = ["STADIUM", "STADE", "STADIO", "ESTADIO"];
private const string StadiumOT_JPN = "スタジアム";
public static readonly EncounterGift1[] Gifts = GetGifts(Util.GetBinaryResource("event1.pkl"));
internal static readonly EncounterGift1[] Gifts =
[
// Stadium 1 (International)
new(001, 05, GameVersion.Stadium) {Moves = new(033, 045), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Bulbasaur
new(004, 05, GameVersion.Stadium) {Moves = new(010, 043), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Charmander
new(007, 05, GameVersion.Stadium) {Moves = new(033, 045), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Squirtle
new(106, 20, GameVersion.Stadium) {Moves = new(024, 096), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Hitmonlee
new(107, 20, GameVersion.Stadium) {Moves = new(004, 097), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Hitmonchan
new(133, 25, GameVersion.Stadium) {Moves = new(033, 039), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Eevee
new(138, 20, GameVersion.Stadium) {Moves = new(055, 110), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Omanyte
new(140, 20, GameVersion.Stadium) {Moves = new(010, 106), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Kabuto
new(054, 15, GameVersion.Stadium) {Moves = new(133, 010), TID16 = 2000, TrainerNames = StadiumOT_Int, Language = EncounterGBLanguage.International}, // Psyduck (Amnesia)
// Stadium 2 (Japan)
new(001, 05, GameVersion.Stadium) {Moves = new(033, 045), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Bulbasaur
new(004, 05, GameVersion.Stadium) {Moves = new(010, 043), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Charmander
new(007, 05, GameVersion.Stadium) {Moves = new(033, 045), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Squirtle
new(106, 20, GameVersion.Stadium) {Moves = new(024, 096), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Hitmonlee
new(107, 20, GameVersion.Stadium) {Moves = new(004, 097), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Hitmonchan
new(133, 25, GameVersion.Stadium) {Moves = new(033, 039), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Eevee
new(138, 20, GameVersion.Stadium) {Moves = new(055, 110), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Omanyte
new(140, 20, GameVersion.Stadium) {Moves = new(010, 106), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Kabuto
new(054, 15, GameVersion.Stadium) {Moves = new(133, 010), TID16 = 1999, OriginalTrainerName = StadiumOT_JPN}, // Psyduck (Amnesia)
new(151, 5, GameVersion.RB) {IVs = Yoshira, TrainerNames = YoshiOT, Language = EncounterGBLanguage.International }, // Yoshira Mew Events
new(151, 5, GameVersion.RB) {IVs = Yoshira, TrainerNames = TourOT, Language = EncounterGBLanguage.International }, // Pokémon 2000 Stadium Tour Mew
];
private static EncounterGift1[] GetGifts(ReadOnlySpan<byte> bin)
{
const int Size = EncounterGift1.SerializedSize;
var result = new EncounterGift1[bin.Length / Size];
for (int i = 0; i < result.Length; i++)
{
var data = bin[..Size];
result[i] = new EncounterGift1(data);
bin = bin[Size..];
}
return result;
}
}

View file

@ -2,12 +2,5 @@ namespace PKHeX.Core;
internal static class Encounters1VC
{
private static readonly IndividualValueSet Flawless15 = new(15, 15, 15, 15, 15, 15);
internal static readonly EncounterGift1[] Gifts =
[
// Event Mew
new(151, 5, GameVersion.RBY) { IVs = Flawless15, TID16 = 22796, OriginalTrainerName = "GF", Language = EncounterGBLanguage.International },
new(151, 5, GameVersion.RBY) { IVs = Flawless15, TID16 = 22796, OriginalTrainerName = "ゲーフリ" },
];
internal static readonly EncounterGift1 Gift = new([151,5, 1,0,0,0, 0,1]); // GF Mew
}

View file

@ -1,254 +1,21 @@
using static PKHeX.Core.GameVersion;
using static PKHeX.Core.EncounterGBLanguage;
using System;
namespace PKHeX.Core;
internal static class Encounters2GBEra
{
private static readonly string[] PCNYx = ["PCNYa", "PCNYb", "PCNYc", "PCNYd"];
private static readonly string[] Stadium2 = ["Stade", "Stadion", "Stadio", "Estadio"];
public static readonly EncounterGift2[] Gifts = GetGifts(Util.GetBinaryResource("event2.pkl"));
internal static readonly EncounterGift2[] StaticEventsGB =
[
// Stadium 2 Baton Pass Farfetch'd
new(083, 05, C) {Moves = new(226, 14, 97, 163), Location = 127, TID16 = 2000, OriginalTrainerName = "スタジアム"},
new(083, 05, C) {Moves = new(226, 14, 97, 163), Location = 127, TID16 = 2000, OriginalTrainerName = "Stadium", Language = International},
new(083, 05, C) {Moves = new(226, 14, 97, 163), Location = 127, TID16 = 2001, TrainerNames = Stadium2, Language = International},
// Stadium 2 Earthquake Gligar
new(207, 05, C) {Moves = new(89, 68, 17), Location = 127, TID16 = 2000, OriginalTrainerName = "スタジアム"},
new(207, 05, C) {Moves = new(89, 68, 17), Location = 127, TID16 = 2000, OriginalTrainerName = "Stadium", Language = International},
new(207, 05, C) {Moves = new(89, 68, 17), Location = 127, TID16 = 2001, TrainerNames = Stadium2, Language = International},
//New York Pokémon Center Events
// Legendary Beasts (November 22 to 29, 2001)
new(243, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Raikou
new(244, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Entei
new(245, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Suicune
// Legendary Birds (November 30 to December 6, 2001)
new(144, 05, C) {TrainerNames = PCNYx, CurrentLevel = 50, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Articuno
new(145, 05, C) {TrainerNames = PCNYx, CurrentLevel = 50, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Zapdos
new(146, 05, C) {TrainerNames = PCNYx, CurrentLevel = 50, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Moltres
// Christmas Week (December 21 to 27, 2001)
new(225, 05, GS) {TrainerNames = PCNYx, Moves = new(006), EggCycles = 10, Language = International}, // Pay Day Delibird
new(251, 05, C) {TrainerNames = PCNYx, Location = 127, Language = International}, // Celebi
// The Initial Three Set (December 28, 2001, to January 31, 2002)
new(001, 05, GS) {TrainerNames = PCNYx, Moves = new(246), EggCycles = 10, Language = International}, // Bulbasaur Ancientpower
new(004, 05, GS) {TrainerNames = PCNYx, Moves = new(242), EggCycles = 10, Language = International}, // Charmander Crunch
new(007, 05, GS) {TrainerNames = PCNYx, Moves = new(192), EggCycles = 10, Language = International}, // Squirtle Zap Cannon
new(152, 05, GS) {TrainerNames = PCNYx, Moves = new(080), EggCycles = 10, Language = International}, // Chikorita Petal Dance
new(155, 05, GS) {TrainerNames = PCNYx, Moves = new(038), EggCycles = 10, Language = International}, // Cyndaquil Double-Edge
new(158, 05, GS) {TrainerNames = PCNYx, Moves = new(066), EggCycles = 10, Language = International}, // Totodile Submission
// Valentine Week (February 1 to 14, 2002)
new(029, 05, GS) {TrainerNames = PCNYx, Moves = new(142), EggCycles = 10, Language = International}, // Nidoran (F) Lovely Kiss
new(029, 05, GS) {TrainerNames = PCNYx, Moves = new(186), EggCycles = 10, Language = International}, // Nidoran (F) Sweet Kiss
new(032, 05, GS) {TrainerNames = PCNYx, Moves = new(142), EggCycles = 10, Language = International}, // Nidoran (M) Lovely Kiss
new(032, 05, GS) {TrainerNames = PCNYx, Moves = new(186), EggCycles = 10, Language = International}, // Nidoran (M) Sweet Kiss
new(069, 05, GS) {TrainerNames = PCNYx, Moves = new(142), EggCycles = 10, Language = International}, // Bellsprout Lovely Kiss
new(069, 05, GS) {TrainerNames = PCNYx, Moves = new(186), EggCycles = 10, Language = International}, // Bellsprout Sweet Kiss
// Swarm Week (February 22 to March 14, 2002)
new(183, 05, GS) {TrainerNames = PCNYx, Moves = new(056), EggCycles = 10, Language = International}, // Marill Hydro Pump
new(193, 05, GS) {TrainerNames = PCNYx, Moves = new(211), EggCycles = 10, Language = International}, // Yanma Steel Wing
new(206, 05, GS) {TrainerNames = PCNYx, Moves = new(032), EggCycles = 10, Language = International}, // Dunsparce Horn Drill
new(209, 05, GS) {TrainerNames = PCNYx, Moves = new(142), EggCycles = 10, Language = International}, // Snubbull Lovely Kiss
new(211, 05, GS) {TrainerNames = PCNYx, Moves = new(038), EggCycles = 10, Language = International}, // Qwilfish Double-Edge
new(223, 05, GS) {TrainerNames = PCNYx, Moves = new(133), EggCycles = 10, Language = International}, // Remoraid Amnesia
// Shiny RBY Starters (March 15 to 21, 2002)
new(003, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Venusaur
new(006, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Charizard
new(009, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Blastoise
// Babies Week (March 22 to April 11, 2002)
new(172, 05, GS) {TrainerNames = PCNYx, Moves = new(047), EggCycles = 10, Language = International}, // Pichu Sing
new(173, 05, GS) {TrainerNames = PCNYx, Moves = new(129), EggCycles = 10, Language = International}, // Cleffa Swift
new(174, 05, GS) {TrainerNames = PCNYx, Moves = new(102), EggCycles = 10, Language = International}, // Igglybuff Mimic
new(238, 05, GS) {TrainerNames = PCNYx, Moves = new(118), EggCycles = 10, Language = International}, // Smoochum Metronome
new(239, 05, GS) {TrainerNames = PCNYx, Moves = new(228), EggCycles = 10, Language = International}, // Elekid Pursuit
new(240, 05, GS) {TrainerNames = PCNYx, Moves = new(185), EggCycles = 10, Language = International}, // Magby Faint Attack
// Spring Into Spring (April 12 to May 4, 2002)
new(054, 05, GS) {TrainerNames = PCNYx, Moves = new(080), EggCycles = 10, Language = International}, // Psyduck Petal Dance
new(152, 05, GS) {TrainerNames = PCNYx, Moves = new(080), EggCycles = 10, Language = International}, // Chikorita Petal Dance
new(172, 05, GS) {TrainerNames = PCNYx, Moves = new(080), EggCycles = 10, Language = International}, // Pichu Petal Dance
new(173, 05, GS) {TrainerNames = PCNYx, Moves = new(080), EggCycles = 10, Language = International}, // Cleffa Petal Dance
new(174, 05, GS) {TrainerNames = PCNYx, Moves = new(080), EggCycles = 10, Language = International}, // Igglybuff Petal Dance
new(238, 05, GS) {TrainerNames = PCNYx, Moves = new(080), EggCycles = 10, Language = International}, // Smoochum Petal Dance
// Baby Weeks (May 5 to June 7, 2002)
new(194, 05, GS) {Moves = new(187), EggCycles = 10, Language = International}, // Wooper Belly Drum
// Tropical Promotion to Summer Festival 1 (June 8 to 21, 2002)
new(060, 05, GS) {TrainerNames = PCNYx, Moves = new(074), EggCycles = 10, Language = International}, // Poliwag Growth
new(116, 05, GS) {TrainerNames = PCNYx, Moves = new(114), EggCycles = 10, Language = International}, // Horsea Haze
new(118, 05, GS) {TrainerNames = PCNYx, Moves = new(014), EggCycles = 10, Language = International}, // Goldeen Swords Dance
new(129, 05, GS) {TrainerNames = PCNYx, Moves = new(179), EggCycles = 10, Language = International}, // Magikarp Reversal
new(183, 05, GS) {TrainerNames = PCNYx, Moves = new(146), EggCycles = 10, Language = International}, // Marill Dizzy Punch
// Tropical Promotion to Summer Festival 2 (July 12 to August 8, 2002)
new(054, 05, GS) {TrainerNames = PCNYx, Moves = new(161), EggCycles = 10, Language = International}, // Psyduck Tri Attack
new(072, 05, GS) {TrainerNames = PCNYx, Moves = new(109), EggCycles = 10, Language = International}, // Tentacool Confuse Ray
new(131, 05, GS) {TrainerNames = PCNYx, Moves = new(044), EggCycles = 10, Language = International}, // Lapras Bite
new(170, 05, GS) {TrainerNames = PCNYx, Moves = new(113), EggCycles = 10, Language = International}, // Chinchou Light Screen
new(223, 05, GS) {TrainerNames = PCNYx, Moves = new(054), EggCycles = 10, Language = International}, // Remoraid Mist
new(226, 05, GS) {TrainerNames = PCNYx, Moves = new(016), EggCycles = 10, Language = International}, // Mantine Gust
// Safari Week (August 9 to 29, 2002)
new(029, 05, GS) {TrainerNames = PCNYx, Moves = new(236), EggCycles = 10, Language = International}, // Nidoran (F) Moonlight
new(032, 05, GS) {TrainerNames = PCNYx, Moves = new(234), EggCycles = 10, Language = International}, // Nidoran (M) Morning Sun
new(113, 05, GS) {TrainerNames = PCNYx, Moves = new(230), EggCycles = 10, Language = International}, // Chansey Sweet Scent
new(115, 05, GS) {TrainerNames = PCNYx, Moves = new(185), EggCycles = 10, Language = International}, // Kangaskhan Faint Attack
new(128, 05, GS) {TrainerNames = PCNYx, Moves = new(098), EggCycles = 10, Language = International}, // Tauros Quick Attack
new(147, 05, GS) {TrainerNames = PCNYx, Moves = new(056), EggCycles = 10, Language = International}, // Dratini Hydro Pump
// Sky Week (August 30 to September 26, 2002)
new(021, 05, GS) {TrainerNames = PCNYx, Moves = new(049), EggCycles = 10, Language = International}, // Spearow SonicBoom
new(083, 05, GS) {TrainerNames = PCNYx, Moves = new(210), EggCycles = 10, Language = International}, // Farfetch'd Fury Cutter
new(084, 05, GS) {TrainerNames = PCNYx, Moves = new(067), EggCycles = 10, Language = International}, // Doduo Low Kick
new(177, 05, GS) {TrainerNames = PCNYx, Moves = new(219), EggCycles = 10, Language = International}, // Natu Safeguard
new(198, 05, GS) {TrainerNames = PCNYx, Moves = new(251), EggCycles = 10, Language = International}, // Murkrow Beat Up
new(227, 05, GS) {TrainerNames = PCNYx, Moves = new(210), EggCycles = 10, Language = International}, // Skarmory Fury Cutter
// The Kanto Initial Three Pokémon (September 27 to October 3, 2002)
new(150, 05, C) {TrainerNames = PCNYx, CurrentLevel = 70, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Mewtwo
// Power Plant Pokémon (October 4 to October 10, 2002)
new(172, 05, GS) {TrainerNames = PCNYx, Moves = new(146), EggCycles = 10, Language = International}, // Pichu Dizzy Punch
new(081, 05, GS) {TrainerNames = PCNYx, Moves = new(097), EggCycles = 10, Language = International}, // Magnemite Agility
new(239, 05, GS) {TrainerNames = PCNYx, Moves = new(146), EggCycles = 10, Language = International}, // Elekid Dizzy Punch
new(100, 05, GS) {TrainerNames = PCNYx, Moves = new(097), EggCycles = 10, Language = International}, // Voltorb Agility
// Scary Face Pokémon (October 25 to October 31, 2002)
new(173, 05, GS) {TrainerNames = PCNYx, Moves = new(184), EggCycles = 10, Language = International}, // Cleffa Scary Face
new(174, 05, GS) {TrainerNames = PCNYx, Moves = new(184), EggCycles = 10, Language = International}, // Igglybuff Scary Face
new(183, 05, GS) {TrainerNames = PCNYx, Moves = new(184), EggCycles = 10, Language = International}, // Marill Scary Face
new(172, 05, GS) {TrainerNames = PCNYx, Moves = new(184), EggCycles = 10, Language = International}, // Pichu Scary Face
new(194, 05, GS) {TrainerNames = PCNYx, Moves = new(184), EggCycles = 10, Language = International}, // Wooper Scary Face
// Silver Cave (November 1 to November 7, 2002)
new(114, 05, GS) {TrainerNames = PCNYx, Moves = new(235), EggCycles = 10, Language = International}, // Tangela Synthesis
new(077, 05, GS) {TrainerNames = PCNYx, Moves = new(067), EggCycles = 10, Language = International}, // Ponyta Low Kick
new(200, 05, GS) {TrainerNames = PCNYx, Moves = new(095), EggCycles = 10, Language = International}, // Misdreavus Hypnosis
new(246, 05, GS) {TrainerNames = PCNYx, Moves = new(099), EggCycles = 10, Language = International}, // Larvitar Rage
// Union Cave Pokémon (November 8 to 14, 2002)
new(120, 05, GS) {TrainerNames = PCNYx, Moves = new(239), EggCycles = 10, Language = International}, // Staryu Twister
new(098, 05, GS) {TrainerNames = PCNYx, Moves = new(232), EggCycles = 10, Language = International}, // Krabby Metal Claw
new(095, 05, GS) {TrainerNames = PCNYx, Moves = new(159), EggCycles = 10, Language = International}, // Onix Sharpen
new(131, 05, GS) {TrainerNames = PCNYx, Moves = new(248), EggCycles = 10, Language = International}, // Lapras Future Sight
// Johto Legendary (November 15 to 21, 2002)
new(250, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Ho-Oh
new(249, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Lugia
// Celebi Present SP (November 22 to 28, 2002)
new(151, 05, C) {TrainerNames = PCNYx, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Mew
// Psychic Type Pokémon (November 29 to December 5, 2002)
new(063, 05, GS) {TrainerNames = PCNYx, Moves = new(193), EggCycles = 10, Language = International}, // Abra Foresight
new(096, 05, GS) {TrainerNames = PCNYx, Moves = new(133), EggCycles = 10, Language = International}, // Drowzee Amnesia
new(102, 05, GS) {TrainerNames = PCNYx, Moves = new(230), EggCycles = 10, Language = International}, // Exeggcute Sweet Scent
new(122, 05, GS) {TrainerNames = PCNYx, Moves = new(170), EggCycles = 10, Language = International}, // Mr. Mime Mind Reader
// The Johto Initial Three Pokémon (December 6 to 12, 2002)
new(154, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Meganium
new(157, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Typhlosion
new(160, 05, C) {TrainerNames = PCNYx, CurrentLevel = 40, Shiny = Shiny.Always, Location = 127, Language = International}, // Shiny Feraligatr
// Rock Tunnel Pokémon (December 13 to December 19, 2002)
new(074, 05, GS) {TrainerNames = PCNYx, Moves = new(229), EggCycles = 10, Language = International}, // Geodude Rapid Spin
new(041, 05, GS) {TrainerNames = PCNYx, Moves = new(175), EggCycles = 10, Language = International}, // Zubat Flail
new(066, 05, GS) {TrainerNames = PCNYx, Moves = new(037), EggCycles = 10, Language = International}, // Machop Thrash
new(104, 05, GS) {TrainerNames = PCNYx, Moves = new(031), EggCycles = 10, Language = International}, // Cubone Fury Attack
// Ice Type Pokémon (December 20 to 26, 2002)
new(225, 05, GS) {TrainerNames = PCNYx, Moves = new(191), EggCycles = 10, Language = International}, // Delibird Spikes
new(086, 05, GS) {TrainerNames = PCNYx, Moves = new(175), EggCycles = 10, Language = International}, // Seel Flail
new(220, 05, GS) {TrainerNames = PCNYx, Moves = new(018), EggCycles = 10, Language = International}, // Swinub Whirlwind
// Pokémon that Appear at Night only (December 27, 2002, to January 2, 2003)
new(163, 05, GS) {TrainerNames = PCNYx, Moves = new(101), EggCycles = 10, Language = International}, // Hoothoot Night Shade
new(215, 05, GS) {TrainerNames = PCNYx, Moves = new(236), EggCycles = 10, Language = International}, // Sneasel Moonlight
// Grass Type ( January 3 to 9, 2003)
new(191, 05, GS) {TrainerNames = PCNYx, Moves = new(150), EggCycles = 10, Language = International}, // Sunkern Splash
new(046, 05, GS) {TrainerNames = PCNYx, Moves = new(235), EggCycles = 10, Language = International}, // Paras Synthesis
new(187, 05, GS) {TrainerNames = PCNYx, Moves = new(097), EggCycles = 10, Language = International}, // Hoppip Agility
new(043, 05, GS) {TrainerNames = PCNYx, Moves = new(073), EggCycles = 10, Language = International}, // Oddish Leech Seed
// Normal Pokémon (January 10 to 16, 2003)
new(161, 05, GS) {TrainerNames = PCNYx, Moves = new(146), EggCycles = 10, Language = International}, // Sentret Dizzy Punch
new(234, 05, GS) {TrainerNames = PCNYx, Moves = new(219), EggCycles = 10, Language = International}, // Stantler Safeguard
new(241, 05, GS) {TrainerNames = PCNYx, Moves = new(025), EggCycles = 10, Language = International}, // Miltank Mega Kick
new(190, 05, GS) {TrainerNames = PCNYx, Moves = new(102), EggCycles = 10, Language = International}, // Aipom Mimic
new(108, 05, GS) {TrainerNames = PCNYx, Moves = new(003), EggCycles = 10, Language = International}, // Lickitung DoubleSlap
new(143, 05, GS) {TrainerNames = PCNYx, Moves = new(150), EggCycles = 10, Language = International}, // Snorlax Splash
// Mt. Mortar (January 24 to 30, 2003)
new(066, 05, GS) {TrainerNames = PCNYx, Moves = new(206), EggCycles = 10, Language = International}, // Machop False Swipe
new(129, 05, GS) {TrainerNames = PCNYx, Moves = new(145), EggCycles = 10, Language = International}, // Magikarp Bubble
new(236, 05, GS) {TrainerNames = PCNYx, Moves = new(099), EggCycles = 10, Language = International}, // Tyrogue Rage
// Dark Cave Pokémon (January 31 to February 6, 2003)
new(206, 05, GS) {TrainerNames = PCNYx, Moves = new(031), EggCycles = 10, Language = International}, // Dunsparce Fury Attack
new(202, 05, GS) {TrainerNames = PCNYx, Moves = new(102), EggCycles = 10, Language = International}, // Wobbuffet Mimic
new(231, 05, GS) {TrainerNames = PCNYx, Moves = new(071), EggCycles = 10, Language = International}, // Phanpy Absorb
new(216, 05, GS) {TrainerNames = PCNYx, Moves = new(230), EggCycles = 10, Language = International}, // Teddiursa Sweet Scent
// Valentine's Day Special (February 7 to 13, 2003)
new(060, 05, GS) {TrainerNames = PCNYx, Moves = new(186), EggCycles = 10, Language = International}, // Poliwag Sweet Kiss
new(060, 05, GS) {TrainerNames = PCNYx, Moves = new(142), EggCycles = 10, Language = International}, // Poliwag Lovely Kiss
new(143, 05, GS) {TrainerNames = PCNYx, Moves = new(186), EggCycles = 10, Language = International}, // Snorlax Sweet Kiss
new(143, 05, GS) {TrainerNames = PCNYx, Moves = new(142), EggCycles = 10, Language = International}, // Snorlax Lovely Kiss
// Rare Pokémon (February 21 to 27, 2003)
new(140, 05, GS) {TrainerNames = PCNYx, Moves = new(088), EggCycles = 10, Language = International}, // Kabuto Rock Throw
new(138, 05, GS) {TrainerNames = PCNYx, Moves = new(088), EggCycles = 10, Language = International}, // Omanyte Rock Throw
new(142, 05, GS) {TrainerNames = PCNYx, Moves = new(088), EggCycles = 10, Language = International}, // Aerodactyl Rock Throw
new(137, 05, GS) {TrainerNames = PCNYx, Moves = new(112), EggCycles = 10, Language = International}, // Porygon Barrier
new(133, 05, GS) {TrainerNames = PCNYx, Moves = new(074), EggCycles = 10, Language = International}, // Eevee Growth
new(185, 05, GS) {TrainerNames = PCNYx, Moves = new(164), EggCycles = 10, Language = International}, // Sudowoodo Substitute
// Bug Type Pokémon (February 28 to March 6, 2003)
new(123, 05, GS) {TrainerNames = PCNYx, Moves = new(049), EggCycles = 10, Language = International}, // Scyther SonicBoom
new(214, 05, GS) {TrainerNames = PCNYx, Moves = new(069), EggCycles = 10, Language = International}, // Heracross Seismic Toss
new(127, 05, GS) {TrainerNames = PCNYx, Moves = new(088), EggCycles = 10, Language = International}, // Pinsir Rock Throw
new(165, 05, GS) {TrainerNames = PCNYx, Moves = new(112), EggCycles = 10, Language = International}, // Ledyba Barrier
new(167, 05, GS) {TrainerNames = PCNYx, Moves = new(074), EggCycles = 10, Language = International}, // Spinarak Growth
new(193, 05, GS) {TrainerNames = PCNYx, Moves = new(186), EggCycles = 10, Language = International}, // Yanma Sweet Kiss
new(204, 05, GS) {TrainerNames = PCNYx, Moves = new(164), EggCycles = 10, Language = International}, // Pineco Substitute
// Japanese Only (all below)
new(251, 30, GSC) {Location = 014}, // Celebi @ Ilex Forest (GBC)
// Gen2 Events
// Egg Cycles Subject to Change. OTs for Eggs are unknown.
// Pokémon Center Mystery Egg #1 (December 15, 2001, to January 14, 2002)
new(152, 05, GSC) {Moves = new(080), EggCycles = 10}, // Chikorita Petal Dance
new(172, 05, GSC) {Moves = new(047), EggCycles = 10}, // Pichu Sing
new(173, 05, GSC) {Moves = new(129), EggCycles = 10}, // Cleffa Swift
new(194, 05, GSC) {Moves = new(187), EggCycles = 10}, // Wooper Belly Drum
new(231, 05, GSC) {Moves = new(227), EggCycles = 10}, // Phanpy Encore
new(238, 05, GSC) {Moves = new(118), EggCycles = 10}, // Smoochum Metronome
// Pokémon Center Mystery Egg #2 (March 16 to April 7, 2002)
new(054, 05, GSC) {Moves = new(080), EggCycles = 10}, // Psyduck Petal Dance
new(152, 05, GSC) {Moves = new(080), EggCycles = 10}, // Chikorita Petal Dance
new(172, 05, GSC) {Moves = new(080), EggCycles = 10}, // Pichu Petal Dance
new(173, 05, GSC) {Moves = new(080), EggCycles = 10}, // Cleffa Petal Dance
new(174, 05, GSC) {Moves = new(080), EggCycles = 10}, // Igglybuff Petal Dance
new(238, 05, GSC) {Moves = new(080), EggCycles = 10}, // Smoochum Petal Dance
// Pokémon Center Mystery Egg #3 (April 27 to May 12, 2002)
new(001, 05, GSC) {Moves = new(246), EggCycles = 10}, // Bulbasaur Ancientpower
new(004, 05, GSC) {Moves = new(242), EggCycles = 10}, // Charmander Crunch
new(158, 05, GSC) {Moves = new(066), EggCycles = 10}, // Totodile Submission
new(163, 05, GSC) {Moves = new(101), EggCycles = 10}, // Hoot-Hoot Night Shade
];
private static EncounterGift2[] GetGifts(ReadOnlySpan<byte> bin)
{
const int Size = EncounterGift2.SerializedSize;
var result = new EncounterGift2[bin.Length / Size];
for (int i = 0; i < result.Length; i++)
{
var data = bin[..Size];
result[i] = new EncounterGift2(data);
bin = bin[Size..];
}
return result;
}
}

View file

@ -157,7 +157,7 @@ public record struct EncounterPossible1(EvoCriteria[] Chain, EncounterTypeGroup
{ State = YieldState.EventVC; goto case YieldState.EventVC; }
State = YieldState.EventGB; goto case YieldState.EventGB;
case YieldState.EventVC:
if (TryGetNext(Encounters1VC.Gifts))
if (TryGetNext(Encounters1VC.Gift))
return true;
break;
case YieldState.EventGB:
@ -196,6 +196,19 @@ public record struct EncounterPossible1(EvoCriteria[] Chain, EncounterTypeGroup
return false;
}
private bool TryGetNext<T>(T enc) where T : class, IEncounterable, IEncounterMatch
{
if (Index++ != 0)
return false;
foreach (var evo in Chain)
{
if (evo.Species != enc.Species)
continue;
return SetCurrent(enc);
}
return false;
}
private bool TryGetNext<T>(T[] db) where T : class, IEncounterable, IEncounterMatch
{
for (; Index < db.Length;)

View file

@ -169,7 +169,7 @@ public record struct EncounterPossible2(EvoCriteria[] Chain, EncounterTypeGroup
return SetCurrent(Encounters2.CelebiVC);
break;
case YieldState.EventGB:
if (TryGetNext(Encounters2GBEra.StaticEventsGB))
if (TryGetNext(Encounters2GBEra.Gifts))
return true;
break;
}

View file

@ -124,7 +124,7 @@ public record struct EncounterEnumerator1(PKM Entity, EvoCriteria[] Chain) : IEn
{ State = YieldState.EventVC; goto case YieldState.EventVC; }
State = YieldState.EventGB; goto case YieldState.EventGB;
case YieldState.EventVC:
if (TryGetNext(Encounters1VC.Gifts))
if (TryGetNext(Encounters1VC.Gift))
return true;
goto case YieldState.Fallback;
case YieldState.EventGB:
@ -209,6 +209,29 @@ public record struct EncounterEnumerator1(PKM Entity, EvoCriteria[] Chain) : IEn
return false;
}
private bool TryGetNext<T>(T enc) where T : class, IEncounterable, IEncounterMatch
{
if (Index++ != 0)
return false;
foreach (var evo in Chain)
{
if (evo.Species != enc.Species)
continue;
if (!enc.IsMatchExact(Entity, evo))
break;
var rating = enc.GetMatchRating(Entity);
if (rating == EncounterMatchRating.Match)
return SetCurrent(enc);
if (rating < Rating)
{
Deferred = enc;
Rating = rating;
}
break;
}
return false;
}
private bool SetCurrent<T>(T enc, EncounterMatchRating rating = EncounterMatchRating.Match) where T : IEncounterable
{
Current = new MatchedEncounter<IEncounterable>(enc, rating);

View file

@ -171,7 +171,7 @@ public record struct EncounterEnumerator2 : IEnumerator<MatchedEncounter<IEncoun
return SetCurrent(Encounters2.CelebiVC);
goto case YieldState.Fallback;
case YieldState.EventGB:
if (TryGetNext(Encounters2GBEra.StaticEventsGB))
if (TryGetNext(Encounters2GBEra.Gifts))
return true;
State = YieldState.Fallback; goto case YieldState.Fallback;

View file

@ -1,16 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Generations 1 &amp; 2 cannot communicate between Japanese &amp; International versions.
/// </summary>
public enum EncounterGBLanguage
{
/// <summary> Can only be obtained in Japanese games. </summary>
Japanese,
/// <summary> Can only be obtained in International (not Japanese) games. </summary>
International,
/// <summary> Can be obtained in any localization. </summary>
Any,
}

View file

@ -1,13 +1,17 @@
using System;
using static PKHeX.Core.EncounterGift1.TrainerType;
using static PKHeX.Core.LanguageID;
namespace PKHeX.Core;
/// <summary>
/// Event data for Generation 1
/// </summary>
public sealed record EncounterGift1(ushort Species, byte Level, GameVersion Version = GameVersion.RB)
: IEncounterable, IEncounterMatch, IEncounterConvertible<PK1>, IFixedGBLanguage, IMoveset, IFixedIVSet
public sealed record EncounterGift1 : IEncounterable, IEncounterMatch, IEncounterConvertible<PK1>,
IMoveset, IFixedIVSet
{
public const int SerializedSize = 8;
public byte Generation => 1;
public EntityContext Context => EntityContext.Gen1;
public bool EggEncounter => false;
@ -16,113 +20,153 @@ public sealed record EncounterGift1(ushort Species, byte Level, GameVersion Vers
public AbilityPermission Ability => AbilityPermission.OnlyHidden;
public bool IsShiny => false;
public ushort Location => 0;
public const ushort UnspecifiedID = 0;
public Shiny Shiny { get; init; } = Shiny.Random;
public byte Form => 0;
public Shiny Shiny => Shiny.Random;
public EncounterGBLanguage Language { get; init; } = EncounterGBLanguage.Japanese;
/// <summary> Trainer name for the event. </summary>
public string OriginalTrainerName { get; init; } = string.Empty;
public ReadOnlyMemory<string> TrainerNames { get; init; }
/// <summary> Trainer ID for the event. </summary>
public ushort TID16 { get; init; } = UnspecifiedID;
public IndividualValueSet IVs { get; init; }
public Moveset Moves { get; init; }
public Moveset Moves { get; }
public IndividualValueSet IVs { get; }
public ushort Species { get; }
public byte Level { get; }
public GameVersion Version { get; }
public LanguageRestriction Language { get; }
public TrainerType Trainer { get; }
public string Name => "Event Gift";
public string LongName => Name;
public byte LevelMin => Level;
public byte LevelMax => Level;
public enum LanguageRestriction : byte
{
Any = 0,
Japanese = 1,
International = 2,
}
public enum TrainerType : byte
{
Recipient,
VirtualConsoleMew = 1,
Stadium = 2,
EuropeTour = 3,
}
private const ushort TrainerIDStadiumJPN = 1999;
private const ushort TrainerIDStadiumINT = 2000;
private const ushort TrainerIDVirtualConsoleMew = 2_27_96; // Red/Green (Japan) release date!
private const string StadiumENG = "STADIUM";
private const string StadiumJPN = "スタジアム";
private const string StadiumFRE = "STADE";
private const string StadiumITA = "STADIO";
private const string StadiumSPA = "ESTADIO";
private const string VirtualConsoleMewINT = "GF";
private const string VirtualConsoleMewJPN = "ゲーフリ";
private const string FirstTourOT = "YOSHIRA";
private static bool IsTourOT(ReadOnlySpan<char> str) => str switch
{
"YOSHIRA" => true,
"YOSHIRB" => true,
"YOSHIBA" => true,
"YOSHIBB" => true,
"LINKE" => true,
"LINKW" => true,
"LUIGE" => true,
"LUIGW" => true,
"LUIGIC" => true,
"YOSHIC" => true,
_ => false,
};
public EncounterGift1(ReadOnlySpan<byte> data)
{
Species = data[0];
Level = data[1];
Moves = new Moveset(data[2], data[3], data[4], data[5]);
Language = (LanguageRestriction)data[6];
Trainer = (TrainerType)data[7];
if (Trainer is EuropeTour)
IVs = new(5, 10, 1, 12, 5, 5);
else if (Trainer is VirtualConsoleMew)
IVs = new(15, 15, 15, 15, 15, 15);
Version = Trainer == Stadium ? GameVersion.Stadium : GameVersion.RB;
}
#region Generating
PKM IEncounterConvertible.ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) => ConvertToPKM(tr, criteria);
PKM IEncounterConvertible.ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr);
public PK1 ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr, EncounterCriteria.Unrestricted);
public PK1 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
{
var lang = GetTemplateLanguage(tr);
var isJapanese = lang == (int)LanguageID.Japanese;
var pi = EncounterUtil.GetPersonal1(Version, Species);
var lang = GetLanguage((LanguageID)tr.Language);
var isJapanese = lang == Japanese;
var pi = PersonalTable.RB[Species];
var rand = Util.Rand;
var pk = new PK1(isJapanese)
{
Species = Species,
CurrentLevel = LevelMin,
CatchRate = GetInitialCatchRate(pi),
DV16 = IVs.IsSpecified ? EncounterUtil.GetDV16(IVs) : EncounterUtil.GetRandomDVs(Util.Rand),
OriginalTrainerName = EncounterUtil.GetTrainerName(tr, lang),
TID16 = tr.TID16,
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, (int)lang, Generation),
Type1 = pi.Type1,
Type2 = pi.Type2,
DV16 = IVs.IsSpecified ? EncounterUtil.GetDV16(IVs) : EncounterUtil.GetRandomDVs(rand),
CatchRate = Trainer switch
{
Stadium => 168, // be nice and give a Gorgeous Box
_ => pi.CatchRate,
},
TID16 = Trainer switch
{
Recipient => tr.TID16,
Stadium => lang == Japanese ? TrainerIDStadiumJPN : TrainerIDStadiumINT,
VirtualConsoleMew => TrainerIDVirtualConsoleMew,
_ => (ushort)rand.Next(10, 200),
},
OriginalTrainerName = Trainer switch
{
Recipient => EncounterUtil.GetTrainerName(tr, (int)lang),
Stadium => lang switch
{
Japanese => StadiumJPN,
English => StadiumENG,
French => StadiumFRE,
Italian => StadiumITA,
German => StadiumENG, // Same as English
Spanish => StadiumSPA,
_ => StadiumENG, // shouldn't hit here
},
EuropeTour => FirstTourOT, // YOSHIRA
_ => string.Empty,
},
};
if (TID16 != UnspecifiedID)
pk.TID16 = TID16;
if (OriginalTrainerName.Length != 0)
pk.OriginalTrainerName = OriginalTrainerName;
else if (TrainerNames.Length != 0)
pk.OriginalTrainerName = TrainerNames.Span[Util.Rand.Next(TrainerNames.Length)];
if (Version == GameVersion.Stadium)
{
// Amnesia Psyduck has different catch rates depending on language
if (Species == (int)Core.Species.Psyduck)
pk.CatchRate = pk.Japanese ? (byte)167 : (byte)168;
else
pk.CatchRate = Util.Rand.Next(2) == 0 ? (byte)167 : (byte)168;
}
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.SetMoves(Moves);
pk.ResetPartyStats();
return pk;
}
private int GetTemplateLanguage(ITrainerInfo tr)
private LanguageID GetLanguage(LanguageID request)
{
// Japanese events must be Japanese
if (Language == EncounterGBLanguage.Japanese)
return 1;
if (Language == LanguageRestriction.Japanese)
return Japanese;
// International events must be non-Japanese
var lang = (int)Core.Language.GetSafeLanguage(Generation, (LanguageID)tr.Language, Version);
if (lang == 1 && Language == EncounterGBLanguage.International)
return 2;
return lang;
if (request is not (English or French or Italian or German or Spanish))
return English;
return request;
}
#endregion
private byte GetInitialCatchRate(PersonalInfo1 pi)
{
if (Version == GameVersion.Stadium)
{
// Amnesia Psyduck has different catch rates depending on language
if (Species == (int)Core.Species.Psyduck)
return Language == EncounterGBLanguage.Japanese ? (byte)167 : (byte)168;
}
// Encounters can have different Catch Rates (RBG vs Y)
return pi.CatchRate;
}
#region Matching
public bool IsMatchExact(PKM pk, EvoCriteria evo)
{
if (Language != EncounterGBLanguage.Any && pk.Japanese != (Language == EncounterGBLanguage.Japanese))
if (Language != LanguageRestriction.Any && pk.Japanese != (Language == LanguageRestriction.Japanese))
return false;
if (!IsMatchEggLocation(pk))
return false;
@ -130,9 +174,6 @@ public sealed record EncounterGift1(ushort Species, byte Level, GameVersion Vers
return false;
if (Level > evo.LevelMax)
return false;
// Encounters with this version have to originate from the Japanese Blue game.
if (Version == GameVersion.BU && !pk.Japanese)
return false;
if (Form != evo.Form && !FormInfo.IsFormChangeable(Species, Form, pk.Form, Context, pk.Context))
return false;
if (IVs.IsSpecified)
@ -151,24 +192,41 @@ public sealed record EncounterGift1(ushort Species, byte Level, GameVersion Vers
// EC/PID check doesn't exist for these, so check Shiny state here.
if (!IsShinyValid(pk))
return false;
if (TID16 != UnspecifiedID && pk.TID16 != TID16)
if (!IsTrainerIDValid(pk))
return false;
if (!IsTrainerNameValid(pk))
return false;
if (OriginalTrainerName.Length != 0)
{
if (pk.OriginalTrainerName != OriginalTrainerName)
return false;
}
else if (TrainerNames.Length != 0)
{
if (!TrainerNames.Span.Contains(pk.OriginalTrainerName))
return false;
}
return true;
}
private bool IsTrainerNameValid(PKM pk) => Trainer switch
{
Recipient => true,
VirtualConsoleMew => pk.OriginalTrainerName == (pk.Language == 1 ? VirtualConsoleMewJPN : VirtualConsoleMewINT),
Stadium => pk.Language switch
{
(int)Japanese => pk.OriginalTrainerName == StadiumJPN,
_ => pk.OriginalTrainerName switch
{
StadiumENG => true,
StadiumFRE => true,
StadiumITA => true,
StadiumSPA => true,
_ => false,
},
},
EuropeTour => IsTourOT(pk.OriginalTrainerName),
_ => true,
};
private bool IsTrainerIDValid(ITrainerID16 pk) => Trainer switch
{
Recipient => true,
VirtualConsoleMew => pk.TID16 == TrainerIDVirtualConsoleMew,
Stadium => pk.TID16 == (Language == LanguageRestriction.Japanese ? TrainerIDStadiumJPN : TrainerIDStadiumINT),
_ => true,
};
private bool IsShinyValid(PKM pk) => Shiny switch
{
Shiny.Never => !pk.IsShiny,
@ -213,15 +271,10 @@ public sealed record EncounterGift1(ushort Species, byte Level, GameVersion Vers
return true;
if (Version == GameVersion.Stadium)
{
// Amnesia Psyduck has different catch rates depending on language
if (Species == (int)Core.Species.Psyduck)
return rate == (Language == EncounterGBLanguage.Japanese ? 167 : 168);
return rate is 167 or 168;
}
// Encounters can have different Catch Rates (RBG vs Y)
return GBRestrictions.RateMatchesEncounter(Species, Version, rate);
var pi = PersonalTable.RB[Species];
return pi.CatchRate == rate;
}
#endregion

View file

@ -1,12 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Exposes info on language restriction for Gen1/2.
/// </summary>
public interface IFixedGBLanguage
{
/// <summary>
/// Language restriction for the encounter template.
/// </summary>
EncounterGBLanguage Language { get; }
}

View file

@ -1,50 +1,88 @@
using System;
using static PKHeX.Core.LanguageID;
using static PKHeX.Core.EncounterGift2.TrainerType;
namespace PKHeX.Core;
/// <summary>
/// Event data for Generation 2
/// </summary>
public sealed record EncounterGift2(ushort Species, byte Level, GameVersion Version = GameVersion.GS)
: IEncounterable, IEncounterMatch, IEncounterConvertible<PK2>, IFixedGBLanguage, IHatchCycle, IMoveset, IFixedIVSet
public sealed record EncounterGift2
: IEncounterable, IEncounterMatch, IEncounterConvertible<PK2>, IHatchCycle, IMoveset, IFixedIVSet
{
public const int SerializedSize = 12;
public byte Generation => 2;
public EntityContext Context => EntityContext.Gen2;
public byte Form => 0;
public Ball FixedBall => Ball.Poke;
ushort ILocation.Location => Location;
public ushort EggLocation => 0;
public bool IsShiny => Shiny == Shiny.Always;
public AbilityPermission Ability => AbilityPermission.OnlyHidden;
public Shiny Shiny { get; init; } = Shiny.Random;
public byte Location { get; init; }
public IndividualValueSet IVs { get; init; }
public Moveset Moves { get; init; }
public bool EggEncounter => EggCycles != 0;
public Moveset Moves { get; }
public IndividualValueSet IVs => default; // future?
public ushort Species { get; }
public byte Level { get; }
public GameVersion Version { get; }
public TrainerType Trainer { get; }
public byte CurrentLevel { get; }
public byte EggCycles { get; }
public byte Location { get; }
public Shiny Shiny { get; }
public LanguageRestriction Language { get; }
public string Name => "GB Era Event Gift";
public string LongName => Name;
public byte LevelMin => Level;
public byte LevelMax => Level;
public EncounterGBLanguage Language { get; init; } = EncounterGBLanguage.Japanese;
/// <summary> Trainer name for the event. </summary>
public string OriginalTrainerName { get; init; } = string.Empty;
public enum LanguageRestriction : byte
{
International = 0,
Japanese = 1,
English = 2,
InternationalNotEnglish = 3,
}
public ReadOnlyMemory<string> TrainerNames { get; init; }
public enum TrainerType : byte
{
Recipient,
GiftStadiumJPN = 1,
GiftStadiumENG = 2,
GiftStadiumINT = 3,
PokemonCenterNewYork = 4,
}
private const ushort UnspecifiedID = 0;
private const ushort TrainerIDStadiumJPN = 2000;
private const ushort TrainerIDStadiumENG = 2000;
private const ushort TrainerIDStadiumINT = 2001;
private const string StadiumJPN = "スタジアム";
private const string StadiumENG = "Stadium";
private const string StadiumFRE = "Stade";
private const string StadiumGER = "Stadion";
private const string StadiumITA = "Stadio";
private const string StadiumSPA = "Estadio";
private const string FirstPCNY = "PCNYa";
/// <summary> Trainer ID for the event. </summary>
public ushort TID16 { get; init; } = UnspecifiedID;
public static bool IsTrainerPCNY(ReadOnlySpan<char> str) => str is "PCNYa" or "PCNYb" or "PCNYc" or "PCNYd";
public bool IsGift => TID16 != UnspecifiedID;
public EncounterGift2(ReadOnlySpan<byte> data)
{
Species = data[0];
Level = data[1];
Moves = new(data[2], data[3], data[4], data[5]);
Location = data[6];
CurrentLevel = data[7];
Shiny = data[8] == 0 ? Shiny.Random : Shiny.Always;
EggCycles = data[9] == 1 ? (byte)10 : (byte)0;
Language = (LanguageRestriction)data[10];
Trainer = (TrainerType)data[11];
public byte CurrentLevel { get; init; }
public byte EggCycles { get; init; }
Version = Location != 0 ? GameVersion.C : GameVersion.GS;
}
#region Generating
@ -54,29 +92,47 @@ public sealed record EncounterGift2(ushort Species, byte Level, GameVersion Vers
public PK2 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
{
var version = this.GetCompatibleVersion(tr.Version);
int lang = GetTemplateLanguage(tr);
var lang = GetLanguage((LanguageID)tr.Language);
var pi = PersonalTable.C[Species];
var pk = new PK2
{
Species = Species,
CurrentLevel = CurrentLevel == 0 ? LevelMin : CurrentLevel,
TID16 = TID16 != UnspecifiedID ? TID16 : tr.TID16,
OriginalTrainerName = GetInitialOT(tr),
OriginalTrainerFriendship = pi.BaseFriendship,
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, (int)lang, Generation),
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
TID16 = Trainer switch
{
Recipient => tr.TID16,
GiftStadiumJPN => TrainerIDStadiumJPN,
GiftStadiumENG => TrainerIDStadiumENG,
GiftStadiumINT => TrainerIDStadiumINT,
_ => (ushort)Util.Rand.Next(10, 200),
},
OriginalTrainerName = Trainer switch
{
Recipient => EncounterUtil.GetTrainerName(tr, (int)lang),
GiftStadiumJPN => StadiumJPN,
GiftStadiumENG => StadiumENG,
GiftStadiumINT => lang switch
{
French => StadiumFRE,
Italian => StadiumITA,
German => StadiumGER,
Spanish => StadiumSPA,
_ => StadiumENG, // shouldn't hit here
},
PokemonCenterNewYork => FirstPCNY,
_ => EncounterUtil.GetTrainerName(tr, 1),
},
};
if (EggEncounter)
{
// Fake as hatched on G/S.
}
else if (Version == GameVersion.C || (Version == GameVersion.GSC && tr.Version == GameVersion.C))
else
{
if (!IsGift)
pk.OriginalTrainerGender = tr.Gender;
pk.MetLevel = LevelMin;
pk.MetLocation = Location;
pk.MetTimeOfDay = EncounterTime.Any.RandomValidTime();
@ -84,42 +140,28 @@ public sealed record EncounterGift2(ushort Species, byte Level, GameVersion Vers
if (Shiny == Shiny.Always)
pk.SetShiny();
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
pk.SetMoves(Moves);
if (IVs.IsSpecified)
criteria.SetRandomIVs(pk, IVs);
else
criteria.SetRandomIVs(pk);
pk.ResetPartyStats();
return pk;
}
private int GetTemplateLanguage(ITrainerInfo tr)
private LanguageID GetLanguage(LanguageID request)
{
// Japanese events must be Japanese
if (Language == EncounterGBLanguage.Japanese)
return 1;
if (Language == LanguageRestriction.Japanese)
return Japanese;
if (Language == LanguageRestriction.English)
return English;
// International events must be non-Japanese
var lang = (int)Core.Language.GetSafeLanguage(Generation, (LanguageID)tr.Language, Version);
if (lang == 1 && Language == EncounterGBLanguage.International)
return 2;
return lang;
}
private string GetInitialOT(ITrainerInfo tr)
{
if (OriginalTrainerName.Length != 0)
return OriginalTrainerName;
if (TrainerNames.Length != 0)
return TrainerNames.Span[Util.Rand.Next(TrainerNames.Length)];
return tr.OT;
if (request is not (English or French or Italian or German or Spanish))
request = English;
if (request == English && Language == LanguageRestriction.InternationalNotEnglish)
return French;
return request;
}
#endregion
@ -152,7 +194,7 @@ public sealed record EncounterGift2(ushort Species, byte Level, GameVersion Vers
if (Form != evo.Form && !FormInfo.IsFormChangeable(Species, Form, pk.Form, Context, pk.Context))
return false;
if (Language != EncounterGBLanguage.Any && pk.Japanese != (Language == EncounterGBLanguage.Japanese))
if (!IsLanguageValid(pk.Language))
return false;
if (CurrentLevel != 0 && CurrentLevel > pk.CurrentLevel)
@ -166,23 +208,46 @@ public sealed record EncounterGift2(ushort Species, byte Level, GameVersion Vers
return true;
// Check OT Details
if (TID16 != UnspecifiedID && pk.TID16 != TID16)
if (!IsTrainerIDValid(pk))
return false;
if (!IsTrainerNameValid(pk))
return false;
if (OriginalTrainerName.Length != 0)
{
if (pk.OriginalTrainerName != OriginalTrainerName)
return false;
}
else if (TrainerNames.Length != 0)
{
if (!TrainerNames.Span.Contains(pk.OriginalTrainerName))
return false;
}
return true;
}
private bool IsLanguageValid(int pkLanguage)
{
if (pkLanguage == (int)Japanese)
return Language is LanguageRestriction.Japanese;
return Language is not LanguageRestriction.Japanese;
}
private bool IsTrainerNameValid(PKM pk) => Trainer switch
{
Recipient => true,
GiftStadiumJPN => pk.OriginalTrainerName == StadiumJPN,
GiftStadiumENG => pk.OriginalTrainerName == StadiumENG,
GiftStadiumINT => pk.OriginalTrainerName switch
{
StadiumGER => true,
StadiumFRE => true,
StadiumITA => true,
StadiumSPA => true,
_ => false,
},
PokemonCenterNewYork => IsTrainerPCNY(pk.OriginalTrainerName),
_ => true,
};
private bool IsTrainerIDValid(ITrainerID16 pk) => Trainer switch
{
Recipient => true,
GiftStadiumJPN => pk.TID16 == TrainerIDStadiumJPN,
GiftStadiumENG => pk.TID16 == TrainerIDStadiumENG,
GiftStadiumINT => pk.TID16 == TrainerIDStadiumINT,
_ => true,
};
private bool IsMatchEggLocation(PKM pk)
{
if (pk is not ICaughtData2 c2)

View file

@ -94,7 +94,7 @@ public sealed class TrainerNameVerifier : Verifier
{
// Transferring from RBY->Gen7 won't have OT Gender in PK1, nor will PK1 originated encounters.
// GSC Trades already checked for OT Gender matching.
if (pk is { Format: > 2, VC1: true } || enc is { Generation: 1 } or EncounterGift2 { IsGift: true })
if (pk is { Format: > 2, VC1: true } || enc is { Generation: 1 } or EncounterGift2 { EggEncounter: false })
data.AddLine(GetInvalid(LG1OTGender));
}

View file

@ -13,7 +13,7 @@ public static class TrashBytes
/// </summary>
/// <param name="buffer">Buffer to check the length of.</param>
/// <param name="terminator">String terminator to search for.</param>
/// <returns>Index of the terminator, or max length if not found.</returns>
/// <returns>Decoded index (char) of the terminator, or max length if not found.</returns>
public static int GetStringLength(ReadOnlySpan<byte> buffer, ushort terminator = 0)
{
int index = FindTerminatorIndex(buffer, terminator);
@ -25,7 +25,7 @@ public static class TrashBytes
/// </summary>
/// <param name="buffer">Backing buffer of the string.</param>
/// <param name="terminator">Terminator character to search for.</param>
/// <returns>Index of the terminator, or -1 if not found.</returns>
/// <returns>Decoded index (char) of the terminator, or -1 if not found.</returns>
/// <remarks>When used on a raw string, returns the computed length of the string, assuming a terminator is present.</remarks>
public static int FindTerminatorIndex(ReadOnlySpan<byte> buffer, ushort terminator = 0)
{

Binary file not shown.

Binary file not shown.