using System; using System.Linq; namespace PKHeX.Core { /// /// Contains extension logic for modifying data. /// public static class CommonEdits { /// /// Setting which enables/disables automatic manipulation of when importing from a . /// public static bool ShowdownSetIVMarkings { get; set; } = true; /// /// Setting which causes the to the in Gen8+ formats. /// public static bool ShowdownSetBehaviorNature { get; set; } /// /// Sets the to the provided value. /// /// Pokémon to modify. /// to set. If no nickname is provided, the is set to the default value for its current language and format. public static void SetNickname(this PKM pk, string nick) { if (string.IsNullOrWhiteSpace(nick)) { pk.ClearNickname(); return; } pk.IsNicknamed = true; pk.Nickname = nick; } /// /// Clears the to the default value. /// /// public static string ClearNickname(this PKM pk) { pk.IsNicknamed = false; string nick = SpeciesName.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format); pk.Nickname = nick; if (pk is GBPKM pk12) pk12.SetNotNicknamed(); return nick; } /// /// Sets the value, with special consideration for values which derive the value. /// /// Pokémon to modify. /// Desired value to set. public static void SetAltForm(this PKM pk, int form) { switch (pk.Format) { case 2: while (pk.AltForm != form) pk.SetRandomIVs(); break; case 3: pk.SetPIDUnown3(form); break; default: pk.AltForm = form; break; } } /// /// Sets the value by sanity checking the provided against the possible pool of abilities. /// /// Pokémon to modify. /// Desired to set. public static void SetAbility(this PKM pk, int abil) { if (abil < 0) return; var abilities = pk.PersonalInfo.Abilities; int abilIndex = Array.IndexOf(abilities, abil); abilIndex = Math.Max(0, abilIndex); pk.SetAbilityIndex(abilIndex); } /// /// Sets the value based on the provided ability index (0-2) /// /// Pokémon to modify. /// Desired (shifted by 1) to set. public static void SetAbilityIndex(this PKM pk, int abilIndex) { if (pk is PK5 pk5 && abilIndex == 2) pk5.HiddenAbility = true; else if (pk.Format <= 5) pk.PID = PKX.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.AltForm, (uint)(abilIndex * 0x10001)); pk.RefreshAbility(abilIndex); } /// /// Sets a Random value. The is not updated if the value should match the instead. /// /// Accounts for Wurmple evolutions. /// Pokémon to modify. public static void SetRandomEC(this PKM pk) { int gen = pk.GenNumber; if (2 < gen && gen < 6) { pk.EncryptionConstant = pk.PID; return; } int wIndex = WurmpleUtil.GetWurmpleEvoGroup(pk.Species); if (wIndex != -1) { pk.EncryptionConstant = WurmpleUtil.GetWurmpleEC(wIndex); return; } pk.EncryptionConstant = Util.Rand32(); } /// /// Sets the derived value. /// /// Pokémon to modify. /// Desired state to set. /// public static bool SetIsShiny(this PKM pk, bool shiny) => shiny ? SetShiny(pk) : pk.SetUnshiny(); /// /// Makes a shiny. /// /// Pokémon to modify. /// Square shiny /// Returns true if the data was modified. public static bool SetShiny(PKM pk, bool xor0 = false) { if (pk.IsShiny) return false; while (true) { pk.SetShiny(); if (pk.Format <= 7) return true; var xor = pk.ShinyXor; if (xor0 ? xor == 0 : xor != 0) return true; } } /// /// Makes a not-shiny. /// /// Pokémon to modify. /// Returns true if the data was modified. public static bool SetUnshiny(this PKM pk) { if (!pk.IsShiny) return false; pk.SetPIDGender(pk.Gender); return true; } /// /// Sets the value, with special consideration for the values which derive the value. /// /// Pokémon to modify. /// Desired value to set. public static void SetNature(this PKM pk, int nature) { var value = Math.Min((int)Nature.Quirky, Math.Max((int)Nature.Hardy, nature)); if (pk.Format >= 8) pk.StatNature = value; else if (pk.Format <= 4) pk.SetPIDNature(value); else pk.Nature = value; } /// /// Copies details to the . /// /// Pokémon to modify. /// details to copy from. public static void ApplySetDetails(this PKM pk, IBattleTemplate Set) { pk.Species = Math.Min(pk.MaxSpeciesID, Set.Species); pk.SetMoves(Set.Moves, true); pk.ApplyHeldItem(Set.HeldItem, Set.Format); pk.CurrentLevel = Set.Level; pk.CurrentFriendship = Set.Friendship; pk.IVs = Set.IVs; pk.EVs = Set.EVs; pk.SetSuggestedHyperTrainingData(Set.IVs); if (ShowdownSetIVMarkings) pk.SetMarkings(); pk.SetNickname(Set.Nickname); pk.SetAltForm(Set.FormIndex); pk.SetGender(Set.Gender); pk.SetMaximumPPUps(Set.Moves); pk.SetAbility(Set.Ability); pk.SetNature(Set.Nature); pk.SetIsShiny(Set.Shiny); pk.SetRandomEC(); if (pk is IAwakened a) { a.SetSuggestedAwakenedValues(pk); if (pk is PB7 b) { for (int i = 0; i < 6; i++) pk.SetEV(i, 0); b.ResetCalculatedValues(); } } if (pk is IGigantamax c) c.CanGigantamax = Set.CanGigantamax; pk.ClearRecordFlags(); pk.SetRecordFlags(Set.Moves); if (ShowdownSetBehaviorNature && pk.Format >= 8) pk.Nature = pk.StatNature; var legal = new LegalityAnalysis(pk); if (legal.Parsed && legal.Info.Relearn.Any(z => !z.Valid)) pk.SetRelearnMoves(legal.GetSuggestedRelearnMoves()); pk.ResetPartyStats(); pk.RefreshChecksum(); } /// /// Sets the value depending on the current format and the provided item index & format. /// /// Pokémon to modify. /// Held Item to apply /// Format required for importing public static void ApplyHeldItem(this PKM pk, int item, int format) { item = ItemConverter.GetFormatHeldItemID(item, format, pk.Format); pk.HeldItem = ((uint)item > pk.MaxItemID) ? 0 : item; } /// /// Sets one of the based on its index within the array. /// /// Pokémon to modify. /// Index to set to /// Value to set public static void SetEV(this PKM pk, int index, int value) { switch (index) { case 0: pk.EV_HP = value; break; case 1: pk.EV_ATK = value; break; case 2: pk.EV_DEF = value; break; case 3: pk.EV_SPE = value; break; case 4: pk.EV_SPA = value; break; case 5: pk.EV_SPD = value; break; default: throw new ArgumentOutOfRangeException(nameof(index)); } } /// /// Sets one of the based on its index within the array. /// /// Pokémon to modify. /// Index to set to /// Value to set public static void SetIV(this PKM pk, int index, int value) { switch (index) { case 0: pk.IV_HP = value; break; case 1: pk.IV_ATK = value; break; case 2: pk.IV_DEF = value; break; case 3: pk.IV_SPE = value; break; case 4: pk.IV_SPA = value; break; case 5: pk.IV_SPD = value; break; default: throw new ArgumentOutOfRangeException(nameof(index)); } } /// /// Fetches the highest value the provided index can be while considering others. /// /// Pokémon to modify. /// Index to fetch for /// Highest value the value can be. public static int GetMaximumEV(this PKM pk, int index) { if (pk.Format < 3) return ushort.MaxValue; var EVs = pk.EVs; EVs[index] = 0; var sum = EVs.Sum(); int remaining = 510 - sum; return Math.Min(Math.Max(remaining, 0), 252); } /// /// Fetches the highest value the provided . /// /// Pokémon to modify. /// Index to fetch for /// Causes the returned value to be dropped down -1 if the value is already at a maxmimum. /// Highest value the value can be. public static int GetMaximumIV(this PKM pk, int index, bool allow30 = false) { if (pk.GetIV(index) == pk.MaxIV && allow30) return pk.MaxIV - 1; return pk.MaxIV; } /// /// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game. /// /// PKM to apply hatch details to /// Re-hatch already hatched inputs public static void ForceHatchPKM(this PKM pk, bool reHatch = false) { if (!pk.IsEgg && !reHatch) return; pk.IsEgg = false; pk.ClearNickname(); pk.CurrentFriendship = pk.PersonalInfo.BaseFriendship; if (pk.IsTradedEgg) pk.Egg_Location = pk.Met_Location; var loc = EncounterSuggestion.GetSuggestedEggMetLocation(pk); if (loc >= 0) pk.Met_Location = loc; pk.MetDate = DateTime.Today; if (pk.Gen6) pk.SetHatchMemory6(); } /// /// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game. /// /// PKM to apply hatch details to /// Game the egg originated from /// Game the egg is currently present on public static void SetEggMetData(this PKM pk, GameVersion origin, GameVersion dest) { bool traded = origin == dest; var today = pk.MetDate = DateTime.Today; pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk, traded); pk.EggMetDate = today; } /// /// Maximizes the . If the , the hatch counter is set to 1. /// /// PKM to apply hatch details to public static void MaximizeFriendship(this PKM pk) { if (pk.IsEgg) pk.OT_Friendship = 1; else pk.CurrentFriendship = byte.MaxValue; if (pk is PB7 pb) pb.ResetCP(); } /// /// Maximizes the . If the , the is ignored. /// /// PKM to apply hatch details to public static void MaximizeLevel(this PKM pk) { if (pk.IsEgg) return; pk.CurrentLevel = 100; if (pk is PB7 pb) pb.ResetCP(); } /// /// Sets the to its default value. /// /// Pokémon to modify. /// Precomputed optional public static void SetDefaultNickname(this PKM pk, LegalityAnalysis la) { if (la.Parsed && la.EncounterOriginal is EncounterTrade t && t.HasNickname) pk.SetNickname(t.GetNickname(pk.Language)); else pk.ClearNickname(); } /// /// Sets the to its default value. /// /// Pokémon to modify. public static void SetDefaultNickname(this PKM pk) => pk.SetDefaultNickname(new LegalityAnalysis(pk)); private static readonly string[] PotentialUnicode = { "★☆☆☆", "★★☆☆", "★★★☆", "★★★★" }; private static readonly string[] PotentialNoUnicode = { "+", "++", "+++", "++++" }; /// /// Gets the Potential evaluation of the input . /// /// Pokémon to analyze. /// Returned value is unicode or not /// Potential string public static string GetPotentialString(this PKM pk, bool unicode = true) { var arr = unicode ? PotentialUnicode : PotentialNoUnicode; return arr[pk.PotentialRating]; } // Extensions /// /// Gets the Location Name for the /// /// PKM to fetch data for /// Location requested is the egg obtained location, not met location. /// Location string public static string GetLocationString(this PKM pk, bool eggmet) { if (pk.Format < 2) return string.Empty; int locval = eggmet ? pk.Egg_Location : pk.Met_Location; return GameInfo.GetLocationName(eggmet, locval, pk.Format, pk.GenNumber, (GameVersion)pk.Version); } } }