Initial support for Pokémon HOME 3.0.0 (#3899)

* Heavily rewrites the `PKH` abstractions.
* Uses HOME's core-side classes as the transfer middlemen instead of direct A->B transfers.
* Revises logic to account for most of HOME's quirks (scale/height copying, safe refuge PLA)

Future revisions hinge on better handling of evotree (need better metadata about existing as specific evolutions in each game).

---------

Co-authored-by: sora10pls <17801814+sora10pls@users.noreply.github.com>
Co-authored-by: Lusamine <30205550+Lusamine@users.noreply.github.com>
This commit is contained in:
Kurt 2023-06-03 18:19:16 -07:00 committed by GitHub
parent 92258f6755
commit 1174e354b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
75 changed files with 1829 additions and 1548 deletions

View file

@ -64,4 +64,6 @@ public static class BallExtensions
/// <param name="ball">Ball ID</param>
/// <returns>True if Apricorn, false if not.</returns>
public static bool IsApricornBall(this Ball ball) => ball is >= Ball.Fast and <= Ball.Moon;
public static bool IsLegendBall(this Ball ball) => ball is >= Ball.LAPoke and <= Ball.LAOrigin;
}

View file

@ -156,10 +156,12 @@ public sealed class MetDataSource
Util.AddCBWithOffset(locations, s.Gen8.Met6, 60000, Locations8.Met6);
// Add in the BDSP+PLA magic met locations.
locations.Add(new ComboItem($"{s.EggName} (BD/SP)", Locations.HOME_SWSHBDSPEgg));
locations.Add(new ComboItem(s.gamelist[(int)BD], Locations.HOME_SWBD));
locations.Add(new ComboItem(s.gamelist[(int)SP], Locations.HOME_SHSP));
locations.Add(new ComboItem(s.gamelist[(int)PLA], Locations.HOME_SWLA));
locations.Add(new ComboItem($"{s.EggName} (HOME)", LocationsHOME.SWSHEgg));
locations.Add(new ComboItem(s.gamelist[(int)SL], LocationsHOME.SWSL));
locations.Add(new ComboItem(s.gamelist[(int)VL], LocationsHOME.SHVL));
locations.Add(new ComboItem(s.gamelist[(int)BD], LocationsHOME.SWBD));
locations.Add(new ComboItem(s.gamelist[(int)SP], LocationsHOME.SHSP));
locations.Add(new ComboItem(s.gamelist[(int)PLA], LocationsHOME.SWLA));
return locations;
}
@ -174,10 +176,10 @@ public sealed class MetDataSource
Util.AddCBWithOffset(locations, s.Gen8a.Met6, 60000, Locations8a.Met6);
// Add in the BDSP+PLA magic met locations.
locations.Add(new ComboItem($"{s.EggName} (BD/SP)", Locations.HOME_SWSHBDSPEgg));
locations.Add(new ComboItem(s.gamelist[(int)BD], Locations.HOME_SWBD));
locations.Add(new ComboItem(s.gamelist[(int)SP], Locations.HOME_SHSP));
locations.Add(new ComboItem(s.gamelist[(int)PLA], Locations.HOME_SWLA));
locations.Add(new ComboItem($"{s.EggName} (HOME)", LocationsHOME.SWSHEgg));
locations.Add(new ComboItem(s.gamelist[(int)BD], LocationsHOME.SWBD));
locations.Add(new ComboItem(s.gamelist[(int)SP], LocationsHOME.SHSP));
locations.Add(new ComboItem(s.gamelist[(int)PLA], LocationsHOME.SWLA));
return locations;
}

View file

@ -238,14 +238,16 @@ public static class GameUtil
var max = obj.MaxGameID;
if (max == Legal.MaxGameID_7b) // edge case
return new[] {GO, GP, GE};
if (max == Legal.MaxGameID_8)
max = Legal.MaxGameID_8a;
var versions = GameVersions
.Where(version => (GameVersion)obj.MinGameID <= version && version <= (GameVersion)max);
if (generation < 0)
return versions;
if (max == Legal.MaxGameID_7 && generation == 7)
versions = versions.Where(version => version != GO);
// HOME allows up-reach to Gen9
if (generation >= 8)
generation = 9;
return versions.Where(version => version.GetGeneration() <= generation);
}
}

View file

@ -103,45 +103,9 @@ public static class Locations
/// <summary> Generation 8 Gift from Pokémon HOME </summary>
public const ushort HOME8 = 30018;
public const ushort HOME_SHSP = 59998; // SP traded to (SW)SH
public const ushort HOME_SWBD = 59999; // BD traded to SW(SH)
public const ushort HOME_SWLA = 60000; // PLA traded to SW(SH)
public const ushort HOME_SWSHBDSPEgg = 65534; // -2 = 8bNone-1..
/// <summary> Generation 8 BD/SP Magic location for "None" since 0 is an actual met location. </summary>
public const ushort Default8bNone = 65535;
/// <summary>
/// Gets the SW/SH-context <see cref="GameVersion"/> when an external entity from the input <see cref="ver"/> resides in SW/SH.
/// </summary>
public static int GetVersionSWSH(int ver) => (GameVersion)ver switch
{
GameVersion.PLA => (int)GameVersion.SW,
GameVersion.BD => (int)GameVersion.SW,
GameVersion.SP => (int)GameVersion.SH,
_ => ver,
};
/// <summary>
/// Gets the SW/SH-context Met Location when an external entity from the input <see cref="ver"/> resides in SW/SH.
/// </summary>
public static ushort GetMetSWSH(ushort loc, int ver) => (GameVersion)ver switch
{
GameVersion.PLA => HOME_SWLA,
GameVersion.BD => HOME_SWBD,
GameVersion.SP => HOME_SHSP,
_ => loc,
};
/// <summary>
/// Checks if the met location is a valid location for the input <see cref="ver"/>.
/// </summary>
/// <remarks>Relevant when a BD/SP entity is transferred to SW/SH.</remarks>
public static bool IsValidMetBDSP(ushort loc, int ver) => loc switch
{
HOME_SHSP when ver == (int)GameVersion.SH => true,
HOME_SWBD when ver == (int)GameVersion.SW => true,
_ => false,
};
/// <summary>
/// Gets the egg location value for a traded unhatched egg.
/// </summary>

View file

@ -0,0 +1,109 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for SW/SH met locations from HOME.
/// </summary>
public static class LocationsHOME
{
// 60000 - (version - PLA)
private const int RemapCount = 5;
public const ushort SHVL = 59996; // VL traded to (SW)SH
public const ushort SWSL = 59997; // SL traded to SW(SH)
public const ushort SHSP = 59998; // SP traded to (SW)SH
public const ushort SWBD = 59999; // BD traded to SW(SH)
public const ushort SWLA = 60000; // PLA traded to SW(SH)
public const ushort SWSHEgg = 65534; // -2 = 8bNone-1..
/// <summary>
/// Gets the external entity version needs to be remapped into a SW/SH location.
/// </summary>
/// <param name="version"></param>
/// <returns>True if a known remap exists.</returns>
public static bool IsVersionRemapNeeded(int version) => GetRemapIndex(version) < RemapCount;
private static int GetRemapIndex(int version) => version - (int)GameVersion.PLA;
/// <summary>
/// Checks if the SW/SH-context Met Location is one of the remapped HOME locations.
/// </summary>
public static bool IsLocationSWSH(int met) => met switch
{
SHVL or SWSL or SHSP or SWBD or SWLA => true,
_ => false,
};
/// <summary>
/// Checks if the SW/SH-context Egg Location is valid with respect to the <see cref="original"/> location.
/// </summary>
public static bool IsLocationSWSHEgg(int ver, int met, int egg, ushort original)
{
if (original > SWLA && egg == SWSHEgg)
return true;
// >60000 can be reset to Link Trade (30001), then altered differently.
var expect = GetMetSWSH(original, ver);
return expect == met && expect == egg;
}
/// <summary>
/// Gets the SW/SH-context Egg Location when an external entity from the input <see cref="ver"/> resides in SW/SH.
/// </summary>
public static ushort GetLocationSWSHEgg(int ver, ushort egg)
{
if (egg == 0)
return 0;
if (egg > SWLA)
return SWSHEgg;
// >60000 can be reset to Link Trade (30001), then altered differently.
return GetMetSWSH(egg, ver);
}
/// <summary>
/// Gets the SW/SH-context <see cref="GameVersion"/> when an external entity from the input <see cref="ver"/> resides in SW/SH.
/// </summary>
public static int GetVersionSWSH(int ver) => (GameVersion)ver switch
{
GameVersion.PLA => (int)GameVersion.SW,
GameVersion.BD => (int)GameVersion.SW,
GameVersion.SP => (int)GameVersion.SH,
GameVersion.SL => (int)GameVersion.SW,
GameVersion.VL => (int)GameVersion.SH,
_ => ver,
};
/// <summary>
/// Gets the SW/SH-context Met Location when an external entity from the input <see cref="ver"/> resides in SW/SH.
/// </summary>
public static ushort GetMetSWSH(ushort loc, int ver) => (GameVersion)ver switch
{
GameVersion.PLA => SWLA,
GameVersion.BD => SWBD,
GameVersion.SP => SHSP,
GameVersion.SL => SWSL,
GameVersion.VL => SHVL,
_ => loc,
};
/// <summary>
/// Checks if the met location is a valid location for the input <see cref="ver"/>.
/// </summary>
/// <remarks>Relevant when a BD/SP entity is transferred to SW/SH.</remarks>
public static bool IsValidMetBDSP(ushort loc, int ver) => loc switch
{
SHSP when ver == (int)GameVersion.SH => true,
SWBD when ver == (int)GameVersion.SW => true,
_ => false,
};
/// <summary>
/// Checks if the met location is a valid location for the input <see cref="ver"/>.
/// </summary>
/// <remarks>Relevant when a S/V entity is transferred to SW/SH.</remarks>
public static bool IsValidMetSV(ushort loc, int ver) => loc switch
{
SHVL when ver == (int)GameVersion.SH => true,
SWSL when ver == (int)GameVersion.SW => true,
_ => false,
};
}

View file

@ -176,9 +176,6 @@ public sealed class ItemStorage9SV : IItemStorage
0016, // Cherish Ball
0111, // Odd Keystone
0112, // [AUCTION] Griseous Orb
0135, // [AUCTION] Adamant Orb
0136, // [AUCTION] Lustrous Orb
0208, // Enigma Berry
0209, // Micle Berry
@ -186,23 +183,6 @@ public sealed class ItemStorage9SV : IItemStorage
0211, // Jaboca Berry
0212, // Rowap Berry
0298, // [AUCTION] Flame Plate
0299, // [AUCTION] Splash Plate
0300, // [AUCTION] Zap Plate
0301, // [AUCTION] Meadow Plate
0302, // [AUCTION] Icicle Plate
0303, // [AUCTION] Fist Plate
0304, // [AUCTION] Toxic Plate
0305, // [AUCTION] Earth Plate
0306, // [AUCTION] Sky Plate
0307, // [AUCTION] Mind Plate
0308, // [AUCTION] Insect Plate
0309, // [AUCTION] Stone Plate
0310, // [AUCTION] Spooky Plate
0311, // [AUCTION] Draco Plate
0312, // [AUCTION] Dread Plate
0313, // [AUCTION] Iron Plate
0485, // Red Apricorn
0486, // Blue Apricorn
0487, // Yellow Apricorn
@ -213,21 +193,13 @@ public sealed class ItemStorage9SV : IItemStorage
0499, // Sport Ball
0500, // Park Ball
0644, // [AUCTION] Pixie Plate
0708, // Lumiose Galette
0709, // Shalour Sable
1103, // [AUCTION] Rusted Sword
1104, // [AUCTION] Rusted Shield
1230, // TM00 - Mega Punch (Nothing learns, not obtainable even though it is assigned a move.)
1582, // [AUCTION] Galarica Cuff
1592, // [AUCTION] Galarica Wreath
1777, // [AUCTION] Adamant Crystal
1778, // [AUCTION] Lustrous Globe
1779, // [AUCTION] Griseous Core
1582, // Galarica Cuff
1592, // Galarica Wreath
1785, // Strange Ball
};

View file

@ -104,14 +104,14 @@ public static class Legal
internal const int MaxMoveID_8a = (int)Move.TakeHeart;
internal const int MaxItemID_8a = 1828; // Legend Plate
internal const int MaxBallID_8a = (int)Ball.LAOrigin;
internal const int MaxGameID_8a = (int)GameVersion.SP;
//internal const int MaxGameID_8a = (int)GameVersion.SP;
internal const int MaxAbilityID_8a = MaxAbilityID_8_R2;
internal const int MaxSpeciesID_8b = MaxSpeciesID_4; // Arceus-493
internal const int MaxMoveID_8b = MaxMoveID_8_R2;
internal const int MaxItemID_8b = 1822; // DS Sounds
internal const int MaxBallID_8b = (int)Ball.LAOrigin;
internal const int MaxGameID_8b = (int)GameVersion.SP;
//internal const int MaxGameID_8b = (int)GameVersion.SP;
internal const int MaxAbilityID_8b = MaxAbilityID_8_R2;
internal const int MaxSpeciesID_9 = (int)Species.IronLeaves;
@ -121,6 +121,7 @@ public static class Legal
internal const int MaxBallID_9 = (int)Ball.LAOrigin;
internal const int MaxGameID_9 = (int)GameVersion.VL;
internal const int MaxGameID_HOME = MaxGameID_9;
internal static readonly ushort[] HeldItems_GSC = ItemStorage2.GetAllHeld();
internal static readonly ushort[] HeldItems_RS = ItemStorage3RS.GetAllHeld();

View file

@ -112,5 +112,9 @@ public static class EncounterServerDate
{0502, (new(2023, 03, 31), new(2023, 07, 01))}, // TCG Flying Lechonk
{0503, (new(2023, 04, 13), new(2023, 04, 18))}, // Gavin's Palafin (-1 start date tolerance for GMT-10 regions)
{0025, (new(2023, 04, 21), new(2023, 07, 01))}, // Pokémon Center Pikachu (Mini & Jumbo)
{9021, (new(2023, 05, 30), Never)}, // Hidden Ability Sprigatito
{9022, (new(2023, 05, 30), Never)}, // Hidden Ability Fuecoco
{9023, (new(2023, 05, 30), Never)}, // Hidden Ability Quaxly
};
}

View file

