diff --git a/PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs b/PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs index b28531c1c..1b4f90ecc 100644 --- a/PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs +++ b/PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs @@ -30,8 +30,7 @@ private static CheckMoveResult[] ParseMovesForEncounters(PKM pkm, LegalInfo info bool pre3DS = pkm.GenNumber < 6; // gather valid moves for encounter species - - info.EncounterMoves = GetEncounterValidMoves(pkm, info); + info.EncounterMoves = new ValidEncounterMoves(pkm, info); if (pkm.GenNumber <= 3) pkm.WasEgg = info.EncounterMatch.EggEncounter; @@ -51,7 +50,7 @@ private static CheckMoveResult[] ParseMovesForEncounters(PKM pkm, LegalInfo info var res = pre3DS ? ParseMovesPre3DS(pkm, Moves, info) - : ParseMoves3DS(pkm, game, Moves, info); + : ParseMoves3DS(pkm, Moves, info); if (res.All(x => x.Valid)) return res; @@ -71,79 +70,44 @@ private static CheckMoveResult[] ParseMovesForSmeargle(PKM pkm, int[] Moves, Leg return ParseMovesSketch(pkm, Moves); // can only know sketch as egg - info.EncounterMoves = new ValidEncounterMoves - { - LevelUpMoves = 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], new int[0], new int[0], new int[0], info); + var levelup = Legal.GetValidMovesAllGens(pkm, info.EvoChainsAllGens, minLvLG1: 1, Tutor: false, Machine: false, RemoveTransferHM: false); + info.EncounterMoves = new ValidEncounterMoves(levelup); + var source = new MoveParseSource { CurrentMoves = pkm.Moves, }; + return ParseMoves(pkm, source, info); } - - private class MoveInfoSet + private static CheckMoveResult[] ParseMovesIsEggPreRelearn(PKM pkm, int[] Moves, int[] SpecialMoves, EncounterEgg e) { - public List SpecialMoves { get; set; } - public List EggMoves { get; set; } - public List BaseMoves { get; set; } - - public bool AllowInherited { get; set; } - public List TutorMoves { get; set; } - public List TMHMMoves { get; set; } - public List LvlMoves { get; set; } - } - private static CheckMoveResult[] ParseMovesIsEggPreRelearn(PKM pkm, int[] Moves, int[] SpecialMoves, bool allowinherited, EncounterEgg e) - { - CheckMoveResult[] res = new CheckMoveResult[4]; - - var baseEggMoves = Legal.GetBaseEggMoves(pkm, e.Species, e.Game, pkm.GenNumber < 4 ? 5 : 1)?.ToList() ?? new List(); - // Level up moves cannot be inherited if Ditto is parent, thus genderless/single gender species cannot have level up moves as an egg - bool 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(); - InheritedLvlMoves.RemoveAll(x => baseEggMoves.Contains(x)); - - var infoset = new MoveInfoSet - { - EggMoves = Legal.GetEggMoves(pkm, e.Species, pkm.AltForm)?.ToList() ?? new List(), - TutorMoves = e.Game == GameVersion.C ? Legal.GetTutorMoves(pkm, pkm.Species, pkm.AltForm, false, 2)?.ToList() : new List(), - TMHMMoves = Legal.GetTMHM(pkm, pkm.Species, pkm.AltForm, pkm.GenNumber, e.Game, false)?.ToList(), - LvlMoves = InheritedLvlMoves, - BaseMoves = baseEggMoves, - SpecialMoves = SpecialMoves.Where(m => m != 0).ToList(), - AllowInherited = allowinherited - }; - // Only TM Hm moves from the source game of the egg, not any other games from the same generation - - 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(pkm, Moves, infoset, e.Game); - } - else if (pkm.Format == 2) - { - infoset.SpecialMoves.Clear(); - infoset.AllowInherited = true; - res = VerifyPreRelearnEggBase(pkm, Moves, infoset, e.Game); - } - - return res; + EggInfoSource infoset = new EggInfoSource(pkm, SpecialMoves, e); + return VerifyPreRelearnEggBase(pkm, Moves, infoset); } private static CheckMoveResult[] ParseMovesWasEggPreRelearn(PKM pkm, int[] Moves, LegalInfo info, EncounterEgg e) { var EventEggMoves = GetSpecialMoves(info.EncounterMatch); // 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); + // that means genderless species and male only species except Nidoran and Volbeat (they breed with female nidoran and illumise) could not have level up moves as an egg + var AllowLevelUp = pkm.PersonalInfo.Gender > 0 && pkm.PersonalInfo.Gender < 255 || Legal.MixedGenderBreeding.Contains(e.Species); + int BaseLevel = AllowLevelUp ? 100 : e.LevelMin; + var LevelUp = Legal.GetBaseEggMoves(pkm, e.Species, e.Game, BaseLevel); 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); + var Egg = 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 + var Special = volt && EventEggMoves.Length == 0 ? new[] { 344 } : new int[0]; // Volt Tackle for bred Pichu line - return ParseMoves(pkm, Moves, SpecialMoves, LvlupEggMoves, EggMoves, NonTradebackLvlMoves, EventEggMoves, new int[0], info); + var source = new MoveParseSource + { + CurrentMoves = Moves, + SpecialSource = Special, + NonTradeBackLevelUpMoves = NonTradebackLvlMoves, + + EggLevelUpSource = LevelUp, + EggMoveSource = Egg, + EggEventSource = EventEggMoves, + }; + return ParseMoves(pkm, source, info); } private static CheckMoveResult[] ParseMovesSketch(PKM pkm, int[] Moves) { @@ -154,7 +118,7 @@ private static CheckMoveResult[] ParseMovesSketch(PKM pkm, int[] Moves) : new CheckMoveResult(MoveSource.Sketch, pkm.Format, CheckIdentifier.Move); return res; } - private static CheckMoveResult[] ParseMoves3DS(PKM pkm, GameVersion game, int[] Moves, LegalInfo info) + private static CheckMoveResult[] ParseMoves3DS(PKM pkm, int[] Moves, LegalInfo info) { info.EncounterMoves.Relearn = pkm.GenNumber >= 6 ? pkm.RelearnMoves : new int[0]; if (info.EncounterMatch is IMoveset) @@ -168,9 +132,7 @@ private static CheckMoveResult[] ParseMovesPre3DS(PKM pkm, int[] Moves, LegalInf if (pkm.IsEgg && info.EncounterMatch is EncounterEgg egg) { int[] SpecialMoves = GetSpecialMoves(info.EncounterMatch); - // Gift do not have special moves but also should not have normal egg moves - var allowinherited = SpecialMoves == null && !pkm.WasGiftEgg && pkm.Species != 489 && pkm.Species != 490; - return ParseMovesIsEggPreRelearn(pkm, Moves, SpecialMoves, allowinherited, egg); + return ParseMovesIsEggPreRelearn(pkm, Moves, SpecialMoves, egg); } var NoMoveReminder = (info.EncounterMatch as IGeneration)?.Generation == 1 || (info.EncounterMatch as IGeneration)?.Generation == 2 && !Legal.AllowGen2MoveReminder; if (pkm.GenNumber <= 2 && NoMoveReminder) @@ -189,13 +151,19 @@ private static CheckMoveResult[] ParseMovesGenGB(PKM pkm, int[] Moves, LegalInfo return ParseMovesSpecialMoveset(pkm, Moves, info); var InitialMoves = new int[0]; int[] SpecialMoves = GetSpecialMoves(info.EncounterMatch); - 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, emptyegg, new int[0], VerInitialMoves, info); + + var source = new MoveParseSource + { + CurrentMoves = Moves, + SpecialSource = SpecialMoves, + Base = VerInitialMoves, + }; + res = ParseMoves(pkm, source, info); if (res.All(r => r.Valid)) return res; InitialMoves = VerInitialMoves; @@ -204,9 +172,12 @@ private static CheckMoveResult[] ParseMovesGenGB(PKM pkm, int[] Moves, LegalInfo } private static CheckMoveResult[] ParseMovesSpecialMoveset(PKM pkm, int[] Moves, LegalInfo info) { - int[] SpecialMoves = GetSpecialMoves(info.EncounterMatch); - var emptyegg = new int[0]; - return ParseMoves(pkm, Moves, SpecialMoves, emptyegg, emptyegg, emptyegg, new int[0], new int[0], info); + var source = new MoveParseSource + { + CurrentMoves = Moves, + SpecialSource = GetSpecialMoves(info.EncounterMatch), + }; + return ParseMoves(pkm, source, info); } private static int[] GetSpecialMoves(IEncounterable EncounterMatch) { @@ -222,251 +193,64 @@ private static int[] GetSpecialMoves(IEncounterable EncounterMatch) private static CheckMoveResult[] ParseMovesRelearn(PKM pkm, int[] Moves, LegalInfo info) { var emptyegg = new int[0]; + var source = new MoveParseSource + { + Base = emptyegg, + CurrentMoves = Moves, + SpecialSource = GetSpecialMoves(info.EncounterMatch), + + EggLevelUpSource = emptyegg, + EggEventSource = emptyegg, + }; var e = info.EncounterMatch as EncounterEgg; - var EggMoves = e != null ? Legal.GetEggMoves(pkm, e.Species, pkm.AltForm) : emptyegg; - 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]; - + if (e != null) + { + source.EggMoveSource = Legal.GetEggMoves(pkm, e.Species, pkm.AltForm); + bool TradebackPreevo = pkm.Format == 2 && info.EncounterMatch.Species > 151 && pkm.InhabitedGeneration(1); + if (TradebackPreevo) + source.NonTradeBackLevelUpMoves = Legal.GetExclusivePreEvolutionMoves(pkm, info.EncounterMatch.Species, info.EvoChainsAllGens[2], 2, e.Game) + .Where(m => m > Legal.MaxMoveID_1).ToArray(); + } + CheckMoveResult[] res = ParseMoves(pkm, source, info); + int[] RelearnMoves = pkm.RelearnMoves; - int[] SpecialMoves = GetSpecialMoves(info.EncounterMatch); - - CheckMoveResult[] res = ParseMoves(pkm, Moves, SpecialMoves, new int[0], EggMoves, NonTradebackLvlMoves, new int[0], new int[0], info); - for (int i = 0; i < 4; i++) if ((pkm.IsEgg || res[i].Flag) && !RelearnMoves.Contains(Moves[i])) res[i] = new CheckMoveResult(res[i], Severity.Invalid, string.Format(V170, res[i].Comment), res[i].Identifier); return res; } - private static CheckMoveResult[] ParseMoves(PKM pkm, int[] moves, int[] special, int[] lvlupegg, int[] egg, int[] NonTradebackLvlMoves, int[] eventegg, int[] initialmoves, LegalInfo info) + private static CheckMoveResult[] ParseMoves(PKM pkm, MoveParseSource source, LegalInfo info) { CheckMoveResult[] res = new CheckMoveResult[4]; - var required = Legal.GetRequiredMoveCount(pkm, moves, info, initialmoves); + bool AllParsed() => res.All(r => r != null); + var required = Legal.GetRequiredMoveCount(pkm, source.CurrentMoves, info, source.Base); - // Check none moves and relearn moves before generation moves + // Check empty moves and relearn moves before generation specific moves for (int m = 0; m < 4; m++) { - if (moves[m] == 0) + if (source.CurrentMoves[m] == 0) res[m] = new CheckMoveResult(MoveSource.None, pkm.Format, m < required ? Severity.Fishy : Severity.Valid, V167, CheckIdentifier.Move); - else if (info.EncounterMoves.Relearn.Contains(moves[m])) + else if (info.EncounterMoves.Relearn.Contains(source.CurrentMoves[m])) res[m] = new CheckMoveResult(MoveSource.Relearn, pkm.GenNumber, Severity.Valid, V172, CheckIdentifier.Move) { Flag = true }; } - if (res.All(r => r != null)) + if (AllParsed()) return res; - bool MixedGen1NonTradebackGen2 = false; - var Gen1MovesLearned = new List(); - var Gen2PreevoMovesLearned = new List(); - var EggMovesLearned = new List(); - var LvlupEggMovesLearned = new List(); - var EventEggMovesLearned = new List(); - var IsGen2Pkm = pkm.Format == 2 || pkm.VC2; - var IncenseMovesLearned = new List(); + // Encapsulate arguments to simplify method calls + var moveInfo = new LearnInfo(pkm) { Source = source }; // Check moves going backwards, marking the move valid in the most current generation when it can be learned int[] generations = GetGenMovesCheckOrder(pkm); if (pkm.Format <= 2) generations = generations.Where(z => z < info.EncounterMoves.LevelUpMoves.Length).ToArray(); + + int lastgen = generations.Last(); foreach (var gen in generations) { - var HMLearned = new int[0]; - // Check if pokemon knows HM moves from generation 3 and 4 but are not valid yet, that means it cant learn the HMs in future generations - bool KnowDefogWhirlpool = false; - if (gen == 4 && pkm.Format > 4) - { - // Copy to array the hm found or else the list will be emptied when the legal status of moves changes in the current generation - HMLearned = moves.Where((m, i) => !(res[i]?.Valid ?? false) && Legal.HM_4_RemovePokeTransfer.Any(l => l == m)).Select((m, i) => i).ToArray(); - // Defog and Whirlpool at the same time, also both can't be learned in future generations or else they will be valid - KnowDefogWhirlpool = moves.Where((m, i) => (m == 250 || m == 432) && !(res[i]?.Valid ?? false)).Count() == 2; - } - else if (gen == 3 && pkm.Format > 3) - HMLearned = moves.Select((m, i) => i).Where(i => !(res[i]?.Valid ?? false) && Legal.HM_3.Any(l => l == moves[i])).ToArray(); - - bool native = gen == pkm.Format; - for (int m = 0; m < 4; m++) - { - if (res[m]?.Valid ?? false) // already validated with another generation - continue; - if (moves[m] == 0) - continue; - - if (gen == 1 && initialmoves.Contains(moves[m])) - res[m] = new CheckMoveResult(MoveSource.Initial, gen, Severity.Valid, native ? V361 : string.Format(V362, gen), CheckIdentifier.Move); - else if (info.EncounterMoves.LevelUpMoves[gen].Contains(moves[m])) - res[m] = new CheckMoveResult(MoveSource.LevelUp, gen, Severity.Valid, native ? V177 : string.Format(V330, gen), CheckIdentifier.Move); - else if (info.EncounterMoves.TMHMMoves[gen].Contains(moves[m])) - res[m] = new CheckMoveResult(MoveSource.TMHM, gen, Severity.Valid, native ? V173 : string.Format(V331, gen), CheckIdentifier.Move); - else if (info.EncounterMoves.TutorMoves[gen].Contains(moves[m])) - 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 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; - } - - if (gen == generations.Last()) - { - // 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 (res[m]?.Valid ?? false) // Skip valid move - continue; - if (moves[m] == 0) - 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) - { - res[m] = new CheckMoveResult(MoveSource.InheritLevelUp, gen, Severity.Invalid, V334, CheckIdentifier.Move); - MixedGen1NonTradebackGen2 = true; - } - else - 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; - } - - // Check egg moves after all the generations and all the moves, every move that can't be learned in another source should have preference - // the moves that can only be learned from egg moves should in the future check if the move combinations can be breed in gens 2 to 5 - for (int m = 0; m < 4; m++) - { - if (res[m]?.Valid ?? false) - continue; - if (moves[m] == 0) - continue; - - if (egg.Contains(moves[m])) - { - if (IsGen2Pkm && Gen1MovesLearned.Any() && moves[m] > Legal.MaxMoveID_1) - { - // 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 CheckMoveResult(MoveSource.EggMove, gen, Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true }; - MixedGen1NonTradebackGen2 = true; - } - else - 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) - pkm.TradebackStatus = TradebackType.WasTradeback; - } - if (!eventegg.Contains(moves[m])) - continue; - - if (!egg.Contains(moves[m])) - { - if (IsGen2Pkm && Gen1MovesLearned.Any() && moves[m] > Legal.MaxMoveID_1) - { - res[m] = new CheckMoveResult(MoveSource.SpecialEgg, gen, Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true }; - MixedGen1NonTradebackGen2 = true; - } - else - 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; - EventEggMovesLearned.Add(m); - } - - // 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(LvlupEggMovesLearned).ToList(); - if (RegularEggMovesLearned.Any() && EventEggMovesLearned.Any()) - { - // Moves that are egg moves or event egg moves but not both - var IncompatibleEggMoves = RegularEggMovesLearned.Except(EventEggMovesLearned).Union(EventEggMovesLearned.Except(RegularEggMovesLearned)).ToList(); - if (IncompatibleEggMoves.Any()) - { - foreach (int m in IncompatibleEggMoves) - { - if (EventEggMovesLearned.Contains(m) && !EggMovesLearned.Contains(m)) - res[m] = new CheckMoveResult(res[m], Severity.Invalid, V337, CheckIdentifier.Move); - else if (!EventEggMovesLearned.Contains(m) && EggMovesLearned.Contains(m)) - res[m] = new CheckMoveResult(res[m], Severity.Invalid, V336, CheckIdentifier.Move); - else if (!EventEggMovesLearned.Contains(m) && LvlupEggMovesLearned.Contains(m)) - res[m] = new CheckMoveResult(res[m], Severity.Invalid, V358, CheckIdentifier.Move); - } - } - } - // If there is no incompatibility with event egg check that there is no inherited move in gift eggs and event eggs - else if (RegularEggMovesLearned.Any() && (pkm.WasGiftEgg || pkm.WasEventEgg)) - { - foreach (int m in RegularEggMovesLearned) - { - if (EggMovesLearned.Contains(m)) - res[m] = new CheckMoveResult(res[m], Severity.Invalid, pkm.WasGiftEgg ? V377 : V341, CheckIdentifier.Move); - else if (LvlupEggMovesLearned.Contains(m)) - res[m] = new CheckMoveResult(res[m], Severity.Invalid, pkm.WasGiftEgg ? V378 : V347, CheckIdentifier.Move); - } - } - } - - if (3 <= gen && gen <= 4 && pkm.Format > gen) - { - // After all the moves from the generations 3 and 4, - // including egg moves if is the origin generation because some hidden moves are also special egg moves in gen 3 - // Check if the marked hidden moves that were invalid at the start are now marked as valid, that means - // the hidden move was learned in gen 3 or 4 but was not removed when transfer to 4 or 5 - if (KnowDefogWhirlpool) - { - int invalidCount = moves.Where((m, i) => (m == 250 || m == 432) && (res[i]?.Valid ?? false)).Count(); - 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 CheckMoveResult(res[i], Severity.Invalid, V338, CheckIdentifier.Move); - } - - for (int i = 0; i < HMLearned.Length; i++) - if (res[i]?.Valid ?? false) - 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 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) - { - // Check moves learned at the same level in red/blue and yellow, illegal because there is no move reminder - // Only two incompatibilites and only there are no illegal combination if generation 2 or 7 are included in the analysis - ParseRedYellowIncompatibleMoves(pkm, res, moves); - - ParseEvolutionsIncompatibleMoves(pkm, res, moves, info.EncounterMoves.TMHMMoves[1]); - } - - 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 - // including special and eggs moves before realearn generations - ParseEvolutionLevelupMove(pkm, res, moves, IncenseMovesLearned, info); - } - - if (res.All(r => r != null)) + ParseMovesByGeneration(pkm, res, gen, info, moveInfo, lastgen); + if (AllParsed()) return res; } @@ -475,7 +259,7 @@ private static CheckMoveResult[] ParseMoves(PKM pkm, int[] moves, int[] special, // Ignore Shedinja if the Encounter was also a Shedinja, assume null Encounter as a Nincada egg // Check Shedinja evolved moves from Ninjask after egg moves // Those moves could also be inherited egg moves - ParseShedinjaEvolveMoves(pkm, res, moves); + ParseShedinjaEvolveMoves(pkm, res, source.CurrentMoves); } for (int m = 0; m < 4; m++) @@ -485,28 +269,228 @@ private static CheckMoveResult[] ParseMoves(PKM pkm, int[] moves, int[] special, } return res; } + private static void ParseMovesByGeneration(PKM pkm, CheckMoveResult[] res, int gen, LegalInfo info, LearnInfo learnInfo, int last) + { + GetHMCompatibility(pkm, learnInfo.Source.CurrentMoves, gen, res, out bool[] HMLearned, out bool KnowDefogWhirlpool); + ParseMovesByGeneration(pkm, res, gen, info, learnInfo); + if (gen == last) + ParseMovesByGenerationLast(pkm, res, gen, learnInfo); + + switch (gen) + { + case 3: + case 4: + if (pkm.Format > gen) + FlagIncompatibleTransferHMs(res, learnInfo.Source.CurrentMoves, gen, HMLearned, KnowDefogWhirlpool); + break; + + case 1: + case 2: + ParseMovesByGeneration12(pkm, res, learnInfo.Source.CurrentMoves, gen, info, learnInfo); + break; + } + + // 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 + // including special and eggs moves before realearn generations + if (Legal.SpeciesEvolutionWithMove.Contains(pkm.Species)) + ParseEvolutionLevelupMove(pkm, res, learnInfo.Source.CurrentMoves, learnInfo.IncenseMoves, info); + } + private static void ParseMovesByGeneration(PKM pkm, CheckMoveResult[] res, int gen, LegalInfo info, LearnInfo learnInfo) + { + var moves = learnInfo.Source.CurrentMoves; + bool native = gen == pkm.Format; + for (int m = 0; m < 4; m++) + { + if (IsCheckValid(res[m])) // already validated with another generation + continue; + if (moves[m] == 0) + continue; + + if (gen == 1 && learnInfo.Source.Base.Contains(moves[m])) + res[m] = new CheckMoveResult(MoveSource.Initial, gen, Severity.Valid, native ? V361 : string.Format(V362, gen), CheckIdentifier.Move); + else if (info.EncounterMoves.LevelUpMoves[gen].Contains(moves[m])) + res[m] = new CheckMoveResult(MoveSource.LevelUp, gen, Severity.Valid, native ? V177 : string.Format(V330, gen), CheckIdentifier.Move); + else if (info.EncounterMoves.TMHMMoves[gen].Contains(moves[m])) + res[m] = new CheckMoveResult(MoveSource.TMHM, gen, Severity.Valid, native ? V173 : string.Format(V331, gen), CheckIdentifier.Move); + else if (info.EncounterMoves.TutorMoves[gen].Contains(moves[m])) + res[m] = new CheckMoveResult(MoveSource.Tutor, gen, Severity.Valid, native ? V174 : string.Format(V332, gen), CheckIdentifier.Move); + else if (gen == pkm.GenNumber && learnInfo.Source.SpecialSource.Contains(moves[m])) + 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 && learnInfo.Source.NonTradeBackLevelUpMoves.Contains(m)) + learnInfo.Gen2PreevoMoves.Add(m); + if (res[m].Valid && gen == 1) + { + learnInfo.Gen1Moves.Add(m); + if (learnInfo.Gen2PreevoMoves.Any()) + learnInfo.MixedGen12NonTradeback = true; + } + + if (res[m].Valid && gen <= 2 && pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber != gen) + pkm.TradebackStatus = TradebackType.WasTradeback; + } + } + private static void ParseMovesByGeneration12(PKM pkm, CheckMoveResult[] res, int[] moves, int gen, LegalInfo info, LearnInfo learnInfo) + { + // Mark the gen 1 exclusive moves as illegal because the pokemon also have Non tradeback egg moves. + if (learnInfo.MixedGen12NonTradeback) + { + foreach (int m in learnInfo.Gen1Moves) + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V335, CheckIdentifier.Move); + + foreach (int m in learnInfo.Gen2PreevoMoves) + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V412, CheckIdentifier.Move); + } + + if (gen == 1 && pkm.Format == 1 && pkm.Gen1_NotTradeback) + { + ParseRedYellowIncompatibleMoves(pkm, res, moves); + ParseEvolutionsIncompatibleMoves(pkm, res, moves, info.EncounterMoves.TMHMMoves[1]); + } + } + private static void ParseMovesByGenerationLast(PKM pkm, CheckMoveResult[] res, int gen, LearnInfo learnInfo) + { + ParseEggMovesInherited(pkm, res, gen, learnInfo); + ParseEggMoves(pkm, res, gen, learnInfo); + ParseEggMovesRemaining(pkm, res, learnInfo); + } + private static void ParseEggMovesInherited(PKM pkm, CheckMoveResult[] res, int gen, LearnInfo learnInfo) + { + var moves = learnInfo.Source.CurrentMoves; + // 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 (res[m]?.Valid ?? false) // Skip valid move + continue; + if (moves[m] == 0) + continue; + if (!learnInfo.Source.EggLevelUpSource.Contains(moves[m])) // Check if contains level-up egg moves from parents + continue; + + if (learnInfo.IsGen2Pkm && learnInfo.Gen1Moves.Any() && moves[m] > Legal.MaxMoveID_1) + { + res[m] = new CheckMoveResult(MoveSource.InheritLevelUp, gen, Severity.Invalid, V334, CheckIdentifier.Move); + learnInfo.MixedGen12NonTradeback = true; + } + else + res[m] = new CheckMoveResult(MoveSource.InheritLevelUp, gen, Severity.Valid, V345, CheckIdentifier.Move); + learnInfo.LevelUpEggMoves.Add(m); + if (pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber == 1) + pkm.TradebackStatus = TradebackType.WasTradeback; + } + } + private static void ParseEggMoves(PKM pkm, CheckMoveResult[] res, int gen, LearnInfo learnInfo) + { + var moves = learnInfo.Source.CurrentMoves; + // Check egg moves after all the generations and all the moves, every move that can't be learned in another source should have preference + // the moves that can only be learned from egg moves should in the future check if the move combinations can be breed in gens 2 to 5 + for (int m = 0; m < 4; m++) + { + if (IsCheckValid(res[m])) + continue; + if (moves[m] == 0) + continue; + + if (learnInfo.Source.EggMoveSource.Contains(moves[m])) + { + // 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 + if (learnInfo.IsGen2Pkm && learnInfo.Gen1Moves.Any() && moves[m] > Legal.MaxMoveID_1) + { + res[m] = new CheckMoveResult(MoveSource.EggMove, gen, Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true }; + learnInfo.MixedGen12NonTradeback = true; + } + else + res[m] = new CheckMoveResult(MoveSource.EggMove, gen, Severity.Valid, V171, CheckIdentifier.Move) { Flag = true }; + + learnInfo.EggMovesLearned.Add(m); + if (pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber == 1) + pkm.TradebackStatus = TradebackType.WasTradeback; + } + if (!learnInfo.Source.EggEventSource.Contains(moves[m])) + continue; + + if (!learnInfo.Source.EggMoveSource.Contains(moves[m])) + { + if (learnInfo.IsGen2Pkm && learnInfo.Gen1Moves.Any() && moves[m] > Legal.MaxMoveID_1) + { + res[m] = new CheckMoveResult(MoveSource.SpecialEgg, gen, Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true }; + learnInfo.MixedGen12NonTradeback = true; + } + else + 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; + learnInfo.EventEggMoves.Add(m); + } + } + private static void ParseEggMovesRemaining(PKM pkm, CheckMoveResult[] res, LearnInfo learnInfo) + { + // 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 = learnInfo.EggMovesLearned.Union(learnInfo.LevelUpEggMoves).ToList(); + if (RegularEggMovesLearned.Any() && learnInfo.EventEggMoves.Any()) + { + // Moves that are egg moves or event egg moves but not both + var IncompatibleEggMoves = RegularEggMovesLearned.Except(learnInfo.EventEggMoves).Union(learnInfo.EventEggMoves.Except(RegularEggMovesLearned)).ToList(); + if (!IncompatibleEggMoves.Any()) + return; + foreach (int m in IncompatibleEggMoves) + { + if (learnInfo.EventEggMoves.Contains(m) && !learnInfo.EggMovesLearned.Contains(m)) + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V337, CheckIdentifier.Move); + else if (!learnInfo.EventEggMoves.Contains(m) && learnInfo.EggMovesLearned.Contains(m)) + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V336, CheckIdentifier.Move); + else if (!learnInfo.EventEggMoves.Contains(m) && learnInfo.LevelUpEggMoves.Contains(m)) + res[m] = new CheckMoveResult(res[m], Severity.Invalid, V358, CheckIdentifier.Move); + } + } + // If there is no incompatibility with event egg check that there is no inherited move in gift eggs and event eggs + else if (RegularEggMovesLearned.Any() && (pkm.WasGiftEgg || pkm.WasEventEgg)) + { + foreach (int m in RegularEggMovesLearned) + { + if (learnInfo.EggMovesLearned.Contains(m)) + res[m] = new CheckMoveResult(res[m], Severity.Invalid, pkm.WasGiftEgg ? V377 : V341, CheckIdentifier.Move); + else if (learnInfo.LevelUpEggMoves.Contains(m)) + res[m] = new CheckMoveResult(res[m], Severity.Invalid, pkm.WasGiftEgg ? V378 : V347, CheckIdentifier.Move); + } + } + } private static void ParseRedYellowIncompatibleMoves(PKM pkm, IList res, int[] moves) { + // Check moves that are learned at the same level in red/blue and yellow, these are illegal because there is no move reminder + // There are only two incompatibilites; there is no illegal combination in generation 2+. var incompatible = new List(); - if (pkm.Species == 134 && pkm.CurrentLevel < 47 && moves.Contains(151)) + + switch (pkm.Species) { - // Vaporeon in Yellow learn Mist and Haze at level 42, Mist only if level up in day-care - // Vaporeon in Red Blue learn Acid Armor at level 42 and level 47 in Yellow - if (moves.Contains(54)) - incompatible.Add(54); - if (moves.Contains(114)) - incompatible.Add(114); - if (incompatible.Any()) - incompatible.Add(151); - } - if (pkm.Species == 136 && pkm.CurrentLevel < 47 && moves.Contains(43) && moves.Contains(123)) - { - // Flareon in Yellow learn Smog at level 42 - // Flareon in Red Blue learn Leer at level 42 and level 47 in Yellow - incompatible.Add(43); - incompatible.Add(123); + // Vaporeon in Yellow learns Mist and Haze at level 42, Mist can only be larned if it levels up in the daycare + // Vaporeon in Red/Blue learns Acid Armor at level 42 and level 47 in Yellow + case 134 when pkm.CurrentLevel < 47 && moves.Contains(151): + if (moves.Contains(54)) + incompatible.Add(54); + if (moves.Contains(114)) + incompatible.Add(114); + if (incompatible.Any()) + incompatible.Add(151); + break; + + // Flareon in Yellow learns Smog at level 42 + // Flareon in Red Blue learns Leer at level 42 and level 47 in Yellow + case 136 when pkm.CurrentLevel < 47 && moves.Contains(43) && moves.Contains(123): + incompatible.Add(43); + incompatible.Add(123); + break; } + for (int m = 0; m < 4; m++) { if (incompatible.Contains(moves[m])) @@ -636,31 +620,74 @@ private static void ParseEvolutionLevelupMove(PKM pkm, IList re for (int m = 0; m < 4; m++) res[m] = new CheckMoveResult(res[m], Severity.Invalid, string.Format(V385, SpeciesStrings[pkm.Species]), CheckIdentifier.Move); } + private static void GetHMCompatibility(PKM pkm, int[] moves, int gen, IReadOnlyList res, out bool[] HMLearned, out bool KnowDefogWhirlpool) + { + HMLearned = new bool[4]; + // Check if pokemon knows HM moves from generation 3 and 4 but are not valid yet, that means it cant learn the HMs in future generations + if (gen == 4 && pkm.Format > 4) + { + IsHMSource(ref HMLearned, Legal.HM_4_RemovePokeTransfer); + KnowDefogWhirlpool = moves.Where((m, i) => IsDefogWhirl(m) && IsCheckInvalid(res[i])).Count() == 2; + return; + } + KnowDefogWhirlpool = false; + if (gen == 3 && pkm.Format > 3) + IsHMSource(ref HMLearned, Legal.HM_3); + + void IsHMSource(ref bool[] flags, int[] source) + { + for (int i = 0; i < 4; i++) + flags[i] = IsCheckInvalid(res[i]) && source.Contains(moves[i]); + } + } + private static bool IsDefogWhirl(int move) => move == 250 || move == 432; + private static bool IsCheckInvalid(CheckResult chk) => !(chk?.Valid ?? false); + private static bool IsCheckValid(CheckResult chk) => chk?.Valid ?? false; + private static void FlagIncompatibleTransferHMs(CheckMoveResult[] res, int[] moves, int gen, bool[] HMLearned, bool KnowDefogWhirlpool) + { + // After all the moves from the generations 3 and 4, + // including egg moves if is the origin generation because some hidden moves are also special egg moves in gen 3 + // Check if the marked hidden moves that were invalid at the start are now marked as valid, that means + // the hidden move was learned in gen 3 or 4 but was not removed when transfer to 4 or 5 + if (KnowDefogWhirlpool) + { + int invalidCount = moves.Where((m, i) => IsDefogWhirl(m) && IsCheckValid(res[i])).Count(); + if (invalidCount == 2) // can't know both at the same time + for (int i = 0; i < 4; i++) // flag both moves + if (IsDefogWhirl(moves[i])) + res[i] = new CheckMoveResult(res[i], Severity.Invalid, V338, CheckIdentifier.Move); + } + + // Flag moves that are only legal when learned from a past-gen HM source + for (int i = 0; i < HMLearned.Length; i++) + if (HMLearned[i] && IsCheckValid(res[i])) + res[i] = new CheckMoveResult(res[i], Severity.Invalid, string.Format(V339, gen, gen + 1), 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 CheckMoveResult[] VerifyPreRelearnEggBase(PKM pkm, int[] Moves, MoveInfoSet infoset, GameVersion ver) + private static CheckMoveResult[] VerifyPreRelearnEggBase(PKM pkm, int[] Moves, EggInfoSource infoset) { CheckMoveResult[] res = new CheckMoveResult[4]; var gen = pkm.GenNumber; // Obtain level1 moves - int baseCt = infoset.BaseMoves.Count; + int baseCt = infoset.Base.Count; if (baseCt > 4) baseCt = 4; // Obtain Inherited moves - var inherited = Moves.Where(m => m != 0 && (!infoset.BaseMoves.Contains(m) || infoset.SpecialMoves.Contains(m) || infoset.EggMoves.Contains(m) || infoset.LvlMoves.Contains(m) || infoset.TMHMMoves.Contains(m) || infoset.TutorMoves.Contains(m))).ToList(); + var inherited = Moves.Where(m => m != 0 && (!infoset.Base.Contains(m) || infoset.Special.Contains(m) || infoset.Egg.Contains(m) || infoset.LevelUp.Contains(m) || infoset.TMHM.Contains(m) || infoset.Tutor.Contains(m))).ToList(); int inheritCt = inherited.Count; // Get required amount of base moves - int unique = infoset.BaseMoves.Concat(inherited).Distinct().Count(); + int unique = infoset.Base.Concat(inherited).Distinct().Count(); int reqBase = inheritCt == 4 || baseCt + inheritCt > 4 ? 4 - inheritCt : baseCt; - if (Moves.Where(m => m != 0).Count() < Math.Min(4, infoset.BaseMoves.Count)) + if (Moves.Where(m => m != 0).Count() < Math.Min(4, infoset.Base.Count)) reqBase = Math.Min(4, unique); var em = string.Empty; // Check if the required amount of Base Egg Moves are present. for (int i = 0; i < reqBase; i++) { - if (infoset.BaseMoves.Contains(Moves[i])) + if (infoset.Base.Contains(Moves[i])) { res[i] = new CheckMoveResult(MoveSource.Initial, gen, Severity.Valid, V179, CheckIdentifier.Move); continue; @@ -671,16 +698,16 @@ private static CheckMoveResult[] VerifyPreRelearnEggBase(PKM pkm, int[] Moves, M 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(", ", infoset.BaseMoves.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m])); + em = string.Join(", ", infoset.Base.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m])); break; } int moveoffset = reqBase; - int endSpecial = moveoffset + infoset.SpecialMoves.Count; + int endSpecial = moveoffset + infoset.Special.Count; // Check also if the required amount of Special Egg Moves are present, ir are after base moves for (int i = moveoffset; i < endSpecial; i++) { - if (infoset.SpecialMoves.Contains(Moves[i])) + if (infoset.Special.Contains(Moves[i])) { res[i] = new CheckMoveResult(MoveSource.SpecialEgg, gen, Severity.Valid, V333, CheckIdentifier.Move); continue; @@ -693,30 +720,27 @@ private static CheckMoveResult[] VerifyPreRelearnEggBase(PKM pkm, int[] Moves, M // provide the list of suggested base moves and species moves for the last required slot if (!string.IsNullOrEmpty(em)) em += ", "; else - em = string.Join(", ", infoset.BaseMoves.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m])) + ", "; - em += string.Join(", ", infoset.SpecialMoves.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m])); + em = string.Join(", ", infoset.Base.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m])) + ", "; + em += string.Join(", ", infoset.Special.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[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 (pkm.GenNumber >= 3 && Legal.LightBall.Contains(pkm.Species)) - infoset.EggMoves.Add(344); // Inherited moves appear after the required base moves. var AllowInheritedSeverity = infoset.AllowInherited ? Severity.Valid : Severity.Invalid; - for (int i = reqBase + infoset.SpecialMoves.Count; i < 4; i++) + for (int i = reqBase + infoset.Special.Count; i < 4; i++) { if (Moves[i] == 0) // empty res[i] = new CheckMoveResult(MoveSource.None, gen, Severity.Valid, V167, CheckIdentifier.Move); - else if (infoset.EggMoves.Contains(Moves[i])) // inherited egg move + else if (infoset.Egg.Contains(Moves[i])) // inherited egg move res[i] = new CheckMoveResult(MoveSource.EggMove, gen, AllowInheritedSeverity, infoset.AllowInherited ? V344 : V341, CheckIdentifier.Move); - else if (infoset.LvlMoves.Contains(Moves[i])) // inherited lvl moves + else if (infoset.LevelUp.Contains(Moves[i])) // inherited lvl moves res[i] = new CheckMoveResult(MoveSource.InheritLevelUp, gen, AllowInheritedSeverity, infoset.AllowInherited ? V345 : V347, CheckIdentifier.Move); - else if (infoset.TMHMMoves.Contains(Moves[i])) // inherited TMHM moves + else if (infoset.TMHM.Contains(Moves[i])) // inherited TMHM moves res[i] = new CheckMoveResult(MoveSource.TMHM, gen, AllowInheritedSeverity, infoset.AllowInherited ? V349 : V350, CheckIdentifier.Move); - else if (infoset.TutorMoves.Contains(Moves[i])) // inherited tutor moves + else if (infoset.Tutor.Contains(Moves[i])) // inherited tutor moves res[i] = new CheckMoveResult(MoveSource.Tutor, gen, AllowInheritedSeverity, infoset.AllowInherited ? V346 : V348, CheckIdentifier.Move); else // not inheritable, flag res[i] = new CheckMoveResult(MoveSource.Unknown, gen, Severity.Invalid, V340, CheckIdentifier.Move); @@ -778,26 +802,5 @@ private static int[] GetGenMovesCheckOrder(PKM pkm) order[i] = pkm.Format - i; return order; } - private static ValidEncounterMoves GetEncounterValidMoves(PKM pkm, LegalInfo info) - { - var minLvLG1 = pkm.GenNumber <= 2 ? info.EncounterMatch.LevelMin + 1 : 0; - var minLvlG2 = Legal.AllowGen2MoveReminder ? 1 : info.EncounterMatch.LevelMin + 1; - var encounterspecies = info.EncounterMatch.Species; - var EvoChainsAllGens = info.EvoChainsAllGens; - // If encounter species is the same species from the first match, the one in variable EncounterMatch, its evolution chains is already in EvoChainsAllGens - var LevelMoves = Legal.GetValidMovesAllGens(pkm, EvoChainsAllGens, minLvLG1: minLvLG1, minLvLG2: minLvlG2, Tutor: false, Machine: false, RemoveTransferHM: false); - var TMHMMoves = Legal.GetValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Tutor: false, MoveReminder: false, RemoveTransferHM: false); - var TutorMoves = Legal.GetValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Machine: false, MoveReminder: false, RemoveTransferHM: false); - return new ValidEncounterMoves - { - EncounterSpecies = encounterspecies, - LevelUpMoves = LevelMoves, - TMHMMoves = TMHMMoves, - TutorMoves = TutorMoves, - EvolutionChains = EvoChainsAllGens, - MinimumLevelGen1 = minLvLG1, - MinimumLevelGen2 = minLvlG2 - }; - } } } diff --git a/PKHeX.Core/Legality/Moves/EggInfoSource.cs b/PKHeX.Core/Legality/Moves/EggInfoSource.cs new file mode 100644 index 000000000..3ef1eeeb9 --- /dev/null +++ b/PKHeX.Core/Legality/Moves/EggInfoSource.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Linq; + +namespace PKHeX.Core +{ + internal class EggInfoSource + { + public EggInfoSource(PKM pkm, IEnumerable specialMoves, EncounterEgg e) + { + // Eggs with special moves cannot inherit levelup moves as the current moves are predefined. + Special = specialMoves.Where(m => m != 0).ToList(); + bool notSpecial = Special.Count == 0; + AllowInherited = notSpecial && !pkm.WasGiftEgg && pkm.Species != 489 && pkm.Species != 490; + + // Level up moves can only be inherited if ditto is not the mother. + var ratio = pkm.PersonalInfo.Gender; // Genderless/Male Only (except a few) cannot inherit. + bool AllowLevelUp = ratio > 0 && ratio < 255 || Legal.MixedGenderBreeding.Contains(e.Species); + Base = Legal.GetBaseEggMoves(pkm, e.Species, e.Game, e.LevelMin).ToList(); + + Egg = Legal.GetEggMoves(pkm, e.Species, pkm.AltForm).ToList(); + LevelUp = AllowLevelUp + ? Legal.GetBaseEggMoves(pkm, e.Species, e.Game, 100).Where(x => !Base.Contains(x)).ToList() + : new List(); + Tutor = 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 + TMHM = Legal.GetTMHM(pkm, pkm.Species, pkm.AltForm, pkm.GenNumber, e.Game, false).ToList(); + + // Non-Base moves that can magically appear in the regular movepool + bool volt = notSpecial && (pkm.GenNumber > 3 || e.Game == GameVersion.E) && Legal.LightBall.Contains(pkm.Species); + if (volt) + Egg.Add(344); // Volt Tackle + } + + public bool AllowInherited { get; } + public List Base { get; } + public List Special { get; } + public List Egg { get; } + public List Tutor { get; } + public List TMHM { get; } + public List LevelUp { get; } + } +} diff --git a/PKHeX.Core/Legality/Moves/LearnInfo.cs b/PKHeX.Core/Legality/Moves/LearnInfo.cs new file mode 100644 index 000000000..7a5dfd16d --- /dev/null +++ b/PKHeX.Core/Legality/Moves/LearnInfo.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace PKHeX.Core +{ + internal class LearnInfo + { + public bool MixedGen12NonTradeback { get; set; } + public List Gen1Moves { get; } = new List(); + public List Gen2PreevoMoves { get; } = new List(); + public List EggMovesLearned { get; } = new List(); + public List LevelUpEggMoves { get; } = new List(); + public List EventEggMoves { get; } = new List(); + public List IncenseMoves { get; } = new List(); + public MoveParseSource Source { get; set; } + + public readonly bool IsGen2Pkm; + public LearnInfo(PKM pkm) + { + IsGen2Pkm = pkm.Format == 2 || pkm.VC2; + } + } +} diff --git a/PKHeX.Core/Legality/Moves/MoveParseSource.cs b/PKHeX.Core/Legality/Moves/MoveParseSource.cs new file mode 100644 index 000000000..715172110 --- /dev/null +++ b/PKHeX.Core/Legality/Moves/MoveParseSource.cs @@ -0,0 +1,19 @@ +namespace PKHeX.Core +{ + internal class MoveParseSource + { + private static readonly int[] Empty = new int[0]; + public int[] CurrentMoves { get; set; } = Empty; + public int[] SpecialSource { get; set; } = Empty; + public int[] NonTradeBackLevelUpMoves { get; set; } = Empty; + + /// + /// Base moves from a standard encounter + /// + public int[] Base { get; set; } = Empty; + + public int[] EggLevelUpSource { get; set; } = Empty; + public int[] EggMoveSource { get; set; } = Empty; + public int[] EggEventSource { get; set; } = Empty; + } +} diff --git a/PKHeX.Core/Legality/Structures/ValidEncounterMoves.cs b/PKHeX.Core/Legality/Structures/ValidEncounterMoves.cs index 1826828d4..7d0a7d71c 100644 --- a/PKHeX.Core/Legality/Structures/ValidEncounterMoves.cs +++ b/PKHeX.Core/Legality/Structures/ValidEncounterMoves.cs @@ -5,16 +5,32 @@ namespace PKHeX.Core { public class ValidEncounterMoves { - public int EncounterSpecies { get; set; } - public DexLevel[][] EvolutionChains { get; set; } - public List[] LevelUpMoves { get; set; } = Empty; - public List[] TMHMMoves { get; set; } = Empty; - public List[] TutorMoves { get; set; } = Empty; + public int EncounterSpecies { get; } + public DexLevel[][] EvolutionChains { get; } + public List[] LevelUpMoves { get; } = Empty; + public List[] TMHMMoves { get; } = Empty; + public List[] TutorMoves { get; } = Empty; public int[] Relearn = new int[0]; - public int MinimumLevelGen1 { get; set; } - public int MinimumLevelGen2 { get; set; } + public int MinimumLevelGen1 { get; } + public int MinimumLevelGen2 { get; } private const int EmptyCount = 7; private static readonly List[] Empty = new int[EmptyCount].Select(z => new List()).ToArray(); + + public ValidEncounterMoves(PKM pkm, LegalInfo info) + { + MinimumLevelGen1 = pkm.GenNumber <= 2 ? info.EncounterMatch.LevelMin + 1 : 0; + MinimumLevelGen2 = Legal.AllowGen2MoveReminder ? 1 : info.EncounterMatch.LevelMin + 1; + EncounterSpecies = info.EncounterMatch.Species; + EvolutionChains = info.EvoChainsAllGens; + LevelUpMoves = Legal.GetValidMovesAllGens(pkm, EvolutionChains, minLvLG1: MinimumLevelGen1, minLvLG2: MinimumLevelGen2, Tutor: false, Machine: false, RemoveTransferHM: false); + TMHMMoves = Legal.GetValidMovesAllGens(pkm, EvolutionChains, LVL: false, Tutor: false, MoveReminder: false, RemoveTransferHM: false); + TutorMoves = Legal.GetValidMovesAllGens(pkm, EvolutionChains, LVL: false, Machine: false, MoveReminder: false, RemoveTransferHM: false); + } + + public ValidEncounterMoves(List[] levelup) + { + LevelUpMoves = levelup; + } } }