PKHeX/PKHeX.Core/Legality/Moves/MoveLevelUp.cs

544 lines
23 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using static PKHeX.Core.Legal;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core
{
public static class MoveLevelUp
{
2018-11-12 06:49:17 +00:00
private static readonly LearnLookup
LearnLA = new(PersonalTable.LA, LevelUpLA, PLA),
LearnBDSP = new(PersonalTable.BDSP, LevelUpBDSP, BDSP),
2020-12-22 01:17:56 +00:00
LearnSWSH = new(PersonalTable.SWSH, LevelUpSWSH, SWSH),
LearnSM = new(PersonalTable.SM, LevelUpSM, SM),
2020-12-22 01:17:56 +00:00
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),
2020-12-22 01:17:56 +00:00
LearnB2W2 = new(PersonalTable.B2W2, LevelUpB2W2, B2W2),
LearnDP = new(PersonalTable.DP, LevelUpDP, DP),
LearnPt = new(PersonalTable.Pt, LevelUpPt, Pt),
2020-12-22 01:17:56 +00:00
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;
2018-11-12 06:49:17 +00:00
2020-12-24 01:14:38 +00:00
return generation switch
{
1 => GetIsLevelUp1(species, form, move, maxLevel, minlvlG1, version),
2020-12-24 01:14:38 +00:00
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),
2021-08-20 20:49:20 +00:00
_ => LearnNONE,
2020-12-24 01:14:38 +00:00
};
}
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 SI 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, ver);
// 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);
2021-07-26 21:14:39 +00:00
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:
2018-11-12 06:49:17 +00:00
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);
case PLA:
if (species > MaxSpeciesID_8a)
return LearnNONE;
return LearnLA.GetIsLevelUp(species, form, move, maxLevel);
case BD or SP or BDSP:
if (species > MaxSpeciesID_8b)
return LearnNONE;
return LearnBDSP.GetIsLevelUp(species, form, move, maxLevel);
}
return LearnNONE;
}
private static LearnVersion GetIsLevelUp3Deoxys(int form, int move, int lvl, GameVersion ver = Any)
{
var moveset = GetDeoxysLearn3(form, ver);
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,
2021-08-20 20:49:20 +00:00
_ => Invalid,
};
PKHeX.Core Nullable cleanup (#2401) * Handle some nullable cases Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data) Make some classes have explicit constructors instead of { } initialization * Handle bits more obviously without null * Make SaveFile.BAK explicitly readonly again * merge constructor methods to have readonly fields * Inline some properties * More nullable handling * Rearrange box actions define straightforward classes to not have any null properties * Make extrabyte reference array immutable * Move tooltip creation to designer * Rearrange some logic to reduce nesting * Cache generated fonts * Split mystery gift album purpose * Handle more tooltips * Disallow null setters * Don't capture RNG object, only type enum * Unify learnset objects Now have readonly properties which are never null don't new() empty learnsets (>800 Learnset objects no longer created, total of 2400 objects since we also new() a move & level array) optimize g1/2 reader for early abort case * Access rewrite Initialize blocks in a separate object, and get via that object removes a couple hundred "might be null" warnings since blocks are now readonly getters some block references have been relocated, but interfaces should expose all that's needed put HoF6 controls in a groupbox, and disable * Readonly personal data * IVs non nullable for mystery gift * Explicitly initialize forced encounter moves * Make shadow objects readonly & non-null Put murkrow fix in binary data resource, instead of on startup * Assign dex form fetch on constructor Fixes legality parsing edge cases also handle cxd parse for valid; exit before exception is thrown in FrameGenerator * Remove unnecessary null checks * Keep empty value until init SetPouch sets the value to an actual one during load, but whatever * Readonly team lock data * Readonly locks Put locked encounters at bottom (favor unlocked) * Mail readonly data / offset Rearrange some call flow and pass defaults Add fake classes for SaveDataEditor mocking Always party size, no need to check twice in stat editor use a fake save file as initial data for savedata editor, and for gamedata (wow i found a usage) constrain eventwork editor to struct variable types (uint, int, etc), thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
private static Learnset? GetDeoxysLearn3(int form, GameVersion ver = Any)
{
2019-06-01 17:22:49 +00:00
const int index = (int)Species.Deoxys;
if (ver != Any && Gen3.Contains(ver))
return GameData.GetLearnsets(ver)[index];
return form switch
{
0 => LevelUpRS[index], // Normal
1 => LevelUpFR[index], // Attack
2 => LevelUpLG[index], // Defense
3 => LevelUpE[index], // Speed
_ => null,
};
}
public static IEnumerable<int> 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),
2021-08-20 20:49:20 +00:00
_ => Array.Empty<int>(),
};
}
private static bool LearnMovesNew2Disallowed(this PKM pkm) => pkm.Format == 1 || (pkm.Format >= 7 && pkm.VC1);
internal static List<int> GetMovesLevelUp1(int species, int form, int maxLevel, int minLevel, GameVersion ver = Any)
{
return AddMovesLevelUp1(new List<int>(), ver, species, form, maxLevel, minLevel);
}
private static List<int> GetMovesLevelUp2(int species, int form, int maxLevel, int minLevel, bool korean, bool removeNewGSCMoves, GameVersion ver = Any)
{
var moves = AddMovesLevelUp2(new List<int>(), ver, species, form, maxLevel, minLevel, korean);
if (removeNewGSCMoves)
moves.RemoveAll(m => m > MaxMoveID_1);
return moves;
}
private static List<int> GetMovesLevelUp3(int species, int form, int maxLevel, GameVersion ver = Any)
{
return AddMovesLevelUp3(new List<int>(), ver, species, form, maxLevel);
}
private static List<int> GetMovesLevelUp4(int species, int form, int maxLevel, GameVersion ver = Any)
{
return AddMovesLevelUp4(new List<int>(), ver, species, form, maxLevel);
}
private static List<int> GetMovesLevelUp5(int species, int form, int maxLevel, GameVersion ver = Any)
{
return AddMovesLevelUp5(new List<int>(), ver, species, form, maxLevel);
}
private static List<int> GetMovesLevelUp6(int species, int form, int maxLevel, GameVersion ver = Any)
{
return AddMovesLevelUp6(new List<int>(), ver, species, form, maxLevel);
}
private static List<int> GetMovesLevelUp7(int species, int form, int maxLevel, bool MoveReminder, GameVersion ver = Any)
{
return AddMovesLevelUp7(new List<int>(), ver, species, form, maxLevel, MoveReminder);
}
private static List<int> GetMovesLevelUp8(int species, int form, int maxLevel, GameVersion ver = Any)
{
return AddMovesLevelUp8(new List<int>(), ver, species, form, maxLevel);
}
private static List<int> AddMovesLevelUp1(List<int> 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<int> AddMovesLevelUp2(List<int> 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 SI 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<int> AddMovesLevelUp3(List<int> 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<int> AddMovesLevelUp4(List<int> 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<int> AddMovesLevelUp5(List<int> moves, GameVersion ver, int species, int form, int maxLevel)
{
switch (ver)
{
case Any:
2021-07-26 21:14:39 +00:00
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<int> AddMovesLevelUp6(List<int> 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<int> AddMovesLevelUp7(List<int> 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);
2018-11-12 06:49:17 +00:00
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<int> AddMovesLevelUp8(List<int> moves, GameVersion ver, int species, int form, int maxLevel)
{
2019-11-16 01:34:18 +00:00
// 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);
case PLA:
if (species > MaxSpeciesID_8a)
return moves;
return LearnLA.AddMoves(moves, species, form, maxLevel);
case BD or SP or BDSP:
if (species > MaxSpeciesID_8b)
return moves;
return LearnBDSP.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));
learn[index].SetEncounterMoves(level, lvl0, start);
return lvl0;
}
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));
learn[index].SetEncounterMoves(level, lvl0, start);
return lvl0;
}
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);
}
}
}