2024-08-10 07:44:46 +00:00
|
|
|
using PkmnLib.Dynamic.Models.Choices;
|
|
|
|
using PkmnLib.Dynamic.ScriptHandling;
|
2024-12-29 12:51:59 +00:00
|
|
|
using PkmnLib.Static.Utils;
|
2024-08-10 07:44:46 +00:00
|
|
|
|
2024-07-27 14:26:45 +00:00
|
|
|
namespace PkmnLib.Dynamic.Models;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The ChoiceQueue is used to run choices one by one.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// 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.
|
|
|
|
/// </remarks>
|
2024-12-29 12:51:59 +00:00
|
|
|
public class BattleChoiceQueue : IDeepCloneable
|
2024-07-27 14:26:45 +00:00
|
|
|
{
|
2024-08-10 07:44:46 +00:00
|
|
|
private readonly ITurnChoice?[] _choices;
|
|
|
|
private int _currentIndex;
|
|
|
|
|
|
|
|
/// <inheritdoc cref="BattleChoiceQueue"/>
|
|
|
|
public BattleChoiceQueue(ITurnChoice[] choices)
|
|
|
|
{
|
|
|
|
_choices = choices;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 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.
|
|
|
|
/// </summary>
|
|
|
|
/// <returns></returns>
|
|
|
|
public ITurnChoice? Dequeue()
|
|
|
|
{
|
|
|
|
if (_currentIndex >= _choices.Length)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
var choice = _choices[_currentIndex];
|
|
|
|
_choices[_currentIndex] = null;
|
|
|
|
_currentIndex++;
|
|
|
|
return choice;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// This reads what the next choice to execute will be, without modifying state.
|
|
|
|
/// </summary>
|
|
|
|
public ITurnChoice? Peek() => _currentIndex >= _choices.Length ? null : _choices[_currentIndex];
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Checks if there are any more choices to execute.
|
|
|
|
/// </summary>
|
|
|
|
public bool HasNext() => _currentIndex < _choices.Length;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 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.
|
|
|
|
/// </summary>
|
|
|
|
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!);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// This moves the choice of a specific Pokémon up to the next choice to be executed.
|
|
|
|
/// </summary>
|
2024-11-01 13:21:01 +00:00
|
|
|
/// <returns>
|
|
|
|
/// Returns true if the Pokémon was found and moved, false otherwise.
|
|
|
|
/// </returns>
|
2024-08-10 07:44:46 +00:00
|
|
|
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<ITurnChoice?> GetChoices() => _choices;
|
2024-07-27 14:26:45 +00:00
|
|
|
}
|