diff --git a/PKHeX/Legality/Analysis.cs b/PKHeX/Legality/Analysis.cs index 4666b2772..a5995962d 100644 --- a/PKHeX/Legality/Analysis.cs +++ b/PKHeX/Legality/Analysis.cs @@ -112,7 +112,6 @@ private void parsePK1(PKM pk) verifyNickname(); verifyDVs(); verifyG1OT(); - AddLine(verifyEggMoves()); } private void parsePK3(PKM pk) { @@ -124,7 +123,6 @@ private void parsePK3(PKM pk) updateMoveLegality(); updateEncounterInfo(); updateChecks(); - AddLine(verifyEggMoves()); } private void parsePK4(PKM pk) { @@ -137,7 +135,6 @@ private void parsePK4(PKM pk) updateMoveLegality(); updateEncounterInfo(); updateChecks(); - AddLine(verifyEggMoves()); } private void parsePK5(PKM pk) { @@ -150,7 +147,6 @@ private void parsePK5(PKM pk) updateMoveLegality(); updateEncounterInfo(); updateChecks(); - AddLine(verifyEggMoves()); } private void parsePK6(PKM pk) { @@ -234,8 +230,6 @@ private void updateChecks() verifyRegion(); verifyVersionEvolution(); } - if (pkm.GenNumber <= 5) - verifyEggMoves(); // SecondaryChecked = true; } diff --git a/PKHeX/Legality/Checks.cs b/PKHeX/Legality/Checks.cs index 7c22aa24d..cb6cffa9d 100644 --- a/PKHeX/Legality/Checks.cs +++ b/PKHeX/Legality/Checks.cs @@ -180,7 +180,7 @@ private void verifyECPID() : V216, CheckIdentifier.ECPID); } } - + #region verifyNickname private void verifyNickname() { // If the Pokémon is not nicknamed, it should match one of the language strings. @@ -348,7 +348,7 @@ private void verifyNicknameTrade() else AddLine(Severity.Valid, V11, CheckIdentifier.Nickname); } - + #endregion private void verifyEVs() { var evs = pkm.EVs; @@ -419,6 +419,7 @@ private void verifyDVs() { // todo } + #region verifyOT private void verifyOT() { if (EncounterType == typeof(EncounterTrade)) @@ -456,7 +457,7 @@ private void verifyG1OT() AddLine(Severity.Invalid, V39, CheckIdentifier.Trainer); } } - + #endregion private void verifyHyperTraining() { if (pkm.Format < 7) @@ -479,7 +480,7 @@ private void verifyHyperTraining() } } } - + #region verifyEncounter private CheckResult verifyEncounterLink() { // Should NOT be Fateful, and should be in Database @@ -788,6 +789,7 @@ private CheckResult verifyEncounter() if (result != null) return result; + EncounterStaticMatch = null; // Mark as no satisfying static result EncounterMatch = null; // Reset Encounter Object, test for remaining encounters } @@ -879,7 +881,7 @@ private CheckResult verifyEncounterG3Transfer() if (pkm.Format != 4 && pkm.Met_Location != 30001) AddLine(Severity.Invalid, V61, CheckIdentifier.Encounter); - return G3Result ?? new CheckResult(Severity.Invalid, V80, CheckIdentifier.Encounter); + return G3Result ?? EggResult ?? new CheckResult(Severity.Invalid, V80, CheckIdentifier.Encounter); } private CheckResult verifyEncounterG4Transfer() { @@ -900,15 +902,13 @@ private CheckResult verifyEncounterG4Transfer() if (result != null) return result; + EncounterStaticMatch = null; EncounterMatch = null; // Reset Encounter Object, test for remaining encounters } if (pkm.WasEgg) // Invalid transfer is already checked in encounter egg return verifyEncounterEgg(); - if (Gen4Result == null && null != (EncounterMatch = Legal.getValidFriendSafari(pkm))) - Gen4Result = verifyEncounterSafari(); - if (Gen4Result == null && null != (EncounterMatch = Legal.getValidWildEncounters(pkm))) Gen4Result = verifyEncounterWild(); @@ -931,14 +931,14 @@ private CheckResult verifyEncounterG4Transfer() switch (pkm.Species) { case 251: // Celebi - if (pkm.Met_Location == 30010 || pkm.Met_Location == 30011) // unused || used + if (loc == 30010 || loc == 30011) // unused || used return Gen4Result; return new CheckResult(Severity.Invalid, V351, CheckIdentifier.Encounter); case 243: // Raikou case 244: // Entei case 245: // Suicune - if (pkm.Met_Location == 30012 || pkm.Met_Location == 30013) // unused || used + if (loc == 30012 || loc == 30013) // unused || used return Gen4Result; return new CheckResult(Severity.Invalid, V351, CheckIdentifier.Encounter); } @@ -981,7 +981,7 @@ private CheckResult verifyVCEncounter(int baseSpecies) return new CheckResult(CheckIdentifier.Encounter); } - + #endregion private void verifyLevel() { MysteryGift MatchedGift = EncounterMatch as MysteryGift; @@ -1015,7 +1015,7 @@ private void verifyLevel() else AddLine(Severity.Valid, V88, CheckIdentifier.Level); } - + #region verifyMedals private void verifyMedals() { if (pkm.Format < 6) @@ -1059,7 +1059,7 @@ private void verifyMedalsEvent() else if (DistCount > 0) { AddLine(Severity.Fishy, V94, CheckIdentifier.Training); } } - + #endregion private void verifyRibbons() { if (!Encounter.Valid) @@ -1330,7 +1330,7 @@ private void verifyAbility() AddLine(Severity.Valid, V115, CheckIdentifier.Ability); } - + #region verifyBall private void verifyBallEquals(params int[] balls) { int ball = pkm.Ball; @@ -1641,7 +1641,7 @@ private void verifyBallEggGen7() } AddLine(Severity.Invalid, V125, CheckIdentifier.Ball); } - + #endregion private CheckResult verifyHistory() { if (!Encounter.Valid) @@ -2310,7 +2310,7 @@ private void verifyVersionEvolution() break; } } - + #region verifyMoves private CheckResult[] verifyMoves(GameVersion game = GameVersion.Any) { var validLevelMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, Tutor: false, Machine: false, RemoveTransferHM:false); @@ -2339,7 +2339,10 @@ private CheckResult[] parseMovesForEncounters(GameVersion game, List[] vali if (null != EncounterStaticMatch) encounters.AddRange(EncounterStaticMatch); encounters.Add(EncounterMatch); // can be null - + + if (pkm.Gen3 && pkm.WasEgg) //Can not distinguish event egg and normal egg after hatching, and not in the EncounterStaticMatch + encounters.AddRange(Legal.getG3SpecialEggEncounter(pkm)); + // Iterate over encounters bool pre3DS = pkm.GenNumber < 6; CheckResult[] res = new CheckResult[4]; @@ -2475,15 +2478,12 @@ private CheckResult[] parseMovesWasEggPreRelearn(int[] Moves, List[] validL { CheckResult[] res = new CheckResult[4]; - // Gen 3 could have an egg origin and a non-egg origin, check first non-egg origin - if (pkm.GenNumber == 3 && !pkm.HasOriginalMetLocation && EncounterMatch !=null) + // Gen 1-3 could have an egg origin and a non-egg origin, check first non-egg origin + if (pkm.GenNumber <= 3 && !pkm.HasOriginalMetLocation && EncounterMatch != null) { - res = EncounterMatch is IMoveset - ? parseMovesSpecialMoveset(Moves, validLevelMoves, validTMHM, validTutor) - : parseMovesPreRelearnEncounter(Moves, validLevelMoves, validTMHM, validTutor); // Everything else, non-egg encounters only + res = parseMovesSpecialMoveset(Moves, validLevelMoves, validTMHM, validTutor); if (res.All(r => r.Valid)) // moves are satisfactory - return res; - // If non-egg encounter is not valid check egg-encounter with eggmoves and without special moves + return res; } // Some games can have different egg movepools. Have to check all situations. @@ -2507,13 +2507,13 @@ private CheckResult[] parseMovesWasEggPreRelearn(int[] Moves, List[] validL int splitctr = Legal.SplitBreed.Contains(pkm.Species) ? 1 : 0; foreach (var ver in Games) { - var EventEggMoves = !pkm.WasGiftEgg? Legal.getSpecialEggMoves(pkm, ver).ToArray() : new int[0]; + var EventEggMoves = (EncounterMatch as IMoveset)?.Moves ?? new int[0]; for (int i = 0; i <= splitctr; i++) { - var baseEggMoves = Legal.getBaseEggMoves(pkm, i, ver, 100)?.ToArray() ?? new int[0]; - var EggMoves = pkm.WasGiftEgg ? (EncounterMatch as EncounterStatic)?.Moves ?? new int[0] : Legal.getEggMoves(pkm, i, ver).ToArray(); + var LvlupEggMoves = Legal.getBaseEggMoves(pkm, i, ver, 100)?.ToArray() ?? new int[0]; + var EggMoves = Legal.getEggMoves(pkm, i, ver).ToArray(); - res = parseMoves(Moves, validLevelMoves, new int[0], validTMHM, validTutor, new int[0], baseEggMoves, EggMoves, EventEggMoves); + res = parseMoves(Moves, validLevelMoves, new int[0], validTMHM, validTutor, new int[0], LvlupEggMoves, EggMoves, EventEggMoves); if (res.All(r => r.Valid)) // moves is satisfactory return res; @@ -2521,11 +2521,6 @@ private CheckResult[] parseMovesWasEggPreRelearn(int[] Moves, List[] validL } return res; } - private CheckResult[] parseMovesIsEggPreRelearnEvent(int[] Moves) - { - int[] SpecialMoves = (EncounterMatch as IMoveset)?.Moves ?? new int[0]; - return parseMovesIsEggPreRelearn(Moves, SpecialMoves, false); - } private CheckResult[] parseMovesSketch(int[] Moves) { CheckResult[] res = new CheckResult[4]; @@ -2549,12 +2544,9 @@ private CheckResult[] parseMovesPre3DS(GameVersion game, List[] validLevelM { if (pkm.IsEgg) { - if (MatchIsMysteryGift) - return parseMovesIsEggPreRelearnEvent(Moves); - - int[] SpecialMoves = (EncounterMatch as IMoveset)?.Moves; - var allowinherited = SpecialMoves == null && !pkm.WasGiftEgg && !pkm.WasEventEgg; - return parseMovesIsEggPreRelearn(Moves, SpecialMoves ?? new int[0], allowinherited); + int[] SpecialMoves = (EncounterMatch as IMoveset)?.Moves ?? new int[0]; + var allowinherited = SpecialMoves == null && !pkm.WasGiftEgg && !pkm.WasEventEgg && !MatchIsMysteryGift; + return parseMovesIsEggPreRelearn(Moves, SpecialMoves, allowinherited); } if (pkm.WasEgg) return parseMovesWasEggPreRelearn(Moves, validLevelMoves, validTMHM, validTutor); @@ -2574,11 +2566,6 @@ private CheckResult[] parseMovesSpecialMoveset(int[] Moves, List[] validLev RelearnBase = (EncounterMatch as MysteryGift).RelearnMoves; return res; } - private CheckResult[] parseMovesPreRelearnEncounter(int[] Moves, List[] validLevelMoves, List[] validTMHM, List[] validTutor) - { - int[] SpecialMoves = (EncounterMatch as IMoveset)?.Moves ?? new int[0]; - return parseMoves(Moves, validLevelMoves, new int[0], validTMHM, validTutor, SpecialMoves, new int[0], new int[0], new int[0]); - } private CheckResult[] parseMovesRelearnSplitBreed(int[] Moves, List[] validLevelMoves, List[] validTMHM, List[] validTutor, GameVersion game) { CheckResult[] res = new CheckResult[4]; @@ -2607,12 +2594,12 @@ private CheckResult[] parseMovesRelearn(int[] Moves, List[] validLevelMoves return res; } - private CheckResult[] parseMoves(int[] moves, List[] learn, int[] relearn, List[] tmhm, List[] tutor, int[] special, int[] baseegg, int[] egg, int[] eventegg) + private CheckResult[] parseMoves(int[] moves, List[] learn, int[] relearn, List[] tmhm, List[] tutor, int[] special, int[] lvlupegg, int[] egg, int[] eventegg) { CheckResult[] res = new CheckResult[4]; var Gen1MovesLearned = new List(); var EggMovesLearned = new List(); - var BaseEggMovesLearned = new List(); + var LvlupEggMovesLearned = new List(); var EventEggMovesLearned = new List(); var IsGen2Pkm = pkm.Format == 2 || pkm.VC2; // Check none moves and relearn moves before generation moves @@ -2671,11 +2658,13 @@ private CheckResult[] parseMoves(int[] moves, List[] learn, int[] relearn, if (gen == generations.Last()) { - // Check base egg moves after all the moves but just before egg moves to different it from normal level up moves + // Check higher-level moves after all the moves but just before egg moves to differentiate it from normal level up moves // Also check if the base egg moves is a non tradeback move for (int m = 0; m < 4; m++) { - if (!baseegg.Contains(moves[m])) + if (res[m]?.Valid ?? false) // Skip valid move + continue; + if (!lvlupegg.Contains(moves[m])) // Check if contains level-up egg moves from parents continue; if (IsGen2Pkm && Gen1MovesLearned.Any() && moves[m] > Legal.MaxMoveID_1) @@ -2685,7 +2674,7 @@ private CheckResult[] parseMoves(int[] moves, List[] learn, int[] relearn, } else res[m] = new CheckResult(Severity.Valid, V345, CheckIdentifier.Move); - BaseEggMovesLearned.Add(m); + LvlupEggMovesLearned.Add(m); } // Check egg moves after all the generations and all the moves, every move that can't be learned in another source should have preference @@ -2725,7 +2714,7 @@ private CheckResult[] parseMoves(int[] moves, List[] learn, int[] relearn, // A pokemon could have normal egg moves and regular egg moves // Only if all regular egg moves are event egg moves or all event egg moves are regular egg moves - var RegularEggMovesLearned = EggMovesLearned.Union(BaseEggMovesLearned).ToList(); + var RegularEggMovesLearned = EggMovesLearned.Union(LvlupEggMovesLearned).ToList(); if (RegularEggMovesLearned.Any() && EventEggMovesLearned.Any()) { // Moves that are egg moves or event egg moves but not both @@ -2738,7 +2727,7 @@ private CheckResult[] parseMoves(int[] moves, List[] learn, int[] relearn, res[m] = new CheckResult(Severity.Invalid, V337, CheckIdentifier.Move); else if (!EventEggMovesLearned.Contains(m) && EggMovesLearned.Contains(m)) res[m] = new CheckResult(Severity.Invalid, V336, CheckIdentifier.Move); - else if (!EventEggMovesLearned.Contains(m) && BaseEggMovesLearned.Contains(m)) + else if (!EventEggMovesLearned.Contains(m) && LvlupEggMovesLearned.Contains(m)) res[m] = new CheckResult(Severity.Invalid, V358, CheckIdentifier.Move); } } @@ -3130,38 +3119,7 @@ private void verifyNoEmptyDuplicates(int[] Moves, CheckResult[] res) res[i] = new CheckResult(Severity.Invalid, V168, res[i].Identifier); } } - - private CheckResult verifyEggMoves() - { - if (!pkm.WasEgg || vMoves.All(m => m.Valid)) - return new CheckResult(CheckIdentifier.Egg); - - // todo: egg move breeding legality - switch (pkm.GenNumber) - { - case 1: - case 2: - // Check Both Egg Moves -- egg moves are initially checked with no game as the base. - foreach (var game in new[] {GameVersion.GS, GameVersion.C}) - { - vMoves = verifyMoves(game); - if (vMoves.Any(m => !m.Valid)) - continue; - - // todo: check compatibility of parents (chain wise) - return new CheckResult(Severity.Valid, string.Format(V185, game), CheckIdentifier.Egg); - } - break; - - case 3: - return new CheckResult(Severity.NotImplemented, V186, CheckIdentifier.Egg); - - case 4: - return new CheckResult(Severity.NotImplemented, V186, CheckIdentifier.Egg); - } - return new CheckResult(CheckIdentifier.Egg); - } - + #endregion public static string[] movelist = Util.getMovesList("en"); private static readonly string[] EventRibName = { diff --git a/PKHeX/Legality/Core.cs b/PKHeX/Legality/Core.cs index fd4e1ad77..6d11414d0 100644 --- a/PKHeX/Legality/Core.cs +++ b/PKHeX/Legality/Core.cs @@ -833,10 +833,21 @@ internal static IEnumerable getEggMoves(PKM pkm, int skipOption, GameVersio { return getEggMoves(pkm, getBaseSpecies(pkm, skipOption), 0, Version); } - - internal static IEnumerable getSpecialEggMoves(PKM pkm, GameVersion Version) + internal static IEnumerable getG3SpecialEggEncounter(PKM pkm) { - return getSpecialEggMoves(pkm, getBaseSpecies(pkm), 0, Version); + IEnumerable dl = getValidPreEvolutions(pkm,MaxSpeciesID_3); + var table = EventEgg_G3.Where(e => dl.Any(d => d.Species == e.Species)); + foreach (EncounterStatic e in table) + { + if (pkm.Moves.All(m => !e.Moves.Contains(m))) // No special move + continue; + if (e.Nature != Nature.Random && pkm.Nature != (int)e.Nature) + continue; + if (e.Gender != -1 && e.Gender != pkm.Gender) + continue; + + yield return e; + } } // Encounter @@ -2639,22 +2650,6 @@ private static IEnumerable getMoves(PKM pkm, int species, int lvl, int form } return r; } - private static IEnumerable getSpecialEggMoves(PKM pkm, int species, int alform, GameVersion Version = GameVersion.Any) - { - if (!pkm.InhabitedGeneration(pkm.GenNumber, species)) - return new List(); - switch (pkm.GenNumber) - { - case 3: - { - var boxencounter = Encounter_Box.FirstOrDefault(e => e.Species == species); - if (boxencounter != null) - return boxencounter.Moves; - break; - } - } - return new List(); - } private static IEnumerable getEggMoves(PKM pkm, int species, int formnum, GameVersion Version = GameVersion.Any) { if (!pkm.InhabitedGeneration(pkm.GenNumber, species)) diff --git a/PKHeX/Legality/Structures/EncounterStatic.cs b/PKHeX/Legality/Structures/EncounterStatic.cs index f450a5af8..47924a049 100644 --- a/PKHeX/Legality/Structures/EncounterStatic.cs +++ b/PKHeX/Legality/Structures/EncounterStatic.cs @@ -3,7 +3,7 @@ public class EncounterStatic : IEncounterable, IMoveset { public int Species { get; set; } - public int[] Moves { get; set; } = new int[4]; + public int[] Moves { get; set; } public int Level; public int Location; diff --git a/PKHeX/Legality/Tables3.cs b/PKHeX/Legality/Tables3.cs index 525804ec1..688640ebe 100644 --- a/PKHeX/Legality/Tables3.cs +++ b/PKHeX/Legality/Tables3.cs @@ -216,6 +216,30 @@ public static partial class Legal new EncounterStatic { Species = 300, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{6} }, // Skitty Egg with Pay Day new EncounterStatic { Species = 172, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{57} }, // Pichu Egg with Surf }; + internal static readonly EncounterStatic[] EventEgg_G3 = Encounter_Box.Concat(new[] + { + // PokePark Eggs + new EncounterStatic { Species = 054, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{300} }, // Psyduck with Mud Sport + new EncounterStatic { Species = 172, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{266} }, // Pichu with Follow me + new EncounterStatic { Species = 174, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{321} }, // Igglybuff with Tickle + new EncounterStatic { Species = 222, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{300} }, // Corsola with Mud Sport + new EncounterStatic { Species = 276, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{294} }, // Taillow with Feather Dance + new EncounterStatic { Species = 283, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{300} }, // Surskit with Mud Sport + new EncounterStatic { Species = 293, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{298} }, // Whismur with Teeter Dance + new EncounterStatic { Species = 300, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{205} }, // Skitty with Rollout + new EncounterStatic { Species = 311, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{346} }, // Plusle with Water Sport + new EncounterStatic { Species = 312, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{300} }, // Minun with Mud Sport + new EncounterStatic { Species = 325, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{253} }, // Spoink with Uproar + new EncounterStatic { Species = 327, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{047} }, // Spinda with Sing + new EncounterStatic { Species = 331, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{227} }, // Cacnea with Encore + new EncounterStatic { Species = 341, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{346} }, // Corphish with Water Sport + new EncounterStatic { Species = 360, Level = 05, EggLocation = 255, Version = GameVersion.RSBOX, Moves = new[]{321} }, // Wynaut with Tickle + // Egg Pokémon Present Eggs + new EncounterStatic { Species = 043, Level = 05, EggLocation = 255, Version = GameVersion.FRLG, Moves = new[]{073} }, // Oddish with Leech Seed + new EncounterStatic { Species = 052, Level = 05, EggLocation = 255, Version = GameVersion.FRLG, Moves = new[]{080} }, // Meowth with Petal Dance + new EncounterStatic { Species = 060, Level = 05, EggLocation = 255, Version = GameVersion.FRLG, Moves = new[]{186} }, // Poliwag with Sweet Kiss + new EncounterStatic { Species = 069, Level = 05, EggLocation = 255, Version = GameVersion.FRLG, Moves = new[]{298} }, // Bellsprout with Teeter Dance + }).ToArray(); internal static readonly EncounterStatic[] Encounter_RSE_Roam = { @@ -330,8 +354,8 @@ public static partial class Legal new EncounterStatic { Species = 386, Level = 30, Location = 187, Version = GameVersion.LG, Form = 2, Fateful = true }, // Deoxys @ Birth Island }; - internal static readonly EncounterStatic[] Encounter_RSE = Encounter_RSE_Roam.SelectMany(e => e.Clone(Roaming_MetLocation_RSE)).Concat(Encounter_RSE_Regular).Concat(Encounter_Box).ToArray(); - internal static readonly EncounterStatic[] Encounter_FRLG = Encounter_FRLG_Roam.SelectMany(e => e.Clone(Roaming_MetLocation_FRLG)).Concat(Encounter_FRLG_Stationary).Concat(Encounter_Box).ToArray(); + internal static readonly EncounterStatic[] Encounter_RSE = Encounter_RSE_Roam.SelectMany(e => e.Clone(Roaming_MetLocation_RSE)).Concat(Encounter_RSE_Regular).Concat(EventEgg_G3).ToArray(); + internal static readonly EncounterStatic[] Encounter_FRLG = Encounter_FRLG_Roam.SelectMany(e => e.Clone(Roaming_MetLocation_FRLG)).Concat(Encounter_FRLG_Stationary).Concat(EventEgg_G3).ToArray(); private static readonly int[] TradeContest_Cool = {30, 05, 05, 05, 05, 10}; private static readonly int[] TradeContest_Beauty = {05, 30, 05, 05, 05, 10};