203 lines
7.1 KiB
C#
203 lines
7.1 KiB
C#
using System;
|
|
using RPGCore.Core.Objects;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace RPGCore.ObjectModules.ActionObjectModule
|
|
{
|
|
public enum ActionState
|
|
{
|
|
Ready,
|
|
Running,
|
|
Finished,
|
|
Cancelled
|
|
}
|
|
|
|
/// <summary>
|
|
/// Action for <see cref="UnitObject"/>.<br/>
|
|
///
|
|
/// Executing order when <see cref="DoIt"/> used:<br/>
|
|
/// <ul>
|
|
/// <li><see cref="CanDoIt"/></li>
|
|
/// <li><see cref="OnDo"/></li>
|
|
/// <li><see cref="OnDoIt"/></li>
|
|
/// <li>Inside OnDoIt: <see cref="OnBeforePerform"/></li>
|
|
/// <li>Inside OnDoIt: Action's logic here</li>
|
|
/// <li>Inside OnDoIt: <see cref="OnAfterPerform"/> - obligatory if action ends immediately</li>
|
|
/// </ul>
|
|
///
|
|
/// Executing order when <see cref="EndIt"/> used:<br/>
|
|
/// <ul>
|
|
/// <li><see cref="OnEndIt"/></li>
|
|
/// <li>Inside OnEndIt: <see cref="OnAfterPerform"/> - obligatory if not used in OnDoIt</li>
|
|
/// <li><see cref="OnEnd"/></li>
|
|
/// </ul>
|
|
///
|
|
/// Executing order when <see cref="CancelIt"/> used:<br/>
|
|
/// <ul>
|
|
/// <li><see cref="OnCancelIt"/></li>
|
|
/// <li>Inside OnCancelIt: <see cref="OnAfterPerform"/> - obligatory if not used in OnDoIt</li>
|
|
/// <li><see cref="OnCancel"/></li>
|
|
/// </ul>
|
|
/// </summary>
|
|
[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.";
|
|
|
|
/// <summary>
|
|
/// <see cref="UnitObject"/> that started executing this action.<br/>
|
|
/// Filled automatically in <see cref="ActionModule.Execute"/>/<see cref="ActionModule.AddToQueue(BaseAction)"/>.<br/>
|
|
/// If you want to use it before <b>executing action</b> you need to <b>fill <see cref="unit"/> yourself!</b>
|
|
/// </summary>
|
|
public UnitObject unit;
|
|
|
|
/// <summary>Run after checking and before executing action.</summary>
|
|
public event Action OnDo;
|
|
|
|
/// <summary>Defined by referenced action. Usually right *before* main point of that action.</summary>
|
|
public event Action OnBeforePerform;
|
|
|
|
/// <summary>Defined by referenced action. Usually right *after* main point of that action.</summary>
|
|
public event Action OnAfterPerform;
|
|
|
|
/// <summary>Run after action ended.</summary>
|
|
public event Action OnEnd;
|
|
|
|
/// <summary>Run after action ended forcefully.</summary>
|
|
public event Action OnCancel;
|
|
|
|
/// <summary>Current state of action.</summary>
|
|
public ActionState state { get; protected set; } = ActionState.Ready;
|
|
|
|
/// <summary>If given, current action will be executed only if given one is finished successfully</summary>
|
|
public BaseAction runAfterAction;
|
|
|
|
/// <summary>Simple check if unit is in range to execute this action.</summary>
|
|
public virtual bool IsInRange() => true;
|
|
|
|
/// <summary>Determines whether this action CAN be stopped early, by something else besides itself.</summary>
|
|
public virtual bool CanBeStopped() => true;
|
|
|
|
/// <summary>
|
|
/// Action's checks if action can be executed. <see cref="Check"/> have to be used here.<br/>
|
|
/// Field <see cref="unit">this.unit</see> may not be available here when called manually not via <see cref="ActionModule"/>.<br/>
|
|
/// </summary>
|
|
public abstract void CanDoIt();
|
|
|
|
/// <summary>Action's logic.</summary>
|
|
protected abstract void OnDoIt();
|
|
|
|
/// <summary>Action's logic when whole action ends successfully.</summary>
|
|
protected abstract void OnEndIt();
|
|
|
|
/// <summary>Action's logic when whole action is forced to end.</summary>
|
|
protected abstract void OnCancelIt();
|
|
|
|
/// <inheritdoc cref="CanBeStopped()"/>
|
|
public bool CanDoItBoolean()
|
|
{
|
|
try
|
|
{
|
|
CanDoIt();
|
|
return true;
|
|
}
|
|
catch (ActionErrorException)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 1. Checking if action can be done by function in child: <see cref="CanDoIt"/><br/>
|
|
/// 2. Executing main login of this action
|
|
/// </summary>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executed by action itself when everything is done correctly.
|
|
/// </summary>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executed by external source. Stopping this action forcefully.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Should be executed from child right before main logic is started, usually in <see cref="OnDoIt"/>
|
|
/// </summary>
|
|
protected void BeforePerform()
|
|
{
|
|
OnBeforePerform?.Invoke();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Should be executed from child right after main logic is done, usually in <see cref="OnDoIt"/>
|
|
/// </summary>
|
|
protected void AfterPerform()
|
|
{
|
|
OnAfterPerform?.Invoke();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute <see cref="Throw"/> if check is false
|
|
/// </summary>
|
|
protected void Check(bool condition, string elseErrorMessage)
|
|
{
|
|
if (!condition) Throw(elseErrorMessage);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancels this Action and then throw <see cref="ActionErrorException"/>
|
|
/// </summary>
|
|
protected void Throw(string errorMessage)
|
|
{
|
|
CancelIt();
|
|
throw new ActionErrorException(this, errorMessage);
|
|
}
|
|
}
|
|
} |