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

View file

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

View file

@ -50,8 +50,8 @@ public static class Pokerus
if (!IsObtainable(pk)) if (!IsObtainable(pk))
return IsSusceptible(strain, days); return IsSusceptible(strain, days);
if (pk.Format <= 2) if (pk.Format <= 2)
return IsStrainValid2(strain); return IsStrainValid2(strain, days);
return IsStrainValid(strain); return IsStrainValid(strain, days);
} }
/// <inheritdoc cref="IsStrainValid(PKM,int,int)"/> /// <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, (int)Greninja or (int)Rockruff or (int)Koraidon or (int)Miraidon => string.Empty,
_ => Legal.Totem_USUM.Contains(species) && form == "Large" _ => FormInfo.HasTotemForm(species) && form == "Large"
? Legal.Totem_Alolan.Contains(species) && species != (int)Mimikyu ? "Alola-Totem" : "Totem" ? species is (int)Raticate or (int)Marowak ? "Alola-Totem" : "Totem"
: form.Replace(' ', '-'), : form.Replace(' ', '-'),
}; };
} }
@ -139,7 +139,7 @@ public static class ShowdownParsing
(int)Maushold when form == "Four" => "Family of Four", (int)Maushold when form == "Four" => "Family of Four",
(int)Urshifu or (int)Pikachu or (int)Alcremie => form.Replace('-', ' '), // Strike and Cosplay (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; var legal = source.LegalMoveDataSource;
return sav switch 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), _ => legal.Where(m => m.Value <= sav.MaxMoveID),
}; };
} }

View file

