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 { private List _actionParallels = new(); private readonly List _actionQueue = new(); private BaseAction _actionCurrent; public List actionParallels => _actionParallels.ToList(); public List actionQueue => _actionQueue.ToList(); public BaseAction actionCurrent => _actionCurrent; private void FixedUpdate() { if (_actionCurrent?.state is not ActionState.Running) NextAction(); } /// /// 1. Checks if the action can be executed outside the queue.
/// 2. If allowed, executes the action and handles potential errors. ///
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)); } /// /// 1. Adds given action to queue /// 2. Starts it if queue is empty /// public void AddToQueue(BaseAction action) => AddToQueue(new List { action }); /// /// 1. Adds given actions to queue /// 2. Runs only when previous action is finished successfully /// 3. Starts it if queue is empty /// public void AddToQueue(List 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(); } /// /// 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 /// 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; } /// /// 1. Gets next action to do /// 2. If there is no more actions - do nothing /// 3. Proceed to execute it /// 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(); } } }