Privatize some static fields, more robust legal helper classes. (#3865)

* Uses LearnSource more throughout the codebase when appropriate, rather than loosely coupled pivot methods.
* Hides Learnset/EggMove data inside the LearnSource classes.
* Extracts functionality from the large Legal class & partial Table*.cs files into better performing helper classes.
* Cleans up some code from prior LearnSource commits.
This commit is contained in:
Kurt 2023-04-20 21:23:15 -07:00 committed by GitHub
parent 9a768dded3
commit 5bdc6b9ef8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
117 changed files with 1281 additions and 1658 deletions

View file

@ -46,12 +46,8 @@ public static class MoveShopRecordApplicator
/// </summary>
public static void SetMoveShopFlags(this IMoveShop8Mastery shop, ReadOnlySpan<ushort> moves, PKM pk)
{
var index = PersonalTable.LA.GetFormIndex(pk.Species, pk.Form);
var learn = Legal.LevelUpLA[index];
var mastery = Legal.MasteryLA[index];
var level = pk.CurrentLevel;
shop.SetMoveShopFlags(moves, learn, mastery, level);
var (learn, mastery) = LearnSource8LA.GetLearnsetAndMastery(pk.Species, pk.Form);
shop.SetMoveShopFlags(moves, learn, mastery, pk.CurrentLevel);
}
/// <summary>
@ -59,12 +55,8 @@ public static class MoveShopRecordApplicator
/// </summary>
public static void SetMoveShopFlagsAll(this IMoveShop8Mastery shop, PKM pk)
{
var index = PersonalTable.LA.GetFormIndex(pk.Species, pk.Form);
var learn = Legal.LevelUpLA[index];
var mastery = Legal.MasteryLA[index];
var level = pk.CurrentLevel;
shop.SetMoveShopFlagsAll(learn, mastery, level);
var (learn, mastery) = LearnSource8LA.GetLearnsetAndMastery(pk.Species, pk.Form);
shop.SetMoveShopFlagsAll(learn, mastery, pk.CurrentLevel);
}
/// <summary>

View file

@ -20,7 +20,7 @@ public static class BatchMods
new TypeSuggestion<IGeoTrack>(nameof(Extensions.ClearGeoLocationData), p => p.ClearGeoLocationData()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningClear), p => p.AwakeningClear()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningMinimum), p => p.AwakeningMinimum()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningMax), p => p.AwakeningMax()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningMaximize), p => p.AwakeningMaximize()),
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.ClearGanbaruValues), p => p.ClearGanbaruValues()),
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.SetSuggestedGanbaruValues), p => p.SetSuggestedGanbaruValues((PKM)p)),

View file

@ -50,8 +50,8 @@ public static class Pokerus
if (!IsObtainable(pk))
return IsSusceptible(strain, days);
if (pk.Format <= 2)
return IsStrainValid2(strain);
return IsStrainValid(strain);
return IsStrainValid2(strain, days);
return IsStrainValid(strain, days);
}
/// <inheritdoc cref="IsStrainValid(PKM,int,int)"/>

View file

@ -106,8 +106,8 @@ public static class ShowdownParsing
(int)Greninja or (int)Rockruff or (int)Koraidon or (int)Miraidon => string.Empty,
_ => Legal.Totem_USUM.Contains(species) && form == "Large"
? Legal.Totem_Alolan.Contains(species) && species != (int)Mimikyu ? "Alola-Totem" : "Totem"
_ => FormInfo.HasTotemForm(species) && form == "Large"
? species is (int)Raticate or (int)Marowak ? "Alola-Totem" : "Totem"
: form.Replace(' ', '-'),
};
}
@ -139,7 +139,7 @@ public static class ShowdownParsing
(int)Maushold when form == "Four" => "Family of Four",
(int)Urshifu or (int)Pikachu or (int)Alcremie => form.Replace('-', ' '), // Strike and Cosplay
_ => Legal.Totem_USUM.Contains(species) && form.EndsWith("Totem", StringComparison.OrdinalIgnoreCase) ? "Large" : form,
_ => FormInfo.HasTotemForm(species) && form.EndsWith("Totem", StringComparison.OrdinalIgnoreCase) ? "Large" : form,
};
}

View file

@ -82,7 +82,7 @@ public sealed class FilteredGameDataSource
var legal = source.LegalMoveDataSource;
return sav switch
{
SAV7b => legal.Where(s => Legal.IsAllowedMoveGG((ushort)s.Value)), // LGPE: Not all moves are available
SAV7b => legal.Where(s => MoveInfo7b.IsAllowedMoveGG((ushort)s.Value)), // LGPE: Not all moves are available
_ => legal.Where(m => m.Value <= sav.MaxMoveID),
};
}

View file

