Refactor out legality settings, add more settings

Extract PP verifier
Add disabling of wordfilter for formats < 6
Add context-specific bypasses for nickname/handler checks
This commit is contained in:
Kurt 2024-04-26 14:33:03 -05:00
parent 94f93d41c5
commit bb6e45db60
55 changed files with 486 additions and 332 deletions

View File

@ -667,7 +667,7 @@ private void ParseSpeciesNickname(ReadOnlySpan<char> line)
{
nickname = line[..index].TrimEnd();
species = line[(index + 1)..];
if (species.Length > 0 && species[^1] == ')')
if (species.Length != 0 && species[^1] == ')')
species = species[..^1];
}
else // parenthesis value before: (Species) Nickname, incorrect

View File

@ -15,7 +15,7 @@ public sealed class BulkAnalysis
public readonly Dictionary<ulong, SlotCache> Trackers = [];
public readonly bool Valid;
public readonly IBulkAnalysisSettings Settings;
public readonly BulkAnalysisSettings Settings;
private readonly bool[] CloneFlags;
/// <summary>
@ -28,7 +28,7 @@ public sealed class BulkAnalysis
/// </summary>
public bool SetIsClone(int entryIndex, bool value = true) => CloneFlags[entryIndex] = value;
public BulkAnalysis(SaveFile sav, IBulkAnalysisSettings settings)
public BulkAnalysis(SaveFile sav, BulkAnalysisSettings settings)
{
Trainer = sav;
Settings = settings;

View File

@ -220,7 +220,7 @@ internal static class Encounters8Nest
internal static bool IsInaccessibleRank12Nest(byte nestID, byte location)
{
var noNest = GetInaccessibleRank12Nests(location);
return noNest.Length > 0 && noNest.Contains(nestID);
return noNest.Length != 0 && noNest.Contains(nestID);
}
private static ReadOnlySpan<byte> GetInaccessibleRank12Nests(byte location) => location switch

View File

@ -45,7 +45,7 @@ public static void FindVerifiedEncounter(PKM pk, LegalInfo info)
// Looks like we might have a good enough match. Check if this is really a good match.
info.EncounterMatch = enc;
if (e.Comment.Length > 0)
if (e.Comment.Length != 0)
info.Parse.Add(e);
if (!VerifySecondaryChecks(pk, info, encounter))
continue;
@ -71,7 +71,7 @@ public static void FindVerifiedEncounter(PKM pk, LegalInfo info)
if (manual != EncounterYieldFlag.None)
{
if (!info.FrameMatches) // if false, all valid RNG frame matches have already been consumed
info.Parse.Add(new CheckResult(info.Generation == 3 ? ParseSettings.RNGFrameNotFound3 : ParseSettings.RNGFrameNotFound4, CheckIdentifier.PID, LEncConditionBadRNGFrame));
info.Parse.Add(new CheckResult(ParseSettings.Settings.FramePattern.GetSeverity(info.Generation), CheckIdentifier.PID, LEncConditionBadRNGFrame));
else if (!info.PIDIVMatches) // if false, all valid PIDIV matches have already been consumed
info.Parse.Add(new CheckResult(Severity.Invalid, CheckIdentifier.PID, LPIDTypeMismatch));
}

View File

@ -84,7 +84,7 @@ public static IEnumerable<IEncounterable> GenerateEncounters(PKM pk, ReadOnlyMem
if (!IsSane(pk, moves.Span))
yield break;
if (versions.Length > 0)
if (versions.Length != 0)
{
foreach (var enc in GenerateEncounters(pk, moves, (IReadOnlyList<GameVersion>)versions))
yield return enc;

View File

@ -19,7 +19,7 @@ public sealed record EncounterTrade3XD : IEncounterable, IEncounterMatch, IEncou
public bool FatefulEncounter => true;
public bool IsFixedTrainer => true;
public bool IsFixedNickname => Nicknames.Length > 0;
public bool IsFixedNickname => Nicknames.Length != 0;
public ushort Species { get; }
public byte Level { get; }

View File

@ -131,7 +131,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
// Must match level exactly.
if (!this.IsLevelWithinRange(pk.MetLevel))
{
if ((Type is not Grass || pk.MetLevel != PressureLevel) || ParseSettings.RNGFrameNotFound4 != Severity.Invalid)
if ((Type is not Grass || pk.MetLevel != PressureLevel) || ParseSettings.Settings.FramePattern.RNGFrameNotFound4 != Severity.Invalid)
return false; // Only allow Pressure Slots through if they'll be checked by the later Lead verification.
}
}

View File

@ -87,7 +87,7 @@ public PK6 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (CanDexNav)
{
var eggMoves = GetDexNavMoves();
if (eggMoves.Length > 0)
if (eggMoves.Length != 0)
pk.RelearnMove1 = eggMoves[Util.Rand.Next(eggMoves.Length)];
}
pk.SetRandomMemory6();

View File

@ -17,7 +17,7 @@ public sealed record EncounterTrade9
public bool IsShiny => false;
public ushort EggLocation => 0;
public bool IsFixedTrainer => true;
public bool IsFixedNickname => Nicknames.Length > 0;
public bool IsFixedNickname => Nicknames.Length != 0;
public GameVersion Version { get; }
private string[] TrainerNames { get; }

View File

@ -191,6 +191,7 @@ private void ParsePK1()
Level.VerifyG1(this);
Trainer.VerifyOTGB(this);
MiscValues.VerifyMiscG1(this);
MovePP.Verify(this);
if (Entity.Format == 2)
Item.Verify(this);
}
@ -298,6 +299,7 @@ private void UpdateChecks()
BallIndex.Verify(this);
FormValues.Verify(this);
MiscValues.Verify(this);
MovePP.Verify(this);
GenderValues.Verify(this);
Item.Verify(this);
Contest.Verify(this);

View File

@ -25,6 +25,7 @@ internal static class LegalityAnalyzers
public static readonly HistoryVerifier History = new();
public static readonly ContestStatVerifier Contest = new();
public static readonly MarkingVerifier Marking = new();
public static readonly MovePPVerifier MovePP = new();
public static readonly TrainerNameVerifier Trainer = new();
public static readonly TrainerIDVerifier TrainerID = new();

View File

@ -0,0 +1,19 @@
using System.ComponentModel;
namespace PKHeX.Core;
/// <summary>
/// Settings object to contain all parameters that can be configured for legality checks.
/// </summary>
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class LegalitySettings
{
public BulkAnalysisSettings Bulk { get; set; } = new();
public FramePatternSettings FramePattern { get; set; } = new();
public GameSpecificSettings Game { get; set; } = new();
public HandlerSettings Handler { get; set; } = new();
public HOMETransferSettings HOMETransfer { get; set; } = new();
public NicknameSettings Nickname { get; set; } = new();
public TradebackSettings Tradeback { get; set; } = new();
public WordFilterSettings WordFilter { get; set; } = new();
}

View File

@ -0,0 +1,86 @@
using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// Settings for Parsing Legality
/// </summary>
/// <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>
/// Master settings configuration for legality analysis.
/// </summary>
/// <remarks>Allows configuring severities away from the default settings for users who want to deviate.</remarks>
public static LegalitySettings Settings { get; set; } = new();
/// <summary>
/// Setting to specify if an analysis should permit data sourced from the physical cartridge era of Game Boy games.
/// </summary>
/// <remarks>If false, indicates to use Virtual Console rules (which are transferable to Gen7+)</remarks>
public static bool AllowGBCartEra { private get; set; }
/// <summary>
/// Setting to specify if an analysis should permit trading a Generation 1 origin file to Generation 2, then back. Useful for checking RBY Metagame rules.
/// </summary>
public static bool AllowGen1Tradeback => Settings.Tradeback.AllowGen1Tradeback;
public static void Initialize(LegalitySettings settings)
{
Settings = settings;
}
public static IReadOnlyList<string> MoveStrings { get; private set; } = Util.GetMovesList(GameLanguage.DefaultLanguage);
public static IReadOnlyList<string> SpeciesStrings { get; private set; } = Util.GetSpeciesList(GameLanguage.DefaultLanguage);
public static string GetMoveName(ushort move) => move >= MoveStrings.Count ? LegalityCheckStrings.L_AError : MoveStrings[move];
public static void ChangeLocalizationStrings(IReadOnlyList<string> moves, IReadOnlyList<string> species)
{
SpeciesStrings = species;
MoveStrings = moves;
}
/// <summary>
/// Checks to see if Crystal is available to visit/originate from.
/// </summary>
/// <param name="pk">Data being checked</param>
/// <returns>True if Crystal data is allowed</returns>
public static bool AllowGen2Crystal(PKM pk) => !pk.Korean;
/// <summary>
/// Checks to see if the Move Reminder (Relearner) is available.
/// </summary>
/// <remarks> Pokémon Stadium 2 was never released in Korea.</remarks>
/// <param name="pk">Data being checked</param>
/// <returns>True if Crystal data is allowed</returns>
public static bool AllowGen2MoveReminder(PKM pk) => !pk.Korean && AllowGBStadium2;
public static bool AllowGen2OddEgg(PKM pk) => !pk.Japanese || AllowGBCartEra;
public static bool AllowGBVirtualConsole3DS => !AllowGBCartEra;
public static bool AllowGBEraEvents => AllowGBCartEra;
public static bool AllowGBStadium2 => AllowGBCartEra;
internal static bool IsFromActiveTrainer(PKM pk) => ActiveTrainer.IsFromTrainer(pk);
/// <summary>
/// Initializes certain settings
/// </summary>
/// <param name="sav">Newly loaded save file</param>
/// <returns>Save file is Physical GB cartridge save file (not Virtual Console)</returns>
public static bool InitFromSaveFileData(SaveFile sav)
{
ActiveTrainer = sav;
return AllowGBCartEra = sav switch
{
SAV1 { IsVirtualConsole: true } => false,
SAV2 { IsVirtualConsole: true } => false,
{ Generation: 1 or 2 } => true,
_ => false,
};
}
internal static bool IgnoreTransferIfNoTracker => Settings.HOMETransfer.HOMETransferTrackerNotPresent == Severity.Invalid;
}

