2016-11-08 16:43:57 +00:00
using System ;
using System.Collections.Generic ;
2019-12-06 06:32:47 +00:00
using System.Linq ;
2019-11-26 06:51:07 +00:00
using static PKHeX . Core . GameVersion ;
2020-04-14 17:52:19 +00:00
using static PKHeX . Core . Legal ;
2016-11-08 16:43:57 +00:00
2017-01-08 07:54:09 +00:00
namespace PKHeX.Core
2016-11-08 16:43:57 +00:00
{
2017-10-24 06:12:58 +00:00
/// <summary>
/// Generation specific Evolution Tree data.
/// </summary>
/// <remarks>
/// Used to determine if a <see cref="PKM.Species"/> can evolve from prior steps in its evolution branch.
/// </remarks>
2018-07-02 02:55:23 +00:00
public sealed class EvolutionTree
2016-11-08 16:43:57 +00:00
{
2022-02-05 01:35:15 +00:00
private static readonly EvolutionTree Evolves1 = new ( GetResource ( "rby" ) , Gen1 , PersonalTable . Y , MaxSpeciesID_1 ) ;
private static readonly EvolutionTree Evolves2 = new ( GetResource ( "gsc" ) , Gen2 , PersonalTable . C , MaxSpeciesID_2 ) ;
private static readonly EvolutionTree Evolves3 = new ( GetResource ( "g3" ) , Gen3 , PersonalTable . RS , MaxSpeciesID_3 ) ;
private static readonly EvolutionTree Evolves4 = new ( GetResource ( "g4" ) , Gen4 , PersonalTable . DP , MaxSpeciesID_4 ) ;
private static readonly EvolutionTree Evolves5 = new ( GetResource ( "g5" ) , Gen5 , PersonalTable . BW , MaxSpeciesID_5 ) ;
private static readonly EvolutionTree Evolves6 = new ( GetReader ( "ao" ) , Gen6 , PersonalTable . AO , MaxSpeciesID_6 ) ;
private static readonly EvolutionTree Evolves7 = new ( GetReader ( "uu" ) , Gen7 , PersonalTable . USUM , MaxSpeciesID_7_USUM ) ;
private static readonly EvolutionTree Evolves7b = new ( GetReader ( "gg" ) , Gen7 , PersonalTable . GG , MaxSpeciesID_7b ) ;
private static readonly EvolutionTree Evolves8 = new ( GetReader ( "ss" ) , Gen8 , PersonalTable . SWSH , MaxSpeciesID_8 ) ;
private static readonly EvolutionTree Evolves8a = new ( GetReader ( "la" ) , Gen8 , PersonalTable . LA , MaxSpeciesID_8a ) ;
private static readonly EvolutionTree Evolves8b = new ( GetReader ( "bs" ) , Gen8 , PersonalTable . BDSP , MaxSpeciesID_8b ) ;
private static ReadOnlySpan < byte > GetResource ( string resource ) = > Util . GetBinaryResource ( $"evos_{resource}.pkl" ) ;
private static BinLinkerAccessor GetReader ( string resource ) = > BinLinkerAccessor . Get ( GetResource ( resource ) , resource ) ;
2017-07-29 18:54:52 +00:00
static EvolutionTree ( )
{
2021-08-20 20:49:20 +00:00
// Add in banned evolution data!
2018-07-02 02:55:23 +00:00
Evolves7 . FixEvoTreeSM ( ) ;
2019-11-23 05:04:34 +00:00
Evolves8 . FixEvoTreeSS ( ) ;
2022-02-05 01:35:15 +00:00
Evolves8a . FixEvoTreeLA ( ) ;
2021-11-20 02:23:49 +00:00
Evolves8b . FixEvoTreeBS ( ) ;
2017-07-29 18:54:52 +00:00
}
2018-07-02 02:55:23 +00:00
2021-01-02 01:08:49 +00:00
public static EvolutionTree GetEvolutionTree ( int generation ) = > generation switch
2017-07-29 18:54:52 +00:00
{
2021-01-02 01:08:49 +00:00
1 = > Evolves1 ,
2 = > Evolves2 ,
3 = > Evolves3 ,
4 = > Evolves4 ,
5 = > Evolves5 ,
6 = > Evolves6 ,
7 = > Evolves7 ,
2021-08-20 20:49:20 +00:00
_ = > Evolves8 ,
2021-01-02 01:08:49 +00:00
} ;
public static EvolutionTree GetEvolutionTree ( PKM pkm , int generation ) = > generation switch
2018-11-14 03:04:58 +00:00
{
2021-01-02 01:08:49 +00:00
1 = > Evolves1 ,
2 = > Evolves2 ,
3 = > Evolves3 ,
4 = > Evolves4 ,
5 = > Evolves5 ,
6 = > Evolves6 ,
7 = > pkm . Version is ( int ) GO or ( int ) GP or ( int ) GE ? Evolves7b : Evolves7 ,
2022-02-05 01:35:15 +00:00
_ = > pkm . Version switch
{
( int ) PLA = > Evolves8a ,
( int ) BD or ( int ) SP = > Evolves8b ,
_ = > Evolves8 ,
} ,
2021-01-02 01:08:49 +00:00
} ;
2018-11-14 03:04:58 +00:00
2019-03-23 17:36:28 +00:00
private readonly IReadOnlyList < EvolutionMethod [ ] > Entries ;
2016-11-15 02:25:08 +00:00
private readonly GameVersion Game ;
2016-11-08 16:43:57 +00:00
private readonly PersonalTable Personal ;
2017-01-29 17:26:40 +00:00
private readonly int MaxSpeciesTree ;
2019-12-06 06:32:47 +00:00
private readonly ILookup < int , EvolutionLink > Lineage ;
private static int GetLookupKey ( int species , int form ) = > species | ( form < < 11 ) ;
#region Constructor
2016-11-08 16:43:57 +00:00
2022-02-05 01:35:15 +00:00
private EvolutionTree ( ReadOnlySpan < byte > data , GameVersion game , PersonalTable personal , int maxSpeciesTree )
{
Game = game ;
Personal = personal ;
MaxSpeciesTree = maxSpeciesTree ;
Entries = GetEntries ( data , game ) ;
// Starting in Generation 7, forms have separate evolution data.
int format = Game - Gen1 + 1 ;
var oldStyle = format < 7 ;
var connections = oldStyle ? CreateTreeOld ( ) : CreateTree ( ) ;
Lineage = connections . ToLookup ( obj = > obj . Key , obj = > obj . Value ) ;
}
private EvolutionTree ( BinLinkerAccessor data , GameVersion game , PersonalTable personal , int maxSpeciesTree )
2016-11-08 16:43:57 +00:00
{
2016-11-15 02:25:08 +00:00
Game = game ;
2016-11-08 16:43:57 +00:00
Personal = personal ;
2017-01-29 17:26:40 +00:00
MaxSpeciesTree = maxSpeciesTree ;
2021-01-02 01:08:49 +00:00
Entries = GetEntries ( data , game ) ;
2019-12-07 01:36:36 +00:00
// Starting in Generation 7, forms have separate evolution data.
2019-12-07 02:16:11 +00:00
int format = Game - Gen1 + 1 ;
2019-12-07 01:36:36 +00:00
var oldStyle = format < 7 ;
var connections = oldStyle ? CreateTreeOld ( ) : CreateTree ( ) ;
2019-12-06 06:32:47 +00:00
Lineage = connections . ToLookup ( obj = > obj . Key , obj = > obj . Value ) ;
}
2019-12-07 01:36:36 +00:00
private IEnumerable < KeyValuePair < int , EvolutionLink > > CreateTreeOld ( )
{
for ( int sSpecies = 1 ; sSpecies < = MaxSpeciesTree ; sSpecies + + )
{
2020-12-11 04:42:30 +00:00
var fc = Personal [ sSpecies ] . FormCount ;
2019-12-07 01:36:36 +00:00
for ( int sForm = 0 ; sForm < fc ; sForm + + )
{
var index = sSpecies ;
var evos = Entries [ index ] ;
foreach ( var evo in evos )
{
var dSpecies = evo . Species ;
if ( dSpecies = = 0 )
continue ;
2020-01-05 06:31:46 +00:00
var dForm = sSpecies = = ( int ) Species . Espurr & & evo . Method = = ( int ) EvolutionType . LevelUpFormFemale1 ? 1 : sForm ;
2019-12-07 01:36:36 +00:00
var key = GetLookupKey ( dSpecies , dForm ) ;
var link = new EvolutionLink ( sSpecies , sForm , evo ) ;
yield return new KeyValuePair < int , EvolutionLink > ( key , link ) ;
}
}
}
}
2019-12-06 06:32:47 +00:00
private IEnumerable < KeyValuePair < int , EvolutionLink > > CreateTree ( )
{
for ( int sSpecies = 1 ; sSpecies < = MaxSpeciesTree ; sSpecies + + )
{
2020-12-11 04:42:30 +00:00
var fc = Personal [ sSpecies ] . FormCount ;
2019-12-06 06:32:47 +00:00
for ( int sForm = 0 ; sForm < fc ; sForm + + )
{
2020-12-11 04:42:30 +00:00
var index = Personal . GetFormIndex ( sSpecies , sForm ) ;
2019-12-06 06:32:47 +00:00
var evos = Entries [ index ] ;
foreach ( var evo in evos )
{
var dSpecies = evo . Species ;
if ( dSpecies = = 0 )
2019-12-07 03:31:18 +00:00
break ;
2019-12-07 01:36:36 +00:00
var dForm = evo . GetDestinationForm ( sForm ) ;
2019-12-06 06:32:47 +00:00
var key = GetLookupKey ( dSpecies , dForm ) ;
var link = new EvolutionLink ( sSpecies , sForm , evo ) ;
yield return new KeyValuePair < int , EvolutionLink > ( key , link ) ;
}
}
}
2018-07-02 02:55:23 +00:00
}
2022-02-05 01:35:15 +00:00
private IReadOnlyList < EvolutionMethod [ ] > GetEntries ( ReadOnlySpan < byte > data , GameVersion game ) = > game switch
{
Gen1 = > EvolutionSet1 . GetArray ( data , MaxSpeciesTree ) ,
Gen2 = > EvolutionSet1 . GetArray ( data , MaxSpeciesTree ) ,
Gen3 = > EvolutionSet3 . GetArray ( data ) ,
Gen4 = > EvolutionSet4 . GetArray ( data ) ,
Gen5 = > EvolutionSet5 . GetArray ( data ) ,
_ = > throw new ArgumentOutOfRangeException ( ) ,
} ;
private IReadOnlyList < EvolutionMethod [ ] > GetEntries ( BinLinkerAccessor data , GameVersion game ) = > game switch
2018-07-02 02:55:23 +00:00
{
2021-01-02 01:08:49 +00:00
Gen6 = > EvolutionSet6 . GetArray ( data ) ,
Gen7 = > EvolutionSet7 . GetArray ( data ) ,
Gen8 = > EvolutionSet7 . GetArray ( data ) ,
2021-08-20 20:49:20 +00:00
_ = > throw new ArgumentOutOfRangeException ( ) ,
2021-01-02 01:08:49 +00:00
} ;
2018-05-12 15:13:39 +00:00
2017-06-18 01:37:19 +00:00
private void FixEvoTreeSM ( )
2016-11-15 02:25:08 +00:00
{
2019-12-06 06:32:47 +00:00
// Sun/Moon lack Ultra's Kantonian evolution methods.
2020-04-06 15:10:49 +00:00
BanEvo ( ( int ) Species . Raichu , 0 , pkm = > pkm . IsUntraded & & pkm . SM ) ;
2019-12-06 06:32:47 +00:00
BanEvo ( ( int ) Species . Marowak , 0 , pkm = > pkm . IsUntraded & & pkm . SM ) ;
2021-08-28 21:44:07 +00:00
BanEvo ( ( int ) Species . Exeggutor , 0 , pkm = > pkm . IsUntraded & & pkm . SM ) ;
2019-11-23 05:04:34 +00:00
}
2017-02-02 06:19:37 +00:00
2019-11-23 05:04:34 +00:00
private void FixEvoTreeSS ( )
{
2019-12-06 06:32:47 +00:00
// Gigantamax Pikachu, Meowth-0, and Eevee are prevented from evolving.
2020-04-06 15:10:49 +00:00
// Raichu cannot be evolved to the Alolan variant at this time.
2020-12-22 06:33:48 +00:00
BanEvo ( ( int ) Species . Raichu , 0 , pkm = > pkm is IGigantamax { CanGigantamax : true } ) ;
2020-12-25 20:30:26 +00:00
BanEvo ( ( int ) Species . Raichu , 1 , pkm = > ( pkm is IGigantamax { CanGigantamax : true } ) | | pkm . Version is ( int ) GO or > = ( int ) GP ) ;
2020-12-22 06:33:48 +00:00
BanEvo ( ( int ) Species . Persian , 0 , pkm = > pkm is IGigantamax { CanGigantamax : true } ) ;
BanEvo ( ( int ) Species . Persian , 1 , pkm = > pkm is IGigantamax { CanGigantamax : true } ) ;
BanEvo ( ( int ) Species . Perrserker , 0 , pkm = > pkm is IGigantamax { CanGigantamax : true } ) ;
2017-06-11 00:36:33 +00:00
2020-12-25 20:30:26 +00:00
BanEvo ( ( int ) Species . Exeggutor , 1 , pkm = > pkm . Version is ( int ) GO or > = ( int ) GP ) ;
BanEvo ( ( int ) Species . Marowak , 1 , pkm = > pkm . Version is ( int ) GO or > = ( int ) GP ) ;
2020-08-07 01:21:40 +00:00
BanEvo ( ( int ) Species . Weezing , 0 , pkm = > pkm . Version > = ( int ) SW ) ;
BanEvo ( ( int ) Species . MrMime , 0 , pkm = > pkm . Version > = ( int ) SW ) ;
2020-02-17 02:16:22 +00:00
2019-12-06 06:32:47 +00:00
foreach ( var s in GetEvolutions ( ( int ) Species . Eevee , 0 ) ) // Eeveelutions
2020-12-22 06:33:48 +00:00
BanEvo ( s , 0 , pkm = > pkm is IGigantamax { CanGigantamax : true } ) ;
2016-11-15 02:25:08 +00:00
}
2021-11-20 02:23:49 +00:00
2022-02-05 01:35:15 +00:00
private void FixEvoTreeLA ( )
{
}
2021-11-20 02:23:49 +00:00
private void FixEvoTreeBS ( )
{
2021-11-27 05:16:13 +00:00
BanEvo ( ( int ) Species . Glaceon , 0 , pkm = > pkm . CurrentLevel = = pkm . Met_Level ) ; // Ice Stone is unreleased, requires Route 217 Ice Rock Level Up instead
BanEvo ( ( int ) Species . Milotic , 0 , pkm = > pkm is IContestStats { CNT_Beauty : < 170 } | | pkm . CurrentLevel = = pkm . Met_Level ) ; // Prism Scale is unreleased, requires 170 Beauty Level Up instead
2021-11-20 02:23:49 +00:00
}
2016-11-08 16:43:57 +00:00
2019-12-06 06:32:47 +00:00
private void BanEvo ( int species , int form , Func < PKM , bool > func )
2016-11-08 16:43:57 +00:00
{
2019-12-06 06:32:47 +00:00
var key = GetLookupKey ( species , form ) ;
var node = Lineage [ key ] ;
foreach ( var link in node )
link . IsBanned = func ;
2016-11-08 16:43:57 +00:00
}
2018-08-03 03:11:42 +00:00
2019-12-06 06:32:47 +00:00
#endregion
2018-07-02 02:55:23 +00:00
/// <summary>
/// Gets a list of evolutions for the input <see cref="PKM"/> by checking each evolution in the chain.
/// </summary>
/// <param name="pkm">Pokémon data to check with.</param>
/// <param name="maxLevel">Maximum level to permit before the chain breaks.</param>
/// <param name="maxSpeciesOrigin">Maximum species ID to permit within the chain.</param>
/// <param name="skipChecks">Ignores an evolution's criteria, causing the returned list to have all possible evolutions.</param>
/// <param name="minLevel">Minimum level to permit before the chain breaks.</param>
/// <returns></returns>
2018-06-19 02:10:21 +00:00
public List < EvoCriteria > GetValidPreEvolutions ( PKM pkm , int maxLevel , int maxSpeciesOrigin = - 1 , bool skipChecks = false , int minLevel = 1 )
2016-11-08 16:43:57 +00:00
{
2017-04-15 02:55:40 +00:00
if ( maxSpeciesOrigin < = 0 )
2021-03-14 23:16:55 +00:00
maxSpeciesOrigin = GetMaxSpeciesOrigin ( pkm ) ;
2020-01-12 05:43:58 +00:00
if ( pkm . IsEgg & & ! skipChecks )
{
return new List < EvoCriteria > ( 1 )
{
2020-12-22 01:17:56 +00:00
new ( pkm . Species , pkm . Form ) { Level = maxLevel , MinLevel = maxLevel } ,
2020-01-12 05:43:58 +00:00
} ;
}
// Shedinja's evolution case can be a little tricky; hard-code handling.
2020-08-21 23:35:49 +00:00
if ( pkm . Species = = ( int ) Species . Shedinja & & maxLevel > = 20 & & ( ! pkm . HasOriginalMetLocation | | minLevel < maxLevel ) )
2020-01-12 05:43:58 +00:00
{
return new List < EvoCriteria > ( 2 )
{
2022-01-14 08:11:33 +00:00
new ( ( int ) Species . Shedinja , 0 ) { Level = maxLevel , MinLevel = Math . Max ( minLevel , 20 ) } ,
2020-12-22 01:17:56 +00:00
new ( ( int ) Species . Nincada , 0 ) { Level = maxLevel , MinLevel = minLevel } ,
2020-01-12 05:43:58 +00:00
} ;
}
2019-12-06 06:32:47 +00:00
return GetExplicitLineage ( pkm , maxLevel , skipChecks , maxSpeciesOrigin , minLevel ) ;
2016-11-08 16:43:57 +00:00
}
2018-11-28 06:05:36 +00:00
2021-06-15 03:26:12 +00:00
public bool IsSpeciesDerivedFrom ( int species , int form , int otherSpecies , int otherForm , bool ignoreForm = true )
{
var evos = GetEvolutionsAndPreEvolutions ( species , form ) ;
foreach ( var evo in evos )
{
var s = evo & 0x3FF ;
if ( s ! = otherSpecies )
continue ;
if ( ignoreForm )
return true ;
var f = evo > > 11 ;
return f = = otherForm ;
}
return false ;
}
2019-11-26 06:51:07 +00:00
/// <summary>
2019-12-07 02:16:11 +00:00
/// Gets all species the <see cref="species"/>-<see cref="form"/> can evolve to & from, yielded in order of increasing evolution stage.
2019-11-26 06:51:07 +00:00
/// </summary>
/// <param name="species">Species ID</param>
/// <param name="form">Form ID</param>
2020-11-17 22:23:15 +00:00
/// <returns>Enumerable of species IDs (with the Form IDs included, left shifted by 11).</returns>
2018-11-28 06:05:36 +00:00
public IEnumerable < int > GetEvolutionsAndPreEvolutions ( int species , int form )
{
foreach ( var s in GetPreEvolutions ( species , form ) )
yield return s ;
yield return species ;
foreach ( var s in GetEvolutions ( species , form ) )
yield return s ;
}
2021-01-30 18:15:38 +00:00
public int GetBaseSpeciesForm ( int species , int form , int skip = 0 )
{
var chain = GetEvolutionsAndPreEvolutions ( species , form ) ;
foreach ( var c in chain )
{
if ( skip = = 0 )
return c ;
skip - - ;
}
return species | ( form < < 11 ) ;
}
2020-11-17 22:23:15 +00:00
/// <summary>
/// Gets all species the <see cref="species"/>-<see cref="form"/> can evolve from, yielded in order of increasing evolution stage.
/// </summary>
/// <param name="species">Species ID</param>
/// <param name="form">Form ID</param>
/// <returns>Enumerable of species IDs (with the Form IDs included, left shifted by 11).</returns>
public IEnumerable < int > GetPreEvolutions ( int species , int form )
2018-11-28 06:05:36 +00:00
{
2019-12-06 06:32:47 +00:00
int index = GetLookupKey ( species , form ) ;
2018-11-28 06:05:36 +00:00
var node = Lineage [ index ] ;
2019-12-06 06:32:47 +00:00
foreach ( var method in node )
2018-11-28 06:05:36 +00:00
{
2019-12-06 06:32:47 +00:00
var s = method . Species ;
2019-12-07 02:16:11 +00:00
if ( s = = 0 )
continue ;
2019-12-06 06:32:47 +00:00
var f = method . Form ;
var preEvolutions = GetPreEvolutions ( s , f ) ;
foreach ( var preEvo in preEvolutions )
yield return preEvo ;
2020-11-17 22:23:15 +00:00
yield return s | ( f < < 11 ) ;
2018-11-28 06:05:36 +00:00
}
}
2020-11-17 22:23:15 +00:00
/// <summary>
/// Gets all species the <see cref="species"/>-<see cref="form"/> can evolve to, yielded in order of increasing evolution stage.
/// </summary>
/// <param name="species">Species ID</param>
/// <param name="form">Form ID</param>
/// <returns>Enumerable of species IDs (with the Form IDs included, left shifted by 11).</returns>
public IEnumerable < int > GetEvolutions ( int species , int form )
2018-11-28 06:05:36 +00:00
{
2019-12-07 02:16:11 +00:00
int format = Game - Gen1 + 1 ;
2020-12-11 04:42:30 +00:00
int index = format < 7 ? species : Personal . GetFormIndex ( species , form ) ;
2019-12-07 02:16:11 +00:00
var evos = Entries [ index ] ;
foreach ( var method in evos )
2018-11-28 06:05:36 +00:00
{
2019-12-06 06:32:47 +00:00
var s = method . Species ;
2019-12-07 02:16:11 +00:00
if ( s = = 0 )
continue ;
var f = method . GetDestinationForm ( form ) ;
2020-11-17 22:23:15 +00:00
yield return s | ( f < < 11 ) ;
2019-12-07 02:16:11 +00:00
var nextEvolutions = GetEvolutions ( s , f ) ;
2020-11-17 22:23:15 +00:00
foreach ( var nextEvo in nextEvolutions )
yield return nextEvo ;
2018-11-28 06:05:36 +00:00
}
}
2019-12-06 06:32:47 +00:00
/// <summary>
/// Generates the reverse evolution path for the input <see cref="pkm"/>.
/// </summary>
/// <param name="pkm">Entity data</param>
/// <param name="maxLevel">Maximum level</param>
/// <param name="skipChecks">Skip the secondary checks that validate the evolution</param>
/// <param name="maxSpeciesOrigin">Clamp for maximum species ID</param>
/// <param name="minLevel">Minimum level</param>
/// <returns></returns>
private List < EvoCriteria > GetExplicitLineage ( PKM pkm , int maxLevel , bool skipChecks , int maxSpeciesOrigin , int minLevel )
{
2019-12-07 03:00:41 +00:00
int species = pkm . Species ;
2020-12-11 04:42:30 +00:00
int form = pkm . Form ;
2019-12-06 06:32:47 +00:00
int lvl = maxLevel ;
2019-12-07 03:00:41 +00:00
var first = new EvoCriteria ( species , form ) { Level = lvl } ;
2019-12-06 06:32:47 +00:00
const int maxEvolutions = 3 ;
var dl = new List < EvoCriteria > ( maxEvolutions ) { first } ;
2019-12-07 03:00:41 +00:00
switch ( species )
{
case ( int ) Species . Silvally : form = 0 ;
break ;
}
2019-12-06 06:32:47 +00:00
// There aren't any circular evolution paths, and all lineages have at most 3 evolutions total.
// There aren't any convergent evolution paths, so only yield the first connection.
while ( true )
{
var key = GetLookupKey ( species , form ) ;
var node = Lineage [ key ] ;
bool oneValid = false ;
foreach ( var link in node )
{
2020-02-17 02:16:22 +00:00
if ( link . IsEvolutionBanned ( pkm ) & & ! skipChecks )
2019-12-06 06:32:47 +00:00
continue ;
var evo = link . Method ;
if ( ! evo . Valid ( pkm , lvl , skipChecks ) )
continue ;
if ( evo . RequiresLevelUp & & minLevel > = lvl )
break ; // impossible evolution
oneValid = true ;
2022-01-14 08:11:33 +00:00
UpdateMinValues ( dl , evo , minLevel ) ;
2019-12-06 06:32:47 +00:00
species = link . Species ;
form = link . Form ;
var detail = evo . GetEvoCriteria ( species , form , lvl ) ;
dl . Add ( detail ) ;
2022-01-14 08:11:33 +00:00
if ( evo . RequiresLevelUp )
lvl - - ;
2019-12-06 06:32:47 +00:00
break ;
}
if ( ! oneValid )
break ;
}
// Remove future gen pre-evolutions; no Munchlax from a Gen3 Snorlax, no Pichu from a Gen1-only Raichu, etc
2021-05-14 22:30:55 +00:00
var last = dl [ ^ 1 ] ;
2019-12-06 06:32:47 +00:00
if ( last . Species > maxSpeciesOrigin & & dl . Any ( d = > d . Species < = maxSpeciesOrigin ) )
dl . RemoveAt ( dl . Count - 1 ) ;
2022-01-14 08:11:33 +00:00
// Last species is the wild/hatched species, the minimum level is because it has not evolved from previous species
2021-05-14 22:30:55 +00:00
last = dl [ ^ 1 ] ;
2022-01-14 08:11:33 +00:00
last . MinLevel = minLevel ;
2019-12-06 06:32:47 +00:00
last . RequiresLvlUp = false ;
2022-01-14 08:11:33 +00:00
// Rectify minimum levels
for ( int i = dl . Count - 2 ; i > = 0 ; i - - )
{
var evo = dl [ i ] ;
var prev = dl [ i + 1 ] ;
evo . MinLevel = Math . Max ( prev . MinLevel + ( evo . RequiresLvlUp ? 1 : 0 ) , evo . MinLevel ) ;
}
2019-12-06 06:32:47 +00:00
return dl ;
}
2022-01-14 08:11:33 +00:00
private static void UpdateMinValues ( IReadOnlyList < EvoCriteria > dl , EvolutionMethod evo , int minLevel )
2019-12-06 06:32:47 +00:00
{
2021-05-14 22:30:55 +00:00
var last = dl [ ^ 1 ] ;
2019-12-06 06:32:47 +00:00
if ( ! evo . RequiresLevelUp )
{
// Evolutions like elemental stones, trade, etc
2022-01-14 08:11:33 +00:00
last . MinLevel = minLevel ;
2019-12-06 06:32:47 +00:00
return ;
}
if ( evo . Level = = 0 )
{
2022-01-14 08:11:33 +00:00
// Friendship based Level Up Evolutions, Pichu -> Pikachu, Eevee -> Umbreon, etc
last . MinLevel = minLevel + 1 ;
2019-12-06 06:32:47 +00:00
var first = dl [ 0 ] ;
if ( dl . Count > 1 & & ! first . RequiresLvlUp )
2022-01-14 08:11:33 +00:00
first . MinLevel = minLevel + 1 ; // Raichu from Pikachu would have a minimum level of 1; accounting for Pichu (level up required) results in a minimum level of 2
2019-12-06 06:32:47 +00:00
}
else // level up evolutions
{
last . MinLevel = evo . Level ;
var first = dl [ 0 ] ;
if ( dl . Count > 1 )
{
if ( first . RequiresLvlUp )
{
if ( first . MinLevel < = evo . Level )
first . MinLevel = evo . Level + 1 ; // Pokemon like Crobat, its minimum level is Golbat minimum level + 1
}
else
{
if ( first . MinLevel < evo . Level )
first . MinLevel = evo . Level ; // Pokemon like Nidoqueen who evolve with an evolution stone, minimum level is prior evolution minimum level
}
}
}
last . RequiresLvlUp = evo . RequiresLevelUp ;
}
/// <summary>
/// Links a <see cref="EvolutionMethod"/> to the source <see cref="Species"/> and <see cref="Form"/> that the method can be triggered from.
/// </summary>
private sealed class EvolutionLink
{
public readonly int Species ;
public readonly int Form ;
public readonly EvolutionMethod Method ;
public Func < PKM , bool > ? IsBanned { private get ; set ; }
public EvolutionLink ( int species , int form , EvolutionMethod method )
{
Species = species ;
Form = form ;
Method = method ;
}
/// <summary>
/// Checks if the <see cref="Method"/> is allowed.
/// </summary>
/// <param name="pkm">Entity to check</param>
/// <returns>True if banned, false if allowed.</returns>
public bool IsEvolutionBanned ( PKM pkm ) = > IsBanned ! = null & & IsBanned ( pkm ) ;
}
2016-11-08 16:43:57 +00:00
}
}