VC1/2: Add handling for other-app language trash

This commit is contained in:
Kurt
2026-06-27 18:20:40 -05:00
parent 25e3c4d352
commit e46ff28b0c
4 changed files with 34 additions and 11 deletions

View File

@@ -166,11 +166,6 @@ private static MoveHealState HasLeftBoxAfterAcquisition(PKM pk, IEncounterTempla
private static bool IsVirtualConsoleUntouched(PK7 pk, ReadOnlySpan<ushort> moves, ReadOnlySpan<int> pp)
{
// Sanity check the language is a possible VC-transfer language.
var language = (LanguageID)pk.Language;
if (!VirtualConsolePP.IsSupportedLanguage(language))
return false;
// Pre-check to ensure the moves are only available from the game they were transferred from.
// Can't leave the box immediately after transfer (else PP would heal), but we don't prevent the Move Verifier from passing Gen3+ moves.
// Maybe in a future update we can add more strict interlocks.
@@ -185,7 +180,7 @@ private static bool IsVirtualConsoleUntouched(PK7 pk, ReadOnlySpan<ushort> moves
return false; // RSE+ moves
}
return VirtualConsolePP.IsMatch(language, moves, pp);
return VirtualConsolePP.IsMatchAnyLanguage(pk.NicknameTrash, moves, pp, pk.Species);
}
}

View File

@@ -4,7 +4,7 @@
namespace PKHeX.Core;
/// <summary>
/// Transfers from Gen1/2=>Gen7 don't set PP correctly (ignoring PP Ups and bad range fetch).
/// Transfers from Gen1/2=>Gen7 don't set PP correctly (ignoring PP Ups and bad range fetch). Transporter app language determines which table (not ROM of source or destination).
/// This class contains the expected PP values for each move in each language, as well as methods to check if a given set of moves and PP values match the expected values.
/// </summary>
public static class VirtualConsolePP
@@ -25,10 +25,13 @@ public static class VirtualConsolePP
/// <summary>
/// Sanity check the languages possible.
/// </summary>
/// <param name="language">Language ID to check.</param>
/// <param name="language">Language ID of the Transporter application to check.</param>
/// <returns>True if the language is supported for VC PP table; false otherwise.</returns>
public static bool IsSupportedLanguage(LanguageID language) => language is (>= Japanese and <= ChineseT) and not UNUSED_6;
/// <summary>
/// Gets the VC PP table for the specified Transporter application language.
/// </summary>
public static ReadOnlySpan<byte> GetTable(LanguageID language) => language switch
{
English => TableENG,
@@ -95,4 +98,30 @@ public static bool IsMatch(LanguageID language, ReadOnlySpan<ushort> moves, Read
}
return true;
}
/// <summary>
/// Checks the moves against all supported Transfer application languages to see if any match their stored PP values.
/// </summary>
public static bool IsMatchAnyLanguage(ReadOnlySpan<byte> trash, ReadOnlySpan<ushort> moves, ReadOnlySpan<int> pp, ushort species)
{
for (var language = Japanese; language <= ChineseT; language++)
{
if (language == UNUSED_6)
continue;
if (!IsMatch(language, moves, pp))
continue;
if (!IsTrashMatch(trash, species, language))
continue;
return true;
}
return false;
}
private static bool IsTrashMatch(ReadOnlySpan<byte> trash, ushort species, LanguageID language)
{
var expectName = SpeciesName.GetSpeciesName(species, (int)language);
var currentLength = TrashBytesUTF16.GetStringLength(trash) + 1; // terminator
var match = TrashBytesUTF16.IsUnderlayerPresent(expectName, trash, currentLength);
return !match.IsInvalid;
}
}

View File

@@ -550,11 +550,10 @@ internal void SetTransferPID(bool isShiny)
}
/// <summary>
/// Resets the PP of moves to match Bank's initial values.
/// Resets the PP of moves to match Transporter's initial values.
/// </summary>
public void SetVirtualConsoleTransferPP()
public void SetVirtualConsoleTransferPP(LanguageID language)
{
var language = (LanguageID)Language;
if (!VirtualConsolePP.IsSupportedLanguage(language))
return;