From 684191772fe9fa334bfcd588e12f1f480b3c799e Mon Sep 17 00:00:00 2001 From: Kurt Date: Mon, 11 Feb 2019 21:49:05 -0800 Subject: [PATCH] Misc refactoring (showdownset null) Set default values to prevent any properties from being null extract ClearNickname logic for hot path & reusable method extract item conversion and move to appropriate logic class --- PKHeX.Core/Editing/CommonEdits.cs | 72 ++------ PKHeX.Core/Editing/ShowdownSet.cs | 240 +++++++++++++++------------ PKHeX.Core/PKM/Util/ItemConverter.cs | 35 ++++ PKHeX.Core/PKM/Util/PKX.cs | 4 +- 4 files changed, 188 insertions(+), 163 deletions(-) diff --git a/PKHeX.Core/Editing/CommonEdits.cs b/PKHeX.Core/Editing/CommonEdits.cs index d6782cb6d..3e5e45278 100644 --- a/PKHeX.Core/Editing/CommonEdits.cs +++ b/PKHeX.Core/Editing/CommonEdits.cs @@ -15,20 +15,23 @@ public static class CommonEdits /// /// 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) + public static void SetNickname(this PKM pk, string nick) { - if (nick != null) + if (string.IsNullOrWhiteSpace(nick)) { - pk.IsNicknamed = true; - pk.Nickname = nick; - } - else - { - pk.IsNicknamed = false; - pk.Nickname = PKX.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format); - if (pk is _K12 pk12) - pk12.SetNotNicknamed(); + pk.ClearNickname(); + return; } + pk.IsNicknamed = true; + pk.Nickname = nick; + } + + public static void ClearNickname(this PKM pk) + { + pk.IsNicknamed = false; + pk.Nickname = PKX.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format); + if (pk is _K12 pk12) + pk12.SetNotNicknamed(); } /// @@ -340,47 +343,8 @@ public static void ApplySetDetails(this PKM pk, ShowdownSet Set) /// Format required for importing public static void ApplyHeldItem(this PKM pk, int item, int format) { - if (item <= 0) - { - pk.HeldItem = 0; - return; - } - - if (format <= 3 && pk.Format != format) - { - if (pk.Format > 3) // try remapping - { - item = format == 2 ? ItemConverter.GetG4Item((byte) item) : ItemConverter.GetG4Item((ushort) item); - pk.ApplyHeldItem(item, pk.Format); - return; - } - - if (pk.Format > format) // can't set past gen items - { - pk.HeldItem = 0; - return; - } - - // ShowdownSet checks gen3 then gen2. For gen2 collisions (if any?) remap 3->4->2. - item = ItemConverter.GetG4Item((ushort) item); - item = ItemConverter.GetG2Item((ushort) item); - if (item == 0 || item < 0) - { - pk.HeldItem = 0; - return; - } - } - - switch (pk.Format) - { - case 3: pk.HeldItem = ItemConverter.GetG3Item((ushort)item); break; - case 2: pk.HeldItem = (byte)item; break; - case 1: pk.HeldItem = 0; break; - default: pk.HeldItem = item; break; - } - - if (pk.HeldItem > pk.MaxItemID) - pk.HeldItem = 0; + item = ItemConverter.GetFormatHeldItemID(item, format, pk.Format); + pk.HeldItem = ((uint)item > pk.MaxItemID) ? 0 : item; } /// @@ -542,7 +506,7 @@ public static void ForceHatchPKM(this PKM pkm, bool reHatch = false) if (!pkm.IsEgg && !reHatch) return; pkm.IsEgg = false; - pkm.SetNickname(); + pkm.ClearNickname(); pkm.CurrentFriendship = pkm.PersonalInfo.BaseFriendship; if (pkm.IsTradedEgg) pkm.Egg_Location = pkm.Met_Location; @@ -688,7 +652,7 @@ public static void SetDefaultNickname(this PKM pk, LegalityAnalysis la = null) if (la.Parsed && la.EncounterOriginal is EncounterTrade t && t.HasNickname) pk.SetNickname(t.GetNickname(pk.Language)); else - pk.SetNickname(); + pk.ClearNickname(); } private static readonly string[] PotentialUnicode = { "★☆☆☆", "★★☆☆", "★★★☆", "★★★★" }; diff --git a/PKHeX.Core/Editing/ShowdownSet.cs b/PKHeX.Core/Editing/ShowdownSet.cs index 1c2dfc9de..3e946da80 100644 --- a/PKHeX.Core/Editing/ShowdownSet.cs +++ b/PKHeX.Core/Editing/ShowdownSet.cs @@ -9,7 +9,6 @@ namespace PKHeX.Core /// public class ShowdownSet { - // String to Values private static readonly string[] genders = {"M", "F", ""}; private static readonly string[] genderForms = {"", "F", ""}; private static readonly string[] StatNames = { "HP", "Atk", "Def", "SpA", "SpD", "Spe" }; @@ -18,8 +17,8 @@ public class ShowdownSet private static readonly string[] LineSplit = {": "}; private static readonly string[] ItemSplit = {" @ "}; private static readonly char[] ParenJunk = { '[', ']', '(', ')' }; - private static readonly int[] DashedSpecies = {782, 783, 784, 250, 032, 029}; // Kommo-o, Ho-Oh, Nidoran-M, Nidoran-F - private static int MAX_SPECIES => PKX.Personal.MaxSpeciesID; + private static readonly ushort[] DashedSpecies = {782, 783, 784, 250, 032, 029}; // Kommo-o, Ho-Oh, Nidoran-M, Nidoran-F + private const int MAX_SPECIES = (int)Core.Species.MAX_COUNT - 1; private const string Language = "en"; private const int DefaultLanguageID = (int)Core.LanguageID.English; private static readonly GameStrings DefaultStrings = GameInfo.GetStrings(Language); @@ -32,17 +31,17 @@ public class ShowdownSet /// /// of the Set entity it is specific to. /// - public int Format { get; } = PKMConverter.Format; + public int Format { get; private set; } = PKMConverter.Format; /// /// of the Set entity. /// - public string Nickname { get; set; } + public string Nickname { get; set; } = string.Empty; /// /// name of the Set entity. /// - public string Gender { get; private set; } + public string Gender { get; private set; } = string.Empty; /// /// of the Set entity. @@ -72,12 +71,12 @@ public class ShowdownSet /// /// of the Set entity. /// - public int Nature { get; set; } + public int Nature { get; set; } = -1; /// /// name of the Set entity, stored in PKHeX style (instead of Showdown's) /// - public string Form { get; private set; } + public string Form { get; private set; } = string.Empty; /// /// of the Set entity. @@ -118,26 +117,26 @@ public class ShowdownSet private int[] EVsSpeedLast => new[] {EVs[0], EVs[1], EVs[2], EVs[4], EVs[5], EVs[3]}; /// - /// Loads a new from the input string. If no string is provided, a blank set is returned. + /// Loads a new blank . + /// + public ShowdownSet() { } + + /// + /// Loads a new from the input string. /// /// Single-line string which will be split before loading. - public ShowdownSet(string input = null) + public ShowdownSet(string input) { - if (input == null) - return; - - string[] lines = input.Split(Splitters, StringSplitOptions.None); + var lines = input.Split(Splitters, StringSplitOptions.None); LoadLines(lines); } /// - /// Loads a new from the input string. If no string is provided, a blank set is returned. + /// Loads a new from the input string. /// /// Enumerable list of lines. public ShowdownSet(IEnumerable lines) { - if (lines == null) - return; LoadLines(lines); } @@ -151,76 +150,89 @@ private void LoadLines(IEnumerable lines) // Showdown Quirks Form = ConvertFormFromShowdown(Form, Species, Ability); // Set Form + if (Form.Length == 0) + { + FormIndex = 0; + return; + } string[] formStrings = PKX.GetFormList(Species, Strings.Types, Strings.forms, genderForms); - FormIndex = string.IsNullOrWhiteSpace(Form) ? 0 : Math.Max(0, Array.FindIndex(formStrings, z => z.Contains(Form))); + FormIndex = Math.Max(0, Array.FindIndex(formStrings, z => z.Contains(Form))); } private void ParseLines(IEnumerable lines) { - int movectr = 0; - var e = lines.GetEnumerator(); - if (!e.MoveNext()) - return; - - ParseFirstLine(e.Current); - while (e.MoveNext()) + using (var e = lines.GetEnumerator()) { - var line = e.Current; - if (line.StartsWith("-")) + if (!e.MoveNext()) + return; + + ParseFirstLine(e.Current); + int movectr = 0; + while (e.MoveNext()) { - string moveString = ParseLineMove(line); - int move = Array.IndexOf(Strings.movelist, moveString); - if (move < 0) - InvalidLines.Add($"Unknown Move: {moveString}"); - else - Moves[movectr++] = move; + var line = e.Current; + if (line.Length == 0) + continue; - if (movectr == 4) - return; // End of moves, end of set data - continue; + if (line[0] == '-') + { + string moveString = ParseLineMove(line); + int move = Array.IndexOf(Strings.movelist, moveString); + if (move < 0) + InvalidLines.Add($"Unknown Move: {moveString}"); + else + Moves[movectr++] = move; + + if (movectr == 4) + return; // End of moves, end of set data + continue; + } + + var split = line.Split(LineSplit, StringSplitOptions.None); + var valid = split.Length == 1 + ? ParseSingle(line) // Nature + : ParseEntry(split[0].Trim(), split[1].Trim()); + if (!valid) + InvalidLines.Add(line); } - - string[] brokenline = line.Split(LineSplit, StringSplitOptions.None); - var piece1 = brokenline[0].Trim(); - var piece2 = brokenline.Length == 1 ? string.Empty : brokenline[1].Trim(); - if (!ParseEntry(piece1, piece2)) - InvalidLines.Add(line); } - e.Dispose(); } - private bool ParseEntry(string first, string second) + private bool ParseSingle(string identifier) { - switch (first) + if (identifier.EndsWith("Nature")) // XXX Nature { - case "Trait": case "Ability": return (Ability = Array.IndexOf(Strings.abilitylist, second)) >= 0; - case "Shiny": return Shiny = second.Trim() == "Yes"; - case "Nature": return (Nature = Array.IndexOf(Strings.natures, second)) >= 0; - case "EV": case "EVs": ParseLineEVs(second); return true; - case "IV": case "IVs": ParseLineIVs(second); return true; + var naturestr = identifier.Split(' ')[0].Trim(); + return (Nature = Array.IndexOf(Strings.natures, naturestr)) >= 0; + } + return false; + } + + private bool ParseEntry(string identifier, string value) + { + switch (identifier) + { + case "Trait": case "Ability": return (Ability = Array.IndexOf(Strings.abilitylist, value)) >= 0; + case "Shiny": return Shiny = value.Trim() == "Yes"; + case "Nature": return (Nature = Array.IndexOf(Strings.natures, value)) >= 0; + case "EV": case "EVs": ParseLineEVs(value); return true; + case "IV": case "IVs": ParseLineIVs(value); return true; case "Level": { - if (!int.TryParse(second.Trim(), out int val)) + if (!int.TryParse(value.Trim(), out int val)) return false; Level = val; return true; } case "Happiness": case "Friendship": { - if (!int.TryParse(second.Trim(), out int val)) + if (!int.TryParse(value.Trim(), out int val)) return false; Friendship = val; return true; } default: - { - if (first.EndsWith("Nature")) // XXX Nature - { - string naturestr = first.Split(' ')[0].Trim(); - return (Nature = Array.IndexOf(Strings.natures, naturestr)) >= 0; - } return false; - } } } @@ -255,19 +267,27 @@ private string GetText(GameStrings strings = null) if (strings != null) Strings = strings; + var result = GetSetLines(); + return string.Join(Environment.NewLine, result); + } + + public List GetSetLines() + { var result = new List(); // First Line: Name, Nickname, Gender, Item - string form = ConvertFormToShowdown(Form, Species); + var form = ConvertFormToShowdown(Form, Species); result.Add(GetStringFirstLine(form)); // IVs - if (GetStringStats(out IEnumerable ivstr, IVsSpeedLast, 31)) - result.Add($"IVs: {string.Join(" / ", ivstr)}"); + var ivs = GetStringStats(IVsSpeedLast, Format < 3 ? 15 : 31); + if (ivs.Count > 0) + result.Add($"IVs: {string.Join(" / ", ivs)}"); // EVs - if (GetStringStats(out IEnumerable evstr, EVsSpeedLast, 0)) - result.Add($"EVs: {string.Join(" / ", evstr)}"); + var evs = GetStringStats(EVsSpeedLast, 0); + if (evs.Count > 0) + result.Add($"EVs: {string.Join(" / ", evs)}"); // Secondary Stats if ((uint)Ability < Strings.Ability.Count) @@ -282,18 +302,17 @@ private string GetText(GameStrings strings = null) // Moves result.AddRange(GetStringMoves()); - - return string.Join(Environment.NewLine, result); + return result; } private string GetStringFirstLine(string form) { string specForm = Strings.Species[Species]; - if (!string.IsNullOrWhiteSpace(form)) + if (form.Length != 0) specForm += $"-{form.Replace("Mega ", "Mega-")}"; string result = GetSpeciesNickname(specForm); - if (!string.IsNullOrEmpty(Gender)) + if (Gender.Length != 0) result += $" ({Gender})"; if (HeldItem > 0) { @@ -306,7 +325,7 @@ private string GetStringFirstLine(string form) private string GetSpeciesNickname(string specForm) { - if (Nickname == null) + if (Nickname.Length == 0) return specForm; var name = PKX.GetSpeciesNameGeneration(Species, LanguageID, Format); if (name == Nickname) @@ -314,16 +333,16 @@ private string GetSpeciesNickname(string specForm) return $"{Nickname} ({specForm})"; } - private static bool GetStringStats(out IEnumerable result, int[] stats, int ignore) + private static IList GetStringStats(int[] stats, int ignore) { - var list = new List(); + var result = new List(); for (int i = 0; i < stats.Length; i++) { - if (stats[i] == ignore) continue; // ignore unused EVs - list.Add($"{stats[i]} {StatNames[i]}"); + if (stats[i] == ignore) + continue; // ignore unused stats + result.Add($"{stats[i]} {StatNames[i]}"); } - result = list; - return list.Count > 0; + return result; } private IEnumerable GetStringMoves() @@ -384,8 +403,13 @@ public ShowdownSet(PKM pkm) public void SetFormString(int index) { FormIndex = index; - string[] Forms = PKX.GetFormList(Species, Strings.Types, Strings.forms, genderForms, Format); - Form = FormIndex > 0 && FormIndex < Forms.Length ? Forms[FormIndex] : string.Empty; + if (index <= 0) + { + Form = string.Empty; + return; + } + var Forms = PKX.GetFormList(Species, Strings.Types, Strings.forms, genderForms, Format); + Form = FormIndex >= Forms.Length ? string.Empty : Forms[index]; } private void ParseFirstLine(string first) @@ -393,7 +417,7 @@ private void ParseFirstLine(string first) if (first.Contains(" @ ")) { string[] pieces = first.Split(ItemSplit, StringSplitOptions.None); - string itemstr = pieces.Last().Trim(); + string itemstr = pieces[pieces.Length - 1].Trim(); ParseItemStr(itemstr); ParseFirstLineNoItem(pieces[0]); @@ -421,6 +445,7 @@ bool tryGetItem(int format) if (item < 0) return false; HeldItem = item; + Format = format; return true; } } @@ -513,15 +538,16 @@ private void ParseSpeciesNickname(string line) private string ParseLineMove(string line) { - string moveString = line.Substring(line[1] == ' ' ? 2 : 1); - if (!moveString.Contains(Strings.Move[237])) // Hidden Power - return moveString; + const int hiddenPower = 237; + string moveString = line.Substring(line[1] == ' ' ? 2 : 1).Trim(); + if (!moveString.StartsWith(Strings.Move[hiddenPower])) // Hidden Power + return moveString; // regular move if (moveString.Length <= 13) - return Strings.Move[237]; + return Strings.Move[hiddenPower]; // Defined Hidden Power - string type = moveString.Remove(0, 13); + string type = moveString.Substring(13); type = RemoveAll(type, ParenJunk); // Trim out excess data int hpVal = Array.IndexOf(Strings.types, type) - 1; // Get HP Type @@ -539,48 +565,46 @@ private string ParseLineMove(string line) { InvalidLines.Add($"Invalid Hidden Power Type: {type}"); } - return Strings.Move[237]; + return Strings.Move[hiddenPower]; } private void ParseLineEVs(string line) { - var evlist = SplitLineStats(line); - if (evlist.Length == 1) + var list = SplitLineStats(line); + if ((list.Length & 1) == 1) InvalidLines.Add("Unknown EV input."); - for (int i = 0; i < evlist.Length / 2; i++) + for (int i = 0; i < list.Length / 2; i++) { int pos = i * 2; - bool valid = ushort.TryParse(evlist[pos + 0], out ushort EV); - int index = Array.IndexOf(StatNames, evlist[pos + 1]); - if (valid && index > -1) + int index = Array.IndexOf(StatNames, list[pos + 1]); + if (index >= 0 && ushort.TryParse(list[pos + 0], out var EV)) EVs[index] = EV; else - InvalidLines.Add($"Unknown EV Type input: {evlist[pos]}"); + InvalidLines.Add($"Unknown EV stat: {list[pos]}"); } EVs = EVsSpeedFirst; } private void ParseLineIVs(string line) { - string[] ivlist = SplitLineStats(line); - if (ivlist.Length == 1) + var list = SplitLineStats(line); + if ((list.Length & 1) == 1) InvalidLines.Add("Unknown IV input."); - for (int i = 0; i < ivlist.Length / 2; i++) + for (int i = 0; i < list.Length / 2; i++) { - var pos = i * 2; - bool valid = byte.TryParse(ivlist[pos + 0], out byte IV); - int index = Array.IndexOf(StatNames, ivlist[pos + 1]); - if (valid && index > -1) + int pos = i * 2; + int index = Array.IndexOf(StatNames, list[pos + 1]); + if (index >= 0 && byte.TryParse(list[pos + 0], out var IV)) IVs[index] = IV; else - InvalidLines.Add($"Unknown IV Type input: {ivlist[pos]}"); + InvalidLines.Add($"Unknown IV stat: {list[pos]}"); } IVs = IVsSpeedFirst; } private static string ConvertFormToShowdown(string form, int spec) { - if (string.IsNullOrWhiteSpace(form)) + if (form.Length == 0) { if (spec == 774) // Minior form = "Meteor"; @@ -618,7 +642,9 @@ private static string ConvertFormToShowdown(string form, int spec) private static string ConvertFormFromShowdown(string form, int spec, int ability) { - form = form?.Replace(' ', '-'); // inconsistencies are great + if (form.Length == 0) + form = form.Replace(' ', '-'); // inconsistencies are great + switch (spec) { case 550 when form == "Blue-Striped": // Basculin @@ -629,10 +655,8 @@ private static string ConvertFormFromShowdown(string form, int spec, int ability return "Poké Ball"; // Zygarde - case 718 when string.IsNullOrWhiteSpace(form) && ability == 211: - return "50%-C"; - case 718 when string.IsNullOrWhiteSpace(form): - return "50%"; + case 718 when form.Length == 0: + return ability == 211 ? "50%-C" : "50%"; case 718 when form == "Complete": return form; case 718 when ability == 211: @@ -642,7 +666,7 @@ private static string ConvertFormFromShowdown(string form, int spec, int ability return "Dusk"; // Minior - case 774 when !string.IsNullOrWhiteSpace(form) && form != "Meteor": + case 774 when form.Length != 0 && form != "Meteor": return $"C-{form}"; // Necrozma @@ -652,7 +676,7 @@ private static string ConvertFormFromShowdown(string form, int spec, int ability return "Dawn"; default: - if (Legal.Totem_USUM.Contains(spec) && form?.EndsWith("Totem") == true) + if (Legal.Totem_USUM.Contains(spec) && form.EndsWith("Totem")) return "Large"; return form; } @@ -676,6 +700,8 @@ private static string[] SplitLineStats(string line) /// objects until is consumed. public static IEnumerable GetShowdownSets(IEnumerable lines) { + // exported sets always have >4 moves; new List will always require 1 resizing, allocate 2x to save 1 reallocation. + // intro, nature, ability, (ivs, evs, shiny, level) 4*moves var setLines = new List(8); foreach (var line in lines) { diff --git a/PKHeX.Core/PKM/Util/ItemConverter.cs b/PKHeX.Core/PKM/Util/ItemConverter.cs index 835189cfd..deb51110b 100644 --- a/PKHeX.Core/PKM/Util/ItemConverter.cs +++ b/PKHeX.Core/PKM/Util/ItemConverter.cs @@ -168,5 +168,40 @@ public static int GetG2ItemTransfer(int g1val) } public static bool IsItemTransferrable12(ushort item) => ((IList) Legal.HeldItems_GSC).Contains(item); + + /// + /// Gets a format specific value depending on the desired format and the provided item index & origin format. + /// + /// Held Item to apply + /// Format from importing + /// Format required for holder + public static int GetFormatHeldItemID(int item, int srcFormat, int destFormat) + { + if (item <= 0) + return 0; + + if (destFormat != srcFormat && srcFormat <= 3) // past gen items + { + if (destFormat > 3) // try remapping + return item = srcFormat == 2 ? GetG4Item((byte)item) : GetG4Item((ushort)item); + + if (destFormat > srcFormat) // can't set past gen items + return 0; + + // ShowdownSet checks gen3 then gen2. For gen2 collisions (if any?) remap 3->4->2. + item = GetG4Item((ushort)item); + item = GetG2Item((ushort)item); + if (item <= 0) + return 0; + } + + switch (destFormat) + { + case 3: return GetG3Item((ushort)item); + case 2: return (byte)item; + case 1: return 0; + default: return item; + } + } } } diff --git a/PKHeX.Core/PKM/Util/PKX.cs b/PKHeX.Core/PKM/Util/PKX.cs index b33f88aef..ab309fade 100644 --- a/PKHeX.Core/PKM/Util/PKX.cs +++ b/PKHeX.Core/PKM/Util/PKX.cs @@ -644,8 +644,8 @@ public static int GetUnownForm(uint pid) /// This method should only be used for Generations 3-5 origin. public static int GetGenderFromPID(int species, uint PID) { - int genderratio = Personal[species].Gender; - return GetGenderFromPIDAndRatio(PID, genderratio); + int gt = Personal[species].Gender; + return GetGenderFromPIDAndRatio(PID, gt); } public static int GetGenderFromPIDAndRatio(uint PID, int gr)