Specify full movesets & course for Pokewalker encs

Didn't trust the existing manual transcribing, and both Bulbapedia and Serebii contain errors that don't match the original raw data. So, let's rip the raw data ourselves and use our own parse pickle (just raw bytes) instead of 162 manual entries.

For ripping logic, see: 203874da12

Partial match if language cannot access walker course
Re-do language override for EncounterStatic->pkm:
- Override gen1/2 event OT/language only if requested language is not possible
- Override walker OT/language if requested language is not possible (->japanese)

Closes #3594
This commit is contained in:
Kurt 2022-09-27 23:14:42 -07:00
parent f42b8609c1
commit 583f96a598
4 changed files with 90 additions and 182 deletions

View file

@ -16,185 +16,14 @@ internal static class Encounters4
internal static readonly EncounterArea4[] SlotsHG = EncounterArea4.GetAreas(Get("hg", "hg"), HG);
internal static readonly EncounterArea4[] SlotsSS = EncounterArea4.GetAreas(Get("ss", "ss"), SS);
private static readonly EncounterStatic4Pokewalker[] Encounter_PokeWalker = EncounterStatic4Pokewalker.GetAll(Util.GetBinaryResource("encounter_walker4.pkl"));
static Encounters4()
{
MarkEncounterTradeStrings(TradeGift_DPPt, TradeDPPt);
MarkEncounterTradeStrings(TradeGift_HGSS, TradeHGSS);
}
#region Pokéwalker Encounter
// all pk are in Poke Ball and have a met location of "PokeWalker"
private static readonly EncounterStatic4Pokewalker[] Encounter_PokeWalker =
{
// Some pk has a pre-level move, an egg move or even a special move, it might be also available via HM/TM/Tutor
// Johto/Kanto Courses
new(084, 1, 08), // Doduo
new(115, 1, 08), // Kangaskhan
new(029, 1, 05), // Nidoran♀
new(032, 0, 05), // Nidoran♂
new(016, 0, 05), // Pidgey
new(161, 1, 05), // Sentret
new(202, 1, 15), // Wobbuffet
new(069, 1, 08), // Bellsprout
new(046, 1, 06), // Paras
new(048, 0, 06), // Venonat
new(021, 0, 05), // Spearow
new(043, 1, 05), // Oddish
new(095, 0, 09), // Onix
new(240, 0, 09) { Moves = new(241) }, // Magby: Sunny Day
new(066, 1, 07), // Machop
new(077, 1, 07), // Ponyta
new(074, 1, 08) { Moves = new(189) }, // Geodude: Mud-Slap
new(163, 1, 06), // Hoothoot
new(054, 1, 10), // Psyduck
new(120, 2, 10), // Staryu
new(060, 0, 08), // Poliwag
new(079, 0, 08), // Slowpoke
new(191, 1, 06), // Sunkern
new(194, 0, 06), // Wooper
new(081, 2, 11), // Magnemite
new(239, 0, 11) { Moves = new(009) }, // Elekid: Thunder Punch
new(081, 2, 08), // Magnemite
new(198, 1, 11), // Murkrow
new(019, 1, 07), // Rattata
new(163, 1, 07), // Hoothoot
new(092, 1, 15) { Moves = new(194) }, // Gastly: Destiny Bond
new(238, 1, 12) { Moves = new(419) }, // Smoochum: Avalanche
new(092, 1, 10), // Gastly
new(095, 0, 10), // Onix
new(041, 0, 08), // Zubat
new(066, 0, 08), // Machop
new(060, 1, 15) { Moves = new(187) }, // Poliwag: Belly Drum
new(147, 1, 10), // Dratini
new(090, 1, 12), // Shellder
new(098, 0, 12) { Moves = new(152) }, // Krabby: Crabhammer
new(072, 1, 09), // Tentacool
new(118, 1, 09), // Goldeen
new(063, 1, 15), // Abra
new(100, 2, 15), // Voltorb
new(088, 0, 13), // Grimer
new(109, 1, 13) { Moves = new(120) }, // Koffing: Self-Destruct
new(019, 1, 16), // Rattata
new(162, 0, 15), // Furret
// Hoenn Courses
new(264, 1, 30), // Linoone
new(300, 1, 30), // Skitty
new(313, 0, 25), // Volbeat
new(314, 1, 25), // Illumise
new(263, 1, 17), // Zigzagoon
new(265, 1, 15), // Wurmple
new(298, 1, 20), // Azurill
new(320, 1, 31), // Wailmer
new(116, 1, 20), // Horsea
new(318, 1, 26), // Carvanha
new(118, 1, 22) { Moves = new(401) }, // Goldeen: Aqua Tail
new(129, 1, 15), // Magikarp
new(218, 1, 31), // Slugma
new(307, 0, 32), // Meditite
new(111, 0, 25), // Rhyhorn
new(228, 0, 27), // Houndour
new(074, 0, 29), // Geodude
new(077, 1, 19), // Ponyta
new(351, 1, 30), // Castform
new(352, 0, 30), // Kecleon
new(203, 1, 28), // Girafarig
new(234, 1, 28), // Stantler
new(044, 1, 14), // Gloom
new(070, 0, 13), // Weepinbell
new(105, 1, 30) { Moves = new(037) }, // Marowak: Thrash
new(128, 0, 30), // Tauros
new(042, 0, 33), // Golbat
new(177, 1, 24), // Natu
new(066, 0, 13) { Moves = new(418) }, // Machop: Bullet Punch
new(092, 1, 15), // Gastly
// Sinnoh Courses
new(415, 0, 30), // Combee
new(439, 0, 29), // Mime Jr.
new(403, 1, 33), // Shinx
new(406, 0, 30), // Budew
new(399, 1, 13), // Bidoof
new(401, 0, 15), // Kricketot
new(361, 1, 28), // Snorunt
new(459, 0, 31) { Moves = new(452) }, // Snover: Wood Hammer
new(215, 0, 28) { Moves = new(306) }, // Sneasel: Crash Claw
new(436, 2, 20), // Bronzor
new(179, 1, 15), // Mareep
new(220, 1, 16), // Swinub
new(357, 1, 35), // Tropius
new(438, 0, 30), // Bonsly
new(114, 1, 30), // Tangela
new(400, 1, 30), // Bibarel
new(102, 1, 17), // Exeggcute
new(179, 0, 19), // Mareep
new(200, 1, 32) { Moves = new(194) }, // Misdreavus: Destiny Bond
new(433, 0, 22) { Moves = new(105) }, // Chingling: Recover
new(093, 0, 25), // Haunter
new(418, 0, 28) { Moves = new(226) }, // Buizel: Baton Pass
new(170, 1, 17), // Chinchou
new(223, 1, 19), // Remoraid
new(422, 1, 30) { Moves = new(243) }, // Shellos: Mirror Coat
new(456, 1, 26), // Finneon
new(086, 1, 27), // Seel
new(129, 1, 30), // Magikarp
new(054, 1, 22) { Moves = new(281) }, // Psyduck: Yawn
new(090, 0, 20), // Shellder
new(025, 1, 30), // Pikachu
new(417, 1, 33) { Moves = new(175) }, // Pachirisu: Flail
new(035, 1, 31), // Clefairy
new(039, 1, 30), // Jigglypuff
new(183, 1, 25), // Marill
new(187, 1, 25), // Hoppip
new(442, 0, 31), // Spiritomb
new(446, 0, 33), // Munchlax
new(349, 0, 30), // Feebas
new(433, 1, 26), // Chingling
new(042, 0, 33), // Golbat
new(164, 1, 30), // Noctowl
// Special Courses
new(120, 2, 18) { Moves = new(113) }, // Staryu: Light Screen
new(224, 1, 19) { Moves = new(324) }, // Octillery: Signal Beam
new(116, 0, 15), // Horsea
new(222, 1, 16), // Corsola
new(170, 1, 12), // Chinchou
new(223, 0, 14), // Remoraid
new(035, 0, 08) { Moves = new(236) }, // Clefairy: Moonlight
new(039, 0, 10), // Jigglypuff
new(041, 0, 09), // Zubat
new(163, 1, 06), // Hoothoot
new(074, 0, 05), // Geodude
new(095, 1, 05) { Moves = new(088) }, // Onix: Rock Throw
new(025, 0, 15) { Moves = new(019) }, // Pikachu: Fly
new(025, 1, 14) { Moves = new(057) }, // Pikachu: Surf
new(025, 1, 12) { Moves = new(344, 252) }, // Pikachu: Volt Tackle, Fake Out
new(025, 0, 13) { Moves = new(175) }, // Pikachu: Flail
new(025, 0, 10), // Pikachu
new(025, 1, 10), // Pikachu
new(302, 1, 15), // Sableye
new(441, 0, 15), // Chatot
new(025, 1, 10), // Pikachu
new(453, 0, 10), // Croagunk
new(417, 0, 05), // Pachirisu
new(427, 1, 05), // Buneary
new(133, 0, 10), // Eevee
new(255, 0, 10), // Torchic
new(061, 1, 15) { Moves = new(003) }, // Poliwhirl: Double Slap
new(279, 0, 15), // Pelipper
new(025, 1, 08), // Pikachu
new(052, 0, 10), // Meowth
new(374, 2, 05) { Moves = new(428,334,442) }, // Beldum: Zen Headbutt, Iron Defense & Iron Head.
new(446, 0, 05) { Moves = new(120) }, // Munchlax: Self-Destruct
new(116, 0, 05) { Moves = new(330) }, // Horsea: Muddy Water
new(355, 0, 05) { Moves = new(286) }, // Duskull: Imprison
new(129, 0, 05) { Moves = new(340) }, // Magikarp: Bounce
new(436, 2, 05) { Moves = new(433) }, // Bronzor: Trick Room
new(239, 0, 05) { Moves = new(9) }, // Elekid: Thunder Punch (can be tutored)
new(240, 0, 05) { Moves = new(7) }, // Magby: Fire Punch (can be tutored)
new(238, 1, 05) { Moves = new(8) }, // Smoochum: Ice Punch (can be tutored)
new(440, 1, 05) { Moves = new(215) }, // Happiny: Heal Bell
new(173, 1, 05) { Moves = new(118) }, // Cleffa: Metronome
new(174, 0, 05) { Moves = new(273) }, // Igglybuff: Wish
};
#endregion
#region Static Encounter/Gift Tables
private static readonly EncounterStatic4[] Encounter_DPPt =
{

View file

@ -185,21 +185,30 @@ public abstract record EncounterStatic(GameVersion Version) : IEncounterable, IM
switch (this)
{
// Cannot trade between languages
case IFixedGBLanguage e:
return e.Language == EncounterGBLanguage.Japanese ? 1 : 2;
case IFixedGBLanguage { Language: EncounterGBLanguage.Japanese } when lang != 1:
pk.OT_Name = "ゲーフリ";
return (int)LanguageID.Japanese;
case IFixedGBLanguage { Language: not EncounterGBLanguage.Japanese } when lang == 1:
pk.OT_Name = "GF";
return (int)LanguageID.English;
// E-Reader was only available to Japanese games.
case EncounterStaticShadow {EReader: true}:
case EncounterStaticShadow { EReader: true } when lang != 1:
// Old Sea Map was only distributed to Japanese games.
case EncounterStatic3 when Species == (int)Core.Species.Mew:
case EncounterStatic3 { Species: (int)Core.Species.Mew } when lang != 1:
pk.OT_Name = "ゲーフリ";
return (int)LanguageID.Japanese;
// Deoxys for Emerald was not available for Japanese games.
case EncounterStatic3 when Species == (int)Core.Species.Deoxys && Version == GameVersion.E && lang == 1:
case EncounterStatic3 { Species: (ushort)Core.Species.Deoxys, Version: GameVersion.E } when lang == 1:
pk.OT_Name = "GF";
return (int)LanguageID.English;
// Some Pokewalker courses are only available in Japanese(/Korean) games.
case EncounterStatic4Pokewalker p when !p.IsCourseAvailable(lang):
pk.OT_Name = "ゲーフリ";
return (int)LanguageID.Japanese;
default:
return lang;
}

View file

@ -1,3 +1,6 @@
using System;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary>
@ -8,12 +11,21 @@ public sealed record EncounterStatic4Pokewalker : EncounterStatic
{
public override int Generation => 4;
public override EntityContext Context => EntityContext.Gen4;
public PokewalkerCourse4 Course { get; }
public EncounterStatic4Pokewalker(ushort species, sbyte gender, byte level) : base(GameVersion.HGSS)
public EncounterStatic4Pokewalker(ReadOnlySpan<byte> data, PokewalkerCourse4 course) : base(GameVersion.HGSS)
{
Species = species;
Gender = gender;
Level = level;
Species = ReadUInt16LittleEndian(data);
Level = data[2];
Gender = (sbyte)data[3];
Course = course;
var move1 = ReadUInt16LittleEndian(data[0x4..]);
var move2 = ReadUInt16LittleEndian(data[0x6..]);
var move3 = ReadUInt16LittleEndian(data[0x8..]);
var move4 = ReadUInt16LittleEndian(data[0xA..]);
Moves = new(move1, move2, move3, move4);
// All obtained entities are in Poke Ball and have a met location of "PokeWalker"
Gift = true;
Location = Locations.PokeWalker4;
}
@ -37,9 +49,19 @@ public sealed record EncounterStatic4Pokewalker : EncounterStatic
{
if (Gift && pk.Ball != Ball)
return true;
if (!IsCourseAvailable(pk.Language))
return true;
return base.IsMatchPartial(pk);
}
public bool IsCourseAvailable(int language) => Course switch
{
PokewalkerCourse4.Rally => language is (int)LanguageID.Japanese,
PokewalkerCourse4.Sightseeing => language is (int)LanguageID.Japanese or (int)LanguageID.Korean,
PokewalkerCourse4.AmityMeadow => language is (int)LanguageID.Japanese,
_ => true,
};
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
{
var pi = pk.PersonalInfo;
@ -52,4 +74,52 @@ public sealed record EncounterStatic4Pokewalker : EncounterStatic
PIDGenerator.SetRandomPIDPokewalker(pk, nature, gender);
criteria.SetRandomIVs(pk);
}
public static EncounterStatic4Pokewalker[] GetAll(ReadOnlySpan<byte> data)
{
const int size = 0xC;
var count = data.Length / size;
System.Diagnostics.Debug.Assert(count == 6 * (int)PokewalkerCourse4.MAX_COUNT);
var result = new EncounterStatic4Pokewalker[count];
for (int i = 0; i < result.Length; i++)
{
var offset = i * size;
var slice = data[offset..];
var course = (PokewalkerCourse4)(i / 6);
result[i] = new(slice, course);
}
return result;
}
}
public enum PokewalkerCourse4 : byte
{
RefreshingField = 0,
NoisyForest = 1,
RuggedRoad = 2,
BeautifulBeach = 3,
SuburbanArea = 4,
DimCave = 5,
BlueLake = 6,
TownOutskirts = 7,
HoennField = 8,
WarmBeach = 9,
VolcanoPath = 10,
Treehouse = 11,
ScaryCave = 12,
SinnohField = 13,
IcyMountainRoad = 14,
BigForest = 15,
WhiteLake = 16,
StormyBeach = 17,
Resort = 18,
QuietCave = 19,
BeyondTheSea = 20,
NightSkysEdge = 21,
YellowForest = 22,
Rally = 23, // JPN Exclusive
Sightseeing = 24, // JPN/KOR Exclusive
WinnersPath = 25,
AmityMeadow = 26, // JPN Exclusive
MAX_COUNT,
}