@ -6,16 +6,14 @@ public sealed class ItemStorage7GG : IItemStorage
{
public static readonly ItemStorage7GG Instance = new();
private static ReadOnlySpan<ushort> Pouch_Candy_GG_Regular => new ushort[]
private static ReadOnlySpan<ushort> Pouch_Candy_GG => new ushort[]
{
050, // Rare Candy
960, 961, 962, 963, 964, 965, // S
966, 967, 968, 969, 970, 971, // L
972, 973, 974, 975, 976, 977, // XL
};
private static ReadOnlySpan<ushort> Pouch_Candy_GG_Species => new ushort[]
{
// Species
978, 979,
980, 981, 982, 983, 984, 985, 986, 987, 988, 989,
990, 991, 992, 993, 994, 995, 996, 997, 998, 999,
@ -28,8 +26,6 @@ public sealed class ItemStorage7GG : IItemStorage
1057,
};
private static readonly ushort[] Pouch_Candy_GG = ArrayUtil.ConcatAll(Pouch_Candy_GG_Regular, Pouch_Candy_GG_Species);
private static ReadOnlySpan<ushort> Pouch_Medicine_GG => new ushort[]
{
017, 018, 019, 020, 021, 022, 023, 024, 025, 026, 027, 028, 029, 030, 031, 032, 038, 039, 040, 041, 709, 903,
@ -117,7 +113,6 @@ public sealed class ItemStorage7GG : IItemStorage
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
{
InventoryType.Medicine => Pouch_Medicine_GG,
InventoryType.TMHMs => Pouch_TM_GG,
InventoryType.Balls => Pouch_Catching_GG,

View file

@ -468,10 +468,16 @@ public sealed class ItemStorage8SWSH : IItemStorage
1578, // ★Aql7235
};
private const int DMAX_START = 1279;
private const int DMAX_END = 1578;
private const int DMAX_LEGAL_END = 1290; // ★Sgr7194 (Eevee)
public static bool IsDynamaxCrystal(ushort item) => item is >= DMAX_START and <= DMAX_END;
public static bool IsDynamaxCrystalAvailable(ushort item) => item is >= DMAX_START and <= DMAX_LEGAL_END;
public static bool IsItemLegal(ushort item)
{
if (Legal.IsDynamaxCrystal(item))
return Legal.IsDynamaxCrystalAvailable(item);
if (IsDynamaxCrystal(item))
return IsDynamaxCrystalAvailable(item);
return Unreleased.BinarySearch(item) < 0;
}

View file

@ -50,8 +50,6 @@ public static class Breeding
/// <returns>True if can inherit moves, false if cannot.</returns>
internal static bool GetCanInheritMoves(ushort species)
{
if (Legal.FixedGenderFromBiGender.Contains(species)) // Nincada -> Shedinja loses gender causing 'false', edge case
return true;
var pi = PKX.Personal[species];
if (pi is { Genderless: false, OnlyMale: false })
return true;

View file

@ -1,5 +1,4 @@
using System;
using static PKHeX.Core.BinLinkerAccessor;
namespace PKHeX.Core;
@ -8,60 +7,135 @@ namespace PKHeX.Core;
/// </summary>
public static partial class Legal
{
// Gen 1
internal static readonly Learnset[] LevelUpRB = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_rb.pkl"), MaxSpeciesID_1);
internal static readonly Learnset[] LevelUpY = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_y.pkl"), MaxSpeciesID_1);
internal const int MaxSpeciesID_1 = 151;
internal const int MaxMoveID_1 = 165;
internal const int MaxItemID_1 = 255;
internal const int MaxAbilityID_1 = 0;
// Gen 2
internal static readonly EggMoves2[] EggMovesGS = EggMoves2.GetArray(Util.GetBinaryResource("eggmove_gs.pkl"), MaxSpeciesID_2);
internal static readonly Learnset[] LevelUpGS = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_gs.pkl"), MaxSpeciesID_2);
internal static readonly EggMoves2[] EggMovesC = EggMoves2.GetArray(Util.GetBinaryResource("eggmove_c.pkl"), MaxSpeciesID_2);
internal static readonly Learnset[] LevelUpC = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_c.pkl"), MaxSpeciesID_2);
internal const int MaxSpeciesID_2 = 251;
internal const int MaxMoveID_2 = 251;
internal const int MaxItemID_2 = 255;
internal const int MaxAbilityID_2 = 0;
// Gen 3
internal static readonly Learnset[] LevelUpE = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_e.pkl"), "em"));
internal static readonly Learnset[] LevelUpRS = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_rs.pkl"), "rs"));
internal static readonly Learnset[] LevelUpFR = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_fr.pkl"), "fr"));
internal static readonly Learnset[] LevelUpLG = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_lg.pkl"), "lg"));
internal static readonly EggMoves6[] EggMovesRS = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_rs.pkl"), "rs"));
internal const int MaxSpeciesIndex_3 = 412;
internal const int MaxSpeciesID_3 = 386;
internal const int MaxMoveID_3 = 354;
internal const int MaxItemID_3 = 374;
internal const int MaxItemID_3_COLO = 547;
internal const int MaxItemID_3_XD = 593;
internal const int MaxAbilityID_3 = 77;
internal const int MaxBallID_3 = 0xC;
internal const int MaxGameID_3 = 15; // CXD
// Gen 4
internal static readonly Learnset[] LevelUpDP = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_dp.pkl"), "dp"));
internal static readonly Learnset[] LevelUpPt = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_pt.pkl"), "pt"));
internal static readonly Learnset[] LevelUpHGSS = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_hgss.pkl"), "hs"));
internal static readonly EggMoves6[] EggMovesDPPt = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_dppt.pkl"), "dp"));
internal static readonly EggMoves6[] EggMovesHGSS = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_hgss.pkl"), "hs"));
internal const int MaxSpeciesID_4 = 493;
internal const int MaxMoveID_4 = 467;
internal const int MaxItemID_4_DP = 464;
internal const int MaxItemID_4_Pt = 467;
internal const int MaxItemID_4_HGSS = 536;
internal const int MaxAbilityID_4 = 123;
internal const int MaxBallID_4 = 0x18;
internal const int MaxGameID_4 = 15; // CXD
// Gen 5
internal static readonly Learnset[] LevelUpBW = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_bw.pkl"), "51"));
internal static readonly Learnset[] LevelUpB2W2 = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_b2w2.pkl"), "52"));
internal static readonly EggMoves6[] EggMovesBW = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_bw.pkl"), "bw"));
internal const int MaxSpeciesID_5 = 649;
internal const int MaxMoveID_5 = 559;
internal const int MaxItemID_5_BW = 632;
internal const int MaxItemID_5_B2W2 = 638;
internal const int MaxAbilityID_5 = 164;
internal const int MaxBallID_5 = 0x19;
internal const int MaxGameID_5 = 23; // B2
// Gen 6
internal static readonly EggMoves6[] EggMovesXY = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_xy.pkl"), "xy"));
internal static readonly Learnset[] LevelUpXY = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_xy.pkl"), "xy"));
internal static readonly EggMoves6[] EggMovesAO = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_ao.pkl"), "ao"));
internal static readonly Learnset[] LevelUpAO = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_ao.pkl"), "ao"));
internal const int MaxSpeciesID_6 = 721;
internal const int MaxMoveID_6_XY = 617;
internal const int MaxMoveID_6_AO = 621;
internal const int MaxItemID_6_XY = 717;
internal const int MaxItemID_6_AO = 775;
internal const int MaxAbilityID_6_XY = 188;
internal const int MaxAbilityID_6_AO = 191;
internal const int MaxBallID_6 = 0x19;
internal const int MaxGameID_6 = 27; // OR
// Gen 7
internal static readonly EggMoves7[] EggMovesSM = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
internal static readonly Learnset[] LevelUpSM = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
internal static readonly EggMoves7[] EggMovesUSUM = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpUSUM = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpGG = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_gg.pkl"), "gg"));
internal const int MaxSpeciesID_7 = 802;
internal const int MaxMoveID_7 = 719;
internal const int MaxItemID_7 = 920;
internal const int MaxAbilityID_7 = 232;
internal const int MaxBallID_7 = 0x1A; // 26
internal const int MaxGameID_7 = 41; // Crystal (VC?)
// Gen 8
internal static readonly EggMoves7[] EggMovesSWSH = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_swsh.pkl"), "ss"));
internal static readonly Learnset[] LevelUpSWSH = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_swsh.pkl"), "ss"));
internal static readonly EggMoves6[] EggMovesBDSP = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_bdsp.pkl"), "bs"));
internal static readonly Learnset[] LevelUpBDSP = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_bdsp.pkl"), "bs"));
internal static readonly Learnset[] LevelUpLA = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_la.pkl"), "la"));
internal static readonly Learnset[] MasteryLA = LearnsetReader.GetArray(Get(Util.GetBinaryResource("mastery_la.pkl"), "la"));
internal const int MaxSpeciesID_7_USUM = 807;
internal const int MaxMoveID_7_USUM = 728;
internal const int MaxItemID_7_USUM = 959;
internal const int MaxAbilityID_7_USUM = 233;
// Gen 9
internal static readonly ushort[][] EggMovesSV = EggMoves9.GetArray(Get(Util.GetBinaryResource("eggmove_sv.pkl"), "sv"));
internal static readonly ushort[][] ReminderSV = EggMoves9.GetArray(Get(Util.GetBinaryResource("reminder_sv.pkl"), "sv"));
internal static readonly Learnset[] LevelUpSV = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_sv.pkl"), "sv"));
internal const int MaxSpeciesID_7b = 809; // Melmetal
internal const int MaxMoveID_7b = 742; // Double Iron Bash
internal const int MaxItemID_7b = 1057; // Magmar Candy
internal const int MaxBallID_7b = (int)Ball.Beast;
internal const int MaxGameID_7b = (int)GameVersion.GE;
internal const int MaxAbilityID_7b = MaxAbilityID_7_USUM;
// Current Binaries
internal const int MaxSpeciesID_8 = MaxSpeciesID_8_R2;
internal const int MaxMoveID_8 = MaxMoveID_8_R2;
internal const int MaxItemID_8 = MaxItemID_8_R2;
internal const int MaxAbilityID_8 = MaxAbilityID_8_R2;
// Orion (No DLC)
internal const int MaxSpeciesID_8_O0 = 890; // Eternatus
internal const int MaxMoveID_8_O0 = 796; // Steel Beam
internal const int MaxItemID_8_O0 = 1278; // Rotom Catalog, ignore all catalog parts
internal const int MaxAbilityID_8_O0 = 258; // Hunger Switch
// Rigel 1 (DLC 1: Isle of Armor)
internal const int MaxSpeciesID_8_R1 = 893; // Zarude
internal const int MaxMoveID_8_R1 = 818; // Surging Strikes
internal const int MaxItemID_8_R1 = 1589; // Mark Charm
internal const int MaxAbilityID_8_R1 = 260; // Unseen Fist
// Rigel 2 (DLC 2: Crown Tundra)
internal const int MaxSpeciesID_8_R2 = 898; // Calyrex
internal const int MaxMoveID_8_R2 = 826; // Eerie Spell
internal const int MaxItemID_8_R2 = 1607; // Reins of Unity
internal const int MaxAbilityID_8_R2 = 267; // As One (Glastrier)
internal const int MaxBallID_8 = 0x1A; // 26 Beast
internal const int MaxGameID_8 = 45; // Shield
internal const int MaxSpeciesID_8a = (int)Species.Enamorus;
internal const int MaxMoveID_8a = (int)Move.TakeHeart;
internal const int MaxItemID_8a = 1828; // Legend Plate
internal const int MaxBallID_8a = (int)Ball.LAOrigin;
internal const int MaxGameID_8a = (int)GameVersion.SP;
internal const int MaxAbilityID_8a = MaxAbilityID_8_R2;
internal const int MaxSpeciesID_8b = MaxSpeciesID_4; // Arceus-493
internal const int MaxMoveID_8b = MaxMoveID_8_R2;
internal const int MaxItemID_8b = 1822; // DS Sounds
internal const int MaxBallID_8b = (int)Ball.LAOrigin;
internal const int MaxGameID_8b = (int)GameVersion.SP;
internal const int MaxAbilityID_8b = MaxAbilityID_8_R2;
internal const int MaxSpeciesID_9 = (int)Species.IronLeaves;
internal const int MaxMoveID_9 = (int)Move.MagicalTorque;
internal const int MaxItemID_9 = 2400; // Yellow Dish
internal const int MaxAbilityID_9 = (int)Ability.MyceliumMight;
internal const int MaxBallID_9 = (int)Ball.LAOrigin;
internal const int MaxGameID_9 = (int)GameVersion.VL;
internal static readonly ushort[] HeldItems_GSC = ItemStorage2.GetAllHeld();
internal static readonly ushort[] HeldItems_RS = ItemStorage3RS.GetAllHeld();
internal static readonly ushort[] HeldItems_DP = ItemStorage4DP.GetAllHeld();
internal static readonly ushort[] HeldItems_Pt = ItemStorage4Pt.GetAllHeld(); // Griseous Orb Added
internal static readonly ushort[] HeldItems_HGSS = HeldItems_Pt;
internal static readonly ushort[] HeldItems_BW = ItemStorage5.GetAllHeld();
internal static readonly ushort[] HeldItem_AO = ItemStorage6XY.GetAllHeld();
internal static readonly ushort[] HeldItems_SM = ItemStorage7SM.GetAllHeld();
internal static readonly ushort[] HeldItems_USUM = ItemStorage7USUM.GetAllHeld();
internal static readonly ushort[] HeldItems_GG = Array.Empty<ushort>();
internal static readonly ushort[] HeldItems_SWSH = ItemStorage8SWSH.GetAllHeld();
internal static readonly ushort[] HeldItems_BS = ItemStorage8BDSP.GetAll();
internal static readonly ushort[] HeldItems_LA = Array.Empty<ushort>();
internal static readonly ushort[] HeldItems_SV = ItemStorage9SV.GetAllHeld();
internal static int GetMaxSpeciesOrigin(int generation) => generation switch
{

View file

@ -153,8 +153,9 @@ public sealed record EncounterEgg(ushort Species, byte Form, byte Level, int Gen
private void SetEncounterMoves(PKM pk, GameVersion version)
{
var learnset = GameData.GetLearnset(version, Species, Form);
var baseMoves = learnset.GetBaseEggMoves(Level);
pk.SetMoves(baseMoves);
var ls = GameData.GetLearnSource(version);
var learn = ls.GetLearnset(Species, Form);
var initial = learn.GetBaseEggMoves(LevelMin);
pk.SetMoves(initial);
}
}

View file

@ -125,7 +125,8 @@ public abstract record EncounterSlot(EncounterArea Area, ushort Species, byte Fo
protected virtual void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, level, version);
var source = GameData.GetLearnSource(version);
source.SetEncounterMoves(Species, Form, level, moves);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View file

@ -84,13 +84,7 @@ public sealed record EncounterSlot8a : EncounterSlot, IAlphaReadOnly, IMasteryIn
}
}
public (Learnset Learn, Learnset Mastery) GetLevelUpInfo()
{
var index = PersonalTable.LA.GetFormIndex(Species, Form);
var learn = Legal.LevelUpLA[index];
var mastery = Legal.MasteryLA[index];
return (learn, mastery);
}
public (Learnset Learn, Learnset Mastery) GetLevelUpInfo() => LearnSource8LA.GetLearnsetAndMastery(Species, Form);
protected override void SetFormatSpecificData(PKM pk)
{
@ -174,14 +168,12 @@ public sealed record EncounterSlot8a : EncounterSlot, IAlphaReadOnly, IMasteryIn
bool allowAlphaPurchaseBug = Area.Type is not SlotType.OverworldMMO; // Everything else Alpha is pre-1.1
var level = pk.Met_Level;
var index = PersonalTable.LA.GetFormIndex(Species, Form);
var learn = Legal.LevelUpLA[index];
var (learn, mastery) = GetLevelUpInfo();
ushort alpha = pk is PA8 pa ? pa.AlphaMove : (ushort)0;
if (!p.IsValidPurchasedEncounter(learn, level, alpha, allowAlphaPurchaseBug))
return false;
Span<ushort> moves = stackalloc ushort[4];
var mastery = Legal.MasteryLA[index];
if (pk is PA8 { AlphaMove: not 0 } pa8)
{
moves[0] = pa8.AlphaMove;

View file

@ -41,10 +41,10 @@ public sealed record EncounterSlot8b : EncounterSlot
var et = PersonalTable.BDSP;
var sf = et.GetFormEntry(Species, Form);
var species = sf.HatchSpecies;
var baseEgg = Legal.EggMovesBDSP[species].Moves;
var baseEgg = LearnSource8BDSP.Instance.GetEggMoves(species, 0);
if (baseEgg.Length == 0)
return move == 0;
return Array.IndexOf(baseEgg, move) >= 0;
return baseEgg.Contains(move);
}
public bool GetBaseEggMove(out ushort move)
@ -52,7 +52,7 @@ public sealed record EncounterSlot8b : EncounterSlot
var et = PersonalTable.BDSP;
var sf = et.GetFormEntry(Species, Form);
var species = sf.HatchSpecies;
var baseEgg = Legal.EggMovesBDSP[species].Moves;
var baseEgg = LearnSource8BDSP.Instance.GetEggMoves(species, 0);
if (baseEgg.Length == 0)
{
move = 0;

View file

@ -50,7 +50,8 @@ public sealed record EncounterSlot7GO : EncounterSlotGO
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, level, GameVersion.GG);
var source = GameData.GetLearnSource(GameVersion.GG);
source.SetEncounterMoves(Species, Form, level, moves);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View file

@ -139,7 +139,11 @@ public sealed record EncounterSlot8GO : EncounterSlotGO, IFixedOTFriendship
pk.SetMaximumPPCurrent(moves);
}
public void GetInitialMoves(int level, Span<ushort> moves) => MoveLevelUp.GetEncounterMoves(moves, Species, Form, level, OriginGroup);
public void GetInitialMoves(int level, Span<ushort> moves)
{
var source = GameData.GetLearnSource(OriginGroup);
source.SetEncounterMoves(Species, Form, level, moves);
}
public override EncounterMatchRating GetMatchRating(PKM pk)
{

View file

@ -161,7 +161,8 @@ public abstract record EncounterStatic(GameVersion Version) : IEncounterable, IM
else
{
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, level, version);
var source = GameData.GetLearnSource(version);
source.SetEncounterMoves(Species, Form, level, moves);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View file

@ -11,7 +11,7 @@ public sealed record EncounterStatic7(GameVersion Version) : EncounterStatic(Ver
public Moveset Relearn { get; init; }
public bool IsTotem => FormInfo.IsTotemForm(Species, Form);
public bool IsTotemNoTransfer => Legal.Totem_NoTransfer.Contains(Species);
public bool IsTotemNoTransfer => Species is (int)Core.Species.Marowak or (int)Core.Species.Araquanid or (int)Core.Species.Togedemaru or (int)Core.Species.Ribombee;
public int GetTotemBaseForm() => FormInfo.GetTotemBaseForm(Species, Form);
public bool IsRandomUnspecificForm => Form >= FormDynamic;

View file

@ -135,13 +135,11 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
const bool allowAlphaPurchaseBug = true; // Everything else Alpha is pre-1.1
var level = pk.Met_Level;
var index = PersonalTable.LA.GetFormIndex(Species, Form);
var learn = Legal.LevelUpLA[index];
var (learn, mastery) = GetLevelUpInfo();
if (!p.IsValidPurchasedEncounter(learn, level, alpha, allowAlphaPurchaseBug))
return false;
Span<ushort> moves = stackalloc ushort[4];
var mastery = Legal.MasteryLA[index];
if (Moves.HasMoves)
Moves.CopyTo(moves);
else
@ -165,10 +163,7 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
public (Learnset Learn, Learnset Mastery) GetLevelUpInfo()
{
var index = PersonalTable.LA.GetFormIndex(Species, Form);
var learn = Legal.LevelUpLA[index];
var mastery = Legal.MasteryLA[index];
return (learn, mastery);
return LearnSource8LA.GetLearnsetAndMastery(Species, Form);
}
public void LoadInitialMoveset(PA8 pa8, Span<ushort> moves, Learnset learn, int level)

View file

@ -166,7 +166,8 @@ public abstract record EncounterTrade(GameVersion Version) : IEncounterable, IMo
else
{
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, level, version);
var source = GameData.GetLearnSource(version);
source.SetEncounterMoves(Species, Form, level, moves);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View file

@ -174,7 +174,7 @@ public static class EncounterFinder
private static bool WasGiftEgg(PKM pk, int gen, ushort loc) => !pk.FatefulEncounter && gen switch
{
3 => pk.IsEgg && (byte)pk.Met_Location == 253, // Gift Egg, indistinguishable from normal eggs after hatch
4 => Legal.GiftEggLocation4.Contains(loc) || (pk.Format != 4 && (loc == Locations.Faraway4 && pk.HGSS)),
4 => (uint)(loc - 2009) <= (2014 - 2009) || (pk.Format != 4 && (loc == Locations.Faraway4 && pk.HGSS)),
5 => loc is Locations.Breeder5,
_ => loc is Locations.Breeder6,
};

View file

@ -302,7 +302,8 @@ public static class EncounterMovesetGenerator
continue;
}
var eggMoves = MoveEgg.GetEggMoves(egg.Species, egg.Form, egg.Version, egg.Generation);
var source = GameData.GetLearnSource(egg.Version);
var eggMoves = source.GetEggMoves(egg.Species, egg.Form);
int flags = Moveset.BitOverlap(eggMoves, needs);
var vt = Array.IndexOf(needs, (ushort)Move.VoltTackle);
if (vt != -1 && egg is EncounterEgg { CanHaveVoltTackle: true })
@ -463,7 +464,8 @@ public static class EncounterMovesetGenerator
private static int GetMoveMaskGen2(ReadOnlySpan<ushort> needs, IEncounterTemplate enc)
{
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, enc.Species, 0, enc.LevelMin, enc.Version);
var source = GameData.GetLearnSource(enc.Version);
source.SetEncounterMoves(enc.Species, 0, enc.LevelMin, moves);
return Moveset.BitOverlap(moves, needs);
}

View file

@ -107,7 +107,7 @@ public static class EncounterSuggestion
if (pk.Format == 4) // Pal Park
return Locations.Transfer3;
if (pk.Format >= 5) // Transporter
return Legal.GetTransfer45MetLocation(pk);
return PK5.GetTransferMetLocation4(pk);
return -1;
}

View file

@ -0,0 +1,67 @@
using System;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
public static class EggHatchLocation3
{
private const byte MaskRS = 1 << 0; // 1
private const byte MaskE = 1 << 1; // 2
private const byte MaskFRLG = 1 << 2; // 4
private const byte MaskAll = MaskRS | MaskE | MaskFRLG; // 7
public static bool IsValidMet3(int location, GameVersion game) => game switch
{
R or S => IsValidMet3RS(location),
E => IsValidMet3E(location),
FR or LG => IsValidMet3FRLG(location),
_ => false,
};
public static bool IsValidMet3RS(int location) => HasLocationFlag(LocationPermitted3, MaskRS, location);
public static bool IsValidMet3E(int location) => HasLocationFlag(LocationPermitted3, MaskE, location);
public static bool IsValidMet3FRLG(int location) => HasLocationFlag(LocationPermitted3, MaskFRLG, location);
public static bool IsValidMet3Any(int location) => HasLocationFlag(LocationPermitted3, MaskAll, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)
{
if ((uint)location >= arr.Length)
return false;
var flags = arr[location];
return (flags & mask) != 0;
}
// 064 is an unused location for Meteor Falls
// 084 is Inside of a truck, no possible pokemon can be hatched there
// 071 is Mirage island, cannot be obtained as the player is technically still on Route 130's map.
// 075 is an unused location for Fiery Path
// 077 is an unused location for Jagged Pass
// 155 - 158 Sevii Isle 6-9 Unused
// 171 - 173 Sevii Isle 22-24 Unused
private static ReadOnlySpan<byte> LocationPermitted3 => new byte[]
{
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
3, 0, 3, 3, 3, 0, 3, 0, 3, 3,
3, 3, 3, 3, 0, 3, 3, 7, 4, 4, // 87 = all
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 0, 0, 0, 0, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 0, 0, 0, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 6, 2, 2, 2, // 196 = fr/lg & e
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2,
};
}

View file

@ -0,0 +1,61 @@
using System;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
public static class EggHatchLocation4
{
private const byte MaskDP = 1 << 0; // 1
private const byte MaskPt = 1 << 1; // 2
private const byte MaskHGSS = 1 << 2; // 4
private const byte MaskAll4 = MaskDP | MaskPt | MaskHGSS; // 7
public static bool IsValidMet4(int location, GameVersion game) => game switch
{
D or P => IsValidMet4DP(location),
Pt => IsValidMet4Pt(location),
HG or SS => IsValidMet4HGSS(location),
_ => false,
};
public static bool IsValidMet4DP(int location) => HasLocationFlag(LocationPermitted4, MaskDP, location);
public static bool IsValidMet4Pt(int location) => HasLocationFlag(LocationPermitted4, MaskPt, location);
public static bool IsValidMet4HGSS(int location) => HasLocationFlag(LocationPermitted4, MaskHGSS, location);
public static bool IsValidMet4Any(int location) => HasLocationFlag(LocationPermitted4, MaskAll4, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)
{
if ((uint)location >= arr.Length)
return false;
var flags = arr[location];
return (flags & mask) != 0;
}
private static ReadOnlySpan<byte> LocationPermitted4 => new byte[]
{
0, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 2, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 2,
7, 3, 3, 3, 3, 2, 0, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 6, 6, 6, 6, 6, 2, 2, 2,
2, 2, 2, 2, 2, 2, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 0, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 0, 4,
};
}

View file

@ -0,0 +1,58 @@
using System;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
public static class EggHatchLocation5
{
private const byte MaskWhite = 1 << 0; // 1
private const byte MaskBlack = 1 << 1; // 2
private const byte MaskWhite2 = 1 << 2; // 4
private const byte MaskBlack2 = 1 << 3; // 8
public static bool IsValidMet5(int location, GameVersion game)
{
var shift = (uint)((int)game - (int)W);
if (shift >= 4)
return false;
var mask = (byte)(1 << (int)shift);
return HasLocationFlag(LocationPermitted5, mask, location);
}
public static bool IsValidMet5W(int location) => HasLocationFlag(LocationPermitted5, MaskWhite, location);
public static bool IsValidMet5B(int location) => HasLocationFlag(LocationPermitted5, MaskBlack, location);
public static bool IsValidMet5W2(int location) => HasLocationFlag(LocationPermitted5, MaskWhite2, location);
public static bool IsValidMet5B2(int location) => HasLocationFlag(LocationPermitted5, MaskBlack2, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)
{
if ((uint)location >= arr.Length)
return false;
var flags = arr[location];
return (flags & mask) != 0;
}
// Two game-specific locations we need to double check for.
// White / White2 cannot access Black Gate (112)
// Black / Black2 cannot access White Gate (113)
private static ReadOnlySpan<byte> LocationPermitted5 => new byte[]
{
00, 00, 00, 00, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 03, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
03, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 03,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 03, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 10, 05, 15, 15, 15, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 00, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12,
};
}

View file

@ -0,0 +1,30 @@
namespace PKHeX.Core;
public static class EggHatchLocation6
{
public static bool IsValidMet6XY(int location)
{
const int min = 6;
const int max = 168;
var delta = location - min;
if ((uint)delta >= max - min)
return false;
if (location % 2 != 0)
return false; // All locations are even
return location != 80; // unused
}
public static bool IsValidMet6AO(int location)
{
const int min = 170;
const int max = 354;
var delta = location - min;
if ((uint)delta >= max - min)
return false;
if (location % 2 != 0)
return false; // All locations are even
return location != 348; // unused
}
}

View file

@ -0,0 +1,59 @@
using System;
namespace PKHeX.Core;
public static class EggHatchLocation7
{
private const byte MaskSM = 1 << 0; // 1
private const byte MaskUSUM = 1 << 1; // 2
public static bool IsValidMet7SM(int location)
{
if (HasLocationFlag(LocationPermitted7, MaskSM, location))
return true;
return location == Locations.Pelago7; // 30016
}
public static bool IsValidMet7USUM(int location)
{
if (HasLocationFlag(LocationPermitted7, MaskUSUM, location))
return true;
return location == Locations.Pelago7; // 30016
}
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)
{
if ((uint)location >= arr.Length)
return false;
var flags = arr[location];
return (flags & mask) != 0;
}
private static ReadOnlySpan<byte> LocationPermitted7 => new byte[]
{
0, 0, 0, 0, 0, 0, 3, 0, 3, 0, // 000
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 0, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
0, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 0, 0, 0, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0, // 100
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 0, 0, 0, 0, 2, 0,
2, 0, 2, 0, 2, 0, 2, 0, 2, 0, // 200
2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
2, 0, 2,
};
}

View file

@ -0,0 +1,36 @@
using System;
namespace PKHeX.Core;
public static class EggHatchLocation8
{
public static bool IsValidMet8SWSH(int location)
{
if (location % 2 != 0)
return false;
var index = location >> 1;
var arr = LocationPermitted8;
if ((uint)index >= arr.Length)
return false;
return arr[index] != 0;
}
// Odd indexes ignored.
private static ReadOnlySpan<byte> LocationPermitted8 => new byte[]
{
0, 0, 0, 1, 1, 0, 1, 1, 1, 1,
1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
1, 0, 1, 1, 1, 0, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 0, 1, 1,
1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1,
};
}

View file

@ -0,0 +1,87 @@
using System;
namespace PKHeX.Core;
public static class EggHatchLocation8b
{
private const byte MaskBD = 1 << 0; // 1
private const byte MaskSP = 1 << 1; // 2
public static bool IsValidMet8BD(int location)
{
if (HasLocationFlag(LocationPermitted8b, MaskBD, location))
return true;
return location == Locations.Pelago7; // 30016
}
public static bool IsValidMet8SP(int location)
{
if (HasLocationFlag(LocationPermitted8b, MaskSP, location))
return true;
return location == Locations.Pelago7; // 30016
}
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)
{
if ((uint)location >= arr.Length)
return false;
var flags = arr[location];
return (flags & mask) != 0;
}
// BD Exclusive
// 216, // Spear Pillar
// 218, // Hall of Origin
// 498, // Ramanas Park (Johto Room)
// 503, // Ramanas Park (Rainbow Room)
// 650, // Ramanas Park (Johto Room)
// 655, // Ramanas Park (Rainbow Room)
// SP Exclusive
// 217, // Spear Pillar
// 497, // Ramanas Park (Kanto Room)
// 504, // Ramanas Park (Squall Room)
// 618, // Hall of Origin
// 649, // Ramanas Park (Kanto Room)
// 656, // Ramanas Park (Squall Room)
// Unobtainable
// 094, 103, 107, // Hearthome City
// 154, 155, 158, // Sunyshore City
// 181, 182, 183, // Pokémon League
// 329, // Lake Acuity
// 337, 338, // Battle Park
// 339, 340, 341, 342, 343, 344, // Battle Tower
// 345, 353, 421, // Mystery Zone
// 474, // Resort Area
// 483, 484, // Mystery Zone
// 491, 492, 493, // Mystery Zone
// 495, // Ramanas Park
// 620, 621, 622, 623, // Grand Underground (Secret Base)
// 625, // Sea (sailing animation)
// 627, 628, 629, 630, 631, 632, // Grand Underground (Secret Base)
// 633, 634, 635, 636, 637, 638, // Grand Underground (Secret Base)
// 639, 640, 641, 642, 643, 644, // Grand Underground (Secret Base)
// 645, 646, 647, // Grand Underground (Secret Base)
private static ReadOnlySpan<byte> LocationPermitted8b => new byte[]
{
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 1, 3, 1, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3,
3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 0, 3, 2, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 3, 3, 3, 3, 3, 3, 2, 3,
};
}

View file

