Move GetIV to pkm, misc simplifications

remove linq for characteristic calc (get max IV) and others
remove some reliance on new[] for iv/ev
pk5 replace bitmagic with property get->set copy for cleanliness
This commit is contained in:
Kurt 2019-01-05 10:51:41 -08:00
parent 559435e82e
commit 0c9b760470
11 changed files with 177 additions and 154 deletions

View file

@ -469,46 +469,6 @@ namespace PKHeX.Core
} }
} }
/// <summary>
/// Gets one of the <see cref="PKM.EVs"/> based on its index within the array.
/// </summary>
/// <param name="pk">Pokémon to check.</param>
/// <param name="index">Index to get</param>
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));
}
}
/// <summary>
/// Gets one of the <see cref="PKM.IVs"/> based on its index within the array.
/// </summary>
/// <param name="pk">Pokémon to check.</param>
/// <param name="index">Index to get</param>
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));
}
}
/// <summary> /// <summary>
/// Updates the <see cref="PKM.IV_ATK"/> for a Generation 1/2 format <see cref="PKM"/>. /// Updates the <see cref="PKM.IV_ATK"/> for a Generation 1/2 format <see cref="PKM"/>.
/// </summary> /// </summary>

View file

@ -914,5 +914,27 @@ namespace PKHeX.Core
default: return gen >= 6 ? 12 : 10; default: return gen >= 6 ? 12 : 10;
} }
} }
public static bool GetIsFixedIVSequenceValid(IReadOnlyList<int> 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<int> IVs, PKM pkm)
{
for (int i = 0; i < 6; i++)
{
if (IVs[i] != pkm.GetIV(i))
return false;
}
return true;
}
} }
} }

View file

@ -328,10 +328,12 @@ namespace PKHeX.Core
return false; return false;
if (TID != pkm.TID) if (TID != pkm.TID)
return false; return false;
if (Gender >= 0 && Gender != pkm.Gender && pkm.Format <= 2) if (pkm.Format <= 2)
return false; {
if (IVs?.SequenceEqual(pkm.IVs) == false && pkm.Format <= 2) if (Gender >= 0 && Gender != pkm.Gender)
return false; return false;
if (IVs != null && !Legal.GetIsFixedIVSequenceValidNoRand(IVs, pkm)) return false;
}
if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126) if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126)
return false; return false;

View file

@ -24,10 +24,9 @@ namespace PKHeX.Core
var top = pid >> 16; var top = pid >> 16;
var bot = pid & 0xFFFF; var bot = pid & 0xFFFF;
var iIVs = pk.IVs;
var IVs = new uint[6]; var IVs = new uint[6];
for (int i = 0; i < 6; i++) 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)) if (GetLCRNGMatch(top, bot, IVs, out PIDIV pidiv))
return pidiv; return pidiv;
@ -739,8 +738,9 @@ namespace PKHeX.Core
return true; return true;
if (PIDType.Method_1 != val) if (PIDType.Method_1 != val)
return false; 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) public static bool IsCompatible4(this PIDType val, IEncounterable encounter, PKM pkm)

View file

@ -19,29 +19,37 @@ namespace PKHeX.Core
return; return;
} }
var EncounterMatch = data.EncounterMatch; var EncounterMatch = data.EncounterMatch;
var evs = pkm.EVs;
int sum = pkm.EVTotal; int sum = pkm.EVTotal;
if (sum > 0 && pkm.IsEgg) if (sum > 0 && pkm.IsEgg)
data.AddLine(GetInvalid(LEffortEgg)); 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. // 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 int format = pkm.Format;
if (format < 3) // can abuse daycare for EV training without EXP gain
return; return;
const int maxEV = 100; // Vitamin Max if (sum > 510) // format >= 3
if (Experience.GetEXP(EncounterMatch.LevelMin, pkm.Species, pkm.AltForm) == pkm.EXP && evs.Any(ev => ev > maxEV)) data.AddLine(GetInvalid(LEffortAbove510));
data.AddLine(GetInvalid(string.Format(LEffortUntrainedCap, maxEV))); 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 // Only one of the following can be true: 0, 508, and x%6!=0

