2016-11-08 16:43:57 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
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>
|
2016-11-08 16:43:57 +00:00
|
|
|
|
public class EvolutionTree
|
|
|
|
|
{
|
2017-07-29 18:54:52 +00:00
|
|
|
|
private static readonly EvolutionTree Evolves1;
|
|
|
|
|
private static readonly EvolutionTree Evolves2;
|
|
|
|
|
private static readonly EvolutionTree Evolves3;
|
|
|
|
|
private static readonly EvolutionTree Evolves4;
|
|
|
|
|
private static readonly EvolutionTree Evolves5;
|
|
|
|
|
private static readonly EvolutionTree Evolves6;
|
|
|
|
|
private static readonly EvolutionTree Evolves7;
|
|
|
|
|
|
|
|
|
|
static EvolutionTree()
|
|
|
|
|
{
|
|
|
|
|
// Evolution tables need Personal Tables initialized beforehand, hence why the EvolutionTree data is initialized here.
|
|
|
|
|
byte[] get(string resource) => Util.GetBinaryResource($"evos_{resource}.pkl");
|
|
|
|
|
byte[][] unpack(string resource) => Data.UnpackMini(get(resource), resource);
|
|
|
|
|
|
|
|
|
|
Evolves1 = new EvolutionTree(new[] { get("rby") }, GameVersion.RBY, PersonalTable.Y, Legal.MaxSpeciesID_1);
|
|
|
|
|
Evolves2 = new EvolutionTree(new[] { get("gsc") }, GameVersion.GSC, PersonalTable.C, Legal.MaxSpeciesID_2);
|
|
|
|
|
Evolves3 = new EvolutionTree(new[] { get("g3") }, GameVersion.RS, PersonalTable.RS, Legal.MaxSpeciesID_3);
|
|
|
|
|
Evolves4 = new EvolutionTree(new[] { get("g4") }, GameVersion.DP, PersonalTable.DP, Legal.MaxSpeciesID_4);
|
|
|
|
|
Evolves5 = new EvolutionTree(new[] { get("g5") }, GameVersion.BW, PersonalTable.BW, Legal.MaxSpeciesID_5);
|
|
|
|
|
Evolves6 = new EvolutionTree(unpack("ao"), GameVersion.ORAS, PersonalTable.AO, Legal.MaxSpeciesID_6);
|
2017-10-06 05:37:45 +00:00
|
|
|
|
Evolves7 = new EvolutionTree(unpack("uu"), GameVersion.USUM, PersonalTable.USUM, Legal.MaxSpeciesID_7_USUM);
|
2017-07-29 18:54:52 +00:00
|
|
|
|
}
|
|
|
|
|
internal static EvolutionTree GetEvolutionTree(int generation)
|
|
|
|
|
{
|
|
|
|
|
switch (generation)
|
|
|
|
|
{
|
|
|
|
|
case 1:
|
|
|
|
|
return Evolves1;
|
|
|
|
|
case 2:
|
|
|
|
|
return Evolves2;
|
|
|
|
|
case 3:
|
|
|
|
|
return Evolves3;
|
|
|
|
|
case 4:
|
|
|
|
|
return Evolves4;
|
|
|
|
|
case 5:
|
|
|
|
|
return Evolves5;
|
|
|
|
|
case 6:
|
|
|
|
|
return Evolves6;
|
|
|
|
|
default:
|
|
|
|
|
return Evolves7;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-08 16:43:57 +00:00
|
|
|
|
private List<EvolutionSet> Entries { get; } = new List<EvolutionSet>();
|
2016-11-17 16:36:45 +00:00
|
|
|
|
private readonly EvolutionLineage[] Lineage;
|
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;
|
2016-11-08 16:43:57 +00:00
|
|
|
|
|
2017-01-29 17:26:40 +00:00
|
|
|
|
public EvolutionTree(byte[][] 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;
|
2016-11-08 16:43:57 +00:00
|
|
|
|
switch (game)
|
|
|
|
|
{
|
2017-02-11 23:20:44 +00:00
|
|
|
|
case GameVersion.RBY:
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Entries = EvolutionSet1.GetArray(data[0], maxSpeciesTree);
|
2017-02-25 20:37:01 +00:00
|
|
|
|
break;
|
|
|
|
|
case GameVersion.GSC:
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Entries = EvolutionSet2.GetArray(data[0], maxSpeciesTree);
|
2016-11-08 16:43:57 +00:00
|
|
|
|
break;
|
2017-03-19 12:33:20 +00:00
|
|
|
|
case GameVersion.RS:
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Entries = EvolutionSet3.GetArray(data[0]);
|
2017-03-19 12:27:23 +00:00
|
|
|
|
break;
|
2017-03-19 12:33:20 +00:00
|
|
|
|
case GameVersion.DP:
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Entries = EvolutionSet4.GetArray(data[0]);
|
2017-03-19 12:27:23 +00:00
|
|
|
|
break;
|
2017-03-19 12:33:20 +00:00
|
|
|
|
case GameVersion.BW:
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Entries = EvolutionSet5.GetArray(data[0]);
|
2017-03-19 12:27:23 +00:00
|
|
|
|
break;
|
2016-11-08 16:43:57 +00:00
|
|
|
|
case GameVersion.ORAS:
|
|
|
|
|
Entries.AddRange(data.Select(d => new EvolutionSet6(d)));
|
|
|
|
|
break;
|
2017-09-01 05:37:28 +00:00
|
|
|
|
case GameVersion.USUM:
|
2017-02-11 23:20:44 +00:00
|
|
|
|
Entries.AddRange(data.Select(d => new EvolutionSet7(d)));
|
|
|
|
|
break;
|
2016-11-08 16:43:57 +00:00
|
|
|
|
}
|
2018-05-12 15:13:39 +00:00
|
|
|
|
|
2016-11-08 16:43:57 +00:00
|
|
|
|
// Create Lineages
|
|
|
|
|
Lineage = new EvolutionLineage[Entries.Count];
|
|
|
|
|
for (int i = 0; i < Entries.Count; i++)
|
|
|
|
|
Lineage[i] = new EvolutionLineage();
|
2016-11-17 16:36:45 +00:00
|
|
|
|
if (Game == GameVersion.ORAS)
|
2017-02-11 23:20:44 +00:00
|
|
|
|
Array.Resize(ref Lineage, MaxSpeciesTree + 1);
|
2016-11-08 16:43:57 +00:00
|
|
|
|
|
|
|
|
|
// Populate Lineages
|
|
|
|
|
for (int i = 1; i < Lineage.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
// Iterate over all possible evolutions
|
|
|
|
|
var s = Entries[i];
|
|
|
|
|
foreach (EvolutionMethod evo in s.PossibleEvolutions)
|
|
|
|
|
{
|
2017-06-18 01:37:19 +00:00
|
|
|
|
int index = GetIndex(evo);
|
2016-11-08 16:43:57 +00:00
|
|
|
|
if (index < 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var sourceEvo = evo.Copy(i);
|
|
|
|
|
|
|
|
|
|
Lineage[index].Insert(sourceEvo);
|
|
|
|
|
// If current entries has a pre-evolution, propagate to evolution as well
|
|
|
|
|
if (Lineage[i].Chain.Count > 0)
|
|
|
|
|
Lineage[index].Insert(Lineage[i].Chain[0]);
|
|
|
|
|
|
|
|
|
|
if (index >= i) continue;
|
|
|
|
|
// If destination species evolves into something (ie a 'baby' Pokemon like Cleffa)
|
|
|
|
|
// Add it to the corresponding parent chains
|
|
|
|
|
foreach (EvolutionMethod mid in Entries[index].PossibleEvolutions)
|
|
|
|
|
{
|
2017-06-18 01:37:19 +00:00
|
|
|
|
int newIndex = GetIndex(mid);
|
2016-11-08 16:43:57 +00:00
|
|
|
|
if (newIndex < 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
Lineage[newIndex].Insert(sourceEvo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-06-18 01:37:19 +00:00
|
|
|
|
FixEvoTreeManually();
|
2016-11-15 02:25:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There's always oddballs.
|
2017-06-18 01:37:19 +00:00
|
|
|
|
private void FixEvoTreeManually()
|
2016-11-15 02:25:08 +00:00
|
|
|
|
{
|
2017-09-02 15:41:36 +00:00
|
|
|
|
if (Game == GameVersion.USUM)
|
2017-06-18 01:37:19 +00:00
|
|
|
|
FixEvoTreeSM();
|
2016-11-15 02:25:08 +00:00
|
|
|
|
}
|
2017-06-18 01:37:19 +00:00
|
|
|
|
private void FixEvoTreeSM()
|
2016-11-15 02:25:08 +00:00
|
|
|
|
{
|
2017-02-02 06:19:37 +00:00
|
|
|
|
// Wormadam -- Copy Burmy 0 to Wormadam-1/2
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Lineage[Personal.GetFormeIndex(413, 1)].Chain.Insert(0, Lineage[413].Chain[0]);
|
|
|
|
|
Lineage[Personal.GetFormeIndex(413, 2)].Chain.Insert(0, Lineage[413].Chain[0]);
|
2017-02-02 06:19:37 +00:00
|
|
|
|
|
2016-11-15 02:25:08 +00:00
|
|
|
|
// Shellos -- Move Shellos-1 evo from Gastrodon-0 to Gastrodon-1
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Lineage[Personal.GetFormeIndex(422 + 1, 1)].Chain.Insert(0, Lineage[422 + 1].Chain[0]);
|
2016-11-15 02:25:08 +00:00
|
|
|
|
Lineage[422+1].Chain.RemoveAt(0);
|
|
|
|
|
|
2017-06-11 00:36:33 +00:00
|
|
|
|
// Meowstic -- Meowstic-1 (F) should point back to Espurr, copy Meowstic-0 (M)
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Lineage[Personal.GetFormeIndex(678, 1)].Chain.Insert(0, Lineage[678].Chain[0]);
|
2017-06-11 00:36:33 +00:00
|
|
|
|
|
|
|
|
|
// Floette doesn't contain evo info for forms 1-4, copy. Florges points to form 0, no fix needed.
|
|
|
|
|
var fbb = Lineage[669+1].Chain[0];
|
|
|
|
|
for (int i = 1; i <= 4; i++) // NOT AZ
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Lineage[Personal.GetFormeIndex(669+1, i)].Chain.Insert(0, fbb);
|
2017-06-17 01:45:26 +00:00
|
|
|
|
// Clear forme chains from Florges
|
|
|
|
|
Lineage[671].Chain.RemoveRange(0, Lineage[671].Chain.Count - 2);
|
2017-06-11 00:36:33 +00:00
|
|
|
|
|
2016-11-15 02:25:08 +00:00
|
|
|
|
// Gourgeist -- Sizes are still relevant. Formes are in reverse order.
|
|
|
|
|
for (int i = 1; i <= 3; i++)
|
|
|
|
|
{
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Lineage[Personal.GetFormeIndex(711, i)].Chain.Clear();
|
|
|
|
|
Lineage[Personal.GetFormeIndex(711, i)].Chain.Add(Lineage[711].Chain[3-i]);
|
2016-11-15 02:25:08 +00:00
|
|
|
|
}
|
|
|
|
|
Lineage[711].Chain.RemoveRange(0, 3);
|
2016-11-15 05:44:55 +00:00
|
|
|
|
|
2017-11-10 03:47:01 +00:00
|
|
|
|
// Ban Raichu Evolution on SM
|
|
|
|
|
Lineage[Personal.GetFormeIndex(26, 0)]
|
|
|
|
|
.Chain[1].StageEntryMethods[0]
|
|
|
|
|
.Banlist = EvolutionMethod.BanSM;
|
|
|
|
|
// Ban Exeggutor Evolution on SM
|
|
|
|
|
Lineage[Personal.GetFormeIndex(103, 0)]
|
|
|
|
|
.Chain[0].StageEntryMethods[0]
|
|
|
|
|
.Banlist = EvolutionMethod.BanSM;
|
|
|
|
|
// Ban Marowak Evolution on SM
|
|
|
|
|
Lineage[Personal.GetFormeIndex(105, 0)]
|
|
|
|
|
.Chain[0].StageEntryMethods[0]
|
|
|
|
|
.Banlist = EvolutionMethod.BanSM;
|
2016-11-15 02:25:08 +00:00
|
|
|
|
}
|
2016-11-08 16:43:57 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
private int GetIndex(PKM pkm)
|
2016-11-08 16:43:57 +00:00
|
|
|
|
{
|
|
|
|
|
if (pkm.Format < 7)
|
|
|
|
|
return pkm.Species;
|
2017-06-18 01:37:19 +00:00
|
|
|
|
return Personal.GetFormeIndex(pkm.Species, pkm.AltForm);
|
2016-11-08 16:43:57 +00:00
|
|
|
|
}
|
2017-06-18 01:37:19 +00:00
|
|
|
|
private int GetIndex(EvolutionMethod evo)
|
2016-11-08 16:43:57 +00:00
|
|
|
|
{
|
|
|
|
|
int evolvesToSpecies = evo.Species;
|
|
|
|
|
if (evolvesToSpecies == 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (Personal == null)
|
|
|
|
|
return evolvesToSpecies;
|
|
|
|
|
|
|
|
|
|
int evolvesToForm = evo.Form;
|
|
|
|
|
if (evolvesToForm < 0)
|
|
|
|
|
evolvesToForm = 0;
|
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
return Personal.GetFormeIndex(evolvesToSpecies, evolvesToForm);
|
2016-11-08 16:43:57 +00:00
|
|
|
|
}
|
2018-06-11 00:55:03 +00:00
|
|
|
|
public List<DexLevel> GetValidPreEvolutions(PKM pkm, int maxLevel, int maxSpeciesOrigin = -1, bool skipChecks = false, int minLevel = 1)
|
2016-11-08 16:43:57 +00:00
|
|
|
|
{
|
2017-06-18 01:37:19 +00:00
|
|
|
|
int index = GetIndex(pkm);
|
2017-04-15 02:55:40 +00:00
|
|
|
|
if (maxSpeciesOrigin <= 0)
|
2017-06-18 01:37:19 +00:00
|
|
|
|
maxSpeciesOrigin = Legal.GetMaxSpeciesOrigin(pkm);
|
2017-08-29 01:12:57 +00:00
|
|
|
|
return Lineage[index].GetExplicitLineage(pkm, maxLevel, skipChecks, MaxSpeciesTree, maxSpeciesOrigin, minLevel);
|
2016-11-08 16:43:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|