@ -108,7 +108,7 @@ public sealed record EncounterSlot8a : EncounterSlot, IAlphaReadOnly, IMasteryIn
private EncounterMatchRating GetMatchRatingInternal(PKM pk)
{
if (pk is IAlpha a && a.IsAlpha != IsAlpha)
if (!MarkRules.IsMarkValidAlpha(pk, IsAlpha))
return EncounterMatchRating.DeferredErrors;
if (FlawlessIVCount is not 0 && pk.FlawlessIVCount < FlawlessIVCount)
return EncounterMatchRating.DeferredErrors;

View file

@ -95,14 +95,20 @@ public sealed record EncounterSlot8GO : EncounterSlotGO, IFixedOTFriendship
{
s.HeightScalar = PokeSizeUtil.GetRandomScalar();
s.WeightScalar = PokeSizeUtil.GetRandomScalar();
if (pk is IScaledSize3 s3)
s3.Scale = s.HeightScalar = PokeSizeUtil.GetRandomScalar();
}
if (OriginFormat is PogoImportFormat.PA8)
if (pk is PA8 pa8)
{
var pa8 = (PA8)pk;
pa8.ResetHeight();
pa8.ResetWeight();
pa8.Scale = pa8.HeightScalar;
}
else if (pk is PK9 pk9)
{
var pi = pk9.PersonalInfo;
pk9.TeraTypeOriginal = pk9.TeraTypeOverride = TeraTypeUtil.GetTeraTypeImport(pi.Type1, pi.Type2);
pk9.Obedience_Level = (byte)pk9.Met_Level;
}
pk.OT_Friendship = OT_Friendship;

View file

@ -53,8 +53,8 @@ public sealed record EncounterFixed9 : EncounterStatic, IGemType
protected override bool IsMatchLocation(PKM pk)
{
if (!pk.HasOriginalMetLocation)
return true;
if (pk is PK8)
return LocationsHOME.IsValidMetSV((ushort)pk.Met_Location, pk.Version);
var loc = pk.Met_Location;
return loc == Location0 || loc == Location1 || loc == Location2 || loc == Location3;
}

View file

@ -211,6 +211,13 @@ public sealed record EncounterMight9 : EncounterStatic, ITeraRaid9
_ => throw new ArgumentOutOfRangeException(nameof(b), b, null),
};
protected override bool IsMatchLocation(PKM pk)
{
if (pk is PK8)
return LocationsHOME.IsValidMetSV((ushort)pk.Met_Location, pk.Version);
return base.IsMatchLocation(pk);
}
protected override EncounterMatchRating IsMatchDeferred(PKM pk)
{
if (Ability != AbilityPermission.Any12H)

View file

@ -87,8 +87,8 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
protected override bool IsMatchLocation(PKM pk)
{
if (pk is PK8)
return pk.Met_Location == Locations.HOME_SWLA;
if (pk is PB8 { Version: (int)GameVersion.PLA, Met_Location: Locations.HOME_SWLA })
return pk.Met_Location == LocationsHOME.SWLA;
if (pk is PB8 { Version: (int)GameVersion.PLA, Met_Location: LocationsHOME.SWLA })
return true;
return base.IsMatchLocation(pk);
@ -115,6 +115,9 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
if (!IsForcedMasteryCorrect(pk))
return EncounterMatchRating.DeferredErrors;
if (!MarkRules.IsMarkValidAlpha(pk, IsAlpha))
return EncounterMatchRating.DeferredErrors;
if (IsAlpha && pk is PA8 { AlphaMove: 0 })
return EncounterMatchRating.Deferred;

View file

@ -20,7 +20,7 @@ public sealed record EncounterStatic8b : EncounterStatic, IStaticCorrelation8b
protected override bool IsMatchLocation(PKM pk)
{
if (pk is PK8)
return Locations.IsValidMetBDSP((ushort)pk.Met_Location, pk.Version);
return LocationsHOME.IsValidMetBDSP((ushort)pk.Met_Location, pk.Version);
if (!Roaming)
return base.IsMatchLocation(pk);
return IsRoamingLocation(pk);
@ -54,12 +54,7 @@ public sealed record EncounterStatic8b : EncounterStatic, IStaticCorrelation8b
return pk.Egg_Location == 0;
if (pk is PK8)
{
if (EggLocation > 60000 && pk.Egg_Location == Locations.HOME_SWSHBDSPEgg)
return true;
// >60000 can be reset to Link Trade (30001), then altered differently.
return Locations.IsValidMetBDSP((ushort)pk.Egg_Location, pk.Version) && pk.Egg_Location == pk.Met_Location;
}
return LocationsHOME.IsLocationSWSHEgg(pk.Version, pk.Met_Location, pk.Egg_Location, (ushort)EggLocation);
// Hatched
return pk.Egg_Location == EggLocation || pk.Egg_Location == Locations.LinkTrade6NPC;

View file

@ -19,6 +19,13 @@ public sealed record EncounterStatic9(GameVersion Version) : EncounterStatic(Ver
public SizeType9 ScaleType => NoScalarsDefined ? SizeType9.RANDOM : SizeType9.VALUE;
protected override bool IsMatchLocation(PKM pk)
{
if (pk is PK8)
return LocationsHOME.IsValidMetSV((ushort)pk.Met_Location, pk.Version);
return base.IsMatchLocation(pk);
}
protected override bool IsMatchPartial(PKM pk)
{
if (pk is IScaledSize v && !NoScalarsDefined)
@ -30,9 +37,9 @@ public sealed record EncounterStatic9(GameVersion Version) : EncounterStatic(Ver
if (v.WeightScalar != Size)
return true;
}
if (pk is PK9 pk9)
if (pk is IScaledSize3 s3)
{
if (pk9.Scale != Size)
if (s3.Scale != Size)
return true;
}
}

View file

@ -95,6 +95,13 @@ public sealed record EncounterTera9 : EncounterStatic, ITeraRaid9
_ => throw new ArgumentOutOfRangeException(nameof(b), b, null),
};
protected override bool IsMatchLocation(PKM pk)
{
if (pk is PK8)
return LocationsHOME.IsValidMetSV((ushort)pk.Met_Location, pk.Version);
return base.IsMatchLocation(pk);
}
protected override EncounterMatchRating IsMatchDeferred(PKM pk)
{
if (Ability != Any12H)

View file

@ -139,7 +139,7 @@ public sealed class EncounterGenerator8a : IEncounterGenerator
// Encounter Slots
if (CanBeWildEncounter(pk))
{
bool hasOriginalLocation = pk is not (PK8 or PB8 { Met_Location: Locations.HOME_SWLA });
bool hasOriginalLocation = pk is not (PK8 or PB8 { Met_Location: LocationsHOME.SWLA });
var location = pk.Met_Location;
foreach (var area in Encounters8a.SlotsLA)
{

View file

@ -273,9 +273,9 @@ public sealed class EncounterGenerator8b : IEncounterGenerator
bool wasEgg = pk.Egg_Location switch
{
Locations.HOME_SWSHBDSPEgg => true, // Regular hatch location (not link trade)
Locations.HOME_SWBD => pk.Met_Location == Locations.HOME_SWBD, // Link Trade transferred over must match Met Location
Locations.HOME_SHSP => pk.Met_Location == Locations.HOME_SHSP, // Link Trade transferred over must match Met Location
LocationsHOME.SWSHEgg => true, // Regular hatch location (not link trade)
LocationsHOME.SWBD => pk.Met_Location == LocationsHOME.SWBD, // Link Trade transferred over must match Met Location
LocationsHOME.SHSP => pk.Met_Location == LocationsHOME.SHSP, // Link Trade transferred over must match Met Location
_ => false,
};
if (wasEgg && pk.Met_Level == 1)
@ -294,7 +294,6 @@ public sealed class EncounterGenerator8b : IEncounterGenerator
EncounterMatchRating rating = MaxNotMatch;
// Trades
if (!pk.IsEgg)
{
foreach (var enc in Encounters8b.TradeGift_BDSP)
{

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using static PKHeX.Core.EncounterStateUtil;
using static PKHeX.Core.EncounterTypeGroup;
using static PKHeX.Core.EncounterMatchRating;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
@ -13,7 +14,12 @@ public sealed class EncounterGenerator9 : IEncounterGenerator
public IEnumerable<IEncounterable> GetEncounters(PKM pk, LegalInfo info)
{
var chain = EncounterOrigin.GetOriginChain(pk);
return GetEncounters(pk, chain, info);
return (GameVersion)pk.Version switch
{
SW when pk.Met_Location == LocationsHOME.SWSL => Instance.GetEncountersSWSH(pk, chain, SL),
SH when pk.Met_Location == LocationsHOME.SHVL => Instance.GetEncountersSWSH(pk, chain, VL),
_ => GetEncounters(pk, chain, info),
};
}
public IEnumerable<IEncounterable> GetPossible(PKM pk, EvoCriteria[] chain, GameVersion game, EncounterTypeGroup groups)
@ -32,7 +38,7 @@ public sealed class EncounterGenerator9 : IEncounterGenerator
}
if (groups.HasFlag(Static))
{
var table = game == GameVersion.SL ? Encounters9.StaticSL : Encounters9.StaticVL;
var table = game == SL ? Encounters9.StaticSL : Encounters9.StaticVL;
foreach (var enc in GetPossibleStatic(chain, table))
yield return enc;
}
@ -99,7 +105,7 @@ public sealed class EncounterGenerator9 : IEncounterGenerator
{
foreach (var enc in table)
{
if (enc.Version != GameVersion.SV && enc.Version != game)
if (enc.Version != SV && enc.Version != game)
continue;
foreach (var evo in chain)
@ -112,6 +118,138 @@ public sealed class EncounterGenerator9 : IEncounterGenerator
}
}
public IEnumerable<IEncounterable> GetEncountersSWSH(PKM pk, EvoCriteria[] chain, GameVersion game)
{
if (pk.FatefulEncounter)
{
bool yielded = false;
foreach (var mg in EncounterEvent.MGDB_G9)
{
foreach (var evo in chain)
{
if (evo.Species != mg.Species)
continue;
if (mg.IsMatchExact(pk, evo))
{
yield return mg;
yielded = true;
}
break;
}
}
if (yielded)
yield break;
}
bool wasEgg = pk.Egg_Location switch
{
LocationsHOME.SWSHEgg => true, // Regular hatch location (not link trade)
LocationsHOME.SWSL => pk.Met_Location == LocationsHOME.SWSL, // Link Trade transferred over must match Met Location
LocationsHOME.SHVL => pk.Met_Location == LocationsHOME.SHVL, // Link Trade transferred over must match Met Location
_ => false,
};
if (wasEgg && pk.Met_Level == 1)
{
bool yielded = false;
var eggs = GetEggs(pk, chain, game);
foreach (var egg in eggs)
{
yield return egg;
yielded = true;
}
if (yielded)
yield break;
}
IEncounterable? cache = null;
EncounterMatchRating rating = MaxNotMatch;
// Trades
{
foreach (var z in Encounters9.TradeGift_SV)
{
foreach (var evo in chain)
{
if (z.Version != SV && z.Version != game)
continue;
if (evo.Species != z.Species)
continue;
if (!z.IsMatchExact(pk, evo))
break;
var match = z.GetMatchRating(pk);
if (match == Match)
{
yield return z;
}
else if (match < rating)
{
cache = z;
rating = match;
}
break;
}
}
if (cache != null)
yield return cache;
}
if (pk is not IRibbonIndex r || !r.HasEncounterMark())
{
var encStatic = game == SL ? Encounters9.StaticSL : Encounters9.StaticVL;
foreach (var z in encStatic)
{
foreach (var evo in chain)
{
if (evo.Species != z.Species)
continue;
if (!z.IsMatchExact(pk, evo))
break;
var match = z.GetMatchRating(pk);
if (match == Match)
{
yield return z;
}
else if (match < rating)
{
cache = z;
rating = match;
}
break;
}
}
}
// Wild encounters are more permissive than static encounters.
// Can have encounter marks, can have varied scales/shiny states.
if (CanBeWildEncounter(pk))
{
var areas = Encounters9.Slots;
foreach (var area in areas)
{
var slots = area.GetMatchingSlots(pk, chain);
foreach (var slot in slots)
{
var match = slot.GetMatchRating(pk);
if (match == Match)
{
yield return slot;
}
else if (match < rating)
{
cache = slot;
rating = match;
}
}
}
}
if (cache != null)
yield return cache;
}
public IEnumerable<IEncounterable> GetEncounters(PKM pk, EvoCriteria[] chain, LegalInfo info)
{
if (pk.FatefulEncounter)
@ -162,7 +300,7 @@ public sealed class EncounterGenerator9 : IEncounterGenerator
{
foreach (var evo in chain)
{
if (z.Version != GameVersion.SV && z.Version != game)
if (z.Version != SV && z.Version != game)
continue;
if (evo.Species != z.Species)
continue;
@ -189,7 +327,7 @@ public sealed class EncounterGenerator9 : IEncounterGenerator
if (pk is not IRibbonIndex r || !r.HasEncounterMark())
{
var encStatic = game == GameVersion.SL ? Encounters9.StaticSL : Encounters9.StaticVL;
var encStatic = game == SL ? Encounters9.StaticSL : Encounters9.StaticVL;
foreach (var z in encStatic)
{
foreach (var evo in chain)
@ -256,7 +394,7 @@ public sealed class EncounterGenerator9 : IEncounterGenerator
if (!devolved.InsideLevelRange(EggLevel))
yield break;
if (version == 0 && pk.IsEgg)
version = GameVersion.SL;
version = SL;
// Ensure most devolved species is the same as the egg species.
// No split-breed to consider.

View file

@ -1,6 +1,5 @@
using System.Collections.Generic;
using static PKHeX.Core.GameVersion;
using static PKHeX.Core.Locations;
namespace PKHeX.Core;
@ -27,9 +26,11 @@ public sealed class EncounterGenerator8X : IEncounterGenerator
GO => EncounterGenerator8GO.Instance.GetEncounters(pk, chain, info),
PLA => EncounterGenerator8a.Instance.GetEncounters(pk, chain, info),
BD or SP => EncounterGenerator8b.Instance.GetEncounters(pk, chain, info),
SW when pk.Met_Location == HOME_SWLA => EncounterGenerator8a.Instance.GetEncounters(pk, chain, info),
SW when pk.Met_Location == HOME_SWBD => EncounterGenerator8b.Instance.GetEncountersSWSH(pk, chain, BD),
SH when pk.Met_Location == HOME_SHSP => EncounterGenerator8b.Instance.GetEncountersSWSH(pk, chain, SP),
SW when pk.Met_Location == LocationsHOME.SWLA => EncounterGenerator8a.Instance.GetEncounters(pk, chain, info),
SW when pk.Met_Location == LocationsHOME.SWBD => EncounterGenerator8b.Instance.GetEncountersSWSH(pk, chain, BD),
SH when pk.Met_Location == LocationsHOME.SHSP => EncounterGenerator8b.Instance.GetEncountersSWSH(pk, chain, SP),
SW when pk.Met_Location == LocationsHOME.SWSL => EncounterGenerator9.Instance.GetEncountersSWSH(pk, chain, SL),
SH when pk.Met_Location == LocationsHOME.SHVL => EncounterGenerator9.Instance.GetEncountersSWSH(pk, chain, VL),
_ => EncounterGenerator8.Instance.GetEncounters(pk, chain, info),
};
}

View file

@ -230,16 +230,21 @@ public static class EncounterVerifier
if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
var met = (ushort)pk.Met_Location;
bool valid = pk.BDSP // Transferred from BD/SP, now acting like a SW/SH egg.
? Locations.IsValidMetBDSP(met, pk.Version)
: EggHatchLocation8.IsValidMet8SWSH(met);
var valid = IsValidMetForeignEggSWSH(pk, (ushort)pk.Met_Location);
if (valid)
return GetValid(LEggLocation);
return GetInvalid(LEggLocationInvalid);
}
private static bool IsValidMetForeignEggSWSH(PKM pk, ushort met)
{
if (pk.BDSP)
return LocationsHOME.IsValidMetBDSP(met, pk.Version);
if (pk.SV)
return LocationsHOME.IsValidMetSV(met, pk.Version);
return EggHatchLocation8.IsValidMet8SWSH(met);
}
private static CheckResult VerifyEncounterEgg8BDSP(PKM pk)
{
const byte level = 1;

View file

@ -12,13 +12,13 @@ public sealed class EvolutionGroup8 : IEvolutionGroup
private const int MaxSpecies = Legal.MaxSpeciesID_8a;
private const int Generation = 8;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => null;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => EvolutionGroup9.Instance;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc)
{
if (enc.Generation >= Generation)
return null;
if ((GameVersion)enc.Version is GP or GE or GG or GO)
return EvolutionGroup7b.Instance;
if (enc.Generation >= Generation)
return null;
return EvolutionGroup7.Instance;
}
@ -37,7 +37,7 @@ public sealed class EvolutionGroup8 : IEvolutionGroup
// Block BD/SP transfers that are impossible
BlockBDSP(history, enc);
if (!pk.IsUntraded && !(ParseSettings.IgnoreTransferIfNoTracker && pk is IHomeTrack { Tracker: 0 }))
if (!pk.IsUntraded && !(ParseSettings.IgnoreTransferIfNoTracker && pk is IHomeTrack { HasTracker: false }))
{
CrossPropagate(history);
}

View file

@ -7,15 +7,9 @@ public sealed class EvolutionGroup9 : IEvolutionGroup
public static readonly EvolutionGroup9 Instance = new();
private static readonly EvolutionTree Tree9 = EvolutionTree.Evolves9;
private const int MaxSpecies = Legal.MaxSpeciesID_9;
private const int Generation = 9;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => null;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc)
{
if (enc.Generation >= Generation)
return null;
return null;
}
public IEvolutionGroup GetPrevious(PKM pk, EvolutionOrigin enc) => EvolutionGroup8.Instance;
public bool Append(PKM pk, EvolutionHistory history, ref ReadOnlySpan<EvoCriteria> chain, EvolutionOrigin enc)
{

View file

@ -226,7 +226,7 @@ public static class MethodFinder
private static bool GetLCRNGRoamerMatch(Span<uint> seeds, uint top, uint bot, ReadOnlySpan<uint> IVs, out PIDIV pidiv)
{
if (IVs is not [_, < 7, 0, 0, 0, 0])
if (IVs is not [_, <= 7, 0, 0, 0, 0])
return GetNonMatch(out pidiv);
var iv1 = GetIVChunk(IVs[..3]);

View file

@ -208,7 +208,7 @@ public static class Encounter9RNG
if (enc.Height == 0)
{
var value = (int)rand.NextInt(0x81) + (int)rand.NextInt(0x80);
if (pk is IScaledSize s && s.HeightScalar != value)
if (!IsHeightMatchSV(pk, value))
return false;
}
if (enc.Weight == 0)
@ -226,6 +226,22 @@ public static class Encounter9RNG
return true;
}
public static bool IsHeightMatchSV(PKM pk, int value)
{
// HOME copies Scale to Height. Untouched by HOME must match the value.
// Viewing the save file in HOME will alter it too. Tracker definitely indicates it was viewed.
if (pk is not (IScaledSize s2 and IScaledSize3 s3))
return true;
// Viewed in HOME.
if (s2.HeightScalar == s3.Scale)
return true;
if (pk is IHomeTrack { HasTracker: true })
return false;
return s2.HeightScalar == value;
}
private static uint GetAdaptedPID(ref Xoroshiro128Plus rand, PKM pk, in GenerateParam9 enc)
{
var fakeTID = (uint)rand.NextInt();

View file

@ -153,11 +153,21 @@ public static class RaidRNG
if (pk is IScaledSize s)
{
var height = (int)rng.NextInt(0x81) + (int)rng.NextInt(0x80);
if (s.HeightScalar != height)
return false;
var weight = (int)rng.NextInt(0x81) + (int)rng.NextInt(0x80);
if (s.WeightScalar != weight)
return false;
if (height == 0 && weight == 0 && pk is IHomeTrack { HasTracker: true})
{
// HOME rerolls height/weight if both are 0
// This behavior started in 3.0.0, so only flag if the context is 9 or above.
if (pk.Context is not (EntityContext.Gen8 or EntityContext.Gen8a or EntityContext.Gen8b))
return false;
}
else
{
if (s.HeightScalar != height)
return false;
if (s.WeightScalar != weight)
return false;
}
}
return true;

View file

@ -144,7 +144,7 @@ public sealed partial class MemoryContext8 : MemoryContext
42 when arg is not (1 or 12 or 22 or 33 or 35 or 37 or 44 or 47 or 53 or 71 or 72 or 76 or 77) => true,
// {0} sat with {1} on a bench {2}. {4} that {3}.
70 when arg is not (12 or 22 or 28 or 33 or 35 or 37 or 38 or 44 or 53 or 77) => true,
70 when arg is not (8 or 12 or 22 or 28 or 33 or 35 or 37 or 38 or 44 or 53 or 77) => true,
_ => !IsGeneralLocation8(arg),
};

View file

@ -58,6 +58,8 @@ public static class TrainerInfoExtensions
pk.HT_Gender = sav.Gender;
pk.HT_Friendship = pk.OT_Friendship;
pk.CurrentHandler = 1;
if (pk is IHandlerLanguage h)
h.HT_Language = (byte)sav.Language;
if (pk is PK6 pk6 && sav is IRegionOrigin o)
{

View file

@ -14,12 +14,14 @@ public static class AbilityChangeRules
/// <param name="enc">Original Encounter</param>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="abilityFlag">Current ability index value</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if possible to obtain <see cref="abilityFlag"/></returns>
public static bool IsAbilityStateValid(this IEncounterTemplate enc, EvolutionHistory evosAll, int abilityFlag) => (enc switch
public static bool IsAbilityStateValid(this IEncounterTemplate enc, EvolutionHistory evosAll, int abilityFlag, EntityContext current, EntityContext original) => (enc switch
{
IFixedAbilityNumber f => f.Ability,
_ => Any12,
}).IsAbilityStateValid(evosAll, abilityFlag);
}).IsAbilityStateValid(evosAll, abilityFlag, current, original);
/// <summary>
/// Checks if the current <see cref="abilityFlag"/> value is possible to obtain based on the original <see cref="ability"/> and game visiting.
@ -27,15 +29,17 @@ public static class AbilityChangeRules
/// <param name="ability">Original Ability Permitted</param>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="abilityFlag">Current ability index value</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if possible to obtain <see cref="abilityFlag"/></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static bool IsAbilityStateValid(this AbilityPermission ability, EvolutionHistory evosAll, int abilityFlag) => ability switch
public static bool IsAbilityStateValid(this AbilityPermission ability, EvolutionHistory evosAll, int abilityFlag, EntityContext current, EntityContext original) => ability switch
{
Any12H => true,
Any12 => abilityFlag != 4 || IsAbilityPatchPossible(evosAll),
OnlyHidden => abilityFlag == 4 || IsAbilityPatchRevertPossible(evosAll, abilityFlag),
OnlyFirst => abilityFlag == 1 || (abilityFlag == 4 && IsAbilityPatchPossible(evosAll)) || (abilityFlag != 4 && IsAbilityCapsulePossible(evosAll)),
OnlySecond => abilityFlag == 2 || (abilityFlag == 4 && IsAbilityPatchPossible(evosAll)) || (abilityFlag != 4 && IsAbilityCapsulePossible(evosAll)),
Any12 => abilityFlag != 4 || IsAbilityPatchPossible(evosAll, current, original),
OnlyHidden => abilityFlag == 4 || IsAbilityPatchRevertPossible(evosAll, abilityFlag, current, original),
OnlyFirst => abilityFlag == 1 || (abilityFlag == 4 && IsAbilityPatchPossible(evosAll, current, original)) || (abilityFlag != 4 && IsAbilityCapsulePossible(evosAll, current, original)),
OnlySecond => abilityFlag == 2 || (abilityFlag == 4 && IsAbilityPatchPossible(evosAll, current, original)) || (abilityFlag != 4 && IsAbilityCapsulePossible(evosAll, current, original)),
_ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null),
};
@ -44,26 +48,30 @@ public static class AbilityChangeRules
/// </summary>
/// <param name="enc">Original Encounter</param>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if the ability can be changed</returns>
public static bool IsAbilityChangeAvailable(this IEncounterTemplate enc, EvolutionHistory evosAll) => (enc switch
public static bool IsAbilityChangeAvailable(this IEncounterTemplate enc, EvolutionHistory evosAll, EntityContext current, EntityContext original) => (enc switch
{
IFixedAbilityNumber f => f.Ability,
_ => Any12,
}).IsAbilityChangeAvailable(evosAll);
}).IsAbilityChangeAvailable(evosAll, current, original);
/// <summary>
/// Checks if the original <see cref="ability"/> value can be changed based on the games visited.
/// </summary>
/// <param name="ability">Original Ability Permitted</param>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if the ability can be changed</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static bool IsAbilityChangeAvailable(this AbilityPermission ability, EvolutionHistory evosAll) => ability switch
public static bool IsAbilityChangeAvailable(this AbilityPermission ability, EvolutionHistory evosAll, EntityContext current, EntityContext original) => ability switch
{
Any12H => true,
Any12 => IsAbilityPatchAvailable(evosAll),
OnlyHidden => IsAbilityPatchRevertAvailable(evosAll),
OnlyFirst or OnlySecond => IsAbilityPatchAvailable(evosAll) || IsAbilityCapsuleAvailable(evosAll),
Any12 => IsAbilityPatchAvailable(evosAll, current, original),
OnlyHidden => IsAbilityPatchRevertAvailable(evosAll, current, original),
OnlyFirst or OnlySecond => IsAbilityPatchAvailable(evosAll, current, original) || IsAbilityCapsuleAvailable(evosAll, current, original),
_ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null),
};
@ -71,11 +79,11 @@ public static class AbilityChangeRules
/// Checks if the Ability Capsule (1 &lt;-&gt; 2) item is available in any game visited.
/// </summary>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if possible</returns>
public static bool IsAbilityCapsuleAvailable(EvolutionHistory evosAll)
public static bool IsAbilityCapsuleAvailable(EvolutionHistory evosAll, EntityContext current, EntityContext original)
{
if (evosAll.HasVisitedGen9)
return true;
if (evosAll.HasVisitedSWSH)
return true;
if (evosAll.HasVisitedBDSP)
@ -84,6 +92,10 @@ public static class AbilityChangeRules
return true;
if (evosAll.HasVisitedGen6)
return true;
if (IsAbilityRestoredHOME(current, original))
return false;
if (evosAll.HasVisitedGen9)
return true;
return false;
}
@ -91,18 +103,22 @@ public static class AbilityChangeRules
/// Checks if any of the games visited allow applying an Ability Capsule (1 &lt;-&gt; 2) item.
/// </summary>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if possible</returns>
public static bool IsAbilityCapsulePossible(EvolutionHistory evosAll)
public static bool IsAbilityCapsulePossible(EvolutionHistory evosAll, EntityContext current, EntityContext original)
{
if (evosAll.HasVisitedGen9 && IsCapsulePossible<PersonalTable9SV, PersonalInfo9SV>(evosAll.Gen9, PersonalTable.SV))
if (evosAll.HasVisitedSWSH && IsCapsulePossible<PersonalTable8SWSH, PersonalInfo8SWSH, EvoCriteria>(evosAll.Gen8, PersonalTable.SWSH))
return true;
if (evosAll.HasVisitedSWSH && IsCapsulePossible<PersonalTable8SWSH, PersonalInfo8SWSH>(evosAll.Gen8, PersonalTable.SWSH))
if (evosAll.HasVisitedBDSP && IsCapsulePossible<PersonalTable8BDSP, PersonalInfo8BDSP, EvoCriteria>(evosAll.Gen8b, PersonalTable.BDSP))
return true;
if (evosAll.HasVisitedBDSP && IsCapsulePossible<PersonalTable8BDSP, PersonalInfo8BDSP>(evosAll.Gen8b, PersonalTable.BDSP))
if (evosAll.HasVisitedGen7 && IsCapsulePossible<PersonalTable7, PersonalInfo7, EvoCriteria>(evosAll.Gen7, PersonalTable.USUM))
return true;
if (evosAll.HasVisitedGen7 && IsCapsulePossible<PersonalTable7, PersonalInfo7>(evosAll.Gen7, PersonalTable.USUM))
if (evosAll.HasVisitedGen6 && IsCapsulePossible<PersonalTable6AO, PersonalInfo6AO, EvoCriteria>(evosAll.Gen6, PersonalTable.AO))
return true;
if (evosAll.HasVisitedGen6 && IsCapsulePossible<PersonalTable6AO, PersonalInfo6AO>(evosAll.Gen6, PersonalTable.AO))
if (IsAbilityRestoredHOME(current, original))
return false;
if (evosAll.HasVisitedGen9 && IsCapsulePossible<PersonalTable9SV, PersonalInfo9SV, EvoCriteria>(evosAll.Gen9, PersonalTable.SV))
return true;
return false;
}
@ -111,13 +127,17 @@ public static class AbilityChangeRules
/// Checks if the Ability Patch (1/2-&gt; H) item is available in any game visited.
/// </summary>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if possible</returns>
public static bool IsAbilityPatchAvailable(EvolutionHistory evosAll)
public static bool IsAbilityPatchAvailable(EvolutionHistory evosAll, EntityContext current, EntityContext original)
{
if (evosAll.HasVisitedGen9)
return true;
if (evosAll.HasVisitedSWSH || evosAll.HasVisitedBDSP)
return true;
if (IsAbilityRestoredHOME(current, original))
return false;
if (evosAll.HasVisitedGen9)
return true;
return false;
}
@ -125,14 +145,18 @@ public static class AbilityChangeRules
/// Checks if any of the games visited allow applying an Ability Patch (1/2-&gt; H) item.
/// </summary>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if possible</returns>
public static bool IsAbilityPatchPossible(EvolutionHistory evosAll)
public static bool IsAbilityPatchPossible(EvolutionHistory evosAll, EntityContext current, EntityContext original)
{
if (evosAll.HasVisitedGen9 && IsPatchPossible<PersonalTable9SV, PersonalInfo9SV>(evosAll.Gen9, PersonalTable.SV))
if (evosAll.HasVisitedSWSH && IsPatchPossible<PersonalTable8SWSH, PersonalInfo8SWSH, EvoCriteria>(evosAll.Gen8, PersonalTable.SWSH))
return true;
if (evosAll.HasVisitedSWSH && IsPatchPossible<PersonalTable8SWSH, PersonalInfo8SWSH>(evosAll.Gen8, PersonalTable.SWSH))
if (evosAll.HasVisitedBDSP && IsPatchPossible<PersonalTable8BDSP, PersonalInfo8BDSP, EvoCriteria>(evosAll.Gen8b, PersonalTable.BDSP))
return true;
if (evosAll.HasVisitedBDSP && IsPatchPossible<PersonalTable8BDSP, PersonalInfo8BDSP>(evosAll.Gen8b, PersonalTable.BDSP))
if (IsAbilityRestoredHOME(current, original))
return false;
if (evosAll.HasVisitedGen9 && IsPatchPossible<PersonalTable9SV, PersonalInfo9SV, EvoCriteria>(evosAll.Gen9, PersonalTable.SV))
return true;
return false;
}
@ -141,30 +165,58 @@ public static class AbilityChangeRules
/// Checks if any of the games visited allow reverting an Ability Patch (1/2-&gt; H) item.
/// </summary>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if possible</returns>
public static bool IsAbilityPatchRevertAvailable(EvolutionHistory evosAll)
public static bool IsAbilityPatchRevertAvailable(EvolutionHistory evosAll, EntityContext current, EntityContext original)
{
if (IsAbilityRestoredHOME(current, original))
return false;
if (evosAll.HasVisitedGen9)
return true;
return false;
}
private static bool IsAbilityRestoredHOME(EntityContext current, EntityContext original)
{
var originalGen = original.Generation();
if (originalGen < 9)
{
// HOME restores abilities if it existed in the current format prior to transferring to a future game with reversion possible.
if (originalGen != 8)
original = EntityContext.Gen8;
if (current == original)
return true;
}
return false;
}
/// <summary>
/// Checks if any of the games visited allow reverting an Ability Patch (1/2-&gt; H) item.
/// </summary>
/// <param name="evosAll">Evolution and game visitation</param>
/// <param name="abilityIndex">Current ability index value</param>
/// <param name="current">Current context</param>
/// <param name="original">Original context</param>
/// <returns>True if possible</returns>
public static bool IsAbilityPatchRevertPossible(EvolutionHistory evosAll, int abilityIndex)
public static bool IsAbilityPatchRevertPossible(EvolutionHistory evosAll, int abilityIndex, EntityContext current, EntityContext original)
{
if (evosAll.HasVisitedGen9 && IsRevertPossible<PersonalTable9SV, PersonalInfo9SV>(evosAll.Gen9, PersonalTable.SV, abilityIndex))
return true;
// With HOME 3.0, ability modifications are locked to sets of games -- modifying in SV won't change SWSH.
if (current == EntityContext.Gen9)
{
if (IsAbilityRestoredHOME(current, original))
return false;
if (evosAll.HasVisitedGen9 && IsRevertPossible<PersonalTable9SV, PersonalInfo9SV, EvoCriteria>(evosAll.Gen9, PersonalTable.SV, abilityIndex))
return true;
}
return false;
}
private static bool IsCapsulePossible<TTable, TInfo>(EvoCriteria[] evos, TTable table)
private static bool IsCapsulePossible<TTable, TInfo, TDex>(ReadOnlySpan<TDex> evos, TTable table)
where TTable : IPersonalTable<TInfo>
where TInfo : class, IPersonalInfo, IPersonalAbility12
where TDex : ISpeciesForm
{
for (int i = evos.Length - 1; i >= 0; i--)
{
@ -176,9 +228,10 @@ public static class AbilityChangeRules
return false;
}
private static bool IsPatchPossible<TTable, TInfo>(EvoCriteria[] evos, TTable table)
private static bool IsPatchPossible<TTable, TInfo, TDex>(ReadOnlySpan<TDex> evos, TTable table)
where TTable : IPersonalTable<TInfo>
where TInfo : class, IPersonalInfo, IPersonalAbility12H
where TDex : ISpeciesForm
{
for (int i = evos.Length - 1; i >= 0; i--)
{
@ -201,9 +254,10 @@ public static class AbilityChangeRules
};
}
private static bool IsRevertPossible<TTable, TInfo>(EvoCriteria[] evos, TTable table, int abilityIndex)
private static bool IsRevertPossible<TTable, TInfo, TDex>(ReadOnlySpan<TDex> evos, TTable table, int abilityIndex)
where TTable : IPersonalTable<TInfo>
where TInfo : class, IPersonalInfo, IPersonalAbility12H
where TDex : ISpeciesForm
{
bool revert = false;
for (var i = evos.Length - 1; i >= 0; i--)

View file

@ -73,12 +73,12 @@ public sealed class AbilityVerifier : Verifier
if (pk.AbilityNumber == 4)
{
if (AbilityChangeRules.IsAbilityPatchPossible(data.Info.EvoChainsAllGens))
if (AbilityChangeRules.IsAbilityPatchPossible(data.Info.EvoChainsAllGens, pk.Context, enc.Context))
return GetValid(LAbilityPatchUsed);
}
else if (enc.Ability == AbilityPermission.OnlyHidden)
{
if (AbilityChangeRules.IsAbilityPatchRevertPossible(data.Info.EvoChainsAllGens, pk.AbilityNumber))
if (AbilityChangeRules.IsAbilityPatchRevertPossible(data.Info.EvoChainsAllGens, pk.AbilityNumber, pk.Context, enc.Context))
return GetValid(LAbilityPatchRevertUsed);
}
}
@ -163,7 +163,7 @@ public sealed class AbilityVerifier : Verifier
var enc = data.Info.EncounterMatch;
if (enc.Generation >= 6)
{
if (IsAbilityCapsuleModified(pk, encounterAbility, data.Info.EvoChainsAllGens))
if (IsAbilityCapsuleModified(pk, encounterAbility, data.Info.EvoChainsAllGens, enc.Context))
return GetValid(LAbilityCapsuleUsed);
if (pk.AbilityNumber != 1 << encounterAbility.GetSingleValue())
return INVALID;
@ -202,7 +202,7 @@ public sealed class AbilityVerifier : Verifier
if (state == AbilityState.CanMismatch || encounterAbility == 0)
return CheckMatch(pk, abilities, enc.Generation, AbilityState.MustMatch, enc);
if (IsAbilityCapsuleModified(pk, encounterAbility, data.Info.EvoChainsAllGens))
if (IsAbilityCapsuleModified(pk, encounterAbility, data.Info.EvoChainsAllGens, enc.Context))
return GetValid(LAbilityCapsuleUsed);
return INVALID;
@ -447,9 +447,9 @@ public sealed class AbilityVerifier : Verifier
}
// Ability Capsule can change between 1/2
private static bool IsAbilityCapsuleModified(PKM pk, AbilityPermission encounterAbility, EvolutionHistory evos)
private static bool IsAbilityCapsuleModified(PKM pk, AbilityPermission encounterAbility, EvolutionHistory evos, EntityContext original)
{
if (!AbilityChangeRules.IsAbilityCapsulePossible(evos))
if (!AbilityChangeRules.IsAbilityCapsulePossible(evos, pk.Context, original))
return false; // Not available.
if (pk.AbilityNumber == 4)
return false; // Cannot alter to hidden ability.

View file

@ -25,7 +25,7 @@ public sealed class HyperTrainingVerifier : Verifier
return;
}
var minLevel = t.GetHyperTrainMinLevel(data.Info.EvoChainsAllGens);
var minLevel = t.GetHyperTrainMinLevel(data.Info.EvoChainsAllGens, pk.Context);
if (pk.CurrentLevel < minLevel)
{
data.AddLine(GetInvalid(string.Format(LHyperTooLow_0, minLevel)));

View file

@ -81,7 +81,7 @@ public sealed class MarkVerifier : Verifier
return;
var affix = (RibbonIndex)affixValue;
var max = MarkRules.GetMaxAffixValue(data.Entity.Format);
var max = MarkRules.GetMaxAffixValue(data.Entity.Format, m is IHomeTrack { HasTracker: true });
if (affix > max)
{
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, GetRibbonNameSafe(affix))));

View file

@ -28,10 +28,8 @@ public sealed class MemoryVerifier : Verifier
private static bool ShouldHaveNoMemory(LegalityAnalysis data, PKM pk)
{
if (pk.BDSP || pk.LA)
if (pk.BDSP || pk.LA || pk.SV || pk is PK9)
return !data.Info.EvoChainsAllGens.HasVisitedSWSH;
if (pk is PK9)
return true; // No memories.
return false;
}

View file

@ -39,7 +39,7 @@ public sealed class MiscVerifier : Verifier
break;
}
if (pk is IHomeTrack {Tracker: not 0})
if (pk is IHomeTrack { HasTracker: true })
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
}
else
@ -138,35 +138,67 @@ public sealed class MiscVerifier : Verifier
private void VerifySVStats(LegalityAnalysis data, PK9 pk9)
{
VerifyStatNature(data, pk9);
VerifyTechRecordSV(data, pk9);
if (!pk9.IsBattleVersionValid(data.Info.EvoChainsAllGens))
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
var enc = data.EncounterOriginal;
if (CheckHeightWeightOdds(enc) && pk9 is { HeightScalar: 0, WeightScalar: 0 } && ParseSettings.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
if (enc is EncounterEgg g && UnreleasedSV.Contains(g.Species | g.Form << 11))
data.AddLine(GetInvalid(LTransferBad));
if (pk9.Tracker != 0 && pk9.HeightScalar != pk9.Scale)
data.AddLine(GetInvalid(LStatInvalidHeightWeight));
if (!IsObedienceLevelValid(pk9, pk9.Obedience_Level, pk9.Met_Level))
data.AddLine(GetInvalid(LTransferObedienceLevel));
if (pk9.Tracker != 0)
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
if (enc is EncounterEgg && !Tera9RNG.IsMatchTeraTypePersonalEgg(enc.Species, enc.Form, (byte)pk9.TeraTypeOriginal))
data.AddLine(GetInvalid(LTeraTypeMismatch));
if (pk9.IsEgg && pk9.TeraTypeOverride != (MoveType)TeraTypeUtil.OverrideNone)
data.AddLine(GetInvalid(LTeraTypeIncorrect));
if (enc is ITeraRaid9)
var enc = data.EncounterOriginal;
if (pk9 is { HeightScalar: 0, WeightScalar: 0 })
{
if (data.EncounterMatch.Context.Generation() < 9 && !data.Info.EvoChainsAllGens.HasVisitedPLA) // <=Gen8 rerolls height/weight, never zero.
data.AddLine(Get(LStatInvalidHeightWeight, Severity.Invalid, Encounter));
else if (CheckHeightWeightOdds(enc) && ParseSettings.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
}
if (enc is EncounterEgg { Context: EntityContext.Gen9 } g)
{
if (UnreleasedSV.Contains(g.Species | g.Form << 11))
data.AddLine(GetInvalid(LTransferBad));
if (!Tera9RNG.IsMatchTeraTypePersonalEgg(g.Species, g.Form, (byte)pk9.TeraTypeOriginal))
data.AddLine(GetInvalid(LTeraTypeMismatch));
}
else if (enc is ITeraRaid9)
{
var seed = Tera9RNG.GetOriginalSeed(pk9);
data.Info.PIDIV = new PIDIV(PIDType.Tera9, seed);
}
else if (enc is not { Context: EntityContext.Gen9 } || pk9 is { GO_HOME: true })
{
if (pk9.TeraTypeOverride == (MoveType)TeraTypeUtil.OverrideNone)
data.AddLine(GetInvalid(LTeraTypeIncorrect));
else if (GetTeraImportMatch(data.Info.EvoChainsAllGens.Gen9, pk9.TeraTypeOriginal) == -1)
data.AddLine(GetInvalid(LTeraTypeIncorrect));
}
else if (enc is EncounterStatic9 { StarterBoxLegend: true })
{
// Ride legends cannot be traded or transferred.
if (pk9.CurrentHandler != 0 || pk9.Tracker != 0)
data.AddLine(GetInvalid(LTransferBad));
}
}
VerifyTechRecordSV(data, pk9);
public static int GetTeraImportMatch(ReadOnlySpan<EvoCriteria> evos, MoveType actual)
{
// Sanitize out Form here for Arceus/Silvally -- rewrite via evotree later.
if (evos.Length == 0 || evos[0].Species is (int)Species.Arceus or (int)Species.Silvally)
return actual == MoveType.Normal ? 0 : -1;
for (int i = evos.Length - 1; i >= 0; i--)
{
var evo = evos[i];
var pi = PersonalTable.SV.GetFormEntry(evo.Species, evo.Form);
var expect = TeraTypeUtil.GetTeraTypeImport(pi.Type1, pi.Type2);
if (expect == actual)
return i;
}
return -1;
}
private static bool IsObedienceLevelValid(PKM pk, byte current, int expectObey)
@ -180,20 +212,6 @@ public sealed class MiscVerifier : Verifier
private static readonly HashSet<int> UnreleasedSV = new()
{
(int)Species.Diglett | (1 << 11), // Diglett-1
(int)Species.Meowth | (1 << 11), // Meowth-1
(int)Species.Growlithe | (1 << 11), // Growlithe-1
(int)Species.Slowpoke | (1 << 11), // Slowpoke-1
(int)Species.Grimer | (1 << 11), // Grimer-1
(int)Species.Voltorb | (1 << 11), // Voltorb-1
(int)Species.Tauros, // Tauros-0
(int)Species.Qwilfish | (1 << 11), // Qwilfish-1
(int)Species.Sneasel | (1 << 11), // Sneasel-1
(int)Species.Basculin | (2 << 11), // Basculin-2
(int)Species.Fennekin, // Fennekin
(int)Species.Carbink, // Carbink
(int)Species.Grookey, // Grookey
// Silly workaround for evolution chain reversal not being iteratively implemented -- block cross-gen evolution cases
(int)Species.Raichu | (1 << 11), // Raichu-1
(int)Species.Typhlosion | (1 << 11), // Typhlosion-1

View file

@ -112,10 +112,30 @@ public static class MarkRules
return enc is EncounterSlot8 { CanEncounterViaFishing: true };
}
/// <summary>
/// Checks if a <see cref="MarkAlpha"/> mark is valid.
/// </summary>
public static bool IsMarkValidAlpha(PKM pk, bool wasAlpha)
{
if (pk is IAlpha a && a.IsAlpha != wasAlpha)
return false;
if (pk is not IRibbonSetMark9 m)
return true;
if (m.RibbonMarkAlpha == wasAlpha)
return true;
// Before HOME 3.0.0, this mark was never set.
return pk is PK8 or PB8 or PA8; // Not yet touched HOME 3.0.0
}
/// <summary>
/// Checks if the input can have the <see cref="IRibbonSetMark9.RibbonMarkAlpha"/> mark.
/// </summary>
public static bool IsMarkPresentAlpha(IEncounterTemplate enc) => enc is IAlphaReadOnly { IsAlpha: true};
public static bool IsMarkValidAlpha(IEncounterTemplate enc, PKM pk)
{
var expect = enc is IAlphaReadOnly { IsAlpha: true };
return IsMarkValidAlpha(pk, expect);
}
/// <summary>
/// Checks if the input can have the <see cref="IRibbonSetMark9.RibbonMarkJumbo"/> mark.
@ -181,9 +201,9 @@ public static class MarkRules
/// <summary>
/// Gets the maximum obtainable <see cref="RibbonIndex"/> value for the format.
/// </summary>
public static RibbonIndex GetMaxAffixValue(int entityFormat) => entityFormat switch
public static RibbonIndex GetMaxAffixValue(int entityFormat, bool visitedHOME) => entityFormat switch
{
<= 8 => MarkSlump, // Pioneer and Twinkling Star cannot be selected in SW/SH.
<= 8 when !visitedHOME => MarkSlump, // Pioneer and Twinkling Star cannot be selected in SW/SH.
_ => MarkTitan, // Max ribbon visible in SV.
};
}

View file

@ -9,7 +9,7 @@ public static class RibbonVerifierMark9
{
public static void Parse(this IRibbonSetMark9 r, RibbonVerifierArguments args, ref RibbonResultList list)
{
if (r.RibbonMarkAlpha != MarkRules.IsMarkPresentAlpha(args.Encounter))
if (!MarkRules.IsMarkValidAlpha(args.Encounter, args.Entity))
list.Add(MarkAlpha, !r.RibbonMarkAlpha);
if (r.RibbonMarkGourmand && !MarkRules.IsMarkValidGourmand(args.History))
list.Add(MarkGourmand);

View file

@ -161,10 +161,6 @@ public sealed class TransferVerifier : Verifier
public void VerifyTransferLegalityG9(LegalityAnalysis data)
{
var enc = data.EncounterMatch;
if (enc.Generation != 9)
data.AddLine(GetInvalid(LTransferBad));
var pk = data.Entity;
var pt = PersonalTable.SV;
if (!pt.IsPresentInGame(pk.Species, pk.Form))
@ -178,6 +174,7 @@ public sealed class TransferVerifier : Verifier
WC8 { IsHOMEGift: true } => true,
WB8 { IsHOMEGift: true } => true,
WA8 { IsHOMEGift: true } => true,
WC9 { IsHOMEGift: true } => true,
_ => enc.Generation < 8,
};
@ -188,17 +185,21 @@ public sealed class TransferVerifier : Verifier
if (pk.LGPE || pk.GO)
return; // can have any size value
if (s.HeightScalar != 0)
data.AddLine(GetInvalid(LTransferBad));
if (s.WeightScalar != 0)
data.AddLine(GetInvalid(LTransferBad));
// HOME in 3.0.0 will actively re-roll (0,0) to non-zero values.
// Transfer between Gen8 can be done before HOME 3.0.0, so we can allow (0,0) or re-rolled.
if (pk.Context is not (EntityContext.Gen8 or EntityContext.Gen8a or EntityContext.Gen8b))
{
if (s is { HeightScalar: 0, WeightScalar: 0 } && !data.Info.EvoChainsAllGens.HasVisitedPLA)
data.AddLine(GetInvalid(LTransferBad));
}
}
private void VerifyHOMETracker(LegalityAnalysis data, PKM pk)
{
// Tracker value is set via Transfer across HOME.
// Can't validate the actual values (we aren't the server), so we can only check against zero.
if (pk is IHomeTrack {Tracker: 0})
if (pk is IHomeTrack { HasTracker: false })
{
data.AddLine(Get(LTransferTrackerMissing, ParseSettings.Gen8TransferTrackerNotPresent));
// To the reader: It seems like the best course of action for setting a tracker is:

View file

@ -648,7 +648,7 @@ public sealed class WA8 : DataMysteryGift, ILangNick, INature, IGigantamax, IDyn
if (OriginGame != 0 && OriginGame != pk.Version)
{
if (OriginGame is (int)GameVersion.PLA && !(pk.Version is (int)GameVersion.SW && pk.Met_Location == Locations.HOME_SWLA))
if (OriginGame is (int)GameVersion.PLA && !(pk.Version is (int)GameVersion.SW && pk.Met_Location == LocationsHOME.SWLA))
return false;
}
if (EncryptionConstant != 0)
@ -684,7 +684,7 @@ public sealed class WA8 : DataMysteryGift, ILangNick, INature, IGigantamax, IDyn
if (!IsMatchEggLocation(pk)) return false;
if (pk is PK8)
{
if (pk.Met_Location != Locations.HOME_SWLA)
if (pk.Met_Location != LocationsHOME.SWLA)
return false;
}
else

View file

@ -36,7 +36,7 @@ public sealed class WB8 : DataMysteryGift, ILangNick, INature, IRibbonIndex, ICo
// TODO: public byte RestrictVersion?
public bool CanBeReceivedByVersion(int v, PKM pk) => v is (int) GameVersion.BD or (int) GameVersion.SP || (pk is PK8 && Locations.IsValidMetBDSP((ushort)pk.Met_Location, pk.Version));
public bool CanBeReceivedByVersion(int v, PKM pk) => v is (int) GameVersion.BD or (int) GameVersion.SP || (pk is PK8 && LocationsHOME.IsValidMetBDSP((ushort)pk.Met_Location, pk.Version));
// General Card Properties
@ -639,9 +639,9 @@ public sealed class WB8 : DataMysteryGift, ILangNick, INature, IRibbonIndex, ICo
if (!string.IsNullOrEmpty(OT) && OT != pk.OT_Name) return false;
if (OriginGame != 0 && OriginGame != pk.Version)
{
if (OriginGame is (int)GameVersion.BD && !(pk.Version is (int)GameVersion.SW && pk.Met_Location == Locations.HOME_SWBD))
if (OriginGame is (int)GameVersion.BD && !(pk.Version is (int)GameVersion.SW && pk.Met_Location == LocationsHOME.SWBD))
return false;
if (OriginGame is (int)GameVersion.SP && !(pk.Version is (int)GameVersion.SH && pk.Met_Location == Locations.HOME_SHSP))
if (OriginGame is (int)GameVersion.SP && !(pk.Version is (int)GameVersion.SH && pk.Met_Location == LocationsHOME.SHSP))
return false;
}
if (EncryptionConstant != 0)
@ -678,7 +678,7 @@ public sealed class WB8 : DataMysteryGift, ILangNick, INature, IRibbonIndex, ICo
if (!IsMatchEggLocation(pk)) return false;
if (pk is PK8)
{
if (!Locations.IsValidMetBDSP((ushort)pk.Met_Location, pk.Version))
if (!LocationsHOME.IsValidMetBDSP((ushort)pk.Met_Location, pk.Version))
return false;
}
else

View file

@ -574,7 +574,7 @@ public sealed class WC8 : DataMysteryGift, ILangNick, INature, IGigantamax, IDyn
if (!tr.IsShiny(pid, 8))
return pid;
if (IsHOMEGift && IsHOMEShinyPossible())
if (IsHOMEGift && !IsHOMEShinyPossible())
return ForceAntiShiny(pid);
return pid;
}

View file

@ -31,12 +31,13 @@ public sealed class WC9 : DataMysteryGift, ILangNick, INature, ITeraType, IRibbo
public byte RestrictVersion { get => Data[0xE]; set => Data[0xE] = value; }
public bool CanBeReceivedByVersion(int v) => RestrictVersion switch
public bool CanBeReceivedByVersion(PKM pk) => RestrictVersion switch
{
0 when !IsEntity => true, // Whatever, essentially unrestricted for SL/VL receipt. No Entity gifts are 0.
1 => v is (int)GameVersion.SL,
2 => v is (int)GameVersion.VL,
3 => v is (int)GameVersion.SL or (int)GameVersion.VL,
1 => pk.Version is (int)GameVersion.SL || pk is PK8 { Met_Location: LocationsHOME.SWSL, Version: (int)GameVersion.SW },
2 => pk.Version is (int)GameVersion.VL || pk is PK8 { Met_Location: LocationsHOME.SHVL, Version: (int)GameVersion.SH },
3 => pk.Version is (int)GameVersion.SL || pk is PK8 { Met_Location: LocationsHOME.SWSL, Version: (int)GameVersion.SW }
|| pk.Version is (int)GameVersion.VL || pk is PK8 { Met_Location: LocationsHOME.SHVL, Version: (int)GameVersion.SH },
_ => throw new ArgumentOutOfRangeException(nameof(RestrictVersion), RestrictVersion, null),
};
@ -417,6 +418,8 @@ public sealed class WC9 : DataMysteryGift, ILangNick, INature, ITeraType, IRibbo
return 0x124 + (index * 0x1C);
}
public bool IsHOMEGift => CardID >= 9000;
public bool CanHandleOT(int language) => !GetHasOT(language);
public override GameVersion Version
@ -492,11 +495,11 @@ public sealed class WC9 : DataMysteryGift, ILangNick, INature, ITeraType, IRibbo
// The game doesn't have random tera types.
pk.SetMaximumPPCurrent();
if ((tr.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version))
if ((tr.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk))
{
// give random valid game
do { pk.Version = (int)GameVersion.SL + rnd.Next(2); }
while (!CanBeReceivedByVersion(pk.Version));
while (!CanBeReceivedByVersion(pk));
}
if (OTGender >= 2)
@ -711,7 +714,16 @@ public sealed class WC9 : DataMysteryGift, ILangNick, INature, ITeraType, IRibbo
{
if (!shinyType.IsValid(pk)) return false;
if (!IsMatchEggLocation(pk)) return false;
if (MetLocation != pk.Met_Location) return false;
if (pk is PK8)
{
if (!LocationsHOME.IsValidMetSV((ushort)pk.Met_Location, pk.Version))
return false;
}
else
{
if (MetLocation != pk.Met_Location)
return false;
}
}
if (MetLevel != 0 && MetLevel != pk.Met_Level) return false;
@ -722,7 +734,7 @@ public sealed class WC9 : DataMysteryGift, ILangNick, INature, ITeraType, IRibbo
if (pk is IScaledSize s)
{
if (s.HeightScalar != HeightValue)
if (!Encounter9RNG.IsHeightMatchSV(pk, HeightValue))
return false;
if (s.WeightScalar != WeightValue)
return false;

View file

@ -14,12 +14,13 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
{
// Internal Attributes set on creation
private readonly Memory<byte> Buffer; // Raw Storage
public int SerializedSize => Buffer.Length;
private Span<byte> Data => Buffer.Span;
public GameDataCore(Memory<byte> buffer)
{
if (buffer.Length != HomeCrypto.SIZE_1CORE)
throw new ArgumentException("Invalid Format 1 Core Data!");
if (buffer.Length != HomeCrypto.SIZE_CORE)
throw new ArgumentException("Invalid Core Data Size!");
Buffer = buffer;
}
@ -39,200 +40,195 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
public ushort TID16 { get => ReadUInt16LittleEndian(Data[0x0F..]); set => WriteUInt16LittleEndian(Data[0x0F..], value); }
public ushort SID16 { get => ReadUInt16LittleEndian(Data[0x11..]); set => WriteUInt16LittleEndian(Data[0x11..], value); }
public uint EXP { get => ReadUInt32LittleEndian(Data[0x13..]); set => WriteUInt32LittleEndian(Data[0x13..], value); }
public int Ability { get => ReadUInt16LittleEndian(Data[0x17..]); set => WriteUInt16LittleEndian(Data[0x17..], (ushort)value); }
public int AbilityNumber { get => Data[0x19] & 7; set => Data[0x19] = (byte)((Data[0x19] & ~7) | (value & 7)); }
public bool IsFavorite { get => Data[0x1A] != 0; set => Data[0x1A] = (byte)(value ? 1 : 0); }
public int MarkValue { get => ReadUInt16LittleEndian(Data[0x1B..]); set => WriteUInt16LittleEndian(Data[0x1B..], (ushort)value); }
public uint PID { get => ReadUInt32LittleEndian(Data[0x1D..]); set => WriteUInt32LittleEndian(Data[0x1D..], value); }
public int Nature { get => Data[0x21]; set => Data[0x21] = (byte)value; }
public int StatNature { get => Data[0x22]; set => Data[0x22] = (byte)value; }
public bool FatefulEncounter { get => Data[0x23] != 0; set => Data[0x23] = (byte)(value ? 1 : 0); }
public int Gender { get => Data[0x24]; set => Data[0x24] = (byte)value; }
public byte Form { get => Data[0x25]; set => WriteUInt16LittleEndian(Data[0x25..], value); }
public int EV_HP { get => Data[0x27]; set => Data[0x27] = (byte)value; }
public int EV_ATK { get => Data[0x28]; set => Data[0x28] = (byte)value; }
public int EV_DEF { get => Data[0x29]; set => Data[0x29] = (byte)value; }
public int EV_SPE { get => Data[0x2A]; set => Data[0x2A] = (byte)value; }
public int EV_SPA { get => Data[0x2B]; set => Data[0x2B] = (byte)value; }
public int EV_SPD { get => Data[0x2C]; set => Data[0x2C] = (byte)value; }
public byte CNT_Cool { get => Data[0x2D]; set => Data[0x2D] = value; }
public byte CNT_Beauty { get => Data[0x2E]; set => Data[0x2E] = value; }
public byte CNT_Cute { get => Data[0x2F]; set => Data[0x2F] = value; }
public byte CNT_Smart { get => Data[0x30]; set => Data[0x30] = value; }
public byte CNT_Tough { get => Data[0x31]; set => Data[0x31] = value; }
public byte CNT_Sheen { get => Data[0x32]; set => Data[0x32] = value; }
private byte PKRS { get => Data[0x33]; set => Data[0x33] = value; }
public int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); }
public int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | (value << 4)); }
public bool IsFavorite { get => Data[0x17] != 0; set => Data[0x17] = (byte)(value ? 1 : 0); }
public int MarkValue { get => ReadUInt16LittleEndian(Data[0x18..]); set => WriteUInt16LittleEndian(Data[0x18..], (ushort)value); }
public uint PID { get => ReadUInt32LittleEndian(Data[0x1A..]); set => WriteUInt32LittleEndian(Data[0x1A..], value); }
public int Nature { get => Data[0x1E]; set => Data[0x1E] = (byte)value; }
public int StatNature { get => Data[0x1F]; set => Data[0x1F] = (byte)value; }
public bool FatefulEncounter { get => Data[0x20] != 0; set => Data[0x20] = (byte)(value ? 1 : 0); }
public int Gender { get => Data[0x21]; set => Data[0x21] = (byte)value; }
public byte Form { get => Data[0x22]; set => WriteUInt16LittleEndian(Data[0x22..], value); }
public int EV_HP { get => Data[0x24]; set => Data[0x24] = (byte)value; }
public int EV_ATK { get => Data[0x25]; set => Data[0x25] = (byte)value; }
public int EV_DEF { get => Data[0x26]; set => Data[0x26] = (byte)value; }
public int EV_SPE { get => Data[0x27]; set => Data[0x27] = (byte)value; }
public int EV_SPA { get => Data[0x28]; set => Data[0x28] = (byte)value; }
public int EV_SPD { get => Data[0x29]; set => Data[0x29] = (byte)value; }
public byte CNT_Cool { get => Data[0x2A]; set => Data[0x2A] = value; }
public byte CNT_Beauty { get => Data[0x2B]; set => Data[0x2B] = value; }
public byte CNT_Cute { get => Data[0x2C]; set => Data[0x2C] = value; }
public byte CNT_Smart { get => Data[0x2D]; set => Data[0x2D] = value; }
public byte CNT_Tough { get => Data[0x2E]; set => Data[0x2E] = value; }
public byte CNT_Sheen { get => Data[0x2F]; set => Data[0x2F] = value; }
private bool GetFlag(int offset, int bit) => FlagUtil.GetFlag(Data, offset, bit);
private void SetFlag(int offset, int bit, bool value) => FlagUtil.SetFlag(Data, offset, bit, value);
public bool RibbonChampionKalos { get => GetFlag(0x34, 0); set => SetFlag(0x34, 0, value); }
public bool RibbonChampionG3 { get => GetFlag(0x34, 1); set => SetFlag(0x34, 1, value); }
public bool RibbonChampionSinnoh { get => GetFlag(0x34, 2); set => SetFlag(0x34, 2, value); }
public bool RibbonBestFriends { get => GetFlag(0x34, 3); set => SetFlag(0x34, 3, value); }
public bool RibbonTraining { get => GetFlag(0x34, 4); set => SetFlag(0x34, 4, value); }
public bool RibbonBattlerSkillful { get => GetFlag(0x34, 5); set => SetFlag(0x34, 5, value); }
public bool RibbonBattlerExpert { get => GetFlag(0x34, 6); set => SetFlag(0x34, 6, value); }
public bool RibbonEffort { get => GetFlag(0x34, 7); set => SetFlag(0x34, 7, value); }
public bool RibbonChampionKalos { get => GetFlag(0x30, 0); set => SetFlag(0x30, 0, value); }
public bool RibbonChampionG3 { get => GetFlag(0x30, 1); set => SetFlag(0x30, 1, value); }
public bool RibbonChampionSinnoh { get => GetFlag(0x30, 2); set => SetFlag(0x30, 2, value); }
public bool RibbonBestFriends { get => GetFlag(0x30, 3); set => SetFlag(0x30, 3, value); }
public bool RibbonTraining { get => GetFlag(0x30, 4); set => SetFlag(0x30, 4, value); }
public bool RibbonBattlerSkillful { get => GetFlag(0x30, 5); set => SetFlag(0x30, 5, value); }
public bool RibbonBattlerExpert { get => GetFlag(0x30, 6); set => SetFlag(0x30, 6, value); }
public bool RibbonEffort { get => GetFlag(0x30, 7); set => SetFlag(0x30, 7, value); }
public bool RibbonAlert { get => GetFlag(0x35, 0); set => SetFlag(0x35, 0, value); }
public bool RibbonShock { get => GetFlag(0x35, 1); set => SetFlag(0x35, 1, value); }
public bool RibbonDowncast { get => GetFlag(0x35, 2); set => SetFlag(0x35, 2, value); }
public bool RibbonCareless { get => GetFlag(0x35, 3); set => SetFlag(0x35, 3, value); }
public bool RibbonRelax { get => GetFlag(0x35, 4); set => SetFlag(0x35, 4, value); }
public bool RibbonSnooze { get => GetFlag(0x35, 5); set => SetFlag(0x35, 5, value); }
public bool RibbonSmile { get => GetFlag(0x35, 6); set => SetFlag(0x35, 6, value); }
public bool RibbonGorgeous { get => GetFlag(0x35, 7); set => SetFlag(0x35, 7, value); }
public bool RibbonAlert { get => GetFlag(0x31, 0); set => SetFlag(0x31, 0, value); }
public bool RibbonShock { get => GetFlag(0x31, 1); set => SetFlag(0x31, 1, value); }
public bool RibbonDowncast { get => GetFlag(0x31, 2); set => SetFlag(0x31, 2, value); }
public bool RibbonCareless { get => GetFlag(0x31, 3); set => SetFlag(0x31, 3, value); }
public bool RibbonRelax { get => GetFlag(0x31, 4); set => SetFlag(0x31, 4, value); }
public bool RibbonSnooze { get => GetFlag(0x31, 5); set => SetFlag(0x31, 5, value); }
public bool RibbonSmile { get => GetFlag(0x31, 6); set => SetFlag(0x31, 6, value); }
public bool RibbonGorgeous { get => GetFlag(0x31, 7); set => SetFlag(0x31, 7, value); }
public bool RibbonRoyal { get => GetFlag(0x36, 0); set => SetFlag(0x36, 0, value); }
public bool RibbonGorgeousRoyal { get => GetFlag(0x36, 1); set => SetFlag(0x36, 1, value); }
public bool RibbonArtist { get => GetFlag(0x36, 2); set => SetFlag(0x36, 2, value); }
public bool RibbonFootprint { get => GetFlag(0x36, 3); set => SetFlag(0x36, 3, value); }
public bool RibbonRecord { get => GetFlag(0x36, 4); set => SetFlag(0x36, 4, value); }
public bool RibbonLegend { get => GetFlag(0x36, 5); set => SetFlag(0x36, 5, value); }
public bool RibbonCountry { get => GetFlag(0x36, 6); set => SetFlag(0x36, 6, value); }
public bool RibbonNational { get => GetFlag(0x36, 7); set => SetFlag(0x36, 7, value); }
public bool RibbonRoyal { get => GetFlag(0x32, 0); set => SetFlag(0x32, 0, value); }
public bool RibbonGorgeousRoyal { get => GetFlag(0x32, 1); set => SetFlag(0x32, 1, value); }
public bool RibbonArtist { get => GetFlag(0x32, 2); set => SetFlag(0x32, 2, value); }
public bool RibbonFootprint { get => GetFlag(0x32, 3); set => SetFlag(0x32, 3, value); }
public bool RibbonRecord { get => GetFlag(0x32, 4); set => SetFlag(0x32, 4, value); }
public bool RibbonLegend { get => GetFlag(0x32, 5); set => SetFlag(0x32, 5, value); }
public bool RibbonCountry { get => GetFlag(0x32, 6); set => SetFlag(0x32, 6, value); }
public bool RibbonNational { get => GetFlag(0x32, 7); set => SetFlag(0x32, 7, value); }
public bool RibbonEarth { get => GetFlag(0x37, 0); set => SetFlag(0x37, 0, value); }
public bool RibbonWorld { get => GetFlag(0x37, 1); set => SetFlag(0x37, 1, value); }
public bool RibbonClassic { get => GetFlag(0x37, 2); set => SetFlag(0x37, 2, value); }
public bool RibbonPremier { get => GetFlag(0x37, 3); set => SetFlag(0x37, 3, value); }
public bool RibbonEvent { get => GetFlag(0x37, 4); set => SetFlag(0x37, 4, value); }
public bool RibbonBirthday { get => GetFlag(0x37, 5); set => SetFlag(0x37, 5, value); }
public bool RibbonSpecial { get => GetFlag(0x37, 6); set => SetFlag(0x37, 6, value); }
public bool RibbonSouvenir { get => GetFlag(0x37, 7); set => SetFlag(0x37, 7, value); }
public bool RibbonEarth { get => GetFlag(0x33, 0); set => SetFlag(0x33, 0, value); }
public bool RibbonWorld { get => GetFlag(0x33, 1); set => SetFlag(0x33, 1, value); }
public bool RibbonClassic { get => GetFlag(0x33, 2); set => SetFlag(0x33, 2, value); }
public bool RibbonPremier { get => GetFlag(0x33, 3); set => SetFlag(0x33, 3, value); }
public bool RibbonEvent { get => GetFlag(0x33, 4); set => SetFlag(0x33, 4, value); }
public bool RibbonBirthday { get => GetFlag(0x33, 5); set => SetFlag(0x33, 5, value); }
public bool RibbonSpecial { get => GetFlag(0x33, 6); set => SetFlag(0x33, 6, value); }
public bool RibbonSouvenir { get => GetFlag(0x33, 7); set => SetFlag(0x33, 7, value); }
// ribbon u32
public bool RibbonWishing { get => GetFlag(0x38, 0); set => SetFlag(0x38, 0, value); }
public bool RibbonChampionBattle { get => GetFlag(0x38, 1); set => SetFlag(0x38, 1, value); }
public bool RibbonChampionRegional { get => GetFlag(0x38, 2); set => SetFlag(0x38, 2, value); }
public bool RibbonChampionNational { get => GetFlag(0x38, 3); set => SetFlag(0x38, 3, value); }
public bool RibbonChampionWorld { get => GetFlag(0x38, 4); set => SetFlag(0x38, 4, value); }
public bool HasContestMemoryRibbon { get => GetFlag(0x38, 5); set => SetFlag(0x38, 5, value); }
public bool HasBattleMemoryRibbon { get => GetFlag(0x38, 6); set => SetFlag(0x38, 6, value); }
public bool RibbonChampionG6Hoenn { get => GetFlag(0x38, 7); set => SetFlag(0x38, 7, value); }
public bool RibbonWishing { get => GetFlag(0x34, 0); set => SetFlag(0x34, 0, value); }
public bool RibbonChampionBattle { get => GetFlag(0x34, 1); set => SetFlag(0x34, 1, value); }
public bool RibbonChampionRegional { get => GetFlag(0x34, 2); set => SetFlag(0x34, 2, value); }
public bool RibbonChampionNational { get => GetFlag(0x34, 3); set => SetFlag(0x34, 3, value); }
public bool RibbonChampionWorld { get => GetFlag(0x34, 4); set => SetFlag(0x34, 4, value); }
public bool HasContestMemoryRibbon { get => GetFlag(0x34, 5); set => SetFlag(0x34, 5, value); }
public bool HasBattleMemoryRibbon { get => GetFlag(0x34, 6); set => SetFlag(0x34, 6, value); }
public bool RibbonChampionG6Hoenn { get => GetFlag(0x34, 7); set => SetFlag(0x34, 7, value); }
public bool RibbonContestStar { get => GetFlag(0x39, 0); set => SetFlag(0x39, 0, value); }
public bool RibbonMasterCoolness { get => GetFlag(0x39, 1); set => SetFlag(0x39, 1, value); }
public bool RibbonMasterBeauty { get => GetFlag(0x39, 2); set => SetFlag(0x39, 2, value); }
public bool RibbonMasterCuteness { get => GetFlag(0x39, 3); set => SetFlag(0x39, 3, value); }
public bool RibbonMasterCleverness { get => GetFlag(0x39, 4); set => SetFlag(0x39, 4, value); }
public bool RibbonMasterToughness { get => GetFlag(0x39, 5); set => SetFlag(0x39, 5, value); }
public bool RibbonChampionAlola { get => GetFlag(0x39, 6); set => SetFlag(0x39, 6, value); }
public bool RibbonBattleRoyale { get => GetFlag(0x39, 7); set => SetFlag(0x39, 7, value); }
public bool RibbonContestStar { get => GetFlag(0x35, 0); set => SetFlag(0x35, 0, value); }
public bool RibbonMasterCoolness { get => GetFlag(0x35, 1); set => SetFlag(0x35, 1, value); }
public bool RibbonMasterBeauty { get => GetFlag(0x35, 2); set => SetFlag(0x35, 2, value); }
public bool RibbonMasterCuteness { get => GetFlag(0x35, 3); set => SetFlag(0x35, 3, value); }
public bool RibbonMasterCleverness { get => GetFlag(0x35, 4); set => SetFlag(0x35, 4, value); }
public bool RibbonMasterToughness { get => GetFlag(0x35, 5); set => SetFlag(0x35, 5, value); }
public bool RibbonChampionAlola { get => GetFlag(0x35, 6); set => SetFlag(0x35, 6, value); }
public bool RibbonBattleRoyale { get => GetFlag(0x35, 7); set => SetFlag(0x35, 7, value); }
public bool RibbonBattleTreeGreat { get => GetFlag(0x3A, 0); set => SetFlag(0x3A, 0, value); }
public bool RibbonBattleTreeMaster { get => GetFlag(0x3A, 1); set => SetFlag(0x3A, 1, value); }
public bool RibbonChampionGalar { get => GetFlag(0x3A, 2); set => SetFlag(0x3A, 2, value); }
public bool RibbonTowerMaster { get => GetFlag(0x3A, 3); set => SetFlag(0x3A, 3, value); }
public bool RibbonMasterRank { get => GetFlag(0x3A, 4); set => SetFlag(0x3A, 4, value); }
public bool RibbonMarkLunchtime { get => GetFlag(0x3A, 5); set => SetFlag(0x3A, 5, value); }
public bool RibbonMarkSleepyTime { get => GetFlag(0x3A, 6); set => SetFlag(0x3A, 6, value); }
public bool RibbonMarkDusk { get => GetFlag(0x3A, 7); set => SetFlag(0x3A, 7, value); }
public bool RibbonBattleTreeGreat { get => GetFlag(0x36, 0); set => SetFlag(0x36, 0, value); }
public bool RibbonBattleTreeMaster { get => GetFlag(0x36, 1); set => SetFlag(0x36, 1, value); }
public bool RibbonChampionGalar { get => GetFlag(0x36, 2); set => SetFlag(0x36, 2, value); }
public bool RibbonTowerMaster { get => GetFlag(0x36, 3); set => SetFlag(0x36, 3, value); }
public bool RibbonMasterRank { get => GetFlag(0x36, 4); set => SetFlag(0x36, 4, value); }
public bool RibbonMarkLunchtime { get => GetFlag(0x36, 5); set => SetFlag(0x36, 5, value); }
public bool RibbonMarkSleepyTime { get => GetFlag(0x36, 6); set => SetFlag(0x36, 6, value); }
public bool RibbonMarkDusk { get => GetFlag(0x36, 7); set => SetFlag(0x36, 7, value); }
public bool RibbonMarkDawn { get => GetFlag(0x3B, 0); set => SetFlag(0x3B, 0, value); }
public bool RibbonMarkCloudy { get => GetFlag(0x3B, 1); set => SetFlag(0x3B, 1, value); }
public bool RibbonMarkRainy { get => GetFlag(0x3B, 2); set => SetFlag(0x3B, 2, value); }
public bool RibbonMarkStormy { get => GetFlag(0x3B, 3); set => SetFlag(0x3B, 3, value); }
public bool RibbonMarkSnowy { get => GetFlag(0x3B, 4); set => SetFlag(0x3B, 4, value); }
public bool RibbonMarkBlizzard { get => GetFlag(0x3B, 5); set => SetFlag(0x3B, 5, value); }
public bool RibbonMarkDry { get => GetFlag(0x3B, 6); set => SetFlag(0x3B, 6, value); }
public bool RibbonMarkSandstorm { get => GetFlag(0x3B, 7); set => SetFlag(0x3B, 7, value); }
public bool RibbonMarkDawn { get => GetFlag(0x37, 0); set => SetFlag(0x37, 0, value); }
public bool RibbonMarkCloudy { get => GetFlag(0x37, 1); set => SetFlag(0x37, 1, value); }
public bool RibbonMarkRainy { get => GetFlag(0x37, 2); set => SetFlag(0x37, 2, value); }
public bool RibbonMarkStormy { get => GetFlag(0x37, 3); set => SetFlag(0x37, 3, value); }
public bool RibbonMarkSnowy { get => GetFlag(0x37, 4); set => SetFlag(0x37, 4, value); }
public bool RibbonMarkBlizzard { get => GetFlag(0x37, 5); set => SetFlag(0x37, 5, value); }
public bool RibbonMarkDry { get => GetFlag(0x37, 6); set => SetFlag(0x37, 6, value); }
public bool RibbonMarkSandstorm { get => GetFlag(0x37, 7); set => SetFlag(0x37, 7, value); }
public byte RibbonCountMemoryContest { get => Data[0x3C]; set => HasContestMemoryRibbon = (Data[0x3C] = value) != 0; }
public byte RibbonCountMemoryBattle { get => Data[0x3D]; set => HasBattleMemoryRibbon = (Data[0x3D] = value) != 0; }
public byte RibbonCountMemoryContest { get => Data[0x38]; set => HasContestMemoryRibbon = (Data[0x38] = value) != 0; }
public byte RibbonCountMemoryBattle { get => Data[0x39]; set => HasBattleMemoryRibbon = (Data[0x39] = value) != 0; }
// !!! no padding, unlike PKM formats!
// 0x3E Ribbon 3
public bool RibbonMarkMisty { get => GetFlag(0x3E, 0); set => SetFlag(0x3E, 0, value); }
public bool RibbonMarkDestiny { get => GetFlag(0x3E, 1); set => SetFlag(0x3E, 1, value); }
public bool RibbonMarkFishing { get => GetFlag(0x3E, 2); set => SetFlag(0x3E, 2, value); }
public bool RibbonMarkCurry { get => GetFlag(0x3E, 3); set => SetFlag(0x3E, 3, value); }
public bool RibbonMarkUncommon { get => GetFlag(0x3E, 4); set => SetFlag(0x3E, 4, value); }
public bool RibbonMarkRare { get => GetFlag(0x3E, 5); set => SetFlag(0x3E, 5, value); }
public bool RibbonMarkRowdy { get => GetFlag(0x3E, 6); set => SetFlag(0x3E, 6, value); }
public bool RibbonMarkAbsentMinded { get => GetFlag(0x3E, 7); set => SetFlag(0x3E, 7, value); }
public bool RibbonMarkMisty { get => GetFlag(0x3A, 0); set => SetFlag(0x3A, 0, value); }
public bool RibbonMarkDestiny { get => GetFlag(0x3A, 1); set => SetFlag(0x3A, 1, value); }
public bool RibbonMarkFishing { get => GetFlag(0x3A, 2); set => SetFlag(0x3A, 2, value); }
public bool RibbonMarkCurry { get => GetFlag(0x3A, 3); set => SetFlag(0x3A, 3, value); }
public bool RibbonMarkUncommon { get => GetFlag(0x3A, 4); set => SetFlag(0x3A, 4, value); }
public bool RibbonMarkRare { get => GetFlag(0x3A, 5); set => SetFlag(0x3A, 5, value); }
public bool RibbonMarkRowdy { get => GetFlag(0x3A, 6); set => SetFlag(0x3A, 6, value); }
public bool RibbonMarkAbsentMinded { get => GetFlag(0x3A, 7); set => SetFlag(0x3A, 7, value); }
public bool RibbonMarkJittery { get => GetFlag(0x3F, 0); set => SetFlag(0x3F, 0, value); }
public bool RibbonMarkExcited { get => GetFlag(0x3F, 1); set => SetFlag(0x3F, 1, value); }
public bool RibbonMarkCharismatic { get => GetFlag(0x3F, 2); set => SetFlag(0x3F, 2, value); }
public bool RibbonMarkCalmness { get => GetFlag(0x3F, 3); set => SetFlag(0x3F, 3, value); }
public bool RibbonMarkIntense { get => GetFlag(0x3F, 4); set => SetFlag(0x3F, 4, value); }
public bool RibbonMarkZonedOut { get => GetFlag(0x3F, 5); set => SetFlag(0x3F, 5, value); }
public bool RibbonMarkJoyful { get => GetFlag(0x3F, 6); set => SetFlag(0x3F, 6, value); }
public bool RibbonMarkAngry { get => GetFlag(0x3F, 7); set => SetFlag(0x3F, 7, value); }
public bool RibbonMarkJittery { get => GetFlag(0x3B, 0); set => SetFlag(0x3B, 0, value); }
public bool RibbonMarkExcited { get => GetFlag(0x3B, 1); set => SetFlag(0x3B, 1, value); }
public bool RibbonMarkCharismatic { get => GetFlag(0x3B, 2); set => SetFlag(0x3B, 2, value); }
public bool RibbonMarkCalmness { get => GetFlag(0x3B, 3); set => SetFlag(0x3B, 3, value); }
public bool RibbonMarkIntense { get => GetFlag(0x3B, 4); set => SetFlag(0x3B, 4, value); }
public bool RibbonMarkZonedOut { get => GetFlag(0x3B, 5); set => SetFlag(0x3B, 5, value); }
public bool RibbonMarkJoyful { get => GetFlag(0x3B, 6); set => SetFlag(0x3B, 6, value); }
public bool RibbonMarkAngry { get => GetFlag(0x3B, 7); set => SetFlag(0x3B, 7, value); }
public bool RibbonMarkSmiley { get => GetFlag(0x40, 0); set => SetFlag(0x40, 0, value); }
public bool RibbonMarkTeary { get => GetFlag(0x40, 1); set => SetFlag(0x40, 1, value); }
public bool RibbonMarkUpbeat { get => GetFlag(0x40, 2); set => SetFlag(0x40, 2, value); }
public bool RibbonMarkPeeved { get => GetFlag(0x40, 3); set => SetFlag(0x40, 3, value); }
public bool RibbonMarkIntellectual { get => GetFlag(0x40, 4); set => SetFlag(0x40, 4, value); }
public bool RibbonMarkFerocious { get => GetFlag(0x40, 5); set => SetFlag(0x40, 5, value); }
public bool RibbonMarkCrafty { get => GetFlag(0x40, 6); set => SetFlag(0x40, 6, value); }
public bool RibbonMarkScowling { get => GetFlag(0x40, 7); set => SetFlag(0x40, 7, value); }
public bool RibbonMarkSmiley { get => GetFlag(0x3C, 0); set => SetFlag(0x3C, 0, value); }
public bool RibbonMarkTeary { get => GetFlag(0x3C, 1); set => SetFlag(0x3C, 1, value); }
public bool RibbonMarkUpbeat { get => GetFlag(0x3C, 2); set => SetFlag(0x3C, 2, value); }
public bool RibbonMarkPeeved { get => GetFlag(0x3C, 3); set => SetFlag(0x3C, 3, value); }
public bool RibbonMarkIntellectual { get => GetFlag(0x3C, 4); set => SetFlag(0x3C, 4, value); }
public bool RibbonMarkFerocious { get => GetFlag(0x3C, 5); set => SetFlag(0x3C, 5, value); }
public bool RibbonMarkCrafty { get => GetFlag(0x3C, 6); set => SetFlag(0x3C, 6, value); }
public bool RibbonMarkScowling { get => GetFlag(0x3C, 7); set => SetFlag(0x3C, 7, value); }
public bool RibbonMarkKindly { get => GetFlag(0x41, 0); set => SetFlag(0x41, 0, value); }
public bool RibbonMarkFlustered { get => GetFlag(0x41, 1); set => SetFlag(0x41, 1, value); }
public bool RibbonMarkPumpedUp { get => GetFlag(0x41, 2); set => SetFlag(0x41, 2, value); }
public bool RibbonMarkZeroEnergy { get => GetFlag(0x41, 3); set => SetFlag(0x41, 3, value); }
public bool RibbonMarkPrideful { get => GetFlag(0x41, 4); set => SetFlag(0x41, 4, value); }
public bool RibbonMarkUnsure { get => GetFlag(0x41, 5); set => SetFlag(0x41, 5, value); }
public bool RibbonMarkHumble { get => GetFlag(0x41, 6); set => SetFlag(0x41, 6, value); }
public bool RibbonMarkThorny { get => GetFlag(0x41, 7); set => SetFlag(0x41, 7, value); }
public bool RibbonMarkKindly { get => GetFlag(0x3D, 0); set => SetFlag(0x3D, 0, value); }
public bool RibbonMarkFlustered { get => GetFlag(0x3D, 1); set => SetFlag(0x3D, 1, value); }
public bool RibbonMarkPumpedUp { get => GetFlag(0x3D, 2); set => SetFlag(0x3D, 2, value); }
public bool RibbonMarkZeroEnergy { get => GetFlag(0x3D, 3); set => SetFlag(0x3D, 3, value); }
public bool RibbonMarkPrideful { get => GetFlag(0x3D, 4); set => SetFlag(0x3D, 4, value); }
public bool RibbonMarkUnsure { get => GetFlag(0x3D, 5); set => SetFlag(0x3D, 5, value); }
public bool RibbonMarkHumble { get => GetFlag(0x3D, 6); set => SetFlag(0x3D, 6, value); }
public bool RibbonMarkThorny { get => GetFlag(0x3D, 7); set => SetFlag(0x3D, 7, value); }
public bool RibbonMarkVigor { get => GetFlag(0x42, 0); set => SetFlag(0x42, 0, value); }
public bool RibbonMarkSlump { get => GetFlag(0x42, 1); set => SetFlag(0x42, 1, value); }
public bool RibbonHisui { get => GetFlag(0x42, 2); set => SetFlag(0x42, 2, value); }
public bool RibbonTwinklingStar { get => GetFlag(0x42, 3); set => SetFlag(0x42, 3, value); }
public bool RibbonChampionPaldea { get => GetFlag(0x42, 4); set => SetFlag(0x42, 4, value); }
public bool RibbonMarkJumbo { get => GetFlag(0x42, 5); set => SetFlag(0x42, 5, value); }
public bool RibbonMarkMini { get => GetFlag(0x42, 6); set => SetFlag(0x42, 6, value); }
public bool RibbonMarkItemfinder { get => GetFlag(0x42, 7); set => SetFlag(0x42, 7, value); }
public bool RibbonMarkVigor { get => GetFlag(0x3E, 0); set => SetFlag(0x3E, 0, value); }
public bool RibbonMarkSlump { get => GetFlag(0x3E, 1); set => SetFlag(0x3E, 1, value); }
public bool RibbonHisui { get => GetFlag(0x3E, 2); set => SetFlag(0x3E, 2, value); }
public bool RibbonTwinklingStar { get => GetFlag(0x3E, 3); set => SetFlag(0x3E, 3, value); }
public bool RibbonChampionPaldea { get => GetFlag(0x3E, 4); set => SetFlag(0x3E, 4, value); }
public bool RibbonMarkJumbo { get => GetFlag(0x3E, 5); set => SetFlag(0x3E, 5, value); }
public bool RibbonMarkMini { get => GetFlag(0x3E, 6); set => SetFlag(0x3E, 6, value); }
public bool RibbonMarkItemfinder { get => GetFlag(0x3E, 7); set => SetFlag(0x3E, 7, value); }
public bool RibbonMarkPartner { get => GetFlag(0x43, 0); set => SetFlag(0x43, 0, value); }
public bool RibbonMarkGourmand { get => GetFlag(0x43, 1); set => SetFlag(0x43, 1, value); }
public bool RibbonOnceInALifetime { get => GetFlag(0x43, 2); set => SetFlag(0x43, 2, value); }
public bool RibbonMarkAlpha { get => GetFlag(0x43, 3); set => SetFlag(0x43, 3, value); }
public bool RibbonMarkMightiest { get => GetFlag(0x43, 4); set => SetFlag(0x43, 4, value); }
public bool RibbonMarkTitan { get => GetFlag(0x43, 5); set => SetFlag(0x43, 5, value); }
public bool RIB45_6 { get => GetFlag(0x43, 6); set => SetFlag(0x43, 6, value); }
public bool RIB45_7 { get => GetFlag(0x43, 7); set => SetFlag(0x43, 7, value); }
public bool RibbonMarkPartner { get => GetFlag(0x3F, 0); set => SetFlag(0x3F, 0, value); }
public bool RibbonMarkGourmand { get => GetFlag(0x3F, 1); set => SetFlag(0x3F, 1, value); }
public bool RibbonOnceInALifetime { get => GetFlag(0x3F, 2); set => SetFlag(0x3F, 2, value); }
public bool RibbonMarkAlpha { get => GetFlag(0x3F, 3); set => SetFlag(0x3F, 3, value); }
public bool RibbonMarkMightiest { get => GetFlag(0x3F, 4); set => SetFlag(0x3F, 4, value); }
public bool RibbonMarkTitan { get => GetFlag(0x3F, 5); set => SetFlag(0x3F, 5, value); }
public bool RIB45_6 { get => GetFlag(0x3F, 6); set => SetFlag(0x3F, 6, value); }
public bool RIB45_7 { get => GetFlag(0x3F, 7); set => SetFlag(0x3F, 7, value); }
public bool RIB46_0 { get => GetFlag(0x44, 0); set => SetFlag(0x44, 0, value); }
public bool RIB46_1 { get => GetFlag(0x44, 1); set => SetFlag(0x44, 1, value); }
public bool RIB46_2 { get => GetFlag(0x44, 2); set => SetFlag(0x44, 2, value); }
public bool RIB46_3 { get => GetFlag(0x44, 3); set => SetFlag(0x44, 3, value); }
public bool RIB46_4 { get => GetFlag(0x44, 4); set => SetFlag(0x44, 4, value); }
public bool RIB46_5 { get => GetFlag(0x44, 5); set => SetFlag(0x44, 5, value); }
public bool RIB46_6 { get => GetFlag(0x44, 6); set => SetFlag(0x44, 6, value); }
public bool RIB46_7 { get => GetFlag(0x44, 7); set => SetFlag(0x44, 7, value); }
public bool RIB46_0 { get => GetFlag(0x40, 0); set => SetFlag(0x40, 0, value); }
public bool RIB46_1 { get => GetFlag(0x40, 1); set => SetFlag(0x40, 1, value); }
public bool RIB46_2 { get => GetFlag(0x40, 2); set => SetFlag(0x40, 2, value); }
public bool RIB46_3 { get => GetFlag(0x40, 3); set => SetFlag(0x40, 3, value); }
public bool RIB46_4 { get => GetFlag(0x40, 4); set => SetFlag(0x40, 4, value); }
public bool RIB46_5 { get => GetFlag(0x40, 5); set => SetFlag(0x40, 5, value); }
public bool RIB46_6 { get => GetFlag(0x40, 6); set => SetFlag(0x40, 6, value); }
public bool RIB46_7 { get => GetFlag(0x40, 7); set => SetFlag(0x40, 7, value); }
public bool RIB47_0 { get => GetFlag(0x45, 0); set => SetFlag(0x45, 0, value); }
public bool RIB47_1 { get => GetFlag(0x45, 1); set => SetFlag(0x45, 1, value); }
public bool RIB47_2 { get => GetFlag(0x45, 2); set => SetFlag(0x45, 2, value); }
public bool RIB47_3 { get => GetFlag(0x45, 3); set => SetFlag(0x45, 3, value); }
public bool RIB47_4 { get => GetFlag(0x45, 4); set => SetFlag(0x45, 4, value); }
public bool RIB47_5 { get => GetFlag(0x45, 5); set => SetFlag(0x45, 5, value); }
public bool RIB47_6 { get => GetFlag(0x45, 6); set => SetFlag(0x45, 6, value); }
public bool RIB47_7 { get => GetFlag(0x45, 7); set => SetFlag(0x45, 7, value); }
public bool RIB47_0 { get => GetFlag(0x41, 0); set => SetFlag(0x41, 0, value); }
public bool RIB47_1 { get => GetFlag(0x41, 1); set => SetFlag(0x41, 1, value); }
public bool RIB47_2 { get => GetFlag(0x41, 2); set => SetFlag(0x41, 2, value); }
public bool RIB47_3 { get => GetFlag(0x41, 3); set => SetFlag(0x41, 3, value); }
public bool RIB47_4 { get => GetFlag(0x41, 4); set => SetFlag(0x41, 4, value); }
public bool RIB47_5 { get => GetFlag(0x41, 5); set => SetFlag(0x41, 5, value); }
public bool RIB47_6 { get => GetFlag(0x41, 6); set => SetFlag(0x41, 6, value); }
public bool RIB47_7 { get => GetFlag(0x41, 7); set => SetFlag(0x41, 7, value); }
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x34..]) & 0b00000000_00011111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x3E..]) & 0b00000000_00000000__00000100_00011100__00000000_00000000__00000000_00000000);
public int MarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x34..]) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x3E..]) & 0b00000000_00000000__00111011_11100011__11111111_11111111__11111111_11111111);
public int RibbonMarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x34..]) & 0b11111111_11111111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x3E..]) & 0b00000000_00000000__00111111_11111111__11111111_11111111__11111111_11111111);
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x30..]) & 0b00000000_00011111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x3A..]) & 0b00000000_00000000__00000100_00011100__00000000_00000000__00000000_00000000);
public int MarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x30..]) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x3A..]) & 0b00000000_00000000__00111011_11100011__11111111_11111111__11111111_11111111);
public int RibbonMarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x30..]) & 0b11111111_11111111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x3A..]) & 0b00000000_00000000__00111111_11111111__11111111_11111111__11111111_11111111);
public bool HasMarkEncounter8 => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x34..]) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x3E..]) & 0b00000000_00000000__00000000_00000011__11111111_11111111__11111111_11111111) != 0;
public bool HasMarkEncounter9 => (Data[0x43] & 0b00111000) != 0;
public bool HasMarkEncounter8 => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x30..]) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x3A..]) & 0b00000000_00000000__00000000_00000011__11111111_11111111__11111111_11111111) != 0;
public bool HasMarkEncounter9 => (Data[0x3F] & 0b00111000) != 0;
public byte HeightScalar { get => Data[0x46]; set => Data[0x46] = value; }
public byte WeightScalar { get => Data[0x47]; set => Data[0x47] = value; }
public byte HeightScalar { get => Data[0x42]; set => Data[0x42] = value; }
public byte WeightScalar { get => Data[0x43]; set => Data[0x43] = value; }
public Span<byte> Nickname_Trash => Data.Slice(0x48, 26);
public Span<byte> Nickname_Trash => Data.Slice(0x44, 26);
public string Nickname
{
@ -240,59 +236,59 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
set => StringConverter8.SetString(Nickname_Trash, value, 12, StringConverterOption.None);
}
public int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data[0x62..]); set => WriteUInt16LittleEndian(Data[0x62..], (ushort)value); }
public int IV_HP { get => Data[0x64]; set => Data[0x64] = (byte)value; }
public int IV_ATK { get => Data[0x65]; set => Data[0x65] = (byte)value; }
public int IV_DEF { get => Data[0x66]; set => Data[0x66] = (byte)value; }
public int IV_SPE { get => Data[0x67]; set => Data[0x67] = (byte)value; }
public int IV_SPA { get => Data[0x68]; set => Data[0x68] = (byte)value; }
public int IV_SPD { get => Data[0x69]; set => Data[0x69] = (byte)value; }
public bool IsEgg { get => Data[0x6A] != 0; set => Data[0x6A] = (byte)(value ? 1 : 0); }
public bool IsNicknamed { get => Data[0x6B] != 0; set => Data[0x6B] = (byte)(value ? 1 : 0); }
public int Status_Condition { get => ReadInt32LittleEndian(Data[0x6C..]); set => WriteInt32LittleEndian(Data[0x6C..], value); }
public Span<byte> HT_Trash => Data.Slice(0x70, 26);
public int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data[0x5E..]); set => WriteUInt16LittleEndian(Data[0x5E..], (ushort)value); }
public int IV_HP { get => Data[0x60]; set => Data[0x60] = (byte)value; }
public int IV_ATK { get => Data[0x61]; set => Data[0x61] = (byte)value; }
public int IV_DEF { get => Data[0x62]; set => Data[0x62] = (byte)value; }
public int IV_SPE { get => Data[0x63]; set => Data[0x63] = (byte)value; }
public int IV_SPA { get => Data[0x64]; set => Data[0x64] = (byte)value; }
public int IV_SPD { get => Data[0x65]; set => Data[0x65] = (byte)value; }
public bool IsEgg { get => Data[0x66] != 0; set => Data[0x66] = (byte)(value ? 1 : 0); }
public bool IsNicknamed { get => Data[0x67] != 0; set => Data[0x67] = (byte)(value ? 1 : 0); }
public int Status_Condition { get => ReadInt32LittleEndian(Data[0x68..]); set => WriteInt32LittleEndian(Data[0x68..], value); }
public Span<byte> HT_Trash => Data.Slice(0x6C, 26);
public string HT_Name
{
get => StringConverter8.GetString(HT_Trash);
set => StringConverter8.SetString(HT_Trash, value, 12, StringConverterOption.None);
}
public int HT_Gender { get => Data[0x8A]; set => Data[0x8A] = (byte)value; }
public byte HT_Language { get => Data[0x8B]; set => Data[0x8B] = value; }
public int CurrentHandler { get => Data[0x8C]; set => Data[0x8C] = (byte)value; }
public int HT_TrainerID { get => ReadUInt16LittleEndian(Data[0x8D..]); set => WriteUInt16LittleEndian(Data[0x8D..], (ushort)value); } // unused?
public int HT_Friendship { get => Data[0x8F]; set => Data[0x8F] = (byte)value; }
public byte HT_Intensity { get => Data[0x90]; set => Data[0x90] = value; }
public byte HT_Memory { get => Data[0x91]; set => Data[0x91] = value; }
public byte HT_Feeling { get => Data[0x92]; set => Data[0x92] = value; }
public ushort HT_TextVar { get => ReadUInt16LittleEndian(Data[0x93..]); set => WriteUInt16LittleEndian(Data[0x93..], value); }
public int Version { get => Data[0x95]; set => Data[0x95] = (byte)value; }
public byte BattleVersion { get => Data[0x96]; set => Data[0x96] = value; }
public int Language { get => Data[0x97]; set => Data[0x97] = (byte)value; }
public uint FormArgument { get => ReadUInt32LittleEndian(Data[0x98..]); set => WriteUInt32LittleEndian(Data[0x98..], value); }
public int HT_Gender { get => Data[0x86]; set => Data[0x86] = (byte)value; }
public byte HT_Language { get => Data[0x87]; set => Data[0x87] = value; }
public int CurrentHandler { get => Data[0x88]; set => Data[0x88] = (byte)value; }
public int HT_TrainerID { get => ReadUInt16LittleEndian(Data[0x89..]); set => WriteUInt16LittleEndian(Data[0x89..], (ushort)value); } // unused?
public int HT_Friendship { get => Data[0x8B]; set => Data[0x8B] = (byte)value; }
public byte HT_Intensity { get => Data[0x8C]; set => Data[0x8C] = value; }
public byte HT_Memory { get => Data[0x8D]; set => Data[0x8D] = value; }
public byte HT_Feeling { get => Data[0x8E]; set => Data[0x8E] = value; }
public ushort HT_TextVar { get => ReadUInt16LittleEndian(Data[0x8F..]); set => WriteUInt16LittleEndian(Data[0x8F..], value); }
public int Version { get => Data[0x91]; set => Data[0x91] = (byte)value; }
public byte BattleVersion { get => Data[0x92]; set => Data[0x92] = value; }
public int Language { get => Data[0x93]; set => Data[0x93] = (byte)value; }
public uint FormArgument { get => ReadUInt32LittleEndian(Data[0x94..]); set => WriteUInt32LittleEndian(Data[0x94..], value); }
public byte FormArgumentRemain { get => (byte)FormArgument; set => FormArgument = (FormArgument & ~0xFFu) | value; }
public byte FormArgumentElapsed { get => (byte)(FormArgument >> 8); set => FormArgument = (FormArgument & ~0xFF00u) | (uint)(value << 8); }
public byte FormArgumentMaximum { get => (byte)(FormArgument >> 16); set => FormArgument = (FormArgument & ~0xFF0000u) | (uint)(value << 16); }
public sbyte AffixedRibbon { get => (sbyte)Data[0x9C]; set => Data[0x9C] = (byte)value; } // selected ribbon
public Span<byte> OT_Trash => Data.Slice(0x9D, 26);
public sbyte AffixedRibbon { get => (sbyte)Data[0x98]; set => Data[0x98] = (byte)value; } // selected ribbon
public Span<byte> OT_Trash => Data.Slice(0x99, 26);
public string OT_Name
{
get => StringConverter8.GetString(OT_Trash);
set => StringConverter8.SetString(OT_Trash, value, 12, StringConverterOption.None);
}
public int OT_Friendship { get => Data[0xB7]; set => Data[0xB7] = (byte)value; }
public byte OT_Intensity { get => Data[0xB8]; set => Data[0xB8] = value; }
public byte OT_Memory { get => Data[0xB9]; set => Data[0xB9] = value; }
public ushort OT_TextVar { get => ReadUInt16LittleEndian(Data[0xBA..]); set => WriteUInt16LittleEndian(Data[0xBA..], value); }
public byte OT_Feeling { get => Data[0xBC]; set => Data[0xBC] = value; }
public int Egg_Year { get => Data[0xBD]; set => Data[0xBD] = (byte)value; }
public int Egg_Month { get => Data[0xBE]; set => Data[0xBE] = (byte)value; }
public int Egg_Day { get => Data[0xBF]; set => Data[0xBF] = (byte)value; }
public int Met_Year { get => Data[0xC0]; set => Data[0xC0] = (byte)value; }
public int Met_Month { get => Data[0xC1]; set => Data[0xC1] = (byte)value; }
public int Met_Day { get => Data[0xC2]; set => Data[0xC2] = (byte)value; }
public int Met_Level { get => Data[0xC3]; set => Data[0xC3] = (byte)value; }
public int OT_Gender { get => Data[0xC4]; set => Data[0xC4] = (byte)value; }
public byte HyperTrainFlags { get => Data[0xC5]; set => Data[0xC5] = value; }
public int OT_Friendship { get => Data[0xB3]; set => Data[0xB3] = (byte)value; }
public byte OT_Intensity { get => Data[0xB4]; set => Data[0xB4] = value; }
public byte OT_Memory { get => Data[0xB5]; set => Data[0xB5] = value; }
public ushort OT_TextVar { get => ReadUInt16LittleEndian(Data[0xB6..]); set => WriteUInt16LittleEndian(Data[0xB6..], value); }
public byte OT_Feeling { get => Data[0xB8]; set => Data[0xB8] = value; }
public int Egg_Year { get => Data[0xB9]; set => Data[0xB9] = (byte)value; }
public int Egg_Month { get => Data[0xBA]; set => Data[0xBA] = (byte)value; }
public int Egg_Day { get => Data[0xBB]; set => Data[0xBB] = (byte)value; }
public int Met_Year { get => Data[0xBC]; set => Data[0xBC] = (byte)value; }
public int Met_Month { get => Data[0xBD]; set => Data[0xBD] = (byte)value; }
public int Met_Day { get => Data[0xBE]; set => Data[0xBE] = (byte)value; }
public int Met_Level { get => Data[0xBF]; set => Data[0xBF] = (byte)value; }
public int OT_Gender { get => Data[0xC0]; set => Data[0xC0] = (byte)value; }
public byte HyperTrainFlags { get => Data[0xC1]; set => Data[0xC1] = value; }
public bool HT_HP { get => ((HyperTrainFlags >> 0) & 1) == 1; set => HyperTrainFlags = (byte)((HyperTrainFlags & ~(1 << 0)) | ((value ? 1 : 0) << 0)); }
public bool HT_ATK { get => ((HyperTrainFlags >> 1) & 1) == 1; set => HyperTrainFlags = (byte)((HyperTrainFlags & ~(1 << 1)) | ((value ? 1 : 0) << 1)); }
public bool HT_DEF { get => ((HyperTrainFlags >> 2) & 1) == 1; set => HyperTrainFlags = (byte)((HyperTrainFlags & ~(1 << 2)) | ((value ? 1 : 0) << 2)); }
@ -300,7 +296,7 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
public bool HT_SPD { get => ((HyperTrainFlags >> 4) & 1) == 1; set => HyperTrainFlags = (byte)((HyperTrainFlags & ~(1 << 4)) | ((value ? 1 : 0) << 4)); }
public bool HT_SPE { get => ((HyperTrainFlags >> 5) & 1) == 1; set => HyperTrainFlags = (byte)((HyperTrainFlags & ~(1 << 5)) | ((value ? 1 : 0) << 5)); }
public int HeldItem { get => ReadUInt16LittleEndian(Data[0xC6..]); set => WriteUInt16LittleEndian(Data[0xC6..], (ushort)value); }
public int HeldItem { get => ReadUInt16LittleEndian(Data[0xC2..]); set => WriteUInt16LittleEndian(Data[0xC2..], (ushort)value); }
public int MarkingCount => 6;
@ -321,6 +317,63 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
MarkValue = (MarkValue & ~(0b11 << shift)) | ((value & 3) << shift);
}
public void CopyFrom(PKM pk)
{
EncryptionConstant = pk.EncryptionConstant;
PID = pk.PID;
Species = pk.Species;
Form = pk.Form;
Gender = pk.Gender;
TID16 = pk.TID16;
SID16 = pk.SID16;
EXP = pk.EXP;
MarkValue = pk.MarkValue;
Nature = pk.Nature;
StatNature = pk.StatNature;
FatefulEncounter = pk.FatefulEncounter;
// HeldItem = pk.HeldItem;
IV_HP = pk.IV_HP;
IV_ATK = pk.IV_ATK;
IV_DEF = pk.IV_DEF;
IV_SPE = pk.IV_SPE;
IV_SPA = pk.IV_SPA;
IV_SPD = pk.IV_SPD;
IsEgg = pk.IsEgg;
IsNicknamed = pk.IsNicknamed;
EV_HP = pk.EV_HP;
EV_ATK = pk.EV_ATK;
EV_DEF = pk.EV_DEF;
EV_SPE = pk.EV_SPE;
EV_SPA = pk.EV_SPA;
EV_SPD = pk.EV_SPD;
HT_Gender = pk.HT_Gender;
CurrentHandler = pk.CurrentHandler;
// pk.HT_TrainerID
HT_Friendship = pk.HT_Friendship;
Version = pk.Version;
Language = pk.Language;
OT_Friendship = pk.OT_Friendship;
Egg_Year = pk.Egg_Year;
Egg_Month = pk.Egg_Month;
Egg_Day = pk.Egg_Day;
Met_Year = pk.Met_Year;
Met_Month = pk.Met_Month;
Met_Day = pk.Met_Day;
Met_Level = pk.Met_Level;
OT_Gender = pk.OT_Gender;
CopyConditionalInterfaceFrom(pk);
pk.OT_Trash.CopyTo(OT_Trash);
pk.Nickname_Trash.CopyTo(Nickname_Trash);
pk.HT_Trash.CopyTo(HT_Trash);
CopyConditionalRibbonMarkFrom(pk);
}
public void CopyTo(PKM pk)
{
pk.EncryptionConstant = EncryptionConstant;
@ -331,8 +384,6 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
pk.TID16 = TID16;
pk.SID16 = SID16;
pk.EXP = EXP;
pk.Ability = Ability;
pk.AbilityNumber = AbilityNumber;
pk.MarkValue = MarkValue;
pk.Nature = Nature;
pk.StatNature = StatNature;
@ -352,8 +403,6 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
pk.EV_SPE = EV_SPE;
pk.EV_SPA = EV_SPA;
pk.EV_SPD = EV_SPD;
pk.PKRS_Strain = PKRS_Strain;
pk.PKRS_Days = PKRS_Days;
pk.HT_Gender = HT_Gender;
pk.CurrentHandler = CurrentHandler;
@ -373,16 +422,16 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
pk.Met_Level = Met_Level;
pk.OT_Gender = OT_Gender;
CopyConditionalInterface(pk);
CopyConditionalInterfaceTo(pk);
OT_Trash.CopyTo(pk.OT_Trash);
Nickname_Trash.CopyTo(pk.Nickname_Trash);
HT_Trash.CopyTo(pk.HT_Trash);
CopyConditionalRibbonMark(pk);
CopyConditionalRibbonMarkTo(pk);
}
private void CopyConditionalInterface(PKM pk)
private void CopyConditionalInterfaceTo(PKM pk)
{
if (pk is IScaledSize ss)
{
@ -411,8 +460,8 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
this.CopyContestStatsTo(cm);
if (pk is IRibbonSetAffixed affix)
affix.AffixedRibbon = AffixedRibbon;
if (pk is IHyperTrain ht)
ht.HyperTrainFlags = HyperTrainFlags;
if (pk is IHyperTrain hy)
hy.HyperTrainFlags = HyperTrainFlags;
if (pk is IFormArgument fa)
fa.FormArgument = FormArgument;
if (pk is IBattleVersion bv)
@ -423,29 +472,74 @@ public sealed class GameDataCore : IHomeTrack, ISpeciesForm, ITrainerID, INature
home.Tracker = Tracker;
}
private void CopyConditionalRibbonMark(PKM pk)
private void CopyConditionalInterfaceFrom(PKM pk)
{
if (pk is IRibbonSetEvent3 e3)
this.CopyRibbonSetEvent3(e3);
if (pk is IRibbonSetEvent4 e4)
this.CopyRibbonSetEvent4(e4);
if (pk is IRibbonSetCommon3 c3)
this.CopyRibbonSetCommon3(c3);
if (pk is IRibbonSetCommon4 c4)
this.CopyRibbonSetCommon4(c4);
if (pk is IRibbonSetCommon6 c6)
this.CopyRibbonSetCommon6(c6);
if (pk is IRibbonSetMemory6 m6)
this.CopyRibbonSetMemory6(m6);
if (pk is IRibbonSetCommon7 c7)
this.CopyRibbonSetCommon7(c7);
if (pk is IRibbonSetCommon8 c8)
this.CopyRibbonSetCommon8(c8);
if (pk is IRibbonSetMark8 m8)
this.CopyRibbonSetMark8(m8);
if (pk is IRibbonSetCommon9 c9)
this.CopyRibbonSetCommon9(c9);
if (pk is IRibbonSetMark9 m9)
this.CopyRibbonSetMark9(m9);
if (pk is IScaledSize ss)
{
HeightScalar = ss.HeightScalar;
WeightScalar = ss.WeightScalar;
}
if (pk is IMemoryOT ot)
{
OT_Intensity = ot.OT_Intensity;
OT_Memory = ot.OT_Memory;
OT_TextVar = ot.OT_TextVar;
OT_Feeling = ot.OT_Feeling;
}
if (pk is IMemoryHT ht)
{
HT_Intensity = ht.HT_Intensity;
HT_Memory = ht.HT_Memory;
HT_Feeling = ht.HT_Feeling;
HT_TextVar = ht.HT_TextVar;
}
if (pk is IHandlerLanguage hl)
HT_Language = hl.HT_Language;
if (pk is IContestStats cm)
cm.CopyContestStatsTo(this);
if (pk is IRibbonSetAffixed affix)
AffixedRibbon = affix.AffixedRibbon;
if (pk is IHyperTrain hy)
HyperTrainFlags = hy.HyperTrainFlags;
if (pk is IFormArgument fa)
FormArgument = fa.FormArgument;
if (pk is IBattleVersion bv)
BattleVersion = bv.BattleVersion;
if (pk is IFavorite fav)
IsFavorite = fav.IsFavorite;
if (pk is IHomeTrack home)
Tracker = home.Tracker;
}
private void CopyConditionalRibbonMarkTo(PKM pk)
{
if (pk is IRibbonSetEvent3 e3) e3.CopyRibbonSetEvent3 (this);
if (pk is IRibbonSetEvent4 e4) e4.CopyRibbonSetEvent4 (this);
if (pk is IRibbonSetCommon3 c3) c3.CopyRibbonSetCommon3(this);
if (pk is IRibbonSetCommon4 c4) c4.CopyRibbonSetCommon4(this);
if (pk is IRibbonSetCommon6 c6) c6.CopyRibbonSetCommon6(this);
if (pk is IRibbonSetMemory6 m6) m6.CopyRibbonSetMemory6(this);
if (pk is IRibbonSetCommon7 c7) c7.CopyRibbonSetCommon7(this);
if (pk is IRibbonSetCommon8 c8) c8.CopyRibbonSetCommon8(this);
if (pk is IRibbonSetMark8 m8) m8.CopyRibbonSetMark8 (this);
if (pk is IRibbonSetCommon9 c9) c9.CopyRibbonSetCommon9(this);
if (pk is IRibbonSetMark9 m9) m9.CopyRibbonSetMark9 (this);
}
private void CopyConditionalRibbonMarkFrom(PKM pk)
{
if (pk is IRibbonSetEvent3 e3) this.CopyRibbonSetEvent3 (e3);
if (pk is IRibbonSetEvent4 e4) this.CopyRibbonSetEvent4 (e4);
if (pk is IRibbonSetCommon3 c3) this.CopyRibbonSetCommon3(c3);
if (pk is IRibbonSetCommon4 c4) this.CopyRibbonSetCommon4(c4);
if (pk is IRibbonSetCommon6 c6) this.CopyRibbonSetCommon6(c6);
if (pk is IRibbonSetMemory6 m6) this.CopyRibbonSetMemory6(m6);
if (pk is IRibbonSetCommon7 c7) this.CopyRibbonSetCommon7(c7);
if (pk is IRibbonSetCommon8 c8) this.CopyRibbonSetCommon8(c8);
if (pk is IRibbonSetMark8 m8) this.CopyRibbonSetMark8 (m8);
if (pk is IRibbonSetCommon9 c9) this.CopyRibbonSetCommon9(c9);
if (pk is IRibbonSetMark9 m9) this.CopyRibbonSetMark9 (m9);
}
}

