init
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace RPGCore.ObjectModules.ActionObjectModule
|
||||
{
|
||||
public class ActionErrorException : Exception
|
||||
{
|
||||
public readonly BaseAction action;
|
||||
|
||||
public ActionErrorException(BaseAction action, string message) : base(message)
|
||||
{
|
||||
this.action = action;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65d26dcf19b44027b8ff0890bb8ab920
|
||||
timeCreated: 1762608145
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RPGCore.Core;
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.ActionObjectModule.Events;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
using RPGCoreCommon.Helpers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.ObjectModules.ActionObjectModule
|
||||
{
|
||||
[Serializable]
|
||||
public class ActionModule : ObjectModule<UnitObject>
|
||||
{
|
||||
private List<BaseActionParallel> _actionParallels = new();
|
||||
private readonly List<BaseAction> _actionQueue = new();
|
||||
private BaseAction _actionCurrent;
|
||||
|
||||
public List<BaseActionParallel> actionParallels => _actionParallels.ToList();
|
||||
public List<BaseAction> actionQueue => _actionQueue.ToList();
|
||||
public BaseAction actionCurrent => _actionCurrent;
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (_actionCurrent?.state is not ActionState.Running) NextAction();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1. Checks if the action can be executed outside the queue.<br/>
|
||||
/// 2. If allowed, executes the action and handles potential errors.
|
||||
/// </summary>
|
||||
public void Execute(BaseActionParallel action)
|
||||
{
|
||||
try
|
||||
{
|
||||
var actionExecuteEvent = new ActionExecuteEvent { action = action };
|
||||
parent.events.InvokeBefore(actionExecuteEvent);
|
||||
if (actionExecuteEvent.isPrevented) throw new ActionErrorException(action, "Action is prevented");
|
||||
parent.events.InvokeAfter(actionExecuteEvent);
|
||||
|
||||
_actionParallels.Add(action);
|
||||
action.unit = parent;
|
||||
action.OnEnd += () => _actionParallels.Remove(action);
|
||||
action.OnCancel += () => _actionParallels.Remove(action);
|
||||
action.DoIt();
|
||||
}
|
||||
catch (ActionErrorException e)
|
||||
{
|
||||
parent.events.Invoke(new ActionErrorEvent
|
||||
{
|
||||
unit = parent,
|
||||
action = e.action,
|
||||
message = e.Message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_actionParallels
|
||||
.Where(action => action.CanBeStopped())
|
||||
.ForEach(action => action.CancelIt())
|
||||
.ForEach(action => _actionParallels.Remove(action));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 1. Adds given action to queue
|
||||
/// 2. Starts it if queue is empty
|
||||
/// </summary>
|
||||
public void AddToQueue(BaseAction action) => AddToQueue(new List<BaseAction> { 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 AddToQueue(List<BaseAction> actions)
|
||||
{
|
||||
actions.ForEach(action => action.unit = parent);
|
||||
|
||||
actions.ForEach((index, action) =>
|
||||
{
|
||||
if (index != 0) action.runAfterAction = actions[index - 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
|
||||
{
|
||||
var actionExecuteEvent = new ActionExecuteEvent { action = _actionCurrent };
|
||||
parent.events.InvokeBefore(actionExecuteEvent);
|
||||
if (actionExecuteEvent.isPrevented) throw new ActionErrorException(_actionCurrent, "Action is prevented");
|
||||
parent.events.InvokeAfter(actionExecuteEvent);
|
||||
|
||||
_actionCurrent.DoIt();
|
||||
}
|
||||
catch (ActionErrorException e)
|
||||
{
|
||||
parent.events.Invoke(new ActionErrorEvent
|
||||
{
|
||||
unit = parent,
|
||||
action = e.action,
|
||||
message = e.Message
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62d81cd897cb4ad29751fab9b0ee8ac6
|
||||
timeCreated: 1762607269
|
||||
@@ -0,0 +1,203 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ac7c378ad684142b41ceea5e99636eb
|
||||
timeCreated: 1762607985
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace RPGCore.ObjectModules.ActionObjectModule
|
||||
{
|
||||
public abstract class BaseActionParallel : BaseAction
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e46b8a02e3894ecb87b4455602287715
|
||||
timeCreated: 1762624491
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbe46bc39ef7485cac06ad32831cac3a
|
||||
timeCreated: 1764885371
|
||||
@@ -0,0 +1,12 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
|
||||
namespace RPGCore.ObjectModules.ActionObjectModule.Events
|
||||
{
|
||||
public class ActionErrorEvent : BaseEvent<UnitObject>
|
||||
{
|
||||
public UnitObject unit;
|
||||
public BaseAction action;
|
||||
public string message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48b0c69fc332472e87f34822bed20ffd
|
||||
timeCreated: 1762611515
|
||||
@@ -0,0 +1,11 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
|
||||
namespace RPGCore.ObjectModules.ActionObjectModule.Events
|
||||
{
|
||||
public class ActionExecuteEvent : BasePreventableEvent<UnitObject>
|
||||
{
|
||||
public UnitObject unit;
|
||||
public BaseAction action;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 414af8b649334795bb3e1bc83149fbc3
|
||||
timeCreated: 1772998772
|
||||
Reference in New Issue
Block a user