mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-22 20:13:06 +00:00
Refactor EvoCriteria to be a struct, reduce allocation (#3483)
* Make EvolutionCriteria struct 8 bytes per object instead of 26 Unify LevelMin/LevelMax to match EncounterTemplate bubble up precise array type for better iteration * Inline queue operations, less allocation * Inline some logic * Update EvolutionChain.cs * Improve clarity on duplicate move check * Search reverse For a dual stage chain, finds it first iteration rather than second.
This commit is contained in:
parent
959b9e998b
commit
ef3cb34387
77 changed files with 349 additions and 300 deletions
|
@ -28,6 +28,6 @@ namespace PKHeX.Core
|
|||
public virtual bool IsMatchLocation(int location) => Location == location;
|
||||
|
||||
public bool HasSpecies(int species) => Raw.Any(z => z.Species == species);
|
||||
public IEnumerable<EncounterSlot> GetSpecies(IReadOnlyList<DexLevel> chain) => Raw.Where(z => chain.Any(c => z.Species == c.Species));
|
||||
public IEnumerable<EncounterSlot> GetSpecies(IReadOnlyList<EvoCriteria> chain) => Raw.Where(z => chain.Any(c => z.Species == c.Species));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace PKHeX.Core
|
|||
if (slot.Species != evo.Species)
|
||||
continue;
|
||||
|
||||
if (slot.LevelMin > evo.Level)
|
||||
if (slot.LevelMin > evo.LevelMax)
|
||||
break;
|
||||
if (slot.Form != evo.Form)
|
||||
break;
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace PKHeX.Core
|
|||
if (slot.Species != (int) Species.Unown || evo.Form >= 26) // Don't yield !? forms
|
||||
break;
|
||||
}
|
||||
if (slot.LevelMin > evo.Level)
|
||||
if (slot.LevelMin > evo.LevelMax)
|
||||
break;
|
||||
|
||||
yield return slot;
|
||||
|
|
|
@ -153,7 +153,7 @@ namespace PKHeX.Core
|
|||
|
||||
if (slot.Form != evo.Form)
|
||||
break;
|
||||
if (slot.LevelMin > evo.Level)
|
||||
if (slot.LevelMin > evo.LevelMax)
|
||||
break;
|
||||
|
||||
yield return slot;
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace PKHeX.Core
|
|||
|
||||
if (slot.Form != evo.Form)
|
||||
break;
|
||||
if (slot.LevelMin > evo.Level)
|
||||
if (slot.LevelMin > evo.LevelMax)
|
||||
break;
|
||||
|
||||
yield return slot;
|
||||
|
|
|
@ -158,7 +158,7 @@ namespace PKHeX.Core
|
|||
if (!slot.IsRandomUnspecificForm)
|
||||
break;
|
||||
}
|
||||
if (slot.LevelMin > evo.Level)
|
||||
if (slot.LevelMin > evo.LevelMax)
|
||||
break;
|
||||
|
||||
yield return slot;
|
||||
|
|
|
@ -79,9 +79,9 @@ namespace PKHeX.Core
|
|||
// Track some metadata about how this slot was matched.
|
||||
var clone = slot with
|
||||
{
|
||||
WhiteFlute = evo.MinLevel < slot.LevelMin,
|
||||
BlackFlute = evo.MinLevel > slot.LevelMax && evo.MinLevel <= slot.LevelMax + FluteBoostMax,
|
||||
DexNav = slot.CanDexNav && (evo.MinLevel != slot.LevelMax || pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4),
|
||||
WhiteFlute = evo.LevelMin < slot.LevelMin,
|
||||
BlackFlute = evo.LevelMin > slot.LevelMax && evo.LevelMin <= slot.LevelMax + FluteBoostMax,
|
||||
DexNav = slot.CanDexNav && (evo.LevelMin != slot.LevelMax || pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4),
|
||||
};
|
||||
yield return clone;
|
||||
break;
|
||||
|
|
|
@ -139,7 +139,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
private bool ExistsPressureSlot(DexLevel evo, ref byte level)
|
||||
private bool ExistsPressureSlot(IDexLevel evo, ref byte level)
|
||||
{
|
||||
bool existsForm = false;
|
||||
foreach (var z in Slots)
|
||||
|
|
|
@ -73,11 +73,11 @@ namespace PKHeX.Core
|
|||
// Since it is possible to evolve before transferring, we only need the highest evolution species possible.
|
||||
// PoGoEncTool has already extrapolated the evolutions to separate encounters!
|
||||
var sf = chain.FirstOrDefault(z => z.Species == Species && z.Form == Form);
|
||||
if (sf == null)
|
||||
if (sf == default)
|
||||
yield break;
|
||||
|
||||
var stamp = EncounterSlotGO.GetTimeStamp(pkm.Met_Year + 2000, pkm.Met_Month, pkm.Met_Day);
|
||||
var met = Math.Max(sf.MinLevel, pkm.Met_Level);
|
||||
var met = Math.Max(sf.LevelMin, pkm.Met_Level);
|
||||
EncounterSlot7GO? deferredIV = null;
|
||||
|
||||
foreach (var slot in Slots)
|
||||
|
|
|
@ -103,11 +103,11 @@ namespace PKHeX.Core
|
|||
// Since it is possible to evolve before transferring, we only need the highest evolution species possible.
|
||||
// PoGoEncTool has already extrapolated the evolutions to separate encounters!
|
||||
var sf = chain.FirstOrDefault(z => z.Species == Species && (z.Form == Form || FormInfo.IsFormChangeable(Species, Form, z.Form, pkm.Format)));
|
||||
if (sf == null)
|
||||
if (sf == default)
|
||||
yield break;
|
||||
|
||||
var ball = (Ball)pkm.Ball;
|
||||
var met = Math.Max(sf.MinLevel, pkm.Met_Level);
|
||||
var met = Math.Max(sf.LevelMin, pkm.Met_Level);
|
||||
EncounterSlot8GO? deferredIV = null;
|
||||
|
||||
foreach (var slot in Slots)
|
||||
|
|
|
@ -196,7 +196,7 @@ namespace PKHeX.Core
|
|||
return LegalityCheckStrings.LEncCondition;
|
||||
}
|
||||
|
||||
public bool IsMatchExact(PKM pkm, DexLevel dl) => true; // Matched by Area
|
||||
public bool IsMatchExact(PKM pkm, IDexLevel evo) => true; // Matched by Area
|
||||
|
||||
public virtual EncounterMatchRating GetMatchRating(PKM pkm)
|
||||
{
|
||||
|
|
|
@ -214,7 +214,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
public virtual bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public virtual bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (Nature != Nature.Random && pkm.Nature != (int) Nature)
|
||||
return false;
|
||||
|
@ -256,7 +256,7 @@ namespace PKHeX.Core
|
|||
return Legal.GetIsFixedIVSequenceValidSkipRand((int[])IVs, pkm);
|
||||
}
|
||||
|
||||
protected virtual bool IsMatchForm(PKM pkm, DexLevel evo)
|
||||
protected virtual bool IsMatchForm(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (IsRandomUnspecificForm)
|
||||
return true;
|
||||
|
@ -288,7 +288,7 @@ namespace PKHeX.Core
|
|||
return Location == pkm.Met_Location;
|
||||
}
|
||||
|
||||
protected virtual bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected virtual bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
return pkm.Met_Level == Level;
|
||||
}
|
||||
|
|
|
@ -33,11 +33,11 @@
|
|||
pk1.Catch_Rate = table[Species].CatchRate;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
// Met Level is not stored in the PK1 format.
|
||||
// Check if it is at or above the encounter level.
|
||||
return Level <= evo.Level;
|
||||
return Level <= evo.LevelMax;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLocation(PKM pkm)
|
||||
|
@ -46,7 +46,7 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (!base.IsMatchExact(pkm, evo))
|
||||
return false;
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (!base.IsMatchExact(pkm, evo))
|
||||
return false;
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace PKHeX.Core
|
|||
Level = level;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (Shiny == Shiny.Always && !pkm.IsShiny)
|
||||
return false;
|
||||
|
@ -56,12 +56,12 @@ namespace PKHeX.Core
|
|||
return true;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm is ICaughtData2 {CaughtData: not 0})
|
||||
return pkm.Met_Level == (EggEncounter ? 1 : Level);
|
||||
|
||||
return Level <= evo.Level;
|
||||
return Level <= evo.LevelMax;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLocation(PKM pkm)
|
||||
|
@ -104,7 +104,7 @@ namespace PKHeX.Core
|
|||
EggCycles = 20;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
// Let it get picked up as regular EncounterEgg under other conditions.
|
||||
if (pkm.Format > 2)
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (!base.IsMatchExact(pkm, evo))
|
||||
return false;
|
||||
|
|
|
@ -25,10 +25,10 @@ namespace PKHeX.Core
|
|||
return pkm.Egg_Location == 0;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Format != 3) // Met Level lost on PK3=>PK4
|
||||
return Level <= evo.Level;
|
||||
return Level <= evo.LevelMax;
|
||||
|
||||
if (EggEncounter)
|
||||
return pkm.Met_Level == 0 && pkm.CurrentLevel >= 5; // met level 0, origin level 5
|
||||
|
|
|
@ -75,10 +75,10 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Format != 4) // Met Level lost on PK4=>PK5
|
||||
return Level <= evo.Level;
|
||||
return Level <= evo.LevelMax;
|
||||
|
||||
return pkm.Met_Level == (EggEncounter ? 0 : Level);
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
return true; // transfer location verified later
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Format != 4) // Met Level lost on PK4=>PK5
|
||||
return Level <= evo.Level;
|
||||
return Level <= evo.LevelMax;
|
||||
|
||||
return pkm.Met_Level == Level;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
Shiny = Shiny.Never;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
// Level from 5->40 depends on the number of badges
|
||||
var met = pkm.Met_Level;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
pk.RefreshAbility(ability);
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (PID != pkm.PID)
|
||||
return false;
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace PKHeX.Core
|
|||
return true;
|
||||
}
|
||||
|
||||
protected override bool IsMatchForm(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchForm(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (IsTotem)
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace PKHeX.Core
|
|||
|
||||
public AreaWeather8 Weather {get; init; } = AreaWeather8.Normal;
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
var met = pkm.Met_Level;
|
||||
var lvl = Level;
|
||||
|
@ -29,7 +29,7 @@ namespace PKHeX.Core
|
|||
return false;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm is IDynamaxLevel d && d.DynamaxLevel < DynamaxLevel)
|
||||
return false;
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace PKHeX.Core
|
|||
55, 60, // 4
|
||||
};
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
var met = pkm.Met_Level;
|
||||
var metLevel = met - 15;
|
||||
|
@ -87,7 +87,7 @@ namespace PKHeX.Core
|
|||
return loc == SharedNest || (loc <= 255 && NestLocations.Contains((byte)loc));
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.FlawlessIVCount < FlawlessIVCount)
|
||||
return false;
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace PKHeX.Core
|
|||
return loc is SharedNest or Watchtower;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
var lvl = pkm.Met_Level;
|
||||
if (lvl == Level)
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace PKHeX.Core
|
|||
FlawlessIVCount = flawless;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
var lvl = pkm.Met_Level;
|
||||
|
||||
|
@ -62,7 +62,7 @@ namespace PKHeX.Core
|
|||
};
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.FlawlessIVCount < FlawlessIVCount)
|
||||
return false;
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace PKHeX.Core
|
|||
public byte DynamaxLevel { get; set; }
|
||||
public override int Location { get => SharedNest; init { } }
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm is IDynamaxLevel d && d.DynamaxLevel < DynamaxLevel)
|
||||
return false;
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace PKHeX.Core
|
|||
FlawlessIVCount = 4;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.FlawlessIVCount < FlawlessIVCount)
|
||||
return false;
|
||||
|
@ -28,6 +28,6 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
// no downleveling, unlike all other raids
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo) => pkm.Met_Level == Level;
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo) => pkm.Met_Level == Level;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
|
|||
|
||||
protected override void ApplyDetailsBall(PKM pk) => pk.Ball = Gift ? Ball : (int)Core.Ball.LAPoke;
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (!base.IsMatchExact(pkm, evo))
|
||||
return false;
|
||||
|
|
|
@ -36,10 +36,10 @@ namespace PKHeX.Core
|
|||
return Version == GameVersion.XD && met is (59 or 90 or 91 or 92 or 113);
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
protected override bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Format != 3) // Met Level lost on PK3=>PK4
|
||||
return Level <= evo.Level;
|
||||
return Level <= evo.LevelMax;
|
||||
|
||||
return pkm.Met_Level == Level;
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ namespace PKHeX.Core
|
|||
pk.MetDate = time;
|
||||
}
|
||||
|
||||
public virtual bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public virtual bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (IVs.Count != 0)
|
||||
{
|
||||
|
@ -213,16 +213,16 @@ namespace PKHeX.Core
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool IsMatchLevel(PKM pkm, DexLevel evo)
|
||||
private bool IsMatchLevel(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (!pkm.HasOriginalMetLocation)
|
||||
return evo.Level >= Level;
|
||||
return evo.LevelMax >= Level;
|
||||
|
||||
if (Location != pkm.Met_Location)
|
||||
return false;
|
||||
|
||||
if (pkm.Format < 5)
|
||||
return evo.Level >= Level;
|
||||
return evo.LevelMax >= Level;
|
||||
|
||||
return pkm.Met_Level == Level;
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace PKHeX.Core
|
|||
return lvl >= LevelMin;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel dl)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel dl)
|
||||
{
|
||||
if (!IsMatchLevel(pkm, pkm.CurrentLevel)) // minimum required level
|
||||
return false;
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace PKHeX.Core
|
|||
TID = tid;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel dl)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel dl)
|
||||
{
|
||||
if (Level > pkm.CurrentLevel) // minimum required level
|
||||
return false;
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace PKHeX.Core
|
|||
Level = level;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (!base.IsMatchExact(pkm, evo))
|
||||
return false;
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
public int MetLocation { get; init; }
|
||||
public override int Location => MetLocation == default ? Locations.LinkTrade4NPC : MetLocation;
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (!base.IsMatchExact(pkm, evo))
|
||||
return false;
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace PKHeX.Core
|
|||
OT_Intensity = intensity;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm is IDynamaxLevel d && d.DynamaxLevel < DynamaxLevel)
|
||||
return false;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
public uint PID { get; init; }
|
||||
public uint EncryptionConstant { get; init; }
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.EncryptionConstant != EncryptionConstant)
|
||||
return false;
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
Level = level;
|
||||
}
|
||||
|
||||
public abstract override bool IsMatchExact(PKM pkm, DexLevel dl);
|
||||
public abstract override bool IsMatchExact(PKM pkm, IDexLevel dl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
var curr = EvolutionChain.GetValidPreEvolutions(pkm, minLevel: 5);
|
||||
var poss = EvolutionChain.GetValidPreEvolutions(pkm, maxLevel: 100, minLevel: 5, skipChecks: true);
|
||||
return curr.Count >= poss.Count;
|
||||
return curr.Length >= poss.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static class EncounterSlotGenerator
|
||||
{
|
||||
public static IEnumerable<EncounterSlot> GetPossible(PKM pkm, IReadOnlyList<DexLevel> chain, GameVersion gameSource = Any)
|
||||
public static IEnumerable<EncounterSlot> GetPossible(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion gameSource = Any)
|
||||
{
|
||||
var possibleAreas = GetAreasByGame(pkm, gameSource);
|
||||
return possibleAreas.SelectMany(z => z.GetSpecies(chain));
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static class EncounterStaticGenerator
|
||||
{
|
||||
public static IEnumerable<EncounterStatic> GetPossible(PKM pkm, IReadOnlyList<DexLevel> chain, GameVersion gameSource = Any)
|
||||
public static IEnumerable<EncounterStatic> GetPossible(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion gameSource = Any)
|
||||
{
|
||||
if (gameSource == Any)
|
||||
gameSource = (GameVersion)pkm.Version;
|
||||
|
@ -37,7 +37,7 @@ namespace PKHeX.Core
|
|||
return table.Where(e => chain.Any(d => d.Species == e.Species));
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterStatic> GetPossibleGBGifts(IReadOnlyList<DexLevel> chain, GameVersion gameSource)
|
||||
public static IEnumerable<EncounterStatic> GetPossibleGBGifts(IReadOnlyList<EvoCriteria> chain, GameVersion gameSource)
|
||||
{
|
||||
static IEnumerable<EncounterStatic> GetEvents(GameVersion g)
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ namespace PKHeX.Core
|
|||
return table.Where(e => chain.Any(d => d.Species == e.Species));
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterStatic> GetValidStaticEncounter(PKM pkm, IReadOnlyList<DexLevel> chain, GameVersion gameSource = Any)
|
||||
public static IEnumerable<EncounterStatic> GetValidStaticEncounter(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion gameSource = Any)
|
||||
{
|
||||
if (gameSource == Any)
|
||||
gameSource = (GameVersion)pkm.Version;
|
||||
|
@ -63,7 +63,7 @@ namespace PKHeX.Core
|
|||
return GetMatchingStaticEncounters(pkm, poss, chain);
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterStatic> GetValidGBGifts(PKM pkm, IReadOnlyList<DexLevel> chain, GameVersion gameSource)
|
||||
public static IEnumerable<EncounterStatic> GetValidGBGifts(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion gameSource)
|
||||
{
|
||||
var poss = GetPossibleGBGifts(chain, gameSource: gameSource);
|
||||
foreach (EncounterStatic e in poss)
|
||||
|
@ -80,7 +80,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<EncounterStatic> GetMatchingStaticEncounters(PKM pkm, IEnumerable<EncounterStatic> poss, IReadOnlyList<DexLevel> evos)
|
||||
private static IEnumerable<EncounterStatic> GetMatchingStaticEncounters(PKM pkm, IEnumerable<EncounterStatic> poss, IReadOnlyList<EvoCriteria> evos)
|
||||
{
|
||||
// check for petty rejection scenarios that will be flagged by other legality checks
|
||||
foreach (var e in poss)
|
||||
|
@ -105,19 +105,34 @@ namespace PKHeX.Core
|
|||
{
|
||||
// Only yield a VC1 template if it could originate in VC1.
|
||||
// Catch anything that can only exist in VC2 (Entei) even if it was "transferred" from VC1.
|
||||
var species = chain.Where(z => z.Species < MaxSpeciesID_1 && z.Form == 0)
|
||||
.LastOrDefault(z => PersonalTable.SM.GetFormEntry(z.Species, z.Form).BaseFriendship == pkm.OT_Friendship)?.Species ?? pkm.Species;
|
||||
var species = GetVCSpecies(chain, pkm, MaxSpeciesID_1);
|
||||
var vc1Species = species > MaxSpeciesID_1 ? enc.Species : species;
|
||||
if (vc1Species <= MaxSpeciesID_1)
|
||||
return EncounterStatic7.GetVC1(vc1Species, met);
|
||||
}
|
||||
// fall through else
|
||||
{
|
||||
var species = chain.LastOrDefault(z => PersonalTable.SM.GetFormEntry(z.Species, z.Form).BaseFriendship == pkm.OT_Friendship)?.Species ?? pkm.Species;
|
||||
var species = GetVCSpecies(chain, pkm, MaxSpeciesID_2);
|
||||
return EncounterStatic7.GetVC2(species > MaxSpeciesID_2 ? enc.Species : species, met);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetVCSpecies(IReadOnlyList<EvoCriteria> chain, PKM pkm, int max)
|
||||
{
|
||||
int species = pkm.Species;
|
||||
foreach (var z in chain)
|
||||
{
|
||||
if (z.Species > max)
|
||||
continue;
|
||||
if (z.Form != 0)
|
||||
continue;
|
||||
if (PersonalTable.SM.GetFormEntry(z.Species, z.Form).BaseFriendship != pkm.OT_Friendship)
|
||||
continue;
|
||||
species = z.Species;
|
||||
}
|
||||
return species;
|
||||
}
|
||||
|
||||
internal static EncounterStatic? GetStaticLocation(PKM pkm, IReadOnlyList<EvoCriteria> chain)
|
||||
{
|
||||
switch (pkm.Generation)
|
||||
|
|
|
@ -7,26 +7,26 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static class EncounterTradeGenerator
|
||||
{
|
||||
public static IEnumerable<EncounterTrade> GetPossible(PKM pkm, IReadOnlyList<DexLevel> chain, GameVersion gameSource)
|
||||
public static IEnumerable<EncounterTrade> GetPossible(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion gameSource)
|
||||
{
|
||||
if (pkm.Format <= 2 || pkm.VC)
|
||||
return GetPossibleVC(chain, gameSource);
|
||||
return GetPossible(chain, gameSource);
|
||||
}
|
||||
|
||||
private static IEnumerable<EncounterTradeGB> GetPossibleVC(IReadOnlyList<DexLevel> chain, GameVersion game)
|
||||
private static IEnumerable<EncounterTradeGB> GetPossibleVC(IReadOnlyList<EvoCriteria> chain, GameVersion game)
|
||||
{
|
||||
var table = GetTableVC(game);
|
||||
return table.Where(e => chain.Any(c => c.Species == e.Species && c.Form == 0));
|
||||
}
|
||||
|
||||
private static IEnumerable<EncounterTrade> GetPossible(IReadOnlyList<DexLevel> chain, GameVersion game)
|
||||
private static IEnumerable<EncounterTrade> GetPossible(IReadOnlyList<EvoCriteria> chain, GameVersion game)
|
||||
{
|
||||
var table = GetTable(game);
|
||||
return table.Where(e => chain.Any(c => c.Species == e.Species));
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterTradeGB> GetValidEncounterTradesVC(PKM pkm, IReadOnlyList<DexLevel> chain, GameVersion game)
|
||||
public static IEnumerable<EncounterTradeGB> GetValidEncounterTradesVC(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion game)
|
||||
{
|
||||
var table = GetTableVC(game);
|
||||
foreach (var p in table)
|
||||
|
@ -42,7 +42,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, IReadOnlyList<DexLevel> chain, GameVersion game = Any)
|
||||
public static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion game = Any)
|
||||
{
|
||||
if (game == Any)
|
||||
game = (GameVersion)pkm.Version;
|
||||
|
@ -57,7 +57,7 @@ namespace PKHeX.Core
|
|||
return GetValidEncounterTrades(pkm, chain, poss);
|
||||
}
|
||||
|
||||
private static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, IReadOnlyList<DexLevel> chain, IEnumerable<EncounterTrade> poss)
|
||||
private static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, IReadOnlyList<EvoCriteria> chain, IEnumerable<EncounterTrade> poss)
|
||||
{
|
||||
foreach (var p in poss)
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static class MysteryGiftGenerator
|
||||
{
|
||||
public static IEnumerable<MysteryGift> GetPossible(PKM pkm, IReadOnlyList<DexLevel> chain)
|
||||
public static IEnumerable<MysteryGift> GetPossible(PKM pkm, IReadOnlyList<EvoCriteria> chain)
|
||||
{
|
||||
// Ranger Manaphy is a PGT and is not in the PCD[] for gen4. Check manually.
|
||||
int gen = pkm.Generation;
|
||||
|
@ -20,7 +20,7 @@ namespace PKHeX.Core
|
|||
yield return enc;
|
||||
}
|
||||
|
||||
public static IEnumerable<MysteryGift> GetValidGifts(PKM pkm, IReadOnlyList<DexLevel> chain)
|
||||
public static IEnumerable<MysteryGift> GetValidGifts(PKM pkm, IReadOnlyList<EvoCriteria> chain)
|
||||
{
|
||||
int gen = pkm.Generation;
|
||||
if (pkm.IsEgg && pkm.Format != gen) // transferred
|
||||
|
@ -43,7 +43,7 @@ namespace PKHeX.Core
|
|||
_ => Array.Empty<MysteryGift>(),
|
||||
};
|
||||
|
||||
private static IEnumerable<MysteryGift> GetMatchingPCD(PKM pkm, IReadOnlyCollection<PCD> DB, IReadOnlyList<DexLevel> chain)
|
||||
private static IEnumerable<MysteryGift> GetMatchingPCD(PKM pkm, IReadOnlyCollection<PCD> DB, IReadOnlyList<EvoCriteria> chain)
|
||||
{
|
||||
if (PGT.IsRangerManaphy(pkm))
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ namespace PKHeX.Core
|
|||
yield return g;
|
||||
}
|
||||
|
||||
private static IEnumerable<MysteryGift> GetMatchingGifts(PKM pkm, IReadOnlyCollection<MysteryGift> DB, IReadOnlyList<DexLevel> chain)
|
||||
private static IEnumerable<MysteryGift> GetMatchingGifts(PKM pkm, IReadOnlyCollection<MysteryGift> DB, IReadOnlyList<EvoCriteria> chain)
|
||||
{
|
||||
foreach (var mg in DB)
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace PKHeX.Core
|
|||
/// <summary>
|
||||
/// Checks if the implementing object's details might have been the originator of the current <see cref="pkm"/> data.
|
||||
/// </summary>
|
||||
bool IsMatchExact(PKM pkm, DexLevel dl);
|
||||
bool IsMatchExact(PKM pkm, IDexLevel dl);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the potential match may not be a perfect match (might be a better match later during iteration).
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
int lvl = GetSuggestedEncounterEggMetLevel(pkm);
|
||||
var met = loc != -1 ? loc : GetSuggestedEggMetLocation(pkm);
|
||||
return new EncounterSuggestionData(pkm, met, lvl);
|
||||
return new EncounterSuggestionData(pkm, met, (byte)lvl);
|
||||
}
|
||||
|
||||
public static int GetSuggestedEncounterEggMetLevel(PKM pkm)
|
||||
|
@ -98,19 +98,19 @@ namespace PKHeX.Core
|
|||
return -1;
|
||||
}
|
||||
|
||||
public static int GetLowestLevel(PKM pkm, int startLevel)
|
||||
public static int GetLowestLevel(PKM pkm, byte startLevel)
|
||||
{
|
||||
if (startLevel == -1)
|
||||
if (startLevel >= 100)
|
||||
startLevel = 100;
|
||||
|
||||
var table = EvolutionTree.GetEvolutionTree(pkm, pkm.Format);
|
||||
int count = 1;
|
||||
for (int i = 100; i >= startLevel; i--)
|
||||
for (byte i = 100; i >= startLevel; i--)
|
||||
{
|
||||
var evos = table.GetValidPreEvolutions(pkm, maxLevel: i, minLevel: startLevel, skipChecks: true);
|
||||
if (evos.Count < count) // lost an evolution, prior level was minimum current level
|
||||
return evos.Max(evo => evo.Level) + 1;
|
||||
count = evos.Count;
|
||||
if (evos.Length < count) // lost an evolution, prior level was minimum current level
|
||||
return evos.Max(evo => evo.LevelMax) + 1;
|
||||
count = evos.Length;
|
||||
}
|
||||
return startLevel;
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ namespace PKHeX.Core
|
|||
LevelMax = enc.LevelMax;
|
||||
}
|
||||
|
||||
public EncounterSuggestionData(PKM pkm, int met, int lvl)
|
||||
public EncounterSuggestionData(PKM pkm, int met, byte lvl)
|
||||
{
|
||||
Species = pkm.Species;
|
||||
Form = pkm.Form;
|
||||
|
@ -202,8 +202,8 @@ namespace PKHeX.Core
|
|||
public int Form { get; }
|
||||
public int Location { get; }
|
||||
|
||||
public int LevelMin { get; }
|
||||
public int LevelMax { get; }
|
||||
public byte LevelMin { get; }
|
||||
public byte LevelMax { get; }
|
||||
|
||||
public int GetSuggestedMetLevel(PKM pkm) => EncounterSuggestion.GetSuggestedMetLevel(pkm, LevelMin);
|
||||
public GroundTileType GetSuggestedGroundTile() => Encounter is IGroundTypeTile t ? t.GroundTile.GetIndex() : 0;
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace PKHeX.Core
|
|||
private static bool IsValidEvolution(PKM pkm, LegalInfo info)
|
||||
{
|
||||
var chains = info.EvoChainsAllGens;
|
||||
if (chains[pkm.Format].Count == 0)
|
||||
if (chains[pkm.Format].Length == 0)
|
||||
return false; // Can't exist as current species
|
||||
|
||||
// OK if un-evolved from original encounter
|
||||
|
|
|
@ -146,20 +146,18 @@ namespace PKHeX.Core
|
|||
{
|
||||
var enc = info.EncounterMatch;
|
||||
var evos = info.EvoChainsAllGens[enc.Generation];
|
||||
var level = evos.Count > 0 ? evos[^1].MinLevel : enc.LevelMin;
|
||||
var level = evos.Length > 0 ? evos[^1].LevelMin : enc.LevelMin;
|
||||
var InitialMoves = Array.Empty<int>();
|
||||
var SpecialMoves = GetSpecialMoves(enc);
|
||||
var games = enc.Generation == 1 ? GBRestrictions.GetGen1Versions(enc) : GBRestrictions.GetGen2Versions(enc, pkm.Korean);
|
||||
foreach (var ver in games)
|
||||
{
|
||||
var VerInitialMoves = enc is IMoveset {Moves.Count: not 0 } x ? (int[])x.Moves : MoveLevelUp.GetEncounterMoves(enc.Species, 0, level, ver);
|
||||
if (VerInitialMoves.Intersect(InitialMoves).Count() == VerInitialMoves.Length)
|
||||
return;
|
||||
if (VerInitialMoves.SequenceEqual(InitialMoves))
|
||||
return; // Already checked this combination, and it wasn't valid. Don't bother repeating.
|
||||
|
||||
var source = new MoveParseSource
|
||||
{
|
||||
CurrentMoves = currentMoves,
|
||||
SpecialSource = SpecialMoves,
|
||||
Base = VerInitialMoves,
|
||||
};
|
||||
ParseMoves(pkm, source, info, parse);
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Small general purpose value passing object with misc data pertaining to an encountered Species.
|
||||
/// </summary>
|
||||
public record DexLevel(int Species, int Form) : ISpeciesForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum Level
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
|
||||
public override string ToString() => $"{(Species)Species}{(Form == 0 ? "" : $"-{Form}")} [{Level}]";
|
||||
}
|
|
@ -15,12 +15,12 @@ namespace PKHeX.Core
|
|||
/// <param name="pkm">Current state of the Pokémon</param>
|
||||
/// <returns>Possible origin species-form-levels to match against encounter data.</returns>
|
||||
/// <remarks>Use <see cref="GetOriginChain12"/> if the <see cref="pkm"/> originated from Generation 1 or 2.</remarks>
|
||||
public static IReadOnlyList<EvoCriteria> GetOriginChain(PKM pkm)
|
||||
public static EvoCriteria[] GetOriginChain(PKM pkm)
|
||||
{
|
||||
bool hasOriginMet = pkm.HasOriginalMetLocation;
|
||||
var maxLevel = GetLevelOriginMax(pkm, hasOriginMet);
|
||||
var minLevel = GetLevelOriginMin(pkm, hasOriginMet);
|
||||
return GetOriginChain(pkm, -1, maxLevel, minLevel, hasOriginMet);
|
||||
return GetOriginChain(pkm, -1, (byte)maxLevel, (byte)minLevel, hasOriginMet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -29,7 +29,7 @@ namespace PKHeX.Core
|
|||
/// <param name="pkm">Current state of the Pokémon</param>
|
||||
/// <param name="gameSource">Game/group the <see cref="pkm"/> 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 IReadOnlyList<EvoCriteria> GetOriginChain12(PKM pkm, GameVersion gameSource)
|
||||
public static EvoCriteria[] GetOriginChain12(PKM pkm, GameVersion gameSource)
|
||||
{
|
||||
bool rby = gameSource == GameVersion.RBY;
|
||||
var maxSpecies = rby ? Legal.MaxSpeciesID_1 : Legal.MaxSpeciesID_2;
|
||||
|
@ -61,10 +61,10 @@ namespace PKHeX.Core
|
|||
minLevel = 2;
|
||||
}
|
||||
|
||||
return GetOriginChain(pkm, maxSpecies, maxLevel, minLevel, hasOriginMet);
|
||||
return GetOriginChain(pkm, maxSpecies, (byte)maxLevel, (byte)minLevel, hasOriginMet);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<EvoCriteria> GetOriginChain(PKM pkm, int maxSpecies, int maxLevel, int minLevel, bool hasOriginMet)
|
||||
private static EvoCriteria[] GetOriginChain(PKM pkm, int maxSpecies, byte maxLevel, byte minLevel, bool hasOriginMet)
|
||||
{
|
||||
if (maxLevel < minLevel)
|
||||
return Array.Empty<EvoCriteria>();
|
||||
|
@ -76,11 +76,9 @@ namespace PKHeX.Core
|
|||
var tempMax = pkm.CurrentLevel;
|
||||
var chain = EvolutionChain.GetValidPreEvolutions(pkm, maxSpecies, tempMax, minLevel);
|
||||
|
||||
foreach (var evo in chain)
|
||||
{
|
||||
evo.Level = maxLevel;
|
||||
evo.MinLevel = minLevel;
|
||||
}
|
||||
for (var i = 0; i < chain.Length; i++)
|
||||
chain[i] = chain[i] with { LevelMax = maxLevel, LevelMin = minLevel };
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
public sealed record EvoCriteria(int Species, int Form) : DexLevel(Species, Form)
|
||||
public readonly record struct EvoCriteria : IDexLevel
|
||||
{
|
||||
public int MinLevel { get; set; }
|
||||
public bool RequiresLvlUp { get; set; }
|
||||
public int Method { get; init; } = -1;
|
||||
public ushort Species { get; init; }
|
||||
public byte Form { get; init; }
|
||||
public byte LevelUpRequired { get; init; }
|
||||
public byte LevelMax { get; init; }
|
||||
public byte LevelMin { get; init; }
|
||||
|
||||
public bool IsTradeRequired => ((EvolutionType) Method).IsTrade();
|
||||
public EvolutionType Method { get; init; }
|
||||
|
||||
public override string ToString() => $"{(Species) Species}{(Form != 0 ? $"-{Form}" : "")}}} [{MinLevel},{Level}] via {(EvolutionType) Method}";
|
||||
public bool RequiresLvlUp => LevelUpRequired != 0;
|
||||
|
||||
public override string ToString() => $"{(Species) Species}{(Form != 0 ? $"-{Form}" : "")}}} [{LevelMin},{LevelMax}] via {Method}";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using static PKHeX.Core.Legal;
|
||||
|
||||
|
@ -8,42 +6,42 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static class EvolutionChain
|
||||
{
|
||||
private static readonly List<EvoCriteria> NONE = new(0);
|
||||
private static readonly EvoCriteria[] NONE = Array.Empty<EvoCriteria>();
|
||||
|
||||
internal static IReadOnlyList<EvoCriteria>[] GetEvolutionChainsAllGens(PKM pkm, IEncounterTemplate enc)
|
||||
internal static EvoCriteria[][] GetEvolutionChainsAllGens(PKM pkm, IEncounterTemplate enc)
|
||||
{
|
||||
var chain = GetEvolutionChain(pkm, enc, pkm.Species, pkm.CurrentLevel);
|
||||
if (enc is EncounterInvalid || pkm.IsEgg || chain.Count == 0)
|
||||
var chain = GetEvolutionChain(pkm, enc, pkm.Species, (byte)pkm.CurrentLevel);
|
||||
if (chain.Length == 0 || pkm.IsEgg || enc is EncounterInvalid)
|
||||
return GetChainSingle(pkm, chain);
|
||||
|
||||
return GetChainAll(pkm, enc, chain);
|
||||
}
|
||||
|
||||
private static List<EvoCriteria>[] GetChainBase(int maxgen)
|
||||
private static EvoCriteria[][] GetAllEmpty(int count)
|
||||
{
|
||||
var GensEvoChains = new List<EvoCriteria>[maxgen + 1];
|
||||
for (int i = 0; i <= maxgen; i++)
|
||||
GensEvoChains[i] = NONE; // default no-evolutions
|
||||
return GensEvoChains;
|
||||
var result = new EvoCriteria[count][];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = NONE; // default no-evolutions
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<EvoCriteria>[] GetChainSingle(PKM pkm, List<EvoCriteria> fullChain)
|
||||
private static EvoCriteria[][] GetChainSingle(PKM pkm, EvoCriteria[] fullChain)
|
||||
{
|
||||
var chain = GetChainBase(Math.Max(2, pkm.Format));
|
||||
var chain = GetAllEmpty(Math.Max(2, pkm.Format) + 1);
|
||||
chain[pkm.Format] = fullChain;
|
||||
return chain;
|
||||
}
|
||||
|
||||
private static List<EvoCriteria>[] GetChainAll(PKM pkm, IEncounterTemplate enc, IReadOnlyList<EvoCriteria> fullChain)
|
||||
private static EvoCriteria[][] GetChainAll(PKM pkm, IEncounterTemplate enc, EvoCriteria[] fullChain)
|
||||
{
|
||||
int maxgen = ParseSettings.AllowGen1Tradeback && pkm is PK1 ? 2 : pkm.Format;
|
||||
var GensEvoChains = GetChainBase(maxgen);
|
||||
var GensEvoChains = GetAllEmpty(maxgen + 1);
|
||||
|
||||
var queue = new Queue<EvoCriteria>(fullChain);
|
||||
var mostEvolved = queue.Dequeue();
|
||||
var head = 0; // inlined FIFO queue indexing
|
||||
var mostEvolved = fullChain[head++];
|
||||
|
||||
int lvl = pkm.CurrentLevel;
|
||||
int maxLevel = lvl;
|
||||
var lvl = (byte)pkm.CurrentLevel;
|
||||
var maxLevel = lvl;
|
||||
int pkGen = enc.Generation;
|
||||
|
||||
// Iterate generations backwards
|
||||
|
@ -53,12 +51,14 @@ namespace PKHeX.Core
|
|||
for (int g = GensEvoChains.Length - 1; g >= mingen; g--)
|
||||
{
|
||||
if (pkGen <= 2 && g == 6)
|
||||
g = 2;
|
||||
g = 2; // skip over 6543 as it never existed in these.
|
||||
|
||||
if (g <= 4 && pkm.Format > 2 && pkm.Format > g && !pkm.HasOriginalMetLocation && lvl > pkm.Met_Level)
|
||||
if (g <= 4 && pkm.Format > 2 && pkm.Format > g && !pkm.HasOriginalMetLocation)
|
||||
{
|
||||
// Met location was lost at this point but it also means the pokemon existed in generations 1 to 4 with maximum level equals to met level
|
||||
lvl = pkm.Met_Level;
|
||||
var met = pkm.Met_Level;
|
||||
if (lvl > pkm.Met_Level)
|
||||
lvl = (byte)met;
|
||||
}
|
||||
|
||||
int maxspeciesgen = g == 2 && pkm.VC1 ? MaxSpeciesID_1 : GetMaxSpeciesOrigin(g);
|
||||
|
@ -67,7 +67,7 @@ namespace PKHeX.Core
|
|||
// If the pokemon origin is illegal (e.g. Gen3 Infernape) the list will be emptied -- species lineage did not exist at any evolution stage.
|
||||
while (mostEvolved.Species > maxspeciesgen)
|
||||
{
|
||||
if (queue.Count == 0)
|
||||
if (head >= fullChain.Length)
|
||||
{
|
||||
if (g <= 2 && pkm.VC1)
|
||||
GensEvoChains[pkm.Format] = NONE; // invalidate here since we haven't reached the regular invalidation
|
||||
|
@ -85,7 +85,7 @@ namespace PKHeX.Core
|
|||
else if (g == 1 && pkm.Format == 2 && lvl == maxLevel)
|
||||
lvl--;
|
||||
}
|
||||
mostEvolved = queue.Dequeue();
|
||||
mostEvolved = fullChain[head++];
|
||||
}
|
||||
|
||||
// Alolan form evolutions, remove from gens 1-6 chains
|
||||
|
@ -93,14 +93,15 @@ namespace PKHeX.Core
|
|||
{
|
||||
if (g < 7 && pkm.Format >= 7 && mostEvolved.Form > 0)
|
||||
{
|
||||
if (queue.Count == 0)
|
||||
if (head >= fullChain.Length)
|
||||
break;
|
||||
mostEvolved = queue.Dequeue();
|
||||
mostEvolved = fullChain[head++];
|
||||
}
|
||||
}
|
||||
|
||||
var genChain = GensEvoChains[g] = GetEvolutionChain(pkm, enc, mostEvolved.Species, lvl);
|
||||
if (genChain.Count == 0)
|
||||
GensEvoChains[g] = GetEvolutionChain(pkm, enc, mostEvolved.Species, lvl);
|
||||
ref var genChain = ref GensEvoChains[g];
|
||||
if (genChain.Length == 0)
|
||||
continue;
|
||||
|
||||
if (g > 2 && !pkm.HasOriginalMetLocation && g >= pkGen && noxfrDecremented)
|
||||
|
@ -115,26 +116,48 @@ namespace PKHeX.Core
|
|||
// For example a gen3 Charizard in format 7 with current level 36 and met level 36, thus could never be Charmander / Charmeleon in Gen5+.
|
||||
// chain level for Charmander is 35, is below met level.
|
||||
int minlvl = GetMinLevelGeneration(pkm, g);
|
||||
genChain.RemoveAll(e => e.Level < minlvl);
|
||||
int minIndex = Array.FindIndex(genChain, e => e.LevelMax >= minlvl);
|
||||
if (minIndex != -1)
|
||||
genChain = genChain.AsSpan(minIndex).ToArray();
|
||||
}
|
||||
else if (g == 1)
|
||||
{
|
||||
var g1 = GensEvoChains[1];
|
||||
// Remove Gen2 post-evolutions (Scizor, Blissey...)
|
||||
if (g1[0].Species > MaxSpeciesID_1)
|
||||
g1.RemoveAt(0);
|
||||
|
||||
// Remove Gen2 pre-evolutions (Pichu, Cleffa...)
|
||||
int lastIndex = g1.Count - 1;
|
||||
if (lastIndex >= 0 && g1[lastIndex].Species > MaxSpeciesID_1)
|
||||
g1.RemoveAt(lastIndex);
|
||||
|
||||
// Remove Gen7 pre-evolutions and chain break scenarios
|
||||
if (pkm.VC1)
|
||||
TrimVC1Transfer(pkm, GensEvoChains);
|
||||
|
||||
ref var lastGen = ref GensEvoChains[1];
|
||||
var g1 = lastGen.AsSpan();
|
||||
// Remove Gen2 post-evolutions (Scizor, Blissey...)
|
||||
if (g1[0].Species > MaxSpeciesID_1)
|
||||
{
|
||||
if (g1.Length == 1)
|
||||
{
|
||||
lastGen = Array.Empty<EvoCriteria>();
|
||||
continue; // done
|
||||
}
|
||||
g1 = g1[1..];
|
||||
}
|
||||
|
||||
// Remove Gen2 pre-evolutions (Pichu, Cleffa...)
|
||||
if (g1[^1].Species > MaxSpeciesID_1)
|
||||
{
|
||||
if (g1.Length == 1)
|
||||
{
|
||||
lastGen = Array.Empty<EvoCriteria>();
|
||||
continue; // done
|
||||
}
|
||||
g1 = g1[..^1];
|
||||
}
|
||||
|
||||
if (g1.Length != lastGen.Length)
|
||||
lastGen = g1.ToArray();
|
||||
// Update min level for the encounter to prevent certain level up moves.
|
||||
if (g1.Count != 0)
|
||||
g1[^1].MinLevel = enc.LevelMin;
|
||||
if (g1.Length != 0)
|
||||
{
|
||||
ref var last = ref g1[^1];
|
||||
last = last with { LevelMin = enc.LevelMin };
|
||||
}
|
||||
}
|
||||
}
|
||||
return GensEvoChains;
|
||||
|
@ -147,23 +170,15 @@ namespace PKHeX.Core
|
|||
_ => false,
|
||||
};
|
||||
|
||||
private static void TrimVC1Transfer(PKM pkm, IList<List<EvoCriteria>> allChains)
|
||||
private static void TrimVC1Transfer(PKM pkm, EvoCriteria[][] allChains)
|
||||
{
|
||||
if (allChains[7].All(z => z.Species > MaxSpeciesID_1))
|
||||
var vc7 = allChains[7];
|
||||
var gen1Index = Array.FindLastIndex(vc7, z => z.Species <= MaxSpeciesID_1);
|
||||
if (gen1Index == -1)
|
||||
allChains[pkm.Format] = NONE; // needed a Gen1 species present; invalidate the chain.
|
||||
}
|
||||
|
||||
internal static int GetEvoChainSpeciesIndex(IReadOnlyList<EvoCriteria> chain, int species)
|
||||
{
|
||||
for (int i = 0; i < chain.Count; i++)
|
||||
{
|
||||
if (chain[i].Species == species)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static List<EvoCriteria> GetEvolutionChain(PKM pkm, IEncounterTemplate enc, int mostEvolvedSpecies, int maxlevel)
|
||||
private static EvoCriteria[] GetEvolutionChain(PKM pkm, IEncounterTemplate enc, int mostEvolvedSpecies, byte maxlevel)
|
||||
{
|
||||
int min = enc.LevelMin;
|
||||
if (pkm.HasOriginalMetLocation && pkm.Met_Level != 0)
|
||||
|
@ -171,72 +186,76 @@ namespace PKHeX.Core
|
|||
var chain = GetValidPreEvolutions(pkm, minLevel: min);
|
||||
if (enc.Species == mostEvolvedSpecies)
|
||||
{
|
||||
if (chain.Count != 1)
|
||||
chain.RemoveAll(z => z.Species != enc.Species);
|
||||
return chain;
|
||||
if (chain.Length == 1)
|
||||
return chain;
|
||||
var index = Array.FindLastIndex(chain, z => z.Species == enc.Species);
|
||||
if (index == -1)
|
||||
return Array.Empty<EvoCriteria>();
|
||||
return new[] { chain[index] };
|
||||
}
|
||||
|
||||
// Evolution chain is in reverse order (devolution)
|
||||
// Find the index of the minimum species to determine the end of the chain
|
||||
int minIndex = GetEvoChainSpeciesIndex(chain, enc.Species);
|
||||
bool last = minIndex < 0 || minIndex == chain.Count - 1;
|
||||
int minIndex = Array.FindLastIndex(chain, z => z.Species == enc.Species);
|
||||
bool last = minIndex < 0 || minIndex == chain.Length - 1;
|
||||
|
||||
// If we remove a pre-evolution, update the chain if appropriate.
|
||||
if (!last)
|
||||
{
|
||||
// Remove chain species after the encounter
|
||||
int count = chain.Count;
|
||||
for (int i = minIndex + 1; i < count; i++)
|
||||
chain.RemoveAt(i);
|
||||
if (minIndex + 1 == chain.Length)
|
||||
return Array.Empty<EvoCriteria>(); // no species left in chain
|
||||
|
||||
if (chain.Count == 0)
|
||||
return chain; // no species left in chain
|
||||
chain = chain.AsSpan(0, minIndex + 1).ToArray();
|
||||
CheckLastEncounterRemoval(enc, chain);
|
||||
}
|
||||
|
||||
// maxspec is used to remove future geneneration evolutions, to gather evolution chain of a pokemon in previous generations
|
||||
int skip = Math.Max(0, GetEvoChainSpeciesIndex(chain, mostEvolvedSpecies));
|
||||
for (int i = 0; i < skip; i++)
|
||||
chain.RemoveAt(0);
|
||||
var maxSpeciesIndex = Array.FindIndex(chain, z => z.Species == mostEvolvedSpecies);
|
||||
if (maxSpeciesIndex > 0)
|
||||
chain = chain.AsSpan(maxSpeciesIndex).ToArray();
|
||||
|
||||
// Gen3->4 and Gen4->5 transfer sets the Met Level property to the Pokémon's current level.
|
||||
// Removes evolutions impossible before the transfer level.
|
||||
// For example a FireRed Charizard with a current level (in XY) is 50 but Met Level is 20; it couldn't be a Charizard in Gen3 and Gen4 games
|
||||
chain.RemoveAll(e => e.MinLevel > maxlevel);
|
||||
var clampIndex = Array.FindIndex(chain, z => z.LevelMin > maxlevel);
|
||||
if (clampIndex != -1)
|
||||
chain = Array.FindAll(chain, z => z.LevelMin <= maxlevel);
|
||||
|
||||
// Reduce the evolution chain levels to max level to limit any later analysis/results.
|
||||
foreach (var d in chain)
|
||||
d.Level = Math.Min(d.Level, maxlevel);
|
||||
for (var i = 0; i < chain.Length; i++)
|
||||
{
|
||||
ref var c = ref chain[i];
|
||||
c = c with { LevelMax = Math.Min(c.LevelMax, maxlevel) };
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
private static void CheckLastEncounterRemoval(IEncounterTemplate enc, IReadOnlyList<EvoCriteria> chain)
|
||||
private static void CheckLastEncounterRemoval(IEncounterTemplate enc, EvoCriteria[] chain)
|
||||
{
|
||||
// Last entry from chain is removed, turn next entry into the encountered Pokémon
|
||||
var last = chain[^1];
|
||||
last.MinLevel = enc.LevelMin;
|
||||
last.RequiresLvlUp = false;
|
||||
ref var last = ref chain[^1];
|
||||
last = last with { LevelMin = enc.LevelMin, LevelUpRequired = 1 };
|
||||
|
||||
var first = chain[0];
|
||||
ref var first = ref chain[0];
|
||||
if (first.RequiresLvlUp)
|
||||
return;
|
||||
|
||||
if (first.MinLevel == 2)
|
||||
if (first.LevelMin == 2)
|
||||
{
|
||||
// Example: Raichu in Gen2 or later
|
||||
// Because Pichu requires a level up, the minimum level of the resulting Raichu must be be >2
|
||||
// But after removing Pichu (because the origin species is Pikachu), the Raichu minimum level should be 1.
|
||||
first.MinLevel = 1;
|
||||
first.RequiresLvlUp = false;
|
||||
first = first with { LevelMin = 1, LevelUpRequired = 0 };
|
||||
}
|
||||
else // in-game trade or evolution stone can evolve immediately
|
||||
{
|
||||
first.MinLevel = last.MinLevel;
|
||||
first = first with { LevelMin = enc.LevelMin };
|
||||
}
|
||||
}
|
||||
|
||||
internal static List<EvoCriteria> GetValidPreEvolutions(PKM pkm, int maxspeciesorigin = -1, int maxLevel = -1, int minLevel = 1, bool skipChecks = false)
|
||||
internal static EvoCriteria[] GetValidPreEvolutions(PKM pkm, int maxspeciesorigin = -1, int maxLevel = -1, int minLevel = 1, bool skipChecks = false)
|
||||
{
|
||||
if (maxLevel < 0)
|
||||
maxLevel = pkm.CurrentLevel;
|
||||
|
@ -246,7 +265,7 @@ namespace PKHeX.Core
|
|||
|
||||
int tree = Math.Max(2, pkm.Format);
|
||||
var et = EvolutionTree.GetEvolutionTree(pkm, tree);
|
||||
return et.GetValidPreEvolutions(pkm, maxLevel: maxLevel, maxSpeciesOrigin: maxspeciesorigin, skipChecks: skipChecks, minLevel: minLevel);
|
||||
return et.GetValidPreEvolutions(pkm, maxLevel: (byte)maxLevel, maxSpeciesOrigin: maxspeciesorigin, skipChecks: skipChecks, minLevel: (byte)minLevel);
|
||||
}
|
||||
|
||||
private static int GetMinLevelGeneration(PKM pkm, int generation)
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace PKHeX.Core
|
|||
/// <summary>
|
||||
/// Conditional Argument (different from <see cref="Argument"/>)
|
||||
/// </summary>
|
||||
public readonly int Level;
|
||||
public readonly byte Level;
|
||||
|
||||
/// <summary>
|
||||
/// Destination Form
|
||||
|
@ -38,7 +38,7 @@ namespace PKHeX.Core
|
|||
// 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, int level = 0, int form = AnyForm)
|
||||
public EvolutionMethod(int method, int species, int argument = 0, byte level = 0, int form = AnyForm)
|
||||
{
|
||||
Method = method;
|
||||
Species = species;
|
||||
|
@ -147,10 +147,13 @@ namespace PKHeX.Core
|
|||
};
|
||||
}
|
||||
|
||||
public EvoCriteria GetEvoCriteria(int species, int form, int lvl) => new(species, form)
|
||||
public EvoCriteria GetEvoCriteria(ushort species, byte form, byte lvl) => new()
|
||||
{
|
||||
Level = lvl,
|
||||
Method = Method,
|
||||
Species = species,
|
||||
Form = form,
|
||||
LevelMax = lvl,
|
||||
LevelMin = 0,
|
||||
Method = (EvolutionType)Method,
|
||||
};
|
||||
|
||||
public static int GetAmpLowKeyResult(int n)
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
int method = data[0];
|
||||
int species = data[1];
|
||||
int arg = data[2];
|
||||
var arg = data[2];
|
||||
|
||||
return (method == 1)
|
||||
? new EvolutionMethod(method, species, level: arg)
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace PKHeX.Core
|
|||
case 6: /* Trade while holding */
|
||||
return new EvolutionMethod(method, species, argument: arg);
|
||||
case 4: /* Level Up */
|
||||
return new EvolutionMethod(4, species, argument: arg, level:arg);
|
||||
return new EvolutionMethod(4, species, argument: arg, level: (byte)arg);
|
||||
case 7: /* Use item */
|
||||
case 15: /* Beauty evolution*/
|
||||
return new EvolutionMethod(method + 1, species, argument: arg);
|
||||
|
@ -36,7 +36,7 @@ namespace PKHeX.Core
|
|||
case 12: /* Wurmple -> Cascoon evolution */
|
||||
case 13: /* Nincada -> Ninjask evolution */
|
||||
case 14: /* Shedinja spawn in Nincada -> Ninjask evolution */
|
||||
return new EvolutionMethod(method + 1, species, argument: arg, level: arg);
|
||||
return new EvolutionMethod(method + 1, species, argument: arg, level: (byte)arg);
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(method));
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace PKHeX.Core
|
|||
method++;
|
||||
|
||||
var lvl = EvolutionSet6.EvosWithArg.Contains(method) ? 0 : arg;
|
||||
return new EvolutionMethod(method, species, argument: arg, level: lvl);
|
||||
return new EvolutionMethod(method, species, argument: arg, level: (byte)lvl);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<EvolutionMethod[]> GetArray(ReadOnlySpan<byte> data)
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace PKHeX.Core
|
|||
throw new ArgumentOutOfRangeException(nameof(method));
|
||||
|
||||
var lvl = EvolutionSet6.EvosWithArg.Contains(method) ? 0 : arg;
|
||||
return new EvolutionMethod(method, species, argument: arg, level: lvl);
|
||||
return new EvolutionMethod(method, species, argument: arg, level: (byte)lvl);
|
||||
}
|
||||
|
||||
private const int bpe = 6; // bytes per evolution entry
|
||||
|
@ -42,7 +42,7 @@ namespace PKHeX.Core
|
|||
|
||||
var set = new EvolutionMethod[count];
|
||||
for (int j = 0; j < set.Length; j++)
|
||||
set[j] = GetMethod(rawEntries.Slice((j * bpe), bpe));
|
||||
set[j] = GetMethod(rawEntries.Slice(j * bpe, bpe));
|
||||
evos[i] = set;
|
||||
}
|
||||
return evos;
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace PKHeX.Core
|
|||
// Argument is used by both Level argument and Item/Move/etc. Clear if appropriate.
|
||||
var lvl = EvosWithArg.Contains(method) ? 0 : arg;
|
||||
|
||||
return new EvolutionMethod(method, species, argument: arg, level: lvl);
|
||||
return new EvolutionMethod(method, species, argument: arg, level: (byte)lvl);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<EvolutionMethod[]> GetArray(BinLinkerAccessor data)
|
||||
|
|
|
@ -229,26 +229,26 @@ namespace PKHeX.Core
|
|||
/// <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>
|
||||
public List<EvoCriteria> GetValidPreEvolutions(PKM pkm, int maxLevel, int maxSpeciesOrigin = -1, bool skipChecks = false, int minLevel = 1)
|
||||
public EvoCriteria[] GetValidPreEvolutions(PKM pkm, byte maxLevel, int maxSpeciesOrigin = -1, bool skipChecks = false, byte minLevel = 1)
|
||||
{
|
||||
if (maxSpeciesOrigin <= 0)
|
||||
maxSpeciesOrigin = GetMaxSpeciesOrigin(pkm);
|
||||
if (pkm.IsEgg && !skipChecks)
|
||||
{
|
||||
return new List<EvoCriteria>(1)
|
||||
return new[]
|
||||
{
|
||||
new(pkm.Species, pkm.Form) { Level = maxLevel, MinLevel = maxLevel },
|
||||
new EvoCriteria{ Species = (ushort)pkm.Species, Form = (byte)pkm.Form, LevelMax = maxLevel, LevelMin = maxLevel },
|
||||
};
|
||||
}
|
||||
|
||||
// Shedinja's evolution case can be a little tricky; hard-code handling.
|
||||
if (pkm.Species == (int)Species.Shedinja && maxLevel >= 20 && (!pkm.HasOriginalMetLocation || minLevel < maxLevel))
|
||||
{
|
||||
return new List<EvoCriteria>(2)
|
||||
var min = Math.Max(minLevel, (byte)20);
|
||||
return new[]
|
||||
{
|
||||
new((int)Species.Shedinja, 0) { Level = maxLevel, MinLevel = Math.Max(minLevel, 20) },
|
||||
new((int)Species.Nincada, 0) { Level = maxLevel, MinLevel = minLevel },
|
||||
new EvoCriteria { Species = (ushort)Species.Shedinja, LevelMax = maxLevel, LevelMin = min, Method = EvolutionType.LevelUp },
|
||||
new EvoCriteria { Species = (ushort)Species.Nincada, LevelMax = maxLevel, LevelMin = minLevel },
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -353,16 +353,16 @@ namespace PKHeX.Core
|
|||
/// <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)
|
||||
private EvoCriteria[] GetExplicitLineage(PKM pkm, byte maxLevel, bool skipChecks, int maxSpeciesOrigin, byte minLevel)
|
||||
{
|
||||
int species = pkm.Species;
|
||||
int form = pkm.Form;
|
||||
int lvl = maxLevel;
|
||||
var first = new EvoCriteria(species, form) { Level = lvl };
|
||||
var species = pkm.Species;
|
||||
var form = pkm.Form;
|
||||
var lvl = maxLevel;
|
||||
var first = new EvoCriteria { Species = (ushort)species, Form = (byte)form, LevelMax = lvl };
|
||||
|
||||
const int maxEvolutions = 3;
|
||||
var dl = new List<EvoCriteria>(maxEvolutions) { first };
|
||||
Span<EvoCriteria> dl = stackalloc EvoCriteria[maxEvolutions];
|
||||
dl[0] = first;
|
||||
|
||||
switch (species)
|
||||
{
|
||||
|
@ -372,6 +372,7 @@ namespace PKHeX.Core
|
|||
|
||||
// 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.
|
||||
int ctr = 1;
|
||||
while (true)
|
||||
{
|
||||
var key = GetLookupKey(species, form);
|
||||
|
@ -391,12 +392,11 @@ namespace PKHeX.Core
|
|||
break; // impossible evolution
|
||||
|
||||
oneValid = true;
|
||||
UpdateMinValues(dl, evo, minLevel);
|
||||
UpdateMinValues(dl[..ctr], evo, minLevel);
|
||||
|
||||
species = link.Species;
|
||||
form = link.Form;
|
||||
var detail = evo.GetEvoCriteria(species, form, lvl);
|
||||
dl.Add(detail);
|
||||
dl[ctr++] = evo.GetEvoCriteria((ushort)species, (byte)form, lvl);
|
||||
if (evo.RequiresLevelUp)
|
||||
lvl--;
|
||||
break;
|
||||
|
@ -406,64 +406,79 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
// Remove future gen pre-evolutions; no Munchlax from a Gen3 Snorlax, no Pichu from a Gen1-only Raichu, etc
|
||||
var last = dl[^1];
|
||||
if (last.Species > maxSpeciesOrigin && dl.Any(d => d.Species <= maxSpeciesOrigin))
|
||||
dl.RemoveAt(dl.Count - 1);
|
||||
|
||||
// Last species is the wild/hatched species, the minimum level is because it has not evolved from previous species
|
||||
last = dl[^1];
|
||||
last.MinLevel = minLevel;
|
||||
last.RequiresLvlUp = false;
|
||||
|
||||
// Rectify minimum levels
|
||||
for (int i = dl.Count - 2; i >= 0; i--)
|
||||
ref var last = ref dl[ctr - 1];
|
||||
if (last.Species > maxSpeciesOrigin)
|
||||
{
|
||||
var evo = dl[i];
|
||||
var prev = dl[i + 1];
|
||||
evo.MinLevel = Math.Max(prev.MinLevel + (evo.RequiresLvlUp ? 1 : 0), evo.MinLevel);
|
||||
for (int i = 0; i < ctr; i++)
|
||||
{
|
||||
if (dl[i].Species > maxSpeciesOrigin)
|
||||
continue;
|
||||
ctr--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dl;
|
||||
// Last species is the wild/hatched species, the minimum level is because it has not evolved from previous species
|
||||
var result = dl[..ctr];
|
||||
last = ref result[^1];
|
||||
last = last with { LevelMin = minLevel, LevelUpRequired = 0 };
|
||||
|
||||
// Rectify minimum levels
|
||||
for (int i = result.Length - 2; i >= 0; i--)
|
||||
{
|
||||
ref var evo = ref result[i];
|
||||
var prev = result[i + 1];
|
||||
var min = (byte)Math.Max(prev.LevelMin + evo.LevelUpRequired, evo.LevelMin);
|
||||
evo = evo with { LevelMin = min };
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static void UpdateMinValues(IReadOnlyList<EvoCriteria> dl, EvolutionMethod evo, int minLevel)
|
||||
private static void UpdateMinValues(Span<EvoCriteria> dl, EvolutionMethod evo, byte minLevel)
|
||||
{
|
||||
var last = dl[^1];
|
||||
ref var last = ref dl[^1];
|
||||
if (!evo.RequiresLevelUp)
|
||||
{
|
||||
// Evolutions like elemental stones, trade, etc
|
||||
last.MinLevel = minLevel;
|
||||
last = last with { LevelMin = minLevel };
|
||||
return;
|
||||
}
|
||||
if (evo.Level == 0)
|
||||
{
|
||||
// Friendship based Level Up Evolutions, Pichu -> Pikachu, Eevee -> Umbreon, etc
|
||||
last.MinLevel = minLevel + 1;
|
||||
last = last with { LevelMin = (byte)(minLevel + 1) };
|
||||
|
||||
var first = dl[0];
|
||||
if (dl.Count > 1 && !first.RequiresLvlUp)
|
||||
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
|
||||
// Raichu from Pikachu would have a minimum level of 1; accounting for Pichu (level up required) results in a minimum level of 2
|
||||
if (dl.Length > 1)
|
||||
{
|
||||
ref var first = ref dl[0];
|
||||
if (!first.RequiresLvlUp)
|
||||
first = first with { LevelMin = (byte)(minLevel + 1) };
|
||||
}
|
||||
}
|
||||
else // level up evolutions
|
||||
{
|
||||
last.MinLevel = evo.Level;
|
||||
last = last with { LevelMin = evo.Level };
|
||||
|
||||
var first = dl[0];
|
||||
if (dl.Count > 1)
|
||||
if (dl.Length > 1)
|
||||
{
|
||||
ref var first = ref dl[0];
|
||||
if (first.RequiresLvlUp)
|
||||
{
|
||||
if (first.MinLevel <= evo.Level)
|
||||
first.MinLevel = evo.Level + 1; // Pokemon like Crobat, its minimum level is Golbat minimum level + 1
|
||||
// Pokemon like Crobat, its minimum level is Golbat minimum level + 1
|
||||
if (first.LevelMin <= evo.Level)
|
||||
first = first with {LevelMin = (byte)(evo.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
|
||||
// Pokemon like Nidoqueen who evolve with an evolution stone, minimum level is prior evolution minimum level
|
||||
if (first.LevelMin < evo.Level)
|
||||
first = first with { LevelMin = evo.Level };
|
||||
}
|
||||
}
|
||||
}
|
||||
last.RequiresLvlUp = evo.RequiresLevelUp;
|
||||
last = last with { LevelUpRequired = evo.RequiresLevelUp ? (byte)1 : (byte)0 };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
11
PKHeX.Core/Legality/Evolutions/IDexLevel.cs
Normal file
11
PKHeX.Core/Legality/Evolutions/IDexLevel.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Small general purpose value passing object with misc data pertaining to an encountered Species.
|
||||
/// </summary>
|
||||
public interface IDexLevel
|
||||
{
|
||||
ushort Species { get; }
|
||||
byte Form { get; }
|
||||
byte LevelMax { get; }
|
||||
}
|
|
@ -195,25 +195,25 @@ namespace PKHeX.Core
|
|||
/// <summary>
|
||||
/// ONLY CALL FOR GEN2 EGGS
|
||||
/// </summary>
|
||||
internal static IEnumerable<int> GetExclusivePreEvolutionMoves(PKM pkm, int Species, IReadOnlyList<EvoCriteria> evoChain, int generation, GameVersion Version)
|
||||
internal static IEnumerable<int> GetExclusivePreEvolutionMoves(PKM pkm, int Species, EvoCriteria[] evoChain, int generation, GameVersion Version)
|
||||
{
|
||||
var preevomoves = new List<int>();
|
||||
var evomoves = new List<int>();
|
||||
var index = EvolutionChain.GetEvoChainSpeciesIndex(evoChain, Species);
|
||||
for (int i = 0; i < evoChain.Count; i++)
|
||||
var index = Array.FindIndex(evoChain, z => z.Species == Species);
|
||||
for (int i = 0; i < evoChain.Length; i++)
|
||||
{
|
||||
int minLvLG2;
|
||||
var evo = evoChain[i];
|
||||
if (ParseSettings.AllowGen2MoveReminder(pkm))
|
||||
minLvLG2 = 0;
|
||||
else if (i == evoChain.Count - 1) // minimum level, otherwise next learnable level
|
||||
else if (i == evoChain.Length - 1) // minimum level, otherwise next learnable level
|
||||
minLvLG2 = 5;
|
||||
else if (evo.RequiresLvlUp)
|
||||
minLvLG2 = evo.Level + 1;
|
||||
minLvLG2 = evo.LevelMax + 1;
|
||||
else
|
||||
minLvLG2 = evo.Level;
|
||||
minLvLG2 = evo.LevelMax;
|
||||
|
||||
var moves = GetMoves(pkm, evo.Species, evo.Form, evo.Level, 0, minLvLG2, Version: Version, types: MoveSourceType.ExternalSources, RemoveTransferHM: false, generation: generation);
|
||||
var moves = GetMoves(pkm, evo.Species, evo.Form, evo.LevelMax, 0, minLvLG2, Version: Version, types: MoveSourceType.ExternalSources, RemoveTransferHM: false, generation: generation);
|
||||
var list = i >= index ? preevomoves : evomoves;
|
||||
list.AddRange(moves);
|
||||
}
|
||||
|
@ -239,15 +239,15 @@ namespace PKHeX.Core
|
|||
if (generation <= 2)
|
||||
{
|
||||
if (encounteredEvo) // minimum level, otherwise next learnable level
|
||||
minLvLG1 = evo.MinLevel + 1;
|
||||
minLvLG1 = evo.LevelMin + 1;
|
||||
else // learns level up moves immediately after evolving
|
||||
minLvLG1 = evo.MinLevel;
|
||||
minLvLG1 = evo.LevelMin;
|
||||
|
||||
if (!ParseSettings.AllowGen2MoveReminder(pkm))
|
||||
minLvLG2 = minLvLG1;
|
||||
}
|
||||
|
||||
var maxLevel = evo.Level;
|
||||
var maxLevel = evo.LevelMax;
|
||||
if (!encounteredEvo) // evolution
|
||||
++maxLevel; // allow lvlmoves from the level it evolved to the next species
|
||||
var moves = GetMoves(pkm, evo.Species, evo.Form, maxLevel, minLvLG1, minLvLG2, version, types, RemoveTransferHM, generation);
|
||||
|
@ -278,7 +278,7 @@ namespace PKHeX.Core
|
|||
formCount = pkm.PersonalInfo.FormCount;
|
||||
|
||||
for (int form = 0; form < formCount; form++)
|
||||
r.AddRange(GetMoves(pkm, species, form, chain[0].Level, 0, 0, version, types, RemoveTransferHM, generation));
|
||||
r.AddRange(GetMoves(pkm, species, form, chain[0].LevelMax, 0, 0, version, types, RemoveTransferHM, generation));
|
||||
if (types.HasFlagFast(MoveSourceType.RelearnMoves))
|
||||
r.AddRange(pkm.RelearnMoves);
|
||||
return r.Distinct();
|
||||
|
|
|
@ -397,7 +397,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
// For species catch rate, discard any species that has no valid encounters and a different catch rate than their pre-evolutions
|
||||
var table = EvolutionTree.GetEvolutionTree(1);
|
||||
var chain = table.GetValidPreEvolutions(pkm, maxLevel: pkm.CurrentLevel);
|
||||
var chain = table.GetValidPreEvolutions(pkm, maxLevel: (byte)pkm.CurrentLevel);
|
||||
foreach (var entry in chain)
|
||||
{
|
||||
var s = entry.Species;
|
||||
|
|
|
@ -55,8 +55,8 @@ namespace PKHeX.Core
|
|||
|
||||
private static readonly ValidEncounterMoves NONE = new();
|
||||
public ValidEncounterMoves EncounterMoves { get; internal set; } = NONE;
|
||||
public IReadOnlyList<EvoCriteria>[] EvoChainsAllGens => _evochains ??= EvolutionChain.GetEvolutionChainsAllGens(pkm, EncounterMatch);
|
||||
private IReadOnlyList<EvoCriteria>[]? _evochains;
|
||||
public EvoCriteria[][] EvoChainsAllGens => _evochains ??= EvolutionChain.GetEvolutionChainsAllGens(pkm, EncounterMatch);
|
||||
private EvoCriteria[][]? _evochains;
|
||||
|
||||
/// <summary><see cref="RNG"/> related information that generated the <see cref="PKM.PID"/>/<see cref="PKM.IVs"/> value(s).</summary>
|
||||
public PIDIV PIDIV
|
||||
|
|
|
@ -232,7 +232,7 @@ namespace PKHeX.Core
|
|||
|
||||
// If the species could not exist in Gen3, must match.
|
||||
var g3 = info.EvoChainsAllGens[3];
|
||||
if (g3.Count == 0)
|
||||
if (g3.Length == 0)
|
||||
return AbilityState.MustMatch;
|
||||
|
||||
// Fall through when gen3 pkm transferred to gen4/5
|
||||
|
@ -251,7 +251,7 @@ namespace PKHeX.Core
|
|||
return AbilityState.MustMatch;
|
||||
|
||||
var chain = data.Info.EvoChainsAllGens;
|
||||
bool evolved45 = chain[4].Count > 1 || (pkm.Format == 5 && chain[5].Count > 1);
|
||||
bool evolved45 = chain[4].Length > 1 || (pkm.Format == 5 && chain[5].Length > 1);
|
||||
if (evolved45)
|
||||
{
|
||||
if (pkm.Ability == pers.Ability1) // Could evolve in Gen4/5 and have a Gen3 only ability
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace PKHeX.Core
|
|||
// The special move verifier has a similar check!
|
||||
if (pkm.HGSS && pkm.Ball == (int)Sport) // Can evolve in DP to retain the HG/SS ball -- not able to be captured in any other ball
|
||||
return VerifyBallEquals(data, (int)Sport);
|
||||
if (Info.Generation != 3 || Info.EvoChainsAllGens[3].Count != 2)
|
||||
if (Info.Generation != 3 || Info.EvoChainsAllGens[3].Length != 2)
|
||||
return VerifyBallEquals(data, (int)Poke); // Pokeball Only
|
||||
}
|
||||
|
||||
|
|
|
@ -105,8 +105,8 @@ public sealed class LegendsArceusVerifier : Verifier
|
|||
// Evolve and try
|
||||
for (int i = 0; i < evos.Count - 1; i++)
|
||||
{
|
||||
var (species, form) = evos[i];
|
||||
index = pt.GetFormIndex(species, form);
|
||||
var evo = evos[i];
|
||||
index = pt.GetFormIndex(evo.Species, evo.Form);
|
||||
moveset = Legal.LevelUpLA[index];
|
||||
moveset.SetEvolutionMoves(moves, purchased, count);
|
||||
count = moves.IndexOf(0);
|
||||
|
@ -210,10 +210,10 @@ public sealed class LegendsArceusVerifier : Verifier
|
|||
// Changing forms do not have separate tutor permissions, so we don't need to bother with form changes.
|
||||
// Level up movepools can grant moves for mastery at lower levels for earlier evolutions... find the minimum.
|
||||
int level = 101;
|
||||
foreach (var (species, form) in data.Info.EvoChainsAllGens[8])
|
||||
foreach (var evo in data.Info.EvoChainsAllGens[8])
|
||||
{
|
||||
var pt = PersonalTable.LA;
|
||||
var index = pt.GetFormIndex(species, form);
|
||||
var index = pt.GetFormIndex(evo.Species, evo.Form);
|
||||
var moveset = Legal.LevelUpLA[index];
|
||||
var lvl = moveset.GetLevelLearnMove(moves[i]);
|
||||
if (lvl == -1)
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace PKHeX.Core
|
|||
public PKM ConvertToPKM(ITrainerInfo sav) => ConvertToPKM(sav, EncounterCriteria.Unrestricted);
|
||||
public abstract PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria);
|
||||
|
||||
public abstract bool IsMatchExact(PKM pkm, DexLevel evo);
|
||||
public abstract bool IsMatchExact(PKM pkm, IDexLevel evo);
|
||||
protected abstract bool IsMatchDeferred(PKM pkm);
|
||||
protected abstract bool IsMatchPartial(PKM pkm);
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace PKHeX.Core
|
|||
|
||||
public bool CanBeReceivedBy(int pkmVersion) => (CardCompatibility >> pkmVersion & 1) == 1;
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
var wc = Gift.PK;
|
||||
if (!wc.IsEgg)
|
||||
|
|
|
@ -358,7 +358,7 @@ namespace PKHeX.Core
|
|||
pk.SetIVs(finalIVs);
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (!IsEgg)
|
||||
{
|
||||
|
|
|
@ -277,7 +277,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
// Nothing is stored as a PGT besides Ranger Manaphy. Nothing should trigger these.
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo) => false;
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo) => false;
|
||||
protected override bool IsMatchDeferred(PKM pkm) => false;
|
||||
protected override bool IsMatchPartial(PKM pkm) => false;
|
||||
|
||||
|
|
|
@ -583,7 +583,7 @@ namespace PKHeX.Core
|
|||
pk.SetIVs(finalIVs);
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
{
|
||||
|
|
|
@ -529,7 +529,7 @@ namespace PKHeX.Core
|
|||
|
||||
public bool CanHandleOT(int language) => string.IsNullOrEmpty(GetOT(language));
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
{
|
||||
|
|
|
@ -579,7 +579,7 @@ namespace PKHeX.Core
|
|||
pk.SetIVs(finalIVs);
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if ((short)pkm.Egg_Location == Locations.Default8bNone) // Not Egg
|
||||
{
|
||||
|
|
|
@ -216,7 +216,7 @@ namespace PKHeX.Core
|
|||
};
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
// Gen3 Version MUST match.
|
||||
if (Version != 0 && !Version.Contains((GameVersion)pkm.Version))
|
||||
|
|
|
@ -518,7 +518,7 @@ namespace PKHeX.Core
|
|||
pk.SetIVs(finalIVs);
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
{
|
||||
|
|
|
@ -554,7 +554,7 @@ namespace PKHeX.Core
|
|||
return CardID == 2046 && (pkm.SID << 16 | pkm.TID) == 0x79F57B49;
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
{
|
||||
|
|
|
@ -582,7 +582,7 @@ namespace PKHeX.Core
|
|||
pk.SetIVs(finalIVs);
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
{
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
// Mystery Gift implementation, unused.
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo) => false;
|
||||
public override bool IsMatchExact(PKM pkm, IDexLevel evo) => false;
|
||||
protected override bool IsMatchDeferred(PKM pkm) => false;
|
||||
protected override bool IsMatchPartial(PKM pkm) => false;
|
||||
public override Shiny Shiny => Shiny.Never;
|
||||
|
|
Loading…
Reference in a new issue