PKHeX/PKHeX.Core/Legality/Evolutions/EncounterOrigin.cs
Kurt dcc0e79435
Evotree: Evolution Traversal Enhancements (#3936)
Like move validation, evolutions are the earliest thing we wish to traverse when determining what encounters may have originated the current Pokémon. To determine the permitted species-form-levels a Pokémon could originate with, we must devolve a Pokémon by traveling down-generation to origin. Once we have an encounter, we can then evolve it to the current species, traversing upwards from origin to the current format.
2023-07-05 21:14:09 -07:00

95 lines
3.6 KiB
C#

using System;
namespace PKHeX.Core;
/// <summary>
/// Contains logic that calculates the evolution chain of a <see cref="PKM"/>, only considering the generation it originated in.
/// </summary>
public static class EncounterOrigin
{
/// <summary>
/// Gets possible evolution details for the input <see cref="pk"/>
/// </summary>
/// <param name="pk">Current state of the Pokémon</param>
/// <param name="generation">Original Generation</param>
/// <returns>Possible origin species-form-levels to match against encounter data.</returns>
/// <remarks>Use <see cref="GetOriginChain12"/> if the <see cref="pk"/> originated from Generation 1 or 2.</remarks>
public static EvoCriteria[] GetOriginChain(PKM pk, byte generation)
{
var (minLevel, maxLevel) = GetMinMax(pk, generation);
var origin = new EvolutionOrigin(pk.Species, (byte)pk.Version, generation, minLevel, maxLevel);
return EvolutionChain.GetOriginChain(pk, origin);
}
/// <summary>
/// Gets possible evolution details for the input <see cref="pk"/> originating from Generation 1 or 2.
/// </summary>
/// <param name="pk">Current state of the Pokémon</param>
/// <param name="gameSource">Game/group the <see cref="pk"/> originated from. If <see cref="GameVersion.RBY"/>, it assumes Gen 1, otherwise Gen 2.</param>
/// <returns>Possible origin species-form-levels to match against encounter data.</returns>
public static EvoCriteria[] GetOriginChain12(PKM pk, GameVersion gameSource)
{
var (minLevel, maxLevel) = GetMinMaxGB(pk);
bool rby = gameSource == GameVersion.RBY;
byte ver = rby ? (byte)GameVersion.RBY : (byte)GameVersion.GSC;
byte gen = rby ? (byte)1 : (byte)2;
var origin = new EvolutionOrigin(pk.Species, ver, gen, minLevel, maxLevel);
return EvolutionChain.GetOriginChain(pk, origin);
}
private static (byte minLevel, byte maxLevel) GetMinMax(PKM pk, byte generation)
{
byte maxLevel = (byte)pk.CurrentLevel;
byte minLevel = GetLevelOriginMin(pk, generation);
return (minLevel, maxLevel);
}
private static (byte minLevel, byte maxLevel) GetMinMaxGB(PKM pk)
{
byte maxLevel = (byte)pk.CurrentLevel;
byte minLevel = GetLevelOriginMinGB(pk);
return (minLevel, maxLevel);
}
private static byte GetLevelOriginMin(PKM pk, byte generation) => generation switch
{
3 => GetLevelOriginMin3(pk),
4 => GetLevelOriginMin4(pk),
_ => Math.Max((byte)1, (byte)pk.Met_Level),
};
private static bool IsEggLocationNonZero(PKM pk) => pk.Egg_Location != LocationEdits.GetNoneLocation(pk.Context);
private static byte GetLevelOriginMinGB(PKM pk)
{
const byte EggLevel = 5;
const byte MinWildLevel = 2;
if (pk.IsEgg)
return EggLevel;
if (pk is not ICaughtData2 { CaughtData: not 0 } pk2)
return MinWildLevel;
return (byte)pk2.Met_Level;
}
private static byte GetLevelOriginMin3(PKM pk)
{
const byte EggLevel = 5;
const byte MinWildLevel = 2;
if (pk.Format != 3)
return MinWildLevel;
if (pk.IsEgg)
return EggLevel;
return (byte)pk.Met_Level;
}
private static byte GetLevelOriginMin4(PKM pk)
{
const byte EggLevel = 1;
const byte MinWildLevel = 2;
if (pk.Format != 4)
return IsEggLocationNonZero(pk) ? EggLevel : MinWildLevel;
if (pk.IsEgg || IsEggLocationNonZero(pk))
return EggLevel;
return (byte)pk.Met_Level;
}
}