From 2d75e93ef03ebebbb18f7fb3ddb4f16d62b1579a Mon Sep 17 00:00:00 2001 From: Kurt Date: Thu, 27 Dec 2018 01:00:08 -0800 Subject: [PATCH] Move encounter matching logic to iencounterable generator logic is now clean --- .../Legality/Encounters/EncounterStatic.cs | 127 +++++ .../Legality/Encounters/EncounterTrade.cs | 127 +++++ .../Generator/EncounterMatchRating.cs | 9 + .../Generator/EncounterStaticGenerator.cs | 121 +---- .../Generator/EncounterTradeGenerator.cs | 135 +---- .../Generator/MysteryGiftGenerator.cs | 513 ++---------------- PKHeX.Core/MysteryGifts/MysteryGift.cs | 15 +- PKHeX.Core/MysteryGifts/PCD.cs | 55 ++ PKHeX.Core/MysteryGifts/PGF.cs | 50 ++ PKHeX.Core/MysteryGifts/PGT.cs | 4 + PKHeX.Core/MysteryGifts/WB7.cs | 59 ++ PKHeX.Core/MysteryGifts/WC3.cs | 80 ++- PKHeX.Core/MysteryGifts/WC6.cs | 65 +++ PKHeX.Core/MysteryGifts/WC7.cs | 76 +++ 14 files changed, 703 insertions(+), 733 deletions(-) create mode 100644 PKHeX.Core/Legality/Encounters/Generator/EncounterMatchRating.cs diff --git a/PKHeX.Core/Legality/Encounters/EncounterStatic.cs b/PKHeX.Core/Legality/Encounters/EncounterStatic.cs index 779b45825..ab6cd4610 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterStatic.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterStatic.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; namespace PKHeX.Core { @@ -238,5 +239,131 @@ private PIDType GetPIDType() default: return PIDType.None; } } + + public bool IsMatch(PKM pkm, int lvl) + { + if (Nature != Nature.Random && pkm.Nature != (int)Nature) + return false; + if (pkm.WasEgg != EggEncounter && pkm.Egg_Location == 0 && pkm.Format > 3 && pkm.GenNumber > 3 && !pkm.IsEgg) + return false; + if (this is EncounterStaticPID p && p.PID != pkm.PID) + return false; + + if (pkm.Gen3 && EggLocation != 0) // Gen3 Egg + { + if (pkm.Format == 3 && pkm.IsEgg && EggLocation != pkm.Met_Location) + return false; + } + else if (pkm.VC || (pkm.GenNumber <= 2 && EggLocation != 0)) // Gen2 Egg + { + if (pkm.Format <= 2) + { + if (pkm.IsEgg) + { + if (pkm.Met_Location != 0 && pkm.Met_Level != 0) + return false; + } + else + { + switch (pkm.Met_Level) + { + case 0 when pkm.Met_Location != 0: + return false; + case 1 when pkm.Met_Location == 0: + return false; + default: + if (pkm.Met_Location == 0 && pkm.Met_Level != 0) + return false; + break; + } + } + if (pkm.Met_Level == 1) + lvl = 5; // met @ 1, hatch @ 5. + } + } + else if (EggLocation != pkm.Egg_Location) + { + if (pkm.IsEgg) // unhatched + { + if (EggLocation != pkm.Met_Location) + return false; + if (pkm.Egg_Location != 0) + return false; + } + else if (pkm.Gen4) + { + if (pkm.Egg_Location != 2002) // Link Trade + { + // check Pt/HGSS data + if (pkm.Format <= 4) + return false; // must match + if (EggLocation >= 3000 || EggLocation <= 2010) // non-Pt/HGSS egg gift + return false; + + // transferring 4->5 clears pt/hgss location value and keeps Faraway Place + if (pkm.Egg_Location != 3002) // Faraway Place + return false; + } + } + else + { + if (pkm.Egg_Location != 30002) // Link Trade + return false; + } + } + else if (EggLocation != 0 && pkm.Gen4) + { + // Check the inverse scenario for 4->5 eggs + if (EggLocation < 3000 && EggLocation > 2010) // Pt/HGSS egg gift + { + if (pkm.Format > 4) + return false; // locations match when it shouldn't + } + } + + if (pkm.HasOriginalMetLocation) + { + if (!EggEncounter && Location != 0 && Location != pkm.Met_Location) + return false; + if (Level != lvl) + { + if (!(pkm.Format == 3 && EggEncounter && lvl == 0)) + return false; + } + } + else if (Level > lvl) + { + return false; + } + + if (Gender != -1 && Gender != pkm.Gender) + return false; + if (Form != pkm.AltForm && !SkipFormCheck && !Legal.IsFormChangeable(pkm, Species)) + return false; + if (EggLocation == 60002 && Relearn.Length == 0 && pkm.RelearnMoves.Any(z => z != 0)) // gen7 eevee edge case + return false; + + if (IVs != null && (Generation > 2 || pkm.Format <= 2)) // 1,2->7 regenerates IVs, only check if original IVs still exist + { + for (int i = 0; i < 6; i++) + { + if (IVs[i] != -1 && IVs[i] != pkm.IVs[i]) + return false; + } + } + + if (pkm is IContestStats s && s.IsContestBelow(this)) + return false; + + // Defer to EC/PID check + // if (e.Shiny != null && e.Shiny != pkm.IsShiny) + // continue; + + // Defer ball check to later + // if (e.Gift && pkm.Ball != 4) // PokéBall + // continue; + + return true; + } } } diff --git a/PKHeX.Core/Legality/Encounters/EncounterTrade.cs b/PKHeX.Core/Legality/Encounters/EncounterTrade.cs index 328496ae1..3a6793361 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterTrade.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterTrade.cs @@ -200,5 +200,132 @@ private static void SetSMOTMemory(PKM pk) pk.OT_TextVar = 40; pk.OT_Feeling = 5; } + + public bool IsMatch(PKM pkm, int lvl) + { + if (IVs != null) + { + for (int i = 0; i < 6; i++) + { + if (IVs[i] != -1 && IVs[i] != pkm.GetIV(i)) + return false; + } + } + + if (this is EncounterTradePID p) + { + if (p.PID != pkm.EncryptionConstant) + return false; + if (Nature != Nature.Random && (int)Nature != pkm.Nature) // gen5 BW only + return false; + } + else + { + if (!Shiny.IsValid(pkm)) + return false; + if (Nature != Nature.Random && (int)Nature != pkm.Nature) + return false; + if (Gender != -1 && Gender != pkm.Gender) + return false; + } + if (TID != pkm.TID) + return false; + if (SID != pkm.SID) + return false; + + if (pkm.HasOriginalMetLocation) + { + var loc = Location > 0 ? Location : DefaultMetLocation[Generation - 1]; + if (loc != pkm.Met_Location) + return false; + + if (pkm.Format < 5) + { + if (Level > lvl) + return false; + } + else if (Level != lvl) + { + return false; + } + } + else if (Level > lvl) + { + return false; + } + + if (CurrentLevel != -1 && CurrentLevel > pkm.CurrentLevel) + return false; + + if (Form != pkm.AltForm && !Legal.IsFormChangeable(pkm, pkm.Species)) + return false; + if (OTGender != -1 && OTGender != pkm.OT_Gender) + return false; + if (EggLocation != pkm.Egg_Location) + return false; + // if (z.Ability == 4 ^ pkm.AbilityNumber == 4) // defer to Ability + // countinue; + if (!Version.Contains((GameVersion)pkm.Version)) + return false; + + if (pkm is IContestStats s && s.IsContestBelow(this)) + return false; + + return true; + } + + public bool IsMatchVC1(PKM pkm) + { + if (Level > pkm.CurrentLevel) // minimum required level + return false; + if (pkm.Format != 1 || !pkm.Gen1_NotTradeback) + return true; + + // Even if the in game trade uses the tables with source pokemon allowing generation 2 games, the traded pokemon could be a non-tradeback pokemon + var rate = (pkm as PK1)?.Catch_Rate; + if (this is EncounterTradeCatchRate r) + { + if (rate != r.Catch_Rate) + return false; + } + else + { + if (Version == GameVersion.YW && rate != PersonalTable.Y[Species].CatchRate) + return false; + if (Version != GameVersion.YW && rate != PersonalTable.RB[Species].CatchRate) + return false; + } + return true; + } + + public bool IsMatchVC2(PKM pkm) + { + if (Level > pkm.CurrentLevel) // minimum required level + return false; + if (TID != pkm.TID) + return false; + if (Gender >= 0 && Gender != pkm.Gender && pkm.Format <= 2) + return false; + if (IVs?.SequenceEqual(pkm.IVs) == false && pkm.Format <= 2) + return false; + if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126) + return false; + + return IsValidTradeOT12(pkm); + } + + private bool IsValidTradeOT12(PKM pkm) + { + var OT = pkm.OT_Name; + if (pkm.Japanese) + return TrainerNames[(int)LanguageID.Japanese] == OT; + if (pkm.Korean) + return TrainerNames[(int)LanguageID.Korean] == OT; + + const int start = (int)LanguageID.English; + const int end = (int)LanguageID.Italian; + var index = Array.FindIndex(TrainerNames, start, end - start, w => w == OT); + return index >= 0; + } } } diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterMatchRating.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterMatchRating.cs new file mode 100644 index 000000000..7eca4071f --- /dev/null +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterMatchRating.cs @@ -0,0 +1,9 @@ +namespace PKHeX.Core +{ + public enum EncounterMatchRating + { + None, + Match, + Deferred, + } +} \ No newline at end of file diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterStaticGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterStaticGenerator.cs index 31bd58b00..5f8b1b626 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterStaticGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterStaticGenerator.cs @@ -85,132 +85,15 @@ private static bool GetIsMatchDeferred(PKM pkm, EncounterStatic e) private static bool GetIsMatchStatic(PKM pkm, EncounterStatic e, int lvl) { - if (e.Nature != Nature.Random && pkm.Nature != (int)e.Nature) + if (!e.IsMatch(pkm, lvl)) return false; - if (pkm.WasEgg != e.EggEncounter && pkm.Egg_Location == 0 && pkm.Format > 3 && pkm.GenNumber > 3 && !pkm.IsEgg) - return false; - if (e is EncounterStaticPID p && p.PID != pkm.PID) - return false; - - if (pkm.Gen3 && e.EggLocation != 0) // Gen3 Egg - { - if (pkm.Format == 3 && pkm.IsEgg && e.EggLocation != pkm.Met_Location) - return false; - } - else if (pkm.VC || (pkm.GenNumber <= 2 && e.EggLocation != 0)) // Gen2 Egg - { - if (pkm.Format <= 2) - { - if (pkm.IsEgg) - { - if (pkm.Met_Location != 0 && pkm.Met_Level != 0) - return false; - } - else - { - switch (pkm.Met_Level) - { - case 0 when pkm.Met_Location != 0: - return false; - case 1 when pkm.Met_Location == 0: - return false; - default: - if (pkm.Met_Location == 0 && pkm.Met_Level != 0) - return false; - break; - } - } - if (pkm.Met_Level == 1) - lvl = 5; // met @ 1, hatch @ 5. - } - } - else if (e.EggLocation != pkm.Egg_Location) - { - if (pkm.IsEgg) // unhatched - { - if (e.EggLocation != pkm.Met_Location) - return false; - if (pkm.Egg_Location != 0) - return false; - } - else if (pkm.Gen4) - { - if (pkm.Egg_Location != 2002) // Link Trade - { - // check Pt/HGSS data - if (pkm.Format <= 4) - return false; // must match - if (e.EggLocation >= 3000 || e.EggLocation <= 2010) // non-Pt/HGSS egg gift - return false; - - // transferring 4->5 clears pt/hgss location value and keeps Faraway Place - if (pkm.Egg_Location != 3002) // Faraway Place - return false; - } - } - else - { - if (pkm.Egg_Location != 30002) // Link Trade - return false; - } - } - else if (e.EggLocation != 0 && pkm.Gen4) - { - // Check the inverse scenario for 4->5 eggs - if (e.EggLocation < 3000 && e.EggLocation > 2010) // Pt/HGSS egg gift - { - if (pkm.Format > 4) - return false; // locations match when it shouldn't - } - } - - if (pkm.HasOriginalMetLocation) - { - if (!e.EggEncounter && e.Location != 0 && e.Location != pkm.Met_Location) - return false; - if (e.Level != lvl) - { - if (!(pkm.Format == 3 && e.EggEncounter && lvl == 0)) - return false; - } - } - else if (e.Level > lvl) - { - return false; - } - - if (e.Gender != -1 && e.Gender != pkm.Gender) - return false; - if (e.Form != pkm.AltForm && !e.SkipFormCheck && !IsFormChangeable(pkm, e.Species)) - return false; - if (e.EggLocation == 60002 && e.Relearn.Length == 0 && pkm.RelearnMoves.Any(z => z != 0)) // gen7 eevee edge case - return false; - - if (e.IVs != null && (e.Generation > 2 || pkm.Format <= 2)) // 1,2->7 regenerates IVs, only check if original IVs still exist - { - for (int i = 0; i < 6; i++) - { - if (e.IVs[i] != -1 && e.IVs[i] != pkm.IVs[i]) - return false; - } - } - - if (pkm is IContestStats s && s.IsContestBelow(e)) - return false; - - // Defer to EC/PID check - // if (e.Shiny != null && e.Shiny != pkm.IsShiny) - // continue; - - // Defer ball check to later - // if (e.Gift && pkm.Ball != 4) // PokéBall - // continue; if (pkm is PK1 pk1 && pk1.Gen1_NotTradeback && !IsValidCatchRatePK1(e, pk1)) return false; if (!ParseSettings.AllowGBCartEra && GameVersion.GBCartEraOnly.Contains(e.Version)) return false; + return true; } diff --git a/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs index 620ee1ec4..6f8042828 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/EncounterTradeGenerator.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using static PKHeX.Core.Legal; @@ -39,7 +38,7 @@ public static IEnumerable GetValidEncounterTrades(PKM pkm, IRead return Enumerable.Empty(); var poss = GetPossibleNonVC(pkm, p, gameSource); - return poss.Where(z => IsEncounterTradeValid(pkm, z, lvl)); + return poss.Where(z => z.IsMatch(pkm, lvl)); } private static IEnumerable GetPossibleNonVC(PKM pkm, IReadOnlyList p, GameVersion gameSource = GameVersion.Any) @@ -86,138 +85,12 @@ private static IEnumerable GetValidEncounterTradesVC(PKM pkm, IR { var poss = GetPossibleVC(p, gameSource); if (gameSource == GameVersion.RBY) - return poss.Where(z => GetIsValidTradeVC1(pkm, z)); - return poss.Where(z => GetIsValidTradeVC2(pkm, z)); - } - - private static bool GetIsValidTradeVC1(PKM pkm, EncounterTrade z) - { - if (z.Level > pkm.CurrentLevel) // minimum required level - return false; - if (pkm.Format != 1 || !pkm.Gen1_NotTradeback) - return true; - - // Even if the in game trade uses the tables with source pokemon allowing generation 2 games, the traded pokemon could be a non-tradeback pokemon - var rate = (pkm as PK1)?.Catch_Rate; - if (z is EncounterTradeCatchRate r) - { - if (rate != r.Catch_Rate) - return false; - } - else - { - if (z.Version == GameVersion.YW && rate != PersonalTable.Y[z.Species].CatchRate) - return false; - if (z.Version != GameVersion.YW && rate != PersonalTable.RB[z.Species].CatchRate) - return false; - } - return true; - } - - private static bool GetIsValidTradeVC2(PKM pkm, EncounterTrade z) - { - if (z.Level > pkm.CurrentLevel) // minimum required level - return false; - if (z.TID != pkm.TID) - return false; - if (z.Gender >= 0 && z.Gender != pkm.Gender && pkm.Format <= 2) - return false; - if (z.IVs?.SequenceEqual(pkm.IVs) == false && pkm.Format <= 2) - return false; - if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126) - return false; - - return IsValidTradeOT12(z, pkm); - } - - private static bool IsValidTradeOT12(EncounterTrade z, PKM pkm) - { - var OT = pkm.OT_Name; - if (pkm.Japanese) - return z.TrainerNames[(int) LanguageID.Japanese] == OT; - if (pkm.Korean) - return z.TrainerNames[(int)LanguageID.Korean] == OT; - - const int start = (int) LanguageID.English; - const int end = (int) LanguageID.Italian; - var index = Array.FindIndex(z.TrainerNames, start, end - start, w => w == OT); - return index >= 0; + return poss.Where(z => z.IsMatchVC1(pkm)); + return poss.Where(z => z.IsMatchVC2(pkm)); } private static bool GetIsFromGB(PKM pkm) => pkm.VC || pkm.Format <= 2; - private static bool IsEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl) - { - if (z.IVs != null) - { - for (int i = 0; i < 6; i++) - { - if (z.IVs[i] != -1 && z.IVs[i] != pkm.IVs[i]) - return false; - } - } - - if (z is EncounterTradePID p) - { - if (p.PID != pkm.EncryptionConstant) - return false; - if (z.Nature != Nature.Random && (int)z.Nature != pkm.Nature) // gen5 BW only - return false; - } - else - { - if (!z.Shiny.IsValid(pkm)) - return false; - if (z.Nature != Nature.Random && (int)z.Nature != pkm.Nature) - return false; - if (z.Gender != -1 && z.Gender != pkm.Gender) - return false; - } - if (z.TID != pkm.TID) - return false; - if (z.SID != pkm.SID) - return false; - if (pkm.HasOriginalMetLocation) - { - var loc = z.Location > 0 ? z.Location : EncounterTrade.DefaultMetLocation[z.Generation - 1]; - if (loc != pkm.Met_Location) - return false; - - if (pkm.Format < 5) - { - if (z.Level > lvl) - return false; - } - else if (z.Level != lvl) - { - return false; - } - } - else if (z.Level > lvl) - { - return false; - } - - if (z.CurrentLevel != -1 && z.CurrentLevel > pkm.CurrentLevel) - return false; - - if (z.Form != pkm.AltForm && !IsFormChangeable(pkm, pkm.Species)) - return false; - if (z.OTGender != -1 && z.OTGender != pkm.OT_Gender) - return false; - if (z.EggLocation != pkm.Egg_Location) - return false; - // if (z.Ability == 4 ^ pkm.AbilityNumber == 4) // defer to Ability - // countinue; - if (!z.Version.Contains((GameVersion)pkm.Version)) - return false; - - if (pkm is IContestStats s && s.IsContestBelow(z)) - return false; - - return true; - } - private static int IsNotTrade(PKM pkm) { int lang = pkm.Language; diff --git a/PKHeX.Core/Legality/Encounters/Generator/MysteryGiftGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/MysteryGiftGenerator.cs index 0b92194ce..cac4908b2 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/MysteryGiftGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/MysteryGiftGenerator.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using static PKHeX.Core.Legal; using static PKHeX.Core.EncounterEvent; namespace PKHeX.Core @@ -9,7 +8,7 @@ public static class MysteryGiftGenerator { public static IEnumerable GetPossible(PKM pkm) { - int maxSpecies = GetMaxSpeciesOrigin(pkm.Format); + int maxSpecies = Legal.GetMaxSpeciesOrigin(pkm.Format); var vs = EvolutionChain.GetValidPreEvolutions(pkm, maxSpecies); return GetPossible(pkm, vs); } @@ -22,18 +21,14 @@ public static IEnumerable GetPossible(PKM pkm, IReadOnlyList GetValidGifts(PKM pkm) { - switch (pkm.GenNumber) - { - case 3: return GetMatchingWC3(pkm, MGDB_G3); - case 4: return GetMatchingPCD(pkm, MGDB_G4); - case 5: return GetMatchingPGF(pkm, MGDB_G5); - case 6: return GetMatchingWC6(pkm, MGDB_G6); - case 7: - return pkm.GG - ? (IEnumerable) GetMatchingWB7(pkm, MGDB_G7GG) - : GetMatchingWC7(pkm, MGDB_G7); - default: return Enumerable.Empty(); - } + int gen = pkm.GenNumber; + if (pkm.IsEgg && pkm.Format != gen) // transferred + return Enumerable.Empty(); + + if (gen == 4) // check for manaphy gift + return GetMatchingPCD(pkm, MGDB_G4); + var table = GetTable(pkm.GenNumber, pkm); + return GetMatchingGifts(pkm, table); } private static IEnumerable GetTable(int generation, PKM pkm) @@ -45,487 +40,49 @@ private static IEnumerable GetTable(int generation, PKM pkm) case 5: return MGDB_G5; case 6: return MGDB_G6; case 7: - return pkm.GG ? (IEnumerable) MGDB_G7GG : MGDB_G7; + if (pkm.GG) + return MGDB_G7GG; + return MGDB_G7; default: return Enumerable.Empty(); } } - private static IEnumerable GetMatchingWC3(PKM pkm, IEnumerable DB) - { - var validWC3 = new List(); - var vs = EvolutionChain.GetValidPreEvolutions(pkm, MaxSpeciesID_3); - var enumerable = DB.Where(wc => vs.Any(dl => dl.Species == wc.Species)); - foreach (var wc in enumerable) - { - if (!GetIsMatchWC3(pkm, wc)) - continue; - - if (wc.Species == pkm.Species) // best match - yield return wc; - else - validWC3.Add(wc); - } - foreach (var z in validWC3) - yield return z; - } - private static IEnumerable GetMatchingPCD(PKM pkm, IEnumerable DB) { - if (pkm.IsEgg && pkm.Format != 4) // transferred - yield break; - if (IsRangerManaphy(pkm)) { - if (pkm.Language != (int)LanguageID.Korean) // never korean - yield return RangerManaphy; + yield return RangerManaphy; yield break; } - var deferred = new List(); - var vs = EvolutionChain.GetValidPreEvolutions(pkm); - var enumerable = DB.Where(wc => vs.Any(dl => dl.Species == wc.Species)); - foreach (var mg in enumerable) - { - var wc = mg.Gift.PK; - if (!GetIsMatchPCD(pkm, wc, vs)) - continue; + foreach (var g in GetMatchingGifts(pkm, DB)) + yield return g; + } - bool receivable = mg.CanBeReceivedBy(pkm.Version); - if (wc.Species == pkm.Species && receivable) // best match + private static IEnumerable GetMatchingGifts(PKM pkm, IEnumerable DB) + { + var vs = EvolutionChain.GetValidPreEvolutions(pkm); + return GetMatchingGifts(pkm, DB, vs); + } + + private static IEnumerable GetMatchingGifts(PKM pkm, IEnumerable DB, IReadOnlyList vs) + { + var deferred = new List(); + var gifts = DB.Where(wc => vs.Any(dl => dl.Species == wc.Species)); + foreach (var mg in gifts) + { + var result = mg.IsMatch(pkm, vs); + if (result == EncounterMatchRating.None) + continue; + if (result == EncounterMatchRating.Match) yield return mg; - else + else if (result == EncounterMatchRating.Deferred) deferred.Add(mg); } foreach (var z in deferred) yield return z; } - private static IEnumerable GetMatchingPGF(PKM pkm, IEnumerable DB) - { - var deferred = new List(); - var vs = EvolutionChain.GetValidPreEvolutions(pkm); - var enumerable = DB.Where(wc => vs.Any(dl => dl.Species == wc.Species)); - foreach (var wc in enumerable) - { - if (!GetIsMatchPGF(pkm, wc, vs)) - continue; - - if (wc.Species == pkm.Species) // best match - yield return wc; - else - deferred.Add(wc); - } - foreach (var z in deferred) - yield return z; - } - - private static IEnumerable GetMatchingWC6(PKM pkm, IEnumerable DB) - { - var deferred = new List(); - var vs = EvolutionChain.GetValidPreEvolutions(pkm); - var enumerable = DB.Where(wc => vs.Any(dl => dl.Species == wc.Species)); - foreach (var wc in enumerable) - { - if (!GetIsMatchWC6(pkm, wc, vs)) - continue; - if (GetIsDeferredWC6(pkm, wc)) - deferred.Add(wc); - else - yield return wc; - } - foreach (var z in deferred) - yield return z; - } - - private static IEnumerable GetMatchingWB7(PKM pkm, IEnumerable DB) - { - var vs = EvolutionChain.GetValidPreEvolutions(pkm); - var enumerable = DB.Where(wc => vs.Any(dl => dl.Species == wc.Species)); - foreach (var wc in enumerable) - { - if (!GetIsMatchWB7(pkm, wc, vs)) - continue; - - if (wc.PIDType == 0 && pkm.PID != wc.PID) - continue; - - yield return wc; - } - } - - private static IEnumerable GetMatchingWC7(PKM pkm, IEnumerable DB) - { - var deferred = new List(); - var vs = EvolutionChain.GetValidPreEvolutions(pkm); - var enumerable = DB.Where(wc => vs.Any(dl => dl.Species == wc.Species)); - foreach (var wc in enumerable) - { - if (!GetIsMatchWC7(pkm, wc, vs)) - continue; - - if ((pkm.SID << 16 | pkm.TID) == 0x79F57B49) // Greninja WC has variant PID and can arrive @ 36 or 37 - { - if (!pkm.IsShiny) - deferred.Add(wc); - continue; - } - if (wc.PIDType == 0 && pkm.PID != wc.PID) - continue; - - if (GetIsDeferredWC7(pkm, wc)) - deferred.Add(wc); - else - yield return wc; - } - foreach (var z in deferred) - yield return z; - } - - private static bool GetIsValidOTMattleHoOh(string wc, string ot, bool ck3) - { - if (ck3 && ot.Length == 10) - return wc == ot; - return ot.Length == 7 && wc.StartsWith(ot); - } - - private static bool GetIsMatchWC3(PKM pkm, WC3 wc) - { - // Gen3 Version MUST match. - if (wc.Version != 0 && !(wc.Version).Contains((GameVersion)pkm.Version)) - return false; - - bool hatchedEgg = wc.IsEgg && !pkm.IsEgg; - if (!hatchedEgg) - { - if (wc.SID != -1 && wc.SID != pkm.SID) return false; - if (wc.TID != -1 && wc.TID != pkm.TID) return false; - if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) return false; - var wcOT = wc.OT_Name; - if (wcOT != null) - { - if (wcOT.Length > 7) // Colosseum Mattle Ho-Oh - { - if (!GetIsValidOTMattleHoOh(wcOT, pkm.OT_Name, pkm is CK3)) - return false; - } - else if (wcOT != pkm.OT_Name) - { - return false; - } - } - } - - if (wc.Language != -1 && wc.Language != pkm.Language) return false; - if (wc.Ball != pkm.Ball) return false; - if (wc.Fateful != pkm.FatefulEncounter) - { - // XD Gifts only at level 20 get flagged after transfer - if (wc.Version == GameVersion.XD != pkm is XK3) - return false; - } - - if (pkm.IsNative) - { - if (hatchedEgg) - return true; // defer egg specific checks to later. - if (wc.Met_Level != pkm.Met_Level) - return false; - if (wc.Location != pkm.Met_Location) - return false; - } - else - { - if (pkm.IsEgg) - return false; - if (wc.Level > pkm.Met_Level) - return false; - } - return true; - } - - private static bool GetIsMatchPCD(PKM pkm, PK4 wc, IEnumerable vs) - { - if (!wc.IsEgg) - { - if (wc.TID != pkm.TID) return false; - if (wc.SID != pkm.SID) return false; - if (wc.OT_Name != pkm.OT_Name) return false; - if (wc.OT_Gender != pkm.OT_Gender) return false; - if (wc.Language != 0 && wc.Language != pkm.Language) return false; - - if (pkm.Format != 4) // transferred - { - // met location: deferred to general transfer check - if (wc.CurrentLevel > pkm.Met_Level) return false; - } - else - { - if (wc.Egg_Location + 3000 != pkm.Met_Location) return false; - if (wc.CurrentLevel != pkm.Met_Level) return false; - } - } - else // Egg - { - if (wc.Egg_Location + 3000 != pkm.Egg_Location && pkm.Egg_Location != 2002) // traded - return false; - if (wc.CurrentLevel != pkm.Met_Level) - return false; - if (pkm.IsEgg && !pkm.IsNative) - return false; - } - - if (wc.AltForm != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) - return false; - - if (wc.Ball != pkm.Ball) return false; - if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) return false; - if (wc.PID == 1 && pkm.IsShiny) return false; - if (wc.Gender != 3 && wc.Gender != pkm.Gender) return false; - - if (pkm is IContestStats s && s.IsContestBelow(wc)) - return false; - - return true; - } - - private static bool GetIsMatchPGF(PKM pkm, PGF wc, IEnumerable vs) - { - if (!wc.IsEgg) - { - if (wc.SID != pkm.SID) return false; - if (wc.TID != pkm.TID) return false; - if (wc.OT_Name != pkm.OT_Name) return false; - if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) return false; - if (wc.PID != 0 && pkm.PID != wc.PID) return false; - if (wc.PIDType == 0 && pkm.IsShiny) return false; - if (wc.PIDType == 2 && !pkm.IsShiny) return false; - if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) return false; - if (wc.Language != 0 && wc.Language != pkm.Language) return false; - - if (wc.EggLocation != pkm.Egg_Location) return false; - if (wc.MetLocation != pkm.Met_Location) return false; - } - else - { - if (wc.EggLocation != pkm.Egg_Location) // traded - { - if (pkm.Egg_Location != 30003) - return false; - } - else if (wc.PIDType == 0 && pkm.IsShiny) - { - return false; // can't be traded away for unshiny - } - - if (pkm.IsEgg && !pkm.IsNative) - return false; - } - - if (wc.Form != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) return false; - - if (wc.Level != pkm.Met_Level) return false; - if (wc.Ball != pkm.Ball) return false; - if (wc.Nature != -1 && wc.Nature != pkm.Nature) return false; - if (wc.Gender != 2 && wc.Gender != pkm.Gender) return false; - - if (pkm is IContestStats s && s.IsContestBelow(wc)) - return false; - - return true; - } - - private static bool GetIsMatchWC6(PKM pkm, WC6 wc, IEnumerable vs) - { - if (pkm.Egg_Location == 0) // Not Egg - { - if (wc.CardID != pkm.SID) return false; - if (wc.TID != pkm.TID) return false; - if (wc.OT_Name != pkm.OT_Name) return false; - if (wc.OTGender != pkm.OT_Gender) return false; - if (wc.PIDType == Shiny.FixedValue && pkm.PID != wc.PID) return false; - if (!wc.PIDType.IsValid(pkm)) return false; - if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) return false; - if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) return false; - if (wc.Language != 0 && wc.Language != pkm.Language) return false; - } - if (wc.Form != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) return false; - - if (wc.IsEgg) - { - if (wc.EggLocation != pkm.Egg_Location) // traded - { - if (pkm.Egg_Location != 30002) - return false; - } - else if (wc.PIDType == 0 && pkm.IsShiny) - { - return false; // can't be traded away for unshiny - } - - if (pkm.IsEgg && !pkm.IsNative) - return false; - } - else - { - if (wc.EggLocation != pkm.Egg_Location) return false; - if (wc.MetLocation != pkm.Met_Location) return false; - } - - if (wc.Level != pkm.Met_Level) return false; - if (wc.Ball != pkm.Ball) return false; - if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) return false; - if (wc.Nature != -1 && wc.Nature != pkm.Nature) return false; - if (wc.Gender != 3 && wc.Gender != pkm.Gender) return false; - - if (pkm is IContestStats s && s.IsContestBelow(wc)) - return false; - - return true; - } - - private static bool GetIsMatchWB7(PKM pkm, WB7 wc, IEnumerable vs) - { - if (pkm.Egg_Location == 0) // Not Egg - { - if (wc.OTGender != 3) - { - if (wc.SID != pkm.SID) return false; - if (wc.TID != pkm.TID) return false; - if (wc.OTGender != pkm.OT_Gender) return false; - } - var OT = wc.GetOT(pkm.Language); - if (!string.IsNullOrEmpty(OT) && OT != pkm.OT_Name) return false; - if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) return false; - if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) return false; - } - - if (wc.Form != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) - return false; - - if (wc.IsEgg) - { - if (wc.EggLocation != pkm.Egg_Location) // traded - { - if (pkm.Egg_Location != 30002) - return false; - } - else if (wc.PIDType == 0 && pkm.IsShiny) - { - return false; // can't be traded away for unshiny - } - - if (pkm.IsEgg && !pkm.IsNative) - return false; - } - else - { - if (!wc.PIDType.IsValid(pkm)) return false; - if (wc.EggLocation != pkm.Egg_Location) return false; - if (wc.MetLocation != pkm.Met_Location) return false; - } - - if (wc.MetLevel != pkm.Met_Level) return false; - if (wc.Ball != pkm.Ball) return false; - if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) return false; - if (wc.Nature != -1 && wc.Nature != pkm.Nature) return false; - if (wc.Gender != 3 && wc.Gender != pkm.Gender) return false; - - if (pkm is IAwakened s && s.IsAwakeningBelow(wc)) - return false; - - return true; - } - - private static bool GetIsMatchWC7(PKM pkm, WC7 wc, IEnumerable vs) - { - if (pkm.Egg_Location == 0) // Not Egg - { - if (wc.OTGender != 3) - { - if (wc.SID != pkm.SID) return false; - if (wc.TID != pkm.TID) return false; - if (wc.OTGender != pkm.OT_Gender) return false; - } - if (!string.IsNullOrEmpty(wc.OT_Name) && wc.OT_Name != pkm.OT_Name) return false; - if (wc.OriginGame != 0 && wc.OriginGame != pkm.Version) return false; - if (wc.EncryptionConstant != 0 && wc.EncryptionConstant != pkm.EncryptionConstant) return false; - if (wc.Language != 0 && wc.Language != pkm.Language) return false; - } - if (wc.Form != pkm.AltForm && vs.All(dl => !IsFormChangeable(pkm, dl.Species))) - { - if (wc.Species == 744 && wc.Form == 1 && pkm.Species == 745 && pkm.AltForm == 2) - { - // Rockruff gift edge case; has altform 1 then evolves to altform 2 - } - else - { - return false; - } - } - - if (wc.IsEgg) - { - if (wc.EggLocation != pkm.Egg_Location) // traded - { - if (pkm.Egg_Location != 30002) - return false; - } - else if (wc.PIDType == 0 && pkm.IsShiny) - { - return false; // can't be traded away for unshiny - } - - if (pkm.IsEgg && !pkm.IsNative) - return false; - } - else - { - if (!wc.PIDType.IsValid(pkm)) return false; - if (wc.EggLocation != pkm.Egg_Location) return false; - if (wc.MetLocation != pkm.Met_Location) return false; - } - - if (wc.MetLevel != pkm.Met_Level) return false; - if (wc.Ball != pkm.Ball) return false; - if (wc.OTGender < 3 && wc.OTGender != pkm.OT_Gender) return false; - if (wc.Nature != -1 && wc.Nature != pkm.Nature) return false; - if (wc.Gender != 3 && wc.Gender != pkm.Gender) return false; - - if (pkm is IContestStats s && s.IsContestBelow(wc)) - return false; - - switch (wc.CardID) - { - case 2046: // Ash Greninja - return pkm.SM; // not USUM - } - return true; - } - - private static bool GetIsDeferredWC6(PKM pkm, WC6 wc) - { - switch (wc.CardID) - { - case 0525 when wc.IV_HP == 0xFE: // Diancie was distributed with no IV enforcement & 3IVs - case 0504 when wc.RibbonClassic != ((IRibbonSetEvent4)pkm).RibbonClassic: // magmar with/without classic - return true; - } - if (wc.RestrictLanguage != 0 && wc.RestrictLanguage != pkm.Language) - return true; - if (!wc.CanBeReceivedByVersion(pkm.Version)) - return true; - return wc.Species != pkm.Species; - } - - private static bool GetIsDeferredWC7(PKM pkm, WC7 wc) - { - if (wc.RestrictLanguage != 0 && wc.RestrictLanguage != pkm.Language) - return true; - if (!wc.CanBeReceivedByVersion(pkm.Version)) - return true; - return wc.Species != pkm.Species; - } - // Utility private static readonly PGT RangerManaphy = new PGT {Data = {[0] = 7, [8] = 1}}; @@ -538,6 +95,10 @@ private static bool IsRangerManaphy(PKM pkm) return egg == linkegg || egg == ranger; if (egg != ranger) return false; + + if (pkm.Language == (int)LanguageID.Korean) // never korean + return false; + var met = pkm.Met_Location; return met == linkegg || met == 0; } diff --git a/PKHeX.Core/MysteryGifts/MysteryGift.cs b/PKHeX.Core/MysteryGifts/MysteryGift.cs index 53acc3efc..6893d746e 100644 --- a/PKHeX.Core/MysteryGifts/MysteryGift.cs +++ b/PKHeX.Core/MysteryGifts/MysteryGift.cs @@ -80,9 +80,22 @@ public static MysteryGift GetMysteryGift(byte[] data) public string Extension => GetType().Name.ToLower(); public string FileName => $"{CardHeader}.{Extension}"; public byte[] Data { get; set; } - public abstract PKM ConvertToPKM(ITrainerInfo SAV); public abstract int Format { get; } + public abstract PKM ConvertToPKM(ITrainerInfo SAV); + + protected abstract bool IsMatchExact(PKM pkm, IEnumerable vs); + protected abstract bool IsMatchDeferred(PKM pkm); + + public EncounterMatchRating IsMatch(PKM pkm, IEnumerable vs) + { + if (!IsMatchExact(pkm, vs)) + return EncounterMatchRating.None; + if (IsMatchDeferred(pkm)) + return EncounterMatchRating.Deferred; + return EncounterMatchRating.Match; + } + /// /// Creates a deep copy of the object data. /// diff --git a/PKHeX.Core/MysteryGifts/PCD.cs b/PKHeX.Core/MysteryGifts/PCD.cs index 86502e6cd..825fa3ee1 100644 --- a/PKHeX.Core/MysteryGifts/PCD.cs +++ b/PKHeX.Core/MysteryGifts/PCD.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; namespace PKHeX.Core { @@ -130,5 +132,58 @@ public override PKM ConvertToPKM(ITrainerInfo SAV) } public bool CanBeReceivedBy(int pkmVersion) => (CardCompatibility >> pkmVersion & 1) == 1; + + protected override bool IsMatchExact(PKM pkm, IEnumerable vs) + { + var wc = Gift.PK; + if (!wc.IsEgg) + { + if (wc.TID != pkm.TID) return false; + if (wc.SID != pkm.SID) return false; + if (wc.OT_Name != pkm.OT_Name) return false; + if (wc.OT_Gender != pkm.OT_Gender) return false; + if (wc.Language != 0 && wc.Language != pkm.Language) return false; + + if (pkm.Format != 4) // transferred + { + // met location: deferred to general transfer check + if (wc.CurrentLevel > pkm.Met_Level) return false; + } + else + { + if (wc.Egg_Location + 3000 != pkm.Met_Location) return false; + if (wc.CurrentLevel != pkm.Met_Level) return false; + } + } + else // Egg + { + if (wc.Egg_Location + 3000 != pkm.Egg_Location && pkm.Egg_Location != 2002) // traded + return false; + if (wc.CurrentLevel != pkm.Met_Level) + return false; + if (pkm.IsEgg && !pkm.IsNative) + return false; + } + + if (wc.AltForm != pkm.AltForm && vs.All(dl => !Legal.IsFormChangeable(pkm, dl.Species))) + return false; + + if (wc.Ball != pkm.Ball) return false; + if (wc.OT_Gender < 3 && wc.OT_Gender != pkm.OT_Gender) return false; + if (wc.PID == 1 && pkm.IsShiny) return false; + if (wc.Gender != 3 && wc.Gender != pkm.Gender) return false; + + if (pkm is IContestStats s && s.IsContestBelow(wc)) + return false; + + return true; + } + + protected override bool IsMatchDeferred(PKM pkm) + { + if (!CanBeReceivedBy(pkm.Version)) + return false; + return Species != pkm.Species; + } } } diff --git a/PKHeX.Core/MysteryGifts/PGF.cs b/PKHeX.Core/MysteryGifts/PGF.cs index 9546551d0..056b89f9b 100644 --- a/PKHeX.Core/MysteryGifts/PGF.cs +++ b/PKHeX.Core/MysteryGifts/PGF.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Text; namespace PKHeX.Core @@ -311,5 +313,53 @@ public override PKM ConvertToPKM(ITrainerInfo SAV) pk.RefreshChecksum(); return pk; } + + protected override bool IsMatchExact(PKM pkm, IEnumerable vs) + { + if (!IsEgg) + { + if (SID != pkm.SID) return false; + if (TID != pkm.TID) return false; + if (OT_Name != pkm.OT_Name) return false; + if (OTGender < 3 && OTGender != pkm.OT_Gender) return false; + if (PID != 0 && pkm.PID != PID) return false; + if (PIDType == 0 && pkm.IsShiny) return false; + if (PIDType == 2 && !pkm.IsShiny) return false; + if (OriginGame != 0 && OriginGame != pkm.Version) return false; + if (Language != 0 && Language != pkm.Language) return false; + + if (EggLocation != pkm.Egg_Location) return false; + if (MetLocation != pkm.Met_Location) return false; + } + else + { + if (EggLocation != pkm.Egg_Location) // traded + { + if (pkm.Egg_Location != 30003) + return false; + } + else if (PIDType == 0 && pkm.IsShiny) + { + return false; // can't be traded away for unshiny + } + + if (pkm.IsEgg && !pkm.IsNative) + return false; + } + + if (Form != pkm.AltForm && vs.All(dl => !Legal.IsFormChangeable(pkm, dl.Species))) return false; + + if (Level != pkm.Met_Level) return false; + if (Ball != pkm.Ball) return false; + if (Nature != -1 && Nature != pkm.Nature) return false; + if (Gender != 2 && Gender != pkm.Gender) return false; + + if (pkm is IContestStats s && s.IsContestBelow(this)) + return false; + + return true; + } + + protected override bool IsMatchDeferred(PKM pkm) => false; } } diff --git a/PKHeX.Core/MysteryGifts/PGT.cs b/PKHeX.Core/MysteryGifts/PGT.cs index d6acc31f1..00bc80297 100644 --- a/PKHeX.Core/MysteryGifts/PGT.cs +++ b/PKHeX.Core/MysteryGifts/PGT.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; namespace PKHeX.Core @@ -221,5 +222,8 @@ private static uint GeneratePID(uint seed, PK4 pk4) pk4.PID = RNG.ARNG.Next(pk4.PID); return seed; } + + protected override bool IsMatchExact(PKM pkm, IEnumerable vs) => false; + protected override bool IsMatchDeferred(PKM pkm) => false; } } diff --git a/PKHeX.Core/MysteryGifts/WB7.cs b/PKHeX.Core/MysteryGifts/WB7.cs index 96575708d..9993ede07 100644 --- a/PKHeX.Core/MysteryGifts/WB7.cs +++ b/PKHeX.Core/MysteryGifts/WB7.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; @@ -456,5 +457,63 @@ public override PKM ConvertToPKM(ITrainerInfo SAV) pk.RefreshChecksum(); return pk; } + + protected override bool IsMatchExact(PKM pkm, IEnumerable vs) + { + if (pkm.Egg_Location == 0) // Not Egg + { + if (OTGender != 3) + { + if (SID != pkm.SID) return false; + if (TID != pkm.TID) return false; + if (OTGender != pkm.OT_Gender) return false; + } + var OT = GetOT(pkm.Language); + 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 (Form != pkm.AltForm && vs.All(dl => !Legal.IsFormChangeable(pkm, dl.Species))) + return false; + + if (IsEgg) + { + if (EggLocation != pkm.Egg_Location) // traded + { + if (pkm.Egg_Location != 30002) + return false; + } + else if (PIDType == 0 && pkm.IsShiny) + { + return false; // can't be traded away for unshiny + } + + if (pkm.IsEgg && !pkm.IsNative) + return false; + } + else + { + if (!PIDType.IsValid(pkm)) return false; + if (EggLocation != pkm.Egg_Location) return false; + if (MetLocation != pkm.Met_Location) return false; + } + + if (MetLevel != pkm.Met_Level) return false; + if (Ball != pkm.Ball) return false; + if (OTGender < 3 && OTGender != pkm.OT_Gender) return false; + if (Nature != -1 && Nature != pkm.Nature) return false; + if (Gender != 3 && Gender != pkm.Gender) return false; + + if (pkm is IAwakened s && s.IsAwakeningBelow(this)) + return false; + + return true; + } + + protected override bool IsMatchDeferred(PKM pkm) + { + return pkm.Species == Species; + } } } diff --git a/PKHeX.Core/MysteryGifts/WC3.cs b/PKHeX.Core/MysteryGifts/WC3.cs index 13d7b58a0..42bc3d0d5 100644 --- a/PKHeX.Core/MysteryGifts/WC3.cs +++ b/PKHeX.Core/MysteryGifts/WC3.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace PKHeX.Core { @@ -39,6 +40,13 @@ public class WC3 : MysteryGift, IRibbonSetEvent3, IVersion public override int Ball { get; set; } = 4; public override bool IsShiny => Shiny == Shiny.Always; + public bool RibbonEarth { get; set; } + public bool RibbonNational { get; set; } + public bool RibbonCountry { get; set; } + public bool RibbonChampionBattle { get; set; } + public bool RibbonChampionRegional { get; set; } + public bool RibbonChampionNational { get; set; } + // Description public override string CardTitle { get; set; } = "Generation 3 Event"; public override string CardHeader => CardTitle; @@ -204,11 +212,71 @@ private static GameVersion GetRandomVersion(GameVersion version) } } - public bool RibbonEarth { get; set; } - public bool RibbonNational { get; set; } - public bool RibbonCountry { get; set; } - public bool RibbonChampionBattle { get; set; } - public bool RibbonChampionRegional { get; set; } - public bool RibbonChampionNational { get; set; } + protected override bool IsMatchExact(PKM pkm, IEnumerable vs) + { + // Gen3 Version MUST match. + if (Version != 0 && !(Version).Contains((GameVersion)pkm.Version)) + return false; + + bool hatchedEgg = IsEgg && !pkm.IsEgg; + if (!hatchedEgg) + { + if (SID != -1 && SID != pkm.SID) return false; + if (TID != -1 && TID != pkm.TID) return false; + if (OT_Gender < 3 && OT_Gender != pkm.OT_Gender) return false; + var wcOT = OT_Name; + if (wcOT != null) + { + if (wcOT.Length > 7) // Colosseum Mattle Ho-Oh + { + if (!GetIsValidOTMattleHoOh(wcOT, pkm.OT_Name, pkm is CK3)) + return false; + } + else if (wcOT != pkm.OT_Name) + { + return false; + } + } + } + + if (Language != -1 && Language != pkm.Language) return false; + if (Ball != pkm.Ball) return false; + if (Fateful != pkm.FatefulEncounter) + { + // XD Gifts only at level 20 get flagged after transfer + if (Version == GameVersion.XD != pkm is XK3) + return false; + } + + if (pkm.IsNative) + { + if (hatchedEgg) + return true; // defer egg specific checks to later. + if (Met_Level != pkm.Met_Level) + return false; + if (Location != pkm.Met_Location) + return false; + } + else + { + if (pkm.IsEgg) + return false; + if (Level > pkm.Met_Level) + return false; + } + return true; + } + + private static bool GetIsValidOTMattleHoOh(string wc, string ot, bool ck3) + { + if (ck3 && ot.Length == 10) + return wc == ot; + return ot.Length == 7 && wc.StartsWith(ot); + } + + protected override bool IsMatchDeferred(PKM pkm) + { + return Species != pkm.Species; + } } } diff --git a/PKHeX.Core/MysteryGifts/WC6.cs b/PKHeX.Core/MysteryGifts/WC6.cs index 353c56992..55f2ac8ae 100644 --- a/PKHeX.Core/MysteryGifts/WC6.cs +++ b/PKHeX.Core/MysteryGifts/WC6.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; @@ -451,5 +452,69 @@ public override PKM ConvertToPKM(ITrainerInfo SAV) pk.RefreshChecksum(); return pk; } + + protected override bool IsMatchExact(PKM pkm, IEnumerable vs) + { + if (pkm.Egg_Location == 0) // Not Egg + { + if (CardID != pkm.SID) return false; + if (TID != pkm.TID) return false; + if (OT_Name != pkm.OT_Name) return false; + if (OTGender != pkm.OT_Gender) return false; + if (PIDType == Shiny.FixedValue && pkm.PID != PID) return false; + if (!PIDType.IsValid(pkm)) return false; + if (OriginGame != 0 && OriginGame != pkm.Version) return false; + if (EncryptionConstant != 0 && EncryptionConstant != pkm.EncryptionConstant) return false; + if (Language != 0 && Language != pkm.Language) return false; + } + if (Form != pkm.AltForm && vs.All(dl => !Legal.IsFormChangeable(pkm, dl.Species))) return false; + + if (IsEgg) + { + if (EggLocation != pkm.Egg_Location) // traded + { + if (pkm.Egg_Location != 30002) + return false; + } + else if (PIDType == 0 && pkm.IsShiny) + { + return false; // can't be traded away for unshiny + } + + if (pkm.IsEgg && !pkm.IsNative) + return false; + } + else + { + if (EggLocation != pkm.Egg_Location) return false; + if (MetLocation != pkm.Met_Location) return false; + } + + if (Level != pkm.Met_Level) return false; + if (Ball != pkm.Ball) return false; + if (OTGender < 3 && OTGender != pkm.OT_Gender) return false; + if (Nature != -1 && Nature != pkm.Nature) return false; + if (Gender != 3 && Gender != pkm.Gender) return false; + + if (pkm is IContestStats s && s.IsContestBelow(this)) + return false; + + return true; + } + + protected override bool IsMatchDeferred(PKM pkm) + { + switch (CardID) + { + case 0525 when IV_HP == 0xFE: // Diancie was distributed with no IV enforcement & 3IVs + case 0504 when RibbonClassic != ((IRibbonSetEvent4)pkm).RibbonClassic: // magmar with/without classic + return true; + } + if (RestrictLanguage != 0 && RestrictLanguage != pkm.Language) + return true; + if (!CanBeReceivedByVersion(pkm.Version)) + return true; + return Species != pkm.Species; + } } } diff --git a/PKHeX.Core/MysteryGifts/WC7.cs b/PKHeX.Core/MysteryGifts/WC7.cs index df677efcb..68ddbb110 100644 --- a/PKHeX.Core/MysteryGifts/WC7.cs +++ b/PKHeX.Core/MysteryGifts/WC7.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; @@ -487,5 +488,80 @@ public bool IsAshGreninjaWC7(PKM pkm) { return CardID == 2046 && (pkm.SID << 16 | pkm.TID) == 0x79F57B49; } + + protected override bool IsMatchExact(PKM pkm, IEnumerable vs) + { + if (pkm.Egg_Location == 0) // Not Egg + { + if (OTGender != 3) + { + if (SID != pkm.SID) return false; + if (TID != pkm.TID) return false; + if (OTGender != pkm.OT_Gender) return false; + } + if (!string.IsNullOrEmpty(OT_Name) && OT_Name != pkm.OT_Name) return false; + if (OriginGame != 0 && OriginGame != pkm.Version) return false; + if (EncryptionConstant != 0 && EncryptionConstant != pkm.EncryptionConstant) return false; + if (Language != 0 && Language != pkm.Language) return false; + } + + if (Form != pkm.AltForm && vs.All(dl => !Legal.IsFormChangeable(pkm, dl.Species))) + { + if (Species == 744 && Form == 1 && pkm.Species == 745 && pkm.AltForm == 2) + { + // Rockruff gift edge case; has altform 1 then evolves to altform 2 + } + else + { + return false; + } + } + + if (IsEgg) + { + if (EggLocation != pkm.Egg_Location) // traded + { + if (pkm.Egg_Location != 30002) + return false; + } + else if (PIDType == 0 && pkm.IsShiny) + { + return false; // can't be traded away for unshiny + } + + if (pkm.IsEgg && !pkm.IsNative) + return false; + } + else + { + if (!PIDType.IsValid(pkm)) return false; + if (EggLocation != pkm.Egg_Location) return false; + if (MetLocation != pkm.Met_Location) return false; + } + + if (MetLevel != pkm.Met_Level) return false; + if (Ball != pkm.Ball) return false; + if (OTGender < 3 && OTGender != pkm.OT_Gender) return false; + if (Nature != -1 && Nature != pkm.Nature) return false; + if (Gender != 3 && Gender != pkm.Gender) return false; + + if (pkm is IContestStats s && s.IsContestBelow(this)) + return false; + + if (CardID == 2046) // Greninja WC has variant PID and can arrive @ 36 or 37 + return pkm.SM; // not USUM + if (PIDType == 0 && pkm.PID != PID) + return false; + return true; + } + + protected override bool IsMatchDeferred(PKM pkm) + { + if (RestrictLanguage != 0 && RestrictLanguage != pkm.Language) + return true; + if (!CanBeReceivedByVersion(pkm.Version)) + return true; + return Species != pkm.Species; + } } }