PKHeX/PKHeX.Core/PKM/Shared/G3PKM.cs
Kurt 94f3937f2f Refactor: extract gen3 save block structures
Changed: Inventory editor no longer needs to clone the save file on GUI open
Changed: some method signatures have moved from SAV3* to the specific block
Allows the block structures to be used without a SAV3 object
Allows the Inventory editor to open from a blank save file.
2026-03-14 13:40:17 -05:00

229 lines
9.2 KiB
C#

using System;
using System.Diagnostics.CodeAnalysis;
namespace PKHeX.Core;
/// <summary>
/// Generation 3 Base <see cref="PKM"/> Class
/// </summary>
public abstract class G3PKM : PKM, IRibbonSetEvent3, IRibbonSetCommon3, IRibbonSetUnique3, IRibbonSetOnly3, IRibbonSetRibbons, IContestStats, IAppliedMarkings3
{
protected G3PKM(Memory<byte> data) : base(data) { }
protected G3PKM([ConstantExpected] int size) : base(size) { }
public abstract override PersonalInfo3 PersonalInfo { get; }
// Maximums
public sealed override ushort MaxMoveID => Legal.MaxMoveID_3;
public sealed override ushort MaxSpeciesID => Legal.MaxSpeciesID_3;
public sealed override int MaxAbilityID => Legal.MaxAbilityID_3;
public sealed override int MaxItemID => Legal.MaxItemID_3_E;
public sealed override int MaxBallID => Legal.MaxBallID_3;
public sealed override GameVersion MaxGameID => Legal.MaxGameID_3;
public sealed override int MaxIV => 31;
public sealed override int MaxEV => EffortValues.Max255;
public sealed override int MaxStringLengthTrainer => 7;
public sealed override int MaxStringLengthNickname => 10;
// Generated Attributes
public sealed override uint PSV => ((PID >> 16) ^ (PID & 0xFFFF)) >> 3;
public sealed override uint TSV => (uint)(TID16 ^ SID16) >> 3;
public sealed override bool Japanese => Language == (int)LanguageID.Japanese;
public sealed override int Ability { get => PersonalInfo.GetAbility(AbilityBit); set { } }
public sealed override uint EncryptionConstant { get => PID; set { } }
public sealed override Nature Nature { get => (Nature)(PID % 25); set { } }
public sealed override bool IsNicknamed { get => SpeciesName.IsNicknamed(Species, Nickname, Language, 3); set { } }
public sealed override byte Gender { get => EntityGender.GetFromPID(Species, PID); set { } }
public sealed override int Characteristic => -1;
public sealed override byte CurrentFriendship { get => OriginalTrainerFriendship; set => OriginalTrainerFriendship = value; }
public sealed override byte CurrentHandler { get => 0; set { } }
public sealed override ushort EggLocation { get => 0; set { } }
public int MarkingCount => 4;
public abstract byte MarkingValue { get; set; }
public bool GetMarking(int index)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MarkingCount);
return ((MarkingValue >> index) & 1) != 0;
}
public void SetMarking(int index, bool value)
{
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)MarkingCount);
MarkingValue = (byte)((MarkingValue & ~(1 << index)) | ((value ? 1 : 0) << index));
}
public bool MarkingCircle { get => GetMarking(0); set => SetMarking(0, value); }
public bool MarkingTriangle { get => GetMarking(1); set => SetMarking(1, value); } // Purposefully reverse because we swap bits already and want to match Gen4+
public bool MarkingSquare { get => GetMarking(2); set => SetMarking(2, value); }
public bool MarkingHeart { get => GetMarking(3); set => SetMarking(3, value); }
public abstract ushort SpeciesInternal { get; set; } // raw access
public sealed override byte Form
{
get => Species == (int)Core.Species.Unown ? EntityPID.GetUnownForm3(PID) : (byte)0;
set
{
if (Species != (int)Core.Species.Unown)
return;
var rnd = Util.Rand;
while (EntityPID.GetUnownForm3(PID) != value)
PID = rnd.Rand32();
}
}
public sealed override int AbilityNumber { get => 1 << (AbilityBit ? 1 : 0); set => AbilityBit = value > 1; } // [0,1]->[1,2] ; [1,x]->[0,1]
public abstract bool AbilityBit { get; set; }
public sealed override void RefreshAbility(int n)
{
if (Species is (int)Core.Species.Granbull or (int)Core.Species.Vibrava or (int)Core.Species.Flygon)
return;
AbilityBit = n == 1 && PersonalInfo.HasSecondAbility;
}
public override bool Valid { get => true; set { } }
public override void RefreshChecksum() { }
public override bool ChecksumValid => Valid;
public abstract bool RibbonEarth { get; set; }
public abstract bool RibbonNational { get; set; }
public abstract bool RibbonCountry { get; set; }
public abstract bool RibbonChampionBattle { get; set; }
public abstract bool RibbonChampionRegional { get; set; }
public abstract bool RibbonChampionNational { get; set; }
public abstract bool RibbonChampionG3 { get; set; }
public abstract bool RibbonArtist { get; set; }
public abstract bool RibbonEffort { get; set; }
public abstract bool RibbonWinning { get; set; }
public abstract bool RibbonVictory { get; set; }
public abstract byte RibbonCountG3Cool { get; set; }
public abstract byte RibbonCountG3Beauty { get; set; }
public abstract byte RibbonCountG3Cute { get; set; }
public abstract byte RibbonCountG3Smart { get; set; }
public abstract byte RibbonCountG3Tough { get; set; }
public abstract bool RibbonWorld { get; set; }
public abstract bool Unused1 { get; set; }
public abstract bool Unused2 { get; set; }
public abstract bool Unused3 { get; set; }
public abstract bool Unused4 { get; set; }
public abstract int RibbonCount { get; }
public abstract byte ContestCool { get; set; }
public abstract byte ContestBeauty { get; set; }
public abstract byte ContestCute { get; set; }
public abstract byte ContestSmart { get; set; }
public abstract byte ContestTough { get; set; }
public abstract byte ContestSheen { get; set; }
/// <summary>
/// Swaps bits at a given position
/// </summary>
/// <param name="value">Value to swap bits for</param>
/// <param name="p1">Position of first bit to be swapped</param>
/// <param name="p2">Position of second bit to be swapped</param>
/// <remarks>Generation 3 marking values are swapped (Square-Triangle, instead of Triangle-Square).</remarks>
/// <returns>Swapped bits value</returns>
protected static int SwapBits(int value, int p1, int p2)
{
int bit1 = (value >> p1) & 1;
int bit2 = (value >> p2) & 1;
int x = bit1 ^ bit2;
x = (x << p1) | (x << p2);
return value ^ x;
}
protected static GameVersion GetGBAVersionID(GCVersion gc) => gc.GetG3VersionID();
protected static GCVersion GetGCVersionID(GameVersion gba) => gba.GetCXDVersionID();
/// <summary>
/// Interconversion for Generation 3 <see cref="PKM"/> formats.
/// </summary>
/// <typeparam name="T">Generation 3 format to convert to</typeparam>
/// <returns>New object with transferred properties.</returns>
protected T ConvertTo<T>() where T : G3PKM, new() => new()
{
SpeciesInternal = SpeciesInternal,
Language = Language,
PID = PID,
TID16 = TID16,
SID16 = SID16,
EXP = EXP,
HeldItem = HeldItem,
AbilityBit = AbilityBit,
IsEgg = IsEgg,
FatefulEncounter = FatefulEncounter,
MetLocation = MetLocation,
MetLevel = MetLevel,
Version = Version,
Ball = Ball,
// Handle string conversion in derived classes
//Nickname = Nickname,
//OriginalTrainerName = OriginalTrainerName,
OriginalTrainerGender = OriginalTrainerGender,
OriginalTrainerFriendship = OriginalTrainerFriendship,
Move1_PPUps = Move1_PPUps,
Move2_PPUps = Move2_PPUps,
Move3_PPUps = Move3_PPUps,
Move4_PPUps = Move4_PPUps,
Move1 = Move1,
Move2 = Move2,
Move3 = Move3,
Move4 = Move4,
Move1_PP = Move1_PP,
Move2_PP = Move2_PP,
Move3_PP = Move3_PP,
Move4_PP = Move4_PP,
IV_HP = IV_HP,
IV_ATK = IV_ATK,
IV_DEF = IV_DEF,
IV_SPE = IV_SPE,
IV_SPA = IV_SPA,
IV_SPD = IV_SPD,
EV_HP = EV_HP,
EV_ATK = EV_ATK,
EV_DEF = EV_DEF,
EV_SPE = EV_SPE,
EV_SPA = EV_SPA,
EV_SPD = EV_SPD,
ContestCool = ContestCool,
ContestBeauty = ContestBeauty,
ContestCute = ContestCute,
ContestSmart = ContestSmart,
ContestTough = ContestTough,
ContestSheen = ContestSheen,
PokerusDays = PokerusDays,
PokerusStrain = PokerusStrain,
// Transfer Ribbons
RibbonCountG3Cool = RibbonCountG3Cool,
RibbonCountG3Beauty = RibbonCountG3Beauty,
RibbonCountG3Cute = RibbonCountG3Cute,
RibbonCountG3Smart = RibbonCountG3Smart,
RibbonCountG3Tough = RibbonCountG3Tough,
RibbonChampionG3 = RibbonChampionG3,
RibbonWinning = RibbonWinning,
RibbonVictory = RibbonVictory,
RibbonArtist = RibbonArtist,
RibbonEffort = RibbonEffort,
RibbonChampionBattle = RibbonChampionBattle,
RibbonChampionRegional = RibbonChampionRegional,
RibbonChampionNational = RibbonChampionNational,
RibbonCountry = RibbonCountry,
RibbonNational = RibbonNational,
RibbonEarth = RibbonEarth,
RibbonWorld = RibbonWorld,
Unused1 = Unused1,
Unused2 = Unused2,
Unused3 = Unused3,
Unused4 = Unused4,
};
}