View File

@ -0,0 +1,10 @@
using System.ComponentModel;
namespace PKHeX.Core;
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class BulkAnalysisSettings
{
[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;
}

View File

@ -0,0 +1,15 @@
using System.ComponentModel;
namespace PKHeX.Core;
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class FramePatternSettings
{
[LocalizedDescription("Severity to flag a Legality Check if the RNG Frame Checking logic does not find a match for Generation 3 encounters.")]
public Severity RNGFrameNotFound3 { get; set; } = Severity.Fishy;
[LocalizedDescription("Severity to flag a Legality Check if the RNG Frame Checking logic does not find a match for Generation 4 encounters.")]
public Severity RNGFrameNotFound4 { get; set; } = Severity.Invalid;
public Severity GetSeverity(byte generation) => generation == 3 ? RNGFrameNotFound3 : RNGFrameNotFound4;
}

View File

@ -0,0 +1,24 @@
using System.ComponentModel;
namespace PKHeX.Core;
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class GameSpecificSettings
{
public GameSpecificSettings7 Gen7 { get; set; } = new();
public GameSpecificSettings8 Gen8 { get; set; } = new();
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class GameSpecificSettings7
{
[LocalizedDescription("Severity to flag a Legality Check if Pokémon from Gen1/2 has a Star Shiny PID.")]
public Severity Gen7TransferStarPID { get; set; } = Severity.Fishy;
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class GameSpecificSettings8
{
[LocalizedDescription("Severity to flag a Legality Check if a Gen8 Memory is missing for the Handling Trainer.")]
public Severity Gen8MemoryMissingHT { get; set; } = Severity.Fishy;
}

View File

@ -0,0 +1,13 @@
using System.ComponentModel;
namespace PKHeX.Core;
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class HOMETransferSettings
{
[LocalizedDescription("Severity to flag a Legality Check if the HOME Tracker is Missing")]
public Severity HOMETransferTrackerNotPresent { get; set; } = Severity.Invalid;
[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;
}

View File

@ -0,0 +1,37 @@
using System.ComponentModel;
namespace PKHeX.Core;
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class HandlerSettings
{
[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;
[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;
public HandlerRestrictions Restrictions { get; set; } = new();
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed record HandlerRestrictions
{
public bool AllowHandleOTGen6 { get; set; }
public bool AllowHandleOTGen7 { get; set; }
public bool AllowHandleOTGen8 { get; set; }
public bool AllowHandleOTGen8a { get; set; }
public bool AllowHandleOTGen8b { get; set; }
public bool AllowHandleOTGen9 { get; set; }
public bool GetCanOTHandle(EntityContext encContext) => encContext switch
{
EntityContext.Gen6 => AllowHandleOTGen6,
EntityContext.Gen7 => AllowHandleOTGen7,
EntityContext.Gen8 => AllowHandleOTGen8,
EntityContext.Gen8a => AllowHandleOTGen8a,
EntityContext.Gen8b => AllowHandleOTGen8b,
EntityContext.Gen9 => AllowHandleOTGen9,
_ => false,
};
}

View File

@ -0,0 +1,79 @@
using System.ComponentModel;
namespace PKHeX.Core;
public sealed record NicknameSettings
{
[LocalizedDescription("Severity to flag a Legality Check if Pokémon has a Nickname matching another Species.")]
public Severity NicknamedAnotherSpecies { get; set; } = Severity.Fishy;
[LocalizedDescription("Nickname rules for Generation 1 and 2.")]
public NicknameRestriction Nickname12 { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 3.")]
public NicknameRestriction Nickname3 { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 4.")]
public NicknameRestriction Nickname4 { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 5.")]
public NicknameRestriction Nickname5 { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 6.")]
public NicknameRestriction Nickname6 { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 7.")]
public NicknameRestriction Nickname7 { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 8.")]
public NicknameRestriction Nickname8 { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 8a.")]
public NicknameRestriction Nickname8a { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 8b.")]
public NicknameRestriction Nickname8b { get; set; } = new();
[LocalizedDescription("Nickname rules for Generation 9.")]
public NicknameRestriction Nickname9 { get; set; } = new();
public Severity NicknamedMysteryGift(EntityContext encContext) => encContext switch
{
EntityContext.Gen1 => Nickname12.NicknamedMysteryGift,
EntityContext.Gen2 => Nickname12.NicknamedMysteryGift,
EntityContext.Gen3 => Nickname3.NicknamedMysteryGift,
EntityContext.Gen4 => Nickname4.NicknamedMysteryGift,
EntityContext.Gen5 => Nickname5.NicknamedMysteryGift,
EntityContext.Gen6 => Nickname6.NicknamedMysteryGift,
EntityContext.Gen7 => Nickname7.NicknamedMysteryGift,
EntityContext.Gen8 => Nickname8.NicknamedMysteryGift,
EntityContext.Gen8a => Nickname8a.NicknamedMysteryGift,
EntityContext.Gen8b => Nickname8b.NicknamedMysteryGift,
EntityContext.Gen9 => Nickname9.NicknamedMysteryGift,
_ => Severity.Valid,
};
public Severity NicknamedTrade(EntityContext encContext) => encContext switch
{
EntityContext.Gen1 => Nickname12.NicknamedTrade,
EntityContext.Gen2 => Nickname12.NicknamedTrade,
EntityContext.Gen3 => Nickname3.NicknamedTrade,
EntityContext.Gen4 => Nickname4.NicknamedTrade,
EntityContext.Gen5 => Nickname5.NicknamedTrade,
EntityContext.Gen6 => Nickname6.NicknamedTrade,
EntityContext.Gen7 => Nickname7.NicknamedTrade,
EntityContext.Gen8 => Nickname8.NicknamedTrade,
EntityContext.Gen9 => Nickname9.NicknamedTrade,
_ => Severity.Valid,
};
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed record NicknameRestriction
{
[LocalizedDescription("Severity to flag a Legality Check if it is a nicknamed In-Game Trade the player cannot normally nickname.")]
public Severity NicknamedTrade { get; set; } = Severity.Invalid;
[LocalizedDescription("Severity to flag a Legality Check if it is a nicknamed Mystery Gift the player cannot normally nickname.")]
public Severity NicknamedMysteryGift { get; set; } = Severity.Invalid;
}

View File

@ -0,0 +1,10 @@
using System.ComponentModel;
namespace PKHeX.Core;
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class TradebackSettings
{
[LocalizedDescription("GB: Allow Generation 2 tradeback learnsets for PK1 formats. Disable when checking RBY Metagame rules.")]
public bool AllowGen1Tradeback { get; set; } = true;
}

View File

@ -0,0 +1,15 @@
using System.ComponentModel;
namespace PKHeX.Core;
[TypeConverter(typeof(ExpandableObjectConverter))]
public sealed class WordFilterSettings
{
[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("Disables the Word Filter check for formats prior to 3DS-era.")]
public bool DisableWordFilterPastGen { get; set; }
public bool IsEnabled(int gen) => CheckWordFilter && (!DisableWordFilterPastGen || gen >= 6);
}

View File

@ -56,7 +56,7 @@ private void VerifyHandlerState(LegalityAnalysis data, bool neverOT)
var Info = data.Info;
// HT Flag
if (ParseSettings.CheckActiveHandler)
if (ParseSettings.Settings.Handler.CheckActiveHandler)
{
var tr = ParseSettings.ActiveTrainer;
var withOT = tr.IsFromTrainer(pk);
@ -214,21 +214,31 @@ public static bool GetCanOTHandle(IEncounterTemplate enc, PKM pk, byte generatio
if (generation < 6)
return generation >= 3;
return enc switch
{
IFixedTrainer { IsFixedTrainer: true } => false,
EncounterSlot8GO => false,
WC6 { OriginalTrainerName.Length: > 0 } => false,
WC7 { OriginalTrainerName.Length: > 0, TID16: not 18075 } => false, // Ash Pikachu QR Gift doesn't set Current Handler
WB7 wb7 when wb7.GetHasOT(pk.Language) => false,
WC8 wc8 when wc8.GetHasOT(pk.Language) => false,
WB8 wb8 when wb8.GetHasOT(pk.Language) => false,
WA8 wa8 when wa8.GetHasOT(pk.Language) => false,
WC8 {IsHOMEGift: true} => false,
_ => true,
};
if (GetCanOTHandle(enc, pk))
return true;
if (ParseSettings.Settings.Handler.Restrictions.GetCanOTHandle(enc.Context))
return true;
return false;
}
private static bool GetCanOTHandle(IEncounterTemplate enc, PKM pk) => enc switch
{
IFixedTrainer { IsFixedTrainer: true } => false,
EncounterSlot8GO => false,
WC6 { OriginalTrainerName.Length: > 0 } => false,
WC7 { OriginalTrainerName.Length: > 0, TID16: not 18075 } => false, // Ash Pikachu QR Gift doesn't set Current Handler
WB7 wb7 when wb7.GetHasOT(pk.Language) => false,
WC8 wc8 when wc8.GetHasOT(pk.Language) => false,
WB8 wb8 when wb8.GetHasOT(pk.Language) => false,
WA8 wa8 when wa8.GetHasOT(pk.Language) => false,
WC9 wc9 when wc9.GetHasOT(pk.Language) => false,
WC8 {IsHOMEGift: true} => false,
WC9 {IsHOMEGift: true} => false,
_ => true,
};
private static int GetBaseFriendship(IEncounterTemplate enc) => enc switch
{
IFixedOTFriendship f => f.OriginalTrainerFriendship,

View File

@ -297,12 +297,18 @@ private void VerifyOTMemory(LegalityAnalysis data)
switch (data.EncounterMatch)
{
case WC6 {IsEgg: false} g when g.OTGender != 3:
if (g.OriginalTrainerMemory is not 0 && ParseSettings.Settings.Handler.Restrictions.AllowHandleOTGen6)
break;
VerifyOTMemoryIs(data, g.OriginalTrainerMemory, g.OriginalTrainerMemoryIntensity, g.OriginalTrainerMemoryVariable, g.OriginalTrainerMemoryFeeling);
return;
case WC7 {IsEgg: false} g when g.OTGender != 3:
if (g.OriginalTrainerMemory is not 0 && ParseSettings.Settings.Handler.Restrictions.AllowHandleOTGen7)
break;
VerifyOTMemoryIs(data, g.OriginalTrainerMemory, g.OriginalTrainerMemoryIntensity, g.OriginalTrainerMemoryVariable, g.OriginalTrainerMemoryFeeling);
return;
case WC8 {IsEgg: false} g when g.OTGender != 3:
if (g.OriginalTrainerMemory is not 0 && ParseSettings.Settings.Handler.Restrictions.AllowHandleOTGen8)
break;
VerifyOTMemoryIs(data, g.OriginalTrainerMemory, g.OriginalTrainerMemoryIntensity, g.OriginalTrainerMemoryVariable, g.OriginalTrainerMemoryFeeling);
return;
@ -429,7 +435,7 @@ private void VerifyHTMemory(LegalityAnalysis data, EntityContext memoryGen)
var severity = mc.Context switch
{
Gen8 when pk is not PK8 && !pk.SWSH => Severity.Valid,
Gen8 => ParseSettings.Gen8MemoryMissingHT,
Gen8 => ParseSettings.Settings.Game.Gen8.Gen8MemoryMissingHT,
_ => Severity.Invalid,
};
if (severity != Severity.Valid)

View File

@ -41,10 +41,6 @@ public override void Verify(LegalityAnalysis data)
if (pk is IHomeTrack { HasTracker: true })
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
}
else
{
VerifyMiscMovePP(data);
}
switch (pk)
{
@ -198,8 +194,8 @@ private void VerifySVStats(LegalityAnalysis data, PK9 pk9)
{
if (enc.Generation < 8 && !data.Info.EvoChainsAllGens.HasVisitedPLA && enc is not IPogoSlot) // <=Gen8 rerolls height/weight, never zero.
data.AddLine(Get(LStatInvalidHeightWeight, Severity.Invalid, Encounter));
else if (CheckHeightWeightOdds(enc) && ParseSettings.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
else if (CheckHeightWeightOdds(enc) && ParseSettings.Settings.HOMETransfer.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.Settings.HOMETransfer.ZeroHeightWeight, Encounter));
}
if (enc is EncounterEgg { Context: EntityContext.Gen9 } g)
@ -443,50 +439,9 @@ private static void VerifyMiscFatefulEncounter(LegalityAnalysis data)
data.AddLine(GetInvalid(LFatefulInvalid, Fateful));
}
private static void VerifyMiscMovePP(LegalityAnalysis data)
{
var pk = data.Entity;
if (!Legal.IsPPUpAvailable(pk)) // No PP Ups for format
{
if (pk.Move1_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 1), CurrentMove));
if (pk.Move2_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 2), CurrentMove));
if (pk.Move3_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 3), CurrentMove));
if (pk.Move4_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 4), CurrentMove));
}
else // Check specific move indexes
{
if (!Legal.IsPPUpAvailable(pk.Move1) && pk.Move1_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 1), CurrentMove));
if (!Legal.IsPPUpAvailable(pk.Move2) && pk.Move2_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 2), CurrentMove));
if (!Legal.IsPPUpAvailable(pk.Move3) && pk.Move3_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 3), CurrentMove));
if (!Legal.IsPPUpAvailable(pk.Move4) && pk.Move4_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 4), CurrentMove));
}
if (pk.Move1_PP > pk.GetMovePP(pk.Move1, pk.Move1_PPUps))
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 1), CurrentMove));
if (pk.Move2_PP > pk.GetMovePP(pk.Move2, pk.Move2_PPUps))
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 2), CurrentMove));
if (pk.Move3_PP > pk.GetMovePP(pk.Move3, pk.Move3_PPUps))
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 3), CurrentMove));
if (pk.Move4_PP > pk.GetMovePP(pk.Move4, pk.Move4_PPUps))
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 4), CurrentMove));
}
private static void VerifyMiscEggCommon(LegalityAnalysis data)
{
var pk = data.Entity;
if (pk.Move1_PPUps != 0 || pk.Move2_PPUps != 0 || pk.Move3_PPUps != 0 || pk.Move4_PPUps != 0)
data.AddLine(GetInvalid(LEggPPUp, Egg));
if (!IsZeroMovePP(pk))
data.AddLine(GetInvalid(LEggPP, Egg));
var enc = data.EncounterMatch;
if (!EggStateLegality.GetIsEggHatchCyclesValid(pk, enc))
@ -517,19 +472,6 @@ private static void VerifyMiscEggCommon(LegalityAnalysis data)
}
}
private static bool IsZeroMovePP(PKM pk)
{
if (pk.Move1_PP != pk.GetMovePP(pk.Move1, 0))
return false;
if (pk.Move2_PP != pk.GetMovePP(pk.Move2, 0))
return false;
if (pk.Move3_PP != pk.GetMovePP(pk.Move3, 0))
return false;
if (pk.Move4_PP != pk.GetMovePP(pk.Move4, 0))
return false;
return true;
}
private static bool MovesMatchRelearn(PKM pk)
{
if (pk.Move1 != pk.RelearnMove1)
@ -720,8 +662,8 @@ private void VerifySWSHStats(LegalityAnalysis data, PK8 pk8)
data.AddLine(GetInvalid(LStatDynamaxInvalid));
}
if (CheckHeightWeightOdds(data.EncounterMatch) && pk8 is { HeightScalar: 0, WeightScalar: 0 } && ParseSettings.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
if (CheckHeightWeightOdds(data.EncounterMatch) && pk8 is { HeightScalar: 0, WeightScalar: 0 } && ParseSettings.Settings.HOMETransfer.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.Settings.HOMETransfer.ZeroHeightWeight, Encounter));
VerifyTechRecordSWSH(data, pk8);
}
@ -748,8 +690,8 @@ private void VerifyPLAStats(LegalityAnalysis data, PA8 pa8)
if (pa8.GetMoveRecordFlagAny() && !pa8.IsEgg) // already checked for eggs
data.AddLine(GetInvalid(LEggRelearnFlags));
if (CheckHeightWeightOdds(data.EncounterMatch) && pa8 is { HeightScalar: 0, WeightScalar: 0 } && ParseSettings.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
if (CheckHeightWeightOdds(data.EncounterMatch) && pa8 is { HeightScalar: 0, WeightScalar: 0 } && ParseSettings.Settings.HOMETransfer.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.Settings.HOMETransfer.ZeroHeightWeight, Encounter));
VerifyTechRecordSWSH(data, pa8);
}
@ -781,8 +723,8 @@ private void VerifyBDSPStats(LegalityAnalysis data, PB8 pb8)
if (pb8.GetMoveRecordFlagAny() && !pb8.IsEgg) // already checked for eggs
data.AddLine(GetInvalid(LEggRelearnFlags));
if (CheckHeightWeightOdds(data.EncounterMatch) && pb8 is { HeightScalar: 0, WeightScalar: 0 } && ParseSettings.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
if (CheckHeightWeightOdds(data.EncounterMatch) && pb8 is { HeightScalar: 0, WeightScalar: 0 } && ParseSettings.Settings.HOMETransfer.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.Settings.HOMETransfer.ZeroHeightWeight, Encounter));
VerifyTechRecordSWSH(data, pb8);
}