@ -6,16 +6,14 @@ public sealed class ItemStorage7GG : IItemStorage
{ {
public static readonly ItemStorage7GG Instance = new(); 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 050, // Rare Candy
960, 961, 962, 963, 964, 965, // S 960, 961, 962, 963, 964, 965, // S
966, 967, 968, 969, 970, 971, // L 966, 967, 968, 969, 970, 971, // L
972, 973, 974, 975, 976, 977, // XL 972, 973, 974, 975, 976, 977, // XL
};
private static ReadOnlySpan<ushort> Pouch_Candy_GG_Species => new ushort[] // Species
{
978, 979, 978, 979,
980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989,
990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999,
@ -28,8 +26,6 @@ public sealed class ItemStorage7GG : IItemStorage
1057, 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[] 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, 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 public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
{ {
InventoryType.Medicine => Pouch_Medicine_GG, InventoryType.Medicine => Pouch_Medicine_GG,
InventoryType.TMHMs => Pouch_TM_GG, InventoryType.TMHMs => Pouch_TM_GG,
InventoryType.Balls => Pouch_Catching_GG, InventoryType.Balls => Pouch_Catching_GG,

View file

@ -468,10 +468,16 @@ public sealed class ItemStorage8SWSH : IItemStorage
1578, // ★Aql7235 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) public static bool IsItemLegal(ushort item)
{ {
if (Legal.IsDynamaxCrystal(item)) if (IsDynamaxCrystal(item))
return Legal.IsDynamaxCrystalAvailable(item); return IsDynamaxCrystalAvailable(item);
return Unreleased.BinarySearch(item) < 0; 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> /// <returns>True if can inherit moves, false if cannot.</returns>
internal static bool GetCanInheritMoves(ushort species) 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]; var pi = PKX.Personal[species];
if (pi is { Genderless: false, OnlyMale: false }) if (pi is { Genderless: false, OnlyMale: false })
return true; return true;

View file

@ -1,5 +1,4 @@
using System; using System;
using static PKHeX.Core.BinLinkerAccessor;
namespace PKHeX.Core; namespace PKHeX.Core;
@ -8,60 +7,135 @@ namespace PKHeX.Core;
/// </summary> /// </summary>
public static partial class Legal public static partial class Legal
{ {
// Gen 1 internal const int MaxSpeciesID_1 = 151;
internal static readonly Learnset[] LevelUpRB = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_rb.pkl"), MaxSpeciesID_1); internal const int MaxMoveID_1 = 165;
internal static readonly Learnset[] LevelUpY = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_y.pkl"), MaxSpeciesID_1); internal const int MaxItemID_1 = 255;
internal const int MaxAbilityID_1 = 0;
// Gen 2 internal const int MaxSpeciesID_2 = 251;
internal static readonly EggMoves2[] EggMovesGS = EggMoves2.GetArray(Util.GetBinaryResource("eggmove_gs.pkl"), MaxSpeciesID_2); internal const int MaxMoveID_2 = 251;
internal static readonly Learnset[] LevelUpGS = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_gs.pkl"), MaxSpeciesID_2); internal const int MaxItemID_2 = 255;
internal static readonly EggMoves2[] EggMovesC = EggMoves2.GetArray(Util.GetBinaryResource("eggmove_c.pkl"), MaxSpeciesID_2); internal const int MaxAbilityID_2 = 0;
internal static readonly Learnset[] LevelUpC = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_c.pkl"), MaxSpeciesID_2);
// Gen 3 internal const int MaxSpeciesIndex_3 = 412;
internal static readonly Learnset[] LevelUpE = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_e.pkl"), "em")); internal const int MaxSpeciesID_3 = 386;
internal static readonly Learnset[] LevelUpRS = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_rs.pkl"), "rs")); internal const int MaxMoveID_3 = 354;
internal static readonly Learnset[] LevelUpFR = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_fr.pkl"), "fr")); internal const int MaxItemID_3 = 374;
internal static readonly Learnset[] LevelUpLG = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_lg.pkl"), "lg")); internal const int MaxItemID_3_COLO = 547;
internal static readonly EggMoves6[] EggMovesRS = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_rs.pkl"), "rs")); 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 const int MaxSpeciesID_4 = 493;
internal static readonly Learnset[] LevelUpDP = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_dp.pkl"), "dp")); internal const int MaxMoveID_4 = 467;
internal static readonly Learnset[] LevelUpPt = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_pt.pkl"), "pt")); internal const int MaxItemID_4_DP = 464;
internal static readonly Learnset[] LevelUpHGSS = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_hgss.pkl"), "hs")); internal const int MaxItemID_4_Pt = 467;
internal static readonly EggMoves6[] EggMovesDPPt = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_dppt.pkl"), "dp")); internal const int MaxItemID_4_HGSS = 536;
internal static readonly EggMoves6[] EggMovesHGSS = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_hgss.pkl"), "hs")); internal const int MaxAbilityID_4 = 123;
internal const int MaxBallID_4 = 0x18;
internal const int MaxGameID_4 = 15; // CXD
// Gen 5 internal const int MaxSpeciesID_5 = 649;
internal static readonly Learnset[] LevelUpBW = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_bw.pkl"), "51")); internal const int MaxMoveID_5 = 559;
internal static readonly Learnset[] LevelUpB2W2 = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_b2w2.pkl"), "52")); internal const int MaxItemID_5_BW = 632;
internal static readonly EggMoves6[] EggMovesBW = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_bw.pkl"), "bw")); 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 const int MaxSpeciesID_6 = 721;
internal static readonly EggMoves6[] EggMovesXY = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_xy.pkl"), "xy")); internal const int MaxMoveID_6_XY = 617;
internal static readonly Learnset[] LevelUpXY = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_xy.pkl"), "xy")); internal const int MaxMoveID_6_AO = 621;
internal static readonly EggMoves6[] EggMovesAO = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_ao.pkl"), "ao")); internal const int MaxItemID_6_XY = 717;
internal static readonly Learnset[] LevelUpAO = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_ao.pkl"), "ao")); 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 const int MaxSpeciesID_7 = 802;
internal static readonly EggMoves7[] EggMovesSM = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_sm.pkl"), "sm")); internal const int MaxMoveID_7 = 719;
internal static readonly Learnset[] LevelUpSM = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm")); internal const int MaxItemID_7 = 920;
internal static readonly EggMoves7[] EggMovesUSUM = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_uu.pkl"), "uu")); internal const int MaxAbilityID_7 = 232;
internal static readonly Learnset[] LevelUpUSUM = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_uu.pkl"), "uu")); internal const int MaxBallID_7 = 0x1A; // 26
internal static readonly Learnset[] LevelUpGG = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_gg.pkl"), "gg")); internal const int MaxGameID_7 = 41; // Crystal (VC?)
// Gen 8 internal const int MaxSpeciesID_7_USUM = 807;
internal static readonly EggMoves7[] EggMovesSWSH = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_swsh.pkl"), "ss")); internal const int MaxMoveID_7_USUM = 728;
internal static readonly Learnset[] LevelUpSWSH = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_swsh.pkl"), "ss")); internal const int MaxItemID_7_USUM = 959;
internal static readonly EggMoves6[] EggMovesBDSP = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_bdsp.pkl"), "bs")); internal const int MaxAbilityID_7_USUM = 233;
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"));
// Gen 9 internal const int MaxSpeciesID_7b = 809; // Melmetal
internal static readonly ushort[][] EggMovesSV = EggMoves9.GetArray(Get(Util.GetBinaryResource("eggmove_sv.pkl"), "sv")); internal const int MaxMoveID_7b = 742; // Double Iron Bash
internal static readonly ushort[][] ReminderSV = EggMoves9.GetArray(Get(Util.GetBinaryResource("reminder_sv.pkl"), "sv")); internal const int MaxItemID_7b = 1057; // Magmar Candy
internal static readonly Learnset[] LevelUpSV = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_sv.pkl"), "sv")); 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 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) private void SetEncounterMoves(PKM pk, GameVersion version)
{ {
var learnset = GameData.GetLearnset(version, Species, Form); var ls = GameData.GetLearnSource(version);
var baseMoves = learnset.GetBaseEggMoves(Level); var learn = ls.GetLearnset(Species, Form);
pk.SetMoves(baseMoves); 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) protected virtual void SetEncounterMoves(PKM pk, GameVersion version, int level)
{ {
Span<ushort> moves = stackalloc ushort[4]; 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.SetMoves(moves);
pk.SetMaximumPPCurrent(moves); pk.SetMaximumPPCurrent(moves);
} }

View file

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

View file

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

View file

@ -50,7 +50,8 @@ public sealed record EncounterSlot7GO : EncounterSlotGO
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level) protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{ {
Span<ushort> moves = stackalloc ushort[4]; 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.SetMoves(moves);
pk.SetMaximumPPCurrent(moves); pk.SetMaximumPPCurrent(moves);
} }

View file

@ -139,7 +139,11 @@ public sealed record EncounterSlot8GO : EncounterSlotGO, IFixedOTFriendship
pk.SetMaximumPPCurrent(moves); 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) public override EncounterMatchRating GetMatchRating(PKM pk)
{ {

View file

@ -161,7 +161,8 @@ public abstract record EncounterStatic(GameVersion Version) : IEncounterable, IM
else else
{ {
Span<ushort> moves = stackalloc ushort[4]; 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.SetMoves(moves);
pk.SetMaximumPPCurrent(moves); pk.SetMaximumPPCurrent(moves);
} }

View file

@ -11,7 +11,7 @@ public sealed record EncounterStatic7(GameVersion Version) : EncounterStatic(Ver
public Moveset Relearn { get; init; } public Moveset Relearn { get; init; }
public bool IsTotem => FormInfo.IsTotemForm(Species, Form); 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 int GetTotemBaseForm() => FormInfo.GetTotemBaseForm(Species, Form);
public bool IsRandomUnspecificForm => Form >= FormDynamic; 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 const bool allowAlphaPurchaseBug = true; // Everything else Alpha is pre-1.1
var level = pk.Met_Level; var level = pk.Met_Level;
var index = PersonalTable.LA.GetFormIndex(Species, Form); var (learn, mastery) = GetLevelUpInfo();
var learn = Legal.LevelUpLA[index];
if (!p.IsValidPurchasedEncounter(learn, level, alpha, allowAlphaPurchaseBug)) if (!p.IsValidPurchasedEncounter(learn, level, alpha, allowAlphaPurchaseBug))
return false; return false;
Span<ushort> moves = stackalloc ushort[4]; Span<ushort> moves = stackalloc ushort[4];
var mastery = Legal.MasteryLA[index];
if (Moves.HasMoves) if (Moves.HasMoves)
Moves.CopyTo(moves); Moves.CopyTo(moves);
else else
@ -165,10 +163,7 @@ public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Ve
public (Learnset Learn, Learnset Mastery) GetLevelUpInfo() public (Learnset Learn, Learnset Mastery) GetLevelUpInfo()
{ {
var index = PersonalTable.LA.GetFormIndex(Species, Form); return LearnSource8LA.GetLearnsetAndMastery(Species, Form);
var learn = Legal.LevelUpLA[index];
var mastery = Legal.MasteryLA[index];
return (learn, mastery);
} }
public void LoadInitialMoveset(PA8 pa8, Span<ushort> moves, Learnset learn, int level) 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 else
{ {
Span<ushort> moves = stackalloc ushort[4]; 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.SetMoves(moves);
pk.SetMaximumPPCurrent(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 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 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, 5 => loc is Locations.Breeder5,
_ => loc is Locations.Breeder6, _ => loc is Locations.Breeder6,
}; };

View file

@ -302,7 +302,8 @@ public static class EncounterMovesetGenerator
continue; 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); int flags = Moveset.BitOverlap(eggMoves, needs);
var vt = Array.IndexOf(needs, (ushort)Move.VoltTackle); var vt = Array.IndexOf(needs, (ushort)Move.VoltTackle);
if (vt != -1 && egg is EncounterEgg { CanHaveVoltTackle: true }) 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) private static int GetMoveMaskGen2(ReadOnlySpan<ushort> needs, IEncounterTemplate enc)
{ {
Span<ushort> moves = stackalloc ushort[4]; 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); return Moveset.BitOverlap(moves, needs);
} }

View file

@ -107,7 +107,7 @@ public static class EncounterSuggestion
if (pk.Format == 4) // Pal Park if (pk.Format == 4) // Pal Park
return Locations.Transfer3; return Locations.Transfer3;
if (pk.Format >= 5) // Transporter if (pk.Format >= 5) // Transporter
return Legal.GetTransfer45MetLocation(pk); return PK5.GetTransferMetLocation4(pk);
return -1; 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;
using System.Collections.Generic;
using static PKHeX.Core.LegalityCheckStrings; using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core; namespace PKHeX.Core;
@ -27,21 +26,21 @@ public static class EncounterVerifier
EncounterSlot w => VerifyEncounterWild(w), EncounterSlot w => VerifyEncounterWild(w),
EncounterStatic s => VerifyEncounterStatic(pk, s), EncounterStatic s => VerifyEncounterStatic(pk, s),
MysteryGift g => VerifyEncounterEvent(pk, g), MysteryGift g => VerifyEncounterEvent(pk, g),
_ => new CheckResult(Severity.Invalid, LEncInvalid, CheckIdentifier.Encounter), _ => GetInvalid(LEncInvalid),
}; };
private static CheckResult VerifyEncounterG12(PKM pk, IEncounterTemplate enc) private static CheckResult VerifyEncounterG12(PKM pk, IEncounterTemplate enc)
{ {
if (enc.EggEncounter) if (enc.EggEncounter)
return VerifyEncounterEgg(pk, enc.Generation); return VerifyEncounterEgg(pk, 2);
return enc switch return enc switch
{ {
EncounterSlot1 => new CheckResult(Severity.Valid, LEncCondition, CheckIdentifier.Encounter), EncounterSlot1 => GetValid(LEncCondition),
EncounterSlot2 s2 => VerifyWildEncounterGen2(pk, s2), EncounterSlot2 s2 => VerifyWildEncounterGen2(pk, s2),
EncounterStatic s => VerifyEncounterStatic(pk, s), EncounterStatic s => VerifyEncounterStatic(pk, s),
EncounterTrade t => VerifyEncounterTrade(pk, t), 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) switch (encounter.Location)
{ {
case 19: // National Park case 19: // National Park
return new CheckResult(Severity.Invalid, LG2InvalidTilePark, CheckIdentifier.Encounter); return GetInvalid(LG2InvalidTilePark);
case 76: // Route 14 case 76: // Route 14
return new CheckResult(Severity.Invalid, LG2InvalidTileR14, CheckIdentifier.Encounter); return GetInvalid(LG2InvalidTileR14);
} }
break; break;
} }
return new CheckResult(Severity.Valid, LEncCondition, CheckIdentifier.Encounter); return GetValid(LEncCondition);
} }
private static CheckResult VerifyWildEncounterCrystalHeadbutt(ITrainerID32 tr, EncounterSlot2 s2) private static CheckResult VerifyWildEncounterCrystalHeadbutt(ITrainerID32 tr, EncounterSlot2 s2)
{ {
return s2.IsTreeAvailable(tr.TID16) return s2.IsTreeAvailable(tr.TID16)
? new CheckResult(Severity.Valid, LG2TreeID, CheckIdentifier.Encounter) ? GetValid(LG2TreeID)
: new CheckResult(Severity.Invalid, LG2InvalidTileTreeNotFound, CheckIdentifier.Encounter); : GetInvalid(LG2InvalidTileTreeNotFound);
} }
// Eggs // 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 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), 8 => pk.IsEgg ? VerifyUnhatchedEgg(pk, Locations.LinkTrade6) : VerifyEncounterEgg8(pk),
9 => pk.IsEgg ? VerifyUnhatchedEgg(pk, Locations.LinkTrade6) : VerifyEncounterEgg9(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) private static CheckResult VerifyUnhatchedEgg3(PKM pk)
{ {
if (pk.Met_Level != 0) 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. // Only EncounterEgg should reach here.
var loc = pk.FRLG ? Locations.HatchLocationFRLG : Locations.HatchLocationRSE; var loc = pk.FRLG ? Locations.HatchLocationFRLG : Locations.HatchLocationRSE;
if (pk.Met_Location != loc) 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) private static CheckResult VerifyEncounterEgg3(PKM pk)
@ -108,172 +107,192 @@ public static class EncounterVerifier
return VerifyEncounterEgg3Transfer(pk); return VerifyEncounterEgg3Transfer(pk);
if (pk.Met_Level != 0) 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. // Check the origin game list.
var met = (byte)pk.Met_Location; var met = (byte)pk.Met_Location;
var locs = pk.FRLG ? Legal.ValidMet_FRLG : pk.E ? Legal.ValidMet_E : Legal.ValidMet_RS; bool valid = EggHatchLocation3.IsValidMet3(met, (GameVersion)pk.Version);
if (locs.Contains(met)) if (valid)
return new CheckResult(Severity.Valid, LEggLocation, CheckIdentifier.Encounter); return GetValid(LEggLocation);
// Version isn't updated when hatching on a different game. Check any game. // 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)) if (EggHatchLocation3.IsValidMet3Any(met))
return new CheckResult(Severity.Valid, LEggLocationTrade, CheckIdentifier.Encounter); return GetValid(LEggLocationTrade);
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter); 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) private static CheckResult VerifyEncounterEgg3Transfer(PKM pk)
{ {
if (pk.IsEgg) if (pk.IsEgg)
return new CheckResult(Severity.Invalid, LTransferEgg, CheckIdentifier.Encounter); return GetInvalid(LTransferEgg);
if (pk.Met_Level < 5) if (pk.Met_Level < 5)
return new CheckResult(Severity.Invalid, LTransferEggMetLevel, CheckIdentifier.Encounter); return GetInvalid(LTransferEggMetLevel);
var expectEgg = pk is PB8 ? Locations.Default8bNone : 0; var expectEgg = pk is PB8 ? Locations.Default8bNone : 0;
if (pk.Egg_Location != expectEgg) if (pk.Egg_Location != expectEgg)
return new CheckResult(Severity.Invalid, LEggLocationNone, CheckIdentifier.Encounter); return GetInvalid(LEggLocationNone);
if (pk.Format != 4) if (pk.Format != 4)
{ {
if (pk.Met_Location != Locations.Transfer4) if (pk.Met_Location != Locations.Transfer4)
return new CheckResult(Severity.Invalid, LTransferEggLocationTransporter, CheckIdentifier.Encounter); return GetInvalid(LTransferEggLocationTransporter);
} }
else else
{ {
if (pk.Met_Location != Locations.Transfer3) 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) 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. if (pk.IsEgg)
var locations = pk.WasTradedEgg ? Legal.ValidMet_4 : return GetInvalid(LTransferEgg);
pk.HGSS ? Legal.ValidMet_HGSS : if (pk.Met_Level < 1)
pk.Pt ? Legal.ValidMet_Pt : return GetInvalid(LTransferEggMetLevel);
Legal.ValidMet_DP; if (pk.Met_Location != Locations.Transfer4)
return VerifyEncounterEggLevelLoc(pk, 0, locations); return GetInvalid(LTransferEggLocationTransporter);
return GetValid(LEggLocation);
} }
if (pk.IsEgg)
return new CheckResult(Severity.Invalid, LTransferEgg, CheckIdentifier.Encounter);
// transferred // Native
if (pk.Met_Level < 1) const byte level = 0;
return new CheckResult(Severity.Invalid, LTransferEggMetLevel, CheckIdentifier.Encounter); if (pk.Met_Level != level)
if (pk.Met_Location != Locations.Transfer4) return GetInvalid(string.Format(LEggFMetLevel_0, level));
return new CheckResult(Severity.Invalid, LTransferEggLocationTransporter, CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, LEggLocation, CheckIdentifier.Encounter); 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) private static CheckResult VerifyEncounterEgg5(PKM pk)
{ {
// Two game-specific locations we need to double check for. const byte level = 1;
// White / White2 cannot access Black Gate (112) if (pk.Met_Level != level)
// Black / Black2 cannot access White Gate (113) return GetInvalid(string.Format(LEggFMetLevel_0, level));
var met = pk.Met_Location;
var delta = (uint)(met - 112); var met = (ushort)pk.Met_Location;
if (delta <= 1) bool valid = EggHatchLocation5.IsValidMet5(met, (GameVersion)pk.Version);
{
var ver = pk.Version & 1; // W*=0, B*=1 if (valid)
if (ver == delta) return GetValid(LEggLocation);
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter); return GetInvalid(LEggLocationInvalid);
}
return VerifyEncounterEggLevelLoc(pk, 1, pk.B2W2 ? Legal.ValidMet_B2W2 : Legal.ValidMet_BW);
} }
private static CheckResult VerifyEncounterEgg6(PKM pk) private static CheckResult VerifyEncounterEgg6(PKM pk)
{ {
if (pk.AO) const byte level = 1;
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_AO); 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. var met = (ushort)pk.Met_Location;
return new CheckResult(Severity.Invalid, LEggMetLocationFail, CheckIdentifier.Encounter); 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) private static CheckResult VerifyEncounterEgg7(PKM pk)
{ {
if (pk.SM) const byte level = 1;
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_SM); if (pk.Met_Level != level)
if (pk.USUM) return GetInvalid(string.Format(LEggFMetLevel_0, level));
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_USUM);
// no other games var met = (ushort)pk.Met_Location;
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter); bool valid = pk.SM
? EggHatchLocation7.IsValidMet7SM(met)
: EggHatchLocation7.IsValidMet7USUM(met);
if (valid)
return GetValid(LEggLocation);
return GetInvalid(LEggLocationInvalid);
} }
private static CheckResult VerifyEncounterEgg8(PKM pk) private static CheckResult VerifyEncounterEgg8(PKM pk)
{ {
if (pk.SWSH) const byte level = 1;
{ if (pk.Met_Level != level)
if (pk.BDSP) return GetInvalid(string.Format(LEggFMetLevel_0, level));
return VerifyEncounterEggLevelLoc(pk, 1, (location, game) => location == (game == GameVersion.SW ? Locations.HOME_SWBD : Locations.HOME_SHSP));
return VerifyEncounterEggLevelLoc(pk, 1, Legal.ValidMet_SWSH);
}
// no other games var met = (ushort)pk.Met_Location;
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter); 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 (valid)
{ return GetValid(LEggLocation);
if (pk.SV) return GetInvalid(LEggLocationInvalid);
return VerifyEncounterEggLevelLoc(pk, 1, Legal.IsValidEggHatchLocation9);
// no other games
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
} }
private static CheckResult VerifyEncounterEgg8BDSP(PKM pk) private static CheckResult VerifyEncounterEgg8BDSP(PKM pk)
{ {
if (pk.BDSP) const byte level = 1;
return VerifyEncounterEggLevelLoc(pk, 1, Legal.IsValidEggHatchLocation8b); if (pk.Met_Level != level)
return GetInvalid(string.Format(LEggFMetLevel_0, level));
// no other games var met = (ushort)pk.Met_Location;
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter); 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) var met = (ushort)pk.Met_Location;
private static CheckResult VerifyEncounterEggLevelLoc(PKM pk, int eggLevel, Func<ushort, GameVersion, bool> isValid) bool valid = pk.Version == (int)GameVersion.SL
{ ? EggHatchLocation9.IsValidMet9SL(met)
if (pk.Met_Level != eggLevel) : EggHatchLocation9.IsValidMet9VL(met);
return new CheckResult(Severity.Invalid, string.Format(LEggFMetLevel_0, eggLevel), CheckIdentifier.Encounter);
return isValid((ushort)pk.Met_Location, (GameVersion)pk.Version) if (valid)
? new CheckResult(Severity.Valid, LEggLocation, CheckIdentifier.Encounter) return GetValid(LEggLocation);
: new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter); return GetInvalid(LEggLocationInvalid);
} }
private static CheckResult VerifyUnhatchedEgg(PKM pk, int tradeLoc, int noneLoc = 0) private static CheckResult VerifyUnhatchedEgg(PKM pk, int tradeLoc, int noneLoc = 0)
{ {
var eggLevel = pk.Format < 5 ? 0 : 1; var eggLevel = pk.Format < 5 ? 0 : 1;
if (pk.Met_Level != eggLevel) 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) if (pk.Egg_Location == tradeLoc)
return new CheckResult(Severity.Invalid, LEggLocationTradeFail, CheckIdentifier.Encounter); return GetInvalid(LEggLocationTradeFail);
var met = pk.Met_Location; var met = pk.Met_Location;
if (met == tradeLoc) if (met == tradeLoc)
return new CheckResult(Severity.Valid, LEggLocationTrade, CheckIdentifier.Encounter); return GetValid(LEggLocationTrade);
return met == noneLoc return met == noneLoc
? new CheckResult(Severity.Valid, LEggUnhatched, CheckIdentifier.Encounter) ? GetValid(LEggUnhatched)
: new CheckResult(Severity.Invalid, LEggLocationNone, CheckIdentifier.Encounter); : GetInvalid(LEggLocationNone);
} }
// Other // Other
private static CheckResult VerifyEncounterWild(EncounterSlot slot) private static CheckResult VerifyEncounterWild(EncounterSlot slot)
{ {
var summary = slot.GetConditionString(out bool valid); 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) private static CheckResult VerifyEncounterStatic(PKM pk, EncounterStatic s)
@ -283,24 +302,24 @@ public static class EncounterVerifier
{ {
case 3: case 3:
if (s is EncounterStaticShadow {EReader: true} && pk.Language != (int)LanguageID.Japanese) // Non-JP E-reader Pokemon 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) switch (s.Species)
{ {
case (int)Species.Mew when s.Location == 201 && pk.Language != (int)LanguageID.Japanese: // Non-JP Mew (Old Sea Map) 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) 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; break;
case 4: 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 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; break;
case 7: case 7:
if (s.EggLocation == Locations.Daycare5 && pk.RelearnMove1 != 0) // Eevee gift egg 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; break;
} }
if (s.EggEncounter && !pk.IsEgg) // hatched if (s.EggEncounter && !pk.IsEgg) // hatched
@ -310,7 +329,7 @@ public static class EncounterVerifier
return hatchCheck; return hatchCheck;
} }
return new CheckResult(Severity.Valid, LEncStaticMatch, CheckIdentifier.Encounter); return GetValid(LEncStaticMatch);
} }
private static CheckResult VerifyEncounterTrade(ISpeciesForm pk, EncounterTrade trade) private static CheckResult VerifyEncounterTrade(ISpeciesForm pk, EncounterTrade trade)
@ -323,9 +342,9 @@ public static class EncounterVerifier
var names = ParseSettings.SpeciesStrings; var names = ParseSettings.SpeciesStrings;
var evolved = names[species + 1]; var evolved = names[species + 1];
var unevolved = names[species]; 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) private static CheckResult VerifyEncounterEvent(PKM pk, MysteryGift gift)
@ -334,7 +353,7 @@ public static class EncounterVerifier
{ {
case PCD pcd: case PCD pcd:
if (!pcd.CanBeReceivedByVersion(pk.Version) && pcd.Gift.PK.Version == 0) 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; break;
} }
if (!pk.IsEgg && gift.IsEgg) // hatched 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. // 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) private static void GetEncounterMoves(IEncounterTemplate enc, Span<ushort> moves)
{ {
if (enc.Version is GameVersion.YW or GameVersion.RBY) ILearnSource ls = enc.Version is GameVersion.YW or GameVersion.RBY
LearnSource1YW.Instance.GetEncounterMoves(enc, enc.LevelMin, moves); ? LearnSource1YW.Instance
else : LearnSource1RB.Instance;
LearnSource1RB.Instance.GetEncounterMoves(enc, enc.LevelMin, moves); 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) 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(); public static readonly LearnSource1RB Instance = new();
private static readonly PersonalTable1 Personal = PersonalTable.RB; 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 LearnEnvironment Game = RB;
private const int MaxSpecies = Legal.MaxSpeciesID_1; 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)) if (!TryGetPersonal(species, 0, out var personal))
return; return;

