diff --git a/PKHeX.Core/Editing/Applicators/MoveApplicator.cs b/PKHeX.Core/Editing/Applicators/MoveApplicator.cs
index f9677c4e2..2115b21b1 100644
--- a/PKHeX.Core/Editing/Applicators/MoveApplicator.cs
+++ b/PKHeX.Core/Editing/Applicators/MoveApplicator.cs
@@ -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)
///
/// Pokémon to modify.
/// to use (if already known). Will fetch the current if not provided.
- public static void SetMaximumPPCurrent(this PKM pk, IReadOnlyList moves)
+ public static void SetMaximumPPCurrent(this PKM pk, ReadOnlySpan 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);
}
///
diff --git a/PKHeX.Core/Legality/Core.cs b/PKHeX.Core/Legality/Core.cs
index 5d715d34f..651bc75a1 100644
--- a/PKHeX.Core/Legality/Core.cs
+++ b/PKHeX.Core/Legality/Core.cs
@@ -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 GetPPTable(PKM pkm, int format) => format switch
{
diff --git a/PKHeX.Core/Legality/Encounters/Data/Encounters8a.cs b/PKHeX.Core/Legality/Encounters/Data/Encounters8a.cs
index 9b442b0e3..1c2f40246 100644
--- a/PKHeX.Core/Legality/Encounters/Data/Encounters8a.cs
+++ b/PKHeX.Core/Legality/Encounters/Data/Encounters8a.cs
@@ -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 (Lunker’s Lair)
- new(642,000,70,M,M) { Location = 129, FlawlessIVCount = 3 }, // Thundurus (Sand’s 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 (Lunker’s Lair)
+ new(642,000,70,M,M) { Location = 129, FlawlessIVCount = 3, Moves = new[] {326,242,087,847} }, // Thundurus (Sand’s 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
};
}
diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot3Swarm.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot3Swarm.cs
index ab1f1f95c..19d8bd941 100644
--- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot3Swarm.cs
+++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot3Swarm.cs
@@ -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);
}
diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot8a.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot8a.cs
index 6d2f4fd90..5ac2ed621 100644
--- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot8a.cs
+++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot8a.cs
@@ -6,7 +6,7 @@ namespace PKHeX.Core;
/// Encounter Slot found in .
///
///
-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 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 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;
diff --git a/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic.cs b/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic.cs
index dd669e476..6ae33b759 100644
--- a/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic.cs
+++ b/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic.cs
@@ -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);
}
diff --git a/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic8a.cs b/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic8a.cs
index 06729dc18..dd26210a4 100644
--- a/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic8a.cs
+++ b/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic8a.cs
@@ -6,9 +6,8 @@ namespace PKHeX.Core;
/// Generation 8 Static Encounter
///
///
-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 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 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()
diff --git a/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs b/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs
index b8d5cadb5..5404efb7d 100644
--- a/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs
+++ b/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs
@@ -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)
diff --git a/PKHeX.Core/Legality/Encounters/IMasteryInitialMoveShop8.cs b/PKHeX.Core/Legality/Encounters/IMasteryInitialMoveShop8.cs
new file mode 100644
index 000000000..8760b08f5
--- /dev/null
+++ b/PKHeX.Core/Legality/Encounters/IMasteryInitialMoveShop8.cs
@@ -0,0 +1,6 @@
+namespace PKHeX.Core;
+
+public interface IMasteryInitialMoveShop8
+{
+ bool IsForcedMasteryCorrect(PKM pkm);
+}
diff --git a/PKHeX.Core/Legality/Formatting/LegalityCheckStrings.cs b/PKHeX.Core/Legality/Formatting/LegalityCheckStrings.cs
index ebb54bb0e..933210ba5 100644
--- a/PKHeX.Core/Legality/Formatting/LegalityCheckStrings.cs
+++ b/PKHeX.Core/Legality/Formatting/LegalityCheckStrings.cs
@@ -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.";
diff --git a/PKHeX.Core/Legality/Learnset/Learnset.cs b/PKHeX.Core/Legality/Learnset/Learnset.cs
index cd1103a94..a575369c3 100644
--- a/PKHeX.Core/Legality/Learnset/Learnset.cs
+++ b/PKHeX.Core/Legality/Learnset/Learnset.cs
@@ -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[] 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();
}
///
@@ -121,6 +123,34 @@ public void SetEncounterMoves(int level, Span moves, int ctr = 0)
}
}
+ public void SetEncounterMovesBackwards(int level, Span 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;
+ }
+ }
+
/// Adds the learned moves by level up to the specified level.
public void SetLevelUpMoves(int startLevel, int endLevel, Span 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? Learn;
private Dictionary GetDictionary()
diff --git a/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs b/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs
index a4a273d57..b5cce04e7 100644
--- a/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs
+++ b/PKHeX.Core/Legality/Verifiers/MiscVerifier.cs
@@ -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);
diff --git a/PKHeX.Core/MysteryGifts/PGT.cs b/PKHeX.Core/MysteryGifts/PGT.cs
index a3316d012..5c0d9fee9 100644
--- a/PKHeX.Core/MysteryGifts/PGT.cs
+++ b/PKHeX.Core/MysteryGifts/PGT.cs
@@ -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 Moves { get => PK.Moves; set => PK.SetMoves(value); }
+ public override IReadOnlyList 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; }
diff --git a/PKHeX.Core/MysteryGifts/WC3.cs b/PKHeX.Core/MysteryGifts/WC3.cs
index 1a662d85c..6b50694c1 100644
--- a/PKHeX.Core/MysteryGifts/WC3.cs
+++ b/PKHeX.Core/MysteryGifts/WC3.cs
@@ -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 _)
diff --git a/PKHeX.Core/PKM/Interfaces/IMoveShop8.cs b/PKHeX.Core/PKM/Interfaces/IMoveShop8.cs
index 14dc809fe..c0fb3250a 100644
--- a/PKHeX.Core/PKM/Interfaces/IMoveShop8.cs
+++ b/PKHeX.Core/PKM/Interfaces/IMoveShop8.cs
@@ -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 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 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;
+ }
+}
diff --git a/PKHeX.Core/PKM/PKM.cs b/PKHeX.Core/PKM/PKM.cs
index 644516479..59ce8920d 100644
--- a/PKHeX.Core/PKM/PKM.cs
+++ b/PKHeX.Core/PKM/PKM.cs
@@ -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 value)
value[0] = Move1;
}
- public void SetMoves(IReadOnlyList value)
+ public void SetMoves(ReadOnlySpan 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
diff --git a/PKHeX.Core/Resources/legality/misc/mastery_la.pkl b/PKHeX.Core/Resources/legality/misc/mastery_la.pkl
new file mode 100644
index 000000000..6771a1f95
Binary files /dev/null and b/PKHeX.Core/Resources/legality/misc/mastery_la.pkl differ