diff --git a/PKHeX.Core/Legality/RNG/MethodFinder.cs b/PKHeX.Core/Legality/RNG/MethodFinder.cs index f1458fd9b..c3007e3fb 100644 --- a/PKHeX.Core/Legality/RNG/MethodFinder.cs +++ b/PKHeX.Core/Legality/RNG/MethodFinder.cs @@ -354,43 +354,36 @@ private static bool GetBACDMatch(PKM pk, uint pid, uint[] IVs, out PIDIV pidiv) var bot = GetIVChunk(IVs, 0); var top = GetIVChunk(IVs, 3); var reg = GetSeedsFromIVs(RNG.LCRNG, top, bot); + PIDType type = PIDType.BACD_U; foreach (var seed in reg) { var B = seed; var A = RNG.LCRNG.Prev(B); + var low = B >> 16; - var PID = A & 0xFFFF0000 | B >> 16; - bool isShiny = (pk.TID ^ pk.SID ^ PID >> 16 ^ PID & 0xFFFF) < 8; - bool forceShiny = false; - bool antiShiny = false; + var PID = A & 0xFFFF0000 | low; if (PID != pid) { + uint idxor = (uint)(pk.TID ^ pk.SID); + bool isShiny = (idxor ^ PID >> 16 ^ PID & 0xFFFF) < 8; if (!isShiny) { - // check for force shiny pkm - if (!pk.IsShiny) - continue; // obviously not force shiny - - // 0-Origin - // 1-PIDH - // 2-PIDL (ends up unused) - // 3-FORCEBITS - // PID = PIDH << 16 | (SID ^ TID ^ PIDH) - - var X = RNG.LCRNG.Prev(A); - PID = X & 0xFFFF0000 | (uint)pk.SID ^ (uint)pk.TID ^ X >> 16; - PID &= 0xFFFFFFF8; - PID |= B >> 16 & 0x7; // lowest 3 bits - - if (PID != pid) - continue; - forceShiny = true; + if (!pk.IsShiny) // check for nyx antishiny + { + if (!IsBACD_U_AX(idxor, pid, low, A, ref type)) + continue; + } + else // check for force shiny pkm + { + if (!IsBACD_U_S(idxor, pid, low, ref A, ref type)) + continue; + } } - if (!forceShiny) + else if (!IsBACD_U_AX(idxor, pid, low, A, ref type)) { if ((PID + 8 & 0xFFFFFFF8) != pid) continue; - antiShiny = true; + type = PIDType.BACD_U_A; } } var s = RNG.LCRNG.Prev(A); @@ -401,13 +394,12 @@ private static bool GetBACDMatch(PKM pk, uint pid, uint[] IVs, out PIDIV pidiv) { if ((sn & 0xFFFF0000) != 0) continue; - var type = forceShiny ? PIDType.BACD_R_S : antiShiny ? PIDType.BACD_R_A : PIDType.BACD_R; - pidiv = new PIDIV {OriginSeed = sn, RNG = RNG.LCRNG, Type = type}; + // shift from unrestricted enum val to restricted enum val + pidiv = new PIDIV {OriginSeed = sn, RNG = RNG.LCRNG, Type = --type }; return true; } // no restricted seed found, thus unrestricted - var t = forceShiny ? PIDType.BACD_U_S : antiShiny ? PIDType.BACD_U_A : PIDType.BACD_U; - pidiv = new PIDIV {OriginSeed = s, RNG = RNG.LCRNG, Type = t}; + pidiv = new PIDIV {OriginSeed = s, RNG = RNG.LCRNG, Type = type}; return true; } return GetNonMatch(out pidiv); @@ -466,6 +458,64 @@ private static bool GetNonMatch(out PIDIV pidiv) pidiv = null; 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 | (SID ^ TID ^ PIDH) + + var X = RNG.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 = PIDType.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 = PIDType.BACD_U_AX; + return true; + } private static PIDIV AnalyzeGB(PKM pk) { diff --git a/PKHeX.Core/Legality/RNG/PIDGenerator.cs b/PKHeX.Core/Legality/RNG/PIDGenerator.cs index a67723e43..68406fa2e 100644 --- a/PKHeX.Core/Legality/RNG/PIDGenerator.cs +++ b/PKHeX.Core/Legality/RNG/PIDGenerator.cs @@ -40,6 +40,11 @@ private static void SetValuesFromSeedBACD(PKM pk, PIDType type, uint seed) pk.PID = PID; } + else if (type == PIDType.BACD_R_AX || type == PIDType.BACD_U_AX) + { + uint low = B >> 16; + pk.PID = A & 0xFFFF0000 ^ (((uint)pk.TID ^ (uint)pk.SID ^ low) << 16) | low; + } else pk.PID = A & 0xFFFF0000 | B >> 16; diff --git a/PKHeX.Core/Legality/RNG/PIDType.cs b/PKHeX.Core/Legality/RNG/PIDType.cs index 0856536b3..a2e6658bb 100644 --- a/PKHeX.Core/Legality/RNG/PIDType.cs +++ b/PKHeX.Core/Legality/RNG/PIDType.cs @@ -44,6 +44,14 @@ public enum PIDType /// Event Reversed Order PID without Origin Seed restrictions, shiny /// BACD_U_S, + /// + /// Event Reversed Order PID restricted to 16bit Origin Seed, antishiny (nyx) + /// + BACD_R_AX, + /// + /// Event Reversed Order PID without Origin Seed restrictions, antishiny (nyx) + /// + BACD_U_AX, /// /// Generation 4 Cute Charm forced 8 bit diff --git a/Tests/PKHeX.Tests/PKM/PIDIVTests.cs b/Tests/PKHeX.Tests/PKM/PIDIVTests.cs index ec8c700cf..e971dbe9a 100644 --- a/Tests/PKHeX.Tests/PKM/PIDIVTests.cs +++ b/Tests/PKHeX.Tests/PKM/PIDIVTests.cs @@ -98,6 +98,11 @@ public void PIDIVMatchingTest3Event() PIDGenerator.SetValuesFromSeed(gkRS, PIDType.BACD_R_S, a_pkRS.OriginSeed); Assert.AreEqual(pkRS.PID, gkRS.PID, "Unable to match generated PID to BACD-R shiny spread"); Assert.IsTrue(pkRS.IVs.SequenceEqual(gkRS.IVs), "Unable to match generated IVs to BACD-R shiny spread"); + + // Unrestricted Antishiny nyx + var nyxUA = new PK3 {PID = 0xBD3DF676, IVs = new[] {00, 15, 05, 04, 21, 05}, TID = 80, SID = 0}; + var nyx_pkUA = MethodFinder.Analyze(nyxUA); + Assert.AreEqual(PIDType.BACD_U_AX, nyx_pkUA?.Type, "Unable to match PID to BACD-U antishiny nyx spread"); } [TestMethod]