View File

@ -0,0 +1,73 @@
using System;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core;
public sealed class MovePPVerifier : Verifier
{
protected override CheckIdentifier Identifier => CheckIdentifier.CurrentMove;
public override void Verify(LegalityAnalysis data)
{
if (data.Entity.IsEgg)
{
VerifyEgg(data);
return;
}
VerifyEntity(data);
}
private void VerifyEgg(LegalityAnalysis data)
{
var pk = data.Entity;
if (pk.Move1_PPUps != 0 || pk.Move2_PPUps != 0 || pk.Move3_PPUps != 0 || pk.Move4_PPUps != 0)
data.AddLine(GetInvalid(LEggPPUp, CheckIdentifier.Egg));
if (!IsZeroMovePP(pk))
data.AddLine(GetInvalid(LEggPP, CheckIdentifier.Egg));
}
private static bool IsZeroMovePP(PKM pk)
{
if (pk.Move1_PP != pk.GetBasePP(pk.Move1))
return false;
if (pk.Move2_PP != pk.GetBasePP(pk.Move2))
return false;
if (pk.Move3_PP != pk.GetBasePP(pk.Move3))
return false;
if (pk.Move4_PP != pk.GetBasePP(pk.Move4))
return false;
return true;
}
private void VerifyEntity(LegalityAnalysis data)
{
var pk = data.Entity;
ReadOnlySpan<int> ups = [pk.Move1_PPUps, pk.Move2_PPUps, pk.Move3_PPUps, pk.Move4_PPUps];
ReadOnlySpan<ushort> moves = [pk.Move1, pk.Move2, pk.Move3, pk.Move4];
ReadOnlySpan<int> pp = [pk.Move1_PP, pk.Move2_PP, pk.Move3_PP, pk.Move4_PP];
if (!Legal.IsPPUpAvailable(pk)) // No PP Ups for format
{
for (int i = 0; i < ups.Length; i++)
{
if (ups[i] != 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, i + 1)));
}
}
else // Check specific move indexes
{
for (int i = 0; i < ups.Length; i++)
{
if (!Legal.IsPPUpAvailable(moves[i]) && ups[i] != 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, i + 1)));
}
}
for (int i = 0; i < pp.Length; i++)
{
if (pp[i] > pk.GetMovePP(moves[i], ups[i]))
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, i + 1)));
}
}
}