View file

@ -6,10 +6,10 @@ namespace PKHeX.Core;
/// <summary>
/// Side game data for <see cref="PA8"/> data transferred into HOME.
/// </summary>
public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsolute
public class GameDataPA8 : HomeOptional1, IGameDataSide<PA8>, IScaledSizeAbsolute, IScaledSize3
{
private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PA8;
private const int SIZE = HomeCrypto.SIZE_1GAME_PA8;
private const int SIZE = HomeCrypto.SIZE_2GAME_PA8;
protected override HomeGameDataFormat Format => ExpectFormat;
public GameDataPA8() : base(SIZE) { }
@ -22,7 +22,7 @@ public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsol
public bool IsAlpha { get => Data[0x00] != 0; set => Data[0x00] = (byte)(value ? 1 : 0); }
public bool IsNoble { get => Data[0x01] != 0; set => Data[0x01] = (byte)(value ? 1 : 0); }
public ushort AlphaMove { get => ReadUInt16LittleEndian(Data[0x02..]); set => WriteUInt16LittleEndian(Data[0x02..], value); }
public byte HeightScalarCopy { get => Data[0x04]; set => Data[0x04] = value; }
public byte Scale { get => Data[0x04]; set => Data[0x04] = value; }
public ushort Move1 { get => ReadUInt16LittleEndian(Data[0x05..]); set => WriteUInt16LittleEndian(Data[0x05..], value); }
public ushort Move2 { get => ReadUInt16LittleEndian(Data[0x07..]); set => WriteUInt16LittleEndian(Data[0x07..], value); }
@ -61,6 +61,10 @@ public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsol
public int Egg_Location { get => ReadUInt16LittleEndian(Data[0x38..]); set => WriteUInt16LittleEndian(Data[0x38..], (ushort)value); }
public int Met_Location { get => ReadUInt16LittleEndian(Data[0x3A..]); set => WriteUInt16LittleEndian(Data[0x3A..], (ushort)value); }
public byte PKRS { get => Data[0x3C]; set => Data[0x3C] = value; }
public ushort Ability { get => ReadUInt16LittleEndian(Data[0x3D..]); set => WriteUInt16LittleEndian(Data[0x3D..], value); }
public byte AbilityNumber { get => Data[0x3F]; set => Data[0x3F] = value; }
// Not stored.
public PersonalInfo GetPersonalInfo(ushort species, byte form) => PersonalTable.LA.GetFormEntry(species, form);
public int Move1_PPUps { get => 0; set { } }
@ -72,13 +76,13 @@ public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsol
#region Conversion
public void CopyTo(PA8 pk)
public void CopyTo(PA8 pk, PKH pkh)
{
((IGameDataSide)this).CopyTo(pk);
this.CopyTo(pk);
pk.IsAlpha = IsAlpha;
pk.IsNoble = IsNoble;
pk.AlphaMove = AlphaMove;
pk.Scale = HeightScalarCopy;
pk.Scale = Scale;
pk.HeightAbsolute = pk.CalcHeightAbsolute; // Ignore the stored value, be nice and recalculate for the user.
pk.WeightAbsolute = pk.CalcWeightAbsolute; // Ignore the stored value, be nice and recalculate for the user.
pk.GV_HP = GV_HP;
@ -89,15 +93,45 @@ public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsol
pk.GV_SPD = GV_SPD;
PurchasedRecord.CopyTo(pk.PurchasedRecord);
MasteredRecord.CopyTo(pk.MasteredRecord);
pk.PKRS = PKRS;
pk.AbilityNumber = AbilityNumber;
pk.Ability = Ability;
}
public PKM ConvertToPKM(PKH pkh) => ConvertToPA8(pkh);
public void CopyFrom(PA8 pk, PKH pkh)
{
this.CopyFrom(pk);
IsAlpha = pk.IsAlpha;
IsNoble = pk.IsNoble;
AlphaMove = pk.AlphaMove;
pkh.HeightScalar = Scale = pk.Scale; // Overwrite Height
HeightAbsolute = pk.CalcHeightAbsolute; // Ignore the stored value, be nice and recalculate for the user.
WeightAbsolute = pk.CalcHeightAbsolute; // Ignore the stored value, be nice and recalculate for the user.
GV_HP = pk.GV_HP;
GV_ATK = pk.GV_ATK;
GV_DEF = pk.GV_DEF;
GV_SPE = pk.GV_SPE;
GV_SPA = pk.GV_SPA;
GV_SPD = pk.GV_SPD;
pk.PurchasedRecord.CopyTo(PurchasedRecord);
pk.MasteredRecord.CopyTo(MasteredRecord);
PKRS = pk.PKRS;
AbilityNumber = (byte)pk.AbilityNumber;
Ability = (ushort)pk.Ability;
public PA8 ConvertToPA8(PKH pkh)
// Special: Add the Mark
if (pk.IsAlpha)
pkh.Core.RibbonMarkAlpha = true;
}
public PA8 ConvertToPKM(PKH pkh)
{
var pk = new PA8();
pkh.CopyTo(pk);
CopyTo(pk);
CopyTo(pk, pkh);
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
@ -106,14 +140,66 @@ public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsol
/// <summary> Reconstructive logic to best apply suggested values. </summary>
public static GameDataPA8? TryCreate(PKH pkh)
{
if (pkh.DataPB7 is { } x)
return GameDataPB7.Create<GameDataPA8>(x);
if (pkh.DataPB8 is { } b)
return GameDataPB8.Create<GameDataPA8>(b);
if (pkh.DataPK9 is { } g9)
return GameDataPK9.Create<GameDataPA8>(g9);
if (pkh.DataPK8 is { } c)
return new GameDataPA8 { Ball = c.Met_Location == Locations.HOME_SWLA ? (int)Core.Ball.LAPoke : c.Ball, Met_Location = c.Met_Location, Egg_Location = c.Egg_Location };
return null;
if (!PersonalTable.LA.IsPresentInGame(pkh.Species, pkh.Form))
return null;
var result = CreateInternal(pkh);
if (result == null)
return null;
result.PopulateFromCore(pkh);
return result;
}
private static GameDataPA8? CreateInternal(PKH pkh)
{
var side = GetNearestNeighbor(pkh);
if (side == null)
return null;
var result = new GameDataPA8();
result.InitializeFrom(side, pkh);
return result;
}
private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide
?? pkh.DataPB8 as IGameDataSide
?? pkh.DataPK8 as IGameDataSide
?? pkh.DataPB7;
public void InitializeFrom(IGameDataSide side, PKH pkh)
{
Ball = GetLegendBall(side.Ball);
Met_Location = side.Met_Location == Locations.Default8bNone ? 0 : side.Met_Location;
Egg_Location = side.Egg_Location == Locations.Default8bNone ? 0 : side.Egg_Location;
if (side is IScaledSize3 s3)
Scale = s3.Scale;
else
Scale = pkh.HeightScalar;
if (side is IPokerusStatus p)
PKRS = p.PKRS;
if (side is IGameDataSplitAbility a)
AbilityNumber = a.AbilityNumber;
else
AbilityNumber = 1;
PopulateFromCore(pkh);
this.ResetMoves(pkh.Species, pkh.Form, pkh.CurrentLevel, LearnSource8LA.Instance, EntityContext.Gen8a);
}
private static int GetLegendBall(int ball)
{
if (((Ball)ball).IsLegendBall())
return ball;
return (byte)Core.Ball.LAPoke;
}
private void PopulateFromCore(PKH pkh)
{
var pi = PersonalTable.LA.GetFormEntry(pkh.Species, pkh.Form);
HeightAbsolute = PA8.GetHeightAbsolute(pi, pkh.HeightScalar);
WeightAbsolute = PA8.GetWeightAbsolute(pi, pkh.HeightScalar, pkh.WeightScalar);
Ability = (ushort)pi.GetAbilityAtIndex(AbilityNumber >> 1);
}
}

View file

@ -6,10 +6,10 @@ namespace PKHeX.Core;
/// <summary>
/// Side game data for <see cref="PB7"/> data transferred into HOME.
/// </summary>
public sealed class GameDataPB7 : HomeOptional1, IGameDataSide, IScaledSizeAbsolute, IMemoryOT
public sealed class GameDataPB7 : HomeOptional1, IGameDataSide<PB7>, IScaledSizeAbsolute, IMemoryOT
{
private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PB7;
private const int SIZE = HomeCrypto.SIZE_1GAME_PB7;
private const int SIZE = HomeCrypto.SIZE_2GAME_PB7;
protected override HomeGameDataFormat Format => ExpectFormat;
public GameDataPB7() : base(SIZE) { }
@ -63,15 +63,19 @@ public sealed class GameDataPB7 : HomeOptional1, IGameDataSide, IScaledSizeAbsol
public int Egg_Location { get => ReadUInt16LittleEndian(Data[0x37..]); set => WriteUInt16LittleEndian(Data[0x37..], (ushort)value); }
public int Met_Location { get => ReadUInt16LittleEndian(Data[0x39..]); set => WriteUInt16LittleEndian(Data[0x39..], (ushort)value); }
public byte PKRS { get => Data[0x3B]; set => Data[0x3B] = value; }
public ushort Ability { get => ReadUInt16LittleEndian(Data[0x3C..]); set => WriteUInt16LittleEndian(Data[0x3C..], value); }
public byte AbilityNumber { get => Data[0x3E]; set => Data[0x3E] = value; }
#endregion
#region Conversion
public PersonalInfo GetPersonalInfo(ushort species, byte form) => PersonalTable.GG.GetFormEntry(species, form);
public void CopyTo(PB7 pk)
public void CopyTo(PB7 pk, PKH pkh)
{
((IGameDataSide)this).CopyTo(pk);
this.CopyTo(pk);
pk.AV_HP = AV_HP;
pk.AV_ATK = AV_ATK;
pk.AV_DEF = AV_DEF;
@ -94,15 +98,52 @@ public sealed class GameDataPB7 : HomeOptional1, IGameDataSide, IScaledSizeAbsol
// pk.OT_Feeling
pk.Enjoyment = Enjoyment;
// pk.GeoPadding = GeoPadding;
pk.AbilityNumber = AbilityNumber;
pk.Ability = Ability;
}
public PKM ConvertToPKM(PKH pkh) => ConvertToPB7(pkh);
public void CopyFrom(PB7 pk, PKH pkh)
{
this.CopyFrom(pk);
AV_HP = pk.AV_HP;
AV_ATK = pk.AV_ATK;
AV_DEF = pk.AV_DEF;
AV_SPE = pk.AV_SPE;
AV_SPA = pk.AV_SPA;
AV_SPD = pk.AV_SPD;
ResortEventState = (byte)pk.ResortEventStatus;
HeightAbsolute = pk.CalcHeightAbsolute; // Ignore the stored value, be nice and recalculate for the user.
WeightAbsolute = pk.CalcWeightAbsolute; // Ignore the stored value, be nice and recalculate for the user.
public PB7 ConvertToPB7(PKH pkh)
// Some fields are unused as PB7, don't bother copying.
FieldEventFatigue1 = pk.FieldEventFatigue1;
FieldEventFatigue2 = pk.FieldEventFatigue2;
Fullness = pk.Fullness;
// Rank = pk.Rank;
// OT_Affection
// OT_Intensity
// OT_Memory
// OT_TextVar
// OT_Feeling
Enjoyment = pk.Enjoyment;
// GeoPadding = pk.GeoPadding;
AbilityNumber = (byte)pk.AbilityNumber;
Ability = (ushort)pk.Ability;
// All other side formats have HT Language. Just fake a value.
if (pkh is { HT_Language: 0, IsUntraded: false })
pkh.HT_Language = (byte)pk.Language;
}
public PB7 ConvertToPKM(PKH pkh)
{
var pk = new PB7();
pkh.CopyTo(pk);
CopyTo(pk);
CopyTo(pk, pkh);
pk.ResetCalculatedValues();
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
@ -111,39 +152,60 @@ public sealed class GameDataPB7 : HomeOptional1, IGameDataSide, IScaledSizeAbsol
/// <summary> Reconstructive logic to best apply suggested values. </summary>
public static GameDataPB7? TryCreate(PKH pkh)
{
int met = 0;
int ball = (int)Core.Ball.Poke;
if (pkh.DataPK8 is { } x)
{
met = x.Met_Location;
ball = x.Ball;
}
else if (pkh.DataPB8 is { } y)
{
met = y.Met_Location;
ball = y.Ball;
}
else if (pkh.DataPA8 is { } z)
{
met = z.Met_Location;
ball = z.Ball;
}
else if (pkh.DataPK9 is { } g9)
{
met = g9.Met_Location;
ball = g9.Ball;
}
if (met == 0)
if (!PersonalTable.GG.IsPresentInGame(pkh.Species, pkh.Form))
return null;
// There isn't an actual preference since this format cannot naturally backwards transfer.
// Just pick out the first one.
var result = CreateInternal(pkh);
if (result == null)
return null;
result.PopulateFromCore(pkh);
return result;
}
private static GameDataPB7? CreateInternal(PKH pkh)
{
var side = GetNearestNeighbor(pkh);
if (side == null)
return null;
var ball = side.Ball;
if (pkh.Version is (int)GameVersion.GO)
return new GameDataPB7 { Ball = ball, Met_Location = Locations.GO7 };
if (pkh.Version is (int)GameVersion.GP or (int)GameVersion.GE)
return new GameDataPB7 { Ball = ball, Met_Location = met };
return new GameDataPB7 { Ball = ball, Met_Location = side.Met_Location };
return null;
var result = new GameDataPB7();
result.InitializeFrom(side, pkh);
return result;
}
public void InitializeFrom(IGameDataSide side, PKH pkh)
{
Met_Location = side.Met_Location == Locations.Default8bNone ? 0 : side.Met_Location;
Egg_Location = side.Egg_Location == Locations.Default8bNone ? 0 : side.Egg_Location;
if (side is IGameDataSplitAbility a)
AbilityNumber = a.AbilityNumber;
else
AbilityNumber = 1;
}
private void PopulateFromCore(PKH pkh)
{
var pi = PersonalTable.GG.GetFormEntry(pkh.Species, pkh.Form);
HeightAbsolute = PB7.GetHeightAbsolute(pi, pkh.HeightScalar);
WeightAbsolute = PB7.GetWeightAbsolute(pi, pkh.HeightScalar, pkh.WeightScalar);
Ability = (ushort)pi.GetAbilityAtIndex(AbilityNumber >> 1);
}
private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide
?? pkh.DataPB8 as IGameDataSide
?? pkh.DataPK8 as IGameDataSide
?? pkh.DataPB7;
public static T Create<T>(GameDataPB7 data) where T : IGameDataSide, new() => new()
{
Ball = data.Ball,

View file

@ -6,10 +6,10 @@ namespace PKHeX.Core;
/// <summary>
/// Side game data for <see cref="PB8"/> data transferred into HOME.
/// </summary>
public sealed class GameDataPB8 : HomeOptional1, IGameDataSide
public sealed class GameDataPB8 : HomeOptional1, IGameDataSide<PB8>, IGameDataSplitAbility, IPokerusStatus
{
private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PB8;
private const int SIZE = HomeCrypto.SIZE_1GAME_PB8;
private const int SIZE = HomeCrypto.SIZE_2GAME_PB8;
protected override HomeGameDataFormat Format => ExpectFormat;
public GameDataPB8() : base(SIZE) { }
@ -48,25 +48,43 @@ public sealed class GameDataPB8 : HomeOptional1, IGameDataSide
public int Egg_Location { get => ReadUInt16LittleEndian(Data[0x27..]); set => WriteUInt16LittleEndian(Data[0x27..], (ushort)value); }
public int Met_Location { get => ReadUInt16LittleEndian(Data[0x29..]); set => WriteUInt16LittleEndian(Data[0x29..], (ushort)value); }
// Rev2 Additions
public byte PKRS { get => Data[0x2B]; set => Data[0x2B] = value; }
public ushort Ability { get => ReadUInt16LittleEndian(Data[0x2C..]); set => WriteUInt16LittleEndian(Data[0x2C..], value); }
public byte AbilityNumber { get => Data[0x2E]; set => Data[0x2E] = value; }
#endregion
#region Conversion
public PersonalInfo GetPersonalInfo(ushort species, byte form) => PersonalTable.BDSP.GetFormEntry(species, form);
public void CopyTo(PB8 pk)
public void CopyTo(PB8 pk, PKH pkh)
{
((IGameDataSide)this).CopyTo(pk);
this.CopyTo(pk);
// Move Records are not settable in PB8; do not copy even if nonzero (illegal).
pk.PKRS = PKRS;
pk.AbilityNumber = AbilityNumber;
pk.Ability = Ability;
}
public PKM ConvertToPKM(PKH pkh) => ConvertToPB8(pkh);
public void CopyFrom(PB8 pk, PKH pkh)
{
this.CopyFrom(pk);
// Move Records are not settable in PB8; do not copy even if nonzero (illegal).
PKRS = pk.PKRS;
AbilityNumber = (byte)pk.AbilityNumber;
Ability = (ushort)pk.Ability;
}
public PB8 ConvertToPB8(PKH pkh)
public PB8 ConvertToPKM(PKH pkh)
{
var pk = new PB8();
pkh.CopyTo(pk);
CopyTo(pk);
CopyTo(pk, pkh);
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
@ -75,28 +93,40 @@ public sealed class GameDataPB8 : HomeOptional1, IGameDataSide
/// <summary> Reconstructive logic to best apply suggested values. </summary>
public static GameDataPB8? TryCreate(PKH pkh)
{
if (pkh.DataPB7 is { } x)
return Create(x);
if (pkh.DataPK8 is { } b)
return Create(b);
if (pkh.DataPA8 is { } a)
return Create(a);
if (pkh.DataPK9 is { } g9)
return Create(g9);
return null;
var side = GetNearestNeighbor(pkh);
if (side == null)
return null;
var result = new GameDataPB8();
result.InitializeFrom(side, pkh);
return result;
}
public static T Create<T>(GameDataPB8 data) where T : IGameDataSide, new() => new()
{
Ball = data.Ball,
Met_Location = data.Met_Location == Locations.Default8bNone ? 0 : data.Met_Location,
Egg_Location = data.Egg_Location == Locations.Default8bNone ? 0 : data.Egg_Location,
};
private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide
?? pkh.DataPK8 as IGameDataSide
?? pkh.DataPB7 as IGameDataSide
?? pkh.DataPA8;
public static GameDataPB8 Create(IGameDataSide data) => new()
public void InitializeFrom(IGameDataSide side, PKH pkh)
{
Ball = data.Ball,
Met_Location = data.Met_Location == 0 ? Locations.Default8bNone : data.Met_Location,
Egg_Location = data.Egg_Location == 0 ? Locations.Default8bNone : data.Egg_Location,
};
Ball = side.Ball;
Met_Location = side.Met_Location == 0 ? Locations.Default8bNone : side.Met_Location;
Egg_Location = side.Egg_Location == 0 ? Locations.Default8bNone : side.Egg_Location;
if (side is IPokerusStatus p)
PKRS = p.PKRS;
if (side is IGameDataSplitAbility a)
AbilityNumber = a.AbilityNumber;
else
AbilityNumber = 1;
PopulateFromCore(pkh);
this.ResetMoves(pkh.Species, pkh.Form, pkh.CurrentLevel, LearnSource8BDSP.Instance, EntityContext.Gen8b);
}
private void PopulateFromCore(PKH pkh)
{
var pi = PersonalTable.BDSP.GetFormEntry(pkh.Species, pkh.Form);
Ability = (ushort)pi.GetAbilityAtIndex(AbilityNumber >> 1);
}
}

View file

@ -6,10 +6,10 @@ namespace PKHeX.Core;
/// <summary>
/// Side game data for <see cref="PK8"/> data transferred into HOME.
/// </summary>
public sealed class GameDataPK8 : HomeOptional1, IGameDataSide, IGigantamax, IDynamaxLevel, ISociability
public sealed class GameDataPK8 : HomeOptional1, IGameDataSide<PK8>, IGigantamax, IDynamaxLevel, ISociability, IGameDataSplitAbility, IPokerusStatus
{
private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PK8;
private const int SIZE = HomeCrypto.SIZE_1GAME_PK8;
private const int SIZE = HomeCrypto.SIZE_2GAME_PK8;
protected override HomeGameDataFormat Format => ExpectFormat;
public GameDataPK8() : base(SIZE) { }
@ -61,15 +61,20 @@ public sealed class GameDataPK8 : HomeOptional1, IGameDataSide, IGigantamax, IDy
public int Egg_Location { get => ReadUInt16LittleEndian(Data[0x40..]); set => WriteUInt16LittleEndian(Data[0x40..], (ushort)value); }
public int Met_Location { get => ReadUInt16LittleEndian(Data[0x42..]); set => WriteUInt16LittleEndian(Data[0x42..], (ushort)value); }
// Rev2 Additions
public byte PKRS { get => Data[0x44]; set => Data[0x44] = value; }
public ushort Ability { get => ReadUInt16LittleEndian(Data[0x45..]); set => WriteUInt16LittleEndian(Data[0x45..], value); }
public byte AbilityNumber { get => Data[0x47]; set => Data[0x47] = value; }
#endregion
#region Conversion
public PersonalInfo GetPersonalInfo(ushort species, byte form) => PersonalTable.SWSH.GetFormEntry(species, form);
public void CopyTo(PK8 pk)
public void CopyTo(PK8 pk, PKH pkh)
{
((IGameDataSide)this).CopyTo(pk);
this.CopyTo(pk);
pk.CanGigantamax = CanGigantamax;
pk.Sociability = Sociability;
pk.DynamaxLevel = DynamaxLevel;
@ -77,15 +82,34 @@ public sealed class GameDataPK8 : HomeOptional1, IGameDataSide, IGigantamax, IDy
pk.Palma = Palma;
PokeJob.CopyTo(pk.PokeJob);
RecordFlags.CopyTo(pk.RecordFlags);
pk.PKRS = PKRS;
pk.AbilityNumber = AbilityNumber;
pk.Ability = Ability;
}
public PKM ConvertToPKM(PKH pkh) => ConvertToPK8(pkh);
public void CopyFrom(PK8 pk, PKH pkh)
{
this.CopyFrom(pk);
CanGigantamax = pk.CanGigantamax;
Sociability = pk.Sociability;
DynamaxLevel = pk.DynamaxLevel;
Fullness = pk.Fullness;
Palma = pk.Palma;
pk.PokeJob.CopyTo(PokeJob);
pk.RecordFlags.CopyTo(RecordFlags);
PKRS = pk.PKRS;
AbilityNumber = (byte)pk.AbilityNumber;
Ability = (ushort)pk.Ability;
}
public PK8 ConvertToPK8(PKH pkh)
public PK8 ConvertToPKM(PKH pkh)
{
var pk = new PK8();
pkh.CopyTo(pk);
CopyTo(pk);
CopyTo(pk, pkh);
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
@ -95,39 +119,79 @@ public sealed class GameDataPK8 : HomeOptional1, IGameDataSide, IGigantamax, IDy
public static GameDataPK8? TryCreate(PKH pkh)
{
if (pkh.DataPB7 is { } x)
return GameDataPB7.Create<GameDataPK8>(x);
return CreateViaPB7(pkh, x);
var side = pkh.DataPB8 as IGameDataSide
?? pkh.DataPA8 as IGameDataSide
?? pkh.DataPK9;
var side = GetNearestNeighbor(pkh);
if (side is not null)
return Create(side, pkh.Version);
return Create(side, pkh);
return null;
}
private static GameDataPK8 Create(IGameDataSide side, int ver)
// Ignores LGP/E, already preferred if exists.
private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide
?? pkh.DataPB8 as IGameDataSide
?? pkh.DataPA8;
private static GameDataPK8 CreateViaPB7(PKH pkh, GameDataPB7 x)
{
var result = new GameDataPK8();
x.CopyTo(result); // Moves are copied by default.
result.AbilityNumber = x.AbilityNumber;
result.PopulateFromCore(pkh);
return result;
}
private static GameDataPK8 Create(IGameDataSide side, PKH pkh)
{
var result = new GameDataPK8();
result.InitializeFrom(side, pkh);
result.ResetMoves(pkh.Species, pkh.Form, pkh.CurrentLevel, LearnSource8SWSH.Instance, EntityContext.Gen8);
return result;
}
public void InitializeFrom(IGameDataSide side, PKH pkh)
{
// 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).
var ver = pkh.Version;
var met = side.Met_Location;
var ball = GetBall(side.Ball);
var egg = GetEggLocation(side.Egg_Location);
if (!IsOriginallySWSH(ver, met))
RemapMetEgg(ver, ref met, ref egg);
return new GameDataPK8 { Ball = ball, Met_Location = met, Egg_Location = egg };
Ball = ball;
Met_Location = met;
Egg_Location = egg;
if (side is IGameDataSplitAbility a)
AbilityNumber = a.AbilityNumber;
if (side is IPokerusStatus p)
PKRS = p.PKRS;
PopulateFromCore(pkh);
}
private void PopulateFromCore(PKH pkh)
{
var pi = PersonalTable.SWSH.GetFormEntry(pkh.Species, pkh.Form);
Ability = (ushort)pi.GetAbilityAtIndex(AbilityNumber >> 1);
}
private static void RemapMetEgg(int ver, ref int met, ref int egg)
{
var remap = Locations.GetMetSWSH((ushort)met, ver);
var remap = LocationsHOME.GetMetSWSH((ushort)met, ver);
if (remap == met)
return;
met = remap;
egg = Locations.HOME_SWSHBDSPEgg;
egg = LocationsHOME.SWSHEgg;
}
private static bool IsOriginallySWSH(int ver, int loc) => ver is (int)GameVersion.SW or (int)GameVersion.SH && !IsFakeMetLocation(loc);
private static bool IsFakeMetLocation(int met) => met is Locations.HOME_SWLA or Locations.HOME_SWBD or Locations.HOME_SHSP;
private static bool IsFakeMetLocation(int met) => LocationsHOME.IsLocationSWSH(met);
private static int GetBall(int ball) => ball > (int)Core.Ball.Beast ? 4 : ball;
private static int GetEggLocation(int egg) => egg == Locations.Default8bNone ? 0 : egg;
}

View file

@ -6,10 +6,10 @@ namespace PKHeX.Core;
/// <summary>
/// Side game data for <see cref="PK9"/> data transferred into HOME.
/// </summary>
public sealed class GameDataPK9 : HomeOptional1, IGameDataSide
public sealed class GameDataPK9 : HomeOptional1, IGameDataSide<PK9>, IScaledSize3, IGameDataSplitAbility
{
private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PK9;
private const int SIZE = HomeCrypto.SIZE_1GAME_PK9;
private const int SIZE = HomeCrypto.SIZE_2GAME_PK9;
protected override HomeGameDataFormat Format => ExpectFormat;
public GameDataPK9() : base(SIZE) { }
@ -19,31 +19,30 @@ public sealed class GameDataPK9 : HomeOptional1, IGameDataSide
#region Structure
public ushort Move1 { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); }
public ushort Move2 { get => ReadUInt16LittleEndian(Data[0x02..]); set => WriteUInt16LittleEndian(Data[0x02..], value); }
public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x04..]); set => WriteUInt16LittleEndian(Data[0x04..], value); }
public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x06..]); set => WriteUInt16LittleEndian(Data[0x06..], value); }
public byte Scale { get => Data[0x00]; set => Data[0x00] = value; }
public ushort Move1 { get => ReadUInt16LittleEndian(Data[0x01..]); set => WriteUInt16LittleEndian(Data[0x01..], value); }
public ushort Move2 { get => ReadUInt16LittleEndian(Data[0x03..]); set => WriteUInt16LittleEndian(Data[0x03..], value); }
public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x05..]); set => WriteUInt16LittleEndian(Data[0x05..], value); }
public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x07..]); set => WriteUInt16LittleEndian(Data[0x07..], value); }
public int Move1_PP { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public int Move2_PP { get => Data[0x09]; set => Data[0x09] = (byte)value; }
public int Move3_PP { get => Data[0x0A]; set => Data[0x0A] = (byte)value; }
public int Move4_PP { get => Data[0x0B]; set => Data[0x0B] = (byte)value; }
public int Move1_PPUps { get => Data[0x0C]; set => Data[0x0C] = (byte)value; }
public int Move2_PPUps { get => Data[0x0D]; set => Data[0x0D] = (byte)value; }
public int Move3_PPUps { get => Data[0x0E]; set => Data[0x0E] = (byte)value; }
public int Move4_PPUps { get => Data[0x0F]; set => Data[0x0F] = (byte)value; }
public int Move1_PP { get => Data[0x09]; set => Data[0x09] = (byte)value; }
public int Move2_PP { get => Data[0x0A]; set => Data[0x0A] = (byte)value; }
public int Move3_PP { get => Data[0x0B]; set => Data[0x0B] = (byte)value; }
public int Move4_PP { get => Data[0x0C]; set => Data[0x0C] = (byte)value; }
public int Move1_PPUps { get => Data[0x0D]; set => Data[0x0D] = (byte)value; }
public int Move2_PPUps { get => Data[0x0E]; set => Data[0x0E] = (byte)value; }
public int Move3_PPUps { get => Data[0x0F]; set => Data[0x0F] = (byte)value; }
public int Move4_PPUps { get => Data[0x10]; set => Data[0x10] = (byte)value; }
public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x10..]); set => WriteUInt16LittleEndian(Data[0x10..], value); }
public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x12..]); set => WriteUInt16LittleEndian(Data[0x12..], value); }
public ushort RelearnMove3 { get => ReadUInt16LittleEndian(Data[0x14..]); set => WriteUInt16LittleEndian(Data[0x14..], value); }
public ushort RelearnMove4 { get => ReadUInt16LittleEndian(Data[0x16..]); set => WriteUInt16LittleEndian(Data[0x16..], value); }
public int Egg_Location { get => ReadUInt16LittleEndian(Data[0x18..]); set => WriteUInt16LittleEndian(Data[0x18..], (ushort)value); }
public int Met_Location { get => ReadUInt16LittleEndian(Data[0x1A..]); set => WriteUInt16LittleEndian(Data[0x1A..], (ushort)value); }
public int Ball { get => Data[0x1C]; set => Data[0x1C] = (byte)value; }
public byte Scale { get => Data[0x1D]; set => Data[0x1D] = value; }
public MoveType TeraTypeOriginal { get => (MoveType)Data[0x1E]; set => Data[0x1E] = (byte)value; }
public MoveType TeraTypeModified { get => (MoveType)Data[0x1F]; set => Data[0x1F] = (byte)value; }
public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x11..]); set => WriteUInt16LittleEndian(Data[0x11..], value); }
public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x13..]); set => WriteUInt16LittleEndian(Data[0x13..], value); }
public ushort RelearnMove3 { get => ReadUInt16LittleEndian(Data[0x15..]); set => WriteUInt16LittleEndian(Data[0x15..], value); }
public ushort RelearnMove4 { get => ReadUInt16LittleEndian(Data[0x17..]); set => WriteUInt16LittleEndian(Data[0x17..], value); }
public MoveType TeraTypeOriginal { get => (MoveType)Data[0x19]; set => Data[0x19] = (byte)value; }
public MoveType TeraTypeOverride { get => (MoveType)Data[0x1A]; set => Data[0x1A] = (byte)value; }
public int Ball { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
public int Egg_Location { get => ReadUInt16LittleEndian(Data[0x1C..]); set => WriteUInt16LittleEndian(Data[0x1C..], (ushort)value); }
public int Met_Location { get => ReadUInt16LittleEndian(Data[0x1E..]); set => WriteUInt16LittleEndian(Data[0x1E..], (ushort)value); }
private const int RecordStart = 0x20;
private const int RecordCount = PK9.COUNT_RECORD; // Up to 200 TM flags, but not all are used.
@ -67,28 +66,49 @@ public sealed class GameDataPK9 : HomeOptional1, IGameDataSide
public bool GetMoveRecordFlagAny() => RecordFlags.IndexOfAnyExcept<byte>(0) >= 0;
public void ClearMoveRecordFlags() => RecordFlags.Clear();
// Rev2 Additions
public byte Obedience_Level { get => Data[0x39]; set => Data[0x39] = value; }
public ushort Ability { get => ReadUInt16LittleEndian(Data[0x3A..]); set => WriteUInt16LittleEndian(Data[0x3A..], value); }
public byte AbilityNumber { get => Data[0x3C]; set => Data[0x3C] = value; }
#endregion
#region Conversion
public PersonalInfo GetPersonalInfo(ushort species, byte form) => PersonalTable.SV.GetFormEntry(species, form);
public void CopyTo(PK9 pk)
public void CopyTo(PK9 pk, PKH pkh)
{
((IGameDataSide)this).CopyTo(pk);
this.CopyTo(pk);
pk.Scale = Scale;
pk.TeraTypeOriginal = TeraTypeOriginal;
pk.TeraTypeOverride = TeraTypeModified;
pk.TeraTypeOverride = TeraTypeOverride;
RecordFlags.CopyTo(pk.RecordFlags);
pk.Obedience_Level = Obedience_Level;
pk.Ability = Ability;
pk.AbilityNumber = AbilityNumber;
}
public PKM ConvertToPKM(PKH pkh) => ConvertToPK9(pkh);
public void CopyFrom(PK9 pk, PKH pkh)
{
this.CopyFrom(pk);
pkh.HeightScalar = Scale = pk.Scale; // Overwrite Height
TeraTypeOriginal = pk.TeraTypeOriginal;
TeraTypeOverride = pk.TeraTypeOverride;
pk.RecordFlags.CopyTo(RecordFlags);
Obedience_Level = pk.Obedience_Level;
Ability = (ushort)pk.Ability;
AbilityNumber = (byte)pk.AbilityNumber;
}
public PK9 ConvertToPK9(PKH pkh)
public PK9 ConvertToPKM(PKH pkh)
{
var pk = new PK9();
pkh.CopyTo(pk);
CopyTo(pk);
CopyTo(pk, pkh);
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
@ -100,28 +120,55 @@ public sealed class GameDataPK9 : HomeOptional1, IGameDataSide
if (!PersonalTable.SV.IsPresentInGame(pkh.Species, pkh.Form))
return null;
if (pkh.DataPB7 is { } x)
return Create(x);
if (pkh.DataPK8 is { } b)
return Create(b);
if (pkh.DataPA8 is { } a)
return Create(a);
if (pkh.DataPB8 is { } bd)
return Create(bd);
return null;
var result = CreateInternal(pkh);
if (result == null)
return null;
result.PopulateFromCore(pkh);
return result;
}
public static T Create<T>(GameDataPK9 data) where T : IGameDataSide, new() => new()
private static GameDataPK9? CreateInternal(PKH pkh)
{
Ball = data.Ball,
Met_Location = data.Met_Location == Locations.Default8bNone ? 0 : data.Met_Location,
Egg_Location = data.Egg_Location == Locations.Default8bNone ? 0 : data.Egg_Location,
};
var side = GetNearestNeighbor(pkh);
if (side == null)
return null;
public static GameDataPK9 Create(IGameDataSide data) => new()
var result = new GameDataPK9();
result.InitializeFrom(side, pkh);
return result;
}
private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK8 as IGameDataSide
?? pkh.DataPB8 as IGameDataSide
?? pkh.DataPB7 as IGameDataSide
?? pkh.DataPA8;
public void InitializeFrom(IGameDataSide side, PKH pkh)
{
Ball = data.Ball,
Met_Location = data.Met_Location == 0 ? Locations.Default8bNone : data.Met_Location,
Egg_Location = data.Egg_Location == 0 ? Locations.Default8bNone : data.Egg_Location,
};
Ball = side.Ball;
Met_Location = side.Met_Location == Locations.Default8bNone ? 0 : side.Met_Location;
Egg_Location = side.Egg_Location == Locations.Default8bNone ? 0 : side.Egg_Location;
if (side is IScaledSize3 s3)
Scale = s3.Scale;
else
Scale = pkh.HeightScalar;
if (side is IGameDataSplitAbility a)
AbilityNumber = a.AbilityNumber;
else
AbilityNumber = 1;
PopulateFromCore(pkh);
this.ResetMoves(pkh.Species, pkh.Form, pkh.CurrentLevel, LearnSource9SV.Instance, EntityContext.Gen9);
}
private void PopulateFromCore(PKH pkh)
{
Obedience_Level = (byte)pkh.Met_Level;
var pi = PersonalTable.SV.GetFormEntry(pkh.Species, pkh.Form);
Ability = (ushort)pi.GetAbilityAtIndex(AbilityNumber >> 1);
TeraTypeOriginal = TeraTypeOverride = TeraTypeUtil.GetTeraTypeImport(pi.Type1, pi.Type2);
}
}

