Minor minor perf

Small changes to reduce some allocations
This commit is contained in:
Kurt 2023-04-15 01:58:37 -07:00
parent e2fd292ffa
commit c73264d4f3
50 changed files with 338 additions and 219 deletions

View file

@ -120,8 +120,8 @@ public static class BallApplicator
static BallApplicator()
{
Span<Ball> exclude = stackalloc Ball[] {None, Poke};
Span<Ball> end = stackalloc Ball[] {Poke};
ReadOnlySpan<Ball> exclude = stackalloc Ball[] {None, Poke};
ReadOnlySpan<Ball> end = stackalloc Ball[] {Poke};
Span<Ball> all = stackalloc Ball[BallList.Length - exclude.Length];
all = all[..FillExcept(all, exclude, BallList)];

View file

@ -174,13 +174,13 @@ public static class CommonEdits
// Under this scenario, just force the Hidden Power type.
if (Array.IndexOf(Set.Moves, (ushort)Move.HiddenPower) != -1 && pk.HPType != Set.HiddenPowerType)
{
if (Array.Exists(Set.IVs, static iv => iv >= 30))
if (Set.IVs.AsSpan().IndexOfAny(30, 31) >= 0)
pk.SetHiddenPower(Set.HiddenPowerType);
}
// In Generation 1/2 Format sets, when EVs are not specified at all, it implies maximum EVs instead!
// Under this scenario, just apply maximum EVs (65535).
if (Array.TrueForAll(Set.EVs, static ev => ev == 0))
if (Set.EVs.AsSpan().IndexOfAnyExcept(0) == -1)
gb.MaxEVs();
else
pk.SetEVs(Set.EVs);

View file

@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
@ -10,22 +10,53 @@ namespace PKHeX.Core;
public sealed class EventWork<T> : EventVar where T : struct
{
public T Value;
/// <summary>
/// Values with known behavior. They are labeled with a humanized string.
/// </summary>
public readonly IList<EventWorkVal> Options = new List<EventWorkVal> { new() };
public EventWork(int index, EventVarType t, IReadOnlyList<string> pieces) : base(index, t, pieces[1])
{
if (pieces.Count < 3)
return;
if (pieces.Count >= 3)
AddLabeledValueOptions(pieces[2], Options);
}
var items = pieces[2]
.Split(',')
.Select(z => z.Split(':'))
.Where(z => z.Length == 2);
private const char OptionSeparator = ','; // KVP,KVP
private const char OptionValueSeparator = ':'; // Name:Value
foreach (var s in items)
/// <summary>
/// Adds the key-value pair options to the <see cref="list"/>.
/// </summary>
/// <param name="text">Text line containing key-value pairs.</param>
/// <param name="list">Object that stores the results</param>
/// <remarks>
/// Decodes in the format of Value:Text,Value:Text.
/// Key-value pairs are separated by <see cref="OptionSeparator"/>.
/// Key and value are separated by <see cref="OptionValueSeparator"/>.
/// </remarks>
private static void AddLabeledValueOptions(ReadOnlySpan<char> text, ICollection<EventWorkVal> list)
{
int i = 0;
while (i < text.Length)
{
if (int.TryParse(s[0], out var value))
Options.Add(new EventWorkVal(s[1], value));
// Scan for the length of this key-value pair.
int commaIndex = text[i..].IndexOf(OptionSeparator);
if (commaIndex == -1)
commaIndex = text.Length - i; // end of string
// Scan for the length of the key.
var pair = text.Slice(i, commaIndex);
i += commaIndex + 1; // skip to next kvp for next iteration
int colonIndex = pair.IndexOf(OptionValueSeparator);
if (colonIndex == -1)
continue; // invalid kvp
// Split to get the key and value.
var valueText = pair[..colonIndex];
var display = pair[(colonIndex + 1)..];
if (int.TryParse(valueText, out int value))
list.Add(new EventWorkVal(display.ToString(), value));
}
}
}

View file

@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;
@ -10,7 +9,6 @@ public abstract record EncounterArea(GameVersion Version) : IVersion
{
public int Location { get; protected init; }
public SlotType Type { get; protected init; }
protected abstract IReadOnlyList<EncounterSlot> Raw { get; }
/// <summary>
/// Gets the slots contained in the area that match the provided data.
@ -26,6 +24,9 @@ public abstract record EncounterArea(GameVersion Version) : IVersion
/// <param name="location">Met Location ID</param>
/// <returns>True if possibly originated from this area, false otherwise.</returns>
public virtual bool IsMatchLocation(int location) => Location == location;
public bool HasSpecies(ushort species) => Raw.Any(z => z.Species == species);
}
internal interface IMemorySpeciesArea
{
bool HasSpecies(ushort species);
}

View file

@ -12,8 +12,6 @@ public sealed record EncounterArea1 : EncounterArea
public readonly int Rate;
public readonly EncounterSlot1[] Slots;
protected override IReadOnlyList<EncounterSlot1> Raw => Slots;
public static EncounterArea1[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea1[input.Length];

View file

@ -18,8 +18,6 @@ public sealed record EncounterArea2 : EncounterArea
public readonly byte[]? Rates;
public readonly EncounterSlot2[] Slots;
protected override IReadOnlyList<EncounterSlot2> Raw => Slots;
public static EncounterArea2[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea2[input.Length];

View file

@ -13,8 +13,6 @@ public sealed record EncounterArea3 : EncounterArea
public readonly int Rate;
public readonly EncounterSlot3[] Slots;
protected override IReadOnlyList<EncounterSlot3> Raw => Slots;
public static EncounterArea3[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea3[input.Length];

View file

@ -11,8 +11,6 @@ public sealed record EncounterArea3XD : EncounterArea
{
public readonly EncounterSlot3PokeSpot[] Slots;
protected override IReadOnlyList<EncounterSlot3PokeSpot> Raw => Slots;
public EncounterArea3XD(int loc, ushort s0, byte l0, ushort s1, byte l1, ushort s2, byte l2) : base(GameVersion.XD)
{
Location = loc;

View file

@ -14,8 +14,6 @@ public sealed record EncounterArea4 : EncounterArea
public readonly GroundTileAllowed GroundTile;
public readonly EncounterSlot4[] Slots;
protected override IReadOnlyList<EncounterSlot4> Raw => Slots;
public static EncounterArea4[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea4[input.Length];

View file

@ -12,8 +12,6 @@ public sealed record EncounterArea5 : EncounterArea
{
public readonly EncounterSlot5[] Slots;
protected override IReadOnlyList<EncounterSlot5> Raw => Slots;
public static EncounterArea5[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea5[input.Length];

View file

@ -8,12 +8,10 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="GameVersion.ORAS"/> encounter area
/// </summary>
public sealed record EncounterArea6AO : EncounterArea
public sealed record EncounterArea6AO : EncounterArea, IMemorySpeciesArea
{
public readonly EncounterSlot6AO[] Slots;
protected override IReadOnlyList<EncounterSlot6AO> Raw => Slots;
public static EncounterArea6AO[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea6AO[input.Length];
@ -88,4 +86,14 @@ public sealed record EncounterArea6AO : EncounterArea
}
}
}
public bool HasSpecies(ushort species)
{
foreach (var slot in Slots)
{
if (slot.Species == species)
return true;
}
return false;
}
}

View file

@ -8,12 +8,10 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="GameVersion.XY"/> encounter area
/// </summary>
public sealed record EncounterArea6XY : EncounterArea
public sealed record EncounterArea6XY : EncounterArea, IMemorySpeciesArea
{
public readonly EncounterSlot6XY[] Slots;
protected override IReadOnlyList<EncounterSlot6XY> Raw => Slots;
public static EncounterArea6XY[] GetAreas(BinLinkerAccessor input, GameVersion game, EncounterArea6XY safari)
{
int count = input.Length;
@ -155,4 +153,14 @@ public sealed record EncounterArea6XY : EncounterArea
}
return existsForm;
}
public bool HasSpecies(ushort species)
{
foreach (var slot in Slots)
{
if (slot.Species == species)
return true;
}
return false;
}
}

View file

@ -12,8 +12,6 @@ public sealed record EncounterArea7 : EncounterArea
{
public readonly EncounterSlot7[] Slots;
protected override IReadOnlyList<EncounterSlot7> Raw => Slots;
public static EncounterArea7[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea7[input.Length];

View file

@ -12,8 +12,6 @@ public sealed record EncounterArea7b : EncounterArea
{
public readonly EncounterSlot7b[] Slots;
protected override IReadOnlyList<EncounterSlot7b> Raw => Slots;
public static EncounterArea7b[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea7b[input.Length];

View file

@ -17,8 +17,6 @@ public sealed record EncounterArea7g : EncounterArea, ISpeciesForm
public byte Form { get; }
public readonly EncounterSlot7GO[] Slots;
protected override IReadOnlyList<EncounterSlot7GO> Raw => Slots;
private EncounterArea7g(ushort species, byte form, EncounterSlot7GO[] slots) : base(GameVersion.GO)
{
Species = species;

View file

@ -10,11 +10,10 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="GameVersion.SWSH"/> encounter area
/// </summary>
public sealed record EncounterArea8 : EncounterArea
public sealed record EncounterArea8 : EncounterArea, IMemorySpeciesArea
{
public readonly EncounterSlot8[] Slots;
protected override IReadOnlyList<EncounterSlot8> Raw => Slots;
/// <summary>
/// Slots from this area can cross over to another area, resulting in a different met location.
/// </summary>
@ -403,6 +402,16 @@ public sealed record EncounterArea8 : EncounterArea
return slots;
}
public bool HasSpecies(ushort species)
{
foreach (var slot in Slots)
{
if (slot.Species == species)
return true;
}
return false;
}
}
/// <summary>

View file

@ -13,8 +13,6 @@ public sealed record EncounterArea8a : EncounterArea
public readonly EncounterSlot8a[] Slots;
private readonly byte[] Locations;
protected override IReadOnlyList<EncounterSlot8a> Raw => Slots;
public override bool IsMatchLocation(int location)
{
return Array.IndexOf(Locations, (byte)location) != -1;

View file

@ -12,8 +12,6 @@ public sealed record EncounterArea8b : EncounterArea
{
public readonly EncounterSlot8b[] Slots;
protected override IReadOnlyList<EncounterSlot8b> Raw => Slots;
public static EncounterArea8b[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea8b[input.Length];

View file

@ -17,8 +17,6 @@ public sealed record EncounterArea8g : EncounterArea, ISpeciesForm
public byte Form { get; }
public readonly EncounterSlot8GO[] Slots;
protected override IReadOnlyList<EncounterSlot8GO> Raw => Slots;
private EncounterArea8g(ushort species, byte form, EncounterSlot8GO[] slots) : base(GameVersion.GO)
{
Species = species;

View file

@ -14,8 +14,6 @@ public sealed record EncounterArea9 : EncounterArea
public ushort CrossFrom { get; }
protected override IReadOnlyList<EncounterSlot9> Raw => Slots;
public static EncounterArea9[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea9[input.Length];

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core.Bulk;
@ -41,7 +40,7 @@ public sealed class BulkAnalysis
CloneFlags = new bool[AllData.Count];
ScanAll();
Valid = Parse.Count == 0 || Parse.All(z => z.Valid);
Valid = Parse.Count == 0 || Parse.TrueForAll(static z => z.Valid);
}
// Remove things that aren't actual stored data, or already flagged by legality checks.

View file

@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using System;
namespace PKHeX.Core;
@ -17,54 +16,58 @@ internal static class EncountersWC3
new() { Species = 385, Level = 05, ID32 = 20043, OT_Gender = 0, Version = GameVersion.R, Method = PIDType.BACD_R, OT_Name = "WISHMKR", CardTitle = "Wishmaker Jirachi", Language = (int)LanguageID.English },
};
private static IEnumerable<WC3> GetIngameCXDData()
private static WC3[] GetIngameCXDData()
{
var langs = new[]{LanguageID.Japanese, LanguageID.English, LanguageID.French, LanguageID.Italian, LanguageID.German, LanguageID.Spanish};
var langs = LanguageCXD;
string[] h = {string.Empty, "ダニー", "HORDEL", "VOLKER", "ODINO", "HORAZ", string.Empty, "HORDEL"};
string[] d = {string.Empty, "ギンザル", "DUKING", "DOKING", "RODRIGO", "GRAND", string.Empty, "GERMÁN"};
string[] z = {string.Empty, "コンセント", "ZAPRONG", "ZAPRONG", "ZAPRONG", "ZAPRONG", string.Empty, "ZAPRONG"};
return langs.SelectMany(l => GetIngame((int)l));
IEnumerable<WC3> GetIngame(int l)
const int count = 5;
var result = new WC3[count * langs.Length];
for (int i = 0, ofs = 0; i < langs.Length; i++)
{
var id = (LanguageID) l;
return new WC3[]
{
// Colosseum
new() { Species = 311, Level = 13, Language = l, Location = 254, ID32 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.COLO, CardTitle = $"Special Gift ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, Moves = new(045, 086, 098, 270) }, // Plusle @ Ingame Trade
// XD
new(true) { Species = 239, Level = 20, Language = l, Location = 164, TID16 = 41400, OT_Gender = 0, OT_Name = h[l], Version = GameVersion.XD, CardTitle = $"Trade Togepi ({id})", Method = PIDType.CXD, Moves = new(008, 007, 009, 238), Nickname = z[l] }, // Elekid @ Snagem Hideout
new(true) { Species = 307, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Trapinch ({id})", Method = PIDType.CXD, Moves = new(223, 093, 247, 197) }, // Meditite @ Pyrite Town
new(true) { Species = 213, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Surskit ({id})", Method = PIDType.CXD, Moves = new(092, 164, 188, 227) }, // Shuckle @ Pyrite Town
new(true) { Species = 246, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Wooper ({id})", Method = PIDType.CXD, Moves = new(201, 349, 044, 200) }, // Larvitar @ Pyrite Town
};
var id = langs[i];
var l = (int)id;
// Colosseum
result[ofs++] = new() { Species = 311, Level = 13, Language = l, Location = 254, ID32 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.COLO, CardTitle = $"Special Gift ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, Moves = new(045, 086, 098, 270) }; // Plusle @ Ingame Trade
// XD
result[ofs++] = new(true) { Species = 239, Level = 20, Language = l, Location = 164, TID16 = 41400, OT_Gender = 0, OT_Name = h[l], Version = GameVersion.XD, CardTitle = $"Trade Togepi ({id})", Method = PIDType.CXD, Moves = new(008, 007, 009, 238), Nickname = z[l] }; // Elekid @ Snagem Hideout
result[ofs++] = new(true) { Species = 307, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Trapinch ({id})", Method = PIDType.CXD, Moves = new(223, 093, 247, 197) }; // Meditite @ Pyrite Town
result[ofs++] = new(true) { Species = 213, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Surskit ({id})", Method = PIDType.CXD, Moves = new(092, 164, 188, 227) }; // Shuckle @ Pyrite Town
result[ofs++] = new(true) { Species = 246, Level = 20, Language = l, Location = 116, TID16 = 37149, OT_Gender = 0, OT_Name = d[l], Version = GameVersion.XD, CardTitle = $"Trade Wooper ({id})", Method = PIDType.CXD, Moves = new(201, 349, 044, 200) }; // Larvitar @ Pyrite Town
}
return result;
}
private static IEnumerable<WC3> GetIngameCXDDataMainline()
private static ReadOnlySpan<LanguageID> LanguageCXD => new[] { LanguageID.Japanese, LanguageID.English, LanguageID.French, LanguageID.Italian, LanguageID.German, LanguageID.Spanish };
private static WC3[] GetIngameCXDDataMainline()
{
var langs = new[] { LanguageID.Japanese, LanguageID.English, LanguageID.French, LanguageID.Italian, LanguageID.German, LanguageID.Spanish };
var langs = LanguageCXD;
string[] p = { string.Empty, "コロシアム", "COLOS", "COLOSSEUM", "ARENA", "COLOSSEUM", string.Empty, "CLAUDIO" };
string[] c = { string.Empty, "アゲト", "AGATE", "SAMARAGD", "SOFO", "EMERITAE", string.Empty, "ÁGATA" };
string[] m = { string.Empty, "バトルやま", "MATTLE", "MT BATAILL", "MONTE LOTT", "DUELLBERG", string.Empty, "ERNESTO" }; // truncated on ck3->pk3 transfer
return langs.SelectMany(l => GetIngame((int)l));
IEnumerable<WC3> GetIngame(int l)
const int count = 3;
var result = new WC3[count * langs.Length];
for (int i = 0, ofs = 0; i < langs.Length; i++)
{
var id = (LanguageID)l;
var id = langs[i];
var l = (int)id;
var nd = id != LanguageID.Japanese;
return new WC3[]
{
// Colosseum
new() { Species = 025, Level = 10, Language = l, Location = 255, ID32 = 31121, OT_Gender = 0, OT_Name = p[l], Version = GameVersion.R, CardTitle = $"Colosseum Pikachu ({id})",Method = PIDType.CXD, Shiny = Shiny.Never, NotDistributed = nd }, // Colosseum Pikachu bonus gift
new() { Species = 251, Level = 10, Language = l, Location = 255, ID32 = 31121, OT_Gender = 1, OT_Name = c[l], Version = GameVersion.R, CardTitle = $"Agate Celebi ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, NotDistributed = nd }, // Ageto Celebi bonus gift
new() { Species = 250, Level = 70, Language = l, Location = 255, ID32 = 10048, OT_Gender = 0, OT_Name = m[l], Version = GameVersion.S, CardTitle = $"Mt. Battle Ho-Oh ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, Moves = new(105, 126, 241, 129) }, // Ho-oh @ Mt. Battle
};
// Colosseum
result[ofs++] = new() { Species = 025, Level = 10, Language = l, Location = 255, ID32 = 31121, OT_Gender = 0, OT_Name = p[l], Version = GameVersion.R, CardTitle = $"Colosseum Pikachu ({id})",Method = PIDType.CXD, Shiny = Shiny.Never, NotDistributed = nd }; // Colosseum Pikachu bonus gift
result[ofs++] = new() { Species = 251, Level = 10, Language = l, Location = 255, ID32 = 31121, OT_Gender = 1, OT_Name = c[l], Version = GameVersion.R, CardTitle = $"Agate Celebi ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, NotDistributed = nd }; // Ageto Celebi bonus gift
result[ofs++] = new() { Species = 250, Level = 70, Language = l, Location = 255, ID32 = 10048, OT_Gender = 0, OT_Name = m[l], Version = GameVersion.S, CardTitle = $"Mt. Battle Ho-Oh ({id})", Method = PIDType.CXD, Shiny = Shiny.Never, Moves = new(105, 126, 241, 129) }; // Ho-oh @ Mt. Battle
}
return result;
}
internal static readonly WC3[] Encounter_WC3CXD = GetIngameCXDData().ToArray();
internal static readonly WC3[] Encounter_WC3CXDMain = GetIngameCXDDataMainline().ToArray();
internal static readonly WC3[] Encounter_WC3CXD = GetIngameCXDData();
internal static readonly WC3[] Encounter_WC3CXDMain = GetIngameCXDDataMainline();
internal static readonly WC3[] Encounter_Event3 = ArrayUtil.ConcatAll(Encounter_Event3_Special, Encounter_WC3CXD, Encounter_WC3CXDMain);

View file

@ -1,4 +1,4 @@
namespace PKHeX.Core;
namespace PKHeX.Core;
/// <summary>
/// Generation 5 Static Encounter from N
@ -47,8 +47,7 @@ internal sealed record EncounterStatic5N : EncounterStatic5
pk5.IV_HP = pk5.IV_ATK = pk5.IV_DEF = pk5.IV_SPA = pk5.IV_SPD = pk5.IV_SPE = 30;
pk5.NSparkle = NSparkle;
pk5.OT_Name = GetOT(lang);
pk5.TID16 = 00002;
pk5.SID16 = 00000;
pk5.ID32 = 00002;
}
public static string GetOT(int lang) => lang == (int)LanguageID.Japanese ? "" : "N";

View file

@ -130,7 +130,7 @@ public static class EncounterFinder
}
else if (pk is PK1 pk1)
{
var hasGen2 = Array.Exists(info.Moves, z => z.Generation is 2);
var hasGen2 = MoveInfo.IsAnyFromGeneration(2, info.Moves);
if (hasGen2)
{
if (!ParseSettings.AllowGen1Tradeback)

View file

@ -2,7 +2,6 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using static PKHeX.Core.EncounterTypeGroup;
@ -172,7 +171,7 @@ public static class EncounterMovesetGenerator
/// <param name="moves">Moves that the resulting <see cref="IEncounterable"/> must be able to learn.</param>
/// <param name="version">Specific version to iterate for.</param>
/// <returns>A consumable <see cref="IEncounterable"/> list of possible encounters.</returns>
private static IEnumerable<IEncounterable> GenerateVersionEncounters(PKM pk, ReadOnlySpan<ushort> moves, GameVersion version)
private static IEnumerable<IEncounterable> GenerateVersionEncounters(PKM pk, ushort[] moves, GameVersion version)
{
pk.Version = (int)version;
@ -185,7 +184,11 @@ public static class EncounterMovesetGenerator
var needs = GetNeededMoves(pk, moves, version);
var generator = EncounterGenerator.GetGenerator(version);
return PriorityList.SelectMany(type => GetPossibleOfType(pk, needs, version, type, chain, generator));
foreach (var type in PriorityList)
{
foreach (var enc in GetPossibleOfType(pk, needs, version, type, chain, generator))
yield return enc;
}
}
private static bool IsSane(PKM pk, ReadOnlySpan<ushort> moves)

View file

@ -75,7 +75,7 @@ public static class EncounterLearn
{
if (species == 0)
return Array.Empty<IEncounterable>();
if (Array.Exists(moves, z => z == 0))
if (moves.AsSpan().Contains<ushort>(0))
return Array.Empty<IEncounterable>();
var vers = GameUtil.GameVersions;

View file

@ -29,10 +29,22 @@ public static class EncounterSuggestion
if (s is null)
return GetSuggestedEncounter(pk, w, loc);
bool isDefinitelySlot = Array.Exists(chain, z => z.Species == w.Species && z.Form == w.Form);
bool isDefinitelyStatic = Array.Exists(chain, z => z.Species == s.Species && z.Form == s.Form);
IEncounterable obj = (isDefinitelySlot || !isDefinitelyStatic) ? w : s;
return GetSuggestedEncounter(pk, obj, loc);
// Prefer the wild slot; fall back to wild slot if none are exact match.
if (IsSpeciesFormMatch(chain, w))
return GetSuggestedEncounter(pk, w, loc);
if (IsSpeciesFormMatch(chain, s))
return GetSuggestedEncounter(pk, s, loc);
return GetSuggestedEncounter(pk, w, loc);
}
private static bool IsSpeciesFormMatch(ReadOnlySpan<EvoCriteria> evos, ISpeciesForm encounter)
{
foreach (var evo in evos)
{
if (evo.Species == encounter.Species && evo.Form == encounter.Form)
return true;
}
return false;
}
private static EncounterSuggestionData GetSuggestedEncounterEgg(PKM pk, int loc = -1)

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core;
/// <summary>
/// Logic for creating a formatted text summary of an encounter.
/// </summary>
public static class EncounterText
{
public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc) => GetTextLines(enc, GameInfo.Strings);
public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc, GameStrings strings)
{
var lines = new List<string>();
var str = strings.Species;
var name = (uint)enc.Species < str.Count ? str[enc.Species] : enc.Species.ToString();
var EncounterName = $"{(enc is IEncounterable ie ? ie.LongName : "Special")} ({name})";
lines.Add(string.Format(L_FEncounterType_0, EncounterName));
if (enc is MysteryGift mg)
{
lines.AddRange(mg.GetDescription());
}
else if (enc is IMoveset m)
{
var moves = m.Moves;
if (moves.HasMoves)
{
string result = moves.GetMovesetLine(strings.movelist);
lines.Add(result);
}
}
var el = enc as ILocation;
var loc = el?.GetEncounterLocation(enc.Generation, (int)enc.Version);
if (!string.IsNullOrEmpty(loc))
lines.Add(string.Format(L_F0_1, "Location", loc));
var game = enc.Version.IsValidSavedVersion() ? strings.gamelist[(int)enc.Version] : enc.Version.ToString();
lines.Add(string.Format(L_F0_1, nameof(GameVersion), game));
lines.Add(enc.LevelMin == enc.LevelMax
? $"Level: {enc.LevelMin}"
: $"Level: {enc.LevelMin}-{enc.LevelMax}");
#if DEBUG
// Record types! Can get a nice summary.
// Won't work neatly for Mystery Gift types since those aren't record types.
if (enc is not MysteryGift)
{
// ReSharper disable once ConstantNullCoalescingCondition
var raw = enc.ToString() ?? throw new ArgumentNullException(nameof(enc));
lines.AddRange(raw.Split(',', '}', '{'));
}
#endif
return lines;
}
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.Encounters6XY;
using static PKHeX.Core.Encounters6AO;
@ -184,7 +183,7 @@ public static class MemoryPermissions
},
EntityContext.Gen8 => version switch
{
GameVersion.Any => GetCanBeCaptured(species, SlotsSW.Concat(SlotsSH), StaticSW.Concat(StaticSH)),
GameVersion.Any => GetCanBeCaptured(species, SlotsSW, StaticSW) || GetCanBeCaptured(species, SlotsSH, StaticSH),
GameVersion.SW => GetCanBeCaptured(species, SlotsSW, StaticSW),
GameVersion.SH => GetCanBeCaptured(species, SlotsSH, StaticSH),
_ => false,
@ -192,12 +191,18 @@ public static class MemoryPermissions
_ => false,
};
private static bool GetCanBeCaptured(ushort species, IEnumerable<EncounterArea> area, IEnumerable<EncounterStatic> statics)
private static bool GetCanBeCaptured<TArea, TStatic>(ushort species, TArea[] areas, TStatic[] statics) where TArea : IMemorySpeciesArea where TStatic : IEncounterTemplate
{
if (area.Any(loc => loc.HasSpecies(species)))
return true;
if (statics.Any(enc => enc.Species == species && !enc.Gift))
return true;
foreach (var area in areas)
{
if (area.HasSpecies(species))
return true;
}
foreach (var s in statics)
{
if (s.Species == species)
return true;
}
return false;
}

View file

@ -42,21 +42,39 @@ public sealed class HyperTrainingVerifier : Verifier
// already checked for 6IV, therefore we're flawed on at least one IV
if (t.IsHyperTrainedAll())
{
// SV gold bottle cap applies to all IVs regardless
// LGPE gold bottle cap applies to all IVs regardless
var evos = data.Info.EvoChainsAllGens;
if (evos.HasVisitedGen9 && Array.Exists(evos.Gen9, x => x.LevelMax >= 50))
return;
if (evos.HasVisitedLGPE && Array.Exists(evos.Gen7b, x => x.LevelMax >= 100))
if (HasVisitedGoldBottleFlawless(data.Info.EvoChainsAllGens))
return;
// Otherwise, could not have hyper trained a flawless IV. Flag a flawless IV with the usual logic.
}
if (IsFlawlessHyperTrained(pk, t, max))
data.AddLine(GetInvalid(LHyperPerfectOne));
}
private static bool HasVisitedGoldBottleFlawless(EvolutionHistory evos)
{
// S/V gold bottle cap applies to all IVs regardless
// LGP/E gold bottle cap applies to all IVs regardless
foreach (ref var x in evos.Gen9.AsSpan())
{
if (x.LevelMax >= 50)
return true;
}
foreach (ref var x in evos.Gen7b.AsSpan())
{
if (x.LevelMax >= 100)
return true;
}
return false;
}
public static bool IsFlawlessHyperTrained(PKM pk, IHyperTrain t, int max)
{
for (int i = 0; i < 6; i++) // Check individual IVs
{
if (pk.GetIV(i) != max || !t.IsHyperTrained(i))
continue;
data.AddLine(GetInvalid(LHyperPerfectOne));
break;
if (pk.GetIV(i) == max && t.IsHyperTrained(i))
return true;
}
return false;
}
}

View file

@ -1,4 +1,3 @@
using System;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core;
@ -131,9 +130,9 @@ public sealed class LevelVerifier : Verifier
var moves = data.Info.Moves;
// Gen2 stuff can be traded between Gen2 games holding an Everstone, assuming it hasn't been transferred to Gen1 for special moves.
if (enc.Generation == 2)
return Array.Exists(moves, z => z.Generation == 1);
return MoveInfo.IsAnyFromGeneration(1, moves);
// Gen1 stuff can only be un-evolved if it was never traded from the OT.
if (Array.Exists(moves, z => z.Generation == 2))
if (MoveInfo.IsAnyFromGeneration(2, moves))
return true; // traded to Gen2 for special moves
if (pk.Format != 1)
return true; // traded to Gen2 (current state)

View file

@ -42,20 +42,7 @@ public sealed class MemoryVerifier : Verifier
// Actionable HM moves
int hmIndex = MemoryContext6.MoveSpecificMemoryHM.IndexOf(memory.MemoryID);
if (hmIndex != -1)
{
if (context != Gen6) // Gen8 has no HMs, so this memory can never exist.
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
if (pk.Species != (int)Species.Smeargle)
{
// All AO hidden machine permissions are super-sets of Gen 3-5 games.
// Don't need to check the move history -- a learned HM in a prior game can still be learned in Gen6.
var evos = info.EvoChainsAllGens.Gen6;
var exists = Array.Exists(evos, z => PersonalTable.AO.GetFormEntry(z.Species, 0).GetIsLearnHM(hmIndex));
if (!exists)
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
}
}
return VerifyMemoryHM6(context, info, mem, memory, hmIndex);
if (mem.IsInvalidGeneralLocationMemoryValue(memory.MemoryID, memory.Variable, info.EncounterMatch, pk))
return GetInvalid(string.Format(LMemoryArgBadLocation, memory.Handler));
@ -71,15 +58,15 @@ public sealed class MemoryVerifier : Verifier
// {0} saw {2} carrying {1} on its back. {4} that {3}.
case 21 when context != Gen6 || !PersonalTable.AO.GetFormEntry(memory.Variable, 0).GetIsLearnHM(2): // Fly
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
return BadSpeciesMove(memory.Handler);
// {0} used {2} at {1}s instruction, but it had no effect. {4} that {3}.
// The Move Deleter that {0} met through {1} made it forget {2}. {4} that {3}.
case 16 or 48 when !CanKnowMove(pk, memory, context, info, memory.MemoryID == 16):
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
return BadSpeciesMove(memory.Handler);
case 49 when memory.Variable == 0 || !GetCanRelearnMove(pk, memory.Variable, context, info.EvoChainsAllGens, info.EncounterOriginal):
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
return BadSpeciesMove(memory.Handler);
// Dynamaxing
// {0} battled at {1}s side against {2} that Dynamaxed. {4} that {3}.
@ -92,7 +79,7 @@ public sealed class MemoryVerifier : Verifier
// {0} studied about how to use {2} in a Box, thinking about {1}. {4} that {3}.
// {0} practiced its cool pose for the move {2} in a Box, wishing to be praised by {1}. {4} that {3}.
case 80 or 81 when !CanKnowMove(pk, memory, context, info):
return Get(string.Format(LMemoryArgBadMove, memory.Handler), Severity.Invalid);
return BadSpeciesMove(memory.Handler);
// Species
// With {1}, {0} went fishing, and they caught {2}. {4} that {3}.
@ -137,6 +124,31 @@ public sealed class MemoryVerifier : Verifier
return VerifyCommonMemoryEtc(memory, mem);
}
private CheckResult VerifyMemoryHM6(EntityContext context, LegalInfo info, MemoryContext mem, MemoryVariableSet memory, int hmIndex)
{
if (context != Gen6) // Gen8 has no HMs, so this memory can never exist.
return BadSpeciesMove(memory.Handler);
if (info.EncounterMatch.Species == (int)Species.Smeargle)
return VerifyCommonMemoryEtc(memory, mem);
// All AO hidden machine permissions are super-sets of Gen 3-5 games.
// Don't need to check the move history -- a learned HM in a prior game can still be learned in Gen6.
var pt = PersonalTable.AO;
foreach (ref var evo in info.EvoChainsAllGens.Gen6.AsSpan())
{
var entry = pt[evo.Species];
var canLearn = entry.GetIsLearnHM(hmIndex);
if (canLearn)
return VerifyCommonMemoryEtc(memory, mem);
break;
}
return BadSpeciesMove(memory.Handler);
}
private CheckResult BadSpeciesMove(string handler) => GetInvalid(string.Format(LMemoryArgBadMove, handler));
private CheckResult VerifyCommonMemoryEtc(MemoryVariableSet memory, MemoryContext context)
{
if (!context.CanHaveIntensity(memory.MemoryID, memory.Intensity))

View file

@ -312,7 +312,7 @@ public sealed class MiscVerifier : Verifier
CheckResult GetWasNotTradeback(TimeCapsuleEvaluation timeCapsuleEvalution)
{
if (Array.Exists(data.Info.Moves, z => z.Generation == 2))
if (MoveInfo.IsAnyFromGeneration(2, data.Info.Moves))
return GetInvalid(LG1CatchRateItem);
var e = data.EncounterMatch;
if (e is EncounterStatic1E {Version: GameVersion.Stadium} or EncounterTrade1)

View file

@ -179,4 +179,14 @@ public static class MoveInfo
return 0;
return types[move];
}
public static bool IsAnyFromGeneration(int generation, ReadOnlySpan<MoveResult> moves)
{
foreach (var move in moves)
{
if (move.Generation == generation)
return true;
}
return false;
}
}

View file

@ -7,7 +7,7 @@ namespace PKHeX.Core;
/// </summary>
public sealed class PersonalTable2 : IPersonalTable, IPersonalTable<PersonalInfo2>
{
private readonly PersonalInfo2[] Table; // internal to share with Gen1 tables
private readonly PersonalInfo2[] Table;
private const int SIZE = PersonalInfo2.SIZE;
private const int MaxSpecies = Legal.MaxSpeciesID_2;
public int MaxSpeciesID => MaxSpecies;

View file

@ -7,7 +7,7 @@ namespace PKHeX.Core;
/// </summary>
public sealed class PersonalTable3 : IPersonalTable, IPersonalTable<PersonalInfo3>
{
private readonly PersonalInfo3[] Table; // internal to share with Gen1 tables
private readonly PersonalInfo3[] Table;
private const int SIZE = PersonalInfo3.SIZE;
private const int MaxSpecies = Legal.MaxSpeciesID_3;
public int MaxSpeciesID => MaxSpecies;

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core;

View file

@ -174,6 +174,8 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37
set => Data[CGearSkinInfoOffset + 2] = Data[PlayerData.Offset + (this is SAV5B2W2 ? 0x6C : 0x54)] = value ? (byte)1 : (byte)0;
}
private static ReadOnlySpan<byte> DLCFooter => new byte[] { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 };
public byte[] CGearSkinData
{
get
@ -193,8 +195,7 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37
WriteUInt16LittleEndian(footer[2..], chk); // checksum
WriteUInt16LittleEndian(footer[0x100..], chk); // second checksum
Span<byte> dlcfooter = stackalloc byte[] { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 };
dlcfooter.CopyTo(footer[0x102..]);
DLCFooter.CopyTo(footer[0x102..]);
ushort skinchkval = Checksums.CRC16_CCITT(footer[0x100..0x104]);
WriteUInt16LittleEndian(footer[0x112..], skinchkval);

View file

@ -245,27 +245,31 @@ public sealed class CGearBackground
if (Width * Height * bpp != data.Length)
throw new ArgumentException("Invalid image data size.");
var pixels = MemoryMarshal.Cast<byte, int>(data);
var colors = GetColorData(pixels);
var colors = GetColorData(data);
var palette = colors.Distinct().ToArray();
if (palette.Length > ColorCount)
throw new ArgumentException($"Too many unique colors. Expected <= 16, got {palette.Length}");
var Palette = colors.Distinct().ToArray();
if (Palette.Length > ColorCount)
throw new ArgumentException($"Too many unique colors. Expected <= 16, got {Palette.Length}");
var tiles = GetTiles(colors, Palette);
var tiles = GetTiles(colors, palette);
GetTileList(tiles, out List<Tile> tilelist, out TileMap tm);
if (tilelist.Count >= 0xFF)
throw new ArgumentException($"Too many unique tiles. Expected < 256, received {tilelist.Count}.");
// Finished!
return new CGearBackground(Palette, tilelist.ToArray(), tm);
return new CGearBackground(palette, tilelist.ToArray(), tm);
}
private static int[] GetColorData(ReadOnlySpan<int> pixels)
private static int[] GetColorData(ReadOnlySpan<byte> data)
{
var pixels = MemoryMarshal.Cast<byte, int>(data);
int[] colors = new int[pixels.Length];
for (int i = 0; i < pixels.Length; i++)
colors[i] = GetRGB555_32(pixels[i]);
{
var pixel = pixels[i];
if (!BitConverter.IsLittleEndian)
pixel = ReverseEndianness(pixel);
colors[i] = GetRGB555_32(pixel);
}
return colors;
}
@ -327,10 +331,10 @@ public sealed class CGearBackground
tm.Rotations[tileIndex] = 0;
}
private CGearBackground(int[] Palette, Tile[] tilelist, TileMap tm)
private CGearBackground(int[] palette, Tile[] tilelist, TileMap tm)
{
Map = tm;
ColorPalette = Palette;
ColorPalette = palette;
Tiles = tilelist;
}

View file

@ -1,7 +1,6 @@
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace PKHeX.Core;
@ -45,10 +44,16 @@ public sealed class GoParkStorage : SaveBlock<SAV7b>, IEnumerable<GP1>
this[i] = value[i];
}
public IEnumerable<string> DumpAll(IReadOnlyList<string> speciesNames) => GetAllEntities()
.Select((z, i) => (Entry: z, Index: i))
.Where(z => z.Entry.Species > 0)
.Select(z => z.Entry.Dump(speciesNames, z.Index));
public IEnumerable<string> DumpAll(IReadOnlyList<string> speciesNames)
{
var arr = GetAllEntities();
for (int i = 0; i < arr.Length; i++)
{
var entry = arr[i];
if (entry.Species > 0)
yield return entry.Dump(speciesNames, i);
}
}
public IEnumerator<GP1> GetEnumerator() => (IEnumerator<GP1>)GetAllEntities().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetAllEntities().GetEnumerator();

View file

@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.Windows.Forms;
using PKHeX.Core;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.WinForms.Controls;
public sealed class SummaryPreviewer
@ -43,7 +41,7 @@ public sealed class SummaryPreviewer
if (Main.Settings.Hover.HoverSlotShowText)
{
var lines = GetTextLines(enc);
var lines = enc.GetTextLines(GameInfo.Strings);
var text = string.Join(Environment.NewLine, lines);
ShowSet.SetToolTip(pb, text);
}
@ -52,49 +50,6 @@ public sealed class SummaryPreviewer
Cry.PlayCry(enc, enc.Context);
}
public static IEnumerable<string> GetTextLines(IEncounterInfo enc)
{
var lines = new List<string>();
var str = GameInfo.Strings.Species;
var name = (uint) enc.Species < str.Count ? str[enc.Species] : enc.Species.ToString();
var EncounterName = $"{(enc is IEncounterable ie ? ie.LongName : "Special")} ({name})";
lines.Add(string.Format(L_FEncounterType_0, EncounterName));
if (enc is MysteryGift mg)
{
lines.AddRange(mg.GetDescription());
}
else if (enc is IMoveset m)
{
var moves = m.Moves;
if (moves.HasMoves)
{
string result = moves.GetMovesetLine(GameInfo.Strings.movelist);
lines.Add(result);
}
}
var el = enc as ILocation;
var loc = el?.GetEncounterLocation(enc.Generation, (int) enc.Version);
if (!string.IsNullOrEmpty(loc))
lines.Add(string.Format(L_F0_1, "Location", loc));
lines.Add(string.Format(L_F0_1, nameof(GameVersion), enc.Version));
lines.Add(enc.LevelMin == enc.LevelMax
? $"Level: {enc.LevelMin}"
: $"Level: {enc.LevelMin}-{enc.LevelMax}");
#if DEBUG
// Record types! Can get a nice summary.
// Won't work neatly for Mystery Gift types since those aren't record types.
if (enc is not MysteryGift)
{
// ReSharper disable once ConstantNullCoalescingCondition
var raw = enc.ToString() ?? throw new ArgumentNullException(nameof(enc));
lines.AddRange(raw.Split(',', '}', '{'));
}
#endif
return lines;
}
public void Clear()
{
ShowSet.RemoveAll();

View file

@ -1063,7 +1063,7 @@ public partial class Main : Form
if (dr != DialogResult.Yes)
return;
#if DEBUG
var enc = SummaryPreviewer.GetTextLines(la.EncounterOriginal);
var enc = la.EncounterOriginal.GetTextLines();
report += Environment.NewLine + Environment.NewLine + string.Join(Environment.NewLine, enc);
#endif
WinFormsUtil.SetClipboardText(report);

View file

@ -80,7 +80,7 @@ public partial class SAV_Encounters : Form
return;
var enc = Results[index];
pb.AccessibleDescription = string.Join(Environment.NewLine, SummaryPreviewer.GetTextLines(enc));
pb.AccessibleDescription = string.Join(Environment.NewLine, enc.GetTextLines());
};
slot.ContextMenuStrip = mnu;
if (Main.Settings.Hover.HoverSlotShowText)

View file

@ -83,7 +83,7 @@ public partial class SAV_MysteryGiftDB : Form
return;
var enc = Results[index];
pb.AccessibleDescription = string.Join(Environment.NewLine, SummaryPreviewer.GetTextLines(enc));
pb.AccessibleDescription = string.Join(Environment.NewLine, enc.GetTextLines());
};
}

View file

@ -112,6 +112,9 @@ public partial class SAV_Misc3 : Form
}
#endregion
private const ushort ItemIDOldSeaMap = 0x178;
private static ReadOnlySpan<ushort> TicketItemIDs => new ushort[] { 0x109, 0x113, 0x172, 0x173, ItemIDOldSeaMap }; // item IDs
#region Ferry
private void B_GetTickets_Click(object sender, EventArgs e)
{
@ -123,9 +126,8 @@ public partial class SAV_Misc3 : Form
itemlist[i] = $"(Item #{i:000})";
}
const ushort oldsea = 0x178;
Span<ushort> tickets = stackalloc ushort[] {0x109, 0x113, 0x172, 0x173, oldsea }; // item IDs
if (!SAV.Japanese && DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, $"Non Japanese save file. Add {itemlist[oldsea]} (unreleased)?"))
var tickets = TicketItemIDs;
if (!SAV.Japanese && DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, $"Non Japanese save file. Add {itemlist[ItemIDOldSeaMap]} (unreleased)?"))
tickets = tickets[..^1]; // remove old sea map
var p = Pouches.FirstOrDefault(z => z.Type == InventoryType.KeyItems);

View file

@ -2,7 +2,6 @@ using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using PKHeX.Core;
@ -105,7 +104,7 @@ public partial class SAV_CGearSkin : Form
private void B_Save_Click(object sender, EventArgs e)
{
byte[] bgdata = bg.GetSkin(SAV is SAV5B2W2);
if (bgdata.Any(z => z != 0))
if (bgdata.AsSpan().IndexOfAnyExcept<byte>(0) != -1)
{
SAV.CGearSkinData = bgdata;
Origin.CopyChangesFrom(SAV);

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Windows.Forms;
using PKHeX.Core;
using static System.Buffers.Binary.BinaryPrimitives;
@ -42,13 +42,15 @@ public partial class SAV_PokeBlockORAS : Form
Close();
}
private void B_RandomizeBerries_Click(object sender, EventArgs e)
private static ReadOnlySpan<byte> DefaultBerryTree => new byte[] { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x40, 0x01, 0x00, 0x00, 0x00 };
private void B_RandomizeBerries_Click(object sender, EventArgs e)
{
if (DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Repopulate all berry plots with random berries?"))
return;
// Randomize the trees.
Span<byte> tree = stackalloc byte[] { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x40, 0x01, 0x00, 0x00, 0x00 };
ReadOnlySpan<byte> tree = DefaultBerryTree;
var plantable = Legal.Pouch_Berry_XY; // 0 index is None, skip with rand
var rnd = Util.Rand;

View file

@ -48,7 +48,7 @@ public partial class SAV_Wondercard : Form
return;
var enc = mga.Gifts[index];
pb.AccessibleDescription = string.Join(Environment.NewLine, SummaryPreviewer.GetTextLines(enc));
pb.AccessibleDescription = string.Join(Environment.NewLine, enc.GetTextLines());
};
}

View file

@ -185,10 +185,12 @@ public static class PIDTests
0x049F2F05, // Mawile
};
private static ReadOnlySpan<int> MawileIVs => new[] {31, 30, 29, 31, 23, 27};
[Fact]
public static void VerifyMawileAntishiny()
{
VerifyResultsAntiShiny(MawileTeamPIDs, Mawile, 12345, 51882, stackalloc[] {31, 30, 29, 31, 23, 27});
VerifyResultsAntiShiny(MawileTeamPIDs, Mawile, 12345, 51882, MawileIVs);
}
private static void VerifyResultsAntiShiny(ReadOnlySpan<uint> resultPIDs, TeamLock[] team, ushort tid, ushort sid, ReadOnlySpan<int> ivs)