This commit is contained in:
2026-04-25 23:37:10 +02:00
commit 19d6bd934a
476 changed files with 9198 additions and 0 deletions
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2f34e092f2964013863c9278745b7c45
timeCreated: 1762607253
@@ -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
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e8d01c287c954d278388eb561ce813cf
timeCreated: 1773604338
@@ -0,0 +1,30 @@
using System;
using RPGCore.Core.Objects;
namespace RPGCore.ObjectModules.EventObjectModule
{
/// <summary>
/// Extend this to create new type of data for &lt;<typeparamref name="T"/>&gt; type of object.<br/>
/// To retrieve this data from &lt;<typeparamref name="T"/>&gt; object use <see cref="DataModule.Get{T}"/>.
/// </summary>
/// <typeparam name="T"><see cref="BaseObject"/> (or any of its children) that this event can be attached to.</typeparam>
[Serializable]
public abstract class BaseData<T> : BaseData where T : BaseObject
{
public new T parent => (T)base.parent;
}
/// <summary>
/// <b>DO NOT EXTEND THIS</b>, use <see cref="BaseData{T}"/> instead!
/// </summary>
[Serializable]
public abstract class BaseData
{
internal BaseObject parent;
private protected BaseData()
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a960c2eadeca4d4aa8d1c454daca552f
timeCreated: 1773604425
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9970c419f4d94c22988563c8f8a50dc7
timeCreated: 1774171193
@@ -0,0 +1,18 @@
using System;
using RPGCore.Core.Objects;
using RPGCoreCommon.Helpers.PropertyAttributeDrawers;
using UnityEngine;
namespace RPGCore.ObjectModules.EventObjectModule.Data
{
[Serializable]
public class BaseObjectData : BaseData<BaseObject>
{
[Header("Uniqueness")]
[field: SerializeField, ReadOnly] public string guid { get; private set; } = Guid.NewGuid().ToString();
[Header("Display")]
public string name;
public Texture2D icon;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 53d2c35f3deb43588a3585e248d0a05e
timeCreated: 1774171200
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RPGCore.Core;
using RPGCore.Core.Objects;
using UnityEngine;
namespace RPGCore.ObjectModules.EventObjectModule
{
[Serializable]
[RequireComponent(typeof(BaseObject))]
public class DataModule : ObjectModule<BaseObject>
{
// SERIALIZED
[SerializeReference] private List<BaseData> _dataList = new();
// RUNTIME
private Dictionary<Type, BaseData> _dataDict;
private void OnValidate()
{
if (!parent) return;
// Remove invalid
if (_dataList.Any(data => data == null))
_dataList = _dataList.Where(data => data != null).ToList();
// Remove duplicates
if (_dataList.GroupBy(data => data.GetType()).Any(g => g.Count() > 1))
_dataList = _dataList.GroupBy(data => data.GetType()).Select(g => g.First()).ToList();
// Create missing datas
UnityEditor.TypeCache.GetTypesDerivedFrom<BaseObject>()
.Append(typeof(BaseObject))
.Where(t => t.IsAssignableFrom(parent.GetType()))
.Select(t => typeof(BaseData<>).MakeGenericType(t))
.SelectMany(t => UnityEditor.TypeCache.GetTypesDerivedFrom(t))
.Where(t => !_dataList.Select(data => data.GetType()).Contains(t))
.ToList().ForEach(t => _dataList.Add(Activator.CreateInstance(t) as BaseData));
_dataList.ForEach(data => data.parent = parent);
}
private void Awake()
{
_dataDict = _dataList.ToDictionary(data => data.GetType(), data => data);
}
public T Get<T>() where T : BaseData
{
return (T)_dataDict[typeof(T)];
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fa1c3e4ec0e74aa1a52278c024599a01
timeCreated: 1773604350
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 850e39c8cad04b979fbb825dc7821c75
timeCreated: 1761863862
@@ -0,0 +1,24 @@
using RPGCore.Core.Objects;
namespace RPGCore.ObjectModules.EventObjectModule
{
/// <summary>
/// Extend this to create new type of event for &lt;<typeparamref name="T"/>&gt; type of object.<br/><br/>
/// This event is called once by <see cref="EventModule"/>.<see cref="EventModule.Invoke{T}"/>:
/// </summary>
/// <typeparam name="T"><see cref="BaseObject"/> (or any of its children) that this event can be attached to.</typeparam>
/// <seealso cref="BasePreventableEvent{T}"/>
public abstract class BaseEvent<T> : BaseEvent where T : BaseObject
{
}
/// <summary>
/// <b>DO NOT EXTEND THIS</b>, use <see cref="BaseEvent{T}"/> instead!
/// </summary>
public abstract class BaseEvent
{
protected internal BaseEvent()
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 71103e95ef1449e096cf7e813cc339dc
timeCreated: 1761865102
@@ -0,0 +1,27 @@
using RPGCore.Core.Objects;
namespace RPGCore.ObjectModules.EventObjectModule
{
/// <summary>
/// Extend this to create new type of preventable event for &lt;<typeparamref name="T"/>&gt; type of object.<br/><br/>
/// This event can be called twice by <see cref="EventModule"/>:
/// <ul>
/// <li><see cref="EventModule.InvokeBefore{T}"/></li>
/// <li><see cref="EventModule.InvokeAfter{T}"/> - if not prevented in before</li>
/// </ul>
/// </summary>
/// <typeparam name="T"><see cref="BaseObject"/> (or any of its children) that this event can be attached to.</typeparam>
/// <seealso cref="BaseEvent{T}"/>
public abstract class BasePreventableEvent<T> : BasePreventableEvent where T : BaseObject
{
}
public abstract class BasePreventableEvent
{
public bool isPrevented;
protected internal BasePreventableEvent()
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ff4239f29db24fc787c11af5bc1fdaca
timeCreated: 1761865162
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using RPGCore.Core;
using RPGCore.Core.Objects;
namespace RPGCore.ObjectModules.EventObjectModule
{
[Serializable]
public class EventModule : ObjectModule<BaseObject>
{
// TODO: tutaj dodać serializowane eventy w postaci SerializableDictionary<SerializableType, UnityAction>
// TODO: dodatkowo mozna zmienić preventableEvents na dwa osobne słowniki before i after
internal readonly Dictionary<Type, Delegate> events = new();
internal readonly Dictionary<Type, Delegate[]> preventableEvents = new();
public void Invoke<T>(T baseEvent) where T : BaseEvent
{
events.TryAdd(typeof(T), null);
(events[typeof(T)] as Action<T>)?.Invoke(baseEvent);
}
public void Register<T>(Action<T> action) where T : BaseEvent
{
events.TryAdd(typeof(T), null);
var temp = (Action<T>)events[typeof(T)];
temp += action;
events[typeof(T)] = temp;
}
public void Unregister<T>(Action<T> action) where T : BaseEvent
{
events.TryAdd(typeof(T), null);
var temp = (Action<T>)events[typeof(T)];
temp -= action;
events[typeof(T)] = temp;
}
public void InvokeBefore<T>(T basePreventableEvent) where T : BasePreventableEvent
{
preventableEvents.TryAdd(typeof(T), new Delegate[2]);
(preventableEvents[typeof(T)][0] as Action<T>)?.Invoke(basePreventableEvent);
}
public void RegisterBefore<T>(Action<T> action) where T : BasePreventableEvent
{
preventableEvents.TryAdd(typeof(T), new Delegate[2]);
var temp = (Action<T>)preventableEvents[typeof(T)][0];
temp += action;
preventableEvents[typeof(T)][0] = temp;
}
public void UnregisterBefore<T>(Action<T> action) where T : BasePreventableEvent
{
preventableEvents.TryAdd(typeof(T), new Delegate[2]);
var temp = (Action<T>)preventableEvents[typeof(T)][0];
temp -= action;
preventableEvents[typeof(T)][0] = temp;
}
public void InvokeAfter<T>(T basePreventableEvent) where T : BasePreventableEvent
{
if (basePreventableEvent.isPrevented) throw new EventPreventedException("Event is prevented and can't be invoked!");
preventableEvents.TryAdd(typeof(T), new Delegate[2]);
(preventableEvents[typeof(T)][1] as Action<T>)?.Invoke(basePreventableEvent);
}
public void RegisterAfter<T>(Action<T> action) where T : BasePreventableEvent
{
preventableEvents.TryAdd(typeof(T), new Delegate[2]);
var temp = (Action<T>)preventableEvents[typeof(T)][1];
temp += action;
preventableEvents[typeof(T)][1] = temp;
}
public void UnregisterAfter<T>(Action<T> action) where T : BasePreventableEvent
{
preventableEvents.TryAdd(typeof(T), new Delegate[2]);
var temp = (Action<T>)preventableEvents[typeof(T)][1];
temp -= action;
preventableEvents[typeof(T)][1] = temp;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3fa533ce2d5945ec9f533cfd956a5394
timeCreated: 1761863930
@@ -0,0 +1,11 @@
using System;
namespace RPGCore.ObjectModules.EventObjectModule
{
public class EventPreventedException : Exception
{
public EventPreventedException(string message) : base(message)
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a647e5d4d4ed44c28fd6961d60943fe4
timeCreated: 1761917482
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 579fbe50273b4fa78b63536a5c316b68
timeCreated: 1762712676
@@ -0,0 +1,9 @@
using RPGCore.Core.Objects;
namespace RPGCore.ObjectModules.EventObjectModule.Events
{
public class RemoveEvent : BaseEvent<BaseObject>
{
public BaseObject obj;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 11c3329262c8465b9a3010eea4f08e69
timeCreated: 1761916133
@@ -0,0 +1,9 @@
using RPGCore.Core.Objects;
namespace RPGCore.ObjectModules.EventObjectModule.Events
{
public class SpawnEvent : BaseEvent<BaseObject>
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d57d2e777dd44799a419a6a81dc1558a
timeCreated: 1761913423
@@ -0,0 +1,11 @@
using RPGCore.Core;
using RPGCore.Core.Objects;
namespace RPGCore.ObjectModules.EventObjectModule.Events
{
public class TriggerEnterEvent : BaseEvent<BaseObject>
{
public BaseObject target;
public ITrigger trigger;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a95611010304d37ad43daf0b18dd30c
timeCreated: 1761916203
@@ -0,0 +1,11 @@
using RPGCore.Core;
using RPGCore.Core.Objects;
namespace RPGCore.ObjectModules.EventObjectModule.Events
{
public class TriggerExitEvent : BaseEvent<BaseObject>
{
public BaseObject target;
public ITrigger trigger;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d36459dce15b4f69b33ddcf21b6bdc3d
timeCreated: 1762710866