diff --git a/PKHeX/Legality/Checks.cs b/PKHeX/Legality/Checks.cs index e86258a95..aa55e2863 100644 --- a/PKHeX/Legality/Checks.cs +++ b/PKHeX/Legality/Checks.cs @@ -2070,12 +2070,24 @@ private CheckResult[] verifyMoves(GameVersion game = GameVersion.Any) Legal.RemoveFutureMoves(pkm, EvoChainsAllGens, ref validLevelMoves, ref validTMHM, ref validTutor); CheckResult[] res; int[] Moves = pkm.Moves; - if (pkm.Species == 235) // Smeargle can have any move except a few + if (pkm.IsEgg && pkm.GenNumber < 6) + { + if(EventGiftMatch?.Count > 0) + res = verifyMovesEggPreRelearnEvent(Moves); + else + { + var SpecialMoves = (EncounterMatch as MysteryGift)?.Moves ?? + (EncounterMatch as EncounterStatic)?.Moves ?? + (EncounterMatch as EncounterTrade)?.Moves ?? + null; + var allowinherited = SpecialMoves == null; + res = verifyMovesEggPreRelearn(Moves, SpecialMoves ?? new int[0], allowinherited); + } + } + else if (pkm.Species == 235) // Smeargle can have any move except a few res = parseMovesSketch(Moves); else if (EventGiftMatch?.Count > 1) // Multiple possible Mystery Gifts matched, get the best match too res = parseMovesGetGift(Moves, validLevelMoves, validTMHM, validTutor); - else if (pkm.WasEgg && pkm.GenNumber < 6) - res = verifyMovesEggPreRelearn(Moves, validLevelMoves, validTMHM, validTutor); else // Everything else res = parseMovesRegular(Moves, validLevelMoves, validTMHM, validTutor, new int[0], game); @@ -2086,11 +2098,8 @@ private CheckResult[] verifyMoves(GameVersion game = GameVersion.Any) return res; } - private CheckResult[] verifyMovesEggPreRelearn(int[] Moves, List[] validLevelMoves, List[] validTMHM, List[] validTutor) + private GameVersion[] getBaseEggGames() { - CheckResult[] res = new CheckResult[4]; - - // Some games can have different egg movepools. Have to check all situations. GameVersion[] Games = { }; switch (pkm.GenNumber) { @@ -2099,29 +2108,103 @@ private CheckResult[] verifyMovesEggPreRelearn(int[] Moves, List[] validLev Games = new[] { GameVersion.GS, GameVersion.C }; break; case 3: - Games = new[] { GameVersion.RS, GameVersion.E, GameVersion.FRLG }; + switch((GameVersion)pkm.Version) + { + case GameVersion.R: + case GameVersion.S: + Games = new[] { GameVersion.RS}; + break; + case GameVersion.E: + Games = new[] { GameVersion.E }; + break; + case GameVersion.FR: + case GameVersion.LG: + Games = new[] { GameVersion.FRLG }; + break; + } break; case 4: - Games = new[] { GameVersion.DP, GameVersion.Pt, GameVersion.HGSS }; + switch ((GameVersion)pkm.Version) + { + case GameVersion.D: + case GameVersion.P: + Games = new[] { GameVersion.DP }; + break; + case GameVersion.Pt: + Games = new[] { GameVersion.Pt }; + break; + case GameVersion.HG: + case GameVersion.SS: + Games = new[] { GameVersion.HGSS }; + break; + } break; case 5: - Games = new[] { GameVersion.BW, GameVersion.B2W2 }; + switch ((GameVersion)pkm.Version) + { + case GameVersion.B: + case GameVersion.W: + Games = new[] { GameVersion.BW }; + break; + case GameVersion.Pt: + Games = new[] { GameVersion.Pt }; + break; + case GameVersion.B2: + case GameVersion.W2: + Games = new[] { GameVersion.B2W2 }; + break; + } break; } - - int splitctr = Legal.SplitBreed.Contains(pkm.Species) ? 1 : 0; + return Games; + } + private CheckResult[] verifyMovesEggPreRelearn(int[] Moves, int[] SpecialMoves, bool allowinherited) + { + CheckResult[] res = new CheckResult[4]; + // Some games can have different egg movepools. Have to check all situations. + GameVersion[] Games = getBaseEggGames(); + int splitctr = Legal.getSplitBreedGeneration(pkm).Contains(pkm.Species) ? 1 : 0; foreach (var ver in Games) { for (int i = 0; i <= splitctr; i++) { var baseEggMoves = Legal.getBaseEggMoves(pkm, i, ver, 100)?.ToArray() ?? new int[0]; - res = parseMovesRegular(Moves, validLevelMoves, validTMHM, validTutor, baseEggMoves, ver); - if (res.All(r => r.Valid)) // moves is satisfactory - return res; + var EggMoves = Legal.getEggMoves(pkm, ver)?.ToList() ?? new List(); + if(pkm.Format > 2 || SpecialMoves.Any()) + { + // For gen 2 is not possible to difference normal eggs from event eggs + // If there is no special moves assume normal egg + res = verifyPreRelearnEggBase(Moves, baseEggMoves, EggMoves, SpecialMoves, allowinherited, ver); + if (res.All(r => r.Valid)) // moves is satisfactory + return res; + } + if(pkm.Format == 2) + { + // For gen 2 if does not match special egg check for normal egg too + res = verifyPreRelearnEggBase(Moves, baseEggMoves, EggMoves, new int[0], true, ver); + if (res.All(r => r.Valid)) // moves is satisfactory + return res; + } } } return res; } + private CheckResult[] verifyMovesEggPreRelearnEvent(int[] Moves) + { + foreach (MysteryGift mg in EventGiftMatch) + { + int[] SpecialMoves = mg.Moves; + CheckResult[] res = verifyMovesEggPreRelearn(Moves, SpecialMoves, false); + if (res.Any(r => !r.Valid)) + continue; + + // Match Found + EncounterMatch = mg; + return res; + } + // no Mystery Gifts matched + return verifyMovesEggPreRelearn(Moves, new int[0], false); + } private CheckResult[] parseMovesSketch(int[] Moves) { CheckResult[] res = new CheckResult[4]; @@ -2461,7 +2544,7 @@ private CheckResult[] verifyRelearnEgg() } bool checkAllGames = pkm.WasTradedEgg; - bool splitBreed = Legal.SplitBreed.Contains(pkm.Species); + bool splitBreed = Legal.getSplitBreedGeneration(pkm).Contains(pkm.Species); int iterate = (checkAllGames ? Games.Length : 1) * (splitBreed ? 2 : 1); for (int i = 0; i < iterate; i++) @@ -2537,6 +2620,85 @@ private CheckResult[] verifyRelearnEggBase(int[] RelearnMoves, int skipOption, G return res; } + /* Similar to verifyRelearnEgg but in pre relearn generation is the moves what should match the expected order + but only if the pokemon is inside an egg */ + private CheckResult[] verifyPreRelearnEggBase(int[] Moves, int[] baseMoves, List eggmoves,int[] specialmoves, bool AllowInherited, GameVersion ver) + { + CheckResult[] res = new CheckResult[4]; + + // Obtain level1 moves + int baseCt = baseMoves.Length; + if (baseCt > 4) baseCt = 4; + + // Obtain Inherited moves + var inherited = Moves.Where(m => m != 0 && (!baseMoves.Contains(m) || eggmoves.Contains(m))).ToList(); + int inheritCt = inherited.Distinct().Count(); + + // Get required amount of base moves + int unique = baseMoves.Concat(inherited).Distinct().Count(); + int reqBase = inheritCt == 4 || baseCt + inheritCt > 4 ? 4 - inheritCt : baseCt; + if (Moves.Where(m => m != 0).Count() < Math.Min(4, baseMoves.Length)) + reqBase = Math.Min(4, unique); + + reqBase = reqBase + specialmoves.Length > 4 ? reqBase - specialmoves.Length : reqBase; + + var em = string.Empty; + // Check if the required amount of Base Egg Moves are present. + for (int i = 0; i < reqBase; i++) + { + if (baseMoves.Contains(Moves[i])) + res[i] = new CheckResult(Severity.Valid, V179, CheckIdentifier.Move); + else if (specialmoves.Length > 0) + { + // mark remaining base egg moves missing + for (int z = i; z < reqBase; z++) + res[z] = new CheckResult(Severity.Invalid, V180, CheckIdentifier.Move); + + // provide the list of suggested base moves for the last required slot + em = string.Join(", ", baseMoves.Select(m => m >= movelist.Length ? V190 : movelist[m])); + break; + } + } + + // Check also if the required amount of Special Egg Moves are present, ir are after base moves + for (int i = reqBase; i < specialmoves.Length; i++) + { + if (specialmoves.Contains(Moves[i])) + res[i] = new CheckResult(Severity.Valid, V333, CheckIdentifier.Move); + else + { + // mark remaining special egg moves missing + for (int z = i; z < specialmoves.Length; z++) + res[z] = new CheckResult(Severity.Invalid, V342, CheckIdentifier.Move); + + // provide the list of suggested base moves for the last required slot + em = string.Join(", ", baseMoves.Union(specialmoves).Select(m => m >= movelist.Length ? V190 : movelist[m])); + break; + } + } + + if(!string.IsNullOrEmpty(em)) + res[reqBase > 0 ? reqBase - 1 : 0].Comment += string.Format(Environment.NewLine + V343, em); + // Non-Base moves that can magically appear in the regular movepool + if (Legal.LightBall.Contains(pkm.Species)) + eggmoves.Add(344); + + // Inherited moves appear after the required base moves. + for (int i = reqBase + specialmoves.Length; i < 4; i++) + { + if (Moves[i] == 0) // empty + res[i] = new CheckResult(Severity.Valid, V167, CheckIdentifier.Move); + else if (AllowInherited && eggmoves.Contains(Moves[i])) // inherited + res[i] = new CheckResult(Severity.Valid, V171, CheckIdentifier.Move); + else if (!AllowInherited && eggmoves.Contains(Moves[i])) // inherited in event/gift pokemon + res[i] = new CheckResult(Severity.Invalid, V341, CheckIdentifier.Move); + else // not inheritable, flag + res[i] = new CheckResult(Severity.Invalid, V340, CheckIdentifier.Move); + } + + return res; + } + private void verifyNoEmptyDuplicates(int[] Moves, CheckResult[] res) { bool emptySlot = false; diff --git a/PKHeX/Legality/Core.cs b/PKHeX/Legality/Core.cs index cabffcd7d..371ce358a 100644 --- a/PKHeX/Legality/Core.cs +++ b/PKHeX/Legality/Core.cs @@ -1371,6 +1371,25 @@ internal static int[] getWildBalls(PKM pkm) return null; } } + + internal static int[] getSplitBreedGeneration(PKM pkm) + { + return getSplitBreedGeneration(pkm.GenNumber); + } + internal static int[] getSplitBreedGeneration(int generation) + { + switch (generation) + { + case 1: return new int[0]; + case 2: return new int[0]; + case 3: return SplitBreed_3; + case 4: return SplitBreed; + case 5: return SplitBreed; + case 6: return SplitBreed; + case 7: return SplitBreed; + default: return new int[0]; + } + } internal static int getMaxSpeciesOrigin(PKM pkm) { if (pkm.Format == 1 || pkm.VC1) // Gen1 VC could not trade with gen 2 yet @@ -1472,7 +1491,7 @@ internal static bool getEvolutionValid(PKM pkm) var curr = getValidPreEvolutions(pkm); var poss = getValidPreEvolutions(pkm, 100, skipChecks: true); - if (SplitBreed.Contains(getBaseSpecies(pkm, 1))) + if (getSplitBreedGeneration(pkm).Contains(getBaseSpecies(pkm, 1))) return curr.Count() >= poss.Count() - 1; return curr.Count() >= poss.Count(); } diff --git a/PKHeX/Legality/LegalityCheckStrings.cs b/PKHeX/Legality/LegalityCheckStrings.cs index ba283e673..8aef7f4fa 100644 --- a/PKHeX/Legality/LegalityCheckStrings.cs +++ b/PKHeX/Legality/LegalityCheckStrings.cs @@ -325,7 +325,11 @@ public static class LegalityCheckStrings public static string V337 {get; set;} = "Event Egg Move. Incompatible with normal egg moves."; public static string V338 {get; set;} = "Defog and whirpool. One of the two moves should have been removed before transfered to generation 5."; public static string V339 {get; set;} = "Generation {0} HM. Should have been removed before transfered to generation {1}."; + public static string V340 {get; set;} = "Not an expected egg move."; + public static string V341 {get; set;} = "Egg Move.Not expected in an event egg."; + public static string V342 {get; set;} = "Event egg move missing."; + public static string V343 {get; set;} = "Expected the following Moves: { 0}"; #endregion - } + } } diff --git a/PKHeX/Legality/Tables3.cs b/PKHeX/Legality/Tables3.cs index d3de454f8..e564499d7 100644 --- a/PKHeX/Legality/Tables3.cs +++ b/PKHeX/Legality/Tables3.cs @@ -10,7 +10,14 @@ public static partial class Legal internal const int MaxItemID_3 = 374; internal const int MaxAbilityID_3 = 77; internal const int MaxBallID_3 = 0xC; - + + public static readonly int[] SplitBreed_3 = + { + // Incense + 183, 184, // Marill + 202, // Wobbuffet + }; + #region RS internal static readonly ushort[] Pouch_Items_RS = { 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 93, 94, 95, 96, 97, 98, 103, 104, 106, 107, 108, 109, 110, 111, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 254, 255, 256, 257, 258