Files
2026-07-09 21:33:33 +02:00

145 lines
4.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using Sirenix.Utilities;
using UnityEngine;
using VOID.Generic.Objects.Unit.Actions;
using VOID.Generic.Objects.Unit.Actions.Exceptions;
using VOID.Generic.Objects.Unit.Events;
namespace VOID.Generic.Objects.Unit
{
[Serializable]
public class UnitObjectActionQueue : ObjectComponent<UnitObject>
{
#region Initialize
protected override void SelfInitialize()
{
parent.basicEvents.onFixedUpdate.AddListener(CheckRunningAction);
}
#endregion
// Getters
public List<AbstractAction> actionQueue => _actionQueue;
public AbstractAction actionCurrent => _actionCurrent;
[ShowInInspector, ReadOnly] private readonly List<AbstractAction> _actionQueue = new();
[ShowInInspector, ReadOnly] private AbstractAction _actionCurrent;
/// <summary>
/// 1. Checks if the action can be executed outside the queue.
/// 2. If allowed, executes the action and handles potential errors.
/// </summary>
public void ExecuteInstantly(AbstractAction action)
{
if (!action.CanSkipQueue())
{
Debug.LogError($"Action: {action.GetType().Name} cannot be executed via {nameof(ExecuteInstantly)}");
return;
}
try
{
action.DoIt();
}
catch (UnitActionErrorException e)
{
parent.unitEvents.onActionError.Invoke(new ActionErrorEvent
{
unit = parent,
action = e.action,
cause = e.unitActionErrorType
});
}
}
/// <summary>
/// 1. Adds given action to queue
/// 2. Starts it if queue is empty
/// </summary>
public void AddAction(AbstractAction action) => AddActionChain(new List<AbstractAction> { action });
/// <summary>
/// 1. Adds given actions to queue
/// 2. Runs only when previous action is finished successfully
/// 3. Starts it if queue is empty
/// </summary>
public void AddActionChain(List<AbstractAction> actions)
{
// Unit is currently not controllable (stunned? doing something unstoppable? magically frozen by dev?)
if (!parent.unitState.isControllable)
{
parent.unitEvents.onActionError.Invoke(new ActionErrorEvent
{
unit = parent,
action = actions.First(),
cause = UnitActionErrorType.UnitStateIsNotControllable
});
return;
}
actions.ForEach((action, actionIndex) =>
{
if (actionIndex != 0) action.runAfterAction = actions[actionIndex - 1];
_actionQueue.Add(action);
});
if (_actionCurrent == null) NextAction();
}
/// <summary>
/// 1. Clear whole action queue
/// 2. Cancel current action IF specified by argument
/// 3. Some actions cant be stopped once fired by normal means, BUT we can force stopping it
/// </summary>
public void StopQueue(bool stopCurrent = true, bool stopForced = false)
{
_actionQueue.Clear();
if (!stopCurrent) return;
if (!stopForced && _actionCurrent != null && !_actionCurrent.CanBeStopped()) return;
_actionCurrent?.CancelIt();
_actionCurrent = null;
}
/// <summary>
/// 1. Gets next action to do
/// 2. If there is no more actions - do nothing
/// 3. Proceed to execute it
/// </summary>
private void NextAction()
{
_actionCurrent = _actionQueue.FirstOrDefault();
_actionQueue.Remove(_actionCurrent);
if (_actionCurrent == null) return;
try
{
_actionCurrent.DoIt();
}
catch (UnitActionErrorException e)
{
parent.unitEvents.onActionError.Invoke(new ActionErrorEvent
{
unit = parent,
action = e.action,
cause = e.unitActionErrorType
});
}
if (_actionCurrent.state is ActionState.Ready) Debug.LogWarning($"Action {_actionCurrent.GetType().Name} is still ready after starting. Skipping.");
if (_actionCurrent.state is not ActionState.Running) NextAction();
}
private void CheckRunningAction()
{
if (_actionCurrent?.state is ActionState.Running) return;
NextAction();
}
}
}