mirror of
https://github.com/kwsch/PKHeX
synced 2024-12-23 19:03:11 +00:00
6441bdadd8
`Moveset` struct stores 4 moves, and exposes methods to interact with a moveset. `IndividualValueSet` stores a 6 IV template (signed). Performance impact: * Less allocating on the heap: Moves - (8 bytes member ptr, 20 bytes heap->8 bytes member) * Less allocating on the heap: IVs - (8 bytes member ptr, 28 bytes heap->8 bytes member) * No heap pointers, no need to jump to grab data. * Easy to inline logic for checking if moves are present (no linq usage with temporary collections). End result is faster ctor times, less memory used, faster program.
133 lines
4.9 KiB
C#
133 lines
4.9 KiB
C#
using System;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace PKHeX.Core;
|
|
|
|
/// <summary>
|
|
/// Logic to verify the current <see cref="PKM.RelearnMoves"/>.
|
|
/// </summary>
|
|
public static class LearnVerifierRelearn
|
|
{
|
|
public static void Verify(Span<MoveResult> result, IEncounterTemplate enc, PKM pk)
|
|
{
|
|
if (ShouldNotHaveRelearnMoves(enc, pk))
|
|
VerifyRelearnNone(pk, result);
|
|
else if (enc is IRelearn {Relearn: {HasMoves: true} x})
|
|
VerifyRelearnSpecifiedMoveset(pk, x, result);
|
|
else if (enc is EncounterEgg e)
|
|
VerifyEggMoveset(e, result, pk.RelearnMoves);
|
|
else if (enc is EncounterSlot6AO { CanDexNav: true } z && pk.RelearnMove1 != 0)
|
|
VerifyRelearnDexNav(pk, result, z);
|
|
else if (enc is EncounterSlot8b { IsUnderground: true } u)
|
|
VerifyRelearnUnderground(pk, result, u);
|
|
else
|
|
VerifyRelearnNone(pk, result);
|
|
}
|
|
|
|
public static bool ShouldNotHaveRelearnMoves(IGeneration enc, PKM pk) => enc.Generation < 6 || pk.IsOriginalMovesetDeleted();
|
|
|
|
private static void VerifyRelearnSpecifiedMoveset(PKM pk, Moveset required, Span<MoveResult> result)
|
|
{
|
|
result[3] = ParseExpect(pk.RelearnMove4, required.Move4);
|
|
result[2] = ParseExpect(pk.RelearnMove3, required.Move3);
|
|
result[1] = ParseExpect(pk.RelearnMove2, required.Move2);
|
|
result[0] = ParseExpect(pk.RelearnMove1, required.Move1);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static MoveResult ParseExpect(int move, int expect = 0)
|
|
{
|
|
if (move != expect)
|
|
return MoveResult.Unobtainable(expect);
|
|
if (move == 0)
|
|
return MoveResult.Empty;
|
|
return MoveResult.Relearn;
|
|
}
|
|
|
|
private static void VerifyRelearnDexNav(PKM pk, Span<MoveResult> result, EncounterSlot6AO slot)
|
|
{
|
|
// All other relearn moves must be empty.
|
|
result[3] = ParseExpect(pk.RelearnMove4);
|
|
result[2] = ParseExpect(pk.RelearnMove3);
|
|
result[1] = ParseExpect(pk.RelearnMove2);
|
|
|
|
// DexNav Pokémon can have 1 random egg move as a relearn move.
|
|
result[0] = slot.CanBeDexNavMove(pk.RelearnMove1) ? MoveResult.Relearn : MoveResult.Unobtainable(); // DexNav
|
|
}
|
|
|
|
private static void VerifyRelearnUnderground(PKM pk, Span<MoveResult> result, EncounterSlot8b slot)
|
|
{
|
|
// All other relearn moves must be empty.
|
|
result[3] = ParseExpect(pk.RelearnMove4);
|
|
result[2] = ParseExpect(pk.RelearnMove3);
|
|
result[1] = ParseExpect(pk.RelearnMove2);
|
|
|
|
// Underground Pokémon can have 1 random egg move as a relearn move.
|
|
result[0] = slot.CanBeUndergroundMove(pk.RelearnMove1) ? MoveResult.Relearn : MoveResult.Unobtainable(); // Underground
|
|
}
|
|
|
|
private static void VerifyRelearnNone(PKM pk, Span<MoveResult> result)
|
|
{
|
|
// No relearn moves should be present.
|
|
result[3] = ParseExpect(pk.RelearnMove4);
|
|
result[2] = ParseExpect(pk.RelearnMove3);
|
|
result[1] = ParseExpect(pk.RelearnMove2);
|
|
result[0] = ParseExpect(pk.RelearnMove1);
|
|
}
|
|
|
|
internal static void VerifyEggMoveset(EncounterEgg e, Span<MoveResult> result, ReadOnlySpan<int> moves)
|
|
{
|
|
int gen = e.Generation;
|
|
Span<byte> origins = stackalloc byte[moves.Length];
|
|
var valid = MoveBreed.Validate(gen, e.Species, e.Form, e.Version, moves, origins);
|
|
if (valid)
|
|
{
|
|
for (int i = result.Length - 1; i >= 0; i--)
|
|
{
|
|
if (moves[i] == 0)
|
|
result[i] = MoveResult.Empty;
|
|
else
|
|
result[i] = new(EggSourceUtil.GetSource(origins[i], gen));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Span<int> expected = stackalloc int[moves.Length];
|
|
_ = MoveBreed.GetExpectedMoves(moves, e, expected);
|
|
_ = MoveBreed.Validate(gen, e.Species, e.Form, e.Version, expected, origins);
|
|
for (int i = moves.Length - 1; i >= 0; i--)
|
|
{
|
|
var current = moves[i];
|
|
var expect = expected[i];
|
|
if (current != expect)
|
|
result[i] = MoveResult.Unobtainable(expect);
|
|
else if (current == 0)
|
|
result[i] = MoveResult.Empty;
|
|
else
|
|
result[i] = new(EggSourceUtil.GetSource(origins[i], gen));
|
|
}
|
|
}
|
|
|
|
var dupe = IsAnyMoveDuplicate(moves);
|
|
if (dupe != NO_DUPE)
|
|
result[dupe] = MoveResult.Duplicate;
|
|
}
|
|
|
|
private const int NO_DUPE = -1;
|
|
|
|
private static int IsAnyMoveDuplicate(ReadOnlySpan<int> move)
|
|
{
|
|
int m1 = move[0];
|
|
int m2 = move[1];
|
|
|
|
if (m1 != 0 && m1 == m2)
|
|
return 1;
|
|
int m3 = move[2];
|
|
if (m3 != 0 && (m1 == m3 || m2 == m3))
|
|
return 2;
|
|
int m4 = move[3];
|
|
if (m4 != 0 && (m1 == m4 || m2 == m4 || m3 == m4))
|
|
return 3;
|
|
return NO_DUPE;
|
|
}
|
|
}
|