From 3489555f74f4352f58e50f52876b93e60fc03fbd Mon Sep 17 00:00:00 2001 From: Kurt Date: Mon, 12 Jan 2026 21:48:02 -0600 Subject: [PATCH] Add edge case handling for forgotten initial moves bdsp/sv/swsh eggs in PLA: original egg relearn are unable to be referenced, so we need to permit all similar for BDSP Underground special moves (egg move sharing via daycare though). Also for any oddballs in SWSH that had relearn moves for special moves. --- .../LearnSource/Group/LearnGroupHOME.cs | 132 ++++++++++++++---- .../Restrictions/EvolutionRestrictions.cs | 2 +- ...E068A61E fakeHOME sharedInherit evolve.pa8 | Bin 0 -> 376 bytes 3 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 Tests/PKHeX.Core.Tests/Legality/Legal/Generation 8/0465 - Tangrowth - E181E068A61E fakeHOME sharedInherit evolve.pa8 diff --git a/PKHeX.Core/Legality/LearnSource/Group/LearnGroupHOME.cs b/PKHeX.Core/Legality/LearnSource/Group/LearnGroupHOME.cs index 316dfb1ab..4a2a2db5c 100644 --- a/PKHeX.Core/Legality/LearnSource/Group/LearnGroupHOME.cs +++ b/PKHeX.Core/Legality/LearnSource/Group/LearnGroupHOME.cs @@ -66,7 +66,7 @@ public sealed class LearnGroupHOME : ILearnGroup // Ignore Battle Version generally; can be transferred back to SW/SH and wiped after the moves have been shared from HOME. // Battle Version is only relevant while in PK8 format, as a wiped moveset can no longer harbor external moves for that format. // 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 (TryAddOriginalMoves(result, current, pk, enc, option)) { if (CleanPurge(result, current, pk, types, local, evos, option)) return true; @@ -157,36 +157,35 @@ private static bool CleanPurge(Span result, ReadOnlySpan cur public void GetAllMoves(Span result, PKM pk, EvolutionHistory history, IEncounterTemplate enc, MoveSourceType types = MoveSourceType.All, LearnOption option = LearnOption.HOME) { - option = LearnOption.HOME; var local = GetCurrent(pk.Context); var evos = history.Get(pk.Context); // Check all adjacent games if (history.HasVisitedGen9 && pk is not PK9) - RentLoopGetAll(LearnGroup9. Instance, result, pk, history, enc, types, option, evos, local); + RentLoopGetAll(LearnGroup9. Instance, result, pk, history, enc, types, Option, evos, local); if (history.HasVisitedZA && pk is not PA9) - RentLoopGetAll(LearnGroup9a.Instance, result, pk, history, enc, types, option, evos, local); + RentLoopGetAll(LearnGroup9a.Instance, result, pk, history, enc, types, Option, evos, local); if (history.HasVisitedSWSH && pk is not PK8) - RentLoopGetAll(LearnGroup8. Instance, result, pk, history, enc, types, option, evos, local); + RentLoopGetAll(LearnGroup8. Instance, result, pk, history, enc, types, Option, evos, local); if (history.HasVisitedPLA && pk is not PA8) - RentLoopGetAll(LearnGroup8a.Instance, result, pk, history, enc, types, option, evos, local); + RentLoopGetAll(LearnGroup8a.Instance, result, pk, history, enc, types, Option, evos, local); if (history.HasVisitedBDSP && pk is not PB8) - RentLoopGetAll(LearnGroup8b.Instance, result, pk, history, enc, types, option, evos, local); - AddOriginalMoves(result, pk, enc, types, local, evos); + RentLoopGetAll(LearnGroup8b.Instance, result, pk, history, enc, types, Option, evos, local); + AddOriginalMoves(result, pk, enc, types, local, evos, option); AddExclusiveMoves(result, pk); // Looking backwards before HOME if (history.HasVisitedLGPE) { - RentLoopGetAll(LearnGroup7b.Instance, result, pk, history, enc, types, option, evos, local); + RentLoopGetAll(LearnGroup7b.Instance, result, pk, history, enc, types, Option, evos, local); } else if (history.HasVisitedGen7) { ILearnGroup instance = LearnGroup7.Instance; while (true) { - RentLoopGetAll(instance, result, pk, history, enc, types, option, evos, local); - var prev = instance.GetPrevious(pk, history, enc, option); + RentLoopGetAll(instance, result, pk, history, enc, types, Option, evos, local); + var prev = instance.GetPrevious(pk, history, enc, Option); if (prev is null) break; instance = prev; @@ -194,37 +193,99 @@ private static bool CleanPurge(Span result, ReadOnlySpan cur } } - private static bool TryAddOriginalMoves(Span result, ReadOnlySpan current, PKM pk, IEncounterTemplate enc) + /// + /// Try to add original moves from the encounter template. + /// + /// if any move is validated. + private static bool TryAddOriginalMoves(Span result, ReadOnlySpan current, PKM pk, IEncounterTemplate enc, LearnOption option) { - if (enc is IMoveset { Moves: { HasMoves: true } x }) - { - if (enc is { Generation: <= 7, Context: not EntityContext.Gen7b } && IsWipedPK8(pk)) - return false; // Battle Version wiped Gen7 and below moves. - var ls = GameData.GetLearnSource(enc.Version); - return AddOriginalMoves(result, current, x, ls.Environment); - } if (enc is EncounterSlot8GO { OriginFormat: PogoImportFormat.PK7 or PogoImportFormat.PB7 } g8) { - if (g8.OriginFormat is PogoImportFormat.PK7 && IsWipedPK8(pk)) + if (option == LearnOption.Current && IsWipedPK8(pk) && g8 is { OriginFormat: PogoImportFormat.PK7 }) return false; // Battle Version wiped Gen7 and below moves. Span moves = stackalloc ushort[4]; g8.GetInitialMoves(pk.MetLevel, moves); return AddOriginalMoves(result, current, moves, g8.OriginFormat == PogoImportFormat.PK7 ? LearnEnvironment.USUM : LearnEnvironment.GG); } + if (enc is IEncounterEgg egg) + { + if (option == LearnOption.Current && IsWipedPK8(pk) && egg is { Generation: <= 7 }) + return false; // Battle Version wiped Gen7 and below moves. + var ls = egg.Learn; + if (AddOriginalMoves(result, current, ls.GetInheritMoves(egg.Species, egg.Form), ls.Environment)) + return true; + if (AddOriginalMoves(result, current, ls.GetEggMoves(egg.Species, egg.Form), ls.Environment)) + return true; + } + if (enc is IMoveset { Moves: { HasMoves: true } x }) + { + if (option == LearnOption.Current && IsWipedPK8(pk) && enc is { Generation: <= 7, Context: not EntityContext.Gen7b }) + return false; // Battle Version wiped Gen7 and below moves. + var ls = GameData.GetLearnSource(enc.Version); + if (AddOriginalMoves(result, current, x, ls.Environment)) + return true; + // fall through + } + if (enc is IRelearn { Relearn: { HasMoves: true } r }) + { + if (option == LearnOption.Current && IsWipedPK8(pk) && enc is { Generation: <= 7, Context: not EntityContext.Gen7b }) + return false; // Battle Version wiped Gen7 and below moves. + var ls = GameData.GetLearnSource(enc.Version); + if (AddOriginalMoves(result, current, r, ls.Environment)) + return true; + // fall through + } + if (enc is ISingleMoveBonus { IsMoveBonusPossible: true } bonus) + { + // Can only have one, but we're looking for an "all possible". + var ls = bonus.GetMoveBonusPossible(); + var learnSource = GameData.GetLearnSource(enc.Version); + if (AddOriginalMovesSingle(result, current, ls, learnSource.Environment)) + return true; + } return false; } - private static void AddOriginalMoves(Span result, PKM pk, IEncounterTemplate enc, MoveSourceType types, IHomeSource local, ReadOnlySpan evos) + /// + /// Adds all possible original moves from the encounter template. + /// + private static void AddOriginalMoves(Span result, PKM pk, IEncounterTemplate enc, MoveSourceType types, IHomeSource local, ReadOnlySpan evos, LearnOption option) { - if (enc is IMoveset { Moves: { HasMoves: true } x }) - { - AddOriginalMoves(result, pk, evos, types, local, x); - } - else if (enc is EncounterSlot8GO { OriginFormat: PogoImportFormat.PK7 or PogoImportFormat.PB7 } g8) + if (enc is EncounterSlot8GO { OriginFormat: PogoImportFormat.PK7 or PogoImportFormat.PB7 } g8) { Span moves = stackalloc ushort[4]; g8.GetInitialMoves(pk.MetLevel, moves); AddOriginalMoves(result, pk, evos, types, local, moves); + return; + } + if (enc is IEncounterEgg egg) + { + if (option == LearnOption.Current && IsWipedPK8(pk) && egg is { Generation: <= 7 }) + return; // Battle Version wiped Gen7 and below moves. + var ls = egg.Learn; + AddOriginalMoves(result, pk, evos, types, local, ls.GetInheritMoves(egg.Species, egg.Form)); + AddOriginalMoves(result, pk, evos, types, local, ls.GetEggMoves(egg.Species, egg.Form)); + return; + } + if (enc is IMoveset { Moves: { HasMoves: true } x }) + { + if (option == LearnOption.Current && IsWipedPK8(pk) && enc is { Generation: <= 7, Context: not EntityContext.Gen7b }) + return; // Battle Version wiped Gen7 and below moves. + AddOriginalMoves(result, pk, evos, types, local, x); + // fall through + } + if (enc is IRelearn { Relearn: { HasMoves: true } r }) + { + if (option == LearnOption.Current && IsWipedPK8(pk) && enc is { Generation: <= 7, Context: not EntityContext.Gen7b }) + return; // Battle Version wiped Gen7 and below moves. + AddOriginalMoves(result, pk, evos, types, local, r); + // fall through + } + if (enc is ISingleMoveBonus { IsMoveBonusPossible: true } bonus) + { + // Can only have one, but we're looking for an "all possible". + var ls = bonus.GetMoveBonusPossible(); + AddOriginalMoves(result, pk, evos, types, local, ls); } } @@ -351,4 +412,23 @@ private static bool AddOriginalMoves(Span result, ReadOnlySpan result, ReadOnlySpan current, ReadOnlySpan moves, LearnEnvironment game) + { + // Only one move to add -- I'm sure there will be issues with this naive approach (in the event that multiple moves are needed, provided another environment is "better" providing.) + foreach (var move in moves) + { + if (move == 0) + break; + var index = current.IndexOf(move); + if (index == -1) + continue; + if (result[index].Valid) + continue; + + result[index] = MoveResult.Initial(game); + return true; + } + return false; + } } diff --git a/PKHeX.Core/Legality/Restrictions/EvolutionRestrictions.cs b/PKHeX.Core/Legality/Restrictions/EvolutionRestrictions.cs index d419361b5..ec54e71ca 100644 --- a/PKHeX.Core/Legality/Restrictions/EvolutionRestrictions.cs +++ b/PKHeX.Core/Legality/Restrictions/EvolutionRestrictions.cs @@ -46,7 +46,7 @@ internal static class EvolutionRestrictions /// /// Checks if the evolution is the "rare" variant (less common). /// - /// Random value used to pivot between evolution results. + /// Random value used to pivot between evolution results. public static bool IsEvolvedSpeciesFormRare(uint encryptionConstant) => encryptionConstant % 100 is 0; /// diff --git a/Tests/PKHeX.Core.Tests/Legality/Legal/Generation 8/0465 - Tangrowth - E181E068A61E fakeHOME sharedInherit evolve.pa8 b/Tests/PKHeX.Core.Tests/Legality/Legal/Generation 8/0465 - Tangrowth - E181E068A61E fakeHOME sharedInherit evolve.pa8 new file mode 100644 index 0000000000000000000000000000000000000000..ab93fab7ff797499d0b0ea2eacf60903e848f6ff GIT binary patch literal 376 zcmb1Jmhpgrq4D8GMh1qqtT~uw;FfbSbMInCsk5wL@ cxhW7GQj9!6#9~;wkC6dn5i&sMBg>(&0a)J`!2kdN literal 0 HcmV?d00001