From c274ff6f3a148e0da61038c81b8ea2249a7a26e0 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 14 Jun 2025 21:45:16 -0500 Subject: [PATCH] Add more generator filtering criteria Gen6+ slots now try to give shinies if possible & requested Gen3/4 slots now try to respect IV requests if only a couple are requested Removes PIDGenerator; no remaining uses (either all inlined or extracted to specialized generator classes). API usage wasn't recommended anyway, as it was incomplete and did not honor all correlations. We want to try to generate it right the first time. --- .../Gen3/Colo/EncounterShadow3Colo.cs | 24 +- .../Templates/Gen3/EncounterStatic3.cs | 54 +++- .../Templates/Gen3/Gifts/EncounterGift3.cs | 39 ++- .../Templates/Gen3/Gifts/EncounterGift3JPN.cs | 8 +- .../Templates/Gen3/Gifts/EncounterGift3NY.cs | 9 +- .../Templates/Gen3/XD/EncounterStatic3XD.cs | 22 +- .../Templates/Gen4/EncounterStatic4.cs | 6 + .../Templates/Gen6/EncounterSlot6AO.cs | 5 + .../Templates/Gen6/EncounterSlot6XY.cs | 5 + .../Templates/Gen7/EncounterSlot7.cs | 5 + .../Templates/Gen7b/EncounterSlot7b.cs | 5 + .../Templates/Gen8/EncounterSlot8.cs | 5 + .../Templates/Gen8b/EncounterSlot8b.cs | 5 + .../Templates/Gen9/EncounterSlot9.cs | 5 + .../Templates/Gen9/EncounterTrade9.cs | 5 + PKHeX.Core/Legality/RNG/CXD/MethodCXD.cs | 6 + .../RNG/ClassicEra/Gen3/GenerateMethodH.cs | 6 + .../RNG/ClassicEra/Gen4/GenerateMethodJ.cs | 3 + .../RNG/ClassicEra/Gen4/GenerateMethodK.cs | 3 + .../RNG/ClassicEra/Gen4/PokewalkerRNG.cs | 10 +- PKHeX.Core/Legality/RNG/PIDGenerator.cs | 291 ------------------ PKHeX.Core/MysteryGifts/PGT.cs | 8 +- 22 files changed, 203 insertions(+), 326 deletions(-) delete mode 100644 PKHeX.Core/Legality/RNG/PIDGenerator.cs diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterShadow3Colo.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterShadow3Colo.cs index 1b19e901c..899ee4b5b 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterShadow3Colo.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Colo/EncounterShadow3Colo.cs @@ -85,7 +85,7 @@ private void SetPINGA(CK3 pk, EncounterCriteria criteria, PersonalInfo3 pi) if (!IsEReader) SetPINGA_Regular(pk, criteria, pi); else - SetPINGA_EReader(pk); + SetPINGA_EReader(pk, criteria); } private void SetPINGA_Regular(CK3 pk, EncounterCriteria criteria, PersonalInfo3 pi) @@ -98,7 +98,7 @@ private void SetPINGA_Regular(CK3 pk, EncounterCriteria criteria, PersonalInfo3 this.SetRandom(pk, EncounterCriteria.Unrestricted, pi, noShiny: false, seed); } - private void SetPINGA_EReader(CK3 pk) + private void SetPINGA_EReader(CK3 pk, EncounterCriteria criteria) { // E-Reader have all IVs == 0 // Skip setting IVs. @@ -113,15 +113,27 @@ private void SetPINGA_EReader(CK3 pk) int ctr = 0; const int max = 100_000; var rnd = Util.Rand; + var gr = pk.PersonalInfo.Gender; do { var seed = rnd.Rand32(); - PIDGenerator.SetValuesFromSeedXDRNG_EReader(pk, seed); - if (pk.Nature != nature || pk.Gender != gender) + var D = XDRNG.Prev3(seed); // PID + var E = XDRNG.Next(D); // PID + var pid = (D & 0xFFFF0000) | (E >> 16); + + if ((Nature)(pid % 25) != nature || EntityGender.GetFromPIDAndRatio(pid, gr) != gender) continue; + + if (criteria.Shiny.IsShiny() && !ShinyUtil.GetIsShiny(pk.ID32, pid, 8)) + continue; + var result = LockFinder.IsAllShadowLockValid(this, seed, pk); - if (result) - break; + if (!result) + continue; + + pk.PID = pid; + pk.RefreshAbility(0); + // IVs always 0 for E-Reader shadows. } while (++ctr <= max); } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterStatic3.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterStatic3.cs index ca0135d05..4b31f1fb8 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterStatic3.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterStatic3.cs @@ -98,15 +98,58 @@ private int GetTemplateLanguage(ITrainerInfo tr) private void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi) { var gr = pi.Gender; - bool truncate = IsRoamingTruncatedIVs; - if (criteria.IsSpecifiedIVsAll() && !truncate) + if (IsRoamingTruncatedIVs) + { + SetRoamerPINGA(pk, criteria); + return; + } + if (criteria.IsSpecifiedIVsAll()) { if (TrySetMethod1(pk, criteria, gr)) return; } SetMethod1(pk, criteria, gr, Util.Rand32()); - if (truncate) // Never happens for eggs or dual-ability encounters. - pk.IV32 &= 0xFF; + } + + private static bool SetRoamerPINGA(PK3 pk, EncounterCriteria criteria) + { + // For every possible 8-bit IV combination, check if it meets the criteria. + var id32 = pk.ID32; + for (uint i = 0; i <= byte.MaxValue; i++) + { + var iv32 = i; + + // IVs can only ever be Hidden Power: Fighting. Don't bother checking if it matches. + if (!criteria.IsSatisfiedIVs(iv32)) + continue; + + // Satisfactory IVs; now check PID/nature/shininess. + var frame = iv32 << 16; + for (uint hi = 0; hi <= byte.MaxValue; hi++) + { + for (uint lo = 0; lo <= ushort.MaxValue; lo++) + { + var state = frame | (hi << 24) | lo; + var rand2 = LCRNG.Prev16(ref state); + var rand1 = LCRNG.Prev16(ref state); + var pid = (rand2 << 16) | rand1; + if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature((Nature)(pid % 25))) + continue; + bool shiny = ShinyUtil.GetIsShiny(id32, pid, 8); + if (criteria.Shiny.IsShiny() != shiny) + continue; + + pk.PID = pid; + pk.IV32 = iv32; + pk.RefreshAbility(0); + return true; + } + } + } + // Should never happen, but just in case. + SetMethod1(pk, criteria, 255, Util.Rand32()); + pk.IV32 &= 0xFF; // truncate to 8 bits (value storage fail) + return false; } private static bool TrySetMethod1(PK3 pk, EncounterCriteria criteria, byte gr) @@ -143,6 +186,7 @@ private static bool TrySetMethod1(PK3 pk, EncounterCriteria criteria, byte gr) private static void SetMethod1(PK3 pk, in EncounterCriteria criteria, byte gr, uint seed) { var id32 = pk.ID32; + bool filterIVs = criteria.IsSpecifiedIVs(2); while (true) { var pid = ClassicEraRNG.GetSequentialPID(ref seed); @@ -164,6 +208,8 @@ private static void SetMethod1(PK3 pk, in EncounterCriteria criteria, byte gr, u var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; pk.PID = pid; pk.IV32 |= iv32; diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs index dcd07b5a6..2f846b485 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3.cs @@ -208,7 +208,7 @@ private uint SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi) continue; pk.PID = pid; - pk.IV32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed); + pk.IV32 = ClassicEraRNG.GetSequentialIVs(ref seed); pk.RefreshAbility((int)(pk.PID & 1)); return seed; } @@ -220,7 +220,7 @@ public bool GenerateSeed32(PKM pk, uint seed) if (Method is Channel) { seed = ChannelJirachi.SkipToPIDIV(seed); - PIDGenerator.SetValuesFromSeedChannel(pk3, seed); + SetValuesFromSeedChannel(pk3, seed); return true; } @@ -233,7 +233,7 @@ public bool GenerateSeed32(PKM pk, uint seed) _ when Method is Method_2 => GetMethod2(ref seed), _ => GetRegular(ref seed), }; - pk3.IV32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed); + pk3.IV32 = ClassicEraRNG.GetSequentialIVs(ref seed); return true; } @@ -248,7 +248,7 @@ private static bool TrySetWishmkrShiny(PK3 pk, EncounterCriteria criteria) if (filterNature && !criteria.IsSatisfiedNature((Nature)(pid % 25))) continue; // try again - var iv32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed); + var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; // try again if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) @@ -278,7 +278,7 @@ private static uint SetPINGAChannel(PK3 pk, EncounterCriteria criteria) { if (!ChannelJirachi.IsPossible(seed)) continue; - PIDGenerator.SetValuesFromSeedChannel(pk, seed); + SetValuesFromSeedChannel(pk, seed); var pid = pk.EncryptionConstant; if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature((Nature)(pid % 25))) continue; // try again @@ -288,24 +288,49 @@ private static uint SetPINGAChannel(PK3 pk, EncounterCriteria criteria) } } + bool filterIVs = criteria.IsSpecifiedIVs(2); while (true) { uint seed = Util.Rand32(); seed = ChannelJirachi.SkipToPIDIV(seed); - PIDGenerator.SetValuesFromSeedChannel(pk, seed); + SetValuesFromSeedChannel(pk, seed); var pid = pk.EncryptionConstant; if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature((Nature)(pid % 25))) continue; // try again if (criteria.Shiny.IsShiny() != ShinyUtil.GetIsShiny(pk.ID32, pid, 8)) continue; // try again - if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(pk.IV32)) + var iv32 = pk.IV32; + if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; // try again + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; return seed; } } + public static void SetValuesFromSeedChannel(PK3 pk, uint seed) + { + const ushort TID16 = 40122; + var sid = XDRNG.Next16(ref seed); + pk.ID32 = (sid << 16) | TID16; + + var pid1 = XDRNG.Next16(ref seed); + var pid2 = XDRNG.Next16(ref seed); + var pid = (pid1 << 16) | pid2; + if ((pid2 > 7 ? 0 : 1) != (pid1 ^ sid ^ TID16)) + pid ^= 0x80000000; + pk.PID = pid; + + pk.HeldItem = (ushort)((XDRNG.Next16(ref seed) >> 15) + 169u); // 0-Ganlon, 1-Salac + pk.Version = GameVersion.S + (byte)(XDRNG.Next16(ref seed) >> 15); // 0-Sapphire, 1-Ruby + pk.OriginalTrainerGender = (byte)(XDRNG.Next16(ref seed) >> 15); + + var iv32 = XDRNG.GetSequentialIV32(seed); + pk.SetIVs(iv32); + } + private uint GetSaneSeed(uint seed) => Method switch { BACD_RBCD => Math.Clamp(seed, 3, 213), // BCD digit sum diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3JPN.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3JPN.cs index 488f7323e..894add596 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3JPN.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3JPN.cs @@ -73,6 +73,7 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi) { uint seed = Util.Rand32(); + var filterIVs = criteria.IsSpecifiedIVs(2); var gr = pi.Gender; var idXor = pk.TID16; // no SID while (true) @@ -82,9 +83,14 @@ private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 p continue; // try again if (criteria.IsSpecifiedGender() && !criteria.IsSatisfiedGender(EntityGender.GetFromPIDAndRatio(pid, gr))) continue; + var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); + if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) + continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; pk.PID = pid; - pk.IV32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed); + pk.IV32 = iv32; pk.RefreshAbility((int)(pid & 1)); pk.OriginalTrainerGender = (byte)GetGender(LCRNG.Next16(ref seed)); return; diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3NY.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3NY.cs index 34afc8bf1..2d3cc4a73 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3NY.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/Gifts/EncounterGift3NY.cs @@ -74,6 +74,7 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi) { uint seed = Util.Rand32(); + var filterIVs = criteria.IsSpecifiedIVs(2); var gr = pi.Gender; var idXor = pk.TID16; // no SID while (true) @@ -84,9 +85,13 @@ private static void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 p var gender = EntityGender.GetFromPIDAndRatio(pid, gr); if (criteria.IsSpecifiedGender() && !criteria.IsSatisfiedGender(gender)) continue; - + var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); + if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) + continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; pk.PID = pid; - pk.IV32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed); + pk.IV32 = iv32; pk.RefreshAbility((int)(pid & 1)); return; } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterStatic3XD.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterStatic3XD.cs index ba42e318b..3992b1e5b 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterStatic3XD.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/XD/EncounterStatic3XD.cs @@ -71,14 +71,7 @@ private void SetPINGA(XK3 pk, EncounterCriteria criteria, PersonalInfo3 pi) { if (Species == (int)Core.Species.Eevee) { - // Prefer IVs if requested, rather than Trainer Matching. - if (criteria.IsSpecifiedIVsAll() && MethodCXD.SetStarterFromIVs(pk, criteria)) - return; - // Fall back to Trainer ID matching. - if (MethodCXD.SetStarterFromTrainerID(pk, criteria, pk.TID16, pk.SID16)) - return; - // Fall back to generating a random PID. - MethodCXD.SetStarterRandom(pk, criteria, Util.Rand32()); + SetStarterPINGA(pk, criteria); return; } @@ -86,6 +79,19 @@ private void SetPINGA(XK3 pk, EncounterCriteria criteria, PersonalInfo3 pi) return; MethodCXD.SetRandom(pk, criteria, pi, noShiny: false, Util.Rand32()); } + + private static void SetStarterPINGA(XK3 pk, EncounterCriteria criteria) + { + // Prefer IVs if requested, rather than Trainer Matching. + if (criteria.IsSpecifiedIVsAll() && MethodCXD.SetStarterFromIVs(pk, criteria)) + return; + // Fall back to Trainer ID matching. + if (MethodCXD.SetStarterFromTrainerID(pk, criteria, pk.TID16, pk.SID16)) + return; + // Fall back to generating a random PID. + MethodCXD.SetStarterRandom(pk, criteria, Util.Rand32()); + } + #endregion #region Matching diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4.cs index 6ad2fbea0..88cc796dd 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterStatic4.cs @@ -159,6 +159,7 @@ private static bool TrySetMethod1(PK4 pk, in EncounterCriteria criteria, byte gr private static void SetMethod1(PK4 pk, in EncounterCriteria criteria, byte gr, uint seed) { var id32 = pk.ID32; + var filterIVs = criteria.IsSpecifiedIVs(2); while (true) { var pid = ClassicEraRNG.GetSequentialPID(ref seed); @@ -180,6 +181,8 @@ private static void SetMethod1(PK4 pk, in EncounterCriteria criteria, byte gr, u var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; pk.PID = pid; pk.IV32 |= iv32; @@ -227,6 +230,7 @@ private static bool TrySetChainShiny(PK4 pk, in EncounterCriteria criteria, byte private static void SetChainShiny(PK4 pk, in EncounterCriteria criteria, byte gr, uint seed) { var id32 = pk.ID32; + var filterIVs = criteria.IsSpecifiedIVs(2); while (true) { var pid = ClassicEraRNG.GetChainShinyPID(ref seed, id32); @@ -244,6 +248,8 @@ private static void SetChainShiny(PK4 pk, in EncounterCriteria criteria, byte gr var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; pk.PID = pid; pk.IV32 = iv32; diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen6/EncounterSlot6AO.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen6/EncounterSlot6AO.cs index 9ba59438d..7561d10d8 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen6/EncounterSlot6AO.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen6/EncounterSlot6AO.cs @@ -108,6 +108,11 @@ private void SetPINGA(PK6 pk, EncounterCriteria criteria, PersonalInfo6AO pi) { var rnd = Util.Rand; pk.PID = rnd.Rand32(); + if (criteria.Shiny.IsShiny()) + pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15)); + else if (criteria.Shiny == Shiny.Never && pk.IsShiny) + pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny + pk.EncryptionConstant = rnd.Rand32(); pk.Nature = criteria.GetNature(); pk.Gender = criteria.GetGender(pi); diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen6/EncounterSlot6XY.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen6/EncounterSlot6XY.cs index 3a1f68b2e..2310f27ae 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen6/EncounterSlot6XY.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen6/EncounterSlot6XY.cs @@ -102,6 +102,11 @@ private void SetPINGA(PK6 pk, EncounterCriteria criteria, PersonalInfo6XY pi) { var rnd = Util.Rand; pk.PID = rnd.Rand32(); + if (criteria.Shiny.IsShiny()) + pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15)); + else if (criteria.Shiny == Shiny.Never && pk.IsShiny) + pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny + pk.EncryptionConstant = rnd.Rand32(); pk.Nature = criteria.GetNature(); pk.Gender = criteria.GetGender(pi); diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen7/EncounterSlot7.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen7/EncounterSlot7.cs index e6ae7ca37..a8369ada9 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen7/EncounterSlot7.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen7/EncounterSlot7.cs @@ -91,6 +91,11 @@ private void SetPINGA(PK7 pk, EncounterCriteria criteria, PersonalInfo7 pi) { var rnd = Util.Rand; pk.PID = rnd.Rand32(); + if (criteria.Shiny.IsShiny()) + pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15)); + else if (criteria.Shiny == Shiny.Never && pk.IsShiny) + pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny + pk.EncryptionConstant = rnd.Rand32(); pk.Nature = criteria.GetNature(); pk.Gender = criteria.GetGender(pi); diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen7b/EncounterSlot7b.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen7b/EncounterSlot7b.cs index 9201f0856..cbe1680c5 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen7b/EncounterSlot7b.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen7b/EncounterSlot7b.cs @@ -64,6 +64,11 @@ private void SetPINGA(PB7 pk, EncounterCriteria criteria, PersonalInfo7GG pi) { var rnd = Util.Rand; pk.PID = rnd.Rand32(); + if (criteria.Shiny.IsShiny()) + pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15)); + else if (criteria.Shiny == Shiny.Never && pk.IsShiny) + pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny + pk.EncryptionConstant = rnd.Rand32(); pk.Nature = criteria.GetNature(); pk.Gender = criteria.GetGender(pi); diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterSlot8.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterSlot8.cs index 9e07f0c6b..d0669733e 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterSlot8.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen8/EncounterSlot8.cs @@ -104,6 +104,11 @@ private void SetPINGA(PK8 pk, EncounterCriteria criteria, PersonalInfo8SWSH pi) var rand = Util.Rand; pk.EncryptionConstant = rand.Rand32(); pk.PID = rand.Rand32(); + if (criteria.Shiny.IsShiny()) + pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rand.Next(1, 15)); + else if (criteria.Shiny == Shiny.Never && pk.IsShiny) + pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny + pk.HeightScalar = PokeSizeUtil.GetRandomScalar(rand); pk.WeightScalar = PokeSizeUtil.GetRandomScalar(rand); criteria.SetRandomIVs(pk); diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen8b/EncounterSlot8b.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen8b/EncounterSlot8b.cs index b90f98d9d..8e18a2763 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen8b/EncounterSlot8b.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen8b/EncounterSlot8b.cs @@ -97,6 +97,11 @@ private void SetPINGA(PB8 pk, EncounterCriteria criteria, PersonalInfo8BDSP pi) { var rnd = Util.Rand; pk.PID = rnd.Rand32(); + if (criteria.Shiny.IsShiny()) + pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15)); + else if (criteria.Shiny == Shiny.Never && pk.IsShiny) + pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny + pk.EncryptionConstant = rnd.Rand32(); criteria.SetRandomIVs(pk); pk.Nature = pk.StatNature = criteria.GetNature(); diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen9/EncounterSlot9.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen9/EncounterSlot9.cs index c02c9b888..bc7392982 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen9/EncounterSlot9.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen9/EncounterSlot9.cs @@ -172,6 +172,11 @@ private void SetPINGA(PK9 pk, EncounterCriteria criteria, PersonalInfo9SV pi) { var rnd = Util.Rand; pk.PID = rnd.Rand32(); + if (criteria.Shiny.IsShiny()) + pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15)); + else if (criteria.Shiny == Shiny.Never && pk.IsShiny) + pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny + pk.EncryptionConstant = rnd.Rand32(); criteria.SetRandomIVs(pk); diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen9/EncounterTrade9.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen9/EncounterTrade9.cs index 74ff5c5e1..f5dfa1c99 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen9/EncounterTrade9.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen9/EncounterTrade9.cs @@ -129,6 +129,11 @@ private void SetPINGA(PK9 pk, EncounterCriteria criteria, PersonalInfo9SV pi) { var rnd = Util.Rand; pk.PID = rnd.Rand32(); + if (criteria.Shiny.IsShiny()) + pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, criteria.Shiny == Shiny.AlwaysSquare ? 0 : (uint)rnd.Next(1, 15)); + else if (criteria.Shiny == Shiny.Never && pk.IsShiny) + pk.PID ^= 0x80000000; // flip top bit to ensure non-shiny + pk.EncryptionConstant = rnd.Rand32(); pk.Nature = pk.StatNature = criteria.GetNature(Nature); pk.Gender = criteria.GetGender(Gender, pi); diff --git a/PKHeX.Core/Legality/RNG/CXD/MethodCXD.cs b/PKHeX.Core/Legality/RNG/CXD/MethodCXD.cs index c02cb19bb..c3d653d0b 100644 --- a/PKHeX.Core/Legality/RNG/CXD/MethodCXD.cs +++ b/PKHeX.Core/Legality/RNG/CXD/MethodCXD.cs @@ -99,6 +99,7 @@ public static bool SetFromIVs(TEntity pk, in EncounterCriteria criteria /// public static bool SetStarterFromTrainerID(CK3 pk, in EncounterCriteria criteria, ushort tid, ushort sid) { + var filterIVs = criteria.IsSpecifiedIVs(2); var id32 = (uint)sid << 16 | tid; var species = pk.Species; // * => TID, SID, fakepid*2, [IVs, ability, PID] @@ -131,6 +132,8 @@ public static bool SetStarterFromTrainerID(CK3 pk, in EncounterCriteria criteria var iv32 = iv2 << 15 | iv1; if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; pk.PID = pid; SetIVs(pk, iv1, iv2); @@ -265,6 +268,7 @@ private static bool IsValidTrainerCombination(in EncounterCriteria criteria, uin public static bool SetStarterFromTrainerID(XK3 pk, EncounterCriteria criteria, ushort tid, ushort sid) { // * => TID, SID, fakepid*2, [IVs, ability, PID] + var filterIVs = criteria.IsSpecifiedIVs(2); Span all = stackalloc uint[XDRNG.MaxCountSeedsPID]; var count = XDRNG.GetSeeds(all, (uint)tid << 16, (uint)sid << 16); var seeds = all[..count]; @@ -285,6 +289,8 @@ public static bool SetStarterFromTrainerID(XK3 pk, EncounterCriteria criteria, u var iv32 = iv2 << 15 | iv1; if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; pk.PID = pid; SetIVs(pk, iv1, iv2); diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/GenerateMethodH.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/GenerateMethodH.cs index de9cf2d61..191fb13f2 100644 --- a/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/GenerateMethodH.cs +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen3/GenerateMethodH.cs @@ -15,6 +15,7 @@ public static void SetRandom(this T enc, PK3 pk, PersonalInfo3 pi, EncounterC var (min, max) = SlotMethodH.GetRange(enc.Type, enc.SlotNumber); bool checkProc = MethodH.IsEncounterCheckApplicable(enc.Type); bool checkLevel = criteria.IsSpecifiedLevelRange() && enc.IsLevelWithinRange(criteria); + bool filterIVs = criteria.IsSpecifiedIVs(2); // Generate Method H correlated PID and IVs, no lead (keep things simple). while (true) @@ -55,6 +56,8 @@ public static void SetRandom(this T enc, PK3 pk, PersonalInfo3 pi, EncounterC var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) break; // try again + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; { var level = (byte)MethodH.GetRandomLevel(enc, lv, LeadRequired.None); @@ -78,6 +81,7 @@ public static void SetRandomUnown(this T enc, PK3 pk, EncounterCriteria crite var (min, max) = SlotMethodH.GetRangeGrass(enc.SlotNumber); // Can't game the seed with % 100 increments as Unown's form calculation is based on the PID. + var filterIVs = criteria.IsSpecifiedIVs(2); while (true) { var esv = LCRNG.Next16(ref seed) % 100; @@ -103,6 +107,8 @@ public static void SetRandomUnown(this T enc, PK3 pk, EncounterCriteria crite var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; pk.PID = pid; pk.IV32 = iv32; diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodJ.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodJ.cs index c69748a1e..5871a85a4 100644 --- a/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodJ.cs +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodJ.cs @@ -25,6 +25,7 @@ public static uint SetRandomJ(this T enc, PK4 pk, PersonalInfo4 pi, Encounter bool randLevel = MethodJ.IsLevelRand(enc); bool checkProc = MethodJ.IsEncounterCheckApplicable(enc.Type); bool checkLevel = criteria.IsSpecifiedLevelRange() && enc.IsLevelWithinRange(criteria); + bool filterIVs = criteria.IsSpecifiedIVs(2); // Generate Method J correlated PID and IVs, no lead (keep things simple). while (true) @@ -64,6 +65,8 @@ public static uint SetRandomJ(this T enc, PK4 pk, PersonalInfo4 pi, Encounter var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) break; // try again + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; if (enc.Species is (ushort)Species.Unown) { diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodK.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodK.cs index c18dd20ad..4fa390979 100644 --- a/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodK.cs +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/GenerateMethodK.cs @@ -26,6 +26,7 @@ public static uint SetRandomK(this T enc, PK4 pk, PersonalInfo4 pi, Encounter var modulo = enc.Type.IsSafari() ? 10u : 100u; bool checkProc = MethodK.IsEncounterCheckApplicable(enc.Type); bool checkLevel = criteria.IsSpecifiedLevelRange() && enc.IsLevelWithinRange(criteria); + bool filterIVs = criteria.IsSpecifiedIVs(2); // Generate Method K correlated PID and IVs, no lead (keep things simple). while (true) @@ -88,6 +89,8 @@ public static uint SetRandomK(this T enc, PK4 pk, PersonalInfo4 pi, Encounter var iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) break; // try again + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; if (enc.Type is SlotType4.BugContest && !MethodK.IsAny31(iv32) && !MethodK.IsAny31(iv32 >> 16)) break; // try again diff --git a/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/PokewalkerRNG.cs b/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/PokewalkerRNG.cs index 5c7932a48..0cde249e7 100644 --- a/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/PokewalkerRNG.cs +++ b/PKHeX.Core/Legality/RNG/ClassicEra/Gen4/PokewalkerRNG.cs @@ -325,8 +325,14 @@ public static bool GetRandomIVs(EncounterCriteria criteria, out uint iv32) private static bool TryApply(ref uint seed, out uint iv32, in EncounterCriteria criteria) { // Act like a Non-Stroll encounter, generate IV rand() results immediately. - iv32 = PIDGenerator.GetIVsFromSeedSequentialLCRNG(ref seed); - return criteria.IsSatisfiedIVs(iv32); + var lo = LCRNG.Next15(ref seed); + var hi = LCRNG.Next15(ref seed); + iv32 = (hi << 15) | lo; + if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) + return false; + if (!criteria.IsSatisfiedIVs(iv32)) + return false; + return true; } } diff --git a/PKHeX.Core/Legality/RNG/PIDGenerator.cs b/PKHeX.Core/Legality/RNG/PIDGenerator.cs deleted file mode 100644 index 18670a180..000000000 --- a/PKHeX.Core/Legality/RNG/PIDGenerator.cs +++ /dev/null @@ -1,291 +0,0 @@ -using System; -using static PKHeX.Core.PIDType; - -namespace PKHeX.Core; - -/// -/// Contains a collection of methods that mutate the input Pokémon object, usually to obtain a correlation. -/// -public static class PIDGenerator -{ - private static uint SetValuesFromSeedLCRNG(PKM pk, PIDType type, uint seed) - { - var a16 = LCRNG.Next16(ref seed); - var b16 = LCRNG.Next16(ref seed); - if (type is Method_3 or Method_3_Unown) // VBlank skip between PID rand() [RARE] - b16 = LCRNG.Next16(ref seed); - - // Unown has switched order of PID halves, "BA**" - pk.PID = type.IsUnown() ? (a16 << 16) | b16 : (b16 << 16) | a16; - - var c16 = LCRNG.Next15(ref seed); - if (type is Method_2 or Method_2_Unown) // VBlank skip after PID - c16 = LCRNG.Next15(ref seed); - - var d16 = LCRNG.Next15(ref seed); - if (type is Method_4 or Method_4_Unown) // VBlank skip between IVs - d16 = LCRNG.Next15(ref seed); - - uint iv32 = (d16 << 15) | c16; - // Roamers only store lowest 8 bits of IV data; zero out the other bits. - if (type == Method_1_Roamer) - iv32 &= 0xFF; - - pk.SetIVs(iv32); - return d16; - } - - private static uint SetValuesFromSeedBACD(PKM pk, PIDType type, uint seed) - { - uint idxor = pk.TID16 ^ (uint)pk.SID16; - pk.PID = GetPIDFromSeedBACD(ref seed, type, idxor); - SetIVsFromSeedSequentialLCRNG(ref seed, pk); - pk.RefreshAbility((int)(pk.PID & 1)); - return seed; - } - - private static uint GetPIDFromSeedBACD(ref uint seed, PIDType type, uint idXor) => type switch - { - BACD => CommonEvent3.GetRegular(ref seed), - BACD_S => CommonEvent3.GetForceShiny(ref seed, idXor), - BACD_AX => CommonEvent3.GetAntishiny(ref seed, idXor), - _ => CommonEvent3.GetRegularAntishiny(ref seed, idXor), - }; - - public static uint GetIVsFromSeedSequentialLCRNG(ref uint seed) - { - var c16 = LCRNG.Next15(ref seed); - var d16 = LCRNG.Next15(ref seed); - return (d16 << 15) | c16; - } - - public static void SetIVsFromSeedSequentialLCRNG(ref uint seed, PKM pk) - { - var iv32 = GetIVsFromSeedSequentialLCRNG(ref seed); - pk.SetIVs(iv32); - } - - private static uint SetValuesFromSeedXDRNG(PKM pk, uint seed) - { - var species = pk.Species; - switch (species) - { - case (int)Species.Umbreon or (int)Species.Eevee: // Colo Umbreon, XD Eevee - pk.TID16 = (ushort)XDRNG.Next16(ref seed); - pk.SID16 = (ushort)XDRNG.Next16(ref seed); - seed = XDRNG.Next2(seed); // fake PID - break; - case (int)Species.Espeon: // Colo Espeon - var tid = pk.TID16 = (ushort)XDRNG.Next16(ref seed); - var sid = pk.SID16 = (ushort)XDRNG.Next16(ref seed); - - seed = XDRNG.Next5(seed); // skip fakePID, IVs, ability - MethodCXD.GetColoStarterPID(ref seed, tid, sid); - seed = XDRNG.Next2(seed); // fake PID - break; - } - var iv1 = XDRNG.Next15(ref seed); // IV1 - var iv2 = XDRNG.Next15(ref seed); // IV2 - - var iv32 = (iv2 << 15) | iv1; - pk.SetIVs(iv32); - - _ = XDRNG.Next16(ref seed); // Ability - - if (species is (int)Species.Umbreon or (int)Species.Espeon) - { - // Reuse existing logic. Forced-male, never shiny. - pk.PID = MethodCXD.GetColoStarterPID(ref seed, pk.TID16, pk.SID16); - } - else - { - // Generate PID. - var d16 = XDRNG.Next16(ref seed); // PID - var e16 = XDRNG.Next16(ref seed); // PID - pk.PID = (d16 << 16) | e16; - } - return seed; - } - - public static uint SetValuesFromSeedXDRNG_EReader(PKM pk, uint seed) - { - var D = XDRNG.Prev3(seed); // PID - var E = XDRNG.Next(D); // PID - - pk.PID = (D & 0xFFFF0000) | (E >> 16); - return seed; - } - - public static uint SetValuesFromSeedChannel(PKM pk, uint seed) - { - const ushort TID16 = 40122; - var sid = XDRNG.Next16(ref seed); - pk.ID32 = (sid << 16) | TID16; - - var pid1 = XDRNG.Next16(ref seed); - var pid2 = XDRNG.Next16(ref seed); - var pid = (pid1 << 16) | pid2; - if ((pid2 > 7 ? 0 : 1) != (pid1 ^ sid ^ TID16)) - pid ^= 0x80000000; - pk.PID = pid; - - pk.HeldItem = (ushort)((XDRNG.Next16(ref seed) >> 15) + 169u); // 0-Ganlon, 1-Salac - pk.Version = GameVersion.S + (byte)(XDRNG.Next16(ref seed) >> 15); // 0-Sapphire, 1-Ruby - pk.OriginalTrainerGender = (byte)(XDRNG.Next16(ref seed) >> 15); - - var iv32 = XDRNG.GetSequentialIV32(seed); - pk.SetIVs(iv32); - - return seed; - } - - public static void SetValuesFromSeed(PKM pk, PIDType type, uint seed) - { - var method = GetGeneratorMethod(type); - method(pk, seed); - } - - private static Func GetGeneratorMethod(PIDType t) - { - switch (t) - { - case Channel: - return SetValuesFromSeedChannel; - case CXD: - return SetValuesFromSeedXDRNG; - - case Method_1 or Method_2 or Method_3 or Method_4: - case Method_1_Unown or Method_2_Unown or Method_3_Unown or Method_4_Unown: - case Method_1_Roamer: - return (pk, seed) => SetValuesFromSeedLCRNG(pk, t, seed); - - case BACD: - case BACD_S: - case BACD_A: - case BACD_AX: - return (pk, seed) => SetValuesFromSeedBACD(pk, t, seed); - - case PokeSpot: - return SetRandomPokeSpotPID; - - case G5MGShiny: - return SetValuesFromSeedMG5Shiny; - - case Pokewalker: - return (pk, seed) => - { - var pid = pk.PID = PokewalkerRNG.GetPID(pk.TID16, pk.SID16, seed % 24, pk.Gender, pk.PersonalInfo.Gender); - pk.RefreshAbility((int)(pid & 1)); - return seed; - }; - - // others: unimplemented - case CuteCharm: - break; - case ChainShiny: - return SetRandomChainShinyPID; - case G4MGAntiShiny: - break; - } - return (_, seed) => seed; - } - - public static uint SetRandomChainShinyPID(PKM pk, uint seed) - { - pk.PID = ClassicEraRNG.GetChainShinyPID(ref seed, pk.ID32); - SetIVsFromSeedSequentialLCRNG(ref seed, pk); - return seed; - } - - private static uint SetRandomPokeSpotPID(PKM pk, uint seed) - { - var D = XDRNG.Next(seed); // PID - var E = XDRNG.Next(D); // PID - pk.PID = (D & 0xFFFF0000) | (E >> 16); - return E; - } - - public static uint GetMG5ShinyPID(uint gval, uint av, ushort TID16, ushort SID16) - { - uint pid = ((gval ^ TID16 ^ SID16) << 16) | gval; - if ((pid & 0x10000) != av << 16) - pid ^= 0x10000; - return pid; - } - - public static uint SetValuesFromSeedMG5Shiny(PKM pk, uint seed) - { - var gv = seed >> 24; - var av = seed & 1; // arbitrary choice - pk.PID = GetMG5ShinyPID(gv, av, pk.TID16, pk.SID16); - pk.SetRandomIVs(); - return seed; // meh - } - - public static uint SetRandomWildPID4(PKM pk, Nature nature, int ability, byte gender, PIDType type) - { - pk.RefreshAbility(ability); - pk.Gender = gender; - var method = GetGeneratorMethod(type); - - var rnd = Util.Rand; - while (true) - { - var seed = rnd.Rand32(); - method(pk, seed); - if (!IsValidCriteria4(pk, nature, ability, gender)) - continue; - return seed; - } - } - - public static bool IsValidCriteria4(PKM pk, Nature nature, int ability, byte gender) - { - if (pk.GetSaneGender() != gender) - return false; - - if (pk.Nature != nature) - return false; - - if ((pk.EncryptionConstant & 1) != ability) - return false; - - return true; - } - - public static void SetRandomWildPID5(PKM pk, Nature nature, int ability, byte gender, PIDType specific = None) - { - var tidbit = (pk.TID16 ^ pk.SID16) & 1; - pk.RefreshAbility(ability); - pk.Gender = gender; - pk.Nature = nature; - - if (ability == 2) - ability = 0; - - var rnd = Util.Rand; - while (true) - { - uint seed = rnd.Rand32(); - if (specific == G5MGShiny) - { - SetValuesFromSeedMG5Shiny(pk, seed); - seed = pk.PID; - } - else - { - var bitxor = (seed >> 31) ^ (seed & 1); - if (bitxor != tidbit) - seed ^= 1; - } - - if (((seed >> 16) & 1) != ability) - continue; - - pk.PID = seed; - if (pk.GetSaneGender() != gender) - continue; - return; - } - } -} diff --git a/PKHeX.Core/MysteryGifts/PGT.cs b/PKHeX.Core/MysteryGifts/PGT.cs index 2fe1f0a98..e4d1d11eb 100644 --- a/PKHeX.Core/MysteryGifts/PGT.cs +++ b/PKHeX.Core/MysteryGifts/PGT.cs @@ -339,18 +339,16 @@ private static void SetPINGA(PK4 pk4, PersonalInfo4 pi, EncounterCriteria criter pk4.IV32 |= criteria.GetCombinedIVs(); return; } - if (criteria.IsSpecifiedIVs()) - { - criteria.SetRandomIVs(pk4); - return; - } var seed = Util.Rand32(); // reseed, do not have method 1 correlation + bool filterIVs = criteria.IsSpecifiedIVs(2); uint iv32; while (true) { iv32 = ClassicEraRNG.GetSequentialIVs(ref seed); if (criteria.IsSpecifiedHiddenPower() && !criteria.IsSatisfiedHiddenPower(iv32)) continue; + if (filterIVs && !criteria.IsSatisfiedIVs(iv32)) + continue; break; } pk4.IV32 |= iv32;