using static PKHeX.Core.EvolutionType;
namespace PKHeX.Core
{
///
/// Criteria for evolving to this branch in the
///
public sealed class EvolutionMethod
{
///
/// Evolution Method
///
public readonly int Method;
///
/// Evolve to Species
///
public readonly int Species;
///
/// Conditional Argument (different from )
///
public readonly int Argument;
///
/// Conditional Argument (different from )
///
public readonly byte Level;
///
/// Destination Form
///
/// Is if the evolved form isn't modified. Special consideration for , which forces 1.
public readonly int Form;
private const int AnyForm = -1;
// Not stored in binary data
public bool RequiresLevelUp; // tracks if this method requires a Level Up, lazily set
public EvolutionMethod(int method, int species, int argument = 0, byte level = 0, int form = AnyForm)
{
Method = method;
Species = species;
Argument = argument;
Form = form;
Level = level;
}
public override string ToString() => $"{(Species) Species}-{Form} [{Argument}] @ {Level}{(RequiresLevelUp ? "X" : "")}";
///
/// Returns the form that the Pokémon will have after evolution.
///
/// Un-evolved Form ID
public int GetDestinationForm(int form)
{
if (Method == (int)LevelUpFormFemale1)
return 1;
if (Form == AnyForm)
return form;
return Form;
}
///
/// Checks the for validity by comparing against the data.
///
/// Entity to check
/// Current level
/// Option to skip some comparisons to return a 'possible' evolution.
/// True if a evolution criteria is valid.
public bool Valid(PKM pkm, int lvl, bool skipChecks)
{
RequiresLevelUp = false;
switch ((EvolutionType)Method)
{
case UseItem or UseItemWormhole or UseItemFullMoon:
case CriticalHitsInBattle or HitPointsLostInBattle or Spin:
case UseAgileStyleMoves or UseStrongStyleMoves:
case TowerOfDarkness or TowerOfWaters:
return true;
case UseItemMale or RecoilDamageMale:
return pkm.Gender == 0;
case UseItemFemale or RecoilDamageFemale:
return pkm.Gender == 1;
case Trade or TradeHeldItem or TradeShelmetKarrablast:
return !pkm.IsUntraded || skipChecks;
// Special Level Up Cases -- return false if invalid
case LevelUpNatureAmped or LevelUpNatureLowKey when GetAmpLowKeyResult(pkm.Nature) != pkm.Form && !skipChecks:
return false;
case LevelUpBeauty when pkm is not IContestStats s || s.CNT_Beauty < Argument:
return skipChecks;
case LevelUpMale when pkm.Gender != 0:
return false;
case LevelUpFemale when pkm.Gender != 1:
return false;
case LevelUpFormFemale1 when pkm.Gender != 1 || pkm.Form != 1:
return false;
case LevelUpVersion or LevelUpVersionDay or LevelUpVersionNight when ((pkm.Version & 1) != (Argument & 1) && pkm.IsUntraded) || skipChecks:
return skipChecks; // Version checks come in pairs, check for any pair match
// Level Up (any); the above Level Up (with condition) cases will reach here if they were valid
default:
if (IsThresholdCheckMode(pkm))
return lvl >= Level;
if (Level == 0 && lvl < 2)
return false;
if (lvl < Level)
return false;
RequiresLevelUp = true;
if (skipChecks)
return lvl >= Level;
// Check Met Level for extra validity
return HasMetLevelIncreased(pkm, lvl);
}
}
private static bool IsThresholdCheckMode(PKM pkm)
{
// Starting in Legends: Arceus, level-up evolutions can be triggered if the current level is >= criteria.
// This allows for evolving over-leveled captures immediately without leveling up from capture level.
return pkm is PA8;
}
private bool HasMetLevelIncreased(PKM pkm, int lvl)
{
int origin = pkm.Generation;
return origin switch
{
// No met data in RBY; No met data in GS, Crystal met data can be reset
1 or 2 => true,
// Pal Park / PokeTransfer updates Met Level
3 or 4 => pkm.Format > origin || pkm.Met_Level < lvl,
// 5=>6 and later transfers keep current level
>=5 => lvl >= Level && (!pkm.IsNative || pkm.Met_Level < lvl),
_ => false,
};
}
public EvoCriteria GetEvoCriteria(ushort species, byte form, byte lvl) => new()
{
Species = species,
Form = form,
LevelMax = lvl,
LevelMin = 0,
Method = (EvolutionType)Method,
};
public static int GetAmpLowKeyResult(int n)
{
var index = n - 1;
if ((uint)index > 22)
return 0;
return (0b_0101_1011_1100_1010_0101_0001 >> index) & 1;
}
}
}