Misc tweaks

ShowdownSet: Fix indentation, use explicit const
ItemStorage8BDSP: Rename GetAll->GetAllHeld to match others
EncounterLearn: Guard against >4 length enumerables, use explicit versions for S/V
EggMoves: Read u16[] directly rather than manually
SaveFinder: simplify expression
SAV_Database: extract func
SAV_Encounters: use RoM to match Moveset generator
This commit is contained in:
Kurt 2024-01-02 15:45:35 -08:00
parent d56eb0e3c4
commit 424cbcff21
9 changed files with 62 additions and 58 deletions

View file

@ -373,7 +373,7 @@ public sealed class ShowdownSet : IBattleTemplate
result.Add($"Ability: {Strings.Ability[Ability]}");
if (Context == EntityContext.Gen9 && TeraType != MoveType.Any)
{
if ((uint)TeraType <= (int)MoveType.Fairy)
if ((uint)TeraType <= TeraTypeUtil.MaxType) // Fairy
result.Add($"Tera Type: {Strings.Types[(int)TeraType]}");
else if ((uint)TeraType == TeraTypeUtil.Stellar)
result.Add($"Tera Type: {Strings.Types[TeraTypeUtil.StellarTypeDisplayStringIndex]}");
@ -740,7 +740,7 @@ public sealed class ShowdownSet : IBattleTemplate
return type;
if (type[0] == '(' && type[^1] == ')')
return type[1..^1].Trim();
return type[1..^1].Trim();
if (type[0] == '[' && type[^1] == ']')
return type[1..^1].Trim();

View file

@ -155,7 +155,7 @@ public sealed class ItemStorage8BDSP : IItemStorage
InventoryType.Berries, InventoryType.Balls, InventoryType.BattleItems, InventoryType.Treasure,
];
public static ushort[] GetAll() => [..Pouch_Regular_BS, ..Pouch_Ball_BS, ..Pouch_Battle_BS, ..Pouch_Berries_BS, ..Pouch_TMHM_BS, ..Pouch_Medicine_BS, ..Pouch_Treasure_BS];
public static ushort[] GetAllHeld() => [..Pouch_Regular_BS, ..Pouch_Ball_BS, ..Pouch_Battle_BS, ..Pouch_Berries_BS, ..Pouch_TMHM_BS, ..Pouch_Medicine_BS, ..Pouch_Treasure_BS];
public static InventoryType GetInventoryPouch(ushort itemIndex)
{

View file

@ -150,7 +150,7 @@ public static class Legal
internal static readonly ushort[] HeldItems_USUM = ItemStorage7USUM.GetAllHeld();
internal static readonly ushort[] HeldItems_GG = [];
internal static readonly ushort[] HeldItems_SWSH = ItemStorage8SWSH.GetAllHeld();
internal static readonly ushort[] HeldItems_BS = ItemStorage8BDSP.GetAll();
internal static readonly ushort[] HeldItems_BS = ItemStorage8BDSP.GetAllHeld();
internal static readonly ushort[] HeldItems_LA = [];
internal static readonly ushort[] HeldItems_SV = ItemStorage9SV.GetAllHeld();

View file

@ -48,41 +48,44 @@ public static class EncounterLearn
if (speciesID <= 0)
return [];
var allMoves = moves.ToList();
var span = new ushort[allMoves.Count];
for (int i = 0; i < span.Length; i++)
const int maxMoves = 4;
Span<ushort> span = stackalloc ushort[maxMoves];
int ctr = 0;
foreach (var move in moves)
{
var move = allMoves[i];
var index = StringUtil.FindIndexIgnoreCase(str.movelist, move);
if (index <= 0)
return [];
span[i] = (ushort)index;
span[ctr++] = (ushort)index;
if (ctr >= span.Length)
break;
}
var moveset = span[..ctr].ToArray();
return GetLearn((ushort)speciesID, span, form);
return GetLearn((ushort)speciesID, moveset, form);
}
/// <summary>
/// Gets all encounters where a <see cref="species"/> can learn all input <see cref="moves"/>.
/// </summary>
public static IEnumerable<IEncounterable> GetLearn(ushort species, ushort[] moves, byte form = 0)
public static IEnumerable<IEncounterable> GetLearn(ushort species, ReadOnlyMemory<ushort> moves, byte form = 0)
{
if (species == 0)
return [];
if (moves.AsSpan().Contains<ushort>(0))
if (moves.Span.Contains<ushort>(0))
return [];
var vers = GameUtil.GameVersions;
return GetLearnInternal(species, form, moves, vers);
}
private static IEnumerable<IEncounterable> GetLearnInternal(ushort species, byte form, ushort[] moves, GameVersion[] vers)
private static IEnumerable<IEncounterable> GetLearnInternal(ushort species, byte form, ReadOnlyMemory<ushort> moves, GameVersion[] vers)
{
bool iterated = false;
if (PersonalTable.SV.IsPresentInGame(species, form))
{
var blank = new PK9 { Species = species, Form = form };
var encs = EncounterMovesetGenerator.GenerateEncounters(blank, moves, vers);
var encs = EncounterMovesetGenerator.GenerateEncounters(blank, moves, GameVersion.SL, GameVersion.VL);
foreach (var enc in encs)
yield return enc;
}
@ -92,7 +95,6 @@ public static class EncounterLearn
var encs = EncounterMovesetGenerator.GenerateEncounters(blank, moves, GameVersion.PLA);
foreach (var enc in encs)
yield return enc;
iterated = true;
}
if (PersonalTable.BDSP.IsPresentInGame(species, form))
{
@ -100,7 +102,6 @@ public static class EncounterLearn
var encs = EncounterMovesetGenerator.GenerateEncounters(blank, moves, GameVersion.BD, GameVersion.SP);
foreach (var enc in encs)
yield return enc;
iterated = true;
}
if (PersonalTable.SWSH.IsPresentInGame(species, form))
{

View file

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -73,10 +74,9 @@ public sealed class EggMoves6 : EggMoves
continue;
}
var moves = new ushort[count];
var span = data[2..];
for (int j = 0; j < moves.Length; j++)
moves[j] = ReadUInt16LittleEndian(span[(j * 2)..]);
var moves = MemoryMarshal.Cast<byte, ushort>(data).Slice(1, count).ToArray();
if (!BitConverter.IsLittleEndian)
ReverseEndianness(moves, moves);
result[i] = new EggMoves6(moves);
}
return result;
@ -91,6 +91,7 @@ public sealed class EggMoves7 : EggMoves
/// <summary>
/// Points to the index where form data is, within the parent Egg Move object array.
/// </summary>
/// <remarks>This value is the same for all form entries for a given species.</remarks>
public readonly ushort FormTableIndex;
private EggMoves7(ushort[] moves, ushort formIndex = 0) : base(moves) => FormTableIndex = formIndex;
@ -114,10 +115,9 @@ public sealed class EggMoves7 : EggMoves
continue;
}
var moves = new ushort[count];
var span = data[4..];
for (int j = 0; j < moves.Length; j++)
moves[j] = ReadUInt16LittleEndian(span[(j * 2)..]);
var moves = MemoryMarshal.Cast<byte, ushort>(data).Slice(2, count).ToArray();
if (!BitConverter.IsLittleEndian)
ReverseEndianness(moves, moves);
result[i] = new EggMoves7(moves, formIndex);
}
return result;
@ -130,17 +130,23 @@ internal static class EggMovesExtensions
{
if (species >= table.Length)
return [];
var entry = table[species];
if (form == 0 || species >= entry.FormTableIndex)
// Sanity check species in the event it is out of range.
var baseIndex = entry.FormTableIndex;
if (species > baseIndex)
return [];
if (form == 0)
return entry.Moves;
// Sanity check form in the event it is out of range.
var baseIndex = entry.FormTableIndex;
var index = baseIndex + form - 1;
if ((uint)index >= table.Length)
// Jump to the associated form's entry within the table.
var index = form - 1u + baseIndex;
if (index >= table.Length)
return [];
entry = table[index];
// Double-check that the entry is still associated to the species.
if (entry.FormTableIndex != baseIndex)
return [];
@ -165,9 +171,9 @@ public static class EggMoves9
result[i] = empty;
continue;
}
var moves = new ushort[data.Length >> 1];
for (int j = 0; j < data.Length; j+=2)
moves[j >> 1] = ReadUInt16LittleEndian(data[j..]);
var moves = MemoryMarshal.Cast<byte, ushort>(data).ToArray();
if (!BitConverter.IsLittleEndian)
ReverseEndianness(moves, moves);
result[i] = moves;
}
return result;

