diff --git a/PKHeX.Core/Editing/CommonEdits.cs b/PKHeX.Core/Editing/CommonEdits.cs
index 5943c304a..6db31b384 100644
--- a/PKHeX.Core/Editing/CommonEdits.cs
+++ b/PKHeX.Core/Editing/CommonEdits.cs
@@ -469,46 +469,6 @@ namespace PKHeX.Core
}
}
- ///
- /// Gets one of the based on its index within the array.
- ///
- /// Pokémon to check.
- /// Index to get
- public static int GetEV(this PKM pk, int index)
- {
- switch (index)
- {
- case 0: return pk.EV_HP ;
- case 1: return pk.EV_ATK;
- case 2: return pk.EV_DEF;
- case 3: return pk.EV_SPE;
- case 4: return pk.EV_SPA;
- case 5: return pk.EV_SPD;
- default:
- throw new ArgumentOutOfRangeException(nameof(index));
- }
- }
-
- ///
- /// Gets one of the based on its index within the array.
- ///
- /// Pokémon to check.
- /// Index to get
- public static int GetIV(this PKM pk, int index)
- {
- switch (index)
- {
- case 0: return pk.IV_HP ;
- case 1: return pk.IV_ATK;
- case 2: return pk.IV_DEF;
- case 3: return pk.IV_SPE;
- case 4: return pk.IV_SPA;
- case 5: return pk.IV_SPD;
- default:
- throw new ArgumentOutOfRangeException(nameof(index));
- }
- }
-
///
/// Updates the for a Generation 1/2 format .
///
diff --git a/PKHeX.Core/Legality/Core.cs b/PKHeX.Core/Legality/Core.cs
index aeb8308ea..bfda4b91f 100644
--- a/PKHeX.Core/Legality/Core.cs
+++ b/PKHeX.Core/Legality/Core.cs
@@ -914,5 +914,27 @@ namespace PKHeX.Core
default: return gen >= 6 ? 12 : 10;
}
}
+
+ public static bool GetIsFixedIVSequenceValid(IReadOnlyList IVs, PKM pkm, int max = 31)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ if ((uint) IVs[i] > max) // random
+ continue;
+ if (IVs[i] != pkm.GetIV(i))
+ return false;
+ }
+ return true;
+ }
+
+ public static bool GetIsFixedIVSequenceValidNoRand(IReadOnlyList IVs, PKM pkm)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ if (IVs[i] != pkm.GetIV(i))
+ return false;
+ }
+ return true;
+ }
}
}
diff --git a/PKHeX.Core/Legality/Encounters/EncounterTrade.cs b/PKHeX.Core/Legality/Encounters/EncounterTrade.cs
index 2a7fde988..d002cb624 100644
--- a/PKHeX.Core/Legality/Encounters/EncounterTrade.cs
+++ b/PKHeX.Core/Legality/Encounters/EncounterTrade.cs
@@ -328,10 +328,12 @@ namespace PKHeX.Core
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.Format <= 2)
+ {
+ if (Gender >= 0 && Gender != pkm.Gender)
+ return false;
+ if (IVs != null && !Legal.GetIsFixedIVSequenceValidNoRand(IVs, pkm)) return false;
+ }
if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126)
return false;
diff --git a/PKHeX.Core/Legality/RNG/MethodFinder.cs b/PKHeX.Core/Legality/RNG/MethodFinder.cs
index c21f11ef6..9268c3cff 100644
--- a/PKHeX.Core/Legality/RNG/MethodFinder.cs
+++ b/PKHeX.Core/Legality/RNG/MethodFinder.cs
@@ -24,10 +24,9 @@ namespace PKHeX.Core
var top = pid >> 16;
var bot = pid & 0xFFFF;
- var iIVs = pk.IVs;
var IVs = new uint[6];
for (int i = 0; i < 6; i++)
- IVs[i] = (uint)iIVs[i];
+ IVs[i] = (uint)pk.GetIV(i);
if (GetLCRNGMatch(top, bot, IVs, out PIDIV pidiv))
return pidiv;
@@ -739,8 +738,9 @@ namespace PKHeX.Core
return true;
if (PIDType.Method_1 != val)
return false;
- var IVs = pkm.IVs;
- return !(IVs[2] != 0 || IVs[3] != 0 || IVs[4] != 0 || IVs[5] != 0 || IVs[1] > 7);
+
+ // only 8 bits are stored instead of 32 -- 5 bits HP, 3 bits for ATK.
+ return !(pkm.IV_DEF != 0 || pkm.IV_SPE != 0 || pkm.IV_SPA != 0 || pkm.IV_SPD != 0 || pkm.IV_ATK > 7);
}
public static bool IsCompatible4(this PIDType val, IEncounterable encounter, PKM pkm)
diff --git a/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs b/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs
index 6676e4310..fd3b28689 100644
--- a/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs
+++ b/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs
@@ -19,29 +19,37 @@ namespace PKHeX.Core
return;
}
var EncounterMatch = data.EncounterMatch;
- var evs = pkm.EVs;
int sum = pkm.EVTotal;
if (sum > 0 && pkm.IsEgg)
data.AddLine(GetInvalid(LEffortEgg));
- if (pkm.Format >= 3 && sum > 510)
- data.AddLine(GetInvalid(LEffortAbove510));
- if (pkm.Format >= 6 && evs.Any(ev => ev > 252))
- data.AddLine(GetInvalid(LEffortAbove252));
- if (pkm.Format == 4 && pkm.Gen4 && EncounterMatch.LevelMin == 100)
- {
- // Cannot EV train at level 100 -- Certain events are distributed at level 100.
- if (evs.Any(ev => ev > 100)) // EVs can only be increased by vitamins to a max of 100.
- data.AddLine(GetInvalid(LEffortCap100));
- }
- else if (pkm.Format < 5)
- {
- // In Generations I and II, when a Pokémon is taken out of the Day Care, its experience will lower to the minimum value for its current level.
- if (pkm.Format < 3) // can abuse daycare for EV training without EXP gain
- return;
- const int maxEV = 100; // Vitamin Max
- if (Experience.GetEXP(EncounterMatch.LevelMin, pkm.Species, pkm.AltForm) == pkm.EXP && evs.Any(ev => ev > maxEV))
- data.AddLine(GetInvalid(string.Format(LEffortUntrainedCap, maxEV)));
+ // In Generations I and II, when a Pokémon is taken out of the Day Care, its experience will lower to the minimum value for its current level.
+ int format = pkm.Format;
+ if (format < 3) // can abuse daycare for EV training without EXP gain
+ return;
+
+ if (sum > 510) // format >= 3
+ data.AddLine(GetInvalid(LEffortAbove510));
+ var evs = pkm.EVs;
+ if (format >= 6 && evs.Any(ev => ev > 252))
+ data.AddLine(GetInvalid(LEffortAbove252));
+
+ const int vitaMax = 100; // Vitamin Max
+ if (format < 5) // 3/4
+ {
+ if (EncounterMatch.LevelMin == 100) // only true for Gen4 and Format=4
+ {
+ // Cannot EV train at level 100 -- Certain events are distributed at level 100.
+ if (evs.Any(ev => ev > vitaMax)) // EVs can only be increased by vitamins to a max of 100.
+ data.AddLine(GetInvalid(LEffortCap100));
+ }
+ else // check for gained EVs without gaining EXP -- don't check gen5+ which have wings to boost above 100.
+ {
+ var growth = PersonalTable.HGSS[EncounterMatch.Species].EXPGrowth;
+ var baseEXP = Experience.GetEXP(EncounterMatch.LevelMin, growth);
+ if (baseEXP == pkm.EXP && evs.Any(ev => ev > vitaMax))
+ data.AddLine(GetInvalid(string.Format(LEffortUntrainedCap, vitaMax)));
+ }
}
// Only one of the following can be true: 0, 508, and x%6!=0
diff --git a/PKHeX.Core/Legality/Verifiers/IndividualValueVerifier.cs b/PKHeX.Core/Legality/Verifiers/IndividualValueVerifier.cs
index 8fa370200..3ac02e8b9 100644
--- a/PKHeX.Core/Legality/Verifiers/IndividualValueVerifier.cs
+++ b/PKHeX.Core/Legality/Verifiers/IndividualValueVerifier.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core
@@ -56,7 +55,7 @@ namespace PKHeX.Core
var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3);
if (ivflag == 0) // Random IVs
{
- bool valid = GetIsFixedIVSequenceValid(IVs, data.pkm.IVs);
+ bool valid = Legal.GetIsFixedIVSequenceValid(IVs, data.pkm);
if (!valid)
data.AddLine(GetInvalid(LEncGiftIVMismatch));
}
@@ -67,16 +66,6 @@ namespace PKHeX.Core
}
}
- private static bool GetIsFixedIVSequenceValid(IReadOnlyList IVs, IReadOnlyList pkIVs)
- {
- for (int i = 0; i < 6; i++)
- {
- if (IVs[i] <= 31 && IVs[i] != pkIVs[i])
- return false;
- }
- return true;
- }
-
private void VerifyIVsSlot(LegalityAnalysis data, EncounterSlot w)
{
switch (w.Generation)
@@ -111,7 +100,7 @@ namespace PKHeX.Core
private void VerifyIVsFlawless(LegalityAnalysis data, int count)
{
- if (data.pkm.IVs.Count(iv => iv == 31) < count)
+ if (data.pkm.FlawlessIVCount < count)
data.AddLine(GetInvalid(string.Format(LIVF_COUNT0_31, count)));
}
diff --git a/PKHeX.Core/PKM/PK5.cs b/PKHeX.Core/PKM/PK5.cs
index 3283e52e0..3dee74ded 100644
--- a/PKHeX.Core/PKM/PK5.cs
+++ b/PKHeX.Core/PKM/PK5.cs
@@ -271,16 +271,14 @@ namespace PKHeX.Core
{
get
{
- // Characteristic with PID%6
- int pm6 = (int)(PID % 6); // PID MOD 6
- int maxIV = IVs.Max();
+ int pm6 = (int)(PID % 6); // PID
+ int maxIV = MaximumIV;
int pm6stat = 0;
-
for (int i = 0; i < 6; i++)
{
pm6stat = (pm6 + i) % 6;
- if (IVs[pm6stat] == maxIV)
- break; // P%6 is this stat
+ if (GetIV(pm6stat) == maxIV)
+ break;
}
return (pm6stat * 5) + (maxIV % 5);
}
@@ -436,10 +434,8 @@ namespace PKHeX.Core
}
// Battle Ribbon Counter
- // Winning Ribbon
- if ((Data[0x3E] & 0x20) >> 5 == 1) battleribbons++;
- // Victory Ribbon
- if ((Data[0x3E] & 0x40) >> 6 == 1) battleribbons++;
+ if (RibbonWinning) battleribbons++;
+ if (RibbonVictory) battleribbons++;
for (int i = 1; i < 7; i++) // Sinnoh Battle Ribbons
if (((Data[0x24] >> i) & 1) == 1) battleribbons++;
@@ -448,57 +444,42 @@ namespace PKHeX.Core
pk6.RibbonCountMemoryBattle = battleribbons;
// Copy Ribbons to their new locations.
- int bx30 = 0;
- // bx30 |= 0; // Kalos Champ - New Kalos Ribbon
- bx30 |= ((Data[0x3E] & 0x10) >> 4) << 1; // Hoenn Champion
- bx30 |= ((Data[0x24] & 0x01) >> 0) << 2; // Sinnoh Champ
- // bx30 |= 0; // Best Friend - New Kalos Ribbon
- // bx30 |= 0; // Training - New Kalos Ribbon
- // bx30 |= 0; // Skillful - New Kalos Ribbon
- // bx30 |= 0; // Expert - New Kalos Ribbon
- bx30 |= ((Data[0x3F] & 0x01) >> 0) << 7; // Effort Ribbon
- pk6.Data[0x30] = (byte)bx30;
+ pk6.RibbonChampionG3Hoenn = RibbonChampionG3Hoenn;
+ pk6.RibbonChampionSinnoh = RibbonChampionSinnoh;
+ pk6.RibbonEffort = RibbonEffort;
- int bx31 = 0;
- bx31 |= ((Data[0x24] & 0x80) >> 7) << 0; // Alert
- bx31 |= ((Data[0x25] & 0x01) >> 0) << 1; // Shock
- bx31 |= ((Data[0x25] & 0x02) >> 1) << 2; // Downcast
- bx31 |= ((Data[0x25] & 0x04) >> 2) << 3; // Careless
- bx31 |= ((Data[0x25] & 0x08) >> 3) << 4; // Relax
- bx31 |= ((Data[0x25] & 0x10) >> 4) << 5; // Snooze
- bx31 |= ((Data[0x25] & 0x20) >> 5) << 6; // Smile
- bx31 |= ((Data[0x25] & 0x40) >> 6) << 7; // Gorgeous
- pk6.Data[0x31] = (byte)bx31;
+ pk6.RibbonAlert = RibbonAlert;
+ pk6.RibbonShock = RibbonShock;
+ pk6.RibbonDowncast = RibbonDowncast;
+ pk6.RibbonCareless = RibbonCareless;
+ pk6.RibbonRelax = RibbonRelax;
+ pk6.RibbonSnooze = RibbonSnooze;
+ pk6.RibbonSmile = RibbonSmile;
+ pk6.RibbonGorgeous = RibbonGorgeous;
- int bx32 = 0;
- bx32 |= ((Data[0x25] & 0x80) >> 7) << 0; // Royal
- bx32 |= ((Data[0x26] & 0x01) >> 0) << 1; // Gorgeous Royal
- bx32 |= ((Data[0x3E] & 0x80) >> 7) << 2; // Artist
- bx32 |= ((Data[0x26] & 0x02) >> 1) << 3; // Footprint
- bx32 |= ((Data[0x26] & 0x04) >> 2) << 4; // Record
- bx32 |= ((Data[0x26] & 0x10) >> 4) << 5; // Legend
- bx32 |= ((Data[0x3F] & 0x10) >> 4) << 6; // Country
- bx32 |= ((Data[0x3F] & 0x20) >> 5) << 7; // National
- pk6.Data[0x32] = (byte)bx32;
+ pk6.RibbonRoyal = RibbonRoyal;
+ pk6.RibbonGorgeousRoyal = RibbonGorgeousRoyal;
+ pk6.RibbonArtist = RibbonArtist;
+ pk6.RibbonFootprint = RibbonFootprint;
+ pk6.RibbonRecord = RibbonRecord;
+ pk6.RibbonLegend = RibbonLegend;
+ pk6.RibbonCountry = RibbonCountry;
+ pk6.RibbonNational = RibbonNational;
- int bx33 = 0;
- bx33 |= ((Data[0x3F] & 0x40) >> 6) << 0; // Earth
- bx33 |= ((Data[0x3F] & 0x80) >> 7) << 1; // World
- bx33 |= ((Data[0x27] & 0x04) >> 2) << 2; // Classic
- bx33 |= ((Data[0x27] & 0x08) >> 3) << 3; // Premier
- bx33 |= ((Data[0x26] & 0x08) >> 3) << 4; // Event
- bx33 |= ((Data[0x26] & 0x40) >> 6) << 5; // Birthday
- bx33 |= ((Data[0x26] & 0x80) >> 7) << 6; // Special
- bx33 |= ((Data[0x27] & 0x01) >> 0) << 7; // Souvenir
- pk6.Data[0x33] = (byte)bx33;
+ pk6.RibbonEarth = RibbonEarth;
+ pk6.RibbonWorld = RibbonWorld;
+ pk6.RibbonClassic = RibbonClassic;
+ pk6.RibbonPremier = RibbonPremier;
+ pk6.RibbonEvent = RibbonEvent;
+ pk6.RibbonBirthday = RibbonBirthday;
+ pk6.RibbonSpecial = RibbonSpecial;
+ pk6.RibbonSouvenir = RibbonSouvenir;
- int bx34 = 0;
- bx34 |= ((Data[0x27] & 0x02) >> 1) << 0; // Wishing Ribbon
- bx34 |= ((Data[0x3F] & 0x02) >> 1) << 1; // Battle Champion
- bx34 |= ((Data[0x3F] & 0x04) >> 2) << 2; // Regional Champion
- bx34 |= ((Data[0x3F] & 0x08) >> 3) << 3; // National Champion
- bx34 |= ((Data[0x26] & 0x20) >> 5) << 4; // World Champion
- pk6.Data[0x34] = (byte)bx34;
+ pk6.RibbonWishing = RibbonWishing;
+ pk6.RibbonChampionBattle = RibbonChampionBattle;
+ pk6.RibbonChampionRegional = RibbonChampionRegional;
+ pk6.RibbonChampionNational = RibbonChampionNational;
+ pk6.RibbonChampionWorld = RibbonChampionWorld;
// Write Transfer Location - location is dependent on 3DS system that transfers.
pk6.Country = PKMConverter.Country;
diff --git a/PKHeX.Core/PKM/PKM.cs b/PKHeX.Core/PKM/PKM.cs
index 1a0f21993..ddc0e57c1 100644
--- a/PKHeX.Core/PKM/PKM.cs
+++ b/PKHeX.Core/PKM/PKM.cs
@@ -342,6 +342,23 @@ namespace PKHeX.Core
public int MarkDiamond { get => Markings[5]; set { var marks = Markings; marks[5] = value; Markings = marks; } }
public int IVTotal => IV_HP + IV_ATK + IV_DEF + IV_SPA + IV_SPD + IV_SPE;
public int EVTotal => EV_HP + EV_ATK + EV_DEF + EV_SPA + EV_SPD + EV_SPE;
+ public int MaximumIV => Math.Max(Math.Max(Math.Max(Math.Max(Math.Max(IV_HP, IV_ATK), IV_DEF), IV_SPA), IV_SPD), IV_SPE);
+
+ public int FlawlessIVCount
+ {
+ get
+ {
+ int max = MaxIV;
+ int ctr = 0;
+ if (IV_HP == max) ++ctr;
+ if (IV_ATK == max) ++ctr;
+ if (IV_DEF == max) ++ctr;
+ if (IV_SPA == max) ++ctr;
+ if (IV_SPD == max) ++ctr;
+ if (IV_SPE == max) ++ctr;
+ return ctr;
+ }
+ }
public string FileName => $"{FileNameWithoutExtension}.{Extension}";
@@ -1002,5 +1019,43 @@ namespace PKHeX.Core
Moves = moves;
FixMoves();
}
+
+ ///
+ /// Gets one of the based on its index within the array.
+ ///
+ /// Index to get
+ public int GetEV(int index)
+ {
+ switch (index)
+ {
+ case 0: return EV_HP;
+ case 1: return EV_ATK;
+ case 2: return EV_DEF;
+ case 3: return EV_SPE;
+ case 4: return EV_SPA;
+ case 5: return EV_SPD;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
+
+ ///
+ /// Gets one of the based on its index within the array.
+ ///
+ /// Index to get
+ public int GetIV(int index)
+ {
+ switch (index)
+ {
+ case 0: return IV_HP;
+ case 1: return IV_ATK;
+ case 2: return IV_DEF;
+ case 3: return IV_SPE;
+ case 4: return IV_SPA;
+ case 5: return IV_SPD;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ }
}
}
diff --git a/PKHeX.Core/PKM/Shared/_K4.cs b/PKHeX.Core/PKM/Shared/_K4.cs
index d68c5b16a..218c0a589 100644
--- a/PKHeX.Core/PKM/Shared/_K4.cs
+++ b/PKHeX.Core/PKM/Shared/_K4.cs
@@ -1,6 +1,4 @@
-using System.Linq;
-
-namespace PKHeX.Core
+namespace PKHeX.Core
{
public abstract class _K4 : PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, IContestStats
{
@@ -23,16 +21,14 @@ namespace PKHeX.Core
{
get
{
- // Characteristic with PID%6
- int pm6 = (int)(PID % 6); // PID MOD 6
- int maxIV = IVs.Max();
+ int pm6 = (int)(EncryptionConstant % 6); // PID
+ int maxIV = MaximumIV;
int pm6stat = 0;
-
for (int i = 0; i < 6; i++)
{
pm6stat = (pm6 + i) % 6;
- if (IVs[pm6stat] == maxIV)
- break; // P%6 is this stat
+ if (GetIV(pm6stat) == maxIV)
+ break;
}
return (pm6stat * 5) + (maxIV % 5);
}
diff --git a/PKHeX.Core/PKM/Shared/_K6.cs b/PKHeX.Core/PKM/Shared/_K6.cs
index e867611ec..aa0a4fba1 100644
--- a/PKHeX.Core/PKM/Shared/_K6.cs
+++ b/PKHeX.Core/PKM/Shared/_K6.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
namespace PKHeX.Core
{
@@ -67,14 +66,13 @@ namespace PKHeX.Core
{
get
{
- // Characteristic with EC%6
- int pm6 = (int)(EncryptionConstant % 6); // EC MOD 6
- int maxIV = IVs.Max();
+ int pm6 = (int)(EncryptionConstant % 6);
+ int maxIV = MaximumIV;
int pm6stat = 0;
-
for (int i = 0; i < 6; i++)
{
- if (IVs[pm6stat = pm6++ % 6] == maxIV)
+ pm6stat = (pm6 + i) % 6;
+ if (GetIV(pm6stat) == maxIV)
break;
}
return (pm6stat * 5) + (maxIV % 5);
diff --git a/PKHeX.Core/PKM/Util/Experience.cs b/PKHeX.Core/PKM/Util/Experience.cs
index d91cb246a..f77a58543 100644
--- a/PKHeX.Core/PKM/Util/Experience.cs
+++ b/PKHeX.Core/PKM/Util/Experience.cs
@@ -30,13 +30,25 @@
/// National Dex number of the Pokémon.
/// AltForm ID (starters in Let's Go)
/// Experience points needed to have specified level.
- public static uint GetEXP(int level, int species, int forme = 0)
+ public static uint GetEXP(int level, int species, int forme)
+ {
+ var growth = PKX.Personal.GetFormeEntry(species, forme).EXPGrowth;
+ return GetEXP(level, growth);
+ }
+
+ ///
+ /// Gets the minimum Experience points for the specified level.
+ ///
+ /// Current level
+ /// Growth Rate type
+ /// Experience points needed to have specified level.
+ public static uint GetEXP(int level, int growth)
{
if (level <= 1)
return 0;
if (level > 100)
level = 100;
- return ExpTable[level - 1, PKX.Personal.GetFormeEntry(species, forme).EXPGrowth];
+ return ExpTable[level - 1, growth];
}
///