PKHeX/PKHeX.Core/PKM/Util/EffortValues.cs
Kurt 95fbf66a6e
Refactor: Gen3/4 Lead Encounters, property fixing (#4193)
In addition to the Method 1 (and other sibling PIDIV types) correlation, an encounter can only be triggered if the calls prior land on the Method {1} seed. The RNG community has dubbed these patterns as "Method J" (D/P/Pt), "Method K" (HG/SS), and "Method H" (Gen3, coined by yours truly). The basic gist of these is that they are pre-requisites, like the Shadow locks of Colosseum/XD. 

Rename/re-type a bunch of properties to get the codebase more in line with correct property names & more obvious underlying types.
2024-02-22 21:20:54 -06:00

146 lines
4.8 KiB
C#

using System;
namespace PKHeX.Core;
/// <summary>
/// Logic for working with Effort Values
/// </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> Vitamin Max for consideration in Gen3 & Gen4. </summary>
public const ushort MaxVitamins34 = 100;
/// <summary>
/// Gets randomized EVs for a given generation format
/// </summary>
/// <param name="evs">Array to store the resulting EVs</param>
/// <param name="generation">Generation specific formatting option</param>
public static void SetRandom(Span<int> evs, byte generation)
{
var rnd = Util.Rand;
if (generation > 2)
SetRandom252(evs, rnd);
else
SetRandom12(evs, rnd);
}
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 = 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
{
int remain = maxTotal;
for (int i = 0; i < evs.Length - 1; i++)
{
var max = Math.Min(maxStatPlusBias, remain);
var amount = rnd.Next(0, max + 1);
if (amount > maxStat)
amount = maxStat;
remain -= (evs[i] = (byte)amount);
}
if (remain > maxStat)
continue; // try again! must have had really low rand rolls.
evs[5] = remain;
break; // done!
}
rnd.Shuffle(evs);
}
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(Max12 + 1);
}
/// <summary>
/// Sets the Effort Values to a reasonable max value.
/// </summary>
/// <param name="evs">Array to store the resulting EVs</param>
/// <param name="entity">Entity that will eventually receive the EVs</param>
public static void SetMax(Span<int> evs, PKM entity)
{
if (entity.Format < 3)
SetMax12(evs);
else
SetMax252(evs, entity);
}
private static void SetMax252(Span<int> evs, PKM entity)
{
// Get the 3 highest base stat indexes from the entity PersonalInfo
var pi = entity.PersonalInfo;
Span<(int Index, int Stat)> tuples = stackalloc (int, int)[6];
pi.GetSortedStatIndexes(tuples);
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] = Max12;
}
/// <summary>
/// Sets all the EVs to zero.
/// </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,
}