Inject rule bypass logic for SV's level100 lvlup

Closes #4143
This commit is contained in:
Kurt 2023-12-31 11:02:23 -08:00
parent 9d83ce1855
commit c021f893f8
18 changed files with 127 additions and 57 deletions

View file

@ -6,6 +6,7 @@ public sealed class EvolutionGroup1 : IEvolutionGroup, IEvolutionEnvironment
{
public static readonly EvolutionGroup1 Instance = new();
private static readonly EvolutionTree Tree = EvolutionTree.Evolves1;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public IEvolutionGroup GetNext(PKM pk, EvolutionOrigin enc) => EvolutionGroup2.Instance;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => pk.Format == 1 && ParseSettings.AllowGen1Tradeback ? EvolutionGroup2.Instance : null;
@ -43,9 +44,10 @@ public sealed class EvolutionGroup1 : IEvolutionGroup, IEvolutionEnvironment
return present;
}
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result)
where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public int Evolve(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EvolutionHistory history)
@ -75,6 +77,6 @@ public sealed class EvolutionGroup1 : IEvolutionGroup, IEvolutionEnvironment
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
}

View file

@ -8,6 +8,7 @@ public sealed class EvolutionGroup2 : IEvolutionGroup
private static readonly EvolutionTree Tree = EvolutionTree.Evolves2;
private const int Generation = 2;
private static PersonalTable2 Personal => PersonalTable.C;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup7.Instance : null;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => pk.Format != 1 ? EvolutionGroup1.Instance : null;
@ -45,7 +46,7 @@ public sealed class EvolutionGroup2 : IEvolutionGroup
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public int Evolve(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EvolutionHistory history)
@ -75,6 +76,6 @@ public sealed class EvolutionGroup2 : IEvolutionGroup
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
}

View file

@ -8,6 +8,7 @@ public sealed class EvolutionGroup3 : IEvolutionGroup
private static readonly EvolutionTree Tree = EvolutionTree.Evolves3;
private const int Generation = 3;
private static PersonalTable3 Personal => PersonalTable.E;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup4.Instance : null;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => null;
@ -39,7 +40,7 @@ public sealed class EvolutionGroup3 : IEvolutionGroup
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public int Evolve(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EvolutionHistory history)
@ -69,6 +70,6 @@ public sealed class EvolutionGroup3 : IEvolutionGroup
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
}

View file

@ -8,6 +8,7 @@ public sealed class EvolutionGroup4 : IEvolutionGroup
private static readonly EvolutionTree Tree = EvolutionTree.Evolves4;
private const int Generation = 4;
private static PersonalTable4 Personal => PersonalTable.HGSS;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup5.Instance : null;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => enc.Generation == 3 ? EvolutionGroup3.Instance : null;
@ -39,7 +40,7 @@ public sealed class EvolutionGroup4 : IEvolutionGroup
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public int Evolve(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EvolutionHistory history)
@ -71,6 +72,6 @@ public sealed class EvolutionGroup4 : IEvolutionGroup
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
}

View file

@ -8,6 +8,7 @@ public sealed class EvolutionGroup5 : IEvolutionGroup
private static readonly EvolutionTree Tree = EvolutionTree.Evolves5;
private const int Generation = 5;
private static PersonalTable5B2W2 Personal => PersonalTable.B2W2;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup6.Instance : null;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => enc.Generation < Generation ? EvolutionGroup4.Instance : null;
@ -32,7 +33,7 @@ public sealed class EvolutionGroup5 : IEvolutionGroup
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public int Evolve(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EvolutionHistory history)
@ -62,6 +63,6 @@ public sealed class EvolutionGroup5 : IEvolutionGroup
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
}

View file

@ -8,6 +8,7 @@ public sealed class EvolutionGroup6 : IEvolutionGroup
private static readonly EvolutionTree Tree = EvolutionTree.Evolves6;
private const int Generation = 6;
private static PersonalTable6AO Personal => PersonalTable.AO;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup7.Instance : null;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => enc.Generation < Generation ? EvolutionGroup5.Instance : null;
@ -39,7 +40,7 @@ public sealed class EvolutionGroup6 : IEvolutionGroup
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public int Evolve(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EvolutionHistory history)
@ -66,6 +67,6 @@ public sealed class EvolutionGroup6 : IEvolutionGroup
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
}

View file

@ -8,6 +8,7 @@ public sealed class EvolutionGroup7 : IEvolutionGroup
private static readonly EvolutionTree Tree = EvolutionTree.Evolves7;
private const int Generation = 7;
private static PersonalTable7 Personal => PersonalTable.USUM;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroupHOME.Instance : null;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc)
@ -75,12 +76,12 @@ public sealed class EvolutionGroup7 : IEvolutionGroup
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
var b = Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
var b = Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
return b && !IsEvolutionBanned(pk, result);
}

