mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-03-21 17:48:28 -05:00
Misc edge case tests
Gen9a Antishiny edge case Evolve-move traversal tweaks; eager checks and more
This commit is contained in:
parent
812f8e847e
commit
18f95269c0
|
|
@ -321,13 +321,15 @@ private static uint GetAdaptedPID(ref Xoroshiro128Plus rand, PKM pk, in Generate
|
|||
else // Never
|
||||
{
|
||||
if (ShinyUtil.GetIsShiny6(fakeTID, pid)) // battled
|
||||
pid ^= 0x1000_0000;
|
||||
pid = AntiShiny(pid);
|
||||
if (ShinyUtil.GetIsShiny6(pk.ID32, pid)) // captured
|
||||
pid ^= 0x1000_0000;
|
||||
pid = AntiShiny(pid);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
public static uint AntiShiny(uint pid) => pid ^ 0x1000_0000;
|
||||
|
||||
private static bool IsMatchUnknownPreFillIVs(PKM pk, in GenerateParam9a enc, Xoroshiro128Plus rand)
|
||||
{
|
||||
int k = enc.FlawlessIVs;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,12 @@ private static bool TryGetSeedConsecutive(in GenerateParam9a param, PKM pk, uint
|
|||
private static bool TryGetSeedSkip(in GenerateParam9a param, PKM pk, uint ec, uint pid, out ulong seed)
|
||||
{
|
||||
var solver = new XoroMachineSkip(ec, pid);
|
||||
if (TryGetSeed(param, pk, solver, out seed))
|
||||
return true;
|
||||
|
||||
// Try again assuming the FakeTrainer XORed the resulting PID to be anti-shiny (terrible luck for the player).
|
||||
pid = LumioseRNG.AntiShiny(pid);
|
||||
solver = new XoroMachineSkip(ec, pid);
|
||||
return TryGetSeed(param, pk, solver, out seed);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ public sealed class LearnGroup7 : ILearnGroup
|
|||
|
||||
public ILearnGroup? GetPrevious(PKM pk, EvolutionHistory history, IEncounterTemplate enc, LearnOption option) => enc.Generation switch
|
||||
{
|
||||
Generation => null,
|
||||
1 => history.HasVisitedGen1 ? LearnGroup1.Instance : null,
|
||||
<= 2 => history.HasVisitedGen2 ? LearnGroup2.Instance : null,
|
||||
_ => history.HasVisitedGen6 ? LearnGroup6.Instance : null,
|
||||
1 => LearnGroup1.Instance,
|
||||
2 => LearnGroup2.Instance,
|
||||
(3 or 4 or 5 or 6) => LearnGroup6.Instance,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
public bool HasVisited(PKM pk, EvolutionHistory history) => history.HasVisitedGen7;
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public sealed class LearnGroup8 : ILearnGroup
|
|||
|
||||
var home = LearnGroupHOME.Instance;
|
||||
if (option != LearnOption.HOME && home.HasVisited(pk, history))
|
||||
return home.Check(result, current, pk, history, enc, types);
|
||||
return home.Check(result, current, pk, history, enc, types, option);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public sealed class LearnGroup8a : ILearnGroup
|
|||
|
||||
var home = LearnGroupHOME.Instance;
|
||||
if (option != LearnOption.HOME && home.HasVisited(pk, history))
|
||||
return home.Check(result, current, pk, history, enc, types);
|
||||
return home.Check(result, current, pk, history, enc, types, option);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public sealed class LearnGroup8b : ILearnGroup
|
|||
|
||||
var home = LearnGroupHOME.Instance;
|
||||
if (option != LearnOption.HOME && home.HasVisited(pk, history))
|
||||
return home.Check(result, current, pk, history, enc, types);
|
||||
return home.Check(result, current, pk, history, enc, types, option);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public sealed class LearnGroup9 : ILearnGroup
|
|||
|
||||
var home = LearnGroupHOME.Instance;
|
||||
if (option != LearnOption.HOME && home.HasVisited(pk, history))
|
||||
return home.Check(result, current, pk, history, enc, types);
|
||||
return home.Check(result, current, pk, history, enc, types, option);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public sealed class LearnGroup9a : ILearnGroup
|
|||
|
||||
var home = LearnGroupHOME.Instance;
|
||||
if (option != LearnOption.HOME && home.HasVisited(pk, history))
|
||||
return home.Check(result, current, pk, history, enc, types);
|
||||
return home.Check(result, current, pk, history, enc, types, option);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,14 @@ namespace PKHeX.Core;
|
|||
public sealed class LearnGroupHOME : ILearnGroup
|
||||
{
|
||||
public static readonly LearnGroupHOME Instance = new();
|
||||
private const LearnOption Option = LearnOption.HOME;
|
||||
public ushort MaxMoveID => 0;
|
||||
|
||||
public ILearnGroup? GetPrevious(PKM pk, EvolutionHistory history, IEncounterTemplate enc, LearnOption option) => null;
|
||||
public bool HasVisited(PKM pk, EvolutionHistory history) => pk is IHomeTrack { HasTracker: true } || !ParseSettings.IgnoreTransferIfNoTracker;
|
||||
|
||||
public bool Check(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, EvolutionHistory history,
|
||||
IEncounterTemplate enc, MoveSourceType types = MoveSourceType.All, LearnOption option = LearnOption.HOME)
|
||||
IEncounterTemplate enc, MoveSourceType types = MoveSourceType.All, LearnOption option = Option)
|
||||
{
|
||||
var context = pk.Context;
|
||||
if (context == EntityContext.None)
|
||||
|
|
@ -29,36 +30,36 @@ public sealed class LearnGroupHOME : ILearnGroup
|
|||
if (history.HasVisitedGen9 && pk is not PK9)
|
||||
{
|
||||
var instance = LearnGroup9.Instance;
|
||||
instance.Check(result, current, pk, history, enc, types, option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
instance.Check(result, current, pk, history, enc, types, Option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
}
|
||||
if (history.HasVisitedZA && pk is not PA9)
|
||||
{
|
||||
var instance = LearnGroup9a.Instance;
|
||||
instance.Check(result, current, pk, history, enc, types, option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
instance.Check(result, current, pk, history, enc, types, Option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
}
|
||||
if (history.HasVisitedSWSH && pk is not PK8)
|
||||
{
|
||||
var instance = LearnGroup8.Instance;
|
||||
instance.Check(result, current, pk, history, enc, types, option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
instance.Check(result, current, pk, history, enc, types, Option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
}
|
||||
if (history.HasVisitedPLA && pk is not PA8)
|
||||
{
|
||||
var instance = LearnGroup8a.Instance;
|
||||
instance.Check(result, current, pk, history, enc, types, option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
instance.Check(result, current, pk, history, enc, types, Option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
}
|
||||
if (history.HasVisitedBDSP && pk is not PB8)
|
||||
{
|
||||
var instance = LearnGroup8b.Instance;
|
||||
instance.Check(result, current, pk, history, enc, types, option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
instance.Check(result, current, pk, history, enc, types, Option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -67,14 +68,14 @@ public sealed class LearnGroupHOME : ILearnGroup
|
|||
// SW/SH is the only game that can ever harbor external moves, and is the only game that uses Battle Version.
|
||||
if (TryAddOriginalMoves(result, current, pk, enc))
|
||||
{
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
}
|
||||
|
||||
// HOME is silly and allows form exclusive moves to be transferred without ever knowing the move.
|
||||
if (TryAddExclusiveMoves(result, current, pk))
|
||||
{
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -82,8 +83,8 @@ public sealed class LearnGroupHOME : ILearnGroup
|
|||
{
|
||||
// PK8 w/ Battle Version can be ignored, as LGP/E has separate HOME data.
|
||||
var instance = LearnGroup7b.Instance;
|
||||
instance.Check(result, current, pk, history, enc, types, option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
instance.Check(result, current, pk, history, enc, types, Option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
}
|
||||
else if (history.HasVisitedGen7)
|
||||
|
|
@ -93,8 +94,8 @@ public sealed class LearnGroupHOME : ILearnGroup
|
|||
ILearnGroup instance = LearnGroup7.Instance;
|
||||
while (true)
|
||||
{
|
||||
instance.Check(result, current, pk, history, enc, types, option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos))
|
||||
instance.Check(result, current, pk, history, enc, types, Option);
|
||||
if (CleanPurge(result, current, pk, types, local, evos, option))
|
||||
return true;
|
||||
var prev = instance.GetPrevious(pk, history, enc, option);
|
||||
if (prev is null)
|
||||
|
|
@ -111,8 +112,11 @@ public sealed class LearnGroupHOME : ILearnGroup
|
|||
/// Scan the results and remove any that are not valid for the game <see cref="local"/> game.
|
||||
/// </summary>
|
||||
/// <returns>True if all results are valid.</returns>
|
||||
private static bool CleanPurge(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, MoveSourceType types, IHomeSource local, ReadOnlySpan<EvoCriteria> evos)
|
||||
private static bool CleanPurge(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, MoveSourceType types, IHomeSource local, ReadOnlySpan<EvoCriteria> evos, LearnOption option)
|
||||
{
|
||||
if (option == LearnOption.AtAnyTime)
|
||||
return MoveResult.AllParsed(result);
|
||||
|
||||
// The logic used to update the results did not check if the move could be learned in the local game.
|
||||
// Double-check the results and remove any that are not valid for the local game.
|
||||
// SW/SH will continue to iterate downwards to previous groups after HOME is checked, so we can exactly check via Environment.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ internal static class EvolutionRestrictions
|
|||
/// <summary>
|
||||
/// List of species that evolve from a previous species having a move while leveling up
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using moves with a counter stored in form argument is explicitly checked via other logic. <see cref="IsFormArgEvolution"/>
|
||||
/// </remarks>
|
||||
private static ushort GetSpeciesEvolutionMove(ushort species) => species switch
|
||||
{
|
||||
(int)Sylveon => EEVEE,
|
||||
|
|
@ -28,15 +31,22 @@ internal static class EvolutionRestrictions
|
|||
(int)Tsareena => (int)Stomp,
|
||||
(int)Naganadel => (int)DragonPulse,
|
||||
(int)Grapploct => (int)Taunt,
|
||||
|
||||
// Form Argument evolutions sometimes with extra conditions; verify here because they aren't checked "completely" elsewhere (yet).
|
||||
(int)Wyrdeer => (int)PsyshieldBash,
|
||||
(int)Overqwil => (int)BarbBarrage,
|
||||
(int)Annihilape => (int)RageFist,
|
||||
|
||||
(int)Farigiraf => (int)TwinBeam,
|
||||
(int)Dudunsparce => (int)HyperDrill,
|
||||
(int)Hydrapple => (int)DragonCheer,
|
||||
_ => NONE,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the evolution is the "rare" variant (less common).
|
||||
/// </summary>
|
||||
/// <param name="encryptionConstant">Random value used to pivot between evolution results.</param>
|
||||
public static bool IsEvolvedSpeciesFormRare(uint encryptionConstant) => encryptionConstant % 100 is 0;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -50,6 +60,9 @@ internal static class EvolutionRestrictions
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(species), species, "Incorrect EC%100 species."),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the evolution result matches what is expected for <see cref="IsEvolvedSpeciesFormRare"/>
|
||||
/// </summary>
|
||||
public static bool GetIsExpectedEvolveFormEC100(ushort species, byte form, bool rare) => species switch
|
||||
{
|
||||
(ushort)Maushold => form == (byte)(rare ? 0 : 1),
|
||||
|
|
@ -76,6 +89,9 @@ public static bool IsFormArgEvolution(ushort species)
|
|||
/// Checks if the <see cref="pk"/> is correctly evolved, assuming it had a known move requirement evolution in its evolution chain.
|
||||
/// </summary>
|
||||
/// <returns>True if unnecessary to check or the evolution was valid.</returns>
|
||||
/// <remarks>
|
||||
/// Performs some eager checks to skip doing a full evolution tree move check.
|
||||
/// </remarks>
|
||||
public static bool IsValidEvolutionWithMove(PKM pk, LegalInfo info)
|
||||
{
|
||||
// Known-move evolutions were introduced in Gen4.
|
||||
|
|
@ -88,6 +104,15 @@ public static bool IsValidEvolutionWithMove(PKM pk, LegalInfo info)
|
|||
if (enc.Species == species)
|
||||
return true;
|
||||
|
||||
// All move evolutions arrive at a maximally-evolved species chain.
|
||||
// Except for Mr. Rime -- just manually devolve and let the rest of the logic check.
|
||||
if (species is (ushort)MrRime)
|
||||
{
|
||||
species = (ushort)MrMime;
|
||||
if (enc.Species == species)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Exclude evolution paths that did not require a move w/level-up evolution
|
||||
var move = GetSpeciesEvolutionMove(species);
|
||||
if (move is NONE)
|
||||
|
|
@ -95,6 +120,10 @@ public static bool IsValidEvolutionWithMove(PKM pk, LegalInfo info)
|
|||
if (!IsMoveSlotAvailable(info.Moves))
|
||||
return false;
|
||||
|
||||
// Check if the move is in relearn moves (can know at any time)
|
||||
if (pk.Format >= 6 && IsMoveInRelearnSource(pk, info, move))
|
||||
return true;
|
||||
|
||||
// Check the entire chain to see if it could have learnt it at any point.
|
||||
var head = LearnGroupUtil.GetCurrentGroup(pk);
|
||||
var pruned = info.EvoChainsAllGens.PruneKeepPreEvolutions(species);
|
||||
|
|
@ -104,6 +133,54 @@ public static bool IsValidEvolutionWithMove(PKM pk, LegalInfo info)
|
|||
return MemoryPermissions.GetCanKnowMove(enc, move, pruned, pk, head);
|
||||
}
|
||||
|
||||
private static bool IsMoveInRelearnSource(PKM pk, LegalInfo info, ushort move)
|
||||
{
|
||||
if (move is not EEVEE)
|
||||
return IsMoveInRelearn(pk, info, move);
|
||||
return IsMoveInRelearn(pk, info, EeveeFairyMoves);
|
||||
}
|
||||
|
||||
private static bool IsMoveInRelearn(PKM pk, LegalInfo info, ReadOnlySpan<ushort> arr)
|
||||
{
|
||||
foreach (var move in arr)
|
||||
{
|
||||
if (IsMoveInRelearn(pk, info, move))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsMoveInRelearn(PKM pk, LegalInfo info, ushort move)
|
||||
{
|
||||
if (pk.IsOriginalMovesetDeleted())
|
||||
return WasMoveInRelearn(pk, info, move);
|
||||
|
||||
var first = pk.RelearnMove1;
|
||||
if (first is 0) // eager check
|
||||
return false;
|
||||
if (pk.RelearnMove1 == move)
|
||||
return true;
|
||||
if (pk.RelearnMove2 == move)
|
||||
return true;
|
||||
if (pk.RelearnMove3 == move)
|
||||
return true;
|
||||
if (pk.RelearnMove4 == move)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool WasMoveInRelearn(PKM pk, LegalInfo info, ushort move)
|
||||
{
|
||||
for (var i = 0; i < info.Moves.Length; i++)
|
||||
{
|
||||
if (pk.GetMove(i) != move)
|
||||
continue;
|
||||
var method = info.Moves[i].Info.Method;
|
||||
return method is { IsEggSource: true } or { IsRelearn: true } or LearnMethod.Encounter;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsValidEvolutionWithMoveAny(IEncounterTemplate enc, ReadOnlySpan<ushort> any, EvolutionHistory history, PKM pk, ILearnGroup head)
|
||||
{
|
||||
foreach (var move in any)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user