mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Less allocation, minor tweaks
NET 8 will have a Shuffle method, which can get rid of the overload in Util. Batch Editor no longer crashes the program when selecting OT_Trash/HT_Trash/Nickname_Trash via dropdown.
This commit is contained in:
parent
82309cf99d
commit
5c13183d02
17 changed files with 119 additions and 99 deletions
|
@ -43,7 +43,7 @@ public static class BallApplicator
|
|||
Span<Ball> balls = stackalloc Ball[MaxBallSpanAlloc];
|
||||
var count = GetBallListFromColor(pk, balls);
|
||||
balls = balls[..count];
|
||||
Util.Shuffle(balls);
|
||||
Util.Rand.Shuffle(balls);
|
||||
return ApplyFirstLegalBall(pk, balls);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ public static class MoveSetApplicator
|
|||
{
|
||||
la.GetSuggestedCurrentMoves(moves, random ? MoveSourceType.All : MoveSourceType.Encounter);
|
||||
if (random && !la.Entity.IsEgg)
|
||||
Util.Shuffle(moves);
|
||||
Util.Rand.Shuffle(moves);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -453,17 +453,12 @@ public static class BatchEditing
|
|||
{
|
||||
switch (cmd.PropertyName)
|
||||
{
|
||||
case nameof(PKM.Nickname_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.Nickname_Trash); return ModifyResult.Modified;
|
||||
case nameof(PKM.OT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.OT_Trash); return ModifyResult.Modified;
|
||||
case nameof(PKM.HT_Trash): ConvertToBytes(cmd.PropertyValue).CopyTo(pk.HT_Trash); return ModifyResult.Modified;
|
||||
case nameof(PKM.Nickname_Trash): StringUtil.LoadHexBytesTo(cmd.PropertyValue.AsSpan(CONST_BYTES.Length), pk.Nickname_Trash, 3); return ModifyResult.Modified;
|
||||
case nameof(PKM.OT_Trash): StringUtil.LoadHexBytesTo(cmd.PropertyValue.AsSpan(CONST_BYTES.Length), pk.OT_Trash, 3); return ModifyResult.Modified;
|
||||
case nameof(PKM.HT_Trash): StringUtil.LoadHexBytesTo(cmd.PropertyValue.AsSpan(CONST_BYTES.Length), pk.HT_Trash, 3); return ModifyResult.Modified;
|
||||
default:
|
||||
return ModifyResult.Error;
|
||||
}
|
||||
static byte[] ConvertToBytes(string str)
|
||||
{
|
||||
var arr = str[CONST_BYTES.Length..].Split(',');
|
||||
return Array.ConvertAll(arr, z => Convert.ToByte(z.Trim(), 16));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -103,13 +103,11 @@ public sealed class GameDataSource
|
|||
00,
|
||||
};
|
||||
|
||||
private static IReadOnlyList<ComboItem> GetBalls(string[] itemList)
|
||||
{
|
||||
// ignores Poke/Great/Ultra
|
||||
ReadOnlySpan<ushort> ball_nums = stackalloc ushort[] { 007, 576, 013, 492, 497, 014, 495, 493, 496, 494, 011, 498, 008, 006, 012, 015, 009, 005, 499, 010, 001, 016, 851, 1785, 1710, 1711, 1712, 1713, 1746, 1747, 1748, 1749, 1750, 1771 };
|
||||
ReadOnlySpan<byte> ball_vals = stackalloc byte[] { 007, 025, 013, 017, 022, 014, 020, 018, 021, 019, 011, 023, 008, 006, 012, 015, 009, 005, 024, 010, 001, 016, 026, 0027, 0028, 0029, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037 };
|
||||
return Util.GetVariedCBListBall(itemList, ball_nums, ball_vals);
|
||||
}
|
||||
private static IReadOnlyList<ComboItem> GetBalls(string[] itemList) => Util.GetVariedCBListBall(itemList, BallStoredIndexes, BallItemIDs);
|
||||
|
||||
// Since Poké Ball (and Great Ball / Ultra Ball) are most common, any list should have them at the top. The rest can be sorted alphabetically.
|
||||
private static ReadOnlySpan<byte> BallStoredIndexes => new byte[] { 004, 003, 002, 001, 005, 006, 007, 008, 009, 010, 011, 012, 013, 014, 015, 016, 017, 018, 019, 020, 021, 022, 023, 024, 025, 026, 0027, 0028, 0029, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037 };
|
||||
private static ReadOnlySpan<ushort> BallItemIDs => new ushort[] { 004, 003, 002, 001, 005, 006, 007, 008, 009, 010, 011, 012, 013, 014, 015, 016, 492, 493, 494, 495, 496, 497, 498, 499, 576, 851, 1785, 1710, 1711, 1712, 1713, 1746, 1747, 1748, 1749, 1750, 1771 };
|
||||
|
||||
private static ComboItem[] GetVersionList(GameStrings s)
|
||||
{
|
||||
|
|
|
@ -208,7 +208,7 @@ public sealed class PK1 : GBPKML, IPersonalType
|
|||
finalIVs[i] = rnd.Next(32);
|
||||
for (var i = 0; i < flawless; i++)
|
||||
finalIVs[i] = 31;
|
||||
Util.Shuffle(finalIVs);
|
||||
rnd.Shuffle(finalIVs);
|
||||
pk7.SetIVs(finalIVs);
|
||||
|
||||
switch (IsShiny ? Shiny.Always : Shiny.Never)
|
||||
|
|
|
@ -176,7 +176,7 @@ public sealed class PK2 : GBPKML, ICaughtData2
|
|||
finalIVs[i] = rnd.Next(32);
|
||||
for (var i = 0; i < flawless; i++)
|
||||
finalIVs[i] = 31;
|
||||
Util.Shuffle(finalIVs);
|
||||
rnd.Shuffle(finalIVs);
|
||||
pk7.SetIVs(finalIVs);
|
||||
|
||||
switch (IsShiny ? Shiny.Always : Shiny.Never)
|
||||
|
|
|
@ -904,7 +904,7 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
|
|||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
ivs[i] = MaxIV;
|
||||
Util.Shuffle(ivs, 0, ivs.Length, rnd); // Randomize IV order
|
||||
rnd.Shuffle(ivs); // Randomize IV order
|
||||
}
|
||||
SetIVs(ivs);
|
||||
}
|
||||
|
|
|
@ -131,20 +131,13 @@ public sealed class SK2 : GBPKM, ICaughtData2
|
|||
public override bool HasOriginalMetLocation => CaughtData != 0;
|
||||
public override int Version { get => (int)GameVersion.GSC; set { } }
|
||||
|
||||
protected override byte[] GetNonNickname(int language)
|
||||
protected override void GetNonNickname(int language, Span<byte> data)
|
||||
{
|
||||
var name = SpeciesName.GetSpeciesNameGeneration(Species, language, 2);
|
||||
byte[] data = new byte[name.Length];
|
||||
StringConverter12.SetString(data, name, data.Length, Japanese, StringConverterOption.Clear50);
|
||||
return data;
|
||||
}
|
||||
|
||||
public override void SetNotNicknamed(int language)
|
||||
{
|
||||
var name = SpeciesName.GetSpeciesNameGeneration(Species, language, 2);
|
||||
Nickname_Trash.Clear();
|
||||
Nickname = name;
|
||||
}
|
||||
public override void SetNotNicknamed(int language) => GetNonNickname(language, Nickname_Trash);
|
||||
|
||||
// Maximums
|
||||
public override ushort MaxMoveID => Legal.MaxMoveID_2;
|
||||
|
|
|
@ -30,13 +30,24 @@ public abstract class GBPKM : PKM
|
|||
public override bool Valid { get => true; set { } }
|
||||
public sealed override void RefreshChecksum() { }
|
||||
|
||||
protected abstract byte[] GetNonNickname(int language);
|
||||
|
||||
private bool? _isnicknamed;
|
||||
protected abstract void GetNonNickname(int language, Span<byte> data);
|
||||
|
||||
public sealed override bool IsNicknamed
|
||||
{
|
||||
get => _isnicknamed ??= !Nickname_Trash.SequenceEqual(GetNonNickname(GuessedLanguage()));
|
||||
get
|
||||
{
|
||||
if (_isnicknamed is {} actual)
|
||||
return actual;
|
||||
|
||||
var current = Nickname_Trash;
|
||||
Span<byte> expect = stackalloc byte[current.Length];
|
||||
var language = GuessedLanguage();
|
||||
GetNonNickname(language, expect);
|
||||
var result = !current.SequenceEqual(expect);
|
||||
_isnicknamed = result;
|
||||
return result;
|
||||
}
|
||||
set
|
||||
{
|
||||
_isnicknamed = value;
|
||||
|
|
|
@ -43,24 +43,28 @@ public abstract class GBPKML : GBPKM
|
|||
RawNickname.AsSpan().Fill(StringConverter12.G1TerminatorCode);
|
||||
}
|
||||
|
||||
public override void SetNotNicknamed(int language) => GetNonNickname(language).AsSpan().CopyTo(RawNickname);
|
||||
public override void SetNotNicknamed(int language) => GetNonNickname(language, RawNickname);
|
||||
|
||||
protected override byte[] GetNonNickname(int language)
|
||||
protected override void GetNonNickname(int language, Span<byte> data)
|
||||
{
|
||||
var name = SpeciesName.GetSpeciesNameGeneration(Species, language, Format);
|
||||
var len = Nickname_Trash.Length;
|
||||
byte[] data = new byte[len];
|
||||
SetString(name, data, len, StringConverterOption.Clear50);
|
||||
if (!Korean)
|
||||
SetString(name, data, data.Length, StringConverterOption.Clear50);
|
||||
if (Korean)
|
||||
return;
|
||||
|
||||
// Decimal point<->period fix
|
||||
foreach (ref var c in data)
|
||||
{
|
||||
// Decimal point<->period fix
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
if (data[i] == 0xF2)
|
||||
data[i] = 0xE8;
|
||||
}
|
||||
if (c == 0xF2)
|
||||
c = 0xE8;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private string GetString(ReadOnlySpan<byte> span)
|
||||
{
|
||||
if (Korean)
|
||||
return StringConverter2KOR.GetString(span);
|
||||
return StringConverter12.GetString(span, Japanese);
|
||||
}
|
||||
|
||||
private int SetString(ReadOnlySpan<char> value, Span<byte> destBuffer, int maxLength, StringConverterOption option = StringConverterOption.None)
|
||||
|
@ -72,12 +76,7 @@ public abstract class GBPKML : GBPKM
|
|||
|
||||
public sealed override string Nickname
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Korean)
|
||||
return StringConverter2KOR.GetString(RawNickname);
|
||||
return StringConverter12.GetString(RawNickname, Japanese);
|
||||
}
|
||||
get => GetString(RawNickname);
|
||||
set
|
||||
{
|
||||
if (!IsNicknamed && Nickname == value)
|
||||
|
@ -89,12 +88,7 @@ public abstract class GBPKML : GBPKM
|
|||
|
||||
public sealed override string OT_Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Korean)
|
||||
return StringConverter2KOR.GetString(RawOT);
|
||||
return StringConverter12.GetString(RawOT, Japanese);
|
||||
}
|
||||
get => GetString(RawOT);
|
||||
set
|
||||
{
|
||||
if (value == OT_Name)
|
||||
|
|
|
@ -46,7 +46,7 @@ public static class EffortValues
|
|||
break; // done!
|
||||
}
|
||||
|
||||
Util.Shuffle(evs, 0, evs.Length, rnd);
|
||||
rnd.Shuffle(evs);
|
||||
}
|
||||
|
||||
private static void SetRandom12(Span<int> evs, Random rnd)
|
||||
|
|
|
@ -43,7 +43,7 @@ public sealed class Puff6 : SaveBlock<SAV6>
|
|||
{
|
||||
for (int i = 0; i < PuffSlots; i++)
|
||||
Data[Offset + i] = (byte)((i % MaxPuffID) + 1);
|
||||
Util.Shuffle(Data.AsSpan(), Offset, Offset + PuffSlots, rnd);
|
||||
rnd.Shuffle(Data.AsSpan(Offset, PuffSlots));
|
||||
}
|
||||
PuffCount = PuffSlots;
|
||||
}
|
||||
|
|
|
@ -112,23 +112,14 @@ public static partial class Util
|
|||
cbList.Sort(beginCount, inStrings.Length, Comparer);
|
||||
}
|
||||
|
||||
public static ComboItem[] GetVariedCBListBall(string[] inStrings, ReadOnlySpan<ushort> stringNum, ReadOnlySpan<byte> stringVal)
|
||||
public static ComboItem[] GetVariedCBListBall(ReadOnlySpan<string> itemNames, ReadOnlySpan<byte> ballIndex, ReadOnlySpan<ushort> ballItemID)
|
||||
{
|
||||
const int forcedTop = 3; // 3 Balls are preferentially first
|
||||
var list = new ComboItem[forcedTop + stringNum.Length];
|
||||
list[0] = new ComboItem(inStrings[4], (int)Ball.Poke);
|
||||
list[1] = new ComboItem(inStrings[3], (int)Ball.Great);
|
||||
list[2] = new ComboItem(inStrings[2], (int)Ball.Ultra);
|
||||
var list = new ComboItem[ballItemID.Length];
|
||||
for (int i = 0; i < ballItemID.Length; i++)
|
||||
list[i] = new ComboItem(itemNames[ballItemID[i]], ballIndex[i]);
|
||||
|
||||
for (int i = 0; i < stringNum.Length; i++)
|
||||
{
|
||||
int index = stringNum[i];
|
||||
var value = stringVal[i];
|
||||
var text = inStrings[index];
|
||||
list[i + 3] = new ComboItem(text, value);
|
||||
}
|
||||
|
||||
Array.Sort(list, 3, list.Length - 3, Comparer);
|
||||
// 3 Balls are preferentially first, sort Master Ball with the rest Alphabetically.
|
||||
list.AsSpan(3).Sort(Comparer);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,23 +14,16 @@ public static partial class Util
|
|||
/// Shuffles the order of items within a collection of items.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Item type</typeparam>
|
||||
/// <param name="items">Item collection</param>
|
||||
public static void Shuffle<T>(Span<T> items) => Shuffle(items, 0, items.Length, Rand);
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles the order of items within a collection of items.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Item type</typeparam>
|
||||
/// <param name="items">Item collection</param>
|
||||
/// <param name="start">Starting position</param>
|
||||
/// <param name="end">Ending position</param>
|
||||
/// <param name="rnd">RNG object to use</param>
|
||||
public static void Shuffle<T>(Span<T> items, int start, int end, Random rnd)
|
||||
/// <param name="items">Item collection</param>
|
||||
public static void Shuffle<T>(this Random rnd, Span<T> items)
|
||||
{
|
||||
for (int i = start; i < end; i++)
|
||||
int n = items.Length;
|
||||
for (int i = 0; i < n - 1; i++)
|
||||
{
|
||||
int index = i + rnd.Next(end - i);
|
||||
(items[index], items[i]) = (items[i], items[index]);
|
||||
int j = rnd.Next(i, n);
|
||||
if (j != i)
|
||||
(items[i], items[j]) = (items[j], items[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,21 +65,39 @@ public static class StringUtil
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an all-caps hex string to a byte array.
|
||||
/// Converts an all-caps hex string to a byte array. Expects no separation between byte tuples.
|
||||
/// </summary>
|
||||
public static byte[] ToByteArray(this string toTransform)
|
||||
{
|
||||
var result = new byte[toTransform.Length / 2];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var ofs = i << 1;
|
||||
var _0 = toTransform[ofs + 0];
|
||||
var _1 = toTransform[ofs + 1];
|
||||
result[i] = DecodeTuple(_0, _1);
|
||||
}
|
||||
LoadHexBytesTo(toTransform, result, 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an all-caps encoded ASCII-Text hex string to a byte array.
|
||||
/// </summary>
|
||||
public static void LoadHexBytesTo(Span<byte> dest, ReadOnlySpan<byte> str, int tupleSize)
|
||||
{
|
||||
// The input string is 2-char hex values optionally separated.
|
||||
// The destination array should always be larger or equal than the bytes written. Let the runtime bounds check us.
|
||||
// Iterate through the string without allocating.
|
||||
for (int i = 0, j = 0; i < str.Length; i += tupleSize)
|
||||
dest[j++] = DecodeTuple((char)str[i + 0], (char)str[i + 1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an all-caps hex string to a byte array.
|
||||
/// </summary>
|
||||
public static void LoadHexBytesTo(ReadOnlySpan<char> str, Span<byte> dest, int tupleSize)
|
||||
{
|
||||
// The input string is 2-char hex values optionally separated.
|
||||
// The destination array should always be larger or equal than the bytes written. Let the runtime bounds check us.
|
||||
// Iterate through the string without allocating.
|
||||
for (int i = 0, j = 0; i < str.Length; i += tupleSize)
|
||||
dest[j++] = DecodeTuple(str[i + 0], str[i + 1]);
|
||||
}
|
||||
|
||||
private static byte DecodeTuple(char _0, char _1)
|
||||
{
|
||||
byte result;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using PKHeX.Core;
|
||||
using static PKHeX.Core.MessageStrings;
|
||||
|
@ -47,8 +48,14 @@ public partial class EntityInstructionBuilder : UserControl
|
|||
L_PropType.Text = BatchEditing.GetPropertyType(CB_Property.Text, CB_Format.SelectedIndex);
|
||||
if (BatchEditing.TryGetHasProperty(Entity, CB_Property.Text, out var pi))
|
||||
{
|
||||
L_PropValue.Text = pi.GetValue(Entity)?.ToString();
|
||||
L_PropType.ForeColor = L_PropValue.ForeColor; // reset color
|
||||
L_PropType.ResetForeColor();
|
||||
|
||||
bool hasValue = GetPropertyDisplayText(pi, Entity, out var display);
|
||||
L_PropValue.Text = display;
|
||||
if (hasValue)
|
||||
L_PropValue.ResetForeColor();
|
||||
else
|
||||
L_PropValue.ForeColor = Color.Red;
|
||||
}
|
||||
else // no property, flag
|
||||
{
|
||||
|
@ -57,6 +64,26 @@ public partial class EntityInstructionBuilder : UserControl
|
|||
}
|
||||
}
|
||||
|
||||
private static bool GetPropertyDisplayText(PropertyInfo pi, PKM pk, out string display)
|
||||
{
|
||||
var type = pi.PropertyType;
|
||||
if (type.IsGenericType && typeof(Span<>) == type.GetGenericTypeDefinition())
|
||||
{
|
||||
display = pi.PropertyType.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
var value = pi.GetValue(pk);
|
||||
if (value?.ToString() is not {} x)
|
||||
{
|
||||
display = "null";
|
||||
return false;
|
||||
}
|
||||
|
||||
display = x;
|
||||
return true;
|
||||
}
|
||||
|
||||
public string Create()
|
||||
{
|
||||
if (CB_Property.SelectedIndex < 0)
|
||||
|
|
|
@ -322,7 +322,7 @@ public partial class SAV_Inventory : Form
|
|||
{
|
||||
items = (ushort[])items.Clone();
|
||||
if (shuffle)
|
||||
Util.Shuffle(items.AsSpan());
|
||||
Util.Rand.Shuffle(items.AsSpan());
|
||||
Array.Resize(ref items, pouch.Items.Length);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue