using System.Collections.Generic; using static PKHeX.Core.Species; namespace PKHeX.Core { /// /// Contains logic for Alternate Form information. /// public static class FormInfo { /// /// Checks if the form cannot exist outside of a Battle. /// /// Entity species /// Entity form /// Current generation format /// True if it can only exist in a battle, false if it can exist outside of battle. public static bool IsBattleOnlyForm(int species, int form, int format) { if (!BattleOnly.Contains(species)) return false; // Some species have battle only forms as well as out-of-battle forms (other than base form). switch (species) { case (int)Slowbro when form == 2 && format >= 8: // Only mark Ultra Necrozma as Battle Only case (int)Darmanitan when form == 2 && format >= 8: // this one is OK, Galarian Slowbro (not a Mega) case (int)Zygarde when form < 4: // Zygarde Complete case (int)Mimikyu when form == 2: // Totem disguise Mimikyu case (int)Necrozma when form < 3: // this one is OK, Galarian non-Zen return false; case (int)Minior: return form < 7; // Minior Shields-Down default: return form != 0; } } /// /// Reverts the Battle Form to the form it would have outside of Battle. /// /// Only call this if you've already checked that returns true. /// Entity species /// Entity form /// Current generation format /// Suggested alt form value. public static int GetOutOfBattleForm(int species, int form, int format) => species switch { (int)Darmanitan => form & 2, (int)Zygarde when format > 6 => 3, (int)Minior => form + 7, _ => 0 }; /// /// Checks if the is a fused form, which indicates it cannot be traded away. /// /// Entity species /// Entity form /// Current generation format /// True if it is a fused species-form, false if it is not fused. public static bool IsFusedForm(int species, int form, int format) => species switch { (int)Kyurem when form != 0 && format >= 5 => true, (int)Necrozma when form != 0 && format >= 7 => true, (int)Calyrex when form != 0 && format >= 8 => true, _ => false }; /// Checks if the form may be different than the original encounter detail. /// Original species /// Original form /// Current form /// Current format public static bool IsFormChangeable(int species, int oldForm, int newForm, int format) { if (FormChange.Contains(species)) return true; // Zygarde Form Changing // Gen6: Introduced; no form changing. // Gen7: Form changing introduced; can only change to Form 2/3 (Power Construct), never to 0/1 (Aura Break). A form-1 can be boosted to form-0. // Gen8: Form changing improved; can pick any Form & Ability combination. if (species == (int)Zygarde) { return format switch { 6 => false, 7 => newForm >= 2 || (oldForm == 1 && newForm == 0), _ => true, }; } return false; } /// /// Species that can be captured normally in the wild and can change between their forms. /// public static readonly HashSet WildChangeFormAfter = new() { 412, // Burmy 479, // Rotom 676, // Furfrou 741, // Oricorio }; /// /// Species that can change between their forms, regardless of origin. /// /// Excludes Zygarde as it has special conditions. Check separately. private static readonly HashSet FormChange = new(WildChangeFormAfter) { 386, // Deoxys 487, // Giratina 492, // Shaymin 493, // Arceus 641, // Tornadus 642, // Thundurus 645, // Landorus 646, // Kyurem 647, // Keldeo 649, // Genesect 720, // Hoopa 773, // Silvally 800, // Necrozma 898, // Calyrex }; /// /// Species that have an alternate form that cannot exist outside of battle. /// private static readonly HashSet BattleForms = new() { (int)Castform, (int)Cherrim, (int)Darmanitan, (int)Meloetta, (int)Aegislash, (int)Xerneas, (int)Zygarde, (int)Wishiwashi, (int)Minior, (int)Mimikyu, (int)Cramorant, (int)Morpeko, (int)Eiscue, (int)Zacian, (int)Zamazenta, (int)Eternatus, }; /// /// Species that have a mega form that cannot exist outside of battle. /// /// Using a held item to change form during battle, via an in-battle transformation feature. private static readonly HashSet BattleMegas = new() { // XY (int)Venusaur, (int)Charizard, (int)Blastoise, (int)Alakazam, (int)Gengar, (int)Kangaskhan, (int)Pinsir, (int)Gyarados, (int)Aerodactyl, (int)Mewtwo, (int)Ampharos, (int)Scizor, (int)Heracross, (int)Houndoom, (int)Tyranitar, (int)Blaziken, (int)Gardevoir, (int)Mawile, (int)Aggron, (int)Medicham, (int)Manectric, (int)Banette, (int)Absol, (int)Latios, (int)Latias, (int)Garchomp, (int)Lucario, (int)Abomasnow, // AO (int)Beedrill, (int)Pidgeot, (int)Slowbro, (int)Steelix, (int)Sceptile, (int)Swampert, (int)Sableye, (int)Sharpedo, (int)Camerupt, (int)Altaria, (int)Glalie, (int)Salamence, (int)Metagross, (int)Rayquaza, (int)Lopunny, (int)Gallade, (int)Audino, (int)Diancie, // USUM (int)Necrozma, // Ultra Necrozma }; /// /// Species that have a primal form that cannot exist outside of battle. /// private static readonly HashSet BattlePrimals = new() { (int)Kyogre, (int)Groudon }; private static readonly HashSet BattleOnly = GetBattleFormSet(); private static HashSet GetBattleFormSet() { var hs = new HashSet(BattleForms); hs.UnionWith(BattleMegas); hs.UnionWith(BattlePrimals); return hs; } /// /// Checks if the for the is a Totem form. /// /// Entity species /// Entity form /// Current generation format public static bool IsTotemForm(int species, int form, int format) => format == 7 && IsTotemForm(species, form); /// /// Checks if the for the is a Totem form. /// /// Use if you aren't 100% sure the format is 7. /// Entity species /// Entity form public static bool IsTotemForm(int species, int form) { if (form == 0) return false; if (!Legal.Totem_USUM.Contains(species)) return false; if (species == (int)Mimikyu) return form is 2 or 3; if (Legal.Totem_Alolan.Contains(species)) return form == 2; return form == 1; } /// /// Gets the base for the when the Totem form is reverted (on transfer). /// /// Entity species /// Entity form public static int GetTotemBaseForm(int species, int form) { if (species == (int)Mimikyu) return form - 2; return form - 1; } /// /// Checks if the exists for the without having an associated index. /// /// Entity species /// Entity form /// Current generation format /// public static bool IsValidOutOfBoundsForm(int species, int form, int format) => (Species) species switch { Unown => form < (format == 2 ? 26 : 28), // A-Z : A-Z?! Mothim => form < 3, // Burmy base form is kept Scatterbug => form <= Vivillon3DS.MaxWildFormID, // Vivillon Pre-evolutions Spewpa => form <= Vivillon3DS.MaxWildFormID, // Vivillon Pre-evolutions _ => false }; /// /// Checks if the data should have a drop-down selection visible for the value. /// /// Game specific personal info /// ID /// ID /// True if has forms that can be provided by , otherwise false for none. public static bool HasFormSelection(PersonalInfo pi, int species, int format) { if (format <= 3 && species != (int)Unown) return false; if (HasFormValuesNotIndicatedByPersonal.Contains(species)) return true; int count = pi.FormCount; return count > 1; } /// /// /// private static readonly HashSet HasFormValuesNotIndicatedByPersonal = new() { (int)Unown, (int)Mothim, // (Burmy form is not cleared on evolution) (int)Scatterbug, (int)Spewpa, // Vivillon pre-evos }; } }