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); }