Sunset PKX -> Latest

Relocate gender ratio info to EntityGender
farewell PKX; once a behemoth catch-all of >4000 lines, now no more. 😢
This commit is contained in:
Kurt
2025-04-13 11:42:31 -05:00
parent 57060cbea5
commit fbfa28a0cf
17 changed files with 102 additions and 56 deletions

View File

@@ -231,7 +231,7 @@ private static int GetFlawedBitCount(ReadOnlySpan<int> ivs, int bitValue)
/// <param name="type">Hidden Power Type</param>
/// <param name="ivs">Individual Values (H/A/B/S/C/D)</param>
/// <param name="context">Generation specific format</param>
public static void SetIVs(int type, Span<int> ivs, EntityContext context = PKX.Context)
public static void SetIVs(int type, Span<int> ivs, EntityContext context = Latest.Context)
{
if (context.Generation() <= 2)
{

View File

@@ -38,12 +38,12 @@ public static class Breeding
};
/// <summary>
/// Checks if the species <see cref="ec"/> is valid for the <see cref="gender"/> if originated from Gen3/4 daycare eggs.
/// Checks if the species <see cref="encryptionConstant"/> is valid for the <see cref="gender"/> if originated from Gen3/4 daycare eggs.
/// </summary>
/// <param name="ec">Encryption Constant</param>
/// <param name="encryptionConstant">Encryption Constant</param>
/// <param name="gender">Gender</param>
/// <returns>True if valid</returns>
public static bool IsValidSpeciesBit34(uint ec, byte gender) => gender != (ec & 0x8000) >> 15; // 1 = Male, 0 = Female, valid if different from Gender value.
public static bool IsValidSpeciesBit34(uint encryptionConstant, byte gender) => gender != (encryptionConstant & 0x8000) >> 15; // 1 = Male, 0 = Female, valid if different from Gender value.
/// <summary>
/// Checks if the <see cref="species"/> can be born with inherited moves from the parents.
@@ -52,8 +52,7 @@ public static class Breeding
/// <returns>True if it can inherit moves, false if cannot.</returns>
internal static bool GetCanInheritMoves(ushort species)
{
var pi = PKX.GetGenderDetail(species);
if (pi is { Genderless: false, OnlyMale: false })
if (EntityGender.IsFemaleOrDualGender(species))
return true;
if (IsMixedGenderBreed(species))
return true;

View File

@@ -11,7 +11,7 @@ public static class MysteryGiftVerifier
private static Dictionary<int, MysteryGiftRestriction>?[] Get()
{
var s = new Dictionary<int, MysteryGiftRestriction>?[PKX.Generation + 1];
var s = new Dictionary<int, MysteryGiftRestriction>?[Latest.Generation + 1];
for (byte i = 3; i < s.Length; i++)
s[i] = GetRestriction(i);
return s;

View File

@@ -36,7 +36,7 @@ private static byte GetGenderRatio(ushort species)
{
return species <= Legal.MaxSpeciesID_4
? PersonalTable.HGSS[species].Gender
: PKX.GetGenderRatio(species); // fallback (don't bother trying to devolve to Gen1-4 encounter species)
: EntityGender.GetGenderRatio(species); // fallback (don't bother trying to devolve to Gen1-4 encounter species)
}
private static bool IsAzurillMale(uint pid) => pid is >= 0xC8 and <= 0xE0;

View File

@@ -130,7 +130,7 @@ private static void VerifyEC100(LegalityAnalysis data, ushort encSpecies)
return; // Evolved, don't need to calculate the final evolution for the verbose report.
// Indicate the evolution for the user.
const EntityContext mostRecent = PKX.Context; // latest ec100 form here
const EntityContext mostRecent = Latest.Context; // latest ec100 form here
uint evoVal = pk.EncryptionConstant % 100;
bool rare = evoVal == 0;
var (species, form) = EvolutionRestrictions.GetEvolvedSpeciesFormEC100(encSpecies, rare);

View File

@@ -26,7 +26,7 @@ public static class EntityFileExtension
/// </summary>
/// <param name="maxGeneration">Maximum Generation to permit</param>
/// <returns>Valid <see cref="PKM"/> file extensions.</returns>
public static string[] GetExtensions(int maxGeneration = PKX.Generation)
public static string[] GetExtensions(int maxGeneration = Latest.Generation)
{
int min = maxGeneration is <= 2 or >= 7 ? 1 : 3;
int size = maxGeneration - min + 1 + CountExtra;

View File

@@ -2,6 +2,9 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for interacting with Entity Gender
/// </summary>
public static class EntityGender
{
public const byte Male = 0;
@@ -39,7 +42,7 @@ public static byte GetFromString(ReadOnlySpan<char> s)
/// <remarks>This method should only be used for Generations 3-5 origin.</remarks>
public static byte GetFromPID(ushort species, uint pid)
{
var gt = PKX.GetGenderRatio(species);
var gt = GetGenderRatio(species);
return GetFromPIDAndRatio(pid, gt);
}
@@ -50,4 +53,65 @@ public static byte GetFromPID(ushort species, uint pid)
PersonalInfo.RatioMagicMale => Male,
_ => (pid & 0xFF) < gr ? Female : Male,
};
/// <summary>
/// Checks if the species (base form) can be female.
/// </summary>
public static bool IsFemaleOrDualGender(ushort species) => GetGenderRatio(species) is not (OM or NG);
/// <summary>
/// Gets the magic gender value used in past generations (3-8) for logic checks.
/// </summary>
public static byte GetGenderRatio(ushort species)
{
var arr = GenderRatios;
if (species < arr.Length)
return arr[species];
return NG;
}
// 256/100% gender ratios
private const byte OM = 000; // 100% Male
private const byte VM = 031; // 7:1 Male
private const byte MM = 063; // 3:1 Male
private const byte HH = 127; // 1:1 50%
private const byte MF = 191; // 3:1 Mostly-Female
private const byte VF = 225; // 7:1 Female
private const byte OF = 254; // 100% Female
private const byte NG = 255; // Only-Genderless
private static ReadOnlySpan<byte> GenderRatios =>
[
NG, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, OF, OF, OF, OM, OM, OM, MF, MF, MF, MF, MF,
MF, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, MM, MM, HH, HH, HH, MM, MM, MM, MM, MM, MM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH,
HH, NG, NG, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, NG, HH, HH, HH, HH, OM, OM, HH, HH, HH, HH, HH, OF, HH, OF, HH, HH, HH, HH,
NG, NG, HH, HH, OF, MM, MM, HH, OM, HH, HH, HH, NG, VM, VM, VM, VM, NG, VM, VM, VM, VM, VM, VM, NG, NG, NG, HH, HH, HH, NG, NG, VM, VM, VM, VM, VM, VM, VM, VM,
VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, MF, MF, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, VM, VM, HH, HH,
// 200
HH, NG, HH, HH, HH, HH, HH, HH, HH, MF, MF, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, MF, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, HH, HH, OM, OM, OF, MM,
MM, OF, OF, NG, NG, NG, HH, HH, HH, NG, NG, NG, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH,
HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, HH, HH, HH, MM, MM, MF, HH, MF, MF, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, OM, OF, HH, HH, HH, HH, HH,
HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, NG, HH, HH, HH, HH, NG, NG, VM, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH,
HH, HH, HH, HH, HH, HH, HH, HH, HH, VM, MF, HH, HH, HH, NG, NG, NG, NG, NG, NG, OF, OM, NG, NG, NG, NG, NG, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, HH, HH, HH,
// 400
HH, HH, HH, HH, HH, HH, HH, HH, VM, VM, VM, VM, HH, OF, OM, VM, OF, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, MF, MF, HH, HH, HH, NG, NG, HH, HH,
OF, HH, HH, HH, HH, HH, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, HH, HH, HH, MM, MM, VM, HH, VM, VM, HH, HH, NG, OM, HH, HH, OF, NG,
NG, NG, NG, NG, NG, HH, NG, NG, OF, NG, NG, NG, NG, NG, NG, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, VM, VM, VM, VM, VM, VM, HH, HH, HH,
HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, MM, MM, MM, HH, HH, HH, OM, OM, HH, HH, HH, HH, HH, HH, HH, HH, OF, OF, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH,
HH, HH, HH, HH, VM, VM, VM, VM, HH, HH, VM, VM, MF, MF, MF, MF, MF, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG,
// 600
NG, NG, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, HH, HH, HH, HH, HH, HH, NG, NG, HH, HH, HH, OM, OM, OF, OF, HH, HH, HH, HH, HH, HH, HH, NG, NG,
NG, OM, OM, NG, NG, OM, NG, NG, NG, NG, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, VF, VF, OF, OF, OF, HH, HH, HH, HH, HH, HH, OM, HH,
HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, VM, VM, VM, VM, VM, HH, HH, NG, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, NG, NG, NG,
NG, NG, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, MF, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, VM, OF, HH,
HH, OF, OF, OF, MF, HH, HH, HH, HH, HH, HH, HH, NG, NG, NG, HH, HH, HH, HH, HH, HH, NG, HH, HH, HH, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG,
// 800
NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH,
HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, NG, OF, OF, OF, OM, OM, OM, HH, HH, MF, HH, HH, HH, OF, OF, NG, HH, HH, HH, HH, HH, OM, HH, HH, HH,
NG, NG, NG, NG, HH, HH, HH, HH, NG, NG, NG, VM, VM, NG, NG, NG, NG, NG, NG, HH, HH, HH, OM, HH, HH, OF, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, OM, HH, HH, HH,
HH, HH, HH, HH, NG, NG, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, OF, OF, OF,
HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, HH, HH, HH, NG,
// 1000
NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, NG, HH, NG, NG, OM, OM, OM, OF, HH, HH, NG, NG, NG, NG, NG,
];
}

View File

@@ -34,7 +34,7 @@ public static class Language
/// </summary>
/// <param name="generation">Generation to check.</param>
/// <returns>Available languages for the given generation.</returns>
public static ReadOnlySpan<byte> GetAvailableGameLanguages(byte generation = PKX.Generation) => generation switch
public static ReadOnlySpan<byte> GetAvailableGameLanguages(byte generation = Latest.Generation) => generation switch
{
1 => Languages_3, // No KOR
2 => Languages_GB,

View File

@@ -0,0 +1,11 @@
namespace PKHeX.Core;
/// <summary>
/// Latest/most-recent game values for <see cref="PKM"/> data providing and manipulation.
/// </summary>
public static class Latest
{
public const EntityContext Context = EntityContext.Gen9;
public const GameVersion Version = GameVersion.SL;
public const byte Generation = 9;
}

View File

@@ -1,26 +0,0 @@
namespace PKHeX.Core;
/// <summary>
/// Latest game values for <see cref="PKM"/> data providing and manipulation.
/// </summary>
public static class PKX
{
public const EntityContext Context = EntityContext.Gen9;
public const GameVersion Version = GameVersion.SL;
public const byte Generation = 9;
// We need to expose gender info for backwards compatibility / arbitrary game logic.
/// <summary>
/// Gets the gender details for the requested species (base form).
/// </summary>
/// <returns>Gender details for the personal info.</returns>
internal static IGenderDetail GetGenderDetail(ushort species)
=> PersonalTable.SV[species];
/// <summary>
/// Gets the magic gender value used in past generations (3-8) for logic checks.
/// </summary>
internal static byte GetGenderRatio(ushort species)
=> PersonalTable.SV[species].Gender;
}

View File

@@ -8,8 +8,6 @@ namespace PKHeX.Core;
/// </summary>
public static class SpeciesName
{
private const int LatestGeneration = PKX.Generation;
/// <summary>
/// Species name lists indexed by the <see cref="LanguageID"/> value.
/// </summary>
@@ -246,7 +244,7 @@ private static string GetEggName1234(ushort species, int language, byte generati
/// <param name="nickname">Current name</param>
/// <param name="generation">Generation specific formatting option</param>
/// <returns>True if it does not match any language name, False if not nicknamed</returns>
public static bool IsNicknamedAnyLanguage(ushort species, ReadOnlySpan<char> nickname, byte generation = LatestGeneration)
public static bool IsNicknamedAnyLanguage(ushort species, ReadOnlySpan<char> nickname, byte generation = Latest.Generation)
{
var langs = Language.GetAvailableGameLanguages(generation);
foreach (var language in langs)
@@ -265,7 +263,7 @@ public static bool IsNicknamedAnyLanguage(ushort species, ReadOnlySpan<char> nic
/// <param name="language">Language ID of the Pokémon</param>
/// <param name="generation">Generation specific formatting option</param>
/// <returns>True if it does not match the language name, False if not nicknamed (matches).</returns>
public static bool IsNicknamed(ushort species, ReadOnlySpan<char> nickname, int language, byte generation = LatestGeneration)
public static bool IsNicknamed(ushort species, ReadOnlySpan<char> nickname, int language, byte generation = Latest.Generation)
{
var expect = GetSpeciesNameGeneration(species, language, generation);
return !nickname.SequenceEqual(expect);
@@ -279,7 +277,7 @@ public static bool IsNicknamed(ushort species, ReadOnlySpan<char> nickname, int
/// <param name="nickname">Current name</param>
/// <param name="generation">Generation specific formatting option</param>
/// <returns>Language ID if it does not match any language name, -1 if no matches</returns>
public static int GetSpeciesNameLanguage(ushort species, int priorityLanguage, ReadOnlySpan<char> nickname, byte generation = LatestGeneration)
public static int GetSpeciesNameLanguage(ushort species, int priorityLanguage, ReadOnlySpan<char> nickname, byte generation = Latest.Generation)
{
var langs = Language.GetAvailableGameLanguages(generation);
var priorityIndex = langs.IndexOf((byte)priorityLanguage);
@@ -300,7 +298,7 @@ public static int GetSpeciesNameLanguage(ushort species, int priorityLanguage, R
/// <param name="nickname">Current name</param>
/// <param name="generation">Generation specific formatting option</param>
/// <returns>Language ID if it does not match any language name, -1 if no matches</returns>
public static int GetSpeciesNameLanguage(ushort species, ReadOnlySpan<char> nickname, byte generation = LatestGeneration)
public static int GetSpeciesNameLanguage(ushort species, ReadOnlySpan<char> nickname, byte generation = Latest.Generation)
{
var langs = Language.GetAvailableGameLanguages(generation);
return GetSpeciesNameLanguage(species, nickname, generation, langs);
@@ -329,7 +327,7 @@ public static bool TryGetSpecies(ReadOnlySpan<char> speciesName, int language, o
return SpeciesDict[language].TryGetValue(speciesName, out species);
}
public static bool TryGetSpeciesAnyLanguage(ReadOnlySpan<char> speciesName, out ushort species, byte generation = LatestGeneration)
public static bool TryGetSpeciesAnyLanguage(ReadOnlySpan<char> speciesName, out ushort species, byte generation = Latest.Generation)
{
foreach (var language in Language.GetAvailableGameLanguages(generation))
{
@@ -340,7 +338,7 @@ public static bool TryGetSpeciesAnyLanguage(ReadOnlySpan<char> speciesName, out
return false;
}
public static bool TryGetSpeciesAnyLanguageCaseInsensitive(ReadOnlySpan<char> speciesName, out ushort species, byte generation = LatestGeneration)
public static bool TryGetSpeciesAnyLanguageCaseInsensitive(ReadOnlySpan<char> speciesName, out ushort species, byte generation = Latest.Generation)
{
Span<char> lowercase = stackalloc char[speciesName.Length];
speciesName.ToLowerInvariant(lowercase);

View File

@@ -19,10 +19,10 @@ public sealed record SimpleTrainerInfo : ITrainerInfo, IRegionOrigin
public byte Country { get; set; } = 49; // USA
public GameVersion Version { get; }
public byte Generation { get; init; } = PKX.Generation;
public EntityContext Context { get; init; } = PKX.Context;
public byte Generation { get; init; } = Latest.Generation;
public EntityContext Context { get; init; } = Latest.Context;
public SimpleTrainerInfo(GameVersion game = PKX.Version)
public SimpleTrainerInfo(GameVersion game = Latest.Version)
{
Version = game;
Context = Version.GetContext();

View File

@@ -8,21 +8,21 @@ public static class TypeSpriteUtil
{
private static Bitmap? Get(string name) => Resources.ResourceManager.GetObject(name) as Bitmap;
public static Bitmap? GetTypeSpriteWide(byte type, byte generation = PKX.Generation)
public static Bitmap? GetTypeSpriteWide(byte type, byte generation = Latest.Generation)
{
if (generation <= 2)
type = (byte)((MoveType)type).GetMoveTypeGeneration(generation);
return Get($"type_wide_{type:00}");
}
public static Bitmap? GetTypeSpriteIcon(byte type, byte generation = PKX.Generation)
public static Bitmap? GetTypeSpriteIcon(byte type, byte generation = Latest.Generation)
{
if (generation <= 2)
type = (byte)((MoveType)type).GetMoveTypeGeneration(generation);
return Get($"type_icon_{type:00}");
}
public static Bitmap? GetTypeSpriteIconSmall(byte type, byte generation = PKX.Generation)
public static Bitmap? GetTypeSpriteIconSmall(byte type, byte generation = Latest.Generation)
{
if (generation <= 2)
type = (byte)((MoveType)type).GetMoveTypeGeneration(generation);

View File

@@ -22,7 +22,7 @@ public static class SpriteName
/// <summary>
/// Gets the resource name of the Pokémon sprite.
/// </summary>
public static string GetResourceStringSprite(ushort species, byte form, byte gender, uint formarg, EntityContext context = PKX.Context, bool shiny = false)
public static string GetResourceStringSprite(ushort species, byte form, byte gender, uint formarg, EntityContext context = Latest.Context, bool shiny = false)
{
if (SpeciesDefaultFormSprite.Contains(species)) // Species who show their default sprite regardless of Form
form = 0;

View File

@@ -156,7 +156,7 @@ public uint RecentlyLoadedMaxCount
}
// Don't let invalid values slip into the startup version.
private GameVersion _defaultSaveVersion = PKX.Version;
private GameVersion _defaultSaveVersion = Latest.Version;
private string _language = WinFormsUtil.GetCultureLanguage();
[Browsable(false)]

View File

@@ -38,7 +38,7 @@ public partial class SAV_Database : Form
private const int RES_MAX = GridWidth * GridHeight;
private readonly string Counter;
private readonly string Viewed;
private const int MAXFORMAT = PKX.Generation;
private const int MAXFORMAT = Latest.Generation;
private readonly SummaryPreviewer ShowSet = new();
private CancellationTokenSource cts = new();

View File

@@ -117,7 +117,7 @@ public SAV_MysteryGiftDB(PKMEditor tabs, SAVEditor sav)
private const int RES_MAX = GridWidth * GridHeight;
private readonly string Counter;
private readonly string Viewed;
private const int MAXFORMAT = PKX.Generation;
private const int MAXFORMAT = Latest.Generation;
private bool GetShiftedIndex(ref int index)
{