diff --git a/PKHeX.Core/Legality/RNG/Util/ShinyUtil.cs b/PKHeX.Core/Legality/RNG/Util/ShinyUtil.cs index f84e800bf..d245dc864 100644 --- a/PKHeX.Core/Legality/RNG/Util/ShinyUtil.cs +++ b/PKHeX.Core/Legality/RNG/Util/ShinyUtil.cs @@ -3,23 +3,57 @@ namespace PKHeX.Core; +/// +/// Logic for checking shiny state. +/// public static class ShinyUtil { + /// + /// Computes a shiny PID from the provided values. + /// + /// Trainer ID. + /// Trainer Secret ID. + /// Entity PID. + /// Shiny XOR type. + /// Shiny PID. public static uint GetShinyPID(in ushort tid, in ushort sid, in uint pid, in uint type) { var low = pid & 0xFFFF; return ((type ^ tid ^ sid ^ low) << 16) | low; } + /// + /// Checks if the PID is shiny. + /// + /// Combined Trainer ID and Secret ID. + /// Entity PID. + /// Comparison threshold. + /// True if shiny, false otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool GetIsShiny(in uint id32, in uint pid, [ConstantExpected(Max = 16, Min = 8)] uint cmp = 16) => GetShinyXor(id32, pid) < cmp; - public static uint GetShinyXor(in uint pid, in uint id32) - { - var xor = pid ^ id32; - return (xor ^ (xor >> 16)) & 0xFFFF; - } + /// + /// Computes the shiny XOR value. + /// + /// Entity PID. + /// Combined Trainer ID and Secret ID. + /// Shiny XOR value. + public static uint GetShinyXor(in uint pid, in uint id32) => GetShinyXor(pid ^ id32); + /// + /// Computes the shiny XOR value. + /// + /// Combined/raw value to compute with. + /// Shiny XOR value. + public static uint GetShinyXor(in uint component) => (component ^ (component >> 16)) & 0xFFFF; + + /// + /// Forces the shiny state of the PID. + /// + /// Indicates if the PID should be shiny. + /// Entity PID. + /// Combined Trainer ID and Secret ID. + /// Shiny XOR type. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ForceShinyState(bool isShiny, ref uint pid, uint id32, in uint xorType) { @@ -35,5 +69,11 @@ public static void ForceShinyState(bool isShiny, ref uint pid, uint id32, in uin } } - public static bool GetIsShinyGB(ushort dv16) => ((dv16 & 0xFFF) == 0xAAA) && (((dv16 >> 12) & 2) != 0); + /// + /// Checks if the PID is shiny. + /// + /// Used for Gen 2. + /// 16-bit DVs. + /// True if shiny, false otherwise. + public static bool GetIsShinyGB(ushort dv16) => (dv16 & 0x2FFF) == 0x2AAA; } diff --git a/PKHeX.Core/Legality/Restrictions/GBRestrictions.cs b/PKHeX.Core/Legality/Restrictions/GBRestrictions.cs index 5155f906c..0eba71041 100644 --- a/PKHeX.Core/Legality/Restrictions/GBRestrictions.cs +++ b/PKHeX.Core/Legality/Restrictions/GBRestrictions.cs @@ -79,6 +79,9 @@ internal static class GBRestrictions _ => false, }; + /// + /// Indicates if the species will always evolve if traded to a Generation 1 player. + /// internal static bool IsTradeEvolution1(ushort species) => species is (int)Kadabra or (int)Machoke or (int)Graveler or (int)Haunter; public static bool RateMatchesEncounter(ushort species, GameVersion version, byte rate) @@ -121,7 +124,7 @@ private static bool GetCatchRateMatchesPreEvolution(PK1 pk, byte rate) /// /// Data to check /// true if it can inhabit, false if it can not. - internal static bool CanInhabitGen1(this PKM pk) + private static bool CanOriginateGen1(this PKM pk) { // Korean Gen2 games can't trade-back because there are no Gen1 Korean games released if (pk.Korean || pk.IsEgg) @@ -131,14 +134,17 @@ internal static bool CanInhabitGen1(this PKM pk) // If you put a Pokémon in the N64 box, the met info is retained, even if you switch over to a Gen1 game to teach it TMs // You can use rare candies from within the lab, so level-up moves from RBY context can be learned this way as well // Stadium 2 is GB Cart Era only (not 3DS Virtual Console). - if (pk is ICaughtData2 {CaughtData: not 0} && !ParseSettings.AllowGBStadium2) + // This method is only called for Encounter enumeration; if it has Gen2 data, it is not a Gen1 encounter. + if (pk is ICaughtData2 {CaughtData: not 0}) return false; // Sanity check species, if it could have existed as a pre-evolution. - ushort species = pk.Species; - if (species <= MaxSpeciesID_1) - return true; - return IsEvolvedFromGen1Species(species); + return CanVisitGen1(pk.Species); + } + + internal static bool CanVisitGen1(ushort species) + { + return species <= MaxSpeciesID_1 || IsEvolvedFromGen1Species(species); } /// @@ -151,7 +157,7 @@ internal static PotentialGBOrigin GetTradebackStatusInitial(PKM pk) return GetTradebackStatusRBY(pk1); if (pk.Format == 2 || pk.VC2) // Check for impossible Tradeback scenarios - return !pk.CanInhabitGen1() ? Gen2Only : Either; + return !pk.CanOriginateGen1() ? Gen2Only : Either; // VC2 is released, we can assume it will be TradebackType.Any. // Is impossible to differentiate a VC1 Pokémon traded to Gen7 after VC2 is available. @@ -183,50 +189,26 @@ private static PotentialGBOrigin GetTradebackStatusRBY(PK1 pk) return Gen1Only; } - public static TimeCapsuleEvaluation IsTimeCapsuleTransferred(PKM pk, ReadOnlySpan moves, IEncounterTemplate enc) + public static TimeCapsuleEvaluation IsTimeCapsuleTransferred(PK1 pk, ReadOnlySpan moves, IEncounterTemplate enc) { - foreach (var z in moves) + if (enc.Generation == 2) + return Transferred21; + + var rate = pk.CatchRate; + if (rate == 0) + return Transferred12; + + if (MoveInfo.IsAnyFromGeneration(2, moves)) { - if (z.Generation == enc.Generation || z.Generation is not (1 or 2)) - continue; - if (pk is PK1 {CatchRate: not 0} g1 && !IsTradebackCatchRate(g1.CatchRate)) + if (pk is {CatchRate: not 0} && !IsTradebackCatchRate(pk.CatchRate)) return BadCatchRate; - return enc.Generation == 2 ? Transferred21 : Transferred12; + return Transferred12; } - if (pk is not GBPKM gb) - { - return enc.Generation switch - { - 1 when pk.VC2 => Transferred12, - 2 when pk.VC1 => Transferred21, - _ => NotTransferred, - }; - } - - if (gb is ICaughtData2 pk2) - { - if (enc.Generation == 1) - return Transferred12; - if (pk2.CaughtData != 0) - return NotTransferred; - if (enc.Version == C) - return Transferred21; - return Indeterminate; - } - - if (gb is PK1 pk1) - { - var rate = pk1.CatchRate; - if (rate == 0) - return Transferred12; - - bool isTradebackItem = IsTradebackCatchRate(rate); - if (IsCatchRateMatchEncounter(enc, pk1)) - return isTradebackItem ? Indeterminate : NotTransferred; - return isTradebackItem ? Transferred12 : BadCatchRate; - } - return Indeterminate; + bool isTradebackItem = IsTradebackCatchRate(rate); + if (IsCatchRateMatchEncounter(enc, pk)) + return isTradebackItem ? Indeterminate : NotTransferred; + return isTradebackItem ? Transferred12 : BadCatchRate; } private static bool IsCatchRateMatchEncounter(IEncounterTemplate enc, PK1 pk1) => enc switch @@ -291,14 +273,3 @@ public enum TimeCapsuleEvaluation /// BadCatchRate, } - -/// -/// Extension methods for . -/// -public static class TimeCapsuleEvlautationExtensions -{ - /// - /// Indicates if the definitely transferred via Time Capsule. - /// - public static bool WasTimeCapsuleTransferred(this TimeCapsuleEvaluation eval) => eval is not (Indeterminate or NotTransferred or BadCatchRate); -} diff --git a/PKHeX.Core/Saves/Abstractions/SimpleTrainerInfo.cs b/PKHeX.Core/Saves/Abstractions/SimpleTrainerInfo.cs index 9b0c31ed1..854b28810 100644 --- a/PKHeX.Core/Saves/Abstractions/SimpleTrainerInfo.cs +++ b/PKHeX.Core/Saves/Abstractions/SimpleTrainerInfo.cs @@ -42,16 +42,12 @@ public SimpleTrainerInfo(ITrainerInfo other, GameVersion specified) : this(speci Language = other.Language; Generation = other.Generation; Context = other.Context; - if (Context is not (EntityContext.Gen6 or EntityContext.Gen7)) - { - ConsoleRegion = Region = Country = 0; - } - else if (other is IRegionOriginReadOnly r) - { - ConsoleRegion = r.ConsoleRegion; - Region = r.Region; - Country = r.Country; - } + return; + + var geo = other.GetRegionOrigin(other.Language); + ConsoleRegion = geo.ConsoleRegion; + Region = geo.Region; + Country = geo.Country; } }