2022-08-25 06:49:00 +00:00
using System ;
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
using System.Collections.Generic ;
2022-01-03 05:35:59 +00:00
using static System . Buffers . Binary . BinaryPrimitives ;
2019-09-23 23:56:47 +00:00
2022-06-18 18:04:24 +00:00
namespace PKHeX.Core ;
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
public sealed class PK8 : G8PKM
2019-09-23 23:56:47 +00:00
{
2022-06-18 18:04:24 +00:00
private static readonly ushort [ ] Unused =
2019-09-23 23:56:47 +00:00
{
2022-06-18 18:04:24 +00:00
// Alignment bytes
0x17 , 0x1A , 0x1B , 0x23 , 0x33 , 0x3E , 0x3F ,
0x4C , 0x4D , 0x4E , 0x4F ,
0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 ,
0x91 , 0x92 , 0x93 ,
0x9C , 0x9D , 0x9E , 0x9F , 0xA0 , 0xA1 , 0xA2 , 0xA3 , 0xA4 , 0xA5 , 0xA6 , 0xA7 ,
0xC5 ,
2022-11-25 01:42:17 +00:00
0xCE , 0xCF , 0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0xD5 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , // Pokejob
2022-06-18 18:04:24 +00:00
0xE0 , 0xE1 , // Old Console Region / Region
0xE9 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF , 0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 ,
0x115 , 0x11F , // Alignment
0x13D , 0x13E , 0x13F ,
0x140 , 0x141 , 0x142 , 0x143 , 0x144 , 0x145 , 0x146 , 0x147 ,
} ;
public override IReadOnlyList < ushort > ExtraBytes = > Unused ;
2023-01-22 04:02:33 +00:00
public override PersonalInfo8SWSH PersonalInfo = > PersonalTable . SWSH . GetFormEntry ( Species , Form ) ;
public override IPermitRecord Permit = > PersonalInfo ;
2022-06-18 18:04:24 +00:00
public override bool IsNative = > SWSH ;
public override EntityContext Context = > EntityContext . Gen8 ;
public PK8 ( ) = > AffixedRibbon = - 1 ; // 00 would make it show Kalos Champion :)
public PK8 ( byte [ ] data ) : base ( data ) { }
2023-01-22 04:02:33 +00:00
public override PK8 Clone ( ) = > new ( ( byte [ ] ) Data . Clone ( ) ) ;
2022-06-18 18:04:24 +00:00
public void Trade ( ITrainerInfo tr , int Day = 1 , int Month = 1 , int Year = 2015 )
{
if ( IsEgg )
2019-09-23 23:56:47 +00:00
{
2022-06-18 18:04:24 +00:00
// Eggs do not have any modifications done if they are traded
// Apply link trade data, only if it left the OT (ignore if dumped & imported, or cloned, etc)
2023-01-22 04:02:33 +00:00
if ( ( tr . TID16 ! = TID16 ) | | ( tr . SID16 ! = SID16 ) | | ( tr . Gender ! = OT_Gender ) | | ( tr . OT ! = OT_Name ) )
2022-06-18 18:04:24 +00:00
SetLinkTradeEgg ( Day , Month , Year , Locations . LinkTrade6 ) ;
return ;
2019-09-23 23:56:47 +00:00
}
2021-11-22 03:29:07 +00:00
2022-06-18 18:04:24 +00:00
// Process to the HT if the OT of the Pokémon does not match the SAV's OT info.
if ( ! TradeOT ( tr ) )
TradeHT ( tr ) ;
}
2019-09-23 23:56:47 +00:00
2022-06-18 18:04:24 +00:00
public int DynamaxType { get = > ReadUInt16LittleEndian ( Data . AsSpan ( 0x156 ) ) ; set = > WriteUInt16LittleEndian ( Data . AsSpan ( 0x156 ) , ( ushort ) value ) ; }
2019-09-23 23:56:47 +00:00
2022-06-18 18:04:24 +00:00
public void FixMemories ( )
{
if ( IsEgg ) // No memories if is egg.
2019-09-23 23:56:47 +00:00
{
2022-06-18 18:04:24 +00:00
HT_Friendship = HT_TextVar = HT_Memory = HT_Intensity = HT_Feeling = HT_Language = 0 ;
/* OT_Friendship */ OT_TextVar = OT_Memory = OT_Intensity = OT_Feeling = 0 ;
2019-09-23 23:56:47 +00:00
2022-06-18 18:04:24 +00:00
// Clear Handler
HT_Trash . Clear ( ) ;
return ;
2019-09-23 23:56:47 +00:00
}
2022-06-18 18:04:24 +00:00
if ( IsUntraded )
HT_Friendship = HT_TextVar = HT_Memory = HT_Intensity = HT_Feeling = HT_Language = 0 ;
int gen = Generation ;
if ( gen < 6 )
OT_TextVar = OT_Memory = OT_Intensity = OT_Feeling = 0 ;
if ( gen ! = 8 ) // must be transferred via HOME, and must have memories
this . SetTradeMemoryHT8 ( ) ; // not faking HOME tracker.
}
private bool TradeOT ( ITrainerInfo tr )
{
// Check to see if the OT matches the SAV's OT info.
2023-01-22 04:02:33 +00:00
if ( ! ( tr . ID32 = = ID32 & & tr . Gender = = OT_Gender & & tr . OT = = OT_Name ) )
2022-06-18 18:04:24 +00:00
return false ;
CurrentHandler = 0 ;
return true ;
}
private void TradeHT ( ITrainerInfo tr )
{
if ( HT_Name ! = tr . OT )
2019-09-23 23:56:47 +00:00
{
2022-06-18 18:04:24 +00:00
HT_Friendship = 50 ;
HT_Name = tr . OT ;
2019-09-23 23:56:47 +00:00
}
2022-06-18 18:04:24 +00:00
CurrentHandler = 1 ;
HT_Gender = tr . Gender ;
HT_Language = ( byte ) tr . Language ;
this . SetTradeMemoryHT8 ( ) ;
}
// Maximums
2022-08-27 06:43:36 +00:00
public override ushort MaxMoveID = > Legal . MaxMoveID_8 ;
public override ushort MaxSpeciesID = > Legal . MaxSpeciesID_8 ;
2022-06-18 18:04:24 +00:00
public override int MaxAbilityID = > Legal . MaxAbilityID_8 ;
public override int MaxItemID = > Legal . MaxItemID_8 ;
public override int MaxBallID = > Legal . MaxBallID_8 ;
public override int MaxGameID = > Legal . MaxGameID_8 ;
2019-09-23 23:56:47 +00:00
2022-06-18 18:04:24 +00:00
public PB8 ConvertToPB8 ( )
{
var pk = ConvertTo < PB8 > ( ) ;
if ( pk . Egg_Location = = 0 )
pk . Egg_Location = Locations . Default8bNone ;
UnmapLocation ( pk ) ;
return pk ;
}
public override PA8 ConvertToPA8 ( )
{
var pk = base . ConvertToPA8 ( ) ;
UnmapLocation ( pk ) ;
return pk ;
}
2022-05-31 04:43:52 +00:00
2022-06-18 18:04:24 +00:00
private static void UnmapLocation ( PKM pk )
{
switch ( pk . Met_Location )
2022-05-31 04:43:52 +00:00
{
2022-06-18 18:04:24 +00:00
case Locations . HOME_SWLA :
pk . Version = ( int ) GameVersion . PLA ;
// Keep location due to bad transfer logic (official) -- server legal.
break ;
case Locations . HOME_SWBD :
pk . Version = ( int ) GameVersion . BD ;
pk . Met_Location = 0 ; // Load whatever value from the server. We don't know.
break ;
case Locations . HOME_SHSP :
pk . Version = ( int ) GameVersion . SP ;
pk . Met_Location = 0 ; // Load whatever value from the server. We don't know.
break ;
2022-05-31 04:43:52 +00:00
}
2022-06-18 18:04:24 +00:00
}
2022-05-31 04:43:52 +00:00
2022-06-18 18:04:24 +00:00
public override void ResetMoves ( )
{
var learnsets = Legal . LevelUpSWSH ;
var table = PersonalTable . SWSH ;
var index = table . GetFormIndex ( Species , Form ) ;
var learn = learnsets [ index ] ;
2022-08-27 06:43:36 +00:00
Span < ushort > moves = stackalloc ushort [ 4 ] ;
2022-06-18 18:04:24 +00:00
learn . SetEncounterMoves ( CurrentLevel , moves ) ;
SetMoves ( moves ) ;
this . SetMaximumPPCurrent ( moves ) ;
}
2022-08-25 06:49:00 +00:00
public bool IsSideTransfer = > Met_Location is Locations . HOME_SHSP or Locations . HOME_SWBD or Locations . HOME_SWLA ;
2022-06-18 18:04:24 +00:00
public override bool BDSP = > Met_Location is Locations . HOME_SWBD or Locations . HOME_SHSP ;
public override bool LA = > Met_Location is Locations . HOME_SWLA ;
public override bool HasOriginalMetLocation = > base . HasOriginalMetLocation & & ! ( BDSP | | LA ) ;
public void SanitizeImport ( )
{
2023-01-27 03:03:06 +00:00
// BDSP->SWSH: Set the Met Location to the magic Location, set the Egg Location to 0 if -1, otherwise BDSPEgg (0 is a valid location, but no eggs can be EggMet there -- only hatched.)
// PLA->SWSH: Set the Met Location to the magic Location, set the Egg Location to 0 (no eggs in game).
2022-06-18 18:04:24 +00:00
var ver = Version ;
if ( ver is ( int ) GameVersion . SP )
2022-05-31 04:43:52 +00:00
{
2022-06-18 18:04:24 +00:00
Version = ( int ) GameVersion . SH ;
2023-01-27 03:03:06 +00:00
Met_Location = Locations . HOME_SHSP ;
Egg_Location = Egg_Location = = Locations . Default8bNone ? 0 : Locations . HOME_SWSHBDSPEgg ;
2022-05-31 04:43:52 +00:00
}
2022-06-18 18:04:24 +00:00
else if ( ver is ( int ) GameVersion . BD )
2022-05-31 04:43:52 +00:00
{
2022-06-18 18:04:24 +00:00
Version = ( int ) GameVersion . SW ;
2023-01-27 03:03:06 +00:00
Met_Location = Locations . HOME_SWBD ;
Egg_Location = Egg_Location = = Locations . Default8bNone ? 0 : Locations . HOME_SWSHBDSPEgg ;
2022-05-31 04:43:52 +00:00
}
2022-06-18 18:04:24 +00:00
else if ( ver is ( int ) GameVersion . PLA )
2022-05-31 04:43:52 +00:00
{
2023-01-27 03:03:06 +00:00
const ushort met = Locations . HOME_SWLA ;
2022-06-18 18:04:24 +00:00
Version = ( int ) GameVersion . SW ;
2023-01-27 03:03:06 +00:00
Met_Location = met ;
Egg_Location = 0 ; // Everything originating from this game has an Egg Location of 0.
2022-05-31 04:43:52 +00:00
}
2022-06-18 18:04:24 +00:00
if ( Ball > ( int ) Core . Ball . Beast )
Ball = ( int ) Core . Ball . Poke ;
2019-09-23 23:56:47 +00:00
}
2022-11-25 01:42:17 +00:00
public PK9 ConvertToPK9 ( )
{
// Todo: Transfer to PK9
return new PK9 ( ) ;
}
2021-11-20 02:23:49 +00:00
}