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)