View File

@ -43,7 +43,7 @@ public override void Verify(LegalityAnalysis data)
if (pk.VC)
VerifyG1NicknameWithinBounds(data, nickname);
else if (enc is MysteryGift {IsEgg: false})
data.AddLine(Get(LEncGiftNicknamed, ParseSettings.NicknamedMysteryGift));
data.AddLine(Get(LEncGiftNicknamed, ParseSettings.Settings.Nickname.NicknamedMysteryGift(enc.Context)));
}
if (enc is IFixedTrainer t)
@ -63,7 +63,7 @@ public override void Verify(LegalityAnalysis data)
return;
// Non-nicknamed strings have already been checked.
if (ParseSettings.CheckWordFilter && pk.IsNicknamed)
if (ParseSettings.Settings.WordFilter.IsEnabled(pk.Format) && pk.IsNicknamed)
{
if (WordFilter.IsFiltered(nickname, out var badPattern))
data.AddLine(GetInvalid($"Word Filter: {badPattern}"));
@ -163,7 +163,7 @@ private bool VerifyUnNicknamedEncounter(LegalityAnalysis data, PKM pk, ReadOnlyS
if (!SpeciesName.TryGetSpecies(nickname, language, out var species))
continue;
var msg = species == pk.Species && language != pk.Language ? LNickMatchNoOthersFail : LNickMatchLanguageFlag;
data.AddLine(Get(msg, ParseSettings.NicknamedAnotherSpecies));
data.AddLine(Get(msg, ParseSettings.Settings.Nickname.NicknamedAnotherSpecies));
return true;
}
if (pk.Format <= 7 && StringConverter.HasEastAsianScriptCharacters(nickname) && pk is not PB7) // East Asian Scripts
@ -403,7 +403,7 @@ private static void VerifyNickname(LegalityAnalysis data, IFixedNickname fn, int
var pk = data.Entity;
var result = fn.IsNicknameMatch(pk, pk.Nickname, language)
? GetValid(LEncTradeUnchanged, CheckIdentifier.Nickname)
: Get(LEncTradeChangedNickname, ParseSettings.NicknamedTrade, CheckIdentifier.Nickname);
: Get(LEncTradeChangedNickname, ParseSettings.Settings.Nickname.NicknamedTrade(data.EncounterOriginal.Context), CheckIdentifier.Nickname);
data.AddLine(result);
}

View File

@ -1,140 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// Settings for Parsing Legality
/// </summary>
/// <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>
/// Toggles whether the word filter should be used when checking the data.
/// </summary>
public static bool CheckWordFilter { get; set; } = true;
/// <summary>
/// Setting to specify if an analysis should permit data sourced from the physical cartridge era of Game Boy games.
/// </summary>
/// <remarks>If false, indicates to use Virtual Console rules (which are transferable to Gen7+)</remarks>
public static bool AllowGBCartEra { private get; set; }
/// <summary>
/// Setting to specify if an analysis should permit trading a Generation 1 origin file to Generation 2, then back. Useful for checking RBY Metagame rules.
/// </summary>
public static bool AllowGen1Tradeback { get; set; } = true;
public static Severity NicknamedTrade { get; private set; } = Severity.Invalid;
public static Severity NicknamedMysteryGift { get; private set; } = Severity.Fishy;
public static Severity RNGFrameNotFound3 { get; private set; } = Severity.Fishy;
public static Severity RNGFrameNotFound4 { get; private set; } = Severity.Invalid;
public static Severity Gen7TransferStarPID { get; private set; } = Severity.Fishy;
public static Severity Gen8MemoryMissingHT { get; private set; } = Severity.Fishy;
public static Severity HOMETransferTrackerNotPresent { 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<string> MoveStrings { get; private set; } = Util.GetMovesList(GameLanguage.DefaultLanguage);
public static IReadOnlyList<string> SpeciesStrings { get; private set; } = Util.GetSpeciesList(GameLanguage.DefaultLanguage);
public static string GetMoveName(ushort move) => move >= MoveStrings.Count ? LegalityCheckStrings.L_AError : MoveStrings[move];
public static void ChangeLocalizationStrings(IReadOnlyList<string> moves, IReadOnlyList<string> species)
{
SpeciesStrings = species;
MoveStrings = moves;
}
/// <summary>
/// Checks to see if Crystal is available to visit/originate from.
/// </summary>
/// <remarks>Pokémon Crystal was never released in Korea.</remarks>
/// <param name="Korean">Korean data being checked</param>
/// <returns>True if Crystal data is allowed</returns>
public static bool AllowGen2Crystal(bool Korean) => !Korean;
/// <summary>
/// Checks to see if Crystal is available to visit/originate from.
/// </summary>
/// <param name="pk">Data being checked</param>
/// <returns>True if Crystal data is allowed</returns>
public static bool AllowGen2Crystal(PKM pk) => !pk.Korean;
/// <summary>
/// Checks to see if the Move Reminder (Relearner) is available.
/// </summary>
/// <remarks> Pokémon Stadium 2 was never released in Korea.</remarks>
/// <param name="pk">Data being checked</param>
/// <returns>True if Crystal data is allowed</returns>
public static bool AllowGen2MoveReminder(PKM pk) => !pk.Korean && AllowGBStadium2;
public static bool AllowGen2OddEgg(PKM pk) => !pk.Japanese || AllowGBCartEra;
public static bool AllowGBVirtualConsole3DS => !AllowGBCartEra;
public static bool AllowGBEraEvents => AllowGBCartEra;
public static bool AllowGBStadium2 => AllowGBCartEra;
internal static bool IsFromActiveTrainer(PKM pk) => ActiveTrainer.IsFromTrainer(pk);
/// <summary>
/// Initializes certain settings
/// </summary>
/// <param name="sav">Newly loaded save file</param>
/// <returns>Save file is Physical GB cartridge save file (not Virtual Console)</returns>
public static bool InitFromSaveFileData(SaveFile sav)
{
ActiveTrainer = sav;
return AllowGBCartEra = sav switch
{
SAV1 { IsVirtualConsole: true } => false,
SAV2 { IsVirtualConsole: true } => false,
{ Generation: 1 or 2 } => true,
_ => false,
};
}
internal static bool IgnoreTransferIfNoTracker => HOMETransferTrackerNotPresent == Severity.Invalid;
public static void InitFromSettings(IParseSettings settings)
{
CheckWordFilter = settings.CheckWordFilter;
AllowGen1Tradeback = settings.AllowGen1Tradeback;
NicknamedTrade = settings.NicknamedTrade;
NicknamedMysteryGift = settings.NicknamedMysteryGift;
RNGFrameNotFound3 = settings.RNGFrameNotFound3;
RNGFrameNotFound4 = settings.RNGFrameNotFound4;
Gen7TransferStarPID = settings.Gen7TransferStarPID;
HOMETransferTrackerNotPresent = settings.HOMETransferTrackerNotPresent;
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; }
Severity NicknamedMysteryGift { get; }
Severity RNGFrameNotFound3 { get; }
Severity RNGFrameNotFound4 { get; }
Severity Gen7TransferStarPID { get; }
Severity Gen8MemoryMissingHT { get; }
Severity HOMETransferTrackerNotPresent { get; }
Severity NicknamedAnotherSpecies { get; }
Severity ZeroHeightWeight { get; }
Severity CurrentHandlerMismatch { get; }
}
public interface IBulkAnalysisSettings
{
bool CheckActiveHandler { get; }
}

View File

@ -42,7 +42,7 @@ public override void Verify(LegalityAnalysis data)
data.AddLine(Get(LOTLong, Severity.Invalid));
}
if (ParseSettings.CheckWordFilter)
if (ParseSettings.Settings.WordFilter.IsEnabled(pk.Format))
{
if (WordFilter.IsFiltered(ot, out var badPattern))
data.AddLine(GetInvalid($"Word Filter: {badPattern}"));

View File

@ -68,7 +68,7 @@ private static void VerifyVCShinyXorIfShiny(LegalityAnalysis data)
// (15:65536, ~1:4096) odds on a given shiny transfer!
var xor = data.Entity.ShinyXor;
if (xor is <= 15 and not 0)
data.AddLine(Get(LEncStaticPIDShiny, ParseSettings.Gen7TransferStarPID, CheckIdentifier.PID));
data.AddLine(Get(LEncStaticPIDShiny, ParseSettings.Settings.Game.Gen7.Gen7TransferStarPID, CheckIdentifier.PID));
}
private static void VerifyVCGeolocation(LegalityAnalysis data)
@ -190,7 +190,7 @@ private void VerifyHOMETracker(LegalityAnalysis data, PKM pk)
// Can't validate the actual values (we aren't the server), so we can only check against zero.
if (pk is IHomeTrack { HasTracker: false })
{
data.AddLine(Get(LTransferTrackerMissing, ParseSettings.HOMETransferTrackerNotPresent));
data.AddLine(Get(LTransferTrackerMissing, ParseSettings.Settings.HOMETransfer.HOMETransferTrackerNotPresent));
// To the reader: It seems like the best course of action for setting a tracker is:
// - Transfer a 0-Tracker pk to HOME to get assigned a valid Tracker
// - Don't make one up.

View File

@ -172,7 +172,7 @@ public override void GetIVs(Span<int> value)
value[5] = IV_SPD;
}
public bool IsNicknamed => Nickname.Length > 0;
public bool IsNicknamed => Nickname.Length != 0;
public override bool IsShiny => PIDType == 2;
public override Moveset Moves => new(Move1, Move2, Move3, Move4);
public override bool IsEntity { get => CardType == 1; set { if (value) CardType = 1; } }