View file

@ -11,28 +11,41 @@ namespace PKHeX.Core;
/// </summary>
public static class HomeCrypto
{
internal const int Version1 = 1;
public const int Version1 = 1;
public const int Version2 = 2;
internal const int SIZE_1HEADER = 0x10; // 16
public const int SIZE_1HEADER = 0x10; // 16
internal const int SIZE_1CORE = 0xC8; // 200
public const int SIZE_1CORE = 0xC8; // 200
public const int SIZE_1GAME_PB7 = 0x3B; // 59
public const int SIZE_1GAME_PK8 = 0x44; // 68
public const int SIZE_1GAME_PA8 = 0x3C; // 60
public const int SIZE_1GAME_PB8 = 0x2B; // 43
public const int SIZE_1STORED = 0x1EE; // 494
internal const int SIZE_1GAME_PB7 = 0x3B; // 59
internal const int SIZE_1GAME_PK8 = 0x44; // 68
internal const int SIZE_1GAME_PA8 = 0x3C; // 60
internal const int SIZE_1GAME_PB8 = 0x2B; // 43
internal const int SIZE_1GAME_PK9 = 0x39; // todo sv
internal const int SIZE_1STORED = 0x1EE; // 494
public const int SIZE_2CORE = 0xC4; // 196
public const int SIZE_2GAME_PB7 = 0x3F; // 63
public const int SIZE_2GAME_PK8 = 0x48; // 72
public const int SIZE_2GAME_PA8 = 0x40; // 64
public const int SIZE_2GAME_PB8 = 0x2F; // 47
public const int SIZE_2GAME_PK9 = 0x3D; // 61
public const int SIZE_2STORED = 0x23A; // 570
public const int SIZE_STORED = SIZE_2STORED;
public const int SIZE_CORE = SIZE_2CORE;
public const int VersionLatest = Version2;
public static bool IsKnownVersion(ushort version) => version is Version1 or Version2;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetFormat1EncryptionKey(Span<byte> key, ulong seed)
public static void SetEncryptionKey(Span<byte> key, ulong seed)
{
WriteUInt64BigEndian(key, seed ^ 0x6B7B5966193DB88B);
WriteUInt64BigEndian(key.Slice(8, 8), seed & 0x937EC53BF8856E87);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void GetFormat1EncryptionIv(Span<byte> iv, ulong seed)
public static void SetEncryptionIv(Span<byte> iv, ulong seed)
{
WriteUInt64BigEndian(iv, seed ^ 0x5F4ED4E84975D976);
WriteUInt64BigEndian(iv.Slice(8, 8), seed | 0xE3CDA917EA9E489C);
@ -45,28 +58,28 @@ public static class HomeCrypto
/// <param name="decrypt">Encryption or Decryption mode</param>
/// <returns>New array with result data.</returns>
/// <exception cref="ArgumentException"> if the format is not supported.</exception>
public static byte[] Crypt1(ReadOnlySpan<byte> data, bool decrypt = true)
public static byte[] Crypt(ReadOnlySpan<byte> data, bool decrypt = true)
{
var format = ReadUInt16LittleEndian(data);
if (format != Version1)
if (!IsKnownVersion(format))
throw new ArgumentException($"Unrecognized format: {format}");
ulong seed = ReadUInt64LittleEndian(data.Slice(2, 8));
var key = new byte[0x10];
GetFormat1EncryptionKey(key, seed);
SetEncryptionKey(key, seed);
var iv = new byte[0x10];
GetFormat1EncryptionIv(iv, seed);
SetEncryptionIv(iv, seed);
var dataSize = ReadUInt16LittleEndian(data[0xE..0x10]);
var result = new byte[SIZE_1HEADER + dataSize];
data[..SIZE_1HEADER].CopyTo(result); // header
Crypt1(data, key, iv, result, dataSize, decrypt);
Crypt(data, key, iv, result, dataSize, decrypt);
return result;
}
private static void Crypt1(ReadOnlySpan<byte> data, byte[] key, byte[] iv, byte[] result, ushort dataSize, bool decrypt)
private static void Crypt(ReadOnlySpan<byte> data, byte[] key, byte[] iv, byte[] result, ushort dataSize, bool decrypt)
{
using var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
@ -89,10 +102,10 @@ public static class HomeCrypto
{
var span = data.AsSpan();
var format = ReadUInt16LittleEndian(span);
if (format == Version1)
if (IsKnownVersion(format))
{
if (GetIsEncrypted1(span))
data = Crypt1(span);
if (GetIsEncrypted(span, format))
data = Crypt(span);
}
else
{
@ -105,7 +118,7 @@ public static class HomeCrypto
/// </summary>
public static byte[] Encrypt(ReadOnlySpan<byte> pk)
{
var result = Crypt1(pk, false);
var result = Crypt(pk, false);
RefreshChecksum(result, result);
return result;
}
@ -125,17 +138,39 @@ public static class HomeCrypto
/// Checks if the format 1 data is encrypted.
/// </summary>
/// <returns>True if encrypted.</returns>
public static bool GetIsEncrypted1(ReadOnlySpan<byte> data)
public static bool GetIsEncrypted(ReadOnlySpan<byte> data, ushort format) => format switch
{
if (ReadUInt16LittleEndian(data[SIZE_1HEADER..]) != SIZE_1CORE)
return true; // Core length should be constant if decrypted.
Version1 => IsEncryptedCore1(data),
Version2 => IsEncryptedCore2(data),
_ => throw new ArgumentException($"Unrecognized format: {format}"),
};
private static bool IsEncryptedCore1(ReadOnlySpan<byte> data)
{
var core = data.Slice(SIZE_1HEADER + 2, SIZE_1CORE);
if (ReadUInt16LittleEndian(core[0xB5..]) != 0)
// Strings should be \0000 terminated if decrypted.
// Any non-zero value is a sign of encryption.
if (ReadUInt16LittleEndian(core[0xB5..]) != 0) // OT
return true; // OT_Name final terminator should be 0 if decrypted.
if (ReadUInt16LittleEndian(core[0x60..]) != 0)
if (ReadUInt16LittleEndian(core[0x60..]) != 0) // Nick
return true; // Nickname final terminator should be 0 if decrypted.
if (ReadUInt16LittleEndian(core[0x88..]) != 0)
if (ReadUInt16LittleEndian(core[0x88..]) != 0) // HT
return true; // HT_Name final terminator should be 0 if decrypted.
//// Fall back to checksum.
//return ReadUInt32LittleEndian(data[0xA..0xE]) == GetChecksum1(data);
return false; // 64 bits checked is enough to feel safe about this check.
}
private static bool IsEncryptedCore2(ReadOnlySpan<byte> data)
{
var core = data.Slice(SIZE_1HEADER + 2, SIZE_2CORE);
if (ReadUInt16LittleEndian(core[0xB1..]) != 0)
return true; // OT_Name final terminator should be 0 if decrypted.
if (ReadUInt16LittleEndian(core[0x5C..]) != 0)
return true; // Nickname final terminator should be 0 if decrypted.
if (ReadUInt16LittleEndian(core[0x84..]) != 0)
return true; // HT_Name final terminator should be 0 if decrypted.
//// Fall back to checksum.

View file

@ -11,6 +11,7 @@ public abstract class HomeOptional1
// Internal Attributes set on creation
private readonly Memory<byte> Buffer; // Raw Storage
protected Span<byte> Data => Buffer.Span;
public int SerializedSize => HeaderSize + Buffer.Length;
public const int HeaderSize = 3; // u8 format, u16 length(data[u8])
protected abstract HomeGameDataFormat Format { get; }

View file

@ -1,5 +1,41 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Inter-format manipulation API for <see cref="IGameDataSide"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IGameDataSide<T> : IGameDataSide where T : PKM, new()
{
/// <summary>
/// Copies the data from the current instance into the input <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Entity to copy to</param>
/// <param name="pkh">Overall HOME data object</param>
void CopyTo(T pk, PKH pkh);
/// <summary>
/// Copies the data from the input <see cref="PKM"/> into the current instance.
/// </summary>
/// <param name="pk">Entity to copy from</param>
/// <param name="pkh">Overall HOME data object</param>
void CopyFrom(T pk, PKH pkh);
/// <summary>
/// Converts the data to a <see cref="PKM"/>.
/// </summary>
/// <param name="pkh">Overall HOME data object</param>
T ConvertToPKM(PKH pkh);
/// <summary>
/// Initializes the instance with data from the input <see cref="PKM"/>.
/// </summary>
/// <param name="other">Other game data to initialize from</param>
/// <param name="pkh">Overall HOME data object</param>
void InitializeFrom(IGameDataSide other, PKH pkh);
}
/// <summary>
/// Common properties stored by HOME's side game data formats.
/// </summary>
@ -17,12 +53,6 @@ public interface IGameDataSide
/// Gets the personal info for the input arguments.
/// </summary>
PersonalInfo GetPersonalInfo(ushort species, byte form);
/// <summary>
/// Converts the data to a <see cref="PKM"/>.
/// </summary>
/// <param name="pkh">HOME entity</param>
PKM ConvertToPKM(PKH pkh);
}
public static class GameDataSideExtensions
@ -35,9 +65,9 @@ public static class GameDataSideExtensions
public static void CopyTo(this IGameDataSide data, PKM pk)
{
pk.Move1 = data.Move1; pk.Move1_PP = data.Move1_PP; pk.Move1_PPUps = data.Move1_PPUps; pk.RelearnMove1 = data.RelearnMove1;
pk.Move2 = data.Move2; pk.Move2_PP = data.Move1_PP; pk.Move2_PPUps = data.Move2_PPUps; pk.RelearnMove2 = data.RelearnMove2;
pk.Move3 = data.Move3; pk.Move3_PP = data.Move1_PP; pk.Move3_PPUps = data.Move3_PPUps; pk.RelearnMove3 = data.RelearnMove3;
pk.Move4 = data.Move4; pk.Move4_PP = data.Move1_PP; pk.Move4_PPUps = data.Move4_PPUps; pk.RelearnMove4 = data.RelearnMove4;
pk.Move2 = data.Move2; pk.Move2_PP = data.Move2_PP; pk.Move2_PPUps = data.Move2_PPUps; pk.RelearnMove2 = data.RelearnMove2;
pk.Move3 = data.Move3; pk.Move3_PP = data.Move3_PP; pk.Move3_PPUps = data.Move3_PPUps; pk.RelearnMove3 = data.RelearnMove3;
pk.Move4 = data.Move4; pk.Move4_PP = data.Move4_PP; pk.Move4_PPUps = data.Move4_PPUps; pk.RelearnMove4 = data.RelearnMove4;
pk.Ball = data.Ball;
pk.Met_Location = data.Met_Location;
pk.Egg_Location = data.Egg_Location;
@ -51,11 +81,45 @@ public static class GameDataSideExtensions
public static void CopyTo(this IGameDataSide data, IGameDataSide pk)
{
pk.Move1 = data.Move1; pk.Move1_PP = data.Move1_PP; pk.Move1_PPUps = data.Move1_PPUps; pk.RelearnMove1 = data.RelearnMove1;
pk.Move2 = data.Move2; pk.Move2_PP = data.Move1_PP; pk.Move2_PPUps = data.Move2_PPUps; pk.RelearnMove2 = data.RelearnMove2;
pk.Move3 = data.Move3; pk.Move3_PP = data.Move1_PP; pk.Move3_PPUps = data.Move3_PPUps; pk.RelearnMove3 = data.RelearnMove3;
pk.Move4 = data.Move4; pk.Move4_PP = data.Move1_PP; pk.Move4_PPUps = data.Move4_PPUps; pk.RelearnMove4 = data.RelearnMove4;
pk.Move2 = data.Move2; pk.Move2_PP = data.Move2_PP; pk.Move2_PPUps = data.Move2_PPUps; pk.RelearnMove2 = data.RelearnMove2;
pk.Move3 = data.Move3; pk.Move3_PP = data.Move3_PP; pk.Move3_PPUps = data.Move3_PPUps; pk.RelearnMove3 = data.RelearnMove3;
pk.Move4 = data.Move4; pk.Move4_PP = data.Move4_PP; pk.Move4_PPUps = data.Move4_PPUps; pk.RelearnMove4 = data.RelearnMove4;
pk.Ball = data.Ball;
pk.Met_Location = data.Met_Location;
pk.Egg_Location = data.Egg_Location;
}
/// <summary>
/// Copies the shared properties into a destination.
/// </summary>
/// <param name="data">Source side game data</param>
/// <param name="pk">Destination entity</param>
public static void CopyFrom(this IGameDataSide data, PKM pk)
{
data.Move1 = pk.Move1; data.Move1_PP = pk.Move1_PP; data.Move1_PPUps = pk.Move1_PPUps; data.RelearnMove1 = pk.RelearnMove1;
data.Move2 = pk.Move2; data.Move2_PP = pk.Move2_PP; data.Move2_PPUps = pk.Move2_PPUps; data.RelearnMove2 = pk.RelearnMove2;
data.Move3 = pk.Move3; data.Move3_PP = pk.Move3_PP; data.Move3_PPUps = pk.Move3_PPUps; data.RelearnMove3 = pk.RelearnMove3;
data.Move4 = pk.Move4; data.Move4_PP = pk.Move4_PP; data.Move4_PPUps = pk.Move4_PPUps; data.RelearnMove4 = pk.RelearnMove4;
data.Ball = pk.Ball;
data.Met_Location = pk.Met_Location;
data.Egg_Location = pk.Egg_Location;
}
/// <summary>
/// Resets the moves using the <see cref="source"/> for the given level.
/// </summary>
public static void ResetMoves(this IGameDataSide data, ushort species, byte form, int level, ILearnSource source, EntityContext context)
{
var learn = source.GetLearnset(species, form);
Span<ushort> moves = stackalloc ushort[4];
learn.SetEncounterMoves(level, moves);
data.Move1 = moves[0];
data.Move2 = moves[1];
data.Move3 = moves[2];
data.Move4 = moves[3];
data.Move1_PP = MoveInfo.GetPP(context, moves[0]);
data.Move2_PP = MoveInfo.GetPP(context, moves[1]);
data.Move3_PP = MoveInfo.GetPP(context, moves[2]);
data.Move4_PP = MoveInfo.GetPP(context, moves[3]);
}
}

View file

@ -0,0 +1,7 @@
namespace PKHeX.Core;
public interface IGameDataSplitAbility
{
ushort Ability { get; set; }
byte AbilityNumber { get; set; }
}

View file

@ -0,0 +1,13 @@
namespace PKHeX.Core;
public interface IHomeStorage
{
bool Exists(ulong tracker);
PKH GetEntity<T>(T pk) where T : PKM;
}
public sealed class HomeStorageFacade : IHomeStorage
{
public bool Exists(ulong tracker) => false;
public PKH GetEntity<T>(T pk) where T : PKM => PKH.ConvertFromPKM(pk);
}

View file

@ -0,0 +1,6 @@
namespace PKHeX.Core;
public interface IPokerusStatus
{
byte PKRS { get; set; }
}

View file

@ -1,14 +1,13 @@
using System;
using static System.Buffers.Binary.BinaryPrimitives;
using static PKHeX.Core.GameVersion;
using static PKHeX.Core.Locations;
namespace PKHeX.Core;
/// <summary> Pokémon HOME <see cref="PKM"/> format. </summary>
public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories, IRibbonSetAffixed, IContestStats, IScaledSize, IRibbonSetRibbons, IRibbonSetMarks
{
public readonly GameDataCore _coreData;
public readonly GameDataCore Core;
public GameDataPB7? DataPB7 { get; private set; }
public GameDataPK8? DataPK8 { get; private set; }
public GameDataPA8? DataPA8 { get; private set; }
@ -23,10 +22,19 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
var core = mem[..CoreDataSize];
var side = mem.Slice(core.Length + 2, GameDataSize);
_coreData = new GameDataCore(core);
Core = new GameDataCore(core);
ReadGameData1(side);
}
public PKH() : base(HomeCrypto.SIZE_STORED)
{
CoreDataSize = HomeCrypto.SIZE_CORE;
var mem = Data.AsMemory(HomeCrypto.SIZE_1HEADER + 2);
var core = mem[..CoreDataSize];
Core = new GameDataCore(core) { AffixedRibbon = -1 };
}
private void ReadGameData1(Memory<byte> data)
{
// Can potentially have no side-game data (GO imports)
@ -39,18 +47,20 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
var chunk = data[..length];
data = data[chunk.Length..];
switch (format)
{
case HomeGameDataFormat.PB7: DataPB7 = new GameDataPB7(chunk); break;
case HomeGameDataFormat.PK8: DataPK8 = new GameDataPK8(chunk); break;
case HomeGameDataFormat.PA8: DataPA8 = new GameDataPA8(chunk); break;
case HomeGameDataFormat.PB8: DataPB8 = new GameDataPB8(chunk); break;
case HomeGameDataFormat.PK9: DataPK9 = new GameDataPK9(chunk); break;
default: throw new ArgumentException($"Unknown {nameof(HomeGameDataFormat)} {format}");
}
_ = ReadGameData1(chunk, format);
}
}
private IGameDataSide ReadGameData1(Memory<byte> chunk, HomeGameDataFormat format) => format switch
{
HomeGameDataFormat.PB7 => DataPB7 = new GameDataPB7(chunk),
HomeGameDataFormat.PK8 => DataPK8 = new GameDataPK8(chunk),
HomeGameDataFormat.PA8 => DataPA8 = new GameDataPA8(chunk),
HomeGameDataFormat.PB8 => DataPB8 = new GameDataPB8(chunk),
HomeGameDataFormat.PK9 => DataPK9 = new GameDataPK9(chunk),
_ => throw new ArgumentException($"Unknown {nameof(HomeGameDataFormat)} {format}")
};
private static byte[] DecryptHome(byte[] data)
{
HomeCrypto.DecryptIfEncrypted(ref data);
@ -65,108 +75,113 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
public ushort CoreDataSize { get => ReadUInt16LittleEndian(Data.AsSpan(0x10)); set => WriteUInt16LittleEndian(Data.AsSpan(0x10), value); }
public ushort GameDataSize { get => ReadUInt16LittleEndian(Data.AsSpan(0x12 + CoreDataSize)); set => WriteUInt16LittleEndian(Data.AsSpan(0x12 + CoreDataSize), value); }
public override Span<byte> Nickname_Trash => _coreData.Nickname_Trash;
public override Span<byte> OT_Trash => _coreData.OT_Trash;
public override Span<byte> HT_Trash => _coreData.HT_Trash;
private const int GameDataStart = HomeCrypto.SIZE_1HEADER + 2 + HomeCrypto.SIZE_CORE + 2;
public override Span<byte> Nickname_Trash => Core.Nickname_Trash;
public override Span<byte> OT_Trash => Core.OT_Trash;
public override Span<byte> HT_Trash => Core.HT_Trash;
public override bool IsUntraded => ReadUInt16LittleEndian(HT_Trash) == 0; // immediately terminated HT_Name data (\0)
#region Core
public ulong Tracker { get => _coreData.Tracker; set => _coreData.Tracker = value; }
public override uint EncryptionConstant { get => _coreData.EncryptionConstant; set => _coreData.EncryptionConstant = value; }
public bool IsBadEgg { get => _coreData.IsBadEgg; set => _coreData.IsBadEgg = value; }
public override ushort Species { get => _coreData.Species; set => _coreData.Species = value; }
public override uint ID32 { get => _coreData.ID32; set => _coreData.ID32 = value; }
public override ushort TID16 { get => _coreData.TID16; set => _coreData.TID16 = value; }
public override ushort SID16 { get => _coreData.SID16; set => _coreData.SID16 = value; }
public override uint EXP { get => _coreData.EXP; set => _coreData.EXP = value; }
public override int Ability { get => _coreData.Ability; set => _coreData.Ability = value; }
public override int AbilityNumber { get => _coreData.AbilityNumber; set => _coreData.AbilityNumber = value; }
public bool Favorite { get => _coreData.IsFavorite; set => _coreData.IsFavorite = value; }
public override int MarkValue { get => _coreData.MarkValue; set => _coreData.MarkValue = value; }
public override uint PID { get => _coreData.PID; set => _coreData.PID = value; }
public override int Nature { get => _coreData.Nature; set => _coreData.Nature = value; }
public override int StatNature { get => _coreData.StatNature; set => _coreData.StatNature = value; }
public override bool FatefulEncounter { get => _coreData.FatefulEncounter; set => _coreData.FatefulEncounter = value; }
public override int Gender { get => _coreData.Gender; set => _coreData.Gender = value; }
public override byte Form { get => _coreData.Form; set => _coreData.Form = value; }
public override int EV_HP { get => _coreData.EV_HP; set => _coreData.EV_HP = value; }
public override int EV_ATK { get => _coreData.EV_ATK; set => _coreData.EV_ATK = value; }
public override int EV_DEF { get => _coreData.EV_DEF; set => _coreData.EV_DEF = value; }
public override int EV_SPE { get => _coreData.EV_SPE; set => _coreData.EV_SPE = value; }
public override int EV_SPA { get => _coreData.EV_SPA; set => _coreData.EV_SPA = value; }
public override int EV_SPD { get => _coreData.EV_SPD; set => _coreData.EV_SPD = value; }
public byte CNT_Cool { get => _coreData.CNT_Cool; set => _coreData.CNT_Cool = value; }
public byte CNT_Beauty { get => _coreData.CNT_Beauty; set => _coreData.CNT_Beauty = value; }
public byte CNT_Cute { get => _coreData.CNT_Cute; set => _coreData.CNT_Cute = value; }
public byte CNT_Smart { get => _coreData.CNT_Smart; set => _coreData.CNT_Smart = value; }
public byte CNT_Tough { get => _coreData.CNT_Tough; set => _coreData.CNT_Tough = value; }
public byte CNT_Sheen { get => _coreData.CNT_Sheen; set => _coreData.CNT_Sheen = value; }
public override int PKRS_Days { get => _coreData.PKRS_Days; set => _coreData.PKRS_Days = value; }
public override int PKRS_Strain { get => _coreData.PKRS_Strain; set => _coreData.PKRS_Strain = value; }
public byte HeightScalar { get => _coreData.HeightScalar; set => _coreData.HeightScalar = value; }
public byte WeightScalar { get => _coreData.WeightScalar; set => _coreData.WeightScalar = value; }
public override int Stat_HPCurrent { get => _coreData.Stat_HPCurrent; set => _coreData.Stat_HPCurrent = value; }
public override int IV_HP { get => _coreData.IV_HP; set => _coreData.IV_HP = value; }
public override int IV_ATK { get => _coreData.IV_ATK; set => _coreData.IV_ATK = value; }
public override int IV_DEF { get => _coreData.IV_DEF; set => _coreData.IV_DEF = value; }
public override int IV_SPE { get => _coreData.IV_SPE; set => _coreData.IV_SPE = value; }
public override int IV_SPA { get => _coreData.IV_SPA; set => _coreData.IV_SPA = value; }
public override int IV_SPD { get => _coreData.IV_SPD; set => _coreData.IV_SPD = value; }
public override bool IsEgg { get => _coreData.IsEgg; set => _coreData.IsEgg = value; }
public override bool IsNicknamed { get => _coreData.IsNicknamed; set => _coreData.IsNicknamed = value; }
public override int Status_Condition { get => _coreData.Status_Condition; set => _coreData.Status_Condition = value; }
public override int HT_Gender { get => _coreData.HT_Gender; set => _coreData.HT_Gender = value; }
public byte HT_Language { get => _coreData.HT_Language; set => _coreData.HT_Language = value; }
public override int CurrentHandler { get => _coreData.CurrentHandler; set => _coreData.CurrentHandler = value; }
public int HT_TrainerID { get => _coreData.HT_TrainerID; set => _coreData.HT_TrainerID = value; }
public override int HT_Friendship { get => _coreData.HT_Friendship; set => _coreData.HT_Friendship = value; }
public byte HT_Intensity { get => _coreData.HT_Intensity; set => _coreData.HT_Intensity = value; }
public byte HT_Memory { get => _coreData.HT_Memory; set => _coreData.HT_Memory = value; }
public byte HT_Feeling { get => _coreData.HT_Feeling; set => _coreData.HT_Feeling = value; }
public ushort HT_TextVar { get => _coreData.HT_TextVar; set => _coreData.HT_TextVar = value; }
public override int Version { get => _coreData.Version; set => _coreData.Version = value; }
public byte BattleVersion { get => _coreData.BattleVersion; set => _coreData.BattleVersion = value; }
public override int Language { get => _coreData.Language; set => _coreData.Language = value; }
public uint FormArgument { get => _coreData.FormArgument; set => _coreData.FormArgument = value; }
public byte FormArgumentRemain { get => _coreData.FormArgumentRemain; set => _coreData.FormArgumentRemain = value; }
public byte FormArgumentElapsed { get => _coreData.FormArgumentElapsed; set => _coreData.FormArgumentElapsed = value; }
public byte FormArgumentMaximum { get => _coreData.FormArgumentMaximum; set => _coreData.FormArgumentMaximum = value; }
public sbyte AffixedRibbon { get => _coreData.AffixedRibbon; set => _coreData.AffixedRibbon = value; }
public override int OT_Friendship { get => _coreData.OT_Friendship; set => _coreData.OT_Friendship = value; }
public byte OT_Intensity { get => _coreData.OT_Intensity; set => _coreData.OT_Intensity = value; }
public byte OT_Memory { get => _coreData.OT_Memory; set => _coreData.OT_Memory = value; }
public ushort OT_TextVar { get => _coreData.OT_TextVar; set => _coreData.OT_TextVar = value; }
public byte OT_Feeling { get => _coreData.OT_Feeling; set => _coreData.OT_Feeling = value; }
public override int Egg_Year { get => _coreData.Egg_Year; set => _coreData.Egg_Year = value; }
public override int Egg_Month { get => _coreData.Egg_Month; set => _coreData.Egg_Month = value; }
public override int Egg_Day { get => _coreData.Egg_Day; set => _coreData.Egg_Day = value; }
public override int Met_Year { get => _coreData.Met_Year; set => _coreData.Met_Year = value; }
public override int Met_Month { get => _coreData.Met_Month; set => _coreData.Met_Month = value; }
public override int Met_Day { get => _coreData.Met_Day; set => _coreData.Met_Day = value; }
public override int Met_Level { get => _coreData.Met_Level; set => _coreData.Met_Level = value; }
public override int OT_Gender { get => _coreData.OT_Gender; set => _coreData.OT_Gender = value; }
public byte HyperTrainFlags { get => _coreData.HyperTrainFlags; set => _coreData.HyperTrainFlags = value; }
public bool HT_HP { get => _coreData.HT_HP; set => _coreData.HT_HP = value; }
public bool HT_ATK { get => _coreData.HT_ATK; set => _coreData.HT_ATK = value; }
public bool HT_DEF { get => _coreData.HT_DEF; set => _coreData.HT_DEF = value; }
public bool HT_SPA { get => _coreData.HT_SPA; set => _coreData.HT_SPA = value; }
public bool HT_SPD { get => _coreData.HT_SPD; set => _coreData.HT_SPD = value; }
public bool HT_SPE { get => _coreData.HT_SPE; set => _coreData.HT_SPE = value; }
public override int HeldItem { get => _coreData.HeldItem; set => _coreData.HeldItem = value; }
public ulong Tracker { get => Core.Tracker; set => Core.Tracker = value; }
public override uint EncryptionConstant { get => Core.EncryptionConstant; set => Core.EncryptionConstant = value; }
public bool IsBadEgg { get => Core.IsBadEgg; set => Core.IsBadEgg = value; }
public override ushort Species { get => Core.Species; set => Core.Species = value; }
public override uint ID32 { get => Core.ID32; set => Core.ID32 = value; }
public override ushort TID16 { get => Core.TID16; set => Core.TID16 = value; }
public override ushort SID16 { get => Core.SID16; set => Core.SID16 = value; }
public override uint EXP { get => Core.EXP; set => Core.EXP = value; }
public bool Favorite { get => Core.IsFavorite; set => Core.IsFavorite = value; }
public override int MarkValue { get => Core.MarkValue; set => Core.MarkValue = value; }
public override uint PID { get => Core.PID; set => Core.PID = value; }
public override int Nature { get => Core.Nature; set => Core.Nature = value; }
public override int StatNature { get => Core.StatNature; set => Core.StatNature = value; }
public override bool FatefulEncounter { get => Core.FatefulEncounter; set => Core.FatefulEncounter = value; }
public override int Gender { get => Core.Gender; set => Core.Gender = value; }
public override byte Form { get => Core.Form; set => Core.Form = value; }
public override int EV_HP { get => Core.EV_HP; set => Core.EV_HP = value; }
public override int EV_ATK { get => Core.EV_ATK; set => Core.EV_ATK = value; }
public override int EV_DEF { get => Core.EV_DEF; set => Core.EV_DEF = value; }
public override int EV_SPE { get => Core.EV_SPE; set => Core.EV_SPE = value; }
public override int EV_SPA { get => Core.EV_SPA; set => Core.EV_SPA = value; }
public override int EV_SPD { get => Core.EV_SPD; set => Core.EV_SPD = value; }
public byte CNT_Cool { get => Core.CNT_Cool; set => Core.CNT_Cool = value; }
public byte CNT_Beauty { get => Core.CNT_Beauty; set => Core.CNT_Beauty = value; }
public byte CNT_Cute { get => Core.CNT_Cute; set => Core.CNT_Cute = value; }
public byte CNT_Smart { get => Core.CNT_Smart; set => Core.CNT_Smart = value; }
public byte CNT_Tough { get => Core.CNT_Tough; set => Core.CNT_Tough = value; }
public byte CNT_Sheen { get => Core.CNT_Sheen; set => Core.CNT_Sheen = value; }
public byte HeightScalar { get => Core.HeightScalar; set => Core.HeightScalar = value; }
public byte WeightScalar { get => Core.WeightScalar; set => Core.WeightScalar = value; }
public override int Stat_HPCurrent { get => Core.Stat_HPCurrent; set => Core.Stat_HPCurrent = value; }
public override int IV_HP { get => Core.IV_HP; set => Core.IV_HP = value; }
public override int IV_ATK { get => Core.IV_ATK; set => Core.IV_ATK = value; }
public override int IV_DEF { get => Core.IV_DEF; set => Core.IV_DEF = value; }
public override int IV_SPE { get => Core.IV_SPE; set => Core.IV_SPE = value; }
public override int IV_SPA { get => Core.IV_SPA; set => Core.IV_SPA = value; }
public override int IV_SPD { get => Core.IV_SPD; set => Core.IV_SPD = value; }
public override bool IsEgg { get => Core.IsEgg; set => Core.IsEgg = value; }
public override bool IsNicknamed { get => Core.IsNicknamed; set => Core.IsNicknamed = value; }
public override int Status_Condition { get => Core.Status_Condition; set => Core.Status_Condition = value; }
public override int HT_Gender { get => Core.HT_Gender; set => Core.HT_Gender = value; }
public byte HT_Language { get => Core.HT_Language; set => Core.HT_Language = value; }
public override int CurrentHandler { get => Core.CurrentHandler; set => Core.CurrentHandler = value; }
public int HT_TrainerID { get => Core.HT_TrainerID; set => Core.HT_TrainerID = value; }
public override int HT_Friendship { get => Core.HT_Friendship; set => Core.HT_Friendship = value; }
public byte HT_Intensity { get => Core.HT_Intensity; set => Core.HT_Intensity = value; }
public byte HT_Memory { get => Core.HT_Memory; set => Core.HT_Memory = value; }
public byte HT_Feeling { get => Core.HT_Feeling; set => Core.HT_Feeling = value; }
public ushort HT_TextVar { get => Core.HT_TextVar; set => Core.HT_TextVar = value; }
public override int Version { get => Core.Version; set => Core.Version = value; }
public byte BattleVersion { get => Core.BattleVersion; set => Core.BattleVersion = value; }
public override int Language { get => Core.Language; set => Core.Language = value; }
public uint FormArgument { get => Core.FormArgument; set => Core.FormArgument = value; }
public byte FormArgumentRemain { get => Core.FormArgumentRemain; set => Core.FormArgumentRemain = value; }
public byte FormArgumentElapsed { get => Core.FormArgumentElapsed; set => Core.FormArgumentElapsed = value; }
public byte FormArgumentMaximum { get => Core.FormArgumentMaximum; set => Core.FormArgumentMaximum = value; }
public sbyte AffixedRibbon { get => Core.AffixedRibbon; set => Core.AffixedRibbon = value; }
public override int OT_Friendship { get => Core.OT_Friendship; set => Core.OT_Friendship = value; }
public byte OT_Intensity { get => Core.OT_Intensity; set => Core.OT_Intensity = value; }
public byte OT_Memory { get => Core.OT_Memory; set => Core.OT_Memory = value; }
public ushort OT_TextVar { get => Core.OT_TextVar; set => Core.OT_TextVar = value; }
public byte OT_Feeling { get => Core.OT_Feeling; set => Core.OT_Feeling = value; }
public override int Egg_Year { get => Core.Egg_Year; set => Core.Egg_Year = value; }
public override int Egg_Month { get => Core.Egg_Month; set => Core.Egg_Month = value; }
public override int Egg_Day { get => Core.Egg_Day; set => Core.Egg_Day = value; }
public override int Met_Year { get => Core.Met_Year; set => Core.Met_Year = value; }
public override int Met_Month { get => Core.Met_Month; set => Core.Met_Month = value; }
public override int Met_Day { get => Core.Met_Day; set => Core.Met_Day = value; }
public override int Met_Level { get => Core.Met_Level; set => Core.Met_Level = value; }
public override int OT_Gender { get => Core.OT_Gender; set => Core.OT_Gender = value; }
public byte HyperTrainFlags { get => Core.HyperTrainFlags; set => Core.HyperTrainFlags = value; }
public bool HT_HP { get => Core.HT_HP; set => Core.HT_HP = value; }
public bool HT_ATK { get => Core.HT_ATK; set => Core.HT_ATK = value; }
public bool HT_DEF { get => Core.HT_DEF; set => Core.HT_DEF = value; }
public bool HT_SPA { get => Core.HT_SPA; set => Core.HT_SPA = value; }
public bool HT_SPD { get => Core.HT_SPD; set => Core.HT_SPD = value; }
public bool HT_SPE { get => Core.HT_SPE; set => Core.HT_SPE = value; }
public override int HeldItem { get => Core.HeldItem; set => Core.HeldItem = value; }
public override string Nickname { get => _coreData.Nickname; set => _coreData.Nickname = value; }
public override string OT_Name { get => _coreData.OT_Name; set => _coreData.OT_Name = value; }
public override string HT_Name { get => _coreData.HT_Name; set => _coreData.HT_Name = value; }
public override string Nickname { get => Core.Nickname; set => Core.Nickname = value; }
public override string OT_Name { get => Core.OT_Name; set => Core.OT_Name = value; }
public override string HT_Name { get => Core.HT_Name; set => Core.HT_Name = value; }
public override int MarkingCount => _coreData.MarkingCount;
public int RibbonCount => _coreData.RibbonCount;
public int MarkCount => _coreData.MarkCount;
public int RibbonMarkCount => _coreData.RibbonMarkCount;
public override int GetMarking(int index) => _coreData.GetMarking(index);
public override void SetMarking(int index, int value) => _coreData.SetMarking(index, value);
public override int MarkingCount => Core.MarkingCount;
public int RibbonCount => Core.RibbonCount;
public int MarkCount => Core.MarkCount;
public int RibbonMarkCount => Core.RibbonMarkCount;
public override int GetMarking(int index) => Core.GetMarking(index);
public override void SetMarking(int index, int value) => Core.SetMarking(index, value);
#endregion
// Used to be in Core, now we just don't bother.
public override int PKRS_Days { get => 0; set { } }
public override int PKRS_Strain { get => 0; set { } }
public override int Ability { get => 0; set { } }
public override int AbilityNumber { get => 0; set { } }
#region Calculated
public override int CurrentFriendship { get => CurrentHandler == 0 ? OT_Friendship : HT_Friendship; set { if (CurrentHandler == 0) OT_Friendship = value; else HT_Friendship = value; } }
@ -233,12 +248,12 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
public override int MaxAbilityID => Legal.MaxAbilityID_8a;
public override int MaxItemID => Legal.MaxItemID_8a;
public override int MaxBallID => Legal.MaxBallID_8a;
public override int MaxGameID => Legal.MaxGameID_8a;
public override int MaxGameID => Legal.MaxGameID_HOME;
#endregion
public override int SIZE_PARTY => HomeCrypto.SIZE_1STORED;
public override int SIZE_STORED => HomeCrypto.SIZE_1STORED;
public override int SIZE_PARTY => HomeCrypto.SIZE_2STORED;
public override int SIZE_STORED => HomeCrypto.SIZE_2STORED;
public override bool Valid { get => true; set { } }
public override PersonalInfo PersonalInfo => LatestGameData.GetPersonalInfo(Species, Form);
public override void RefreshChecksum() => Checksum = 0;
@ -250,8 +265,6 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
return HomeCrypto.Encrypt(result);
}
private const int GameDataStart = HomeCrypto.SIZE_1HEADER + 2 + HomeCrypto.SIZE_1CORE + 2;
public byte[] Rebuild()
{
var length = WriteLength;
@ -267,7 +280,7 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
// Header and Core are already in the current byte array.
// Write each part, starting with header and core.
int ctr = HomeCrypto.SIZE_1HEADER + 2;
ctr += _coreData.WriteTo(span[ctr..]);
ctr += Core.WriteTo(span[ctr..]);
var gameDataLengthSpan = span[ctr..];
int gameDataStart = (ctr += 2);
if (DataPK8 is { } pk8) ctr += pk8.WriteTo(span[ctr..]);
@ -278,9 +291,9 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
WriteUInt16LittleEndian(gameDataLengthSpan, GameDataSize = (ushort)(ctr - gameDataStart));
// Update metadata to ensure we're a valid object.
DataVersion = HomeCrypto.Version1;
DataVersion = HomeCrypto.VersionLatest;
EncodedDataSize = (ushort)(result.Length - HomeCrypto.SIZE_1HEADER);
CoreDataSize = HomeCrypto.SIZE_1CORE;
CoreDataSize = (ushort)Core.SerializedSize;
Data.AsSpan(0, HomeCrypto.SIZE_1HEADER + 2).CopyTo(span); // Copy updated header & CoreData length.
return result;
@ -291,11 +304,11 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
get
{
var length = GameDataStart;
if (DataPK8 is not null) length += HomeOptional1.HeaderSize + HomeCrypto.SIZE_1GAME_PK8;
if (DataPB7 is not null) length += HomeOptional1.HeaderSize + HomeCrypto.SIZE_1GAME_PB7;
if (DataPA8 is not null) length += HomeOptional1.HeaderSize + HomeCrypto.SIZE_1GAME_PA8;
if (DataPB8 is not null) length += HomeOptional1.HeaderSize + HomeCrypto.SIZE_1GAME_PB8;
if (DataPK9 is not null) length += HomeOptional1.HeaderSize + HomeCrypto.SIZE_1GAME_PK9;
if (DataPK8 is {} k8) length += k8.SerializedSize;
if (DataPB7 is {} b7) length += b7.SerializedSize;
if (DataPA8 is {} a8) length += a8.SerializedSize;
if (DataPB8 is {} b8) length += b8.SerializedSize;
if (DataPK9 is {} k9) length += k9.SerializedSize;
return length;
}
}
@ -327,18 +340,77 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
(int)PLA => DataPA8,
(int)SL or (int)VL => DataPK9,
(int)SW or (int)SH when DataPK8 is { Met_Location: HOME_SWLA } => DataPA8,
(int)SW or (int)SH when DataPK8 is { Met_Location: HOME_SWBD or HOME_SHSP } => DataPB8,
(int)SW or (int)SH => DataPK8,
// SW/SH can be confused with others if we didn't seed with the original transfer data.
(int)SW or (int)SH => DataPK8 switch
{
{ Met_Location: LocationsHOME.SWLA } => DataPA8,
{ Met_Location: LocationsHOME.SWBD or LocationsHOME.SHSP } => DataPB8,
{ Met_Location: LocationsHOME.SWSL or LocationsHOME.SHVL } => DataPK9,
_ => DataPK8,
},
_ => DataPK8,
_ => DataPK8, // Gen7 and below.
};
public PKM? ConvertToPB7() => DataPB7 is { } x ? x.ConvertToPB7(this) : (DataPB7 ??= GameDataPB7.TryCreate(this))?.ConvertToPB7(this);
public PK8? ConvertToPK8() => DataPK8 is { } x ? x.ConvertToPK8(this) : (DataPK8 ??= GameDataPK8.TryCreate(this))?.ConvertToPK8(this);
public PB8? ConvertToPB8() => DataPB8 is { } x ? x.ConvertToPB8(this) : (DataPB8 ??= GameDataPB8.TryCreate(this))?.ConvertToPB8(this);
public PA8? ConvertToPA8() => DataPA8 is { } x ? x.ConvertToPA8(this) : (DataPA8 ??= GameDataPA8.TryCreate(this))?.ConvertToPA8(this);
public PK9? ConvertToPK9() => DataPK9 is { } x ? x.ConvertToPK9(this) : (DataPK9 ??= GameDataPK9.TryCreate(this))?.ConvertToPK9(this);
public PB7? ConvertToPB7() => DataPB7 is { } x ? x.ConvertToPKM(this) : (DataPB7 ??= GameDataPB7.TryCreate(this))?.ConvertToPKM(this);
public PK8? ConvertToPK8() => DataPK8 is { } x ? x.ConvertToPKM(this) : (DataPK8 ??= GameDataPK8.TryCreate(this))?.ConvertToPKM(this);
public PB8? ConvertToPB8() => DataPB8 is { } x ? x.ConvertToPKM(this) : (DataPB8 ??= GameDataPB8.TryCreate(this))?.ConvertToPKM(this);
public PA8? ConvertToPA8() => DataPA8 is { } x ? x.ConvertToPKM(this) : (DataPA8 ??= GameDataPA8.TryCreate(this))?.ConvertToPKM(this);
public PK9? ConvertToPK9() => DataPK9 is { } x ? x.ConvertToPKM(this) : (DataPK9 ??= GameDataPK9.TryCreate(this))?.ConvertToPKM(this);
public void CopyTo(PKM pk) => _coreData.CopyTo(pk);
public void CopyTo(PKM pk) => Core.CopyTo(pk);
public static HomeGameDataFormat GetType(Type type)
{
if (type == typeof(PB7)) return HomeGameDataFormat.PB7;
if (type == typeof(PK8)) return HomeGameDataFormat.PK8;
if (type == typeof(PB8)) return HomeGameDataFormat.PB8;
if (type == typeof(PA8)) return HomeGameDataFormat.PA8;
if (type == typeof(PK9)) return HomeGameDataFormat.PK9;
return HomeGameDataFormat.None;
}
public PKM? ConvertToPKM(HomeGameDataFormat type) => type switch
{
HomeGameDataFormat.PB7 => ConvertToPB7(),
HomeGameDataFormat.PK8 => ConvertToPK8(),
HomeGameDataFormat.PB8 => ConvertToPB8(),
HomeGameDataFormat.PA8 => ConvertToPA8(),
HomeGameDataFormat.PK9 => ConvertToPK9(),
_ => null,
};
public static PKH ConvertFromPKM(PKM pk)
{
var blank = new PKH();
blank.CopyFrom(pk);
blank.EnsureScaleSizeExists();
if (blank.Species is (int)PKHeX.Core.Species.Arceus or (int)PKHeX.Core.Species.Silvally)
blank.Form = 0;
return blank;
}
public void CopyFrom(PKM pk)
{
Core.CopyFrom(pk);
if (pk is PB7 pb7) (DataPB7 ??= new GameDataPB7()).CopyFrom(pb7, this);
else if (pk is PK8 pk8) (DataPK8 ??= new GameDataPK8()).CopyFrom(pk8, this);
else if (pk is PB8 pb8) (DataPB8 ??= new GameDataPB8()).CopyFrom(pb8, this);
else if (pk is PA8 pa8) (DataPA8 ??= new GameDataPA8()).CopyFrom(pa8, this);
else if (pk is PK9 pk9) (DataPK9 ??= new GameDataPK9()).CopyFrom(pk9, this);
}
private IGameDataSide? FirstScaleData => DataPK9 ?? DataPA8 as IGameDataSide;
private void EnsureScaleSizeExists()
{
if (FirstScaleData is IScaledSize3)
return; // data exists for scale, keep values.
while (HeightScalar == 0 && WeightScalar == 0)
{
var rnd = Util.Rand;
HeightScalar = PokeSizeUtil.GetRandomScalar(rnd);
WeightScalar = PokeSizeUtil.GetRandomScalar(rnd);
}
}
}

View file

@ -21,7 +21,7 @@ public static class BattleVersionExtensions
public static bool IsBattleVersionValid<T>(this T pk, EvolutionHistory h) where T : PKM, IBattleVersion => pk.BattleVersion switch
{
0 => true,
(int)GameVersion.SW or (int)GameVersion.SH => h.HasVisitedSWSH && !(pk.SWSH || pk.BDSP || pk.LA),
(int)GameVersion.SW or (int)GameVersion.SH => h.HasVisitedSWSH && !(pk.SWSH || pk.BDSP || pk.LA || pk.SV),
_ => false,
};

View file

@ -126,8 +126,8 @@ public static class FormArgumentUtil
(int)Stantler or (int)Wyrdeer when generation >= 8 => 9999,
(int)Basculin when form == 2 => 9999, // 294
(int)Basculegion => 9999, // 294
(int)Primeape or (int)Annihilape when generation >= 9 => 9999,
(int)Bisharp or (int)Kingambit when generation >= 9 => 9999,
(int)Primeape or (int)Annihilape when generation >= 8 => 9999,
(int)Bisharp or (int)Kingambit when generation >= 8 => 9999,
(int)Gimmighoul => 998,
(int)Gholdengo => 999,
(int)Koraidon or (int)Miraidon => 1,

View file

@ -1,4 +1,4 @@
namespace PKHeX.Core;
namespace PKHeX.Core;
/// <summary>
/// Interface that exposes a <see cref="Tracker"/> for Pokémon HOME.
@ -10,4 +10,6 @@ public interface IHomeTrack
/// Tracker for the associated <see cref="PKM"/>
/// </summary>
ulong Tracker { get; set; }
bool HasTracker => Tracker != 0;
}

View file

@ -144,7 +144,7 @@ public static partial class Extensions
return currentLevel <= min;
}
/// <inheritdoc cref="GetHyperTrainMinLevel(IHyperTrain,EvolutionHistory)"/>
/// <inheritdoc cref="GetHyperTrainMinLevel(IHyperTrain,EvolutionHistory, EntityContext)"/>
public static int GetHyperTrainMinLevel(this EntityContext c) => c switch
{
EntityContext.Gen7 or EntityContext.Gen8 or EntityContext.Gen8b => 100,
@ -157,9 +157,15 @@ public static partial class Extensions
/// </summary>
/// <param name="_">Entity to train</param>
/// <param name="h">History of evolutions present as</param>
/// <param name="current">Current context</param>
/// <returns>True if available, otherwise false.</returns>
public static int GetHyperTrainMinLevel(this IHyperTrain _, EvolutionHistory h)
public static int GetHyperTrainMinLevel(this IHyperTrain _, EvolutionHistory h, EntityContext current)
{
// HOME 3.0.0+ disallows inbound transfers of Hyper Trained Pokémon below level 100.
// PokeDupeChecker in BD/SP will DprIllegal if < 100, even if it was legitimately trained in S/V+.
if (current == EntityContext.Gen8b)
return 100;
if (h.HasVisitedGen9)
return 50;
return 100;
@ -176,7 +182,7 @@ public static partial class Extensions
return false;
// Gated behind level.
var min = t.GetHyperTrainMinLevel(h);
var min = t.GetHyperTrainMinLevel(h, pk.Context);
return pk.CurrentLevel >= min;
}
}

View file

@ -1,12 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Allows resetting the moveset back to an initial state.
/// </summary>
public interface IMoveReset
{
/// <summary>
/// Resets the current moves to the current level up set.
/// </summary>
void ResetMoves();
}

View file

@ -86,4 +86,13 @@ public static class TeraTypeUtil
/// <param name="t">Entity to set the value to.</param>
/// <param name="type">Value to update with.</param>
public static void SetTeraType(this ITeraType t, byte type) => t.SetTeraType((MoveType)type);
/// <summary>
/// Gets the preferred Tera Type to set for the given <see cref="IPersonalType"/>.
/// </summary>
public static MoveType GetTeraTypeImport(byte type1, byte type2)
{
var type = (MoveType)type1;
return type != (byte)MoveType.Normal ? type : (MoveType)type2;
}
}

