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;
}
}