Extract switch-entity HT update logic, fix

Closes #4227
SlotWrite no longer revises the data incorrectly

if ot&gender mismatch, clear memories and set new values
can retain wrong values by having ot&gender shared between games
This commit is contained in:
Kurt
2024-05-12 23:40:50 -05:00
parent 28add13282
commit 5af96eab95
11 changed files with 91 additions and 76 deletions

View File

@@ -1,3 +1,4 @@
using System;
using static PKHeX.Core.CheckIdentifier;
namespace PKHeX.Core.Bulk;
@@ -35,7 +36,11 @@ private static void Verify(BulkAnalysis input, SlotCache cs)
if (flag != 1)
return;
if (pk.HandlingTrainerName != tr.OT)
Span<char> ht = stackalloc char[pk.TrashCharCountTrainer];
var len = pk.LoadString(pk.HandlingTrainerTrash, ht);
ht = ht[..len];
if (!ht.SequenceEqual(tr.OT))
input.AddLine(cs, LegalityCheckStrings.LTransferHTMismatchName, Trainer);
if (pk is IHandlerLanguage h && h.HandlingTrainerLanguage != tr.Language)
input.AddLine(cs, LegalityCheckStrings.LTransferHTMismatchLanguage, Trainer);

View File

@@ -70,7 +70,11 @@ private void VerifyHandlerState(LegalityAnalysis data, bool neverOT)
if (flag == 1)
{
if (pk.HandlingTrainerName != tr.OT)
Span<char> ht = stackalloc char[pk.TrashCharCountTrainer];
var len = pk.LoadString(pk.HandlingTrainerTrash, ht);
ht = ht[..len];
if (!ht.SequenceEqual(tr.OT))
data.AddLine(GetInvalid(LTransferHTMismatchName));
if (pk is IHandlerLanguage h && h.HandlingTrainerLanguage != tr.Language)
data.AddLine(Get(LTransferHTMismatchLanguage, Severity.Fishy));

View File

@@ -432,4 +432,43 @@ public override int LoadString(ReadOnlySpan<byte> data, Span<char> destBuffer)
=> StringConverter8.LoadString(data, destBuffer);
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
=> StringConverter8.SetString(destBuffer, value, maxLength, option);
/// <summary>
/// Revises the Handler details of a <see cref="PKM"/> to match the current <see cref="ITrainerInfo"/>.
/// </summary>
/// <remarks>Logic used starting in SW/SH games.</remarks>
public static void UpdateHandler<T>(T pk, ITrainerInfo tr)
where T : PKM, IHandlerLanguage, IMemoryHT
{
pk.CurrentHandler = 1;
var gender = tr.Gender;
var other = tr.OT;
if (IsHandlerSame(pk, other, gender))
return;
// Clear the old Handler details and write our new ones.
pk.ClearMemoriesHT();
pk.HandlingTrainerName = other;
pk.HandlingTrainerLanguage = (byte)tr.Language;
pk.HandlingTrainerGender = gender;
pk.HandlingTrainerFriendship = pk.PersonalInfo.BaseFriendship;
// Memories are deferred to the game. SW/SH does not immediately set memories.
}
private static bool IsHandlerSame<T>(T pk, ReadOnlySpan<char> newHT, byte newGender)
where T : PKM, IHandlerLanguage
{
if (newGender != pk.HandlingTrainerGender)
return false;
// Does not check Language or Version for equality! Can result in mismatches or empty (0) language value.
// Check the trainer string for equality (most expensive of all last).
Span<char> exist = stackalloc char[pk.TrashCharCountTrainer];
var len = pk.LoadString(pk.HandlingTrainerTrash, exist);
exist = exist[..len];
return exist.SequenceEqual(newHT);
}
}

View File

