mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-22 03:53:08 +00:00
Revise ActiveTrainer checks if unset (unit tests)
No longer need to disable correct-handler-state check for unit tests Adds indication for HT not matching gender (if active trainer is set)
This commit is contained in:
parent
0f7d6e1b6a
commit
3dc84d6a39
9 changed files with 81 additions and 51 deletions
|
@ -483,6 +483,7 @@ public static class LegalityCheckStrings
|
|||
public static string LTransferFlagIllegal { get; set; } = "Flagged as illegal by the game (glitch abuse).";
|
||||
public static string LTransferHTFlagRequired { get; set; } = "Current handler cannot be past gen OT for transferred specimen.";
|
||||
public static string LTransferHTMismatchName { get; set; } = "Handling trainer does not match the expected trainer name.";
|
||||
public static string LTransferHTMismatchGender { get; set; } = "Handling trainer does not match the expected trainer gender.";
|
||||
public static string LTransferHTMismatchLanguage { get; set; } = "Handling trainer does not match the expected trainer language.";
|
||||
public static string LTransferMet { get; set; } = "Invalid Met Location, expected Poké Transfer or Crown.";
|
||||
public static string LTransferNotPossible { get; set; } = "Unable to transfer into current format from origin format.";
|
||||
|
|
|
@ -25,7 +25,7 @@ public sealed class LegalitySettings
|
|||
/// <param name="allowRNG">If true, allows special encounters to be nicknamed.</param>
|
||||
public void SetCheckWithoutSaveFile(bool checkHOME = true, bool allowRNG = false)
|
||||
{
|
||||
Handler.CheckActiveHandler = false;
|
||||
ParseSettings.ClearActiveTrainer();
|
||||
if (!checkHOME)
|
||||
HOMETransfer.Disable();
|
||||
if (allowRNG)
|
||||
|
|
|
@ -8,7 +8,20 @@ namespace PKHeX.Core;
|
|||
/// <remarks><see cref="LegalityAnalysis"/></remarks>
|
||||
public static class ParseSettings
|
||||
{
|
||||
internal static ITrainerInfo ActiveTrainer { get; set; } = new SimpleTrainerInfo(GameVersion.Any) { OT = string.Empty, Language = -1 };
|
||||
/// <summary>
|
||||
/// Current Trainer of the active Save Data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Used for legality checks to determine if the data is from the active save file.
|
||||
/// Defaults to a blank trainer with no data to prevent matching unless another reference (save file) is loaded.
|
||||
/// </remarks>
|
||||
internal static ITrainerInfo? ActiveTrainer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resets active trainer to null, disabling any legality checks that compare to a currently loaded trainer.
|
||||
/// </summary>
|
||||
/// <remarks>Shouldn't need to use this unless you want to undo any loading of save data to revert to an uninitialized state.</remarks>
|
||||
public static void ClearActiveTrainer() => ActiveTrainer = null;
|
||||
|
||||
/// <summary>
|
||||
/// Master settings configuration for legality analysis.
|
||||
|
@ -63,8 +76,6 @@ public static class ParseSettings
|
|||
public static bool AllowGBEraEvents => AllowGBCartEra;
|
||||
public static bool AllowGBStadium2 => AllowGBCartEra;
|
||||
|
||||
internal static bool IsFromActiveTrainer(PKM pk) => ActiveTrainer.IsFromTrainer(pk);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes certain settings
|
||||
/// </summary>
|
||||
|
|
|
@ -107,7 +107,7 @@ public sealed class FormArgumentVerifier : Verifier
|
|||
{
|
||||
// Starter Legend has '1' when present in party, to differentiate.
|
||||
// Cannot be traded to other games.
|
||||
EncounterStatic9 { StarterBoxLegend: true } x when !(ParseSettings.ActiveTrainer is SAV9SV sv && sv.Version == x.Version) => GetInvalid(LTradeNotAvailable),
|
||||
EncounterStatic9 { StarterBoxLegend: true } x when ParseSettings.ActiveTrainer is { } tr && (tr is not SAV9SV sv || sv.Version != x.Version) => GetInvalid(LTradeNotAvailable),
|
||||
EncounterStatic9 { StarterBoxLegend: true } => arg switch
|
||||
{
|
||||
< 1 => GetInvalid(LFormArgumentLow),
|
||||
|
|
|
@ -53,41 +53,56 @@ public sealed class HistoryVerifier : Verifier
|
|||
private void VerifyHandlerState(LegalityAnalysis data, bool neverOT)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
var Info = data.Info;
|
||||
var info = data.Info;
|
||||
var enc = info.EncounterOriginal;
|
||||
var current = pk.CurrentHandler;
|
||||
|
||||
// HT Flag
|
||||
if (ParseSettings.Settings.Handler.CheckActiveHandler)
|
||||
if (ParseSettings.Settings.Handler.CheckActiveHandler && ParseSettings.ActiveTrainer is { } tr)
|
||||
{
|
||||
var tr = ParseSettings.ActiveTrainer;
|
||||
var withOT = tr.IsFromTrainer(pk);
|
||||
var flag = pk.CurrentHandler;
|
||||
var expect = withOT ? 0 : 1;
|
||||
if (flag != expect)
|
||||
var shouldBe0 = tr.IsFromTrainer(pk);
|
||||
byte expect = shouldBe0 ? (byte)0 : (byte)1;
|
||||
if (!IsHandlerStateCorrect(enc, pk, current, expect))
|
||||
{
|
||||
if (flag == 0 && !IsHandlerOriginalBug(Info.EncounterOriginal, pk))
|
||||
{
|
||||
data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid));
|
||||
return;
|
||||
}
|
||||
data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid));
|
||||
return;
|
||||
}
|
||||
|
||||
if (flag == 1)
|
||||
{
|
||||
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));
|
||||
}
|
||||
if (current == 1)
|
||||
CheckHandlingTrainerEquals(data, pk, tr);
|
||||
}
|
||||
|
||||
if (!pk.IsUntraded && IsUntradeableEncounter(Info.EncounterMatch)) // Starter, untradeable
|
||||
data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid));
|
||||
if ((Info.Generation != pk.Format || neverOT) && pk.CurrentHandler != 1)
|
||||
if (current != 1 && (enc.Context != pk.Context || neverOT))
|
||||
data.AddLine(GetInvalid(LTransferHTFlagRequired));
|
||||
if (!pk.IsUntraded && IsUntradeableEncounter(enc)) // Starter, untradeable
|
||||
data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid));
|
||||
}
|
||||
|
||||
private static bool IsHandlerStateCorrect(IEncounterTemplate enc, PKM pk, byte current, byte expect)
|
||||
{
|
||||
if (current == expect)
|
||||
return true;
|
||||
|
||||
if (current == 0)
|
||||
return IsHandlerOriginalBug(enc, pk);
|
||||
return false; // HT [1] should be OT [0].
|
||||
}
|
||||
|
||||
private void CheckHandlingTrainerEquals(LegalityAnalysis data, PKM pk, ITrainerInfo tr)
|
||||
{
|
||||
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.HandlingTrainerGender != tr.Gender)
|
||||
data.AddLine(GetInvalid(LTransferHTMismatchGender));
|
||||
|
||||
// If the format exposes a language, check if it matches.
|
||||
// Can be mismatched as the game only checks OT/Gender equivalence -- if it matches, don't update everything else.
|
||||
// Statistically unlikely that players will play in different languages, but it's technically possible.
|
||||
if (pk is IHandlerLanguage h && h.HandlingTrainerLanguage != tr.Language)
|
||||
data.AddLine(Get(LTransferHTMismatchLanguage, Severity.Fishy));
|
||||
}
|
||||
|
||||
private static bool IsUntradeableEncounter(IEncounterTemplate enc) => enc switch
|
||||
|
|
|
@ -23,7 +23,7 @@ public sealed class LanguageVerifier : Verifier
|
|||
}
|
||||
|
||||
// Korean Gen4 games can not trade with other Gen4 languages, but can use Pal Park with any Gen3 game/language.
|
||||
if (pk.Format == 4 && enc.Generation == 4 && !IsValidG4Korean(currentLanguage)
|
||||
if (pk.Format == 4 && enc.Generation == 4 && !IsValidGen4Korean(currentLanguage)
|
||||
&& enc is not EncounterTrade4PID {Species: (int)Species.Pikachu or (int)Species.Magikarp} // ger magikarp / eng pikachu
|
||||
)
|
||||
{
|
||||
|
@ -61,18 +61,24 @@ public sealed class LanguageVerifier : Verifier
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the <see cref="currentLanguage"/> can exist in the Generation 4 savefile.
|
||||
/// Check if the <see cref="pkmLanguage"/> can exist in the Generation 4 save file.
|
||||
/// </summary>
|
||||
/// <param name="currentLanguage"></param>
|
||||
public static bool IsValidG4Korean(int currentLanguage)
|
||||
/// <remarks>
|
||||
/// Korean Gen4 games can not trade with other Gen4 languages, but can use Pal Park with any Gen3 game/language.
|
||||
/// Anything with Gen4 origin cannot exist in the other language save file.
|
||||
/// </remarks>
|
||||
public static bool IsValidGen4Korean(int pkmLanguage)
|
||||
{
|
||||
var activeTr = ParseSettings.ActiveTrainer;
|
||||
var activeLang = activeTr.Language;
|
||||
bool savKOR = activeLang == (int) LanguageID.Korean;
|
||||
bool pkmKOR = currentLanguage == (int) LanguageID.Korean;
|
||||
if (savKOR == pkmKOR)
|
||||
return true;
|
||||
if (ParseSettings.ActiveTrainer is not SAV4 tr)
|
||||
return true; // ignore
|
||||
return IsValidGen4Korean(pkmLanguage, tr);
|
||||
}
|
||||
|
||||
return activeLang < 0; // check not overriden by Legality settings
|
||||
/// <inheritdoc cref="IsValidGen4Korean(int)"/>
|
||||
public static bool IsValidGen4Korean(int pkmLanguage, SAV4 tr)
|
||||
{
|
||||
bool savKOR = tr.Language == (int)LanguageID.Korean;
|
||||
bool pkmKOR = pkmLanguage == (int)LanguageID.Korean;
|
||||
return savKOR == pkmKOR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ public sealed class LevelVerifier : Verifier
|
|||
// Context check is only applicable to Gen1/2; transferring to Gen2 is a trade.
|
||||
// Stadium 2 can transfer across game/generation boundaries without initiating a trade.
|
||||
// Ignore this check if the environment's loaded trainer is not from Gen1/2 or is from GB Era.
|
||||
if (ParseSettings.ActiveTrainer.Generation >= 3 || ParseSettings.AllowGBStadium2)
|
||||
if (ParseSettings.AllowGBStadium2 || ParseSettings.ActiveTrainer is { Generation: not (1 or 2) })
|
||||
return false;
|
||||
|
||||
var moves = data.Info.Moves;
|
||||
|
@ -135,6 +135,9 @@ public sealed class LevelVerifier : Verifier
|
|||
return true; // traded to Gen2 for special moves
|
||||
if (pk.Format != 1)
|
||||
return true; // traded to Gen2 (current state)
|
||||
return !ParseSettings.IsFromActiveTrainer(pk); // not with OT
|
||||
|
||||
if (ParseSettings.ActiveTrainer is { } tr)
|
||||
return !tr.IsFromTrainer(pk); // not with OT
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,6 @@ namespace PKHeX.Core.Tests.Simulator;
|
|||
|
||||
public class ShowdownSetTests
|
||||
{
|
||||
static ShowdownSetTests()
|
||||
{
|
||||
ParseSettings.Settings.Handler.CheckActiveHandler = false;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimulatorGetParse()
|
||||
{
|
||||
|
|
|
@ -22,7 +22,6 @@ internal static class TestUtil
|
|||
|
||||
public static void InitializeLegality()
|
||||
{
|
||||
ParseSettings.Settings.Handler.CheckActiveHandler = false; // not checking in context of saves
|
||||
lock (InitLock)
|
||||
{
|
||||
if (IsInitialized)
|
||||
|
|
Loading…
Reference in a new issue