View file

@ -165,20 +165,17 @@ public static class SaveFinder
/// <inheritdoc cref="GetSaveFiles"/>
public static IEnumerable<SaveFile> DetectSaveFiles() => GetSaveFiles(Environment.GetLogicalDrives(), true, CustomBackupPaths, true);
/// <inheritdoc cref="TryDetectSaveFile(out SaveFile?)"/>
/// <returns>
/// True if a valid save file was found, false otherwise.
/// </returns>
/// <inheritdoc cref="FindMostRecentSaveFile(IReadOnlyList{string},string[])"/>
public static bool TryDetectSaveFile([NotNullWhen(true)] out SaveFile? sav) => TryDetectSaveFile(Environment.GetLogicalDrives(), out sav);
/// <inheritdoc cref="TryDetectSaveFile(out SaveFile)"/>
public static bool TryDetectSaveFile(IReadOnlyList<string> drives, [NotNullWhen(true)] out SaveFile? sav)
{
var result = FindMostRecentSaveFile(drives, CustomBackupPaths);
if (result == null)
{
sav = null;
return false;
}
var path = result.Metadata.FilePath!;
sav = result;
sav = FindMostRecentSaveFile(drives, CustomBackupPaths);
var path = sav?.Metadata.FilePath;
return File.Exists(path);
}
}

