Refactoring

pull out some move data fetching to separate class
add methods to quickly fetch if the move is learnable via that method

eventual plan is this:
replace
'fetch all possible moves then look within' move validation
with:
'peek if learnable, and get info how' move validation

advantages:
- returns game the move was learned in and the level (if appropriate)
- infinitely less object creation (garbage collection)
- only looks for a given move, doesn't have to fetch everything before
checking
- faster than full fetch, can be made even faster by optimizing lookups.

subject to change :)
This commit is contained in:
Kurt
2018-06-08 20:10:41 -07:00
parent b1cd68e3f3
commit 9b43677bb9
8 changed files with 994 additions and 185 deletions

View File

@@ -1982,186 +1982,11 @@ private static int[] GetEggMoves(int gen, int species, int formnum, GameVersion
internal static IEnumerable<int> GetTMHM(PKM pkm, int species, int form, int generation, GameVersion Version = GameVersion.Any, bool RemoveTransferHM = true)
{
List<int> moves = new List<int>();
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<int> GetTutorMoves(PKM pkm, int species, int form, bool specialTutors, int generation)
{
List<int> moves = new List<int>();
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<int> 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);

View File

@@ -84,5 +84,23 @@ public int GetLevelLearnMove(int move)
{
return (Learn ?? (Learn = GetDictionary())).TryGetValue(move, out var level) ? level : -1;
}
/// <summary>Returns the level that a Pokémon can learn the specified move.</summary>
/// <param name="move">Move ID</param>
/// <param name="min">Minimum level to start looking at.</param>
/// <returns>Level the move is learned at. If the result is below 0, it cannot be learned by levelup.</returns>
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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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<int> 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<int> AddMovesLevelUp1(int species, int max, int min)
{
List<int> moves = new List<int>();
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<int> AddMovesLevelUp2(int species, int max, int min, bool korean, int format)
{
var r = new List<int>();
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<int> AddMovesLevelUp3(int species, int lvl, int form)
{
var moves = new List<int>();
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<int> AddMovesLevelUp4(int species, int lvl, int form)
{
var moves = new List<int>();
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<int> AddMovesLevelUp5(int species, int lvl, int form)
{
var moves = new List<int>();
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<int> AddMovesLevelUp6(int species, int lvl, int form, GameVersion ver)
{
var moves = new List<int>();
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<int> 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<int> 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<int> AddMovesLevelUp7(int species, int lvl, int form, GameVersion ver, bool MoveReminder)
{
var moves = new List<int>();
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<int> 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<int> 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));
}
}
}

View File

@@ -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<int> GetTMHM(PKM pkm, int species, int form, int generation, GameVersion ver = GameVersion.Any, bool RemoveTransfer = true)
{
List<int> r = new List<int>();
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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> r, int species, int form)
{
var pi = PersonalTable.USUM.GetFormeEntry(species, form);
r.AddRange(Legal.TMHM_SM.Where((_, m) => pi.TMHM[m]));
}
}
}

View File

@@ -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<int> GetTutorMoves(PKM pkm, int species, int form, bool specialTutors, int generation)
{
List<int> moves = new List<int>();
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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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];
}
}
}

View File

@@ -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<int> ValidEggMet_RSE = new HashSet<int>
{
32, //Route 117
32, //Route 117
253, //Ingame egg gift
255 // event/pokemon box
};

View File

@@ -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,