mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-09-28 10:56:56 -05:00
Extract CuteCharm4 api
This commit is contained in:
@@ -182,7 +182,7 @@ public bool IsCompatible(PIDType type, PKM pk)
|
||||
if (type is PIDType.ChainShiny)
|
||||
return pk.IsShiny && CanUseRadar;
|
||||
if (type is PIDType.CuteCharm)
|
||||
return MethodFinder.IsCuteCharm4Valid(this, pk);
|
||||
return CuteCharm4.IsValid(this, pk);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -394,7 +394,7 @@ public bool IsCompatible(PIDType type, PKM pk)
|
||||
if (type is PIDType.Method_1)
|
||||
return true;
|
||||
if (type is PIDType.CuteCharm)
|
||||
return MethodFinder.IsCuteCharm4Valid(this, pk);
|
||||
return CuteCharm4.IsValid(this, pk);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ public bool IsCompatible(PIDType type, PKM pk)
|
||||
|
||||
// Pokewalker can sometimes be confused with CuteCharm due to the PID creation routine. Double check if it is okay.
|
||||
if (type is PIDType.CuteCharm)
|
||||
return MethodFinder.IsCuteCharm(pk, pk.EncryptionConstant) && MethodFinder.IsCuteCharm4Valid(this, pk);
|
||||
return CuteCharm4.IsCuteCharm(pk, pk.EncryptionConstant) && CuteCharm4.IsValid(this, pk);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public static class LeadFinder
|
||||
// There's a very-very rare chance that the PID-IV can be from Cute Charm too.
|
||||
// It may match Method 1, but since we early-return, we don't check for Cute Charm.
|
||||
// So, we check for Cute Charm here and try checking Cute Charm frames if it matches.
|
||||
if (MethodFinder.IsCuteCharm(pk, pk.EncryptionConstant))
|
||||
if (CuteCharm4.IsCuteCharm(pk, pk.EncryptionConstant))
|
||||
type = CuteCharm;
|
||||
}
|
||||
else if (type is Method_1)
|
||||
@@ -63,7 +63,7 @@ public static class LeadFinder
|
||||
// There's a very-very rare chance that the PID-IV can be from Cute Charm too.
|
||||
// It may match Method 1, but since we early-return, we don't check for Cute Charm.
|
||||
// So, we check for Cute Charm here and try checking Cute Charm frames if it matches.
|
||||
if (MethodFinder.IsCuteCharm(pk, pk.EncryptionConstant))
|
||||
if (CuteCharm4.IsCuteCharm(pk, pk.EncryptionConstant))
|
||||
type = CuteCharm;
|
||||
}
|
||||
if (type is CuteCharm)
|
||||
|
||||
130
PKHeX.Core/Legality/RNG/CuteCharm4.cs
Normal file
130
PKHeX.Core/Legality/RNG/CuteCharm4.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using static PKHeX.Core.Species;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// PID generating logic for Generation 4's <see cref="Ability.CuteCharm"/> mechanic.
|
||||
/// </summary>
|
||||
public static class CuteCharm4
|
||||
{
|
||||
private const uint NatureCount = 25;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the PID of a Male wild encounter when the player leads a Female Pokémon with Cute Charm.
|
||||
/// </summary>
|
||||
/// <param name="genderRatio">Gender ratio of the encounter species.</param>
|
||||
/// <param name="nature">Nature of the encounter Pokémon.</param>
|
||||
public static uint GetPIDMale(byte genderRatio, uint nature)
|
||||
{
|
||||
// pid >= ratio is male
|
||||
// get the lowest PID that will be male for Hardy (0)
|
||||
var basePID = NatureCount * ((genderRatio / NatureCount) + 1);
|
||||
// add the desired nature to the base PID
|
||||
return basePID + nature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the PID of a Female wild encounter when the player leads a Male Pokémon with Cute Charm.
|
||||
/// </summary>
|
||||
/// <param name="nature">Nature of the encounter Pokémon.</param>
|
||||
public static uint GetPIDFemale(uint nature) => nature;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Gender Ratio of the species. If the species is not in Gen 4, it will grab whatever its de-evolved species is.
|
||||
/// </summary>
|
||||
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)
|
||||
}
|
||||
|
||||
private static bool IsAzurillMale(uint pid) => pid is >= 0xC8 and <= 0xE0;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="pk"/> can have Cute Charm as its PID type. Special consideration for male Azurill encounters, which can change into female.
|
||||
/// </summary>
|
||||
/// <param name="enc">Encounter (species)</param>
|
||||
/// <param name="pk">Current entity state</param>
|
||||
/// <returns>True if the Cute Charm type is valid</returns>
|
||||
internal static bool IsValid<TEnc>(TEnc enc, PKM pk) where TEnc : ISpeciesForm
|
||||
{
|
||||
if (pk.Gender is not (0 or 1))
|
||||
return pk.Species == (ushort)Shedinja;
|
||||
if (pk.Species is not ((int)Marill or (int)Azumarill))
|
||||
return true;
|
||||
if (!IsAzurillMale(pk.EncryptionConstant)) // recognized as not Azurill
|
||||
return true;
|
||||
return enc.Species == (int)Azurill; // encounter must be male Azurill
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the PID can arise as a result of Cute Charm.
|
||||
/// </summary>
|
||||
public static bool IsCuteCharm(PKM pk, uint pid)
|
||||
{
|
||||
if (pid > 0xFF)
|
||||
return false;
|
||||
|
||||
var (species, gender) = GetGenderSpecies(pk, pid, pk.Species);
|
||||
|
||||
switch (gender)
|
||||
{
|
||||
// case 2: break; // can't cute charm a genderless pk
|
||||
case 0: // male
|
||||
var gr = GetGenderRatio(species);
|
||||
if (gr >= PersonalInfo.RatioMagicFemale) // no modification for PID
|
||||
break;
|
||||
var nature = pid % NatureCount;
|
||||
var expect = GetPIDMale(gr, nature);
|
||||
if (pid != expect)
|
||||
break;
|
||||
return true;
|
||||
case 1: // female
|
||||
if (pid >= NatureCount)
|
||||
break; // nope, this isn't a valid nature
|
||||
if (GetGenderRatio(species) >= PersonalInfo.RatioMagicFemale) // no modification for PID
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// There are some edge cases when the gender ratio changes across evolutions.
|
||||
/// </summary>
|
||||
private static (ushort Species, byte Gender) GetGenderSpecies(PKM pk, uint pid, ushort currentSpecies) => currentSpecies switch
|
||||
{
|
||||
// Nincada evo chain travels from M/F -> Genderless Shedinja
|
||||
(int)Shedinja => ((int)Nincada, EntityGender.GetFromPID((int)Nincada, pid)),
|
||||
|
||||
// These evolved species cannot be encountered with cute charm.
|
||||
// 100% fixed gender does not modify PID; override this with the encounter species for correct calculation.
|
||||
// We can assume the re-mapped species' [gender ratio] is what was encountered.
|
||||
(int)Wormadam => ((int)Burmy, 1),
|
||||
(int)Mothim => ((int)Burmy, 0),
|
||||
(int)Vespiquen => ((int)Combee, 1),
|
||||
(int)Gallade => ((int)Kirlia, 0),
|
||||
(int)Froslass => ((int)Snorunt, 1),
|
||||
// Azurill & Marill/Azumarill collision
|
||||
// Changed gender ratio (25% M -> 50% M) needs special treatment.
|
||||
// Double-check the encounter species with IsValid afterward.
|
||||
(int)Marill or (int)Azumarill when IsAzurillMale(pid) => ((int)Azurill, 0),
|
||||
|
||||
// Future evolutions
|
||||
_ => (GetSpeciesGen4(currentSpecies), pk.Gender),
|
||||
};
|
||||
|
||||
private static ushort GetSpeciesGen4(ushort species) => species switch
|
||||
{
|
||||
<= Legal.MaxSpeciesID_4 => species, // has a valid personal reference, all good
|
||||
(int)Sylveon => (int)Eevee,
|
||||
(int)MrRime => (int)MrMime,
|
||||
(int)Wyrdeer => (int)Stantler,
|
||||
(int)Kleavor => (int)Scyther,
|
||||
(int)Sneasler => (int)Sneasel,
|
||||
(int)Ursaluna => (int)Ursaring,
|
||||
(int)Annihilape => (int)Primeape,
|
||||
_ => species, // throw an exception? Hitting here is an invalid case.
|
||||
};
|
||||
}
|
||||
@@ -396,48 +396,12 @@ private static bool GetG5MGShinyMatch(PKM pk, uint pid, out PIDIV pidiv)
|
||||
|
||||
private static bool GetCuteCharmMatch(PKM pk, uint pid, out PIDIV pidiv)
|
||||
{
|
||||
if (!IsCuteCharm(pk, pid))
|
||||
if (!CuteCharm4.IsCuteCharm(pk, pid))
|
||||
return GetNonMatch(out pidiv);
|
||||
pidiv = PIDIV.CuteCharm;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsCuteCharm(PKM pk, uint pid)
|
||||
{
|
||||
if (pid > 0xFF)
|
||||
return false;
|
||||
|
||||
var (species, gender) = GetCuteCharmGenderSpecies(pk, pid, pk.Species);
|
||||
static byte getRatio(ushort species)
|
||||
{
|
||||
return species <= Legal.MaxSpeciesID_4
|
||||
? PersonalTable.HGSS[species].Gender
|
||||
: PKX.GetGenderRatio(species);
|
||||
}
|
||||
|
||||
const uint n = 25;
|
||||
switch (gender)
|
||||
{
|
||||
// case 2: break; // can't cute charm a genderless pk
|
||||
case 0: // male
|
||||
var gr = getRatio(species);
|
||||
if (gr >= PersonalInfo.RatioMagicFemale) // no modification for PID
|
||||
break;
|
||||
var rate = n * ((gr / n) + 1); // buffered
|
||||
var nature = pid % n;
|
||||
if (nature + rate != pid)
|
||||
break;
|
||||
return true;
|
||||
case 1: // female
|
||||
if (pid >= n)
|
||||
break; // nope, this isn't a valid nature
|
||||
if (getRatio(species) >= PersonalInfo.RatioMagicFemale) // no modification for PID
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool GetChainShinyMatch(Span<uint> seeds, ITrainerID32 pk, uint pid, uint iv1, uint iv2, out PIDIV pidiv)
|
||||
{
|
||||
// 13 shiny bits
|
||||
@@ -550,8 +514,8 @@ private static bool GetPokewalkerMatch(PKM pk, uint actualPID, out PIDIV pidiv)
|
||||
if (mid is not (0 or midMask))
|
||||
return GetNonMatch(out pidiv);
|
||||
|
||||
// Quirky Nature is not possible with the algorithm.
|
||||
var nature = actualPID % 25;
|
||||
// Quirky Nature is not possible with the algorithm.
|
||||
if (nature == 24)
|
||||
return GetNonMatch(out pidiv);
|
||||
|
||||
@@ -659,57 +623,6 @@ public static bool IsPokeSpotActivation(int slot, uint seed, out uint s)
|
||||
_ => false,
|
||||
};
|
||||
|
||||
internal static bool IsCuteCharm4Valid(ISpeciesForm enc, PKM pk)
|
||||
{
|
||||
if (pk.Gender is not (0 or 1))
|
||||
return pk.Species == (ushort)Species.Shedinja;
|
||||
if (pk.Species is not ((int)Species.Marill or (int)Species.Azumarill))
|
||||
return true;
|
||||
if (!IsCuteCharmAzurillMale(pk.PID)) // recognized as not Azurill
|
||||
return true;
|
||||
return enc.Species == (int)Species.Azurill; // encounter must be male Azurill
|
||||
}
|
||||
|
||||
private static bool IsCuteCharmAzurillMale(uint pid) => pid is >= 0xC8 and <= 0xE0;
|
||||
|
||||
/// <summary>
|
||||
/// There are some edge cases when the gender ratio changes across evolutions.
|
||||
/// </summary>
|
||||
private static (ushort Species, byte Gender) GetCuteCharmGenderSpecies(PKM pk, uint pid, ushort currentSpecies) => currentSpecies switch
|
||||
{
|
||||
// Nincada evo chain travels from M/F -> Genderless Shedinja
|
||||
(int)Species.Shedinja => ((int)Species.Nincada, EntityGender.GetFromPID((int)Species.Nincada, pid)),
|
||||
|
||||
// These evolved species cannot be encountered with cute charm.
|
||||
// 100% fixed gender does not modify PID; override this with the encounter species for correct calculation.
|
||||
// We can assume the re-mapped species' [gender ratio] is what was encountered.
|
||||
(int)Species.Wormadam => ((int)Species.Burmy, 1),
|
||||
(int)Species.Mothim => ((int)Species.Burmy, 0),
|
||||
(int)Species.Vespiquen => ((int)Species.Combee, 1),
|
||||
(int)Species.Gallade => ((int)Species.Kirlia, 0),
|
||||
(int)Species.Froslass => ((int)Species.Snorunt, 1),
|
||||
// Azurill & Marill/Azumarill collision
|
||||
// Changed gender ratio (25% M -> 50% M) needs special treatment.
|
||||
// Double-check the encounter species with IsCuteCharm4Valid afterward.
|
||||
(int)Species.Marill or (int)Species.Azumarill when IsCuteCharmAzurillMale(pid) => ((int)Species.Azurill, 0),
|
||||
|
||||
// Future evolutions
|
||||
_ => GetCuteCharmSpeciesGen4(currentSpecies, pk.Gender),
|
||||
};
|
||||
|
||||
private static (ushort Species, byte Gender) GetCuteCharmSpeciesGen4(ushort species, byte gender) => species switch
|
||||
{
|
||||
<= Legal.MaxSpeciesID_4 => (species, gender), // has a valid personal reference, all good
|
||||
(int)Species.Sylveon => ((int)Species.Eevee, gender),
|
||||
(int)Species.MrRime => ((int)Species.MrMime, gender),
|
||||
(int)Species.Wyrdeer => ((int)Species.Stantler, gender),
|
||||
(int)Species.Kleavor => ((int)Species.Scyther, gender),
|
||||
(int)Species.Sneasler => ((int)Species.Sneasel, gender),
|
||||
(int)Species.Ursaluna => ((int)Species.Ursaring, gender),
|
||||
(int)Species.Annihilape => ((int)Species.Primeape, gender),
|
||||
_ => (species, gender), // throw an exception? Hitting here is an invalid case.
|
||||
};
|
||||
|
||||
public static PIDIV GetPokeSpotSeedFirst(PKM pk, byte slot)
|
||||
{
|
||||
// Activate (rand % 3)
|
||||
|
||||
Reference in New Issue
Block a user