From 3d7a371395ff3c3cec81b46e5c2cce4cd47f22b5 Mon Sep 17 00:00:00 2001 From: Kurt Date: Wed, 22 Apr 2026 23:37:11 -0500 Subject: [PATCH] Gen3: encounter legality tweaks Gen3-5: Egg (breeding) requesting shiny now loops to match criteria. Gen3: Add evolution nickname bypass for trash byte application Gen3: Fix japanese OT prefill check from not actually working Gen3: Add missing Voltorb encounter (was superseded by Wild slot, but aggressive checks since added). --- .../Encounters/Data/Gen3/Encounters3RSE.cs | 1 + .../Encounters/Templates/Gen3/EncounterEgg3.cs | 8 ++++++-- .../Encounters/Templates/Gen4/EncounterEgg4.cs | 8 ++++++-- .../Encounters/Templates/Gen5/EncounterEgg5.cs | 7 +++++-- .../Legality/Verifiers/Misc/MiscVerifierG3.cs | 17 +++++++++++++---- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/PKHeX.Core/Legality/Encounters/Data/Gen3/Encounters3RSE.cs b/PKHeX.Core/Legality/Encounters/Data/Gen3/Encounters3RSE.cs index 533915fd2..682124df2 100644 --- a/PKHeX.Core/Legality/Encounters/Data/Gen3/Encounters3RSE.cs +++ b/PKHeX.Core/Legality/Encounters/Data/Gen3/Encounters3RSE.cs @@ -61,6 +61,7 @@ private static EncounterArea3[] GetSwarm([ConstantExpected] string resource, [Le // Stationary new(352, 30, RSE) { Location = 034 }, // Kecleon @ Route 119 new(352, 30, RSE) { Location = 035 }, // Kecleon @ Route 120 + new(100, 25, RSE) { Location = 062 }, // Voltorb @ New Mauville // Stationary Lengendary new(377, 40, RSE) { Location = 082 }, // Regirock @ Desert Ruins diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterEgg3.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterEgg3.cs index 2699b8e00..8ec1e5170 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterEgg3.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen3/EncounterEgg3.cs @@ -79,14 +79,14 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) // Get a random PID that matches gender/nature/ability criteria var pi = PersonalTable.E[Species]; var gr = pi.Gender; - var pid = GetRandomPID(criteria, gr); + var pid = GetRandomPID(criteria, gr, tr.ID32); pk.PID = pid; pk.RefreshAbility((int)(pid % 2)); return pk; } - private uint GetRandomPID(in EncounterCriteria criteria, byte gr) + private uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id32) { var seed = Util.Rand32(); while (true) @@ -109,6 +109,10 @@ private uint GetRandomPID(in EncounterCriteria criteria, byte gr) if (!Daycare3.IsValidProcPID(pid, Version)) continue; // 0-value PID is invalid + var shiny = ShinyUtil.GetIsShiny3(id32, pid); + if (criteria.Shiny.IsShiny() != shiny) + continue; + return pid; } } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterEgg4.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterEgg4.cs index 94ce77293..064dae4f5 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterEgg4.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen4/EncounterEgg4.cs @@ -84,7 +84,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) // Get a random PID that matches gender/nature/ability criteria var pi = PersonalTable.HGSS[Species]; var gr = pi.Gender; - var pid = GetRandomPID(criteria, gr, out var gender); + var pid = GetRandomPID(criteria, gr, tr.ID32, out var gender); pk.PID = pid; pk.Gender = gender; pk.RefreshAbility((int)(pid & 1)); @@ -92,7 +92,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) return pk; } - private uint GetRandomPID(in EncounterCriteria criteria, byte gr, out byte gender) + private uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id32, out byte gender) { var seed = Util.Rand32(); while (true) @@ -115,6 +115,10 @@ private uint GetRandomPID(in EncounterCriteria criteria, byte gr, out byte gende // PID is rolled forward upon picking up the egg. // Not worth skipping 0-value PIDs. Too rare to be worth trying again, since it can be a valid PID. + var shiny = ShinyUtil.GetIsShiny3(id32, pid); + if (criteria.Shiny.IsShiny() != shiny) + continue; + return pid; } } diff --git a/PKHeX.Core/Legality/Encounters/Templates/Gen5/EncounterEgg5.cs b/PKHeX.Core/Legality/Encounters/Templates/Gen5/EncounterEgg5.cs index 3ff338d65..a0416eed1 100644 --- a/PKHeX.Core/Legality/Encounters/Templates/Gen5/EncounterEgg5.cs +++ b/PKHeX.Core/Legality/Encounters/Templates/Gen5/EncounterEgg5.cs @@ -72,7 +72,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) var pi = PersonalTable.B2W2[Species]; var gr = pi.Gender; var ability = criteria.GetAbilityFromNumber(Ability); - var pid = GetRandomPID(criteria, gr, out var gender); + var pid = GetRandomPID(criteria, gr, tr.ID32, out var gender); pid = (pid & 0xFFFEFFFFu) | (uint)(ability & 1) << 16; // 0x00000000 or 0x00010000 pk.PID = pid; pk.Gender = gender; @@ -81,7 +81,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) return pk; } - private static uint GetRandomPID(in EncounterCriteria criteria, byte gr, out byte gender) + private static uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id32, out byte gender) { var seed = Util.Rand32(); while (true) @@ -91,6 +91,9 @@ private static uint GetRandomPID(in EncounterCriteria criteria, byte gr, out byt gender = EntityGender.GetFromPIDAndRatio(pid, gr); if (criteria.IsSpecifiedGender() && !criteria.IsSatisfiedGender(gender)) continue; + var shiny = ShinyUtil.GetIsShiny3(id32, pid); + if (criteria.Shiny.IsShiny() != shiny) + continue; return pid; } } diff --git a/PKHeX.Core/Legality/Verifiers/Misc/MiscVerifierG3.cs b/PKHeX.Core/Legality/Verifiers/Misc/MiscVerifierG3.cs index d6f4f062d..acf4a9553 100644 --- a/PKHeX.Core/Legality/Verifiers/Misc/MiscVerifierG3.cs +++ b/PKHeX.Core/Legality/Verifiers/Misc/MiscVerifierG3.cs @@ -139,7 +139,11 @@ private static void FlagIsNicknameClean(LegalityAnalysis data, PK3 pk) // International games are 10 chars (full buffer) max; implicit terminator if full. var nick = pk.GetNicknamePrefillRegion(); if (!TrashByteRules3.IsTerminatedFF(nick)) - data.AddLine(GetInvalid(Trainer, TrashBytesMismatchInitial)); + { + // Trade to another language and evolve will treat it like a nickname, without actually filling with FF. + if (!TrashByteRules3.IsTerminatedFFZero(nick) || pk.Species == data.EncounterOriginal.Species) // not evolved + data.AddLine(GetInvalid(Trainer, TrashBytesMismatchInitial)); + } } } @@ -192,9 +196,15 @@ public static bool IsTerminatedFF(ReadOnlySpan data) var first = TrashBytes8.GetTerminatorIndex(data); if (first == -1 || first >= data.Length - 1) return true; - return !data[(first + 1)..].ContainsAnyExcept(0xFF); + return !data[(first + 1)..].ContainsAnyExcept(Terminator); } + /// + /// Checks if the matches the pattern of a pre-filled array with terminators of count . + /// + /// + /// + /// public static bool IsTerminatedFFZero(ReadOnlySpan data, int preFill = 0) { if (preFill == 0) @@ -211,8 +221,7 @@ public static bool IsTerminatedFFZero(ReadOnlySpan data, int preFill = 0) if (inner.ContainsAnyExcept(Terminator)) return false; first = preFill; - first++; - if (first >= data.Length - 1) + if (first >= data.Length) return true; } return !data[first..].ContainsAnyExcept(0);