From c77ea8fd4c49a23ee3fc69f4119771ae79a856ad Mon Sep 17 00:00:00 2001 From: Kurt Date: Mon, 10 Mar 2025 00:11:29 -0500 Subject: [PATCH] PokeSpot: add IV animation pattern check Realign slot match check to what was documented: https://www.smogon.com/forums/threads/past-gen-rng-research.61090/post-4755981 Most RNG tools assume that both are available to spawn, as that would be an annoying setup. IV animation pattern: https://github.com/yatsuna827/PokemonXDRNGLibrary/blob/075f1340c6637760df674aa2c377f2eef50096a3/PokemonXDRNGLibrary/Generators/PokeSpotGenerator.cs#L48-L56 --- PKHeX.Core/Legality/RNG/CXD/MethodPokeSpot.cs | 96 ++++++++++++++----- 1 file changed, 70 insertions(+), 26 deletions(-) diff --git a/PKHeX.Core/Legality/RNG/CXD/MethodPokeSpot.cs b/PKHeX.Core/Legality/RNG/CXD/MethodPokeSpot.cs index 65d219032..e95cfa17c 100644 --- a/PKHeX.Core/Legality/RNG/CXD/MethodPokeSpot.cs +++ b/PKHeX.Core/Legality/RNG/CXD/MethodPokeSpot.cs @@ -12,42 +12,37 @@ public static class MethodPokeSpot /// Origin seed. public static PokeSpotSetup IsValidActivation(byte slot, uint seed, out uint origin) { - // Forward call structure: depends on if Bonsly is available. - // No Bonsly + // Forward call structure: depends on if Munchlax (10%) or Bonsly (30%) available to spawn // 0: Origin // 1: rand(003) == 0 - Activation - // 2: rand(100) < 10 - if Munchlax available, spawn Munchlax & return. + // w: rand(100) < [10 or 30 or 40] -- if neither available to spawn, skip. // x: rand(100) encounter slot - // y: pid + // yz: pid - // Bonsly - // 0: Origin - // 1: rand(003) == 0 - Activation - // 2: ??? - // 3: rand(100) < 30 - Bonsly; < 40 - Munchlax - // x: rand(100) encounter slot - // y: pid - - origin = seed; + origin = 0; // wild var esv = (seed >> 16) % 100; if (!IsSlotValid(slot, esv)) return PokeSpotSetup.Invalid; - // Assume only Munchlax available - var preSlot = XDRNG.Prev16(ref origin); - if (preSlot % 100 < 10) - return PokeSpotSetup.Invalid; // worst case: only Munchlax available - preSlot = XDRNG.Prev16(ref origin); + // Assume neither available to spawn. + var preSlot = XDRNG.Prev16(ref seed); if (preSlot % 3 == 0) - return PokeSpotSetup.NoBonsly; + { + origin = XDRNG.Prev(seed); + return PokeSpotSetup.Neither; + } - // Assume both available - if (preSlot % 100 < 30) + // Assume only Munchlax available + if (preSlot % 100 < 10) return PokeSpotSetup.Invalid; - preSlot = XDRNG.Prev16(ref origin); + preSlot = XDRNG.Prev16(ref seed); if (preSlot % 3 == 0) - return PokeSpotSetup.Bonsly; + { + origin = XDRNG.Prev(seed); + return PokeSpotSetup.Munchlax; + } + return PokeSpotSetup.Invalid; } @@ -119,6 +114,9 @@ public static bool TryGetOriginSeedIVs(uint iv1, uint iv2, byte levelMin, byte l // {u16 fakePID, u16 fakePID} => you are here (origin) // {u16 iv1, u16 iv2} // ability + origin = XDRNG.Prev6(preIV); + if (!IsValidAnimation(origin, out origin, out _)) + continue; var lvl16 = XDRNG.Prev2(preIV) >> 16; var lvlRnd = lvl16 % levelDelta; @@ -129,7 +127,6 @@ public static bool TryGetOriginSeedIVs(uint iv1, uint iv2, byte levelMin, byte l var abil16 = XDRNG.Next3(preIV) >> 16; var abit = abil16 & 1; // don't care about ability, might be reset on evolution - origin = XDRNG.Prev6(preIV); return true; } @@ -149,6 +146,10 @@ public static bool TrySetIVs(XK3 pk, EncounterCriteria criteria, byte levelMin, bool checkLevel = criteria.IsSpecifiedLevelRange() && criteria.IsLevelWithinRange(levelMin, levelMax); foreach (var preIV in seeds[..count]) { + var origin = XDRNG.Prev6(preIV); + if (!IsValidAnimation(origin, out origin, out _)) + continue; + // origin // {u16 fakeID, u16 fakeID} // ??? @@ -221,6 +222,10 @@ public static void SetRandomIVs(XK3 pk, EncounterCriteria criteria, byte levelMi // ability var preIV = seed; + var origin = XDRNG.Prev6(preIV); + if (!IsValidAnimation(origin, out origin, out _)) + continue; + var iv1 = XDRNG.Next15(ref seed); var iv2 = XDRNG.Next15(ref seed); var iv32 = iv2 << 15 | iv1; @@ -265,11 +270,50 @@ private static uint GetPIDRegular(ref uint seed) < 85 => 1, _ => 2, }; + + public static bool IsValidAnimation(uint seed, out uint origin, out uint animation) + { + // Origin + // Get a random animation that isn't 3: + // rand(10) until != 3 + // Based on animation, sub-randomize it: + // if 8, Next5 + // if >= 5, Next2 + // if <= 4, Next + + // Look backwards, starting with Prev + var prev16 = XDRNG.Prev16(ref seed); + animation = prev16 % 10; + if (animation is < 5 and not 3) + { + origin = XDRNG.Prev(seed); + return true; + } + + prev16 = XDRNG.Prev16(ref seed); + animation = prev16 % 10; + if (animation is >= 5 and not 8) + { + origin = XDRNG.Prev(seed); + return true; + } + + seed = XDRNG.Prev3(seed); + animation = (seed >> 16) % 10; + if (animation is 8) + { + origin = XDRNG.Prev(seed); + return true; + } + + origin = 0; + return false; + } } public enum PokeSpotSetup : byte { Invalid, - NoBonsly, - Bonsly, + Neither, + Munchlax, }