From 8c5773969a40f8bee9daecb618a32af3afac9737 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 15 Jul 2023 13:58:41 -0700 Subject: [PATCH] Add HOME hoopa move share, add form specific check Hoopa cannot know both moves due to form reversion Kyurem cannot know both moves due to form reversion Kyurem cannot know Scary Face when fused in past games Add dexnav for AtAnyTime/HOME lookback when original moveset deleted (no longer in local relearn moves) BDSP Underground encounters w/ egg moves can be shared by other means --- .../Legality/LearnSource/Group/LearnGroup6.cs | 25 ++++++++- .../Legality/LearnSource/Group/LearnGroup7.cs | 2 +- .../Legality/LearnSource/Group/LearnGroup8.cs | 2 +- .../Legality/LearnSource/Group/LearnGroup9.cs | 2 +- .../LearnSource/Group/LearnGroupHOME.cs | 33 +++++++++++ .../LearnSource/Verify/LearnVerifier.cs | 55 ++++++++++++++++++- PKHeX.Core/Legality/Tables/FormChangeUtil.cs | 2 +- 7 files changed, 113 insertions(+), 8 deletions(-) diff --git a/PKHeX.Core/Legality/LearnSource/Group/LearnGroup6.cs b/PKHeX.Core/Legality/LearnSource/Group/LearnGroup6.cs index 555bbe6d4..d1bfdf144 100644 --- a/PKHeX.Core/Legality/LearnSource/Group/LearnGroup6.cs +++ b/PKHeX.Core/Legality/LearnSource/Group/LearnGroup6.cs @@ -22,8 +22,13 @@ public sealed class LearnGroup6 : ILearnGroup for (var i = 0; i < evos.Length; i++) Check(result, current, pk, evos[i], i, types, option, mode); - if (option is not LearnOption.Current && types.HasFlag(MoveSourceType.Encounter) && enc is EncounterEgg { Generation: Generation } egg) - CheckEncounterMoves(result, current, egg); + if (option.IsPast() && types.HasFlag(MoveSourceType.Encounter)) + { + if (enc is EncounterEgg { Generation: Generation } egg) + CheckEncounterMoves(result, current, egg); + else if (enc is EncounterSlot6AO { CanDexNav: true } dexnav && pk.IsOriginalMovesetDeleted()) + CheckDexNavMoves(result, current, dexnav); + } return MoveResult.AllParsed(result); } @@ -48,6 +53,22 @@ private static void CheckEncounterMoves(Span result, ReadOnlySpan result, ReadOnlySpan current, EncounterSlot6AO dexnav) + { + // DexNav moves are only available in OR/AS + // Can only have one DexNav move + for (var i = 0; i < result.Length; i++) + { + if (result[i].Valid) + continue; + var move = current[i]; + if (!dexnav.CanBeDexNavMove(move)) + continue; + result[i] = new(new(LearnMethod.Special, LearnEnvironment.ORAS), Generation); + break; + } + } + private static void Check(Span result, ReadOnlySpan current, PKM pk, EvoCriteria evo, int stage, MoveSourceType types, LearnOption option, CheckMode mode) { if (!FormChangeUtil.ShouldIterateForms(evo.Species, evo.Form, Generation, option)) diff --git a/PKHeX.Core/Legality/LearnSource/Group/LearnGroup7.cs b/PKHeX.Core/Legality/LearnSource/Group/LearnGroup7.cs index 8aaa4c0d4..a0c7b0198 100644 --- a/PKHeX.Core/Legality/LearnSource/Group/LearnGroup7.cs +++ b/PKHeX.Core/Legality/LearnSource/Group/LearnGroup7.cs @@ -29,7 +29,7 @@ public sealed class LearnGroup7 : ILearnGroup for (var i = 0; i < evos.Length; i++) Check(result, current, pk, evos[i], i, types, option, mode); - if (option is not LearnOption.Current && types.HasFlag(MoveSourceType.Encounter) && enc is EncounterEgg { Generation: Generation } egg) + if (option.IsPast() && types.HasFlag(MoveSourceType.Encounter) && enc is EncounterEgg { Generation: Generation } egg) CheckEncounterMoves(result, current, egg); return MoveResult.AllParsed(result); diff --git a/PKHeX.Core/Legality/LearnSource/Group/LearnGroup8.cs b/PKHeX.Core/Legality/LearnSource/Group/LearnGroup8.cs index 713e96e7e..c382ed94f 100644 --- a/PKHeX.Core/Legality/LearnSource/Group/LearnGroup8.cs +++ b/PKHeX.Core/Legality/LearnSource/Group/LearnGroup8.cs @@ -46,7 +46,7 @@ public sealed class LearnGroup8 : ILearnGroup CheckSharedMoves(result, current, evos[0]); - if (option is not LearnOption.Current && types.HasFlag(MoveSourceType.Encounter) && pk.IsOriginalMovesetDeleted() && enc is EncounterEgg { Generation: Generation } egg) + if (option.IsPast() && types.HasFlag(MoveSourceType.Encounter) && pk.IsOriginalMovesetDeleted() && enc is EncounterEgg { Generation: Generation } egg) CheckEncounterMoves(result, current, egg); if (MoveResult.AllParsed(result)) diff --git a/PKHeX.Core/Legality/LearnSource/Group/LearnGroup9.cs b/PKHeX.Core/Legality/LearnSource/Group/LearnGroup9.cs index d3f3d9895..6ec1da255 100644 --- a/PKHeX.Core/Legality/LearnSource/Group/LearnGroup9.cs +++ b/PKHeX.Core/Legality/LearnSource/Group/LearnGroup9.cs @@ -27,7 +27,7 @@ public sealed class LearnGroup9 : ILearnGroup CheckSharedMoves(result, current, evos[0]); - if (option is not LearnOption.Current && types.HasFlag(MoveSourceType.Encounter) && pk.IsOriginalMovesetDeleted() && enc is EncounterEgg { Generation: Generation } egg) + if (option.IsPast() && types.HasFlag(MoveSourceType.Encounter) && pk.IsOriginalMovesetDeleted() && enc is EncounterEgg { Generation: Generation } egg) CheckEncounterMoves(result, current, egg); if (MoveResult.AllParsed(result)) diff --git a/PKHeX.Core/Legality/LearnSource/Group/LearnGroupHOME.cs b/PKHeX.Core/Legality/LearnSource/Group/LearnGroupHOME.cs index e5c1b1be0..e0fe90278 100644 --- a/PKHeX.Core/Legality/LearnSource/Group/LearnGroupHOME.cs +++ b/PKHeX.Core/Legality/LearnSource/Group/LearnGroupHOME.cs @@ -60,6 +60,13 @@ public sealed class LearnGroupHOME : ILearnGroup 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)) + return true; + } + // Ignore Battle Version; can be transferred back to SW/SH and wiped after the moves have been shared from HOME if (history.HasVisitedLGPE) @@ -143,6 +150,7 @@ private static bool CleanPurge(Span result, ReadOnlySpan cur 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); + AddExclusiveMoves(result, pk); // Looking backwards before HOME if (history.HasVisitedLGPE) @@ -196,6 +204,31 @@ private static void AddOriginalMoves(Span result, PKM pk, IEncounterTempla } } + + private static bool TryAddExclusiveMoves(Span result, ReadOnlySpan current, PKM pk) + { + if (pk.Species is (int)Species.Hoopa) + { + var move = pk.Form == 0 ? (ushort)Move.HyperspaceHole : (ushort)Move.HyperspaceFury; + var index = current.IndexOf(move); + if (index < 0) + return false; + ref var exist = ref result[index]; + if (exist.Valid) + return false; + exist = new MoveResult(new MoveLearnInfo(LearnMethod.Special, LearnEnvironment.HOME)); + return true; + } + // Kyurem as Fused cannot move into HOME and trigger move sharing. + return false; + } + + private static void AddExclusiveMoves(Span result, PKM pk) + { + if (pk.Species == (int)Species.Hoopa) + result[pk.Form == 0 ? (int)Move.HyperspaceHole : (int)Move.HyperspaceFury] = true; + } + /// /// Get the current HOME source for the given context. /// diff --git a/PKHeX.Core/Legality/LearnSource/Verify/LearnVerifier.cs b/PKHeX.Core/Legality/LearnSource/Verify/LearnVerifier.cs index 8055e75f3..3698f3377 100644 --- a/PKHeX.Core/Legality/LearnSource/Verify/LearnVerifier.cs +++ b/PKHeX.Core/Legality/LearnSource/Verify/LearnVerifier.cs @@ -29,9 +29,14 @@ public static void Verify(Span result, PKM pk, IEncounterTemplate en private static void VerifyMoves(Span result, ReadOnlySpan current, PKM pk, IEncounterTemplate enc, EvolutionHistory history) { if (pk.IsEgg) + { LearnVerifierEgg.Verify(result, current, enc, pk); - else - LearnVerifierHistory.Verify(result, current, enc, pk, history); + return; + } + + LearnVerifierHistory.Verify(result, current, enc, pk, history); + // Check for form exclusive interlocked moves. + FlagFormExclusiveMoves(result, current, pk); } private static void Finalize(Span result, ReadOnlySpan current) @@ -89,4 +94,50 @@ private static void FlagEmptySlotsBeforeIndex(Span result, ReadOnlyS result[i] = EmptyInvalid; } } + + private static void FlagFormExclusiveMoves(Span result, ReadOnlySpan current, PKM pk) + { + if (pk is { Species: (int)Species.Hoopa }) + FlagFormExclusiveHoopa(result, current, pk); + else if (pk is { Species: (int)Species.Kyurem }) + FlagFormExclusiveKyurem(result, current, pk); + } + + private static void FlagFormExclusiveHoopa(Span result, ReadOnlySpan current, PKM pk) + { + if (pk.Format < 8) + return; + + // Hoopa in Gen8+ cannot have Hyperspace Hole if not form 0, and cannot have Hyperspace Fury if not form 1. + var disallow = pk.Form != 0 ? (ushort)Move.HyperspaceHole : (ushort)Move.HyperspaceFury; + var index = current.IndexOf(disallow); + if (index >= 0) + result[index] = MoveResult.Unobtainable(pk.Form == 0 ? (ushort)Move.HyperspaceHole : (ushort)Move.HyperspaceFury); + } + + private static void FlagFormExclusiveKyurem(Span result, ReadOnlySpan current, PKM pk) + { + // Kyurem forms replace Scary Face with their Fusion Move and vice versa. + if (pk.Form is not 1) // not White + { + // Disallow Fusion Flare + var index = current.IndexOf((ushort)Move.FusionFlare); + if (index >= 0) + result[index] = MoveResult.Unobtainable((ushort)Move.ScaryFace); + } + if (pk.Form is not 2) // not Black + { + // Disallow Fusion Flare + var index = current.IndexOf((ushort)Move.FusionBolt); + if (index >= 0) + result[index] = MoveResult.Unobtainable((ushort)Move.ScaryFace); + } + if (pk.Form is not 0 && pk.Format < 8) // unfused + { + // Disallow Scary Face in formats < 8 + var index = current.IndexOf((ushort)Move.ScaryFace); + if (index >= 0) + result[index] = MoveResult.Unobtainable(pk.Form == 1 ? (ushort)Move.FusionFlare : (ushort)Move.FusionBolt); + } + } } diff --git a/PKHeX.Core/Legality/Tables/FormChangeUtil.cs b/PKHeX.Core/Legality/Tables/FormChangeUtil.cs index 7dd43c97b..9f75e74dd 100644 --- a/PKHeX.Core/Legality/Tables/FormChangeUtil.cs +++ b/PKHeX.Core/Legality/Tables/FormChangeUtil.cs @@ -17,7 +17,7 @@ public static class FormChangeUtil /// Conditions we're checking with public static bool ShouldIterateForms(ushort species, byte form, int generation, LearnOption option) { - if (option is not LearnOption.Current) + if (option.IsPast()) return FormChangeMoves.TryGetValue(species, out var func) && func((generation, form)); return IterateAllForms(species); }