@ -0,0 +1,39 @@
using System;
namespace PKHeX.Core;
public static class EggHatchLocation9
{
private const byte MaskScarlet = 1 << 0; // 1
private const byte MaskViolet = 1 << 1; // 2
public static bool IsValidMet9SL(int location) => HasLocationFlag(LocationPermitted9, MaskScarlet, location);
public static bool IsValidMet9VL(int location) => HasLocationFlag(LocationPermitted9, MaskViolet, location);
private static bool HasLocationFlag(ReadOnlySpan<byte> arr, byte mask, int location)
{
if ((uint)location >= arr.Length)
return false;
var flags = arr[location];
return (flags & mask) != 0;
}
// 130 Naranja Academy does not exist in Violet
// 131 Uva Academy does not exist in Scarlet
private static ReadOnlySpan<byte> LocationPermitted9 => new byte[]
{
0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 0, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 0, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 0, 3, 0, 3,
3, 0, 3, 0, 0, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 3, 0,
3, 0, 3, 0, 3, 0, 3, 0, 0, 3,
0, 3, 0, 3, 0, 3, 0, 3, 0, 3,
0, 3, 0, 3, 0, 3, 0, 3, 3, 0,
0, 0, 0, 0, 3, 0, 0, 0, 0, 0,
1, 2,
};
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core;
@ -27,21 +26,21 @@ public static class EncounterVerifier
EncounterSlot w => VerifyEncounterWild(w),
EncounterStatic s => VerifyEncounterStatic(pk, s),
MysteryGift g => VerifyEncounterEvent(pk, g),
_ => new CheckResult(Severity.Invalid, LEncInvalid, CheckIdentifier.Encounter),
_ => GetInvalid(LEncInvalid),
};
private static CheckResult VerifyEncounterG12(PKM pk, IEncounterTemplate enc)
{
if (enc.EggEncounter)
return VerifyEncounterEgg(pk, enc.Generation);
return VerifyEncounterEgg(pk, 2);
return enc switch
{
EncounterSlot1 => new CheckResult(Severity.Valid, LEncCondition, CheckIdentifier.Encounter),
EncounterSlot1 => GetValid(LEncCondition),
EncounterSlot2 s2 => VerifyWildEncounterGen2(pk, s2),
EncounterStatic s => VerifyEncounterStatic(pk, s),
EncounterTrade t => VerifyEncounterTrade(pk, t),
_ => new CheckResult(Severity.Invalid, LEncInvalid, CheckIdentifier.Encounter),
_ => GetInvalid(LEncInvalid),
};
}
@ -57,21 +56,21 @@ public static class EncounterVerifier
switch (encounter.Location)
{
case 19: // National Park
return new CheckResult(Severity.Invalid, LG2InvalidTilePark, CheckIdentifier.Encounter);
return GetInvalid(LG2InvalidTilePark);
case 76: // Route 14
return new CheckResult(Severity.Invalid, LG2InvalidTileR14, CheckIdentifier.Encounter);
return GetInvalid(LG2InvalidTileR14);
}
break;
}
return new CheckResult(Severity.Valid, LEncCondition, CheckIdentifier.Encounter);
return GetValid(LEncCondition);
}
private static CheckResult VerifyWildEncounterCrystalHeadbutt(ITrainerID32 tr, EncounterSlot2 s2)
{
return s2.IsTreeAvailable(tr.TID16)
? new CheckResult(Severity.Valid, LG2TreeID, CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, LG2InvalidTileTreeNotFound, CheckIdentifier.Encounter);
? GetValid(LG2TreeID)
: GetInvalid(LG2InvalidTileTreeNotFound);
}
// Eggs
@ -86,20 +85,20 @@ public static class EncounterVerifier
8 when GameVersion.BDSP.Contains((GameVersion)pk.Version) => pk.IsEgg ? VerifyUnhatchedEgg(pk, Locations.LinkTrade6NPC, Locations.Default8bNone) : VerifyEncounterEgg8BDSP(pk),
8 => pk.IsEgg ? VerifyUnhatchedEgg(pk, Locations.LinkTrade6) : VerifyEncounterEgg8(pk),
9 => pk.IsEgg ? VerifyUnhatchedEgg(pk, Locations.LinkTrade6) : VerifyEncounterEgg9(pk),
_ => new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter),
_ => GetInvalid(LEggLocationInvalid),
};
private static CheckResult VerifyUnhatchedEgg3(PKM pk)
{
if (pk.Met_Level != 0)
return new CheckResult(Severity.Invalid, string.Format(LEggFMetLevel_0, 0), CheckIdentifier.Encounter);
return GetInvalid(string.Format(LEggFMetLevel_0, 0));
// Only EncounterEgg should reach here.
var loc = pk.FRLG ? Locations.HatchLocationFRLG : Locations.HatchLocationRSE;
if (pk.Met_Location != loc)
return new CheckResult(Severity.Invalid, LEggMetLocationFail, CheckIdentifier.Encounter);
return GetInvalid(LEggMetLocationFail);
return new CheckResult(Severity.Valid, LEggLocation, CheckIdentifier.Encounter);
return GetValid(LEggLocation);
}
private static CheckResult VerifyEncounterEgg3(PKM pk)
@ -108,172 +107,192 @@ public static class EncounterVerifier
return VerifyEncounterEgg3Transfer(pk);
if (pk.Met_Level != 0)
return new CheckResult(Severity.Invalid, string.Format(LEggFMetLevel_0, 0), CheckIdentifier.Encounter);
return GetInvalid(string.Format(LEggFMetLevel_0, 0));
// Check the origin game list.
var met = (byte)pk.Met_Location;
var locs = pk.FRLG ? Legal.ValidMet_FRLG : pk.E ? Legal.ValidMet_E : Legal.ValidMet_RS;
if (locs.Contains(met))
return new CheckResult(Severity.Valid, LEggLocation, CheckIdentifier.Encounter);
bool valid = EggHatchLocation3.IsValidMet3(met, (GameVersion)pk.Version);
if (valid)
return GetValid(LEggLocation);
// Version isn't updated when hatching on a different game. Check any game.
if (Legal.ValidMet_FRLG.Contains(met) || Legal.ValidMet_E.Contains(met) || Legal.ValidMet_RS.Contains(met))
return new CheckResult(Severity.Valid, LEggLocationTrade, CheckIdentifier.Encounter);
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
if (EggHatchLocation3.IsValidMet3Any(met))
return GetValid(LEggLocationTrade);
return GetInvalid(LEggLocationInvalid);
}
private static CheckResult GetInvalid(string message, CheckIdentifier ident = CheckIdentifier.Encounter) => new(Severity.Invalid, message, ident);
private static CheckResult GetValid(string message) => new(Severity.Valid, message, CheckIdentifier.Encounter);
private static CheckResult VerifyEncounterEgg3Transfer(PKM pk)
{
if (pk.IsEgg)
return new CheckResult(Severity.Invalid, LTransferEgg, CheckIdentifier.Encounter);
return GetInvalid(LTransferEgg);
if (pk.Met_Level < 5)
return new CheckResult(Severity.Invalid, LTransferEggMetLevel, CheckIdentifier.Encounter);
return GetInvalid(LTransferEggMetLevel);
var expectEgg = pk is PB8 ? Locations.Default8bNone : 0;
if (pk.Egg_Location != expectEgg)
return new CheckResult(Severity.Invalid, LEggLocationNone, CheckIdentifier.Encounter);
return GetInvalid(LEggLocationNone);
if (pk.Format != 4)
{
if (pk.Met_Location != Locations.Transfer4)
return new CheckResult(Severity.Invalid, LTransferEggLocationTransporter, CheckIdentifier.Encounter);
return GetInvalid(LTransferEggLocationTransporter);
}
else
{
if (pk.Met_Location != Locations.Transfer3)
return new CheckResult(Severity.Invalid, LEggLocationPalPark, CheckIdentifier.Encounter);
return GetInvalid(LEggLocationPalPark);
}
return new CheckResult(Severity.Valid, LEggLocation, CheckIdentifier.Encounter);
return GetValid(LEggLocation);
}
private static CheckResult VerifyEncounterEgg4(PKM pk)
{
if (pk.Format == 4)
if (pk.Format != 4) // transferred
{
// Traded eggs don't update Version, like in future games.
var locations = pk.WasTradedEgg ? Legal.ValidMet_4 :
pk.HGSS ? Legal.ValidMet_HGSS :
pk.Pt ? Legal.ValidMet_Pt :
Legal.ValidMet_DP;
return VerifyEncounterEggLevelLoc(pk, 0, locations);
if (pk.IsEgg)
return GetInvalid(LTransferEgg);
if (pk.Met_Level < 1)
return GetInvalid(LTransferEggMetLevel);
if (pk.Met_Location != Locations.Transfer4)
return GetInvalid(LTransferEggLocationTransporter);
return GetValid(LEggLocation);
}
if (pk.IsEgg)
return new CheckResult(Severity.Invalid, LTransferEgg, CheckIdentifier.Encounter);
// transferred
if (pk.Met_Level < 1)
return new CheckResult(Severity.Invalid, LTransferEggMetLevel, CheckIdentifier.Encounter);
if (pk.Met_Location != Locations.Transfer4)
return new CheckResult(Severity.Invalid, LTransferEggLocationTransporter, CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, LEggLocation, CheckIdentifier.Encounter);
// Native
const byte level = 0;
if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
var met = (ushort)pk.Met_Location;
bool valid = EggHatchLocation4.IsValidMet4(met, (GameVersion)pk.Version);
if (valid)
return GetValid(LEggLocation);
// Version isn't updated when hatching on a different game. Check any game.
if (EggHatchLocation4.IsValidMet4Any(met))
return GetValid(LEggLocationTrade);
return GetInvalid(LEggLocationInvalid);
}
private static CheckResult VerifyEncounterEgg5(PKM pk)
{
// Two game-specific locations we need to double check for.
// White / White2 cannot access Black Gate (112)
// Black / Black2 cannot access White Gate (113)
var met = pk.Met_Location;
var delta = (uint)(met - 112);
if (delta <= 1)
{
var ver = pk.Version & 1; // W*=0, B*=1
if (ver == delta)
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
}
return VerifyEncounterEggLevelLoc(pk, 1, pk.B2W2 ? Legal.ValidMet_B2W2 : Legal.ValidMet_BW);
const byte level = 1;
if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
var met = (ushort)pk.Met_Location;
bool valid = EggHatchLocation5.IsValidMet5(met, (GameVersion)pk.Version);
if (valid)
return GetValid(LEggLocation);
return GetInvalid(LEggLocationInvalid);
}
private static CheckResult VerifyEncounterEgg6(PKM pk)
{
if (pk.AO)
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_AO);
const byte level = 1;
if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
if (pk.Egg_Location == Locations.HatchLocation6AO) // Battle Resort Daycare is only OR/AS.
return new CheckResult(Severity.Invalid, LEggMetLocationFail, CheckIdentifier.Encounter);
var met = (ushort)pk.Met_Location;
bool valid = pk.XY
? EggHatchLocation6.IsValidMet6XY(met)
: EggHatchLocation6.IsValidMet6AO(met);
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_XY);
if (valid)
return GetValid(LEggLocation);
return GetInvalid(LEggLocationInvalid);
}
private static CheckResult VerifyEncounterEgg7(PKM pk)
{
if (pk.SM)
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_SM);
if (pk.USUM)
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_USUM);
const byte level = 1;
if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
// no other games
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
var met = (ushort)pk.Met_Location;
bool valid = pk.SM
? EggHatchLocation7.IsValidMet7SM(met)
: EggHatchLocation7.IsValidMet7USUM(met);
if (valid)
return GetValid(LEggLocation);
return GetInvalid(LEggLocationInvalid);
}
private static CheckResult VerifyEncounterEgg8(PKM pk)
{
if (pk.SWSH)
{
if (pk.BDSP)
return VerifyEncounterEggLevelLoc(pk, 1, (location, game) => location == (game == GameVersion.SW ? Locations.HOME_SWBD : Locations.HOME_SHSP));
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_SWSH);
}
const byte level = 1;
if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
// no other games
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
}
var met = (ushort)pk.Met_Location;
bool valid = pk.BDSP // Transferred from BD/SP, now acting like a SW/SH egg.
? Locations.IsValidMetBDSP(met, pk.Version)
: EggHatchLocation8.IsValidMet8SWSH(met);
private static CheckResult VerifyEncounterEgg9(PKM pk)
{
if (pk.SV)
return VerifyEncounterEggLevelLoc(pk, 1, Legal.IsValidEggHatchLocation9);
// no other games
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
if (valid)
return GetValid(LEggLocation);
return GetInvalid(LEggLocationInvalid);
}
private static CheckResult VerifyEncounterEgg8BDSP(PKM pk)
{
if (pk.BDSP)
return VerifyEncounterEggLevelLoc(pk, 1, Legal.IsValidEggHatchLocation8b);
const byte level = 1;
if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
// no other games
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
var met = (ushort)pk.Met_Location;
bool valid = pk.Version == (int)GameVersion.BD
? EggHatchLocation8b.IsValidMet8BD(met)
: EggHatchLocation8b.IsValidMet8SP(met);
if (valid)
return GetValid(LEggLocation);
return GetInvalid(LEggLocationInvalid);
}
private static CheckResult VerifyEncounterEggLevelLoc(PKM pk, int eggLevel, IReadOnlySet<ushort> MetLocations)
private static CheckResult VerifyEncounterEgg9(PKM pk)
{
return VerifyEncounterEggLevelLoc(pk, eggLevel, (location, _) => MetLocations.Contains(location));
}
const byte level = 1;
if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
// (hatch location, hatch version, bool result)
private static CheckResult VerifyEncounterEggLevelLoc(PKM pk, int eggLevel, Func<ushort, GameVersion, bool> isValid)
{
if (pk.Met_Level != eggLevel)
return new CheckResult(Severity.Invalid, string.Format(LEggFMetLevel_0, eggLevel), CheckIdentifier.Encounter);
return isValid((ushort)pk.Met_Location, (GameVersion)pk.Version)
? new CheckResult(Severity.Valid, LEggLocation, CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
var met = (ushort)pk.Met_Location;
bool valid = pk.Version == (int)GameVersion.SL
? EggHatchLocation9.IsValidMet9SL(met)
: EggHatchLocation9.IsValidMet9VL(met);
if (valid)
return GetValid(LEggLocation);
return GetInvalid(LEggLocationInvalid);
}
private static CheckResult VerifyUnhatchedEgg(PKM pk, int tradeLoc, int noneLoc = 0)
{
var eggLevel = pk.Format < 5 ? 0 : 1;
if (pk.Met_Level != eggLevel)
return new CheckResult(Severity.Invalid, string.Format(LEggFMetLevel_0, eggLevel), CheckIdentifier.Encounter);
return GetInvalid(string.Format(LEggFMetLevel_0, eggLevel));
if (pk.Egg_Location == tradeLoc)
return new CheckResult(Severity.Invalid, LEggLocationTradeFail, CheckIdentifier.Encounter);
return GetInvalid(LEggLocationTradeFail);
var met = pk.Met_Location;
if (met == tradeLoc)
return new CheckResult(Severity.Valid, LEggLocationTrade, CheckIdentifier.Encounter);
return GetValid(LEggLocationTrade);
return met == noneLoc
? new CheckResult(Severity.Valid, LEggUnhatched, CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, LEggLocationNone, CheckIdentifier.Encounter);
? GetValid(LEggUnhatched)
: GetInvalid(LEggLocationNone);
}
// Other
private static CheckResult VerifyEncounterWild(EncounterSlot slot)
{
var summary = slot.GetConditionString(out bool valid);
return new CheckResult(valid ? Severity.Valid : Severity.Invalid, summary, CheckIdentifier.Encounter);
return valid ? GetValid(summary) : GetInvalid(summary);
}
private static CheckResult VerifyEncounterStatic(PKM pk, EncounterStatic s)
@ -283,24 +302,24 @@ public static class EncounterVerifier
{
case 3:
if (s is EncounterStaticShadow {EReader: true} && pk.Language != (int)LanguageID.Japanese) // Non-JP E-reader Pokemon
return new CheckResult(Severity.Invalid, LG3EReader, CheckIdentifier.Encounter);
return GetInvalid(LG3EReader);
switch (s.Species)
{
case (int)Species.Mew when s.Location == 201 && pk.Language != (int)LanguageID.Japanese: // Non-JP Mew (Old Sea Map)
return new CheckResult(Severity.Invalid, LEncUnreleasedEMewJP, CheckIdentifier.Encounter);
return GetInvalid(LEncUnreleasedEMewJP);
case (int)Species.Deoxys when s.Location == 200 && pk.Language == (int)LanguageID.Japanese: // JP Deoxys (Birth Island)
return new CheckResult(Severity.Invalid, LEncUnreleased, CheckIdentifier.Encounter);
return GetInvalid(LEncUnreleased);
}
break;
case 4:
if (s is EncounterStatic4 {Roaming: true} && pk.Met_Location == 193 && pk is IGroundTile {GroundTile:GroundTileType.Water}) // Roaming pokemon surfing in Johto Route 45
return new CheckResult(Severity.Invalid, LG4InvalidTileR45Surf, CheckIdentifier.Encounter);
return GetInvalid(LG4InvalidTileR45Surf);
break;
case 7:
if (s.EggLocation == Locations.Daycare5 && pk.RelearnMove1 != 0) // Eevee gift egg
return new CheckResult(Severity.Invalid, LEncStaticRelearn, CheckIdentifier.RelearnMove); // not gift egg
return GetInvalid(LEncStaticRelearn, CheckIdentifier.RelearnMove); // not gift egg
break;
}
if (s.EggEncounter && !pk.IsEgg) // hatched
@ -310,7 +329,7 @@ public static class EncounterVerifier
return hatchCheck;
}
return new CheckResult(Severity.Valid, LEncStaticMatch, CheckIdentifier.Encounter);
return GetValid(LEncStaticMatch);
}
private static CheckResult VerifyEncounterTrade(ISpeciesForm pk, EncounterTrade trade)
@ -323,9 +342,9 @@ public static class EncounterVerifier
var names = ParseSettings.SpeciesStrings;
var evolved = names[species + 1];
var unevolved = names[species];
return new CheckResult(Severity.Invalid, string.Format(LEvoTradeReq, unevolved, evolved), CheckIdentifier.Encounter);
return GetInvalid(string.Format(LEvoTradeReq, unevolved, evolved));
}
return new CheckResult(Severity.Valid, LEncTradeMatch, CheckIdentifier.Encounter);
return GetValid(LEncTradeMatch);
}
private static CheckResult VerifyEncounterEvent(PKM pk, MysteryGift gift)
@ -334,7 +353,7 @@ public static class EncounterVerifier
{
case PCD pcd:
if (!pcd.CanBeReceivedByVersion(pk.Version) && pcd.Gift.PK.Version == 0)
return new CheckResult(Severity.Invalid, string.Format(L_XMatches0_1, gift.CardHeader, $"-- {LEncGiftVersionNotDistributed}"), CheckIdentifier.Encounter);
return GetInvalid(string.Format(L_XMatches0_1, gift.CardHeader, $"-- {LEncGiftVersionNotDistributed}"));
break;
}
if (!pk.IsEgg && gift.IsEgg) // hatched
@ -345,6 +364,6 @@ public static class EncounterVerifier
}
// Strict matching already performed by EncounterGenerator. May be worth moving some checks here to better flag invalid gifts.
return new CheckResult(Severity.Valid, string.Format(L_XMatches0_1, gift.CardHeader, string.Empty), CheckIdentifier.Encounter);
return GetValid(string.Format(L_XMatches0_1, gift.CardHeader, string.Empty));
}
}

View file

@ -153,10 +153,10 @@ public sealed class LearnGroup1 : ILearnGroup
private static void GetEncounterMoves(IEncounterTemplate enc, Span<ushort> moves)
{
if (enc.Version is GameVersion.YW or GameVersion.RBY)
LearnSource1YW.Instance.GetEncounterMoves(enc, enc.LevelMin, moves);
else
LearnSource1RB.Instance.GetEncounterMoves(enc, enc.LevelMin, moves);
ILearnSource ls = enc.Version is GameVersion.YW or GameVersion.RBY
? LearnSource1YW.Instance
: LearnSource1RB.Instance;
ls.SetEncounterMoves(enc.Species, 0, enc.LevelMin, moves);
}
private static void Check(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, EvoCriteria evo, int stage, LearnOption option = LearnOption.Current, MoveSourceType types = MoveSourceType.All)

View file

