mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-09-30 22:07:03 -05:00
Misc tweaks
Seal some classes Use derived pkm class for template->pk* moves (pk3/pa8) Revise inventory9 to better handle empty slots Cache legality for Summary report grid (does this make it faster?? seems to open instantly); add shift-quit to skip prompt Fix handling for level range on encountercriteria passing level range for gen3/4 encounter slot method1/etc lead checks
This commit is contained in:
@@ -41,7 +41,10 @@ public class EntitySummary : IFatefulEncounterReadOnly // do NOT seal, allow inh
|
||||
public string OT => Entity.OriginalTrainerName;
|
||||
public string Version => Get(Strings.gamelist, (int)Entity.Version);
|
||||
public string OTLang => ((LanguageID)Entity.Language).ToString();
|
||||
public string Legal { get { var la = new LegalityAnalysis(Entity); return la.Parsed ? la.Valid.ToString() : "-"; } }
|
||||
public string Legal => Legality.Parsed ? Legality.Valid.ToString() : "-";
|
||||
public string EncounterType => Legality.EncounterMatch.LongName;
|
||||
|
||||
private LegalityAnalysis Legality { get; }
|
||||
|
||||
#region Extraneous
|
||||
public string EC => Entity.EncryptionConstant.ToString("X8");
|
||||
@@ -113,6 +116,7 @@ protected EntitySummary(PKM pk, GameStrings strings)
|
||||
Entity = pk;
|
||||
Strings = strings;
|
||||
Stats = Entity.GetStats(Entity.PersonalInfo);
|
||||
Legality = new LegalityAnalysis(Entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -92,7 +92,7 @@ private void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SetEncounterMoves(PKM pk) => EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
|
||||
protected virtual void SetEncounterMoves(PK3 pk) => EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
|
||||
#endregion
|
||||
|
||||
#region Matching
|
||||
|
||||
@@ -9,5 +9,5 @@ namespace PKHeX.Core;
|
||||
internal sealed record EncounterSlot3Swarm(EncounterArea3 Parent, ushort Species, byte LevelMin, byte LevelMax, byte SlotNumber, Moveset Moves)
|
||||
: EncounterSlot3(Parent, Species, 0, LevelMin, LevelMax, SlotNumber, 0, 0, 0, 0), IMoveset
|
||||
{
|
||||
protected override void SetEncounterMoves(PKM pk) => pk.SetMoves(Moves);
|
||||
protected override void SetEncounterMoves(PK3 pk) => pk.SetMoves(Moves);
|
||||
}
|
||||
|
||||
@@ -117,16 +117,15 @@ private OverworldParam8a GetParams(PersonalInfo8LA pi) => new()
|
||||
_ => 0,
|
||||
});
|
||||
|
||||
private void SetEncounterMoves(PKM pk, int level)
|
||||
private void SetEncounterMoves(PA8 pk, int level)
|
||||
{
|
||||
var pa8 = (PA8)pk;
|
||||
Span<ushort> moves = stackalloc ushort[4];
|
||||
var (learn, mastery) = GetLevelUpInfo();
|
||||
LoadInitialMoveset(pa8, moves, learn, level);
|
||||
LoadInitialMoveset(pk, moves, learn, level);
|
||||
pk.SetMoves(moves);
|
||||
pa8.SetEncounterMasteryFlags(moves, mastery, level);
|
||||
if (pa8.AlphaMove != 0)
|
||||
pa8.SetMasteryFlagMove(pa8.AlphaMove);
|
||||
pk.SetEncounterMasteryFlags(moves, mastery, level);
|
||||
if (pk.AlphaMove != 0)
|
||||
pk.SetMasteryFlagMove(pk.AlphaMove);
|
||||
}
|
||||
|
||||
public void LoadInitialMoveset(PA8 pa8, Span<ushort> moves, Learnset learn, int level)
|
||||
|
||||
@@ -604,7 +604,7 @@ private static bool IsSlotValid<T>(T enc, uint u16SlotRand)
|
||||
|
||||
private static bool IsOriginalLevelValid(byte min, byte max, byte format, uint level)
|
||||
{
|
||||
if (format == Format)
|
||||
if (format == Format && min > 1)
|
||||
return level == min; // Met Level matches
|
||||
return LevelRangeExtensions.IsLevelWithinRange((int)level, min, max);
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ private static bool IsSlotValid<T>(T enc, uint u16SlotRand)
|
||||
|
||||
private static bool IsOriginalLevelValid(byte min, byte max, byte format, uint level)
|
||||
{
|
||||
if (format == Format)
|
||||
if (format == Format && min > 1)
|
||||
return level == min; // Met Level matches
|
||||
return LevelRangeExtensions.IsLevelWithinRange((int)level, min, max);
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ private static bool IsSlotValid<T>(T enc, uint u16SlotRand)
|
||||
|
||||
private static bool IsOriginalLevelValid(byte min, byte max, byte format, uint level)
|
||||
{
|
||||
if (format == Format)
|
||||
if (format == Format && min > 1)
|
||||
return level == min; // Met Level matches
|
||||
return LevelRangeExtensions.IsLevelWithinRange((int)level, min, max);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public class BattleTest5(Memory<byte> Raw)
|
||||
public sealed class BattleTest5(Memory<byte> Raw)
|
||||
{
|
||||
public const int SIZE = 0x5C8;
|
||||
public const string Extension = "bt5";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public class PokestarMovie5(Memory<byte> Raw)
|
||||
public sealed class PokestarMovie5(Memory<byte> Raw)
|
||||
{
|
||||
public const int SIZE = 0x4B4;
|
||||
public const string Extension = "psm5";
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public class Record5(SAV5 SAV, Memory<byte> raw) : SaveBlock<SAV5>(SAV, raw)
|
||||
public sealed class Record5(SAV5 SAV, Memory<byte> raw) : SaveBlock<SAV5>(SAV, raw)
|
||||
{
|
||||
public const int Max32 = 999_999_999;
|
||||
public const ushort Max16 = 65535;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public class WorldTournament5(Memory<byte> Raw)
|
||||
public sealed class WorldTournament5(Memory<byte> Raw)
|
||||
{
|
||||
public const int SIZE = 0x1214;
|
||||
public const string Extension = "pwt";
|
||||
|
||||
@@ -13,7 +13,17 @@ public sealed class MyItem9(SAV9SV sav, SCBlock block) : MyItem(sav, block.Raw)
|
||||
|
||||
private Span<byte> GetItemSpan(ushort itemIndex) => InventoryPouch9.GetItemSpan(Data, itemIndex);
|
||||
|
||||
public void DeleteItem(ushort itemIndex) => InventoryItem9.Clear(GetItemSpan(itemIndex));
|
||||
public uint DefaultInitPouch => System.Buffers.Binary.BinaryPrimitives.ReadUInt32LittleEndian(Data); // Item 0
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the item at the requested <paramref name="itemIndex"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Copies item 0 to the requested item index, effectively deleting it.
|
||||
/// Item 0 should always be un-tarnished, so this is a safe operation.
|
||||
/// <see cref="InventoryItem9.PouchInvalid"/> for remarks on Pouch type quirks. This aims to retain consistency within the block.
|
||||
/// </remarks>
|
||||
public void DeleteItem(ushort itemIndex) => GetItemSpan(0).CopyTo(GetItemSpan(itemIndex));
|
||||
public InventoryItem9 GetItem(ushort itemIndex) => InventoryItem9.Read(itemIndex, GetItemSpan(itemIndex));
|
||||
|
||||
public uint GetItemQuantity(ushort itemIndex) => InventoryItem9.GetItemCount(GetItemSpan(itemIndex));
|
||||
@@ -21,7 +31,7 @@ public sealed class MyItem9(SAV9SV sav, SCBlock block) : MyItem(sav, block.Raw)
|
||||
public void SetItemQuantity(ushort itemIndex, int quantity)
|
||||
{
|
||||
var pouch = GetPouchIndex(GetType(itemIndex));
|
||||
if (pouch == InventoryItem9.PouchNone)
|
||||
if (pouch == InventoryItem9.PouchInvalid)
|
||||
{
|
||||
DeleteItem(itemIndex); // don't allow setting items that don't exist
|
||||
return;
|
||||
@@ -30,6 +40,7 @@ public void SetItemQuantity(ushort itemIndex, int quantity)
|
||||
var item = InventoryItem9.Read(itemIndex, span);
|
||||
item.Count = quantity;
|
||||
item.Pouch = GetPouchIndex(GetType(itemIndex));
|
||||
item.IsObtained = true;
|
||||
item.Write(span);
|
||||
}
|
||||
|
||||
@@ -79,6 +90,20 @@ private void CleanIllegalSlots()
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetToDefault()
|
||||
{
|
||||
var block = Data;
|
||||
var defaultPouch = DefaultInitPouch;
|
||||
ResetToDefault(block, defaultPouch);
|
||||
}
|
||||
|
||||
public static void ResetToDefault(Span<byte> block, uint defaultPouch)
|
||||
{
|
||||
block.Clear();
|
||||
for (int i = 0; i < block.Length; i += InventoryItem9.SIZE)
|
||||
System.Buffers.Binary.BinaryPrimitives.WriteUInt32LittleEndian(block[i..], defaultPouch);
|
||||
}
|
||||
|
||||
private static InventoryPouch9 MakePouch(InventoryType type)
|
||||
{
|
||||
var info = ItemStorage9SV.Instance;
|
||||
@@ -98,6 +123,6 @@ private static InventoryPouch9 MakePouch(InventoryType type)
|
||||
InventoryType.Treasure => InventoryItem9.PouchTreasure,
|
||||
InventoryType.Ingredients => InventoryItem9.PouchPicnic,
|
||||
InventoryType.Candy => InventoryItem9.PouchMaterial,
|
||||
_ => InventoryItem9.PouchNone,
|
||||
_ => InventoryItem9.PouchInvalid,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ public sealed record InventoryItem9 : InventoryItem, IItemFavorite, IItemNewFlag
|
||||
{
|
||||
public const int SIZE = 0x10;
|
||||
|
||||
public const uint PouchNone = 0xFFFFFFFF;
|
||||
public const uint PouchMedicine = 0;
|
||||
public const uint PouchBall = 1;
|
||||
public const uint PouchBattle = 2;
|
||||
@@ -20,31 +19,38 @@ public sealed record InventoryItem9 : InventoryItem, IItemFavorite, IItemNewFlag
|
||||
public const uint PouchMaterial = 9;
|
||||
public const uint PouchRecipe = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Invalid pouch ID.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only used internally to this program, not stored in the data.
|
||||
/// The game did have behavior that initialized to this value on early patches, but was later changed to initialize to 0.
|
||||
/// Therefore, we cannot use this value to set. Technically it can be used to determine if the game was started on an early patch.
|
||||
/// </remarks>
|
||||
public const uint PouchInvalid = 0xFFFFFFFF;
|
||||
|
||||
public uint Pouch { get; set; }
|
||||
public uint Flags { get; set; }
|
||||
public uint Padding { get; set; }
|
||||
|
||||
public bool IsNew { get => (Flags & 0x1) != 0; set => Flags = (Flags & ~0x1u) | (value ? 0x1u : 0x0u); } // red dot
|
||||
public bool IsFavorite { get => (Flags & 0x2) != 0; set => Flags = (Flags & ~0x2u) | (value ? 0x2u : 0x0u); }
|
||||
public bool IsUpdated { get => (Flags & 0x4) != 0; set => Flags = (Flags & ~0x4u) | (value ? 0x4u : 0x0u); } // always true if pouch is set
|
||||
public bool IsObtained { get => (Flags & 0x4) != 0; set => Flags = (Flags & ~0x4u) | (value ? 0x4u : 0x0u); } // Has Been Obtained At Least Once
|
||||
|
||||
public override string ToString() => $"{Index:000} x{Count}{(IsNew ? "*" : "")}{(IsFavorite ? "F" : "")} - {Flags:X8}";
|
||||
public override string ToString()
|
||||
{
|
||||
if (IsObtained)
|
||||
return $"{Index:0000} Empty";
|
||||
return $"{Index:0000} x{Count}{(IsNew ? "*" : "")}{(IsFavorite ? "F" : "")} - {Flags:X8}";
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
Index = Count = 0;
|
||||
Pouch = 0;
|
||||
Count = 0;
|
||||
Flags = Padding = 0;
|
||||
IsFavorite = false;
|
||||
IsUpdated = false;
|
||||
IsNew = false;
|
||||
Pouch = PouchNone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the item has been acquired by the player.
|
||||
/// </summary>
|
||||
public bool IsValidPouch => Pouch != PouchNone;
|
||||
|
||||
public static InventoryItem9 Read(ushort index, ReadOnlySpan<byte> data) => new()
|
||||
{
|
||||
Index = index,
|
||||
@@ -56,8 +62,6 @@ public static InventoryItem9 Read(ushort index, ReadOnlySpan<byte> data) => new(
|
||||
|
||||
public void Write(Span<byte> data)
|
||||
{
|
||||
IsUpdated = Pouch != PouchNone;
|
||||
|
||||
// Index is not saved.
|
||||
WriteUInt32LittleEndian(data, Pouch);
|
||||
WriteUInt32LittleEndian(data[4..], (uint)Count);
|
||||
@@ -65,16 +69,13 @@ public void Write(Span<byte> data)
|
||||
WriteUInt32LittleEndian(data[12..], Padding);
|
||||
}
|
||||
|
||||
public static void Clear(Span<byte> data) => WriteUInt128LittleEndian(data, PouchNone);
|
||||
public static uint GetItemCount(Span<byte> data) => ReadUInt32LittleEndian(data[4..]);
|
||||
|
||||
public override void SetNewDetails(int count)
|
||||
{
|
||||
base.SetNewDetails(count);
|
||||
if (IsValidPouch)
|
||||
return;
|
||||
IsNew = true;
|
||||
IsUpdated = true;
|
||||
IsObtained = true;
|
||||
IsFavorite = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ public sealed class InventoryPouch9(InventoryType type, IItemStorage info, int m
|
||||
public uint PouchIndex { get; set; } = pouch;
|
||||
|
||||
public override InventoryItem9 GetEmpty(int itemID = 0, int count = 0) => new() { Index = itemID, Count = count, IsNew = true };
|
||||
public static int GetItemOffset(ushort index) => InventoryItem9.SIZE * index;
|
||||
public static Span<byte> GetItemSpan(Span<byte> block, ushort index) => block[GetItemOffset(index)..];
|
||||
|
||||
public override void GetPouch(ReadOnlySpan<byte> data)
|
||||
{
|
||||
@@ -19,71 +21,87 @@ public override void GetPouch(ReadOnlySpan<byte> data)
|
||||
|
||||
int ctr = 0;
|
||||
foreach (var index in LegalItems)
|
||||
{
|
||||
var item = GetItem(data, index);
|
||||
if (!item.IsValidPouch)
|
||||
continue;
|
||||
items[ctr++] = item;
|
||||
}
|
||||
while (ctr != LegalItems.Length)
|
||||
items[ctr++] = new InventoryItem9();
|
||||
items[ctr++] = GetItem(data, index);
|
||||
|
||||
Items = items;
|
||||
}
|
||||
|
||||
public static InventoryItem9 GetItem(ReadOnlySpan<byte> data, ushort itemID)
|
||||
public static InventoryItem9 GetItem(ReadOnlySpan<byte> block, ushort itemID)
|
||||
{
|
||||
var ofs = GetItemOffset(itemID);
|
||||
return InventoryItem9.Read(itemID, data[ofs..]);
|
||||
return InventoryItem9.Read(itemID, block[ofs..]);
|
||||
}
|
||||
|
||||
public override void SetPouch(Span<byte> data)
|
||||
public override void SetPouch(Span<byte> block)
|
||||
{
|
||||
HashSet<ushort> processed = [];
|
||||
|
||||
// Write all the item slots still present in the pouch. Keep track of the item IDs processed.
|
||||
var items = (InventoryItem9[])Items;
|
||||
var processed = new HashSet<ushort>(items.Length);
|
||||
|
||||
var LegalItems = Info.GetItems(Type);
|
||||
var legal = Info.GetItems(Type);
|
||||
foreach (var item in items)
|
||||
{
|
||||
var index = (ushort)item.Index;
|
||||
if (index == 0)
|
||||
continue;
|
||||
if (!LegalItems.Contains(index))
|
||||
if (!legal.Contains(index))
|
||||
{
|
||||
Debug.WriteLine($"Invalid Item ID returned within this pouch: {index}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SetNew && item.Index != 0)
|
||||
{
|
||||
var original = GetItem(data, (ushort)item.Index);
|
||||
item.IsNew |= !original.IsValidPouch;
|
||||
}
|
||||
|
||||
item.Pouch = PouchIndex;
|
||||
|
||||
var ofs = GetItemOffset(index);
|
||||
item.Write(data[ofs..]);
|
||||
var span = GetItemSpan(block, index);
|
||||
var original = InventoryItem9.Read(index, span);
|
||||
EnsureFlagsConsistent(item, original);
|
||||
|
||||
// In the event of duplicates, we just overwrite what was previously written by a prior duplicate.
|
||||
// Don't care if we've already processed this item, just write it again.
|
||||
item.Write(span);
|
||||
processed.Add(index);
|
||||
}
|
||||
|
||||
// For all the items that were not present in the pouch, clear the data for them.
|
||||
foreach (var index in LegalItems)
|
||||
foreach (var index in legal)
|
||||
{
|
||||
if (processed.Contains(index))
|
||||
continue;
|
||||
ClearItem(data, index);
|
||||
SetQuantityZero(block, index);
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetItemOffset(ushort index) => InventoryItem9.SIZE * index;
|
||||
public static Span<byte> GetItemSpan(Span<byte> data, ushort index) => data[GetItemOffset(index)..];
|
||||
public static void ClearItem(Span<byte> data, ushort index) => InventoryItem9.Clear(GetItemSpan(data, index));
|
||||
private void EnsureFlagsConsistent(InventoryItem9 item, InventoryItem9 original)
|
||||
{
|
||||
if (item.Count != 0)
|
||||
{
|
||||
// Ensure the flag is set; 0->X and Y->Z
|
||||
item.IsObtained = true;
|
||||
if (!original.IsObtained && SetNew)
|
||||
item.IsNew = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!item.IsObtained)
|
||||
{
|
||||
item.IsNew = item.IsFavorite = false;
|
||||
if (item.Pouch is not (0 or uint.MaxValue))
|
||||
item.Pouch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.IsObtained)
|
||||
item.Pouch = PouchIndex; // ensure the pouch is set
|
||||
}
|
||||
|
||||
public static void SetQuantityZero(Span<byte> block, ushort index)
|
||||
{
|
||||
var span = GetItemSpan(block, index);
|
||||
var exist = InventoryItem9.Read(index, span);
|
||||
if (exist.Count == 0)
|
||||
return;
|
||||
exist.Count = 0;
|
||||
exist.IsObtained = true;
|
||||
exist.Write(span);
|
||||
}
|
||||
|
||||
public static int GetSuggestedCount(InventoryType t, int item, int requestVal)
|
||||
{
|
||||
|
||||
@@ -161,6 +161,8 @@ private void Data_Sorted(object sender, EventArgs e)
|
||||
|
||||
private void PromptSaveCSV(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
if (ModifierKeys.HasFlag(Keys.Shift))
|
||||
return;
|
||||
if (WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgReportExportCSV) != DialogResult.Yes)
|
||||
return;
|
||||
using var savecsv = new SaveFileDialog();
|
||||
|
||||
Reference in New Issue
Block a user