Fix inventory item ID mapping, PokedexLA save, SuperTrain bags

- Inventory: use model.Index for write-back instead of Array.IndexOf
  by name (118+ duplicate English item names caused silent corruption)
- InventoryItemModel: sync Index when ItemName changes via combo box
- PokedexLA: Save() now writes Seen/ResearchLevel/Perfect back to dex
  (was silently discarding all user edits)
- PokedexSave8a: expose SetPokeHasBeenUpdatedFlag, SetPokeResearchRate,
  SetPokePerfect write methods for ViewModel use
- SuperTrain6: clear trailing bag slots after compaction to prevent
  stale ghost bags (format has no count field)
This commit is contained in:
montanon 2026-03-17 12:55:18 -03:00
parent 075d22d2d7
commit ae206b80a0
6 changed files with 60 additions and 10 deletions

View File

@ -39,8 +39,13 @@ public partial class BatchEditorViewModel : SaveEditorViewModelBase
/// </summary>
public int CurrentBox { get; set; }
private readonly SaveFile _origin;
private readonly SaveFile _sav;
public BatchEditorViewModel(SaveFile sav) : base(sav)
{
_origin = sav;
_sav = (SaveFile)sav.Clone();
}
[RelayCommand]
@ -79,6 +84,7 @@ private async Task ExecuteAsync()
try
{
var result = await Task.Run(() => RunBatchEdit(sets));
_origin.CopyChangesFrom(_sav);
ResultLog = result;
Modified = true;
}
@ -107,15 +113,15 @@ private string RunBatchEdit(StringInstructionSet[] sets)
switch (SelectedScope)
{
case 0: // Current Box
SlotInfoLoader.AddBoxData(SAV, data);
SlotInfoLoader.AddBoxData(_sav, data);
// Filter to current box only
data = data.Where(s => s.Source is SlotInfoBox b && b.Box == CurrentBox).ToList();
break;
case 1: // All Boxes
SlotInfoLoader.AddBoxData(SAV, data);
SlotInfoLoader.AddBoxData(_sav, data);
break;
case 2: // Party
SlotInfoLoader.AddPartyData(SAV, data);
SlotInfoLoader.AddPartyData(_sav, data);
break;
}
@ -133,9 +139,9 @@ private string RunBatchEdit(StringInstructionSet[] sets)
}
}
// Write back modified data
// Write back modified data to clone
foreach (var slot in data)
slot.Source.WriteTo(SAV, slot.Entity, EntityImportSettings.None);
slot.Source.WriteTo(_sav, slot.Entity, EntityImportSettings.None);
return editor.GetEditorResults(sets);
}

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using CommunityToolkit.Mvvm.ComponentModel;
using PKHeX.Core;
@ -10,6 +11,8 @@ namespace PKHeX.Avalonia.ViewModels.Subforms;
/// </summary>
public partial class InventoryItemModel : ObservableObject
{
private readonly string[] _itemNames;
[ObservableProperty]
private string _itemName;
@ -33,8 +36,18 @@ public InventoryItemModel(InventoryItem item, string[] itemNames, IReadOnlyList<
{
BackingItem = item;
ValidItems = validItems;
_itemNames = itemNames;
_index = item.Index;
_count = item.Count;
_itemName = item.Index < itemNames.Length ? itemNames[item.Index] : $"(Item #{item.Index:000})";
}
partial void OnItemNameChanged(string value)
{
// Update Index when the user changes the item name (e.g. via text edit).
// This keeps Index in sync so WriteBackAllPouches can use it directly.
var id = Array.IndexOf(_itemNames, value);
if (id >= 0)
Index = id;
}
}

View File

@ -113,10 +113,7 @@ private void WriteBackAllPouches()
foreach (var itemModel in pouchModel.Items)
{
var itemName = itemModel.ItemName;
var itemId = Array.IndexOf(_itemNames, itemName);
if (itemId < 0)
itemId = 0;
var itemId = itemModel.Index;
var count = itemModel.Count;

View File

@ -110,6 +110,15 @@ private void ApplyFilter()
[RelayCommand]
private void Save()
{
// Write edited entry values back to the dex data before copying.
foreach (var entry in AllEntries)
{
var species = entry.Species;
_dex.SetPokeHasBeenUpdatedFlag(species, entry.Seen);
_dex.SetPokeResearchRate(species, entry.ResearchLevel);
_dex.SetPokePerfect(species, entry.Perfect);
}
_origin.CopyChangesFrom(_sav);
Modified = true;
}

View File

@ -143,7 +143,7 @@ private void Save()
{
SaveStageRecord(SelectedStageIndex);
// Save bags
// Save bags (compact non-empty slots to the front)
int emptySlots = 0;
for (int i = 0; i < 12; i++)
{
@ -156,6 +156,10 @@ private void Save()
_stb.SetBag(i - emptySlots, (byte)bagIndex);
}
// Clear trailing stale slots left after compaction
for (int i = 12 - emptySlots; i < 12; i++)
_stb.SetBag(i, 0);
_origin.CopyChangesFrom(_sav);
Modified = true;
}

View File

@ -138,6 +138,27 @@ public int GetPokeResearchRate(ushort species)
public bool IsPerfect(ushort species) => SaveData.IsPerfect(species);
public void SetPokeHasBeenUpdatedFlag(ushort species, bool value)
{
if (species >= MAX_SPECIES)
return;
SaveData.GetResearchEntry(species).HasEverBeenUpdated = value;
}
public void SetPokeResearchRate(ushort species, int value)
{
if (species >= MAX_SPECIES)
return;
SaveData.GetResearchEntry(species).ResearchRate = (ushort)Math.Min(Math.Max(value, 0), PokedexConstants8a.MaxPokedexResearchPoints);
}
public void SetPokePerfect(ushort species, bool value)
{
if (species >= MAX_SPECIES)
return;
SaveData.GetResearchEntry(species).IsPerfect = value;
}
public int GetUpdateIndex(ushort species) => SaveData.GetResearchEntry(species).UpdateCounter;
public int GetLastReportedIndex(ushort species) => SaveData.GetResearchEntry(species).LastUpdatedReportCounter;