View file

@ -6,9 +6,9 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
public sealed class PA8 : PKM, ISanityChecksum, IMoveReset,
IGanbaru, IAlpha, INoble, ITechRecord, ISociability, IMoveShop8Mastery, IContestStats, IHyperTrain, IScaledSizeValue, IScaledSize3, IGigantamax, IFavorite, IDynamaxLevel, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories,
IRibbonIndex, IRibbonSetAffixed, IRibbonSetRibbons, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetMemory6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMarks, IRibbonSetMark8
public sealed class PA8 : PKM, ISanityChecksum,
IGanbaru, IAlpha, INoble, ITechRecord, ISociability, IMoveShop8Mastery, IContestStats, IHyperTrain, IScaledSizeValue, IScaledSize3, IGigantamax, IFavorite, IDynamaxLevel, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories, IPokerusStatus,
IRibbonIndex, IRibbonSetAffixed, IRibbonSetRibbons, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetMemory6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMarks, IRibbonSetMark8, IRibbonSetCommon9, IRibbonSetMark9
{
public override ReadOnlySpan<ushort> ExtraBytes => new ushort[]
{
@ -175,7 +175,7 @@ public sealed class PA8 : PKM, ISanityChecksum, IMoveReset,
public byte CNT_Smart { get => Data[0x2F]; set => Data[0x2F] = value; }
public byte CNT_Tough { get => Data[0x30]; set => Data[0x30] = value; }
public byte CNT_Sheen { get => Data[0x31]; set => Data[0x31] = value; }
private byte PKRS { get => Data[0x32]; set => Data[0x32] = value; }
public byte PKRS { get => Data[0x32]; set => Data[0x32] = 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)); }
// 0x33 unused padding
@ -296,21 +296,21 @@ public sealed class PA8 : PKM, ISanityChecksum, IMoveReset,
public bool RibbonMarkThorny { get => FlagUtil.GetFlag(Data, 0x43, 7); set => FlagUtil.SetFlag(Data, 0x43, 7, value); }
// 0x44 Ribbon 2
public bool RibbonMarkVigor { get => FlagUtil.GetFlag(Data, 0x44, 0); set => FlagUtil.SetFlag(Data, 0x44, 0, value); }
public bool RibbonMarkSlump { get => FlagUtil.GetFlag(Data, 0x44, 1); set => FlagUtil.SetFlag(Data, 0x44, 1, value); }
public bool RibbonHisui { get => FlagUtil.GetFlag(Data, 0x44, 2); set => FlagUtil.SetFlag(Data, 0x44, 2, value); }
public bool RibbonTwinklingStar { get => FlagUtil.GetFlag(Data, 0x44, 3); set => FlagUtil.SetFlag(Data, 0x44, 3, value); }
public bool RIB44_4 { get => FlagUtil.GetFlag(Data, 0x44, 4); set => FlagUtil.SetFlag(Data, 0x44, 4, value); }
public bool RIB44_5 { get => FlagUtil.GetFlag(Data, 0x44, 5); set => FlagUtil.SetFlag(Data, 0x44, 5, value); }
public bool RIB44_6 { get => FlagUtil.GetFlag(Data, 0x44, 6); set => FlagUtil.SetFlag(Data, 0x44, 6, value); }
public bool RIB44_7 { get => FlagUtil.GetFlag(Data, 0x44, 7); set => FlagUtil.SetFlag(Data, 0x44, 7, value); }
public bool RibbonMarkVigor { get => FlagUtil.GetFlag(Data, 0x44, 0); set => FlagUtil.SetFlag(Data, 0x44, 0, value); }
public bool RibbonMarkSlump { get => FlagUtil.GetFlag(Data, 0x44, 1); set => FlagUtil.SetFlag(Data, 0x44, 1, value); }
public bool RibbonHisui { get => FlagUtil.GetFlag(Data, 0x44, 2); set => FlagUtil.SetFlag(Data, 0x44, 2, value); }
public bool RibbonTwinklingStar { get => FlagUtil.GetFlag(Data, 0x44, 3); set => FlagUtil.SetFlag(Data, 0x44, 3, value); }
public bool RibbonChampionPaldea { get => FlagUtil.GetFlag(Data, 0x44, 4); set => FlagUtil.SetFlag(Data, 0x44, 4, value); }
public bool RibbonMarkJumbo { get => FlagUtil.GetFlag(Data, 0x44, 5); set => FlagUtil.SetFlag(Data, 0x44, 5, value); }
public bool RibbonMarkMini { get => FlagUtil.GetFlag(Data, 0x44, 6); set => FlagUtil.SetFlag(Data, 0x44, 6, value); }
public bool RibbonMarkItemfinder { get => FlagUtil.GetFlag(Data, 0x44, 7); set => FlagUtil.SetFlag(Data, 0x44, 7, value); }
public bool RIB45_0 { get => FlagUtil.GetFlag(Data, 0x45, 0); set => FlagUtil.SetFlag(Data, 0x45, 0, value); }
public bool RIB45_1 { get => FlagUtil.GetFlag(Data, 0x45, 1); set => FlagUtil.SetFlag(Data, 0x45, 1, value); }
public bool RIB45_2 { get => FlagUtil.GetFlag(Data, 0x45, 2); set => FlagUtil.SetFlag(Data, 0x45, 2, value); }
public bool RIB45_3 { get => FlagUtil.GetFlag(Data, 0x45, 3); set => FlagUtil.SetFlag(Data, 0x45, 3, value); }
public bool RIB45_4 { get => FlagUtil.GetFlag(Data, 0x45, 4); set => FlagUtil.SetFlag(Data, 0x45, 4, value); }
public bool RIB45_5 { get => FlagUtil.GetFlag(Data, 0x45, 5); set => FlagUtil.SetFlag(Data, 0x45, 5, value); }
public bool RibbonMarkPartner { get => FlagUtil.GetFlag(Data, 0x45, 0); set => FlagUtil.SetFlag(Data, 0x45, 0, value); }
public bool RibbonMarkGourmand { get => FlagUtil.GetFlag(Data, 0x45, 1); set => FlagUtil.SetFlag(Data, 0x45, 1, value); }
public bool RibbonOnceInALifetime { get => FlagUtil.GetFlag(Data, 0x45, 2); set => FlagUtil.SetFlag(Data, 0x45, 2, value); }
public bool RibbonMarkAlpha { get => FlagUtil.GetFlag(Data, 0x45, 3); set => FlagUtil.SetFlag(Data, 0x45, 3, value); }
public bool RibbonMarkMightiest { get => FlagUtil.GetFlag(Data, 0x45, 4); set => FlagUtil.SetFlag(Data, 0x45, 4, value); }
public bool RibbonMarkTitan { get => FlagUtil.GetFlag(Data, 0x45, 5); set => FlagUtil.SetFlag(Data, 0x45, 5, value); }
public bool RIB45_6 { get => FlagUtil.GetFlag(Data, 0x45, 6); set => FlagUtil.SetFlag(Data, 0x45, 6, value); }
public bool RIB45_7 { get => FlagUtil.GetFlag(Data, 0x45, 7); set => FlagUtil.SetFlag(Data, 0x45, 7, value); }
@ -331,15 +331,17 @@ public sealed class PA8 : PKM, ISanityChecksum, IMoveReset,
public bool RIB47_5 { get => FlagUtil.GetFlag(Data, 0x47, 5); set => FlagUtil.SetFlag(Data, 0x47, 5, value); }
public bool RIB47_6 { get => FlagUtil.GetFlag(Data, 0x47, 6); set => FlagUtil.SetFlag(Data, 0x47, 6, value); }
public bool RIB47_7 { get => FlagUtil.GetFlag(Data, 0x47, 7); set => FlagUtil.SetFlag(Data, 0x47, 7, value); }
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b00000000_00011111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00001100__00000000_00000000__00000000_00000000);
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000100_00011100__00000000_00000000__00000000_00000000);
public int MarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00000011__11111111_11111111__11111111_11111111);
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00111011_11100011__11111111_11111111__11111111_11111111);
public int RibbonMarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11111111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00001111__11111111_11111111__11111111_11111111);
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00111111_11111111__11111111_11111111__11111111_11111111);
public bool HasMarkEncounter8 => MarkCount != 0;
public bool HasMarkEncounter8 => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00000011__11111111_11111111__11111111_11111111) != 0;
public bool HasMarkEncounter9 => (Data[0x45] & 0b00111000) != 0;
public uint Sociability { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); }
@ -699,7 +701,7 @@ public sealed class PA8 : PKM, ISanityChecksum, IMoveReset,
public override int MaxAbilityID => Legal.MaxAbilityID_8a;
public override int MaxItemID => Legal.MaxItemID_8a;
public override int MaxBallID => Legal.MaxBallID_8a;
public override int MaxGameID => Legal.MaxGameID_8a;
public override int MaxGameID => Legal.MaxGameID_HOME;
public float HeightRatio => GetHeightRatio(HeightScalar);
public float WeightRatio => GetWeightRatio(WeightScalar);
@ -794,244 +796,4 @@ public sealed class PA8 : PKM, ISanityChecksum, IMoveReset,
if (permit.IsRecordPermitted(flagIndex))
SetMasteredRecordFlag(flagIndex, true);
}
public PK8 ConvertToPK8()
{
var pk = ConvertTo<PK8>();
pk.SanitizeImport();
return pk;
}
public PB8 ConvertToPB8()
{
var pk = ConvertTo<PB8>();
if (pk.Egg_Location == 0)
pk.Egg_Location = Locations.Default8bNone;
return pk;
}
private T ConvertTo<T>() where T : G8PKM, new()
{
var pk = new T
{
EncryptionConstant = EncryptionConstant,
PID = PID,
Species = Species,
Form = Form,
FormArgument = FormArgument,
Gender = Gender,
Nature = Nature,
StatNature = StatNature,
TID16 = TID16,
SID16 = SID16,
EXP = EXP,
Ability = Ability,
AbilityNumber = AbilityNumber,
Language = Language,
Version = Version,
IV_HP = IV_HP,
IV_ATK = IV_ATK,
IV_DEF = IV_DEF,
IV_SPE = IV_SPE,
IV_SPA = IV_SPA,
IV_SPD = IV_SPD,
IsEgg = IsEgg,
EV_HP = EV_HP,
EV_ATK = EV_ATK,
EV_DEF = EV_DEF,
EV_SPE = EV_SPE,
EV_SPA = EV_SPA,
EV_SPD = EV_SPD,
OT_Gender = OT_Gender,
OT_Friendship = OT_Friendship,
OT_Intensity = OT_Intensity,
OT_Memory = OT_Memory,
OT_TextVar = OT_TextVar,
OT_Feeling = OT_Feeling,
Egg_Year = Egg_Year,
Egg_Month = Egg_Month,
Egg_Day = Egg_Day,
Met_Year = Met_Year,
Met_Month = Met_Month,
Met_Day = Met_Day,
Ball = Ball,
Egg_Location = Egg_Location,
Met_Location = Met_Location,
Met_Level = Met_Level,
Tracker = Tracker,
IsNicknamed = IsNicknamed,
CurrentHandler = CurrentHandler,
HT_Gender = HT_Gender,
HT_Language = HT_Language,
HT_Friendship = HT_Friendship,
HT_Intensity = HT_Intensity,
HT_Memory = HT_Memory,
HT_Feeling = HT_Feeling,
HT_TextVar = HT_TextVar,
FatefulEncounter = FatefulEncounter,
CNT_Cool = CNT_Cool,
CNT_Beauty = CNT_Beauty,
CNT_Cute = CNT_Cute,
CNT_Smart = CNT_Smart,
CNT_Tough = CNT_Tough,
CNT_Sheen = CNT_Sheen,
RibbonChampionKalos = RibbonChampionKalos,
RibbonChampionG3 = RibbonChampionG3,
RibbonChampionSinnoh = RibbonChampionSinnoh,
RibbonBestFriends = RibbonBestFriends,
RibbonTraining = RibbonTraining,
RibbonBattlerSkillful = RibbonBattlerSkillful,
RibbonBattlerExpert = RibbonBattlerExpert,
RibbonEffort = RibbonEffort,
RibbonAlert = RibbonAlert,
RibbonShock = RibbonShock,
RibbonDowncast = RibbonDowncast,
RibbonCareless = RibbonCareless,
RibbonRelax = RibbonRelax,
RibbonSnooze = RibbonSnooze,
RibbonSmile = RibbonSmile,
RibbonGorgeous = RibbonGorgeous,
RibbonRoyal = RibbonRoyal,
RibbonGorgeousRoyal = RibbonGorgeousRoyal,
RibbonArtist = RibbonArtist,
RibbonFootprint = RibbonFootprint,
RibbonRecord = RibbonRecord,
RibbonLegend = RibbonLegend,
RibbonCountry = RibbonCountry,
RibbonNational = RibbonNational,
RibbonEarth = RibbonEarth,
RibbonWorld = RibbonWorld,
RibbonClassic = RibbonClassic,
RibbonPremier = RibbonPremier,
RibbonEvent = RibbonEvent,
RibbonBirthday = RibbonBirthday,
RibbonSpecial = RibbonSpecial,
RibbonSouvenir = RibbonSouvenir,
RibbonWishing = RibbonWishing,
RibbonChampionBattle = RibbonChampionBattle,
RibbonChampionRegional = RibbonChampionRegional,
RibbonChampionNational = RibbonChampionNational,
RibbonChampionWorld = RibbonChampionWorld,
HasContestMemoryRibbon = HasContestMemoryRibbon,
HasBattleMemoryRibbon = HasBattleMemoryRibbon,
RibbonChampionG6Hoenn = RibbonChampionG6Hoenn,
RibbonContestStar = RibbonContestStar,
RibbonMasterCoolness = RibbonMasterCoolness,
RibbonMasterBeauty = RibbonMasterBeauty,
RibbonMasterCuteness = RibbonMasterCuteness,
RibbonMasterCleverness = RibbonMasterCleverness,
RibbonMasterToughness = RibbonMasterToughness,
RibbonChampionAlola = RibbonChampionAlola,
RibbonBattleRoyale = RibbonBattleRoyale,
RibbonBattleTreeGreat = RibbonBattleTreeGreat,
RibbonBattleTreeMaster = RibbonBattleTreeMaster,
RibbonChampionGalar = RibbonChampionGalar,
RibbonTowerMaster = RibbonTowerMaster,
RibbonMasterRank = RibbonMasterRank,
RibbonMarkLunchtime = RibbonMarkLunchtime,
RibbonMarkSleepyTime = RibbonMarkSleepyTime,
RibbonMarkDusk = RibbonMarkDusk,
RibbonMarkDawn = RibbonMarkDawn,
RibbonMarkCloudy = RibbonMarkCloudy,
RibbonMarkRainy = RibbonMarkRainy,
RibbonMarkStormy = RibbonMarkStormy,
RibbonMarkSnowy = RibbonMarkSnowy,
RibbonMarkBlizzard = RibbonMarkBlizzard,
RibbonMarkDry = RibbonMarkDry,
RibbonMarkSandstorm = RibbonMarkSandstorm,
RibbonCountMemoryContest = RibbonCountMemoryContest,
RibbonCountMemoryBattle = RibbonCountMemoryBattle,
RibbonMarkMisty = RibbonMarkMisty,
RibbonMarkDestiny = RibbonMarkDestiny,
RibbonMarkFishing = RibbonMarkFishing,
RibbonMarkCurry = RibbonMarkCurry,
RibbonMarkUncommon = RibbonMarkUncommon,
RibbonMarkRare = RibbonMarkRare,
RibbonMarkRowdy = RibbonMarkRowdy,
RibbonMarkAbsentMinded = RibbonMarkAbsentMinded,
RibbonMarkJittery = RibbonMarkJittery,
RibbonMarkExcited = RibbonMarkExcited,
RibbonMarkCharismatic = RibbonMarkCharismatic,
RibbonMarkCalmness = RibbonMarkCalmness,
RibbonMarkIntense = RibbonMarkIntense,
RibbonMarkZonedOut = RibbonMarkZonedOut,
RibbonMarkJoyful = RibbonMarkJoyful,
RibbonMarkAngry = RibbonMarkAngry,
RibbonMarkSmiley = RibbonMarkSmiley,
RibbonMarkTeary = RibbonMarkTeary,
RibbonMarkUpbeat = RibbonMarkUpbeat,
RibbonMarkPeeved = RibbonMarkPeeved,
RibbonMarkIntellectual = RibbonMarkIntellectual,
RibbonMarkFerocious = RibbonMarkFerocious,
RibbonMarkCrafty = RibbonMarkCrafty,
RibbonMarkScowling = RibbonMarkScowling,
RibbonMarkKindly = RibbonMarkKindly,
RibbonMarkFlustered = RibbonMarkFlustered,
RibbonMarkPumpedUp = RibbonMarkPumpedUp,
RibbonMarkZeroEnergy = RibbonMarkZeroEnergy,
RibbonMarkPrideful = RibbonMarkPrideful,
RibbonMarkUnsure = RibbonMarkUnsure,
RibbonMarkHumble = RibbonMarkHumble,
RibbonMarkThorny = RibbonMarkThorny,
RibbonMarkVigor = RibbonMarkVigor,
RibbonMarkSlump = RibbonMarkSlump,
RibbonHisui = RibbonHisui,
RibbonTwinklingStar = RibbonTwinklingStar,
AffixedRibbon = AffixedRibbon,
HyperTrainFlags = HyperTrainFlags,
Sociability = Sociability,
Fullness = Fullness,
Enjoyment = Enjoyment,
BattleVersion = BattleVersion,
PKRS_Days = PKRS_Days,
PKRS_Strain = PKRS_Strain,
HeightScalar = HeightScalar,
WeightScalar = WeightScalar,
CanGigantamax = CanGigantamax,
DynamaxLevel = DynamaxLevel,
IsFavorite = IsFavorite,
MarkValue = MarkValue,
};
Nickname_Trash.CopyTo(pk.Nickname_Trash);
OT_Trash.CopyTo(pk.OT_Trash);
HT_Trash.CopyTo(pk.HT_Trash);
pk.ResetMoves();
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
public void SanitizeImport()
{
Scale = HeightScalar;
ResetHeight();
ResetWeight();
}
public void ResetMoves()
{
var learn = LearnSource8LA.Instance.GetLearnset(Species, Form);
Span<ushort> moves = stackalloc ushort[4];
learn.SetEncounterMoves(CurrentLevel, moves);
SetMoves(moves);
this.SetMaximumPPCurrent(moves);
}
public PK9 ConvertToPK9()
{
// Todo: Transfer to PK9
return new PK9();
}
}