View file

@ -8,6 +8,7 @@ public sealed class EvolutionGroup7b : IEvolutionGroup
private static readonly EvolutionTree Tree = EvolutionTree.Evolves7b;
private const int Generation = 7;
private static PersonalTable7GG Personal => PersonalTable.GG;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroupHOME.Instance : null;
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => null;
@ -32,7 +33,7 @@ public sealed class EvolutionGroup7b : IEvolutionGroup
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public int Evolve(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, EvolutionHistory history)
@ -59,6 +60,6 @@ public sealed class EvolutionGroup7b : IEvolutionGroup
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
}

View file

@ -215,15 +215,16 @@ public sealed class EvolutionGroupHOME : IEvolutionGroup
public sealed class EvolutionEnvironment8 : IEvolutionEnvironment
{
private static readonly EvolutionTree Tree = EvolutionTree.Evolves8;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
return Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
var b = Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
var b = Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
return b && !IsEvolutionBanned(pk, head);
}
@ -240,38 +241,35 @@ public sealed class EvolutionEnvironment8 : IEvolutionEnvironment
public sealed class EvolutionEnvironment8a : IEvolutionEnvironment
{
private static readonly EvolutionTree Tree = EvolutionTree.Evolves8a;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
=> Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
=> Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
=> Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
=> Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public sealed class EvolutionEnvironment8b : IEvolutionEnvironment
{
private static readonly EvolutionTree Tree = EvolutionTree.Evolves8b;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
=> Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
=> Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
=> Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
=> Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}
public sealed class EvolutionEnvironment9 : IEvolutionEnvironment
{
private static readonly EvolutionTree Tree = EvolutionTree.Evolves9;
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Level100;
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
=> Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, out result);
=> Tree.Reverse.TryDevolve(head, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
{
var b = Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, out result);
return b && !IsEvolutionBanned(head);
}
// Unreleased Item
private static bool IsEvolutionBanned(in ISpeciesForm head) => false;
=> Tree.Forward.TryEvolve(head, next, pk, currentMaxLevel, levelMin, skipChecks, Tweak, out result);
}

View file

@ -0,0 +1,22 @@
namespace PKHeX.Core;
/// <summary>
/// Tweaks to Evolution rules to account for game-specific behaviors.
/// </summary>
public sealed class EvolutionRuleTweak
{
/// <summary>
/// Default Evolution logic (no tweaks).
/// </summary>
public static readonly EvolutionRuleTweak Default = new();
/// <summary>
/// In Scarlet &amp; Violet, level 100 Pokemon can trigger evolution methods via Rare Candy level up.
/// </summary>
public static readonly EvolutionRuleTweak Level100 = new() { AllowLevelUpEvolution100 = true };
/// <summary>
/// Allow Level Up Evolutions to trigger if already level 100.
/// </summary>
public bool AllowLevelUpEvolution100 { get; init; }
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace PKHeX.Core;
@ -36,7 +37,7 @@ public sealed class EvolutionForwardPersonal(EvolutionMethod[][] Entries, IPerso
}
}
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, EvolutionRuleTweak tweak, out EvoCriteria result) where T : ISpeciesForm
{
var methods = GetForward(head.Species, head.Form);
foreach (var method in methods.Span)
@ -47,11 +48,11 @@ public sealed class EvolutionForwardPersonal(EvolutionMethod[][] Entries, IPerso
if (next.Form != expectForm)
continue;
var chk = method.Check(pk, currentMaxLevel, levelMin, skipChecks);
var chk = method.Check(pk, currentMaxLevel, levelMin, skipChecks, tweak);
if (chk != EvolutionCheckResult.Valid)
continue;
result = Create(next.Species, next.Form, method, currentMaxLevel, levelMin);
result = Create(next.Species, next.Form, method, currentMaxLevel, levelMin, tweak);
return true;
}
@ -59,7 +60,7 @@ public sealed class EvolutionForwardPersonal(EvolutionMethod[][] Entries, IPerso
return false;
}
private static EvoCriteria Create(ushort species, byte form, EvolutionMethod method, byte currentMaxLevel, byte min) => new()
private static EvoCriteria Create(ushort species, byte form, EvolutionMethod method, byte currentMaxLevel, byte min, EvolutionRuleTweak tweak) => new()
{
Species = species,
Form = form,
@ -68,6 +69,20 @@ public sealed class EvolutionForwardPersonal(EvolutionMethod[][] Entries, IPerso
// Temporarily store these and overwrite them when we clean the list.
LevelMin = Math.Max(min, method.Level),
LevelUpRequired = method.LevelUp,
LevelUpRequired = GetLevelUp(method.LevelUp, currentMaxLevel, tweak),
};
/// <summary>
/// Gets the level up requirement for the evolution.
/// </summary>
/// <seealso cref="EvolutionReversal.GetLevelUp"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte GetLevelUp(byte flag, byte currentMaxLevel, EvolutionRuleTweak tweak)
{
if (flag == 0)
return 0;
if (currentMaxLevel == 100 && tweak.AllowLevelUpEvolution100)
return 0;
return flag;
}
}

View file

@ -37,7 +37,8 @@ public sealed class EvolutionForwardSpecies(EvolutionMethod[][] Entries) : IEvol
}
}
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
public bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks,
EvolutionRuleTweak tweak, out EvoCriteria result) where T : ISpeciesForm
{
var methods = GetForward(head.Species, head.Form);
foreach (var method in methods.Span)
@ -48,7 +49,7 @@ public sealed class EvolutionForwardSpecies(EvolutionMethod[][] Entries) : IEvol
if (next.Form != expectForm)
continue;
var chk = method.Check(pk, currentMaxLevel, levelMin, skipChecks);
var chk = method.Check(pk, currentMaxLevel, levelMin, skipChecks, tweak);
if (chk != EvolutionCheckResult.Valid)
continue;
@ -69,6 +70,6 @@ public sealed class EvolutionForwardSpecies(EvolutionMethod[][] Entries) : IEvol
// Temporarily store these and overwrite them when we clean the list.
LevelMin = Math.Max(min, method.Level),
LevelUpRequired = method.LevelUp,
LevelUpRequired = method.LevelUp, // No need to tweak this, all games of this Type have the same default behavior.
};
}

