init
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49335e95b3ea4fb7a07dbe4feb5ba86e
|
||||
timeCreated: 1759057728
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7fc7377b1d5469abc7437b0ae10b63e
|
||||
timeCreated: 1761485555
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11f768f38d9c4fb18dc357fde085548a
|
||||
timeCreated: 1761774372
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67aaf260711d4718a2d97c29246a5ec7
|
||||
timeCreated: 1761919244
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
using RPGCoreCommon.Helpers;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RPGCore.Editor.Drawers.SceneModules.Event
|
||||
{
|
||||
[CustomEditor(typeof(EventModule))]
|
||||
public class EventModuleDrawer : UnityEditor.Editor
|
||||
{
|
||||
private class DrawerContext
|
||||
{
|
||||
public SerializedObject obj;
|
||||
public List<Type> events = new();
|
||||
public List<Type> preventableEvents = new();
|
||||
}
|
||||
|
||||
public override VisualElement CreateInspectorGUI()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
var property = serializedObject.GetIterator();
|
||||
|
||||
var enterChildren = true;
|
||||
while (property.NextVisible(enterChildren))
|
||||
{
|
||||
root.Add(new PropertyField(property) { enabledSelf = property.name != "m_Script" });
|
||||
enterChildren = false;
|
||||
}
|
||||
|
||||
root.Add(CreateEventList());
|
||||
return root;
|
||||
}
|
||||
|
||||
private VisualElement CreateEventList()
|
||||
{
|
||||
if (!EditorApplication.isPlaying)
|
||||
return new HelpBox("Subscribed events preview available only in play mode.", HelpBoxMessageType.Info);
|
||||
|
||||
var wrapper = new VisualElement();
|
||||
|
||||
var list1 = new Foldout();
|
||||
list1.text = "<b>Events</b> registered:";
|
||||
list1.style.marginLeft = 12;
|
||||
list1.Add(new Label("List empty."));
|
||||
wrapper.Add(list1);
|
||||
|
||||
var list2 = new Foldout();
|
||||
list2.text = "<b>Preventable Events (before)</b> registered:";
|
||||
list2.style.marginLeft = 12;
|
||||
list2.Add(new Label("List empty."));
|
||||
wrapper.Add(list2);
|
||||
|
||||
var list3 = new Foldout();
|
||||
list3.text = "<b>Preventable Events (after)</b> registered:";
|
||||
list3.style.marginLeft = 12;
|
||||
list3.Add(new Label("List empty."));
|
||||
wrapper.Add(list3);
|
||||
|
||||
var context = new DrawerContext{obj = serializedObject};
|
||||
wrapper.schedule.Execute(() => Refresh(context, list1, list2, list3)).Every(1000);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private void Refresh(DrawerContext context, VisualElement list, VisualElement beforeList, VisualElement afterList)
|
||||
{
|
||||
var eventModule = context.obj.targetObject as EventModule;
|
||||
RefreshList(context.events, eventModule.events, list);
|
||||
RefreshList(context.preventableEvents, eventModule.preventableEvents.ToDictionary(pair => pair.Key, pair => pair.Value[0]), beforeList);
|
||||
RefreshList(context.preventableEvents, eventModule.preventableEvents.ToDictionary(pair => pair.Key, pair => pair.Value[1]), afterList);
|
||||
|
||||
context.events = eventModule.events.Keys.ToList();
|
||||
context.preventableEvents = eventModule.preventableEvents.Keys.ToList();
|
||||
}
|
||||
|
||||
private void RefreshList(List<Type> before, Dictionary<Type, Delegate> after, VisualElement list)
|
||||
{
|
||||
var toRemove = before.Except(after.Keys).ToList();
|
||||
var toAdd = after.Keys.Except(before).ToList();
|
||||
|
||||
if (!toRemove.Any() && !toAdd.Any()) return;
|
||||
|
||||
list.Clear();
|
||||
after.DictSelect((type, action) =>
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.style.flexDirection = FlexDirection.Row;
|
||||
row.Add(new HelpBox { text = type.Name });
|
||||
|
||||
var callers = action?.GetInvocationList().Select(a => $"{a.Target.GetType().Name} : {a.Method.Name}").StringJoin("\n");
|
||||
if (string.IsNullOrEmpty(callers)) callers = "<u>No registered callers</u>";
|
||||
row.Add(string.IsNullOrEmpty(callers) ? new Label() : new HelpBox { text = callers });
|
||||
|
||||
return row;
|
||||
|
||||
}).ForEach(list.Add);
|
||||
|
||||
if (!after.Any()) list.Add(new Label("List empty."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3379740cce21445b882d2175c6f699ce
|
||||
timeCreated: 1761919852
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d95afed367c4251b29cc8c3137f99dd
|
||||
timeCreated: 1761774365
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa8e75168dd14631a1776e9eb6f4b0fd
|
||||
timeCreated: 1761839848
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Linq;
|
||||
using RPGCore.SceneModules.PathVisualizerSceneModule;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RPGCore.Editor.Drawers.SceneModules.PathVisualizer
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(PathVisualizerModule))]
|
||||
public class PathVisualizerModuleDrawer : PropertyDrawer
|
||||
{
|
||||
public override VisualElement CreatePropertyGUI(SerializedProperty property)
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
root.Add(new PropertyField(property));
|
||||
|
||||
if (EditorApplication.isPlaying)
|
||||
{
|
||||
root.Add(CreateList(property));
|
||||
}
|
||||
else
|
||||
{
|
||||
root.Add(new HelpBox("Subscribed events preview available only in play mode.", HelpBoxMessageType.Info));
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private VisualElement CreateList(SerializedProperty property)
|
||||
{
|
||||
var wrapper = new VisualElement();
|
||||
|
||||
var helpBox = new HelpBox();
|
||||
helpBox.text = "Usable only runtime via script.<br>Below is list of currently managed paths.";
|
||||
helpBox.messageType = HelpBoxMessageType.Info;
|
||||
helpBox.style.marginTop = 15;
|
||||
wrapper.Add(helpBox);
|
||||
|
||||
var list = new VisualElement();
|
||||
list.schedule.Execute(() => RefreshList(property, list)).Every(2500);
|
||||
list.Add(new Label("List empty."));
|
||||
wrapper.Add(list);
|
||||
|
||||
RefreshList(property, list);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
private void RefreshList(SerializedProperty property, VisualElement list)
|
||||
{
|
||||
var pathVisualizers = (property.boxedValue as PathVisualizerModule).pathVisualizers;
|
||||
|
||||
// No elements, only label with info
|
||||
if (pathVisualizers.Count == 0 && list.Children().FirstOrDefault() is Label) return;
|
||||
|
||||
// Nothing changed
|
||||
if (pathVisualizers.Count == list.childCount && list.Children().FirstOrDefault() is not Label) return;
|
||||
|
||||
list.Clear();
|
||||
|
||||
if (!pathVisualizers.Any())
|
||||
{
|
||||
list.Add(new Label("List empty."));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var pathVisualizer in pathVisualizers)
|
||||
{
|
||||
if (!pathVisualizer) continue;
|
||||
var objectField = new ObjectField();
|
||||
objectField.value = pathVisualizer;
|
||||
objectField.SetEnabled(false);
|
||||
list.Add(objectField);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 774f2a8b09b7472c982b804adde32e41
|
||||
timeCreated: 1761839859
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "RPGCore.Editor",
|
||||
"rootNamespace": "RPGCore.Editor",
|
||||
"references": [
|
||||
"RPGCore",
|
||||
"RPGCoreCommon.Helpers"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06fa85d8d22f490491ae2a4a024e353a
|
||||
timeCreated: 1759057737
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b92b1f3ab7748eeaa2e25278725fa75
|
||||
timeCreated: 1759057688
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 488b2a899d8449a9afedbbd05043acf9
|
||||
timeCreated: 1761773545
|
||||
@@ -0,0 +1,17 @@
|
||||
using RPGCoreCommon.Helpers.PropertyAttributeDrawers;
|
||||
using RPGCoreCommon.Settings;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Core
|
||||
{
|
||||
[CustomSettings("RPG Core/Main Settings")]
|
||||
public class CoreSettings : CustomSettingsSO
|
||||
{
|
||||
[Header("Layers")]
|
||||
[Layer] public int dynamicLayer;
|
||||
[Layer] public int staticLayer;
|
||||
[Layer] public int staticHideableLayer;
|
||||
[Layer] public int triggerLayer;
|
||||
[Layer] public int cameraLayer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae66b654cdd34f0eb7cb566026a04553
|
||||
timeCreated: 1761916774
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Extend this abstract class to create new module for global logic.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class GlobalModule : MonoBehaviour
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bfe3de119af4cca916793ab7ab84f34
|
||||
timeCreated: 1761424144
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace RPGCore.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// With this interface you can create your own list of requirements for specific <see cref="Type"/> (and its implementations).
|
||||
/// <see cref="Check"/> returns TRUE if defined requirement is met, otherwise FALSE.
|
||||
/// Thanks to this we can create list of reusable requirements that further can be used to gate logic.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type for which this requirement is</typeparam>
|
||||
public interface IRequirement<in T>
|
||||
{
|
||||
public bool Check(T obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e865880889b3491aa34c60bc2fbca102
|
||||
timeCreated: 1764886296
|
||||
@@ -0,0 +1,15 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface allow any <see cref="BaseObject"/> to call <see cref="OnEnter"/> and <see cref="OnExit"/>.<br/>
|
||||
/// Setting <see cref="Collider"/>'s <see cref="Collider.isTrigger"/> to true is required!<br/>
|
||||
/// </summary>
|
||||
public interface ITrigger
|
||||
{
|
||||
public void OnEnter(BaseObject obj);
|
||||
public void OnExit(BaseObject obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c44b68608de84013b92b61d898ea8e2a
|
||||
timeCreated: 1761916614
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using RPGCore.Core.Objects;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Extend this abstract class to create new module for <<typeparamref name="T"/>> object type.<br/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of object that this module will be attached to. Can be <see cref="BaseObject"/> or any of its children.</typeparam>
|
||||
[Serializable]
|
||||
public abstract class ObjectModule<T> : ObjectModule where T : BaseObject
|
||||
{
|
||||
public new T parent => (T)base.parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <b>DO NOT EXTEND THIS</b>, use <see cref="ObjectModule{T}"/> instead!
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class ObjectModule : MonoBehaviour
|
||||
{
|
||||
private BaseObject _parent;
|
||||
internal BaseObject parent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_parent) _parent = GetComponent<BaseObject>();
|
||||
return _parent;
|
||||
}
|
||||
set => _parent = value;
|
||||
}
|
||||
|
||||
private protected ObjectModule()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c6794c4f1c6dd149860a41f06978cad
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 182b5998cfbb6644da7a90e3b1765485
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using RPGCore.ObjectModules.ActionObjectModule;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
using RPGCore.ObjectModules.EventObjectModule.Events;
|
||||
using RPGCoreCommon.DynamicValues;
|
||||
using RPGCoreCommon.Helpers;
|
||||
using RPGCoreCommon.Helpers.PropertyAttributeDrawers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Core.Objects
|
||||
{
|
||||
[SelectionBase]
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
[RequireComponent(typeof(EventModule))]
|
||||
[RequireComponent(typeof(ActionModule))]
|
||||
[RequireComponent(typeof(DataModule))]
|
||||
public abstract class BaseObject : MonoBehaviour
|
||||
{
|
||||
[field: SerializeField, ReadOnly] public new Rigidbody rigidbody { get; private set; }
|
||||
[field: SerializeField, ReadOnly] public EventModule events { get; private set; }
|
||||
[field: SerializeField, ReadOnly] public ActionModule actions { get; private set; }
|
||||
[field: SerializeField, ReadOnly] public DataModule data { get; private set; }
|
||||
|
||||
[DynamicValueProvider]
|
||||
private ObjectModule<BaseObject> BaseModuleProvider(Type moduleType) => GetComponent(moduleType) as ObjectModule<BaseObject>;
|
||||
|
||||
protected void Start()
|
||||
{
|
||||
GetComponent<EventModule>().Invoke(new SpawnEvent());
|
||||
}
|
||||
|
||||
protected void OnValidate()
|
||||
{
|
||||
rigidbody = GetComponent<Rigidbody>();
|
||||
events = GetComponent<EventModule>();
|
||||
actions = GetComponent<ActionModule>();
|
||||
data = GetComponent<DataModule>();
|
||||
GetComponents<ObjectModule>().ForEach(module => module.parent = this);
|
||||
}
|
||||
|
||||
/// <summary>Removes this object from game.</summary>
|
||||
public void Remove()
|
||||
{
|
||||
events.Invoke(new RemoveEvent{ obj = this });
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
/// <summary>It'll execute <see cref="ITrigger"/>.<see cref="ITrigger.OnEnter"/> when this object enter its collider.</summary>
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
other.GetComponentsInParent<ITrigger>().ForEach(trigger => {
|
||||
trigger.OnEnter(this);
|
||||
events.Invoke(new TriggerEnterEvent { target = this, trigger = trigger });
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>It'll execute <see cref="ITrigger"/>.<see cref="ITrigger.OnExit"/> when this object exit its collider.</summary>
|
||||
private void OnTriggerExit(Collider other)
|
||||
{
|
||||
other.GetComponentsInParent<ITrigger>().ForEach(trigger => {
|
||||
trigger.OnExit(this);
|
||||
events.Invoke(new TriggerExitEvent { target = this, trigger = trigger });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2798185b793dc9498d74d746f680c1c
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using RPGCoreCommon.DynamicValues;
|
||||
using RPGCoreCommon.Helpers.PropertyAttributeDrawers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Core.Objects
|
||||
{
|
||||
[RequireComponent(typeof(CapsuleCollider))]
|
||||
public class UnitObject : BaseObject
|
||||
{
|
||||
[DynamicValueProvider]
|
||||
private ObjectModule<UnitObject> UnitModuleProvider(Type moduleType) => GetComponent(moduleType) as ObjectModule<UnitObject>;
|
||||
|
||||
[field: SerializeField, ReadOnly] public CapsuleCollider unitCollider { get; private set; }
|
||||
|
||||
protected new void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
unitCollider = GetComponent<CapsuleCollider>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0e44d2cfbb316343b9be0d7bee42909
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Extend this abstract class to create new module for scene logic.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class SceneModule : MonoBehaviour
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee04853ad1f04cf097b8b6c946f67896
|
||||
timeCreated: 1761424233
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b96ac46c8c044213bc24947f701eba38
|
||||
timeCreated: 1761773609
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edd53e70728b4e699b060546ad967812
|
||||
timeCreated: 1761769944
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.GlobalModules.ProfileManagerGlobalModule
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class Profile<T> : Profile where T : class
|
||||
{
|
||||
public new T data
|
||||
{
|
||||
set => base.data = value;
|
||||
get => base.data as T;
|
||||
}
|
||||
|
||||
public Profile() : this(Environment.MachineName)
|
||||
{
|
||||
}
|
||||
|
||||
public Profile(string profileName)
|
||||
{
|
||||
name = profileName;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Profile
|
||||
{
|
||||
private protected Profile()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public readonly string guid = Guid.NewGuid().ToString();
|
||||
public readonly string version = Application.version;
|
||||
public string name;
|
||||
public object data;
|
||||
|
||||
public static bool operator ==(Profile obj1, Profile obj2) => obj1?.guid == obj2?.guid;
|
||||
public static bool operator !=(Profile obj1, Profile obj2) => !(obj1 == obj2);
|
||||
public override bool Equals(object obj) => obj is Profile other && guid == other.guid;
|
||||
public override int GetHashCode() => guid.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6fd81ed96154f9c807c110c191c8cdc
|
||||
timeCreated: 1761770134
|
||||
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using RPGCore.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.GlobalModules.ProfileManagerGlobalModule
|
||||
{
|
||||
[Serializable]
|
||||
public class ProfileManagerModule : GlobalModule
|
||||
{
|
||||
// Paths
|
||||
private static string directoryPath => Path.Combine(Application.persistentDataPath, "profiles");
|
||||
private static string fileNameTemplate => "{0}_{1}.profile";
|
||||
private static string fileNameMask => "*.profile";
|
||||
private static string fileNameWithDefaultProfile => "default";
|
||||
|
||||
// Runtime
|
||||
private Profile _activeProfile;
|
||||
public event Action<Profile> OnProfileChange;
|
||||
|
||||
public Profile<T> LoadDefault<T>() where T : class
|
||||
{
|
||||
var defaultPath = Path.Combine(directoryPath, fileNameWithDefaultProfile);
|
||||
|
||||
// Default file not found
|
||||
if (!File.Exists(defaultPath)) return null;
|
||||
var guid = File.ReadAllText(defaultPath);
|
||||
|
||||
// Profile not found, by guid from default file
|
||||
var file = Directory.GetFiles(directoryPath, string.Format(fileNameTemplate, "*", guid)).FirstOrDefault();
|
||||
if (file == null) return null;
|
||||
|
||||
return JsonUtility.FromJson<Profile<T>>(File.ReadAllText(file));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets currently active profile
|
||||
/// </summary>
|
||||
public Profile<T> Get<T>() where T : class
|
||||
{
|
||||
return _activeProfile as Profile<T>;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse all profiles from files and returns them.
|
||||
/// There is no caching, it will scan files everytime it is called.
|
||||
/// </summary>
|
||||
public List<Profile<T>> LoadAll<T>() where T : class
|
||||
{
|
||||
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
|
||||
|
||||
return Directory.GetFiles(directoryPath, fileNameMask)
|
||||
.Select(File.ReadAllText)
|
||||
.Select(JsonUtility.FromJson<Profile<T>>)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new profile with given name and saves it as file.
|
||||
/// </summary>
|
||||
public Profile<T> Create<T>(string name) where T : class
|
||||
{
|
||||
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
|
||||
|
||||
var newProfile = Activator.CreateInstance<Profile<T>>();
|
||||
newProfile.name = name;
|
||||
Save(newProfile);
|
||||
|
||||
return newProfile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes given profile from files and if its currently active unselects it.
|
||||
/// </summary>
|
||||
/// <param name="playerProfile"></param>
|
||||
public void Delete<T>(Profile<T> playerProfile) where T : class
|
||||
{
|
||||
if (playerProfile == _activeProfile) Select<T>(null);
|
||||
|
||||
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
|
||||
|
||||
File.Delete(GetFullPathFor(playerProfile));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves currently active profile as file.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
Save(_activeProfile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves given profile as file.
|
||||
/// </summary>
|
||||
private void Save(Profile profile)
|
||||
{
|
||||
if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath);
|
||||
File.WriteAllText(GetFullPathFor(profile), JsonUtility.ToJson(profile, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set active given profile. Will be selected automatically next time.
|
||||
/// </summary>
|
||||
public void Select<T>(Profile<T> playerProfile) where T : class
|
||||
{
|
||||
_activeProfile = playerProfile;
|
||||
OnProfileChange?.Invoke(_activeProfile);
|
||||
|
||||
// No profile found - create it for user
|
||||
_activeProfile ??= Create<T>("New Profile");
|
||||
|
||||
// Our selected profile will be default one when starting game again
|
||||
File.WriteAllText(Path.Combine(directoryPath, fileNameWithDefaultProfile), _activeProfile.guid);
|
||||
}
|
||||
|
||||
private string GetFullPathFor(Profile playerProfile)
|
||||
{
|
||||
var fileName = string.Format(fileNameTemplate, playerProfile.name, playerProfile.guid);
|
||||
return Path.Combine(directoryPath, fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 453d820269db447db331f632048a4215
|
||||
timeCreated: 1761769954
|
||||
@@ -0,0 +1,8 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("RPGCore.Editor")]
|
||||
|
||||
namespace RPGCore
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc35dedc7f3842f683276368e463c9cf
|
||||
timeCreated: 1761496306
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc81f48411244970b9151551a021ad98
|
||||
timeCreated: 1761773601
|
||||
@@ -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 <<typeparamref name="T"/>> type of object.<br/>
|
||||
/// To retrieve this data from <<typeparamref name="T"/>> 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 <<typeparamref name="T"/>> 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 <<typeparamref name="T"/>> 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
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "RPGCore",
|
||||
"rootNamespace": "RPGCore",
|
||||
"references": [
|
||||
"RPGCoreCommon.Settings",
|
||||
"RPGCoreCommon.Helpers",
|
||||
"RPGCoreCommon.DynamicValues",
|
||||
"Unity.InputSystem"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91139c31a3a64b34786bf786ad0bafe6
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1b8f1cf4fa940859c0492e9733264fb
|
||||
timeCreated: 1761773615
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d9da26ac20749b18818b4a4cefe65ea
|
||||
timeCreated: 1761774471
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace RPGCore.SceneModules.PathVisualizerSceneModule
|
||||
{
|
||||
public class PathVisualizer : MonoBehaviour
|
||||
{
|
||||
private Func<IEnumerable<Vector3>> _pathGetter;
|
||||
private LineRenderer _lineRenderer;
|
||||
|
||||
private float _autoUpdateDelta;
|
||||
|
||||
public bool autoUpdate;
|
||||
public float autoUpdateTime;
|
||||
public float verticalOffset;
|
||||
|
||||
public float lineSize
|
||||
{
|
||||
get => _lineRenderer.startWidth;
|
||||
set => _lineRenderer.startWidth = _lineRenderer.endWidth = value;
|
||||
}
|
||||
|
||||
public Material material
|
||||
{
|
||||
get => _lineRenderer.material;
|
||||
set => _lineRenderer.material = value;
|
||||
}
|
||||
|
||||
public void SetPath(Vector3 start, Vector3 end, int navMeshAreaMask)
|
||||
{
|
||||
var path = new NavMeshPath();
|
||||
NavMesh.CalculatePath(start, end, navMeshAreaMask, path);
|
||||
_pathGetter = () => path.corners;
|
||||
UpdatePath();
|
||||
}
|
||||
|
||||
public void SetPath(NavMeshAgent agent, Vector3 destination)
|
||||
{
|
||||
var path = new NavMeshPath();
|
||||
agent.CalculatePath(destination, path);
|
||||
_pathGetter = () => path.corners;
|
||||
UpdatePath();
|
||||
}
|
||||
|
||||
public void SetPath(NavMeshPath path)
|
||||
{
|
||||
_pathGetter = () => path.corners;
|
||||
UpdatePath();
|
||||
}
|
||||
|
||||
public void SetPath(IEnumerable<Transform> customPath)
|
||||
{
|
||||
_pathGetter = () => customPath.Select(t => t.position);
|
||||
UpdatePath();
|
||||
}
|
||||
|
||||
public void SetPath(IEnumerable<Vector3> customPath)
|
||||
{
|
||||
_pathGetter = () => customPath;
|
||||
UpdatePath();
|
||||
}
|
||||
|
||||
public void SetPath(Func<IEnumerable<Vector3>> pathGetter)
|
||||
{
|
||||
_pathGetter = pathGetter;
|
||||
UpdatePath();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_lineRenderer = gameObject.AddComponent<LineRenderer>();
|
||||
_lineRenderer.enabled = true;
|
||||
_lineRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
_lineRenderer.numCapVertices = 3;
|
||||
_lineRenderer.numCornerVertices = 3;
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
if (!autoUpdate) return;
|
||||
_autoUpdateDelta += Time.fixedDeltaTime;
|
||||
if (_autoUpdateDelta < autoUpdateTime) return;
|
||||
_autoUpdateDelta = 0;
|
||||
UpdatePath();
|
||||
}
|
||||
|
||||
private void UpdatePath()
|
||||
{
|
||||
if (_pathGetter == null) return;
|
||||
var path = _pathGetter().Select(pos => pos + Vector3.up * verticalOffset).ToArray();
|
||||
_lineRenderer.positionCount = path.Length;
|
||||
_lineRenderer.SetPositions(path);
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
_lineRenderer.enabled = true;
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
_lineRenderer.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fdd2eabc020430e881b60cda5926a0a
|
||||
timeCreated: 1761830900
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RPGCore.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.SceneModules.PathVisualizerSceneModule
|
||||
{
|
||||
[Serializable]
|
||||
public class PathVisualizerModule : SceneModule
|
||||
{
|
||||
// Configuration
|
||||
[SerializeField] private float _defaultAutoUpdateTime = 0.1f;
|
||||
[SerializeField] private float _defaultLineSize = 0.05f;
|
||||
[SerializeField] private float _defaultVerticalOffset = 0.1f;
|
||||
[SerializeField] private Material _defaultMaterial;
|
||||
|
||||
// References
|
||||
internal List<PathVisualizer> pathVisualizers = new();
|
||||
|
||||
public PathVisualizer Create()
|
||||
{
|
||||
var gameObject = new GameObject("PathVisualizer");
|
||||
gameObject.transform.parent = this.gameObject.transform;
|
||||
|
||||
var pathVisualizer = gameObject.AddComponent<PathVisualizer>();
|
||||
pathVisualizers.Add(pathVisualizer);
|
||||
pathVisualizer.autoUpdateTime = _defaultAutoUpdateTime;
|
||||
pathVisualizer.lineSize = _defaultLineSize;
|
||||
pathVisualizer.verticalOffset = _defaultVerticalOffset;
|
||||
pathVisualizer.material = _defaultMaterial;
|
||||
return pathVisualizer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6eeee4c859ea44119fd2347e9b778b72
|
||||
timeCreated: 1761830760
|
||||
Reference in New Issue
Block a user