PKHeX/PKHeX.Core/Legality/Verifiers/GenderVerifier.cs
Kurt d4bbb6dd02 Misc tweaks
Ball: all ball IDs are in a sequence +1'd. No need to have an array when we can just increment within the range. Ez removal of static constructor and allocation, and better iteration (and skips index 0!)
Disallow E/FR/LG item deposits of anything besides general pouch (R/S is like G/S/C, any pouch). Confirmed via testing in-game and matches Bulbapedia's testing.
Disallow Gen2 held item being an HM; no longer considered valid as a tradeback catch rate value. Oops that HMs were "allowed" for so long!
Encode Gen2 held items to bitflag array to not need to compute the merged array. Relocate duplicated logic to a single location in ItemConverter.
Fix gender-changing marill edge case comparing the wrong ratio
2025-08-08 23:43:22 -05:00

92 lines
3.4 KiB
C#

using static PKHeX.Core.LegalityCheckResultCode;
namespace PKHeX.Core;
/// <summary>
/// Verifies the <see cref="PKM.Gender"/>.
/// </summary>
public sealed class GenderVerifier : Verifier
{
protected override CheckIdentifier Identifier => CheckIdentifier.Gender;
public override void Verify(LegalityAnalysis data)
{
var pk = data.Entity;
var pi = data.PersonalInfo;
var gender = pk.Gender;
if (pi.Genderless != (gender == 2))
{
// D/P/Pt & HG/SS Shedinja glitch -- only generation 4 spawns
bool ignore = pk is { Format: 4, Species: (int)Species.Shedinja } && pk.MetLevel != pk.CurrentLevel;
if (!ignore)
data.AddLine(GetInvalid(GenderInvalidNone));
return;
}
// Check for PID relationship to Gender & Nature if applicable
var gen = data.Info.Generation;
if (gen is 3 or 4 or 5)
{
// Gender-PID & Nature-PID relationship check
var result = IsValidGenderPID(data) ? GetValid(PIDGenderMatch) : GetInvalid(PIDGenderMismatch);
data.AddLine(result);
if (gen != 5)
VerifyNaturePID(data);
return;
}
// Check fixed gender cases
if ((pi.OnlyFemale && gender != 1) || (pi.OnlyMale && gender != 0))
data.AddLine(GetInvalid(GenderInvalidNone));
}
private static void VerifyNaturePID(LegalityAnalysis data)
{
var pk = data.Entity;
var result = GetExpectedNature(pk) == pk.Nature
? GetValid(CheckIdentifier.Nature, PIDNatureMatch)
: GetInvalid(CheckIdentifier.Nature, PIDNatureMismatch);
data.AddLine(result);
}
private static Nature GetExpectedNature(PKM pk) => (Nature)(pk.EncryptionConstant % 25);
private static bool IsValidGenderPID(LegalityAnalysis data)
{
var pk = data.Entity;
bool genderValid = pk.IsGenderValid();
if (!genderValid)
return IsValidGenderMismatch(pk);
// Check for mixed->fixed gender incompatibility by checking the gender of the original species
if (SpeciesCategory.IsFixedGenderFromDual(pk.Species))
return IsValidFixedGenderFromBiGender(pk, data.EncounterMatch.Species);
return true;
}
private static bool IsValidFixedGenderFromBiGender(PKM pk, ushort originalSpecies)
{
var current = pk.Gender;
if (current == 2) // Shedinja, genderless
return true;
var gender = EntityGender.GetFromPID(originalSpecies, pk.EncryptionConstant);
return gender == current;
}
/// <summary>
/// Checks the un-evolved species' gender ratio instead of the current species.
/// </summary>
private static bool IsValidGenderMismatch(PKM pk) => pk.Species switch
{
// Shedinja evolution gender glitch (doesn't set as Genderless): should match original Gender
(int) Species.Shedinja when pk.Format == 4 => pk.Gender == EntityGender.GetFromPIDAndRatio(pk.EncryptionConstant, EntityGender.HH), // 1:1 (Nincada)
// Azurill gender changing: Different gender ratios, will "change" genders if evolved in games where PID-Gender is still coupled (<= Gen5).
(int) Species.Marill or (int) Species.Azumarill when pk.Format >= 6 => pk.Gender == 1 && (byte)pk.EncryptionConstant is >= EntityGender.HH and < EntityGender.MF, // 3F:1M (Azurill)
_ => false,
};
}