Create initial movesets for Alpha entities correctly, verify initial mastery (#3489)

* Draft checks for encounter slot mastery

* Check encounter mastery flags

* Add moves for LA static encounters that don't follow learnset

* Add moves on crossover LA static encounters

* add alpha moveset population method

Now generates and applies moves as the game does
Updates some handling of other methods to use Span

* Show better message for bad mastery init flags

* Insert descending if candidates have same level

Level 78 Yanmega:
- [01] [10] Quick Attack
- [06] [15] Gust
- [11] [20] Silver Wind
- [18] [28] Hypnosis
- [25] [35] Air Slash
- [34] [45] Ancient Power
- [43] [54] Crunch
- [43] [54] Bug Buzz

Yields:
AlphaMove
Crunch*
Bug Buzz*
Ancient Power

* Descending order due to iteration

Co-authored-by: Lusamine <30205550+Lusamine@users.noreply.github.com>
This commit is contained in:
Kurt
2022-05-06 15:43:23 -07:00
committed by GitHub
parent 232c65ed3a
commit 9deafa851a
17 changed files with 264 additions and 111 deletions

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@@ -56,12 +55,12 @@ public static void SetMoves(this PKM pk, int[] moves, bool maxPP = false)
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="moves"><see cref="PKM.Moves"/> to use (if already known). Will fetch the current <see cref="PKM.Moves"/> if not provided.</param>
public static void SetMaximumPPCurrent(this PKM pk, IReadOnlyList<int> moves)
public static void SetMaximumPPCurrent(this PKM pk, ReadOnlySpan<int> moves)
{
pk.Move1_PP = moves.Count == 0 ? 0 : pk.GetMovePP(moves[0], pk.Move1_PPUps);
pk.Move2_PP = moves.Count <= 1 ? 0 : pk.GetMovePP(moves[1], pk.Move2_PPUps);
pk.Move3_PP = moves.Count <= 2 ? 0 : pk.GetMovePP(moves[2], pk.Move3_PPUps);
pk.Move4_PP = moves.Count <= 3 ? 0 : pk.GetMovePP(moves[3], pk.Move4_PPUps);
pk.Move1_PP = moves.Length == 0 ? 0 : pk.GetMovePP(moves[0], pk.Move1_PPUps);
pk.Move2_PP = moves.Length <= 1 ? 0 : pk.GetMovePP(moves[1], pk.Move2_PPUps);
pk.Move3_PP = moves.Length <= 2 ? 0 : pk.GetMovePP(moves[2], pk.Move3_PPUps);
pk.Move4_PP = moves.Length <= 3 ? 0 : pk.GetMovePP(moves[3], pk.Move4_PPUps);
}
/// <summary>

View File

@@ -54,6 +54,7 @@ public static partial class Legal
internal static readonly EggMoves6[] EggMovesBDSP = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_bdsp.pkl"), "bs"));
internal static readonly Learnset[] LevelUpBDSP = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_bdsp.pkl"), "bs"));
internal static readonly Learnset[] LevelUpLA = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_la.pkl"), "la"));
internal static readonly Learnset[] MasteryLA = LearnsetReader.GetArray(Get(Util.GetBinaryResource("mastery_la.pkl"), "la"));
public static IReadOnlyList<byte> GetPPTable(PKM pkm, int format) => format switch
{

View File

@@ -25,16 +25,16 @@ internal static class Encounters8a
new(493,000,75,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Method = Fixed, Ball = (int)Ball.LAPoke, Fateful = true }, // Arceus
// Static Encounters - Scripted Table Slots
new(480,000,70,M,M) { Location = 111, FlawlessIVCount = 3 }, // Uxie
new(481,000,70,M,M) { Location = 104, FlawlessIVCount = 3 }, // Mesprit
new(482,000,70,M,M) { Location = 105, FlawlessIVCount = 3 }, // Azelf
new(485,000,70,M,M) { Location = 068, FlawlessIVCount = 3 }, // Heatran
new(488,000,70,M,M) { Location = 082, FlawlessIVCount = 3 }, // Cresselia
new(480,000,70,M,M) { Location = 111, FlawlessIVCount = 3, Moves = new[] {129,326,832,095} }, // Uxie
new(481,000,70,M,M) { Location = 104, FlawlessIVCount = 3, Moves = new[] {129,326,832,105} }, // Mesprit
new(482,000,70,M,M) { Location = 105, FlawlessIVCount = 3, Moves = new[] {129,458,326,832} }, // Azelf
new(485,000,70,M,M) { Location = 068, FlawlessIVCount = 3, Moves = new[] {442,242,414,463} }, // Heatran
new(488,000,70,M,M) { Location = 082, FlawlessIVCount = 3, Moves = new[] {427,094,585,849} }, // Cresselia
new(641,000,70,M,M) { Location = 090, FlawlessIVCount = 3 }, // Tornadus
new(642,000,70,M,M) { Location = 009, FlawlessIVCount = 3 }, // Thundurus
new(645,000,70,M,M) { Location = 027, FlawlessIVCount = 3 }, // Landorus
new(905,000,70,M,M) { Location = 038, FlawlessIVCount = 3 }, // Enamorus
new(641,000,70,M,M) { Location = 090, FlawlessIVCount = 3, Moves = new[] {326,242,542,846} }, // Tornadus
new(642,000,70,M,M) { Location = 009, FlawlessIVCount = 3, Moves = new[] {326,242,087,847} }, // Thundurus
new(645,000,70,M,M) { Location = 027, FlawlessIVCount = 3, Moves = new[] {326,242,414,848} }, // Landorus
new(905,000,70,M,M) { Location = 038, FlawlessIVCount = 3, Moves = new[] {326,242,585,831} }, // Enamorus
new(077,000,15 ) { Location = 014, Shiny = Always}, // Ponyta*
new(442,000,60,M,M) { Location = 043, FlawlessIVCount = 3 }, // Spiritomb
@@ -42,13 +42,13 @@ internal static class Encounters8a
new(570,001,28 ) { Location = 027 }, // Zorua
new(570,001,29 ) { Location = 027 }, // Zorua
new(489,000,33 ) { Location = 064, Fateful = true }, // Phione
new(489,000,34 ) { Location = 064, Fateful = true }, // Phione
new(489,000,35 ) { Location = 064, Fateful = true }, // Phione
new(489,000,36 ) { Location = 064, Fateful = true }, // Phione
new(490,000,50,M,M) { Location = 064, FlawlessIVCount = 3, Fateful = true }, // Manaphy
new(491,000,70,M,M) { Location = 010, FlawlessIVCount = 3, Fateful = true }, // Darkrai
new(492,000,70,M,M) { Location = 026, FlawlessIVCount = 3, Fateful = true }, // Shaymin
new(489,000,33 ) { Location = 064, Fateful = true, Moves = new[] {145,352,151,428} }, // Phione
new(489,000,34 ) { Location = 064, Fateful = true, Moves = new[] {145,352,151,428} }, // Phione
new(489,000,35 ) { Location = 064, Fateful = true, Moves = new[] {145,352,151,428} }, // Phione
new(489,000,36 ) { Location = 064, Fateful = true, Moves = new[] {145,352,151,428} }, // Phione
new(490,000,50,M,M) { Location = 064, FlawlessIVCount = 3, Fateful = true, Moves = new[] {352,428,585,145} }, // Manaphy
new(491,000,70,M,M) { Location = 010, FlawlessIVCount = 3, Fateful = true, Moves = new[] {506,399,094,464} }, // Darkrai
new(492,000,70,M,M) { Location = 026, FlawlessIVCount = 3, Fateful = true, Moves = new[] {403,412,414,465} }, // Shaymin
// Unown Notes
new(201,000,25,U) { Location = 040, Method = Fixed }, // Unown A
@@ -88,10 +88,10 @@ internal static class Encounters8a
new(201,024,25,U) { Location = 097, Method = Fixed }, // Unown Y
new(201,006,25,U) { Location = 007, Method = Fixed }, // Unown G
new(642,000,70,M,M) { Location = 059, FlawlessIVCount = 3 }, // Thundurus (Lunkers Lair)
new(642,000,70,M,M) { Location = 129, FlawlessIVCount = 3 }, // Thundurus (Sands Reach)
new(488,000,70,M,M) { Location = 010, FlawlessIVCount = 3 }, // Cresselia (Coronet Highlands)
new(491,000,70,M,M) { Location = 074, FlawlessIVCount = 3, Fateful = true }, // Darkrai (Lonely Spring)
new(642,000,70,M,M) { Location = 059, FlawlessIVCount = 3, Moves = new[] {326,242,087,847} }, // Thundurus (Lunkers Lair)
new(642,000,70,M,M) { Location = 129, FlawlessIVCount = 3, Moves = new[] {326,242,087,847} }, // Thundurus (Sands Reach)
new(488,000,70,M,M) { Location = 010, FlawlessIVCount = 3, Moves = new[] {427,094,585,849} }, // Cresselia (Coronet Highlands)
new(491,000,70,M,M) { Location = 074, FlawlessIVCount = 3, Fateful = true, Moves = new[] {506,399,094,464} }, // Darkrai (Lonely Spring)
// Static Encounters
new(046,000,50,M,M) { Location = 019, Method = Fixed }, // paras01: Paras
@@ -106,11 +106,11 @@ internal static class Encounters8a
new(486,000,70,M,M) { Location = 095, Method = Fixed, FlawlessIVCount = 3 }, // regigigas01: Regigigas
new(487,001,70,M,M) { Location = 067, Method = Fixed, FlawlessIVCount = 3 }, // giratina02: Giratina-1
new(362,000,64,A,A) { Location = 011, Method = Fixed, IsAlpha = true, Moves = new[] {442,059,556,242}, Mastery = new[] {true,true,true, true } }, // onigohri01: Glalie
new(402,000,12,A,A) { Location = 007, Method = Fixed, IsAlpha = true, Gender = 0, Moves = new[] {206,071,033,332}, Mastery = new[] {true,true,false,false} }, // mev002: Kricketune
new(416,000,60,A,A) { Location = 022, Method = Fixed, IsAlpha = true, Gender = 1, FlawlessIVCount = 3, Moves = new[] {188,403,408,405}, Mastery = new[] {true,true,true ,true } }, // beequen01: Vespiquen
new(571,001,58,M,M) { Location = 111, Method = Fixed, IsAlpha = true, Moves = new[] {555,421,841,417}, Mastery = new[] {true,true,true ,true } }, // zoroark01: Zoroark-1
new(706,001,58,M,M) { Location = 104, Method = Fixed, IsAlpha = true, Moves = new[] {231,406,842,056}, Mastery = new[] {true,true,true ,true } }, // numelgon01: Goodra-1
new(904,000,58,M,M) { Location = 105, Method = Fixed, IsAlpha = true, Moves = new[] {301,398,401,038}, Mastery = new[] {true,true,true ,false} }, // harysen01: Overqwil
new(362,000,64,A,A) { Location = 011, Method = Fixed, IsAlpha = true, Moves = new[] {442,059,556,242} }, // onigohri01: Glalie
new(402,000,12,A,A) { Location = 007, Method = Fixed, IsAlpha = true, Gender = 0, Moves = new[] {206,071,033,332} }, // mev002: Kricketune
new(416,000,60,A,A) { Location = 022, Method = Fixed, IsAlpha = true, Gender = 1, FlawlessIVCount = 3, Moves = new[] {188,403,408,405} }, // beequen01: Vespiquen
new(571,001,58,M,M) { Location = 111, Method = Fixed, IsAlpha = true, Moves = new[] {555,421,841,417} }, // zoroark01: Zoroark-1
new(706,001,58,M,M) { Location = 104, Method = Fixed, IsAlpha = true, Moves = new[] {231,406,842,056} }, // numelgon01: Goodra-1
new(904,000,58,M,M) { Location = 105, Method = Fixed, IsAlpha = true, Moves = new[] {301,398,401,038} }, // harysen01: Overqwil
};
}

