Handle "random" AV gains from level up

This commit is contained in:
Kurt 2022-07-10 15:53:33 -07:00
parent 45af203a26
commit 3dbf46be92
8 changed files with 127 additions and 49 deletions

View file

@ -15,8 +15,8 @@ public static class BatchMods
new TypeSuggestion<IScaledSizeValue>(nameof(IScaledSizeValue.WeightAbsolute), p => p.ResetWeight()),
new TypeSuggestion<IHyperTrain>(nameof(Extensions.HyperTrainClear), p => p.HyperTrainClear()),
new TypeSuggestion<IGeoTrack>(nameof(Extensions.ClearGeoLocationData), p => p.ClearGeoLocationData()),
new TypeSuggestion<IAwakened>(nameof(Extensions.AwakeningClear), p => p.AwakeningClear()),
new TypeSuggestion<IAwakened>(nameof(Extensions.AwakeningMax), p => p.AwakeningMax()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningClear), p => p.AwakeningClear()),
new TypeSuggestion<IAwakened>(nameof(AwakeningUtil.AwakeningMax), p => p.AwakeningMax()),
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.ClearGanbaruValues), p => p.ClearGanbaruValues()),
new TypeSuggestion<IGanbaru>(nameof(GanbaruExtensions.SetSuggestedGanbaruValues), p => p.SetSuggestedGanbaruValues((PKM)p)),

View file

@ -153,4 +153,9 @@ public enum CheckIdentifier : byte
/// The <see cref="CheckResult"/> pertains to <see cref="PKM.MarkValue"/> values.
/// </summary>
Marking,
/// <summary>
/// The <see cref="CheckResult"/> pertains to the <see cref="PKM"/> <see cref="IAwakened"/> values.
/// </summary>
AVs,
}

View file

@ -1,4 +1,4 @@
#define SUPPRESS
#define SUPPRESS
using System;
using System.Collections.Generic;
@ -229,6 +229,8 @@ public sealed class LegalityAnalysis
UpdateChecks();
if (Entity.Format >= 8)
Transfer.VerifyTransferLegalityG8(this);
else if (Entity is PB7)
Awakening.Verify(this);
}
private void ParsePK8()

View file

@ -1,4 +1,4 @@
namespace PKHeX.Core;
namespace PKHeX.Core;
/// <summary>
/// Collection of analyzers that are used for parsing secondary details.
@ -34,4 +34,5 @@ internal static class LegalityAnalyzers
public static readonly TransferVerifier Transfer = new();
public static readonly MarkVerifier Mark = new();
public static readonly LegendsArceusVerifier Arceus = new();
public static readonly AwakenedValueVerifier Awakening = new();
}

View file

@ -0,0 +1,42 @@
using System;
namespace PKHeX.Core;
public sealed class AwakenedValueVerifier : Verifier
{
protected override CheckIdentifier Identifier => CheckIdentifier.AVs;
public override void Verify(LegalityAnalysis data)
{
if (data.Entity is not PB7 pb7)
return;
int sum = pb7.EVTotal;
if (sum != 0)
data.AddLine(GetInvalid(LegalityCheckStrings.LEffortShouldBeZero));
if (!pb7.AwakeningAllValid())
data.AddLine(GetInvalid(LegalityCheckStrings.LAwakenedCap));
Span<byte> required = stackalloc byte[6];
AwakeningUtil.GetExpectedMinimumAVs(required, pb7);
ReadOnlySpan<byte> current = stackalloc byte[6]
{
pb7.GetAV(0),
pb7.GetAV(1),
pb7.GetAV(2),
pb7.GetAV(4),
pb7.GetAV(5),
pb7.GetAV(3), // Speed last!
};
for (int i = 0; i < required.Length; i++)
{
if (current[i] >= required[i])
continue;
data.AddLine(GetInvalid(string.Format(LegalityCheckStrings.LAwakenedShouldBeValue, required[i])));
break;
}
}
}

View file

