Handle Ability Patch changed abilities (#3024)

Handles conditions when Ability Patch has been used to modify the PKM's ability => hidden ability.

Per the description, it goes 1/2=>H, not the other way around.

Verify ability bit first, as mystery gift case handling skips the bit check logic :P
This commit is contained in:
Kurt 2020-10-10 12:30:57 -07:00 committed by GitHub
parent e6e3166f13
commit a0a8dd0f91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 34 deletions

View file

@ -64,6 +64,7 @@ namespace PKHeX.Core
public static string L_XWurmpleEvo_0 { get; set; } = "Wurmple Evolution: {0}";
public static string LAbilityCapsuleUsed { get; set; } = "Ability modified with Ability Capsule.";
public static string LAbilityPatchUsed { get; set; } = "Ability modified with Ability Patch.";
public static string LAbilityFlag { get; set; } = "Ability matches ability number.";
public static string LAbilityHiddenFail { get; set; } = "Hidden Ability mismatch for encounter type.";
public static string LAbilityHiddenUnavailable { get; set; } = "Hidden Ability not available.";

View file

@ -111,8 +111,15 @@ namespace PKHeX.Core
abil = ability_param;
abil <<= 1; // 1/2/4
if ((pk.AbilityNumber == 4) != (abil == 4))
var current = pk.AbilityNumber;
if (current == 4 && abil != 4)
{
// Defer for Ability Checks
}
else if (abil == 4)
{
return false;
}
switch (gender_ratio)
{

View file

@ -36,24 +36,34 @@ namespace PKHeX.Core
if (abilval < 0)
return GetInvalid(LAbilityUnexpected);
var enc = data.EncounterMatch;
var abilities = pi.Abilities;
int format = pkm.Format;
if (format >= 6)
{
// Check AbilityNumber is a single set bit
var num = pkm.AbilityNumber;
if (!(num != 0 && (num & (num - 1)) == 0)) // not [!zero, and power of 2]
return GetInvalid(LAbilityMismatchFlag);
// Check AbilityNumber points to ability
int an = num >> 1;
if (an >= abilities.Count || abilities[an] != ability)
return GetInvalid(LAbilityMismatchFlag);
}
if (format >= 8) // Ability Patch
{
if (pkm.AbilityNumber == 4 && CanAbilityPatch(format, abilities))
return GetValid(LAbilityPatchUsed);
}
var enc = data.EncounterMatch;
if (enc is MysteryGift g && g.Format >= 4)
return VerifyAbilityMG(data, g, abilities);
if (pkm.Format < 6)
if (format < 6)
return VerifyAbility345(data, enc, abilities, abilval);
// Check AbilityNumber is a single set bit
var num = pkm.AbilityNumber;
if (num == 0 || num == 0b101 || num == 0b110 || num == 0b011)
return GetInvalid(LAbilityMismatchFlag);
// Check AbilityNumber points to ability
int an = num >> 1;
if (an >= abilities.Count || abilities[an] != ability)
return GetInvalid(LAbilityMismatchFlag);
return VerifyAbility(data, abilities, abilval);
}
@ -225,9 +235,10 @@ namespace PKHeX.Core
private CheckResult VerifyAbilityPCD(LegalityAnalysis data, IReadOnlyList<int> abilities, PCD pcd)
{
var pkm = data.pkm;
if (pkm.Format >= 6)
var format = pkm.Format;
if (format >= 6)
{
if (abilities[0] == abilities[1])
if (CanAbilityCapsule(format, abilities))
{
// Gen3-5 transfer with same ability -> 1st ability that matches
if (pkm.AbilityNumber == 1)
@ -334,10 +345,8 @@ namespace PKHeX.Core
// Ability Capsule can change between 1/2
private static bool IsAbilityCapsuleModified(PKM pkm, IReadOnlyList<int> abilities, int EncounterAbility)
{
if (pkm.Format < 6)
return false; // Ability Capsule does not exist
if (abilities[0] == abilities[1])
return false; // Cannot alter ability index if it is the same as the other ability.
if (!CanAbilityCapsule(pkm.Format, abilities))
return false;
if (pkm.AbilityNumber == 4)
return false; // Cannot alter to hidden ability.
if (EncounterAbility == 4)
@ -345,6 +354,21 @@ namespace PKHeX.Core
return true;
}
private static bool CanAbilityCapsule(int format, IReadOnlyList<int> abilities)
{
if (format < 6) // Ability Capsule does not exist
return false;
return abilities[0] != abilities[1]; // Cannot alter ability index if it is the same as the other ability.
}
public static bool CanAbilityPatch(int format, IReadOnlyList<int> abilities)
{
if (format < 8) // Ability Patch does not exist
return false;
var h = abilities[2];
return h != abilities[0] || h != abilities[1]; // Cannot alter ability index if it is the same as the other abilities.
}
private static int GetEncounterFixedAbilityNumber(IEncounterable enc)
{
return enc switch

View file

@ -122,7 +122,7 @@ namespace PKHeX.Core
{
if (!Legal.Inherit_Safari.Contains(species))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4)
if (IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -130,7 +130,7 @@ namespace PKHeX.Core
{
if (!Legal.Inherit_Apricorn6.Contains(species))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4)
if (IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -138,13 +138,13 @@ namespace PKHeX.Core
{
if (!Legal.Inherit_Sport.Contains(species))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4)
if (IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
if (ball == Dream) // Dream Ball
{
if (pkm.AbilityNumber == 4 && Legal.Ban_DreamHidden.Contains(species))
if (Legal.Ban_DreamHidden.Contains(species) && IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility);
if (Legal.Inherit_Dream.Contains(species))
return GetValid(LBallSpeciesPass);
@ -160,7 +160,7 @@ namespace PKHeX.Core
{
if (Legal.Ban_Gen3Ball.Contains(species))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4 && Legal.Ban_Gen3BallHidden.Contains(pkm.SpecForm))
if (Legal.Ban_Gen3BallHidden.Contains(pkm.SpecForm) && IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -191,7 +191,7 @@ namespace PKHeX.Core
{
if (!(Legal.Inherit_Safari.Contains(species) || Legal.Inherit_SafariMale.Contains(species)))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4 && Legal.Ban_SafariBallHidden_7.Contains(species))
if (Legal.Ban_SafariBallHidden_7.Contains(species) && IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -199,7 +199,7 @@ namespace PKHeX.Core
{
if (!Legal.Inherit_Apricorn7.Contains(species))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4 && Legal.Ban_NoHidden7Apricorn.Contains(species | pkm.AltForm << 11)) // lineage is 3->2->origin
if (Legal.Ban_NoHidden7Apricorn.Contains(species | pkm.AltForm << 11) && IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -207,7 +207,7 @@ namespace PKHeX.Core
{
if (!Legal.Inherit_Sport.Contains(species))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4 && (species == (int)Species.Volbeat || species == (int)Species.Illumise)) // Volbeat/Illumise
if ((species == (int)Species.Volbeat || species == (int)Species.Illumise) && IsHiddenAndNotPossible(pkm)) // Volbeat/Illumise
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -232,9 +232,9 @@ namespace PKHeX.Core
if (ball == Beast)
{
if (species == (int)Species.Flabébé && pkm.AltForm == 3 && pkm.AbilityNumber == 4)
if (species == (int)Species.Flabébé && pkm.AltForm == 3 && IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility); // Can't obtain Flabébé-Blue with Hidden Ability in wild
if (species == (int)Species.Voltorb && pkm.AbilityNumber == 4)
if (species == (int)Species.Voltorb && IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility); // Can't obtain with Hidden Ability in wild (can only breed with Ditto)
if (((int)Species.Pikipek <= species && species <= (int)Species.Kommoo) || (Legal.AlolanCaptureOffspring.Contains(species) && !Legal.PastGenAlolanNativesUncapturable.Contains(species)))
return GetValid(LBallSpeciesPass);
@ -275,7 +275,7 @@ namespace PKHeX.Core
{
if (!(Legal.Inherit_Safari.Contains(species) || Legal.Inherit_SafariMale.Contains(species)))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4 && Legal.Ban_SafariBallHidden_7.Contains(species))
if (Legal.Ban_SafariBallHidden_7.Contains(species) && IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -283,7 +283,7 @@ namespace PKHeX.Core
{
if (!Legal.Inherit_Apricorn7.Contains(species))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4 && Legal.Ban_NoHidden8Apricorn.Contains(species | pkm.AltForm << 11)) // lineage is 3->2->origin
if (Legal.Ban_NoHidden8Apricorn.Contains(species | pkm.AltForm << 11) && IsHiddenAndNotPossible(pkm)) // lineage is 3->2->origin
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -291,7 +291,7 @@ namespace PKHeX.Core
{
if (!Legal.Inherit_Sport.Contains(species))
return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4 && (species == (int)Species.Volbeat || species == (int)Species.Illumise)) // Volbeat/Illumise
if ((species == (int)Species.Volbeat || species == (int)Species.Illumise) && IsHiddenAndNotPossible(pkm)) // Volbeat/Illumise
return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass);
}
@ -316,7 +316,7 @@ namespace PKHeX.Core
if (ball == Beast)
{
if (species == (int)Species.Flabébé && pkm.AltForm == 3 && pkm.AbilityNumber == 4)
if (species == (int)Species.Flabébé && pkm.AltForm == 3 && IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility); // Can't obtain Flabébé-Blue with Hidden Ability in wild
if (((int)Species.Pikipek <= species && species <= (int)Species.Kommoo) || (Legal.AlolanCaptureOffspring.Contains(species) && !Legal.PastGenAlolanNativesUncapturable.Contains(species)))
return GetValid(LBallSpeciesPass);
@ -337,6 +337,13 @@ namespace PKHeX.Core
return NONE;
}
private static bool IsHiddenAndNotPossible(PKM pkm)
{
if (pkm.AbilityNumber != 4)
return false;
return !AbilityVerifier.CanAbilityPatch(pkm.Format, pkm.PersonalInfo.Abilities);
}
public static bool IsGalarCatchAndBreed(int species)
{
if ((int)Species.Grookey <= species && species <= (int)Species.Inteleon) // starter

View file

@ -80,7 +80,7 @@ namespace PKHeX.Core
var pkm = data.pkm;
if (pkm.GO)
VerifyIVsGoTransfer(data);
else if (pkm.AbilityNumber == 4)
else if (pkm.AbilityNumber == 4 && !AbilityVerifier.CanAbilityPatch(pkm.Format, pkm.PersonalInfo.Abilities))
VerifyIVsFlawless(data, 2); // Chain of 10 yields 5% HA and 2 flawless IVs
}