Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/kwsch/PKHeX/llms.txt

Use this file to discover all available pages before exploring further.

The evolution system in PKHeX handles evolution chain construction, validation, and reverse lookup across all generations.

Core Classes

EvolutionTree

Generation-specific evolution data.
public sealed class EvolutionTree : EvolutionNetwork
{
    // Static instances for each generation
    public static readonly EvolutionTree Evolves1;   // Gen 1
    public static readonly EvolutionTree Evolves2;   // Gen 2
    public static readonly EvolutionTree Evolves3;   // Gen 3
    public static readonly EvolutionTree Evolves4;   // Gen 4
    public static readonly EvolutionTree Evolves5;   // Gen 5
    public static readonly EvolutionTree Evolves6;   // Gen 6
    public static readonly EvolutionTree Evolves7;   // Gen 7 (SM/USUM)
    public static readonly EvolutionTree Evolves7b;  // Let's Go
    public static readonly EvolutionTree Evolves8;   // Sword/Shield
    public static readonly EvolutionTree Evolves8a;  // Legends Arceus
    public static readonly EvolutionTree Evolves8b;  // BDSP
    public static readonly EvolutionTree Evolves9;   // Scarlet/Violet
    public static readonly EvolutionTree Evolves9a;  // Legends Z-A
    
    public static EvolutionTree GetEvolutionTree(EntityContext context);
}
Source: PKHeX.Core/Legality/Evolutions/EvolutionTree.cs Usage:
// Get evolution tree for Scarlet/Violet
var tree = EvolutionTree.GetEvolutionTree(EntityContext.Gen9);

// Get evolution tree from context
var tree = EvolutionTree.GetEvolutionTree(pk.Context);

EvolutionChain

Builds evolution histories for Pokémon.
public static class EvolutionChain
{
    // Build complete evolution history
    public static EvolutionHistory GetEvolutionChainsAllGens(
        PKM pk, 
        IEncounterTemplate enc
    );
    
    // Get possible original forms
    public static EvoCriteria[] GetOriginChain(
        PKM pk, 
        EvolutionOrigin enc, 
        ushort encSpecies = 0, 
        bool discard = true
    );
}
Source: PKHeX.Core/Legality/Evolutions/EvolutionChain.cs Example:
var pk = new PK9 { Species = (int)Species.Charizard, CurrentLevel = 50 };
var enc = new EncounterStatic9 { Species = (int)Species.Charmander, Level = 5 };

// Build evolution history across all generations
var history = EvolutionChain.GetEvolutionChainsAllGens(pk, enc);

// Access generation-specific chains
var gen9Chain = history.Gen9;  // Charmander -> Charmeleon -> Charizard
var gen8Chain = history.Gen8;  // Same chain if transferred

EvolutionHistory

Stores evolution chains for all generations.
public sealed class EvolutionHistory
{
    public EvoCriteria[] Gen1 { get; set; } = [];
    public EvoCriteria[] Gen2 { get; set; } = [];
    public EvoCriteria[] Gen3 { get; set; } = [];
    public EvoCriteria[] Gen4 { get; set; } = [];
    public EvoCriteria[] Gen5 { get; set; } = [];
    public EvoCriteria[] Gen6 { get; set; } = [];
    public EvoCriteria[] Gen7 { get; set; } = [];
    public EvoCriteria[] Gen7b { get; set; } = [];
    public EvoCriteria[] Gen8 { get; set; } = [];
    public EvoCriteria[] Gen8a { get; set; } = [];
    public EvoCriteria[] Gen8b { get; set; } = [];
    public EvoCriteria[] Gen9 { get; set; } = [];
    public EvoCriteria[] Gen9a { get; set; } = [];
}
Source: PKHeX.Core/Legality/Evolutions/EvolutionHistory.cs

EvoCriteria

