From 9124a3c62b7dc1aa4671c8e272954dc5c367a805 Mon Sep 17 00:00:00 2001 From: Kurt Date: Mon, 1 Feb 2021 18:35:37 -0800 Subject: [PATCH] Improve handling of multi-nicknamed mystery gifts --- .../Legality/Verifiers/NicknameVerifier.cs | 38 ++++++++++++ PKHeX.Core/MysteryGifts/WB7.cs | 61 ++++++++++++++++--- PKHeX.Core/MysteryGifts/WC8.cs | 57 ++++++++++++++--- .../PKM/Shared/ILangNicknamedTemplate.cs | 11 ++++ 4 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 PKHeX.Core/PKM/Shared/ILangNicknamedTemplate.cs diff --git a/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs b/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs index a77a59ef3..62d17c0ce 100644 --- a/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs @@ -30,6 +30,12 @@ public override void Verify(LegalityAnalysis data) } var enc = data.EncounterOriginal; + if (enc is ILangNicknamedTemplate n) + { + VerifyFixedNicknameEncounter(data, n, enc, pkm, nickname); + return; + } + if (pkm.Format <= 7 && pkm.IsNicknamed) // can nickname afterwards { if (pkm.VC) @@ -65,6 +71,38 @@ public override void Verify(LegalityAnalysis data) } } + private void VerifyFixedNicknameEncounter(LegalityAnalysis data, ILangNicknamedTemplate n, IEncounterTemplate enc, PKM pkm, string nickname) + { + var nick = n.GetNickname(pkm.Language); + + if (string.IsNullOrWhiteSpace(nick)) + { + if (pkm.IsNicknamed) + data.AddLine(Get(LEncGiftNicknamed, Severity.Invalid)); + return; + } + + if (!pkm.IsNicknamed) + { + // Check if it had a nickname at all + var orig = SpeciesName.GetSpeciesNameGeneration(enc.Species, pkm.Language, enc.Generation); + if (orig == nick) + { + // Didn't have a nickname. Ensure that the language matches the current nickname string. + if (!SpeciesName.IsNicknamed(pkm.Species, nickname, pkm.Language, pkm.Format)) + return; + } + + // Should have a nickname present. + data.AddLine(GetInvalid(LNickMatchLanguageFail)); + return; + } + + // Encounter has a nickname, and PKM should have it. + var severity = nick != nickname || !pkm.IsNicknamed ? Severity.Invalid : Severity.Valid; + data.AddLine(Get(LEncGiftNicknamed, severity)); + } + private bool VerifyUnNicknamedEncounter(LegalityAnalysis data, PKM pkm, string nickname) { if (pkm.IsNicknamed) diff --git a/PKHeX.Core/MysteryGifts/WB7.cs b/PKHeX.Core/MysteryGifts/WB7.cs index 7effcdae6..b15fd5da8 100644 --- a/PKHeX.Core/MysteryGifts/WB7.cs +++ b/PKHeX.Core/MysteryGifts/WB7.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core /// /// Generation 7 Mystery Gift Template File /// - public sealed class WB7 : DataMysteryGift, ILangNick, IAwakened, INature + public sealed class WB7 : DataMysteryGift, ILangNick, IAwakened, INature, ILangNicknamedTemplate { public const int Size = 0x108; public const int SizeFull = 0x310; @@ -22,7 +22,7 @@ public sealed class WB7 : DataMysteryGift, ILangNick, IAwakened, INature public bool CanBeReceivedByVersion(int v) { - if (v < (int)GameVersion.GP || v > (int)GameVersion.GE) + if (v is not ((int)GameVersion.GP or (int)GameVersion.GE)) return false; if (RestrictVersion == 0) return true; // no data @@ -274,6 +274,14 @@ public override IReadOnlyList Relearn public bool IsNicknamed => false; public int Language => 2; + public bool IsForcedNickname() => false; + + public int GetLanguage(int redeemLanguage) + { + var languageOffset = GetLanguageIndex(redeemLanguage); + return Data[0x1D8 + languageOffset]; + } + public string GetNickname(int language) => StringConverter.GetString7(Data, GetNicknameOffset(language), 0x1A); public void SetNickname(int language, string value) => StringConverter.SetString7b(value, 12, 2, 13).CopyTo(Data, GetNicknameOffset(language)); @@ -302,7 +310,11 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) int currentLevel = Level > 0 ? Level : rnd.Next(1, 101); int metLevel = MetLevel > 0 ? MetLevel : currentLevel; var pi = PersonalTable.GG.GetFormEntry(Species, Form); - var OT = GetOT(sav.Language); + + var redeemLanguage = sav.Language; + var language = GetLanguage(redeemLanguage); + var OT = GetOT(redeemLanguage); + bool isRedeemHT = OT.Length != 0; var pk = new PB7 { @@ -314,7 +326,7 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) Form = Form, EncryptionConstant = EncryptionConstant != 0 ? EncryptionConstant : Util.Rand32(), Version = OriginGame != 0 ? OriginGame : sav.Game, - Language = sav.Language, + Language = language, Ball = Ball, Move1 = Move1, Move2 = Move2, @@ -333,17 +345,22 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) AV_SPA = AV_SPA, AV_SPD = AV_SPD, - OT_Name = OT.Length > 0 ? OT : sav.OT, + OT_Name = isRedeemHT ? OT : sav.OT, OT_Gender = OTGender != 3 ? OTGender % 2 : sav.Gender, - HT_Name = OT_Name.Length > 0 ? sav.OT : string.Empty, - HT_Gender = OT_Name.Length > 0 ? sav.Gender : 0, - CurrentHandler = OT_Name.Length > 0 ? 1 : 0, + CurrentHandler = isRedeemHT ? 1 : 0, EXP = Experience.GetEXP(currentLevel, pi.EXPGrowth), OT_Friendship = pi.BaseFriendship, FatefulEncounter = true, }; + + if (isRedeemHT) + { + pk.HT_Name = sav.OT; + pk.HT_Gender = sav.Gender; + } + pk.SetMaximumPPCurrent(); if ((sav.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version)) @@ -360,8 +377,8 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) } pk.MetDate = Date ?? DateTime.Now; - pk.IsNicknamed = GetIsNicknamed(pk.Language); - pk.Nickname = pk.IsNicknamed ? GetNickname(pk.Language) : SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation); + pk.IsNicknamed = GetIsNicknamed(redeemLanguage); + pk.Nickname = pk.IsNicknamed ? GetNickname(redeemLanguage) : SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation); SetPINGA(pk, criteria); @@ -445,6 +462,27 @@ private void SetIVs(PKM pk) pk.IVs = finalIVs; } + public bool CanHaveLanguage(int language) + { + if (language is < (int) LanguageID.Japanese or > (int) LanguageID.ChineseT) + return false; + + if (CanBeAnyLanguage()) + return true; + + return Array.IndexOf(Data, language, 0x1D8, 9) >= 0; + } + + public bool CanBeAnyLanguage() + { + for (int i = 0; i < 9; i++) + { + if (Data[0x1D8 + i] != 0) + return false; + } + return true; + } + public override bool IsMatchExact(PKM pkm, DexLevel evo) { if (pkm.Egg_Location == 0) // Not Egg @@ -459,6 +497,9 @@ public override bool IsMatchExact(PKM pkm, DexLevel evo) if (!string.IsNullOrEmpty(OT) && OT != pkm.OT_Name) return false; if (OriginGame != 0 && OriginGame != pkm.Version) return false; if (EncryptionConstant != 0 && EncryptionConstant != pkm.EncryptionConstant) return false; + + if (!CanBeAnyLanguage() && !CanHaveLanguage(pkm.Language)) + return false; } if (Form != evo.Form && !FormInfo.IsFormChangeable(Species, Form, pkm.Form, pkm.Format)) diff --git a/PKHeX.Core/MysteryGifts/WC8.cs b/PKHeX.Core/MysteryGifts/WC8.cs index 5ac531c3d..711eb6529 100644 --- a/PKHeX.Core/MysteryGifts/WC8.cs +++ b/PKHeX.Core/MysteryGifts/WC8.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core /// /// Generation 8 Mystery Gift Template File /// - public sealed class WC8 : DataMysteryGift, ILangNick, INature, IGigantamax, IDynamaxLevel, IRibbonIndex, IMemoryOT, + public sealed class WC8 : DataMysteryGift, ILangNick, INature, IGigantamax, IDynamaxLevel, IRibbonIndex, IMemoryOT, ILangNicknamedTemplate, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8 { public const int Size = 0x2D0; @@ -259,7 +259,38 @@ public int[] EVs public bool GetIsNicknamed(int language) => BitConverter.ToUInt16(Data, GetNicknameOffset(language)) != 0; - public int GetNicknameLanguage(int language) => Data[GetNicknameOffset(language) + 0x1A]; + public bool CanBeAnyLanguage() + { + for (int i = 0; i < 9; i++) + { + var ofs = GetLanguageOffset(i); + var lang = BitConverter.ToInt16(Data, ofs); + if (lang != 0) + return false; + } + return true; + } + + public bool CanHaveLanguage(int language) + { + if (language is < (int)LanguageID.Japanese or > (int)LanguageID.ChineseT) + return false; + + if (CanBeAnyLanguage()) + return true; + + for (int i = 0; i < 9; i++) + { + var ofs = GetLanguageOffset(i); + var lang = BitConverter.ToInt16(Data, ofs); + if (lang == language) + return true; + } + return false; + } + + public int GetLanguage(int redeemLanguage) => Data[GetLanguageOffset(GetLanguageIndex(redeemLanguage))]; + private static int GetLanguageOffset(int index) => 0x12C + (index * 0x1C) + 0x1A; public bool GetHasOT(int language) => BitConverter.ToUInt16(Data, GetOTOffset(language)) != 0; @@ -330,7 +361,9 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) int currentLevel = Level > 0 ? Level : Util.Rand.Next(1, 101); int metLevel = MetLevel > 0 ? MetLevel : currentLevel; var pi = PersonalTable.SWSH.GetFormEntry(Species, Form); - var OT = GetOT(sav.Language); + var language = sav.Language; + var OT = GetOT(language); + bool hasOT = GetHasOT(language); var pk = new PK8 { @@ -359,10 +392,10 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) OT_Name = OT.Length > 0 ? OT : sav.OT, OT_Gender = OTGender < 2 ? OTGender : sav.Gender, - HT_Name = GetHasOT(Language) ? sav.OT : string.Empty, - HT_Gender = GetHasOT(Language) ? sav.Gender : 0, - HT_Language = GetHasOT(Language) ? sav.Language : 0, - CurrentHandler = GetHasOT(Language) ? 1 : 0, + HT_Name = hasOT ? sav.OT : string.Empty, + HT_Gender = hasOT ? sav.Gender : 0, + HT_Language = hasOT ? language : 0, + CurrentHandler = hasOT ? 1 : 0, OT_Friendship = pi.BaseFriendship, OT_Intensity = OT_Intensity, @@ -410,10 +443,10 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) pk.MetDate = DateTime.Now; - var nickname_language = GetNicknameLanguage(sav.Language); + var nickname_language = GetLanguage(language); pk.Language = nickname_language != 0 ? nickname_language : sav.Language; - pk.IsNicknamed = GetIsNicknamed(pk.Language); - pk.Nickname = pk.IsNicknamed ? Nickname : SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation); + pk.IsNicknamed = GetIsNicknamed(language); + pk.Nickname = pk.IsNicknamed ? GetNickname(language) : SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation); for (var i = 0; i < RibbonBytesCount; i++) { @@ -524,6 +557,10 @@ public override bool IsMatchExact(PKM pkm, DexLevel evo) if (TID != pkm.TID) return false; if (OTGender != pkm.OT_Gender) return false; } + + if (!CanBeAnyLanguage() && !CanHaveLanguage(pkm.Language)) + return false; + var OT = GetOT(pkm.Language); // May not be guaranteed to work. if (!string.IsNullOrEmpty(OT) && OT != pkm.OT_Name) return false; if (OriginGame != 0 && OriginGame != pkm.Version) return false; diff --git a/PKHeX.Core/PKM/Shared/ILangNicknamedTemplate.cs b/PKHeX.Core/PKM/Shared/ILangNicknamedTemplate.cs new file mode 100644 index 000000000..af60fb68a --- /dev/null +++ b/PKHeX.Core/PKM/Shared/ILangNicknamedTemplate.cs @@ -0,0 +1,11 @@ +namespace PKHeX.Core +{ + internal interface ILangNicknamedTemplate + { + string GetNickname(int language); + bool GetIsNicknamed(int language); + + bool CanBeAnyLanguage(); + bool CanHaveLanguage(int language); + } +}