2022-11-25 01:42:17 +00:00
|
|
|
using System;
|
|
|
|
using static PKHeX.Core.AbilityPermission;
|
|
|
|
|
|
|
|
namespace PKHeX.Core;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Rules for changing an entity's current ability index.
|
|
|
|
/// </summary>
|
|
|
|
public static class AbilityChangeRules
|
|
|
|
{
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if the <see cref="abilityFlag"/> value is possible to obtain based on the original <see cref="enc"/> and game visiting.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="enc">Original Encounter</param>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
|
|
|
/// <param name="abilityFlag">Current ability index value</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if possible to obtain <see cref="abilityFlag"/></returns>
|
2024-03-25 01:12:33 +00:00
|
|
|
public static bool IsAbilityStateValid(this IEncounterTemplate enc, EvolutionHistory evosAll, int abilityFlag, EntityContext current, EntityContext original)
|
|
|
|
=> enc.Ability.IsAbilityStateValid(evosAll, abilityFlag, current, original);
|
2022-11-25 01:42:17 +00:00
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if the current <see cref="abilityFlag"/> value is possible to obtain based on the original <see cref="ability"/> and game visiting.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="ability">Original Ability Permitted</param>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
|
|
|
/// <param name="abilityFlag">Current ability index value</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if possible to obtain <see cref="abilityFlag"/></returns>
|
|
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
2023-06-04 01:19:16 +00:00
|
|
|
public static bool IsAbilityStateValid(this AbilityPermission ability, EvolutionHistory evosAll, int abilityFlag, EntityContext current, EntityContext original) => ability switch
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
|
|
|
Any12H => true,
|
2023-06-04 01:19:16 +00:00
|
|
|
Any12 => abilityFlag != 4 || IsAbilityPatchPossible(evosAll, current, original),
|
|
|
|
OnlyHidden => abilityFlag == 4 || IsAbilityPatchRevertPossible(evosAll, abilityFlag, current, original),
|
|
|
|
OnlyFirst => abilityFlag == 1 || (abilityFlag == 4 && IsAbilityPatchPossible(evosAll, current, original)) || (abilityFlag != 4 && IsAbilityCapsulePossible(evosAll, current, original)),
|
|
|
|
OnlySecond => abilityFlag == 2 || (abilityFlag == 4 && IsAbilityPatchPossible(evosAll, current, original)) || (abilityFlag != 4 && IsAbilityCapsulePossible(evosAll, current, original)),
|
2022-11-25 01:42:17 +00:00
|
|
|
_ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null),
|
|
|
|
};
|
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if the original <see cref="enc"/> ability value can be changed based on the games visited.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="enc">Original Encounter</param>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if the ability can be changed</returns>
|
2024-03-25 01:12:33 +00:00
|
|
|
public static bool IsAbilityChangeAvailable(this IEncounterTemplate enc, EvolutionHistory evosAll, EntityContext current, EntityContext original)
|
|
|
|
=> enc.Ability.IsAbilityChangeAvailable(evosAll, current, original);
|
2022-11-25 01:42:17 +00:00
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if the original <see cref="ability"/> value can be changed based on the games visited.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="ability">Original Ability Permitted</param>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if the ability can be changed</returns>
|
|
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
2023-06-04 01:19:16 +00:00
|
|
|
public static bool IsAbilityChangeAvailable(this AbilityPermission ability, EvolutionHistory evosAll, EntityContext current, EntityContext original) => ability switch
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
|
|
|
Any12H => true,
|
2023-06-04 01:19:16 +00:00
|
|
|
Any12 => IsAbilityPatchAvailable(evosAll, current, original),
|
|
|
|
OnlyHidden => IsAbilityPatchRevertAvailable(evosAll, current, original),
|
|
|
|
OnlyFirst or OnlySecond => IsAbilityPatchAvailable(evosAll, current, original) || IsAbilityCapsuleAvailable(evosAll, current, original),
|
2022-11-25 01:42:17 +00:00
|
|
|
_ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null),
|
|
|
|
};
|
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if the Ability Capsule (1 <-> 2) item is available in any game visited.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if possible</returns>
|
2023-06-04 01:19:16 +00:00
|
|
|
public static bool IsAbilityCapsuleAvailable(EvolutionHistory evosAll, EntityContext current, EntityContext original)
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
|
|
|
if (evosAll.HasVisitedSWSH)
|
|
|
|
return true;
|
|
|
|
if (evosAll.HasVisitedBDSP)
|
|
|
|
return true;
|
|
|
|
if (evosAll.HasVisitedGen7)
|
|
|
|
return true;
|
|
|
|
if (evosAll.HasVisitedGen6)
|
|
|
|
return true;
|
2023-06-04 01:19:16 +00:00
|
|
|
if (IsAbilityRestoredHOME(current, original))
|
|
|
|
return false;
|
|
|
|
if (evosAll.HasVisitedGen9)
|
|
|
|
return true;
|
2022-11-25 01:42:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if any of the games visited allow applying an Ability Capsule (1 <-> 2) item.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if possible</returns>
|
2023-06-04 01:19:16 +00:00
|
|
|
public static bool IsAbilityCapsulePossible(EvolutionHistory evosAll, EntityContext current, EntityContext original)
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
2023-06-04 01:19:16 +00:00
|
|
|
if (evosAll.HasVisitedSWSH && IsCapsulePossible<PersonalTable8SWSH, PersonalInfo8SWSH, EvoCriteria>(evosAll.Gen8, PersonalTable.SWSH))
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
2023-06-04 01:19:16 +00:00
|
|
|
if (evosAll.HasVisitedBDSP && IsCapsulePossible<PersonalTable8BDSP, PersonalInfo8BDSP, EvoCriteria>(evosAll.Gen8b, PersonalTable.BDSP))
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
2023-06-04 01:19:16 +00:00
|
|
|
if (evosAll.HasVisitedGen7 && IsCapsulePossible<PersonalTable7, PersonalInfo7, EvoCriteria>(evosAll.Gen7, PersonalTable.USUM))
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
2023-06-04 01:19:16 +00:00
|
|
|
if (evosAll.HasVisitedGen6 && IsCapsulePossible<PersonalTable6AO, PersonalInfo6AO, EvoCriteria>(evosAll.Gen6, PersonalTable.AO))
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
2023-06-04 01:19:16 +00:00
|
|
|
if (IsAbilityRestoredHOME(current, original))
|
|
|
|
return false;
|
|
|
|
if (evosAll.HasVisitedGen9 && IsCapsulePossible<PersonalTable9SV, PersonalInfo9SV, EvoCriteria>(evosAll.Gen9, PersonalTable.SV))
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if the Ability Patch (1/2-> H) item is available in any game visited.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if possible</returns>
|
2023-06-04 01:19:16 +00:00
|
|
|
public static bool IsAbilityPatchAvailable(EvolutionHistory evosAll, EntityContext current, EntityContext original)
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
|
|
|
if (evosAll.HasVisitedSWSH || evosAll.HasVisitedBDSP)
|
|
|
|
return true;
|
2023-06-04 01:19:16 +00:00
|
|
|
if (IsAbilityRestoredHOME(current, original))
|
|
|
|
return false;
|
|
|
|
if (evosAll.HasVisitedGen9)
|
|
|
|
return true;
|
2022-11-25 01:42:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if any of the games visited allow applying an Ability Patch (1/2-> H) item.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if possible</returns>
|
2023-06-04 01:19:16 +00:00
|
|
|
public static bool IsAbilityPatchPossible(EvolutionHistory evosAll, EntityContext current, EntityContext original)
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
2023-06-04 01:19:16 +00:00
|
|
|
if (evosAll.HasVisitedSWSH && IsPatchPossible<PersonalTable8SWSH, PersonalInfo8SWSH, EvoCriteria>(evosAll.Gen8, PersonalTable.SWSH))
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
2023-06-04 01:19:16 +00:00
|
|
|
if (evosAll.HasVisitedBDSP && IsPatchPossible<PersonalTable8BDSP, PersonalInfo8BDSP, EvoCriteria>(evosAll.Gen8b, PersonalTable.BDSP))
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
2023-06-04 01:19:16 +00:00
|
|
|
if (IsAbilityRestoredHOME(current, original))
|
|
|
|
return false;
|
|
|
|
if (evosAll.HasVisitedGen9 && IsPatchPossible<PersonalTable9SV, PersonalInfo9SV, EvoCriteria>(evosAll.Gen9, PersonalTable.SV))
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if any of the games visited allow reverting an Ability Patch (1/2-> H) item.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if possible</returns>
|
2023-06-04 01:19:16 +00:00
|
|
|
public static bool IsAbilityPatchRevertAvailable(EvolutionHistory evosAll, EntityContext current, EntityContext original)
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
2023-06-04 01:19:16 +00:00
|
|
|
if (IsAbilityRestoredHOME(current, original))
|
|
|
|
return false;
|
|
|
|
|
2022-11-25 01:42:17 +00:00
|
|
|
if (evosAll.HasVisitedGen9)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-04 01:19:16 +00:00
|
|
|
private static bool IsAbilityRestoredHOME(EntityContext current, EntityContext original)
|
|
|
|
{
|
|
|
|
var originalGen = original.Generation();
|
|
|
|
if (originalGen < 9)
|
|
|
|
{
|
|
|
|
// HOME restores abilities if it existed in the current format prior to transferring to a future game with reversion possible.
|
|
|
|
if (originalGen != 8)
|
|
|
|
original = EntityContext.Gen8;
|
|
|
|
if (current == original)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if any of the games visited allow reverting an Ability Patch (1/2-> H) item.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="evosAll">Evolution and game visitation</param>
|
|
|
|
/// <param name="abilityIndex">Current ability index value</param>
|
2023-06-04 01:19:16 +00:00
|
|
|
/// <param name="current">Current context</param>
|
|
|
|
/// <param name="original">Original context</param>
|
2023-03-31 20:00:34 +00:00
|
|
|
/// <returns>True if possible</returns>
|
2023-06-04 01:19:16 +00:00
|
|
|
public static bool IsAbilityPatchRevertPossible(EvolutionHistory evosAll, int abilityIndex, EntityContext current, EntityContext original)
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
2023-06-04 01:19:16 +00:00
|
|
|
// With HOME 3.0, ability modifications are locked to sets of games -- modifying in SV won't change SWSH.
|
|
|
|
if (current == EntityContext.Gen9)
|
|
|
|
{
|
|
|
|
if (IsAbilityRestoredHOME(current, original))
|
|
|
|
return false;
|
|
|
|
if (evosAll.HasVisitedGen9 && IsRevertPossible<PersonalTable9SV, PersonalInfo9SV, EvoCriteria>(evosAll.Gen9, PersonalTable.SV, abilityIndex))
|
|
|
|
return true;
|
|
|
|
}
|
2022-11-25 01:42:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-04 01:19:16 +00:00
|
|
|
private static bool IsCapsulePossible<TTable, TInfo, TDex>(ReadOnlySpan<TDex> evos, TTable table)
|
2022-11-25 01:42:17 +00:00
|
|
|
where TTable : IPersonalTable<TInfo>
|
2023-03-26 06:23:43 +00:00
|
|
|
where TInfo : class, IPersonalInfo, IPersonalAbility12
|
2023-06-04 01:19:16 +00:00
|
|
|
where TDex : ISpeciesForm
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
|
|
|
for (int i = evos.Length - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
var evo = evos[i];
|
|
|
|
var pi = table[evo.Species, evo.Form];
|
2022-11-25 21:54:14 +00:00
|
|
|
if (!pi.GetIsAbility12Same())
|
|
|
|
return true;
|
2022-11-25 01:42:17 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-04 01:19:16 +00:00
|
|
|
private static bool IsPatchPossible<TTable, TInfo, TDex>(ReadOnlySpan<TDex> evos, TTable table)
|
2022-11-25 01:42:17 +00:00
|
|
|
where TTable : IPersonalTable<TInfo>
|
2023-03-26 06:23:43 +00:00
|
|
|
where TInfo : class, IPersonalInfo, IPersonalAbility12H
|
2023-06-04 01:19:16 +00:00
|
|
|
where TDex : ISpeciesForm
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
|
|
|
for (int i = evos.Length - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
var evo = evos[i];
|
|
|
|
var pi = table[evo.Species, evo.Form];
|
|
|
|
if (pi.GetIsAbilityHiddenUnique())
|
2022-11-25 21:54:14 +00:00
|
|
|
return true;
|
2022-11-25 01:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-06-04 01:19:16 +00:00
|
|
|
private static bool IsRevertPossible<TTable, TInfo, TDex>(ReadOnlySpan<TDex> evos, TTable table, int abilityIndex)
|
2022-11-25 01:42:17 +00:00
|
|
|
where TTable : IPersonalTable<TInfo>
|
2023-03-26 06:23:43 +00:00
|
|
|
where TInfo : class, IPersonalInfo, IPersonalAbility12H
|
2023-06-04 01:19:16 +00:00
|
|
|
where TDex : ISpeciesForm
|
2022-11-25 01:42:17 +00:00
|
|
|
{
|
|
|
|
bool revert = false;
|
|
|
|
for (var i = evos.Length - 1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
var evo = evos[i];
|
|
|
|
var pi = table[evo.Species, evo.Form];
|
2022-11-27 06:21:33 +00:00
|
|
|
if (revert && !pi.GetIsAbility12Same())
|
2022-11-25 01:42:17 +00:00
|
|
|
return true;
|
2022-11-27 06:21:33 +00:00
|
|
|
if (!pi.GetIsAbilityHiddenUnique())
|
2022-11-25 01:42:17 +00:00
|
|
|
continue;
|
2022-11-27 06:21:33 +00:00
|
|
|
if (abilityIndex == 1 || !pi.GetIsAbility12Same())
|
|
|
|
return true;
|
2022-11-25 01:42:17 +00:00
|
|
|
revert = true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|