View File

@@ -18,7 +18,7 @@ internal sealed record EncounterSlot3Swarm : EncounterSlot3, IMoveset
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var moves = Moves;
var moves = (int[])Moves;
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View File

@@ -6,7 +6,7 @@ namespace PKHeX.Core;
/// Encounter Slot found in <see cref="GameVersion.SWSH"/>.
/// </summary>
/// <inheritdoc cref="EncounterSlot"/>
public sealed record EncounterSlot8a : EncounterSlot, IAlpha
public sealed record EncounterSlot8a : EncounterSlot, IAlpha, IMasteryInitialMoveShop8
{
public override int Generation => 8;
public SlotType Type => Area.Type;
@@ -25,9 +25,9 @@ public EncounterSlot8a(EncounterArea8a area, ushort species, byte form, byte min
public bool HasAlphaMove => IsAlpha && Type is not SlotType.Landmark;
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
{
base.ApplyDetails(sav, criteria, pk);
base.SetPINGA(pk, criteria);
if (Gender != Gender.Random)
pk.Gender = (int)Gender;
@@ -35,20 +35,39 @@ protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteri
var (_, slotSeed) = Overworld8aRNG.ApplyDetails(pk, criteria, para, HasAlphaMove);
if (LevelMin != LevelMax)
pk.CurrentLevel = pk.Met_Level = Overworld8aRNG.GetRandomLevel(slotSeed, LevelMin, LevelMax);
}
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var pa8 = (PA8)pk;
Span<int> moves = stackalloc int[4];
var index = PersonalTable.LA.GetFormIndex(Species, Form);
var mastery = Legal.MasteryLA[index];
var learn = Legal.LevelUpLA[index];
if (pa8.AlphaMove != 0)
{
moves[0] = pa8.AlphaMove;
learn.SetEncounterMovesBackwards(level, moves, 1);
}
else
{
learn.SetEncounterMoves(level, moves);
}
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
foreach (var move in moves)
{
if (mastery.GetMoveLevel(move) <= level)
pa8.SetMasteryFlagMove(move);
}
}
protected override void SetFormatSpecificData(PKM pk)
{
var pa8 = (PA8)pk;
if (IsAlpha)
{
if (pk is IAlpha a)
a.IsAlpha = true;
if (pk is PA8 { AlphaMove: not 0 } pa)
pk.PushMove(pa.AlphaMove);
}
if (pk is PA8 pa8)
{
pa8.HeightScalarCopy = pa8.HeightScalar;
pa8.SetMasteryFlags();
}
pa8.IsAlpha = true;
pa8.HeightScalarCopy = pa8.HeightScalar;
}
protected override void ApplyDetailsBall(PKM pk) => pk.Ball = (int)Ball.LAPoke;
@@ -71,8 +90,10 @@ private EncounterMatchRating GetMatchRatingInternal(PKM pkm)
return EncounterMatchRating.DeferredErrors;
if (IsFormArgMismatch(pkm))
return EncounterMatchRating.DeferredErrors;
if (!IsForcedMasteryCorrect(pkm))
return EncounterMatchRating.DeferredErrors;
return GetAlphaMoveCompatibility(pkm);
return GetMoveCompatibility(pkm);
}
private bool IsFormArgMismatch(PKM pkm) => pkm.Species switch
@@ -83,7 +104,7 @@ private EncounterMatchRating GetMatchRatingInternal(PKM pkm)
_ => false,
};
private EncounterMatchRating GetAlphaMoveCompatibility(PKM pkm)
private EncounterMatchRating GetMoveCompatibility(PKM pkm)
{
// Check for Alpha move compatibility.
if (pkm is not PA8 pa)
@@ -115,6 +136,30 @@ private EncounterMatchRating GetAlphaMoveCompatibility(PKM pkm)
return EncounterMatchRating.Match;
}
public bool IsForcedMasteryCorrect(PKM pkm)
{
if (pkm is not IMoveShop8Mastery p)
return true; // Can't check.
Span<int> moves = stackalloc int[4];
var level = pkm.Met_Level;
var index = PersonalTable.LA.GetFormIndex(Species, Form);
var mastery = Legal.MasteryLA[index];
var learn = Legal.LevelUpLA[index];
ushort alpha = 0;
if (pkm is PA8 { AlphaMove: not 0 } pa8)
{
moves[0] = alpha = pa8.AlphaMove;
learn.SetEncounterMovesBackwards(level, moves, 1);
}
else
{
learn.SetEncounterMoves(level, moves);
}
return p.IsValidMasteredEncounter(moves, mastery, level, alpha);
}
private OverworldParam8a GetParams()
{
var pt = PersonalTable.LA;

View File

@@ -156,9 +156,9 @@ protected virtual void SetMetData(PKM pk, int level, DateTime today)
pk.MetDate = today;
}
private void SetEncounterMoves(PKM pk, GameVersion version, int level)
protected virtual void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var moves = Moves.Count > 0 ? Moves : MoveLevelUp.GetEncounterMoves(pk, level, version);
var moves = Moves.Count > 0 ? (int[])Moves : MoveLevelUp.GetEncounterMoves(pk, level, version);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View File

