using System; using System.Collections.Generic; using static PKHeX.Core.Legal; using static PKHeX.Core.GameVersion; namespace PKHeX.Core { public static class MoveLevelUp { private static readonly LearnLookup LearnSWSH = new(PersonalTable.SWSH, LevelUpSWSH, SWSH), LearnSM = new(PersonalTable.SM, LevelUpSM, SM), LearnUSUM = new(PersonalTable.USUM, LevelUpUSUM, USUM), LearnGG = new(PersonalTable.GG, LevelUpGG, Gen7b), LearnXY = new(PersonalTable.XY, LevelUpXY, XY), LearnAO = new(PersonalTable.AO, LevelUpAO, ORAS), LearnBW = new(PersonalTable.BW, LevelUpBW, BW), LearnB2W2 = new(PersonalTable.B2W2, LevelUpB2W2, B2W2), LearnDP = new(PersonalTable.DP, LevelUpDP, DP), LearnPt = new(PersonalTable.Pt, LevelUpPt, Pt), LearnHGSS = new(PersonalTable.HGSS, LevelUpHGSS, HGSS), LearnRSE = new(PersonalTable.RS, LevelUpRS, RSE), LearnFRLG = new(PersonalTable.LG, LevelUpLG, FRLG), LearnGS = new(PersonalTable.GS, LevelUpGS, GS), LearnC = new(PersonalTable.C, LevelUpC, C), LearnRB = new(PersonalTable.RB, LevelUpRB, RB), LearnY = new(PersonalTable.Y, LevelUpY, YW); public static LearnVersion GetIsLevelUpMove(PKM pkm, int species, int form, int maxLevel, int generation, int move, int minlvlG1, int minlvlG2, GameVersion version = Any) { if (pkm.IsMovesetRestricted(generation)) version = (GameVersion)pkm.Version; return generation switch { 1 => GetIsLevelUp1(species, form, move, maxLevel, minlvlG1, version), 2 when move > MaxMoveID_1 && pkm.LearnMovesNew2Disallowed() => LearnNONE, 2 => GetIsLevelUp2(species, form, move, maxLevel, minlvlG2, version, pkm.Korean), 3 => GetIsLevelUp3(species, form, move, maxLevel, version), 4 => GetIsLevelUp4(species, form, move, maxLevel, version), 5 => GetIsLevelUp5(species, form, move, maxLevel, version), 6 => GetIsLevelUp6(species, form, move, maxLevel, version), 7 => GetIsLevelUp7(species, form, move, version), // move reminder can give any move 1-100 8 => GetIsLevelUp8(species, form, move, maxLevel, version), _ => LearnNONE }; } internal static LearnVersion GetIsLevelUp1(int species, int form, int move, int maxLevel, int minLevel, GameVersion ver = Any) { if (move > MaxMoveID_1) return LearnNONE; switch (ver) { case Any: case RBY: var first = LearnRB.GetIsLevelUpG1(species, form, move, maxLevel, minLevel); var second = LearnY.GetIsLevelUpG1(species, form, move, maxLevel, minLevel); if (!first.IsLevelUp) return second; if (!second.IsLevelUp) return first; return first.Level > second.Level ? second : first; case RD or BU or GN or RB: return LearnRB.GetIsLevelUpG1(species, form, move, maxLevel, minLevel); case YW: return LearnY.GetIsLevelUpG1(species, form, move, maxLevel, minLevel); } return LearnNONE; } private static LearnVersion GetIsLevelUp2(int species, int form, int move, int maxLevel, int minLevel, GameVersion ver = Any, bool korean = false) { // No Korean Crystal switch (ver) { case Any: case GSC: var first = LearnGS.GetIsLevelUpMin(species, move, maxLevel, minLevel, form); if (first.IsLevelUp || korean) return first; return LearnC.GetIsLevelUpMin(species, move, maxLevel, minLevel, form); case GD or SV or GS: return LearnGS.GetIsLevelUpMin(species, move, maxLevel, minLevel, form); case C when !korean: return LearnC.GetIsLevelUpMin(species, move, maxLevel, minLevel, form); } return LearnNONE; } private static LearnVersion GetIsLevelUp3(int species, int form, int move, int lvlLevel, GameVersion ver = Any) { if (species == (int)Species.Deoxys) return GetIsLevelUp3Deoxys(form, move, lvlLevel); // Emerald level up tables are equal to R/S level up tables switch (ver) { case Any: var first = LearnRSE.GetIsLevelUp(species, form, move, lvlLevel); if (first.IsLevelUp) return first; return LearnFRLG.GetIsLevelUp(species, form, move, lvlLevel); case R or S or E or RS or RSE: return LearnRSE.GetIsLevelUp(species, form, move, lvlLevel); case FR or LG or FRLG: return LearnFRLG.GetIsLevelUp(species, form, move, lvlLevel); } return LearnNONE; } private static LearnVersion GetIsLevelUp4(int species, int form, int move, int maxLevel, GameVersion ver = Any) { switch (ver) { case Any: case DPPt: var first = LearnDP.GetIsLevelUp(species, form, move, maxLevel); if (first.IsLevelUp) return first; var second = LearnPt.GetIsLevelUp(species, form, move, maxLevel); if (second.IsLevelUp) return second; if (ver == DPPt) // stop here return LearnNONE; return LearnHGSS.GetIsLevelUp(species, form, move, maxLevel); case D or P or DP: return LearnDP.GetIsLevelUp(species, form, move, maxLevel); case Pt: return LearnPt.GetIsLevelUp(species, form, move, maxLevel); case HG or SS or HGSS: return LearnHGSS.GetIsLevelUp(species, form, move, maxLevel); } return LearnNONE; } private static LearnVersion GetIsLevelUp5(int species, int form, int move, int maxLevel, GameVersion ver = Any) { switch (ver) { case Any: var first = LearnBW.GetIsLevelUp(species, form, move, maxLevel); if (first.IsLevelUp && species != 646) // Kyurem moves are same for both versions, but form movepool not present. return first; return LearnB2W2.GetIsLevelUp(species, form, move, maxLevel); case B or W or BW: return LearnBW.GetIsLevelUp(species, form, move, maxLevel); case B2 or W2 or B2W2: return LearnB2W2.GetIsLevelUp(species, form, move, maxLevel); } return LearnNONE; } private static LearnVersion GetIsLevelUp6(int species, int form, int move, int maxLevel, GameVersion ver = Any) { switch (ver) { case Any: var first = LearnXY.GetIsLevelUp(species, form, move, maxLevel); if (first.IsLevelUp) return first; return LearnAO.GetIsLevelUp(species, form, move, maxLevel); case X or Y or XY: return LearnXY.GetIsLevelUp(species, form, move, maxLevel); case OR or AS or ORAS: return LearnAO.GetIsLevelUp(species, form, move, maxLevel); } return LearnNONE; } private static LearnVersion GetIsLevelUp7(int species, int form, int move, GameVersion ver = Any) { switch (ver) { case GP or GE or GG or GO: return LearnGG.GetIsLevelUp(species, form, move); case Any: if (species > MaxSpeciesID_7_USUM) return LearnNONE; var first = LearnUSUM.GetIsLevelUp(species, form, move); if (first.IsLevelUp) return first; if (species > MaxSpeciesID_7) return LearnNONE; return LearnSM.GetIsLevelUp(species, form, move); case SN or MN or SM: if (species > MaxSpeciesID_7) return LearnNONE; return LearnSM.GetIsLevelUp(species, form, move); case US or UM or USUM: if (species > MaxSpeciesID_7_USUM) return LearnNONE; return LearnUSUM.GetIsLevelUp(species, form, move); } return LearnNONE; } private static LearnVersion GetIsLevelUp8(int species, int form, int move, int maxLevel, GameVersion ver = Any) { switch (ver) { case Any: case GO: case SW or SH or SWSH: if (species > MaxSpeciesID_8) return LearnNONE; return LearnSWSH.GetIsLevelUp(species, form, move, maxLevel); } 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 GameVersion GetDeoxysGameVersion3(int form) => form switch { 0 => RS, 1 => FR, 2 => LG, 3 => E, _ => Invalid }; private static Learnset? GetDeoxysLearn3(int form, GameVersion ver = Any) { const int index = (int)Species.Deoxys; if (ver == Any) { return form switch { 0 => LevelUpRS[index], // Normal 1 => LevelUpFR[index], // Attack 2 => LevelUpLG[index], // Defense 3 => LevelUpE[index], // Speed _ => null, }; } var gen = ver.GetGeneration(); if (gen != 3) return GetDeoxysLearn3(form); return GameData.GetLearnsets(ver)[index]; } public static IEnumerable GetMovesLevelUp(PKM pkm, int species, int form, int maxLevel, int minlvlG1, int minlvlG2, GameVersion version, bool MoveReminder, int generation) { if (pkm.IsMovesetRestricted(generation)) version = (GameVersion)pkm.Version; return generation switch { 1 => GetMovesLevelUp1(species, form, maxLevel, minlvlG1, version), 2 => GetMovesLevelUp2(species, form, maxLevel, minlvlG2, pkm.Korean, pkm.LearnMovesNew2Disallowed(), version), 3 => GetMovesLevelUp3(species, form, maxLevel, version), 4 => GetMovesLevelUp4(species, form, maxLevel, version), 5 => GetMovesLevelUp5(species, form, maxLevel, version), 6 => GetMovesLevelUp6(species, form, maxLevel, version), 7 => GetMovesLevelUp7(species, form, maxLevel, MoveReminder, version), 8 => GetMovesLevelUp8(species, form, maxLevel, version), _ => Array.Empty() }; } private static bool LearnMovesNew2Disallowed(this PKM pkm) => pkm.Format == 1 || (pkm.Format >= 7 && pkm.VC1); internal static List GetMovesLevelUp1(int species, int form, int maxLevel, int minLevel, GameVersion ver = Any) { return AddMovesLevelUp1(new List(), ver, species, form, maxLevel, minLevel); } private static List GetMovesLevelUp2(int species, int form, int maxLevel, int minLevel, bool korean, bool removeNewGSCMoves, GameVersion ver = Any) { var moves = AddMovesLevelUp2(new List(), ver, species, form, maxLevel, minLevel, korean); if (removeNewGSCMoves) moves.RemoveAll(m => m > MaxMoveID_1); return moves; } private static List GetMovesLevelUp3(int species, int form, int maxLevel, GameVersion ver = Any) { return AddMovesLevelUp3(new List(), ver, species, form, maxLevel); } private static List GetMovesLevelUp4(int species, int form, int maxLevel, GameVersion ver = Any) { return AddMovesLevelUp4(new List(), ver, species, form, maxLevel); } private static List GetMovesLevelUp5(int species, int form, int maxLevel, GameVersion ver = Any) { return AddMovesLevelUp5(new List(), ver, species, form, maxLevel); } private static List GetMovesLevelUp6(int species, int form, int maxLevel, GameVersion ver = Any) { return AddMovesLevelUp6(new List(), ver, species, form, maxLevel); } private static List GetMovesLevelUp7(int species, int form, int maxLevel, bool MoveReminder, GameVersion ver = Any) { return AddMovesLevelUp7(new List(), ver, species, form, maxLevel, MoveReminder); } private static List GetMovesLevelUp8(int species, int form, int maxLevel, GameVersion ver = Any) { return AddMovesLevelUp8(new List(), ver, species, form, maxLevel); } private static List AddMovesLevelUp1(List moves, GameVersion ver, int species, int form, int max, int min) { switch (ver) { case Any: case RBY: LearnRB.AddMoves1(moves, species, form, max, min); return LearnY.AddMoves1(moves, species, form, max, min); case RD or BU or GN or RB: return LearnRB.AddMoves1(moves, species, form, max, min); case YW: return LearnY.AddMoves1(moves, species, form, max, min); } return moves; } private static List AddMovesLevelUp2(List moves, GameVersion ver, int species, int form, int max, int min, bool korean) { switch (ver) { case Any: case GSC: LearnGS.AddMoves(moves, species, form, max, min); if (korean) return moves; return LearnC.AddMoves(moves, species, form, max, min); case GD or SV or GS: return LearnGS.AddMoves(moves, species, form, max, min); case C when !korean: return LearnC.AddMoves(moves, species, form, max, min); } return moves; } private static List AddMovesLevelUp3(List moves, GameVersion ver, int species, int form, int maxLevel) { if (species == (int)Species.Deoxys) { var learn = GetDeoxysLearn3(form, ver); if (learn != null) moves.AddRange(learn.GetMoves(maxLevel)); return moves; } // Emerald level up tables are equal to R/S level up tables switch (ver) { case Any: LearnRSE.AddMoves(moves, species, form, maxLevel); return LearnFRLG.AddMoves(moves, species, form, maxLevel); case R or S or E or RS or RSE: return LearnRSE.AddMoves(moves, species, form, maxLevel); case FR or LG or FRLG: return LearnFRLG.AddMoves(moves, species, form, maxLevel); } return moves; } private static List AddMovesLevelUp4(List moves, GameVersion ver, int species, int form, int maxLevel) { switch (ver) { case Any: case DPPt: LearnDP.AddMoves(moves, species, form, maxLevel); LearnPt.AddMoves(moves, species, form, maxLevel); if (ver == DPPt) // stop here return moves; return LearnHGSS.AddMoves(moves, species, form, maxLevel); case D or P or DP: return LearnDP.AddMoves(moves, species, form, maxLevel); case Pt: return LearnPt.AddMoves(moves, species, form, maxLevel); case HG or SS or HGSS: return LearnHGSS.AddMoves(moves, species, form, maxLevel); } return moves; } private static List AddMovesLevelUp5(List moves, GameVersion ver, int species, int form, int maxLevel) { switch (ver) { case Any: if (species != 646) // Kyurem moves are same for both versions, but form movepool not present. LearnBW.AddMoves(moves, species, form, maxLevel); return LearnB2W2.AddMoves(moves, species, form, maxLevel); case B or W or BW: return LearnBW.AddMoves(moves, species, form, maxLevel); case B2 or W2 or B2W2: return LearnB2W2.AddMoves(moves, species, form, maxLevel); } return moves; } private static List AddMovesLevelUp6(List moves, GameVersion ver, int species, int form, int maxLevel) { switch (ver) { case Any: LearnXY.AddMoves(moves, species, form, maxLevel); return LearnAO.AddMoves(moves, species, form, maxLevel); case X or Y or XY: return LearnXY.AddMoves(moves, species, form, maxLevel); case AS or OR or ORAS: return LearnAO.AddMoves(moves, species, form, maxLevel); } return moves; } private static List AddMovesLevelUp7(List moves, GameVersion ver, int species, int form, int maxLevel, bool reminder) { if (reminder) maxLevel = 100; // Move reminder can teach any level in movepool now! switch (ver) { case GP or GE or GG or GO: return LearnGG.AddMoves(moves, species, form, maxLevel); case Any: if (species > MaxSpeciesID_7_USUM) return moves; LearnUSUM.AddMoves(moves, species, form, maxLevel); if (species > MaxSpeciesID_7) return moves; return LearnSM.AddMoves(moves, species, form, maxLevel); case SN or MN or SM: if (species > MaxSpeciesID_7) return moves; return LearnSM.AddMoves(moves, species, form, maxLevel); case US or UM or USUM: if (species > MaxSpeciesID_7_USUM) return moves; LearnUSUM.AddMoves(moves, species, form, maxLevel); break; } return moves; } private static List AddMovesLevelUp8(List moves, GameVersion ver, int species, int form, int maxLevel) { // Move reminder can NOT teach any level like Gen7 switch (ver) { case Any: case GO: case SW or SH or SWSH: if (species > MaxSpeciesID_8) return moves; return LearnSWSH.AddMoves(moves, species, form, maxLevel); } return moves; } public static int[] GetEncounterMoves(PKM pk, int level, GameVersion version) { if (version <= 0) version = (GameVersion)pk.Version; return GetEncounterMoves(pk.Species, pk.Form, level, version); } private static int[] GetEncounterMoves1(int species, int level, GameVersion version) { var learn = GameData.GetLearnsets(version); var table = GameData.GetPersonal(version); var index = table.GetFormIndex(species, 0); var lvl0 = (int[])((PersonalInfoG1) table[index]).Moves.Clone(); int start = Math.Max(0, Array.IndexOf(lvl0, 0)); return learn[index].GetEncounterMoves(level, lvl0, start); } private static int[] GetEncounterMoves2(int species, int level, GameVersion version) { var learn = GameData.GetLearnsets(version); var table = GameData.GetPersonal(version); var index = table.GetFormIndex(species, 0); var lvl0 = learn[species].GetEncounterMoves(1); int start = Math.Max(0, Array.IndexOf(lvl0, 0)); return learn[index].GetEncounterMoves(level, lvl0, start); } public static int[] GetEncounterMoves(int species, int form, int level, GameVersion version) { if (RBY.Contains(version)) return GetEncounterMoves1(species, level, version); if (GSC.Contains(version)) return GetEncounterMoves2(species, level, version); var learn = GameData.GetLearnsets(version); var table = GameData.GetPersonal(version); var index = table.GetFormIndex(species, form); return learn[index].GetEncounterMoves(level); } } }