Extract EV limit logic

This commit is contained in:
Kurt
2023-09-27 22:15:59 -07:00
parent 0cda774b97
commit 12ee201431
29 changed files with 105 additions and 56 deletions

View File

@@ -309,11 +309,11 @@ public static void ApplyHeldItem(this PKM pk, int item, EntityContext context)
public static int GetMaximumEV(this PKM pk, int index)
{
if (pk.Format < 3)
return ushort.MaxValue;
return EffortValues.Max12;
var sum = pk.EVTotal - pk.GetEV(index);
int remaining = 510 - sum;
return Math.Clamp(remaining, 0, 252);
int remaining = EffortValues.Max510 - sum;
return Math.Clamp(remaining, 0, EffortValues.Max252);
}
/// <summary>

View File

@@ -16,7 +16,6 @@ public sealed class EffortValueVerifier : Verifier
public override void Verify(LegalityAnalysis data)
{
var pk = data.Entity;
var enc = data.EncounterMatch;
if (pk.IsEgg)
{
if (pk.EVTotal is not 0)
@@ -33,6 +32,7 @@ public override void Verify(LegalityAnalysis data)
if (sum > totalMax) // format >= 3
data.AddLine(GetInvalid(LEffortAbove510));
var enc = data.EncounterMatch;
Span<int> evs = stackalloc int[6];
pk.GetEVs(evs);
if (format >= 6 && evs.IndexOfAny(253, 254, 255) != -1)
@@ -43,7 +43,7 @@ public override void Verify(LegalityAnalysis data)
// Only one of the following can be true: 0, 508, and x%6!=0
if (sum == 0 && !enc.IsWithinEncounterRange(pk))
data.AddLine(Get(LEffortEXPIncreased, Severity.Fishy));
else if (sum == 508)
else if (sum == EffortValues.MaxEffective)
data.AddLine(Get(LEffort2Remaining, Severity.Fishy));
else if (evs[0] != 0 && evs.IndexOfAnyExcept(evs[0]) == -1)
data.AddLine(Get(LEffortAllEqual, Severity.Fishy));

View File

@@ -68,7 +68,7 @@ public override int CurrentFriendship
// Maximums
public override int MaxIV => 31;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int MaxStringLengthOT => 12;
public override int MaxStringLengthNickname => 12;

View File

@@ -285,7 +285,7 @@ private static byte[] DecryptParty(byte[] data)
public override int MaxBallID => Legal.MaxBallID_5;
public override int MaxGameID => Legal.MaxGameID_5; // B2
public override int MaxIV => 31;
public override int MaxEV => 255;
public override int MaxEV => EffortValues.Max255;
public override int MaxStringLengthOT => 7;
public override int MaxStringLengthNickname => 10;
@@ -351,13 +351,13 @@ public PK6 ConvertToPK6()
CNT_Tough = CNT_Tough,
CNT_Sheen = CNT_Sheen,
// Cap EVs
EV_HP = Math.Min(EV_HP, 252),
EV_ATK = Math.Min(EV_ATK, 252),
EV_DEF = Math.Min(EV_DEF, 252),
EV_SPA = Math.Min(EV_SPA, 252),
EV_SPD = Math.Min(EV_SPD, 252),
EV_SPE = Math.Min(EV_SPE, 252),
// Cap EVs -- anything above 252 is dropped down to 252.
EV_HP = Math.Min(EV_HP , EffortValues.Max252),
EV_ATK = Math.Min(EV_ATK, EffortValues.Max252),
EV_DEF = Math.Min(EV_DEF, EffortValues.Max252),
EV_SPA = Math.Min(EV_SPA, EffortValues.Max252),
EV_SPD = Math.Min(EV_SPD, EffortValues.Max252),
EV_SPE = Math.Min(EV_SPE, EffortValues.Max252),
Move1 = Move1,
Move2 = Move2,

View File

@@ -69,7 +69,7 @@ public override int CurrentFriendship
// Maximums
public override int MaxIV => 31;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int MaxStringLengthOT => 12;
public override int MaxStringLengthNickname => 12;

View File

@@ -45,8 +45,8 @@ public static bool SatisfiesFilterLevel(PKM pk, SearchComparison option, int lev
{
1 => pk.EVTotal == 0, // None (0)
2 => pk.EVTotal is (not 0) and < 128, // Some (127-1)
3 => pk.EVTotal is >= 128 and < 508, // Half (128-507)
4 => pk.EVTotal >= 508, // Full (508+)
3 => pk.EVTotal is >= 128 and < EffortValues.MaxEffective, // Half (128-507)
4 => pk.EVTotal >= EffortValues.MaxEffective, // Full (508+)
_ => true,
};

View File

@@ -18,7 +18,7 @@ public abstract class G4PKM : PKM,
public sealed override int MaxBallID => Legal.MaxBallID_4;
public sealed override int MaxGameID => Legal.MaxGameID_4;
public sealed override int MaxIV => 31;
public sealed override int MaxEV => 255;
public sealed override int MaxEV => EffortValues.Max255;
public sealed override int MaxStringLengthOT => 7;
public sealed override int MaxStringLengthNickname => 10;

View File

@@ -44,7 +44,7 @@ public override int CurrentFriendship
// Maximums
public override int MaxIV => 31;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int MaxStringLengthOT => 12;
public override int MaxStringLengthNickname => 12;

View File

@@ -16,7 +16,7 @@ public abstract class GBPKM : PKM
public sealed override int MinGameID => (int)GameVersion.RD;
public sealed override int MaxGameID => (int)GameVersion.C;
public sealed override int MaxIV => 15;
public sealed override int MaxEV => ushort.MaxValue;
public sealed override int MaxEV => EffortValues.Max12;
public sealed override ReadOnlySpan<ushort> ExtraBytes => ReadOnlySpan<ushort>.Empty;

View File

@@ -7,6 +7,19 @@ namespace PKHeX.Core;
/// </summary>
public static class EffortValues
{
/// <summary> Maximum value for a single stat in Generation 1/2 formats. </summary>
public const ushort Max12 = ushort.MaxValue;
/// <summary> Maximum value for a single stat in Generation 6+ formats. </summary>
public const byte Max252 = 252;
/// <summary> Maximum value for a single stat in Generation 3-5 formats. </summary>
public const byte Max255 = 255;
/// <summary> Maximum value for the sum of all stats in Generation 3+ formats. </summary>
public const ushort Max510 = 510;
/// <summary> Since EVs are effective in multiples of 4, the leftover EVs (2) have no impact regardless of stat gained. </summary>
public const ushort MaxEffective = 508;
/// <summary> The leftover EVs if two stats are <see cref="Max252"/>. </summary>
public const byte LeftoverDual252 = 6;
/// <summary>
/// Gets randomized EVs for a given generation format
/// </summary>
@@ -25,8 +38,8 @@ private static void SetRandom252(Span<int> evs, Random rnd)
{
// Set random EVs (max 252 per stat) until we run out of EVs.
// The last stat index receives the remaining EVs
const int maxTotal = 510;
const int maxStat = 252;
const int maxTotal = Max510;
const int maxStat = Max252;
const int maxStatPlusBias = 300; // weight more towards the high end of received EVs
while (true) // loop until we get a valid set of 6 stats
{
@@ -34,7 +47,7 @@ private static void SetRandom252(Span<int> evs, Random rnd)
for (int i = 0; i < evs.Length - 1; i++)
{
var max = Math.Min(maxStatPlusBias, remain);
var amount = rnd.Next(0, max);
var amount = rnd.Next(0, max + 1);
if (amount > maxStat)
amount = maxStat;
remain -= (evs[i] = (byte)amount);
@@ -53,7 +66,7 @@ private static void SetRandom12(Span<int> evs, Random rnd)
{
// In generation 1/2, EVs can be 0-65535.
for (int i = 0; i < evs.Length; i++)
evs[i] = rnd.Next(ushort.MaxValue + 1);
evs[i] = rnd.Next(Max12 + 1);
}
/// <summary>
@@ -76,15 +89,15 @@ private static void SetMax252(Span<int> evs, PKM entity)
Span<(int Index, int Stat)> tuples = stackalloc (int, int)[6];
pi.GetSortedStatIndexes(tuples);
evs[tuples[0].Index] = 252;
evs[tuples[1].Index] = 252;
evs[tuples[2].Index] = 6;
evs[tuples[0].Index] = Max252;
evs[tuples[1].Index] = Max252;
evs[tuples[2].Index] = LeftoverDual252;
}
private static void SetMax12(Span<int> evs)
{
for (int i = 0; i < evs.Length; i++)
evs[i] = ushort.MaxValue;
evs[i] = Max12;
}
/// <summary>
@@ -92,4 +105,40 @@ private static void SetMax12(Span<int> evs)
/// </summary>
/// <param name="evs">Array to store the resulting EVs</param>
public static void Clear(Span<int> evs) => evs.Clear();
public static EffortValueGrade GetGrade(int sum) => sum switch
{
0 => EffortValueGrade.None,
<= 128 => EffortValueGrade.Quarter,
<= 256 => EffortValueGrade.Half,
< MaxEffective => EffortValueGrade.NearFull,
MaxEffective => EffortValueGrade.MaxEffective,
Max510 - 1 => EffortValueGrade.MaxNearCap,
Max510 => EffortValueGrade.MaxLegal,
_ => EffortValueGrade.Illegal,
};
}
/// <summary>
/// Assessment of the total EVs, compared to the maximum allowed.
/// </summary>
public enum EffortValueGrade
{
/// <summary> No EVs </summary>
None,
/// <summary> 1-128 EVs </summary>
Quarter,
/// <summary> 129-256 EVs </summary>
Half,
/// <summary> 257-508 EVs </summary>
NearFull,
/// <summary> 508 EVs </summary>
MaxEffective,
/// <summary> 509 EVs </summary>
MaxNearCap,
/// <summary> 510 EVs </summary>
MaxLegal,
/// <summary> 511+ EVs </summary>
Illegal,
}

View File

@@ -227,7 +227,7 @@ private int GetBoxRawDataOffset(int box)
public override int MaxCoins => 9999;
public override int BoxCount => Japanese ? 8 : 12;
public override int MaxEV => 65535;
public override int MaxEV => EffortValues.Max12;
public override int MaxIV => 15;
public override int Generation => 1;
public override EntityContext Context => EntityContext.Gen1;

View File

@@ -12,7 +12,7 @@ public sealed class SAV1Stadium : SAV_STADIUM
public override string SaveRevisionString => Japanese ? "J" : "U";
public override PersonalTable1 Personal => PersonalTable.Y;
public override int MaxEV => ushort.MaxValue;
public override int MaxEV => EffortValues.Max12;
public override ReadOnlySpan<ushort> HeldItems => Array.Empty<ushort>();
public override GameVersion Version { get; protected set; } = GameVersion.Stadium;

View File

@@ -13,7 +13,7 @@ public sealed class SAV1StadiumJ : SAV_STADIUM
public override string SaveRevisionString => "0"; // so we're different from Japanese SAV1Stadium naming...
public override PersonalTable1 Personal => PersonalTable.Y;
public override int MaxEV => ushort.MaxValue;
public override int MaxEV => EffortValues.Max12;
public override ReadOnlySpan<ushort> HeldItems => Array.Empty<ushort>();
public override GameVersion Version { get; protected set; } = GameVersion.StadiumJ;

View File

@@ -280,7 +280,7 @@ protected override byte[] GetFinalData()
public int EventFlagCount => 2000;
public override int BoxCount => Japanese ? 9 : 14;
public override int MaxEV => 65535;
public override int MaxEV => EffortValues.Max12;
public override int MaxIV => 15;
public override int Generation => 2;
public override EntityContext Context => EntityContext.Gen2;

View File

@@ -185,7 +185,7 @@ public void WriteBothSaveSlots(Span<byte> data)
public sealed override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_RS;
public sealed override int BoxCount => 14;
public sealed override int MaxEV => 255;
public sealed override int MaxEV => EffortValues.Max255;
public sealed override int Generation => 3;
public sealed override EntityContext Context => EntityContext.Gen3;
protected sealed override int GiftCountMax => 1;

View File

@@ -145,7 +145,7 @@ private byte[] GetInnerData()
public override int MaxItemID => Legal.MaxItemID_3_COLO;
public override int MaxGameID => Legal.MaxGameID_3;
public override int MaxEV => 255;
public override int MaxEV => EffortValues.Max255;
public override int Generation => 3;
public override EntityContext Context => EntityContext.Gen3;
protected override int GiftCountMax => 1;

View File

@@ -108,7 +108,7 @@ private byte[] GetInnerData()
public override int MaxBallID => Legal.MaxBallID_3;
public override int MaxGameID => Legal.MaxGameID_3;
public override int MaxEV => 255;
public override int MaxEV => EffortValues.Max255;
public override int Generation => 3;
public override EntityContext Context => EntityContext.Gen3;
protected override int GiftCountMax => 1;

View File

@@ -188,7 +188,7 @@ private byte[] GetInnerData()
public override int MaxItemID => Legal.MaxItemID_3_XD;
public override int MaxGameID => Legal.MaxGameID_3;
public override int MaxEV => 255;
public override int MaxEV => EffortValues.Max255;
public override int Generation => 3;
public override EntityContext Context => EntityContext.Gen3;
protected override int GiftCountMax => 1;

View File

@@ -104,7 +104,7 @@ public int CurrentSlot
public override int MaxBallID => Legal.MaxBallID_4;
public override int MaxGameID => Legal.MaxGameID_4;
public override int MaxEV => 255;
public override int MaxEV => EffortValues.Max255;
public override int Generation => 4;
public override EntityContext Context => EntityContext.Gen4;
protected override int GiftCountMax => 1;

View File

@@ -23,7 +23,7 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37
public override Type PKMType => typeof(PK5);
public override int BoxCount => 24;
public override int MaxEV => 255;
public override int MaxEV => EffortValues.Max255;
public override int Generation => 5;
public override EntityContext Context => EntityContext.Gen5;
public override int MaxStringLengthOT => 7;

View File

@@ -23,7 +23,7 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg
public override Type PKMType => typeof(PK6);
public override int BoxCount => 31;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int Generation => 6;
public override EntityContext Context => EntityContext.Gen6;
protected override int GiftCountMax => 24;

View File

@@ -70,7 +70,7 @@ protected void ReloadBattleTeams()
public override Type PKMType => typeof(PK7);
public override int BoxCount => 32;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int Generation => 7;
public override EntityContext Context => EntityContext.Gen7;
protected override int GiftCountMax => 48;

View File

@@ -74,7 +74,7 @@ private void Initialize()
public override int MaxAbilityID => Legal.MaxAbilityID_7b;
public override int MaxIV => 31;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int MaxStringLengthOT => 12;
public override int MaxStringLengthNickname => 12;
protected override int GiftCountMax => 48;

View File

@@ -106,7 +106,7 @@ private void Initialize()
public override Type PKMType => typeof(PB8);
public override int BoxCount => BoxLayout8b.BoxCount;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int Generation => 8;
public override EntityContext Context => EntityContext.Gen8b;

View File

@@ -60,7 +60,7 @@ public override void CopyChangesFrom(SaveFile sav)
public override PA8 BlankPKM => new();
public override Type PKMType => typeof(PA8);
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int Generation => 8;
public override EntityContext Context => EntityContext.Gen8a;
public override int MaxStringLengthOT => 12;

View File

@@ -147,7 +147,7 @@ private void Initialize()
public override Type PKMType => typeof(PK8);
public override int BoxCount => BoxLayout8.BoxCount;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int Generation => 8;
public override EntityContext Context => EntityContext.Gen8;
public override int MaxStringLengthOT => 12;

View File

@@ -142,7 +142,7 @@ private void Initialize()
public override Type PKMType => typeof(PK9);
public override int BoxCount => BoxLayout9.BoxCount;
public override int MaxEV => 252;
public override int MaxEV => EffortValues.Max252;
public override int Generation => 9;
public override EntityContext Context => EntityContext.Gen9;
public override int MaxStringLengthOT => 12;

View File

@@ -371,11 +371,11 @@ private void UpdateEVTotals()
EVTip.SetToolTip(TB_EVTotal, $"Remaining: {510 - evtotal}");
}
private Color GetEVTotalColor(int evtotal, Color defaultColor) => evtotal switch
private Color GetEVTotalColor(int evtotal, Color defaultColor) => EffortValues.GetGrade(evtotal) switch
{
> 510 => EVsInvalid, // Background turns Red
510 => EVsMaxed, // Maximum EVs
508 => EVsFishy, // Fishy EVs
EffortValueGrade.Illegal => EVsInvalid, // Background turns Red
EffortValueGrade.MaxLegal => EVsMaxed, // Maximum EVs
EffortValueGrade.MaxEffective => EVsFishy, // Fishy EVs
_ => defaultColor,
};

View File

@@ -192,12 +192,12 @@ private void SavePKM(SecretBase6PKM pk)
pk.Nature = WinFormsUtil.GetIndex(CB_Nature);
pk.Gender = EntityGender.GetFromString(Label_Gender.Text);
pk.Form = (byte)CB_Form.SelectedIndex;
pk.EV_HP = Math.Min(Convert.ToInt32(TB_HPEV.Text), 252);
pk.EV_ATK = Math.Min(Convert.ToInt32(TB_ATKEV.Text), 252);
pk.EV_DEF = Math.Min(Convert.ToInt32(TB_DEFEV.Text), 252);
pk.EV_SPA = Math.Min(Convert.ToInt32(TB_SPAEV.Text), 252);
pk.EV_SPD = Math.Min(Convert.ToInt32(TB_SPDEV.Text), 252);
pk.EV_SPE = Math.Min(Convert.ToInt32(TB_SPEEV.Text), 252);
pk.EV_HP = Math.Clamp(Convert.ToInt32(TB_HPEV.Text) , 0, EffortValues.Max252);
pk.EV_ATK = Math.Clamp(Convert.ToInt32(TB_ATKEV.Text), 0, EffortValues.Max252);
pk.EV_DEF = Math.Clamp(Convert.ToInt32(TB_DEFEV.Text), 0, EffortValues.Max252);
pk.EV_SPA = Math.Clamp(Convert.ToInt32(TB_SPAEV.Text), 0, EffortValues.Max252);
pk.EV_SPD = Math.Clamp(Convert.ToInt32(TB_SPDEV.Text), 0, EffortValues.Max252);
pk.EV_SPE = Math.Clamp(Convert.ToInt32(TB_SPEEV.Text), 0, EffortValues.Max252);
pk.Move1 = (ushort)WinFormsUtil.GetIndex(CB_Move1);
pk.Move2 = (ushort)WinFormsUtil.GetIndex(CB_Move2);
pk.Move3 = (ushort)WinFormsUtil.GetIndex(CB_Move3);
@@ -206,7 +206,7 @@ private void SavePKM(SecretBase6PKM pk)
pk.Move2_PPUps = CB_PPu2.SelectedIndex;
pk.Move3_PPUps = CB_PPu3.SelectedIndex;
pk.Move4_PPUps = CB_PPu4.SelectedIndex;
pk.IV_HP = Convert.ToByte(TB_HPIV.Text) & 0x1F;
pk.IV_HP = Convert.ToByte(TB_HPIV.Text) & 0x1F;
pk.IV_ATK = Convert.ToByte(TB_ATKIV.Text) & 0x1F;
pk.IV_DEF = Convert.ToByte(TB_DEFIV.Text) & 0x1F;
pk.IV_SPA = Convert.ToByte(TB_SPAIV.Text) & 0x1F;