@@ -6,9 +6,8 @@ namespace PKHeX.Core;
/// Generation 8 Static Encounter
/// </summary>
/// <inheritdoc cref="EncounterStatic"/>
public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Version), IAlpha
public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Version), IAlpha, IMasteryInitialMoveShop8
{
public bool[]? Mastery;
public override int Generation => 8;
public byte HeightScalar { get; }
@@ -33,31 +32,20 @@ public EncounterStatic8a(ushort species, ushort form, byte level, byte h = NoSca
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
{
base.ApplyDetails(sav, criteria, pk);
if (pk is IScaledSize s)
{
if (HasFixedHeight)
s.HeightScalar = HeightScalar;
if (HasFixedWeight)
s.WeightScalar = WeightScalar;
if (pk is IScaledSizeValue v)
{
v.ResetHeight();
v.ResetWeight();
}
}
if (IsAlpha && pk is IAlpha a)
a.IsAlpha = true;
var pa = (PA8)pk;
if (pk is PA8 pa)
{
if (IsAlpha && Moves.Count != 0)
pa.AlphaMove = (ushort)Moves[0];
if (pa.AlphaMove != 0)
pk.PushMove(pa.AlphaMove);
pa.SetMasteryFlags();
pa.HeightScalarCopy = pa.HeightScalar;
}
if (IsAlpha)
pa.IsAlpha = true;
if (HasFixedHeight)
pa.HeightScalar = HeightScalar;
if (HasFixedWeight)
pa.WeightScalar = WeightScalar;
pa.HeightScalarCopy = pa.HeightScalar;
pa.ResetHeight();
pa.ResetWeight();
}
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
@@ -97,9 +85,6 @@ public override bool IsMatchExact(PKM pkm, IDexLevel evo)
public override EncounterMatchRating GetMatchRating(PKM pkm)
{
if (!IsForcedMasteryCorrect(pkm))
return EncounterMatchRating.PartialMatch;
var result = GetMatchRatingInternal(pkm);
var orig = base.GetMatchRating(pkm);
return result > orig ? result : orig;
@@ -116,39 +101,61 @@ private EncounterMatchRating GetMatchRatingInternal(PKM pkm)
if (orig is not EncounterMatchRating.Match)
return orig;
if (!IsForcedMasteryCorrect(pkm))
return EncounterMatchRating.DeferredErrors;
if (IsAlpha && pkm is PA8 { AlphaMove: 0 })
return EncounterMatchRating.Deferred;
return EncounterMatchRating.Match;
}
private bool IsForcedMasteryCorrect(PKM pkm)
public bool IsForcedMasteryCorrect(PKM pkm)
{
if (Mastery is not { } m)
return true;
ushort alpha = 0;
if (IsAlpha && Moves.Count != 0)
{
if (pkm is PA8 pa && pa.AlphaMove != Moves[0])
if (pkm is PA8 pa && (alpha = pa.AlphaMove) != Moves[0])
return false;
}
if (pkm is not IMoveShop8Mastery p)
return true;
for (int i = 0; i < m.Length; i++)
{
if (!m[i])
continue;
var move = Moves[i];
var index = p.MoveShopPermitIndexes.IndexOf((ushort)move);
if (index == -1)
continue; // manually mastered for encounter, not a tutor
if (!p.GetMasteredRecordFlag(index))
return false;
}
Span<int> m = stackalloc int[4];
var level = pkm.Met_Level;
var index = PersonalTable.LA.GetFormIndex(Species, Form);
var mastery = Legal.MasteryLA[index];
if (Moves.Count != 0)
m = (int[])Moves;
else
Legal.LevelUpLA[index].SetEncounterMoves(level, m);
return true;
return p.IsValidMasteredEncounter(m, mastery, level, alpha);
}
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var pa8 = (PA8)pk;
Span<int> moves = stackalloc int[4];
var index = PersonalTable.LA.GetFormIndex(Species, Form);
var mastery = Legal.MasteryLA[index];
var learn = Legal.LevelUpLA[index];
if (IsAlpha && Moves.Count != 0)
{
moves = (int[])Moves;
}
else
{
learn.SetEncounterMoves(level, moves);
}
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
foreach (var move in moves)
{
if (mastery.GetMoveLevel(move) <= level)
pa8.SetMasteryFlagMove(move);
}
}
private OverworldParam8a GetParams()

View File

@@ -161,8 +161,8 @@ private void SetMoves(PKM pk, GameVersion version, int level)
var moves = Moves.Count != 0 ? Moves : MoveLevelUp.GetEncounterMoves(pk, level, version);
if (pk.Format == 1 && moves.All(z => z == 0))
moves = ((PersonalInfoG1)PersonalTable.RB[Species]).Moves;
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
pk.SetMoves((int[])moves);
pk.SetMaximumPPCurrent((int[])moves);
}
private void SetEggMetData(PKM pk, DateTime time)

