PKHeX/PKHeX.Core/Legality/Verifiers/EffortValueVerifier.cs
Kurt f632aedd15
Encounter Templates: Searching and Creating (#3955)
We implement simple state machine iterators to iterate through every split type encounter array, and more finely control the path we iterate through. And, by using generics, we can have the compiler generate optimized code to avoid virtual calls.

In addition to this, we shift away from the big-5 encounter types and not inherit from an abstract class. This allows for creating a PK* of a specific type and directly writing properties (no virtual calls). Plus we can now fine-tune each encounter type to call specific code, and not have to worry about future game encounter types bothering the generation routines.
2023-08-12 16:01:16 -07:00

68 lines
2.7 KiB
C#

using System;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core;
/// <summary>
/// Verifies the <see cref="EffortValues"/>.
/// </summary>
public sealed class EffortValueVerifier : Verifier
{
protected override CheckIdentifier Identifier => CheckIdentifier.EVs;
private const int totalMax = 510; // Total Max
private const int vitaMax = 100; // Vitamin Max for consideration in Gen3 & Gen4.
public override void Verify(LegalityAnalysis data)
{
var pk = data.Entity;
var enc = data.EncounterMatch;
if (pk.IsEgg)
{
if (pk.EVTotal is not 0)
data.AddLine(GetInvalid(LEffortEgg));
return;
}
// 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 = pk.Format;
if (format < 3) // can abuse daycare for EV training without EXP gain
return;
int sum = pk.EVTotal;
if (sum > totalMax) // format >= 3
data.AddLine(GetInvalid(LEffortAbove510));
Span<int> evs = stackalloc int[6];
pk.GetEVs(evs);
if (format >= 6 && evs.IndexOfAny(253, 254, 255) != -1)
data.AddLine(GetInvalid(LEffortAbove252));
else if (format < 5) // 3/4
VerifyGainedEVs34(data, enc, evs, pk);
// 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)
data.AddLine(Get(LEffort2Remaining, Severity.Fishy));
else if (evs[0] != 0 && evs.IndexOfAnyExcept(evs[0]) == -1)
data.AddLine(Get(LEffortAllEqual, Severity.Fishy));
}
private void VerifyGainedEVs34(LegalityAnalysis data, IEncounterTemplate enc, Span<int> evs, PKM pk)
{
if (enc.LevelMin == 100) // only true for Gen4 and Format=4
{
// Cannot EV train at level 100 -- Certain events are distributed at level 100.
if (evs.Find(static ev => ev > vitaMax) != default) // 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[enc.Species].EXPGrowth;
var baseEXP = Experience.GetEXP(enc.LevelMin, growth);
if (baseEXP == pk.EXP && evs.Find(static ev => ev > vitaMax) != default)
data.AddLine(GetInvalid(string.Format(LEffortUntrainedCap, vitaMax)));
}
}
}