Update EffortValueVerifier.cs

Closes #4679
ty @andrebastosdias !

Revise untrained EV check to flag Pokespot encounters (the only varied level range encounter in gen3/4) where min level might be less than the actual level it was obtained at. If/when pokespot correlation is better refined, can switch to evotree level min for a gen3->4 transfer, so that something at a not-minimum exact level can be flagged if it has non-Vitamin EVs.
This commit is contained in:
Kurt 2026-01-02 13:10:17 -06:00
parent 5f65b333ba
commit fb52a5ef18

View File

@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using static PKHeX.Core.LegalityCheckResultCode;
namespace PKHeX.Core;
@ -50,8 +51,8 @@ public override void Verify(LegalityAnalysis data)
private void VerifyGainedEVs34(LegalityAnalysis data, IEncounterTemplate enc, ReadOnlySpan<int> evs, PKM pk)
{
bool anyAbove100 = IsAnyAboveVitaminLimit(evs);
if (!anyAbove100)
var isVitaminResult = IsWithinVitaminRange34(evs, EffortValues.MaxVitamins34);
if (isVitaminResult)
return;
if (enc.LevelMin == Experience.MaxLevel) // only true for Gen4 and Format=4
@ -62,8 +63,15 @@ private void VerifyGainedEVs34(LegalityAnalysis data, IEncounterTemplate enc, Re
}
else // Check for gained EVs without gaining EXP -- don't check Gen5+ which have wings to boost above 100.
{
// Be slightly forgiving for past gen transfers where Met Level is overwritten; we might not be able to infer the exact level met (RNG correlations?).
// Current code won't be able to flag PokéSpot not-min-level-met. If correlation and pruning (evo chain) improved, we can use that level min instead.
// (Wonder if this also impacts wild encounter slots with multiple levels? Hopefully the lowest level is yielded first).
var metLevel = enc.Generation == pk.Format ? pk.MetLevel : enc.LevelMin;
if (metLevel < enc.LevelMin)
metLevel = enc.LevelMin;
var growth = PersonalTable.HGSS[enc.Species].EXPGrowth;
var baseEXP = Experience.GetEXP(enc.LevelMin, growth);
var baseEXP = Experience.GetEXP(metLevel, growth);
if (baseEXP == pk.EXP)
data.AddLine(GetInvalid(EffortUntrainedCap_0, EffortValues.MaxVitamins34));
}
@ -73,7 +81,21 @@ private void VerifyGainedEVs34(LegalityAnalysis data, IEncounterTemplate enc, Re
private static bool IsAnyAboveHardLimit6(ReadOnlySpan<int> evs)
=> evs.ContainsAnyExceptInRange(0, EffortValues.Max252);
// Vitamins can only raise to 100 in Gen3/4
private static bool IsAnyAboveVitaminLimit(ReadOnlySpan<int> evs)
=> evs.ContainsAnyExceptInRange(0, EffortValues.MaxVitamins34);
// Vitamins can only raise to 100 in Gen3/4, only in multiples of 10.
private static bool IsWithinVitaminRange34(ReadOnlySpan<int> evs, [ConstantExpected] ushort limit)
{
foreach (var ev in evs)
{
// The majority of cases will pass here; 0 is default & valid.
// Eager check to skip unnecessary less performant checks.
if (ev is 0)
continue;
if (ev > limit)
return false;
if (ev % 10 != 0)
return false;
}
return true;
}
}