View File

@@ -0,0 +1,6 @@
namespace PKHeX.Core;
public interface IMasteryInitialMoveShop8
{
bool IsForcedMasteryCorrect(PKM pkm);
}

View File

@@ -142,6 +142,7 @@ public static class LegalityCheckStrings
public static string LEncGiftVersionNotDistributed { get; set; } = "Mystery Gift cannot be received by this version.";
public static string LEncInvalid { get; set; } = "Unable to match an encounter from origin game.";
public static string LEncMasteryInitial { get; set; } = "Initial move mastery flags do not match the encounter's expected state.";
public static string LEncTradeChangedNickname { get; set; } = "In-game Trade Nickname has been altered.";
public static string LEncTradeChangedOT { get; set; } = "In-game Trade OT has been altered.";

View File

@@ -42,9 +42,11 @@ public int[] GetMoves(int maxLevel, int minLevel = 0)
int end = Array.FindLastIndex(Levels, z => z <= maxLevel);
if (end < 0)
return Array.Empty<int>();
int[] result = new int[end - start + 1];
Array.Copy(Moves, start, result, 0, result.Length);
return result;
var length = end - start + 1;
if (length == Moves.Length)
return Moves;
return Moves.AsSpan(start, length).ToArray();
}
/// <summary>
@@ -121,6 +123,34 @@ public void SetEncounterMoves(int level, Span<int> moves, int ctr = 0)
}
}
public void SetEncounterMovesBackwards(int level, Span<int> moves, int ctr = 0)
{
int index = Array.FindLastIndex(Levels, z => z <= level);
while (true)
{
if (index == -1)
return; // no moves to add?
// In the event we have multiple moves at the same level, insert them in regular descending order.
int start = index;
while (start != 0 && Levels[start] == Levels[start - 1])
start--;
for (int i = start; i <= index; i++)
{
var move = Moves[i];
if (moves.IndexOf(move) == -1)
moves[ctr++] = move;
if (ctr == 4)
return;
}
index = start - 1;
}
}
/// <summary>Adds the learned moves by level up to the specified level.</summary>
public void SetLevelUpMoves(int startLevel, int endLevel, Span<int> moves, int ctr = 0)
{
@@ -228,6 +258,14 @@ public int GetMinMoveLevel(int level)
return Math.Max(end - 4, 1);
}
public int GetMoveLevel(int move)
{
var index = Array.LastIndexOf(Moves, move);
if (index == -1)
return -1;
return Levels[index];
}
private Dictionary<int, int>? Learn;
private Dictionary<int, int> GetDictionary()

