diff --git a/PKHeX.Core/Legality/Core.cs b/PKHeX.Core/Legality/Core.cs index cee276b60..e9998e722 100644 --- a/PKHeX.Core/Legality/Core.cs +++ b/PKHeX.Core/Legality/Core.cs @@ -1982,186 +1982,11 @@ private static int[] GetEggMoves(int gen, int species, int formnum, GameVersion internal static IEnumerable GetTMHM(PKM pkm, int species, int form, int generation, GameVersion Version = GameVersion.Any, bool RemoveTransferHM = true) { - List moves = new List(); - int index; - switch (generation) - { - case 1: - index = PersonalTable.RB.GetFormeIndex(species, 0); - if (index == 0) - return moves; - var pi_rb = (PersonalInfoG1)PersonalTable.RB[index]; - var pi_y = (PersonalInfoG1)PersonalTable.Y[index]; - moves.AddRange(TMHM_RBY.Where((_, m) => pi_rb.TMHM[m])); - moves.AddRange(TMHM_RBY.Where((_, m) => pi_y.TMHM[m])); - break; - case 2: - index = PersonalTable.C.GetFormeIndex(species, 0); - if (index == 0) - return moves; - var pi_c = (PersonalInfoG2)PersonalTable.C[index]; - moves.AddRange(TMHM_GSC.Where((_, m) => pi_c.TMHM[m])); - if (Version == GameVersion.Any) - goto case 1; // rby - break; - case 3: - index = PersonalTable.E.GetFormeIndex(species, 0); - var pi_e = PersonalTable.E[index]; - moves.AddRange(TM_3.Where((_, m) => pi_e.TMHM[m])); - if (!RemoveTransferHM || pkm.Format == 3) // HM moves must be removed for 3->4, only give if current format. - moves.AddRange(HM_3.Where((_, m) => pi_e.TMHM[m + 50])); - break; - case 4: - index = PersonalTable.HGSS.GetFormeIndex(species, 0); - if (index == 0) - return moves; - var pi_hgss = PersonalTable.HGSS[index]; - var pi_dppt = PersonalTable.Pt[index]; - moves.AddRange(TM_4.Where((_, m) => pi_hgss.TMHM[m])); - // The combination of both these moves is illegal, it should be checked that the pokemon only learn one - // except if it can learn any of these moves in gen 5 or later - if (Version == GameVersion.Any || Version == GameVersion.DP || Version == GameVersion.D || Version == GameVersion.P || Version == GameVersion.Pt) - { - if (RemoveTransferHM && pkm.Format > 4) - { - if (pi_dppt.TMHM[96]) - moves.Add(432); // Defog - } - else - { - moves.AddRange(HM_DPPt.Where((_, m) => pi_dppt.TMHM[m + 92])); - } - } - if (Version == GameVersion.Any || Version == GameVersion.HGSS || Version == GameVersion.HG || Version == GameVersion.SS) - { - if (RemoveTransferHM && pkm.Format > 4) - { - if (pi_hgss.TMHM[96]) - moves.Add(432); // Defog - } - else - { - moves.AddRange(HM_HGSS.Where((_, m) => pi_dppt.TMHM[m + 92])); - } - } - break; - case 5: - index = PersonalTable.B2W2.GetFormeIndex(species, 0); - if (index == 0) - return moves; - - var pi_bw = PersonalTable.B2W2[index]; - moves.AddRange(TMHM_BW.Where((_, m) => pi_bw.TMHM[m])); - break; - case 6: - switch (Version) - { - case GameVersion.Any: // Start at the top, hit every table - case GameVersion.X: - case GameVersion.Y: - case GameVersion.XY: - { - index = PersonalTable.XY.GetFormeIndex(species, form); - if (index == 0) - return moves; - - PersonalInfo pi_xy = PersonalTable.XY[index]; - moves.AddRange(TMHM_XY.Where((_, m) => pi_xy.TMHM[m])); - - if (Version == GameVersion.Any) // Fall Through - goto case GameVersion.ORAS; - break; - } - case GameVersion.AS: - case GameVersion.OR: - case GameVersion.ORAS: - { - index = PersonalTable.AO.GetFormeIndex(species, form); - if (index == 0) - return moves; - - PersonalInfo pi_ao = PersonalTable.AO[index]; - moves.AddRange(TMHM_AO.Where((_, m) => pi_ao.TMHM[m])); - break; - } - } - break; - case 7: - index = PersonalTable.USUM.GetFormeIndex(species, form); - if (index == 0) - return moves; - - PersonalInfo pi_sm = PersonalTable.USUM[index]; - moves.AddRange(TMHM_SM.Where((_, m) => pi_sm.TMHM[m])); - break; - } - return moves.Distinct(); + return MoveTechnicalMachine.GetTMHM(pkm, species, form, generation, Version, RemoveTransferHM); } internal static IEnumerable GetTutorMoves(PKM pkm, int species, int form, bool specialTutors, int generation) { - List moves = new List(); - PersonalInfo info; - switch (generation) - { - case 1: - if (AllowGBCartEra && pkm.Format < 3 && (pkm.Species == 25 || pkm.Species == 26)) // Surf Pikachu via Stadium - moves.Add(57); - break; - case 2: - if (!AllowGen2Crystal(pkm)) - break; - info = PersonalTable.C[species]; - moves.AddRange(Tutors_GSC.Where((_, i) => info.TMHM[57 + i])); - goto case 1; - case 3: - // E Tutors (Free) - // E Tutors (BP) - info = PersonalTable.E[species]; - moves.AddRange(Tutor_E.Where((_, i) => info.TypeTutors[i])); - // FRLG Tutors - // Only special tutor moves, normal tutor moves are already included in Emerald data - moves.AddRange(SpecialTutors_FRLG.Where((_, i) => SpecialTutors_Compatibility_FRLG[i].Any(e => e == species))); - // XD - moves.AddRange(SpecialTutors_XD_Exclusive.Where((_, i) => SpecialTutors_Compatibility_XD_Exclusive[i].Any(e => e == species))); - // XD (Mew) - if (species == 151) - moves.AddRange(Tutor_3Mew); - - break; - case 4: - info = PersonalTable.HGSS.GetFormeEntry(species, form); - moves.AddRange(Tutors_4.Where((_, i) => info.TypeTutors[i])); - moves.AddRange(SpecialTutors_4.Where((_, i) => SpecialTutors_Compatibility_4[i].Any(e => e == species))); - break; - case 5: - info = PersonalTable.B2W2[species]; - moves.AddRange(TypeTutor6.Where((_, i) => info.TypeTutors[i])); - if (pkm.InhabitedGeneration(5) && specialTutors) - moves.AddRange(GetTutors(PersonalTable.B2W2, Tutors_B2W2)); - break; - case 6: - info = PersonalTable.AO[species]; - moves.AddRange(TypeTutor6.Where((_, i) => info.TypeTutors[i])); - if (pkm.InhabitedGeneration(6) && specialTutors && (pkm.AO || !pkm.IsUntraded)) - moves.AddRange(GetTutors(PersonalTable.AO, Tutors_AO)); - break; - case 7: - info = PersonalTable.USUM.GetFormeEntry(species, form); - moves.AddRange(TypeTutor6.Where((_, i) => info.TypeTutors[i])); - if (pkm.InhabitedGeneration(7) && specialTutors && (pkm.USUM || !pkm.IsUntraded)) - moves.AddRange(GetTutors(PersonalTable.USUM, Tutors_USUM)); - break; - } - return moves.Distinct(); - - IEnumerable GetTutors(PersonalTable t, params int[][] tutors) - { - var pi = t.GetFormeEntry(species, form); - for (int i = 0; i < tutors.Length; i++) - for (int b = 0; b < tutors[i].Length; b++) - if (pi.SpecialTutors[i][b]) - yield return tutors[i][b]; - } + return MoveTutor.GetTutorMoves(pkm, species, form, specialTutors, generation); } internal static bool IsTradedKadabraG1(PKM pkm) { @@ -2207,6 +2032,13 @@ internal static bool IsOutsider(PKM pkm) return Outsider; } + internal const GameVersion NONE = GameVersion.Invalid; + internal static readonly LearnVersion LearnNONE = new LearnVersion(-1); + + internal static bool HasVisitedB2W2(this PKM pkm) => pkm.InhabitedGeneration(5); + internal static bool HasVisitedORAS(this PKM pkm) => pkm.InhabitedGeneration(6) && (pkm.AO || !pkm.IsUntraded); + internal static bool HasVisitedUSUM(this PKM pkm) => pkm.InhabitedGeneration(7) && (pkm.USUM || !pkm.IsUntraded); + internal static TreeEncounterAvailable GetGSCHeadbuttAvailability(EncounterSlot encounter, int TID) { var Area = Array.Find(HeadbuttTreesC, a => a.Location == encounter.Location); diff --git a/PKHeX.Core/Legality/Learnset/Learnset.cs b/PKHeX.Core/Legality/Learnset/Learnset.cs index 88b29fe43..c363e40e9 100644 --- a/PKHeX.Core/Legality/Learnset/Learnset.cs +++ b/PKHeX.Core/Legality/Learnset/Learnset.cs @@ -84,5 +84,23 @@ public int GetLevelLearnMove(int move) { return (Learn ?? (Learn = GetDictionary())).TryGetValue(move, out var level) ? level : -1; } + + /// Returns the level that a Pokémon can learn the specified move. + /// Move ID + /// Minimum level to start looking at. + /// Level the move is learned at. If the result is below 0, it cannot be learned by levelup. + public int GetLevelLearnMove(int move, int min) + { + for (int i = 0; i < Moves.Length; i++) + { + if (move != Moves[i]) + continue; + + var lv = Levels[i]; + if (lv >= min) + return lv; + } + return -1; + } } } diff --git a/PKHeX.Core/Legality/Moves/LearnInfo.cs b/PKHeX.Core/Legality/Moves/LearnInfo.cs index 7a5dfd16d..040e46180 100644 --- a/PKHeX.Core/Legality/Moves/LearnInfo.cs +++ b/PKHeX.Core/Legality/Moves/LearnInfo.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace PKHeX.Core { @@ -19,4 +20,15 @@ public LearnInfo(PKM pkm) IsGen2Pkm = pkm.Format == 2 || pkm.VC2; } } + + public struct LearnVersion + { + public readonly GameVersion Game; + public readonly int Level; + public LearnVersion(int lv, GameVersion game = GameVersion.Any) + { + Game = game; + Level = lv; + } + } } diff --git a/PKHeX.Core/Legality/Moves/MoveLevelUp.cs b/PKHeX.Core/Legality/Moves/MoveLevelUp.cs new file mode 100644 index 000000000..e894bd263 --- /dev/null +++ b/PKHeX.Core/Legality/Moves/MoveLevelUp.cs @@ -0,0 +1,430 @@ +using System.Collections.Generic; +using System.Linq; + +using static PKHeX.Core.Legal; + +namespace PKHeX.Core +{ + internal static class MoveLevelUp + { + public static LearnVersion GetIsLevelUpMove(PKM pkm, int species, int minlvlG1, int minlvlG2, int lvl, int form, int Generation, int move, GameVersion Version = GameVersion.Any) + { + switch (Generation) + { + case 1: return GetIsLevelUp1(species, move, lvl, minlvlG1); + case 2: return GetIsLevelUp2(species, move, lvl, minlvlG2, pkm.Korean); + case 3: return GetIsLevelUp3(species, move, lvl, form); + case 4: return GetIsLevelUp4(species, move, lvl, form); + case 5: return GetIsLevelUp5(species, move, lvl, form); + case 6: return GetIsLevelUp6(species, move, lvl, form, Version); + case 7: return GetIsLevelUp7(species, move, form, Version); + } + return LearnNONE; + } + + private static LearnVersion GetIsLevelUp1(int species, int move, int max, int min) + { + int index = PersonalTable.RB.GetFormeIndex(species, 0); + if (index == 0) + return LearnNONE; + + if (min == 1) + { + var pi_rb = (PersonalInfoG1)PersonalTable.RB[index]; + var pi_y = (PersonalInfoG1)PersonalTable.Y[index]; + if (pi_rb.Moves.Contains(move)) + return new LearnVersion(0, GameVersion.RB); + if (pi_y.Moves.Contains(move)) + return new LearnVersion(0, GameVersion.YW); + } + + // No relearner -- have to be learned on levelup + var lv = LevelUpRB[index].GetLevelLearnMove(move, min); + if (lv >= 0 && lv <= max) + return new LearnVersion(lv, GameVersion.RB); + + // No relearner -- have to be learned on levelup + lv = LevelUpY[index].GetLevelLearnMove(move, min); + if (lv >= 0 && lv <= max) + return new LearnVersion(lv, GameVersion.YW); + + return LearnNONE; + } + private static LearnVersion GetIsLevelUp2(int species, int move, int max, int min, bool korean) + { + if (move > MaxMoveID_1) + return LearnNONE; + + int index = PersonalTable.C.GetFormeIndex(species, 0); + if (index == 0) + return LearnNONE; + + // No relearner -- have to be learned on levelup + var lv = LevelUpGS[index].GetLevelLearnMove(move, min); + if (lv >= 0 && lv <= max) + return new LearnVersion(lv, GameVersion.GS); + + if (korean) // No Korean Crystal + return LearnNONE; + + // No relearner -- have to be learned on levelup + lv = LevelUpC[index].GetLevelLearnMove(move, min); + if (lv >= 0 && lv <= max) + return new LearnVersion(lv, GameVersion.C); + + return LearnNONE; + } + private static LearnVersion GetIsLevelUp3(int species, int move, int lvl, int form) + { + + int index = PersonalTable.E.GetFormeIndex(species, 0); + if (index == 0) + return LearnNONE; + if (index == 386) + return GetIsLevelUp3Deoxys(form, move, lvl); + + // Emerald level up table are equals to R/S level up tables + var lv = LevelUpE[index].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.RSE); + + // fire red and leaf green are equals between each other but different than RSE + // Do not use FR Levelup table. It have 67 moves for charmander but Leaf Green moves table is correct + lv = LevelUpLG[index].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.FRLG); + + return LearnNONE; + } + private static LearnVersion GetIsLevelUp4(int species, int move, int lvl, int form) + { + int index = PersonalTable.HGSS.GetFormeIndex(species, form); + if (index == 0) + return LearnNONE; + + var lv = LevelUpDP[index].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.DP); + + lv = LevelUpPt[index].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.Pt); + + lv = LevelUpHGSS[index].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.HGSS); + + return LearnNONE; + } + private static LearnVersion GetIsLevelUp5(int species, int form, int move, int lvl) + { + int index1 = PersonalTable.BW.GetFormeIndex(species, form); + int index2 = PersonalTable.B2W2.GetFormeIndex(species, form); + if (index1 == 0 && index2 == 0) + return LearnNONE; + if (index1 != 0) + { + var lv = LevelUpBW[index1].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.BW); + } + if (index2 != 0) + { + var lv = LevelUpB2W2[index2].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.B2W2); + } + return LearnNONE; + } + private static LearnVersion GetIsLevelUp6(int species, int move, int lvl, int form, GameVersion ver) + { + switch (ver) + { + case GameVersion.SN: + case GameVersion.MN: + case GameVersion.SM: + return GetIsLevelUp6XY(species, form, move, lvl); + case GameVersion.Any: + case GameVersion.US: + case GameVersion.UM: + case GameVersion.USUM: + return GetIsLevelUp6AO(species, form, move, lvl); + } + return LearnNONE; + } + private static LearnVersion GetIsLevelUp7(int species, int move, int form, GameVersion ver) + { + switch (ver) + { + case GameVersion.SN: + case GameVersion.MN: + case GameVersion.SM: + return GetIsLevelUp7SM(species, form, move); + case GameVersion.Any: + case GameVersion.US: + case GameVersion.UM: + case GameVersion.USUM: + return GetIsLevelUp7USUM(species, form, move); + } + return LearnNONE; + } + + private static LearnVersion GetIsLevelUp3Deoxys(int form, int move, int lvl) + { + var moveset = GetDeoxysLearn3(form); + if (moveset == null) + return LearnNONE; + var lv = moveset.GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GetDeoxysGameVersion3(form)); + return LearnNONE; + } + private static LearnVersion GetIsLevelUp6XY(int species, int form, int move, int lvl) + { + int index = PersonalTable.XY.GetFormeIndex(species, form); + if (index == 0) + return LearnNONE; + + var lv = LevelUpXY[index].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.XY); + + return LearnNONE; + } + private static LearnVersion GetIsLevelUp6AO(int species, int form, int move, int lvl) + { + int index = PersonalTable.AO.GetFormeIndex(species, form); + if (index == 0) + return LearnNONE; + + var lv = LevelUpAO[index].GetLevelLearnMove(move); + if (lv >= 0 && lv <= lvl) + return new LearnVersion(lv, GameVersion.ORAS); + + return GetIsLevelUp7SM(species, form, move); + } + private static LearnVersion GetIsLevelUp7SM(int species, int form, int move) + { + if (species > MaxSpeciesID_7) + return LearnNONE; + int index = PersonalTable.SM.GetFormeIndex(species, form); + if (index == 0) + return LearnNONE; + + var lv = LevelUpSM[index].GetLevelLearnMove(move); + if (lv >= 0) + return new LearnVersion(lv, GameVersion.SM); + + return LearnNONE; + } + private static LearnVersion GetIsLevelUp7USUM(int species, int form, int move) + { + if (species > MaxSpeciesID_7_USUM) + return LearnNONE; + int index = PersonalTable.USUM.GetFormeIndex(species, form); + if (index == 0) + return LearnNONE; + + var lv = LevelUpUSUM[index].GetLevelLearnMove(move); + if (lv >= 0) + return new LearnVersion(lv, GameVersion.USUM); + + return GetIsLevelUp7SM(species, form, move); + } + + private static GameVersion GetDeoxysGameVersion3(int form) + { + switch (form) + { + case 0: return GameVersion.RS; + case 1: return GameVersion.FR; + case 2: return GameVersion.LG; + case 3: return GameVersion.E; + default: + return GameVersion.Invalid; + } + } + private static Learnset GetDeoxysLearn3(int form) + { + switch (form) + { + case 0: return LevelUpRS[386]; // Normal + case 1: return LevelUpFR[386]; // Attack + case 2: return LevelUpLG[386]; // Defense + case 3: return LevelUpE[386]; // Speed + default: return null; + } + } + + public static IEnumerable GetLevelUpMoves(PKM pkm, int species, int minlvlG1, int minlvlG2, int lvl, int form, GameVersion version, bool MoveReminder, int Generation) + { + switch (Generation) + { + case 1: return AddMovesLevelUp1(species, lvl, minlvlG1); + case 2: return AddMovesLevelUp2(species, lvl, minlvlG2, pkm.Korean, pkm.Format); + case 3: return AddMovesLevelUp3(species, lvl, form); + case 4: return AddMovesLevelUp4(species, lvl, form); + case 5: return AddMovesLevelUp5(species, lvl, form); + case 6: return AddMovesLevelUp6(species, lvl, form, version); + case 7: return AddMovesLevelUp7(species, lvl, form, version, MoveReminder); + } + return null; + } + + private static List AddMovesLevelUp1(int species, int max, int min) + { + List moves = new List(); + int index = PersonalTable.RB.GetFormeIndex(species, 0); + if (index == 0) + return moves; + + if (min == 1) + { + var pi_rb = (PersonalInfoG1)PersonalTable.RB[index]; + var pi_y = (PersonalInfoG1)PersonalTable.Y[index]; + moves.AddRange(pi_rb.Moves); + moves.AddRange(pi_y.Moves); + } + moves.AddRange(LevelUpRB[index].GetMoves(max, min)); + moves.AddRange(LevelUpY[index].GetMoves(max, min)); + return moves; + } + private static List AddMovesLevelUp2(int species, int max, int min, bool korean, int format) + { + var r = new List(); + int index = PersonalTable.C.GetFormeIndex(species, 0); + if (index == 0) + return r; + r.AddRange(LevelUpGS[index].GetMoves(max, min)); + if (!korean) + r.AddRange(LevelUpC[index].GetMoves(max, min)); + if (format == 1) //tradeback gen 2 -> gen 1 + r = r.Where(m => m <= MaxMoveID_1).ToList(); + return r; + } + private static List AddMovesLevelUp3(int species, int lvl, int form) + { + var moves = new List(); + if (species == 386) + { + var learn = GetDeoxysLearn3(form); + if (learn != null) + moves.AddRange(learn.GetMoves(lvl)); + return moves; + } + + int index = PersonalTable.E.GetFormeIndex(species, 0); + if (index == 0) + return moves; + + // Emerald level up table are equals to R/S level up tables + moves.AddRange(LevelUpE[index].GetMoves(lvl)); + // fire red and leaf green are equals between each other but different than RSE + // Do not use FR Levelup table. It have 67 moves for charmander but Leaf Green moves table is correct + moves.AddRange(LevelUpLG[index].GetMoves(lvl)); + return moves; + } + private static List AddMovesLevelUp4(int species, int lvl, int form) + { + var moves = new List(); + int index = PersonalTable.HGSS.GetFormeIndex(species, form); + if (index == 0) + return moves; + if (index < LevelUpDP.Length) + moves.AddRange(LevelUpDP[index].GetMoves(lvl)); + moves.AddRange(LevelUpPt[index].GetMoves(lvl)); + moves.AddRange(LevelUpHGSS[index].GetMoves(lvl)); + return moves; + } + private static List AddMovesLevelUp5(int species, int lvl, int form) + { + var moves = new List(); + int index1 = PersonalTable.BW.GetFormeIndex(species, form); + if (index1 != 0) + moves.AddRange(LevelUpBW[index1].GetMoves(lvl)); + + int index2 = PersonalTable.B2W2.GetFormeIndex(species, form); + if (index2 != 0) + moves.AddRange(LevelUpB2W2[index2].GetMoves(lvl)); + return moves; + } + private static List AddMovesLevelUp6(int species, int lvl, int form, GameVersion ver) + { + var moves = new List(); + switch (ver) + { + case GameVersion.Any: + AddMovesLevelUp6XY(moves, species, lvl, form); + AddMovesLevelUp6AO(moves, species, lvl, form); + break; + case GameVersion.X: + case GameVersion.Y: + case GameVersion.XY: + AddMovesLevelUp6XY(moves, species, lvl, form); + break; + case GameVersion.AS: + case GameVersion.OR: + case GameVersion.ORAS: + AddMovesLevelUp6AO(moves, species, lvl, form); + break; + } + return moves; + } + private static void AddMovesLevelUp6XY(List moves, int species, int lvl, int form) + { + int index = PersonalTable.XY.GetFormeIndex(species, form); + if (index == 0) + return; + moves.AddRange(LevelUpXY[index].GetMoves(lvl)); + } + private static void AddMovesLevelUp6AO(List moves, int species, int lvl, int form) + { + int index = PersonalTable.AO.GetFormeIndex(species, form); + if (index == 0) + return; + moves.AddRange(LevelUpAO[index].GetMoves(lvl)); + } + private static List AddMovesLevelUp7(int species, int lvl, int form, GameVersion ver, bool MoveReminder) + { + var moves = new List(); + switch (ver) + { + case GameVersion.Any: + AddMovesLevelUp7SM(moves, species, lvl, form, MoveReminder); + AddMovesLevelUp7USUM(moves, species, lvl, form, MoveReminder); + break; + case GameVersion.SN: + case GameVersion.MN: + case GameVersion.SM: + AddMovesLevelUp7SM(moves, species, lvl, form, MoveReminder); + break; + case GameVersion.US: + case GameVersion.UM: + case GameVersion.USUM: + AddMovesLevelUp7USUM(moves, species, lvl, form, MoveReminder); + break; + } + return moves; + } + private static void AddMovesLevelUp7SM(List moves, int species, int lvl, int form, bool MoveReminder) + { + if (species > MaxSpeciesID_7) + return; + int index = PersonalTable.SM.GetFormeIndex(species, form); + if (MoveReminder) + lvl = 100; // Move reminder can teach any level in movepool now! + + moves.AddRange(LevelUpSM[index].GetMoves(lvl)); + } + private static void AddMovesLevelUp7USUM(List moves, int species, int lvl, int form, bool MoveReminder) + { + if (species > MaxSpeciesID_7_USUM) + return; + int index = PersonalTable.USUM.GetFormeIndex(species, form); + if (MoveReminder) + lvl = 100; // Move reminder can teach any level in movepool now! + + moves.AddRange(LevelUpUSUM[index].GetMoves(lvl)); + } + } +} diff --git a/PKHeX.Core/Legality/Moves/MoveTechnicalMachine.cs b/PKHeX.Core/Legality/Moves/MoveTechnicalMachine.cs new file mode 100644 index 000000000..75103f72c --- /dev/null +++ b/PKHeX.Core/Legality/Moves/MoveTechnicalMachine.cs @@ -0,0 +1,307 @@ +using System.Collections.Generic; +using System.Linq; + +namespace PKHeX.Core +{ + internal static class MoveTechnicalMachine + { + internal static GameVersion GetIsMachineMove(PKM pkm, int species, int form, int generation, int move, GameVersion ver = GameVersion.Any, bool RemoveTransfer = false) + { + switch (generation) + { + case 1: return GetIsMachine1(species, move); + case 2: return GetIsMachine2(species, move); + case 3: return GetIsMachine3(species, move, pkm.Format, RemoveTransfer); + case 4: return GetIsMachine4(species, move, pkm.Format, RemoveTransfer, form); + case 5: return GetIsMachine5(species, move, form); + case 6: return GetIsMachine6(species, move, form, ver); + case 7: return GetIsMachine7(species, move, form, ver); + default: + return Legal.NONE; + } + } + + private static GameVersion GetIsMachine1(int species, int move) + { + for (int i = 0; i < Legal.TMHM_RBY.Length; i++) + if (Legal.TMHM_RBY[i] == move) + { + if (PersonalTable.RB[species].TMHM[i]) + return GameVersion.RB; + if (PersonalTable.Y[species].TMHM[i]) + return GameVersion.Y; + } + + return Legal.NONE; + } + private static GameVersion GetIsMachine2(int species, int move) + { + for (int i = 0; i < Legal.TMHM_GSC.Length; i++) + if (Legal.TMHM_GSC[i] == move && PersonalTable.C[species].TMHM[i]) + return GameVersion.GS; + + return Legal.NONE; + } + private static GameVersion GetIsMachine3(int species, int move, int format, bool RemoveTransfer) + { + for (int i = 0; i < Legal.TM_3.Length; i++) + if (Legal.TM_3[i] == move && PersonalTable.E[species].TMHM[i]) + return GameVersion.Gen3; + + if (!RemoveTransfer && format <= 3) + return GetIsMachine3HM(species, move); + + return Legal.NONE; + } + private static GameVersion GetIsMachine3HM(int species, int move) + { + int x = 0; + foreach (var m in Legal.HM_3) + { + if (m == move && PersonalTable.E[species].TMHM[x + 50]) + return GameVersion.Gen3; + x++; + } + return Legal.NONE; + } + private static GameVersion GetIsMachine4(int species, int move, int format, bool RemoveTransfer, int form) + { + for (int i = 0; i < Legal.TM_3.Length; i++) + if (Legal.TM_4[i] == move && PersonalTable.HGSS.GetFormeEntry(species, form).TMHM[i]) + return GameVersion.Gen4; + + if (RemoveTransfer && format > 4) + return GetIsMachine4HMTransfer(species, move, form); + + return GetIsMachine4HM(species, move, form); + } + private static GameVersion GetIsMachine4HMTransfer(int species, int move, int form) + { + // The combination of both these moves is illegal, it should be checked that the pokemon only learn one + // except if it can learn any of these moves in gen 5 or later + switch (move) + { + case 250: // Whirlpool + if (PersonalTable.HGSS.GetFormeEntry(species, form).TMHM[96]) + return GameVersion.HGSS; + break; + case 432: // Defog + if (PersonalTable.Pt.GetFormeEntry(species, form).TMHM[96]) + return GameVersion.DPPt; + break; + } + return Legal.NONE; + } + private static GameVersion GetIsMachine4HM(int species, int move, int form) + { + { + int i = 0; + foreach (var m in Legal.HM_DPPt) + { + if (m == move) + { + if (PersonalTable.Pt.GetFormeEntry(species, form).TMHM[i + 92]) + return GameVersion.DPPt; + break; + } + i++; + } + foreach (var m in Legal.HM_HGSS) + { + if (m == move) + { + if (PersonalTable.HGSS.GetFormeEntry(species, form).TMHM[i + 92]) + return GameVersion.HGSS; + break; + } + i++; + } + } + return Legal.NONE; + } + private static GameVersion GetIsMachine5(int species, int move, int form) + { + for (int i = 0; i < Legal.TMHM_BW.Length; i++) + if (Legal.TMHM_BW[i] == move) + return PersonalTable.B2W2.GetFormeEntry(species, form).TMHM[i] ? GameVersion.Gen5 : Legal.NONE; + return Legal.NONE; + } + private static GameVersion GetIsMachine6(int species, int move, int form, GameVersion ver) + { + if (GameVersion.XY.Contains(ver)) + for (int i = 0; i < Legal.TMHM_XY.Length; i++) + if (Legal.TMHM_XY[i] == move) + { + if (PersonalTable.XY.GetFormeEntry(species, form).TMHM[i]) + return GameVersion.XY; + break; + } + if (GameVersion.ORAS.Contains(ver)) + for (int i = 0; i < Legal.TMHM_AO.Length; i++) + if (Legal.TMHM_AO[i] == move) + { + if (PersonalTable.AO.GetFormeEntry(species, form).TMHM[i]) + return GameVersion.ORAS; + break; + } + return Legal.NONE; + } + private static GameVersion GetIsMachine7(int species, int move, int form, GameVersion ver) + { + if (GameVersion.SM.Contains(ver) && species <= Legal.MaxSpeciesID_7) + for (int i = 0; i < Legal.TMHM_XY.Length; i++) + if (Legal.TMHM_XY[i] == move) + { + if (PersonalTable.SM.GetFormeEntry(species, form).TMHM[i]) + return GameVersion.SM; + break; + } + if (GameVersion.ORAS.Contains(ver) && species <= Legal.MaxSpeciesID_7_USUM) + for (int i = 0; i < Legal.TMHM_SM.Length; i++) + if (Legal.TMHM_SM[i] == move) + { + if (PersonalTable.USUM.GetFormeEntry(species, form).TMHM[i]) + return GameVersion.USUM; + break; + } + return Legal.NONE; + } + + + internal static IEnumerable GetTMHM(PKM pkm, int species, int form, int generation, GameVersion ver = GameVersion.Any, bool RemoveTransfer = true) + { + List r = new List(); + + switch (generation) + { + case 1: AddMachine1(r, species); break; + case 2: AddMachine2(r, species); break; + case 3: AddMachine3(r, species, pkm.Format, RemoveTransfer); break; + case 4: AddMachine4(r, species, pkm.Format, RemoveTransfer, form); break; + case 5: AddMachine5(r, species, form); break; + case 6: AddMachine6(r, species, form, ver); break; + case 7: AddMachine7(r, species, form, ver); break; + } + return r; + } + + private static void AddMachine1(List r, int species) + { + int index = PersonalTable.RB.GetFormeIndex(species, 0); + if (index == 0) + return; + var pi_rb = (PersonalInfoG1)PersonalTable.RB[index]; + var pi_y = (PersonalInfoG1)PersonalTable.Y[index]; + r.AddRange(Legal.TMHM_RBY.Where((_, m) => pi_rb.TMHM[m])); + r.AddRange(Legal.TMHM_RBY.Where((_, m) => pi_y.TMHM[m])); + } + private static void AddMachine2(List r, int species) + { + int index = PersonalTable.C.GetFormeIndex(species, 0); + if (index == 0) + return; + var pi_c = PersonalTable.C[index]; + r.AddRange(Legal.TMHM_GSC.Where((_, m) => pi_c.TMHM[m])); + } + private static void AddMachine3(List r, int species, int format, bool RemoveTransfer) + { + int index = PersonalTable.E.GetFormeIndex(species, 0); + if (index == 0) + return; + var pi_c = PersonalTable.E[index]; + r.AddRange(Legal.TM_3.Where((_, m) => pi_c.TMHM[m])); + + if (!RemoveTransfer || format == 3) // HM moves must be removed for 3->4, only give if current format. + r.AddRange(Legal.HM_3.Where((_, m) => pi_c.TMHM[m + 50])); + else if (format > 3) //Remove HM + r.AddRange(Legal.HM_3.Where((_, m) => pi_c.TMHM[m + 50]).Except(Legal.HM_3)); + } + private static void AddMachine4(List r, int species, int format, bool RemoveTransfer, int form) + { + var pi_hgss = PersonalTable.HGSS.GetFormeEntry(species, form); + var pi_dppt = PersonalTable.Pt.GetFormeEntry(species, form); + r.AddRange(Legal.TM_4.Where((_, m) => pi_hgss.TMHM[m])); + + if (RemoveTransfer && format > 4) + { + // The combination of both these moves is illegal, it should be checked that the pokemon only learn one + // except if it can learn any of these moves in gen 5 or later + if (pi_hgss.TMHM[96]) + r.Add(250); // Whirlpool + if (pi_dppt.TMHM[96]) + r.Add(432); // Defog + } + else + { + r.AddRange(Legal.HM_DPPt.Where((_, m) => pi_dppt.TMHM[m + 92])); + r.AddRange(Legal.HM_HGSS.Where((_, m) => pi_hgss.TMHM[m + 92])); + } + } + private static void AddMachine5(List r, int species, int form) + { + var pi = PersonalTable.B2W2.GetFormeEntry(species, form); + r.AddRange(Legal.TMHM_BW.Where((_, m) => pi.TMHM[m])); + } + private static void AddMachine6(List r, int species, int form, GameVersion ver = GameVersion.Any) + { + switch (ver) + { + case GameVersion.Any: // Start at the top, hit every table + case GameVersion.X: + case GameVersion.Y: + case GameVersion.XY: + AddMachine6XY(r, species, form); + if (ver == GameVersion.Any) // Fall Through + AddMachine6AO(r, species, form); + break; + case GameVersion.AS: + case GameVersion.OR: + case GameVersion.ORAS: + AddMachine6AO(r, species, form); + break; + } + } + private static void AddMachine7(List r, int species, int form, GameVersion ver = GameVersion.Any) + { + switch (ver) + { + case GameVersion.SN: + case GameVersion.MN: + case GameVersion.SM: + if (species <= Legal.MaxSpeciesID_7) + AddMachineSM(r, species, form); + return; + case GameVersion.Any: + case GameVersion.US: + case GameVersion.UM: + case GameVersion.USUM: + AddMachineUSUM(r, species, form); + if (ver == GameVersion.Any) // Fall Through + AddMachineSM(r, species, form); + return; + } + } + private static void AddMachine6XY(List r, int species, int form) + { + var pi = PersonalTable.XY.GetFormeEntry(species, form); + r.AddRange(Legal.TMHM_XY.Where((_, m) => pi.TMHM[m])); + } + private static void AddMachine6AO(List r, int species, int form) + { + var pi = PersonalTable.AO.GetFormeEntry(species, form); + r.AddRange(Legal.TMHM_AO.Where((_, m) => pi.TMHM[m])); + } + private static void AddMachineSM(List r, int species, int form) + { + if (species > Legal.MaxSpeciesID_7) + return; + var pi = PersonalTable.SM.GetFormeEntry(species, form); + r.AddRange(Legal.TMHM_SM.Where((_, m) => pi.TMHM[m])); + } + private static void AddMachineUSUM(List r, int species, int form) + { + var pi = PersonalTable.USUM.GetFormeEntry(species, form); + r.AddRange(Legal.TMHM_SM.Where((_, m) => pi.TMHM[m])); + } + } +} \ No newline at end of file diff --git a/PKHeX.Core/Legality/Moves/MoveTutor.cs b/PKHeX.Core/Legality/Moves/MoveTutor.cs new file mode 100644 index 000000000..79db4bf00 --- /dev/null +++ b/PKHeX.Core/Legality/Moves/MoveTutor.cs @@ -0,0 +1,215 @@ +using System.Collections.Generic; +using System.Linq; + +using static PKHeX.Core.Legal; + +namespace PKHeX.Core +{ + public static class MoveTutor + { + public static GameVersion GetIsTutorMove(PKM pkm, int species, int form, int generation, int move, bool specialTutors = true) + { + switch (generation) + { + case 1: return GetIsTutor1(pkm, species, move); + case 2: return GetIsTutor2(pkm, species, move); + case 3: return GetIsTutor3(species, move); + case 4: return GetIsTutor4(species, form, move); + case 5: return GetIsTutor5(pkm, species, form, specialTutors, move); + case 6: return GetIsTutor6(pkm, species, form, specialTutors, move); + case 7: return GetIsTutor7(pkm, species, form, specialTutors, move); + default: + return NONE; + } + } + + private static GameVersion GetIsTutor1(PKM pkm, int species, int move) + { + // Surf Pikachu via Stadium + if (move != 57 || AllowGBCartEra) + return NONE; + if (pkm.Format < 3 && (species == 25 || species == 26)) + return GameVersion.Stadium; + return NONE; + } + private static GameVersion GetIsTutor2(PKM pkm, int species, int move) + { + if (!AllowGen2Crystal(pkm)) + return NONE; + var info = PersonalTable.C[species]; + for (int i = 0; i < Tutors_GSC.Length; i++) + { + if (Tutors_GSC[i] == move) + return info.TMHM[57 + i] ? GameVersion.C : NONE; + } + return GetIsTutor1(pkm, species, move); + } + private static GameVersion GetIsTutor3(int species, int move) + { + // E Tutors (Free) + // E Tutors (BP) + var info = PersonalTable.E[species]; + for (int i = 0; i < Tutor_E.Length; i++) + if (Tutor_E[i] == move && info.TypeTutors[i]) + return GameVersion.E; + + // FRLG Tutors + // Only special tutor moves, normal tutor moves are already included in Emerald data + for (int i = 0; i < SpecialTutors_FRLG.Length; i++) + { + if (Tutor_FRLG[i] == move && species == SpecialTutors_Compatibility_FRLG[i]) + return GameVersion.FRLG; + } + + // XD + for (int i = 0; i < SpecialTutors_XD_Exclusive.Length; i++) + { + if (SpecialTutors_XD_Exclusive[i] == move && SpecialTutors_Compatibility_XD_Exclusive[i].Any(e => e == species)) + return GameVersion.XD; + } + + // XD (Mew) + if (species == 151 && Tutor_3Mew.Contains(move)) + return GameVersion.XD; + + return NONE; + } + private static GameVersion GetIsTutor4(int species, int form, int move) + { + var pi = PersonalTable.HGSS.GetFormeEntry(species, form); + for (int i = 0; i < Tutors_4.Length; i++) + if (Tutors_4[i] == move && pi.TypeTutors[i]) + return GameVersion.Gen4; + + for (int i = 0; i < SpecialTutors_4.Length; i++) + if (SpecialTutors_4[i] == move && SpecialTutors_Compatibility_4[i].Any(e => e == species)) + return GameVersion.HGSS; + + return NONE; + } + private static GameVersion GetIsTutor5(PKM pkm, int species, int form, bool specialTutors, int move) + { + var pi = PersonalTable.B2W2.GetFormeEntry(species, form); + for (int i = 0; i < TypeTutor6.Length; i++) + if (TypeTutor6[i] == move && pi.TypeTutors[i]) + return GameVersion.Gen5; + + if (specialTutors && pkm.HasVisitedB2W2()) + for (int i = 0; i < Tutors_B2W2.Length; i++) + for (int j = 0; j < Tutors_B2W2[i].Length; j++) + if (Tutors_B2W2[i][j] == move && pi.SpecialTutors[i][j]) + return GameVersion.B2W2; + + return NONE; + } + private static GameVersion GetIsTutor6(PKM pkm, int species, int form, bool specialTutors, int move) + { + var pi = PersonalTable.AO.GetFormeEntry(species, form); + for (int i = 0; i < TypeTutor6.Length; i++) + if (TypeTutor6[i] == move && pi.TypeTutors[i]) + return GameVersion.Gen6; + + if (specialTutors && pkm.HasVisitedORAS()) + for (int i = 0; i < Tutors_AO.Length; i++) + for (int j = 0; j < Tutors_AO[i].Length; j++) + if (Tutors_AO[i][j] == move && pi.SpecialTutors[i][j]) + return GameVersion.ORAS; + + return NONE; + } + private static GameVersion GetIsTutor7(PKM pkm, int species, int form, bool specialTutors, int move) + { + var pi = PersonalTable.USUM.GetFormeEntry(species, form); + for (int i = 0; i < TypeTutor6.Length; i++) + if (TypeTutor6[i] == move && pi.TypeTutors[i]) + return GameVersion.Gen7; + + if (specialTutors && pkm.HasVisitedUSUM()) + for (int i = 0; i < Tutors_USUM.Length; i++) + if (Tutors_USUM[i] == move && pi.SpecialTutors[0][i]) + return GameVersion.USUM; + + return NONE; + } + + public static IEnumerable GetTutorMoves(PKM pkm, int species, int form, bool specialTutors, int generation) + { + List moves = new List(); + switch (generation) + { + case 1: AddMovesTutor1(moves, species, pkm.Format); break; + case 2: AddMovesTutor2(moves, species, pkm.Format, pkm.Korean); break; + case 3: AddMovesTutor3(moves, species); break; + case 4: AddMovesTutor4(moves, species, form); break; + case 5: AddMovesTutor5(moves, species, form, pkm, specialTutors); break; + case 6: AddMovesTutor6(moves, species, form, pkm, specialTutors); break; + case 7: AddMovesTutor7(moves, species, form, pkm, specialTutors); break; + } + return moves.Distinct(); + } + + private static void AddMovesTutor1(List moves, int species, int format) + { + if (AllowGBCartEra && format < 3 && (species == 25 || species == 26)) // Surf Pikachu via Stadium + moves.Add(57); + } + private static void AddMovesTutor2(List moves, int species, int format, bool korean = false) + { + if (korean) + return; + var pi = PersonalTable.C[species]; + moves.AddRange(Tutors_GSC.Where((_, i) => pi.TMHM[57 + i])); + AddMovesTutor1(moves, species, format); + } + private static void AddMovesTutor3(List moves, int species) + { + // E Tutors (Free) + // E Tutors (BP) + var pi = PersonalTable.E[species]; + moves.AddRange(Tutor_E.Where((_, i) => pi.TypeTutors[i])); + // FRLG Tutors + // Only special tutor moves, normal tutor moves are already included in Emerald data + moves.AddRange(SpecialTutors_FRLG.Where((_, i) => SpecialTutors_Compatibility_FRLG[i] == species)); + // XD + moves.AddRange(SpecialTutors_XD_Exclusive.Where((_, i) => SpecialTutors_Compatibility_XD_Exclusive[i].Any(e => e == species))); + // XD (Mew) + if (species == 151) + moves.AddRange(Tutor_3Mew); + } + private static void AddMovesTutor4(List moves, int species, int form) + { + var pi = PersonalTable.HGSS.GetFormeEntry(species, form); + moves.AddRange(Tutors_4.Where((_, i) => pi.TypeTutors[i])); + moves.AddRange(SpecialTutors_4.Where((_, i) => SpecialTutors_Compatibility_4[i].Any(e => e == species))); + } + private static void AddMovesTutor5(List moves, int species, int form, PKM pkm, bool specialTutors) + { + var pi = PersonalTable.B2W2[species]; + moves.AddRange(TypeTutor6.Where((_, i) => pi.TypeTutors[i])); + if (pkm.InhabitedGeneration(5) && specialTutors) + moves.AddRange(GetTutors(PersonalTable.B2W2.GetFormeEntry(species, form), Tutors_B2W2)); + } + private static void AddMovesTutor6(List moves, int species, int form, PKM pkm, bool specialTutors) + { + var pi = PersonalTable.AO[species]; + moves.AddRange(TypeTutor6.Where((_, i) => pi.TypeTutors[i])); + if (specialTutors && pkm.HasVisitedORAS()) + moves.AddRange(GetTutors(PersonalTable.AO.GetFormeEntry(species, form), Tutors_AO)); + } + private static void AddMovesTutor7(List moves, int species, int form, PKM pkm, bool specialTutors) + { + var pi = PersonalTable.USUM.GetFormeEntry(species, form); + moves.AddRange(TypeTutor6.Where((_, i) => pi.TypeTutors[i])); + if (specialTutors && pkm.HasVisitedUSUM()) + moves.AddRange(GetTutors(PersonalTable.USUM.GetFormeEntry(species, form), Tutors_USUM)); + } + + private static IEnumerable GetTutors(PersonalInfo pi, params int[][] tutors) + { + for (int i = 0; i < tutors.Length; i++) + for (int b = 0; b < tutors[i].Length; b++) + if (pi.SpecialTutors[i][b]) + yield return tutors[i][b]; + } + } +} diff --git a/PKHeX.Core/Legality/Tables3.cs b/PKHeX.Core/Legality/Tables3.cs index 230ef66e1..0c021c638 100644 --- a/PKHeX.Core/Legality/Tables3.cs +++ b/PKHeX.Core/Legality/Tables3.cs @@ -186,12 +186,7 @@ public static partial class Legal 307, 308, 338 }; - internal static readonly int[][] SpecialTutors_Compatibility_FRLG = - { - new[] { 6 }, - new[] { 9 }, - new[] { 3 }, - }; + internal static readonly int[] SpecialTutors_Compatibility_FRLG = { 6, 9, 3 }; // Tutor moves from XD that can be learned as tutor moves in emerald // For this moves compatibility data is the same in XD and Emerald @@ -230,7 +225,7 @@ public static partial class Legal internal static readonly HashSet ValidEggMet_RSE = new HashSet { - 32, //Route 117 + 32, //Route 117 253, //Ingame egg gift 255 // event/pokemon box }; diff --git a/PKHeX.Core/Legality/Tables7.cs b/PKHeX.Core/Legality/Tables7.cs index 73ce34334..7793b06e0 100644 --- a/PKHeX.Core/Legality/Tables7.cs +++ b/PKHeX.Core/Legality/Tables7.cs @@ -486,7 +486,7 @@ public static partial class Legal 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, }; - private static readonly int[] TMHM_SM = + internal static readonly int[] TMHM_SM = { 526, 337, 473, 347, 046, 092, 258, 339, 474, 237, 241, 269, 058, 059, 063, 113, 182, 240, 355, 219,