View file

@ -609,79 +609,4 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
5 => 4,
_ => throw new ArgumentOutOfRangeException(nameof(characterIndex)), // never happens, characteristic is always 0-29
};
public PK8 ConvertToPK8()
{
var pk8 = new PK8
{
EncryptionConstant = EncryptionConstant,
Species = Species,
TID16 = TID16,
SID16 = SID16,
EXP = EXP,
PID = PID,
Ability = Ability,
AbilityNumber = AbilityNumber,
MarkValue = MarkValue & 0b1111_1111_1111,
Language = Language,
EV_HP = EV_HP,
EV_ATK = EV_ATK,
EV_DEF = EV_DEF,
EV_SPA = EV_SPA,
EV_SPD = EV_SPD,
EV_SPE = EV_SPE,
Move1 = Move1,
Move2 = Move2,
Move3 = Move3,
Move4 = Move4,
Move1_PPUps = Move1_PPUps,
Move2_PPUps = Move2_PPUps,
Move3_PPUps = Move3_PPUps,
Move4_PPUps = Move4_PPUps,
RelearnMove1 = RelearnMove1,
RelearnMove2 = RelearnMove2,
RelearnMove3 = RelearnMove3,
RelearnMove4 = RelearnMove4,
IV_HP = IV_HP,
IV_ATK = IV_ATK,
IV_DEF = IV_DEF,
IV_SPA = IV_SPA,
IV_SPD = IV_SPD,
IV_SPE = IV_SPE,
IsNicknamed = IsNicknamed,
FatefulEncounter = FatefulEncounter,
Gender = Gender,
Form = Form,
Nature = Nature,
Nickname = Nickname,
Version = Version,
OT_Name = OT_Name,
MetDate = MetDate,
Met_Location = Met_Location,
Ball = Ball,
Met_Level = Met_Level,
OT_Gender = OT_Gender,
HyperTrainFlags = HyperTrainFlags,
// Memories don't exist in LGPE, and no memories are set on transfer.
OT_Friendship = OT_Friendship,
// No Ribbons or Markings on transfer.
StatNature = Nature,
HeightScalar = HeightScalar,
WeightScalar = WeightScalar,
IsFavorite = IsFavorite,
};
// Fix PP and Stats
pk8.Heal();
// Fix Checksum
pk8.RefreshChecksum();
return pk8; // Done!
}
}

