mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 12:33:06 +00:00
4e1a520b0a
Evolution info was not checked for non-eggs; for mystery gifts check that the level of the evolution is not less than the level of the matched encounter fix spacing (cosmetic) add temp flagging for magnetpull/static slots (will eventually refactor into something functional that considers a set of slots including swarm/etc)
3949 lines
189 KiB
C#
3949 lines
189 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using static PKHeX.Core.LegalityCheckStrings;
|
|
|
|
namespace PKHeX.Core
|
|
{
|
|
public static partial class Legal
|
|
{
|
|
/// <summary>Event Database for a given Generation</summary>
|
|
public static MysteryGift[] MGDB_G4, MGDB_G5, MGDB_G6, MGDB_G7 = new MysteryGift[0];
|
|
|
|
/// <summary>Setting to specify if an analysis should permit data sourced from the physical cartridge era of GameBoy games.</summary>
|
|
public static bool AllowGBCartEra = false;
|
|
public static bool AllowGen1Tradeback = false;
|
|
public static bool AllowGen2VCTransfer => AllowGen1Tradeback;
|
|
|
|
/// <summary>Setting to specify if the e-berry index item is an eningma berry or a e-reader berry and the name of the e-reader berry</summary>
|
|
public static bool EReaderBerryIsEnigma = true;
|
|
public static string EReaderBerryName = string.Empty;
|
|
public static string EReaderBerryDisplayName => string.Format(V372, Util.toTitleCase(EReaderBerryName.ToLower()));
|
|
public static bool SavegameJapanese = false;
|
|
public static string Savegame_OT = string.Empty;
|
|
public static int Savegame_TID = 0;
|
|
public static int Savegame_SID = 0;
|
|
public static int Savegame_Gender = 0;
|
|
public static GameVersion Savegame_Version = GameVersion.Any;
|
|
|
|
// Gen 1
|
|
private static readonly Learnset[] LevelUpRB = Learnset1.getArray(Util.getBinaryResource("lvlmove_rb.pkl"), MaxSpeciesID_1);
|
|
private static readonly Learnset[] LevelUpY = Learnset1.getArray(Util.getBinaryResource("lvlmove_y.pkl"), MaxSpeciesID_1);
|
|
private static readonly EvolutionTree Evolves1;
|
|
private static readonly EncounterArea[] SlotsRBY;
|
|
private static readonly EncounterStatic[] StaticRBY;
|
|
|
|
// Gen 2
|
|
private static readonly EggMoves[] EggMovesGS = EggMoves2.getArray(Util.getBinaryResource("eggmove_gs.pkl"), MaxSpeciesID_2);
|
|
private static readonly Learnset[] LevelUpGS = Learnset1.getArray(Util.getBinaryResource("lvlmove_gs.pkl"), MaxSpeciesID_2);
|
|
private static readonly EggMoves[] EggMovesC = EggMoves2.getArray(Util.getBinaryResource("eggmove_c.pkl"), MaxSpeciesID_2);
|
|
private static readonly Learnset[] LevelUpC = Learnset1.getArray(Util.getBinaryResource("lvlmove_c.pkl"), MaxSpeciesID_2);
|
|
private static readonly EvolutionTree Evolves2;
|
|
private static readonly EncounterArea[] SlotsGSC, SlotsGS, SlotsC;
|
|
private static readonly EncounterStatic[] StaticGSC, StaticGS, StaticC;
|
|
|
|
// Gen 3
|
|
private static readonly Learnset[] LevelUpE = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_e.pkl"), "em"));
|
|
private static readonly Learnset[] LevelUpRS = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_rs.pkl"), "rs"));
|
|
private static readonly Learnset[] LevelUpFR = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_fr.pkl"), "fr"));
|
|
private static readonly Learnset[] LevelUpLG = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_lg.pkl"), "lg"));
|
|
private static readonly EggMoves[] EggMovesRS = EggMoves6.getArray(Data.unpackMini(Util.getBinaryResource("eggmove_rs.pkl"), "rs"));
|
|
private static readonly EvolutionTree Evolves3;
|
|
private static readonly EncounterArea[] SlotsR, SlotsS, SlotsE, SlotsFR, SlotsLG;
|
|
private static readonly EncounterStatic[] StaticR, StaticS, StaticE, StaticFR, StaticLG;
|
|
|
|
// Gen 4
|
|
private static readonly Learnset[] LevelUpDP = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_dp.pkl"), "dp"));
|
|
private static readonly Learnset[] LevelUpPt = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_pt.pkl"), "pt"));
|
|
private static readonly Learnset[] LevelUpHGSS = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_hgss.pkl"), "hs"));
|
|
private static readonly EggMoves[] EggMovesDPPt = EggMoves6.getArray(Data.unpackMini(Util.getBinaryResource("eggmove_dppt.pkl"), "dp"));
|
|
private static readonly EggMoves[] EggMovesHGSS = EggMoves6.getArray(Data.unpackMini(Util.getBinaryResource("eggmove_hgss.pkl"), "hs"));
|
|
private static readonly EvolutionTree Evolves4;
|
|
private static readonly EncounterArea[] SlotsD, SlotsP, SlotsPt, SlotsHG, SlotsSS;
|
|
private static readonly EncounterStatic[] StaticD, StaticP, StaticPt, StaticHG, StaticSS;
|
|
|
|
// Gen 5
|
|
private static readonly Learnset[] LevelUpBW = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_bw.pkl"), "51"));
|
|
private static readonly Learnset[] LevelUpB2W2 = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_b2w2.pkl"), "52"));
|
|
private static readonly EggMoves[] EggMovesBW = EggMoves6.getArray(Data.unpackMini(Util.getBinaryResource("eggmove_bw.pkl"), "bw"));
|
|
private static readonly EvolutionTree Evolves5;
|
|
private static readonly EncounterArea[] SlotsB, SlotsW, SlotsB2, SlotsW2;
|
|
private static readonly EncounterStatic[] StaticB, StaticW, StaticB2, StaticW2;
|
|
|
|
// Gen 6
|
|
private static readonly EggMoves[] EggMovesXY = EggMoves6.getArray(Data.unpackMini(Util.getBinaryResource("eggmove_xy.pkl"), "xy"));
|
|
private static readonly Learnset[] LevelUpXY = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_xy.pkl"), "xy"));
|
|
private static readonly EggMoves[] EggMovesAO = EggMoves6.getArray(Data.unpackMini(Util.getBinaryResource("eggmove_ao.pkl"), "ao"));
|
|
private static readonly Learnset[] LevelUpAO = Learnset6.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_ao.pkl"), "ao"));
|
|
private static readonly EvolutionTree Evolves6;
|
|
private static readonly EncounterArea[] SlotsX, SlotsY, SlotsA, SlotsO;
|
|
private static readonly EncounterStatic[] StaticX, StaticY, StaticA, StaticO;
|
|
|
|
// Gen 7
|
|
private static readonly EggMoves[] EggMovesSM = EggMoves7.getArray(Data.unpackMini(Util.getBinaryResource("eggmove_sm.pkl"), "sm"));
|
|
private static readonly Learnset[] LevelUpSM = Learnset7.getArray(Data.unpackMini(Util.getBinaryResource("lvlmove_sm.pkl"), "sm"));
|
|
private static readonly EvolutionTree Evolves7;
|
|
private static readonly EncounterArea[] SlotsSN, SlotsMN;
|
|
private static readonly EncounterStatic[] StaticSN, StaticMN;
|
|
|
|
// Setup Help
|
|
private static HashSet<MysteryGift> getPCDDB(byte[] bin)
|
|
{
|
|
var db = new HashSet<MysteryGift>();
|
|
for (int i = 0; i < bin.Length; i += PCD.Size)
|
|
{
|
|
byte[] data = new byte[PCD.Size];
|
|
Buffer.BlockCopy(bin, i, data, 0, PCD.Size);
|
|
db.Add(new PCD(data));
|
|
}
|
|
return db;
|
|
}
|
|
private static HashSet<MysteryGift> getPGFDB(byte[] bin)
|
|
{
|
|
var db = new HashSet<MysteryGift>();
|
|
for (int i = 0; i < bin.Length; i += PGF.Size)
|
|
{
|
|
byte[] data = new byte[PGF.Size];
|
|
Buffer.BlockCopy(bin, i, data, 0, PGF.Size);
|
|
db.Add(new PGF(data));
|
|
}
|
|
return db;
|
|
}
|
|
private static HashSet<MysteryGift> getWC6DB(byte[] wc6bin, byte[] wc6full)
|
|
{
|
|
var db = new HashSet<MysteryGift>();
|
|
for (int i = 0; i < wc6bin.Length; i += WC6.Size)
|
|
{
|
|
byte[] data = new byte[WC6.Size];
|
|
Buffer.BlockCopy(wc6bin, i, data, 0, WC6.Size);
|
|
db.Add(new WC6(data));
|
|
}
|
|
for (int i = 0; i < wc6full.Length; i += WC6.SizeFull)
|
|
{
|
|
byte[] data = new byte[WC6.SizeFull];
|
|
Buffer.BlockCopy(wc6full, i, data, 0, WC6.SizeFull);
|
|
db.Add(new WC6(data));
|
|
}
|
|
return db;
|
|
}
|
|
private static HashSet<MysteryGift> getWC7DB(byte[] wc7bin, byte[] wc7full)
|
|
{
|
|
var db = new HashSet<MysteryGift>();
|
|
for (int i = 0; i < wc7bin.Length; i += WC7.Size)
|
|
{
|
|
byte[] data = new byte[WC7.Size];
|
|
Buffer.BlockCopy(wc7bin, i, data, 0, WC7.Size);
|
|
db.Add(new WC7(data));
|
|
}
|
|
for (int i = 0; i < wc7full.Length; i += WC7.SizeFull)
|
|
{
|
|
byte[] data = new byte[WC7.SizeFull];
|
|
Buffer.BlockCopy(wc7full, i, data, 0, WC7.SizeFull);
|
|
db.Add(new WC7(data));
|
|
}
|
|
return db;
|
|
}
|
|
public static void RefreshMGDB(string localDbPath)
|
|
{
|
|
var g4 = getPCDDB(Util.getBinaryResource("pcd.pkl"));
|
|
var g5 = getPGFDB(Util.getBinaryResource("pgf.pkl"));
|
|
var g6 = getWC6DB(Util.getBinaryResource("wc6.pkl"), Util.getBinaryResource("wc6full.pkl"));
|
|
var g7 = getWC7DB(Util.getBinaryResource("wc7.pkl"), Util.getBinaryResource("wc7full.pkl"));
|
|
|
|
if (Directory.Exists(localDbPath))
|
|
foreach (var file in Directory.GetFiles(localDbPath, "*", SearchOption.AllDirectories))
|
|
{
|
|
var fi = new FileInfo(file);
|
|
if (!MysteryGift.getIsMysteryGift(fi.Length))
|
|
continue;
|
|
|
|
var gift = MysteryGift.getMysteryGift(File.ReadAllBytes(file), fi.Extension);
|
|
switch (gift?.Format)
|
|
{
|
|
case 4: g4.Add(gift); continue;
|
|
case 5: g5.Add(gift); continue;
|
|
case 6: g6.Add(gift); continue;
|
|
case 7: g7.Add(gift); continue;
|
|
}
|
|
}
|
|
|
|
MGDB_G4 = g4.ToArray();
|
|
MGDB_G5 = g5.ToArray();
|
|
MGDB_G6 = g6.ToArray();
|
|
MGDB_G7 = g7.ToArray();
|
|
}
|
|
|
|
private static EncounterStatic[] getStaticEncounters(GameVersion Game)
|
|
{
|
|
EncounterStatic[] table;
|
|
switch (Game)
|
|
{
|
|
case GameVersion.RBY:
|
|
return Encounter_RBY; // GameVersion filtering not possible, return immediately
|
|
case GameVersion.GS:
|
|
return Encounter_GS;
|
|
case GameVersion.C:
|
|
return Encounter_C;
|
|
case GameVersion.GSC:
|
|
return Encounter_GSC;
|
|
|
|
case GameVersion.R: case GameVersion.S: case GameVersion.E:
|
|
table = Encounter_RSE;
|
|
break;
|
|
case GameVersion.FR: case GameVersion.LG:
|
|
table = Encounter_FRLG;
|
|
break;
|
|
case GameVersion.CXD:
|
|
table = Encounter_CXD;
|
|
break;
|
|
|
|
case GameVersion.D: case GameVersion.P: case GameVersion.Pt:
|
|
table = Encounter_DPPt;
|
|
break;
|
|
case GameVersion.HG: case GameVersion.SS:
|
|
table = Encounter_HGSS.Concat(Encounter_PokeWalker).ToArray();
|
|
break;
|
|
|
|
case GameVersion.B: case GameVersion.W:
|
|
table = Encounter_BW.Concat(BW_DreamWorld).ToArray();
|
|
break;
|
|
case GameVersion.B2: case GameVersion.W2:
|
|
table = Encounter_B2W2.Concat(B2W2_DreamWorld).ToArray();
|
|
break;
|
|
|
|
case GameVersion.X: case GameVersion.Y:
|
|
table = Encounter_XY;
|
|
break;
|
|
case GameVersion.AS: case GameVersion.OR:
|
|
table = Encounter_AO;
|
|
break;
|
|
case GameVersion.SN: case GameVersion.MN:
|
|
table = Encounter_SM;
|
|
break;
|
|
|
|
default: return null;
|
|
}
|
|
return table?.Where(s => s.Version.Contains(Game)).ToArray();
|
|
}
|
|
private static EncounterArea[] getEncounterTables(GameVersion Game)
|
|
{
|
|
string ident = null;
|
|
byte[] tables = null;
|
|
switch (Game)
|
|
{
|
|
case GameVersion.R: return EncounterArea.getArray3(Data.unpackMini(Util.getBinaryResource("encounter_r.pkl"), "ru"));
|
|
case GameVersion.S: return EncounterArea.getArray3(Data.unpackMini(Util.getBinaryResource("encounter_s.pkl"), "sa"));
|
|
case GameVersion.E: return EncounterArea.getArray3(Data.unpackMini(Util.getBinaryResource("encounter_e.pkl"), "em"));
|
|
case GameVersion.FR: return EncounterArea.getArray3(Data.unpackMini(Util.getBinaryResource("encounter_fr.pkl"), "fr"));
|
|
case GameVersion.LG: return EncounterArea.getArray3(Data.unpackMini(Util.getBinaryResource("encounter_lg.pkl"), "lg"));
|
|
case GameVersion.D: return EncounterArea.getArray4DPPt(Data.unpackMini(Util.getBinaryResource("encounter_d.pkl"), "da"));
|
|
case GameVersion.P: return EncounterArea.getArray4DPPt(Data.unpackMini(Util.getBinaryResource("encounter_p.pkl"), "pe"));
|
|
case GameVersion.Pt: return EncounterArea.getArray4DPPt(Data.unpackMini(Util.getBinaryResource("encounter_pt.pkl"), "pt"));
|
|
case GameVersion.HG: return EncounterArea.getArray4HGSS(Data.unpackMini(Util.getBinaryResource("encounter_hg.pkl"), "hg"));
|
|
case GameVersion.SS: return EncounterArea.getArray4HGSS(Data.unpackMini(Util.getBinaryResource("encounter_ss.pkl"), "ss"));
|
|
case GameVersion.B: ident = "51"; tables = Util.getBinaryResource("encounter_b.pkl"); break;
|
|
case GameVersion.W: ident = "51"; tables = Util.getBinaryResource("encounter_w.pkl"); break;
|
|
case GameVersion.B2: ident = "52"; tables = Util.getBinaryResource("encounter_b2.pkl"); break;
|
|
case GameVersion.W2: ident = "52"; tables = Util.getBinaryResource("encounter_w2.pkl"); break;
|
|
case GameVersion.X: ident = "xy"; tables = Util.getBinaryResource("encounter_x.pkl"); break;
|
|
case GameVersion.Y: ident = "xy"; tables = Util.getBinaryResource("encounter_y.pkl"); break;
|
|
case GameVersion.AS: ident = "ao"; tables = Util.getBinaryResource("encounter_a.pkl"); break;
|
|
case GameVersion.OR: ident = "ao"; tables = Util.getBinaryResource("encounter_o.pkl"); break;
|
|
case GameVersion.SN: ident = "sm"; tables = Util.getBinaryResource("encounter_sn.pkl"); break;
|
|
case GameVersion.MN: ident = "sm"; tables = Util.getBinaryResource("encounter_mn.pkl"); break;
|
|
}
|
|
if (ident == null)
|
|
return new EncounterArea[0];
|
|
|
|
return getEncounterTables(tables, ident);
|
|
}
|
|
private static EncounterArea[] getEncounterTables(byte[] mini, string ident)
|
|
{
|
|
return EncounterArea.getArray(Data.unpackMini(mini, ident));
|
|
}
|
|
private static EncounterArea[] addExtraTableSlots(params EncounterArea[][] tables)
|
|
{
|
|
return tables.SelectMany(s => s).GroupBy(l => l.Location)
|
|
.Select(t => t.Count() == 1
|
|
? t.First() // only one table, just return the area
|
|
: new EncounterArea {Location = t.First().Location, Slots = t.SelectMany(s => s.Slots).ToArray()})
|
|
.ToArray();
|
|
}
|
|
private static void MarkEncountersStaticMagnetPull(ref EncounterArea[] Areas, PersonalTable t)
|
|
{
|
|
const int steel = 8;
|
|
const int electric = 12;
|
|
foreach (EncounterArea Area in Areas)
|
|
{
|
|
var s = new List<EncounterSlot>(); // Static
|
|
var m = new List<EncounterSlot>(); // Magnet Pull
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
var types = t[Slot.Species].Types;
|
|
if (types[0] == steel || types[1] == steel)
|
|
m.Add(Slot);
|
|
if (types[0] == electric || types[1] == electric)
|
|
s.Add(Slot);
|
|
}
|
|
foreach (var slot in s)
|
|
{
|
|
slot.Static = true;
|
|
slot.StaticCount = s.Count;
|
|
}
|
|
foreach (var slot in m)
|
|
{
|
|
slot.MagnetPull = true;
|
|
slot.MagnetPullCount = s.Count;
|
|
}
|
|
}
|
|
}
|
|
private static void MarkEncountersGeneration(ref EncounterStatic[] Encounters, int Generation)
|
|
{
|
|
foreach (EncounterStatic Encounter in Encounters)
|
|
Encounter.Generation = Generation;
|
|
}
|
|
private static void MarkEncountersGeneration(ref EncounterArea[] Areas, int Generation)
|
|
{
|
|
foreach (EncounterArea Area in Areas)
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
Slot.Generation = Generation;
|
|
}
|
|
private static void ReduceAreasSize(ref EncounterArea[] Areas)
|
|
{
|
|
// Group areas by location id, the raw data have areas with different slots but the same location id
|
|
Areas = Areas.GroupBy(a => a.Location).Select(a => new EncounterArea
|
|
{
|
|
Location = a.First().Location,
|
|
Slots = a.SelectMany(m => m.Slots).ToArray()
|
|
}).ToArray();
|
|
}
|
|
private static void MarkG2Slots(ref EncounterArea[] Areas)
|
|
{
|
|
ReduceAreasSize(ref Areas);
|
|
}
|
|
private static void MarkG3Slots_FRLG(ref EncounterArea[] Areas)
|
|
{
|
|
// Remove slots for unown, those slots does not contains alt form info, it will be added manually in SlotsRFLGAlt
|
|
// Group areas by location id, the raw data have areas with different slots but the same location id
|
|
Areas = Areas.Where(a => a.Location < 188 || a.Location > 194).GroupBy(a => a.Location).Select(a => new EncounterArea
|
|
{
|
|
Location = a.First().Location,
|
|
Slots = a.SelectMany(m => m.Slots).ToArray()
|
|
}).ToArray();
|
|
}
|
|
private static void MarkG3Slots_RSE(ref EncounterArea[] Areas)
|
|
{
|
|
ReduceAreasSize(ref Areas);
|
|
}
|
|
private static void MarkG3SlotsSafariZones(ref EncounterArea[] Areas, int location)
|
|
{
|
|
foreach (EncounterArea Area in Areas.Where(a => a.Location == location))
|
|
{
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
SlotType t;
|
|
switch (Slot.Type)
|
|
{
|
|
case SlotType.Grass: t = SlotType.Grass_Safari; break;
|
|
case SlotType.Surf: t = SlotType.Surf_Safari; break;
|
|
case SlotType.Old_Rod: t = SlotType.Old_Rod_Safari; break;
|
|
case SlotType.Good_Rod: t = SlotType.Good_Rod_Safari; break;
|
|
case SlotType.Super_Rod: t = SlotType.Super_Rod_Safari; break;
|
|
case SlotType.Rock_Smash: t = SlotType.Rock_Smash_Safari; break;
|
|
default: continue;
|
|
}
|
|
Slot.Type = t;
|
|
}
|
|
}
|
|
}
|
|
private static void MarkG4PokeWalker(ref EncounterStatic[] t)
|
|
{
|
|
foreach (EncounterStatic s in t)
|
|
{
|
|
s.Location = 233; //Pokéwalker
|
|
s.Gift = true; //Pokeball only
|
|
}
|
|
}
|
|
private static void MarkG4SlotsGreatMarsh(ref EncounterArea[] Areas, int location)
|
|
{
|
|
foreach (EncounterArea Area in Areas.Where(a => a.Location == location))
|
|
{
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
SlotType t;
|
|
switch (Slot.Type)
|
|
{
|
|
case SlotType.Grass: t = SlotType.Grass_Safari; break;
|
|
case SlotType.Surf: t = SlotType.Surf_Safari; break;
|
|
case SlotType.Old_Rod: t = SlotType.Old_Rod_Safari; break;
|
|
case SlotType.Good_Rod: t = SlotType.Good_Rod_Safari; break;
|
|
case SlotType.Super_Rod: t = SlotType.Super_Rod_Safari; break;
|
|
case SlotType.Pokeradar: t = SlotType.Pokeradar_Safari; break;
|
|
default: continue;
|
|
}
|
|
Slot.Type = t;
|
|
}
|
|
}
|
|
}
|
|
private static void MarkG4SwarmSlots(ref EncounterArea[] Areas, EncounterArea[] SwarmAreas)
|
|
{
|
|
// Grass Swarm slots replace slots 0 and 1 from encounters data
|
|
// for surfing only replace slots 0 from encounters data
|
|
// for fishing replace one or several random slots from encounters data, but all slots have the same level, it's ok to only replace the first
|
|
// Species id are not included in encounter tables but levels can be copied from the encounter raw data
|
|
foreach (EncounterArea Area in Areas)
|
|
{
|
|
var SwarmSlots = SwarmAreas.Where(a => a.Location == Area.Location).SelectMany(s => s.Slots);
|
|
var OutputSlots = new List<EncounterSlot>();
|
|
foreach (EncounterSlot SwarmSlot in SwarmSlots)
|
|
{
|
|
int slotsnum = SwarmSlot.Type == SlotType.Grass ? 2 : 1;
|
|
foreach (var swarmSlot in Area.Slots.Where(s => s.Type == SwarmSlot.Type).Take(slotsnum).Select(slot => slot.Clone()))
|
|
{
|
|
swarmSlot.Species = SwarmSlot.Species;
|
|
OutputSlots.Add(swarmSlot);
|
|
}
|
|
}
|
|
Area.Slots = Area.Slots.Concat(OutputSlots).Where(a => a.Species > 0).ToArray();
|
|
}
|
|
}
|
|
// Gen 4 raw encounter data does not contains info for alt slots
|
|
// Shellos and Gastrodom East Sea form should be modified
|
|
private static void MarkG4AltFormSlots(ref EncounterArea[] Areas, int Species, int form, int[] Locations)
|
|
{
|
|
foreach(EncounterArea Area in Areas.Where(a => Locations.Contains(a.Location)))
|
|
{
|
|
foreach (EncounterSlot Slot in Area.Slots.Where(s=>s.Species == Species))
|
|
{
|
|
Slot.Form = form;
|
|
}
|
|
}
|
|
}
|
|
private static void MarkG4Slots(ref EncounterArea[] Areas)
|
|
{
|
|
ReduceAreasSize(ref Areas);
|
|
}
|
|
private static EncounterType GetEncounterTypeBySlotDPPt( SlotType Type, EncounterType GrassType)
|
|
{
|
|
switch (Type)
|
|
{
|
|
case SlotType.Pokeradar:
|
|
case SlotType.Pokeradar_Safari:
|
|
case SlotType.Swarm:
|
|
case SlotType.Grass: return GrassType;
|
|
case SlotType.Surf:
|
|
case SlotType.Old_Rod:
|
|
case SlotType.Good_Rod:
|
|
case SlotType.Super_Rod:
|
|
case SlotType.Surf_Safari:
|
|
case SlotType.Old_Rod_Safari:
|
|
case SlotType.Good_Rod_Safari:
|
|
case SlotType.Super_Rod_Safari: return EncounterType.Surfing_Fishing;
|
|
case SlotType.Grass_Safari: return EncounterType.MarshSafari;
|
|
case SlotType.HoneyTree: return EncounterType.None;
|
|
}
|
|
return EncounterType.None;
|
|
}
|
|
private static EncounterType GetEncounterTypeBySlotHGSS(SlotType Type, EncounterType GrassType, EncounterType HeadbuttType)
|
|
{
|
|
switch (Type)
|
|
{
|
|
// HGSS Safari encounters have normal water/grass encounter type, not safari encounter type
|
|
case SlotType.Grass:
|
|
case SlotType.Grass_Safari:
|
|
case SlotType.BugContest: return GrassType;
|
|
case SlotType.Surf:
|
|
case SlotType.Old_Rod:
|
|
case SlotType.Good_Rod:
|
|
case SlotType.Super_Rod:
|
|
case SlotType.Surf_Safari:
|
|
case SlotType.Old_Rod_Safari:
|
|
case SlotType.Good_Rod_Safari:
|
|
case SlotType.Super_Rod_Safari: return EncounterType.Surfing_Fishing;
|
|
case SlotType.Rock_Smash:
|
|
case SlotType.Rock_Smash_Safari: return EncounterType.RockSmash;
|
|
case SlotType.Headbutt: return HeadbuttType;
|
|
case SlotType.Headbutt_Special: return EncounterType.None;
|
|
}
|
|
return EncounterType.None;
|
|
}
|
|
private static void MarkDPPtEncounterTypeSlots_MultipleTypes(ref EncounterArea[] Areas, int Location, int SpecialEncounterFile, EncounterType NormalEncounterType)
|
|
{
|
|
// Area with two different encounter type for grass encounters
|
|
// SpecialEncounterFile is tall grass encounter type, the other files have the normal encounter type for this location
|
|
var numfile = 0;
|
|
foreach (EncounterArea Area in Areas.Where(x => x.Location == Location))
|
|
{
|
|
numfile++;
|
|
var GrassType = numfile == SpecialEncounterFile ? EncounterType.TallGrass : NormalEncounterType;
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
Slot.TypeEncounter = GetEncounterTypeBySlotDPPt(Slot.Type, GrassType);
|
|
}
|
|
}
|
|
}
|
|
private static void MarkDPPtEncounterTypeSlots_MultipleTypes(ref EncounterArea[] Areas, int Location, int[] SpecialEncounterFiles, EncounterType NormalEncounterType)
|
|
{
|
|
var numfile = 0;
|
|
foreach (EncounterArea Area in Areas.Where(x => x.Location == Location))
|
|
{
|
|
numfile++;
|
|
var GrassType = SpecialEncounterFiles.Contains(numfile) ? EncounterType.TallGrass : NormalEncounterType;
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
Slot.TypeEncounter = GetEncounterTypeBySlotDPPt(Slot.Type, GrassType);
|
|
}
|
|
}
|
|
}
|
|
private static void MarkHGSSEncounterTypeSlots_MultipleTypes(ref EncounterArea[] Areas, int Location, int SpecialEncounterFile, EncounterType NormalEncounterType)
|
|
{
|
|
// Area with two different encounter type for grass encounters
|
|
// SpecialEncounterFile is taall grass encounter type, the other files have the normal encounter type for this location
|
|
var HeadbuttType = getHeadbuttEncounter(Location);
|
|
var numfile = 0;
|
|
foreach (EncounterArea Area in Areas.Where(x => x.Location == Location))
|
|
{
|
|
numfile++;
|
|
var GrassType = numfile == SpecialEncounterFile ? EncounterType.TallGrass : NormalEncounterType;
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
Slot.TypeEncounter = GetEncounterTypeBySlotHGSS(Slot.Type, GrassType, HeadbuttType);
|
|
}
|
|
}
|
|
}
|
|
private static void MarkHGSSEncounterTypeSlots_MultipleTypes(ref EncounterArea[] Areas, int Location, int[] SpecialEncounterFiles, EncounterType NormalEncounterType)
|
|
{
|
|
var HeadbuttType = getHeadbuttEncounter(Location);
|
|
var numfile = 0;
|
|
foreach (EncounterArea Area in Areas.Where(x => x.Location == Location))
|
|
{
|
|
numfile++;
|
|
var GrassType = SpecialEncounterFiles.Contains(numfile) ? EncounterType.TallGrass : NormalEncounterType;
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
Slot.TypeEncounter = GetEncounterTypeBySlotHGSS(Slot.Type, GrassType, HeadbuttType);
|
|
}
|
|
}
|
|
}
|
|
private static void MarkDPPtEncounterTypeSlots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach(EncounterArea Area in Areas)
|
|
{
|
|
if (DPPt_MixInteriorExteriorLocations.Contains(Area.Location))
|
|
continue;
|
|
var GrassType = (Area.Location == 70) ? EncounterType.Building_EnigmaStone :// Old Chateau
|
|
DPPt_CaveLocations.Contains(Area.Location) ? EncounterType.Cave_HallOfOrigin :
|
|
EncounterType.TallGrass;
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
Slot.TypeEncounter = GetEncounterTypeBySlotDPPt(Slot.Type, GrassType);
|
|
}
|
|
}
|
|
}
|
|
private static EncounterType getHeadbuttEncounter(int Location)
|
|
{
|
|
// Routes with trees adjacent to water tiles
|
|
var allowsurf= HGSS_SurfingHeadbutt_Locations.Contains(Location);
|
|
// Cities
|
|
if (HGSS_CityLocations.Contains(Location))
|
|
{
|
|
return allowsurf ? EncounterType.Headbutt_CitySurf : EncounterType.Building_EnigmaStone;
|
|
}
|
|
// Caves with no exterior zones
|
|
if (!HGSS_MixInteriorExteriorLocations.Contains(Location) && HGSS_CaveLocations.Contains(Location))
|
|
{
|
|
return allowsurf ? EncounterType.Headbutt_CaveSurf : EncounterType.Cave_HallOfOrigin;
|
|
}
|
|
// Routes and exterior areas
|
|
// Routes with trees adjacent to grass tiles
|
|
var allowgrass = HGSS_GrassHeadbutt_Locations.Contains(Location);
|
|
return allowgrass && allowsurf ? EncounterType.Headbutt_GrassSurf :
|
|
allowgrass ? EncounterType.Headbutt_Grass :
|
|
allowsurf ? EncounterType.Headbutt_Surf :
|
|
EncounterType.None;
|
|
}
|
|
|
|
private static void MarkHGSSEncounterTypeSlots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach (EncounterArea Area in Areas)
|
|
{
|
|
if (HGSS_MixInteriorExteriorLocations.Contains(Area.Location))
|
|
continue;
|
|
var GrassType = HGSS_CaveLocations.Contains(Area.Location) ? EncounterType.Cave_HallOfOrigin: EncounterType.TallGrass;
|
|
var HeadbuttType = getHeadbuttEncounter(Area.Location);
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
{
|
|
Slot.TypeEncounter = GetEncounterTypeBySlotHGSS(Slot.Type, GrassType, HeadbuttType);
|
|
}
|
|
}
|
|
}
|
|
private static void MarkBWSwarmSlots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach (EncounterSlot s in Areas.SelectMany(area => area.Slots))
|
|
{
|
|
s.LevelMin = 15; s.LevelMax = 55; s.Type = SlotType.Swarm;
|
|
}
|
|
}
|
|
private static void MarkB2W2SwarmSlots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach (EncounterSlot s in Areas.SelectMany(area => area.Slots))
|
|
{
|
|
s.LevelMin = 40; s.LevelMax = 55; s.Type = SlotType.Swarm;
|
|
}
|
|
}
|
|
private static void MarkG5HiddenGrottoSlots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach (EncounterSlot s in Areas[0].Slots) //Only 1 area
|
|
s.Type = SlotType.HiddenGrotto;
|
|
}
|
|
private static void MarkG5DreamWorld(ref EncounterStatic[] t)
|
|
{
|
|
foreach (EncounterStatic s in t)
|
|
{
|
|
s.Location = 75; //Entree Forest
|
|
s.Ability = PersonalTable.B2W2.getAbilities(s.Species, s.Form)[2] == 0 ? 1 : 4; // Check if has HA
|
|
}
|
|
|
|
// Split encounters with multiple permitted special moves -- a pkm can only be obtained with 1 of the special moves!
|
|
var list = new List<EncounterStatic>();
|
|
foreach (EncounterStatic s in t)
|
|
{
|
|
if (s.Moves == null || s.Moves.Length <= 1) // no special moves
|
|
{
|
|
list.Add(s);
|
|
continue;
|
|
}
|
|
|
|
var loc = s.Location;
|
|
for (int i = 0; i < s.Moves.Length; i++)
|
|
{
|
|
var clone = s.Clone(loc);
|
|
clone.Moves = new[] {s.Moves[i]};
|
|
list.Add(clone);
|
|
}
|
|
}
|
|
t = list.ToArray();
|
|
}
|
|
private static void MarkG5Slots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach (var area in Areas)
|
|
{
|
|
int ctr = 0;
|
|
do
|
|
{
|
|
for (int i = 0; i < 12; i++)
|
|
area.Slots[ctr++].Type = SlotType.Grass; // Single
|
|
|
|
for (int i = 0; i < 12; i++)
|
|
area.Slots[ctr++].Type = SlotType.Grass; // Double
|
|
|
|
for (int i = 0; i < 12; i++)
|
|
area.Slots[ctr++].Type = SlotType.Grass; // Shaking
|
|
|
|
for (int i = 0; i < 5; i++) // 5
|
|
area.Slots[ctr++].Type = SlotType.Surf; // Surf
|
|
|
|
for (int i = 0; i < 5; i++) // 5
|
|
area.Slots[ctr++].Type = SlotType.Surf; // Surf Spot
|
|
|
|
for (int i = 0; i < 5; i++) // 5
|
|
area.Slots[ctr++].Type = SlotType.Super_Rod; // Fish
|
|
|
|
for (int i = 0; i < 5; i++) // 5
|
|
area.Slots[ctr++].Type = SlotType.Super_Rod; // Fish Spot
|
|
} while (ctr != area.Slots.Length);
|
|
area.Slots = area.Slots.Where(slot => slot.Species != 0).ToArray();
|
|
}
|
|
ReduceAreasSize(ref Areas);
|
|
}
|
|
private static void MarkG6XYSlots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach (var area in Areas)
|
|
{
|
|
int slotct = area.Slots.Length;
|
|
for (int i = slotct - 15; i < slotct; i++)
|
|
area.Slots[i].Type = SlotType.Horde;
|
|
}
|
|
ReduceAreasSize(ref Areas);
|
|
}
|
|
private static void MarkG6AOSlots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach (var area in Areas)
|
|
{
|
|
for (int i = 32; i < 37; i++)
|
|
area.Slots[i].Type = SlotType.Rock_Smash;
|
|
int slotct = area.Slots.Length;
|
|
for (int i = slotct - 15; i < slotct; i++)
|
|
area.Slots[i].Type = SlotType.Horde;
|
|
|
|
for (int i = 0; i < slotct; i++)
|
|
area.Slots[i].AllowDexNav = area.Slots[i].Type != SlotType.Rock_Smash;
|
|
}
|
|
ReduceAreasSize(ref Areas);
|
|
}
|
|
private static void MarkG7REGSlots(ref EncounterArea[] Areas)
|
|
{
|
|
ReduceAreasSize(ref Areas);
|
|
}
|
|
private static void MarkG7SMSlots(ref EncounterArea[] Areas)
|
|
{
|
|
foreach (EncounterSlot s in Areas.SelectMany(area => area.Slots))
|
|
s.Type = SlotType.SOS;
|
|
ReduceAreasSize(ref Areas);
|
|
}
|
|
private static EncounterArea[] getTables1()
|
|
{
|
|
var red_gw = EncounterArea.getArray1_GW(Util.getBinaryResource("encounter_red.pkl"));
|
|
var blu_gw = EncounterArea.getArray1_GW(Util.getBinaryResource("encounter_blue.pkl"));
|
|
var ylw_gw = EncounterArea.getArray1_GW(Util.getBinaryResource("encounter_yellow.pkl"));
|
|
var rb_fish = EncounterArea.getArray1_F(Util.getBinaryResource("encounter_rb_f.pkl"));
|
|
var ylw_fish = EncounterArea.getArray1_FY(Util.getBinaryResource("encounter_yellow_f.pkl"));
|
|
|
|
var table = addExtraTableSlots(red_gw, blu_gw, ylw_gw, rb_fish, ylw_fish);
|
|
Array.Resize(ref table, table.Length + 1);
|
|
table[table.Length - 1] = FishOldGood_RBY;
|
|
|
|
return table;
|
|
}
|
|
private static EncounterArea[] getTables2(GameVersion Version)
|
|
{
|
|
EncounterArea[] Slots = null;
|
|
// Fishing
|
|
var f = EncounterArea.getArray2_F(Util.getBinaryResource("encounter_gsc_f.pkl"));
|
|
if (Version == GameVersion.GS || Version == GameVersion.GSC)
|
|
{
|
|
// Grass/Water
|
|
var g = EncounterArea.getArray2_GW(Util.getBinaryResource("encounter_gold.pkl"));
|
|
var s = EncounterArea.getArray2_GW(Util.getBinaryResource("encounter_silver.pkl"));
|
|
// Headbutt/Rock Smash
|
|
var h_g = EncounterArea.getArray2_H(Util.getBinaryResource("encounter_gold_h.pkl"));
|
|
var h_s = EncounterArea.getArray2_H(Util.getBinaryResource("encounter_silver_h.pkl"));
|
|
|
|
Slots = addExtraTableSlots(g, s, h_g, h_s, f);
|
|
}
|
|
if (Version == GameVersion.C || Version == GameVersion.GSC)
|
|
{
|
|
// Grass/Water
|
|
var c = EncounterArea.getArray2_GW(Util.getBinaryResource("encounter_crystal.pkl"));
|
|
// Headbutt/Rock Smash
|
|
var h_c = EncounterArea.getArray2_H(Util.getBinaryResource("encounter_crystal_h.pkl"));
|
|
|
|
var extra = addExtraTableSlots(c, h_c, f);
|
|
return Version == GameVersion.C ? extra : addExtraTableSlots(Slots, extra);
|
|
}
|
|
|
|
return Slots;
|
|
}
|
|
|
|
static Legal() // Setup
|
|
{
|
|
// Gen 1
|
|
{
|
|
StaticRBY = getStaticEncounters(GameVersion.RBY);
|
|
SlotsRBY = getTables1();
|
|
// Gen 1 is the only gen where ReduceAreasSize is not needed
|
|
MarkEncountersGeneration(ref SlotsRBY, 1);
|
|
MarkEncountersGeneration(ref StaticRBY, 1);
|
|
Evolves1 = new EvolutionTree(new[] { Util.getBinaryResource("evos_rby.pkl") }, GameVersion.RBY, PersonalTable.Y, MaxSpeciesID_1);
|
|
FixPersonalTableY();
|
|
}
|
|
// Gen 2
|
|
{
|
|
StaticGS = getStaticEncounters(GameVersion.GS);
|
|
StaticC = getStaticEncounters(GameVersion.C);
|
|
StaticGSC = getStaticEncounters(GameVersion.GSC);
|
|
SlotsGS = getTables2(GameVersion.GS);
|
|
SlotsC = getTables2(GameVersion.C);
|
|
SlotsGSC = getTables2(GameVersion.GSC);
|
|
MarkG2Slots(ref SlotsGS);
|
|
MarkG2Slots(ref SlotsC);
|
|
MarkG2Slots(ref SlotsGSC);
|
|
MarkEncountersGeneration(ref SlotsGS, 2);
|
|
MarkEncountersGeneration(ref SlotsC, 2);
|
|
MarkEncountersGeneration(ref SlotsGSC, 2);
|
|
MarkEncountersGeneration(ref StaticGS, 2);
|
|
MarkEncountersGeneration(ref StaticC, 2);
|
|
MarkEncountersGeneration(ref StaticGSC, 2);
|
|
Evolves2 = new EvolutionTree(new[] { Util.getBinaryResource("evos_gsc.pkl") }, GameVersion.GSC, PersonalTable.C, MaxSpeciesID_2);
|
|
}
|
|
// Gen3
|
|
{
|
|
StaticR = getStaticEncounters(GameVersion.R);
|
|
StaticS = getStaticEncounters(GameVersion.S);
|
|
StaticE = getStaticEncounters(GameVersion.E);
|
|
StaticFR = getStaticEncounters(GameVersion.FR);
|
|
StaticLG = getStaticEncounters(GameVersion.LG);
|
|
|
|
var R_Slots = getEncounterTables(GameVersion.R);
|
|
var S_Slots = getEncounterTables(GameVersion.S);
|
|
var E_Slots = getEncounterTables(GameVersion.E);
|
|
var FR_Slots = getEncounterTables(GameVersion.FR);
|
|
var LG_Slots = getEncounterTables(GameVersion.LG);
|
|
|
|
MarkG3Slots_RSE(ref R_Slots);
|
|
MarkG3Slots_RSE(ref S_Slots);
|
|
MarkG3Slots_RSE(ref E_Slots);
|
|
MarkG3Slots_FRLG(ref FR_Slots);
|
|
MarkG3Slots_FRLG(ref LG_Slots);
|
|
MarkG3SlotsSafariZones(ref R_Slots, 57);
|
|
MarkG3SlotsSafariZones(ref S_Slots, 57);
|
|
MarkG3SlotsSafariZones(ref E_Slots, 57);
|
|
MarkG3SlotsSafariZones(ref FR_Slots, 136);
|
|
MarkG3SlotsSafariZones(ref LG_Slots, 136);
|
|
|
|
MarkEncountersStaticMagnetPull(ref R_Slots, PersonalTable.SM);
|
|
MarkEncountersStaticMagnetPull(ref S_Slots, PersonalTable.SM);
|
|
MarkEncountersStaticMagnetPull(ref E_Slots, PersonalTable.SM);
|
|
MarkEncountersStaticMagnetPull(ref FR_Slots, PersonalTable.SM);
|
|
MarkEncountersStaticMagnetPull(ref LG_Slots, PersonalTable.SM);
|
|
|
|
SlotsR = addExtraTableSlots(R_Slots, SlotsRSEAlt);
|
|
SlotsS = addExtraTableSlots(S_Slots, SlotsRSEAlt);
|
|
SlotsE = addExtraTableSlots(E_Slots, SlotsRSEAlt);
|
|
SlotsFR = addExtraTableSlots(FR_Slots, SlotsFRLGAlt);
|
|
SlotsLG = addExtraTableSlots(LG_Slots, SlotsFRLGAlt);
|
|
|
|
Evolves3 = new EvolutionTree(new[] { Util.getBinaryResource("evos_g3.pkl") }, GameVersion.RS, PersonalTable.RS, MaxSpeciesID_3);
|
|
|
|
// Update Personal Entries with TM/Tutor Data
|
|
var TMHM = Data.unpackMini(Util.getBinaryResource("hmtm_g3.pkl"), "g3");
|
|
for (int i = 0; i <= MaxSpeciesID_3; i++)
|
|
PersonalTable.E[i].AddTMHM(TMHM[i]);
|
|
// Tutors g3 contains tutor compatiblity data extracted from emerald,
|
|
// fire red and leaf green tutors data is a subset of emerald data
|
|
var tutors = Data.unpackMini(Util.getBinaryResource("tutors_g3.pkl"), "g3");
|
|
for (int i = 0; i <= MaxSpeciesID_3; i++)
|
|
PersonalTable.E[i].AddTypeTutors(tutors[i]);
|
|
}
|
|
// Gen 4
|
|
{
|
|
MarkG4PokeWalker(ref Encounter_PokeWalker);
|
|
StaticD = getStaticEncounters(GameVersion.D);
|
|
StaticP = getStaticEncounters(GameVersion.P);
|
|
StaticPt = getStaticEncounters(GameVersion.Pt);
|
|
StaticHG = getStaticEncounters(GameVersion.HG);
|
|
StaticSS = getStaticEncounters(GameVersion.SS);
|
|
|
|
var D_Slots = getEncounterTables(GameVersion.D);
|
|
var P_Slots = getEncounterTables(GameVersion.P);
|
|
var Pt_Slots = getEncounterTables(GameVersion.Pt);
|
|
var HG_Slots = getEncounterTables(GameVersion.HG);
|
|
var SS_Slots = getEncounterTables(GameVersion.SS);
|
|
|
|
MarkEncountersStaticMagnetPull(ref D_Slots, PersonalTable.SM);
|
|
MarkEncountersStaticMagnetPull(ref P_Slots, PersonalTable.SM);
|
|
MarkEncountersStaticMagnetPull(ref Pt_Slots, PersonalTable.SM);
|
|
MarkEncountersStaticMagnetPull(ref HG_Slots, PersonalTable.SM);
|
|
MarkEncountersStaticMagnetPull(ref SS_Slots, PersonalTable.SM);
|
|
|
|
var DP_Trophy = EncounterArea.getTrophyArea(TrophyDP, new[] {16, 18});
|
|
var Pt_Trophy = EncounterArea.getTrophyArea(TrophyPt, new[] {22, 22});
|
|
var HG_Headbutt_Slots = EncounterArea.getArray4HGSS_Headbutt(Data.unpackMini(Util.getBinaryResource("encunters_hb_hg.pkl"), "hg"));
|
|
var SS_Headbutt_Slots = EncounterArea.getArray4HGSS_Headbutt(Data.unpackMini(Util.getBinaryResource("encunters_hb_ss.pkl"), "ss"));
|
|
|
|
var D_HoneyTrees_Slots = SlotsD_HoneyTree.Clone(HoneyTreesLocation);
|
|
var P_HoneyTrees_Slots = SlotsP_HoneyTree.Clone(HoneyTreesLocation);
|
|
var Pt_HoneyTrees_Slots = SlotsPt_HoneyTree.Clone(HoneyTreesLocation);
|
|
|
|
MarkG4SwarmSlots(ref HG_Slots, SlotsHG_Swarm);
|
|
MarkG4SwarmSlots(ref SS_Slots, SlotsSS_Swarm);
|
|
|
|
MarkG4AltFormSlots(ref D_Slots, 422, 1, Shellos_EastSeaLocation_DP);
|
|
MarkG4AltFormSlots(ref D_Slots, 423, 1, Gastrodon_EastSeaLocation_DP);
|
|
MarkG4AltFormSlots(ref P_Slots, 422, 1, Shellos_EastSeaLocation_DP);
|
|
MarkG4AltFormSlots(ref P_Slots, 423, 1, Gastrodon_EastSeaLocation_DP);
|
|
MarkG4AltFormSlots(ref Pt_Slots, 422, 1, Shellos_EastSeaLocation_Pt);
|
|
MarkG4AltFormSlots(ref Pt_Slots, 423, 1, Gastrodon_EastSeaLocation_Pt);
|
|
|
|
// Route 209
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref D_Slots, 24, 1, EncounterType.Building_EnigmaStone);
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref P_Slots, 24, 1, EncounterType.Building_EnigmaStone);
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref Pt_Slots, 24, 1, EncounterType.Building_EnigmaStone);
|
|
|
|
// Stark Mountain
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref D_Slots, 84, 1, EncounterType.Cave_HallOfOrigin);
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref P_Slots, 84, 1, EncounterType.Cave_HallOfOrigin);
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref Pt_Slots, 84, 1, EncounterType.Cave_HallOfOrigin);
|
|
// Mt Coronet
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref D_Slots, 50, DPPt_MtCoronetExteriorEncounters, EncounterType.Cave_HallOfOrigin);
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref P_Slots, 50, DPPt_MtCoronetExteriorEncounters, EncounterType.Cave_HallOfOrigin);
|
|
MarkDPPtEncounterTypeSlots_MultipleTypes(ref Pt_Slots, 50, DPPt_MtCoronetExteriorEncounters, EncounterType.Cave_HallOfOrigin);
|
|
|
|
// Ruins of Alph
|
|
MarkHGSSEncounterTypeSlots_MultipleTypes(ref HG_Slots, 209, 1, EncounterType.Cave_HallOfOrigin);
|
|
MarkHGSSEncounterTypeSlots_MultipleTypes(ref SS_Slots, 209, 1, EncounterType.Cave_HallOfOrigin);
|
|
// Mt Silver Cave
|
|
MarkHGSSEncounterTypeSlots_MultipleTypes(ref HG_Slots, 219, HGSS_MtSilverCaveExteriorEncounters, EncounterType.Cave_HallOfOrigin);
|
|
MarkHGSSEncounterTypeSlots_MultipleTypes(ref SS_Slots, 219, HGSS_MtSilverCaveExteriorEncounters, EncounterType.Cave_HallOfOrigin);
|
|
|
|
MarkG4Slots(ref D_Slots);
|
|
MarkG4Slots(ref P_Slots);
|
|
MarkG4Slots(ref Pt_Slots);
|
|
MarkG4Slots(ref HG_Slots);
|
|
MarkG4Slots(ref SS_Slots);
|
|
MarkG4Slots(ref HG_Headbutt_Slots);
|
|
MarkG4Slots(ref SS_Headbutt_Slots);
|
|
|
|
MarkG4SlotsGreatMarsh(ref D_Slots, 52);
|
|
MarkG4SlotsGreatMarsh(ref P_Slots, 52);
|
|
MarkG4SlotsGreatMarsh(ref Pt_Slots, 52);
|
|
|
|
SlotsD = addExtraTableSlots(D_Slots, D_HoneyTrees_Slots, DP_GreatMarshAlt, SlotsDPPPtAlt, DP_Trophy);
|
|
SlotsP = addExtraTableSlots(P_Slots, P_HoneyTrees_Slots, DP_GreatMarshAlt, SlotsDPPPtAlt, DP_Trophy);
|
|
SlotsPt = addExtraTableSlots(Pt_Slots, Pt_HoneyTrees_Slots, Pt_GreatMarshAlt, SlotsDPPPtAlt, Pt_Trophy);
|
|
SlotsHG = addExtraTableSlots(HG_Slots, HG_Headbutt_Slots, SlotsHGSSAlt);
|
|
SlotsSS = addExtraTableSlots(SS_Slots, SS_Headbutt_Slots, SlotsHGSSAlt);
|
|
|
|
MarkDPPtEncounterTypeSlots(ref SlotsD);
|
|
MarkDPPtEncounterTypeSlots(ref SlotsP);
|
|
MarkDPPtEncounterTypeSlots(ref SlotsPt);
|
|
MarkHGSSEncounterTypeSlots(ref SlotsHG);
|
|
MarkHGSSEncounterTypeSlots(ref SlotsSS);
|
|
|
|
Evolves4 = new EvolutionTree(new[] { Util.getBinaryResource("evos_g4.pkl") }, GameVersion.DP, PersonalTable.DP, MaxSpeciesID_4);
|
|
|
|
// Update Personal Entries with Tutor Data
|
|
var tutors = Data.unpackMini(Util.getBinaryResource("tutors_g4.pkl"), "g4");
|
|
for (int i = 0; i <= MaxSpeciesID_4; i++)
|
|
PersonalTable.HGSS[i].AddTypeTutors(tutors[i]);
|
|
}
|
|
// Gen 5
|
|
{
|
|
MarkG5DreamWorld(ref BW_DreamWorld);
|
|
MarkG5DreamWorld(ref B2W2_DreamWorld);
|
|
StaticB = getStaticEncounters(GameVersion.B);
|
|
StaticW = getStaticEncounters(GameVersion.W);
|
|
StaticB2 = getStaticEncounters(GameVersion.B2);
|
|
StaticW2 = getStaticEncounters(GameVersion.W2);
|
|
|
|
var BSlots = getEncounterTables(GameVersion.B);
|
|
var WSlots = getEncounterTables(GameVersion.W);
|
|
MarkG5Slots(ref BSlots);
|
|
MarkG5Slots(ref WSlots);
|
|
MarkBWSwarmSlots(ref SlotsB_Swarm);
|
|
MarkBWSwarmSlots(ref SlotsW_Swarm);
|
|
SlotsB = addExtraTableSlots(BSlots, SlotsB_Swarm);
|
|
SlotsW = addExtraTableSlots(WSlots, SlotsW_Swarm, WhiteForestSlot);
|
|
|
|
var B2Slots = getEncounterTables(GameVersion.B2);
|
|
var W2Slots = getEncounterTables(GameVersion.W2);
|
|
MarkG5Slots(ref B2Slots);
|
|
MarkG5Slots(ref W2Slots);
|
|
MarkB2W2SwarmSlots(ref SlotsB2_Swarm);
|
|
MarkB2W2SwarmSlots(ref SlotsW2_Swarm);
|
|
MarkG5HiddenGrottoSlots(ref SlotsB2_HiddenGrotto);
|
|
MarkG5HiddenGrottoSlots(ref SlotsW2_HiddenGrotto);
|
|
SlotsB2 = addExtraTableSlots(B2Slots, SlotsB2_Swarm, SlotsB2_HiddenGrotto);
|
|
SlotsW2 = addExtraTableSlots(W2Slots, SlotsW2_Swarm, SlotsW2_HiddenGrotto);
|
|
|
|
Evolves5 = new EvolutionTree(new[] { Util.getBinaryResource("evos_g5.pkl") }, GameVersion.BW, PersonalTable.BW, MaxSpeciesID_5);
|
|
}
|
|
// Gen 6
|
|
{
|
|
StaticX = getStaticEncounters(GameVersion.X);
|
|
StaticY = getStaticEncounters(GameVersion.Y);
|
|
StaticA = getStaticEncounters(GameVersion.AS);
|
|
StaticO = getStaticEncounters(GameVersion.OR);
|
|
|
|
var XSlots = getEncounterTables(GameVersion.X);
|
|
var YSlots = getEncounterTables(GameVersion.Y);
|
|
MarkG6XYSlots(ref XSlots);
|
|
MarkG6XYSlots(ref YSlots);
|
|
SlotsX = addExtraTableSlots(XSlots, SlotsXYAlt);
|
|
SlotsY = addExtraTableSlots(YSlots, SlotsXYAlt);
|
|
|
|
SlotsA = getEncounterTables(GameVersion.AS);
|
|
SlotsO = getEncounterTables(GameVersion.OR);
|
|
MarkG6AOSlots(ref SlotsA);
|
|
MarkG6AOSlots(ref SlotsO);
|
|
|
|
Evolves6 = new EvolutionTree(Data.unpackMini(Util.getBinaryResource("evos_ao.pkl"), "ao"), GameVersion.ORAS, PersonalTable.AO, MaxSpeciesID_6);
|
|
}
|
|
// Gen 7
|
|
{
|
|
StaticSN = getStaticEncounters(GameVersion.SN);
|
|
StaticMN = getStaticEncounters(GameVersion.MN);
|
|
var REG_SN = getEncounterTables(GameVersion.SN);
|
|
var REG_MN = getEncounterTables(GameVersion.MN);
|
|
var SOS_SN = getEncounterTables(Util.getBinaryResource("encounter_sn_sos.pkl"), "sm");
|
|
var SOS_MN = getEncounterTables(Util.getBinaryResource("encounter_mn_sos.pkl"), "sm");
|
|
MarkG7REGSlots(ref REG_SN);
|
|
MarkG7REGSlots(ref REG_MN);
|
|
MarkG7SMSlots(ref SOS_SN);
|
|
MarkG7SMSlots(ref SOS_MN);
|
|
SlotsSN = addExtraTableSlots(REG_SN, SOS_SN, Encounter_Pelago_SM, Encounter_Pelago_SN);
|
|
SlotsMN = addExtraTableSlots(REG_MN, SOS_MN, Encounter_Pelago_SM, Encounter_Pelago_MN);
|
|
|
|
Evolves7 = new EvolutionTree(Data.unpackMini(Util.getBinaryResource("evos_sm.pkl"), "sm"), GameVersion.SM, PersonalTable.SM, MaxSpeciesID_7);
|
|
}
|
|
}
|
|
|
|
private static void FixPersonalTableY()
|
|
{
|
|
// Personal Table from Yellow do not have yellow catch rate for Pikachu and Kadabra, have RedBlue instead
|
|
PersonalTable.Y[25].CatchRate = 163; // Pikachu
|
|
PersonalTable.Y[64].CatchRate = 96; // Kadabra
|
|
}
|
|
|
|
// Moves
|
|
internal static int[] getMinLevelLearnMove(int species, int Generation, List<int> moves)
|
|
{
|
|
var r = new int[moves.Count];
|
|
switch (Generation)
|
|
{
|
|
case 1:
|
|
{
|
|
int index = PersonalTable.RB.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
|
|
var pi_rb = (PersonalInfoG1)PersonalTable.RB[index];
|
|
var pi_y = (PersonalInfoG1)PersonalTable.Y[index];
|
|
|
|
for (int m = 0; m < moves.Count; m++)
|
|
{
|
|
if (pi_rb.Moves.Contains(moves[m]) || pi_y.Moves.Contains(moves[m]))
|
|
r[m] = 1;
|
|
else
|
|
{
|
|
var rb_level = LevelUpRB[index].getLevelLearnMove(moves[m]);
|
|
var y_level = LevelUpY[index].getLevelLearnMove(moves[m]);
|
|
// 0 means it is not learned in that game, select the other game
|
|
r[m] = rb_level == 0 ? y_level :
|
|
y_level == 0 ? rb_level :
|
|
Math.Min(rb_level, y_level);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
internal static int[] getMaxLevelLearnMove(int species, int Generation, List<int> moves)
|
|
{
|
|
var r = new int[moves.Count];
|
|
switch (Generation)
|
|
{
|
|
case 1:
|
|
{
|
|
int index = PersonalTable.RB.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
|
|
var pi_rb = (PersonalInfoG1)PersonalTable.RB[index];
|
|
var pi_y = (PersonalInfoG1)PersonalTable.Y[index];
|
|
|
|
for (int m = 0; m < moves.Count; m++)
|
|
{
|
|
if (pi_rb.Moves.Contains(moves[m]) && pi_y.Moves.Contains(moves[m]))
|
|
r[m] = 1;
|
|
else
|
|
r[m] = Math.Max(LevelUpRB[index].getLevelLearnMove(moves[m]), LevelUpY[index].getLevelLearnMove(moves[m]));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
internal static List<int>[] getExclusiveMoves(int species1, int species2, int Generation, List<int> tmhm, int[] moves)
|
|
{
|
|
// Return from two species the exclusive moves that only one could learn and also the current pokemon have it in its current moveset
|
|
var moves1 = getLvlMoves(species1, 0, Generation, 1, 100).Distinct().ToList();
|
|
var moves2 = getLvlMoves(species2, 0, Generation, 1, 100).Distinct().ToList();
|
|
var common = moves1.Intersect(moves2).ToList();
|
|
// Remove common moves, remove not learned moves and remove tmhm
|
|
moves1.RemoveAll(x => !moves.Contains(x) || common.Contains(x) || tmhm.Contains(x));
|
|
moves2.RemoveAll(x => !moves.Contains(x) || common.Contains(x) || tmhm.Contains(x));
|
|
return new[] { moves1, moves2 };
|
|
}
|
|
internal static IEnumerable<int> getLvlMoves(int species, int form, int Generation, int minlvl, int lvl, GameVersion Version = GameVersion.Any)
|
|
{
|
|
var r = new List<int>();
|
|
var ver = Version;
|
|
switch (Generation)
|
|
{
|
|
case 1:
|
|
{
|
|
int index = PersonalTable.RB.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
|
|
var pi_rb = (PersonalInfoG1)PersonalTable.RB[index];
|
|
var pi_y = (PersonalInfoG1)PersonalTable.Y[index];
|
|
if (minlvl == 1)
|
|
{
|
|
r.AddRange(pi_rb.Moves);
|
|
r.AddRange(pi_y.Moves);
|
|
}
|
|
r.AddRange(LevelUpRB[index].getMoves(lvl, minlvl));
|
|
r.AddRange(LevelUpY[index].getMoves(lvl, minlvl));
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
int index = PersonalTable.C.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
r.AddRange(LevelUpGS[index].getMoves(lvl));
|
|
r.AddRange(LevelUpC[index].getMoves(lvl));
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
int index = PersonalTable.E.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
if (index == 386)
|
|
{
|
|
switch (form)
|
|
{
|
|
case 0: r.AddRange(LevelUpRS[index].getMoves(lvl)); break;
|
|
case 1: r.AddRange(LevelUpFR[index].getMoves(lvl)); break;
|
|
case 2: r.AddRange(LevelUpLG[index].getMoves(lvl)); break;
|
|
case 3: r.AddRange(LevelUpE[index].getMoves(lvl)); break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Emerald level up table are equals to R/S level up tables
|
|
r.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
|
|
r.AddRange(LevelUpLG[index].getMoves(lvl));
|
|
}
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
int index = PersonalTable.HGSS.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
r.AddRange(LevelUpDP[index].getMoves(lvl));
|
|
r.AddRange(LevelUpPt[index].getMoves(lvl));
|
|
r.AddRange(LevelUpHGSS[index].getMoves(lvl));
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
int index = PersonalTable.B2W2.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
r.AddRange(LevelUpBW[index].getMoves(lvl));
|
|
r.AddRange(LevelUpB2W2[index].getMoves(lvl));
|
|
break;
|
|
}
|
|
case 6:
|
|
switch (ver)
|
|
{
|
|
case GameVersion.Any: // Start at the top, hit every table
|
|
case GameVersion.X:
|
|
case GameVersion.Y:
|
|
case GameVersion.XY:
|
|
{
|
|
int index = PersonalTable.XY.getFormeIndex(species, form);
|
|
if (index == 0)
|
|
return r;
|
|
r.AddRange(LevelUpXY[index].getMoves(lvl));
|
|
if (ver == GameVersion.Any) // Fall Through
|
|
goto case GameVersion.ORAS;
|
|
break;
|
|
}
|
|
|
|
case GameVersion.AS:
|
|
case GameVersion.OR:
|
|
case GameVersion.ORAS:
|
|
{
|
|
int index = PersonalTable.AO.getFormeIndex(species, form);
|
|
if (index == 0)
|
|
return r;
|
|
r.AddRange(LevelUpAO[index].getMoves(lvl));
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 7:
|
|
switch (ver)
|
|
{
|
|
case GameVersion.Any:
|
|
case GameVersion.SN:
|
|
case GameVersion.MN:
|
|
case GameVersion.SM:
|
|
{
|
|
int index = PersonalTable.SM.getFormeIndex(species, form);
|
|
r.AddRange(LevelUpSM[index].getMoves(lvl));
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return r;
|
|
}
|
|
return r;
|
|
}
|
|
internal static void RemoveFutureMoves(PKM pkm, ref List<int>[] validLevelMoves, ref List<int>[] validTMHM, ref List<int>[] validTutor)
|
|
{
|
|
var FutureMoves = new List<int>();
|
|
FutureMoves.AddRange(validLevelMoves[pkm.Format]);
|
|
FutureMoves.AddRange(validTMHM[pkm.Format]);
|
|
FutureMoves.AddRange(validTutor[pkm.Format]);
|
|
if (pkm.Format >= 3)
|
|
{
|
|
if (pkm.VC1)
|
|
{
|
|
validLevelMoves[1]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
validTMHM[1]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
validTutor[1]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
}
|
|
else if (pkm.VC2)
|
|
{
|
|
for (int i = 2; i >= 1; i--)
|
|
{
|
|
validLevelMoves[i]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
validTMHM[i]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
validTutor[i]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
|
|
if (validLevelMoves[i] == null || validTMHM[i] == null || validTutor[i] == null)
|
|
continue;
|
|
FutureMoves.AddRange(validLevelMoves[i].Concat(validTMHM[i]).Concat(validTutor[i]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = pkm.Format - 1; i >= pkm.GenNumber; i--)
|
|
{
|
|
validLevelMoves[i]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
validTMHM[i]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
validTutor[i]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
|
|
if (validLevelMoves[i] == null || validTMHM[i] == null || validTutor[i] == null)
|
|
continue;
|
|
FutureMoves.AddRange(validLevelMoves[i].Concat(validTMHM[i]).Concat(validTutor[i]));
|
|
}
|
|
}
|
|
}
|
|
else if (validLevelMoves.Length >= 3)
|
|
{
|
|
int tradeback = pkm.Format == 2 ? 1 : 2;
|
|
validLevelMoves[tradeback]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
validTMHM[tradeback]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
validTutor[tradeback]?.RemoveAll(x => FutureMoves.Contains(x));
|
|
}
|
|
}
|
|
internal static List<int>[] getValidMovesAllGens(PKM pkm, DexLevel[][] evoChains, int minLvLG1 = 1, bool LVL = true, bool Tutor = true, bool Machine = true, bool MoveReminder = true, bool RemoveTransferHM = true)
|
|
{
|
|
List<int>[] Moves = new List<int>[evoChains.Length];
|
|
for (int i = 1; i < evoChains.Length; i++)
|
|
if (evoChains[i].Any())
|
|
Moves[i] = getValidMoves(pkm, evoChains[i], i, minLvLG1, LVL, Tutor, Machine, MoveReminder, RemoveTransferHM).ToList();
|
|
else
|
|
Moves[i] = new List<int>();
|
|
return Moves;
|
|
}
|
|
internal static IEnumerable<int> getValidMoves(PKM pkm, DexLevel[][] evoChains, int minLvLG1 = 1, bool LVL = true, bool Tutor = true, bool Machine = true, bool MoveReminder = true, bool RemoveTransferHM = true)
|
|
{
|
|
GameVersion version = (GameVersion)pkm.Version;
|
|
if (!pkm.IsUntraded)
|
|
version = GameVersion.Any;
|
|
return getValidMoves(pkm, version, evoChains, minLvLG1: minLvLG1, LVL: LVL, Relearn: false, Tutor: Tutor, Machine: Machine, MoveReminder: MoveReminder, RemoveTransferHM: RemoveTransferHM);
|
|
}
|
|
internal static IEnumerable<int> getValidMoves(PKM pkm, DexLevel[] evoChain, int generation, int minLvLG1 = 1, bool LVL = true, bool Tutor = true, bool Machine = true, bool MoveReminder = true, bool RemoveTransferHM = true)
|
|
{
|
|
GameVersion version = (GameVersion)pkm.Version;
|
|
if (!pkm.IsUntraded)
|
|
version = GameVersion.Any;
|
|
return getValidMoves(pkm, version, evoChain, generation, minLvLG1: minLvLG1, LVL: LVL, Relearn: false, Tutor: Tutor, Machine: Machine, MoveReminder: MoveReminder, RemoveTransferHM: RemoveTransferHM);
|
|
}
|
|
internal static IEnumerable<int> getValidRelearn(PKM pkm, int skipOption)
|
|
{
|
|
List<int> r = new List<int> { 0 };
|
|
if (pkm.GenNumber < 6 || pkm.VC)
|
|
return r;
|
|
|
|
int species = getBaseSpecies(pkm, skipOption);
|
|
r.AddRange(getRelearnLVLMoves(pkm, species, 1, pkm.AltForm));
|
|
|
|
int form = pkm.AltForm;
|
|
if (pkm.Format == 6 && pkm.Species != 678)
|
|
form = 0;
|
|
|
|
r.AddRange(getEggMoves(pkm, species, form));
|
|
if (pkm.Species != 489)
|
|
r.AddRange(getRelearnLVLMoves(pkm, species, 100, pkm.AltForm));
|
|
return r.Distinct();
|
|
}
|
|
internal static List<int>[] getShedinjaEvolveMoves(PKM pkm, int lvl = -1, int generation = 0)
|
|
{
|
|
var size = pkm.Format > 3 ? 4 : 3;
|
|
List<int>[] r = new List<int>[size + 1];
|
|
for (int i = 1; i <= size; i++)
|
|
r[i] = new List<int>();
|
|
if (lvl == -1)
|
|
lvl = pkm.CurrentLevel;
|
|
if (pkm.Species != 292 || lvl < 20)
|
|
return r;
|
|
|
|
// If nincada evolves into Ninjask an learn in the evolution a move from ninjask learnset pool
|
|
// Shedinja would appear with that move learned. Only one move above level 20 allowed, only in generations 3 and 4
|
|
switch (generation)
|
|
{
|
|
case 0: // Default (both)
|
|
case 3: // Ninjask have the same learnset in every gen 3 games
|
|
if (pkm.InhabitedGeneration(3))
|
|
r[3] = LevelUpE[291].getMoves(lvl, 20).ToList();
|
|
|
|
if (generation == 0)
|
|
goto case 4;
|
|
break;
|
|
case 4: // Ninjask have the same learnset in every gen 4 games
|
|
if (pkm.InhabitedGeneration(4))
|
|
r[4] = LevelUpPt[291].getMoves(lvl, 20).ToList();
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
internal static IEnumerable<int> getBaseEggMoves(PKM pkm, int skipOption, GameVersion gameSource, int lvl)
|
|
{
|
|
int species = getBaseEggSpecies(pkm, skipOption);
|
|
|
|
if (gameSource == GameVersion.Any)
|
|
gameSource = (GameVersion) pkm.Version;
|
|
|
|
switch (gameSource)
|
|
{
|
|
case GameVersion.GS:
|
|
if (pkm.InhabitedGeneration(2))
|
|
if (pkm.Format == 1)
|
|
return LevelUpGS[species].getMoves(lvl).Where(m => m <= MaxMoveID_1).ToArray();
|
|
else
|
|
return LevelUpGS[species].getMoves(lvl);
|
|
break;
|
|
case GameVersion.C:
|
|
if (pkm.InhabitedGeneration(2))
|
|
if (pkm.Format == 1)
|
|
return LevelUpC[species].getMoves(lvl).Where(m => m <= MaxMoveID_1).ToArray();
|
|
else
|
|
return LevelUpC[species].getMoves(lvl);
|
|
break;
|
|
|
|
case GameVersion.R:
|
|
case GameVersion.S:
|
|
case GameVersion.RS:
|
|
if (pkm.InhabitedGeneration(3))
|
|
return LevelUpRS[species].getMoves(lvl);
|
|
break;
|
|
case GameVersion.E:
|
|
if (pkm.InhabitedGeneration(3))
|
|
return LevelUpE[species].getMoves(lvl);
|
|
break;
|
|
case GameVersion.FR:
|
|
case GameVersion.LG:
|
|
case GameVersion.FRLG:
|
|
// only difference in FR/LG is deoxys which doesn't breed.
|
|
if (pkm.InhabitedGeneration(3))
|
|
return LevelUpFR[species].getMoves(lvl);
|
|
break;
|
|
|
|
case GameVersion.D:
|
|
case GameVersion.P:
|
|
case GameVersion.DP:
|
|
if (pkm.InhabitedGeneration(4))
|
|
return LevelUpDP[species].getMoves(lvl);
|
|
break;
|
|
case GameVersion.Pt:
|
|
if (pkm.InhabitedGeneration(4))
|
|
return LevelUpPt[species].getMoves(lvl);
|
|
break;
|
|
case GameVersion.HG:
|
|
case GameVersion.SS:
|
|
case GameVersion.HGSS:
|
|
if (pkm.InhabitedGeneration(4))
|
|
return LevelUpHGSS[species].getMoves(lvl);
|
|
break;
|
|
|
|
case GameVersion.B:
|
|
case GameVersion.W:
|
|
case GameVersion.BW:
|
|
if (pkm.InhabitedGeneration(5))
|
|
return LevelUpBW[species].getMoves(lvl);
|
|
break;
|
|
|
|
case GameVersion.B2:
|
|
case GameVersion.W2:
|
|
case GameVersion.B2W2:
|
|
if (pkm.InhabitedGeneration(5))
|
|
return LevelUpBW[species].getMoves(lvl);
|
|
break;
|
|
|
|
case GameVersion.X:
|
|
case GameVersion.Y:
|
|
case GameVersion.XY:
|
|
if (pkm.InhabitedGeneration(6))
|
|
return LevelUpXY[species].getMoves(lvl);
|
|
break;
|
|
|
|
case GameVersion.AS:
|
|
case GameVersion.OR:
|
|
case GameVersion.ORAS:
|
|
if (pkm.InhabitedGeneration(6))
|
|
return LevelUpAO[species].getMoves(lvl);
|
|
break;
|
|
|
|
case GameVersion.SN:
|
|
case GameVersion.MN:
|
|
case GameVersion.SM:
|
|
int index = PersonalTable.SM.getFormeIndex(species, pkm.AltForm);
|
|
if (pkm.InhabitedGeneration(7))
|
|
return LevelUpSM[index].getMoves(lvl);
|
|
break;
|
|
}
|
|
return new int[0];
|
|
}
|
|
internal static List<int> getValidPostEvolutionMoves(PKM pkm, int Species, DexLevel[][] evoChains, GameVersion Version)
|
|
{
|
|
// Return moves that the pokemon could learn after evolving
|
|
var moves = new List<int>();
|
|
for (int i = 1; i < evoChains.Length; i++)
|
|
if (evoChains[i].Any())
|
|
moves.AddRange(getValidPostEvolutionMoves(pkm, Species, evoChains[i], i, Version));
|
|
if (pkm.GenNumber >= 6)
|
|
moves.AddRange(pkm.RelearnMoves.Where(m => m != 0));
|
|
return moves.Distinct().ToList();
|
|
}
|
|
internal static IEnumerable<int> getValidPostEvolutionMoves(PKM pkm, int Species, DexLevel[] evoChain, int Generation, GameVersion Version)
|
|
{
|
|
var evomoves = new List<int>();
|
|
var index = Array.FindIndex(evoChain, e => e.Species == Species);
|
|
for (int i = 0; i <= index; i++)
|
|
{
|
|
var evo = evoChain[i];
|
|
var moves = getMoves(pkm, evo.Species, 1, evo.Level, pkm.AltForm, moveTutor: true, Version: Version, LVL: true, specialTutors: true, Machine: true, MoveReminder: true, RemoveTransferHM: false, Generation: Generation);
|
|
// Moves from Species or any species after in the evolution phase
|
|
evomoves.AddRange(moves);
|
|
}
|
|
return evomoves;
|
|
}
|
|
internal static List<int>[] getExclusivePreEvolutionMoves(PKM pkm, int Species, DexLevel[][] evoChains, GameVersion Version)
|
|
{
|
|
// Return moves that the pokemon could only learn throught the preevolution Species
|
|
List<int>[] Moves = new List<int>[evoChains.Length];
|
|
for (int i = 1; i < evoChains.Length; i++)
|
|
if (evoChains[i].Any())
|
|
Moves[i] = getExclusivePreEvolutionMoves(pkm, Species, evoChains[i], i, Version).ToList();
|
|
else
|
|
Moves[i] = new List<int>();
|
|
return Moves;
|
|
}
|
|
internal static IEnumerable<int> getExclusivePreEvolutionMoves(PKM pkm, int Species, DexLevel[] evoChain, int Generation, GameVersion Version)
|
|
{
|
|
var preevomoves = new List<int>();
|
|
var evomoves = new List<int>();
|
|
var index = Array.FindIndex(evoChain, e => e.Species == Species);
|
|
for (int i = 0; i < evoChain.Length; i++)
|
|
{
|
|
var evo = evoChain[i];
|
|
var moves = getMoves(pkm, evo.Species, 1, evo.Level, pkm.AltForm, moveTutor: true, Version: Version, LVL: true, specialTutors: true, Machine: true, MoveReminder: true, RemoveTransferHM: false, Generation: Generation);
|
|
if (i >= index)
|
|
// Moves from Species or any species bellow in the evolution phase
|
|
preevomoves.AddRange(moves);
|
|
else
|
|
// Moves in phase evolutions after the limit species, this moves should be removed
|
|
evomoves.AddRange(moves);
|
|
}
|
|
preevomoves.RemoveAll(x => evomoves.Contains(x));
|
|
return preevomoves.Distinct().ToList();
|
|
}
|
|
internal static List<int>[] getBaseEggMoves(PKM pkm, GameVersion gameSource, int lvl)
|
|
{
|
|
if (getSplitBreedGeneration(pkm.GenNumber).Contains(pkm.Species))
|
|
return new[]
|
|
{
|
|
getBaseEggMoves(pkm, 0, gameSource, lvl).ToList(),
|
|
getBaseEggMoves(pkm, 1, gameSource, lvl).ToList(),
|
|
};
|
|
return new[] { getBaseEggMoves(pkm, 0, gameSource, lvl).ToList(), };
|
|
}
|
|
internal static List<int>[] getEggMoves(PKM pkm, GameVersion Version)
|
|
{
|
|
if (getSplitBreedGeneration(pkm.GenNumber).Contains(pkm.Species))
|
|
return new[]
|
|
{
|
|
getEggMoves(pkm, getBaseEggSpecies(pkm, 0), 0, Version).ToList(),
|
|
getEggMoves(pkm, getBaseEggSpecies(pkm, 1), 0, Version).ToList()
|
|
};
|
|
return new[] { getEggMoves(pkm, getBaseEggSpecies(pkm, 0), 0, Version).ToList() };
|
|
}
|
|
internal static IEnumerable<int> getEggMoves(PKM pkm, int skipOption, GameVersion Version)
|
|
{
|
|
return getEggMoves(pkm, getBaseEggSpecies(pkm, skipOption), 0, Version);
|
|
}
|
|
internal static IEnumerable<EncounterStatic> getG3SpecialEggEncounter(PKM pkm)
|
|
{
|
|
IEnumerable<DexLevel> dl = getValidPreEvolutions(pkm, MaxSpeciesID_3);
|
|
var sttctable = pkm.E ? EventEgg_G3_Common : pkm.FRLG ? EventEgg_FRLG : EventEgg_RS;
|
|
var table = sttctable.Where(e => dl.Any(d => d.Species == e.Species));
|
|
foreach (EncounterStatic e in table)
|
|
{
|
|
if (pkm.Moves.All(m => !e.Moves.Contains(m))) // No special move
|
|
continue;
|
|
if (e.Nature != Nature.Random && pkm.Nature != (int)e.Nature)
|
|
continue;
|
|
if (e.Gender != -1 && e.Gender != pkm.Gender)
|
|
continue;
|
|
|
|
yield return e;
|
|
}
|
|
}
|
|
|
|
// Encounter
|
|
internal static EncounterLink getValidLinkGifts(PKM pkm)
|
|
{
|
|
switch (pkm.GenNumber)
|
|
{
|
|
case 6:
|
|
return LinkGifts6.FirstOrDefault(g => g.Species == pkm.Species && g.Level == pkm.Met_Level);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
internal static bool IsSafariSlot(SlotType t)
|
|
{
|
|
if (t == SlotType.Grass_Safari || t == SlotType.Surf_Safari ||
|
|
t == SlotType.Rock_Smash_Safari || t == SlotType.Pokeradar_Safari ||
|
|
t == SlotType.Old_Rod_Safari || t == SlotType.Good_Rod_Safari || t == SlotType.Super_Rod_Safari)
|
|
return true;
|
|
return false;
|
|
}
|
|
internal static EncounterSlot[] getValidWildEncounters(PKM pkm, GameVersion gameSource = GameVersion.Any)
|
|
{
|
|
if (gameSource == GameVersion.Any)
|
|
gameSource = (GameVersion) pkm.Version;
|
|
|
|
List<EncounterSlot> s = new List<EncounterSlot>();
|
|
|
|
foreach (var area in getEncounterAreas(pkm, gameSource))
|
|
s.AddRange(getValidEncounterSlots(pkm, area, DexNav: pkm.AO, gameSource: gameSource));
|
|
|
|
if (s.Count <= 1 || 3 > pkm.GenNumber || pkm.GenNumber > 4 || (pkm.Gen3 && pkm.HasOriginalMetLocation))
|
|
return s.Any() ? s.ToArray() : null;
|
|
|
|
// If has original met location or there is only one possible slot does not check safari zone
|
|
// defer to ball legality
|
|
var IsSafariBall = pkm.Ball == 5;
|
|
var s_Safari = IsSafariBall
|
|
? s.Where(slot => IsSafariSlot(slot.Type)).ToList()
|
|
: s.Where(slot => !IsSafariSlot(slot.Type)).ToList();
|
|
if (s_Safari.Any())
|
|
// safari ball only in safari zones and non safari ball only outside safari zones
|
|
s = s_Safari.ToList();
|
|
|
|
if (s.Count <= 1 || pkm.GenNumber != 4)
|
|
return s.Any() ? s.ToArray() : null;
|
|
|
|
// BCC should be checked even if the pokemon have original met location, there are encounters of the same species
|
|
// in the national park as both normal wild encounters and contest encounters
|
|
var IsSportsBall = pkm.Ball == 0x18;
|
|
var s_BugContest = IsSportsBall
|
|
? s.Where(slot => slot.Type == SlotType.BugContest).ToList()
|
|
: s.Where(slot => slot.Type != SlotType.BugContest).ToList();
|
|
if (s_BugContest.Any())
|
|
// sport ball only in BCC and non sport balls only outside BCC
|
|
s = s_BugContest.ToList();
|
|
|
|
// If there is only one valid encounter defer encountertype check to verify encounter type
|
|
if (s.Count <= 1 || pkm.Format == 7)
|
|
return s.Any() ? s.ToArray() : null;
|
|
|
|
var s_EncounterTypes = s.Where(slot => slot.TypeEncounter.Contains(pkm.EncounterType)).ToList();
|
|
if (s_EncounterTypes.Any())
|
|
return s_EncounterTypes.ToArray();
|
|
|
|
return s.Any() ? s.ToArray() : null;
|
|
}
|
|
internal static List<EncounterStatic> getValidStaticEncounter(PKM pkm, GameVersion gameSource = GameVersion.Any)
|
|
{
|
|
if (gameSource == GameVersion.Any)
|
|
gameSource = (GameVersion)pkm.Version;
|
|
|
|
// Get possible encounters
|
|
IEnumerable<EncounterStatic> poss = getStaticEncounters(pkm, gameSource: gameSource);
|
|
|
|
int lvl = getMinLevelEncounter(pkm);
|
|
if (lvl <= 0)
|
|
return null;
|
|
|
|
// Back Check against pkm
|
|
var enc = getMatchingStaticEncounters(pkm, poss, lvl).ToList();
|
|
|
|
// Filter for encounter types; type is cleared on 6->7 transfer
|
|
if (!pkm.Gen4 || pkm.Format >= 7 || enc.Count <= 1)
|
|
return enc.Count == 0 ? null : enc;
|
|
|
|
int type = pkm.EncounterType;
|
|
var enctype = type == 0
|
|
? enc.Where(z => !(z is EncounterStaticTyped)) // no typed encounters
|
|
: enc.Where(z => z is EncounterStaticTyped && ((EncounterStaticTyped)z).TypeEncounter.Contains(type));
|
|
|
|
// Filter only if there are encounters that match types and encounters that do not match types, like wild encounters
|
|
// IF there is only one of the two possibilities encounter type check will detect encounter type does not match encounter
|
|
var encList = enctype.ToList();
|
|
if (encList.Count > 0)
|
|
enc = encList;
|
|
|
|
return enc.Count == 0 ? null : enc;
|
|
}
|
|
private static IEnumerable<EncounterStatic> getMatchingStaticEncounters(PKM pkm, IEnumerable<EncounterStatic> poss, int lvl)
|
|
{
|
|
foreach (EncounterStatic e in poss)
|
|
{
|
|
if (e.Nature != Nature.Random && pkm.Nature != (int)e.Nature)
|
|
continue;
|
|
if (pkm.Gen3 && e.EggLocation != 0)
|
|
{
|
|
// Hatched gen 3 gift egg can not be differentiated from normal eggs
|
|
if (!pkm.IsEgg || pkm.Format > 3)
|
|
continue;
|
|
if (e.EggLocation != pkm.Met_Location)
|
|
continue;
|
|
}
|
|
else if (e.EggLocation != pkm.Egg_Location)
|
|
continue;
|
|
if (pkm.HasOriginalMetLocation)
|
|
{
|
|
if (e.Location != 0 && e.Location != pkm.Met_Location)
|
|
continue;
|
|
if (e.Level != lvl)
|
|
continue;
|
|
}
|
|
else if (e.Level > lvl)
|
|
continue;
|
|
if (e.Gender != -1 && e.Gender != pkm.Gender)
|
|
continue;
|
|
if (e.Form != pkm.AltForm && !e.SkipFormCheck && !getCanFormChange(pkm, e.Species))
|
|
continue;
|
|
|
|
if (pkm.Format == 1 && pkm.Gen1_NotTradeback)
|
|
{
|
|
var catch_rate = (pkm as PK1).Catch_Rate;
|
|
var japanese = (pkm as PK1).Japanese;
|
|
// Pure gen 1, trades can be filter by catch rate
|
|
if ((pkm.Species == 25 || pkm.Species == 26) && catch_rate == 190)
|
|
// Red Blue Pikachu, is not a static encounter
|
|
continue;
|
|
|
|
if (e.Version == GameVersion.Stadium)
|
|
{
|
|
if (e.Species != 054 && !Stadium_CatchRate.Contains(catch_rate))
|
|
continue;
|
|
// Amnesia Psyduck have different catch rate in japanese stadium and international stadium
|
|
if (e.Species == 054 && japanese && catch_rate != 167)
|
|
continue;
|
|
if (e.Species == 054 && !japanese && catch_rate != 168)
|
|
continue;
|
|
}
|
|
else if (catch_rate != PersonalTable.RB[e.Species].CatchRate && catch_rate != PersonalTable.Y[e.Species].CatchRate)
|
|
continue;
|
|
}
|
|
|
|
// Defer to EC/PID check
|
|
// if (e.Shiny != null && e.Shiny != pkm.IsShiny)
|
|
// continue;
|
|
|
|
// Defer ball check to later
|
|
// if (e.Gift && pkm.Ball != 4) // PokéBall
|
|
// continue;
|
|
|
|
if (!AllowGBCartEra && GameVersion.GBCartEraOnly.Contains(e.Version))
|
|
continue; // disallow gb cart era encounters (as they aren't obtainable by Main/VC series)
|
|
|
|
yield return e;
|
|
}
|
|
}
|
|
internal static EncounterTrade getValidIngameTrade(PKM pkm, GameVersion gameSource = GameVersion.Any)
|
|
{
|
|
if (gameSource == GameVersion.Any)
|
|
gameSource = (GameVersion) pkm.Version;
|
|
if (pkm.VC || pkm.Format <= 2)
|
|
return getValidEncounterTradeVC(pkm, gameSource);
|
|
|
|
int lang = pkm.Language;
|
|
if (lang == 0 || lang == 6)
|
|
return null;
|
|
|
|
int lvl = getMinLevelEncounter(pkm);
|
|
if (lvl <= 0)
|
|
return null;
|
|
// Get valid pre-evolutions
|
|
IEnumerable<DexLevel> p = getValidPreEvolutions(pkm);
|
|
|
|
EncounterTrade[] table = getEncounterTradeTable(pkm);
|
|
var poss = table?.Where(f => p.Any(r => r.Species == f.Species) && f.Version.Contains((GameVersion)pkm.Version));
|
|
return poss?.FirstOrDefault(z => getEncounterTradeValid(pkm, z, lvl));
|
|
}
|
|
private static bool getEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl)
|
|
{
|
|
for (int i = 0; i < 6; i++)
|
|
if (z.IVs[i] != -1 && z.IVs[i] != pkm.IVs[i])
|
|
return false;
|
|
|
|
if (z.Shiny ^ pkm.IsShiny) // Are PIDs static?
|
|
return false;
|
|
if (z.TID != pkm.TID)
|
|
return false;
|
|
if (z.SID != pkm.SID)
|
|
return false;
|
|
if (pkm.HasOriginalMetLocation)
|
|
{
|
|
z.Location = z.Location > 0 ? z.Location : EncounterTrade.DefaultMetLocation[pkm.GenNumber - 3];
|
|
if (z.Location != pkm.Met_Location)
|
|
return false;
|
|
if (pkm.Format < 5)
|
|
{
|
|
if (z.Level > lvl)
|
|
return false;
|
|
}
|
|
else if (z.Level != lvl)
|
|
return false;
|
|
}
|
|
else if (z.Level > lvl)
|
|
return false;
|
|
|
|
if (z.Nature != Nature.Random && (int)z.Nature != pkm.Nature)
|
|
return false;
|
|
if (z.Gender != -1 && z.Gender != pkm.Gender)
|
|
return false;
|
|
if (z.OTGender != -1 && z.OTGender != pkm.OT_Gender)
|
|
return false;
|
|
// if (z.Ability == 4 ^ pkm.AbilityNumber == 4) // defer to Ability
|
|
// countinue;
|
|
|
|
return true;
|
|
}
|
|
internal static GameVersion[] getGen1GameEncounter(PKM pk)
|
|
{
|
|
if (pk.Format != 1 || !pk.Gen1_NotTradeback)
|
|
return new[] { GameVersion.RD, GameVersion.YW };
|
|
if (25 <= pk.Species && pk.Species <= 26)
|
|
// Yellow Pikachu detected by its special catch rate
|
|
return new[] { ((pk as PK1).Catch_Rate == 163) ? GameVersion.YW : GameVersion.RD };
|
|
if (64 <= pk.Species && pk.Species <= 65)
|
|
// Yellow Kadabra detected by its special catch rate
|
|
return new[] { ((pk as PK1).Catch_Rate == 96) ? GameVersion.YW : GameVersion.RD };
|
|
if (148 <= pk.Species && pk.Species <= 149 && ((pk as PK1).Catch_Rate == 27))
|
|
// Yellow Dragonair detected by its special catch rate, is have another catch rate could be red/blue dratini or yellow dratini
|
|
return new[] { GameVersion.YW };
|
|
return new[] { GameVersion.RD, GameVersion.YW };
|
|
}
|
|
internal static IEnumerable<int> getInitialMovesGBEncounter(int species, int lvl, GameVersion ver)
|
|
{
|
|
int[] InitialMoves;
|
|
int[] LevelUpMoves;
|
|
int diff;
|
|
switch (ver)
|
|
{
|
|
case GameVersion.YW:
|
|
case GameVersion.RD:
|
|
case GameVersion.BU:
|
|
case GameVersion.GN:
|
|
{
|
|
var LevelTable = ver == GameVersion.YW ? LevelUpY : LevelUpRB;
|
|
int index = PersonalTable.RB.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return new int[0];
|
|
LevelUpMoves = LevelTable[species].getEncounterMoves(lvl);
|
|
diff = 4 - LevelUpMoves.Length;
|
|
if (diff == 0)
|
|
return LevelUpMoves.ToArray();
|
|
InitialMoves = ver == GameVersion.YW ? ((PersonalInfoG1)PersonalTable.Y[index]).Moves : ((PersonalInfoG1)PersonalTable.RB[index]).Moves;
|
|
break;
|
|
}
|
|
case GameVersion.C:
|
|
case GameVersion.GS:
|
|
{
|
|
if (species == 235)
|
|
return new[] { 166 }; // Smeargle only learns Sketch, is duplicated in the level up tables
|
|
var LevelTable = ver == GameVersion.C ? LevelUpC : LevelUpGS;
|
|
int index = PersonalTable.C.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return new int[0];
|
|
LevelUpMoves = LevelTable[species].getEncounterMoves(2, lvl);
|
|
diff = 4 - LevelUpMoves.Length;
|
|
if (diff == 0)
|
|
return LevelUpMoves.ToArray();
|
|
// Level Up 1 moves are initial moves, it can be duplicated in levels 2-100
|
|
InitialMoves = LevelTable[species].getEncounterMoves(1, 1);
|
|
break;
|
|
}
|
|
default:
|
|
return new int[0];
|
|
}
|
|
// Initial Moves could be duplicated in the level up table
|
|
// level up table moves have preferences
|
|
var moves = InitialMoves.Where(p => p != 0 && !LevelUpMoves.Any(m => m == p)).ToList();
|
|
// If not all the personal table move cant be included the last moves have preference
|
|
int pop = moves.Count - diff;
|
|
if (pop > 0)
|
|
moves.RemoveRange(0, pop);
|
|
// The order for the pokemon default moves are first moves from personal table and then moves from level up table
|
|
return moves.Union(LevelUpMoves).ToArray();
|
|
}
|
|
internal static int getMoveMinLevelGBEncounter(int species, int lvl, GameVersion[] versions)
|
|
{
|
|
int movelvl = 100;
|
|
foreach(GameVersion ver in versions)
|
|
{
|
|
movelvl = Math.Min(movelvl, getMoveMinLevelGBEncounter(species, lvl, ver));
|
|
}
|
|
return movelvl;
|
|
}
|
|
internal static int getMoveMinLevelGBEncounter(int species, int lvl, GameVersion ver = GameVersion.Any)
|
|
{
|
|
int movelvl = 100;
|
|
switch (ver)
|
|
{
|
|
case GameVersion.RD:
|
|
case GameVersion.BU:
|
|
case GameVersion.GN:
|
|
case GameVersion.RBY:
|
|
{
|
|
movelvl = LevelUpRB[species].getMinMoveLevel(lvl);
|
|
if (ver.Contains(GameVersion.YW))
|
|
goto case GameVersion.YW;
|
|
return movelvl;
|
|
}
|
|
case GameVersion.YW:
|
|
{
|
|
movelvl = Math.Min(movelvl, LevelUpY[species].getMinMoveLevel(lvl));
|
|
if (ver.Contains(GameVersion.GS))
|
|
goto case GameVersion.GS;
|
|
return movelvl;
|
|
}
|
|
case GameVersion.GS:
|
|
case GameVersion.GSC:
|
|
{
|
|
movelvl = Math.Min(movelvl, LevelUpGS[species].getMinMoveLevel(lvl));
|
|
if (ver.Contains(GameVersion.C))
|
|
goto case GameVersion.C;
|
|
return movelvl;
|
|
}
|
|
case GameVersion.C:
|
|
{
|
|
return Math.Min(movelvl, LevelUpC[species].getMinMoveLevel(lvl));
|
|
}
|
|
}
|
|
return movelvl;
|
|
}
|
|
private static EncounterTrade[] getEncounterTradeTable(PKM pkm)
|
|
{
|
|
switch (pkm.GenNumber)
|
|
{
|
|
case 3:
|
|
return pkm.FRLG ? TradeGift_FRLG : TradeGift_RSE;
|
|
case 4:
|
|
return pkm.HGSS ? TradeGift_HGSS : TradeGift_DPPt;
|
|
case 5:
|
|
return pkm.B2W2 ? TradeGift_B2W2 : TradeGift_BW;
|
|
case 6:
|
|
return pkm.XY ? TradeGift_XY : TradeGift_AO;
|
|
case 7:
|
|
return pkm.SM ? TradeGift_SM : null;
|
|
}
|
|
return null;
|
|
}
|
|
internal static int getRequiredMoveCount(PKM pk, int[] moves, List<int>[] learn, List<int>[] tmhm, List<int>[] tutor, int[] initialmoves)
|
|
{
|
|
if (pk.Format != 1 || !pk.Gen1_NotTradeback) // No MoveDeleter
|
|
return 1; // Move deleter exits, slots from 2 onwards can allways be empty
|
|
|
|
int required = getRequiredMoveCount(pk, moves, learn, initialmoves);
|
|
if (required >= 4)
|
|
return 4;
|
|
|
|
// tm, hm and tutor moves replace a free slots if the pokemon have less than 4 moves
|
|
// Ignore tm, hm and tutor moves already in the learnset table
|
|
required += moves.Where(m => m != 0 && initialmoves.Union(learn[1]).All(l => l != m) && (tmhm[1].Any(t => t == m) || tutor[1].Any(t => t == m))).Count();
|
|
|
|
return Math.Min(4, required);
|
|
}
|
|
private static int getRequiredMoveCount(PKM pk, int[] moves, List<int>[] learn, int[] initialmoves)
|
|
{
|
|
if (SpecialMinMoveSlots.Contains(pk.Species))
|
|
return getRequiredMoveCountSpecial(pk, moves, learn);
|
|
|
|
// A pokemon is captured with initial moves and can't forget any until have all 4 slots used
|
|
// If it has learn a move before having 4 it will be in one of the free slots
|
|
int required = getRequiredMoveSlotsRegular(pk, moves, learn, initialmoves);
|
|
return required != 0 ? required : getRequiredMoveCountDecrement(pk, moves, learn, initialmoves);
|
|
}
|
|
private static int getRequiredMoveSlotsRegular(PKM pk, int[] moves, List<int>[] learn, int[] initialmoves)
|
|
{
|
|
int species = pk.Species;
|
|
int catch_rate = (pk as PK1).Catch_Rate;
|
|
// Caterpie and Metapod evolution lines have different count of possible slots available if captured in different evolutionary phases
|
|
// Example: a level 7 caterpie evolved into metapod will have 3 learned moves, a captured metapod will have only 1 move
|
|
if ((species == 011 || species == 012) && catch_rate == 120)
|
|
{
|
|
// Captured as Metapod without Caterpie moves
|
|
return initialmoves.Union(learn[1]).Distinct().Count(lm => lm != 0 && !G1CaterpieMoves.Contains(lm));
|
|
// There is no valid Butterfree encounter in generation 1 games
|
|
}
|
|
if ((species == 014 || species == 015) && (catch_rate == 45 || catch_rate == 120))
|
|
{
|
|
if (species == 15 && catch_rate == 45) // Captured as Beedril without Weedle and Kakuna moves
|
|
return initialmoves.Union(learn[1]).Distinct().Count(lm => lm != 0 && !G1KakunaMoves.Contains(lm));
|
|
|
|
// Captured as Kakuna without Weedle moves
|
|
return initialmoves.Union(learn[1]).Distinct().Count(lm => lm != 0 && !G1WeedleMoves.Contains(lm));
|
|
}
|
|
|
|
return getRequiredMoveCountSpecies3(species, pk.CurrentLevel, moves) ? 3 : 0; // no match
|
|
}
|
|
private static bool getRequiredMoveCountSpecies3(int species, int level, int[] moves)
|
|
{
|
|
// Species that evolve and learn the 4th move as evolved species at a greather level than base species
|
|
// The 4th move is included in the level up table set as a preevolution move,
|
|
// it should be removed from the used slots count if is not the learn move
|
|
switch (species)
|
|
{
|
|
case 017: return level < 21 && !moves.Contains(018); // Pidgeotto without Whirlwind
|
|
case 028: return level < 27 && !moves.Contains(040); // Sandslash without Poison Sting
|
|
case 047: return level < 30 && !moves.Contains(147); // Parasect without Spore
|
|
case 055: return level < 39 && !moves.Contains(093); // Golduck without Confusion
|
|
case 087: return level < 44 && !moves.Contains(156); // Dewgong without Rest
|
|
case 093:
|
|
case 094: return level < 29 && !moves.Contains(095); // Haunter/Gengar without Hypnosis
|
|
case 110: return level < 39 && !moves.Contains(108); // Weezing without Smoke Screen
|
|
}
|
|
return false;
|
|
}
|
|
private static int getRequiredMoveCountDecrement(PKM pk, int[] moves, List<int>[] learn, int[] initialmoves)
|
|
{
|
|
int catch_rate = (pk as PK1).Catch_Rate;
|
|
int usedslots = initialmoves.Union(learn[1]).Where(m => m != 0).Distinct().Count();
|
|
// Yellow optional moves, reduce usedslots if the yellow move is not present
|
|
// The count wont go bellow 1 because the yellow moves were already counted and are not the only initial or level up moves
|
|
if (pk.Species == 031) //Venonat
|
|
{
|
|
// ignore Venomoth, by the time Venonat evolved it will always have 4 moves
|
|
if (pk.CurrentLevel >= 11 && !moves.Contains(48)) // Supersonic
|
|
usedslots--;
|
|
if (pk.CurrentLevel >= 19 && !moves.Contains(93)) // Confusion
|
|
usedslots--;
|
|
}
|
|
if (pk.Species == 056 && pk.CurrentLevel >= 9 && !moves.Contains(67)) // Mankey Yellow Low Kick, Primeape will always have 4 moves
|
|
usedslots--;
|
|
|
|
if (064 == pk.Species || pk.Species == 065)
|
|
{
|
|
if (catch_rate != 100)// Initial Yellow Kadabra Kinesis (move 134)
|
|
usedslots--;
|
|
if (catch_rate == 200 && pk.CurrentLevel < 20) // Kadabra Disable, not learned until 20 if captured as Abra (move 50)
|
|
usedslots--;
|
|
}
|
|
if (104 == pk.Species || pk.Species == 105) // Cubone and Marowak
|
|
{
|
|
if (!moves.Contains(39)) // Initial Yellow Tail Whip
|
|
usedslots--;
|
|
if (!moves.Contains(125)) // Initial Yellow Bone Club
|
|
usedslots--;
|
|
if (pk.Species == 105 && pk.CurrentLevel < 33 && !moves.Contains(116)) // Marowak evolved without Focus Energy
|
|
usedslots--;
|
|
}
|
|
if (pk.Species == 113) // Chansey
|
|
{
|
|
if (!moves.Contains(39)) // Yellow Initial Tail Whip
|
|
usedslots--;
|
|
if (!moves.Contains(3)) // Yellow Lvl 12 and Initial Red/Blue Double Slap
|
|
usedslots--;
|
|
}
|
|
if (pk.Species == 130 && pk.CurrentLevel < 32) // Wild Gyarados from yellow do not learn splash, evolved gyarados do not learn tackle
|
|
usedslots--;
|
|
if (pk.Species == 127 && pk.CurrentLevel >= 21 && !moves.Contains(20)) // Pinsir Yellow Bind
|
|
usedslots--;
|
|
return usedslots;
|
|
}
|
|
|
|
private static int getRequiredMoveCountSpecial(PKM pk, int[] moves, List<int>[] learn)
|
|
{
|
|
// Species with few mandatory slots, species with stone evolutions that could evolve at lower level and do not learn any more moves
|
|
// and Pikachu and Nidoran family, those only have mandatory the initial moves and a few have one level up moves,
|
|
// every other move could be avoided switching game or evolving
|
|
var basespecies = getBaseSpecies(pk);
|
|
var maxlevel = 1;
|
|
var minlevel = 1;
|
|
if (029 <= pk.Species && pk.Species <= 034 && pk.CurrentLevel >= 8)
|
|
maxlevel = 8; // Always lean a third move at level 8
|
|
if (pk.Species == 114)
|
|
{
|
|
//Tangela moves before level 32 are different in red/blue and yellow
|
|
minlevel = 32;
|
|
maxlevel = pk.CurrentLevel;
|
|
}
|
|
var mandatory = minlevel <= pk.CurrentLevel ? getLvlMoves(basespecies, 0, 1, minlevel, maxlevel).Where(m => m != 0).Distinct().ToList() : new List<int>();
|
|
if (pk.Species == 103 && pk.CurrentLevel >= 28) // Exeggutor
|
|
{
|
|
// At level 28 learn different move if is a Exeggute or Exeggutor
|
|
if (moves.Contains(73))
|
|
mandatory.Add(73); // Leech Seed level 28 Exeggute
|
|
if (moves.Contains(23))
|
|
mandatory.Add(23); // Stomp level 28 Exeggutor
|
|
}
|
|
if (pk.Species == 25 && pk.CurrentLevel >= 33)
|
|
mandatory.Add(97); // Pikachu always learn Agility
|
|
if (pk.Species == 114)
|
|
mandatory.Add(132); // Tangela always learn Constrict as Initial Move
|
|
|
|
// Add to used slots the non-mandatory moves from the learnset table that the pokemon have learned
|
|
return mandatory.Count + moves.Where(m => m != 0 && mandatory.All(l => l != m) && learn[1].Any(t => t == m)).Count();
|
|
}
|
|
|
|
private static EncounterTrade getValidEncounterTradeVC(PKM pkm, GameVersion gameSource)
|
|
{
|
|
var p = getValidPreEvolutions(pkm).ToArray();
|
|
|
|
switch (gameSource)
|
|
{
|
|
case GameVersion.RBY:
|
|
var table = !AllowGen1Tradeback ? TradeGift_RBY_NoTradeback : TradeGift_RBY_Tradeback;
|
|
return getValidEncounterTradeVC1(pkm, p, table);
|
|
case GameVersion.GSC:
|
|
return getValidEncounterTradeVC2(pkm, p);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
private static EncounterTrade getValidEncounterTradeVC2(PKM pkm, DexLevel[] p)
|
|
{
|
|
// Check GSC trades. Reuse generic table fetch-match
|
|
var z = getValidEncounterTradeVC1(pkm, p, TradeGift_GSC);
|
|
|
|
// Filter Criteria
|
|
if (z?.Gender != pkm.Gender)
|
|
return null;
|
|
if (z.TID != pkm.TID)
|
|
return null;
|
|
if (!z.IVs.SequenceEqual(pkm.IVs))
|
|
return null;
|
|
if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != z.Location)
|
|
return null;
|
|
|
|
int index = Array.IndexOf(TradeGift_GSC, z);
|
|
if (TradeGift_GSC_OTs[index].All(ot => ot != pkm.OT_Name))
|
|
return null;
|
|
|
|
return z;
|
|
}
|
|
private static EncounterTrade getValidEncounterTradeVC1(PKM pkm, DexLevel[] p, IEnumerable<EncounterTrade> table)
|
|
{
|
|
var z = table.FirstOrDefault(f => p.Any(r => r.Species == f.Species));
|
|
if (z == null)
|
|
return null;
|
|
if (z.Level > pkm.CurrentLevel) // minimum required level
|
|
return null;
|
|
if (pkm.Format != 1 || !pkm.Gen1_NotTradeback)
|
|
return z;
|
|
|
|
// Even if the in game trade use the tables with source pokemon allowing generaion 2 games, the traded pokemon could be a non-tradeback pokemon
|
|
var catch_rate = (pkm as PK1)?.Catch_Rate;
|
|
if (z is EncounterTradeCatchRate)
|
|
{
|
|
if (catch_rate != ((EncounterTradeCatchRate)z).Catch_Rate)
|
|
return null;
|
|
}
|
|
else if (catch_rate != PersonalTable.RB[z.Species].CatchRate && catch_rate != PersonalTable.Y[z.Species].CatchRate)
|
|
return null;
|
|
return z;
|
|
}
|
|
private static GBEncounterData getEncounter12(PKM pkm, GameVersion game)
|
|
{
|
|
var gen = game == GameVersion.GSC ? 2 : 1;
|
|
bool WasEgg = !pkm.Gen1_NotTradeback && game == GameVersion.GSC && getWasEgg23(pkm) && !NoHatchFromEgg.Contains(pkm.Species);
|
|
if (WasEgg)
|
|
{
|
|
// Further Filtering
|
|
if (pkm.Format < 3)
|
|
{
|
|
WasEgg &= pkm.Met_Location == 0 || pkm.Met_Level == 1; // 2->1->2 clears met info
|
|
WasEgg &= pkm.CurrentLevel >= 5;
|
|
}
|
|
}
|
|
|
|
// Since encounter matching is super weak due to limited stored data in the structure
|
|
// Calculate all 3 at the same time and pick the best result (by species).
|
|
// Favor special event move gifts as Static Encounters when applicable
|
|
var maxspeciesorigin = game == GameVersion.GSC ? MaxSpeciesID_2 : MaxSpeciesID_1;
|
|
DexLevel[] vs = getValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin).ToArray();
|
|
|
|
var s = getValidStaticEncounter(pkm, game);
|
|
|
|
// Valid stadium and non-stadium encounters, return only non-stadium encounters, they are less restrictive
|
|
if (s != null && game == GameVersion.RBY && s.Any(st => st.Species != 54 && st.Version == GameVersion.Stadium) && s.Any(st => st.Species != 54 && st.Version != GameVersion.Stadium))
|
|
s = s.Where(st => st.Version != GameVersion.Stadium).ToList();
|
|
|
|
var e = getValidWildEncounters(pkm, game);
|
|
var t = getValidIngameTrade(pkm, game);
|
|
|
|
if (s == null && e == null && t == null && !WasEgg)
|
|
return null;
|
|
|
|
const byte invalid = 255;
|
|
var sm = s != null ? vs.Reverse().First(evo => s.Any(slot => slot.Species == evo.Species)).Species : invalid;
|
|
var em = e != null ? vs.Reverse().First(evo => e.Any(slot => slot.Species == evo.Species)).Species : invalid;
|
|
var tm = t?.Species ?? invalid;
|
|
|
|
// check for special move static encounter
|
|
var special = s?.FirstOrDefault(m => m.Moves != null && m.Moves[0] != 0 && pkm.Moves.Contains(m.Moves[0]));
|
|
if (special != null) // return with high priority
|
|
return new GBEncounterData(pkm, gen, special);
|
|
|
|
if (game == GameVersion.GSC)
|
|
{
|
|
if (t != null && t.TID != 0)
|
|
return new GBEncounterData(pkm, 2, t); // gen2 trade
|
|
if (WasEgg && new[] { sm, em, tm }.Min(a => a) >= 5)
|
|
return new GBEncounterData(getBaseEggSpecies(pkm)); // gen2 egg
|
|
}
|
|
if (em <= sm && em <= tm)
|
|
return new GBEncounterData(pkm, gen, e.Where(slot => slot.Species == em).OrderBy(slot => slot.LevelMin).First());
|
|
if (sm <= em && sm <= tm)
|
|
return new GBEncounterData(pkm, gen, s.Where(slot => slot.Species == sm).OrderBy(slot => slot.Level).First());
|
|
if (tm <= sm && tm <= em)
|
|
return new GBEncounterData(pkm, gen, t);
|
|
return null;
|
|
}
|
|
internal static List<GBEncounterData> getEncounter12(PKM pkm)
|
|
{
|
|
var g1 = pkm.Gen2_NotTradeback ? null : getEncounter12(pkm, GameVersion.RBY);
|
|
var g2 = pkm.Gen1_NotTradeback || !AllowGen2VCTransfer ? null : getEncounter12(pkm, GameVersion.GSC);
|
|
if (g1 == null && g2 == null)
|
|
return null;
|
|
if (g1 == null || g2 == null)
|
|
return new List<GBEncounterData> { g1 ?? g2 };
|
|
|
|
if (g1.Encounter is EncounterTrade && getEncounterTrade1Valid(pkm))
|
|
return new List<GBEncounterData> { g1 };
|
|
|
|
// Both generations can provide an encounter. Return highest preference
|
|
g1.MoveLevel = getMoveMinLevelGBEncounter(g1.Species, g1.Level, getGen1GameEncounter(pkm));
|
|
if (g1.Type > g2.Type)
|
|
// Return also generation 2 encounters, is only needed to determine that pokemon is not exclusive from generation 1
|
|
return new List<GBEncounterData> { g1, g2 };
|
|
if (g1.Type <= g2.Type ||
|
|
// Return lowest level encounter
|
|
g2.MoveLevel < g1.MoveLevel)
|
|
{
|
|
// Return also generation 1 moves, it could have different encounter moves
|
|
return new List<GBEncounterData> { g2, g1 };
|
|
}
|
|
return new List<GBEncounterData> { g1, g2 };
|
|
}
|
|
internal static bool getEncounterTrade1Valid(PKM pkm)
|
|
{
|
|
string ot = pkm.OT_Name;
|
|
string tr = pkm.Format <= 2 ? "TRAINER" : "Trainer"; // decaps on transfer
|
|
return ot == "トレーナー" || ot == tr;
|
|
}
|
|
internal static EncounterSlot[] getValidFriendSafari(PKM pkm)
|
|
{
|
|
if (!pkm.XY)
|
|
return null;
|
|
if (pkm.Met_Location != 148) // Friend Safari
|
|
return null;
|
|
if (pkm.Met_Level != 30)
|
|
return null;
|
|
|
|
IEnumerable<DexLevel> vs = getValidPreEvolutions(pkm);
|
|
List<EncounterSlot> slots = new List<EncounterSlot>();
|
|
foreach (DexLevel d in vs.Where(d => FriendSafari.Contains(d.Species) && d.Level >= 30))
|
|
{
|
|
slots.Add(new EncounterSlot
|
|
{
|
|
Species = d.Species,
|
|
LevelMin = 30,
|
|
LevelMax = 30,
|
|
Form = 0,
|
|
Type = SlotType.FriendSafari,
|
|
});
|
|
}
|
|
|
|
return slots.Any() ? slots.ToArray() : null;
|
|
}
|
|
internal static bool getWasEgg23(PKM pkm)
|
|
{
|
|
if (pkm.IsEgg)
|
|
return true;
|
|
if (pkm.Format > 2 && pkm.Ball != 4)
|
|
return false;
|
|
if (pkm.Format == 3)
|
|
return pkm.WasEgg;
|
|
|
|
int lvl = pkm.CurrentLevel;
|
|
if (lvl < 5)
|
|
return false;
|
|
|
|
if (pkm.Format > 3 && pkm.Met_Level <5)
|
|
return false;
|
|
if (pkm.Format > 3 && pkm.FatefulEncounter)
|
|
return false;
|
|
|
|
return getEvolutionValid(pkm);
|
|
}
|
|
|
|
// Generation Specific Fetching
|
|
private static EvolutionTree getEvolutionTable(PKM pkm)
|
|
{
|
|
return getEvolutionTable(pkm.Format);
|
|
}
|
|
private static EvolutionTree getEvolutionTable(int generation)
|
|
{
|
|
switch (generation)
|
|
{
|
|
case 1:
|
|
return Evolves1;
|
|
case 2:
|
|
return Evolves2;
|
|
case 3:
|
|
return Evolves3;
|
|
case 4:
|
|
return Evolves4;
|
|
case 5:
|
|
return Evolves5;
|
|
case 6:
|
|
return Evolves6;
|
|
default:
|
|
return Evolves7;
|
|
}
|
|
}
|
|
|
|
internal static IEnumerable<MysteryGift> getValidGifts(PKM pkm)
|
|
{
|
|
switch (pkm.GenNumber)
|
|
{
|
|
case 4:
|
|
return getMatchingPCD(pkm, MGDB_G4);
|
|
case 5:
|
|
return getMatchingPGF(pkm, MGDB_G5);
|
|
case 6:
|
|
return getMatchingWC6(pkm, MGDB_G6);
|
|
case 7:
|
|
return getMatchingWC7(pkm, MGDB_G7);
|
|
default:
|
|
return new List<MysteryGift>();
|
|
}
|
|
}
|
|
private static IEnumerable<MysteryGift> getMatchingPCD(PKM pkm, IEnumerable<MysteryGift> DB)
|
|
{
|
|
var validPCD = new List<MysteryGift>();
|
|
if (DB == null)
|
|
return validPCD;
|
|
|
|
if (pkm.Species == 490 && (pkm.WasEgg || pkm.IsEgg)) // Manaphy
|
|
{
|
|
int loc = pkm.IsEgg ? pkm.Met_Location : pkm.Egg_Location;
|
|
bool valid = loc == 2002; // Link Trade Egg
|
|
valid |= loc == 3001 && !pkm.IsShiny; // Ranger & notShiny
|
|
if (pkm.IsEgg && !pkm.IsNative) // transferred
|
|
valid = false;
|
|
if (valid)
|
|
validPCD.Add(new PGT { Data = { [0] = 7, [8] = 1 } });
|
|
return validPCD;
|
|
}
|
|
|
|
var vs = getValidPreEvolutions(pkm).ToArray();
|
|
foreach (PCD mg in DB.OfType<PCD>().Where(wc => vs.Any(dl => dl.Species == wc.Species)))
|
|
{
|
|
var wc = mg.Gift.PK;
|
|
if (pkm.Egg_Location == 0) // Not Egg
|
|
{
|
|
if (wc.SID != pkm.SID) continue;
|
|
if (wc.TID != pkm.TID) continue;
|
|
if (wc.OT_Name != pkm.OT_Name) continue;
|
|
if (wc.OT_Gender != pkm.OT_Gender) continue;
|
|
if (wc.Version != 0 && wc.Version != pkm.Version) continue;
|
|
if (wc.Language != 0 && wc.Language != pkm.Language) continue;
|
|
}
|
|
if (wc.AltForm != pkm.AltForm && vs.All(dl => !getCanFormChange(pkm, dl.Species))) continue;
|
|
|
|
if (wc.IsEgg)
|
|
{
|
|
if (wc.Egg_Location + 3000 != pkm.Egg_Location && pkm.Egg_Location != 2002) // traded
|
|
continue;
|
|
if (wc.CurrentLevel != pkm.Met_Level) continue;
|
|
if (!pkm.IsNative)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (pkm.Format != 4) // transferred
|
|
{
|
|
// met location: deferred to general transfer check
|
|
if (wc.CurrentLevel > pkm.Met_Level) continue;
|
|
}
|
|
else
|
|
{
|
|
if (wc.Egg_Location + 3000 != pkm.Met_Location) continue;
|
|
if (wc.CurrentLevel != pkm.Met_Level) continue;
|
|
}
|
|
}
|
|
|
|
if (wc.Ball != pkm.Ball) continue;
|
|
if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) continue;
|
|
if (wc.PID == 1 && pkm.IsShiny) continue;
|
|
if (wc.Gender != 3 && wc.Gender != pkm.Gender) continue;
|
|
|
|
if (wc.CNT_Cool > pkm.CNT_Cool) continue;
|
|
if (wc.CNT_Beauty > pkm.CNT_Beauty) continue;
|
|
if (wc.CNT_Cute > pkm.CNT_Cute) continue;
|
|
if (wc.CNT_Smart > pkm.CNT_Smart) continue;
|
|
if (wc.CNT_Tough > pkm.CNT_Tough) continue;
|
|
if (wc.CNT_Sheen > pkm.CNT_Sheen) continue;
|
|
|
|
// Some checks are best performed separately as they are caused by users screwing up valid data.
|
|
// if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality
|
|
// RIBBONS: Defer to ribbon legality
|
|
|
|
validPCD.Add(mg);
|
|
}
|
|
return validPCD;
|
|
}
|
|
private static IEnumerable<MysteryGift> getMatchingPGF(PKM pkm, IEnumerable<MysteryGift> DB)
|
|
{
|
|
var validPGF = new List<MysteryGift>();
|
|
if (DB == null)
|
|
return validPGF;
|
|
|
|
var vs = getValidPreEvolutions(pkm).ToArray();
|
|
foreach (PGF wc in DB.OfType<PGF>().Where(wc => vs.Any(dl => dl.Species == wc.Species)))
|
|
{
|
|
if (pkm.Egg_Location == 0) // Not Egg
|
|
{
|
|
if (wc.SID != pkm.SID) continue;
|
|
if (wc.TID != pkm.TID) continue;
|
|
if (wc.OT != pkm.OT_Name) continue;
|
|
if (wc.PID != 0 && pkm.PID != wc.PID) continue;
|
|
if (wc.PIDType == 0 && pkm.IsShiny) continue;
|
|
if (wc.PIDType == 2 && !pkm.IsShiny) continue;
|
|
if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) continue;
|
|
if (wc.Language != 0 && wc.Language != pkm.Language) continue;
|
|
}
|
|
if (wc.Form != pkm.AltForm && vs.All(dl => !getCanFormChange(pkm, dl.Species))) continue;
|
|
|
|
if (wc.IsEgg)
|
|
{
|
|
if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded
|
|
continue;
|
|
if (!pkm.IsNative)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (wc.EggLocation != pkm.Egg_Location) continue;
|
|
if (wc.MetLocation != pkm.Met_Location) continue;
|
|
}
|
|
|
|
if (wc.Level != pkm.Met_Level) continue;
|
|
if (wc.Ball != pkm.Ball) continue;
|
|
if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) continue;
|
|
if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) continue;
|
|
if (wc.Gender != 2 && wc.Gender != pkm.Gender) continue;
|
|
|
|
if (wc.CNT_Cool > pkm.CNT_Cool) continue;
|
|
if (wc.CNT_Beauty > pkm.CNT_Beauty) continue;
|
|
if (wc.CNT_Cute > pkm.CNT_Cute) continue;
|
|
if (wc.CNT_Smart > pkm.CNT_Smart) continue;
|
|
if (wc.CNT_Tough > pkm.CNT_Tough) continue;
|
|
if (wc.CNT_Sheen > pkm.CNT_Sheen) continue;
|
|
|
|
// Some checks are best performed separately as they are caused by users screwing up valid data.
|
|
// if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality
|
|
// RIBBONS: Defer to ribbon legality
|
|
|
|
validPGF.Add(wc);
|
|
}
|
|
return validPGF;
|
|
}
|
|
private static IEnumerable<MysteryGift> getMatchingWC6(PKM pkm, IEnumerable<MysteryGift> DB)
|
|
{
|
|
List<MysteryGift> validWC6 = new List<MysteryGift>();
|
|
if (DB == null)
|
|
return validWC6;
|
|
var vs = getValidPreEvolutions(pkm).ToArray();
|
|
foreach (WC6 wc in DB.OfType<WC6>().Where(wc => vs.Any(dl => dl.Species == wc.Species)))
|
|
{
|
|
if (pkm.Egg_Location == 0) // Not Egg
|
|
{
|
|
if (wc.CardID != pkm.SID) continue;
|
|
if (wc.TID != pkm.TID) continue;
|
|
if (wc.OT != pkm.OT_Name) continue;
|
|
if (wc.OTGender != pkm.OT_Gender) continue;
|
|
if (wc.PIDType == 0 && pkm.PID != wc.PID) continue;
|
|
if (wc.PIDType == 2 && !pkm.IsShiny) continue;
|
|
if (wc.PIDType == 3 && pkm.IsShiny) continue;
|
|
if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) continue;
|
|
if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) continue;
|
|
if (wc.Language != 0 && wc.Language != pkm.Language) continue;
|
|
}
|
|
if (wc.Form != pkm.AltForm && vs.All(dl => !getCanFormChange(pkm, dl.Species))) continue;
|
|
|
|
if (wc.IsEgg)
|
|
{
|
|
if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded
|
|
continue;
|
|
if (!pkm.IsNative)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (wc.EggLocation != pkm.Egg_Location) continue;
|
|
if (wc.MetLocation != pkm.Met_Location) continue;
|
|
}
|
|
|
|
if (wc.Level != pkm.Met_Level) continue;
|
|
if (wc.Ball != pkm.Ball) continue;
|
|
if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) continue;
|
|
if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) continue;
|
|
if (wc.Gender != 3 && wc.Gender != pkm.Gender) continue;
|
|
|
|
if (wc.CNT_Cool > pkm.CNT_Cool) continue;
|
|
if (wc.CNT_Beauty > pkm.CNT_Beauty) continue;
|
|
if (wc.CNT_Cute > pkm.CNT_Cute) continue;
|
|
if (wc.CNT_Smart > pkm.CNT_Smart) continue;
|
|
if (wc.CNT_Tough > pkm.CNT_Tough) continue;
|
|
if (wc.CNT_Sheen > pkm.CNT_Sheen) continue;
|
|
|
|
// Some checks are best performed separately as they are caused by users screwing up valid data.
|
|
// if (!wc.RelearnMoves.SequenceEqual(pkm.RelearnMoves)) continue; // Defer to relearn legality
|
|
// if (wc.OT.Length > 0 && pkm.CurrentHandler != 1) continue; // Defer to ownership legality
|
|
// if (wc.OT.Length > 0 && pkm.OT_Friendship != PKX.getBaseFriendship(pkm.Species)) continue; // Friendship
|
|
// if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality
|
|
// RIBBONS: Defer to ribbon legality
|
|
|
|
validWC6.Add(wc);
|
|
}
|
|
return validWC6;
|
|
}
|
|
private static IEnumerable<MysteryGift> getMatchingWC7(PKM pkm, IEnumerable<MysteryGift> DB)
|
|
{
|
|
List<MysteryGift> validWC7 = new List<MysteryGift>();
|
|
if (DB == null)
|
|
return validWC7;
|
|
var vs = getValidPreEvolutions(pkm).ToArray();
|
|
foreach (WC7 wc in DB.OfType<WC7>().Where(wc => vs.Any(dl => dl.Species == wc.Species)))
|
|
{
|
|
if (pkm.Egg_Location == 0) // Not Egg
|
|
{
|
|
if (wc.OTGender != 3)
|
|
{
|
|
if (wc.SID != pkm.SID) continue;
|
|
if (wc.TID != pkm.TID) continue;
|
|
if (wc.OTGender != pkm.OT_Gender) continue;
|
|
}
|
|
if (!string.IsNullOrEmpty(wc.OT) && wc.OT != pkm.OT_Name) continue;
|
|
if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) continue;
|
|
if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) continue;
|
|
if (wc.Language != 0 && wc.Language != pkm.Language) continue;
|
|
}
|
|
if (wc.Form != pkm.AltForm && vs.All(dl => !getCanFormChange(pkm, dl.Species))) continue;
|
|
|
|
if (wc.IsEgg)
|
|
{
|
|
if (wc.EggLocation != pkm.Egg_Location && pkm.Egg_Location != 30002) // traded
|
|
continue;
|
|
if (!pkm.IsNative)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (wc.EggLocation != pkm.Egg_Location) continue;
|
|
if (wc.MetLocation != pkm.Met_Location) continue;
|
|
}
|
|
|
|
if (wc.MetLevel != pkm.Met_Level) continue;
|
|
if (wc.Ball != pkm.Ball) continue;
|
|
if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) continue;
|
|
if (wc.Nature != 0xFF && wc.Nature != pkm.Nature) continue;
|
|
if (wc.Gender != 3 && wc.Gender != pkm.Gender) continue;
|
|
|
|
if (wc.CNT_Cool > pkm.CNT_Cool) continue;
|
|
if (wc.CNT_Beauty > pkm.CNT_Beauty) continue;
|
|
if (wc.CNT_Cute > pkm.CNT_Cute) continue;
|
|
if (wc.CNT_Smart > pkm.CNT_Smart) continue;
|
|
if (wc.CNT_Tough > pkm.CNT_Tough) continue;
|
|
if (wc.CNT_Sheen > pkm.CNT_Sheen) continue;
|
|
|
|
if (wc.PIDType == 2 && !pkm.IsShiny) continue;
|
|
if (wc.PIDType == 3 && pkm.IsShiny) continue;
|
|
|
|
if ((pkm.SID << 16 | pkm.TID) == 0x79F57B49) // Greninja WC has variant PID and can arrive @ 36 or 37
|
|
{
|
|
if (!pkm.IsShiny)
|
|
validWC7.Add(wc);
|
|
continue;
|
|
}
|
|
if (wc.PIDType == 0 && pkm.PID != wc.PID) continue;
|
|
|
|
// Some checks are best performed separately as they are caused by users screwing up valid data.
|
|
// if (!wc.RelearnMoves.SequenceEqual(pkm.RelearnMoves)) continue; // Defer to relearn legality
|
|
// if (wc.OT.Length > 0 && pkm.CurrentHandler != 1) continue; // Defer to ownership legality
|
|
// if (wc.OT.Length > 0 && pkm.OT_Friendship != PKX.getBaseFriendship(pkm.Species)) continue; // Friendship
|
|
// if (wc.Level > pkm.CurrentLevel) continue; // Defer to level legality
|
|
// RIBBONS: Defer to ribbon legality
|
|
|
|
validWC7.Add(wc);
|
|
}
|
|
return validWC7;
|
|
}
|
|
internal static IEnumerable<int> getLineage(PKM pkm)
|
|
{
|
|
if (pkm.IsEgg)
|
|
return new[] {pkm.Species};
|
|
|
|
var table = getEvolutionTable(pkm);
|
|
var lineage = table.getValidPreEvolutions(pkm, pkm.CurrentLevel);
|
|
return lineage.Select(evolution => evolution.Species);
|
|
}
|
|
internal static int[] getWildBalls(PKM pkm)
|
|
{
|
|
switch (pkm.GenNumber)
|
|
{
|
|
case 1:
|
|
return WildPokeBalls1;
|
|
case 2:
|
|
return WildPokeBalls2;
|
|
case 3:
|
|
return WildPokeBalls3;
|
|
case 4:
|
|
return pkm.HGSS ? WildPokeBalls4_HGSS : WildPokeBalls4_DPPt;
|
|
case 5:
|
|
return WildPokeBalls5;
|
|
case 6:
|
|
return WildPokeballs6;
|
|
case 7:
|
|
return WildPokeballs7;
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal static int[] getSplitBreedGeneration(PKM pkm)
|
|
{
|
|
return getSplitBreedGeneration(pkm.GenNumber);
|
|
}
|
|
internal static int[] getSplitBreedGeneration(int generation)
|
|
{
|
|
switch (generation)
|
|
{
|
|
case 1: return new int[0];
|
|
case 2: return new int[0];
|
|
case 3: return SplitBreed_3;
|
|
case 4: return SplitBreed;
|
|
case 5: return SplitBreed;
|
|
case 6: return SplitBreed;
|
|
case 7: return SplitBreed;
|
|
default: return new int[0];
|
|
}
|
|
}
|
|
internal static int getMaxSpeciesOrigin(PKM pkm)
|
|
{
|
|
if (pkm.Format == 1 || pkm.VC1) // Gen1 VC could not trade with gen 2 yet
|
|
return getMaxSpeciesOrigin(1);
|
|
if (pkm.Format == 2 || pkm.VC2)
|
|
return getMaxSpeciesOrigin(2);
|
|
return getMaxSpeciesOrigin(pkm.GenNumber);
|
|
}
|
|
internal static int getMaxSpeciesOrigin(int generation)
|
|
{
|
|
switch (generation)
|
|
{
|
|
case 1: return MaxSpeciesID_1;
|
|
case 2: return MaxSpeciesID_2;
|
|
case 3: return MaxSpeciesID_3;
|
|
case 4: return MaxSpeciesID_4;
|
|
case 5: return MaxSpeciesID_5;
|
|
case 6: return MaxSpeciesID_6;
|
|
case 7: return MaxSpeciesID_7;
|
|
default: return -1;
|
|
}
|
|
}
|
|
internal static IEnumerable<int> getFutureGenEvolutions(int generation)
|
|
{
|
|
switch (generation)
|
|
{
|
|
case 1: return FutureEvolutionsGen1;
|
|
case 2: return FutureEvolutionsGen2;
|
|
case 3: return FutureEvolutionsGen3;
|
|
case 4: return FutureEvolutionsGen4;
|
|
case 5: return FutureEvolutionsGen5;
|
|
default: return new int[0];
|
|
}
|
|
}
|
|
|
|
internal static bool[] getReleasedHeldItems(int generation)
|
|
{
|
|
switch (generation)
|
|
{
|
|
case 2: return ReleasedHeldItems_2;
|
|
case 3: return ReleasedHeldItems_3;
|
|
case 4: return ReleasedHeldItems_4;
|
|
case 5: return ReleasedHeldItems_5;
|
|
case 6: return ReleasedHeldItems_6;
|
|
case 7: return ReleasedHeldItems_7;
|
|
default: return new bool[0];
|
|
}
|
|
}
|
|
internal static bool getHeldItemAllowed(int generation, int item)
|
|
{
|
|
if (item < 0)
|
|
return false;
|
|
if (item == 0)
|
|
return true;
|
|
|
|
var items = getReleasedHeldItems(generation);
|
|
return items.Length > item && items[item];
|
|
}
|
|
|
|
internal static bool getDexNavValid(PKM pkm)
|
|
{
|
|
if (!pkm.AO || !pkm.InhabitedGeneration(6))
|
|
return false;
|
|
|
|
IEnumerable<EncounterArea> locs = getDexNavAreas(pkm);
|
|
return locs.Select(loc => getValidEncounterSlots(pkm, loc, DexNav: true)).Any(slots => slots.Any(slot => slot.AllowDexNav && slot.DexNav));
|
|
}
|
|
internal static bool getHasEvolved(PKM pkm)
|
|
{
|
|
if (pkm.IsEgg)
|
|
return false;
|
|
|
|
return getValidPreEvolutions(pkm).Count() > 1;
|
|
}
|
|
internal static bool getHasEvolvedFormChange(PKM pkm)
|
|
{
|
|
if (pkm.IsEgg)
|
|
return false;
|
|
|
|
if (pkm.Format >= 7 && EvolveToAlolanForms.Contains(pkm.Species))
|
|
return pkm.AltForm == 1;
|
|
if (pkm.Species == 678 && pkm.Gender == 1)
|
|
return pkm.AltForm == 1;
|
|
if (pkm.Species == 773)
|
|
return true;
|
|
return false;
|
|
}
|
|
internal static bool getHasTradeEvolved(PKM pkm)
|
|
{
|
|
if (pkm.IsEgg)
|
|
return false;
|
|
|
|
var table = getEvolutionTable(pkm);
|
|
var lineage = table.getValidPreEvolutions(pkm, 100, skipChecks:true);
|
|
return lineage.Any(evolution => EvolutionMethod.TradeMethods.Any(method => method == evolution.Flag)); // Trade Evolutions
|
|
}
|
|
internal static bool getEvolutionValid(PKM pkm)
|
|
{
|
|
var curr = getValidPreEvolutions(pkm);
|
|
var poss = getValidPreEvolutions(pkm, lvl: 100, skipChecks: true);
|
|
|
|
if (getSplitBreedGeneration(pkm).Contains(getBaseSpecies(pkm, 1)))
|
|
return curr.Count() >= poss.Count() - 1;
|
|
return curr.Count() >= poss.Count();
|
|
}
|
|
internal static bool getCanFormChange(PKM pkm, int species)
|
|
{
|
|
if (FormChange.Contains(species))
|
|
return true;
|
|
if (getHasEvolvedFormChange(pkm))
|
|
return true;
|
|
if (pkm.Species == 718 && pkm.InhabitedGeneration(7) && pkm.AltForm == 3)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
internal static EncounterArea getCaptureLocation(PKM pkm)
|
|
{
|
|
return (from area in getEncounterSlots(pkm, 100)
|
|
let slots = getValidEncounterSlots(pkm, area, pkm.AO, ignoreLevel:true).ToArray()
|
|
where slots.Any()
|
|
select new EncounterArea
|
|
{
|
|
Location = area.Location, Slots = slots,
|
|
}).OrderBy(area => area.Slots.Min(x => x.LevelMin)).FirstOrDefault();
|
|
}
|
|
internal static EncounterStatic getRBYStaticTransfer(int species)
|
|
{
|
|
return new EncounterStatic
|
|
{
|
|
Species = species,
|
|
Gift = true, // Forces Poké Ball
|
|
Ability = TransferSpeciesDefaultAbility_1.Contains(species) ? 1 : 4, // Hidden by default, else first
|
|
Shiny = species == 151 ? (bool?)false : null,
|
|
Fateful = species == 151,
|
|
Location = 30013,
|
|
EggLocation = 0,
|
|
IV3 = true,
|
|
Version = GameVersion.RBY
|
|
};
|
|
}
|
|
internal static EncounterStatic getStaticLocation(PKM pkm, int species = -1)
|
|
{
|
|
switch (pkm.GenNumber)
|
|
{
|
|
case 1:
|
|
return getRBYStaticTransfer(species);
|
|
default:
|
|
return getStaticEncounters(pkm, 100).OrderBy(s => s.Level).FirstOrDefault();
|
|
}
|
|
}
|
|
|
|
public static int getLowestLevel(PKM pkm, int refSpecies = -1)
|
|
{
|
|
if (refSpecies == -1)
|
|
refSpecies = getBaseSpecies(pkm);
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
var table = getEvolutionTable(pkm);
|
|
var evos = table.getValidPreEvolutions(pkm, i, skipChecks:true).ToArray();
|
|
if (evos.Any(evo => evo.Species == refSpecies))
|
|
return evos.OrderByDescending(evo => evo.Level).First().Level;
|
|
}
|
|
return 100;
|
|
}
|
|
internal static bool getCanBeCaptured(int species, int gen, GameVersion version = GameVersion.Any)
|
|
{
|
|
switch (gen)
|
|
{
|
|
// Capture Memory only obtainable via Gen 6.
|
|
case 6:
|
|
switch (version)
|
|
{
|
|
case GameVersion.Any:
|
|
return getCanBeCaptured(species, SlotsX, StaticX, XY:true)
|
|
|| getCanBeCaptured(species, SlotsY, StaticY, XY:true)
|
|
|| getCanBeCaptured(species, SlotsA, StaticA)
|
|
|| getCanBeCaptured(species, SlotsO, StaticO);
|
|
case GameVersion.X:
|
|
return getCanBeCaptured(species, SlotsX, StaticX, XY:true);
|
|
case GameVersion.Y:
|
|
return getCanBeCaptured(species, SlotsY, StaticY, XY:true);
|
|
case GameVersion.AS:
|
|
return getCanBeCaptured(species, SlotsA, StaticA);
|
|
case GameVersion.OR:
|
|
return getCanBeCaptured(species, SlotsO, StaticO);
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
private static bool getCanBeCaptured(int species, IEnumerable<EncounterArea> area, IEnumerable<EncounterStatic> statics, bool XY = false)
|
|
{
|
|
if (XY && FriendSafari.Contains(species))
|
|
return true;
|
|
|
|
if (area.Any(loc => loc.Slots.Any(slot => slot.Species == species)))
|
|
return true;
|
|
if (statics.Any(enc => enc.Species == species && !enc.Gift))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
internal static bool getCanLearnMachineMove(PKM pkm, int move, IEnumerable<int> generations, GameVersion version = GameVersion.Any)
|
|
{
|
|
return generations.Any(generation => getCanLearnMachineMove(pkm, move, generation, version));
|
|
}
|
|
internal static bool getCanRelearnMove(PKM pkm, int move, IEnumerable<int> generations, GameVersion version = GameVersion.Any)
|
|
{
|
|
return generations.Any(generation => getCanRelearnMove(pkm, move, generation, version));
|
|
}
|
|
internal static bool getCanLearnMove(PKM pkm, int move, IEnumerable<int> generations, GameVersion version = GameVersion.Any)
|
|
{
|
|
return generations.Any(generation => getCanLearnMove(pkm, move, generation, version));
|
|
}
|
|
internal static bool getCanKnowMove(PKM pkm, int move, IEnumerable<int> generations, GameVersion version = GameVersion.Any)
|
|
{
|
|
return generations.Any(generation => getCanKnowMove(pkm, move, generation, version));
|
|
}
|
|
internal static bool getCanLearnMachineMove(PKM pkm, int move, int generation, GameVersion version = GameVersion.Any)
|
|
{
|
|
return getValidMoves(pkm, version, getValidPreEvolutions(pkm).ToArray(), generation, Machine: true).Contains(move);
|
|
}
|
|
internal static bool getCanRelearnMove(PKM pkm, int move, int generation, GameVersion version = GameVersion.Any)
|
|
{
|
|
return getValidMoves(pkm, version, getValidPreEvolutions(pkm).ToArray(), generation, LVL: true, Relearn: true).Contains(move);
|
|
}
|
|
internal static bool getCanLearnMove(PKM pkm, int move, int generation, GameVersion version = GameVersion.Any)
|
|
{
|
|
return getValidMoves(pkm, version, getValidPreEvolutions(pkm).ToArray(), generation, Tutor: true, Machine: true).Contains(move);
|
|
}
|
|
internal static bool getCanKnowMove(PKM pkm, int move, int generation, GameVersion version = GameVersion.Any)
|
|
{
|
|
if (pkm.Species == 235 && !InvalidSketch.Contains(move))
|
|
return true;
|
|
return getValidMoves(pkm, version, getValidPreEvolutions(pkm).ToArray(), generation, LVL: true, Relearn: true, Tutor: true, Machine: true).Contains(move);
|
|
}
|
|
internal static int getBaseEggSpecies(PKM pkm, int skipOption = 0)
|
|
{
|
|
if (pkm.Format == 1)
|
|
return getBaseSpecies(pkm, skipOption : skipOption, generation : 2);
|
|
return getBaseSpecies(pkm, skipOption);
|
|
}
|
|
internal static int getBaseSpecies(PKM pkm, int skipOption = 0, int generation = -1)
|
|
{
|
|
if (pkm.Species == 292)
|
|
return 290;
|
|
if (pkm.Species == 242 && pkm.CurrentLevel < 3) // Never Cleffa
|
|
return 113;
|
|
|
|
var table = generation != -1 ? getEvolutionTable(generation): getEvolutionTable(pkm);
|
|
int maxSpeciesOrigin = generation != -1 ? getMaxSpeciesOrigin(generation) : - 1;
|
|
var evos = table.getValidPreEvolutions(pkm, 100, maxSpeciesOrigin: maxSpeciesOrigin, skipChecks:true).ToArray();
|
|
|
|
switch (skipOption)
|
|
{
|
|
case -1: return pkm.Species;
|
|
case 1: return evos.Length <= 1 ? pkm.Species : evos[evos.Length - 2].Species;
|
|
default: return evos.Length <= 0 ? pkm.Species : evos.Last().Species;
|
|
}
|
|
}
|
|
internal static int getMaxLevelGeneration(PKM pkm)
|
|
{
|
|
return getMaxLevelGeneration(pkm, pkm.GenNumber);
|
|
}
|
|
internal static int getMaxLevelGeneration(PKM pkm, int generation)
|
|
{
|
|
if (!pkm.InhabitedGeneration(generation))
|
|
return -1;
|
|
|
|
if (pkm.Format <= 2)
|
|
{
|
|
if (generation == 1 && FutureEvolutionsGen1_Gen2LevelUp.Contains(pkm.Species))
|
|
return pkm.CurrentLevel - 1;
|
|
return pkm.CurrentLevel;
|
|
}
|
|
|
|
if (pkm.Species == 700 && generation == 5)
|
|
return pkm.CurrentLevel - 1;
|
|
|
|
if (pkm.Gen3 && pkm.Format > 4 && pkm.Met_Level == pkm.CurrentLevel && FutureEvolutionsGen3_LevelUpGen4.Contains(pkm.Species))
|
|
return pkm.Met_Level - 1;
|
|
|
|
if (!pkm.HasOriginalMetLocation)
|
|
return pkm.Met_Level;
|
|
|
|
return pkm.CurrentLevel;
|
|
}
|
|
internal static int getMinLevelEncounter(PKM pkm)
|
|
{
|
|
if (pkm.Format == 3 && pkm.WasEgg)
|
|
// Only for gen 3 pokemon in format 3, after transfer to gen 4 it should return transfer level
|
|
return 5;
|
|
if (pkm.Format == 4 && pkm.GenNumber == 4 && pkm.WasEgg)
|
|
// Only for gen 4 pokemon in format 4, after transfer to gen 5 it should return transfer level
|
|
return 1;
|
|
return pkm.HasOriginalMetLocation ? pkm.Met_Level : getMaxLevelGeneration(pkm);
|
|
}
|
|
internal static int getMinLevelGeneration(PKM pkm)
|
|
{
|
|
return getMinLevelGeneration(pkm, pkm.GenNumber);
|
|
}
|
|
internal static int getMinLevelGeneration(PKM pkm, int generation)
|
|
{
|
|
if (!pkm.InhabitedGeneration(generation))
|
|
return 0;
|
|
|
|
if (pkm.Format <= 2)
|
|
return 2;
|
|
|
|
if (!pkm.HasOriginalMetLocation && generation != pkm.GenNumber)
|
|
return pkm.Met_Level;
|
|
|
|
if (pkm.GenNumber <= 3)
|
|
return 2;
|
|
|
|
return 1;
|
|
}
|
|
|
|
internal static DexLevel[][] getEvolutionChainsAllGens(PKM pkm, object Encounter)
|
|
{
|
|
var CompleteEvoChain = getEvolutionChain(pkm, Encounter).ToArray();
|
|
int maxgen = pkm.Format == 1 && !pkm.Gen1_NotTradeback ? 2 : pkm.Format;
|
|
int mingen = pkm.Format == 2 && !pkm.Gen2_NotTradeback ? 1 : pkm.GenNumber;
|
|
DexLevel[][] GensEvoChains = new DexLevel[maxgen + 1][];
|
|
for (int i = 0; i <= maxgen; i++)
|
|
GensEvoChains[i] = new DexLevel[0];
|
|
|
|
if (pkm.Species == 0 || pkm.Format > 2 && pkm.GenU) // Illegal origin or empty pokemon, return only chain for current format
|
|
{
|
|
GensEvoChains[pkm.Format] = CompleteEvoChain;
|
|
return GensEvoChains;
|
|
}
|
|
// If is egg skip the other checks and just return the evo chain for GenNumber, that will contains only the pokemon inside the egg
|
|
// Empty list returned if is an impossible egg (like a gen 3 infernape inside an egg)
|
|
if (pkm.IsEgg)
|
|
{
|
|
if (getMaxSpeciesOrigin(pkm.GenNumber) >= pkm.Species)
|
|
GensEvoChains[pkm.GenNumber] = CompleteEvoChain;
|
|
return GensEvoChains;
|
|
}
|
|
|
|
int lvl = pkm.CurrentLevel;
|
|
|
|
// Iterate generations backwards because level will be decreased from current level in each generation
|
|
for (int gen = maxgen; gen >= mingen; gen--)
|
|
{
|
|
if (pkm.GenNumber == 1 && pkm.Gen1_NotTradeback && gen == 2)
|
|
continue;
|
|
if (pkm.GenNumber <= 2 && 3 <= gen && gen <= 6)
|
|
continue;
|
|
if (!pkm.HasOriginalMetLocation && pkm.Format > 2 && gen < pkm.Format && gen <= 4 && lvl > pkm.Met_Level)
|
|
{
|
|
// Met location was lost at this point but it also means the pokemon existed in generations 1 to 4 with maximum level equals to met level
|
|
lvl = pkm.Met_Level;
|
|
}
|
|
|
|
int maxspeciesgen = getMaxSpeciesOrigin(gen);
|
|
|
|
// Remove future gen evolutions after a few special considerations,
|
|
// it the pokemon origin is illegal like a "gen 3" Infernape the list will be emptied, it didnt existed in gen 3 in any evolution phase
|
|
while (CompleteEvoChain.Any() && CompleteEvoChain.First().Species > maxspeciesgen)
|
|
{
|
|
// Eevee requires to level one time to be Sylveon, it can be deduced in gen 5 and before it existed with maximum one level bellow current
|
|
if (CompleteEvoChain.First().Species == 700 && gen == 5)
|
|
lvl--;
|
|
// This is a gen 3 pokemon in a gen 4 phase evolution that requieres level up and then transfered to gen 5+
|
|
// We can deduce that it existed in gen 4 until met level,
|
|
// but if current level is met level we can also deduce it existed in gen 3 until maximum met level -1
|
|
if (gen == 3 && pkm.Format > 4 && lvl == pkm.CurrentLevel && CompleteEvoChain.First().Species > MaxSpeciesID_3 && CompleteEvoChain.First().RequiresLvlUp)
|
|
lvl--;
|
|
// The same condition for gen2 evolution of gen 1 pokemon, level of the pokemon in gen 1 games would be CurrentLevel -1 one level bellow gen 2 level
|
|
if (gen == 1 && pkm.Format == 2 && lvl == pkm.CurrentLevel && CompleteEvoChain.First().Species > MaxSpeciesID_1 && CompleteEvoChain.First().RequiresLvlUp)
|
|
lvl--;
|
|
CompleteEvoChain = CompleteEvoChain.Skip(1).ToArray();
|
|
}
|
|
|
|
// Alolan form evolutions, remove from gens 1-6 chains
|
|
if (gen < 7 && pkm.Format >= 7 && CompleteEvoChain.Any() && CompleteEvoChain.First().Form > 0 && EvolveToAlolanForms.Contains(CompleteEvoChain.First().Species))
|
|
CompleteEvoChain = CompleteEvoChain.Skip(1).ToArray();
|
|
|
|
if (!CompleteEvoChain.Any())
|
|
continue;
|
|
|
|
GensEvoChains[gen] = getEvolutionChain(pkm, Encounter, CompleteEvoChain.First().Species, lvl);
|
|
if (gen > 2 && !pkm.HasOriginalMetLocation && gen >= pkm.GenNumber)
|
|
//Remove previous evolutions bellow transfer level
|
|
//For example a gen3 charizar in format 7 with current level 36 and met level 36
|
|
//chain level for charmander is 35, is bellow met level
|
|
GensEvoChains[gen] = GensEvoChains[gen].Where(e => e.Level >= getMinLevelGeneration(pkm, gen)).ToArray();
|
|
|
|
if (gen == 1 && GensEvoChains[gen].LastOrDefault()?.Species > MaxSpeciesID_1)
|
|
// Remove generation 2 pre-evolutions
|
|
GensEvoChains[gen] = GensEvoChains[gen].Take(GensEvoChains[gen].Length - 1).ToArray();
|
|
}
|
|
return GensEvoChains;
|
|
}
|
|
internal static DexLevel[] getEvolutionChain(PKM pkm, object Encounter)
|
|
{
|
|
return getEvolutionChain(pkm, Encounter, pkm.Species, 100);
|
|
}
|
|
internal static DexLevel[] getEvolutionChain(PKM pkm, object Encounter, int maxspec, int maxlevel)
|
|
{
|
|
int minspec;
|
|
DexLevel[] vs = getValidPreEvolutions(pkm).ToArray();
|
|
|
|
// Evolution chain is in reverse order (devolution)
|
|
|
|
if (Encounter is IEncounterable[])
|
|
minspec = vs.Reverse().First(s => ((IEncounterable[]) Encounter).Any(slot => slot.Species == s.Species)).Species;
|
|
else if (Encounter is IEncounterable)
|
|
minspec = vs.Reverse().First(s => ((IEncounterable) Encounter).Species == s.Species).Species;
|
|
else
|
|
minspec = vs.Last().Species;
|
|
|
|
int minindex = Math.Max(0, Array.FindIndex(vs, p => p.Species == minspec));
|
|
Array.Resize(ref vs, minindex + 1);
|
|
if (vs.Last().MinLevel > 1) // Last entry from vs is removed, turn next entry into the wild/hatched pokemon
|
|
{
|
|
vs.Last().MinLevel = 1;
|
|
vs.Last().RequiresLvlUp = false;
|
|
if (vs.First().MinLevel == 2 && !vs.First().RequiresLvlUp)
|
|
{
|
|
// Example Raichu in gen 2 or later,
|
|
// because Pichu requires level up Minimum level of Raichu would be 2
|
|
// but after removing Pichu because the origin species is Pikachu, Raichu min level should be 1
|
|
vs.First().MinLevel = 1;
|
|
vs.First().RequiresLvlUp = false;
|
|
}
|
|
}
|
|
// Maxspec is used to remove future gen evolutions, to gather evolution chain of a pokemon in previous generations
|
|
int skip = Math.Max(0, Array.FindIndex(vs, p => p.Species == maxspec));
|
|
// Maxlevel is also used for previous generations, it removes evolutions imposible before the transfer level
|
|
// For example a fire red charizard whose current level in XY is 50 but met level is 20, it couldnt be a Charizard in gen 3 and 4 games
|
|
vs = vs.Skip(skip).Where(e => e.MinLevel <= maxlevel).ToArray();
|
|
// Reduce the evolution chain levels to max level, because met level is the last one when the pokemon could be and learn moves in that generation
|
|
foreach (DexLevel d in vs)
|
|
d.Level = Math.Min(d.Level, maxlevel);
|
|
return vs;
|
|
}
|
|
internal static string getEncounterTypeName(PKM pkm, object Encounter)
|
|
{
|
|
var t = Encounter;
|
|
if (pkm.WasEgg)
|
|
return "Egg";
|
|
if (t is IEncounterable e)
|
|
return e.Name;
|
|
if (t is IEncounterable[] arr && arr.Length != 0)
|
|
return arr[0].Name;
|
|
if (t is int)
|
|
return "Unknown";
|
|
return t?.GetType().Name ?? "Unknown";
|
|
}
|
|
private static IEnumerable<EncounterArea> getDexNavAreas(PKM pkm)
|
|
{
|
|
switch (pkm.Version)
|
|
{
|
|
case (int)GameVersion.AS:
|
|
return SlotsA.Where(l => l.Location == pkm.Met_Location);
|
|
case (int)GameVersion.OR:
|
|
return SlotsO.Where(l => l.Location == pkm.Met_Location);
|
|
default:
|
|
return new EncounterArea[0];
|
|
}
|
|
}
|
|
private static IEnumerable<int> getRelearnLVLMoves(PKM pkm, int species, int lvl, int formnum)
|
|
{
|
|
List<int> moves = new List<int>();
|
|
switch (pkm.GenNumber)
|
|
{
|
|
case 6:
|
|
if (pkm.InhabitedGeneration(6))
|
|
{
|
|
int ind_XY = PersonalTable.XY.getFormeIndex(species, formnum);
|
|
moves.AddRange(LevelUpXY[ind_XY].getMoves(lvl));
|
|
int ind_AO = PersonalTable.AO.getFormeIndex(species, formnum);
|
|
moves.AddRange(LevelUpAO[ind_AO].getMoves(lvl));
|
|
}
|
|
break;
|
|
case 7:
|
|
if (pkm.InhabitedGeneration(7))
|
|
{
|
|
int ind_SM = PersonalTable.SM.getFormeIndex(species, formnum);
|
|
moves.AddRange(LevelUpSM[ind_SM].getMoves(lvl));
|
|
}
|
|
break;
|
|
}
|
|
return moves;
|
|
}
|
|
private static IEnumerable<EncounterArea> getEncounterSlots(PKM pkm, int lvl = -1, GameVersion gameSource = GameVersion.Any)
|
|
{
|
|
if (gameSource == GameVersion.Any)
|
|
gameSource = (GameVersion)pkm.Version;
|
|
|
|
switch (gameSource)
|
|
{
|
|
case GameVersion.RBY:
|
|
case GameVersion.RD: case GameVersion.BU:
|
|
case GameVersion.GN: case GameVersion.YW:
|
|
return getSlots(pkm, SlotsRBY, lvl);
|
|
|
|
case GameVersion.GSC:
|
|
case GameVersion.GD: case GameVersion.SV:
|
|
case GameVersion.C:
|
|
return getSlots(pkm, getSlotsTableGen2(pkm), lvl);
|
|
|
|
case GameVersion.R:
|
|
return getSlots(pkm, SlotsR, lvl);
|
|
case GameVersion.S:
|
|
return getSlots(pkm, SlotsS, lvl);
|
|
case GameVersion.E:
|
|
return getSlots(pkm, SlotsE, lvl);
|
|
case GameVersion.FR:
|
|
return getSlots(pkm, SlotsFR, lvl);
|
|
case GameVersion.LG:
|
|
return getSlots(pkm, SlotsLG, lvl);
|
|
case GameVersion.CXD:
|
|
return getSlots(pkm, SlotsXD, lvl);
|
|
|
|
case GameVersion.D:
|
|
return getSlots(pkm, SlotsD, lvl);
|
|
case GameVersion.P:
|
|
return getSlots(pkm, SlotsP, lvl);
|
|
case GameVersion.Pt:
|
|
return getSlots(pkm, SlotsPt, lvl);
|
|
case GameVersion.HG:
|
|
return getSlots(pkm, SlotsHG, lvl);
|
|
case GameVersion.SS:
|
|
return getSlots(pkm, SlotsSS, lvl);
|
|
|
|
case GameVersion.B:
|
|
return getSlots(pkm, SlotsB, lvl);
|
|
case GameVersion.W:
|
|
return getSlots(pkm, SlotsW, lvl);
|
|
case GameVersion.B2:
|
|
return getSlots(pkm, SlotsB2, lvl);
|
|
case GameVersion.W2:
|
|
return getSlots(pkm, SlotsW2, lvl);
|
|
|
|
case GameVersion.X:
|
|
return getSlots(pkm, SlotsX, lvl);
|
|
case GameVersion.Y:
|
|
return getSlots(pkm, SlotsY, lvl);
|
|
case GameVersion.AS:
|
|
return getSlots(pkm, SlotsA, lvl);
|
|
case GameVersion.OR:
|
|
return getSlots(pkm, SlotsO, lvl);
|
|
|
|
case GameVersion.SN:
|
|
return getSlots(pkm, SlotsSN, lvl);
|
|
case GameVersion.MN:
|
|
return getSlots(pkm, SlotsMN, lvl);
|
|
default: return new List<EncounterArea>();
|
|
}
|
|
}
|
|
private static IEnumerable<EncounterStatic> getStaticEncounters(PKM pkm, int lvl = -1, GameVersion gameSource = GameVersion.Any)
|
|
{
|
|
if (gameSource == GameVersion.Any)
|
|
gameSource = (GameVersion) pkm.Version;
|
|
|
|
switch (gameSource)
|
|
{
|
|
case GameVersion.RBY:
|
|
case GameVersion.RD: case GameVersion.BU:
|
|
case GameVersion.GN: case GameVersion.YW:
|
|
return getStatic(pkm, StaticRBY, maxspeciesorigin:MaxSpeciesID_1, lvl: lvl);
|
|
|
|
case GameVersion.GSC:
|
|
case GameVersion.GD: case GameVersion.SV:
|
|
case GameVersion.C:
|
|
return getStatic(pkm, getStaticTableGen2(pkm), maxspeciesorigin: MaxSpeciesID_2, lvl: lvl);
|
|
|
|
case GameVersion.R:
|
|
return getStatic(pkm, StaticR, lvl);
|
|
case GameVersion.S:
|
|
return getStatic(pkm, StaticS, lvl);
|
|
case GameVersion.E:
|
|
return getStatic(pkm, StaticE, lvl);
|
|
case GameVersion.FR:
|
|
return getStatic(pkm, StaticFR, lvl);
|
|
case GameVersion.LG:
|
|
return getStatic(pkm, StaticLG, lvl);
|
|
case GameVersion.CXD:
|
|
return getStatic(pkm, Encounter_CXD, lvl);
|
|
|
|
case GameVersion.D:
|
|
return getStatic(pkm, StaticD, lvl);
|
|
case GameVersion.P:
|
|
return getStatic(pkm, StaticP, lvl);
|
|
case GameVersion.Pt:
|
|
return getStatic(pkm, StaticPt, lvl);
|
|
case GameVersion.HG:
|
|
return getStatic(pkm, StaticHG, lvl);
|
|
case GameVersion.SS:
|
|
return getStatic(pkm, StaticSS, lvl);
|
|
|
|
case GameVersion.B:
|
|
return getStatic(pkm, StaticB, lvl);
|
|
case GameVersion.W:
|
|
return getStatic(pkm, StaticW, lvl);
|
|
case GameVersion.B2:
|
|
return getStatic(pkm, StaticB2, lvl);
|
|
case GameVersion.W2:
|
|
return getStatic(pkm, StaticW2, lvl);
|
|
|
|
case GameVersion.X:
|
|
return getStatic(pkm, StaticX, lvl);
|
|
case GameVersion.Y:
|
|
return getStatic(pkm, StaticY, lvl);
|
|
case GameVersion.AS:
|
|
return getStatic(pkm, StaticA, lvl);
|
|
case GameVersion.OR:
|
|
return getStatic(pkm, StaticO, lvl);
|
|
|
|
case GameVersion.SN:
|
|
return getStatic(pkm, StaticSN, lvl);
|
|
case GameVersion.MN:
|
|
return getStatic(pkm, StaticMN, lvl);
|
|
|
|
default: return new List<EncounterStatic>();
|
|
}
|
|
}
|
|
private static IEnumerable<EncounterArea> getEncounterAreas(PKM pkm, GameVersion gameSource = GameVersion.Any)
|
|
{
|
|
if (gameSource == GameVersion.Any)
|
|
gameSource = (GameVersion)pkm.Version;
|
|
|
|
var slots = getEncounterSlots(pkm, gameSource: gameSource);
|
|
bool noMet = !pkm.HasOriginalMetLocation;
|
|
return noMet ? slots : slots.Where(area => area.Location == pkm.Met_Location);
|
|
}
|
|
private static IEnumerable<EncounterSlot> getValidEncounterSlots(PKM pkm, EncounterArea loc, bool DexNav, bool ignoreLevel = false, GameVersion gameSource = GameVersion.Any)
|
|
{
|
|
int fluteBoost = pkm.Format < 3 ? 0 : 4;
|
|
const int dexnavBoost = 30;
|
|
|
|
int df = DexNav ? fluteBoost : 0;
|
|
int dn = DexNav ? fluteBoost + dexnavBoost : 0;
|
|
List<EncounterSlot> slotdata = new List<EncounterSlot>();
|
|
|
|
var maxspeciesorigin = -1;
|
|
if (gameSource == GameVersion.RBY) maxspeciesorigin = MaxSpeciesID_1;
|
|
if (gameSource == GameVersion.GSC) maxspeciesorigin = MaxSpeciesID_2;
|
|
|
|
// Get Valid levels
|
|
IEnumerable<DexLevel> vs = getValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: ignoreLevel ? 100 : -1, skipChecks:ignoreLevel);
|
|
|
|
bool IsRGBKadabra = false;
|
|
if (pkm.Format == 1 && pkm.Gen1_NotTradeback)
|
|
{
|
|
// Pure gen 1, slots can be filter by catch rate
|
|
if ((pkm.Species == 25 || pkm.Species == 26) && (pkm as PK1).Catch_Rate == 163)
|
|
// Yellow Pikachu, is not a wild encounter
|
|
return slotdata;
|
|
if ((pkm.Species == 64 || pkm.Species == 65) && (pkm as PK1).Catch_Rate == 96)
|
|
// Yellow Kadabra, ignore Abra encounters
|
|
vs = vs.Where(s => s.Species == 64);
|
|
if ((pkm.Species == 148 || pkm.Species == 149) && (pkm as PK1).Catch_Rate == 27)
|
|
// Yellow Dragonair, ignore Dratini encounters
|
|
vs = vs.Where(s => s.Species == 148);
|
|
else
|
|
{
|
|
IsRGBKadabra = (pkm.Species == 64 || pkm.Species == 65) && (pkm as PK1).Catch_Rate == 100;
|
|
vs = vs.Where(s => (pkm as PK1).Catch_Rate == PersonalTable.RB[s.Species].CatchRate);
|
|
}
|
|
}
|
|
|
|
// Get slots where pokemon can exist
|
|
bool ignoreSlotLevel = ignoreLevel;
|
|
IEnumerable<EncounterSlot> slots = loc.Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species && (ignoreSlotLevel || evo.Level >= slot.LevelMin - df)));
|
|
|
|
int lvl = getMinLevelEncounter(pkm);
|
|
if (lvl <= 0)
|
|
return slotdata;
|
|
int gen = pkm.GenNumber;
|
|
|
|
List<EncounterSlot> encounterSlots;
|
|
if (ignoreLevel)
|
|
encounterSlots = slots.ToList();
|
|
else if (pkm.HasOriginalMetLocation)
|
|
encounterSlots = slots.Where(slot => slot.LevelMin - df <= lvl && lvl <= slot.LevelMax + (slot.AllowDexNav ? dn : df)).ToList();
|
|
else // check for any less than current level
|
|
encounterSlots = slots.Where(slot => slot.LevelMin <= lvl).ToList();
|
|
|
|
if (gen <= 2)
|
|
{
|
|
if (IsRGBKadabra)
|
|
//Red Kadabra slots : Level 49 and 51 in RGB, but level 20 and 27 in Yellow
|
|
encounterSlots = encounterSlots.Where(slot => slot.LevelMin >= 49).ToList();
|
|
|
|
// For gen 1 and 2 return Minimum level slot
|
|
// Minimum level is needed to check available moves, because there is no move reminder in gen 1,
|
|
// There are moves in the level up table that cant be legally obtained
|
|
EncounterSlot slotMin = encounterSlots.OrderBy(slot => slot.LevelMin).FirstOrDefault();
|
|
if (slotMin != null)
|
|
slotdata.Add(slotMin);
|
|
return slotdata;
|
|
}
|
|
|
|
// Pressure Slot
|
|
EncounterSlot slotMax = encounterSlots.OrderByDescending(slot => slot.LevelMax).FirstOrDefault();
|
|
if (slotMax != null)
|
|
{
|
|
slotMax = slotMax.Clone();
|
|
slotMax.Pressure = true;
|
|
slotMax.Form = pkm.AltForm;
|
|
}
|
|
|
|
if (gen >= 6 && !DexNav)
|
|
{
|
|
// Filter for Form Specific
|
|
slotdata.AddRange(WildForms.Contains(pkm.Species)
|
|
? encounterSlots.Where(slot => slot.Form == pkm.AltForm)
|
|
: encounterSlots);
|
|
if (slotMax != null)
|
|
slotdata.Add(slotMax);
|
|
return slotdata;
|
|
}
|
|
|
|
List<EncounterSlot> eslots = encounterSlots.Where(slot => !WildForms.Contains(pkm.Species) || slot.Form == pkm.AltForm).ToList();
|
|
if (gen <= 5)
|
|
{
|
|
slotdata.AddRange(eslots);
|
|
return slotdata;
|
|
}
|
|
if (slotMax != null)
|
|
eslots.Add(slotMax);
|
|
foreach (EncounterSlot s in eslots)
|
|
{
|
|
bool nav = s.AllowDexNav && (pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4);
|
|
EncounterSlot slot = s.Clone();
|
|
slot.DexNav = nav;
|
|
|
|
if (slot.LevelMin > lvl)
|
|
slot.WhiteFlute = true;
|
|
if (slot.LevelMax + 1 <= lvl && lvl <= slot.LevelMax + fluteBoost)
|
|
slot.BlackFlute = true;
|
|
if (slot.LevelMax != lvl && slot.AllowDexNav)
|
|
slot.DexNav = true;
|
|
slotdata.Add(slot);
|
|
}
|
|
return slotdata;
|
|
}
|
|
private static IEnumerable<EncounterArea> getSlotsTableGen2(PKM pkm)
|
|
{
|
|
if (pkm.Format != 2)
|
|
// Gen 2 met location is lost outside gen 2 games
|
|
return SlotsGSC;
|
|
|
|
if (pkm.HasOriginalMetLocation)
|
|
// Format 2 with met location, encounter should be from crystal
|
|
return SlotsC;
|
|
|
|
if (pkm.Species > 151 && !FutureEvolutionsGen1.Contains(pkm.Species))
|
|
// Format 2 without met location but pokemon could not be tradeback to gen 1,
|
|
// encounter should be from gold or silver
|
|
return SlotsGS;
|
|
|
|
// Encounter could be any gen 2 game, it can have empty met location for have a g/s origin
|
|
// or it can be a crystal pokemon that lost met location after being tradeback to gen 1 games
|
|
return SlotsGSC;
|
|
}
|
|
private static IEnumerable<EncounterStatic> getStaticTableGen2(PKM pkm)
|
|
{
|
|
if (pkm.Format != 2)
|
|
return StaticGSC;
|
|
|
|
if (pkm.HasOriginalMetLocation)
|
|
return StaticC;
|
|
if (pkm.Species > 151 && !FutureEvolutionsGen1.Contains(pkm.Species))
|
|
return StaticGS;
|
|
|
|
return StaticGSC;
|
|
}
|
|
|
|
private static IEnumerable<EncounterArea> getSlots(PKM pkm, IEnumerable<EncounterArea> tables, int lvl = -1)
|
|
{
|
|
IEnumerable<DexLevel> vs = getValidPreEvolutions(pkm, lvl: lvl);
|
|
List<EncounterArea> slotLocations = new List<EncounterArea>();
|
|
foreach (var loc in tables)
|
|
{
|
|
IEnumerable<EncounterSlot> slots = loc.Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species));
|
|
|
|
EncounterSlot[] es = slots.ToArray();
|
|
if (es.Length > 0)
|
|
slotLocations.Add(new EncounterArea { Location = loc.Location, Slots = es });
|
|
}
|
|
return slotLocations;
|
|
}
|
|
internal static int getEncounterLevel(PKM pkm, object encounter)
|
|
{
|
|
return (encounter as IEncounterable[])?.Min(e => e.LevelMin) ??
|
|
(encounter as IEncounterable)?.LevelMin ??
|
|
(pkm.GenNumber <= 3 ? 5 : 1 ); //egg
|
|
}
|
|
internal static int getEncounterSpecies(object encounter, DexLevel[] vs)
|
|
{
|
|
if (encounter is int)
|
|
return (int)encounter;
|
|
if (encounter is IEncounterable[])
|
|
return vs.Reverse().First(s => ((IEncounterable[])encounter).Any(slot => slot.Species == s.Species)).Species;
|
|
if (encounter is IEncounterable)
|
|
return vs.Reverse().First(s => ((IEncounterable)encounter).Species == s.Species).Species;
|
|
// fallback to base species on unknown origin
|
|
return vs.Last().Species;
|
|
}
|
|
internal static IEnumerable<DexLevel> getValidPreEvolutions(PKM pkm, int maxspeciesorigin = -1, int lvl = -1, bool skipChecks = false)
|
|
{
|
|
if (lvl < 0)
|
|
lvl = pkm.CurrentLevel;
|
|
if (lvl == 1 && pkm.IsEgg)
|
|
return new List<DexLevel>
|
|
{
|
|
new DexLevel { Species = pkm.Species, Level = 1, MinLevel = 1 },
|
|
};
|
|
if (pkm.Species == 292 && lvl >= 20 && (!pkm.HasOriginalMetLocation || pkm.Met_Level + 1 <= lvl))
|
|
return new List<DexLevel>
|
|
{
|
|
new DexLevel { Species = 292, Level = lvl, MinLevel = 20 },
|
|
new DexLevel { Species = 290, Level = lvl - 1, MinLevel = 1 }
|
|
};
|
|
if (maxspeciesorigin == -1 && pkm.InhabitedGeneration(2) && pkm.GenNumber == 1)
|
|
maxspeciesorigin = MaxSpeciesID_2;
|
|
|
|
var et = maxspeciesorigin == MaxSpeciesID_2 ? getEvolutionTable(2) : getEvolutionTable(pkm);
|
|
return et.getValidPreEvolutions(pkm, lvl: lvl, maxSpeciesOrigin: maxspeciesorigin, skipChecks: skipChecks);
|
|
}
|
|
private static IEnumerable<EncounterStatic> getStatic(PKM pkm, IEnumerable<EncounterStatic> table, int maxspeciesorigin = -1, int lvl = -1)
|
|
{
|
|
IEnumerable<DexLevel> dl = getValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: lvl);
|
|
return table.Where(e => dl.Any(d => d.Species == e.Species));
|
|
}
|
|
private static IEnumerable<int> getValidMoves(PKM pkm, GameVersion Version, IReadOnlyList<DexLevel[]> vs, int minLvLG1 = 1, bool LVL = false, bool Relearn = false, bool Tutor = false, bool Machine = false, bool MoveReminder = true, bool RemoveTransferHM = true)
|
|
{
|
|
List<int> r = new List<int> { 0 };
|
|
if (Relearn && pkm.Format >= 6)
|
|
r.AddRange(pkm.RelearnMoves);
|
|
|
|
for (int gen = pkm.GenNumber; gen <= pkm.Format; gen++)
|
|
if (vs[gen].Any())
|
|
r.AddRange(getValidMoves(pkm, Version, vs[gen], gen, minLvLG1:minLvLG1, LVL: LVL, Relearn: false, Tutor: Tutor, Machine: Machine, MoveReminder: MoveReminder, RemoveTransferHM: RemoveTransferHM));
|
|
|
|
return r.Distinct().ToArray();
|
|
}
|
|
private static IEnumerable<int> getValidMoves(PKM pkm, GameVersion Version, DexLevel[] vs, int Generation, int minLvLG1 = 1, bool LVL = false, bool Relearn = false, bool Tutor = false, bool Machine = false, bool MoveReminder = true, bool RemoveTransferHM = true)
|
|
{
|
|
List<int> r = new List<int> { 0 };
|
|
if (!vs.Any())
|
|
return r;
|
|
int species = pkm.Species;
|
|
|
|
// Special Type Tutors Availability
|
|
bool moveTutor = Tutor || MoveReminder; // Usually true, except when called for move suggestions (no tutored moves)
|
|
|
|
if (FormChangeMoves.Contains(species)) // Deoxys & Shaymin & Giratina (others don't have extra but whatever)
|
|
{
|
|
int formcount = pkm.PersonalInfo.FormeCount;
|
|
if (species == 386 && pkm.Format == 3)
|
|
// In gen 3 deoxys has different forms depending on the current game, in personal info there is no alter form info
|
|
formcount = 4;
|
|
for (int i = 0; i < formcount; i++)
|
|
r.AddRange(getMoves(pkm, species, minLvLG1, vs.First().Level, i, moveTutor, Version, LVL, Tutor, Machine, MoveReminder, RemoveTransferHM, Generation));
|
|
if (Relearn) r.AddRange(pkm.RelearnMoves);
|
|
return r.Distinct();
|
|
}
|
|
|
|
foreach (DexLevel evo in vs)
|
|
{
|
|
var minlvlevo = 1;
|
|
if (Generation == 1)
|
|
{
|
|
// Return moves from minLvLG1 if species if the species encounters
|
|
// For evolutions return moves using evolution min level as min level
|
|
minlvlevo = minLvLG1;
|
|
if (evo.MinLevel > 1)
|
|
minlvlevo = Math.Min(pkm.CurrentLevel, evo.MinLevel);
|
|
}
|
|
r.AddRange(getMoves(pkm, evo.Species, minlvlevo, evo.Level, pkm.AltForm, moveTutor, Version, LVL, Tutor, Machine, MoveReminder, RemoveTransferHM, Generation));
|
|
}
|
|
|
|
if (pkm.Format <= 3)
|
|
return r.Distinct();
|
|
if (LVL)
|
|
{
|
|
if (species == 479 && Generation >= 4) // Rotom
|
|
r.Add(RotomMoves[pkm.AltForm]);
|
|
|
|
if (species == 718 && Generation == 7) // Zygarde
|
|
r.AddRange(ZygardeMoves);
|
|
}
|
|
if (Tutor)
|
|
{
|
|
if (species == 647) // Keldeo
|
|
r.Add(548); // Secret Sword
|
|
if (species == 648) // Meloetta
|
|
r.Add(547); // Relic Song
|
|
|
|
if (species == 25 && pkm.Format == 6 && Generation == 6) // Pikachu
|
|
r.Add(PikachuMoves[pkm.AltForm]);
|
|
|
|
if ((species == 25 || species == 26) && Generation == 7) // Pikachu/Raichu Tutor
|
|
r.Add(344); // Volt Tackle
|
|
}
|
|
if (Relearn && Generation >= 6)
|
|
r.AddRange(pkm.RelearnMoves);
|
|
return r.Distinct();
|
|
}
|
|
private static IEnumerable<int> getMoves(PKM pkm, int species, int minlvlG1, int lvl, int form, bool moveTutor, GameVersion Version, bool LVL, bool specialTutors, bool Machine, bool MoveReminder, bool RemoveTransferHM)
|
|
{
|
|
List<int> r = new List<int> { 0 };
|
|
int gen = pkm.GenNumber;
|
|
if (pkm.GenNumber < 3)
|
|
{
|
|
int max = pkm.Format < 3 ? 2 : 1;
|
|
for (; gen <= max; gen++)
|
|
if (pkm.InhabitedGeneration(gen, species))
|
|
r.AddRange(getMoves(pkm, species, minlvlG1, lvl, form, moveTutor, Version, LVL, specialTutors, Machine, MoveReminder, RemoveTransferHM, gen));
|
|
gen = 7;
|
|
}
|
|
|
|
for (; gen <= pkm.Format; gen++)
|
|
if (pkm.InhabitedGeneration(gen))
|
|
r.AddRange(getMoves(pkm, species, minlvlG1, lvl, form, moveTutor, Version, LVL, specialTutors, Machine, MoveReminder, RemoveTransferHM, gen));
|
|
return r.Distinct();
|
|
}
|
|
private static IEnumerable<int> getMoves(PKM pkm, int species, int minlvlG1, int lvl, int form, bool moveTutor, GameVersion Version, bool LVL, bool specialTutors, bool Machine, bool MoveReminder, bool RemoveTransferHM, int Generation)
|
|
{
|
|
List<int> r = new List<int>();
|
|
|
|
var ver = Version;
|
|
switch (Generation)
|
|
{
|
|
case 1:
|
|
{
|
|
int index = PersonalTable.RB.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
|
|
var pi_rb = (PersonalInfoG1)PersonalTable.RB[index];
|
|
var pi_y = (PersonalInfoG1)PersonalTable.Y[index];
|
|
if (LVL)
|
|
{
|
|
if (minlvlG1 == 1)
|
|
{
|
|
r.AddRange(pi_rb.Moves);
|
|
r.AddRange(pi_y.Moves);
|
|
}
|
|
r.AddRange(LevelUpRB[index].getMoves(lvl, minlvlG1));
|
|
r.AddRange(LevelUpY[index].getMoves(lvl, minlvlG1));
|
|
}
|
|
if (Machine)
|
|
{
|
|
r.AddRange(TMHM_RBY.Where((t, m) => pi_rb.TMHM[m]));
|
|
r.AddRange(TMHM_RBY.Where((t, m) => pi_y.TMHM[m]));
|
|
}
|
|
if (moveTutor)
|
|
r.AddRange(getTutorMoves(pkm, species, form, specialTutors, Generation));
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
int index = PersonalTable.C.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
if (LVL)
|
|
{
|
|
r.AddRange(LevelUpGS[index].getMoves(lvl));
|
|
r.AddRange(LevelUpC[index].getMoves(lvl));
|
|
}
|
|
if (Machine)
|
|
{
|
|
var pi_c = (PersonalInfoG2)PersonalTable.C[index];
|
|
r.AddRange(TMHM_GSC.Where((t, m) => pi_c.TMHM[m]));
|
|
}
|
|
if (moveTutor)
|
|
r.AddRange(getTutorMoves(pkm, species, form, specialTutors, Generation));
|
|
if (pkm.Format == 1) //tradeback gen 2 -> gen 1
|
|
r = r.Where(m => m <= MaxMoveID_1).ToList();
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
int index = PersonalTable.E.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
if (LVL)
|
|
{
|
|
if (index == 386)
|
|
{
|
|
switch(form)
|
|
{
|
|
case 0: r.AddRange(LevelUpRS[index].getMoves(lvl)); break;
|
|
case 1: r.AddRange(LevelUpFR[index].getMoves(lvl)); break;
|
|
case 2: r.AddRange(LevelUpLG[index].getMoves(lvl)); break;
|
|
case 3: r.AddRange(LevelUpE[index].getMoves(lvl)); break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Emerald level up table are equals to R/S level up tables
|
|
r.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
|
|
r.AddRange(LevelUpLG[index].getMoves(lvl));
|
|
}
|
|
}
|
|
if (Machine)
|
|
{
|
|
var pi_c = PersonalTable.E[index];
|
|
r.AddRange(TM_3.Where((t, m) => pi_c.TMHM[m]));
|
|
if (!RemoveTransferHM || pkm.Format == 3) // HM moves must be removed for 3->4, only give if current format.
|
|
r.AddRange(HM_3.Where((t, m) => pi_c.TMHM[m+50]));
|
|
}
|
|
if (moveTutor)
|
|
r.AddRange(getTutorMoves(pkm, species, form, specialTutors, Generation));
|
|
if (RemoveTransferHM && pkm.Format > 3) //Remove HM
|
|
r = r.Except(HM_3).ToList();
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
int index = PersonalTable.HGSS.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
if (LVL)
|
|
{
|
|
r.AddRange(LevelUpDP[index].getMoves(lvl));
|
|
r.AddRange(LevelUpPt[index].getMoves(lvl));
|
|
r.AddRange(LevelUpHGSS[index].getMoves(lvl));
|
|
}
|
|
if (Machine)
|
|
{
|
|
var pi_hgss = PersonalTable.HGSS[index];
|
|
var pi_dppt = PersonalTable.Pt[index];
|
|
r.AddRange(TM_4.Where((t, m) => pi_hgss.TMHM[m]));
|
|
if (RemoveTransferHM && pkm.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(HM_DPPt.Where((t, m) => pi_dppt.TMHM[m + 92]));
|
|
r.AddRange(HM_HGSS.Where((t, m) => pi_hgss.TMHM[m + 92]));
|
|
}
|
|
}
|
|
if (moveTutor)
|
|
r.AddRange(getTutorMoves(pkm, species, form, specialTutors, Generation));
|
|
if (RemoveTransferHM && pkm.Format > 4) //Remove HM
|
|
r = r.Except(HM_4_RemovePokeTransfer).ToList();
|
|
break;
|
|
}
|
|
case 5:
|
|
{
|
|
int index = PersonalTable.B2W2.getFormeIndex(species, 0);
|
|
if (index == 0)
|
|
return r;
|
|
if (LVL)
|
|
{
|
|
r.AddRange(LevelUpBW[index].getMoves(lvl));
|
|
r.AddRange(LevelUpB2W2[index].getMoves(lvl));
|
|
}
|
|
if (Machine)
|
|
{
|
|
var pi_c = PersonalTable.B2W2[index];
|
|
r.AddRange(TMHM_BW.Where((t, m) => pi_c.TMHM[m]));
|
|
}
|
|
if (moveTutor)
|
|
r.AddRange(getTutorMoves(pkm, species, form, specialTutors, Generation));
|
|
break;
|
|
}
|
|
case 6:
|
|
switch (ver)
|
|
{
|
|
case GameVersion.Any: // Start at the top, hit every table
|
|
case GameVersion.X: case GameVersion.Y: case GameVersion.XY:
|
|
{
|
|
int index = PersonalTable.XY.getFormeIndex(species, form);
|
|
if (index == 0)
|
|
return r;
|
|
|
|
if (LVL)
|
|
r.AddRange(LevelUpXY[index].getMoves(lvl));
|
|
if (moveTutor)
|
|
r.AddRange(getTutorMoves(pkm, species, form, specialTutors, Generation));
|
|
if (Machine)
|
|
{
|
|
PersonalInfo pi = PersonalTable.XY[index];
|
|
r.AddRange(TMHM_XY.Where((t, m) => pi.TMHM[m]));
|
|
}
|
|
|
|
if (ver == GameVersion.Any) // Fall Through
|
|
goto case GameVersion.ORAS;
|
|
break;
|
|
}
|
|
|
|
case GameVersion.AS: case GameVersion.OR: case GameVersion.ORAS:
|
|
{
|
|
int index = PersonalTable.AO.getFormeIndex(species, form);
|
|
if (index == 0)
|
|
return r;
|
|
|
|
if (LVL)
|
|
r.AddRange(LevelUpAO[index].getMoves(lvl));
|
|
if (moveTutor)
|
|
r.AddRange(getTutorMoves(pkm, species, form, specialTutors, Generation));
|
|
if (Machine)
|
|
{
|
|
PersonalInfo pi = PersonalTable.AO[index];
|
|
r.AddRange(TMHM_AO.Where((t, m) => pi.TMHM[m]));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 7:
|
|
switch (ver)
|
|
{
|
|
case GameVersion.Any:
|
|
case GameVersion.SN: case GameVersion.MN: case GameVersion.SM:
|
|
{
|
|
int index = PersonalTable.SM.getFormeIndex(species, form);
|
|
if (MoveReminder)
|
|
lvl = 100; // Move reminder can teach any level in movepool now!
|
|
|
|
if (LVL)
|
|
r.AddRange(LevelUpSM[index].getMoves(lvl));
|
|
if (moveTutor)
|
|
r.AddRange(getTutorMoves(pkm, species, form, specialTutors, Generation));
|
|
if (Machine)
|
|
{
|
|
PersonalInfo pi = PersonalTable.SM.getFormeEntry(species, form);
|
|
r.AddRange(TMHM_SM.Where((t, m) => pi.TMHM[m]));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return r;
|
|
}
|
|
return r;
|
|
}
|
|
private static IEnumerable<int> getEggMoves(PKM pkm, int species, int formnum, GameVersion Version = GameVersion.Any)
|
|
{
|
|
if (!pkm.InhabitedGeneration(pkm.GenNumber, species))
|
|
return new List<int>();
|
|
|
|
switch (pkm.GenNumber)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
if (pkm.Format != 2)
|
|
return EggMovesC[species].Moves;
|
|
if (pkm.HasOriginalMetLocation)
|
|
return EggMovesC[species].Moves;
|
|
if (pkm.Species > 151 && !FutureEvolutionsGen1.Contains(pkm.Species))
|
|
return EggMovesGS[species].Moves;
|
|
return EggMovesC[species].Moves;
|
|
case 3:
|
|
return EggMovesRS[species].Moves;
|
|
case 4:
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
case GameVersion.Pt:
|
|
return EggMovesDPPt[species].Moves;
|
|
case GameVersion.HGSS:
|
|
return EggMovesHGSS[species].Moves;
|
|
default:
|
|
return new List<int>();
|
|
}
|
|
case 5:
|
|
return EggMovesBW[species].Moves;
|
|
case 6: // entries per species
|
|
return EggMovesAO[species].Moves.Concat(EggMovesXY[species].Moves);
|
|
|
|
case 7: // entries per form if required
|
|
var entry = EggMovesSM[species];
|
|
if (formnum > 0 && AlolanOriginForms.Contains(species))
|
|
entry = EggMovesSM[entry.FormTableIndex + formnum - 1];
|
|
return entry.Moves;
|
|
|
|
default:
|
|
return new List<int>();
|
|
}
|
|
}
|
|
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((t, m) => pi_rb.TMHM[m]));
|
|
moves.AddRange(TMHM_RBY.Where((t, 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((t, 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((t, 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((t, 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((t, 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((t, 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((t, 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((t, 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((t, 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((t, m) => pi_ao.TMHM[m]));
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 7:
|
|
index = PersonalTable.SM.getFormeIndex(species, form);
|
|
if (index == 0)
|
|
return moves;
|
|
|
|
PersonalInfo pi_sm = PersonalTable.SM[index];
|
|
moves.AddRange(TMHM_SM.Where((t, m) => pi_sm.TMHM[m]));
|
|
break;
|
|
}
|
|
return moves.Distinct();
|
|
}
|
|
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:
|
|
info = PersonalTable.C[species];
|
|
moves.AddRange(Tutors_GSC.Where((t, 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((t, i) => info.TypeTutors[i]));
|
|
// FRLG Tutors
|
|
// Only special tutor moves, normal tutor moves are already included in Emerald data
|
|
moves.AddRange(SpecialTutors_FRLG.Where((t, i) => SpecialTutors_Compatibility_FRLG[i].Any(e => e == species)));
|
|
// XD
|
|
moves.AddRange(SpecialTutors_XD_Exclusive.Where((t, 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[species];
|
|
moves.AddRange(Tutors_4.Where((t, i) => info.TypeTutors[i]));
|
|
moves.AddRange(SpecialTutors_4.Where((t, i) => SpecialTutors_Compatibility_4[i].Any(e => e == species)));
|
|
break;
|
|
case 5:
|
|
info = PersonalTable.B2W2[species];
|
|
moves.AddRange(TypeTutor6.Where((t, i) => info.TypeTutors[i]));
|
|
if (pkm.InhabitedGeneration(5) && specialTutors)
|
|
{
|
|
PersonalInfo pi = PersonalTable.B2W2.getFormeEntry(species, form);
|
|
for (int i = 0; i < Tutors_B2W2.Length; i++)
|
|
for (int b = 0; b < Tutors_B2W2[i].Length; b++)
|
|
if (pi.SpecialTutors[i][b])
|
|
moves.Add(Tutors_B2W2[i][b]);
|
|
}
|
|
break;
|
|
case 6:
|
|
info = PersonalTable.AO[species];
|
|
moves.AddRange(TypeTutor6.Where((t, i) => info.TypeTutors[i]));
|
|
if (pkm.InhabitedGeneration(6) && specialTutors && (pkm.AO || !pkm.IsUntraded))
|
|
{
|
|
PersonalInfo pi = PersonalTable.AO.getFormeEntry(species, form);
|
|
for (int i = 0; i < Tutors_AO.Length; i++)
|
|
for (int b = 0; b < Tutors_AO[i].Length; b++)
|
|
if (pi.SpecialTutors[i][b])
|
|
moves.Add(Tutors_AO[i][b]);
|
|
}
|
|
break;
|
|
case 7:
|
|
info = PersonalTable.SM.getFormeEntry(species, form);
|
|
moves.AddRange(TypeTutor6.Where((t, i) => info.TypeTutors[i]));
|
|
// No special tutors in G7
|
|
break;
|
|
}
|
|
return moves.Distinct();
|
|
}
|
|
internal static List<int>[] GetEmptyMovesList(DexLevel[][] EvoChainsAllGens)
|
|
{
|
|
var empty = new List<int>[EvoChainsAllGens.Length];
|
|
for (int i = 0; i < empty.Length; i++)
|
|
empty[i] = new List<int>();
|
|
return empty;
|
|
}
|
|
|
|
internal static List<int>[] GetEmptyEggMovesList()
|
|
{
|
|
var emptyegg = new List<int>[1];
|
|
emptyegg[0] = new List<int>();
|
|
return emptyegg;
|
|
}
|
|
|
|
internal static bool IsOutsider(PKM pkm)
|
|
{
|
|
var Outsider = Savegame_TID != pkm.TID || Savegame_OT != pkm.OT_Name;
|
|
if (pkm.Format <= 2)
|
|
return Outsider;
|
|
Outsider |= Savegame_SID != pkm.SID;
|
|
if (pkm.Format == 3) // Generation 3 does not check ot geneder nor pokemon version
|
|
return Outsider;
|
|
Outsider |= Savegame_Gender != pkm.OT_Gender || Savegame_Version != (GameVersion) pkm.Version;
|
|
return Outsider;
|
|
}
|
|
}
|
|
}
|