diff --git a/PKHeX.Core/Legality/BulkAnalysis.cs b/PKHeX.Core/Legality/BulkAnalysis.cs index e2b52e4b2..e7c20a6e0 100644 --- a/PKHeX.Core/Legality/BulkAnalysis.cs +++ b/PKHeX.Core/Legality/BulkAnalysis.cs @@ -20,11 +20,13 @@ public sealed class BulkAnalysis public readonly Dictionary Trackers = new(); public readonly bool Valid; + private readonly IBulkAnalysisSettings Settings; private readonly bool[] CloneFlags; - public BulkAnalysis(SaveFile sav) + public BulkAnalysis(SaveFile sav, IBulkAnalysisSettings settings) { Trainer = sav; + Settings = settings; var list = new List(sav.BoxSlotCount + (sav.HasParty ? 6 : 0) + 5); SlotInfoLoader.AddFromSaveFile(sav, list); list.RemoveAll(IsEmptyData); @@ -55,7 +57,11 @@ private bool ScanAll() CheckIDReuse(); CheckPIDReuse(); if (Trainer.Generation >= 6) + { CheckECReuse(); + if (Settings.CheckActiveHandler) + CheckHandlerFlag(); + } CheckDuplicateOwnedGifts(); return Parse.All(z => z.Valid); @@ -185,6 +191,31 @@ private void CheckPIDReuse() } } + private void CheckHandlerFlag() + { + for (var i = 0; i < AllData.Count; i++) + { + if (!AllAnalysis[i].Valid) + continue; + var cs = AllData[i]; + var pk = cs.Entity; + var tr = cs.SAV; + var withOT = tr.IsFromTrainer(pk); + var flag = pk.CurrentHandler; + var expect = withOT ? 0 : 1; + if (flag != expect) + AddLine(cs, LegalityCheckStrings.LTransferCurrentHandlerInvalid, CheckIdentifier.Trainer); + + if (flag == 1) + { + if (pk.HT_Name != tr.OT) + AddLine(cs, LegalityCheckStrings.LTransferHTMismatchName, CheckIdentifier.Trainer); + if (pk is IHandlerLanguage h && h.HT_Language != tr.Language) + AddLine(cs, LegalityCheckStrings.LTransferHTMismatchLanguage, CheckIdentifier.Trainer); + } + } + } + private sealed record CombinedReference(SlotCache Slot, LegalityAnalysis Analysis); private void CheckIDReuse() diff --git a/PKHeX.Core/Legality/Formatting/LegalityCheckStrings.cs b/PKHeX.Core/Legality/Formatting/LegalityCheckStrings.cs index 0c355e4bc..b496e21c8 100644 --- a/PKHeX.Core/Legality/Formatting/LegalityCheckStrings.cs +++ b/PKHeX.Core/Legality/Formatting/LegalityCheckStrings.cs @@ -465,11 +465,15 @@ public static class LegalityCheckStrings public static string LSuperUnused { get; set; } = "Unused Super Training Flag is flagged."; public static string LTransferBad { get; set; } = "Incorrectly transferred from previous generation."; + + public static string LTransferCurrentHandlerInvalid { get; set; } = "Invalid Current handler value, trainer details for save file expected another value."; public static string LTransferEgg { get; set; } = "Can't transfer Eggs between Generations."; public static string LTransferEggLocationTransporter { get; set; } = "Invalid Met Location, expected Poké Transfer."; public static string LTransferEggMetLevel { get; set; } = "Invalid Met Level for transfer."; 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 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."; public static string LTransferMetLocation { get; set; } = "Invalid Transfer Met Location."; diff --git a/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs b/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs index f7e06712d..b44995e75 100644 --- a/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs @@ -55,6 +55,27 @@ private void VerifyHandlerState(LegalityAnalysis data, bool neverOT) var Info = data.Info; // HT Flag + if (ParseSettings.CheckActiveHandler) + { + var tr = ParseSettings.ActiveTrainer; + var withOT = tr.IsFromTrainer(pkm); + var flag = pkm.CurrentHandler; + var expect = withOT ? 0 : 1; + if (flag != expect) + { + data.AddLine(GetInvalid(LegalityCheckStrings.LTransferCurrentHandlerInvalid)); + return; + } + + if (flag == 1) + { + if (pkm.HT_Name != tr.OT) + data.AddLine(GetInvalid(LegalityCheckStrings.LTransferHTMismatchName)); + if (pkm is IHandlerLanguage h && h.HT_Language != tr.Language) + data.AddLine(GetInvalid(LegalityCheckStrings.LTransferHTMismatchLanguage)); + } + } + if ((Info.Generation != pkm.Format || neverOT) && pkm.CurrentHandler != 1) data.AddLine(GetInvalid(LegalityCheckStrings.LTransferHTFlagRequired)); } diff --git a/PKHeX.Core/Legality/Verifiers/ParseSettings.cs b/PKHeX.Core/Legality/Verifiers/ParseSettings.cs index 6bb53f323..b74cd618f 100644 --- a/PKHeX.Core/Legality/Verifiers/ParseSettings.cs +++ b/PKHeX.Core/Legality/Verifiers/ParseSettings.cs @@ -31,6 +31,8 @@ public static class ParseSettings public static Severity Gen8TransferTrackerNotPresent { get; private set; } = Severity.Fishy; public static Severity NicknamedAnotherSpecies { get; private set; } = Severity.Fishy; public static Severity ZeroHeightWeight { get; private set; } = Severity.Fishy; + public static Severity CurrentHandlerMismatch { get; private set; } = Severity.Invalid; + public static bool CheckActiveHandler { get; set; } public static IReadOnlyList MoveStrings = Util.GetMovesList(GameLanguage.DefaultLanguage); public static IReadOnlyList SpeciesStrings = Util.GetSpeciesList(GameLanguage.DefaultLanguage); @@ -97,12 +99,15 @@ public static void InitFromSettings(IParseSettings settings) Gen8MemoryMissingHT = settings.Gen8MemoryMissingHT; NicknamedAnotherSpecies = settings.NicknamedAnotherSpecies; ZeroHeightWeight = settings.ZeroHeightWeight; + CurrentHandlerMismatch = settings.CurrentHandlerMismatch; + CheckActiveHandler = settings.CheckActiveHandler; } } public interface IParseSettings { bool CheckWordFilter { get; } + bool CheckActiveHandler { get; } bool AllowGen1Tradeback { get; } Severity NicknamedTrade { get; } @@ -113,5 +118,11 @@ public interface IParseSettings Severity Gen8TransferTrackerNotPresent { get; } Severity NicknamedAnotherSpecies { get; } Severity ZeroHeightWeight { get; } + Severity CurrentHandlerMismatch { get; } + } + + public interface IBulkAnalysisSettings + { + bool CheckActiveHandler { get; } } } diff --git a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs index 6c01fdefc..34042dc16 100644 --- a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs +++ b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs @@ -735,7 +735,7 @@ private void ClickVerifyCHK(object sender, EventArgs e) private void ClickVerifyStoredEntities(object sender, EventArgs e) { - var bulk = new BulkAnalysis(SAV); + var bulk = new BulkAnalysis(SAV, Main.Settings.Bulk); if (bulk.Parse.Count == 0) { WinFormsUtil.Alert("Clean!"); diff --git a/PKHeX.WinForms/Properties/PKHeXSettings.cs b/PKHeX.WinForms/Properties/PKHeXSettings.cs index 71452fced..dd265dc85 100644 --- a/PKHeX.WinForms/Properties/PKHeXSettings.cs +++ b/PKHeX.WinForms/Properties/PKHeXSettings.cs @@ -34,6 +34,7 @@ public sealed class PKHeXSettings public EntityDatabaseSettings EntityDb { get; set; } = new(); public EncounterDatabaseSettings EncounterDb { get; set; } = new(); public MysteryGiftDatabaseSettings MysteryDb { get; set; } = new(); + public BulkAnalysisSettings Bulk { get; set; } = new(); public static PKHeXSettings GetSettings(string configPath) { @@ -182,6 +183,9 @@ public sealed class LegalitySettings : IParseSettings [LocalizedDescription("Checks player given Nicknames and Trainer Names for profanity. Bad words will be flagged using the 3DS console's regex lists.")] public bool CheckWordFilter { get; set; } = true; + [LocalizedDescription("Checks the last loaded player save file data and Current Handler state to determine if the Pokémon's Current Handler does not match the expected value.")] + public bool CheckActiveHandler { get; set; } + [LocalizedDescription("GB: Allow Generation 2 tradeback learnsets for PK1 formats. Disable when checking RBY Metagame rules.")] public bool AllowGen1Tradeback { get; set; } = true; @@ -208,6 +212,9 @@ public sealed class LegalitySettings : IParseSettings [LocalizedDescription("Severity to flag a Legality Check if Pokémon has a zero value for both Height and Weight.")] public Severity ZeroHeightWeight { get; set; } = Severity.Fishy; + + [LocalizedDescription("Severity to flag a Legality Check if Pokémon's Current Handler does not match the expected value.")] + public Severity CurrentHandlerMismatch { get; set; } = Severity.Invalid; } [Serializable] @@ -388,4 +395,11 @@ public sealed class PrivacySettings [LocalizedDescription("Hide Secret Details in Editors")] public bool HideSecretDetails { get; set; } } + + [Serializable] + public sealed class BulkAnalysisSettings : IBulkAnalysisSettings + { + [LocalizedDescription("Checks the save file data and Current Handler state to determine if the Pokémon's Current Handler does not match the expected value.")] + public bool CheckActiveHandler { get; set; } = true; + } }