From 06f54bb3d0d4e591c822d39bdc35bbc7ab9b12aa Mon Sep 17 00:00:00 2001 From: wwwwwwzx Date: Fri, 14 Apr 2017 16:24:41 -0700 Subject: [PATCH] Gen3/5 Ability Check Fixes (#1056) * Fix gen3 abiltiy legality check Seperate Ability (AbilityBit i.e top bit of IV32) and AbilityNumber (PID) Fix in-game trade Electrode's ability * Fix function deal with "encountermatch is int" Keep encountermatch as null after UpdateEncounterInfo * Fix gen5-7 ability check Consider ability capsule gen5 personal table have HA, ability_count won't work Some simplification and avoid duplicate invalid message * Simplify ability check Use bool? to mark 3 states GameVersion CXD needs more info * Temp fix gen3 in-game trade ability check Lickitung have 2 regular ability at gen3, there is a AbilityBit and AbilityNumber mismatch Guess those ingame trade have static PID * Change abilitybit to bool and revert change revert change in UpdateEncounterChain. let egg encounter / null encounter choose vs.Last().Species as before * Update Chinese legality translation strings --- PKHeX.WinForms/MainWindow/MainPK3.cs | 3 +- PKHeX/Legality/Analysis.cs | 3 +- PKHeX/Legality/Checks.cs | 90 +++++++++---------- PKHeX/Legality/Core.cs | 4 +- PKHeX/Legality/Tables3.cs | 2 +- PKHeX/PKM/PK3.cs | 5 +- .../text/zh/LegalityCheckStrings_zh.txt | 28 +++--- 7 files changed, 66 insertions(+), 69 deletions(-) diff --git a/PKHeX.WinForms/MainWindow/MainPK3.cs b/PKHeX.WinForms/MainWindow/MainPK3.cs index 75bfb647c..de96df190 100644 --- a/PKHeX.WinForms/MainWindow/MainPK3.cs +++ b/PKHeX.WinForms/MainWindow/MainPK3.cs @@ -29,8 +29,7 @@ private void populateFieldsPK3() Label_OTGender.ForeColor = pk3.OT_Gender == 1 ? Color.Red : Color.Blue; TB_PID.Text = pk3.PID.ToString("X8"); CB_HeldItem.SelectedValue = pk3.HeldItem; - int abil = pk3.AbilityNumber >> 1; - CB_Ability.SelectedIndex = abil > CB_Ability.Items.Count ? 0 : abil; + CB_Ability.SelectedIndex = pk3.AbilityBit && CB_Ability.Items.Count > 1 ? 1 : 0; CB_Nature.SelectedValue = pk3.Nature; TB_TID.Text = pk3.TID.ToString("00000"); TB_SID.Text = pk3.SID.ToString("00000"); diff --git a/PKHeX/Legality/Analysis.cs b/PKHeX/Legality/Analysis.cs index a509cae27..c026b3201 100644 --- a/PKHeX/Legality/Analysis.cs +++ b/PKHeX/Legality/Analysis.cs @@ -201,9 +201,8 @@ private void updateEncounterInfo() { if (pkm.VC && pkm.Format == 7) EncounterMatch = Legal.getRBYStaticTransfer(pkm.Species); - EncounterMatch = EncounterMatch ?? pkm.Species; - EncounterType = (EncounterOriginalGB ?? EncounterMatch)?.GetType(); + EncounterType = (EncounterOriginalGB ?? EncounterMatch ?? pkm.Species)?.GetType(); if (EncounterType == typeof (MysteryGift)) EncounterType = EncounterType?.BaseType; } diff --git a/PKHeX/Legality/Checks.cs b/PKHeX/Legality/Checks.cs index 3ed277836..21f7e3095 100644 --- a/PKHeX/Legality/Checks.cs +++ b/PKHeX/Legality/Checks.cs @@ -1242,17 +1242,28 @@ private void verifyAbility() return; } - if (EncounterMatch != null && (!pkm.Gen3 || pkm.Format ==3)) + bool? AbilityUnchanged = true; + // 3 states flag: true for unchanged, false for changed, null for uncertain/allowing PID mismatch + // if true, check encounter ability + // if true or false, check PID/AbilityNumber + if (3 <= pkm.Format && pkm.Format <= 5 && abilities[0] != abilities[1]) // 3-5 and have 2 distinct ability now + AbilityUnchanged = verifyAbilityPreCapsule(abilities, abilval); + + if (EncounterMatch != null) { - // Gen 3 transfered to 4 could change ability, defer to verifyAbilityPreCapsule // Check Ability Mismatches int? EncounterAbility = (EncounterMatch as EncounterStatic)?.Ability ?? (EncounterMatch as EncounterTrade)?.Ability ?? (EncounterMatch as EncounterLink)?.Ability; - if (EncounterAbility != null && EncounterAbility != 0 && pkm.AbilityNumber != EncounterAbility) + if ((AbilityUnchanged ?? false) && EncounterAbility != null && EncounterAbility != 0 && pkm.AbilityNumber != EncounterAbility) { - AddLine(Severity.Invalid, V223, CheckIdentifier.Ability); + if (pkm.Format >= 6 && abilities[0] != abilities[1] && pkm.AbilityNumber < 4) //Ability Capsule + AddLine(Severity.Valid, V109, CheckIdentifier.Ability); + else if (pkm.Gen3 && EncounterMatch is EncounterTrade && EncounterAbility == 1 << abilval) // Edge case (Static PID?) + AddLine(Severity.Valid, V115, CheckIdentifier.Ability); + else + AddLine(Severity.Invalid, V223, CheckIdentifier.Ability); return; } @@ -1263,58 +1274,47 @@ private void verifyAbility() case 7: verifyAbility7(abilities); break; } } - var AbilityMatchPID = true; - if (3 <= pkm.Format && pkm.Format <= 5) // 3-5 - AbilityMatchPID = verifyAbilityPreCapsule(abilities, abilval); if (3 <= pkm.GenNumber && pkm.GenNumber <= 4 && pkm.AbilityNumber == 4) AddLine(Severity.Invalid, V112, CheckIdentifier.Ability); - else if (AbilityMatchPID && abilities[pkm.AbilityNumber >> 1] != pkm.Ability) - AddLine(Severity.Invalid, V114, CheckIdentifier.Ability); + else if (AbilityUnchanged != null && abilities[pkm.AbilityNumber >> 1] != pkm.Ability) + AddLine(Severity.Invalid, pkm.Format < 6 ? V113 : V114, CheckIdentifier.Ability); else AddLine(Severity.Valid, V115, CheckIdentifier.Ability); } - private bool verifyAbilityPreCapsule(int[] abilities, int abilval) + private bool? verifyAbilityPreCapsule(int[] abilities, int abilval) { - var abilities_count = abilities.Distinct().Count(); - var AbilityMatchPID = abilities_count == 2; - if (pkm.Format >= 4 && pkm.InhabitedGeneration(3) && pkm.Species <= Legal.MaxSpeciesID_3) - { - // gen3Species will be zero for pokemon with illegal gen 3 encounters, like Infernape with gen 3 "origin" - // Do not check for gen 3 pokemon that has evolved into gen 4 species, - // those have evolved in generation 4 or 5 and ability must match PID, not need to check gen 3 data - var gen3Species = EvoChainsAllGens[3].FirstOrDefault()?.Species ?? 0; - if (gen3Species > 0) - AbilityMatchPID = verifyAbilityGen3Transfer(abilities, abilval, gen3Species, abilities_count); - } - - // Gen 4,5 pokemon or gen 3 pokemon evolved in gen 4,5 games, ability must match PID - if (AbilityMatchPID && pkm.AbilityNumber != 1 << abilval) - AddLine(Severity.Invalid, V113, CheckIdentifier.Ability); - return AbilityMatchPID; - } - private bool verifyAbilityGen3Transfer(int[] abilities, int abilval, int Species_g3, int abilities_count) - { - if (abilities_count == 1) - // Only one ability in generation 4-5 + // Shadow Colosseum pokemon could habe any PID without maching PID + if (pkm.Version == (int)GameVersion.CXD && pkm.Format == 3) + return null; + + // gen3 native or gen4/5 origin + if (pkm.Format == 3 || !pkm.InhabitedGeneration(3)) + return true; + + // Evovled in gen4/5 + if (pkm.Species > Legal.MaxSpeciesID_3) return false; - var abilities_g3 = PersonalTable.E.getAbilities(Species_g3, pkm.AltForm).Where(a => a != 0).Distinct().ToArray(); + + // gen3Species will be zero for pokemon with illegal gen 3 encounters, like Infernape with gen 3 "origin" + var gen3Species = EvoChainsAllGens[3].FirstOrDefault()?.Species ?? 0; + if (gen3Species == 0) + return true; + + // Fall through when gen3 pkm transferred to gen4/5 + return verifyAbilityGen3Transfer(abilities, abilval, gen3Species); + } + private bool? verifyAbilityGen3Transfer(int[] abilities, int abilval, int Species_g3) + { + var abilities_g3 = PersonalTable.E[Species_g3].Abilities.Where(a => a != 0).Distinct().ToArray(); if (abilities_g3.Length == 2) - { - int? EncounterAbility = (EncounterMatch as EncounterTrade)?.Ability; - // If there were two abilities in generation 3 then ability match PID in gen 3 (is impossible not to do it) and will be the same ability if evolved in gen 4-5 - if (EncounterAbility != null && EncounterAbility != 0 && pkm.AbilityNumber != EncounterAbility) - { - AddLine(Severity.Invalid, V223, CheckIdentifier.Ability); - } - // Shadow Colosseum pokemon could habe any PID without maching PID if has 2 abilities in generation 3 // For non-GC, it has 2 abilities in gen 3, must match PID return pkm.Version != (int)GameVersion.CXD; - } + var Species_g45 = Math.Max(EvoChainsAllGens[4].FirstOrDefault()?.Species ?? 0, pkm.Format == 5 ? EvoChainsAllGens[5].FirstOrDefault()?.Species ?? 0 : 0); if (Species_g45 > Species_g3) // it have evolved in gen 4 or 5 games, ability must match PID - return true; + return false; var Evolutions_g45 = Math.Max(EvoChainsAllGens[4].Length, pkm.Format == 5 ? EvoChainsAllGens[5].Length : 0); if (Evolutions_g45 > 1) @@ -1323,18 +1323,18 @@ private bool verifyAbilityGen3Transfer(int[] abilities, int abilval, int Species if (pkm.Ability == abilities_g3[0]) // It could evolve in gen 4-5 an have generation 3 only ability // that means it have not actually evolved in gen 4-5, ability do not need to match PID - return false; + return null; if (pkm.Ability == abilities[1]) // It could evolve in gen4-5 an have generation 4 second ability // that means it have actually evolved in gen 4-5, ability must match PID - return true; + return false; } // Evolutions_g45 == 1 means it have not evolved in gen 4-5 games, // ability do not need to match PID, but only generation 3 ability is allowed if (pkm.Ability != abilities_g3[0]) // Not evolved in gen4-5 but do not have generation 3 only ability AddLine(Severity.Invalid, V373, CheckIdentifier.Ability); - return false; + return null; } private void verifyAbility5(int[] abilities) { diff --git a/PKHeX/Legality/Core.cs b/PKHeX/Legality/Core.cs index e8bf12b68..df0b7bddd 100644 --- a/PKHeX/Legality/Core.cs +++ b/PKHeX/Legality/Core.cs @@ -2507,9 +2507,7 @@ internal static DexLevel[] getEvolutionChain(PKM pkm, object Encounter, int maxs // Evolution chain is in reverse order (devolution) - if (Encounter is int) - minspec = (int)Encounter; - else if (Encounter is IEncounterable[]) + if (Encounter is IEncounterable[]) minspec = vs.Reverse().First(s => ((IEncounterable[]) Encounter).Any(slot => slot.Species == s.Species)).Species; else if (Encounter is IEncounterable) minspec = vs.Reverse().First(s => ((IEncounterable) Encounter).Species == s.Species).Species; diff --git a/PKHeX/Legality/Tables3.cs b/PKHeX/Legality/Tables3.cs index 08c5aa118..f8a644246 100644 --- a/PKHeX/Legality/Tables3.cs +++ b/PKHeX/Legality/Tables3.cs @@ -436,7 +436,7 @@ public static partial class Legal new EncounterTrade { Species = 108, Ability = 1, TID = 01239, SID = 00000, OTGender = 0, Gender = 0, IVs = new[] {24,19,21,15,23,21}, Nature = Nature.Relaxed, Contest = TradeContest_Tough, }, // Lickitung * new EncounterTrade { Species = 124, Ability = 1, TID = 36728, SID = 00000, OTGender = 0, Gender = 1, IVs = new[] {18,17,18,22,25,21}, Nature = Nature.Mild, Contest = TradeContest_Beauty, }, // Jynx new EncounterTrade { Species = 083, Ability = 1, TID = 08810, SID = 00000, OTGender = 0, Gender = 0, IVs = new[] {20,25,21,24,15,20}, Nature = Nature.Adamant, Contest = TradeContest_Cool, }, // Farfetch'd - new EncounterTrade { Species = 101, Ability = 1, TID = 50298, SID = 00000, OTGender = 0, Gender = 2, IVs = new[] {19,16,18,25,25,19}, Nature = Nature.Hasty, Contest = TradeContest_Cool, }, // Electrode + new EncounterTrade { Species = 101, Ability = 2, TID = 50298, SID = 00000, OTGender = 0, Gender = 2, IVs = new[] {19,16,18,25,25,19}, Nature = Nature.Hasty, Contest = TradeContest_Cool, }, // Electrode new EncounterTrade { Species = 114, Ability = 1, TID = 60042, SID = 00000, OTGender = 1, Gender = 0, IVs = new[] {22,17,25,16,23,20}, Nature = Nature.Sassy, Contest = TradeContest_Cute, }, // Tangela new EncounterTrade { Species = 086, Ability = 1, TID = 09853, SID = 00000, OTGender = 0, Gender = 0, IVs = new[] {24,15,22,16,23,22}, Nature = Nature.Bold, Contest = TradeContest_Tough, }, // Seel * // If Pokémon with * is evolved in a Generation IV or V game, its Ability will become its second Ability. diff --git a/PKHeX/PKM/PK3.cs b/PKHeX/PKM/PK3.cs index f58e1d6ba..bafde51b1 100644 --- a/PKHeX/PKM/PK3.cs +++ b/PKHeX/PKM/PK3.cs @@ -40,7 +40,7 @@ public PK3(byte[] decryptedData = null, string ident = null) public override int Gender { get { return PKX.getGender(Species, PID); } set { } } public override int Characteristic => -1; public override int CurrentFriendship { get { return OT_Friendship; } set { OT_Friendship = value; } } - public override int Ability { get { int[] abils = PersonalInfo.Abilities; return abils[abils[1] == 0 ? 0 : AbilityNumber >> 1]; } set { } } + public override int Ability { get { int[] abils = PersonalInfo.Abilities; return abils[AbilityBit && abils[1] != 0 ? 1 : 0]; } set { } } public override int CurrentHandler { get { return 0; } set { } } public override int Egg_Location { get { return 0; } set { } } @@ -116,7 +116,7 @@ public PK3(byte[] decryptedData = null, string ident = null) public override int IV_SPA { get { return (int)(IV32 >> 20) & 0x1F; } set { IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); } } public override int IV_SPD { get { return (int)(IV32 >> 25) & 0x1F; } set { IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); } } public override bool IsEgg { get { return ((IV32 >> 30) & 1) == 1; } set { IV32 = (uint)((IV32 & ~0x40000000) | (uint)(value ? 0x40000000 : 0)); } } - public override int AbilityNumber { get { return 1 << (int)((IV32 >> 31) & 1); } set { IV32 = (IV32 & 0x7FFFFFFF) | (value > 1 ? 0x80000000 : 0); } } + public bool AbilityBit { get { return (IV32 >> 31) == 1; } set { IV32 = (IV32 & 0x7FFFFFFF) | (uint)(value ? 1 << 31 : 0 ); } } private uint RIB0 { get { return BitConverter.ToUInt32(Data, 0x4C); } set { BitConverter.GetBytes(value).CopyTo(Data, 0x4C); } } public int RibbonCountG3Cool { get { return (int)(RIB0 >> 00) & 7; } set { RIB0 = (uint)((RIB0 & ~(7 << 00)) | (uint)(value & 7) << 00); } } @@ -153,6 +153,7 @@ public PK3(byte[] decryptedData = null, string ident = null) public override int Stat_SPD { get { return BitConverter.ToUInt16(Data, 0x62); } set { BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x62); } } // Generated Attributes + public override int AbilityNumber { get { return 1 << PIDAbility; } set { } } public override int PSV => (int)((PID >> 16 ^ PID & 0xFFFF) >> 3); public override int TSV => (TID ^ SID) >> 3; public bool Japanese => IsEgg || Language == 1; diff --git a/PKHeX/Resources/text/zh/LegalityCheckStrings_zh.txt b/PKHeX/Resources/text/zh/LegalityCheckStrings_zh.txt index 87c205df9..5c37e0fc4 100644 --- a/PKHeX/Resources/text/zh/LegalityCheckStrings_zh.txt +++ b/PKHeX/Resources/text/zh/LegalityCheckStrings_zh.txt @@ -31,8 +31,8 @@ V349 = 遗传了TM/HM招式。 V355 = 通过土居忍士进化为铁面忍者习得。 V356 = 通过土居忍士在第{0}世代进化为铁面忍者习得。 V361 = 默认招式。 -V362 = 在第{0}世代的默认招式. -V372 = {0} Berry +V362 = 在第{0}世代的默认招式。 +V372 = {0}树果 V203 = 无性别宝可梦不能有性别。 V201 = 未设置加密常数。 V204 = 持有物未解禁。 @@ -297,15 +297,15 @@ V360 = 无法在来源版本中匹配到相应的配信蛋。 V363 = 不共存招式。 在四色中同一等级的升级招式。 V365 = 不共存进化招式。招式 {0} 比其他招式 {1} 习得等级低。 V366 = 不共存进化招式。招式 {1} 比其他招式 {0} 习得等级高。 -V367 = Individual EV for a level 100 encounter in generation 4 cannot be greater than 100. -V368 = Eggs can not be infected with Pokérus. -V369 = Invalid E-Reader Berry. -V370 = Japanese E-Reader Berry in international savegame. -V371 = American E-Reader Berry in Japanese savegame. -V373 = Ability does not match generation 3 species ability. -V374 = Invalid egg hatch cycles. -V375 = {0} Egg Move. Incompatible with {1} egg moves. -V376 = {0} Exclusive Move. Incompatible with {1} egg moves. -V377 = Egg Move. Not expected in a gift egg. -V378 = Inherited move learned by Level-up. Not expected in a gift egg. -V379 = {0} Inherited Move. Incompatible with {1} inherited moves. \ No newline at end of file +V367 = 对于遇见等级为100的4代宝可梦,单项努力值不能大于100。 +V368 = 蛋不能感染宝可病毒。 +V369 = 不合法E-Reader树果。 +V370 = 日版E-Reader树果在国际版存档中。 +V371 = 美版E-Reader树果在日版存档中。 +V373 = 特性与三代种类特性不一致。 +V374 = 蛋剩余孵化周期不合法。 +V375 = {0}遗传招式。与遗传招式{1}不共存。 +V376 = {0}专属招式。与遗传招式{1}不共存。 +V377 = 遗传招式。礼物蛋不应有。 +V378 = 遗传升级招式。礼物蛋不应有。 +V379 = {0}的遗传招式。与遗传的招式{1}不共存。 \ No newline at end of file