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;