View File

@ -267,7 +267,7 @@ public int[] EVs
}
}
public bool IsNicknamed => Nickname.Length > 0;
public bool IsNicknamed => Nickname.Length != 0;
public override Moveset Moves
{
@ -325,11 +325,11 @@ public override PK6 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
ContestTough = ContestTough,
ContestSheen = ContestSheen,
OriginalTrainerName = OriginalTrainerName.Length > 0 ? OriginalTrainerName : tr.OT,
OriginalTrainerName = OriginalTrainerName.Length != 0 ? OriginalTrainerName : tr.OT,
OriginalTrainerGender = OTGender != 3 ? (byte)(OTGender % 2) : tr.Gender,
HandlingTrainerName = OriginalTrainerName.Length > 0 ? tr.OT : string.Empty,
HandlingTrainerGender = OriginalTrainerName.Length > 0 ? tr.Gender : default,
CurrentHandler = OriginalTrainerName.Length > 0 ? (byte)1 : (byte)0,
HandlingTrainerName = OriginalTrainerName.Length != 0 ? tr.OT : string.Empty,
HandlingTrainerGender = OriginalTrainerName.Length != 0 ? tr.Gender : default,
CurrentHandler = OriginalTrainerName.Length != 0 ? (byte)1 : (byte)0,
EXP = Experience.GetEXP(Level, pi.EXPGrowth),

View File

@ -328,7 +328,7 @@ public int[] EVs
}
}
public bool IsNicknamed => Nickname.Length > 0 || IsEgg;
public bool IsNicknamed => Nickname.Length != 0 || IsEgg;
public override Moveset Moves
{
@ -390,11 +390,11 @@ public override PK7 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
ContestTough = ContestTough,
ContestSheen = ContestSheen,
OriginalTrainerName = OriginalTrainerName.Length > 0 ? OriginalTrainerName : tr.OT,
OriginalTrainerName = OriginalTrainerName.Length != 0 ? OriginalTrainerName : tr.OT,
OriginalTrainerGender = OTGender != 3 ? (byte)(OTGender % 2) : tr.Gender,
HandlingTrainerName = OriginalTrainerName.Length > 0 ? tr.OT : string.Empty,
HandlingTrainerGender = OriginalTrainerName.Length > 0 ? tr.Gender : default,
CurrentHandler = OriginalTrainerName.Length > 0 ? (byte)1 : (byte)0,
HandlingTrainerName = OriginalTrainerName.Length != 0 ? tr.OT : string.Empty,
HandlingTrainerGender = OriginalTrainerName.Length != 0 ? tr.Gender : default,
CurrentHandler = OriginalTrainerName.Length != 0 ? (byte)1 : (byte)0,
EXP = Experience.GetEXP(currentLevel, pi.EXPGrowth),

View File

@ -824,7 +824,7 @@ protected void SetLinkTradeEgg(int day, int month, int year, ushort location)
/// </summary>
/// <param name="move">Move ID</param>
/// <returns>Amount of PP the move has by default (no PP Ups).</returns>
private int GetBasePP(ushort move) => MoveInfo.GetPP(Context, move);
public int GetBasePP(ushort move) => MoveInfo.GetPP(Context, move);
/// <summary>
/// Applies a shiny <see cref="PID"/> to the <see cref="PKM"/>.

View File

@ -25,7 +25,7 @@ public SCBlockMetadata(SCBlockAccessor accessor, IEnumerable<string> extraKeyNam
BlockList = aType.GetAllPropertiesOfType<IDataIndirect>(accessor);
ValueList = aType.GetAllConstantsOfType<uint>();
AddExtraKeyNames(ValueList, extraKeyNames);
if (exclusions.Length > 0)
if (exclusions.Length != 0)
ValueList = ValueList.Where(z => !exclusions.Any(z.Value.Contains)).ToDictionary();
Accessor = accessor;
}

View File

@ -53,9 +53,9 @@ public sealed record SaveFileMetadata(SaveFile SAV)
/// <returns>Final save file data.</returns>
public byte[] Finalize(byte[] data, BinaryExportSetting setting)
{
if (Footer.Length > 0 && setting.HasFlag(BinaryExportSetting.IncludeFooter))
if (Footer.Length != 0 && setting.HasFlag(BinaryExportSetting.IncludeFooter))
data = [..data, ..Footer];
if (Header.Length > 0 && setting.HasFlag(BinaryExportSetting.IncludeHeader))
if (Header.Length != 0 && setting.HasFlag(BinaryExportSetting.IncludeHeader))
data = [..Header, ..data];
if (setting != BinaryExportSetting.None)
Handler?.Finalize(data);

View File

@ -686,7 +686,7 @@ private void ClickGT(object? sender, EventArgs e)
byte handler = 0;
if (sender == GB_OT)
handler = 0;
else if (TB_HT.Text.Length > 0)
else if (TB_HT.Text.Length != 0)
handler = 1;
UpdateHandlerSelected(handler);
}

View File

@ -871,7 +871,7 @@ private void ClickVerifyCHK(object sender, EventArgs e)
private void ClickVerifyStoredEntities(object sender, EventArgs e)
{
var bulk = new Core.Bulk.BulkAnalysis(SAV, Main.Settings.Bulk);
var bulk = new Core.Bulk.BulkAnalysis(SAV, Main.Settings.Legality.Bulk);
if (bulk.Valid)
{
WinFormsUtil.Alert("Clean!");

View File

@ -153,7 +153,7 @@ private static (string Detail, string Encounter) GetStatsString(PKM pk, Legality
if (remaining[^1] == ')')
remaining = remaining[..^3]; // lop off gender
var item = remaining.Trim();
if (item.Length > 0)
if (item.Length != 0)
sb.AppendLine($"Held Item: {item}");
}

View File

@ -227,7 +227,7 @@ private static void FormLoadConfig(out bool BAKprompt, out bool showChangelog)
showChangelog = false;
// Version Check
if (Settings.Startup.Version.Length > 0 && Settings.Startup.ShowChangelogOnUpdate) // already run on system
if (Settings.Startup.Version.Length != 0 && Settings.Startup.ShowChangelogOnUpdate) // already run on system
{
bool parsed = Version.TryParse(Settings.Startup.Version, out var lastrev);
showChangelog = parsed && lastrev < Program.CurrentVersion;
@ -422,7 +422,7 @@ private void ReloadProgramSettings(PKHeXSettings settings)
CommonEdits.ShowdownSetBehaviorNature = settings.Import.ApplyNature;
C_SAV.FlagIllegal = settings.Display.FlagIllegal;
C_SAV.M.Hover.GlowHover = settings.Hover.HoverSlotGlowEdges;
ParseSettings.InitFromSettings(settings.Legality);
ParseSettings.Initialize(settings.Legality);
PKME_Tabs.HideSecretValues = C_SAV.HideSecretDetails = settings.Privacy.HideSecretDetails;
WinFormsUtil.DetectSaveFileOnFileOpen = settings.Startup.TryDetectRecentSave;
SelectablePictureBox.FocusBorderDeflate = GenderToggle.FocusBorderDeflate = settings.Display.FocusBorderDeflate;

View File

@ -43,7 +43,6 @@ 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();
[Browsable(false)]
public SlotExportSettings SlotExport { get; set; } = new();
@ -204,48 +203,6 @@ public enum PluginLoadSetting
UnsafeMerged,
}
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;
[LocalizedDescription("Severity to flag a Legality Check if it is a nicknamed In-Game Trade the player cannot normally nickname.")]
public Severity NicknamedTrade { get; set; } = Severity.Invalid;
[LocalizedDescription("Severity to flag a Legality Check if it is a nicknamed Mystery Gift the player cannot normally nickname.")]
public Severity NicknamedMysteryGift { get; set; } = Severity.Fishy;
[LocalizedDescription("Severity to flag a Legality Check if the RNG Frame Checking logic does not find a match for Generation 3 encounters.")]
public Severity RNGFrameNotFound3 { get; set; } = Severity.Fishy;
[LocalizedDescription("Severity to flag a Legality Check if the RNG Frame Checking logic does not find a match for Generation 4 encounters.")]
public Severity RNGFrameNotFound4 { get; set; } = Severity.Invalid;
[LocalizedDescription("Severity to flag a Legality Check if Pokémon from Gen1/2 has a Star Shiny PID.")]
public Severity Gen7TransferStarPID { get; set; } = Severity.Fishy;
[LocalizedDescription("Severity to flag a Legality Check if a Gen8 Memory is missing for the Handling Trainer.")]
public Severity Gen8MemoryMissingHT { get; set; } = Severity.Fishy;
[LocalizedDescription("Severity to flag a Legality Check if the HOME Tracker is Missing")]
public Severity HOMETransferTrackerNotPresent { get; set; } = Severity.Invalid;
[LocalizedDescription("Severity to flag a Legality Check if Pokémon has a Nickname matching another Species.")]
public Severity NicknamedAnotherSpecies { get; set; } = Severity.Fishy;
[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;
}
public sealed class EntityConverterSettings
{
[LocalizedDescription("Allow PKM file conversion paths that are not possible via official methods. Individual properties will be copied sequentially.")]
@ -508,12 +465,6 @@ public void Apply()
}
}
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;
}
public sealed class SlotExportSettings
{
[LocalizedDescription("Settings to use for box exports.")]

View File

@ -71,7 +71,7 @@ private void B_Add_Click(object sender, EventArgs e)
// If we already have text, add a new line (except if the last line is blank).
var tb = RTB_Instructions;
var batchText = tb.Text;
if (batchText.Length > 0 && !batchText.EndsWith('\n'))
if (batchText.Length != 0 && !batchText.EndsWith('\n'))
tb.AppendText(Environment.NewLine);
RTB_Instructions.AppendText(s);
}
@ -111,7 +111,7 @@ private void RunBackgroundWorker()
{ WinFormsUtil.Error(MsgBEInstructionNone); return; }
var emptyVal = sets.SelectMany(s => s.Instructions.Where(z => string.IsNullOrWhiteSpace(z.PropertyValue))).ToArray();
if (emptyVal.Length > 0)
if (emptyVal.Length != 0)
{
string props = string.Join(", ", emptyVal.Select(z => z.PropertyName));
string invalid = MsgBEPropertyEmpty + Environment.NewLine + props;

View File

@ -593,11 +593,11 @@ private async void B_Search_Click(object sender, EventArgs e)
var search = SearchDatabase();
bool legalSearch = Menu_SearchLegal.Checked ^ Menu_SearchIllegal.Checked;
bool wordFilter = ParseSettings.CheckWordFilter;
bool wordFilter = ParseSettings.Settings.WordFilter.CheckWordFilter;
if (wordFilter && legalSearch && WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgDBSearchLegalityWordfilter) == DialogResult.No)
ParseSettings.CheckWordFilter = false;
ParseSettings.Settings.WordFilter.CheckWordFilter = false;
var results = await Task.Run(() => search.ToList()).ConfigureAwait(true);
ParseSettings.CheckWordFilter = wordFilter;
ParseSettings.Settings.WordFilter.CheckWordFilter = wordFilter;
if (results.Count == 0)
{
@ -783,7 +783,7 @@ private void B_Add_Click(object sender, EventArgs e)
// If we already have text, add a new line (except if the last line is blank).
var tb = RTB_Instructions;
var batchText = tb.Text;
if (batchText.Length > 0 && !batchText.EndsWith('\n'))
if (batchText.Length != 0 && !batchText.EndsWith('\n'))
tb.AppendText(Environment.NewLine);
tb.AppendText(s);
}

View File

@ -283,7 +283,7 @@ private IEnumerable<IEncounterInfo> SearchDatabase(CancellationToken token)
return results;
ReadOnlySpan<char> batchText = RTB_Instructions.Text;
if (batchText.Length > 0 && !StringInstructionSet.HasEmptyLine(batchText))
if (batchText.Length != 0 && !StringInstructionSet.HasEmptyLine(batchText))
{
var filters = StringInstruction.GetFilters(batchText);
BatchEditing.ScreenStrings(filters);
@ -503,8 +503,8 @@ private void B_Add_Click(object sender, EventArgs e)
// If we already have text, add a new line (except if the last line is blank).
var tb = RTB_Instructions;
var batchText = tb.Text;
if (batchText.Length > 0 && !batchText.EndsWith('\n'))
if (batchText.Length != 0 && !batchText.EndsWith('\n'))
tb.AppendText(Environment.NewLine);
RTB_Instructions.AppendText(s);
tb.AppendText(s);
}
}