View File

@@ -114,6 +114,11 @@ public override void Verify(LegalityAnalysis data)
if (!valid)
data.AddLine(GetInvalid(LPIDTypeMismatch));
}
else if (enc is IMasteryInitialMoveShop8 m)
{
if (!m.IsForcedMasteryCorrect(pkm))
data.AddLine(GetInvalid(LEncMasteryInitial));
}
VerifyMiscFatefulEncounter(data);
VerifyMiscPokerus(data);

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core
@@ -110,7 +111,7 @@ private void EncryptPK()
public override bool IsPokémon { get => PGTGiftType is GiftType.Pokémon or GiftType.PokémonEgg or GiftType.ManaphyEgg; set { } }
public override int Species { get => IsManaphyEgg ? 490 : PK.Species; set => PK.Species = value; }
public override IReadOnlyList<int> Moves { get => PK.Moves; set => PK.SetMoves(value); }
public override IReadOnlyList<int> Moves { get => PK.Moves; set => PK.SetMoves(value.ToArray()); }
public override int HeldItem { get => PK.HeldItem; set => PK.HeldItem = value; }
public override bool IsShiny => PK.IsShiny;
public override int Gender { get => PK.Gender; set => PK.Gender = value; }

View File

@@ -168,8 +168,9 @@ private void SetMoves(PK3 pk)
Moves = moves;
}
pk.SetMoves(Moves);
pk.SetMaximumPPCurrent(Moves);
var m = (int[])Moves;
pk.SetMoves(m);
pk.SetMaximumPPCurrent(m);
}
private void SetPINGA(PK3 pk, EncounterCriteria _)

