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]