using System; using Sirenix.Utilities; using UnityEngine; using VOID.Generic.Objects.Unit.Actions.Exceptions; namespace VOID.Generic.Objects.Unit.Actions { public enum ActionState { Ready, Running, Finished, Cancelled } /// /// Action for .
/// /// Executing order when used:
///
    ///
  • ///
  • ///
  • ///
  • Inside OnDoIt:
  • ///
  • Inside OnDoIt: Action's logic here
  • ///
  • Inside OnDoIt: - obligatory if action ends immediately
  • ///
/// /// Executing order when used:
///
    ///
  • ///
  • Inside OnEndIt: - obligatory if not used in OnDoIt
  • ///
  • ///
/// /// Executing order when used:
///
    ///
  • ///
  • Inside OnCancelIt: - obligatory if not used in OnDoIt
  • ///
  • ///
///
[Serializable] public abstract class AbstractAction { /// Run after checking and before executing action. public event Action OnDo; /// Defined by referenced action. Usually right *before* main point of that action. public event Action OnBeforePerform; /// Defined by referenced action. Usually right *after* main point of that action. public event Action OnAfterPerform; /// Run after action ended. public event Action OnEnd; /// Run after action ended forcefully. public event Action OnCancel; /// Current state of action. public ActionState state { get; protected set; } = ActionState.Ready; /// If given, current action will be executed only if given one is finished successfully public AbstractAction runAfterAction; /// Simple check if unit is in range to execute this action. public abstract bool IsInRange(); /// Determines whether this action CAN be executed INSTANTLY outside the queue. public virtual bool CanSkipQueue() => false; /// Determines whether this action CAN be stopped early, by something else besides itself. public virtual bool CanBeStopped() => true; /// Action's checks if action can be executed. have to be used here. public abstract void CanDoIt(); /// Action's logic. protected abstract void OnDoIt(); /// Action's logic when whole action ends successfully. protected abstract void OnEndIt(); /// Action's logic when whole action is forced to end. protected abstract void OnCancelIt(); public bool CanDoItBoolean() { try { CanDoIt(); return true; } catch (UnitActionErrorException) { return false; } } /// /// 1. Checking if action can be done by function in child:
/// 2. Executing main login of this action ///
public void DoIt() { Check( runAfterAction == null || runAfterAction.state == ActionState.Finished, UnitActionErrorType.PreviousActionNotFinished); CanDoIt(); state = ActionState.Running; OnDo?.Invoke(); OnDoIt(); } /// /// Executed by action itself when everything is done correctly. /// protected void EndIt() { if (state is ActionState.Finished or ActionState.Cancelled) { Debug.LogWarning($"{GetType().GetNiceName()} can't be ended. State: '{state}'"); return; } OnEndIt(); state = ActionState.Finished; OnEnd?.Invoke(); } /// /// Executed by external source. Stopping this action forcefully. /// public void CancelIt() { // Cancel events only when action really started otherwise there will be nothing to cancel if (state == ActionState.Running) { OnCancelIt(); OnCancel?.Invoke(); } state = ActionState.Cancelled; } /// /// Should be executed from child right before main logic is started, usually in /// protected void BeforePerform() { OnBeforePerform?.Invoke(); } /// /// Should be executed from child right after main logic is done, usually in /// protected void AfterPerform() { OnAfterPerform?.Invoke(); } /// /// Execute if check is false /// protected void Check(bool condition, UnitActionErrorType elseError) { if (!condition) Throw(elseError); } /// /// Cancels this Action and then throw /// protected void Throw(UnitActionErrorType errorType) { CancelIt(); throw new UnitActionErrorException(this, errorType); } } }