View file

@ -30,7 +30,8 @@ public interface IEvolutionForward
/// <param name="currentMaxLevel">Maximum allowed level for the result</param>
/// <param name="levelMin">Minimum level for the result</param>
/// <param name="skipChecks">Skip evolution exclusion checks</param>
/// <param name="tweak">Rule tweaks to use when checking evolution criteria</param>
/// <param name="result">Resulting evolution criteria</param>
/// <returns>True if the evolution is possible and <see cref="result"/> is valid.</returns>
bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm;
bool TryEvolve<T>(T head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, EvolutionRuleTweak tweak, out EvoCriteria result) where T : ISpeciesForm;
}

View file

@ -39,8 +39,9 @@ public readonly record struct EvolutionMethod(ushort Species, ushort Argument, b
/// <param name="lvl">Current level</param>
/// <param name="levelMin">Minimum level to sanity check with</param>
/// <param name="skipChecks">Option to skip some comparisons to return a 'possible' evolution.</param>
/// <param name="tweak"></param>
/// <returns>True if the evolution criteria is valid.</returns>
public EvolutionCheckResult Check(PKM pk, byte lvl, byte levelMin, bool skipChecks)
public EvolutionCheckResult Check(PKM pk, byte lvl, byte levelMin, bool skipChecks, EvolutionRuleTweak tweak)
{
if (!Method.IsLevelUpRequired())
return ValidNotLevelUp(pk, skipChecks);
@ -57,7 +58,11 @@ public readonly record struct EvolutionMethod(ushort Species, ushort Argument, b
if (Level == 0 && lvl < 2)
return InsufficientLevel;
if (lvl < levelMin + LevelUp && !skipChecks)
{
if (lvl == 100 && tweak.AllowLevelUpEvolution100)
return Valid;
return InsufficientLevel;
}
return Valid;
}

View file

@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
namespace PKHeX.Core;
@ -19,19 +20,20 @@ public static class EvolutionReversal
/// <param name="levelMax">Maximum the entity may exist as</param>
/// <param name="stopSpecies">Species ID that should be the last node, if at all. Provide 0 to fully devolve.</param>
/// <param name="skipChecks">Option to bypass some evolution criteria</param>
/// <param name="tweak">Rule tweaks to use when checking evolution criteria</param>
/// <returns>Reversed evolution lineage, with the lowest index being the current species-form.</returns>
public static int Devolve(this IEvolutionLookup lineage, Span<EvoCriteria> result, ushort species, byte form,
PKM pk, byte levelMin, byte levelMax, ushort stopSpecies, bool skipChecks)
PKM pk, byte levelMin, byte levelMax, ushort stopSpecies, bool skipChecks, EvolutionRuleTweak tweak)
{
// Store our results -- trim at the end when we place it on the heap.
var head = result[0] = new EvoCriteria { Species = species, Form = form, LevelMax = levelMax };
int ctr = Devolve(lineage, result, head, pk, levelMax, levelMin, skipChecks, stopSpecies);
int ctr = Devolve(lineage, result, head, pk, levelMax, levelMin, skipChecks, stopSpecies, tweak);
EvolutionUtil.CleanDevolve(result[..ctr], levelMin);
return ctr;
}
private static int Devolve(IEvolutionLookup lineage, Span<EvoCriteria> result, EvoCriteria head,
PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, ushort stopSpecies)
PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, ushort stopSpecies, EvolutionRuleTweak tweak)
{
// 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.
@ -39,7 +41,7 @@ public static class EvolutionReversal
while (head.Species != stopSpecies)
{
ref readonly var node = ref lineage[head.Species, head.Form];
if (!node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, out var x))
if (!node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, tweak, out var x))
return ctr;
result[ctr++] = x;
@ -48,7 +50,7 @@ public static class EvolutionReversal
return ctr;
}
public static bool TryDevolve(this EvolutionNode node, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result)
public static bool TryDevolve(this EvolutionNode node, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, EvolutionRuleTweak tweak, out EvoCriteria result)
{
// Multiple methods can exist to devolve to the same species-form.
// The first method is less restrictive (no LevelUp req), if two {level/other} methods exist.
@ -59,10 +61,10 @@ public static class EvolutionReversal
return false;
}
var chk = link.Method.Check(pk, currentMaxLevel, levelMin, skipChecks);
var chk = link.Method.Check(pk, currentMaxLevel, levelMin, skipChecks, tweak);
if (chk == EvolutionCheckResult.Valid)
{
result = Create(link, currentMaxLevel);
result = Create(link, currentMaxLevel, tweak);
return true;
}
@ -73,10 +75,10 @@ public static class EvolutionReversal
return false;
}
chk = link.Method.Check(pk, currentMaxLevel, levelMin, skipChecks);
chk = link.Method.Check(pk, currentMaxLevel, levelMin, skipChecks, tweak);
if (chk == EvolutionCheckResult.Valid)
{
result = Create(link, currentMaxLevel);
result = Create(link, currentMaxLevel, tweak);
return true;
}
@ -84,7 +86,7 @@ public static class EvolutionReversal
return false;
}
private static EvoCriteria Create(in EvolutionLink link, byte currentMaxLevel) => new()
private static EvoCriteria Create(in EvolutionLink link, byte currentMaxLevel, EvolutionRuleTweak tweak) => new()
{
Species = link.Species,
Form = link.Form,
@ -93,6 +95,20 @@ public static class EvolutionReversal
// Temporarily store these and overwrite them when we clean the list.
LevelMin = link.Method.Level,
LevelUpRequired = link.Method.LevelUp,
LevelUpRequired = GetLevelUp(link.Method.LevelUp, currentMaxLevel, tweak),
};
/// <summary>
/// Gets the level up requirement for the evolution.
/// </summary>
/// <seealso cref="EvolutionForwardPersonal.GetLevelUp"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte GetLevelUp(byte flag, byte currentMaxLevel, EvolutionRuleTweak tweak)
{
if (flag == 0)
return 0;
if (currentMaxLevel == 100 && tweak.AllowLevelUpEvolution100)
return 0;
return flag;
}
}

