using System; using System.Collections.Generic; namespace PKHeX.Core; /// /// Level Up Learn Movepool Information /// public sealed class Learnset { /// /// Moves that can be learned. /// internal readonly int[] Moves; /// /// Levels at which a move at a given index can be learned. /// private readonly int[] Levels; public Learnset(int[] moves, int[] levels) { Moves = moves; Levels = levels; } /// /// Returns the moves a Pokémon can learn between the specified level range. /// /// Maximum level /// Minimum level /// Array of Move IDs public int[] GetMoves(int maxLevel, int minLevel = 0) { if (minLevel <= 1 && maxLevel >= 100) return Moves; if (minLevel > maxLevel) return Array.Empty(); int start = Array.FindIndex(Levels, z => z >= minLevel); if (start < 0) return Array.Empty(); int end = Array.FindLastIndex(Levels, z => z <= maxLevel); if (end < 0) return Array.Empty(); var length = end - start + 1; if (length == Moves.Length) return Moves; return Moves.AsSpan(start, length).ToArray(); } public (bool HasMoves, int Start, int End) GetMoveRange(int maxLevel, int minLevel = 0) { if (minLevel <= 1 && maxLevel >= 100) return (true, 0, Moves.Length - 1); if (minLevel > maxLevel) return default; int start = Array.FindIndex(Levels, z => z >= minLevel); if (start < 0) return default; int end = Array.FindLastIndex(Levels, z => z <= maxLevel); if (end < 0) return default; return (true, start, end); } /// Returns the moves a Pokémon would have if it were encountered at the specified level. /// In Generation 1, it is not possible to learn any moves lower than these encounter moves. /// The level the Pokémon was encountered at. /// Array of Move IDs public int[] GetEncounterMoves(int level) { const int count = 4; var moves = new int[count]; SetEncounterMoves(level, moves); return moves; } /// Returns the moves a Pokémon would have if it were encountered at the specified level. /// In Generation 1, it is not possible to learn any moves lower than these encounter moves. /// The level the Pokémon was encountered at. /// Move array to write to /// Starting index to begin overwriting at /// Array of Move IDs public void SetEncounterMoves(int level, Span moves, int ctr = 0) { for (int i = 0; i < Moves.Length; i++) { if (Levels[i] > level) break; int move = Moves[i]; bool alreadyHasMove = moves.IndexOf(move) >= 0; if (alreadyHasMove) continue; moves[ctr++] = move; ctr &= 3; } } 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) { int startIndex = Array.FindIndex(Levels, z => z >= startLevel); int endIndex = Array.FindIndex(Levels, z => z > endLevel); for (int i = startIndex; i < endIndex; i++) { int move = Moves[i]; bool alreadyHasMove = moves.IndexOf(move) >= 0; if (alreadyHasMove) continue; moves[ctr++] = move; ctr &= 3; } } /// Adds the moves that are gained upon evolving. /// Move array to write to /// Starting index to begin overwriting at public void SetEvolutionMoves(Span moves, int ctr = 0) { for (int i = 0; i < Moves.Length; i++) { if (Levels[i] != 0) break; int move = Moves[i]; bool alreadyHasMove = moves.IndexOf(move) >= 0; if (alreadyHasMove) continue; moves[ctr++] = move; ctr &= 3; } } /// Adds the learned moves by level up to the specified level. public void SetLevelUpMoves(int startLevel, int endLevel, Span moves, ReadOnlySpan ignore, int ctr = 0) { int startIndex = Array.FindIndex(Levels, z => z >= startLevel); if (startIndex == -1) return; // No more remain int endIndex = Array.FindIndex(Levels, z => z > endLevel); if (endIndex == -1) endIndex = Levels.Length; for (int i = startIndex; i < endIndex; i++) { int move = Moves[i]; if (ignore.IndexOf(move) >= 0) continue; bool alreadyHasMove = moves.IndexOf(move) >= 0; if (alreadyHasMove) continue; moves[ctr++] = move; ctr &= 3; } } /// Adds the moves that are gained upon evolving. /// Move array to write to /// Ignored moves /// Starting index to begin overwriting at public void SetEvolutionMoves(Span moves, ReadOnlySpan ignore, int ctr = 0) { for (int i = 0; i < Moves.Length; i++) { if (Levels[i] != 0) break; int move = Moves[i]; if (ignore.IndexOf(move) >= 0) continue; bool alreadyHasMove = moves.IndexOf(move) >= 0; if (alreadyHasMove) continue; moves[ctr++] = move; ctr &= 3; } } public IList GetUniqueMovesLearned(IEnumerable seed, int maxLevel, int minLevel = 0) { int start = Array.FindIndex(Levels, z => z >= minLevel); int end = Array.FindLastIndex(Levels, z => z <= maxLevel); var list = new List(seed); for (int i = start; i <= end; i++) { if (!list.Contains(Moves[i])) list.Add(Moves[i]); } return list; } /// Returns the index of the lowest level move if the Pokémon were encountered at the specified level. /// Helps determine the minimum level an encounter can be at. /// The level the Pokémon was encountered at. /// Array of Move IDs public int GetMinMoveLevel(int level) { if (Levels.Length == 0) return 1; int end = Array.FindLastIndex(Levels, z => z <= 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() { var dict = new Dictionary(); for (int i = 0; i < Moves.Length; i++) { if (!dict.ContainsKey(Moves[i])) dict.Add(Moves[i], Levels[i]); } return dict; } /// Returns the level that a Pokémon can learn the specified move. /// Move ID /// Level the move is learned at. If the result is below 0, the move cannot be learned by leveling up. public int GetLevelLearnMove(int move) { return (Learn ??= GetDictionary()).TryGetValue(move, out var level) ? level : -1; } /// Returns the level that a Pokémon can learn the specified move. /// Move ID /// Minimum level to start looking at. /// Level the move is learned at. If the result is below 0, the move cannot be learned by leveling up. public int GetLevelLearnMove(int move, int min) { for (int i = 0; i < Moves.Length; i++) { if (move != Moves[i]) continue; var lv = Levels[i]; if (lv >= min) return lv; } return -1; } public ReadOnlySpan GetBaseEggMoves(int level) { // Count moves <= level var count = 0; foreach (var x in Levels) { if (x > level) break; count++; } // Return a slice containing the moves <= level. if (count == 0) return ReadOnlySpan.Empty; int start = 0; if (count > 4) { start = count - 4; count = 4; } return Moves.AsSpan(start, count); } }