Represents a single evolution state.
public struct EvoCriteria
{
    public ushort Species { get; set; }
    public byte Form { get; set; }
    public byte LevelMin { get; set; }  // Earliest level in this form
    public byte LevelMax { get; set; }  // Latest level in this form
    public byte Method { get; set; }    // Evolution method used
}
Example:
var evo = new EvoCriteria 
{
    Species = (int)Species.Charmeleon,
    Form = 0,
    LevelMin = 16,  // Evolved from Charmander at 16
    LevelMax = 35,  // Evolved to Charizard at 36
};

EvolutionOrigin

Defines the starting point for evolution chain building.
public readonly record struct EvolutionOrigin(
    ushort Species,
    EntityContext Context,
    byte Generation,
    byte LevelMin,
    byte LevelMax
);
Source: PKHeX.Core/Legality/Evolutions/EncounterOrigin.cs

Evolution Methods

Evolution methods are defined per generation:
public abstract class EvolutionMethod
{
    public EvolutionType Type { get; init; }  // Level, Item, Trade, etc.
    public ushort Argument { get; init; }     // Level, Item ID, etc.
    public ushort Species { get; init; }      // Evolves into
    public byte Form { get; init; }           // Evolves into (form)
    public byte Level { get; init; }          // Minimum level
}

Common Evolution Types

TypeDescriptionArgument
LevelUpLevel upMinimum level
LevelUpFriendshipLevel with high friendshipMinimum level
LevelUpNatureLevel with specific natureNature ID
UseItemUse evolution stone/itemItem ID
TradeTrade evolution0 or held item
LevelUpMoveLevel knowing specific moveMove ID
LevelUpVersionLevel in specific gameGame ID
LevelUpTimeLevel at specific timeTime (day/night)

Evolution Chain Building

Forward Evolution (Devolve)

Building from current species back to base form:
public static EvoCriteria[] GetOriginChain(PKM pk, EvolutionOrigin enc)
{
    Span<EvoCriteria> result = stackalloc EvoCriteria[EvolutionTree.MaxEvolutions];
    
    // Start with current species
    result[0] = new EvoCriteria 
    { 
        Species = pk.Species, 
        Form = pk.Form, 
        LevelMax = pk.CurrentLevel 
    };
    
    // Devolve backwards to base form
    var count = DevolveFrom(result, pk, enc, pk.Context, encSpecies, discard);
    
    return result[..count].ToArray();
}

Reverse Evolution (Evolve)

Building from base form to current species:
public void Evolve(
    ReadOnlySpan<EvoCriteria> chain, 
    PKM pk, 
    EvolutionOrigin enc, 
    EvolutionHistory history
)
{
    // For each form in the chain
    foreach (var evo in chain)
    {
        // Check if can evolve from previous form
        var next = GetEvolutions(evo.Species, evo.Form);
        // Store evolution path
        history.AddEvolution(evo);
    }
}

Evolution Validation

Level Requirements

Validates evolution happened at valid level:
public static bool IsLevelValid(PKM pk, EvoCriteria evo)
{
    var met = pk.MetLevel;
    var current = pk.CurrentLevel;
    
    // Must be within evolution level range
    if (current < evo.LevelMin)
        return false;
    
    // Met level must be before evolution
    if (met > evo.LevelMax)
        return false;
    
    return true;
}

Form Changes

Distinguishes between evolution and form change:
public static bool IsFormChangeable(
    ushort species, 
    byte form, 
    byte toForm, 
    EntityContext context, 
    EntityContext toContext
)
{
    // Battle forms (Mega, Primal, etc.)
    if (IsBattleOnlyForm(species, form, toForm))
        return true;
    
    // Time-based (Shaymin, Hoopa)
    if (IsTimeBasedForm(species, form, toForm))
        return true;
    
    // Regional forms are NOT changeable
    if (IsRegionalForm(species, form, toForm))
        return false;
    
    return false;
}

Cross-Generation Evolution

