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 L_XWurmpleEvo_0 { get; set; } = "Wurmple Evolution: {0}";
public static string LAbilityCapsuleUsed { get; set; } = "Ability modified with Ability Capsule."; 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 LAbilityFlag { get; set; } = "Ability matches ability number.";
public static string LAbilityHiddenFail { get; set; } = "Hidden Ability mismatch for encounter type."; public static string LAbilityHiddenFail { get; set; } = "Hidden Ability mismatch for encounter type.";
public static string LAbilityHiddenUnavailable { get; set; } = "Hidden Ability not available."; public static string LAbilityHiddenUnavailable { get; set; } = "Hidden Ability not available.";

View file

@ -111,8 +111,15 @@ namespace PKHeX.Core
abil = ability_param; abil = ability_param;
abil <<= 1; // 1/2/4 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; return false;
}
switch (gender_ratio) switch (gender_ratio)
{ {

View file

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

View file

@ -122,7 +122,7 @@ namespace PKHeX.Core
{ {
if (!Legal.Inherit_Safari.Contains(species)) if (!Legal.Inherit_Safari.Contains(species))
return GetInvalid(LBallSpecies); return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4) if (IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility); return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -130,7 +130,7 @@ namespace PKHeX.Core
{ {
if (!Legal.Inherit_Apricorn6.Contains(species)) if (!Legal.Inherit_Apricorn6.Contains(species))
return GetInvalid(LBallSpecies); return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4) if (IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility); return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -138,13 +138,13 @@ namespace PKHeX.Core
{ {
if (!Legal.Inherit_Sport.Contains(species)) if (!Legal.Inherit_Sport.Contains(species))
return GetInvalid(LBallSpecies); return GetInvalid(LBallSpecies);
if (pkm.AbilityNumber == 4) if (IsHiddenAndNotPossible(pkm))
return GetInvalid(LBallAbility); return GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
if (ball == Dream) // Dream Ball 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); return GetInvalid(LBallAbility);
if (Legal.Inherit_Dream.Contains(species)) if (Legal.Inherit_Dream.Contains(species))
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
@ -160,7 +160,7 @@ namespace PKHeX.Core
{ {
if (Legal.Ban_Gen3Ball.Contains(species)) if (Legal.Ban_Gen3Ball.Contains(species))
return GetInvalid(LBallSpecies); 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 GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -191,7 +191,7 @@ namespace PKHeX.Core
{ {
if (!(Legal.Inherit_Safari.Contains(species) || Legal.Inherit_SafariMale.Contains(species))) if (!(Legal.Inherit_Safari.Contains(species) || Legal.Inherit_SafariMale.Contains(species)))
return GetInvalid(LBallSpecies); 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 GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -199,7 +199,7 @@ namespace PKHeX.Core
{ {
if (!Legal.Inherit_Apricorn7.Contains(species)) if (!Legal.Inherit_Apricorn7.Contains(species))
return GetInvalid(LBallSpecies); 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 GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -207,7 +207,7 @@ namespace PKHeX.Core
{ {
if (!Legal.Inherit_Sport.Contains(species)) if (!Legal.Inherit_Sport.Contains(species))
return GetInvalid(LBallSpecies); 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 GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -232,9 +232,9 @@ namespace PKHeX.Core
if (ball == Beast) 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 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) 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))) if (((int)Species.Pikipek <= species && species <= (int)Species.Kommoo) || (Legal.AlolanCaptureOffspring.Contains(species) && !Legal.PastGenAlolanNativesUncapturable.Contains(species)))
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
@ -275,7 +275,7 @@ namespace PKHeX.Core
{ {
if (!(Legal.Inherit_Safari.Contains(species) || Legal.Inherit_SafariMale.Contains(species))) if (!(Legal.Inherit_Safari.Contains(species) || Legal.Inherit_SafariMale.Contains(species)))
return GetInvalid(LBallSpecies); 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 GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -283,7 +283,7 @@ namespace PKHeX.Core
{ {
if (!Legal.Inherit_Apricorn7.Contains(species)) if (!Legal.Inherit_Apricorn7.Contains(species))
return GetInvalid(LBallSpecies); 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 GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -291,7 +291,7 @@ namespace PKHeX.Core
{ {
if (!Legal.Inherit_Sport.Contains(species)) if (!Legal.Inherit_Sport.Contains(species))
return GetInvalid(LBallSpecies); 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 GetInvalid(LBallAbility);
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
} }
@ -316,7 +316,7 @@ namespace PKHeX.Core
if (ball == Beast) 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 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))) if (((int)Species.Pikipek <= species && species <= (int)Species.Kommoo) || (Legal.AlolanCaptureOffspring.Contains(species) && !Legal.PastGenAlolanNativesUncapturable.Contains(species)))
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);
@ -337,6 +337,13 @@ namespace PKHeX.Core
return NONE; 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) public static bool IsGalarCatchAndBreed(int species)
{ {
if ((int)Species.Grookey <= species && species <= (int)Species.Inteleon) // starter if ((int)Species.Grookey <= species && species <= (int)Species.Inteleon) // starter

View file

@ -80,7 +80,7 @@ namespace PKHeX.Core
var pkm = data.pkm; var pkm = data.pkm;
if (pkm.GO) if (pkm.GO)
VerifyIVsGoTransfer(data); 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 VerifyIVsFlawless(data, 2); // Chain of 10 yields 5% HA and 2 flawless IVs
} }