mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 12:33:06 +00:00
88830e0d00
Updates from net46->net7, dropping support for mono in favor of using the latest runtime (along with the performance/API improvements). Releases will be posted as 64bit only for now. Refactors a good amount of internal API methods to be more performant and more customizable for future updates & fixes. Adds functionality for Batch Editor commands to `>`, `<` and <=/>= TID/SID properties renamed to TID16/SID16 for clarity; other properties exposed for Gen7 / display variants. Main window has a new layout to account for DPI scaling (8 point grid) Fixed: Tatsugiri and Paldean Tauros now output Showdown form names as Showdown expects Changed: Gen9 species now interact based on the confirmed National Dex IDs (closes #3724) Fixed: Pokedex set all no longer clears species with unavailable non-base forms (closes #3720) Changed: Hyper Training suggestions now apply for level 50 in SV. (closes #3714) Fixed: B2/W2 hatched egg met locations exclusive to specific versions are now explicitly checked (closes #3691) Added: Properties for ribbon/mark count (closes #3659) Fixed: Traded SV eggs are now checked correctly (closes #3692)
177 lines
5.3 KiB
C#
177 lines
5.3 KiB
C#
using System;
|
||
using static PKHeX.Core.LegalityCheckStrings;
|
||
|
||
namespace PKHeX.Core;
|
||
|
||
/// <summary>
|
||
/// Verifies the <see cref="PKM.OT_Name"/>.
|
||
/// </summary>
|
||
public sealed class TrainerNameVerifier : Verifier
|
||
{
|
||
protected override CheckIdentifier Identifier => CheckIdentifier.Trainer;
|
||
|
||
private static readonly string[] SuspiciousOTNames =
|
||
{
|
||
"PKHeX",
|
||
"PKHeX",
|
||
};
|
||
|
||
public override void Verify(LegalityAnalysis data)
|
||
{
|
||
var pk = data.Entity;
|
||
var enc = data.EncounterMatch;
|
||
if (!IsPlayerOriginalTrainer(enc))
|
||
return; // already verified
|
||
|
||
var ot = pk.OT_Name;
|
||
if (ot.Length == 0)
|
||
data.AddLine(GetInvalid(LOTShort));
|
||
|
||
if (IsOTNameSuspicious(ot))
|
||
{
|
||
data.AddLine(Get(LOTSuspicious, Severity.Fishy));
|
||
}
|
||
|
||
if (pk.VC)
|
||
{
|
||
VerifyOTG1(data);
|
||
}
|
||
else if (ot.Length > Legal.GetMaxLengthOT(data.Info.Generation, (LanguageID)pk.Language))
|
||
{
|
||
if (!IsEdgeCaseLength(pk, data.EncounterOriginal, ot))
|
||
data.AddLine(Get(LOTLong, Severity.Invalid));
|
||
}
|
||
|
||
if (ParseSettings.CheckWordFilter)
|
||
{
|
||
if (WordFilter.IsFiltered(ot, out var badPattern))
|
||
data.AddLine(GetInvalid($"Wordfilter: {badPattern}"));
|
||
if (ContainsTooManyNumbers(ot, data.Info.Generation))
|
||
data.AddLine(GetInvalid("Wordfilter: Too many numbers."));
|
||
|
||
if (WordFilter.IsFiltered(pk.HT_Name, out badPattern))
|
||
data.AddLine(GetInvalid($"Wordfilter: {badPattern}"));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Checks if any player (human) was the original OT.
|
||
/// </summary>
|
||
internal static bool IsPlayerOriginalTrainer(IEncounterable enc) => enc switch
|
||
{
|
||
EncounterTrade { HasTrainerName: true } => false,
|
||
MysteryGift { IsEgg: false } => false,
|
||
EncounterStatic5N => false,
|
||
_ => true,
|
||
};
|
||
|
||
public static bool IsEdgeCaseLength(PKM pk, IEncounterTemplate e, string ot)
|
||
{
|
||
if (e.EggEncounter)
|
||
{
|
||
if (e is WC3 wc3 && pk.IsEgg && wc3.OT_Name == ot)
|
||
return true; // Fixed OT Mystery Gift Egg
|
||
bool eggEdge = pk.IsEgg ? pk.IsTradedEgg || pk.Format == 3 : pk.WasTradedEgg;
|
||
if (!eggEdge)
|
||
return false;
|
||
var len = Legal.GetMaxLengthOT(e.Generation, LanguageID.English); // max case
|
||
return ot.Length <= len;
|
||
}
|
||
|
||
if (e is EncounterTrade { HasTrainerName: true })
|
||
return true; // already verified
|
||
|
||
if (e is MysteryGift mg && mg.OT_Name.Length == ot.Length)
|
||
return true; // Mattle Ho-Oh
|
||
return false;
|
||
}
|
||
|
||
public void VerifyOTG1(LegalityAnalysis data)
|
||
{
|
||
var pk = data.Entity;
|
||
string tr = pk.OT_Name;
|
||
|
||
if (tr.Length == 0)
|
||
{
|
||
if (pk is SK2 {TID16: 0, IsRental: true})
|
||
{
|
||
data.AddLine(Get(LOTShort, Severity.Fishy));
|
||
}
|
||
else
|
||
{
|
||
data.AddLine(GetInvalid(LOTShort));
|
||
return;
|
||
}
|
||
}
|
||
|
||
VerifyG1OTWithinBounds(data, tr);
|
||
|
||
if (pk.OT_Gender == 1)
|
||
{
|
||
if (pk is ICaughtData2 {CaughtData:0} or { Format: > 2, VC1: true } || data is {EncounterOriginal: {Generation:1} or EncounterStatic2E {IsGift:true}})
|
||
data.AddLine(GetInvalid(LG1OTGender));
|
||
}
|
||
}
|
||
|
||
private void VerifyG1OTWithinBounds(LegalityAnalysis data, ReadOnlySpan<char> str)
|
||
{
|
||
if (StringConverter12.GetIsG1English(str))
|
||
{
|
||
if (str.Length > 7 && data.EncounterOriginal is not EncounterTradeGB) // OT already verified; GER shuckle has 8 chars
|
||
data.AddLine(GetInvalid(LOTLong));
|
||
}
|
||
else if (StringConverter12.GetIsG1Japanese(str))
|
||
{
|
||
if (str.Length > 5)
|
||
data.AddLine(GetInvalid(LOTLong));
|
||
}
|
||
else if (data.Entity.Korean && StringConverter2KOR.GetIsG2Korean(str))
|
||
{
|
||
if (str.Length > 5)
|
||
data.AddLine(GetInvalid(LOTLong));
|
||
}
|
||
else if (data.EncounterOriginal is not EncounterTrade2) // OT already verified; SPA Shuckle/Voltorb transferred from French can yield 2 inaccessible chars
|
||
{
|
||
data.AddLine(GetInvalid(LG1CharOT));
|
||
}
|
||
}
|
||
|
||
private static bool IsOTNameSuspicious(string name)
|
||
{
|
||
foreach (var s in SuspiciousOTNames)
|
||
{
|
||
if (s.StartsWith(name, StringComparison.InvariantCultureIgnoreCase))
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
public static bool ContainsTooManyNumbers(string str, int originalGeneration)
|
||
{
|
||
if (originalGeneration <= 3)
|
||
return false; // no limit from these generations
|
||
int max = originalGeneration < 6 ? 4 : 5;
|
||
if (str.Length <= max)
|
||
return false;
|
||
int count = GetNumberCount(str);
|
||
return count > max;
|
||
}
|
||
|
||
private static int GetNumberCount(string str)
|
||
{
|
||
static bool IsNumber(char c)
|
||
{
|
||
if ('0' <= c)
|
||
return c <= '9';
|
||
return (uint)(c - '0') <= 9;
|
||
}
|
||
|
||
int ctr = 0;
|
||
foreach (var c in str)
|
||
{
|
||
if (IsNumber(c))
|
||
++ctr;
|
||
}
|
||
return ctr;
|
||
}
|
||
}
|