Handles evolutions that span multiple generations:
private static EvolutionHistory EvolutionChainsSearch(
    PKM pk, 
    EvolutionOrigin enc, 
    EntityContext context, 
    ushort encSpecies, 
    Span<EvoCriteria> chain
)
{
    var history = new EvolutionHistory();
    
    // Special case: Gen 2 -> Gen 1 devolution
    if (context == EntityContext.Gen2)
    {
        EvolutionGroup2.Instance.Evolve(chain, pk, enc, history);
        EvolutionGroup1.Instance.Evolve(chain, pk, enc, history);
        
        if (pk.Format > 2)
            context = EntityContext.Gen7; // Skip to Gen 7
        else
            return history;
    }
    
    // Evolve through each generation
    var group = EvolutionGroupUtil.GetGroup(context);
    while (true)
    {
        group.Evolve(chain, pk, enc, history);
        var next = group.GetNext(pk, enc);
        if (next is null)
            break;
        group = next;
    }
    
    return history;
}

Special Evolution Cases

Nincada → Shedinja

Shedinja appears in empty party slot when Nincada evolves:
if (enc.Species == (int)Species.Nincada && pk.Species == (int)Species.Shedinja)
{
    // Shedinja inherits ball in Gen 3 only
    if (info.EvoChainsAllGens.Gen3.Length == 2)
        return VerifyBall(enc, current, pk);
    
    // Gen 4+ reverts to Poké Ball
    return VerifyBallEquals(current, Ball.Poke);
}

Regional Form Evolutions

Alolan/Galarian/Paldean forms:
// Pikachu (Alola) -> Raichu (Alola)
if (enc.Form != evo.Form && IsRegionalForm(enc.Species))
{
    // Regional form must match encounter form
    if (!IsRegionalFormEvolution(enc.Species, enc.Form, evo.Form))
        return false;
}

Trade Evolutions with Items

public sealed record EvolutionMethodTrade : EvolutionMethod
{
    public ushort HeldItem { get; init; }  // Required held item (0 = none)
    
    public bool IsValid(PKM pk) =>
        HeldItem == 0 || pk.HeldItem == HeldItem;
}

Evolution Groups

Evolution logic is grouped by generation:
public interface IEvolutionGroup
{
    void Evolve(
        ReadOnlySpan<EvoCriteria> chain, 
        PKM pk, 
        EvolutionOrigin enc, 
        EvolutionHistory history
    );
    
    void Devolve(
        Span<EvoCriteria> result, 
        PKM pk, 
        EvolutionOrigin enc
    );
    
    IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc);
    IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc);
}
Implementations:
  • EvolutionGroup1 - Gen 1 (RBY)
  • EvolutionGroup2 - Gen 2 (GSC)
  • EvolutionGroup3 - Gen 3 (RSE/FRLG)
  • EvolutionGroup4 - Gen 4 (DPPt/HGSS)
  • EvolutionGroup5 - Gen 5 (BW/B2W2)
  • EvolutionGroup6 - Gen 6 (XY/ORAS)
  • EvolutionGroup7 - Gen 7 (SM/USUM)
  • EvolutionGroup7b - Let’s Go
  • EvolutionGroup8 - Gen 8 (SwSh)
  • EvolutionGroup8a - Legends Arceus
  • EvolutionGroup8b - BDSP
  • EvolutionGroup9 - Gen 9 (SV)
  • EvolutionGroup9a - Legends Z-A

Example: Complete Evolution Chain

// Build complete evolution history
public EvolutionHistory GetCompleteHistory(PK9 pk, IEncounterTemplate enc)
{
    // Get the evolution chains
    var history = EvolutionChain.GetEvolutionChainsAllGens(pk, enc);
    
    // Gen 9 chain (current)
    foreach (var evo in history.Gen9)
    {
        Console.WriteLine($"{evo.Species} (Form {evo.Form}) Lv.{evo.LevelMin}-{evo.LevelMax}");
    }
    
    // Gen 8 chain (if transferred from SwSh)
    if (history.Gen8.Length > 0)
    {
        foreach (var evo in history.Gen8)
        {
            Console.WriteLine($"Gen8: {evo.Species}");
        }
    }
    
    return history;
}
Output:
004 (Form 0) Lv.5-15   # Charmander
005 (Form 0) Lv.16-35  # Charmeleon  
006 (Form 0) Lv.36-50  # Charizard