View File

@ -342,7 +342,7 @@ private void B_Search_Click(object sender, EventArgs e)
slotSelected = -1; // reset the slot last viewed
ReadOnlySpan<char> batchText = RTB_Instructions.Text;
if (batchText.Length > 0 && !StringInstructionSet.HasEmptyLine(batchText))
if (batchText.Length != 0 && !StringInstructionSet.HasEmptyLine(batchText))
{
var filters = StringInstruction.GetFilters(batchText);
BatchEditing.ScreenStrings(filters);
@ -472,8 +472,8 @@ private void B_Add_Click(object sender, EventArgs e)
// If we already have text, add a new line (except if the last line is blank).
var tb = RTB_Instructions;
var batchText = tb.Text;
if (batchText.Length > 0 && !batchText.EndsWith('\n'))
if (batchText.Length != 0 && !batchText.EndsWith('\n'))
tb.AppendText(Environment.NewLine);
RTB_Instructions.AppendText(s);
tb.AppendText(s);
}
}

View File

@ -162,7 +162,7 @@ static string Format(ReadOnlySpan<ushort> items, ReadOnlySpan<string> names)
var sbAdd = new StringBuilder();
foreach (var item in items)
{
if (sbAdd.Length > 0)
if (sbAdd.Length != 0)
sbAdd.Append(", ");
sbAdd.Append(names[item]);
}
@ -170,7 +170,7 @@ static string Format(ReadOnlySpan<ushort> items, ReadOnlySpan<string> names)
}
var added = Format(missing, itemlist);
var addmsg = $"Add the following items?{Environment.NewLine}{added}";
if (have.Length > 0)
if (have.Length != 0)
{
string had = Format(have, itemlist);
var havemsg = $"Already have:{Environment.NewLine}{had}";

View File

@ -182,7 +182,7 @@ private void SetEntry()
}
var forms = SAV.Dex.GetForms(species);
if (forms.Length > 0)
if (forms.Length != 0)
{
var items = LB_Form.Items;
Span<byte> arr = stackalloc byte[items.Count];

View File

@ -179,7 +179,7 @@ private void ChangeBoxDetails(object sender, EventArgs e)
private void B_Save_Click(object sender, EventArgs e)
{
if (flagArr.Length > 0)
if (flagArr.Length != 0)
SAV.BoxFlags = Array.ConvertAll(flagArr, i => (byte)i.Value);
if (CB_Unlocked.Visible)
SAV.BoxesUnlocked = CB_Unlocked.SelectedIndex;

View File

@ -61,7 +61,7 @@ private void FillTrainingBags()
{
foreach (string t in trba)
{
if (t.Length > 0)
if (t.Length != 0)
dgvBag.Items.Add(t);
}

View File

@ -229,7 +229,7 @@ private void B_Save_Click(object sender, EventArgs e)
private void ChangeSAV()
{
if (TB_NewSAV.Text.Length > 0 && TB_OldSAV.Text.Length > 0)
if (TB_NewSAV.Text.Length != 0 && TB_OldSAV.Text.Length != 0)
DiffSaves();
}

View File

@ -267,7 +267,7 @@ private void ChangeCustomConst(object sender, EventArgs e)
private void ChangeSAV(object sender, EventArgs e)
{
if (TB_NewSAV.Text.Length > 0 && TB_OldSAV.Text.Length > 0)
if (TB_NewSAV.Text.Length != 0 && TB_OldSAV.Text.Length != 0)
DiffSaves();
}

View File

@ -266,7 +266,7 @@ private void ChangeCustomConst(object sender, EventArgs e)
private void ChangeSAV(object sender, EventArgs e)
{
if (TB_NewSAV.Text.Length > 0 && TB_OldSAV.Text.Length > 0)
if (TB_NewSAV.Text.Length != 0 && TB_OldSAV.Text.Length != 0)
DiffSaves();
}

View File

@ -220,7 +220,7 @@ private void ChangeConstantIndex(object sender, EventArgs e)
private void ChangeSAV()
{
if (TB_NewSAV.Text.Length > 0 && TB_OldSAV.Text.Length > 0)
if (TB_NewSAV.Text.Length != 0 && TB_OldSAV.Text.Length != 0)
DiffSaves();
}

View File

@ -179,9 +179,9 @@ private void LoadAllBags()
var outOfBounds = Array.FindAll(invalid, item => item.Index >= itemlist.Length);
var incorrectPouch = Array.FindAll(invalid, item => item.Index < itemlist.Length);
if (outOfBounds.Length > 0)
if (outOfBounds.Length != 0)
WinFormsUtil.Error(MsgItemPouchUnknown, $"Item ID(s): {string.Join(", ", outOfBounds.Select(item => item.Index))}");
if (!Main.HaX && incorrectPouch.Length > 0)
if (!Main.HaX && incorrectPouch.Length != 0)
WinFormsUtil.Alert(string.Format(MsgItemPouchRemoved, pouch.Type), string.Join(", ", incorrectPouch.Select(item => itemlist[item.Index])), MsgItemPouchWarning);
pouch.Sanitize(itemlist.Length - 1, Main.HaX);

View File

@ -66,7 +66,7 @@ private static void VerifyAll(string folder, string subFolder, bool isValid, boo
var dn = fi.DirectoryName ?? string.Empty;
ParseSettings.AllowGBCartEra = dn.Contains("GBCartEra");
ParseSettings.AllowGen1Tradeback = dn.Contains("1 Tradeback");
ParseSettings.Settings.Tradeback.AllowGen1Tradeback = dn.Contains("1 Tradeback");
var pk = EntityFormat.GetFromBytes(data, prefer);
pk.Should().NotBeNull($"the PKM '{new FileInfo(file).Name}' should have been loaded");
if (pk == null)

View File

@ -22,6 +22,7 @@ public static string GetRepoPath()
public static void InitializeLegality()
{
ParseSettings.Settings.Handler.CheckActiveHandler = false; // not checking in context of saves
lock (InitLock)
{
if (IsInitialized)