From de57e197ad3aeeecb8798eda7922cca9a29435e2 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 10 Nov 2024 19:11:30 -0600 Subject: [PATCH] Fix gen3 reversal exploration for 50% of seeds line 340 - variable reuse! In looking at the emerald disassembly, when Pressure/Hustle/Vital Spirit fails, it reduces the max level of the slot by 1 so that the max level doesn't randomly appear. Essentially 50% flat for the max to appear, for varied slots, not 50% + (1/range). Thus we require a separate branch of logic to check for this scenario. https://github.com/pret/pokeemerald/blob/f8119bedd4eae6aa614e7378f78d548e4f721d52/src/wild_encounter.c#L298 DPPt (and assumedly HGSS) do not decrease the random range on failure. ty TFS for bringing this to my attention on discord --- .../Legality/RNG/ClassicEra/Gen3/MethodH.cs | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/MethodH.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/MethodH.cs index eb54e5e4e..c80f52f95 100644 --- a/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/MethodH.cs +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/MethodH.cs @@ -337,9 +337,9 @@ public static class MethodH if (syncProc) { var ctx = new FrameCheckDetails(enc, seed, levelMin, levelMax, format); - if (IsSlotValidRegular(ctx, out seed)) + if (IsSlotValidRegular(ctx, out var regular)) { - result = new(seed, Synchronize); + result = new(regular, Synchronize); return true; } } @@ -421,7 +421,9 @@ public static class MethodH if (IsHustleVitalPass(ctx.Prev1)) // should have triggered { result = default; return false; } - return IsSlotValidFrom1Skip(ctx, out result); + // When it fails, level range is reduced by 1 so that it is not the maximum level, if possible. + + return IsSlotValidFrom1SkipMinus1(ctx, out result); } private static bool TryGetMatchNoSync(in FrameCheckDetails ctx, out LeadSeed result) @@ -467,6 +469,21 @@ public static class MethodH result = default; return false; } + private static bool IsSlotValidFrom1SkipMinus1(FrameCheckDetails ctx, out uint result) + where T : IEncounterSlot3 + { + // -3 ESV + // -2 Level (range biased down by 1) + // -1 (Proc Already Checked) + // 0 Nature + if (IsLevelValidMinus1(ctx.Encounter, ctx.LevelMin, ctx.LevelMax, ctx.Format, ctx.Prev2)) + { + if (IsSlotValid(ctx.Encounter, ctx.Prev3)) + { result = ctx.Seed4; return true; } + } + result = default; return false; + } + private static bool IsSlotValidRegular(in FrameCheckDetails ctx, out uint result) where T : IEncounterSlot3 { @@ -571,6 +588,22 @@ public static class MethodH return (u16LevelRand % mod) + min; } + private static bool IsLevelValidMinus1(T enc, byte min, byte max, byte format, uint u16LevelRand) where T : ILevelRange + { + var level = GetExpectedLevelMinus1(enc, u16LevelRand); + return IsOriginalLevelValid(min, max, format, level); + } + + private static uint GetExpectedLevelMinus1(T enc, uint u16LevelRand) where T : ILevelRange + { + var min = enc.LevelMin; + uint mod = 1u + enc.LevelMax - min; + var bias = (u16LevelRand % mod); + if (bias != 0) + bias--; + return min + bias; + } + private static bool IsRockSmashPossible(byte areaRate, ref uint seed) { if (IsRatePass(seed, areaRate, None)) // Lead doesn't matter, doesn't influence.