From e45754c8302382f94e797e8ef6c1e4103a34ea95 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sat, 15 Nov 2025 22:21:07 -0600 Subject: [PATCH] Track more move sources No change in coloration, but will show them for past games such as Egg Moves that it could have learned. --- PKHeX.Core/Editing/PKM/LegalMoveInfo.cs | 106 +++++++++++++++++++++--- 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/PKHeX.Core/Editing/PKM/LegalMoveInfo.cs b/PKHeX.Core/Editing/PKM/LegalMoveInfo.cs index fd7cf799d..5c532d000 100644 --- a/PKHeX.Core/Editing/PKM/LegalMoveInfo.cs +++ b/PKHeX.Core/Editing/PKM/LegalMoveInfo.cs @@ -1,5 +1,6 @@ using System; using System.Buffers; +using static PKHeX.Core.IndicatedSourceType; namespace PKHeX.Core; @@ -8,16 +9,16 @@ namespace PKHeX.Core; /// public sealed class LegalMoveInfo { - // Use a bool array instead of a HashSet; we have a limited range of moves. + // Use a byte array instead of a HashSet; we have a limited range of moves. // This implementation is faster (no hashcode or bucket search) with lower memory overhead (1 byte per move ID). - private readonly bool[] AllowedMoves = new bool[(int)Move.MAX_COUNT + 1]; + private readonly IndicatedSourceType[] AllowedMoves = new IndicatedSourceType[(int)Move.MAX_COUNT + 1]; /// /// Checks if the requested is legally able to be learned. /// /// Move to check if it can be learned /// True if it can learn the move - public bool CanLearn(ushort move) => AllowedMoves[move]; + public bool CanLearn(ushort move) => AllowedMoves[move] != None; /// /// Reloads the legality sources to permit the provided legal info. @@ -25,16 +26,95 @@ public sealed class LegalMoveInfo /// Details of analysis, moves to allow public bool ReloadMoves(LegalityAnalysis la) { - var rent = ArrayPool.Shared.Rent(AllowedMoves.Length); - var span = rent.AsSpan(0, AllowedMoves.Length); - LearnPossible.Get(la.Entity, la.EncounterOriginal, la.Info.EvoChainsAllGens, span); + var rentLearn = ArrayPool.Shared.Rent(AllowedMoves.Length); + var spanLearn = rentLearn.AsSpan(0, AllowedMoves.Length); + var rentEval = ArrayPool.Shared.Rent(spanLearn.Length); + var spanEval = rentEval.AsSpan(0, spanLearn.Length); + try + { + LearnPossible.Get(la.Entity, la.EncounterOriginal, la.Info.EvoChainsAllGens, spanLearn); + ComputeEval(spanEval, spanLearn, la); + if (spanEval.SequenceEqual(AllowedMoves)) + return false; + spanEval.CopyTo(AllowedMoves); + return true; + } + catch + { + if (Array.TrueForAll(AllowedMoves, z => z == None)) + return false; + AllowedMoves.AsSpan().Clear(); + return true; + } + finally + { + spanLearn.Clear(); + spanEval.Clear(); + ArrayPool.Shared.Return(rentEval); + ArrayPool.Shared.Return(rentLearn); + } + } - // check prior move-pool to not needlessly refresh the data set - bool diff = !span.SequenceEqual(AllowedMoves); - if (diff) // keep - span.CopyTo(AllowedMoves); - span.Clear(); - ArrayPool.Shared.Return(rent); - return diff; + private static void ComputeEval(Span type, ReadOnlySpan learn, LegalityAnalysis la) + { + for (int i = 0; i < type.Length; i++) + type[i] = learn[i] ? Learn : None; + + if (!la.Entity.IsOriginalMovesetDeleted()) + AddEncounterMoves(type, la.EncounterOriginal); + + type[0] = None; // Move ID 0 is always None + } + + private static void AddEncounterMoves(Span type, IEncounterTemplate enc) + { + if (enc is IEncounterEgg egg) + { + var moves = egg.Learn.GetEggMoves(enc.Species, enc.Form); + foreach (var move in moves) + type[move] = Egg; + } + else if (enc is IMoveset {Moves: {HasMoves: true} set}) + { + if (type[set.Move1] == None) + type[set.Move1] = Encounter; + if (type[set.Move2] == None) + type[set.Move2] = Encounter; + if (type[set.Move3] == None) + type[set.Move3] = Encounter; + if (type[set.Move4] == None) + type[set.Move4] = Encounter; + } + else if (enc is ISingleMoveBonus single) + { + var moves = single.GetMoveBonusPossible(); + foreach (var move in moves) + { + if (type[move] == None) + type[move] = EncounterSingle; + } + } + + if (enc is IRelearn { Relearn: {HasMoves: true} relearn}) + { + if (type[relearn.Move1] == None) + type[relearn.Move1] = Relearn; + if (type[relearn.Move2] == None) + type[relearn.Move2] = Relearn; + if (type[relearn.Move3] == None) + type[relearn.Move3] = Relearn; + if (type[relearn.Move4] == None) + type[relearn.Move4] = Relearn; + } } } + +public enum IndicatedSourceType : byte +{ + None = 0, + Learn, + Egg, + Encounter, + EncounterSingle, + Relearn, +}