View file

@ -124,39 +124,9 @@ public sealed class PB8 : G8PKM
public override int MaxAbilityID => Legal.MaxAbilityID_8b;
public override int MaxItemID => Legal.MaxItemID_8b;
public override int MaxBallID => Legal.MaxBallID_8b;
public override int MaxGameID => Legal.MaxGameID_8b;
public override int MaxGameID => Legal.MaxGameID_HOME;
public override bool WasEgg => IsEgg || Egg_Day != 0;
public PK8 ConvertToPK8()
{
var pk = ConvertTo<PK8>();
pk.SanitizeImport();
return pk;
}
public override PA8 ConvertToPA8()
{
var pk = base.ConvertToPA8();
if (pk.Egg_Location == Locations.Default8bNone)
pk.Egg_Location = 0;
return pk;
}
public override bool HasOriginalMetLocation => base.HasOriginalMetLocation && !(LA && Met_Location == Locations.HOME_SWLA);
public override void ResetMoves()
{
var learn = LearnSource8BDSP.Instance.GetLearnset(Species, Form);
Span<ushort> moves = stackalloc ushort[4];
learn.SetEncounterMoves(CurrentLevel, moves);
SetMoves(moves);
this.SetMaximumPPCurrent(moves);
}
public PK9 ConvertToPK9()
{
// Todo: Transfer to PK9
return new PK9();
}
public override bool HasOriginalMetLocation => base.HasOriginalMetLocation && !(LA && Met_Location == LocationsHOME.SWLA);
}

