Update gen4 Shedinja handling

PK4->PK5 fixes gendered Shedinja -> genderless.
Clean up PK4 ball value setters and expose those 2 properties separately.
Add edge case permission for HG/SS Sport Ball Shedinja being legal if evolved on DP (DP doesn't change 0x86, lol)
Copy PokéathlonStat and ball values individually for BK4<->PK4 conversion

Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
This commit is contained in:
Kurt 2021-12-26 20:13:36 -08:00
parent f50684b794
commit 24e1898410
4 changed files with 56 additions and 78 deletions

View file

@ -47,6 +47,8 @@ namespace PKHeX.Core
// 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
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -305,34 +304,12 @@ namespace PKHeX.Core
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 @@ namespace PKHeX.Core
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();

View file

@ -283,35 +283,12 @@ namespace PKHeX.Core
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 @@ namespace PKHeX.Core
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<BK4>();
@ -424,6 +389,10 @@ namespace PKHeX.Core
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;
}

View file

@ -165,6 +165,46 @@ namespace PKHeX.Core
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<T>() where T : G4PKM, new()
{
@ -222,8 +262,10 @@ namespace PKHeX.Core
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,