View file

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.LegalityCheckStrings; using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core namespace PKHeX.Core
@ -56,7 +55,7 @@ namespace PKHeX.Core
var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3); var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3);
if (ivflag == 0) // Random IVs if (ivflag == 0) // Random IVs
{ {
bool valid = GetIsFixedIVSequenceValid(IVs, data.pkm.IVs); bool valid = Legal.GetIsFixedIVSequenceValid(IVs, data.pkm);
if (!valid) if (!valid)
data.AddLine(GetInvalid(LEncGiftIVMismatch)); data.AddLine(GetInvalid(LEncGiftIVMismatch));
} }
@ -67,16 +66,6 @@ namespace PKHeX.Core
} }
} }
private static bool GetIsFixedIVSequenceValid(IReadOnlyList<int> IVs, IReadOnlyList<int> 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) private void VerifyIVsSlot(LegalityAnalysis data, EncounterSlot w)
{ {
switch (w.Generation) switch (w.Generation)
@ -111,7 +100,7 @@ namespace PKHeX.Core
private void VerifyIVsFlawless(LegalityAnalysis data, int count) 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))); data.AddLine(GetInvalid(string.Format(LIVF_COUNT0_31, count)));
} }

View file

@ -271,16 +271,14 @@ namespace PKHeX.Core
{ {
get get
{ {
// Characteristic with PID%6 int pm6 = (int)(PID % 6); // PID
int pm6 = (int)(PID % 6); // PID MOD 6 int maxIV = MaximumIV;
int maxIV = IVs.Max();
int pm6stat = 0; int pm6stat = 0;
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
pm6stat = (pm6 + i) % 6; pm6stat = (pm6 + i) % 6;
if (IVs[pm6stat] == maxIV) if (GetIV(pm6stat) == maxIV)
break; // P%6 is this stat break;
} }
return (pm6stat * 5) + (maxIV % 5); return (pm6stat * 5) + (maxIV % 5);
} }
@ -436,10 +434,8 @@ namespace PKHeX.Core
} }
// Battle Ribbon Counter // Battle Ribbon Counter
// Winning Ribbon if (RibbonWinning) battleribbons++;
if ((Data[0x3E] & 0x20) >> 5 == 1) battleribbons++; if (RibbonVictory) battleribbons++;
// Victory Ribbon
if ((Data[0x3E] & 0x40) >> 6 == 1) battleribbons++;
for (int i = 1; i < 7; i++) // Sinnoh Battle Ribbons for (int i = 1; i < 7; i++) // Sinnoh Battle Ribbons
if (((Data[0x24] >> i) & 1) == 1) battleribbons++; if (((Data[0x24] >> i) & 1) == 1) battleribbons++;
@ -448,57 +444,42 @@ namespace PKHeX.Core
pk6.RibbonCountMemoryBattle = battleribbons; pk6.RibbonCountMemoryBattle = battleribbons;
// Copy Ribbons to their new locations. // Copy Ribbons to their new locations.
int bx30 = 0; pk6.RibbonChampionG3Hoenn = RibbonChampionG3Hoenn;
// bx30 |= 0; // Kalos Champ - New Kalos Ribbon pk6.RibbonChampionSinnoh = RibbonChampionSinnoh;
bx30 |= ((Data[0x3E] & 0x10) >> 4) << 1; // Hoenn Champion pk6.RibbonEffort = RibbonEffort;
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;
int bx31 = 0; pk6.RibbonAlert = RibbonAlert;
bx31 |= ((Data[0x24] & 0x80) >> 7) << 0; // Alert pk6.RibbonShock = RibbonShock;
bx31 |= ((Data[0x25] & 0x01) >> 0) << 1; // Shock pk6.RibbonDowncast = RibbonDowncast;
bx31 |= ((Data[0x25] & 0x02) >> 1) << 2; // Downcast pk6.RibbonCareless = RibbonCareless;
bx31 |= ((Data[0x25] & 0x04) >> 2) << 3; // Careless pk6.RibbonRelax = RibbonRelax;
bx31 |= ((Data[0x25] & 0x08) >> 3) << 4; // Relax pk6.RibbonSnooze = RibbonSnooze;
bx31 |= ((Data[0x25] & 0x10) >> 4) << 5; // Snooze pk6.RibbonSmile = RibbonSmile;
bx31 |= ((Data[0x25] & 0x20) >> 5) << 6; // Smile pk6.RibbonGorgeous = RibbonGorgeous;
bx31 |= ((Data[0x25] & 0x40) >> 6) << 7; // Gorgeous
pk6.Data[0x31] = (byte)bx31;
int bx32 = 0; pk6.RibbonRoyal = RibbonRoyal;
bx32 |= ((Data[0x25] & 0x80) >> 7) << 0; // Royal pk6.RibbonGorgeousRoyal = RibbonGorgeousRoyal;
bx32 |= ((Data[0x26] & 0x01) >> 0) << 1; // Gorgeous Royal pk6.RibbonArtist = RibbonArtist;
bx32 |= ((Data[0x3E] & 0x80) >> 7) << 2; // Artist pk6.RibbonFootprint = RibbonFootprint;
bx32 |= ((Data[0x26] & 0x02) >> 1) << 3; // Footprint pk6.RibbonRecord = RibbonRecord;
bx32 |= ((Data[0x26] & 0x04) >> 2) << 4; // Record pk6.RibbonLegend = RibbonLegend;
bx32 |= ((Data[0x26] & 0x10) >> 4) << 5; // Legend pk6.RibbonCountry = RibbonCountry;
bx32 |= ((Data[0x3F] & 0x10) >> 4) << 6; // Country pk6.RibbonNational = RibbonNational;
bx32 |= ((Data[0x3F] & 0x20) >> 5) << 7; // National
pk6.Data[0x32] = (byte)bx32;
int bx33 = 0; pk6.RibbonEarth = RibbonEarth;
bx33 |= ((Data[0x3F] & 0x40) >> 6) << 0; // Earth pk6.RibbonWorld = RibbonWorld;
bx33 |= ((Data[0x3F] & 0x80) >> 7) << 1; // World pk6.RibbonClassic = RibbonClassic;
bx33 |= ((Data[0x27] & 0x04) >> 2) << 2; // Classic pk6.RibbonPremier = RibbonPremier;
bx33 |= ((Data[0x27] & 0x08) >> 3) << 3; // Premier pk6.RibbonEvent = RibbonEvent;
bx33 |= ((Data[0x26] & 0x08) >> 3) << 4; // Event pk6.RibbonBirthday = RibbonBirthday;
bx33 |= ((Data[0x26] & 0x40) >> 6) << 5; // Birthday pk6.RibbonSpecial = RibbonSpecial;
bx33 |= ((Data[0x26] & 0x80) >> 7) << 6; // Special pk6.RibbonSouvenir = RibbonSouvenir;
bx33 |= ((Data[0x27] & 0x01) >> 0) << 7; // Souvenir
pk6.Data[0x33] = (byte)bx33;
int bx34 = 0; pk6.RibbonWishing = RibbonWishing;
bx34 |= ((Data[0x27] & 0x02) >> 1) << 0; // Wishing Ribbon pk6.RibbonChampionBattle = RibbonChampionBattle;
bx34 |= ((Data[0x3F] & 0x02) >> 1) << 1; // Battle Champion pk6.RibbonChampionRegional = RibbonChampionRegional;
bx34 |= ((Data[0x3F] & 0x04) >> 2) << 2; // Regional Champion pk6.RibbonChampionNational = RibbonChampionNational;
bx34 |= ((Data[0x3F] & 0x08) >> 3) << 3; // National Champion pk6.RibbonChampionWorld = RibbonChampionWorld;
bx34 |= ((Data[0x26] & 0x20) >> 5) << 4; // World Champion
pk6.Data[0x34] = (byte)bx34;
// Write Transfer Location - location is dependent on 3DS system that transfers. // Write Transfer Location - location is dependent on 3DS system that transfers.
pk6.Country = PKMConverter.Country; pk6.Country = PKMConverter.Country;

