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);
}
}
}