using PkmnLib.Dynamic.Models.Choices; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Static.Utils; namespace PkmnLib.Dynamic.Models; /// /// The ChoiceQueue is used to run choices one by one. /// /// /// It functions internally by holding a list of choices, and one by one setting items that have been returned to null, /// It holds several helper functions to change the turn order while doing the execution. This is needed, as several /// moves in Pokémon actively mess with this order. /// public class BattleChoiceQueue : IDeepCloneable { private readonly ITurnChoice?[] _choices; private int _currentIndex; /// public BattleChoiceQueue(ITurnChoice[] choices) { _choices = choices; } /// /// Dequeues the next turn choice to be executed. This gives back the choice and sets it to null in the queue. It /// also increments the internal index. /// /// public ITurnChoice? Dequeue() { if (_currentIndex >= _choices.Length) return null; var choice = _choices[_currentIndex]; _choices[_currentIndex] = null; _currentIndex++; return choice; } /// /// This reads what the next choice to execute will be, without modifying state. /// public ITurnChoice? Peek() => _currentIndex >= _choices.Length ? null : _choices[_currentIndex]; /// /// Checks if there are any more choices to execute. /// public bool HasNext() => _currentIndex < _choices.Length; /// /// This resorts the yet to be executed choices. This can be useful for dealing with situations /// such as Pokémon changing forms just after the very start of a turn, when turn order has /// technically already been decided. /// public void Resort() { var length = _choices.Length; var currentIndex = _currentIndex; for (var i = currentIndex; i < length; i++) { var choice = _choices[i]; if (choice == null) continue; // Ensure that the speed is up to date var speed = choice.User.BoostedStats.Speed; choice.User.RunScriptHook(script => script.ChangeSpeed(choice, ref speed)); choice.Speed = speed; } // We only sort the choices that are left Array.Sort(_choices, currentIndex, length - currentIndex, TurnChoiceComparer.Instance!); } /// /// This moves the choice of a specific Pokémon up to the next choice to be executed. /// /// /// Returns true if the Pokémon was found and moved, false otherwise. /// public bool MovePokemonChoiceNext(IPokemon pokemon) { var index = Array.FindIndex(_choices, _currentIndex, choice => choice?.User == pokemon); if (index == -1) return false; if (index == _currentIndex) return true; var choice = _choices[index]; _choices[index] = null; // Push all choices back for (var i = index; i > _currentIndex; i--) _choices[i] = _choices[i - 1]; // And insert the choice at the front _choices[_currentIndex] = choice; return true; } internal IReadOnlyList GetChoices() => _choices; }