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>
/// Updates the <see cref="PKM.IV_ATK"/> for a Generation 1/2 format <see cref="PKM"/>.
/// </summary>

View file

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

View file

@ -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)

View file

@ -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

View file

@ -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<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)
{
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)));
}

View file

@ -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;

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 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();
}
/// <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
{
@ -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);
}

View file

@ -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);

View file

@ -30,13 +30,25 @@
/// <param name="species">National Dex number of the Pokémon.</param>
/// <param name="forme">AltForm ID (starters in Let's Go)</param>
/// <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)
return 0;
if (level > 100)
level = 100;
return ExpTable[level - 1, PKX.Personal.GetFormeEntry(species, forme).EXPGrowth];
return ExpTable[level - 1, growth];
}
/// <summary>