using System; using System.Linq; namespace PKHeX.Core { /// /// Contains extension logic for modifying data. /// public static class CommonEdits { public static bool ShowdownSetIVMarkings { get; set; } = true; /// /// 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 = null) { if (nick != null) { pk.IsNicknamed = true; pk.Nickname = nick; } else { pk.IsNicknamed = false; pk.Nickname = PKX.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format); if (pk is PK1 pk1) pk1.SetNotNicknamed(); if (pk is PK2 pk2) pk2.SetNotNicknamed(); } } /// /// 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) { var abilities = pk.PersonalInfo.Abilities; int abilIndex = Array.IndexOf(abilities, abil); abilIndex = Math.Max(0, abilIndex); if (pk is PK5 pk5 && abilIndex == 2) pk5.HiddenAbility = true; else if (pk.Format <= 5) pk.PID = PKX.GetRandomPID(pk.Species, pk.Gender, pk.Version, pk.Nature, pk.Format, (uint)(abilIndex * 0x10001)); pk.RefreshAbility(abilIndex); } /// /// Sets a Random value. The is not updated if the value should match the instead. /// /// Pokémon to modify. public static void SetRandomEC(this PKM pk) { int gen = pk.GenNumber; if (gen < 6 && gen > 2) pk.EncryptionConstant = pk.PID; else 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 ? pk.SetShiny() : pk.SetUnshiny(); /// /// Makes a shiny. /// /// Pokémon to modify. /// Returns true if the data was modified. public static bool SetShiny(this PKM pk) { if (pk.IsShiny) return false; if (pk.Format > 2) pk.SetShinyPID(); else pk.SetShinyIVs(); 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) { if (pk.Format <= 4) pk.SetPIDNature(Math.Max(0, nature)); else pk.Nature = Math.Max(0, nature); } /// /// 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, Nature nature) => pk.SetNature((int) nature); /// /// Sets the individual PP Up count values depending if a Move is present in the moveslot or not. /// /// Pokémon to modify. /// to use (if already known). Will fetch the current if not provided. public static void SetMaximumPPUps(this PKM pk, int[] Moves = null) { if (Moves == null) Moves = pk.Moves; pk.Move1_PPUps = GetPPUpCount(Moves[0]); pk.Move2_PPUps = GetPPUpCount(Moves[1]); pk.Move3_PPUps = GetPPUpCount(Moves[2]); pk.Move4_PPUps = GetPPUpCount(Moves[3]); pk.SetMaximumPPCurrent(Moves); int GetPPUpCount(int moveID) => moveID > 0 ? 3 : 0; } /// /// Updates the and updates the current PP counts. /// /// Pokémon to modify. /// to set. Will be resized if 4 entries are not present. /// Option to maximize PP Ups public static void SetMoves(this PKM pk, int[] Moves, bool maxPP = false) { if (Moves.Length != 4) Array.Resize(ref Moves, 4); pk.Moves = Moves; if (maxPP) pk.SetMaximumPPUps(Moves); else pk.SetMaximumPPCurrent(Moves); pk.FixMoves(); } /// /// Updates the individual PP count values for each moveslot based on the maximum possible value. /// /// Pokémon to modify. /// to use (if already known). Will fetch the current if not provided. public static void SetMaximumPPCurrent(this PKM pk, int[] Moves = null) { if (Moves == null) Moves = pk.Moves; pk.Move1_PP = Moves.Length <= 0 ? 0 : pk.GetMovePP(Moves[0], pk.Move1_PPUps); pk.Move2_PP = Moves.Length <= 1 ? 0 : pk.GetMovePP(Moves[1], pk.Move2_PPUps); pk.Move3_PP = Moves.Length <= 2 ? 0 : pk.GetMovePP(Moves[2], pk.Move3_PPUps); pk.Move4_PP = Moves.Length <= 3 ? 0 : pk.GetMovePP(Moves[3], pk.Move4_PPUps); } /// /// Sets the value, with special consideration for the values which derive the value. /// /// Pokémon to modify. /// Desired value to set. public static void SetGender(this PKM pk, string gender) { if (gender == null) { int cg = pk.Gender; int sane = pk.GetSaneGender(cg); if (cg != sane) pk.Gender = sane; return; } int Gender = PKX.GetGenderFromString(gender); pk.SetGender(Gender); } /// /// Sets the value, with special consideration for the values which derive the value. /// /// Pokémon to modify. /// Desired value to set. public static void SetGender(this PKM pk, int gender) { gender = Math.Min(2, Math.Max(0, gender)); if (pk.Format <= 2) pk.SetATKIVGender(gender); else if (pk.Format <= 4) pk.SetPIDGender(gender); else pk.Gender = gender; } /// /// Sets to valid values which may best enhance the stats. /// /// /// to use (if already known). Will fetch the current if not provided. public static void SetSuggestedHyperTrainingData(this PKM pkm, int[] IVs = null) { if (pkm.Format < 7) return; if (pkm.CurrentLevel < 100) { pkm.HyperTrainFlags = 0; return; } if (IVs == null) IVs = pkm.IVs; pkm.HT_HP = IVs[0] != 31; pkm.HT_ATK = IVs[1] != 31 && IVs[1] > 2; pkm.HT_DEF = IVs[2] != 31; pkm.HT_SPE = IVs[3] != 31 && IVs[3] > 2; pkm.HT_SPA = IVs[4] != 31; pkm.HT_SPD = IVs[5] != 31; } /// /// Fetches based on the provided . /// /// Pokémon to modify. /// which contains parsed information pertaining to legality. /// best suited for the current data. public static int[] GetSuggestedRelearnMoves(this PKM pk, LegalityAnalysis legal) { int[] m = legal.GetSuggestedRelearn(); if (!m.All(z => z == 0)) return m; if (pk.WasEgg || pk.WasEvent || pk.WasEventEgg || pk.WasLink) return m; var encounter = legal.GetSuggestedMetInfo(); if (encounter != null) m = encounter.Relearn; return m; } /// /// Sanity checks the provided value, and returns a sane value. /// /// /// Current preference /// Most-legal value public static int GetSaneGender(this PKM pkm, int cg) { int gt = pkm.PersonalInfo.Gender; switch (gt) { case 255: return 2; // Genderless case 254: return 1; // Female-Only case 0: return 0; // Male-Only } if (cg == 2 || pkm.GenNumber < 6) return (byte)pkm.PID <= gt ? 1 : 0; return cg; } /// /// Copies details to the . /// /// Pokémon to modify. /// details to copy from. public static void ApplySetDetails(this PKM pk, ShowdownSet Set) { pk.Species = Set.Species; pk.Moves = Set.Moves; pk.HeldItem = Set.HeldItem < 0 ? 0 : Set.HeldItem; 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.SetGender(Set.Gender); pk.SetAltForm(Set.FormIndex); pk.SetMaximumPPUps(Set.Moves); pk.SetAbility(Set.Ability); pk.SetNature(Set.Nature); pk.SetIsShiny(Set.Shiny); pk.SetRandomEC(); var legal = new LegalityAnalysis(pk); if (legal.Info.Relearn.Any(z => !z.Valid)) pk.RelearnMoves = pk.GetSuggestedRelearnMoves(legal); } /// /// Sets all Memory related data to the default value (zero). /// /// Pokémon to modify. public static void ClearMemories(this PKM pk) { pk.OT_Memory = pk.OT_Affection = pk.OT_Feeling = pk.OT_Intensity = pk.OT_TextVar = pk.HT_Memory = pk.HT_Affection = pk.HT_Feeling = pk.HT_Intensity = pk.HT_TextVar = 0; } /// /// Sets the to indicate flawless (or near-flawless) . /// /// Pokémon to modify. /// to use (if already known). Will fetch the current if not provided. public static void SetMarkings(this PKM pk, int[] IVs = null) { if (pk.Format <= 3) return; // no markings (gen3 only has 4; can't mark stats intelligently if (IVs == null) IVs = pk.IVs; pk.Markings = IVs.Select(MarkingMethod(pk)).ToArray(); } public static Func> MarkingMethod { get; set; } = FlagHighLow; private static Func FlagHighLow(PKM pk) { if (pk.Format < 7) return GetSimpleMarking; return GetComplexMarking; int GetSimpleMarking(int val, int index) => val == 31 ? 1 : 0; int GetComplexMarking(int val, int index) { if (val == 31 || val == 1) return 1; if (val == 30 || val == 0) return 2; return 0; } } /// /// 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)); } } /// /// Updates the for a Generation 1/2 format . /// /// Pokémon to modify. /// Desired . public static void SetATKIVGender(this PKM pk, int gender) { do { pk.IV_ATK = (int)(Util.Rand32() & pk.MaxIV); } while (pk.Gender != gender); } /// /// 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; var newEV = Math.Min(Math.Max(remaining, 0), 252); return newEV; } /// /// 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.IVs[index] == pk.MaxIV && Allow30) return pk.MaxIV - 1; return pk.MaxIV; } /// /// Sets the to match a provided . /// /// Pokémon to modify. /// Desired Hidden Power typing. public static void SetHiddenPower(this PKM pk, int hptype) { var IVs = pk.IVs; HiddenPower.SetIVsForType(hptype, pk.IVs); pk.IVs = IVs; } /// /// Sets the to match a provided . /// /// Pokémon to modify. /// Desired Hidden Power typing. public static void SetHiddenPower(this PKM pk, MoveType hptype) => pk.SetHiddenPower((int) hptype); /// /// Sets the Memory details to a Hatched Egg's memories. /// /// Pokémon to modify. public static void SetHatchMemory6(this PKM pk) { pk.OT_Memory = 2; pk.OT_Affection = 0; pk.OT_Feeling = Util.Rand.Next(0, 10); pk.OT_Intensity = 1; pk.OT_TextVar = pk.XY ? 43 : 27; // riverside road : battling spot } public static void SetRandomMemory6(this PKM pk) { // for lack of better randomization :) pk.OT_Memory = 63; pk.OT_Intensity = 6; pk.OT_Feeling = 3; } } }