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;
}
}