View file

@ -342,6 +342,23 @@ namespace PKHeX.Core
public int MarkDiamond { get => Markings[5]; set { var marks = Markings; marks[5] = value; Markings = marks; } } 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 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 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}"; public string FileName => $"{FileNameWithoutExtension}.{Extension}";
@ -1002,5 +1019,43 @@ namespace PKHeX.Core
Moves = moves; Moves = moves;
FixMoves(); FixMoves();
} }
/// <summary>
/// Gets one of the <see cref="EVs"/> based on its index within the array.
/// </summary>
/// <param name="index">Index to get</param>
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));
}
}
/// <summary>
/// Gets one of the <see cref="IVs"/> based on its index within the array.
/// </summary>
/// <param name="index">Index to get</param>
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));
}
}
} }
} }

View file

@ -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 public abstract class _K4 : PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, IContestStats
{ {
@ -23,16 +21,14 @@ namespace PKHeX.Core
{ {
get get
{ {
// Characteristic with PID%6 int pm6 = (int)(EncryptionConstant % 6); // PID
int pm6 = (int)(PID % 6); // PID MOD 6 int maxIV = MaximumIV;
int maxIV = IVs.Max();
int pm6stat = 0; int pm6stat = 0;
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
pm6stat = (pm6 + i) % 6; pm6stat = (pm6 + i) % 6;
if (IVs[pm6stat] == maxIV) if (GetIV(pm6stat) == maxIV)
break; // P%6 is this stat break;
} }
return (pm6stat * 5) + (maxIV % 5); return (pm6stat * 5) + (maxIV % 5);
} }

View file

@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
namespace PKHeX.Core namespace PKHeX.Core
{ {
@ -67,14 +66,13 @@ namespace PKHeX.Core
{ {
get get
{ {
// Characteristic with EC%6 int pm6 = (int)(EncryptionConstant % 6);
int pm6 = (int)(EncryptionConstant % 6); // EC MOD 6 int maxIV = MaximumIV;
int maxIV = IVs.Max();
int pm6stat = 0; int pm6stat = 0;
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
if (IVs[pm6stat = pm6++ % 6] == maxIV) pm6stat = (pm6 + i) % 6;
if (GetIV(pm6stat) == maxIV)
break; break;
} }
return (pm6stat * 5) + (maxIV % 5); return (pm6stat * 5) + (maxIV % 5);

View file

@ -30,13 +30,25 @@
/// <param name="species">National Dex number of the Pokémon.</param> /// <param name="species">National Dex number of the Pokémon.</param>
/// <param name="forme">AltForm ID (starters in Let's Go)</param> /// <param name="forme">AltForm ID (starters in Let's Go)</param>
/// <returns>Experience points needed to have specified level.</returns> /// <returns>Experience points needed to have specified level.</returns>
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);
}
/// <summary>
/// Gets the minimum Experience points for the specified level.
/// </summary>
/// <param name="level">Current level</param>
/// <param name="growth">Growth Rate type</param>
/// <returns>Experience points needed to have specified level.</returns>
public static uint GetEXP(int level, int growth)
{ {
if (level <= 1) if (level <= 1)
return 0; return 0;
if (level > 100) if (level > 100)
level = 100; level = 100;
return ExpTable[level - 1, PKX.Personal.GetFormeEntry(species, forme).EXPGrowth]; return ExpTable[level - 1, growth];
} }
/// <summary> /// <summary>