using System; using static PKHeX.Core.AbilityPermission; namespace PKHeX.Core; /// /// Rules for changing an entity's current ability index. /// public static class AbilityChangeRules { /// /// Checks if the value is possible to obtain based on the original and game visiting. /// /// Original Encounter /// Evolution and game visitation /// Current ability index value /// True if possible to obtain public static bool IsAbilityStateValid(this IEncounterTemplate enc, EvolutionHistory evosAll, int abilityFlag) => (enc switch { IFixedAbilityNumber f => f.Ability, _ => Any12, }).IsAbilityStateValid(evosAll, abilityFlag); /// /// Checks if the current value is possible to obtain based on the original and game visiting. /// /// Original Ability Permitted /// Evolution and game visitation /// Current ability index value /// True if possible to obtain /// public static bool IsAbilityStateValid(this AbilityPermission ability, EvolutionHistory evosAll, int abilityFlag) => ability switch { Any12H => true, Any12 => abilityFlag != 4 || IsAbilityPatchPossible(evosAll), OnlyHidden => abilityFlag == 4 || IsAbilityPatchRevertPossible(evosAll, abilityFlag), OnlyFirst => abilityFlag == 1 || (abilityFlag == 4 && IsAbilityPatchPossible(evosAll)) || (abilityFlag != 4 && IsAbilityCapsulePossible(evosAll)), OnlySecond => abilityFlag == 2 || (abilityFlag == 4 && IsAbilityPatchPossible(evosAll)) || (abilityFlag != 4 && IsAbilityCapsulePossible(evosAll)), _ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null), }; /// /// Checks if the original ability value can be changed based on the games visited. /// /// Original Encounter /// Evolution and game visitation /// True if the ability can be changed public static bool IsAbilityChangeAvailable(this IEncounterTemplate enc, EvolutionHistory evosAll) => (enc switch { IFixedAbilityNumber f => f.Ability, _ => Any12, }).IsAbilityChangeAvailable(evosAll); /// /// Checks if the original value can be changed based on the games visited. /// /// Original Ability Permitted /// Evolution and game visitation /// True if the ability can be changed /// public static bool IsAbilityChangeAvailable(this AbilityPermission ability, EvolutionHistory evosAll) => ability switch { Any12H => true, Any12 => IsAbilityPatchAvailable(evosAll), OnlyHidden => IsAbilityPatchRevertAvailable(evosAll), OnlyFirst or OnlySecond => IsAbilityPatchAvailable(evosAll) || IsAbilityCapsuleAvailable(evosAll), _ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null), }; /// /// Checks if the Ability Capsule (1 <-> 2) item is available in any game visited. /// /// Evolution and game visitation /// True if possible public static bool IsAbilityCapsuleAvailable(EvolutionHistory evosAll) { if (evosAll.HasVisitedGen9) return true; if (evosAll.HasVisitedSWSH) return true; if (evosAll.HasVisitedBDSP) return true; if (evosAll.HasVisitedGen7) return true; if (evosAll.HasVisitedGen6) return true; return false; } /// /// Checks if any of the games visited allow applying an Ability Capsule (1 <-> 2) item. /// /// Evolution and game visitation /// True if possible public static bool IsAbilityCapsulePossible(EvolutionHistory evosAll) { if (evosAll.HasVisitedGen9 && IsCapsulePossible(evosAll.Gen9, PersonalTable.SV)) return true; if (evosAll.HasVisitedSWSH && IsCapsulePossible(evosAll.Gen8, PersonalTable.SWSH)) return true; if (evosAll.HasVisitedBDSP && IsCapsulePossible(evosAll.Gen8b, PersonalTable.BDSP)) return true; if (evosAll.HasVisitedGen7 && IsCapsulePossible(evosAll.Gen7, PersonalTable.USUM)) return true; if (evosAll.HasVisitedGen6 && IsCapsulePossible(evosAll.Gen6, PersonalTable.AO)) return true; return false; } /// /// Checks if the Ability Patch (1/2-> H) item is available in any game visited. /// /// Evolution and game visitation /// True if possible public static bool IsAbilityPatchAvailable(EvolutionHistory evosAll) { if (evosAll.HasVisitedGen9) return true; if (evosAll.HasVisitedSWSH || evosAll.HasVisitedBDSP) return true; return false; } /// /// Checks if any of the games visited allow applying an Ability Patch (1/2-> H) item. /// /// Evolution and game visitation /// True if possible public static bool IsAbilityPatchPossible(EvolutionHistory evosAll) { if (evosAll.HasVisitedGen9 && IsPatchPossible(evosAll.Gen9, PersonalTable.SV)) return true; if (evosAll.HasVisitedSWSH && IsPatchPossible(evosAll.Gen8, PersonalTable.SWSH)) return true; if (evosAll.HasVisitedBDSP && IsPatchPossible(evosAll.Gen8b, PersonalTable.BDSP)) return true; return false; } /// /// Checks if any of the games visited allow reverting an Ability Patch (1/2-> H) item. /// /// Evolution and game visitation /// True if possible public static bool IsAbilityPatchRevertAvailable(EvolutionHistory evosAll) { if (evosAll.HasVisitedGen9) return true; return false; } /// /// Checks if any of the games visited allow reverting an Ability Patch (1/2-> H) item. /// /// Evolution and game visitation /// Current ability index value /// True if possible public static bool IsAbilityPatchRevertPossible(EvolutionHistory evosAll, int abilityIndex) { if (evosAll.HasVisitedGen9 && IsRevertPossible(evosAll.Gen9, PersonalTable.SV, abilityIndex)) return true; return false; } private static bool IsCapsulePossible(EvoCriteria[] evos, TTable table) where TTable : IPersonalTable where TInfo : class, IPersonalInfo, IPersonalAbility12 { for (int i = evos.Length - 1; i >= 0; i--) { var evo = evos[i]; var pi = table[evo.Species, evo.Form]; if (!pi.GetIsAbility12Same()) return true; } return false; } private static bool IsPatchPossible(EvoCriteria[] evos, TTable table) where TTable : IPersonalTable where TInfo : class, IPersonalInfo, IPersonalAbility12H { for (int i = evos.Length - 1; i >= 0; i--) { var evo = evos[i]; var pi = table[evo.Species, evo.Form]; if (pi.GetIsAbilityHiddenUnique()) return true; } // Some species have a distinct hidden ability only on another form, and can change between that form and its current form. var first = evos[0]; return first.Form != 0 && first.Species switch { (int)Species.Giratina => true, // Form-0 is a/a/h (int)Species.Tornadus => true, // Form-0 is a/a/h (int)Species.Thundurus => true, // Form-0 is a/a/h (int)Species.Landorus => true, // Form-0 is a/a/h (int)Species.Enamorus => true, // Form-0 is a/a/h _ => false, }; } private static bool IsRevertPossible(EvoCriteria[] evos, TTable table, int abilityIndex) where TTable : IPersonalTable where TInfo : class, IPersonalInfo, IPersonalAbility12H { bool revert = false; for (var i = evos.Length - 1; i >= 0; i--) { var evo = evos[i]; var pi = table[evo.Species, evo.Form]; if (revert && !pi.GetIsAbility12Same()) return true; if (!pi.GetIsAbilityHiddenUnique()) continue; if (abilityIndex == 1 || !pi.GetIsAbility12Same()) return true; revert = true; } return false; } }