@@ -628,17 +628,7 @@ private bool TradeOT(ITrainerInfo tr)
return true;
}
private void TradeHT(ITrainerInfo tr)
{
if (HandlingTrainerName != tr.OT)
{
HandlingTrainerFriendship = PersonalInfo.BaseFriendship;
HandlingTrainerName = tr.OT;
}
CurrentHandler = 1;
HandlingTrainerGender = tr.Gender;
HandlingTrainerLanguage = (byte)tr.Language;
}
private void TradeHT(ITrainerInfo tr) => PKH.UpdateHandler(this, tr);
// Maximums
public override ushort MaxMoveID => Legal.MaxMoveID_8a;

View File

@@ -346,10 +346,15 @@ protected override bool TradeOT(ITrainerInfo tr)
protected override void TradeHT(ITrainerInfo tr)
{
if (HandlingTrainerName != tr.OT)
Span<char> ht = stackalloc char[TrashCharCountTrainer];
var len = LoadString(HandlingTrainerTrash, ht);
ht = ht[..len];
var other = tr.OT;
if (!ht.SequenceEqual(other))
{
HandlingTrainerName = other;
HandlingTrainerFriendship = CurrentFriendship; // copy friendship instead of resetting (don't alter CP)
HandlingTrainerName = tr.OT;
}
CurrentHandler = 1;
HandlingTrainerGender = tr.Gender;

View File

@@ -121,18 +121,7 @@ private bool TradeOT(ITrainerInfo tr)
return true;
}
private void TradeHT(ITrainerInfo tr)
{
if (HandlingTrainerName != tr.OT)
{
HandlingTrainerFriendship = PersonalInfo.BaseFriendship;
HandlingTrainerName = tr.OT;
}
CurrentHandler = 1;
HandlingTrainerGender = tr.Gender;
HandlingTrainerLanguage = (byte)tr.Language;
//this.SetTradeMemoryHT8();
}
private void TradeHT(ITrainerInfo tr) => PKH.UpdateHandler(this, tr);
// Maximums
public override ushort MaxMoveID => Legal.MaxMoveID_8b;

View File

@@ -428,17 +428,18 @@ protected override bool TradeOT(ITrainerInfo tr)
protected override void TradeHT(ITrainerInfo tr)
{
if (tr.OT != HandlingTrainerName || tr.Gender != HandlingTrainerGender || (Geo1_Country == 0 && Geo1_Region == 0 && !IsUntradedEvent6))
{
if (tr is IRegionOrigin o)
this.TradeGeoLocation(o.Country, o.Region);
}
Span<char> ht = stackalloc char[TrashCharCountTrainer];
var len = LoadString(HandlingTrainerTrash, ht);
ht = ht[..len];
if (tr.OT != HandlingTrainerName)
var other = tr.OT;
if (!ht.SequenceEqual(other) || tr.Gender != HandlingTrainerGender || (Geo1_Country == 0 && Geo1_Region == 0 && !IsUntradedEvent6))
{
HandlingTrainerName = other;
HandlingTrainerFriendship = PersonalInfo.BaseFriendship;
HandlingTrainerAffection = 0;
HandlingTrainerName = tr.OT;
if (tr is IRegionOrigin o)
this.TradeGeoLocation(o.Country, o.Region);
}
CurrentHandler = 1;
HandlingTrainerGender = tr.Gender;

View File

@@ -486,17 +486,19 @@ protected override bool TradeOT(ITrainerInfo tr)
protected override void TradeHT(ITrainerInfo tr)
{
if (tr.OT != HandlingTrainerName || tr.Gender != HandlingTrainerGender || (Geo1_Country == 0 && Geo1_Region == 0 && !IsUntradedEvent6))
{
// No geolocations are set ingame -- except for bank transfers. Don't emulate bank transfers
// this.TradeGeoLocation(tr.Country, tr.SubRegion);
}
// No geolocations are set in-game -- except for bank transfers. Don't emulate bank transfers
// this.TradeGeoLocation(tr.Country, tr.SubRegion);
if (HandlingTrainerName != tr.OT)
Span<char> ht = stackalloc char[TrashCharCountTrainer];
var len = LoadString(HandlingTrainerTrash, ht);
ht = ht[..len];
var other = tr.OT;
if (!ht.SequenceEqual(other))
{
HandlingTrainerName = other;
HandlingTrainerFriendship = PersonalInfo.BaseFriendship;
HandlingTrainerAffection = 0;
HandlingTrainerName = tr.OT;
}
CurrentHandler = 1;
HandlingTrainerGender = tr.Gender;

View File

@@ -103,18 +103,7 @@ private bool TradeOT(ITrainerInfo tr)
return true;
}
private void TradeHT(ITrainerInfo tr)
{
if (HandlingTrainerName != tr.OT)
{
HandlingTrainerFriendship = 50;
HandlingTrainerName = tr.OT;
}
CurrentHandler = 1;
HandlingTrainerGender = tr.Gender;
HandlingTrainerLanguage = (byte)tr.Language;
this.SetTradeMemoryHT8();
}
private void TradeHT(ITrainerInfo tr) => PKH.UpdateHandler(this, tr);
// Maximums
public override ushort MaxMoveID => Legal.MaxMoveID_8;

View File

@@ -661,19 +661,7 @@ private bool TradeOT(ITrainerInfo tr)
return true;
}
private void TradeHT(ITrainerInfo tr)
{
if (HandlingTrainerName != tr.OT)
{
HandlingTrainerFriendship = 50;
HandlingTrainerName = tr.OT;
}
CurrentHandler = 1;
HandlingTrainerGender = tr.Gender;
if (HandlingTrainerLanguage == 0)
this.ClearMemoriesHT();
HandlingTrainerLanguage = (byte)tr.Language;
}
private void TradeHT(ITrainerInfo tr) => PKH.UpdateHandler(this, tr);
// Maximums
public override ushort MaxMoveID => Legal.MaxMoveID_9;

