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
This commit is contained in:
wwwwwwzx 2017-04-14 16:24:41 -07:00 committed by Kurt
parent 18794c4484
commit 06f54bb3d0
7 changed files with 66 additions and 69 deletions

View file

@ -29,8 +29,7 @@ namespace PKHeX.WinForms
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");

View file

@ -201,9 +201,8 @@ namespace PKHeX.Core
{
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;
}

View file

@ -1242,16 +1242,27 @@ namespace PKHeX.Core
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)
{
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 @@ namespace PKHeX.Core
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);
}
// Shadow Colosseum pokemon could habe any PID without maching PID
if (pkm.Version == (int)GameVersion.CXD && pkm.Format == 3)
return null;
// 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
// 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();
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);
// 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);
}
// Shadow Colosseum pokemon could habe any PID without maching PID if has 2 abilities in generation 3
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)
// 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 @@ namespace PKHeX.Core
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)
{

View file

@ -2507,9 +2507,7 @@ namespace PKHeX.Core
// 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;

View file

@ -436,7 +436,7 @@ namespace PKHeX.Core
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.

View file

@ -40,7 +40,7 @@ namespace PKHeX.Core
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 @@ namespace PKHeX.Core
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 @@ namespace PKHeX.Core
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;

View file

@ -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.
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}不共存。