View file

@ -105,88 +105,9 @@ public sealed class PK8 : G8PKM
public override int MaxItemID => Legal.MaxItemID_8;
public override int MaxBallID => Legal.MaxBallID_8;
public override int MaxGameID => Legal.MaxGameID_8;
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;
}
private static void UnmapLocation(PKM pk)
{
switch (pk.Met_Location)
{
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;
}
}
public override void ResetMoves()
{
var learn = LearnSource8SWSH.Instance.GetLearnset(Species, Form);
Span<ushort> moves = stackalloc ushort[4];
learn.SetEncounterMoves(CurrentLevel, moves);
SetMoves(moves);
this.SetMaximumPPCurrent(moves);
}
public bool IsSideTransfer => Met_Location is Locations.HOME_SHSP or Locations.HOME_SWBD or Locations.HOME_SWLA;
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 bool IsSideTransfer => LocationsHOME.IsLocationSWSH(Met_Location);
public override bool SV => Met_Location is LocationsHOME.SWSL or LocationsHOME.SHVL;
public override bool BDSP => Met_Location is LocationsHOME.SWBD or LocationsHOME.SHSP;
public override bool LA => Met_Location is LocationsHOME.SWLA;
public override bool HasOriginalMetLocation => base.HasOriginalMetLocation && !(BDSP || LA);
public void SanitizeImport()
{
// 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).
var ver = Version;
if (ver is (int)GameVersion.SP)
{
Version = (int)GameVersion.SH;
Met_Location = Locations.HOME_SHSP;
Egg_Location = Egg_Location == Locations.Default8bNone ? 0 : Locations.HOME_SWSHBDSPEgg;
}
else if (ver is (int)GameVersion.BD)
{
Version = (int)GameVersion.SW;
Met_Location = Locations.HOME_SWBD;
Egg_Location = Egg_Location == Locations.Default8bNone ? 0 : Locations.HOME_SWSHBDSPEgg;
}
else if (ver is (int)GameVersion.PLA)
{
const ushort met = Locations.HOME_SWLA;
Version = (int)GameVersion.SW;
Met_Location = met;
Egg_Location = 0; // Everything originating from this game has an Egg Location of 0.
}
if (Ball > (int)Core.Ball.Beast)
Ball = (int)Core.Ball.Poke;
}
public PK9 ConvertToPK9()
{
// Todo: Transfer to PK9
return new PK9();
}
}

View file

@ -5,7 +5,7 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary> Generation 9 <see cref="PKM"/> format. </summary>
public sealed class PK9 : PKM, ISanityChecksum, ITeraType, IMoveReset, ITechRecord, IObedienceLevel,
public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedienceLevel,
IContestStats, IHyperTrain, IScaledSize, IScaledSize3, IFavorite, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories,
IRibbonIndex, IRibbonSetAffixed, IRibbonSetRibbons, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetMemory6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetCommon9, IRibbonSetMarks, IRibbonSetMark8, IRibbonSetMark9
{
@ -608,20 +608,11 @@ public sealed class PK9 : PKM, ISanityChecksum, ITeraType, IMoveReset, ITechReco
HT_Language = (byte)tr.Language;
}
public void ResetMoves()
{
var learn = LearnSource9SV.Instance.GetLearnset(Species, Form);
Span<ushort> moves = stackalloc ushort[4];
learn.SetEncounterMoves(CurrentLevel, moves);
SetMoves(moves);
this.SetMaximumPPCurrent(moves);
}
// Maximums
public override ushort MaxMoveID => Legal.MaxMoveID_9;
public override ushort MaxSpeciesID => Legal.MaxSpeciesID_9;
public override int MaxAbilityID => Legal.MaxAbilityID_9;
public override int MaxItemID => Legal.MaxItemID_9;
public override int MaxBallID => Legal.MaxBallID_9;
public override int MaxGameID => Legal.MaxGameID_9;
public override int MaxGameID => Legal.MaxGameID_HOME;
}

View file

@ -282,7 +282,7 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
public bool SWSH => Version is (int)SW or (int)SH;
public virtual bool BDSP => Version is (int)BD or (int)SP;
public virtual bool LA => Version is (int)PLA;
public bool SV => Version is (int)SL or (int)VL;
public virtual bool SV => Version is (int)SL or (int)VL;
public bool GO_LGPE => GO && Met_Location == Locations.GO7;
public bool GO_HOME => GO && Met_Location == Locations.GO8;

View file

@ -5,15 +5,13 @@ using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
public abstract class G8PKM : PKM, ISanityChecksum, IMoveReset,
ITechRecord, ISociability, IContestStats, IHyperTrain, IScaledSize, IGigantamax, IFavorite, IDynamaxLevel, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories,
IRibbonIndex, IRibbonSetAffixed, IRibbonSetRibbons, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetMemory6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMarks, IRibbonSetMark8
public abstract class G8PKM : PKM, ISanityChecksum,
ITechRecord, ISociability, IContestStats, IHyperTrain, IScaledSize, IGigantamax, IFavorite, IDynamaxLevel, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories, IPokerusStatus,
IRibbonIndex, IRibbonSetAffixed, IRibbonSetRibbons, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetMemory6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetCommon9, IRibbonSetMarks, IRibbonSetMark8, IRibbonSetMark9
{
protected G8PKM() : base(PokeCrypto.SIZE_8PARTY) { }
protected G8PKM(byte[] data) : base(DecryptParty(data)) { }
public abstract void ResetMoves();
private static byte[] DecryptParty(byte[] data)
{
PokeCrypto.DecryptIfEncrypted8(ref data);
@ -151,7 +149,7 @@ public abstract class G8PKM : PKM, ISanityChecksum, IMoveReset,
public byte CNT_Smart { get => Data[0x2F]; set => Data[0x2F] = value; }
public byte CNT_Tough { get => Data[0x30]; set => Data[0x30] = value; }
public byte CNT_Sheen { get => Data[0x31]; set => Data[0x31] = value; }
private byte PKRS { get => Data[0x32]; set => Data[0x32] = value; }
public byte PKRS { get => Data[0x32]; set => Data[0x32] = 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)); }
// 0x33 unused padding
@ -272,21 +270,21 @@ public abstract class G8PKM : PKM, ISanityChecksum, IMoveReset,
public bool RibbonMarkThorny { get => FlagUtil.GetFlag(Data, 0x43, 7); set => FlagUtil.SetFlag(Data, 0x43, 7, value); }
// 0x44 Ribbon 2
public bool RibbonMarkVigor { get => FlagUtil.GetFlag(Data, 0x44, 0); set => FlagUtil.SetFlag(Data, 0x44, 0, value); }
public bool RibbonMarkSlump { get => FlagUtil.GetFlag(Data, 0x44, 1); set => FlagUtil.SetFlag(Data, 0x44, 1, value); }
public bool RibbonHisui { get => FlagUtil.GetFlag(Data, 0x44, 2); set => FlagUtil.SetFlag(Data, 0x44, 2, value); }
public bool RibbonTwinklingStar { get => FlagUtil.GetFlag(Data, 0x44, 3); set => FlagUtil.SetFlag(Data, 0x44, 3, value); }
public bool RIB44_4 { get => FlagUtil.GetFlag(Data, 0x44, 4); set => FlagUtil.SetFlag(Data, 0x44, 4, value); }
public bool RIB44_5 { get => FlagUtil.GetFlag(Data, 0x44, 5); set => FlagUtil.SetFlag(Data, 0x44, 5, value); }
public bool RIB44_6 { get => FlagUtil.GetFlag(Data, 0x44, 6); set => FlagUtil.SetFlag(Data, 0x44, 6, value); }
public bool RIB44_7 { get => FlagUtil.GetFlag(Data, 0x44, 7); set => FlagUtil.SetFlag(Data, 0x44, 7, value); }
public bool RibbonMarkVigor { get => FlagUtil.GetFlag(Data, 0x44, 0); set => FlagUtil.SetFlag(Data, 0x44, 0, value); }
public bool RibbonMarkSlump { get => FlagUtil.GetFlag(Data, 0x44, 1); set => FlagUtil.SetFlag(Data, 0x44, 1, value); }
public bool RibbonHisui { get => FlagUtil.GetFlag(Data, 0x44, 2); set => FlagUtil.SetFlag(Data, 0x44, 2, value); }
public bool RibbonTwinklingStar { get => FlagUtil.GetFlag(Data, 0x44, 3); set => FlagUtil.SetFlag(Data, 0x44, 3, value); }
public bool RibbonChampionPaldea { get => FlagUtil.GetFlag(Data, 0x44, 4); set => FlagUtil.SetFlag(Data, 0x44, 4, value); }
public bool RibbonMarkJumbo { get => FlagUtil.GetFlag(Data, 0x44, 5); set => FlagUtil.SetFlag(Data, 0x44, 5, value); }
public bool RibbonMarkMini { get => FlagUtil.GetFlag(Data, 0x44, 6); set => FlagUtil.SetFlag(Data, 0x44, 6, value); }
public bool RibbonMarkItemfinder { get => FlagUtil.GetFlag(Data, 0x44, 7); set => FlagUtil.SetFlag(Data, 0x44, 7, value); }
public bool RIB45_0 { get => FlagUtil.GetFlag(Data, 0x45, 0); set => FlagUtil.SetFlag(Data, 0x45, 0, value); }
public bool RIB45_1 { get => FlagUtil.GetFlag(Data, 0x45, 1); set => FlagUtil.SetFlag(Data, 0x45, 1, value); }
public bool RIB45_2 { get => FlagUtil.GetFlag(Data, 0x45, 2); set => FlagUtil.SetFlag(Data, 0x45, 2, value); }
public bool RIB45_3 { get => FlagUtil.GetFlag(Data, 0x45, 3); set => FlagUtil.SetFlag(Data, 0x45, 3, value); }
public bool RIB45_4 { get => FlagUtil.GetFlag(Data, 0x45, 4); set => FlagUtil.SetFlag(Data, 0x45, 4, value); }
public bool RIB45_5 { get => FlagUtil.GetFlag(Data, 0x45, 5); set => FlagUtil.SetFlag(Data, 0x45, 5, value); }
public bool RibbonMarkPartner { get => FlagUtil.GetFlag(Data, 0x45, 0); set => FlagUtil.SetFlag(Data, 0x45, 0, value); }
public bool RibbonMarkGourmand { get => FlagUtil.GetFlag(Data, 0x45, 1); set => FlagUtil.SetFlag(Data, 0x45, 1, value); }
public bool RibbonOnceInALifetime { get => FlagUtil.GetFlag(Data, 0x45, 2); set => FlagUtil.SetFlag(Data, 0x45, 2, value); }
public bool RibbonMarkAlpha { get => FlagUtil.GetFlag(Data, 0x45, 3); set => FlagUtil.SetFlag(Data, 0x45, 3, value); }
public bool RibbonMarkMightiest { get => FlagUtil.GetFlag(Data, 0x45, 4); set => FlagUtil.SetFlag(Data, 0x45, 4, value); }
public bool RibbonMarkTitan { get => FlagUtil.GetFlag(Data, 0x45, 5); set => FlagUtil.SetFlag(Data, 0x45, 5, value); }
public bool RIB45_6 { get => FlagUtil.GetFlag(Data, 0x45, 6); set => FlagUtil.SetFlag(Data, 0x45, 6, value); }
public bool RIB45_7 { get => FlagUtil.GetFlag(Data, 0x45, 7); set => FlagUtil.SetFlag(Data, 0x45, 7, value); }
@ -309,13 +307,15 @@ public abstract class G8PKM : PKM, ISanityChecksum, IMoveReset,
public bool RIB47_7 { get => FlagUtil.GetFlag(Data, 0x47, 7); set => FlagUtil.SetFlag(Data, 0x47, 7, value); }
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b00000000_00011111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00001100__00000000_00000000__00000000_00000000);
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000100_00011100__00000000_00000000__00000000_00000000);
public int MarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00000011__11111111_11111111__11111111_11111111);
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00111011_11100011__11111111_11111111__11111111_11111111);
public int RibbonMarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11111111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00001111__11111111_11111111__11111111_11111111);
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00111111_11111111__11111111_11111111__11111111_11111111);
public bool HasMarkEncounter8 => MarkCount != 0;
public bool HasMarkEncounter8 => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00000011__11111111_11111111__11111111_11111111) != 0;
public bool HasMarkEncounter9 => (Data[0x45] & 0b00111000) != 0;
public uint Sociability { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); }
@ -504,227 +504,4 @@ public abstract class G8PKM : PKM, ISanityChecksum, IMoveReset,
index -= 64;
return 0x40 + (index >> 3);
}
protected T ConvertTo<T>() where T : G8PKM, new()
{
var pk = new T();
Data.AsSpan().CopyTo(pk.Data);
pk.Move1_PPUps = pk.Move2_PPUps = pk.Move3_PPUps = pk.Move4_PPUps = 0;
pk.RelearnMove1 = pk.RelearnMove2 = pk.RelearnMove3 = pk.RelearnMove4 = 0;
pk.ClearMoveRecordFlags();
pk.ClearPokeJobFlags();
pk.CanGigantamax = false;
pk.DynamaxLevel = 0;
pk.Sociability = 0;
pk.Fullness = 0;
pk.Data[0x52] = 0; // BD/SP IsDprIllegal
pk.ResetMoves();
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
public virtual PA8 ConvertToPA8()
{
var pk = new PA8
{
EncryptionConstant = EncryptionConstant,
PID = PID,
Species = Species,
Form = Form,
FormArgument = FormArgument,
Gender = Gender,
Nature = Nature,
StatNature = StatNature,
TID16 = TID16,
SID16 = SID16,
EXP = EXP,
Ability = Ability,
AbilityNumber = AbilityNumber,
Language = Language,
Version = Version,
IV_HP = IV_HP,
IV_ATK = IV_ATK,
IV_DEF = IV_DEF,
IV_SPE = IV_SPE,
IV_SPA = IV_SPA,
IV_SPD = IV_SPD,
IsEgg = IsEgg,
EV_HP = EV_HP,
EV_ATK = EV_ATK,
EV_DEF = EV_DEF,
EV_SPE = EV_SPE,
EV_SPA = EV_SPA,
EV_SPD = EV_SPD,
OT_Gender = OT_Gender,
OT_Friendship = OT_Friendship,
OT_Intensity = OT_Intensity,
OT_Memory = OT_Memory,
OT_TextVar = OT_TextVar,
OT_Feeling = OT_Feeling,
Egg_Year = Egg_Year,
Egg_Month = Egg_Month,
Egg_Day = Egg_Day,
Met_Year = Met_Year,
Met_Month = Met_Month,
Met_Day = Met_Day,
Ball = Ball,
Egg_Location = Egg_Location,
Met_Location = Met_Location,
Met_Level = Met_Level,
Tracker = Tracker,
IsNicknamed = IsNicknamed,
CurrentHandler = CurrentHandler,
HT_Gender = HT_Gender,
HT_Language = HT_Language,
HT_Friendship = HT_Friendship,
HT_Intensity = HT_Intensity,
HT_Memory = HT_Memory,
HT_Feeling = HT_Feeling,
HT_TextVar = HT_TextVar,
FatefulEncounter = FatefulEncounter,
CNT_Cool = CNT_Cool,
CNT_Beauty = CNT_Beauty,
CNT_Cute = CNT_Cute,
CNT_Smart = CNT_Smart,
CNT_Tough = CNT_Tough,
CNT_Sheen = CNT_Sheen,
RibbonChampionKalos = RibbonChampionKalos,
RibbonChampionG3 = RibbonChampionG3,
RibbonChampionSinnoh = RibbonChampionSinnoh,
RibbonBestFriends = RibbonBestFriends,
RibbonTraining = RibbonTraining,
RibbonBattlerSkillful = RibbonBattlerSkillful,
RibbonBattlerExpert = RibbonBattlerExpert,
RibbonEffort = RibbonEffort,
RibbonAlert = RibbonAlert,
RibbonShock = RibbonShock,
RibbonDowncast = RibbonDowncast,
RibbonCareless = RibbonCareless,
RibbonRelax = RibbonRelax,
RibbonSnooze = RibbonSnooze,
RibbonSmile = RibbonSmile,
RibbonGorgeous = RibbonGorgeous,
RibbonRoyal = RibbonRoyal,
RibbonGorgeousRoyal = RibbonGorgeousRoyal,
RibbonArtist = RibbonArtist,
RibbonFootprint = RibbonFootprint,
RibbonRecord = RibbonRecord,
RibbonLegend = RibbonLegend,
RibbonCountry = RibbonCountry,
RibbonNational = RibbonNational,
RibbonEarth = RibbonEarth,
RibbonWorld = RibbonWorld,
RibbonClassic = RibbonClassic,
RibbonPremier = RibbonPremier,
RibbonEvent = RibbonEvent,
RibbonBirthday = RibbonBirthday,
RibbonSpecial = RibbonSpecial,
RibbonSouvenir = RibbonSouvenir,
RibbonWishing = RibbonWishing,
RibbonChampionBattle = RibbonChampionBattle,
RibbonChampionRegional = RibbonChampionRegional,
RibbonChampionNational = RibbonChampionNational,
RibbonChampionWorld = RibbonChampionWorld,
HasContestMemoryRibbon = HasContestMemoryRibbon,
HasBattleMemoryRibbon = HasBattleMemoryRibbon,
RibbonChampionG6Hoenn = RibbonChampionG6Hoenn,
RibbonContestStar = RibbonContestStar,
RibbonMasterCoolness = RibbonMasterCoolness,
RibbonMasterBeauty = RibbonMasterBeauty,
RibbonMasterCuteness = RibbonMasterCuteness,
RibbonMasterCleverness = RibbonMasterCleverness,
RibbonMasterToughness = RibbonMasterToughness,
RibbonChampionAlola = RibbonChampionAlola,
RibbonBattleRoyale = RibbonBattleRoyale,
RibbonBattleTreeGreat = RibbonBattleTreeGreat,
RibbonBattleTreeMaster = RibbonBattleTreeMaster,
RibbonChampionGalar = RibbonChampionGalar,
RibbonTowerMaster = RibbonTowerMaster,
RibbonMasterRank = RibbonMasterRank,
RibbonMarkLunchtime = RibbonMarkLunchtime,
RibbonMarkSleepyTime = RibbonMarkSleepyTime,
RibbonMarkDusk = RibbonMarkDusk,
RibbonMarkDawn = RibbonMarkDawn,
RibbonMarkCloudy = RibbonMarkCloudy,
RibbonMarkRainy = RibbonMarkRainy,
RibbonMarkStormy = RibbonMarkStormy,
RibbonMarkSnowy = RibbonMarkSnowy,
RibbonMarkBlizzard = RibbonMarkBlizzard,
RibbonMarkDry = RibbonMarkDry,
RibbonMarkSandstorm = RibbonMarkSandstorm,
RibbonCountMemoryContest = RibbonCountMemoryContest,
RibbonCountMemoryBattle = RibbonCountMemoryBattle,
RibbonMarkMisty = RibbonMarkMisty,
RibbonMarkDestiny = RibbonMarkDestiny,
RibbonMarkFishing = RibbonMarkFishing,
RibbonMarkCurry = RibbonMarkCurry,
RibbonMarkUncommon = RibbonMarkUncommon,
RibbonMarkRare = RibbonMarkRare,
RibbonMarkRowdy = RibbonMarkRowdy,
RibbonMarkAbsentMinded = RibbonMarkAbsentMinded,
RibbonMarkJittery = RibbonMarkJittery,
RibbonMarkExcited = RibbonMarkExcited,
RibbonMarkCharismatic = RibbonMarkCharismatic,
RibbonMarkCalmness = RibbonMarkCalmness,
RibbonMarkIntense = RibbonMarkIntense,
RibbonMarkZonedOut = RibbonMarkZonedOut,
RibbonMarkJoyful = RibbonMarkJoyful,
RibbonMarkAngry = RibbonMarkAngry,
RibbonMarkSmiley = RibbonMarkSmiley,
RibbonMarkTeary = RibbonMarkTeary,
RibbonMarkUpbeat = RibbonMarkUpbeat,
RibbonMarkPeeved = RibbonMarkPeeved,
RibbonMarkIntellectual = RibbonMarkIntellectual,
RibbonMarkFerocious = RibbonMarkFerocious,
RibbonMarkCrafty = RibbonMarkCrafty,
RibbonMarkScowling = RibbonMarkScowling,
RibbonMarkKindly = RibbonMarkKindly,
RibbonMarkFlustered = RibbonMarkFlustered,
RibbonMarkPumpedUp = RibbonMarkPumpedUp,
RibbonMarkZeroEnergy = RibbonMarkZeroEnergy,
RibbonMarkPrideful = RibbonMarkPrideful,
RibbonMarkUnsure = RibbonMarkUnsure,
RibbonMarkHumble = RibbonMarkHumble,
RibbonMarkThorny = RibbonMarkThorny,
RibbonMarkVigor = RibbonMarkVigor,
RibbonMarkSlump = RibbonMarkSlump,
RibbonHisui = RibbonHisui,
RibbonTwinklingStar = RibbonTwinklingStar,
AffixedRibbon = AffixedRibbon,
HyperTrainFlags = HyperTrainFlags,
BattleVersion = BattleVersion,
PKRS_Days = PKRS_Days,
PKRS_Strain = PKRS_Strain,
HeightScalar = HeightScalar,
WeightScalar = WeightScalar,
IsFavorite = IsFavorite,
MarkValue = MarkValue,
};
Nickname_Trash.CopyTo(pk.Nickname_Trash);
OT_Trash.CopyTo(pk.OT_Trash);
HT_Trash.CopyTo(pk.HT_Trash);
pk.SanitizeImport();
pk.ResetMoves();
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
}

View file

@ -24,6 +24,11 @@ public static class EntityConverter
/// </summary>
public static IEntityRejuvenator RejuvenatorHOME { get; set; } = new LegalityRejuvenator();
/// <summary>
/// Responsible for converting a <see cref="PKM"/> to a <see cref="PKH"/> for HOME.
/// </summary>
public static IHomeStorage HOME { get; set; } = new HomeStorageFacade();
/// <summary>
/// Checks if the input <see cref="PKM"/> file is capable of being converted to the desired format.
/// </summary>
@ -121,12 +126,6 @@ public static class EntityConverter
PK4 pk4 when destType == typeof(BK4) => pk4.ConvertToBK4(),
PK4 pk4 when destType == typeof(RK4) => pk4.ConvertToRK4(),
PB8 pb8 when destType == typeof(PK8) => pb8.ConvertToPK8(),
PK8 pk8 when destType == typeof(PB8) => pk8.ConvertToPB8(),
G8PKM pk8 when destType == typeof(PA8) => pk8.ConvertToPA8(),
PA8 pa8 when destType == typeof(PK8) => pa8.ConvertToPK8(),
PA8 pa8 when destType == typeof(PB8) => pa8.ConvertToPB8(),
// Sequential
PK1 pk1 => pk1.ConvertToPK2(),
PK2 pk2 => pk2.ConvertToPK1(),
@ -135,11 +134,6 @@ public static class EntityConverter
PK5 pk5 => pk5.ConvertToPK6(),
PK6 pk6 => pk6.ConvertToPK7(),
PK7 pk7 => pk7.ConvertToPK8(),
PB7 pb7 => pb7.ConvertToPK8(),
PK8 pk8 => pk8.ConvertToPK9(),
PB8 pb8 => pb8.ConvertToPK9(),
PA8 pa8 => pa8.ConvertToPK9(),
// Side-Formats back to Mainline
SK2 sk2 => sk2.ConvertToPK2(),
@ -148,12 +142,17 @@ public static class EntityConverter
BK4 bk4 => bk4.ConvertToPK4(),
RK4 rk4 => rk4.ConvertToPK4(),
_ => InvalidTransfer(out result, NoTransferRoute),
_ => GetFinalResult(pk, destType, ref result),
};
private static PKM? InvalidTransfer(out EntityConverterResult result, EntityConverterResult value)
private static PKM? GetFinalResult(PKM pk, Type destType, ref EntityConverterResult result)
{
result = value;
// Every format can eventually feed into HOME. Don't bother checking current type.
var type = PKH.GetType(destType);
if (type is not HomeGameDataFormat.None)
return HOME.GetEntity(pk).ConvertToPKM(type);
result = NoTransferRoute;
return null;
}

View file

@ -117,7 +117,7 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE
public override ushort MaxSpeciesID => Legal.MaxSpeciesID_8b;
public override int MaxItemID => Legal.MaxItemID_8b;
public override int MaxBallID => Legal.MaxBallID_8b;
public override int MaxGameID => Legal.MaxGameID_8a;
public override int MaxGameID => Legal.MaxGameID_HOME;
public override int MaxAbilityID => Legal.MaxAbilityID_8b;
public bool HasFirstSaveFileExpansion => (Gem8Version)SaveRevision >= Gem8Version.V1_1;

View file

@ -101,7 +101,7 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray, ISaveFileRe
public override ushort MaxSpeciesID => Legal.MaxSpeciesID_8a;
public override int MaxItemID => Legal.MaxItemID_8a;
public override int MaxBallID => Legal.MaxBallID_8a;
public override int MaxGameID => Legal.MaxGameID_8a;
public override int MaxGameID => Legal.MaxGameID_HOME;
public override int MaxAbilityID => Legal.MaxAbilityID_8a;
#region Blocks

View file

@ -91,7 +91,7 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile
public override ushort MaxSpeciesID => Legal.MaxSpeciesID_9;
public override int MaxItemID => Legal.MaxItemID_9;
public override int MaxBallID => Legal.MaxBallID_9;
public override int MaxGameID => Legal.MaxGameID_9;
public override int MaxGameID => Legal.MaxGameID_HOME;
public override int MaxAbilityID => Legal.MaxAbilityID_9;
private void Initialize()

View file

@ -13,7 +13,7 @@ public static class HomeTests
{
var folder = TestUtil.GetRepoPath();
var path = Path.Combine(folder, "TestData");
return Directory.EnumerateFiles(path, "*.eh1", SearchOption.TopDirectoryOnly);
return Directory.EnumerateFiles(path, "*.eh2", SearchOption.TopDirectoryOnly);
}
[Fact]
@ -28,19 +28,20 @@ public static class HomeTests
var chk = HomeCrypto.GetChecksum1(data);
oldCHK.Should().Be(chk);
bool encrypted = HomeCrypto.GetIsEncrypted1(data);
var version = ReadUInt16LittleEndian(data);
bool encrypted = HomeCrypto.GetIsEncrypted(data, version);
encrypted.Should().BeTrue();
var ph1 = new PKH(data);
ph1.DataVersion.Should().Be(1);
HomeCrypto.IsKnownVersion(ph1.DataVersion).Should().BeTrue();
var decrypted = HomeCrypto.Crypt1(data);
var decrypted = HomeCrypto.Crypt(data);
decrypted.Length.Should().Be(data.Length);
decrypted.Length.Should().Be(ph1.Data.Length);
for (int i = 0; i < decrypted.Length; i++)
decrypted[i].Should().Be(ph1.Data[i]);
bool check = HomeCrypto.GetIsEncrypted1(decrypted);
bool check = HomeCrypto.GetIsEncrypted(decrypted, version);
check.Should().BeFalse();
ph1.Clone().Should().NotBeNull();