diff --git a/PKHeX.Core/Legality/Analysis.cs b/PKHeX.Core/Legality/Analysis.cs index f971adfb2..0c7ced1ef 100644 --- a/PKHeX.Core/Legality/Analysis.cs +++ b/PKHeX.Core/Legality/Analysis.cs @@ -44,7 +44,10 @@ private IEnumerable AllSuggestedRelearnMoves if (Error) return new int[4]; if (_allSuggestedRelearnMoves == null) - return _allSuggestedRelearnMoves = pkm == null || !pkm.IsOriginValid ? new int[4] : Legal.getValidRelearn(pkm, info.EncounterMatch.Species).ToArray(); + { + var inheritLvlMoves = pkm.PersonalInfo.Gender > 0 && pkm.PersonalInfo.Gender < 255 || Legal.MixedGenderBreeding.Contains(info.EncounterMatch.Species); + return _allSuggestedRelearnMoves = pkm == null || !pkm.IsOriginValid ? new int[4] : Legal.getValidRelearn(pkm, info.EncounterMatch.Species, inheritLvlMoves).ToArray(); + } return _allSuggestedRelearnMoves; } } diff --git a/PKHeX.Core/Legality/Checks.cs b/PKHeX.Core/Legality/Checks.cs index 35213e350..de922d205 100644 --- a/PKHeX.Core/Legality/Checks.cs +++ b/PKHeX.Core/Legality/Checks.cs @@ -626,7 +626,7 @@ private void verifyLevel() } private void verifyG1TradeEvo() { - var mustevolve = pkm.TradebackStatus == TradebackType.WasTradeback || (pkm.Format == 1 && Legal.IsOutsider(pkm)); + var mustevolve = pkm.TradebackStatus == TradebackType.WasTradeback || (pkm.Format == 1 && Legal.IsOutsider(pkm)) || Legal.IsTradedKadabraG1(pkm); if (!mustevolve) return; // Pokemon have been traded but it is not evolved, trade evos are sequential dex numbers @@ -1792,7 +1792,7 @@ private void verifyForm() int index = Array.IndexOf(pkm.Moves, 548); // Secret Sword bool noSword = index < 0; if (pkm.AltForm == 0 ^ noSword) // mismatch - info.vMoves[noSword ? 0 : index] = new CheckResult(Severity.Invalid, V169, CheckIdentifier.Move); + info.vMoves[noSword ? 0 : index] = new CheckMoveResult(info.vMoves[noSword ? 0 : index], Severity.Invalid, V169, CheckIdentifier.Move); break; } case 649: // Genesect diff --git a/PKHeX.Core/Legality/Core.cs b/PKHeX.Core/Legality/Core.cs index 892c4e91d..b1c8f9a89 100644 --- a/PKHeX.Core/Legality/Core.cs +++ b/PKHeX.Core/Legality/Core.cs @@ -1206,7 +1206,7 @@ internal static IEnumerable getValidMoves(PKM pkm, DexLevel[] evoChain, int version = GameVersion.Any; return getValidMoves(pkm, version, evoChain, generation, minLvLG1: minLvLG1, LVL: LVL, Relearn: false, Tutor: Tutor, Machine: Machine, MoveReminder: MoveReminder, RemoveTransferHM: RemoveTransferHM); } - internal static IEnumerable getValidRelearn(PKM pkm, int species) + internal static IEnumerable getValidRelearn(PKM pkm, int species, bool inheritlvlmoves) { List r = new List { 0 }; if (pkm.GenNumber < 6 || pkm.VC) @@ -1219,7 +1219,7 @@ internal static IEnumerable getValidRelearn(PKM pkm, int species) form = 0; r.AddRange(getEggMoves(pkm, species, form)); - if (pkm.Species != 489) + if (inheritlvlmoves) r.AddRange(getRelearnLVLMoves(pkm, species, 100, pkm.AltForm)); return r.Distinct(); } @@ -1997,6 +1997,63 @@ internal static bool getEvolutionValid(PKM pkm, int minSpecies = -1) return curr.Count() >= poss.Count() - 1; return curr.Count() >= poss.Count(); } + internal static bool getEvolutionWithMoveValid(PKM pkm, LegalInfo info) + { + // Exclude species that do not evolve leveling with a move + // Exclude gen 1-3 games + // Exclude Mr Mime and Snorlax for gen 1-3 games + if (!SpeciesEvolutionWithMove.Contains(pkm.Species) || pkm.Format <= 3 || (BabyEvolutionWithMove.Contains(pkm.Species) && pkm.GenNumber <= 3)) + return true; + + var index = Array.FindIndex(SpeciesEvolutionWithMove, p => p == pkm.Species); + var levels = MinLevelEvolutionWithMove[index]; + var moves = MoveEvolutionWithMove[index]; + var allowegg = EggMoveEvolutionWithMove[index][pkm.GenNumber]; + + // Get the minimun level in any generation when the pokemon could learn the evolve move + var LearnLevel = 101; + for(int g = pkm.GenNumber; g <= pkm.Format; g++) + if(pkm.InhabitedGeneration(g) && levels[g] > 0) + LearnLevel = Math.Min(LearnLevel, levels[g]); + + // Check also if the current encounter include the evolve move as an special move + // That means the pokemon have the move from the encounter level + int[] SpecialMoves = (info.EncounterMatch as IMoveset)?.Moves ?? new int[0]; + if (SpecialMoves.Any(m => moves.Contains(m))) + LearnLevel = Math.Min(LearnLevel, info.EncounterMatch.LevelMin); + + // If the encounter is a player hatched egg check if the move could be an egg move or inherited level up move + if (info.EncounterMatch.EggEncounter && !pkm.WasGiftEgg && !pkm.WasEventEgg && allowegg) + { + var inheritmove = false; + if (pkm.GenNumber >= 6) + // 3DS games, if the move is not a relearn move that means the pokemon was hatched without the move + inheritmove = pkm.RelearnMoves.Any(m => moves.Contains(m)); + else if (pkm.Moves.Any(m => moves.Contains(m))) + // Pre-3DS games, if the pokemon was an egg and it have the move and also and egg from this species could hatch with the move + // that means is a valid egg move + inheritmove = true; + else + { + // If the pokemon does not have the move it still could be an egg move that was forgotten + // But that requires for the pokemon to do not have 4 other moves identified as egg moves or inherited level up moves + var eggmoves = info.vMoves.Count(m => m.Source == MoveSource.EggMove || m.Source == MoveSource.InheritLevelUp); + inheritmove = eggmoves < 4; + } + if (inheritmove) + LearnLevel = Math.Min(LearnLevel, pkm.GenNumber < 4 ? 5 : 1); + } + + // If has original met location the minimun evolution level is one level after met level + // Gen 3 pokemon in gen 4 games minimun level is one level after transfer to generation 4 + // VC pokemon minimun level is one leve after transfer to generation 7 + // Sylveon always one level after met level, for gen 4 and 5 eevees in gen 6 games minimun for evolution is one leve after transfer to generation 5 + if (pkm.HasOriginalMetLocation || pkm.Format == 4 && pkm.Gen3 || pkm.VC || pkm.Species == 700) + LearnLevel = Math.Max(pkm.Met_Level, LearnLevel); + + // Current level must be at leats one level after the minimun learn level + return pkm.CurrentLevel > LearnLevel; + } internal static bool getCanFormChange(PKM pkm, int species) { if (FormChange.Contains(species)) @@ -2642,7 +2699,7 @@ private static IEnumerable getMoves(PKM pkm, int species, int minlvlG1, int internal static int[] getEggMoves(PKM pkm, int species, int formnum, GameVersion Version = GameVersion.Any) { - if (!pkm.InhabitedGeneration(pkm.GenNumber, species)) + if (!pkm.InhabitedGeneration(pkm.GenNumber, species) || pkm.PersonalInfo.Gender == 255) return new int[0]; switch (pkm.GenNumber) @@ -2872,6 +2929,34 @@ internal static List[] GetEmptyMovesList(DexLevel[][] EvoChainsAllGens) empty[i] = new List(); return empty; } + internal static bool IsTradedKadabraG1(PKM pkm) + { + if (pkm.SpecForm != 64 || pkm.Format > 1) + return false; + if (pkm.TradebackStatus == TradebackType.WasTradeback) + return true; + var IsYellow = Savegame_Version == GameVersion.Y; + if (pkm.TradebackStatus == TradebackType.Gen1_NotTradeback) + { + // If catch rate is abra catch rate it wont trigger as invalid trade without evolution, it could be traded as Abra + var catch_rate = (pkm as PK1).Catch_Rate; + // Yellow Kadabra catch rate in Red/Blue game, must be Allakazham + if (catch_rate == PersonalTable.Y[64].CatchRate && !IsYellow) + return true; + // Red/Blue Kadabra catch rate in Yellow game, must be Allakazham + if (catch_rate == PersonalTable.RB[64].CatchRate && IsYellow) + return true; + } + if (IsYellow) + return false; + // Yellow only moves in Red/Blue game, must be Allakazham + if (pkm.Moves.Contains(134)) // Kinesis, yellow only move + return true; + if (pkm.CurrentLevel < 20 && pkm.Moves.Contains(50)) // Disable bellow level 20, yellow only move + return true; + + return false; + } internal static bool IsOutsider(PKM pkm) { var Outsider = Savegame_TID != pkm.TID || Savegame_OT != pkm.OT_Name; diff --git a/PKHeX.Core/Legality/Encounters/EncounterFinder.cs b/PKHeX.Core/Legality/Encounters/EncounterFinder.cs index 580f96c93..24dbf98df 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterFinder.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterFinder.cs @@ -41,7 +41,7 @@ public static LegalInfo verifyEncounter(PKM pkm) if (info.vMoves.Any(z => !z.Valid) && encounter.PeekIsNext()) continue; - var evo = VerifyEvolution.verifyEvolution(pkm, EncounterMatch); + var evo = VerifyEvolution.verifyEvolution(pkm, info); if (!evo.Valid && encounter.PeekIsNext()) continue; info.Parse.Add(evo); diff --git a/PKHeX.Core/Legality/Encounters/LegalInfo.cs b/PKHeX.Core/Legality/Encounters/LegalInfo.cs index 356776e49..e91136532 100644 --- a/PKHeX.Core/Legality/Encounters/LegalInfo.cs +++ b/PKHeX.Core/Legality/Encounters/LegalInfo.cs @@ -31,7 +31,7 @@ public IEncounterable EncounterMatch public readonly List Parse = new List(); public CheckResult[] vRelearn = new CheckResult[4]; - public CheckResult[] vMoves = new CheckResult[4]; + public CheckMoveResult[] vMoves = new CheckMoveResult[4]; public DexLevel[][] EvoChainsAllGens => _evochains ?? (_evochains = Legal.getEvolutionChainsAllGens(pkm, EncounterMatch)); public ValidEncounterMoves EncounterMoves { get; set; } diff --git a/PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs b/PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs index 47a1034a1..c0553023d 100644 --- a/PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs +++ b/PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core { public static class VerifyCurrentMoves { - public static CheckResult[] verifyMoves(PKM pkm, LegalInfo info, GameVersion game = GameVersion.Any) + public static CheckMoveResult[] verifyMoves(PKM pkm, LegalInfo info, GameVersion game = GameVersion.Any) { int[] Moves = pkm.Moves; var res = parseMovesForEncounters(pkm, info, game, Moves); @@ -16,12 +16,12 @@ public static CheckResult[] verifyMoves(PKM pkm, LegalInfo info, GameVersion gam // Duplicate Moves Check verifyNoEmptyDuplicates(Moves, res); if (Moves[0] == 0) // Can't have an empty moveslot for the first move. - res[0] = new CheckResult(Severity.Invalid, V167, CheckIdentifier.Move); + res[0] = new CheckMoveResult(res[0], Severity.Invalid, V167, CheckIdentifier.Move); return res; } - private static CheckResult[] parseMovesForEncounters(PKM pkm, LegalInfo info, GameVersion game, int[] Moves) + private static CheckMoveResult[] parseMovesForEncounters(PKM pkm, LegalInfo info, GameVersion game, int[] Moves) { if (pkm.Species == 235) // special handling for Smeargle return parseMovesForSmeargle(pkm, Moves, info); // Smeargle can have any moves except a few @@ -55,10 +55,10 @@ private static CheckResult[] parseMovesForEncounters(PKM pkm, LegalInfo info, Ga pkm.TradebackStatus = defaultTradeback; return res; } - private static CheckResult[] parseMovesForSmeargle(PKM pkm, int[] Moves, LegalInfo info) + private static CheckMoveResult[] parseMovesForSmeargle(PKM pkm, int[] Moves, LegalInfo info) { if (!pkm.IsEgg) - return parseMovesSketch(Moves); + return parseMovesSketch(pkm, Moves); // can only know sketch as egg var empty = ValidEncounterMoves.Empty; @@ -66,15 +66,18 @@ private static CheckResult[] parseMovesForSmeargle(PKM pkm, int[] Moves, LegalIn { validLevelUpMoves = Legal.getValidMovesAllGens(pkm, info.EvoChainsAllGens, minLvLG1: 1, Tutor: false, Machine: false, RemoveTransferHM: false) }; - return parseMoves(pkm, pkm.Moves, new int[0], new int[0], new int[0], empty, new int[0], new int[0], false, info); + return parseMoves(pkm, pkm.Moves, new int[0], new int[0], new int[0], new int[0], new int[0], new int[0], false, info); } - private static CheckResult[] parseMovesIsEggPreRelearn(PKM pkm, int[] Moves, int[] SpecialMoves, bool allowinherited, EncounterEgg e) + private static CheckMoveResult[] parseMovesIsEggPreRelearn(PKM pkm, int[] Moves, int[] SpecialMoves, bool allowinherited, EncounterEgg e) { - CheckResult[] res = new CheckResult[4]; + CheckMoveResult[] res = new CheckMoveResult[4]; var ValidSpecialMoves = SpecialMoves.Where(m => m != 0).ToList(); var baseEggMoves = Legal.getBaseEggMoves(pkm, e.Species, e.Game, pkm.GenNumber < 4 ? 5 : 1)?.ToList() ?? new List(); - var InheritedLvlMoves = Legal.getBaseEggMoves(pkm, e.Species, e.Game, 100)?.ToList() ?? new List(); + // Level up moves could not be inherited if Ditto is parent, + // that means genderless species and male only species except Nidoran and Volbet (they breed with female nidoran and illumise) could not have level up moves as an egg + var AllowLvlMoves = pkm.PersonalInfo.Gender > 0 && pkm.PersonalInfo.Gender < 255 || Legal.MixedGenderBreeding.Contains(e.Species); + var InheritedLvlMoves = !AllowLvlMoves? new List() : Legal.getBaseEggMoves(pkm, e.Species, e.Game, 100)?.ToList() ?? new List(); var EggMoves = Legal.getEggMoves(pkm, e.Species, pkm.AltForm)?.ToList() ?? new List(); var InheritedTutorMoves = e.Game == GameVersion.C ? Legal.getTutorMoves(pkm, pkm.Species, pkm.AltForm, false, 2)?.ToList() : new List(); // Only TM Hm moves from the source game of the egg, not any other games from the same generation @@ -94,30 +97,35 @@ private static CheckResult[] parseMovesIsEggPreRelearn(PKM pkm, int[] Moves, int return res; } - private static CheckResult[] parseMovesWasEggPreRelearn(PKM pkm, int[] Moves, LegalInfo info, EncounterEgg e) + private static CheckMoveResult[] parseMovesWasEggPreRelearn(PKM pkm, int[] Moves, LegalInfo info, EncounterEgg e) { var EventEggMoves = (info.EncounterMatch as IMoveset)?.Moves ?? new int[0]; - int BaseLvlMoves = 489 <= pkm.Species && pkm.Species <= 490 ? 1 : 100; + // Level up moves could not be inherited if Ditto is parent, + // that means genderless species and male only species except Nidoran and Volbet (they breed with female nidoran and illumise) could not have level up moves as an egg + var inheritLvlMoves = pkm.PersonalInfo.Gender > 0 && pkm.PersonalInfo.Gender < 255 || Legal.MixedGenderBreeding.Contains(e.Species); + int BaseLvlMoves = inheritLvlMoves ? 100 : pkm.GenNumber <= 3 ? 5 : 1; var LvlupEggMoves = Legal.getBaseEggMoves(pkm, e.Species, e.Game, BaseLvlMoves); - // Level up, TMHM or tutor moves exclusive to the incense egg species, like Azurill, incompatible with the non-incense species egg moves - var ExclusiveIncenseMoves = e.SplitBreed ? Legal.getExclusivePreEvolutionMoves(pkm, Legal.getBaseEggSpecies(pkm), info.EvoChainsAllGens, e.Game) : null; + var TradebackPreevo = pkm.Format == 2 && info.EncounterMatch.Species > 151; + var NonTradebackLvlMoves = new int[0]; + if (TradebackPreevo) + NonTradebackLvlMoves = Legal.getExclusivePreEvolutionMoves(pkm, info.EncounterMatch.Species, info.EvoChainsAllGens[2], 2, e.Game).Where(m => m > Legal.MaxMoveID_1).ToArray(); var EggMoves = Legal.getEggMoves(pkm, e.Species, pkm.AltForm); bool volt = (pkm.GenNumber > 3 || e.Game == GameVersion.E) && Legal.LightBall.Contains(pkm.Species); var SpecialMoves = volt && EventEggMoves.Length == 0 ? new[] { 344 } : new int[0]; // Volt Tackle for bred Pichu line - return parseMoves(pkm, Moves, SpecialMoves, LvlupEggMoves, EggMoves, ExclusiveIncenseMoves, EventEggMoves, new int[0], e.SplitBreed, info); + return parseMoves(pkm, Moves, SpecialMoves, LvlupEggMoves, EggMoves, NonTradebackLvlMoves, EventEggMoves, new int[0], e.SplitBreed, info); } - private static CheckResult[] parseMovesSketch(int[] Moves) + private static CheckMoveResult[] parseMovesSketch(PKM pkm, int[] Moves) { - CheckResult[] res = new CheckResult[4]; + CheckMoveResult[] res = new CheckMoveResult[4]; for (int i = 0; i < 4; i++) res[i] = Legal.InvalidSketch.Contains(Moves[i]) - ? new CheckResult(Severity.Invalid, V166, CheckIdentifier.Move) - : new CheckResult(CheckIdentifier.Move); + ? new CheckMoveResult(MoveSource.Unknown, pkm.Format, Severity.Invalid, V166, CheckIdentifier.Move) + : new CheckMoveResult(MoveSource.Sketch, pkm.Format, CheckIdentifier.Move); return res; } - private static CheckResult[] parseMoves3DS(PKM pkm, GameVersion game, int[] Moves, LegalInfo info) + private static CheckMoveResult[] parseMoves3DS(PKM pkm, GameVersion game, int[] Moves, LegalInfo info) { info.EncounterMoves.Relearn = pkm.GenNumber >= 6 ? pkm.RelearnMoves : new int[0]; if (info.EncounterMatch is IMoveset) @@ -126,7 +134,7 @@ private static CheckResult[] parseMoves3DS(PKM pkm, GameVersion game, int[] Move // Everything else return parseMovesRelearn(pkm, Moves, info); } - private static CheckResult[] parseMovesPre3DS(PKM pkm, int[] Moves, LegalInfo info) + private static CheckMoveResult[] parseMovesPre3DS(PKM pkm, int[] Moves, LegalInfo info) { if (pkm.IsEgg && info.EncounterMatch is EncounterEgg egg) { @@ -142,66 +150,65 @@ private static CheckResult[] parseMovesPre3DS(PKM pkm, int[] Moves, LegalInfo in return parseMovesSpecialMoveset(pkm, Moves, info); } - private static CheckResult[] parseMovesGen1(PKM pkm, int[] Moves, LegalInfo info) + private static CheckMoveResult[] parseMovesGen1(PKM pkm, int[] Moves, LegalInfo info) { GameVersion[] games = Legal.getGen1GameEncounter(pkm); - CheckResult[] res = new CheckResult[4]; + CheckMoveResult[] res = new CheckMoveResult[4]; var G1Encounter = info.EncounterMatch; if (G1Encounter == null) return parseMovesSpecialMoveset(pkm, Moves, info); var InitialMoves = new int[0]; int[] SpecialMoves = (info.EncounterMatch as IMoveset)?.Moves ?? new int[0]; - var empty = Legal.GetEmptyMovesList(info.EvoChainsAllGens); var emptyegg = new int[0]; foreach (GameVersion ver in games) { var VerInitialMoves = Legal.getInitialMovesGBEncounter(G1Encounter.Species, G1Encounter.LevelMin, ver).ToArray(); if (VerInitialMoves.SequenceEqual(InitialMoves)) return res; - res = parseMoves(pkm, Moves, SpecialMoves, emptyegg, emptyegg, empty, new int[0], VerInitialMoves, false, info); + res = parseMoves(pkm, Moves, SpecialMoves, emptyegg, emptyegg, emptyegg, new int[0], VerInitialMoves, false, info); if (res.All(r => r.Valid)) return res; InitialMoves = VerInitialMoves; } return res; } - private static CheckResult[] parseMovesSpecialMoveset(PKM pkm, int[] Moves, LegalInfo info) + private static CheckMoveResult[] parseMovesSpecialMoveset(PKM pkm, int[] Moves, LegalInfo info) { var mg = info.EncounterMatch as IMoveset; int[] SpecialMoves = mg?.Moves ?? new int[0]; - var empty = Legal.GetEmptyMovesList(info.EvoChainsAllGens); var emptyegg = new int[0]; - CheckResult[] res = parseMoves(pkm, Moves, SpecialMoves, emptyegg, emptyegg, empty, new int[0], new int[0], false, info); + CheckMoveResult[] res = parseMoves(pkm, Moves, SpecialMoves, emptyegg, emptyegg, emptyegg, new int[0], new int[0], false, info); if (res.Any(r => !r.Valid)) return res; return res; } - private static CheckResult[] parseMovesRelearn(PKM pkm, int[] Moves, LegalInfo info) + private static CheckMoveResult[] parseMovesRelearn(PKM pkm, int[] Moves, LegalInfo info) { var emptyegg = new int[0]; var issplitbreed = pkm.WasEgg && Legal.SplitBreed.Contains(pkm.Species); var e = info.EncounterMatch as EncounterEgg; var EggMoves = e != null ? Legal.getEggMoves(pkm, e.Species, pkm.AltForm, e.Game) : emptyegg; - // Level up, TMHM or tutor moves exclusive to the incense egg species, like Azurill, incompatible with the non-incense species egg moves - var ExclusiveIncenseMoves = issplitbreed ? Legal.getExclusivePreEvolutionMoves(pkm, Legal.getBaseEggSpecies(pkm), info.EvoChainsAllGens, e.Game) : Legal.GetEmptyMovesList(info.EvoChainsAllGens); - + var TradebackPreevo = pkm.Format == 2 && info.EncounterMatch.Species > 151 && pkm.InhabitedGeneration(1); + var NonTradebackLvlMoves = TradebackPreevo ? Legal.getExclusivePreEvolutionMoves(pkm, info.EncounterMatch.Species, info.EvoChainsAllGens[2], 2, e.Game).Where(m => m > Legal.MaxMoveID_1).ToArray() : new int[0]; + int[] RelearnMoves = pkm.RelearnMoves; int[] SpecialMoves = (info.EncounterMatch as IMoveset)?.Moves ?? new int[0]; - CheckResult[] res = parseMoves(pkm, Moves, SpecialMoves, new int[0], EggMoves, ExclusiveIncenseMoves, new int[0], new int[0], issplitbreed, info); + CheckMoveResult[] res = parseMoves(pkm, Moves, SpecialMoves, new int[0], EggMoves, NonTradebackLvlMoves, new int[0], new int[0], issplitbreed, info); for (int i = 0; i < 4; i++) if ((pkm.IsEgg || res[i].Flag) && !RelearnMoves.Contains(Moves[i])) - res[i] = new CheckResult(Severity.Invalid, string.Format(V170, res[i].Comment), res[i].Identifier); + res[i] = new CheckMoveResult(res[i], Severity.Invalid, string.Format(V170, res[i].Comment), res[i].Identifier); return res; } - private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int[] lvlupegg, int[] egg, List[] IncenseExclusiveMoves, int[] eventegg, int[] initialmoves, bool issplitbreed, LegalInfo info) + private static CheckMoveResult[] parseMoves(PKM pkm, int[] moves, int[] special, int[] lvlupegg, int[] egg, int[] NonTradebackLvlMoves, int[] eventegg, int[] initialmoves, bool issplitbreed, LegalInfo info) { - CheckResult[] res = new CheckResult[4]; + CheckMoveResult[] res = new CheckMoveResult[4]; var Gen1MovesLearned = new List(); + var Gen2PreevoMovesLearned = new List(); var EggMovesLearned = new List(); var LvlupEggMovesLearned = new List(); var EventEggMovesLearned = new List(); @@ -213,9 +220,9 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int for (int m = 0; m < 4; m++) { if (moves[m] == 0) - res[m] = new CheckResult(m < required ? Severity.Invalid : Severity.Valid, V167, CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.None, pkm.Format, m < required ? Severity.Invalid : Severity.Valid, V167, CheckIdentifier.Move); else if (info.EncounterMoves.Relearn.Contains(moves[m])) - res[m] = new CheckResult(Severity.Valid, V172, CheckIdentifier.Move) { Flag = true }; + res[m] = new CheckMoveResult(MoveSource.Relearn, pkm.GenNumber, Severity.Valid, V172, CheckIdentifier.Move) { Flag = true }; } if (res.All(r => r != null)) @@ -251,21 +258,28 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int continue; if (gen == 1 && initialmoves.Contains(moves[m])) - res[m] = new CheckResult(Severity.Valid, native ? V361 : string.Format(V362, gen), CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.Initial, gen, Severity.Valid, native ? V361 : string.Format(V362, gen), CheckIdentifier.Move); else if (info.EncounterMoves.validLevelUpMoves[gen].Contains(moves[m])) - res[m] = new CheckResult(Severity.Valid, native ? V177 : string.Format(V330, gen), CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.LevelUp, gen, Severity.Valid, native ? V177 : string.Format(V330, gen), CheckIdentifier.Move); else if (info.EncounterMoves.validTMHMMoves[gen].Contains(moves[m])) - res[m] = new CheckResult(Severity.Valid, native ? V173 : string.Format(V331, gen), CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.TMHM, gen, Severity.Valid, native ? V173 : string.Format(V331, gen), CheckIdentifier.Move); else if (info.EncounterMoves.validTutorMoves[gen].Contains(moves[m])) - res[m] = new CheckResult(Severity.Valid, native ? V174 : string.Format(V332, gen), CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.Tutor, gen, Severity.Valid, native ? V174 : string.Format(V332, gen), CheckIdentifier.Move); else if (gen == pkm.GenNumber && special.Contains(moves[m])) - res[m] = new CheckResult(Severity.Valid, V175, CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.Special, gen, Severity.Valid, V175, CheckIdentifier.Move); if (res[m] == null || gen < 3) continue; + if (res[m].Valid && gen == 2 && NonTradebackLvlMoves.Contains(m)) + Gen2PreevoMovesLearned.Add(m); if (res[m].Valid && gen == 1) + { Gen1MovesLearned.Add(m); + if (Gen2PreevoMovesLearned.Any()) + MixedGen1NonTradebackGen2 = true; + } + if (res[m].Valid && gen <= 2 && pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber != gen) pkm.TradebackStatus = TradebackType.WasTradeback; } @@ -285,11 +299,11 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int if (IsGen2Pkm && Gen1MovesLearned.Any() && moves[m] > Legal.MaxMoveID_1) { - res[m] = new CheckResult(Severity.Invalid, V334, CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.InheritLevelUp, gen, Severity.Invalid, V334, CheckIdentifier.Move); MixedGen1NonTradebackGen2 = true; } else - res[m] = new CheckResult(Severity.Valid, V345, CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.InheritLevelUp, gen, Severity.Valid, V345, CheckIdentifier.Move); LvlupEggMovesLearned.Add(m); if (pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber == 1) pkm.TradebackStatus = TradebackType.WasTradeback; @@ -310,11 +324,11 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int { // To learn exclusive generation 1 moves the pokemon was tradeback, but it can't be trade to generation 1 // without removing moves above MaxMoveID_1, egg moves above MaxMoveID_1 and gen 1 moves are incompatible - res[m] = new CheckResult(Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true }; + res[m] = new CheckMoveResult(MoveSource.EggMove, gen, Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true }; MixedGen1NonTradebackGen2 = true; } else - res[m] = new CheckResult(Severity.Valid, V171, CheckIdentifier.Move) { Flag = true }; + res[m] = new CheckMoveResult(MoveSource.EggMove, gen, Severity.Valid, V171, CheckIdentifier.Move) { Flag = true }; EggMovesLearned.Add(m); if (pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber == 1) @@ -327,11 +341,11 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int { if (IsGen2Pkm && Gen1MovesLearned.Any() && moves[m] > Legal.MaxMoveID_1) { - res[m] = new CheckResult(Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true }; + res[m] = new CheckMoveResult(MoveSource.SpecialEgg, gen, Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true }; MixedGen1NonTradebackGen2 = true; } else - res[m] = new CheckResult(Severity.Valid, V333, CheckIdentifier.Move) { Flag = true }; + res[m] = new CheckMoveResult(MoveSource.SpecialEgg, gen, Severity.Valid, V333, CheckIdentifier.Move) { Flag = true }; } if (pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber == 1) pkm.TradebackStatus = TradebackType.WasTradeback; @@ -350,11 +364,11 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int foreach (int m in IncompatibleEggMoves) { if (EventEggMovesLearned.Contains(m) && !EggMovesLearned.Contains(m)) - res[m] = new CheckResult(Severity.Invalid, V337, CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V337, CheckIdentifier.Move); else if (!EventEggMovesLearned.Contains(m) && EggMovesLearned.Contains(m)) - res[m] = new CheckResult(Severity.Invalid, V336, CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V336, CheckIdentifier.Move); else if (!EventEggMovesLearned.Contains(m) && LvlupEggMovesLearned.Contains(m)) - res[m] = new CheckResult(Severity.Invalid, V358, CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V358, CheckIdentifier.Move); } } } @@ -364,9 +378,9 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int foreach (int m in RegularEggMovesLearned) { if (EggMovesLearned.Contains(m)) - res[m] = new CheckResult(Severity.Invalid, pkm.WasGiftEgg ? V377 : V341, CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, pkm.WasGiftEgg ? V377 : V341, CheckIdentifier.Move); else if (LvlupEggMovesLearned.Contains(m)) - res[m] = new CheckResult(Severity.Invalid, pkm.WasGiftEgg ? V378 : V347, CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, pkm.WasGiftEgg ? V378 : V347, CheckIdentifier.Move); } } } @@ -383,18 +397,23 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int if (invalidCount == 2) // can't know both at the same time for (int i = 0; i < 4; i++) // flag both moves if (moves[i] == 250 || moves[i] == 432) - res[i] = new CheckResult(Severity.Invalid, V338, CheckIdentifier.Move); + res[i] = new CheckMoveResult(res[i], Severity.Invalid, V338, CheckIdentifier.Move); } for (int i = 0; i < HMLearned.Length; i++) if (res[i]?.Valid ?? false) - res[i] = new CheckResult(Severity.Invalid, string.Format(V339, gen, gen + 1), CheckIdentifier.Move); + res[i] = new CheckMoveResult(res[i], Severity.Invalid, string.Format(V339, gen, gen + 1), CheckIdentifier.Move); } // Mark the gen 1 exclusive moves as illegal because the pokemon also have Non tradeback egg moves. if (MixedGen1NonTradebackGen2) + { foreach (int m in Gen1MovesLearned) - res[m] = new CheckResult(Severity.Invalid, V335, CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V335, CheckIdentifier.Move); + + foreach (int m in Gen2PreevoMovesLearned) + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V412, CheckIdentifier.Move); + } if (gen == 1 && pkm.Format == 1 && pkm.Gen1_NotTradeback) { @@ -405,7 +424,7 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int ParseEvolutionsIncompatibleMoves(pkm, moves, info.EncounterMoves.validTMHMMoves[1], ref res); } - if (Legal.EvolutionWithMove.Contains(pkm.Species)) + if (Legal.SpeciesEvolutionWithMove.Contains(pkm.Species)) { // Pokemon that evolved by leveling up while learning a specific move // This pokemon could only have 3 moves from preevolutions that are not the move used to evolved @@ -428,12 +447,12 @@ private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int for (int m = 0; m < 4; m++) { if (res[m] == null) - res[m] = new CheckResult(Severity.Invalid, V176, CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.Unknown, pkm.GenNumber, Severity.Invalid, V176, CheckIdentifier.Move); } return res; } - private static void ParseRedYellowIncompatibleMoves(PKM pkm, int[] moves, ref CheckResult[] res) + private static void ParseRedYellowIncompatibleMoves(PKM pkm, int[] moves, ref CheckMoveResult[] res) { var incompatible = new List(); if (pkm.Species == 134 && pkm.CurrentLevel < 47 && moves.Contains(151)) @@ -457,10 +476,10 @@ private static void ParseRedYellowIncompatibleMoves(PKM pkm, int[] moves, ref Ch for (int m = 0; m < 4; m++) { if (incompatible.Contains(moves[m])) - res[m] = new CheckResult(Severity.Invalid, V363, CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V363, CheckIdentifier.Move); } } - private static void ParseEvolutionsIncompatibleMoves(PKM pkm, int[] moves, List tmhm, ref CheckResult[] res) + private static void ParseEvolutionsIncompatibleMoves(PKM pkm, int[] moves, List tmhm, ref CheckMoveResult[] res) { var species = specieslist; var currentspecies = species[pkm.Species]; @@ -508,12 +527,12 @@ private static void ParseEvolutionsIncompatibleMoves(PKM pkm, int[] moves, List< for (int m = 0; m < 4; m++) { if (incompatible_current.Contains(moves[m])) - res[m] = new CheckResult(Severity.Invalid, string.Format(V365, currentspecies, previousspecies), CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, string.Format(V365, currentspecies, previousspecies), CheckIdentifier.Move); if (incompatible_previous.Contains(moves[m])) - res[m] = new CheckResult(Severity.Invalid, string.Format(V366, currentspecies, previousspecies), CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, string.Format(V366, currentspecies, previousspecies), CheckIdentifier.Move); } } - private static void ParseShedinjaEvolveMoves(PKM pkm, int[] moves, ref CheckResult[] res) + private static void ParseShedinjaEvolveMoves(PKM pkm, int[] moves, ref CheckMoveResult[] res) { List[] ShedinjaEvoMoves = Legal.getShedinjaEvolveMoves(pkm); var ShedinjaEvoMovesLearned = new List(); @@ -528,7 +547,7 @@ private static void ParseShedinjaEvolveMoves(PKM pkm, int[] moves, ref CheckResu if (!ShedinjaEvoMoves[gen].Contains(moves[m])) continue; - res[m] = new CheckResult(Severity.Valid, native ? V355 : string.Format(V356, gen), CheckIdentifier.Move); + res[m] = new CheckMoveResult(MoveSource.ShedinjaEvo, gen, Severity.Valid, native ? V355 : string.Format(V356, gen), CheckIdentifier.Move); ShedinjaEvoMovesLearned.Add(m); } } @@ -537,9 +556,9 @@ private static void ParseShedinjaEvolveMoves(PKM pkm, int[] moves, ref CheckResu return; foreach (int m in ShedinjaEvoMovesLearned) - res[m] = new CheckResult(Severity.Invalid, V357, CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V357, CheckIdentifier.Move); } - private static void ParseEvolutionLevelupMove(PKM pkm, int[] moves, List IncenseMovesLearned, ref CheckResult[] res, LegalInfo info) + private static void ParseEvolutionLevelupMove(PKM pkm, int[] moves, List IncenseMovesLearned, ref CheckMoveResult[] res, LegalInfo info) { // Ignore if there is an invalid move or an empty move, this validation is only for 4 non-empty moves that are all valid, but invalid as a 4 combination // Ignore Mr. Mime and Sudowodoo from generations 1 to 3, they cant be evolved from Bonsly or Munchlax @@ -549,11 +568,6 @@ private static void ParseEvolutionLevelupMove(PKM pkm, int[] moves, List In info.EncounterMatch.Species == pkm.Species) return; - // Mr.Mime and Sodowodoo from eggs that does not have any exclusive egg move or level up move from Mime Jr or Bonsly. - // The egg can be assumed to be a non-incense egg if the pokemon was not evolved by the player - if (info.EncounterMatch.EggEncounter && Legal.BabyEvolutionWithMove.Contains(pkm.Species) && !IncenseMovesLearned.Any()) - return; - var ValidMoves = Legal.getValidPostEvolutionMoves(pkm, pkm.Species, info.EvoChainsAllGens, GameVersion.Any); // Add the evolution moves to valid moves in case some of this moves could not be learned after evolving switch (pkm.Species) @@ -586,14 +600,14 @@ private static void ParseEvolutionLevelupMove(PKM pkm, int[] moves, List In return; for (int m = 0; m < 4; m++) - res[m] = new CheckResult(Severity.Invalid, string.Format(V385, specieslist[pkm.Species]), CheckIdentifier.Move); + res[m] = new CheckMoveResult(res[m], Severity.Invalid, string.Format(V385, specieslist[pkm.Species]), CheckIdentifier.Move); } /* 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 static CheckResult[] verifyPreRelearnEggBase(PKM pkm, int[] Moves, List baseMoves, List eggmoves, List lvlmoves, List tmhmmoves, List tutormoves, List specialmoves, bool AllowInherited, GameVersion ver) + private static CheckMoveResult[] verifyPreRelearnEggBase(PKM pkm, int[] Moves, List baseMoves, List eggmoves, List lvlmoves, List tmhmmoves, List tutormoves, List specialmoves, bool AllowInherited, GameVersion ver) { - CheckResult[] res = new CheckResult[4]; - + CheckMoveResult[] res = new CheckMoveResult[4]; + var gen = pkm.GenNumber; // Obtain level1 moves int baseCt = baseMoves.Count; if (baseCt > 4) baseCt = 4; @@ -614,12 +628,12 @@ private static CheckResult[] verifyPreRelearnEggBase(PKM pkm, int[] Moves, List< for (int i = moveoffset; i < reqBase; i++) { if (baseMoves.Contains(Moves[i])) - res[i] = new CheckResult(Severity.Valid, V179, CheckIdentifier.Move); + res[i] = new CheckMoveResult(MoveSource.Initial, gen, Severity.Valid, V179, CheckIdentifier.Move); else { // mark remaining base egg moves missing for (int z = i; z < reqBase; z++) - res[z] = new CheckResult(Severity.Invalid, V180, CheckIdentifier.Move); + res[z] = new CheckMoveResult(MoveSource.Initial, gen, 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])); @@ -633,12 +647,12 @@ private static CheckResult[] verifyPreRelearnEggBase(PKM pkm, int[] Moves, List< for (int i = moveoffset; i < moveoffset + specialmoves.Count; i++) { if (specialmoves.Contains(Moves[i])) - res[i] = new CheckResult(Severity.Valid, V333, CheckIdentifier.Move); + res[i] = new CheckMoveResult(MoveSource.SpecialEgg, gen, Severity.Valid, V333, CheckIdentifier.Move); else { // mark remaining special egg moves missing for (int z = i; z < moveoffset + specialmoves.Count; z++) - res[z] = new CheckResult(Severity.Invalid, V342, CheckIdentifier.Move); + res[z] = new CheckMoveResult(MoveSource.SpecialEgg, gen, Severity.Invalid, V342, CheckIdentifier.Move); // provide the list of suggested base moves and species moves for the last required slot if (!string.IsNullOrEmpty(em)) em += ", "; @@ -660,23 +674,23 @@ private static CheckResult[] verifyPreRelearnEggBase(PKM pkm, int[] Moves, List< for (int i = reqBase + specialmoves.Count; i < 4; i++) { if (Moves[i] == 0) // empty - res[i] = new CheckResult(Severity.Valid, V167, CheckIdentifier.Move); + res[i] = new CheckMoveResult(MoveSource.None, gen, Severity.Valid, V167, CheckIdentifier.Move); else if (eggmoves.Contains(Moves[i])) // inherited egg move - res[i] = new CheckResult(AllowInheritedSeverity, AllowInherited ? V344 : V341, CheckIdentifier.Move); + res[i] = new CheckMoveResult(MoveSource.EggMove, gen, AllowInheritedSeverity, AllowInherited ? V344 : V341, CheckIdentifier.Move); else if (lvlmoves.Contains(Moves[i])) // inherited lvl moves - res[i] = new CheckResult(AllowInheritedSeverity, AllowInherited ? V345 : V347, CheckIdentifier.Move); + res[i] = new CheckMoveResult(MoveSource.InheritLevelUp, gen, AllowInheritedSeverity, AllowInherited ? V345 : V347, CheckIdentifier.Move); else if (tmhmmoves.Contains(Moves[i])) // inherited TMHM moves - res[i] = new CheckResult(AllowInheritedSeverity, AllowInherited ? V349 : V350, CheckIdentifier.Move); + res[i] = new CheckMoveResult(MoveSource.TMHM, gen, AllowInheritedSeverity, AllowInherited ? V349 : V350, CheckIdentifier.Move); else if (tutormoves.Contains(Moves[i])) // inherited tutor moves - res[i] = new CheckResult(AllowInheritedSeverity, AllowInherited ? V346 : V348, CheckIdentifier.Move); + res[i] = new CheckMoveResult(MoveSource.Tutor, gen, AllowInheritedSeverity, AllowInherited ? V346 : V348, CheckIdentifier.Move); else // not inheritable, flag - res[i] = new CheckResult(Severity.Invalid, V340, CheckIdentifier.Move); + res[i] = new CheckMoveResult(MoveSource.Unknown, gen, Severity.Invalid, V340, CheckIdentifier.Move); } return res; } - private static void verifyNoEmptyDuplicates(int[] Moves, CheckResult[] res) + private static void verifyNoEmptyDuplicates(int[] Moves, CheckMoveResult[] res) { bool emptySlot = false; for (int i = 0; i < 4; i++) @@ -684,9 +698,9 @@ private static void verifyNoEmptyDuplicates(int[] Moves, CheckResult[] res) if (Moves[i] == 0) emptySlot = true; else if (emptySlot) - res[i] = new CheckResult(Severity.Invalid, V167, res[i].Identifier); + res[i] = new CheckMoveResult(res[i], Severity.Invalid, V167, res[i].Identifier); else if (Moves.Count(m => m == Moves[i]) > 1) - res[i] = new CheckResult(Severity.Invalid, V168, res[i].Identifier); + res[i] = new CheckMoveResult(res[i], Severity.Invalid, V168, res[i].Identifier); } } private static void UptateGen1LevelUpMoves(PKM pkm, ValidEncounterMoves EncounterMoves, int defaultLvlG1, int generation, LegalInfo info) diff --git a/PKHeX.Core/Legality/Encounters/VerifyEvolution.cs b/PKHeX.Core/Legality/Encounters/VerifyEvolution.cs index b8d6928a5..3c3875e80 100644 --- a/PKHeX.Core/Legality/Encounters/VerifyEvolution.cs +++ b/PKHeX.Core/Legality/Encounters/VerifyEvolution.cs @@ -1,24 +1,30 @@ using static PKHeX.Core.LegalityCheckStrings; - +using System.Linq; namespace PKHeX.Core { public static class VerifyEvolution { // Evolutions - public static CheckResult verifyEvolution(PKM pkm, IEncounterable EncounterMatch) + public static CheckResult verifyEvolution(PKM pkm, LegalInfo info) { - return isValidEvolution(pkm, EncounterMatch) + return isValidEvolution(pkm, info) ? new CheckResult(CheckIdentifier.Evolution) : new CheckResult(Severity.Invalid, V86, CheckIdentifier.Evolution); } - private static bool isValidEvolution(PKM pkm, IEncounterable EncounterMatch) + private static bool isValidEvolution(PKM pkm, LegalInfo info) { int species = pkm.Species; - if (EncounterMatch.Species == species) + if (info.EncounterMatch.Species == species) return true; - if (EncounterMatch.EggEncounter && species == 350) + if (info.EncounterMatch.EggEncounter && species == 350) return true; - return Legal.getEvolutionValid(pkm, EncounterMatch.Species); + if(!Legal.getEvolutionValid(pkm, info.EncounterMatch.Species)) + return false; + // If current species evolved with a move evolution and encounter species is not current species check if the evolution by move is valid + // Only the evolution by move is checked, if there is another evolution before the evolution by move is covered in getEvolutionValid + if (Legal.SpeciesEvolutionWithMove.Contains(pkm.Species)) + return Legal.getEvolutionWithMoveValid(pkm, info); + return true; } } } diff --git a/PKHeX.Core/Legality/Encounters/VerifyRelearnMoves.cs b/PKHeX.Core/Legality/Encounters/VerifyRelearnMoves.cs index 8515a7a87..8472d9b8f 100644 --- a/PKHeX.Core/Legality/Encounters/VerifyRelearnMoves.cs +++ b/PKHeX.Core/Legality/Encounters/VerifyRelearnMoves.cs @@ -49,7 +49,7 @@ private static CheckResult[] verifyRelearnDexNav(PKM pkm, LegalInfo info) int[] RelearnMoves = pkm.RelearnMoves; // DexNav Pokémon can have 1 random egg move as a relearn move. - res[0] = !Legal.getValidRelearn(pkm, Legal.getBaseEggSpecies(pkm)).Contains(RelearnMoves[0]) + res[0] = !Legal.getValidRelearn(pkm, Legal.getBaseEggSpecies(pkm),true).Contains(RelearnMoves[0]) ? new CheckResult(Severity.Invalid, V183, CheckIdentifier.RelearnMove) : new CheckResult(CheckIdentifier.RelearnMove); @@ -85,6 +85,9 @@ private static CheckResult[] verifyRelearnEggBase(PKM pkm, LegalInfo info, Encou int[] RelearnMoves = pkm.RelearnMoves; info.RelearnBase = new int[4]; CheckResult[] res = new CheckResult[4]; + // Level up moves could not be inherited if Ditto is parent, + // that means genderless species and male only species except Nidoran and Volbet (they breed with female nidoran and illumise) could not have level up moves as an egg + var inheritLvlMoves = pkm.PersonalInfo.Gender > 0 && pkm.PersonalInfo.Gender < 255 || Legal.MixedGenderBreeding.Contains(e.Species); // Obtain level1 moves List baseMoves = new List(Legal.getBaseEggMoves(pkm, e.Species, e.Game, 1)); @@ -92,7 +95,7 @@ private static CheckResult[] verifyRelearnEggBase(PKM pkm, LegalInfo info, Encou if (baseCt > 4) baseCt = 4; // Obtain Inherited moves - var inheritMoves = Legal.getValidRelearn(pkm, e.Species).ToList(); + var inheritMoves = Legal.getValidRelearn(pkm, e.Species, inheritLvlMoves).ToList(); var inherited = RelearnMoves.Where(m => m != 0 && (!baseMoves.Contains(m) || inheritMoves.Contains(m))).ToList(); int inheritCt = inherited.Count; @@ -129,7 +132,7 @@ private static CheckResult[] verifyRelearnEggBase(PKM pkm, LegalInfo info, Encou // If any splitbreed moves are invalid, flag accordingly var splitInvalid = false; - var splitMoves = e.SplitBreed ? Legal.getValidRelearn(pkm, Legal.getBaseEggSpecies(pkm)).ToList() : new List(); + var splitMoves = e.SplitBreed ? Legal.getValidRelearn(pkm, Legal.getBaseEggSpecies(pkm), inheritLvlMoves).ToList() : new List(); // Inherited moves appear after the required base moves. for (int i = reqBase; i < 4; i++) diff --git a/PKHeX.Core/Legality/LegalityCheckStrings.cs b/PKHeX.Core/Legality/LegalityCheckStrings.cs index b193a59c6..89c993d29 100644 --- a/PKHeX.Core/Legality/LegalityCheckStrings.cs +++ b/PKHeX.Core/Legality/LegalityCheckStrings.cs @@ -347,7 +347,7 @@ public static class LegalityCheckStrings public static string V328 {get; set;} = "Version Specific evolution requires a trade to opposite version. A Handling Trainer is required."; public static string V334 {get; set;} = "Non-tradeback egg move. Incompatible with generation 1 exclusive moves."; - public static string V335 {get; set;} = "Generation 1 exclusive move. Incompatible with Non-tradeback egg moves."; + public static string V335 {get; set;} = "Generation 1 exclusive move. Incompatible with Non-tradeback moves."; public static string V336 {get; set;} = "Egg Move. Incompatible with event egg moves."; 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."; @@ -405,6 +405,7 @@ public static class LegalityCheckStrings public static string V409 {get; set;} = "Mystery Gift shiny mismatch."; public static string V410 {get; set;} = "Mystery Gift fixed PID mismatch."; public static string V411 {get; set;} = "Encounter Type PID mismatch."; + public static string V412 {get; set;} = "Non-tradeback pre evolution move. Incompatible with generation 1 exclusive moves."; #endregion } diff --git a/PKHeX.Core/Legality/Structures/CheckMoveResult.cs b/PKHeX.Core/Legality/Structures/CheckMoveResult.cs new file mode 100644 index 000000000..e3ce6ca86 --- /dev/null +++ b/PKHeX.Core/Legality/Structures/CheckMoveResult.cs @@ -0,0 +1,44 @@ +namespace PKHeX.Core +{ + public enum MoveSource + { + Unknown, + None, + Relearn, + Initial, + LevelUp, + TMHM, + Tutor, + EggMove, + InheritLevelUp, + Special, + SpecialEgg, + ShedinjaEvo, + Sketch, + } + + public class CheckMoveResult : CheckResult + { + public readonly MoveSource Source; + public readonly int Generation; + + internal CheckMoveResult(MoveSource m, int g, CheckIdentifier i) + : base(i) + { + Source = m; + Generation = g; + } + internal CheckMoveResult(MoveSource m, int g, Severity s, string c, CheckIdentifier i) + : base(s, c, i) + { + Source = m; + Generation = g; + } + internal CheckMoveResult(CheckMoveResult Org, Severity s, string c, CheckIdentifier i) + : base(s, c, i) + { + Source = Org?.Source ?? MoveSource.Unknown; + Generation = Org?.Generation ?? 0; + } + } +} diff --git a/PKHeX.Core/Legality/Tables.cs b/PKHeX.Core/Legality/Tables.cs index 10c547076..067539c18 100644 --- a/PKHeX.Core/Legality/Tables.cs +++ b/PKHeX.Core/Legality/Tables.cs @@ -224,8 +224,9 @@ public static partial class Legal 122, // Mr. Mime (Mime Jr with Mimic) 185, // Sudowoodo (Bonsly with Mimic) }; - - internal static readonly int[] EvolutionWithMove = + + // List of species that evolve from a previous species having a move while leveling up + internal static readonly int[] SpeciesEvolutionWithMove = { 122, // Mr. Mime (Mime Jr with Mimic) 185, // Sudowoodo (Bonsly with Mimic) @@ -237,6 +238,64 @@ public static partial class Legal 700, // Sylveon (Eevee with Fairy Move) 763, // Tsareena (Steenee with Stomp) }; + // Moves that trigger the evolution by move + internal static readonly int[][] MoveEvolutionWithMove = + { + new [] { 102 }, // Mr. Mime (Mime Jr with Mimic) + new [] { 102 }, // Sudowoodo (Bonsly with Mimic) + new [] { 458 }, // Ambipom (Aipom with Double Hit) + new [] { 205 }, // Lickilicky (Lickitung with Rollout) + new [] { 246 }, // Tangrowth (Tangela with Ancient Power) + new [] { 246 }, // Yanmega (Yamma with Ancient Power) + new [] { 246 }, // Mamoswine (Piloswine with Ancient Power) + FairyMoves, // Sylveon (Eevee with Fairy Move) + new [] { 023 }, // Tsareena (Steenee with Stomp) + }; + // Min level for any species for every generation to learn the move for evolution by move + // 0 means it cant be learned in that generation + internal static readonly int[][] MinLevelEvolutionWithMove = + { + // Mr. Mime (Mime Jr with Mimic) + new [] { 0, 0, 0, 0, 18, 15, 15, 15 }, + // Sudowoodo (Bonsly with Mimic) + new [] { 0, 0, 0, 0, 17, 17, 15, 15 }, + // Ambipom (Aipom with Double Hit) + new [] { 0, 0, 0, 0, 32, 32, 32, 32 }, + // Lickilicky (Lickitung with Rollout) + new [] { 0, 0, 1, 0, 1, 33, 33, 33 }, + // Tangrowth (Tangela with Ancient Power) + new [] { 0, 0, 0, 0, 1, 36, 38, 38 }, + // Yanmega (Yanma with Ancient Power) + new [] { 0, 0, 0, 0, 1, 33, 33, 33 }, + // Mamoswine (Piloswine with Ancient Power) + new [] { 0, 0, 0, 0, 1, 1, 1, 1 }, + // Sylveon (Eevee with Fairy Move) + new [] { 0, 0, 0, 0, 0, 29, 9, 9 }, + // Tsareena (Steenee with Stomp) + new [] { 0, 0, 0, 0, 0, 0, 0, 29 }, + }; + // True -> the pokemon could hatch from an egg with the move for evolution as an egg move + internal static readonly bool[][] EggMoveEvolutionWithMove = + { + // Mr. Mime (Mime Jr with Mimic) + new [] { false, false, false, false, true, true, true, true }, + // Sudowoodo (Bonsly with Mimic) + new [] { false, false, false, false, true, true, true, true }, + // Ambipom (Aipom with Double Hit) + new [] { false, false, false, false, true, true, true, true }, + // Lickilicky (Lickitung with Rollout) + new [] { false, false, true, false, true, true, true, true }, + // Tangrowth (Tangela with Ancient Power) + new [] { false, false, false, false, true, true, true, true }, + // Yanmega (Yanma with Ancient Power) + new [] { false, false, false, false, true, true, true, true }, + // Mamoswine (Piloswine with Ancient Power) + new [] { false, false, true, true, true, true, true, true }, + // Sylveon (Eevee with Fairy Move) + new [] { false, false, true, true, true, true, true, true }, + // Tsareena (Steenee with Stomp) + new [] { false, false, false, false, false, false, false, false }, + }; internal static readonly int[] FairyMoves = { @@ -264,7 +323,13 @@ public static partial class Legal 705, //Fleur Cannon 717, //Nature's Madness }; - + internal static readonly int[] MixedGenderBreeding = + { + 29, // Nidoran♀ + 32, // Nidoran♂ + 314, // Volbeat + 314, // Illumise + }; #region Games public static readonly int[] Games_7vc2 = { 39, 40, 41 }; // Gold, Silver, Crystal diff --git a/PKHeX.Core/Resources/text/en/LegalityCheckStrings_en.txt b/PKHeX.Core/Resources/text/en/LegalityCheckStrings_en.txt index 8836048dd..02464b502 100644 --- a/PKHeX.Core/Resources/text/en/LegalityCheckStrings_en.txt +++ b/PKHeX.Core/Resources/text/en/LegalityCheckStrings_en.txt @@ -284,7 +284,7 @@ V326 = Special ingame N's Sparkle flag missing. V327 = Special ingame N's Sparkle flag should not be checked. V328 = Version Specific evolution requires a trade to opposite version. A Handling Trainer is required. V334 = Non-tradeback egg move. Incompatible with generation 1 exclusive moves. -V335 = Generation 1 exclusive move. Incompatible with Non-tradeback egg moves. +V335 = Generation 1 exclusive move. Incompatible with Non-tradeback moves. V336 = Egg Move. Incompatible with event egg moves. V337 = Event Egg Move. Incompatible with normal egg moves. V338 = Defog and whirpool. One of the two moves should have been removed before transfered to generation 5. @@ -339,4 +339,7 @@ V405 = Outsider {0} should have evolved into {1}. V406 = Non Japanese Shadow E-reader Pokémon. Unreleased encounter. V407 = OT from Colosseum/XD cannot be female. V408 = Female OT from Generation 1/2 is invalid. -V409 = Mystery Gift shiny mismatch. \ No newline at end of file +V409 = Mystery Gift shiny mismatch. +V410 = Mystery Gift fixed PID mismatch. +V411 = Encounter Type PID mismatch. +V412 = Non-tradeback pre evolution move. Incompatible with generation 1 exclusive moves. \ No newline at end of file diff --git a/PKHeX.Core/Resources/text/ko/LegalityCheckStrings_ko.txt b/PKHeX.Core/Resources/text/ko/LegalityCheckStrings_ko.txt index 0e62d6ce4..d1f8f4d9c 100644 --- a/PKHeX.Core/Resources/text/ko/LegalityCheckStrings_ko.txt +++ b/PKHeX.Core/Resources/text/ko/LegalityCheckStrings_ko.txt @@ -340,4 +340,7 @@ V405 = Outsider {0} should have evolved into {1}. V406 = Non Japanese Shadow E-reader Pokémon. Unreleased encounter. V407 = OT from Colosseum/XD cannot be female. V408 = Female OT from Generation 1/2 is invalid. -V409 = Mystery Gift shiny mismatch. \ No newline at end of file +V409 = Mystery Gift shiny mismatch. +V410 = Mystery Gift fixed PID mismatch. +V411 = Encounter Type PID mismatch. +V412 = Non-tradeback pre evolution move. Incompatible with generation 1 exclusive moves. \ No newline at end of file diff --git a/PKHeX.Core/Resources/text/zh/LegalityCheckStrings_zh.txt b/PKHeX.Core/Resources/text/zh/LegalityCheckStrings_zh.txt index 2b826308f..65ba5d962 100644 --- a/PKHeX.Core/Resources/text/zh/LegalityCheckStrings_zh.txt +++ b/PKHeX.Core/Resources/text/zh/LegalityCheckStrings_zh.txt @@ -339,4 +339,7 @@ V405 = 外来的{0}应当进化为{1}. V406 = 非日版黑暗E-reader宝可梦。未解禁遇见方式。 V407 = OT from Colosseum/XD cannot be female. V408 = Female OT from Generation 1/2 is invalid. -V409 = Mystery Gift shiny mismatch. \ No newline at end of file +V409 = Mystery Gift shiny mismatch. +V410 = Mystery Gift fixed PID mismatch. +V411 = Encounter Type PID mismatch. +V412 = Non-tradeback pre evolution move. Incompatible with generation 1 exclusive moves. \ No newline at end of file