View File

@@ -18,3 +18,52 @@ public interface IMoveShop8Mastery : IMoveShop8
void SetMasteredRecordFlag(int index, bool value);
bool GetMasteredRecordFlagAny();
}
public static class MoveShop8MasteryExtensions
{
public static bool IsValidMasteredEncounter(this IMoveShop8Mastery shop, Span<int> moves, Learnset mastery, int level, ushort alpha)
{
foreach (var move in moves)
{
if (move == 0)
continue;
var index = shop.MoveShopPermitIndexes.IndexOf((ushort)move);
if (index == -1)
continue; // manually mastered for encounter, not a tutor
bool p = shop.GetPurchasedRecordFlag(index);
bool m = shop.GetMasteredRecordFlag(index);
var masteryLevel = mastery.GetMoveLevel(move);
if (masteryLevel > level && move != alpha) // no master flag set
{
if (!p && m)
return false;
}
else
{
if (p || !m)
return false;
}
}
return true;
}
private static bool IsPurchasedEncounterMove(IMoveShop8 p, Span<int> moves)
{
foreach (var move in moves)
{
if (move == 0)
continue;
var index = p.MoveShopPermitIndexes.IndexOf((ushort)move);
if (index == -1)
continue; // manually mastered for encounter, not a tutor
if (p.GetPurchasedRecordFlag(index))
return true;
}
return false;
}
}

View File

@@ -439,7 +439,7 @@ public int[] Stats
public int[] Moves
{
get => new[] { Move1, Move2, Move3, Move4 };
set => SetMoves(value);
set => SetMoves(value.AsSpan());
}
public void PushMove(int move)
@@ -464,12 +464,12 @@ public void GetMoves(Span<int> value)
value[0] = Move1;
}
public void SetMoves(IReadOnlyList<int> value)
public void SetMoves(ReadOnlySpan<int> value)
{
Move1 = value.Count > 0 ? value[0] : 0;
Move2 = value.Count > 1 ? value[1] : 0;
Move3 = value.Count > 2 ? value[2] : 0;
Move4 = value.Count > 3 ? value[3] : 0;
Move1 = value.Length > 0 ? value[0] : 0;
Move2 = value.Length > 1 ? value[1] : 0;
Move3 = value.Length > 2 ? value[2] : 0;
Move4 = value.Length > 3 ? value[3] : 0;
}
public int[] RelearnMoves

Binary file not shown.