View file

@ -13,7 +13,7 @@ public sealed class LearnSource1YW : ILearnSource<PersonalInfo1>
{ {
public static readonly LearnSource1YW Instance = new(); public static readonly LearnSource1YW Instance = new();
private static readonly PersonalTable1 Personal = PersonalTable.Y; 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 LearnEnvironment Game = YW;
private const int MaxSpecies = Legal.MaxSpeciesID_1; 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)) if (!TryGetPersonal(species, 0, out var personal))
return; return;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,7 +12,7 @@ public sealed class LearnSource7GG : ILearnSource<PersonalInfo7GG>
{ {
public static readonly LearnSource7GG Instance = new(); public static readonly LearnSource7GG Instance = new();
private static readonly PersonalTable7GG Personal = PersonalTable.GG; 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 int MaxSpecies = Legal.MaxSpeciesID_7b;
private const LearnEnvironment Game = GG; private const LearnEnvironment Game = GG;
private const int ReminderBonus = 100; // Move reminder allows re-learning ALL level up moves regardless of level. 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(); public static readonly LearnSource7SM Instance = new();
private static readonly PersonalTable7 Personal = PersonalTable.SM; private static readonly PersonalTable7 Personal = PersonalTable.SM;
private static readonly Learnset[] Learnsets = Legal.LevelUpSM; private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
private static readonly EggMoves7[] EggMoves = Legal.EggMovesSM; private static readonly EggMoves7[] EggMoves = EggMoves7.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
private const int MaxSpecies = Legal.MaxSpeciesID_7; private const int MaxSpecies = Legal.MaxSpeciesID_7;
private const LearnEnvironment Game = SM; private const LearnEnvironment Game = SM;
private const int ReminderBonus = 100; // Move reminder allows re-learning ALL level up moves regardless of level. 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) if (species > MaxSpecies)
return false; return false;
var moves = MoveEgg.GetFormEggMoves(species, form, EggMoves).AsSpan(); var moves = EggMoves.GetFormEggMoves(species, form);
return moves.IndexOf(move) != -1; return moves.Contains(move);
} }
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form) public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form)
{ {
if (species > MaxSpecies) if (species > MaxSpecies)
return ReadOnlySpan<ushort>.Empty; 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) 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(); public static readonly LearnSource7USUM Instance = new();
private static readonly PersonalTable7 Personal = PersonalTable.USUM; private static readonly PersonalTable7 Personal = PersonalTable.USUM;
private static readonly Learnset[] Learnsets = Legal.LevelUpUSUM; private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_uu.pkl"), "uu"));
private static readonly EggMoves7[] EggMoves = Legal.EggMovesUSUM; 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 int MaxSpecies = Legal.MaxSpeciesID_7_USUM;
private const LearnEnvironment Game = USUM; private const LearnEnvironment Game = USUM;
private const int ReminderBonus = 100; // Move reminder allows re-learning ALL level up moves regardless of level. 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) if (species > MaxSpecies)
return false; return false;
var moves = MoveEgg.GetFormEggMoves(species, form, EggMoves).AsSpan(); var moves = EggMoves.GetFormEggMoves(species, form);
return moves.IndexOf(move) != -1; return moves.Contains(move);
} }
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form) public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form)
{ {
if (species > MaxSpecies) if (species > MaxSpecies)
return ReadOnlySpan<ushort>.Empty; 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) 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(); public static readonly LearnSource8BDSP Instance = new();
private static readonly PersonalTable8BDSP Personal = PersonalTable.BDSP; private static readonly PersonalTable8BDSP Personal = PersonalTable.BDSP;
private static readonly Learnset[] Learnsets = Legal.LevelUpBDSP; private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_bdsp.pkl"), "bs"));
private static readonly EggMoves6[] EggMoves = Legal.EggMovesBDSP; private static readonly EggMoves6[] EggMoves = EggMoves6.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_bdsp.pkl"), "bs"));
private const int MaxSpecies = Legal.MaxSpeciesID_8b; private const int MaxSpecies = Legal.MaxSpeciesID_8b;
private const LearnEnvironment Game = BDSP; private const LearnEnvironment Game = BDSP;

View file

@ -12,12 +12,19 @@ public sealed class LearnSource8LA : ILearnSource<PersonalInfo8LA>
{ {
public static readonly LearnSource8LA Instance = new(); public static readonly LearnSource8LA Instance = new();
private static readonly PersonalTable8LA Personal = PersonalTable.LA; 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 int MaxSpecies = Legal.MaxSpeciesID_8a;
private const LearnEnvironment Game = PLA; private const LearnEnvironment Game = PLA;
public Learnset GetLearnset(ushort species, byte form) => Learnsets[Personal.GetFormIndex(species, form)]; 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) public bool TryGetPersonal(ushort species, byte form, [NotNullWhen(true)] out PersonalInfo8LA? pi)
{ {
pi = null; pi = null;

View file

@ -12,8 +12,8 @@ public sealed class LearnSource8SWSH : ILearnSource<PersonalInfo8SWSH>, IEggSour
{ {
public static readonly LearnSource8SWSH Instance = new(); public static readonly LearnSource8SWSH Instance = new();
private static readonly PersonalTable8SWSH Personal = PersonalTable.SWSH; private static readonly PersonalTable8SWSH Personal = PersonalTable.SWSH;
private static readonly Learnset[] Learnsets = Legal.LevelUpSWSH; private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_swsh.pkl"), "ss"));
private static readonly EggMoves7[] EggMoves = Legal.EggMovesSWSH; 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 int MaxSpecies = Legal.MaxSpeciesID_8_R2;
private const LearnEnvironment Game = SWSH; private const LearnEnvironment Game = SWSH;
@ -32,15 +32,15 @@ public sealed class LearnSource8SWSH : ILearnSource<PersonalInfo8SWSH>, IEggSour
{ {
if (species > MaxSpecies) if (species > MaxSpecies)
return false; return false;
var moves = MoveEgg.GetFormEggMoves(species, form, EggMoves).AsSpan(); var moves = EggMoves.GetFormEggMoves(species, form);
return moves.IndexOf(move) != -1; return moves.Contains(move);
} }
public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form) public ReadOnlySpan<ushort> GetEggMoves(ushort species, byte form)
{ {
if (species > MaxSpecies) if (species > MaxSpecies)
return ReadOnlySpan<ushort>.Empty; 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) 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(); public static readonly LearnSource9SV Instance = new();
private static readonly PersonalTable9SV Personal = PersonalTable.SV; private static readonly PersonalTable9SV Personal = PersonalTable.SV;
private static readonly Learnset[] Learnsets = Legal.LevelUpSV; private static readonly Learnset[] Learnsets = LearnsetReader.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("lvlmove_sv.pkl"), "sv"));
private static readonly ushort[][] EggMoves = Legal.EggMovesSV; private static readonly ushort[][] EggMoves = EggMoves9.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("eggmove_sv.pkl"), "sv"));
private static readonly ushort[][] Reminder = Legal.ReminderSV; private static readonly ushort[][] Reminder = EggMoves9.GetArray(BinLinkerAccessor.Get(Util.GetBinaryResource("reminder_sv.pkl"), "sv"));
private const int MaxSpecies = Legal.MaxSpeciesID_9; private const int MaxSpecies = Legal.MaxSpeciesID_9;
private const LearnEnvironment Game = SV; private const LearnEnvironment Game = SV;

View file

@ -23,6 +23,15 @@ public interface ILearnSource
/// <param name="species">Entity species</param> /// <param name="species">Entity species</param>
/// <param name="form">Entity form</param> /// <param name="form">Entity form</param>
public Learnset GetLearnset(ushort species, byte form); 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> /// <summary>

View file

@ -2,8 +2,10 @@ using System;
namespace PKHeX.Core; 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[] internal static ReadOnlySpan<ushort> TM_3 => new ushort[]
{ {
264, 337, 352, 347, 046, 092, 258, 339, 331, 237, 264, 337, 352, 347, 046, 092, 258, 339, 331, 237,

View file

@ -2,8 +2,10 @@ using System;
namespace PKHeX.Core; 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> /// <summary>
/// Gets the preferred list of HM moves to disallow on transfer from <see cref="PK4"/> to <see cref="PK5"/>. /// Gets the preferred list of HM moves to disallow on transfer from <see cref="PK4"/> to <see cref="PK5"/>.
/// </summary> /// </summary>

View file

@ -2,8 +2,10 @@ using System;
namespace PKHeX.Core; 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[] internal static ReadOnlySpan<ushort> TMHM_BW => new ushort[]
{ {
468, 337, 473, 347, 046, 092, 258, 339, 474, 237, 468, 337, 473, 347, 046, 092, 258, 339, 474, 237,

View file

@ -31,7 +31,9 @@ internal static class LearnVerifierEgg
} }
else 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); 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> /// <summary>
/// Raw Egg Move storage /// Raw Egg Move storage
/// </summary> /// </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 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; return;
} }
@ -26,14 +27,16 @@ public static class MoveListSuggest
if (enc.Generation <= 2) if (enc.Generation <= 2)
{ {
var lvl = pk.Format >= 7 ? pk.Met_Level : pk.CurrentLevel; var lvl = pk.Format >= 7 ? pk.Met_Level : pk.CurrentLevel;
var ver = enc.Version; var source = GameData.GetLearnSource(enc.Version);
MoveLevelUp.GetEncounterMoves(moves, enc.Species, 0, lvl, ver); source.SetEncounterMoves(enc.Species, 0, lvl, moves);
return; return;
} }
if (pk.Species == enc.Species) 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; 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. // 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. // Let's remove all present base moves, and get a fresh set of base moves.
var learn = GameData.GetLearnsets(version); var learn = GameData.GetLearnSource(version);
var table = GameData.GetPersonal(version); var learnset = learn.GetLearnset(species, form);
var index = table.GetFormIndex(species, form);
var learnset = learn[index];
var eggLevel = EggStateLegality.GetEggLevel(generation); var eggLevel = EggStateLegality.GetEggLevel(generation);
var baseMoves = learnset.GetBaseEggMoves(eggLevel); var baseMoves = learnset.GetBaseEggMoves(eggLevel);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,62 +5,53 @@ namespace PKHeX.Core;
public static class GameData public static class GameData
{ {
public static Learnset[] GetLearnsets(GameVersion game) => Learnsets(game);
public static IPersonalTable GetPersonal(GameVersion game) => Personal(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); RD or GN or BU or RB => LearnSource1RB.Instance,
var index = pt.GetFormIndex(species, form); YW or RBY => LearnSource1YW.Instance,
var sets = Learnsets(game); GD or SI or GS => LearnSource2GS.Instance,
return sets[index]; C or GSC => LearnSource2C.Instance,
}
private static Learnset[] Learnsets(GameVersion game) => game switch R or S or RS or RSE => LearnSource3RS.Instance,
{ E or COLO or XD or FRLG or CXD => LearnSource3E.Instance,
RD or GN or BU or RB => Legal.LevelUpRB, FR => LearnSource3FR.Instance,
YW or RBY => Legal.LevelUpY, LG => LearnSource3LG.Instance,
GD or SI or GS => Legal.LevelUpGS,
C or GSC => Legal.LevelUpC,
R or S or RS or RSE => Legal.LevelUpRS, D or P or DP => LearnSource4DP.Instance,
E or COLO or XD or FRLG or CXD => Legal.LevelUpE, Pt or DPPt => LearnSource4Pt.Instance,
FR => Legal.LevelUpFR, HG or SS or HGSS => LearnSource4HGSS.Instance,
LG => Legal.LevelUpLG,
D or P or DP => Legal.LevelUpDP, B or W or BW => LearnSource5BW.Instance,
Pt or DPPt => Legal.LevelUpPt, B2 or W2 or B2W2 => LearnSource5B2W2.Instance,
HG or SS or HGSS => Legal.LevelUpHGSS,
B or W or BW => Legal.LevelUpBW, X or Y or XY => LearnSource6XY.Instance,
B2 or W2 or B2W2 => Legal.LevelUpB2W2, AS or OR or ORAS => LearnSource6AO.Instance,
X or Y or XY => Legal.LevelUpXY, SN or MN or SM => LearnSource7SM.Instance,
AS or OR or ORAS => Legal.LevelUpAO, US or UM or USUM => LearnSource7USUM.Instance,
GO or GP or GE or GG => LearnSource7GG.Instance,
SN or MN or SM => Legal.LevelUpSM, SW or SH or SWSH => LearnSource8SWSH.Instance,
US or UM or USUM => Legal.LevelUpUSUM, BD or SP or BDSP => LearnSource8BDSP.Instance,
GO or GP or GE or GG => Legal.LevelUpGG, PLA => LearnSource8LA.Instance,
SW or SH or SWSH => Legal.LevelUpSWSH, SL or VL or SV => LearnSource9SV.Instance,
BD or SP or BDSP => Legal.LevelUpBDSP,
PLA => Legal.LevelUpLA,
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, Stadium => LearnSource1YW.Instance,
Gen2 => Legal.LevelUpC, Stadium2 => LearnSource2GS.Instance,
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,
_ => throw new ArgumentOutOfRangeException(nameof(game), $"{game} is not a valid entry in the expression."), _ => 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, EntityContext.Gen8b => ReleasedHeldItems_8b,
_ => Array.Empty<bool>(), // lgp/e, pla, etc _ => 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. // Relearns can be wiped via Battle Version. Check for eggs too.
if (IsSpecialEncounterMoveEggDeleted(pk, enc)) 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)) if (em.Contains(move))
return true; return true;
} }