@ -13,7 +13,7 @@ public sealed class LearnSource1RB : ILearnSource<PersonalInfo1>
{
public static readonly LearnSource1RB Instance = new();
private static readonly PersonalTable1 Personal = PersonalTable.RB;
private static readonly Learnset[] Learnsets = Legal.LevelUpRB;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_rb.pkl"), Legal.MaxSpeciesID_1);
private const LearnEnvironment Game = RB;
private const int MaxSpecies = Legal.MaxSpeciesID_1;
@ -100,9 +100,8 @@ public sealed class LearnSource1RB : ILearnSource<PersonalInfo1>
}
}
public void GetEncounterMoves(ISpeciesForm enc, int level, Span<ushort> init)
public void SetEncounterMoves(ushort species, byte form, int level, Span<ushort> init)
{
var species = enc.Species;
if (!TryGetPersonal(species, 0, out var personal))
return;

View file

@ -13,7 +13,7 @@ public sealed class LearnSource1YW : ILearnSource<PersonalInfo1>
{
public static readonly LearnSource1YW Instance = new();
private static readonly PersonalTable1 Personal = PersonalTable.Y;
private static readonly Learnset[] Learnsets = Legal.LevelUpY;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_y.pkl"), Legal.MaxSpeciesID_1);
private const LearnEnvironment Game = YW;
private const int MaxSpecies = Legal.MaxSpeciesID_1;
@ -100,9 +100,8 @@ public sealed class LearnSource1YW : ILearnSource<PersonalInfo1>
}
}
public void GetEncounterMoves(ISpeciesForm enc, int level, Span<ushort> init)
public void SetEncounterMoves(ushort species, byte form, int level, Span<ushort> init)
{
var species = enc.Species;
if (!TryGetPersonal(species, 0, out var personal))
return;

View file

@ -13,8 +13,8 @@ public sealed class LearnSource2C : ILearnSource<PersonalInfo2>, IEggSource
{
public static readonly LearnSource2C Instance = new();
private static readonly PersonalTable2 Personal = PersonalTable.C;
private static readonly EggMoves2[] EggMoves = Legal.EggMovesC;
private static readonly Learnset[] Learnsets = Legal.LevelUpC;
private static readonly EggMoves2[] EggMoves = EggMoves2.GetArray(Util.GetBinaryResource("eggmove_c.pkl"), Legal.MaxSpeciesID_2);
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_c.pkl"), Legal.MaxSpeciesID_2);
private const int MaxSpecies = Legal.MaxSpeciesID_2;
private const LearnEnvironment Game = C;

View file

@ -13,8 +13,8 @@ public sealed class LearnSource2GS : ILearnSource<PersonalInfo2>, IEggSource
{
public static readonly LearnSource2GS Instance = new();
private static readonly PersonalTable2 Personal = PersonalTable.GS;
private static readonly EggMoves2[] EggMoves = Legal.EggMovesGS;
private static readonly Learnset[] Learnsets = Legal.LevelUpGS;
private static readonly EggMoves2[] EggMoves = EggMoves2.GetArray(Util.GetBinaryResource("eggmove_gs.pkl"), Legal.MaxSpeciesID_2);
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_gs.pkl"), Legal.MaxSpeciesID_2);
private const int MaxSpecies = Legal.MaxSpeciesID_2;
private const LearnEnvironment Game = GS;

View file

@ -2,19 +2,17 @@ using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LearnMethod;
using static PKHeX.Core.LearnEnvironment;
using static PKHeX.Core.LearnSource3;
namespace PKHeX.Core;
/// <summary>
/// Exposes information about how moves are learned in <see cref="E"/>.
/// </summary>
public sealed class LearnSource3E : ILearnSource<PersonalInfo3>, IEggSource
public sealed class LearnSource3E : LearnSource3, ILearnSource<PersonalInfo3>, IEggSource
{
public static readonly LearnSource3E Instance = new();
private static readonly PersonalTable3 Personal = PersonalTable.E;
private static readonly Learnset[] Learnsets = Legal.LevelUpE;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesRS; // same for all Gen3 games
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_e.pkl"), "em"));
private const int MaxSpecies = Legal.MaxSpeciesID_3;
private const LearnEnvironment Game = E;
private const int Generation = 3;

View file

@ -2,19 +2,17 @@ using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LearnMethod;
using static PKHeX.Core.LearnEnvironment;
using static PKHeX.Core.LearnSource3;
namespace PKHeX.Core;
/// <summary>
/// Exposes information about how moves are learned in <see cref="FR"/>.
/// </summary>
public sealed class LearnSource3FR : ILearnSource<PersonalInfo3>, IEggSource
public sealed class LearnSource3FR : LearnSource3, ILearnSource<PersonalInfo3>, IEggSource
{
public static readonly LearnSource3FR Instance = new();
private static readonly PersonalTable3 Personal = PersonalTable.FR;
private static readonly Learnset[] Learnsets = Legal.LevelUpFR;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesRS; // same for all Gen3 games
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_fr.pkl"), "fr"));
private const int MaxSpecies = Legal.MaxSpeciesID_3;
private const LearnEnvironment Game = FR;
private const int Generation = 3;

View file

@ -2,19 +2,17 @@ using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LearnMethod;
using static PKHeX.Core.LearnEnvironment;
using static PKHeX.Core.LearnSource3;
namespace PKHeX.Core;
/// <summary>
/// Exposes information about how moves are learned in <see cref="LG"/>.
/// </summary>
public sealed class LearnSource3LG : ILearnSource<PersonalInfo3>, IEggSource
public sealed class LearnSource3LG : LearnSource3, ILearnSource<PersonalInfo3>, IEggSource
{
public static readonly LearnSource3LG Instance = new();
private static readonly PersonalTable3 Personal = PersonalTable.LG;
private static readonly Learnset[] Learnsets = Legal.LevelUpLG;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesRS; // same for all Gen3 games
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_lg.pkl"), "lg"));
private const int MaxSpecies = Legal.MaxSpeciesID_3;
private const LearnEnvironment Game = LG;
private const int Generation = 3;

View file

@ -2,19 +2,17 @@ using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LearnMethod;
using static PKHeX.Core.LearnEnvironment;
using static PKHeX.Core.LearnSource3;
namespace PKHeX.Core;
/// <summary>
/// Exposes information about how moves are learned in <see cref="RS"/>.
/// </summary>
public sealed class LearnSource3RS : ILearnSource<PersonalInfo3>, IEggSource
public sealed class LearnSource3RS : LearnSource3, ILearnSource<PersonalInfo3>, IEggSource
{
public static readonly LearnSource3RS Instance = new();
private static readonly PersonalTable3 Personal = PersonalTable.RS;
private static readonly Learnset[] Learnsets = Legal.LevelUpRS;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesRS; // same for all Gen3 games
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_rs.pkl"), "rs"));
private const int MaxSpecies = Legal.MaxSpeciesID_3;
private const LearnEnvironment Game = RS;
private const int Generation = 3;

View file

@ -2,19 +2,17 @@ using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LearnMethod;
using static PKHeX.Core.LearnEnvironment;
using static PKHeX.Core.LearnSource4;
namespace PKHeX.Core;
/// <summary>
/// Exposes information about how moves are learned in <see cref="DP"/>.
/// </summary>
public sealed class LearnSource4DP : ILearnSource<PersonalInfo4>, IEggSource
public sealed class LearnSource4DP : LearnSource4, ILearnSource<PersonalInfo4>, IEggSource
{
public static readonly LearnSource4DP Instance = new();
private static readonly PersonalTable4 Personal = PersonalTable.DP;
private static readonly Learnset[] Learnsets = Legal.LevelUpDP;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesDPPt;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_dp.pkl"), "dp"));
private const int MaxSpecies = Legal.MaxSpeciesID_4;
private const LearnEnvironment Game = DP;
private const int Generation = 4;

View file

@ -13,8 +13,8 @@ public sealed class LearnSource4HGSS : ILearnSource<PersonalInfo4>, IEggSource
{
public static readonly LearnSource4HGSS Instance = new();
private static readonly PersonalTable4 Personal = PersonalTable.HGSS;
private static readonly Learnset[] Learnsets = Legal.LevelUpHGSS;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesHGSS;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_hgss.pkl"), "hs"));
private static readonly EggMoves6[] EggMoves = EggMoves6.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_hgss.pkl"), "hs"));
private const int MaxSpecies = Legal.MaxSpeciesID_4;
private const LearnEnvironment Game = HGSS;
private const int Generation = 4;

View file

@ -2,19 +2,17 @@ using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LearnMethod;
using static PKHeX.Core.LearnEnvironment;
using static PKHeX.Core.LearnSource4;
namespace PKHeX.Core;
/// <summary>
/// Exposes information about how moves are learned in <see cref="Pt"/>.
/// </summary>
public sealed class LearnSource4Pt : ILearnSource<PersonalInfo4>, IEggSource
public sealed class LearnSource4Pt : LearnSource4, ILearnSource<PersonalInfo4>, IEggSource
{
public static readonly LearnSource4Pt Instance = new();
private static readonly PersonalTable4 Personal = PersonalTable.Pt;
private static readonly Learnset[] Learnsets = Legal.LevelUpPt;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesDPPt;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_pt.pkl"), "pt"));
private const int MaxSpecies = Legal.MaxSpeciesID_4;
private const LearnEnvironment Game = Pt;
private const int Generation = 4;

View file

@ -2,19 +2,17 @@ using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LearnMethod;
using static PKHeX.Core.LearnEnvironment;
using static PKHeX.Core.LearnSource5;
namespace PKHeX.Core;
/// <summary>
/// Exposes information about how moves are learned in <see cref="B2W2"/>.
/// </summary>
public sealed class LearnSource5B2W2 : ILearnSource<PersonalInfo5B2W2>, IEggSource
public sealed class LearnSource5B2W2 : LearnSource5, ILearnSource<PersonalInfo5B2W2>, IEggSource
{
public static readonly LearnSource5B2W2 Instance = new();
private static readonly PersonalTable5B2W2 Personal = PersonalTable.B2W2;
private static readonly Learnset[] Learnsets = Legal.LevelUpB2W2;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesBW; // same
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_b2w2.pkl"), "52"));
private const int MaxSpecies = Legal.MaxSpeciesID_5;
private const LearnEnvironment Game = B2W2;

View file

@ -2,19 +2,17 @@ using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LearnMethod;
using static PKHeX.Core.LearnEnvironment;
using static PKHeX.Core.LearnSource5;
namespace PKHeX.Core;
/// <summary>
/// Exposes information about how moves are learned in <see cref="BW"/>.
/// </summary>
public sealed class LearnSource5BW : ILearnSource<PersonalInfo5BW>, IEggSource
public sealed class LearnSource5BW : LearnSource5, ILearnSource<PersonalInfo5BW>, IEggSource
{
public static readonly LearnSource5BW Instance = new();
private static readonly PersonalTable5BW Personal = PersonalTable.BW;
private static readonly Learnset[] Learnsets = Legal.LevelUpBW;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesBW;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_bw.pkl"), "51"));
private const int MaxSpecies = Legal.MaxSpeciesID_5;
private const LearnEnvironment Game = BW;

View file

@ -12,8 +12,8 @@ public sealed class LearnSource6AO : ILearnSource<PersonalInfo6AO>, IEggSource
{
public static readonly LearnSource6AO Instance = new();
private static readonly PersonalTable6AO Personal = PersonalTable.AO;
private static readonly Learnset[] Learnsets = Legal.LevelUpAO;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesAO;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_ao.pkl"), "ao"));
private static readonly EggMoves6[] EggMoves = EggMoves6.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_ao.pkl"), "ao"));
private const int MaxSpecies = Legal.MaxSpeciesID_6;
private const LearnEnvironment Game = ORAS;

View file

@ -12,8 +12,8 @@ public sealed class LearnSource6XY : ILearnSource<PersonalInfo6XY>, IEggSource
{
public static readonly LearnSource6XY Instance = new();
private static readonly PersonalTable6XY Personal = PersonalTable.XY;
private static readonly Learnset[] Learnsets = Legal.LevelUpXY;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesXY;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_xy.pkl"), "xy"));
private static readonly EggMoves6[] EggMoves = EggMoves6.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_xy.pkl"), "xy"));
private const int MaxSpecies = Legal.MaxSpeciesID_6;
private const LearnEnvironment Game = XY;

View file

@ -12,7 +12,7 @@ public sealed class LearnSource7GG : ILearnSource<PersonalInfo7GG>
{
public static readonly LearnSource7GG Instance = new();
private static readonly PersonalTable7GG Personal = PersonalTable.GG;
private static readonly Learnset[] Learnsets = Legal.LevelUpGG;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_gg.pkl"), "gg"));
private const int MaxSpecies = Legal.MaxSpeciesID_7b;
private const LearnEnvironment Game = GG;
private const int ReminderBonus = 100; // Move reminder allows re-learning ALL level up moves regardless of level.

View file

@ -13,8 +13,8 @@ public sealed class LearnSource7SM : ILearnSource<PersonalInfo7>, IEggSource
{
public static readonly LearnSource7SM Instance = new();
private static readonly PersonalTable7 Personal = PersonalTable.SM;
private static readonly Learnset[] Learnsets = Legal.LevelUpSM;
private static readonly EggMoves7[] EggMoves = Legal.EggMovesSM;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
private static readonly EggMoves7[] EggMoves = EggMoves7.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
private const int MaxSpecies = Legal.MaxSpeciesID_7;
private const LearnEnvironment Game = SM;
private const int ReminderBonus = 100; // Move reminder allows re-learning ALL level up moves regardless of level.
@ -34,15 +34,15 @@ public sealed class LearnSource7SM : ILearnSource<PersonalInfo7>, IEggSource
{
if (species > MaxSpecies)
return false;
var moves = MoveEgg.GetFormEggMoves(species, form, EggMoves).AsSpan();
return moves.IndexOf(move) != -1;
var moves = EggMoves.GetFormEggMoves(species, form);
return moves.Contains(move);
}
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form)
{
if (species > MaxSpecies)
return ReadOnlySpan<ushort>.Empty;
return MoveEgg.GetFormEggMoves(species, form, EggMoves);
return EggMoves.GetFormEggMoves(species, form);
}
public MoveLearnInfo GetCanLearn(PKM pk, PersonalInfo7 pi, EvoCriteria evo, ushort move, MoveSourceType types = MoveSourceType.All, LearnOption option = LearnOption.Current)

View file

@ -13,8 +13,8 @@ public sealed class LearnSource7USUM : ILearnSource<PersonalInfo7>, IEggSource
{
public static readonly LearnSource7USUM Instance = new();
private static readonly PersonalTable7 Personal = PersonalTable.USUM;
private static readonly Learnset[] Learnsets = Legal.LevelUpUSUM;
private static readonly EggMoves7[] EggMoves = Legal.EggMovesUSUM;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_uu.pkl"), "uu"));
private static readonly EggMoves7[] EggMoves = EggMoves7.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_uu.pkl"), "uu"));
private const int MaxSpecies = Legal.MaxSpeciesID_7_USUM;
private const LearnEnvironment Game = USUM;
private const int ReminderBonus = 100; // Move reminder allows re-learning ALL level up moves regardless of level.
@ -34,15 +34,15 @@ public sealed class LearnSource7USUM : ILearnSource<PersonalInfo7>, IEggSource
{
if (species > MaxSpecies)
return false;
var moves = MoveEgg.GetFormEggMoves(species, form, EggMoves).AsSpan();
return moves.IndexOf(move) != -1;
var moves = EggMoves.GetFormEggMoves(species, form);
return moves.Contains(move);
}
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form)
{
if (species > MaxSpecies)
return ReadOnlySpan<ushort>.Empty;
return MoveEgg.GetFormEggMoves(species, form, EggMoves);
return EggMoves.GetFormEggMoves(species, form);
}
public MoveLearnInfo GetCanLearn(PKM pk, PersonalInfo7 pi, EvoCriteria evo, ushort move, MoveSourceType types = MoveSourceType.All, LearnOption option = LearnOption.Current)

View file

@ -12,8 +12,8 @@ public sealed class LearnSource8BDSP : ILearnSource<PersonalInfo8BDSP>, IEggSour
{
public static readonly LearnSource8BDSP Instance = new();
private static readonly PersonalTable8BDSP Personal = PersonalTable.BDSP;
private static readonly Learnset[] Learnsets = Legal.LevelUpBDSP;
private static readonly EggMoves6[] EggMoves = Legal.EggMovesBDSP;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_bdsp.pkl"), "bs"));
private static readonly EggMoves6[] EggMoves = EggMoves6.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_bdsp.pkl"), "bs"));
private const int MaxSpecies = Legal.MaxSpeciesID_8b;
private const LearnEnvironment Game = BDSP;

View file

@ -12,12 +12,19 @@ public sealed class LearnSource8LA : ILearnSource<PersonalInfo8LA>
{
public static readonly LearnSource8LA Instance = new();
private static readonly PersonalTable8LA Personal = PersonalTable.LA;
private static readonly Learnset[] Learnsets = Legal.LevelUpLA;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_la.pkl"), "la"));
private static readonly Learnset[] Mastery = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("mastery_la.pkl"), "la"));
private const int MaxSpecies = Legal.MaxSpeciesID_8a;
private const LearnEnvironment Game = PLA;
public Learnset GetLearnset(ushort species, byte form) => Learnsets[Personal.GetFormIndex(species, form)];
public static (Learnset Learn, Learnset Mastery) GetLearnsetAndMastery(ushort species, byte form)
{
var index = Personal.GetFormIndex(species, form);
return (Learnsets[index], Mastery[index]);
}
public bool TryGetPersonal(ushort species, byte form, [NotNullWhen(true)] out PersonalInfo8LA? pi)
{
pi = null;

View file

@ -12,8 +12,8 @@ public sealed class LearnSource8SWSH : ILearnSource<PersonalInfo8SWSH>, IEggSour
{
public static readonly LearnSource8SWSH Instance = new();
private static readonly PersonalTable8SWSH Personal = PersonalTable.SWSH;
private static readonly Learnset[] Learnsets = Legal.LevelUpSWSH;
private static readonly EggMoves7[] EggMoves = Legal.EggMovesSWSH;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_swsh.pkl"), "ss"));
private static readonly EggMoves7[] EggMoves = EggMoves7.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_swsh.pkl"), "ss"));
private const int MaxSpecies = Legal.MaxSpeciesID_8_R2;
private const LearnEnvironment Game = SWSH;
@ -32,15 +32,15 @@ public sealed class LearnSource8SWSH : ILearnSource<PersonalInfo8SWSH>, IEggSour
{
if (species > MaxSpecies)
return false;
var moves = MoveEgg.GetFormEggMoves(species, form, EggMoves).AsSpan();
return moves.IndexOf(move) != -1;
var moves = EggMoves.GetFormEggMoves(species, form);
return moves.Contains(move);
}
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form)
{
if (species > MaxSpecies)
return ReadOnlySpan<ushort>.Empty;
return MoveEgg.GetFormEggMoves(species, form, EggMoves);
return EggMoves.GetFormEggMoves(species, form);
}
public MoveLearnInfo GetCanLearn(PKM pk, PersonalInfo8SWSH pi, EvoCriteria evo, ushort move, MoveSourceType types = MoveSourceType.All, LearnOption option = LearnOption.Current)

View file

@ -12,9 +12,9 @@ public sealed class LearnSource9SV : ILearnSource<PersonalInfo9SV>, IEggSource,
{
public static readonly LearnSource9SV Instance = new();
private static readonly PersonalTable9SV Personal = PersonalTable.SV;
private static readonly Learnset[] Learnsets = Legal.LevelUpSV;
private static readonly ushort[][] EggMoves = Legal.EggMovesSV;
private static readonly ushort[][] Reminder = Legal.ReminderSV;
private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_sv.pkl"), "sv"));
private static readonly ushort[][] EggMoves = EggMoves9.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_sv.pkl"), "sv"));
private static readonly ushort[][] Reminder = EggMoves9.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("reminder_sv.pkl"), "sv"));
private const int MaxSpecies = Legal.MaxSpeciesID_9;
private const LearnEnvironment Game = SV;

View file

@ -23,6 +23,15 @@ public interface ILearnSource
/// <param name="species">Entity species</param>
/// <param name="form">Entity form</param>
public Learnset GetLearnset(ushort species, byte form);
public void SetEncounterMoves(ushort species, byte form, int level, Span<ushort> init)
{
var start = (init.LastIndexOfAnyExcept<ushort>(0) + 1) & 3;
var learn = GetLearnset(species, form);
learn.SetEncounterMoves(level, init, start);
}
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form) => ReadOnlySpan<ushort>.Empty;
}
/// <summary>

View file

@ -2,8 +2,10 @@ using System;
namespace PKHeX.Core;
internal static class LearnSource3
public abstract class LearnSource3
{
private protected readonly EggMoves6[] EggMoves = EggMoves6.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_rs.pkl"), "rs")); // same for all Gen3 games
internal static ReadOnlySpan<ushort> TM_3 => new ushort[]
{
264, 337, 352, 347, 046, 092, 258, 339, 331, 237,

View file

@ -2,8 +2,10 @@ using System;
namespace PKHeX.Core;
internal static class LearnSource4
public abstract class LearnSource4
{
private protected static readonly EggMoves6[] EggMoves = EggMoves6.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_dppt.pkl"), "dp"));
/// <summary>
/// Gets the preferred list of HM moves to disallow on transfer from <see cref="PK4"/> to <see cref="PK5"/>.
/// </summary>

View file

@ -2,8 +2,10 @@ using System;
namespace PKHeX.Core;
internal static class LearnSource5
public abstract class LearnSource5
{
private protected readonly EggMoves6[] EggMoves = EggMoves6.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_bw.pkl"), "bw"));
internal static ReadOnlySpan<ushort> TMHM_BW => new ushort[]
{
468, 337, 473, 347, 046, 092, 258, 339, 474, 237,

View file

@ -31,7 +31,9 @@ internal static class LearnVerifierEgg
}
else
{
ReadOnlySpan<ushort> initial = GameData.GetLearnset(enc.Version, enc.Species, enc.Form).GetBaseEggMoves(enc.LevelMin);
var ls = GameData.GetLearnSource(enc.Version);
var learn = ls.GetLearnset(enc.Species, enc.Form);
var initial = learn.GetBaseEggMoves(enc.LevelMin);
VerifyMovesInitial(result, current, initial);
}
}

View file

@ -125,6 +125,30 @@ public sealed class EggMoves7 : EggMoves
}
}
internal static class EggMovesExtensions
{
public static ReadOnlySpan<ushort> GetFormEggMoves(this EggMoves7[] table, ushort species, byte form)
{
if (species >= table.Length)
return ReadOnlySpan<ushort>.Empty;
var entry = table[species];
if (form == 0 || species >= entry.FormTableIndex)
return entry.Moves;
// Sanity check form in the event it is out of range.
var baseIndex = entry.FormTableIndex;
var index = baseIndex + form - 1;
if ((uint)index >= table.Length)
return ReadOnlySpan<ushort>.Empty;
entry = table[index];
if (entry.FormTableIndex != baseIndex)
return ReadOnlySpan<ushort>.Empty;
return entry.Moves;
}
}
/// <summary>
/// Raw Egg Move storage
/// </summary>

View file

@ -1,84 +0,0 @@
using System;
using static PKHeX.Core.Legal;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
/// <summary>
/// Logic for obtaining a list of moves.
/// </summary>
internal static class MoveList
{
internal static void GetCurrentMoves(PKM pk, ushort species, byte form, GameVersion gameSource, int lvl, Span<ushort> moves) => _ = gameSource switch
{
GSC or GS => Get(moves, LevelUpGS, species, lvl, pk.Format),
C => Get(moves, LevelUpC, species, lvl, pk.Format),
R or S or RS => Get(moves, LevelUpRS, species, lvl),
E => Get(moves, LevelUpE, species, lvl),
FR or LG or FRLG => Get(moves, LevelUpFR, species, lvl),
D or P or DP => Get(moves, LevelUpDP, species, lvl),
Pt => Get(moves, LevelUpPt, species, lvl),
HG or SS or HGSS => Get(moves, LevelUpHGSS, species, lvl),
B or W or BW => Get(moves, LevelUpBW, species, lvl),
B2 or W2 or B2W2 => Get(moves, LevelUpB2W2, species, lvl),
X or Y or XY => Get(moves, LevelUpXY, species, lvl),
AS or OR or ORAS => Get(moves, LevelUpAO, species, lvl),
SN or MN or SM => Get(moves, LevelUpSM, PersonalTable.SM, species, form, lvl),
US or UM or USUM => Get(moves, LevelUpUSUM, PersonalTable.USUM, species, form, lvl),
SW or SH or SWSH => Get(moves, LevelUpSWSH, PersonalTable.SWSH, species, form, lvl),
BD or SP or BDSP => Get(moves, LevelUpBDSP, PersonalTable.BDSP, species, form, lvl),
PLA => Get(moves, LevelUpLA, PersonalTable.LA, species, form, lvl),
SL or VL or SV => Get(moves, LevelUpSV, PersonalTable.SV, species, form, lvl),
_ => moves,
};
private static Span<ushort> Get(Span<ushort> moves, Learnset[] source, ushort species, int lvl)
{
if (species >= source.Length)
return moves;
source[species].SetLevelUpMoves(1, lvl, moves);
return moves;
}
private static Span<ushort> Get<T>(Span<ushort> moves, Learnset[] source, T pt, ushort species, byte form, int lvl) where T : IPersonalTable
{
if (!pt.IsPresentInGame(species, form))
return moves;
int index = pt.GetFormIndex(species, form);
source[index].SetLevelUpMoves(1, lvl, moves);
return moves;
}
private static Span<ushort> Get(Span<ushort> moves, Learnset[] source, ushort species, int lvl, int format)
{
if (species > MaxSpeciesID_2)
return moves;
source[species].SetLevelUpMoves(1, lvl, moves);
if (format != 1)
return moves;
// If checking back-transfer specimen (GSC->RBY), remove moves that must be deleted prior to transfer
// Remove all values greater than MaxMoveID_1, and shift the remaining indexes down.
for (int i = 0; i < moves.Length; i++)
{
if (moves[i] <= MaxMoveID_1)
continue;
// Shift remaining indexes down, set last index to 0
for (int j = i; j < moves.Length - 1; j++)
moves[j] = moves[j + 1];
moves[^1] = 0;
}
return moves;
}
}

View file

@ -12,7 +12,8 @@ public static class MoveListSuggest
{
if (pk is { IsEgg: true, Format: <= 5 }) // pre relearn
{
MoveList.GetCurrentMoves(pk, pk.Species, 0, (GameVersion)pk.Version, pk.CurrentLevel, moves);
var source = GameData.GetLearnSource(enc.Version);
source.SetEncounterMoves(enc.Species, 0, enc.LevelMin, moves);
return;
}
@ -26,14 +27,16 @@ public static class MoveListSuggest
if (enc.Generation <= 2)
{
var lvl = pk.Format >= 7 ? pk.Met_Level : pk.CurrentLevel;
var ver = enc.Version;
MoveLevelUp.GetEncounterMoves(moves, enc.Species, 0, lvl, ver);
var source = GameData.GetLearnSource(enc.Version);
source.SetEncounterMoves(enc.Species, 0, lvl, moves);
return;
}
if (pk.Species == enc.Species)
{
MoveLevelUp.GetEncounterMoves(moves, pk.Species, pk.Form, pk.CurrentLevel, (GameVersion)pk.Version);
var game = (GameVersion)pk.Version; // account for SW/SH foreign mutated versions
var source = GameData.GetLearnSource(game);
source.SetEncounterMoves(pk.Species, pk.Form, pk.CurrentLevel, moves);
return;
}

View file

@ -73,10 +73,8 @@ public static class MoveBreed
// Well, that didn't work; probably because one or more moves aren't valid.
// Let's remove all present base moves, and get a fresh set of base moves.
var learn = GameData.GetLearnsets(version);
var table = GameData.GetPersonal(version);
var index = table.GetFormIndex(species, form);
var learnset = learn[index];
var learn = GameData.GetLearnSource(version);
var learnset = learn.GetLearnset(species, form);
var eggLevel = EggStateLegality.GetEggLevel(generation);
var baseMoves = learnset.GetBaseEggMoves(eggLevel);

View file

@ -23,22 +23,21 @@ public static class MoveBreed2
if (count == -1)
count = moves.Length;
(Learnset[] learn, PersonalTable2 table) = version == GameVersion.C
? (Legal.LevelUpC, PersonalTable.C)
: (Legal.LevelUpGS, PersonalTable.GS);
if (!table.IsSpeciesInGame(species))
ILearnSource<PersonalInfo2> ls = version == GameVersion.C ? LearnSource2C.Instance : LearnSource2GS.Instance;
if (!ls.TryGetPersonal(species, 0, out var pi))
return false;
var learnset = learn[species];
var pi = table[species];
var egg = (version == GameVersion.C ? Legal.EggMovesC : Legal.EggMovesGS)[species].Moves;
var learnset = ls.GetLearnset(species, 0);
var eggMoves = version == GameVersion.C
? LearnSource2C.Instance.GetEggMoves(species, 0)
: LearnSource2GS.Instance.GetEggMoves(species, 0);
var actual = MemoryMarshal.Cast<byte, EggSource2>(origins);
Span<byte> possible = stackalloc byte[count];
var value = new BreedInfo<EggSource2>(actual, possible, learnset, moves, level);
bool inherit = Breeding.GetCanInheritMoves(species);
MarkMovesForOrigin(value, egg, count, inherit, pi, version);
MarkMovesForOrigin(value, eggMoves, count, inherit, pi, version);
var valid = RecurseMovesForOrigin(value, count - 1);
if (!valid)
CleanResult(actual, possible);

View file

@ -24,20 +24,19 @@ public static class MoveBreed3
if (count == -1)
count = moves.Length;
(Learnset[] learn, PersonalTable3 table) = version switch
ILearnSource<PersonalInfo3> ls = version switch
{
R or S => (Legal.LevelUpRS, PersonalTable.RS),
E => (Legal.LevelUpE, PersonalTable.E ),
FR => (Legal.LevelUpFR, PersonalTable.FR),
LG => (Legal.LevelUpLG, PersonalTable.LG),
_ => throw new ArgumentException($"Invalid version: {version}"),
R => LearnSource3RS.Instance,
S => LearnSource3RS.Instance,
E => LearnSource3E.Instance,
FR => LearnSource3FR.Instance,
LG => LearnSource3LG.Instance,
_ => throw new ArgumentOutOfRangeException(nameof(version), version, $"Invalid version: {version}"),
};
if (!table.IsSpeciesInGame(species))
if (!ls.TryGetPersonal(species, 0, out var pi))
return false;
var learnset = learn[species];
var pi = table[species];
var learnset = ls.GetLearnset(species, 0);
var actual = MemoryMarshal.Cast<byte, EggSource34>(origins);
Span<byte> possible = stackalloc byte[count];
var value = new BreedInfo<EggSource34>(actual, possible, learnset, moves, level);
@ -51,7 +50,7 @@ public static class MoveBreed3
}
else
{
var egg = Legal.EggMovesRS[species].Moves;
var egg = ls.GetEggMoves(species, 0);
bool inherit = Breeding.GetCanInheritMoves(species);
MarkMovesForOrigin(value, egg, count, inherit, pi);
valid = RecurseMovesForOrigin(value, count - 1);

View file

@ -24,8 +24,8 @@ public static class MoveBreed4
if (count == -1)
count = moves.Length;
var learn = GameData.GetLearnsets(version);
var learnset = learn[species];
var learn = GameData.GetLearnSource(version);
var learnset = learn.GetLearnset(species, 0);
var table = version switch
{
HG or SS => PersonalTable.HGSS,
@ -48,8 +48,10 @@ public static class MoveBreed4
else
{
bool inherit = Breeding.GetCanInheritMoves(species);
var egg = (version is HG or SS ? Legal.EggMovesHGSS : Legal.EggMovesDPPt)[species].Moves;
MarkMovesForOrigin(value, egg, count, inherit, pi, version);
var eggMoves = version is HG or SS
? LearnSource4HGSS.Instance.GetEggMoves(species, 0)
: LearnSource4DP.Instance.GetEggMoves(species, 0);
MarkMovesForOrigin(value, eggMoves, count, inherit, pi, version);
valid = RecurseMovesForOrigin(value, count - 1);
}

View file

@ -22,8 +22,8 @@ public static class MoveBreed5
if (count == -1)
count = moves.Length;
var learn = GameData.GetLearnsets(version);
var learnset = learn[species];
var learn = GameData.GetLearnSource(version);
var learnset = learn.GetLearnset(species, 0);
IPersonalInfoTM pi = version switch
{
GameVersion.B or GameVersion.W => PersonalTable.BW[species],
@ -44,7 +44,7 @@ public static class MoveBreed5
else
{
bool inherit = Breeding.GetCanInheritMoves(species);
var egg = Legal.EggMovesBW[species].Moves;
var egg = LearnSource5BW.Instance.GetEggMoves(species, 0);
MarkMovesForOrigin(value, egg, count, inherit, pi);
valid = RecurseMovesForOrigin(value, count - 1);
}

View file

@ -22,10 +22,8 @@ public static class MoveBreed6
if (count == -1)
count = moves.Length;
var learn = GameData.GetLearnsets(version);
var table = GameData.GetPersonal(version);
var index = table.GetFormIndex(species, form);
var learnset = learn[index];
var learn = GameData.GetLearnSource(version);
var learnset = learn.GetLearnset(species, form);
var actual = MemoryMarshal.Cast<byte, EggSource6>(origins);
Span<byte> possible = stackalloc byte[count];
@ -40,7 +38,7 @@ public static class MoveBreed6
}
else
{
var egg = MoveEgg.GetEggMoves(generation, species, form, version);
var egg = learn.GetEggMoves(species, form);
bool inherit = Breeding.GetCanInheritMoves(species);
MarkMovesForOrigin(value, egg, count, inherit);
valid = RecurseMovesForOrigin(value, count - 1);

View file

@ -5,62 +5,53 @@ namespace PKHeX.Core;
public static class GameData
{
public static Learnset[] GetLearnsets(GameVersion game) => Learnsets(game);
public static IPersonalTable GetPersonal(GameVersion game) => Personal(game);
public static Learnset GetLearnset(GameVersion game, ushort species, byte form)
public static ILearnSource GetLearnSource(GameVersion game) => game switch
{
var pt = Personal(game);
var index = pt.GetFormIndex(species, form);
var sets = Learnsets(game);
return sets[index];
}
RD or GN or BU or RB => LearnSource1RB.Instance,
YW or RBY => LearnSource1YW.Instance,
GD or SI or GS => LearnSource2GS.Instance,
C or GSC => LearnSource2C.Instance,
private static Learnset[] Learnsets(GameVersion game) => game switch
{
RD or GN or BU or RB => Legal.LevelUpRB,
YW or RBY => Legal.LevelUpY,
GD or SI or GS => Legal.LevelUpGS,
C or GSC => Legal.LevelUpC,
R or S or RS or RSE => LearnSource3RS.Instance,
E or COLO or XD or FRLG or CXD => LearnSource3E.Instance,
FR => LearnSource3FR.Instance,
LG => LearnSource3LG.Instance,
R or S or RS or RSE => Legal.LevelUpRS,
E or COLO or XD or FRLG or CXD => Legal.LevelUpE,
FR => Legal.LevelUpFR,
LG => Legal.LevelUpLG,
D or P or DP => LearnSource4DP.Instance,
Pt or DPPt => LearnSource4Pt.Instance,
HG or SS or HGSS => LearnSource4HGSS.Instance,
D or P or DP => Legal.LevelUpDP,
Pt or DPPt => Legal.LevelUpPt,
HG or SS or HGSS => Legal.LevelUpHGSS,
B or W or BW => LearnSource5BW.Instance,
B2 or W2 or B2W2 => LearnSource5B2W2.Instance,
B or W or BW => Legal.LevelUpBW,
B2 or W2 or B2W2 => Legal.LevelUpB2W2,
X or Y or XY => LearnSource6XY.Instance,
AS or OR or ORAS => LearnSource6AO.Instance,
X or Y or XY => Legal.LevelUpXY,
AS or OR or ORAS => Legal.LevelUpAO,
SN or MN or SM => LearnSource7SM.Instance,
US or UM or USUM => LearnSource7USUM.Instance,
GO or GP or GE or GG => LearnSource7GG.Instance,
SN or MN or SM => Legal.LevelUpSM,
US or UM or USUM => Legal.LevelUpUSUM,
GO or GP or GE or GG => Legal.LevelUpGG,
SW or SH or SWSH => LearnSource8SWSH.Instance,
BD or SP or BDSP => LearnSource8BDSP.Instance,
PLA => LearnSource8LA.Instance,
SW or SH or SWSH => Legal.LevelUpSWSH,
BD or SP or BDSP => Legal.LevelUpBDSP,
PLA => Legal.LevelUpLA,
SL or VL or SV => LearnSource9SV.Instance,
SL or VL or SV => Legal.LevelUpSV,
Gen1 => LearnSource1YW.Instance,
Gen2 => LearnSource2C.Instance,
Gen3 => LearnSource3E.Instance,
Gen4 => LearnSource4HGSS.Instance,
Gen5 => LearnSource5B2W2.Instance,
Gen6 => LearnSource6AO.Instance,
Gen7 => LearnSource7USUM.Instance,
Gen7b => LearnSource7GG.Instance,
Gen8 => LearnSource8BDSP.Instance,
Gen9 => LearnSource9SV.Instance,
Gen1 => Legal.LevelUpY,
Gen2 => Legal.LevelUpC,
Gen3 => Legal.LevelUpE,
Gen4 => Legal.LevelUpHGSS,
Gen5 => Legal.LevelUpB2W2,
Gen6 => Legal.LevelUpAO,
Gen7 => Legal.LevelUpSM,
Gen7b => Legal.LevelUpGG,
Gen8 => Legal.LevelUpSWSH,
Gen9 => Legal.LevelUpSV,
Stadium => Legal.LevelUpY,
Stadium2 => Legal.LevelUpGS,
Stadium => LearnSource1YW.Instance,
Stadium2 => LearnSource2GS.Instance,
_ => throw new ArgumentOutOfRangeException(nameof(game), $"{game} is not a valid entry in the expression."),
};

View file

@ -1,81 +0,0 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.Legal;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
/// <summary>
/// Shared logic for interacting with <see cref="EggMoves"/> data sources.
/// </summary>
public static class MoveEgg
{
/// <summary>
/// Gets the <see cref="EggMoves"/> data for the <see cref="version"/> and <see cref="species"/>.
/// </summary>
public static ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form, GameVersion version, int generation)
{
if (!Breeding.CanGameGenerateEggs(version))
return Array.Empty<ushort>();
return GetEggMoves(generation, species, form, version);
}
/// <summary>
/// Gets the <see cref="EggMoves"/> data for the <see cref="version"/> and <see cref="species"/>.
/// </summary>
/// <remarks>Only use this if you're sure the game can generate eggs.</remarks>
public static ReadOnlySpan<ushort> GetEggMoves(int generation, ushort species, byte form, GameVersion version) => generation switch
{
1 or 2 => GetMovesSafe(version == C ? EggMovesC : EggMovesGS, species),
3 => GetMovesSafe(EggMovesRS, species),
4 when version is D or P or Pt => GetMovesSafe(EggMovesDPPt, species),
4 when version is HG or SS => GetMovesSafe(EggMovesHGSS, species),
5 => GetMovesSafe(EggMovesBW, species),
6 when version is X or Y => GetMovesSafe(EggMovesXY, species),
6 when version is OR or AS => GetMovesSafe(EggMovesAO, species),
7 when version is SN or MN => GetFormEggMoves(species, form, EggMovesSM),
7 when version is US or UM => GetFormEggMoves(species, form, EggMovesUSUM),
8 when version is SW or SH => GetFormEggMoves(species, form, EggMovesSWSH),
8 when version is BD or SP => GetMovesSafe(EggMovesBDSP, species),
9 when version is SL or VL => GetFormEggMoves(EggMovesSV, PersonalTable.SV.GetFormIndex(species, form)),
_ => Array.Empty<ushort>(),
};
private static ushort[] GetMovesSafe<T>(IReadOnlyList<T> moves, ushort species) where T : EggMoves
{
if (species >= moves.Count)
return Array.Empty<ushort>();
return moves[species].Moves;
}
public static ushort[] GetFormEggMoves(IReadOnlyList<ushort[]> moves, int index)
{
if ((ushort)index >= moves.Count)
return Array.Empty<ushort>();
return moves[index];
}
public static ushort[] GetFormEggMoves(ushort species, byte form, IReadOnlyList<EggMoves7> table)
{
if (species >= table.Count)
return Array.Empty<ushort>();
var entry = table[species];
if (form == 0 || species >= entry.FormTableIndex)
return entry.Moves;
// Sanity check form in the event it is out of range.
var baseIndex = entry.FormTableIndex;
var index = baseIndex + form - 1;
if ((uint)index >= table.Count)
return Array.Empty<ushort>();
entry = table[index];
if (entry.FormTableIndex != baseIndex)
return Array.Empty<ushort>();
return entry.Moves;
}
}

View file

@ -1,43 +0,0 @@
using System;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
public static class MoveLevelUp
{
public static void GetEncounterMoves(Span<ushort> moves, PKM pk, int level, GameVersion version)
{
if (version <= 0)
version = (GameVersion)pk.Version;
GetEncounterMoves(moves, pk.Species, pk.Form, level, version);
}
private static void GetEncounterMoves1(Span<ushort> result, ushort species, int level, GameVersion version)
{
var learn = GameData.GetLearnsets(version);
var table = version is YW or RBY ? PersonalTable.Y : PersonalTable.RB;
var index = table.GetFormIndex(species, 0);
// The initial moves are seeded from Personal rather than learn.
table[index].GetMoves(result);
int start = Math.Max(0, result.IndexOf<ushort>(0));
learn[index].SetEncounterMoves(level, result, start);
}
public static void GetEncounterMoves(Span<ushort> result, ushort species, byte form, int level, GameVersion version)
{
if (RBY.Contains(version))
{
GetEncounterMoves1(result, species, level, version);
}
else
{
var learn = GameData.GetLearnsets(version);
var table = GameData.GetPersonal(version);
var index = table.GetFormIndex(species, form);
learn[index].SetEncounterMoves(level, result);
}
}
}

View file

@ -47,4 +47,41 @@ public static class ItemRestrictions
EntityContext.Gen8b => ReleasedHeldItems_8b,
_ => Array.Empty<bool>(), // lgp/e, pla, etc
};
private static readonly bool[] ReleasedHeldItems_2 = GetPermitList(MaxItemID_2, HeldItems_GSC);
private static readonly bool[] ReleasedHeldItems_3 = GetPermitList(MaxItemID_3, HeldItems_RS, ItemStorage3RS.Unreleased); // Safari Ball
private static readonly bool[] ReleasedHeldItems_4 = GetPermitList(MaxItemID_4_HGSS, HeldItems_HGSS, ItemStorage4.Unreleased);
private static readonly bool[] ReleasedHeldItems_5 = GetPermitList(MaxItemID_5_B2W2, HeldItems_BW, ItemStorage5.Unreleased);
private static readonly bool[] ReleasedHeldItems_6 = GetPermitList(MaxItemID_6_AO, HeldItem_AO, ItemStorage6XY.Unreleased);
private static readonly bool[] ReleasedHeldItems_7 = GetPermitList(MaxItemID_7_USUM, HeldItems_USUM, ItemStorage7SM.Unreleased);
private static readonly bool[] ReleasedHeldItems_8 = GetPermitList(MaxItemID_8, HeldItems_SWSH, ItemStorage8SWSH.Unreleased);
private static readonly bool[] ReleasedHeldItems_8b = GetPermitList(MaxItemID_8b, HeldItems_BS, ItemStorage8BDSP.Unreleased);
private static readonly bool[] ReleasedHeldItems_9 = GetPermitList(MaxItemID_9, HeldItems_SV, ItemStorage9SV.Unreleased);
/// <summary>
/// Gets a permit list with the permitted indexes, then un-flags the indexes that are not permitted.
/// </summary>
/// <param name="max">Maximum index expected to allow</param>
/// <param name="allowed">Allowed indexes</param>
private static bool[] GetPermitList(int max, ReadOnlySpan<ushort> allowed)
{
var result = new bool[max + 1];
foreach (var index in allowed)
result[index] = true;
return result;
}
/// <summary>
/// Gets a permit list with the permitted indexes, then un-flags the indexes that are not permitted.
/// </summary>
/// <param name="max">Maximum index expected to allow</param>
/// <param name="allowed">Allowed indexes (may have some disallowed)</param>
/// <param name="disallow">Disallowed indexes</param>
private static bool[] GetPermitList(int max, ReadOnlySpan<ushort> allowed, ReadOnlySpan<ushort> disallow)
{
var result = GetPermitList(max, allowed);
foreach (var index in disallow)
result[index] = false;
return result;
}
}

View file

@ -91,7 +91,8 @@ public static class MemoryPermissions
// Relearns can be wiped via Battle Version. Check for eggs too.
if (IsSpecialEncounterMoveEggDeleted(pk, enc))
{
var em = MoveEgg.GetEggMoves(enc.Generation, enc.Species, enc.Form, enc.Version);
var learn = GameData.GetLearnSource(enc.Version);
var em = learn.GetEggMoves(enc.Species, enc.Form);
if (em.Contains(move))
return true;
}

View file

@ -16,7 +16,7 @@ public interface IMasteryInitialMoveShop8
{
Span<ushort> moves = stackalloc ushort[4];
var level = pa8.Met_Level;
var (learn, mastery) = GetLevelUpInfo();
var (learn, mastery) = LearnSource8LA.GetLearnsetAndMastery(pk.Species, pk.Form);
LoadInitialMoveset(pa8, moves, learn, level);
pa8.SetEncounterMasteryFlags(moves, mastery, level);
}

View file

@ -120,7 +120,7 @@ public static class FormInfo
return false;
}
public static bool IsFormChangeEgg(ushort species) => FormChangeEgg.IndexOf(species) != -1;
public static bool IsFormChangeEgg(ushort species) => FormChangeEgg.Contains(species);
private static ReadOnlySpan<ushort> FormChangeEgg => new ushort[]
{
@ -237,6 +237,28 @@ public static class FormInfo
return hs;
}
/// <summary>
/// Species has a Totem form in Gen7 (S/M &amp; US/UM) that can be captured and owned.
/// </summary>
/// <param name="species"></param>
/// <returns>True if the species exists as a Totem.</returns>
/// <remarks>Excludes <see cref="Wishiwashi"/> because it cannot be captured.</remarks>
public static bool HasTotemForm(ushort species) => species switch
{
(ushort)Raticate => true,
(ushort)Marowak => true,
(ushort)Gumshoos => true,
(ushort)Vikavolt => true,
(ushort)Ribombee => true,
(ushort)Araquanid => true,
(ushort)Lurantis => true,
(ushort)Salazzle => true,
(ushort)Mimikyu => true,
(ushort)Kommoo => true,
(ushort)Togedemaru => true,
_ => false,
};
/// <summary>
/// Checks if the <see cref="form"/> for the <see cref="species"/> is a Totem form.
/// </summary>
@ -255,11 +277,11 @@ public static class FormInfo
{
if (form == 0)
return false;
if (!Legal.Totem_USUM.Contains(species))
if (!HasTotemForm(species))
return false;
if (species == (int)Mimikyu)
return form is 2 or 3;
if (Legal.Totem_Alolan.Contains(species))
if (species is (int)Raticate or (int)Marowak)
return form == 2;
return form == 1;
}
@ -323,7 +345,7 @@ public static class FormInfo
if (format <= 3 && species != (int)Unown)
return false;
if (HasFormValuesNotIndicatedByPersonal.Contains(species))
if (HasFormValuesNotIndicatedByPersonal(species))
return true;
int count = pi.FormCount;
@ -333,10 +355,11 @@ public static class FormInfo
/// <summary>
/// <seealso cref="IsValidOutOfBoundsForm"/>
/// </summary>
private static readonly HashSet<ushort> HasFormValuesNotIndicatedByPersonal = new()
private static bool HasFormValuesNotIndicatedByPersonal(ushort species) => species switch
{
(int)Unown,
(int)Mothim, // (Burmy form is not cleared on evolution)
(int)Scatterbug, (int)Spewpa, // Vivillon pre-evos
(int)Unown => true,
(int)Mothim => true, // (Burmy form is not cleared on evolution)
(int)Scatterbug or (int)Spewpa => true, // Vivillon pre-evos
_ => false,
};
}

View file

@ -0,0 +1,89 @@
using System.Collections.Generic;
using static PKHeX.Core.Species;
namespace PKHeX.Core;
/// <summary>
/// Categorizes <see cref="Species"/> into groups, or specific edge-case qualities.
/// </summary>
public static class SpeciesCategory
{
/// <summary>
/// Checks if a Species is classified as a Mythical Distributions (disallowed species for competitive rulesets)
/// </summary>
public static bool IsMythical(ushort species) => Mythicals.Contains(species);
/// <summary>
/// Checks if a Species is classified as "Legend" by the game code.
/// </summary>
/// <remarks>Previous games may have included Mythicals in this species list, but that list should be considered separately.</remarks>
public static bool IsLegendary(ushort species) => Legends.Contains(species);
/// <summary>
/// Checks if a Species is classified as "SubLegend" by the game code.
/// </summary>
public static bool IsSubLegendary(ushort species) => SubLegends.Contains(species);
private static readonly HashSet<ushort> Mythicals = new()
{
(int)Mew,
(int)Celebi,
(int)Jirachi, (int)Deoxys,
(int)Phione, (int)Manaphy, (int)Darkrai, (int)Shaymin, (int)Arceus,
(int)Victini, (int)Keldeo, (int)Meloetta, (int)Genesect,
(int)Diancie, (int)Hoopa, (int)Volcanion,
(int)Magearna, (int)Marshadow,
(int)Zeraora, (int)Meltan, (int)Melmetal,
(int)Zarude,
};
private static readonly HashSet<ushort> Legends = new()
{
(int)Mewtwo,
(int)Lugia, (int)HoOh,
(int)Kyogre, (int)Groudon, (int)Rayquaza,
(int)Dialga, (int)Palkia, (int)Giratina,
(int)Reshiram, (int)Zekrom, (int)Kyurem,
(int)Xerneas, (int)Yveltal, (int)Zygarde,
(int)Cosmog, (int)Cosmoem, (int)Solgaleo, (int)Lunala, (int)Necrozma,
(int)Zacian, (int)Zamazenta, (int)Eternatus, (int)Calyrex,
(int)Koraidon, (int)Miraidon,
};
private static readonly HashSet<ushort> SubLegends = new()
{
(int)Articuno, (int)Zapdos, (int)Moltres,
(int)Raikou, (int)Entei, (int)Suicune,
(int)Regirock, (int)Regice, (int)Registeel, (int)Latias, (int)Latios,
(int)Uxie, (int)Mesprit, (int)Azelf, (int)Heatran, (int)Regigigas, (int)Cresselia,
(int)Cobalion, (int)Terrakion, (int)Virizion, (int)Tornadus, (int)Thundurus, (int)Landorus,
(int)TypeNull, (int)Silvally, (int)TapuKoko, (int)TapuLele, (int)TapuBulu, (int)TapuFini,
(int)Kubfu, (int)Urshifu, (int)Regieleki, (int)Regidrago, (int)Glastrier, (int)Spectrier, (int)Enamorus,
(int)WoChien, (int)ChienPao, (int)TingLu, (int)ChiYu,
};
/// <summary>
/// Checks if the <see cref="species"/> is an Ultra Beast Pokémon.
/// </summary>
public static bool IsUltraBeast(ushort species) => species is (>= (int)Nihilego and <= (int)Guzzlord) or (>= (int)Poipole and <= (int)Blacephalon);
/// <summary>
/// Checks if the <see cref="species"/> is a Paradox Pokémon.
/// </summary>
public static bool IsParadox(ushort species) => species is (>= (int)GreatTusk and <= (int)IronThorns) or (int)RoaringMoon or (int)IronValiant;
public static bool IsFixedGenderFromDual(ushort currentSpecies) => currentSpecies switch
{
(int)Shedinja => true, // Genderless
(int)Wormadam => true, //(F)
(int)Mothim => true, // (M)
(int)Gallade => true, // (M)
(int)Froslass => true, // (F)
(int)Meowstic => true, // (M/F) form specific
(int)Oinkologne => true, // (M/F) form specific
_ => false,
};
}

View file

@ -1,136 +0,0 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.Species;
namespace PKHeX.Core;
public static partial class Legal
{
/// <summary>
/// Generation 3 &amp; 4 Battle Frontier Species banlist. When referencing this in context to generation 4, be sure to disallow <see cref="Pichu"/> with Form 1 (Spiky).
/// </summary>
public static readonly HashSet<ushort> BattleFrontierBanlist = new()
{
(int)Mewtwo, (int)Mew,
(int)Lugia, (int)HoOh, (int)Celebi,
(int)Kyogre, (int)Groudon, (int)Rayquaza, (int)Jirachi, (int)Deoxys,
(int)Dialga, (int)Palkia, (int)Giratina, (int)Phione, (int)Manaphy, (int)Darkrai, (int)Shaymin, (int)Arceus,
(int)Victini, (int)Reshiram, (int)Zekrom, (int)Kyurem, (int)Keldeo, (int)Meloetta, (int)Genesect,
(int)Xerneas, (int)Yveltal, (int)Zygarde, (int)Diancie, (int)Hoopa, (int)Volcanion,
(int)Cosmog, (int)Cosmoem, (int)Solgaleo, (int)Lunala, (int)Necrozma, (int)Magearna, (int)Marshadow, (int)Zeraora,
(int)Meltan, (int)Melmetal,
(int)Koraidon, (int)Miraidon,
};
/// <summary>
/// Species that are from Mythical Distributions (disallowed species for competitive rulesets)
/// </summary>
public static readonly HashSet<ushort> Mythicals = new()
{
(int)Mew,
(int)Celebi,
(int)Jirachi, (int)Deoxys,
(int)Phione, (int)Manaphy, (int)Darkrai, (int)Shaymin, (int)Arceus,
(int)Victini, (int)Keldeo, (int)Meloetta, (int)Genesect,
(int)Diancie, (int)Hoopa, (int)Volcanion,
(int)Magearna, (int)Marshadow,
(int)Zeraora, (int)Meltan, (int)Melmetal,
(int)Zarude,
};
/// <summary>
/// Species classified as "Legend" by the game code.
/// </summary>
/// <remarks>Previous games may have included Mythicals in this species list, but that list should be considered separately.</remarks>
public static readonly HashSet<ushort> Legends = new()
{
(int)Mewtwo,
(int)Lugia, (int)HoOh,
(int)Kyogre, (int)Groudon, (int)Rayquaza,
(int)Dialga, (int)Palkia, (int)Giratina,
(int)Reshiram, (int)Zekrom, (int)Kyurem,
(int)Xerneas, (int)Yveltal, (int)Zygarde,
(int)Cosmog, (int)Cosmoem, (int)Solgaleo, (int)Lunala, (int)Necrozma,
(int)Zacian, (int)Zamazenta, (int)Eternatus, (int)Calyrex,
(int)Koraidon, (int)Miraidon,
};
/// <summary>
/// Species classified as "SubLegend" by the game code.
/// </summary>
public static readonly HashSet<ushort> SubLegends = new()
{
(int)Articuno, (int)Zapdos, (int)Moltres,
(int)Raikou, (int)Entei, (int)Suicune,
(int)Regirock, (int)Regice, (int)Registeel, (int)Latias, (int)Latios,
(int)Uxie, (int)Mesprit, (int)Azelf, (int)Heatran, (int)Regigigas, (int)Cresselia,
(int)Cobalion, (int)Terrakion, (int)Virizion, (int)Tornadus, (int)Thundurus, (int)Landorus,
(int)TypeNull, (int)Silvally, (int)TapuKoko, (int)TapuLele, (int)TapuBulu, (int)TapuFini,
(int)Kubfu, (int)Urshifu, (int)Regieleki, (int)Regidrago, (int)Glastrier, (int)Spectrier, (int)Enamorus,
(int)WoChien, (int)ChienPao, (int)TingLu, (int)ChiYu,
};
/// <summary>
/// Checks if the <see cref="species"/> is an Ultra Beast Pokémon.
/// </summary>
public static bool IsUltraBeast(ushort species) => species is
(int)Nihilego or (int)Buzzwole or (int)Pheromosa or (int)Xurkitree or (int)Celesteela or (int)Kartana or (int)Guzzlord or
(int)Poipole or (int)Naganadel or (int)Stakataka or (int)Blacephalon;
/// <summary>
/// Checks if the <see cref="species"/> is a Paradox Pokémon.
/// </summary>
public static bool IsParadox(ushort species) => species is (>= (int)GreatTusk and <= (int)IronThorns) or (int)RoaringMoon or (int)IronValiant;
/// <summary>
/// Species that evolve from a Bi-Gendered species into a Single-Gender.
/// </summary>
public static readonly HashSet<ushort> FixedGenderFromBiGender = new()
{
(int)Nincada,
(int)Shedinja, // (G)
(int)Burmy,
(int)Wormadam, //(F)
(int)Mothim, // (M)
(int)Ralts,
(int)Gallade, // (M)
(int)Snorunt,
(int)Froslass, // (F)
(int)Espurr,
(int)Meowstic, // (M/F) form specific
(int)Lechonk,
(int)Oinkologne, // (M/F) form specific
};
/// <summary>
/// Gets a permit list with the permitted indexes, then un-flags the indexes that are not permitted.
/// </summary>
/// <param name="max">Maximum index expected to allow</param>
/// <param name="allowed">Allowed indexes</param>
private static bool[] GetPermitList(int max, ReadOnlySpan<ushort> allowed)
{
var result = new bool[max + 1];
foreach (var index in allowed)
result[index] = true;
return result;
}
/// <summary>
/// Gets a permit list with the permitted indexes, then un-flags the indexes that are not permitted.
/// </summary>
/// <param name="max">Maximum index expected to allow</param>
/// <param name="allowed">Allowed indexes (may have some disallowed)</param>
/// <param name="disallow">Disallowed indexes</param>
private static bool[] GetPermitList(int max, ReadOnlySpan<ushort> allowed, ReadOnlySpan<ushort> disallow)
{
var result = GetPermitList(max, allowed);
foreach (var index in disallow)
result[index] = false;
return result;
}
}

View file

@ -1,9 +0,0 @@
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_1 = 151;
internal const int MaxMoveID_1 = 165;
internal const int MaxItemID_1 = 255;
internal const int MaxAbilityID_1 = 0;
}

View file

@ -1,13 +0,0 @@
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_2 = 251;
internal const int MaxMoveID_2 = 251;
internal const int MaxItemID_2 = 255;
internal const int MaxAbilityID_2 = 0;
internal static readonly ushort[] HeldItems_GSC = ItemStorage2.GetAllHeld();
internal static readonly bool[] ReleasedHeldItems_2 = GetPermitList(MaxItemID_2, HeldItems_GSC);
}

View file

@ -1,67 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesIndex_3 = 412;
internal const int MaxSpeciesID_3 = 386;
internal const int MaxMoveID_3 = 354;
internal const int MaxItemID_3 = 374;
internal const int MaxItemID_3_COLO = 547;
internal const int MaxItemID_3_XD = 593;
internal const int MaxAbilityID_3 = 77;
internal const int MaxBallID_3 = 0xC;
internal const int MaxGameID_3 = 15; // CXD
#region RS
internal static readonly ushort[] HeldItems_RS = ItemStorage3RS.GetAllHeld();
#endregion
internal static readonly bool[] ReleasedHeldItems_3 = GetPermitList(MaxItemID_3, HeldItems_RS, ItemStorage3RS.Unreleased); // Safari Ball
// 064 is an unused location for Meteor Falls
// 084 is Inside of a truck, no possible pokemon can be hatched there
// 071 is Mirage island, cannot be obtained as the player is technically still on Route 130's map.
// 075 is an unused location for Fiery Path
// 077 is an unused location for Jagged Pass
internal static readonly HashSet<byte> ValidMet_RS = new()
{
000, 001, 002, 003, 004, 005, 006, 007, 008, 009,
010, 011, 012, 013, 014, 015, 016, 017, 018, 019,
020, 021, 022, 023, 024, 025, 026, 027, 028, 029,
030, 031, 032, 033, 034, 035, 036, 037, 038, 039,
040, 041, 042, 043, 044, 045, 046, 047, 048, 049,
050, 051, 052, 053, 054, 055, 056, 057, 058, 059,
060, 061, 062, 063, 065, 066, 067, 068, 069,
070, 072, 073, 074, 076, 078, 079,
080, 081, 082, 083, 085, 086, 087,
};
// 155 - 158 Sevii Isle 6-9 Unused
// 171 - 173 Sevii Isle 22-24 Unused
internal static readonly HashSet<byte> ValidMet_FRLG = new()
{
087, 088, 089,
090, 091, 092, 093, 094, 095, 096, 097, 098, 099,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
170, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
190, 191, 192, 193, 194, 195, 196,
};
internal static readonly HashSet<byte> ValidMet_E = new(ValidMet_RS)
{
196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212,
};
}

View file

@ -1,93 +0,0 @@
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_4 = 493;
internal const int MaxMoveID_4 = 467;
internal const int MaxItemID_4_DP = 464;
internal const int MaxItemID_4_Pt = 467;
internal const int MaxItemID_4_HGSS = 536;
internal const int MaxAbilityID_4 = 123;
internal const int MaxBallID_4 = 0x18;
internal const int MaxGameID_4 = 15; // CXD
internal static readonly ushort[] HeldItems_DP = ItemStorage4DP.GetAllHeld();
internal static readonly ushort[] HeldItems_Pt = ItemStorage4Pt.GetAllHeld(); // Griseous Orb Added
internal static readonly ushort[] HeldItems_HGSS = HeldItems_Pt;
internal static readonly bool[] ReleasedHeldItems_4 = GetPermitList(MaxItemID_4_HGSS, HeldItems_HGSS, ItemStorage4.Unreleased);
internal static readonly HashSet<ushort> ValidMet_DP = new()
{
// 063: Flower Paradise unreleased DP event
// 079: Newmoon Island unreleased DP event
// 085: Seabreak Path unreleased DP event
// 086: Hall of Origin unreleased event
001, 002, 003, 004, 005, 006, 007, 008, 009,
010, 011, 012, 013, 014, 015, 016, 017, 018, 019,
020, 021, 022, 023, 024, 025, 026, 027, 028, 029,
030, 031, 032, 033, 034, 035, 036, 037, 038, 039,
040, 041, 042, 043, 044, 045, 046, 047, 048, 049,
050, 051, 052, 053, 054, 055, 056, 057, 058, 059,
060, 061, 062, 064, 065, 066, 067, 068, 069,
070, 071, 072, 073, 074, 075, 076, 077, 078,
080, 081, 082, 083, 084, 087, 088, 089,
090, 091, 092, 093, 094, 095, 096, 097, 098, 099,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111,
};
internal static readonly HashSet<ushort> ValidMet_Pt = new(ValidMet_DP)
{
// 086: Hall of Origin unreleased event
63, 79, 85,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125,
};
internal static readonly HashSet<ushort> ValidMet_HGSS = new()
{
080,
112, 113, 114, 115, 116,
126, 127, 128, 129,
130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
170, 172, 173, 174, 175, 176, 177, 178, 179, //171: Route 23 no longer exists
180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
230, 231, 232, 234, //233: Pokéwalker
};
internal static readonly HashSet<ushort> ValidMet_4 = new(ValidMet_Pt.Concat(ValidMet_HGSS));
internal static readonly HashSet<ushort> GiftEggLocation4 = new()
{
2009, 2010, 2011, 2013, 2014,
};
internal static int GetTransfer45MetLocation(PKM pk)
{
// Everything except for crown beasts and Celebi get the default transfer location.
// Crown beasts and Celebi are 100% identifiable by the species ID and fateful encounter, originating from Gen4.
if (!pk.Gen4 || !pk.FatefulEncounter)
return Locations.Transfer4; // Pokétransfer
return pk.Species switch
{
// Crown Beast
(int)Species.Raikou or (int)Species.Entei or (int)Species.Suicune => Locations.Transfer4_CrownUnused,
// Celebi
(int)Species.Celebi => Locations.Transfer4_CelebiUnused,
// Default
_ => Locations.Transfer4,
};
}
}

View file

@ -1,55 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_5 = 649;
internal const int MaxMoveID_5 = 559;
internal const int MaxItemID_5_BW = 632;
internal const int MaxItemID_5_B2W2 = 638;
internal const int MaxAbilityID_5 = 164;
internal const int MaxBallID_5 = 0x19;
internal const int MaxGameID_5 = 23; // B2
internal static readonly ushort[] HeldItems_BW = ItemStorage5.GetAllHeld();
internal static readonly bool[] ReleasedHeldItems_5 = GetPermitList(MaxItemID_5_B2W2, HeldItems_BW, ItemStorage5.Unreleased);
internal static readonly HashSet<ushort> ValidMet_BW = new()
{
004, 005, 006, 007, 008, 009,
010, 011, 012, 013, 014, 015, 016, 017, 018, 019,
020, 021, 022, 023, 024, 025, 026, 027, 028, 029,
030, 031, 032, 033, 034, 035, 036, 037, 038, 039,
040, 041, 042, 043, 044, 045, 046, 047, 048, 049,
050, 051, 052, 053, 054, 055, 056, 057, 058, 059,
060, 061, 062, 063, 064, 065, 066, 067, 068, 069,
070, 071, 072, 073, 074, 075, 076, 077, 078, 079,
080, 081, 082, 083, 084, 085, 086, 087, 088, 089,
090, 091, 092, 093, 094, 095, 096, 097, 098, 099,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116,
};
internal static readonly HashSet<ushort> ValidMet_B2W2 = new()
{
004, 005, 006, 007, 008, 009,
010, 011, 012, 013, 014, 015, 016, 017, 018, 019,
020, 021, 022, 024, 025, 026, 027, 028, 029, // 023 Route 10
030, 031, 032, 033, 034, 035, 036, 037, 038, 039,
041, 042, 043, 044, 045, 046, 047, 048, 049, // 040->134 Victory Road
050, 051, 052, 053, 054, 055, 056, 057, 058, // 059 Challenger's cave
060, 061, 062, 063, 064, 065, 066, 067, 068, 069,
070, 071, 072, 074, 075, 076, 077, 078, 079, // 073 Trial Chamber
080, 081, 082, 083, 084, 085, 086, 087, 088, 089,
090, 091, 092, 093, 094, 095, 096, 097, 098, 099,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116,
117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131, 132, 133, 134, 135, 136, 137, 139, // 138 ---
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153,
};
}

View file

@ -1,64 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_6 = 721;
internal const int MaxMoveID_6_XY = 617;
internal const int MaxMoveID_6_AO = 621;
internal const int MaxItemID_6_XY = 717;
internal const int MaxItemID_6_AO = 775;
internal const int MaxAbilityID_6_XY = 188;
internal const int MaxAbilityID_6_AO = 191;
internal const int MaxBallID_6 = 0x19;
internal const int MaxGameID_6 = 27; // OR
internal static readonly ushort[] HeldItem_AO = ItemStorage6XY.GetAllHeld();
internal static readonly HashSet<ushort> ValidMet_XY = new()
{
006, 008,
010, 012, 014, 016, 018,
020, 022, 024, 026, 028,
030, 032, 034, 036, 038,
040, 042, 044, 046, 048,
050, 052, 054, 056, 058,
060, 062, 064, 066, 068,
070, 072, 074, 076, 078,
082, 084, 086, 088,
090, 092, 094, 096, 098,
100, 102, 104, 106, 108,
110, 112, 114, 116, 118,
120, 122, 124, 126, 128,
130, 132, 134, 136, 138,
140, 142, 144, 146, 148,
150, 152, 154, 156, 158,
160, 162, 164, 166, 168,
};
internal static readonly HashSet<ushort> ValidMet_AO = new()
{
170, 172, 174, 176, 178,
180, 182, 184, 186, 188,
190, 192, 194, 196, 198,
200, 202, 204, 206, 208,
210, 212, 214, 216, 218,
220, 222, 224, 226, 228,
230, 232, 234, 236, 238,
240, 242, 244, 246, 248,
250, 252, 254, 256, 258,
260, 262, 264, 266, 268,
270, 272, 274, 276, 278,
280, 282, 284, 286, 288,
290, 292, 294, 296, 298,
300, 302, 304, 306, 308,
310, 312, 314, 316, 318,
320, 322, 324, 326, 328,
330, 332, 334, 336, 338,
340, 342, 344, 346,
350, 352, 354,
};
internal static readonly bool[] ReleasedHeldItems_6 = GetPermitList(MaxItemID_6_AO, HeldItem_AO, ItemStorage6XY.Unreleased);
}

View file

@ -1,174 +0,0 @@
using System.Collections.Generic;
using static PKHeX.Core.Species;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_7 = 802;
internal const int MaxMoveID_7 = 719;
internal const int MaxItemID_7 = 920;
internal const int MaxAbilityID_7 = 232;
internal const int MaxBallID_7 = 0x1A; // 26
internal const int MaxGameID_7 = 41; // Crystal (VC?)
internal const int MaxSpeciesID_7_USUM = 807;
internal const int MaxMoveID_7_USUM = 728;
internal const int MaxItemID_7_USUM = 959;
internal const int MaxAbilityID_7_USUM = 233;
private static Dictionary<ushort, ushort> GetDictionary(IReadOnlyList<ushort> key, IReadOnlyList<ushort> held)
{
var result = new Dictionary<ushort, ushort>(held.Count);
for (int i = 0; i < key.Count; i++)
result.Add(key[i], held[i]);
return result;
}
internal static readonly ushort[] HeldItems_SM = ItemStorage7SM.GetAllHeld();
internal static readonly ushort[] HeldItems_USUM = ItemStorage7USUM.GetAllHeld();
internal static readonly HashSet<ushort> AlolanOriginForms = new()
{
(int)Rattata,
(int)Raticate,
(int)Sandshrew,
(int)Sandslash,
(int)Vulpix,
(int)Ninetales,
(int)Diglett,
(int)Dugtrio,
(int)Meowth,
(int)Persian,
(int)Geodude,
(int)Graveler,
(int)Golem,
(int)Grimer,
(int)Muk,
};
internal static readonly HashSet<ushort> AlolanVariantEvolutions12 = new()
{
(int)Raichu,
(int)Exeggutor,
(int)Marowak,
};
public static readonly HashSet<ushort> PastGenAlolanNatives = new()
{
010, 011, 012, 019, 020, 021, 022, 025, 026, 027, 028, 035, 036, 037, 038, 039, 040, 041, 042, 046, 047, 050,
051, 052, 053, 054, 055, 056, 057, 058, 059, 060, 061, 062, 063, 064, 065, 066, 067, 068, 072, 073, 074, 075,
076, 079, 080, 081, 082, 088, 089, 090, 091, 092, 093, 094, 096, 097, 102, 103, 104, 105, 113, 115, 118, 119,
120, 121, 123, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 142, 143, 147, 148, 149, 165,
166, 167, 168, 169, 170, 171, 172, 173, 174, 185, 186, 196, 197, 198, 199, 200, 209, 210, 212, 215, 222, 225,
227, 233, 235, 239, 240, 241, 242, 278, 279, 283, 284, 296, 297, 299, 302, 318, 319, 320, 321, 324, 327, 328,
329, 330, 339, 340, 349, 350, 351, 359, 361, 362, 369, 370, 371, 372, 373, 374, 375, 376, 408, 409, 410, 411,
422, 423, 425, 426, 429, 430, 438, 440, 443, 444, 445, 446, 447, 448, 456, 457, 461, 462, 466, 467, 470, 471,
474, 476, 478, 506, 507, 508, 524, 525, 526, 546, 547, 548, 549, 551, 552, 553, 564, 565, 566, 567, 568, 569,
582, 583, 584, 587, 594, 627, 628, 629, 630, 661, 662, 663, 674, 675, 700, 703, 704, 705, 706, 707, 708, 709,
718,
// Regular
023, 086, 108, 122, 138, 140, 163, 177, 179, 190, 204,
206, 214, 223, 228, 238, 246, 303, 309, 341, 343,
345, 347, 352, 353, 357, 366, 427, 439, 458, 550,
559, 570, 572, 592, 605, 619, 621, 622, 624, 636,
667, 669, 676, 686, 690, 692, 696, 698, 701, 702,
714,
// Wormhole
333, 334, // Altaria
193, 469, // Yanmega
561, // Sigilyph
580, 581, // Swanna
276, 277, // Swellow
451, 452, // Drapion
531, // Audino
694, 695, // Heliolisk
273, 274, 275, // Nuzleaf
325, 326, // Gumpig
459, 460, // Abomasnow
307, 308, // Medicham
449, 450, // Hippowdon
557, 558, // Crustle
218, 219, // Magcargo
688, 689, // Barbaracle
270, 271, 272, // Lombre
618, // Stunfisk
418, 419, // Floatzel
194, 195, // Quagsire
100, 101, // Voltorb & Electrode
};
internal static readonly HashSet<ushort> Totem_Alolan = new()
{
(int)Raticate, // (Normal, Alolan, Totem)
(int)Marowak, // (Normal, Alolan, Totem)
(int)Mimikyu, // (Normal, Busted, Totem, Totem_Busted)
};
internal static readonly HashSet<ushort> Totem_NoTransfer = new()
{
(int)Marowak,
(int)Araquanid,
(int)Togedemaru,
(int)Ribombee,
};
internal static readonly HashSet<ushort> Totem_USUM = new()
{
(int)Raticate,
(int)Gumshoos,
//(int)Wishiwashi,
(int)Salazzle,
(int)Lurantis,
(int)Vikavolt,
(int)Mimikyu,
(int)Kommoo,
(int)Marowak,
(int)Araquanid,
(int)Togedemaru,
(int)Ribombee,
};
internal static readonly HashSet<ushort> ValidMet_SM = new()
{
006, 008,
010, 012, 014, 016, 018,
020, 022, 024, 026, 028,
030, 032, 034, 036, 038,
040, 042, 044, 046, 048,
050, 052, 054, 056, 058,
060, 062, 064, 068,
070, 072, 074, 076, 078,
082, 084, 086, 088,
090, 092, 094,
100, 102, 104, 106, 108,
110, 112, 114, 116, 118,
120, 122, 124, 126, 128,
130, 132, 134, 136, 138,
140, 142, 144, 146, 148,
150, 152, 154, 156, 158,
160, 162, 164, 166, 168,
170, 172, 174, 176, 178,
180, 182, 184, 186, 188,
190, 192,
Locations.Pelago7, // 30016
};
internal static readonly HashSet<ushort> ValidMet_USUM = new(ValidMet_SM)
{
// 194, 195, 196, 197, // Unobtainable new Locations
198,
200, 202, 204, 206, 208,
210, 212, 214, 216, 218,
220, 222, 224, 226, 228,
230, 232,
};
#region Unreleased Items
internal static readonly bool[] ReleasedHeldItems_7 = GetPermitList(MaxItemID_7_USUM, HeldItems_USUM, ItemStorage7SM.Unreleased);
#endregion
}

View file

@ -1,50 +0,0 @@
using System;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_7b = 809; // Melmetal
internal const int MaxMoveID_7b = 742; // Double Iron Bash
internal const int MaxItemID_7b = 1057; // Magmar Candy
internal const int MaxBallID_7b = (int)Ball.Beast;
internal const int MaxGameID_7b = (int)GameVersion.GE;
internal const int MaxAbilityID_7b = MaxAbilityID_7_USUM;
internal static readonly ushort[] HeldItems_GG = Array.Empty<ushort>();
public const byte AwakeningMax = 200;
#region Moves
public static bool IsAllowedMoveGG(ushort move) => AllowedMovesGG.BinarySearch(move) >= 0;
private static ReadOnlySpan<ushort> AllowedMovesGG => new ushort[]
{
000, 001, 002, 003, 004, 005, 006, 007, 008, 009,
010, 011, 012, 013, 014, 015, 016, 017, 018, 019,
020, 021, 022, 023, 024, 025, 026, 027, 028, 029,
030, 031, 032, 033, 034, 035, 036, 037, 038, 039,
040, 041, 042, 043, 044, 045, 046, 047, 048, 049,
050, 051, 052, 053, 054, 055, 056, 057, 058, 059,
060, 061, 062, 063, 064, 065, 066, 067, 068, 069,
070, 071, 072, 073, 074, 075, 076, 077, 078, 079,
080, 081, 082, 083, 084, 085, 086, 087, 088, 089,
090, 091, 092, 093, 094, 095, 096, 097, 098, 099,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164,
182, 188, 200, 224, 227, 231, 242, 243, 247, 252,
257, 261, 263, 269, 270, 276, 280, 281, 339, 347,
355, 364, 369, 389, 394, 398, 399, 403, 404, 405,
406, 417, 420, 430, 438, 446, 453, 483, 492, 499,
503, 504, 525, 529, 583, 585, 603, 605, 606, 607,
729, 730, 731, 733, 734, 735, 736, 737, 738, 739,
740, 742,
};
#endregion
}

View file

@ -1,100 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public static partial class Legal
{
// Current Binaries
internal const int MaxSpeciesID_8 = MaxSpeciesID_8_R2;
internal const int MaxMoveID_8 = MaxMoveID_8_R2;
internal const int MaxItemID_8 = MaxItemID_8_R2;
internal const int MaxAbilityID_8 = MaxAbilityID_8_R2;
// Orion (No DLC)
internal const int MaxSpeciesID_8_O0 = 890; // Eternatus
internal const int MaxMoveID_8_O0 = 796; // Steel Beam
internal const int MaxItemID_8_O0 = 1278; // Rotom Catalog, ignore all catalog parts
internal const int MaxAbilityID_8_O0 = 258; // Hunger Switch
// Rigel 1 (DLC 1: Isle of Armor)
internal const int MaxSpeciesID_8_R1 = 893; // Zarude
internal const int MaxMoveID_8_R1 = 818; // Surging Strikes
internal const int MaxItemID_8_R1 = 1589; // Mark Charm
internal const int MaxAbilityID_8_R1 = 260; // Unseen Fist
// Rigel 2 (DLC 2: Crown Tundra)
internal const int MaxSpeciesID_8_R2 = 898; // Calyrex
internal const int MaxMoveID_8_R2 = 826; // Eerie Spell
internal const int MaxItemID_8_R2 = 1607; // Reins of Unity
internal const int MaxAbilityID_8_R2 = 267; // As One (Glastrier)
internal const int MaxBallID_8 = 0x1A; // 26 Beast
internal const int MaxGameID_8 = 45; // Shield
internal static readonly ushort[] HeldItems_SWSH = ItemStorage8SWSH.GetAllHeld();
internal static readonly HashSet<ushort> GalarOriginForms = new()
{
(int)Species.Meowth,
(int)Species.Ponyta,
(int)Species.Rapidash,
(int)Species.Slowpoke,
(int)Species.Farfetchd,
(int)Species.MrMime,
(int)Species.Corsola,
(int)Species.Zigzagoon,
(int)Species.Linoone,
(int)Species.Yamask,
(int)Species.Darumaka,
(int)Species.Darmanitan,
(int)Species.Stunfisk,
};
internal static readonly HashSet<ushort> GalarVariantFormEvolutions = new()
{
(int)Species.MrMime,
(int)Species.Weezing,
};
internal static readonly HashSet<ushort> ValidMet_SWSH = new()
{
006, 008,
012, 014, 016, 018,
020, 022, 024, 028,
030, 032, 034, 036,
040, 044, 046, 048,
052, 054, 056, 058,
060, 064, 066, 068,
070, 072, 076, 078,
080, 084, 086, 088,
090, 092, 094, 096, 098,
102, 104, 106, 108,
110, 112, 114, 116, 118,
120, 122, 124, 126, 128,
130, 132, 134, 136, 138,
140, 142, 144, 146, 148,
150, 152, 154, 156, 158,
160, 164, 166, 168,
170, 172, 174, 176, 178,
180, 182, 184, 186, 188,
190, 192, 194, 196, 198,
200,
202, 204, 206, 208, 210,
212, 214, 216, 218, 220,
222, 224, 226, 228, 230,
232, 234, 236, 238, 240,
242, 244, 246,
};
#region Unreleased Items
private const int DMAX_START = 1279;
private const int DMAX_END = 1578;
private const int DMAX_LEGAL_END = 1290; // ★Sgr7194 (Eevee)
public static bool IsDynamaxCrystal(ushort item) => item is >= DMAX_START and <= DMAX_END;
public static bool IsDynamaxCrystalAvailable(ushort item) => item is >= DMAX_START and <= DMAX_LEGAL_END;
internal static readonly bool[] ReleasedHeldItems_8 = GetPermitList(MaxItemID_8, HeldItems_SWSH, ItemStorage8SWSH.Unreleased);
#endregion
}

View file

@ -1,15 +0,0 @@
using System;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_8a = (int)Species.Enamorus;
internal const int MaxMoveID_8a = (int)Move.TakeHeart;
internal const int MaxItemID_8a = 1828; // Legend Plate
internal const int MaxBallID_8a = (int)Ball.LAOrigin;
internal const int MaxGameID_8a = (int)GameVersion.SP;
internal const int MaxAbilityID_8a = MaxAbilityID_8_R2;
internal static readonly ushort[] HeldItems_LA = Array.Empty<ushort>();
}

View file

@ -1,75 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_8b = MaxSpeciesID_4; // Arceus-493
internal const int MaxMoveID_8b = MaxMoveID_8_R2;
internal const int MaxItemID_8b = 1822; // DS Sounds
internal const int MaxBallID_8b = (int)Ball.LAOrigin;
internal const int MaxGameID_8b = (int)GameVersion.SP;
internal const int MaxAbilityID_8b = MaxAbilityID_8_R2;
internal static readonly ushort[] HeldItems_BS = ItemStorage8BDSP.GetAll();
#region Unreleased Items
internal static readonly bool[] ReleasedHeldItems_8b = GetPermitList(MaxItemID_8b, HeldItems_BS, ItemStorage8BDSP.Unreleased);
#endregion
private const int MaxValidHatchLocation8b = 657;
public static bool IsValidEggHatchLocation8b(ushort location, GameVersion version)
{
if ((uint)location > MaxValidHatchLocation8b)
return false;
var loc16 = location;
if (LocationsNoHatchBDSP.Contains(loc16))
return false;
// Check if the location isn't an exclusive location that is only accessible in the other game.
var table = version == GameVersion.BD ? LocationsExclusiveSP : LocationsExclusiveBD;
return !table.Contains(loc16);
}
private static readonly HashSet<ushort> LocationsExclusiveBD = new()
{
216, // Spear Pillar
218, // Hall of Origin
498, // Ramanas Park (Johto Room)
503, // Ramanas Park (Rainbow Room)
650, // Ramanas Park (Johto Room)
655, // Ramanas Park (Rainbow Room)
};
private static readonly HashSet<ushort> LocationsExclusiveSP = new()
{
217, // Spear Pillar
497, // Ramanas Park (Kanto Room)
504, // Ramanas Park (Squall Room)
618, // Hall of Origin
649, // Ramanas Park (Kanto Room)
656, // Ramanas Park (Squall Room)
};
private static readonly HashSet<ushort> LocationsNoHatchBDSP = new()
{
094, 103, 107, // Hearthome City
154, 155, 158, // Sunyshore City
181, 182, 183, // Pokémon League
329, // Lake Acuity
337, 338, // Battle Park
339, 340, 341, 342, 343, 344, // Battle Tower
345, 353, 421, // Mystery Zone
474, // Resort Area
483, 484, // Mystery Zone
491, 492, 493, // Mystery Zone
495, // Ramanas Park
620, 621, 622, 623, // Grand Underground (Secret Base)
625, // Sea (sailing animation)
627, 628, 629, 630, 631, 632, // Grand Underground (Secret Base)
633, 634, 635, 636, 637, 638, // Grand Underground (Secret Base)
639, 640, 641, 642, 643, 644, // Grand Underground (Secret Base)
645, 646, 647, // Grand Underground (Secret Base)
};
}

View file

@ -1,50 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
public static partial class Legal
{
internal const int MaxSpeciesID_9 = (int)Species.IronLeaves;
internal const int MaxMoveID_9 = (int)Move.MagicalTorque;
internal const int MaxItemID_9 = 2400; // Yellow Dish
internal const int MaxAbilityID_9 = (int)Ability.MyceliumMight;
internal const int MaxBallID_9 = (int)Ball.LAOrigin;
internal const int MaxGameID_9 = (int)GameVersion.VL;
internal static readonly ushort[] HeldItems_SV = ItemStorage9SV.GetAllHeld();
internal static readonly HashSet<ushort> ValidMet_SV = new()
{
006,
010, 012, 014, 016, 018,
020, 022, 024, 026, 028,
030, 032, 034, 036, 038,
040, 044, 046, 048,
050, 052, 056, 058,
060, 062, 064,
067, 069, 070, 072, 076,
078, 080, 082, 084, 086,
088, 090, 092, 094, 096,
099, 101, 103, 105, 107,
109, 111, 113, 115, 117,
118, 124, 130,
131,
};
public static bool IsValidEggHatchLocation9(ushort location, GameVersion version)
{
if (version == GameVersion.SL && location == 131) // Uva Academy does not exist in Scarlet
return false;
if (version == GameVersion.VL && location == 130) // Naranja Academy does not exist in Violet
return false;
return ValidMet_SV.Contains(location);
}
#region Unreleased Items
internal static readonly bool[] ReleasedHeldItems_9 = GetPermitList(MaxItemID_9, HeldItems_SV, ItemStorage9SV.Unreleased);
#endregion
}

View file

@ -179,35 +179,6 @@ public sealed class FormVerifier : Verifier
if (FormInfo.IsBattleOnlyForm(species, form, format))
return GetInvalid(LFormBattle);
if (form == 0)
return VALID;
// everything below here is not Form 0, so it has a form.
if (format >= 7 && Info.Generation < 7)
{
if (species == 25 || Legal.AlolanOriginForms.Contains(species) || Legal.AlolanVariantEvolutions12.Contains(enc.Species))
return GetInvalid(LFormInvalidGame);
}
if (format >= 8 && Info.Generation < 8)
{
var orig = enc.Species;
if (Legal.GalarOriginForms.Contains(species) || Legal.GalarVariantFormEvolutions.Contains(orig))
{
if (species == (int)Meowth && enc.Form != 2)
{
// We're okay here. There's also Alolan Meowth...
}
else if (((Species)orig is MrMime or MimeJr) && pk.CurrentLevel > enc.LevelMin && Info.Generation >= 4)
{
// We're okay with a Mime Jr. that has evolved via level up.
}
else if (enc.Version != GameVersion.GO)
{
return GetInvalid(LFormInvalidGame);
}
}
}
return VALID;
}

View file

@ -57,9 +57,8 @@ public sealed class GenderVerifier : Verifier
return IsValidGenderMismatch(pk);
// check for mixed->fixed gender incompatibility by checking the gender of the original species
var original = data.EncounterMatch.Species;
if (Legal.FixedGenderFromBiGender.Contains(original))
return IsValidFixedGenderFromBiGender(pk, original);
if (SpeciesCategory.IsFixedGenderFromDual(pk.Species))
return IsValidFixedGenderFromBiGender(pk, data.EncounterMatch.Species);
return true;
}

View file

@ -82,10 +82,8 @@ public sealed class LegendsArceusVerifier : Verifier
private static int LoadBareMinimumMoveset(ISpeciesForm enc, EvolutionHistory h, PA8 pa, Span<ushort> moves)
{
// Get any encounter moves
var pt = PersonalTable.LA;
var index = pt.GetFormIndex(enc.Species, enc.Form);
var learn = Legal.LevelUpLA;
var moveset = learn[index];
var ls = LearnSource8LA.Instance;
var moveset = ls.GetLearnset(enc.Species, enc.Form);
if (enc is IMasteryInitialMoveShop8 ms)
ms.LoadInitialMoveset(pa, moves, moveset, pa.Met_Level);
else
@ -114,8 +112,7 @@ public sealed class LegendsArceusVerifier : Verifier
for (int i = 0; i < evos.Length - 1; i++)
{
var evo = evos[i];
var x = pt.GetFormIndex(evo.Species, evo.Form);
var m = learn[x];
var m = ls.GetLearnset(evo.Species, evo.Form);
m.SetEvolutionMoves(moves, purchased, count);
count = moves.IndexOf((ushort)0);
if ((uint)count >= 4)
@ -123,8 +120,7 @@ public sealed class LegendsArceusVerifier : Verifier
}
// Any tutored moves we don't know about??
var currentIndex = pt.GetFormIndex(evos[0].Species, evos[0].Form);
var currentLearn = learn[currentIndex];
var currentLearn = ls.GetLearnset(evos[0].Species, evos[0].Form);
return AddMasteredMissing(pa, moves, count, moveset, currentLearn, level);
}
@ -230,9 +226,7 @@ public sealed class LegendsArceusVerifier : Verifier
int level = 101;
foreach (var evo in data.Info.EvoChainsAllGens.Gen8a)
{
var pt = PersonalTable.LA;
var index = pt.GetFormIndex(evo.Species, evo.Form);
var moveset = Legal.LevelUpLA[index];
var moveset = LearnSource8LA.Instance.GetLearnset(evo.Species, evo.Form);
var lvl = moveset.GetLevelLearnMove(moves[i]);
if (lvl == -1)
continue; // cannot learn via level up

View file

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.Species;
namespace PKHeX.Core;
@ -101,7 +103,7 @@ public static class RibbonRules
{
// Ranked is still ongoing, but the use of Mythicals was restricted to Series 13 only.
var met = pk.MetDate;
if (Legal.Mythicals.Contains(pk.Species) && met > new DateOnly(2022, 11, 1))
if (SpeciesCategory.IsMythical(pk.Species) && met > new DateOnly(2022, 11, 1))
return false;
}
@ -113,9 +115,9 @@ public static class RibbonRules
private static bool IsRibbonValidMasterRankSV(ISpeciesForm pk)
{
var species = pk.Species;
if (Legal.Mythicals.Contains(species))
if (SpeciesCategory.IsMythical(species))
return false;
if (Legal.Legends.Contains(species))
if (SpeciesCategory.IsLegendary(species))
return false;
var pt = PersonalTable.SV;
@ -146,7 +148,7 @@ public static class RibbonRules
if (!evos.HasVisitedBDSP)
return false;
// Mythicals cannot be used in BD/SP's Battle Tower
return !Legal.Mythicals.Contains(evos.Gen8b[0].Species);
return !SpeciesCategory.IsMythical(evos.Gen8b[0].Species);
}
/// <summary>
@ -360,7 +362,7 @@ public static class RibbonRules
/// <summary>
/// Checks if the input species could have participated in any Battle Frontier trial.
/// </summary>
public static bool IsAllowedBattleFrontier(ushort species) => !Legal.BattleFrontierBanlist.Contains(species);
public static bool IsAllowedBattleFrontier(ushort species) => !BattleFrontierBanlist.Contains(species);
/// <summary>
/// Checks if the input species could have participated in Generation 4's Battle Frontier.
@ -382,4 +384,20 @@ public static class RibbonRules
return false;
return IsAllowedBattleFrontier(species);
}
/// <summary>
/// Generation 3 &amp; 4 Battle Frontier Species banlist. When referencing this in context to generation 4, be sure to disallow <see cref="Pichu"/> with Form 1 (Spiky).
/// </summary>
public static readonly HashSet<ushort> BattleFrontierBanlist = new()
{
(int)Mewtwo, (int)Mew,
(int)Lugia, (int)HoOh, (int)Celebi,
(int)Kyogre, (int)Groudon, (int)Rayquaza, (int)Jirachi, (int)Deoxys,
(int)Dialga, (int)Palkia, (int)Giratina, (int)Phione, (int)Manaphy, (int)Darkrai, (int)Shaymin, (int)Arceus,
(int)Victini, (int)Reshiram, (int)Zekrom, (int)Kyurem, (int)Keldeo, (int)Meloetta, (int)Genesect,
(int)Xerneas, (int)Yveltal, (int)Zygarde, (int)Diancie, (int)Hoopa, (int)Volcanion,
(int)Cosmog, (int)Cosmoem, (int)Solgaleo, (int)Lunala, (int)Necrozma, (int)Magearna, (int)Marshadow, (int)Zeraora,
(int)Meltan, (int)Melmetal,
(int)Koraidon, (int)Miraidon,
};
}

