mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-15 00:37:11 +00:00
e29cf2a903
Checks.cs initially started out small, but over the years it has grown to handle multiple types of checks. With all these checks next to eachother, it's hard to see the overall groups. Splitting them up (potentially further?) allows for more focused maintenance & understanding. Not sure if I'm happy with the overall bandaids used (checks no longer done within LegalityAnalysis so variable repointing is excessively used), but I'm happier the way it is now compared to the huge Checks.cs
386 lines
16 KiB
C#
386 lines
16 KiB
C#
using System;
|
||
using System.Linq;
|
||
using static PKHeX.Core.LegalityCheckStrings;
|
||
|
||
namespace PKHeX.Core
|
||
{
|
||
public sealed class NicknameVerifier : Verifier
|
||
{
|
||
protected override CheckIdentifier Identifier => CheckIdentifier.Nickname;
|
||
|
||
public override void Verify(LegalityAnalysis data)
|
||
{
|
||
var pkm = data.pkm;
|
||
var EncounterMatch = data.EncounterMatch;
|
||
|
||
// If the Pokémon is not nicknamed, it should match one of the language strings.
|
||
if (pkm.Nickname.Length == 0)
|
||
{
|
||
data.AddLine(GetInvalid(V2));
|
||
return;
|
||
}
|
||
if (pkm.Species > PKX.SpeciesLang[0].Length)
|
||
{
|
||
data.AddLine(Get(V2, Severity.Indeterminate));
|
||
return;
|
||
}
|
||
|
||
if (pkm.VC && pkm.IsNicknamed)
|
||
{
|
||
VerifyG1NicknameWithinBounds(data, pkm.Nickname);
|
||
}
|
||
else if (EncounterMatch is MysteryGift m)
|
||
{
|
||
if (pkm.IsNicknamed && !m.IsEgg)
|
||
data.AddLine(Get(V0, Severity.Fishy));
|
||
}
|
||
|
||
if (EncounterMatch is EncounterTrade)
|
||
{
|
||
VerifyNicknameTrade(data);
|
||
return;
|
||
}
|
||
|
||
if (pkm.IsEgg)
|
||
{
|
||
VerifyNicknameEgg(data);
|
||
return;
|
||
}
|
||
|
||
string nickname = pkm.Nickname.Replace("'", "’");
|
||
if (VerifyUnNicknamedEncounter(data, pkm, nickname))
|
||
return;
|
||
|
||
// Non-nicknamed strings have already been checked.
|
||
if (Legal.CheckWordFilter && pkm.IsNicknamed && WordFilter.IsFiltered(nickname, out string bad))
|
||
data.AddLine(GetInvalid($"Wordfilter: {bad}"));
|
||
}
|
||
|
||
private bool VerifyUnNicknamedEncounter(LegalityAnalysis data, PKM pkm, string nickname)
|
||
{
|
||
if (pkm.IsNicknamed)
|
||
{
|
||
for (int i = 0; i < PKX.SpeciesLang.Length; i++)
|
||
{
|
||
if (!PKX.SpeciesDict[i].TryGetValue(nickname, out int index))
|
||
continue;
|
||
var msg = index == pkm.Species && i != pkm.Language ? V15 : V16;
|
||
data.AddLine(Get(msg, Severity.Fishy));
|
||
return true;
|
||
}
|
||
if (StringConverter.HasEastAsianScriptCharacters(nickname)) // East Asian Scripts
|
||
{
|
||
data.AddLine(GetInvalid(V222));
|
||
return true;
|
||
}
|
||
data.AddLine(GetValid(V17));
|
||
}
|
||
else if (pkm.Format < 3)
|
||
{
|
||
// pk1/pk2 IsNicknamed getter checks for match, logic should only reach here if matches.
|
||
data.AddLine(GetValid(V18));
|
||
}
|
||
else
|
||
{
|
||
var EncounterMatch = data.EncounterMatch;
|
||
// Can't have another language name if it hasn't evolved or wasn't a language-traded egg.
|
||
bool evolved = EncounterMatch.Species != pkm.Species;
|
||
bool match = PKX.GetSpeciesNameGeneration(pkm.Species, pkm.Language, pkm.Format) == nickname;
|
||
if (pkm.WasTradedEgg || evolved)
|
||
match |= !PKX.IsNicknamedAnyLanguage(pkm.Species, nickname, pkm.Format);
|
||
if (!match && pkm.Format == 5 && !pkm.IsNative) // transfer
|
||
{
|
||
if (evolved)
|
||
match |= !PKX.IsNicknamedAnyLanguage(pkm.Species, nickname, 4);
|
||
else
|
||
match |= PKX.GetSpeciesNameGeneration(pkm.Species, pkm.Language, 4) == nickname;
|
||
}
|
||
|
||
if (!match)
|
||
{
|
||
if (EncounterMatch is WC7 wc7 && wc7.IsAshGreninjaWC7(pkm))
|
||
data.AddLine(GetValid(V19));
|
||
else
|
||
data.AddLine(GetInvalid(V20));
|
||
}
|
||
else
|
||
data.AddLine(GetValid(V18));
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private void VerifyNicknameEgg(LegalityAnalysis data)
|
||
{
|
||
var Info = data.Info;
|
||
var pkm = data.pkm;
|
||
var EncounterMatch = Info.EncounterMatch;
|
||
switch (pkm.Format)
|
||
{
|
||
case 4:
|
||
if (pkm.IsNicknamed) // gen4 doesn't use the nickname flag for eggs
|
||
data.AddLine(GetInvalid(V224, CheckIdentifier.Egg));
|
||
break;
|
||
case 7:
|
||
if (EncounterMatch is EncounterStatic ^ !pkm.IsNicknamed) // gen7 doesn't use for ingame gifts
|
||
data.AddLine(GetInvalid(pkm.IsNicknamed ? V224 : V12, CheckIdentifier.Egg));
|
||
break;
|
||
default:
|
||
if (!pkm.IsNicknamed)
|
||
data.AddLine(GetInvalid(V12, CheckIdentifier.Egg));
|
||
break;
|
||
}
|
||
|
||
if (pkm.Format == 2 && pkm.IsEgg && !PKX.IsNicknamedAnyLanguage(0, pkm.Nickname, 2))
|
||
data.AddLine(GetValid(V14, CheckIdentifier.Egg));
|
||
else if (PKX.GetSpeciesNameGeneration(0, pkm.Language, Info.Generation) != pkm.Nickname)
|
||
data.AddLine(GetInvalid(V13, CheckIdentifier.Egg));
|
||
else
|
||
data.AddLine(GetValid(V14, CheckIdentifier.Egg));
|
||
}
|
||
private void VerifyNicknameTrade(LegalityAnalysis data)
|
||
{
|
||
switch (data.Info.Generation)
|
||
{
|
||
case 1:
|
||
case 2: VerifyTrade12(data); return;
|
||
case 3: VerifyTrade3(data); return;
|
||
case 4: VerifyTrade4(data); return;
|
||
case 5: VerifyTrade5(data); return;
|
||
case 6: VerifyTrade6(data); return;
|
||
case 7: VerifyTrade7(data); return;
|
||
}
|
||
}
|
||
|
||
private void VerifyG1NicknameWithinBounds(LegalityAnalysis data, string str)
|
||
{
|
||
var pkm = data.pkm;
|
||
if (StringConverter.GetIsG1English(str))
|
||
{
|
||
if (str.Length > 10)
|
||
data.AddLine(GetInvalid(V1));
|
||
}
|
||
else if (StringConverter.GetIsG1Japanese(str))
|
||
{
|
||
if (str.Length > 5)
|
||
data.AddLine(GetInvalid(V1));
|
||
}
|
||
else if (pkm.Korean && StringConverter.GetIsG2Korean(str))
|
||
{
|
||
if (str.Length > 5)
|
||
data.AddLine(GetInvalid(V38));
|
||
}
|
||
else
|
||
{
|
||
data.AddLine(GetInvalid(V422));
|
||
}
|
||
}
|
||
|
||
|
||
private void VerifyTrade12(LegalityAnalysis data)
|
||
{
|
||
var et = (EncounterTrade)data.EncounterOriginal;
|
||
if (et.TID != 0) // Gen2 Trade
|
||
return; // already checked all relevant properties when fetching with getValidEncounterTradeVC2
|
||
|
||
if (!EncounterGenerator.IsEncounterTrade1Valid(data.pkm))
|
||
data.AddLine(GetInvalid(V10, CheckIdentifier.Trainer));
|
||
}
|
||
private void VerifyTrade3(LegalityAnalysis data)
|
||
{
|
||
var pkm = data.pkm;
|
||
var EncounterMatch = data.EncounterMatch;
|
||
if (pkm.FRLG)
|
||
{
|
||
int lang = pkm.Language;
|
||
if (EncounterMatch.Species == 124) // Jynx
|
||
lang = DetectTradeLanguageG3DANTAEJynx(pkm, lang);
|
||
VerifyTradeTable(data, Encounters3.TradeFRLG, Encounters3.TradeGift_FRLG, lang);
|
||
}
|
||
else
|
||
VerifyTradeTable(data, Encounters3.TradeRSE, Encounters3.TradeGift_RSE);
|
||
}
|
||
private void VerifyTrade4(LegalityAnalysis data)
|
||
{
|
||
var pkm = data.pkm;
|
||
var EncounterMatch = data.EncounterMatch;
|
||
if (pkm.TID == 1000)
|
||
{
|
||
VerifyTrade4Ranch(data);
|
||
return;
|
||
}
|
||
if (pkm.HGSS)
|
||
{
|
||
int lang = pkm.Language;
|
||
if (EncounterMatch.Species == 25) // Pikachu
|
||
{
|
||
lang = DetectTradeLanguageG4SurgePikachu(pkm, lang);
|
||
// flag korean magikarp on gen4 saves since the pkm.Language is German
|
||
if (pkm.Format == 4 && lang == (int)LanguageID.Korean && Legal.SavegameLanguage != (int)LanguageID.Korean && Legal.SavegameLanguage >= 0)
|
||
data.AddLine(GetInvalid(string.Format(V610, V611, V612), CheckIdentifier.Language));
|
||
}
|
||
VerifyTradeTable(data, Encounters4.TradeHGSS, Encounters4.TradeGift_HGSS, lang);
|
||
}
|
||
else // DPPt
|
||
{
|
||
int lang = pkm.Language;
|
||
if (EncounterMatch.Species == 129) // Magikarp
|
||
{
|
||
lang = DetectTradeLanguageG4MeisterMagikarp(pkm, lang);
|
||
// flag korean magikarp on gen4 saves since the pkm.Language is German
|
||
if (pkm.Format == 4 && lang == (int)LanguageID.Korean && Legal.SavegameLanguage != (int)LanguageID.Korean && Legal.SavegameLanguage >= 0)
|
||
data.AddLine(GetInvalid(string.Format(V610, V611, V612), CheckIdentifier.Language));
|
||
}
|
||
else if (!pkm.Pt && lang == 1) // DP English origin are Japanese lang
|
||
{
|
||
int index = Array.IndexOf(Encounters4.TradeGift_DPPt, data.EncounterMatch);
|
||
if (Encounters4.TradeDPPt[1][index] != pkm.Nickname) // not japanese
|
||
lang = 2; // English
|
||
}
|
||
VerifyTradeTable(data, Encounters4.TradeDPPt, Encounters4.TradeGift_DPPt, lang);
|
||
}
|
||
}
|
||
private static int DetectTradeLanguageG3DANTAEJynx(PKM pk, int lang)
|
||
{
|
||
if (lang != (int)LanguageID.Italian)
|
||
return lang;
|
||
|
||
if (pk.Version == (int)GameVersion.LG)
|
||
lang = (int)LanguageID.English; // translation error; OT was not localized => same as English
|
||
return lang;
|
||
}
|
||
private static int DetectTradeLanguageG4MeisterMagikarp(PKM pkm, int lang)
|
||
{
|
||
if (lang == (int)LanguageID.English)
|
||
return (int)LanguageID.German;
|
||
|
||
// All have German, regardless of origin version.
|
||
// Detect which language they originated from... roughly.
|
||
var table = Encounters4.TradeDPPt;
|
||
for (int i = 0; i < table.Length; i++)
|
||
{
|
||
if (table[i].Length == 0)
|
||
continue;
|
||
// Nick @ 3, OT @ 7
|
||
if (table[i][7] != pkm.OT_Name)
|
||
continue;
|
||
lang = i;
|
||
break;
|
||
}
|
||
if (lang == 2) // possible collision with FR/ES/DE. Check nickname
|
||
return pkm.Nickname == table[3][3] ? (int)LanguageID.French : (int)LanguageID.Spanish; // Spanish is same as English
|
||
|
||
return lang;
|
||
}
|
||
private static int DetectTradeLanguageG4SurgePikachu(PKM pkm, int lang)
|
||
{
|
||
if (lang == (int)LanguageID.French)
|
||
return (int)LanguageID.English;
|
||
|
||
// All have English, regardless of origin version.
|
||
// Detect which language they originated from... roughly.
|
||
var table = Encounters4.TradeHGSS;
|
||
for (int i = 0; i < table.Length; i++)
|
||
{
|
||
if (table[i].Length == 0)
|
||
continue;
|
||
// Nick @ 6, OT @ 18
|
||
if (table[i][18] != pkm.OT_Name)
|
||
continue;
|
||
lang = i;
|
||
break;
|
||
}
|
||
if (lang == 2) // possible collision with ES/IT. Check nickname
|
||
return pkm.Nickname == table[4][6] ? (int)LanguageID.Italian : (int)LanguageID.Spanish;
|
||
|
||
return lang;
|
||
}
|
||
private void VerifyTrade5(LegalityAnalysis data)
|
||
{
|
||
var pkm = data.pkm;
|
||
var EncounterMatch = data.EncounterMatch;
|
||
// Trades for JPN games have language ID of 0, not 1.
|
||
if (pkm.BW)
|
||
{
|
||
int lang = pkm.Language;
|
||
if (pkm.Format == 5 && lang == (int)LanguageID.Japanese)
|
||
data.AddLine(GetInvalid(string.Format(V5, 0, (int)LanguageID.Japanese), CheckIdentifier.Language));
|
||
|
||
lang = Math.Max(lang, 1);
|
||
VerifyTradeTable(data, Encounters5.TradeBW, Encounters5.TradeGift_BW, lang);
|
||
}
|
||
else // B2W2
|
||
{
|
||
if (Encounters5.TradeGift_B2W2_YancyCurtis.Contains(EncounterMatch))
|
||
VerifyTradeOTOnly(data, pkm.OT_Gender == 0 ? Encounters5.TradeOT_B2W2_M : Encounters5.TradeOT_B2W2_F);
|
||
else
|
||
VerifyTradeTable(data, Encounters5.TradeB2W2, Encounters5.TradeGift_B2W2);
|
||
}
|
||
}
|
||
private void VerifyTrade6(LegalityAnalysis data)
|
||
{
|
||
var pkm = data.pkm;
|
||
if (pkm.XY)
|
||
VerifyTradeTable(data, Encounters6.TradeXY, Encounters6.TradeGift_XY, pkm.Language);
|
||
else if (pkm.AO)
|
||
VerifyTradeTable(data, Encounters6.TradeAO, Encounters6.TradeGift_AO, pkm.Language);
|
||
}
|
||
private void VerifyTrade7(LegalityAnalysis data)
|
||
{
|
||
var pkm = data.pkm;
|
||
if (pkm.SM)
|
||
VerifyTradeTable(data, Encounters7.TradeSM, Encounters7.TradeGift_SM, pkm.Language);
|
||
else if (pkm.USUM)
|
||
VerifyTradeTable(data, Encounters7.TradeUSUM, Encounters7.TradeGift_USUM, pkm.Language);
|
||
}
|
||
private void VerifyTrade4Ranch(LegalityAnalysis data) => VerifyTradeOTOnly(data, Encounters4.RanchOTNames);
|
||
|
||
private void VerifyTradeTable(LegalityAnalysis data, string[][] ots, EncounterTrade[] table) => VerifyTradeTable(data, ots, table, data.pkm.Language);
|
||
private void VerifyTradeTable(LegalityAnalysis data, string[][] ots, EncounterTrade[] table, int language)
|
||
{
|
||
var validOT = language >= ots.Length ? ots[0] : ots[language];
|
||
var index = Array.IndexOf(table, data.EncounterMatch);
|
||
VerifyTradeOTNick(data, validOT, index);
|
||
}
|
||
private void VerifyTradeOTOnly(LegalityAnalysis data, string[] validOT)
|
||
{
|
||
var pkm = data.pkm;
|
||
if (pkm.IsNicknamed)
|
||
data.AddLine(GetInvalid(V9, CheckIdentifier.Nickname));
|
||
int lang = pkm.Language;
|
||
if (validOT.Length <= lang)
|
||
data.AddLine(GetInvalid(V8, CheckIdentifier.Trainer));
|
||
else if (validOT[lang] != pkm.OT_Name)
|
||
data.AddLine(GetInvalid(V10, CheckIdentifier.Trainer));
|
||
else
|
||
data.AddLine(GetValid(V11, CheckIdentifier.Nickname));
|
||
}
|
||
private void VerifyTradeOTNick(LegalityAnalysis data, string[] validOT, int index)
|
||
{
|
||
if (validOT.Length == 0)
|
||
{
|
||
data.AddLine(Get(V7, Severity.Indeterminate, CheckIdentifier.Trainer));
|
||
return;
|
||
}
|
||
if (index == -1 || validOT.Length < index * 2)
|
||
{
|
||
data.AddLine(Get(V8, Severity.Indeterminate, CheckIdentifier.Trainer));
|
||
return;
|
||
}
|
||
|
||
string nick = validOT[index];
|
||
string OT = validOT[validOT.Length / 2 + index];
|
||
|
||
var pkm = data.pkm;
|
||
var EncounterMatch = data.EncounterMatch;
|
||
if (nick != pkm.Nickname
|
||
&& !(nick == "Quacklin’" && pkm.Nickname == "Quacklin'") // apostrophe farfetch'd edge case
|
||
&& ((EncounterTrade)EncounterMatch).IsNicknamed) // trades that are not nicknamed (but are present in a table with others being named)
|
||
data.AddLine(GetInvalid(V9, CheckIdentifier.Nickname));
|
||
else
|
||
data.AddLine(GetValid(V11, CheckIdentifier.Nickname));
|
||
|
||
if (OT != pkm.OT_Name)
|
||
data.AddLine(GetInvalid(V10, CheckIdentifier.Trainer));
|
||
}
|
||
}
|
||
}
|