using System; using RPGCore.Core.Objects; using UnityEditor; using UnityEngine; namespace RPGCore.ObjectModules.ActionObjectModule { 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 BaseAction { public const string ActionNotReadyMessage = "This action is not ready to execute."; public const string PreviousActionNotFinishedMessage = "Previous required action not finished successfully."; public const string NotInRangeMessage = "Target is out of range."; public const string UnitIsBusyMessage = "Unit is busy."; public const string ItemNotMovableMessage = "Item is not on ground."; public const string ItemNotOnGroundMessage = "Item is not on ground."; public const string ActionWasPreventedMessage = "{0} action was prevented."; /// /// that started executing this action.
/// Filled automatically in /.
/// If you want to use it before executing action you need to fill yourself! ///
public UnitObject unit; /// 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 BaseAction runAfterAction; /// Simple check if unit is in range to execute this action. public virtual bool IsInRange() => true; /// 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.
/// Field this.unit may not be available here when called manually not via .
///
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 (ActionErrorException) { return false; } } /// /// 1. Checking if action can be done by function in child:
/// 2. Executing main login of this action ///
internal void DoIt() { Check( state == ActionState.Ready, ActionNotReadyMessage); Check( runAfterAction == null || runAfterAction.state == ActionState.Finished, PreviousActionNotFinishedMessage); Check( IsInRange(), NotInRangeMessage); 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($"{ObjectNames.NicifyVariableName(GetType().Name)} is already finished. 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, string elseErrorMessage) { if (!condition) Throw(elseErrorMessage); } /// /// Cancels this Action and then throw /// protected void Throw(string errorMessage) { CancelIt(); throw new ActionErrorException(this, errorMessage); } } }