View file

@ -51,4 +51,35 @@ internal static class MoveInfo7b
05, 15, 05, 01, 01, 01, 01, 01, 01, 15, 15, 15, 20, 15, 15, 15, 15, 15, 15, 15,
15, 20, 05,
};
public static bool IsAllowedMoveGG(ushort move) => AllowedMovesGG.BinarySearch(move) >= 0;
private static ReadOnlySpan<ushort> AllowedMovesGG => new ushort[]
{
000, 001, 002, 003, 004, 005, 006, 007, 008, 009,
010, 011, 012, 013, 014, 015, 016, 017, 018, 019,
020, 021, 022, 023, 024, 025, 026, 027, 028, 029,
030, 031, 032, 033, 034, 035, 036, 037, 038, 039,
040, 041, 042, 043, 044, 045, 046, 047, 048, 049,
050, 051, 052, 053, 054, 055, 056, 057, 058, 059,
060, 061, 062, 063, 064, 065, 066, 067, 068, 069,
070, 071, 072, 073, 074, 075, 076, 077, 078, 079,
080, 081, 082, 083, 084, 085, 086, 087, 088, 089,
090, 091, 092, 093, 094, 095, 096, 097, 098, 099,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164,
182, 188, 200, 224, 227, 231, 242, 243, 247, 252,
257, 261, 263, 269, 270, 276, 280, 281, 339, 347,
355, 364, 369, 389, 394, 398, 399, 403, 404, 405,
406, 417, 420, 430, 438, 446, 453, 483, 492, 499,
503, 504, 525, 529, 583, 585, 603, 605, 606, 607,
729, 730, 731, 733, 734, 735, 736, 737, 738, 739,
740, 742,
};
}