View file

@ -402,18 +402,9 @@ public partial class SAV_Database : Form
if (Main.Settings.EntityDb.FilterUnavailableSpecies)
{
static bool IsPresentInGameSV(ISpeciesForm pk) => pk is PK9 || PersonalTable.SV.IsPresentInGame(pk.Species, pk.Form);
static bool IsPresentInGameSWSH(ISpeciesForm pk) => pk is PK8 || PersonalTable.SWSH.IsPresentInGame(pk.Species, pk.Form);
static bool IsPresentInGameBDSP(ISpeciesForm pk) => pk is PB8 || PersonalTable.BDSP.IsPresentInGame(pk.Species, pk.Form);
static bool IsPresentInGamePLA(ISpeciesForm pk) => pk is PA8 || PersonalTable.LA.IsPresentInGame(pk.Species, pk.Form);
if (sav is SAV9SV)
result.RemoveAll(z => !IsPresentInGameSV(z.Entity));
else if (sav is SAV8SWSH)
result.RemoveAll(z => !IsPresentInGameSWSH(z.Entity));
else if (sav is SAV8BS)
result.RemoveAll(z => !IsPresentInGameBDSP(z.Entity));
else if (sav is SAV8LA)
result.RemoveAll(z => !IsPresentInGamePLA(z.Entity));
var filter = GetFilterForSaveFile(sav);
if (filter != null)
result.RemoveAll(z => !filter(z.Entity));
}
var sort = Main.Settings.EntityDb.InitialSortMode;
@ -426,6 +417,15 @@ public partial class SAV_Database : Form
return result;
}
private static Func<PKM, bool>? GetFilterForSaveFile(SaveFile sav) => sav switch
{
SAV8SWSH => static pk => pk is PK9 || PersonalTable.SV.IsPresentInGame(pk.Species, pk.Form),
SAV8BS => static pk => pk is PK8 || PersonalTable.SWSH.IsPresentInGame(pk.Species, pk.Form),
SAV8LA => static pk => pk is PB8 || PersonalTable.BDSP.IsPresentInGame(pk.Species, pk.Form),
SAV9SV => static pk => pk is PA8 || PersonalTable.LA.IsPresentInGame(pk.Species, pk.Form),
_ => null,
};
private static void TryAddPKMsFromSaveFilePath(ConcurrentBag<SlotCache> dbTemp, string file)
{
var sav = SaveUtil.GetVariantSAV(file);

View file

@ -299,7 +299,7 @@ public partial class SAV_Encounters : Form
yield return i;
}
private IEnumerable<IEncounterInfo> GetAllSpeciesFormEncounters(IEnumerable<ushort> species, IPersonalTable pt, IReadOnlyList<GameVersion> versions, ushort[] moves, PKM pk, CancellationToken token)
private IEnumerable<IEncounterInfo> GetAllSpeciesFormEncounters(IEnumerable<ushort> species, IPersonalTable pt, IReadOnlyList<GameVersion> versions, ReadOnlyMemory<ushort> moves, PKM pk, CancellationToken token)
{
foreach (var s in species)
{
@ -339,7 +339,7 @@ public partial class SAV_Encounters : Form
public int GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj);
}
private IEnumerable<IEncounterInfo> GetEncounters(ushort species, byte form, ushort[] moves, PKM pk, IReadOnlyList<GameVersion> vers)
private IEnumerable<IEncounterInfo> GetEncounters(ushort species, byte form, ReadOnlyMemory<ushort> moves, PKM pk, IReadOnlyList<GameVersion> vers)
{
pk.Species = species;
pk.Form = form;

View file

@ -54,7 +54,7 @@ public class ShowdownSetTests
public void SimGetVivillonPostcardSV(byte form)
{
var pk9 = new PK9 { Species = (int)Species.Vivillon, Form = form };
ushort[] moves = [];
var moves = ReadOnlyMemory<ushort>.Empty;
var encounters = EncounterMovesetGenerator.GenerateEncounters(pk9, moves, GameVersion.SL);
encounters.OfType<EncounterSlot9>().Should().NotBeEmpty();
}