View File

@@ -10,6 +10,13 @@ namespace PKHeX.WinForms;
public partial class TrashEditor : Form
{
private readonly IStringConverter Converter;
private readonly ToolTip Tip = new() { InitialDelay = 200, IsBalloon = false, AutoPopDelay = 32_767 };
private readonly List<NumericUpDown> Bytes = [];
public string FinalString { get; private set; }
public byte[] FinalBytes { get; private set; }
private readonly byte[] Raw;
private bool editing;
public TrashEditor(TextBoxBase TB_NN, IStringConverter sav, byte generation) : this(TB_NN, [], sav, generation) { }
@@ -60,12 +67,6 @@ public TrashEditor(TextBoxBase TB_NN, Span<byte> raw, IStringConverter converter
};
}
private readonly ToolTip Tip = new() { InitialDelay = 200, IsBalloon = false, AutoPopDelay = 32_767 };
private readonly List<NumericUpDown> Bytes = [];
public string FinalString;
public byte[] FinalBytes;
private readonly byte[] Raw;
private bool editing;
private void B_Cancel_Click(object sender, EventArgs e) => Close();
private void B_Save_Click(object sender, EventArgs e)
@@ -153,8 +154,10 @@ private void UpdateString(object sender, EventArgs e)
return;
editing = true;
// build bytes
byte[] data = SetString(TB_Text.Text);
Array.Copy(data, Raw, Math.Min(data.Length, Raw.Length));
ReadOnlySpan<byte> data = SetString(TB_Text.Text);
if (data.Length > Raw.Length)
data = data[..Raw.Length];
data.CopyTo(Raw);
for (int i = 0; i < Raw.Length; i++)
Bytes[i].Value = Raw[i];
editing = false;
@@ -163,8 +166,8 @@ private void UpdateString(object sender, EventArgs e)
private void B_ApplyTrash_Click(object sender, EventArgs e)
{
string text = GetTrashString();
byte[] data = SetString(text);
byte[] current = SetString(TB_Text.Text);
ReadOnlySpan<byte> data = SetString(text);
ReadOnlySpan<byte> current = SetString(TB_Text.Text);
if (data.Length <= current.Length)
{
WinFormsUtil.Alert("Trash byte layer is hidden by current text.",