using System; namespace PKHeX.Core; /// /// Loosely aggregated legality logic. /// public static 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; 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 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 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 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 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 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; internal const int MaxSpeciesID_7b = 809; // Melmetal internal const int MaxMoveID_7b = 742; // Double Iron Bash internal const int MaxItemID_7b = 1057; // Magmar Candy internal const int MaxBallID_7b = (int)Ball.Beast; internal const int MaxGameID_7b = (int)GameVersion.GE; internal const int MaxAbilityID_7b = MaxAbilityID_7_USUM; // Current Binaries internal const int MaxSpeciesID_8 = MaxSpeciesID_8_R2; internal const int MaxMoveID_8 = MaxMoveID_8_R2; internal const int MaxItemID_8 = MaxItemID_8_R2; internal const int MaxAbilityID_8 = MaxAbilityID_8_R2; // Orion (No DLC) internal const int MaxSpeciesID_8_O0 = 890; // Eternatus internal const int MaxMoveID_8_O0 = 796; // Steel Beam internal const int MaxItemID_8_O0 = 1278; // Rotom Catalog, ignore all catalog parts internal const int MaxAbilityID_8_O0 = 258; // Hunger Switch // Rigel 1 (DLC 1: Isle of Armor) internal const int MaxSpeciesID_8_R1 = 893; // Zarude internal const int MaxMoveID_8_R1 = 818; // Surging Strikes internal const int MaxItemID_8_R1 = 1589; // Mark Charm internal const int MaxAbilityID_8_R1 = 260; // Unseen Fist // Rigel 2 (DLC 2: Crown Tundra) internal const int MaxSpeciesID_8_R2 = 898; // Calyrex internal const int MaxMoveID_8_R2 = 826; // Eerie Spell internal const int MaxItemID_8_R2 = 1607; // Reins of Unity internal const int MaxAbilityID_8_R2 = 267; // As One (Glastrier) internal const int MaxBallID_8 = 0x1A; // 26 Beast internal const int MaxGameID_8 = 45; // Shield internal const int MaxSpeciesID_8a = (int)Species.Enamorus; internal const int MaxMoveID_8a = (int)Move.TakeHeart; internal const int MaxItemID_8a = 1828; // Legend Plate internal const int MaxBallID_8a = (int)Ball.LAOrigin; //internal const int MaxGameID_8a = (int)GameVersion.SP; internal const int MaxAbilityID_8a = MaxAbilityID_8_R2; internal const int MaxSpeciesID_8b = MaxSpeciesID_4; // Arceus-493 internal const int MaxMoveID_8b = MaxMoveID_8_R2; internal const int MaxItemID_8b = 1822; // DS Sounds internal const int MaxBallID_8b = (int)Ball.LAOrigin; //internal const int MaxGameID_8b = (int)GameVersion.SP; internal const int MaxAbilityID_8b = MaxAbilityID_8_R2; internal const int MaxSpeciesID_9 = (int)Species.IronLeaves; internal const int MaxMoveID_9 = (int)Move.MagicalTorque; internal const int MaxItemID_9 = 2400; // Yellow Dish internal const int MaxAbilityID_9 = (int)Ability.MyceliumMight; internal const int MaxBallID_9 = (int)Ball.LAOrigin; internal const int MaxGameID_9 = (int)GameVersion.VL; internal const int MaxGameID_HOME = MaxGameID_9; 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[] HeldItems_XY = ItemStorage6XY.GetAllHeld(); internal static readonly ushort[] HeldItems_AO = ItemStorage6AO.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(); internal static readonly ushort[] HeldItems_SWSH = ItemStorage8SWSH.GetAllHeld(); internal static readonly ushort[] HeldItems_BS = ItemStorage8BDSP.GetAll(); internal static readonly ushort[] HeldItems_LA = Array.Empty(); internal static readonly ushort[] HeldItems_SV = ItemStorage9SV.GetAllHeld(); internal static int GetMaxLanguageID(int generation) => generation switch { 1 => (int) LanguageID.Spanish, // 1-7 except 6 3 => (int) LanguageID.Spanish, // 1-7 except 6 2 => (int) LanguageID.Korean, 4 => (int) LanguageID.Korean, 5 => (int) LanguageID.Korean, 6 => (int) LanguageID.Korean, 7 => (int) LanguageID.ChineseT, 8 => (int) LanguageID.ChineseT, 9 => (int) LanguageID.ChineseT, _ => -1, }; /// /// Checks if the relearn moves should be wiped. /// /// Already checked for generations < 8. /// Entity to check internal static bool IsOriginalMovesetDeleted(this PKM pk) => pk switch { PA8 pa8 => !pa8.LA, PB8 pb8 => !pb8.BDSP, PK8 pk8 => pk8.IsSideTransfer || pk8.BattleVersion != 0, PK9 pk9 => !(pk9.SV || pk9 is { IsEgg: true, Version: 0 }), _ => false, }; /// /// Indicates if PP Ups are available for use. /// /// Entity to check public static bool IsPPUpAvailable(PKM pk) { return pk is not PA8; } /// /// Indicate if PP Ups are available for use. /// /// Move ID public static bool IsPPUpAvailable(ushort moveID) => moveID switch { 0 => false, (int)Move.Sketch => false, // BD/SP v1.0 could use PP Ups on Sketch, but not in later versions. Disallow anyways. (int)Move.RevivalBlessing => false, _ => true, }; /// /// Gets the maximum length of a Trainer Name for the input and . /// /// Generation of the Trainer /// Language of the Trainer public static int GetMaxLengthOT(int generation, LanguageID language) => language switch { LanguageID.ChineseS or LanguageID.ChineseT => 6, LanguageID.Japanese or LanguageID.Korean => generation >= 6 ? 6 : 5, _ => generation >= 6 ? 12 : 7, }; /// /// Gets the maximum length of a Nickname for the input and . /// /// Generation of the Trainer /// Language of the Trainer public static int GetMaxLengthNickname(int generation, LanguageID language) => language switch { LanguageID.ChineseS or LanguageID.ChineseT => 6, LanguageID.Japanese or LanguageID.Korean => generation >= 6 ? 6 : 5, _ => generation >= 6 ? 12 : 10, }; /// /// Checks if the input has IVs that match the template . /// public static bool GetIsFixedIVSequenceValidSkipRand(ReadOnlySpan IVs, PKM pk, uint max = 31) { for (int i = 5; i >= 0; i--) { var iv = IVs[i]; if ((uint)iv > max) // random continue; if (iv != pk.GetIV(i)) return false; } return true; } /// /// Checks if the input has IVs that match the template . /// public static bool GetIsFixedIVSequenceValidSkipRand(IndividualValueSet IVs, PKM pk, int max = 31) { // Template IVs not in the [0,max] range are random. Only check for IVs within the "specified" range. if ((uint)IVs.HP <= max && IVs.HP != pk.IV_HP ) return false; if ((uint)IVs.ATK <= max && IVs.ATK != pk.IV_ATK) return false; if ((uint)IVs.DEF <= max && IVs.DEF != pk.IV_DEF) return false; if ((uint)IVs.SPE <= max && IVs.SPE != pk.IV_SPE) return false; if ((uint)IVs.SPA <= max && IVs.SPA != pk.IV_SPA) return false; if ((uint)IVs.SPD <= max && IVs.SPD != pk.IV_SPD) return false; return true; } /// /// Checks if the input has IVs that match the template . /// public static bool GetIsFixedIVSequenceValidNoRand(IndividualValueSet IVs, PKM pk) { if (IVs.HP != pk.IV_HP ) return false; if (IVs.ATK != pk.IV_ATK) return false; if (IVs.DEF != pk.IV_DEF) return false; if (IVs.SPE != pk.IV_SPE) return false; if (IVs.SPA != pk.IV_SPA) return false; if (IVs.SPD != pk.IV_SPD) return false; return true; } }