diff --git a/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs b/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs index 36bcbbb3f..16aa2d90d 100644 --- a/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs @@ -47,6 +47,8 @@ private CheckResult VerifyBall(LegalityAnalysis data) // Only Gen3 origin Shedinja can copy the wild ball. // Evolution chains will indicate if it could have existed as Shedinja in Gen3. // The special move verifier has a similar check! + if (pkm.HGSS && pkm.Ball == (int)Sport) // Can evolve in DP to retain the HG/SS ball -- not able to be captured in any other ball + return VerifyBallEquals(data, (int)Sport); if (Info.Generation != 3 || Info.EvoChainsAllGens[3].Count != 2) return VerifyBallEquals(data, (int)Poke); // Pokeball Only } diff --git a/PKHeX.Core/PKM/BK4.cs b/PKHeX.Core/PKM/BK4.cs index 70f99eda3..9862d0058 100644 --- a/PKHeX.Core/PKM/BK4.cs +++ b/PKHeX.Core/PKM/BK4.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace PKHeX.Core { @@ -305,34 +304,12 @@ public override int Met_Location private byte PKRS { get => Data[0x82]; set => Data[0x82] = value; } public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); } public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | (value << 4)); } - - public override int Ball - { - get => Math.Max(Data[0x86], Data[0x83]); - // Pokemon obtained in HGSS have the HGSS ball set (@0x86) - // However, this info is not set when receiving a wondercard! - // The PGT contains a preformatted PK4 file, which is slightly modified. - // No HGSS balls were used, and no HGSS ball info is set. - - // Sneaky way = return the higher of the two values. - - set - { - // Ball to display in DPPt - Data[0x83] = (byte)(value <= 0x10 ? value : 4); // Cap at Cherish Ball - - // HGSS Exclusive Balls -- If the user wants to screw things up, let them. Any legality checking could catch hax. - if (value > 0x10 || (HGSS && !FatefulEncounter)) - Data[0x86] = (byte)(value <= 0x18 ? value : 4); // Cap at Comp Ball - else - Data[0x86] = 0; // Unused - } - } - + public override byte BallDPPt { get => Data[0x83]; set => Data[0x83] = value; } public override int Met_Level { get => Data[0x84] >> 1; set => Data[0x84] = (byte)((Data[0x84] & 0x1) | value << 1); } public override int OT_Gender { get => Data[0x84] & 1; set => Data[0x84] = (byte)((Data[0x84] & ~0x1) | (value & 1)); } public override GroundTileType GroundTile { get => (GroundTileType)Data[0x85]; set => Data[0x85] = (byte)value; } - // Unused 0x87 + public override byte BallHGSS { get => Data[0x86]; set => Data[0x86] = value; } + public override byte PokéathlonStat { get => Data[0x87]; set => Data[0x87] = value; } #endregion // Not stored @@ -355,18 +332,6 @@ protected override ushort CalculateChecksum() return chk; } - // Synthetic Trading Logic - public bool Trade(string SAV_Trainer, int SAV_TID, int SAV_SID, int SAV_GENDER, int Day = 1, int Month = 1, int Year = 2009) - { - // Eggs do not have any modifications done if they are traded - if (IsEgg && !(SAV_Trainer == OT_Name && SAV_TID == TID && SAV_SID == SID && SAV_GENDER == OT_Gender)) - { - SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade4); - return true; - } - return false; - } - protected override byte[] Encrypt() { RefreshChecksum(); diff --git a/PKHeX.Core/PKM/PK4.cs b/PKHeX.Core/PKM/PK4.cs index 5ca903862..66fbf642e 100644 --- a/PKHeX.Core/PKM/PK4.cs +++ b/PKHeX.Core/PKM/PK4.cs @@ -283,35 +283,12 @@ public override int Met_Location private byte PKRS { get => Data[0x82]; set => Data[0x82] = value; } public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); } public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | (value << 4)); } - - public override int Ball - { - get => - // Pokemon obtained in HGSS have the HGSS ball set (@0x86) - // However, this info is not set when receiving a wondercard! - // The PGT contains a preformatted PK4 file, which is slightly modified. - // No HGSS balls were used, and no HGSS ball info is set. - - // Sneaky way = return the higher of the two values. - Math.Max(Data[0x86], Data[0x83]); - set - { - // Ball to display in DPPt - Data[0x83] = (byte)(value <= 0x10 ? value : 4); // Cap at Cherish Ball - - // HGSS Exclusive Balls -- If the user wants to screw things up, let them. Any legality checking could catch hax. - if (value > 0x10 || (HGSS && !FatefulEncounter)) - Data[0x86] = (byte)(value <= 0x18 ? value : 4); // Cap at Comp Ball - else - Data[0x86] = 0; // Unused - } - } - + public override byte BallDPPt { get => Data[0x83]; set => Data[0x83] = value; } public override int Met_Level { get => Data[0x84] & ~0x80; set => Data[0x84] = (byte)((Data[0x84] & 0x80) | value); } public override int OT_Gender { get => Data[0x84] >> 7; set => Data[0x84] = (byte)((Data[0x84] & ~0x80) | value << 7); } public override GroundTileType GroundTile { get => (GroundTileType)Data[0x85]; set => Data[0x85] = (byte)value; } - public byte PokéathlonStat { get => Data[0x87]; set => Data[0x87] = value; } - // Unused 0x87 + public override byte BallHGSS { get => Data[0x86]; set => Data[0x86] = value; } + public override byte PokéathlonStat { get => Data[0x87]; set => Data[0x87] = value; } #endregion #region Battle Stats @@ -337,18 +314,6 @@ protected override byte[] Encrypt() return PokeCrypto.EncryptArray45(Data); } - // Synthetic Trading Logic - public bool Trade(string SAV_Trainer, int SAV_TID, int SAV_SID, int SAV_GENDER, int Day = 1, int Month = 1, int Year = 2009) - { - // Eggs do not have any modifications done if they are traded - if (IsEgg && !(SAV_Trainer == OT_Name && SAV_TID == TID && SAV_SID == SID && SAV_GENDER == OT_Gender)) - { - SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade4); - return true; - } - return false; - } - public BK4 ConvertToBK4() { BK4 bk4 = ConvertTo(); @@ -424,6 +389,10 @@ public PK5 ConvertToPK5() if (Array.IndexOf(banned, Move4) != -1) Move4 = 0; pk5.FixMoves(); + // D/P(not Pt)/HG/SS created Shedinja forget to set Gender to Genderless. + if (pk5.Species is (int)Core.Species.Shedinja) + pk5.Gender = 2; // Genderless + pk5.RefreshChecksum(); return pk5; } diff --git a/PKHeX.Core/PKM/Shared/G4PKM.cs b/PKHeX.Core/PKM/Shared/G4PKM.cs index c28686c16..ff0883308 100644 --- a/PKHeX.Core/PKM/Shared/G4PKM.cs +++ b/PKHeX.Core/PKM/Shared/G4PKM.cs @@ -165,6 +165,46 @@ public sealed override int Characteristic public abstract byte CNT_Sheen { get; set; } public abstract GroundTileType GroundTile { get; set; } + public abstract byte BallDPPt { get; set; } + public abstract byte BallHGSS { get; set; } + public abstract byte PokéathlonStat { get; set; } + + public sealed override int Ball + { + // HG/SS added new ball IDs mid-generation, and the previous Gen4 games couldn't handle invalid ball values. + // Pokémon obtained in HG/SS have the HG/SS ball value set (@0x86) to the capture ball. + // However, this info is not set in event gift data! + // Event gift data contains a pre-formatted PK4 template, which is slightly mutated. + // No HG/SS ball values were used in these event gifts, and no HG/SS ball values are set (0). + + // To get the display ball (assume HG/SS +), return the higher of the two values. + get => Math.Max(BallHGSS, BallDPPt); + set + { + static byte Clamp(int value, Ball max) => (uint)value <= (uint)max ? (byte)value : (byte)Core.Ball.Poke; + + // Ball to display in DPPt + BallDPPt = Clamp(value, Core.Ball.Cherish); + + // Only set the HG/SS value if it originated in HG/SS and was not an event. + if (!HGSS || FatefulEncounter) + BallHGSS = 0; + else + BallHGSS = Clamp(value, Core.Ball.Sport); + } + } + + // Synthetic Trading Logic + public bool Trade(string SAV_Trainer, int SAV_TID, int SAV_SID, int SAV_GENDER, int Day = 1, int Month = 1, int Year = 2009) + { + // Eggs do not have any modifications done if they are traded + if (IsEgg && !(SAV_Trainer == OT_Name && SAV_TID == TID && SAV_SID == SID && SAV_GENDER == OT_Gender)) + { + SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade4); + return true; + } + return false; + } protected T ConvertTo() where T : G4PKM, new() { @@ -222,8 +262,10 @@ protected T ConvertTo() where T : G4PKM, new() Version = Version, PKRS_Days = PKRS_Days, PKRS_Strain = PKRS_Strain, - Ball = Ball, + BallDPPt = BallDPPt, + BallHGSS = BallHGSS, GroundTile = GroundTile, + PokéathlonStat = PokéathlonStat, FatefulEncounter = FatefulEncounter, Met_Level = Met_Level,