View file

@ -247,7 +247,8 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
if (Move1 == 0) // No moves defined
{
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, Species, Form, Level, (GameVersion)pk.Version);
var source = GameData.GetLearnSource((GameVersion)pk.Version);
source.SetEncounterMoves(Species, Form, Level, moves);
pk.SetMoves(moves);
}

View file

@ -165,8 +165,18 @@ public sealed class WC3 : MysteryGift, IRibbonSetEvent3, ILangNicknamedTemplate
{
if (!Moves.HasMoves) // not completely defined
{
var ver = (GameVersion)pk.Version;
ILearnSource ls = ver switch
{
GameVersion.R or GameVersion.S => LearnSource3RS.Instance,
GameVersion.FR => LearnSource3FR.Instance,
GameVersion.LG => LearnSource3LG.Instance,
_ => LearnSource3E.Instance,
};
var learn = ls.GetLearnset(Species, Form);
Span<ushort> moves = stackalloc ushort[4];
MoveList.GetCurrentMoves(pk, Species, Form, (GameVersion)pk.Version, Level, moves);
learn.SetEncounterMoves(Level, moves);
Moves = new(moves[0], moves[1], moves[2], moves[3]);
}

View file

@ -16,8 +16,13 @@ public interface IAwakened
byte AV_SPD { get; set; }
}
/// <summary>
/// Logic for interacting with LGP/E Awakening values.
/// </summary>
public static class AwakeningUtil
{
public const byte AwakeningMax = 200;
/// <summary>
/// Sums all values.
/// </summary>
@ -34,7 +39,7 @@ public static class AwakeningUtil
/// Sets all values to the maximum value.
/// </summary>
/// <param name="pk">Data to set values for</param>
public static void AwakeningMax(this IAwakened pk) => pk.AwakeningSetAllTo(Legal.AwakeningMax);
public static void AwakeningMaximize(this IAwakened pk) => pk.AwakeningSetAllTo(AwakeningMax);
/// <summary>
/// Sets all values to the specified value.
@ -63,7 +68,7 @@ public static class AwakeningUtil
/// <param name="pk">Data to set values for</param>
/// <param name="min">Minimum value to set</param>
/// <param name="max">Maximum value to set</param>
public static void AwakeningSetRandom(this IAwakened pk, byte min = 0, int max = Legal.AwakeningMax)
public static void AwakeningSetRandom(this IAwakened pk, byte min = 0, int max = AwakeningMax)
{
if (pk is not PB7 pb7)
return;
@ -117,17 +122,17 @@ public static class AwakeningUtil
/// <param name="pk">Data to check</param>
public static bool AwakeningAllValid(this IAwakened pk)
{
if (pk.AV_HP > Legal.AwakeningMax)
if (pk.AV_HP > AwakeningMax)
return false;
if (pk.AV_ATK > Legal.AwakeningMax)
if (pk.AV_ATK > AwakeningMax)
return false;
if (pk.AV_DEF > Legal.AwakeningMax)
if (pk.AV_DEF > AwakeningMax)
return false;
if (pk.AV_SPE > Legal.AwakeningMax)
if (pk.AV_SPE > AwakeningMax)
return false;
if (pk.AV_SPA > Legal.AwakeningMax)
if (pk.AV_SPA > AwakeningMax)
return false;
if (pk.AV_SPD > Legal.AwakeningMax)
if (pk.AV_SPD > AwakeningMax)
return false;
return true;
}
@ -189,12 +194,12 @@ public static class AwakeningUtil
{
Span<byte> result = stackalloc byte[6];
SetExpectedMinimumAVs(result, (PB7)a);
a.AV_HP = Legal.AwakeningMax;
a.AV_ATK = pk.IV_ATK == 0 ? result[1] : Legal.AwakeningMax;
a.AV_DEF = Legal.AwakeningMax;
a.AV_SPA = Legal.AwakeningMax;
a.AV_SPD = Legal.AwakeningMax;
a.AV_SPE = pk.IV_SPE == 0 ? result[5] : Legal.AwakeningMax;
a.AV_HP = AwakeningMax;
a.AV_ATK = pk.IV_ATK == 0 ? result[1] : AwakeningMax;
a.AV_DEF = AwakeningMax;
a.AV_SPA = AwakeningMax;
a.AV_SPD = AwakeningMax;
a.AV_SPE = pk.IV_SPE == 0 ? result[5] : AwakeningMax;
}
public static bool IsAwakeningBelow(this IAwakened current, IAwakened initial) => !current.IsAwakeningAboveOrEqual(initial);

View file

@ -38,7 +38,8 @@ public static class BattleVersionExtensions
pk.SetRelearnMoves(empty);
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, pk.CurrentLevel, version);
var source = GameData.GetLearnSource(version);
source.SetEncounterMoves(pk.Species, pk.Form, pk.CurrentLevel, moves);
pk.SetMoves(moves);
pk.FixMoves();
v.BattleVersion = (byte)version;

View file

@ -1055,11 +1055,7 @@ public sealed class PA8 : PKM, ISanityChecksum, IMoveReset,
public void ResetMoves()
{
var learnsets = Legal.LevelUpLA;
var table = PersonalTable.LA;
var index = table.GetFormIndex(Species, Form);
var learn = learnsets[index];
var learn = LearnSource8LA.Instance.GetLearnset(Species, Form);
Span<ushort> moves = stackalloc ushort[4];
learn.SetEncounterMoves(CurrentLevel, moves);
SetMoves(moves);

View file

@ -147,11 +147,7 @@ public sealed class PB8 : G8PKM
public override void ResetMoves()
{
var learnsets = Legal.LevelUpBDSP;
var table = PersonalTable.BDSP;
var index = table.GetFormIndex(Species, Form);
var learn = learnsets[index];
var learn = LearnSource8BDSP.Instance.GetLearnset(Species, Form);
Span<ushort> moves = stackalloc ushort[4];
learn.SetEncounterMoves(CurrentLevel, moves);
SetMoves(moves);

Some files were not shown because too many files have changed in this diff Show more