View file

@ -47,9 +47,10 @@ public sealed class EvolutionReversePersonal(EvolutionMethod[][] Entries, IPerso
yield return (s.Species, s.Form);
}
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, EvolutionRuleTweak tweak, out EvoCriteria result)
where T : ISpeciesForm
{
ref readonly var node = ref Lineage[head.Species, head.Form];
return node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, out result);
return node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, tweak, out result);
}
}

View file

@ -47,9 +47,10 @@ public sealed class EvolutionReverseSpecies(EvolutionMethod[][] Entries, IPerson
yield return (s.Species, s.Form);
}
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm
public bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, EvolutionRuleTweak tweak, out EvoCriteria result)
where T : ISpeciesForm
{
ref readonly var node = ref Lineage[head.Species, head.Form];
return node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, out result);
return node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, tweak, out result);
}
}

View file

@ -28,7 +28,8 @@ public interface IEvolutionReverse
/// <param name="currentMaxLevel">Maximum allowed level for the result</param>
/// <param name="levelMin">Minimum level for the result</param>
/// <param name="skipChecks">Skip evolution exclusion checks</param>
/// <param name="tweak">Rule tweaks to use when checking evolution criteria</param>
/// <param name="result">Resulting evolution criteria</param>
/// <returns>True if the de-evolution is possible and <see cref="result"/> is valid.</returns>
bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result) where T : ISpeciesForm;
bool TryDevolve<T>(T head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, EvolutionRuleTweak tweak, out EvoCriteria result) where T : ISpeciesForm;
}