diff --git a/PKHeX.Core/Legality/Encounters/EncounterStatic.cs b/PKHeX.Core/Legality/Encounters/EncounterStatic.cs index ca8ee2aad..928239c2d 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterStatic.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterStatic.cs @@ -26,10 +26,10 @@ public class EncounterStatic : IEncounterable, IMoveset, IGeneration, ILocation public bool Gift { get; set; } public int Ball { get; set; } = 4; // Only checked when is Gift public GameVersion Version = GameVersion.Any; - public int[] IVs { get; set; } = { -1, -1, -1, -1, -1, -1 }; + public int[] IVs { get; set; } public int FlawlessIVCount { get; internal set; } public bool IV3 { set => FlawlessIVCount = value ? 3 : 0; } - public int[] Contest { get; set; } = { 0, 0, 0, 0, 0, 0 }; + public int[] Contest { get; set; } public int HeldItem { get; set; } public int EggCycles { get; set; } @@ -44,8 +44,8 @@ private void CloneArrays() // dereference original arrays with new copies Moves = (int[])Moves?.Clone(); Relearn = (int[])Relearn.Clone(); - IVs = (int[])IVs.Clone(); - Contest = (int[])Contest.Clone(); + IVs = (int[])IVs?.Clone(); + Contest = (int[])Contest?.Clone(); } internal virtual EncounterStatic Clone() { diff --git a/PKHeX.Core/Legality/Encounters/EncounterTrade.cs b/PKHeX.Core/Legality/Encounters/EncounterTrade.cs index 86fb83dd9..b103c270f 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterTrade.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterTrade.cs @@ -21,8 +21,8 @@ public class EncounterTrade : IEncounterable, IMoveset, IGeneration, ILocation public int TID { get; set; } public int SID { get; set; } public GameVersion Version { get; set; } = GameVersion.Any; - public int[] IVs { get; set; } = { -1, -1, -1, -1, -1, -1 }; - public int[] Contest { get; set; } = { 0, 0, 0, 0, 0, 0 }; + public int[] IVs { get; set; } + public int[] Contest { get; set; } public int Form { get; set; } public bool Shiny { get; set; } = false; public int Gender { get; set; } = -1; diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator.cs index 01f56a3d4..6873622cc 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterGenerator.cs @@ -513,11 +513,16 @@ private static bool GetIsMatchStatic(PKM pkm, EncounterStatic e, int lvl) if (e.EggLocation == 60002 && e.Relearn[0] == 0 && pkm.RelearnMoves.Any(z => z != 0)) // gen7 eevee edge case return false; - if (e.Generation > 2 || pkm.Format <= 2) // 1,2->7 regenerates IVs, only check if original IVs still exist + if (e.IVs != null && (e.Generation > 2 || pkm.Format <= 2)) // 1,2->7 regenerates IVs, only check if original IVs still exist for (int i = 0; i < 6; i++) if (e.IVs[i] != -1 && e.IVs[i] != pkm.IVs[i]) return false; + if (e.Contest != null) + for (int i = 0; i < 6; i++) + if (e.Contest[i] > pkm.Contest[i]) + return false; + // Defer to EC/PID check // if (e.Shiny != null && e.Shiny != pkm.IsShiny) // continue; @@ -907,7 +912,7 @@ private static IEnumerable GetValidEncounterTradesVC2(PKM pkm, D continue; if (z.Gender >= 0 && z.Gender != pkm.Gender && pkm.Format <= 2) continue; - if (z.IVs[0] >= 0 && !z.IVs.SequenceEqual(pkm.IVs) && pkm.Format <= 2) + if (z.IVs != null && !z.IVs.SequenceEqual(pkm.IVs) && pkm.Format <= 2) continue; if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126) continue; @@ -995,6 +1000,7 @@ private static IEnumerable GetValidEncounterTrades(PKM pkm, Game } private static bool IsEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl) { + if (z.IVs != null) for (int i = 0; i < 6; i++) if (z.IVs[i] != -1 && z.IVs[i] != pkm.IVs[i]) return false; @@ -1047,6 +1053,11 @@ private static bool IsEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl) // if (z.Ability == 4 ^ pkm.AbilityNumber == 4) // defer to Ability // countinue; + if (z.Contest != null) + for (int i = 0; i < 6; i++) + if (z.Contest[i] > pkm.Contest[i]) + return false; + return true; } diff --git a/PKHeX.Core/PKM/Editing/CommonEdits.cs b/PKHeX.Core/PKM/Editing/CommonEdits.cs new file mode 100644 index 000000000..5e4788804 --- /dev/null +++ b/PKHeX.Core/PKM/Editing/CommonEdits.cs @@ -0,0 +1,432 @@ +using System; +using System.Linq; + +namespace PKHeX.Core +{ + /// + /// Contains extension logic for modifying data. + /// + public static class CommonEdits + { + /// + /// 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 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 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 SetPPUps(this PKM pk, int[] Moves = null) + { + if (Moves == null) + Moves = pk.Moves; + + pk.Move1_PP = pk.GetMovePP(Moves[0], pk.Move1_PPUps = GetPPUpCount(Moves[0])); + pk.Move2_PP = pk.GetMovePP(Moves[1], pk.Move2_PPUps = GetPPUpCount(Moves[1])); + pk.Move3_PP = pk.GetMovePP(Moves[2], pk.Move3_PPUps = GetPPUpCount(Moves[2])); + pk.Move4_PP = pk.GetMovePP(Moves[3], pk.Move4_PPUps = GetPPUpCount(Moves[3])); + + int GetPPUpCount(int moveID) => moveID > 0 ? 3 : 0; + } + + /// + /// 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) + 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.ApplyMarkings(); + pk.SetSuggestedHyperTrainingData(Set.IVs); + + pk.SetNickname(Set.Nickname); + pk.SetGender(Set.Gender); + pk.SetAltForm(Set.FormIndex); + pk.SetPPUps(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 ApplyMarkings(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; + + var remapper = pk.Format < 7 ? (Func)GetSimpleMarking : GetComplexMarking; + pk.Markings = IVs.Select(remapper).ToArray(); + + int GetSimpleMarking(int val) => val == 31 ? 1 : 0; + int GetComplexMarking(int val) + { + 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); + } +} diff --git a/PKHeX.Core/PKM/Editing/HiddenPower.cs b/PKHeX.Core/PKM/Editing/HiddenPower.cs new file mode 100644 index 000000000..db28ad096 --- /dev/null +++ b/PKHeX.Core/PKM/Editing/HiddenPower.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.Linq; + +namespace PKHeX.Core +{ + public static class HiddenPower + { + public static int GetType(IReadOnlyList IVs) + { + int hp = 0; + for (int i = 0; i < 6; i++) + hp |= (IVs[i] & 1) << i; + hp *= 0xF; + hp /= 0x3F; + return hp; + } + public static bool SetIVsForType(int hpVal, int[] IVs) + { + if (IVs.All(z => z == 31)) + { + PKX.SetHPIVs(hpVal, IVs); // Get IVs + return true; + } + + int current = GetType(IVs); + if (current == hpVal) + return true; // no mods necessary + + // Required HP type doesn't match IVs. Make currently-flawless IVs flawed. + int[] best = GetSuggestedHiddenPowerIVs(hpVal, IVs); + if (best == null) + return false; // can't force hidden power? + + // set IVs back to array + for (int i = 0; i < IVs.Length; i++) + IVs[i] = best[i]; + return true; + } + private static int[] GetSuggestedHiddenPowerIVs(int hpVal, int[] IVs) + { + var flawless = IVs.Select((v, i) => v == 31 ? i : -1).Where(v => v != -1).ToArray(); + var permutations = GetPermutations(flawless, flawless.Length); + int flawedCount = 0; + int[] best = null; + foreach (var permute in permutations) + { + var ivs = (int[])IVs.Clone(); + foreach (var item in permute) + { + ivs[item] ^= 1; + if (hpVal != GetType(ivs)) + continue; + + int ct = ivs.Count(z => z == 31); + if (ct <= flawedCount) + break; // any further flaws are always worse + + flawedCount = ct; + best = ivs; + break; // any further flaws are always worse + } + } + return best; + } + private static IEnumerable> GetPermutations(IList list, int length) + { + // https://stackoverflow.com/a/10630026 + if (length == 1) + return list.Select(t => new[] { t }); + + return GetPermutations(list, length - 1) + .SelectMany(t => list.Where(e => !t.Contains(e)), + (t1, t2) => t1.Concat(new[] { t2 })); + } + } +} diff --git a/PKHeX.Core/PKM/PKM.cs b/PKHeX.Core/PKM/PKM.cs index bc6cda585..513a4d99d 100644 --- a/PKHeX.Core/PKM/PKM.cs +++ b/PKHeX.Core/PKM/PKM.cs @@ -400,6 +400,16 @@ public int[] RelearnMoves if (value.Length > 3) RelearnMove4 = value[3]; } } + public int[] Contest + { + get => new[] { CNT_Cool, CNT_Beauty, CNT_Cute, CNT_Smart, CNT_Tough, CNT_Sheen }; + set + { + if (value?.Length != 6) return; + CNT_Cool = value[0]; CNT_Beauty = value[1]; CNT_Cute = value[2]; + CNT_Smart = value[3]; CNT_Tough = value[4]; CNT_Sheen = value[5]; + } + } public int PIDAbility { get diff --git a/PKHeX.Core/PKM/ShowdownSet.cs b/PKHeX.Core/PKM/ShowdownSet.cs index 428f0f650..fd129b17c 100644 --- a/PKHeX.Core/PKM/ShowdownSet.cs +++ b/PKHeX.Core/PKM/ShowdownSet.cs @@ -217,10 +217,7 @@ private IEnumerable GetStringMoves() { var str = $"- {moves[move]}"; if (move == 237) // Hidden Power - { - int hp = GetHiddenPowerType(IVs); - str += $" [{hptypes[hp]}]"; - } + str += $" [{hptypes[HiddenPower.GetType(IVs)]}]"; yield return str; } } @@ -354,7 +351,7 @@ private string ParseLineMove(string line) if (IVs.Any(z => z != 31)) { - if (!SetIVsForHiddenPower(hpVal, IVs)) + if (!HiddenPower.SetIVsForType(hpVal, IVs)) InvalidLines.Add($"Invalid IVs for Hidden Power Type: {type}"); } else if (hpVal >= 0) @@ -365,73 +362,6 @@ private string ParseLineMove(string line) moveString = "Hidden Power"; return moveString; } - private static int GetHiddenPowerType(IReadOnlyList IVs) - { - int hp = 0; - for (int i = 0; i < 6; i++) - hp |= (IVs[i] & 1) << i; - hp *= 0xF; - hp /= 0x3F; - return hp; - } - private static bool SetIVsForHiddenPower(int hpVal, int[] IVs) - { - if (IVs.All(z => z == 31)) - { - PKX.SetHPIVs(hpVal, IVs); // Get IVs - return true; - } - - int current = GetHiddenPowerType(IVs); - if (current == hpVal) - return true; // no mods necessary - - // Required HP type doesn't match IVs. Make currently-flawless IVs flawed. - int[] best = GetSuggestedHiddenPowerIVs(hpVal, IVs); - if (best == null) - return false; // can't force hidden power? - - // set IVs back to array - for (int i = 0; i < IVs.Length; i++) - IVs[i] = best[i]; - return true; - } - private static int[] GetSuggestedHiddenPowerIVs(int hpVal, int[] IVs) - { - var flawless = IVs.Select((v, i) => v == 31 ? i : -1).Where(v => v != -1).ToArray(); - var permutations = GetPermutations(flawless, flawless.Length); - int flawedCount = 0; - int[] best = null; - foreach (var permute in permutations) - { - var ivs = (int[])IVs.Clone(); - foreach (var item in permute) - { - ivs[item] ^= 1; - if (hpVal != GetHiddenPowerType(ivs)) - continue; - - int ct = ivs.Count(z => z == 31); - if (ct <= flawedCount) - break; // any further flaws are always worse - - flawedCount = ct; - best = ivs; - break; // any further flaws are always worse - } - } - return best; - } - private static IEnumerable> GetPermutations(IList list, int length) - { - // https://stackoverflow.com/a/10630026 - if (length == 1) - return list.Select(t => new[] { t }); - - return GetPermutations(list, length - 1) - .SelectMany(t => list.Where(e => !t.Contains(e)), - (t1, t2) => t1.Concat(new[] { t2 })); - } private void ParseLineEVs(string line) { string[] evlist = SplitLineStats(line); @@ -563,41 +493,5 @@ private static string ReplaceAll(string original, string to, params string[] toB { return toBeReplaced.Aggregate(original, (current, v) => current.Replace(v, to)); } - - public void ApplyToPKM(PKM pkm) - { - if (Species < 0) - return; - pkm.Species = Species; - - if (Nickname != null && Nickname.Length <= pkm.NickLength) - pkm.Nickname = Nickname; - else - pkm.Nickname = PKX.GetSpeciesName(pkm.Species, pkm.Language); - - int gender = PKX.GetGenderFromString(Gender); - pkm.Gender = Math.Max(0, gender); - - var list = PKX.GetFormList(pkm.Species, types, forms, genders); - int formnum = Array.IndexOf(list, Form); - pkm.AltForm = Math.Max(0, formnum); - - var abils = pkm.PersonalInfo.Abilities; - int index = Array.IndexOf(abils, Ability); - pkm.RefreshAbility(Math.Max(0, Math.Min(2, index))); - - if (Shiny && !pkm.IsShiny) - pkm.SetShinyPID(); - else if (!Shiny && pkm.IsShiny) - pkm.PID = Util.Rand32(); - - pkm.CurrentLevel = Level; - pkm.HeldItem = Math.Max(0, HeldItem); - pkm.CurrentFriendship = Friendship; - pkm.Nature = Nature; - pkm.EVs = EVs; - pkm.IVs = IVs; - pkm.Moves = Moves; - } } } diff --git a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs index 4ec451ba6..0a6c6090b 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs @@ -26,6 +26,16 @@ public PKMEditor() TB_OT.Font = (Font)TB_Nickname.Font.Clone(); TB_OTt2.Font = (Font)TB_Nickname.Font.Clone(); + // Commonly reused Control arrays + Moves = new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 }; + Relearn = new[] { CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 }; + PPUps = new[] { CB_PPu1, CB_PPu2, CB_PPu3, CB_PPu4 }; + MovePP = new[] { TB_PP1, TB_PP2, TB_PP3, TB_PP4 }; + ValidationRequired = Moves.Concat(Relearn).Concat(new[] + { + CB_Species, CB_Nature, CB_HeldItem, CB_Ability, // Main Tab + CB_MetLocation, CB_EggLocation, CB_Ball, // Met Tab + }).ToArray(); relearnPB = new[] { PB_WarnRelearn1, PB_WarnRelearn2, PB_WarnRelearn3, PB_WarnRelearn4 }; movePB = new[] { PB_WarnMove1, PB_WarnMove2, PB_WarnMove3, PB_WarnMove4 }; foreach (var c in WinFormsUtil.GetAllControlsOfType(this, typeof(ComboBox))) @@ -33,6 +43,7 @@ public PKMEditor() Stats.SetMainEditor(this); LoadShowdownSet = LoadShowdownSetDefault; + } private void UpdateStats() => Stats.UpdateStats(); @@ -75,6 +86,9 @@ private void SavePartyStats(PKM pk) public bool PKMIsUnsaved => FieldsInitialized && FieldsLoaded && LastData != null && LastData.Any(b => b != 0) && !LastData.SequenceEqual(CurrentPKM.Data); public bool IsEmptyOrEgg => CHK_IsEgg.Checked || CB_Species.SelectedIndex == 0; + private readonly ComboBox[] Moves, Relearn, ValidationRequired, PPUps; + private readonly MaskedTextBox[] MovePP; + private bool forceValidation; public PKM PreparePKM(bool click = true) { @@ -92,14 +106,8 @@ public bool VerifiedPKM() if (ModifierKeys == (Keys.Control | Keys.Shift | Keys.Alt)) return true; // Override // Make sure the PKX Fields are filled out properly (color check) - ComboBox[] cba = { - CB_Species, CB_Nature, CB_HeldItem, CB_Ability, // Main Tab - CB_MetLocation, CB_EggLocation, CB_Ball, // Met Tab - CB_Move1, CB_Move2, CB_Move3, CB_Move4, // Moves - CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 // Moves - }; - ComboBox cb = cba.FirstOrDefault(c => c.BackColor == Color.DarkSalmon && c.Items.Count != 0); + var cb = ValidationRequired.FirstOrDefault(c => c.BackColor == Color.DarkSalmon && c.Items.Count != 0); if (cb != null) { Control c = cb.Parent; while (!(c is TabPage)) c = c.Parent; @@ -232,6 +240,7 @@ public void UpdateLegality(LegalityAnalysis la = null, bool skipMoveRepop = fals { PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible = PB_WarnRelearn1.Visible = PB_WarnRelearn2.Visible = PB_WarnRelearn3.Visible = PB_WarnRelearn4.Visible = false; + LegalityChanged?.Invoke(Legality.Valid, null); return; } @@ -248,10 +257,9 @@ public void UpdateLegality(LegalityAnalysis la = null, bool skipMoveRepop = fals // Resort moves bool tmp = FieldsLoaded; FieldsLoaded = false; - var cb = new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 }; var moves = Legality.AllSuggestedMovesAndRelearn; var moveList = GameInfo.MoveDataSource.OrderByDescending(m => moves.Contains(m.Value)).ToList(); - foreach (ComboBox c in cb) + foreach (var c in Moves) { var index = WinFormsUtil.GetIndex(c); c.DataSource = new BindingSource(moveList, null); @@ -465,8 +473,7 @@ private static bool GetMarkingColor(int markval, out Color c) private void UpdateGender() { int cg = PKX.GetGenderFromString(Label_Gender.Text); - int Gender = GetSaneGender(pkm, cg); - + int Gender = pkm.GetSaneGender(cg); Label_Gender.Text = gendersymbols[Gender]; Label_Gender.ForeColor = GetGenderColor(Gender); } @@ -660,25 +667,16 @@ private bool SetSuggestedMoves(bool random = false, bool silent = false) return false; } - CB_Move1.SelectedValue = m[0]; - CB_Move2.SelectedValue = m[1]; - CB_Move3.SelectedValue = m[2]; - CB_Move4.SelectedValue = m[3]; + pkm.Moves = m; + pkm.SetPPUps(m); + LoadMoves(pkm); return true; } private bool SetSuggestedRelearnMoves(bool silent = false) { if (pkm.Format < 6) return false; - - int[] m = Legality.GetSuggestedRelearn(); - if (m.All(z => z == 0)) - if (!pkm.WasEgg && !pkm.WasEvent && !pkm.WasEventEgg && !pkm.WasLink) - { - var encounter = Legality.GetSuggestedMetInfo(); - if (encounter != null) - m = encounter.Relearn; - } + int[] m = pkm.GetSuggestedRelearnMoves(Legality); if (pkm.RelearnMoves.SequenceEqual(m)) return false; @@ -720,17 +718,7 @@ private bool SetSuggestedMetLocation(bool silent = false) if (!silent) { - var suggestion = new List { "Suggested:" }; - if (pkm.Format >= 3) - { - var met_list = GameInfo.GetLocationList((GameVersion)pkm.Version, pkm.Format, egg: false); - var locstr = met_list.FirstOrDefault(loc => loc.Value == location).Text; - suggestion.Add($"Met Location: {locstr}"); - suggestion.Add($"Met Level: {level}"); - } - if (pkm.CurrentLevel < minlvl) - suggestion.Add($"Current Level: {minlvl}"); - + List suggestion = GetSuggestionMessage(pkm, level, location, minlvl); if (suggestion.Count == 1) // no suggestion return false; @@ -910,23 +898,21 @@ private void UpdateHaXForm(object sender, EventArgs e) } private void UpdatePP(object sender, EventArgs e) { - ComboBox[] cbs = { CB_Move1, CB_Move2, CB_Move3, CB_Move4 }; - ComboBox[] pps = { CB_PPu1, CB_PPu2, CB_PPu3, CB_PPu4 }; - MaskedTextBox[] tbs = { TB_PP1, TB_PP2, TB_PP3, TB_PP4 }; - int index = Array.IndexOf(cbs, sender); + if (!(sender is ComboBox cb)) + return; + int index = Array.IndexOf(Moves, cb); if (index < 0) - index = Array.IndexOf(pps, sender); + index = Array.IndexOf(PPUps, cb); if (index < 0) return; - int move = WinFormsUtil.GetIndex(cbs[index]); - int pp = pps[index].SelectedIndex; - if (move == 0 && pp != 0) - { - pps[index].SelectedIndex = 0; - return; // recursively triggers - } - tbs[index].Text = pkm.GetMovePP(move, pp).ToString(); + int move = WinFormsUtil.GetIndex(Moves[index]); + var ppctrl = PPUps[index]; + int ppups = ppctrl.SelectedIndex; + if (move <= 0) + ppctrl.SelectedIndex = 0; + else + MovePP[index].Text = pkm.GetMovePP(move, ppups).ToString(); } private void UpdatePKRSstrain(object sender, EventArgs e) { @@ -1104,13 +1090,13 @@ private void UpdateOriginGame(object sender, EventArgs e) } private void UpdateExtraByteValue(object sender, EventArgs e) { - if (CB_ExtraBytes.Items.Count == 0) + if (CB_ExtraBytes.Items.Count == 0 || !(sender is MaskedTextBox mtb)) return; // Changed Extra Byte's Value - if (Util.ToInt32(((MaskedTextBox)sender).Text) > byte.MaxValue) - ((MaskedTextBox)sender).Text = "255"; + if (Util.ToInt32(mtb.Text) > byte.MaxValue) + mtb.Text = "255"; - int value = Util.ToInt32(TB_ExtraByte.Text); + int value = Util.ToInt32(mtb.Text); int offset = Convert.ToInt32(CB_ExtraBytes.Text, 16); pkm.Data[offset] = (byte)value; } @@ -1269,8 +1255,7 @@ private void UpdateIsEgg(object sender, EventArgs e) // Wipe egg memories if (pkm.Format >= 6 && ModifyPKM) - pkm.OT_Memory = pkm.OT_Affection = pkm.OT_Feeling = pkm.OT_Intensity = pkm.OT_TextVar = - pkm.HT_Memory = pkm.HT_Affection = pkm.HT_Feeling = pkm.HT_Intensity = pkm.HT_TextVar = 0; + pkm.ClearMemories(); } else // Not Egg { @@ -1467,12 +1452,12 @@ private void ValidateMove(object sender, EventArgs e) if (!FieldsLoaded) return; - if (new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 }.Contains(sender)) // Move + if (Moves.Contains(sender)) // Move UpdatePP(sender, e); // Legality - pkm.Moves = new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 }.Select(WinFormsUtil.GetIndex).ToArray(); - pkm.RelearnMoves = new[] { CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 }.Select(WinFormsUtil.GetIndex).ToArray(); + pkm.Moves = Moves.Select(WinFormsUtil.GetIndex).ToArray(); + pkm.RelearnMoves = Relearn.Select(WinFormsUtil.GetIndex).ToArray(); UpdateLegality(skipMoveRepop: true); } private void ValidateMovePaint(object sender, DrawItemEventArgs e) @@ -1673,56 +1658,9 @@ public void EnableDragDrop(DragEventHandler enter, DragEventHandler drop) public Action LoadShowdownSet; private void LoadShowdownSetDefault(ShowdownSet Set) { - CB_Species.SelectedValue = Set.Species; - CHK_Nicknamed.Checked = Set.Nickname != null; - if (Set.Nickname != null) - TB_Nickname.Text = Set.Nickname; - if (Set.Gender != null) - { - int Gender = PKX.GetGenderFromString(Set.Gender); - Label_Gender.Text = gendersymbols[Gender]; - Label_Gender.ForeColor = GetGenderColor(Gender); - } - - CB_Form.SelectedIndex = Math.Min(CB_Form.Items.Count - 1, Set.FormIndex); - - // Set Ability and Moves - int abil = Math.Max(0, Array.IndexOf(pkm.PersonalInfo.Abilities, Set.Ability)); - CB_Ability.SelectedIndex = Math.Min(CB_Ability.Items.Count - 1, abil); - ComboBox[] m = { CB_Move1, CB_Move2, CB_Move3, CB_Move4 }; - ComboBox[] p = { CB_PPu1, CB_PPu2, CB_PPu3, CB_PPu4 }; - for (int i = 0; i < 4; i++) - { - m[i].SelectedValue = Set.Moves[i]; - p[i].SelectedIndex = Set.Moves[i] != 0 ? 3 : 0; // max PP - } - - // Set Item and Nature - CB_HeldItem.SelectedValue = Set.HeldItem < 0 ? 0 : Set.HeldItem; - CB_Nature.SelectedValue = Set.Nature < 0 ? 0 : Set.Nature; - - // Set Level and Friendship - TB_Level.Text = Set.Level.ToString(); - TB_Friendship.Text = Set.Friendship.ToString(); - SetSuggestedHyperTrainingData(pkm, Set.IVs); - - // Set IVs - Stats.LoadIVs(Set.IVs); - Stats.LoadEVs(Set.EVs); - - UpdateRandomEC(null, null); - if (Set.Shiny && !pkm.IsShiny) - UpdateShiny(true); - else if (!Set.Shiny && pkm.IsShiny) - UpdateRandomPID(BTN_RerollPID, null); - else - UpdateRandomPID(null, null); - - pkm = PreparePKM(); - UpdateLegality(); - - if (Legality.Info.Relearn.Any(z => !z.Valid)) - SetSuggestedRelearnMoves(silent: true); + var pk = PreparePKM(); + pk.ApplySetDetails(Set); + PopulateFields(pk); } public void ChangeLanguage(SaveFile sav, PKM pk) @@ -1786,42 +1724,26 @@ private void PopulateFilteredDataSources(SaveFile SAV) // Set the Move ComboBoxes too.. GameInfo.MoveDataSource = (HaX ? GameInfo.HaXMoveDataSource : GameInfo.LegalMoveDataSource).Where(m => m.Value <= SAV.MaxMoveID).ToList(); // Filter Z-Moves if appropriate - foreach (ComboBox cb in new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4, CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 }) + foreach (var cb in Moves.Concat(Relearn)) { cb.DisplayMember = "Text"; cb.ValueMember = "Value"; cb.DataSource = new BindingSource(GameInfo.MoveDataSource, null); } } - private static int GetSaneGender(PKM pkm, int cg) + private static List GetSuggestionMessage(PKM pkm, int level, int location, int minlvl) { - int gt = pkm.PersonalInfo.Gender; - if (gt == 255) // Genderless - return 2; - if (gt == 254) // Female Only - return 1; - if (gt == 0) // Male Only - return 0; - if (cg == 2 || pkm.GenNumber < 6) - return (byte)pkm.PID <= gt ? 1 : 0; - return cg; - } - private static void SetSuggestedHyperTrainingData(PKM pkm, int[] IVs) - { - if (pkm.Format < 7) - return; - if (pkm.CurrentLevel < 100) + var suggestion = new List { "Suggested:" }; + if (pkm.Format >= 3) { - pkm.HyperTrainFlags = 0; - return; + var met_list = GameInfo.GetLocationList((GameVersion)pkm.Version, pkm.Format, egg: false); + var locstr = met_list.FirstOrDefault(loc => loc.Value == location).Text; + suggestion.Add($"Met Location: {locstr}"); + suggestion.Add($"Met Level: {level}"); } - - 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; + if (pkm.CurrentLevel < minlvl) + suggestion.Add($"Current Level: {minlvl}"); + return suggestion; } } } diff --git a/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs index 5af828a84..81dfb9a16 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs @@ -397,59 +397,5 @@ public static string GetPotentialString(this PKM pkm, bool unicode = true) var arr = unicode ? PotentialUnicode : PotentialNoUnicode; return arr[pkm.PotentialRating]; } - - 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)); - } - } - 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)); - } - } - - public static void SetATKIVGender(this PKM pk, int gender) - { - do { pk.IV_ATK = (int)(Util.Rand32() & pk.MaxIV); } - while (pk.Gender != gender); - } - - public static int GetMaximumEV(this PKM pkm, int index) - { - if (pkm.Format < 3) - return ushort.MaxValue; - - var EVs = pkm.EVs; - EVs[index] = 0; - var sum = EVs.Sum(); - int remaining = 510 - sum; - var newEV = Math.Min(Math.Max(remaining, 0), 252); - return newEV; - } - public static int GetMaximumIV(this PKM pkm, int index, bool Allow30 = false) - { - if (pkm.IVs[index] == pkm.MaxIV && Allow30) - return pkm.MaxIV - 1; - return pkm.MaxIV; - } } }