@ -13,11 +13,6 @@ public sealed class EffortValueVerifier : Verifier
public override void Verify(LegalityAnalysis data)
{
var pk = data.Entity;
if (pk is IAwakened a)
{
VerifyAwakenedValues(data, a);
return;
}
var enc = data.EncounterMatch;
if (pk.IsEgg)
{
@ -65,36 +60,4 @@ public sealed class EffortValueVerifier : Verifier
else if (evs[0] != 0 && evs.Count(evs[0]) == evs.Length)
data.AddLine(Get(LEffortAllEqual, Severity.Fishy));
}
private void VerifyAwakenedValues(LegalityAnalysis data, IAwakened awakened)
{
var pk = data.Entity;
int sum = pk.EVTotal;
if (sum != 0)
data.AddLine(GetInvalid(LEffortShouldBeZero));
if (!awakened.AwakeningAllValid())
data.AddLine(GetInvalid(LAwakenedCap));
var enc = data.EncounterMatch;
// go park transfers have 2 AVs for all stats.
if (enc is EncounterSlot7GO)
{
Span<byte> avs = stackalloc byte[6];
awakened.GetAVs(avs);
foreach (var av in avs)
{
if (av >= 2)
continue;
data.AddLine(GetInvalid(string.Format(LAwakenedShouldBeValue, 2)));
break;
}
return;
}
if (awakened.AwakeningSum() == 0 && !enc.IsWithinEncounterRange(pk))
data.AddLine(Get(LAwakenedEXPIncreased, Severity.Fishy));
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
namespace PKHeX.Core;
@ -12,7 +12,7 @@ public interface IAwakened
byte AV_SPD { get; set; }
}
public static partial class Extensions
public static class AwakeningUtil
{
/// <summary>
/// Sums all values.
@ -47,10 +47,26 @@ public static partial class Extensions
/// <param name="max">Maximum value to set</param>
public static void AwakeningSetRandom(this IAwakened pk, byte min = 0, int max = Legal.AwakeningMax)
{
if (pk is not PB7 pb7)
return;
Span<byte> result = stackalloc byte[6];
GetExpectedMinimumAVs(result, pb7);
var rnd = Util.Rand;
int randClamp = max + 1;
for (int index = 0; index < 6; index++)
pk.SetAV(index, (byte)rnd.Next(min, randClamp));
for (int i = 0; i < 6; i++)
result[i] = (byte)rnd.Next(result[i], randClamp);
AwakeningSetVisual(pb7, result);
}
public static void AwakeningSetVisual(IAwakened pk, Span<byte> result)
{
pk.AV_HP = result[0];
pk.AV_ATK = result[1];
pk.AV_DEF = result[2];
pk.AV_SPA = result[3];
pk.AV_SPD = result[4];
pk.AV_SPE = result[5];
}
/// <summary>
@ -96,7 +112,7 @@ public static partial class Extensions
/// </summary>
/// <param name="pk">Pokémon to check.</param>
/// <param name="index">Index to get</param>
public static int GetAV(this IAwakened pk, int index) => index switch
public static byte GetAV(this IAwakened pk, int index) => index switch
{
0 => pk.AV_HP,
1 => pk.AV_ATK,
@ -129,12 +145,14 @@ public static partial class Extensions
/// <param name="pk">Retriever for IVs</param>
public static void SetSuggestedAwakenedValues(this IAwakened a, PKM pk)
{
Span<byte> result = stackalloc byte[6];
GetExpectedMinimumAVs(result, (PB7)a);
a.AV_HP = Legal.AwakeningMax;
a.AV_ATK = pk.IV_ATK == 0 ? (byte)0 : Legal.AwakeningMax;
a.AV_ATK = pk.IV_ATK == 0 ? result[1] : Legal.AwakeningMax;
a.AV_DEF = Legal.AwakeningMax;
a.AV_SPE = pk.IV_SPE == 0 ? (byte)0 : Legal.AwakeningMax;
a.AV_SPA = Legal.AwakeningMax;
a.AV_SPD = Legal.AwakeningMax;
a.AV_SPE = pk.IV_SPE == 0 ? result[5] : Legal.AwakeningMax;
}
public static bool IsAwakeningBelow(this IAwakened current, IAwakened initial) => !current.IsAwakeningAboveOrEqual(initial);
@ -155,4 +173,26 @@ public static partial class Extensions
return false;
return true;
}
public static void GetExpectedMinimumAVs(Span<byte> result, PB7 pk)
{
// go park transfers have 2 AVs for all stats.
// leveling up in-game applies 1 AV to a "random" index.
if (pk.Version == (int)GameVersion.GO)
result.Fill(2);
var nature = pk.Nature;
var character = pk.Characteristic;
var ec = pk.EncryptionConstant;
var start = pk.Met_Level;
var end = pk.CurrentLevel;
for (int i = start + 1; i <= end; i++)
{
var lm10 = i % 10;
var bits = (ec >> (3 * lm10)) & 7;
var index = PB7.GetRandomIndex((int)bits, character, nature);
++result[index];
}
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using static System.Buffers.Binary.BinaryPrimitives;
@ -585,6 +585,31 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
return (byte)Math.Min(255, unsigned);
}
public static int GetRandomIndex(int bits, int characterIndex, int nature)
{
if (bits is 6 or 7)
return GetRandomIndex(characterIndex);
if (bits is 0)
return 0;
Span<sbyte> amps = NatureAmpTable.AsSpan(5 * nature, 5);
if (amps[bits - 1] != -1) // not a negative stat
return bits;
// remap a negative stat to positive
return 1 + amps.IndexOf((sbyte)1);
}
private static int GetRandomIndex(int characterIndex) => (characterIndex / 5) switch
{
0 => 0,
1 => 1,
2 => 2,
3 => 5,
4 => 3,
5 => 4,
_ => throw new ArgumentOutOfRangeException(nameof(characterIndex)), // never happens, characteristic is always 0-29
};
public PK8 ConvertToPK8()
{
var pk8 = new PK8