View file

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

View file

@ -120,7 +120,7 @@ public static class FormInfo
return false; 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[] private static ReadOnlySpan<ushort> FormChangeEgg => new ushort[]
{ {
@ -237,6 +237,28 @@ public static class FormInfo
return hs; 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> /// <summary>
/// Checks if the <see cref="form"/> for the <see cref="species"/> is a Totem form. /// Checks if the <see cref="form"/> for the <see cref="species"/> is a Totem form.
/// </summary> /// </summary>
@ -255,11 +277,11 @@ public static class FormInfo
{ {
if (form == 0) if (form == 0)
return false; return false;
if (!Legal.Totem_USUM.Contains(species)) if (!HasTotemForm(species))
return false; return false;
if (species == (int)Mimikyu) if (species == (int)Mimikyu)
return form is 2 or 3; 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 == 2;
return form == 1; return form == 1;
} }
@ -323,7 +345,7 @@ public static class FormInfo
if (format <= 3 && species != (int)Unown) if (format <= 3 && species != (int)Unown)
return false; return false;
if (HasFormValuesNotIndicatedByPersonal.Contains(species)) if (HasFormValuesNotIndicatedByPersonal(species))
return true; return true;
int count = pi.FormCount; int count = pi.FormCount;
@ -333,10 +355,11 @@ public static class FormInfo
/// <summary> /// <summary>
/// <seealso cref="IsValidOutOfBoundsForm"/> /// <seealso cref="IsValidOutOfBoundsForm"/>
/// </summary> /// </summary>
private static readonly HashSet<ushort> HasFormValuesNotIndicatedByPersonal = new() private static bool HasFormValuesNotIndicatedByPersonal(ushort species) => species switch
{ {
(int)Unown, (int)Unown => true,
(int)Mothim, // (Burmy form is not cleared on evolution) (int)Mothim => true, // (Burmy form is not cleared on evolution)
(int)Scatterbug, (int)Spewpa, // Vivillon pre-evos (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)) if (FormInfo.IsBattleOnlyForm(species, form, format))
return GetInvalid(LFormBattle); 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; return VALID;
} }

View file

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

View file

@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using static PKHeX.Core.Species;
namespace PKHeX.Core; 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. // Ranked is still ongoing, but the use of Mythicals was restricted to Series 13 only.
var met = pk.MetDate; 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; return false;
} }
@ -113,9 +115,9 @@ public static class RibbonRules
private static bool IsRibbonValidMasterRankSV(ISpeciesForm pk) private static bool IsRibbonValidMasterRankSV(ISpeciesForm pk)
{ {
var species = pk.Species; var species = pk.Species;
if (Legal.Mythicals.Contains(species)) if (SpeciesCategory.IsMythical(species))
return false; return false;
if (Legal.Legends.Contains(species)) if (SpeciesCategory.IsLegendary(species))
return false; return false;
var pt = PersonalTable.SV; var pt = PersonalTable.SV;
@ -146,7 +148,7 @@ public static class RibbonRules
if (!evos.HasVisitedBDSP) if (!evos.HasVisitedBDSP)
return false; return false;
// Mythicals cannot be used in BD/SP's Battle Tower // 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> /// <summary>
@ -360,7 +362,7 @@ public static class RibbonRules
/// <summary> /// <summary>
/// Checks if the input species could have participated in any Battle Frontier trial. /// Checks if the input species could have participated in any Battle Frontier trial.
/// </summary> /// </summary>
public static bool IsAllowedBattleFrontier(ushort species) => !Legal.BattleFrontierBanlist.Contains(species); public static bool IsAllowedBattleFrontier(ushort species) => !BattleFrontierBanlist.Contains(species);
/// <summary> /// <summary>
/// Checks if the input species could have participated in Generation 4's Battle Frontier. /// 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 false;
return IsAllowedBattleFrontier(species); 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, 05, 15, 05, 01, 01, 01, 01, 01, 01, 15, 15, 15, 20, 15, 15, 15, 15, 15, 15, 15,
15, 20, 05, 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 if (Move1 == 0) // No moves defined
{ {
Span<ushort> moves = stackalloc ushort[4]; 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); pk.SetMoves(moves);
} }

View file

@ -165,8 +165,18 @@ public sealed class WC3 : MysteryGift, IRibbonSetEvent3, ILangNicknamedTemplate
{ {
if (!Moves.HasMoves) // not completely defined 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]; 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]); Moves = new(moves[0], moves[1], moves[2], moves[3]);
} }

View file

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

View file

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

View file

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

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