init
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d99760e987ce41308cdba70a87f65816
|
||||
timeCreated: 1762188549
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.CustomTypes
|
||||
{
|
||||
[Serializable]
|
||||
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
|
||||
{
|
||||
[SerializeField] internal TKey _tempKey;
|
||||
[SerializeField] internal List<TKey> _keys = new();
|
||||
[SerializeReference] internal List<TValue> _values = new();
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
_keys = Keys.ToList();
|
||||
_values = Values.ToList();
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Clear();
|
||||
for (var i = 0; i < _keys.Count; i++)
|
||||
{
|
||||
if (_keys[i] != null) Add(_keys[i], _values[i]);
|
||||
}
|
||||
|
||||
_keys.Clear();
|
||||
_values.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24246e0e86ad4108b4d1b8515aeca9e9
|
||||
timeCreated: 1762188557
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.CustomTypes
|
||||
{
|
||||
public class SerializableDictionaryAttribute : PropertyAttribute
|
||||
{
|
||||
public string keyName { get; private set; } = "KEY";
|
||||
public string valueName { get; private set; } = "VALUE";
|
||||
public bool isKeyEditable { get; private set; } = true;
|
||||
|
||||
public SerializableDictionaryAttribute()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public SerializableDictionaryAttribute(string keyName, string valueName, bool isKeyEditable = true)
|
||||
{
|
||||
this.keyName = keyName;
|
||||
this.valueName = valueName;
|
||||
this.isKeyEditable = isKeyEditable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4615aa55725644e48fc7cf85bd9c4ba0
|
||||
timeCreated: 1762345454
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.CustomTypes
|
||||
{
|
||||
[Serializable]
|
||||
public class SerializableType : ISerializationCallbackReceiver, ICloneable
|
||||
{
|
||||
public Type type { get; internal set; }
|
||||
[SerializeField] internal string assemblyQualifiedName;
|
||||
|
||||
public SerializableType(Type type)
|
||||
{
|
||||
this.type = type;
|
||||
assemblyQualifiedName = type?.AssemblyQualifiedName;
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
assemblyQualifiedName = type?.AssemblyQualifiedName ?? assemblyQualifiedName;
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
if (string.IsNullOrEmpty(assemblyQualifiedName) == false)
|
||||
type = Type.GetType(assemblyQualifiedName, false);
|
||||
}
|
||||
|
||||
public SerializableType Clone() => new(type);
|
||||
object ICloneable.Clone() => Clone();
|
||||
|
||||
public static implicit operator Type(SerializableType serializableType) => serializableType.type;
|
||||
|
||||
public static implicit operator SerializableType(Type type) => new(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e60b347531c46aaa17029cd96c78bf4
|
||||
timeCreated: 1762516011
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.CustomTypes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class SerializableTypeAttribute : PropertyAttribute
|
||||
{
|
||||
public bool includeSelf { get; private set; }
|
||||
public bool allowAbstract { get; private set; }
|
||||
public Type derivedFrom { get; private set; }
|
||||
public Type[] limitedTo { get; private set; }
|
||||
|
||||
public SerializableTypeAttribute(Type derivedFrom, bool includeSelf = true, bool allowAbstract = false)
|
||||
{
|
||||
this.derivedFrom = derivedFrom;
|
||||
this.includeSelf = includeSelf;
|
||||
this.allowAbstract = allowAbstract;
|
||||
}
|
||||
|
||||
public SerializableTypeAttribute(Type[] limitedTo)
|
||||
{
|
||||
this.limitedTo = limitedTo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea3922244e274799a4113964b9da4f83
|
||||
timeCreated: 1762517259
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1554117a8b14cbdb719f40f40ea71f2
|
||||
timeCreated: 1759334728
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.Exceptions
|
||||
{
|
||||
public class MemberNotFoundException : Exception
|
||||
{
|
||||
public readonly string path;
|
||||
public readonly Type objType;
|
||||
|
||||
public MemberNotFoundException(string path, Type objType)
|
||||
: base($"Property '{path}' not found in '{objType}'")
|
||||
{
|
||||
this.path = path;
|
||||
this.objType = objType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 418add21a24b4cee9309453d1ada1530
|
||||
timeCreated: 1757322594
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.Exceptions
|
||||
{
|
||||
public class MemberValueWrongTypeException : Exception
|
||||
{
|
||||
public readonly string path;
|
||||
public readonly Type objType;
|
||||
public readonly Type expectedType;
|
||||
public readonly Type wrongType;
|
||||
|
||||
public MemberValueWrongTypeException(string path, Type objType, Type expectedType, Type wrongType)
|
||||
: base($"Property '{path}' in {objType} is of type '{wrongType}', expected type '{expectedType}'")
|
||||
{
|
||||
this.path = path;
|
||||
this.objType = objType;
|
||||
this.expectedType = expectedType;
|
||||
this.wrongType = wrongType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7594d527fc654804a2c564b71fe756bd
|
||||
timeCreated: 1757322677
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.Exceptions
|
||||
{
|
||||
public class NotEnoughElementsForRandomPickException : Exception
|
||||
{
|
||||
public NotEnoughElementsForRandomPickException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25fedc34477644f8b9df4ddcf4c0b701
|
||||
timeCreated: 1756991204
|
||||
@@ -0,0 +1,8 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("RPGCoreCommon.Helpers.Editor")]
|
||||
|
||||
namespace RPGCoreCommon.Helpers
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fab5a3ff08dd4b35b7d68f496c013e8a
|
||||
timeCreated: 1762190797
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace RPGCoreCommon.Helpers
|
||||
{
|
||||
public static class LinqExtensions
|
||||
{
|
||||
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action)
|
||||
{
|
||||
foreach (var element in source) action.Invoke(element);
|
||||
return source;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<int, T> action)
|
||||
{
|
||||
var index = 0;
|
||||
foreach (var element in source) action(index++, element);
|
||||
return source;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Sort<T>(this IEnumerable<T> source, Comparison<T> comparison)
|
||||
{
|
||||
var list = source.ToList();
|
||||
list.Sort(comparison);
|
||||
return list;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Sort<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return source.OrderBy(x => x);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> SortDescending<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return source.OrderByDescending(x => x);
|
||||
}
|
||||
|
||||
public static IEnumerable<TResult> DictSelect<TKey, TValue, TResult>(this IDictionary<TKey, TValue> source, Func<TKey, TValue, TResult> func)
|
||||
{
|
||||
foreach (var pair in source) yield return func(pair.Key, pair.Value);
|
||||
}
|
||||
|
||||
public static IDictionary<TKey,TValue> DictForEach<TKey, TValue>(this IDictionary<TKey,TValue> source, Action<TKey, TValue> action)
|
||||
{
|
||||
foreach (var pair in source) action(pair.Key, pair.Value);
|
||||
return source;
|
||||
}
|
||||
|
||||
public static string StringJoin(this IEnumerable<string> source, string separator)
|
||||
{
|
||||
return string.Join(separator, source);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> AppendIf<T>(this IEnumerable<T> source, T element, bool condition)
|
||||
{
|
||||
return condition ? source.Append(element) : source;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ac97972c1444fa0aaebfdb837a58fc2
|
||||
timeCreated: 1759338756
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be7897ed56a743f98832cf13a5f9f866
|
||||
timeCreated: 1762713378
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.PropertyAttributeDrawers
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class LayerAttribute : PropertyAttribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3349d2cb583b4a56805895c7eb808a72
|
||||
timeCreated: 1762713403
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.PropertyAttributeDrawers
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||
public class LayerMaskAttribute : PropertyAttribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cfd2f0c410cf43b38901fd4bc70bb606
|
||||
timeCreated: 1762714485
|
||||
@@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.PropertyAttributeDrawers
|
||||
{
|
||||
public class ReadOnlyAttribute : PropertyAttribute
|
||||
{
|
||||
public ReadOnlyAttribute(bool applyToCollection = false) : base(applyToCollection)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 544dc97fbe8f4c258ef3a72e450297eb
|
||||
timeCreated: 1764603113
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.PropertyAttributeDrawers
|
||||
{
|
||||
public class SerializeReferenceHelperAttribute : PropertyAttribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67561284ae71463fbd4a1ec370d0fafd
|
||||
timeCreated: 1773956312
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "RPGCoreCommon.Helpers",
|
||||
"rootNamespace": "RPGCoreCommon.Helpers",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfaf89ec9b1449debf236030e69884d6
|
||||
timeCreated: 1759335092
|
||||
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RPGCoreCommon.Helpers.Exceptions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// This utility class basses on Unity's <see cref="UnityEngine.Random"/> which uses:
|
||||
/// <ul>
|
||||
/// <li><see cref="UnityEngine.Random.InitState"/> to manage seed</li>
|
||||
/// <li><see cref="UnityEngine.Random.value"/> to get random value</li>
|
||||
/// </ul>
|
||||
/// </summary>
|
||||
public static class RandomHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="UnityEngine.Random.InitState"/>
|
||||
/// Alias to Unity's <see cref="UnityEngine.Random.InitState"/>.
|
||||
/// </summary>
|
||||
/// <param name="seed"><inheritdoc cref="UnityEngine.Random.InitState" path="/param[@name='seed']"/></param>
|
||||
public static void InitState(int seed)
|
||||
{
|
||||
UnityEngine.Random.InitState(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <b>float</b> between min (inclusive) and max (inclusive).
|
||||
/// </summary>
|
||||
public static float RandomFloat(float min = 0f, float max = 1f)
|
||||
{
|
||||
return UnityEngine.Random.value * (max-min) + min;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns integer between minimum (inclusive) and maximum (inclusive).
|
||||
/// </summary>
|
||||
public static int RandomInt(int minInclusive = 0, int maxInclusive = 100)
|
||||
{
|
||||
return Mathf.RoundToInt(UnityEngine.Random.value * (maxInclusive-minInclusive)) + minInclusive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns random key from given list.
|
||||
/// </summary>
|
||||
public static int RandomKey<T>(IEnumerable<T> enumerable)
|
||||
{
|
||||
var list = enumerable.ToList();
|
||||
if (!list.Any()) throw new NotEnoughElementsForRandomPickException("List need at least one element.");
|
||||
|
||||
return RandomInt(0, list.Count - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns random element from given list.
|
||||
/// </summary>
|
||||
public static T RandomElement<T>(IEnumerable<T> enumerable)
|
||||
{
|
||||
return RandomElements(enumerable, 1).First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns new list with randomized elements.<br/>
|
||||
/// List needs to have at least equal or more elements that given count! If repeatable then list needs to have at least 1 element.<br/>
|
||||
/// </summary>
|
||||
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have enough elements to choose from.</exception>
|
||||
public static List<T> RandomElements<T>(IEnumerable<T> enumerable, int count, bool repeatable = true)
|
||||
{
|
||||
var list = enumerable.ToList();
|
||||
var neededCount = repeatable ? 1 : list.Count;
|
||||
if (list.Count < neededCount) throw new NotEnoughElementsForRandomPickException($"Not enough elements in list. Need={neededCount}, Provided={list.Count}");
|
||||
|
||||
if (!repeatable) return list.OrderBy(_ => RandomFloat()).Take(count).ToList();
|
||||
|
||||
return new T[count].Select(_ => list[RandomInt(0, list.Count - 1)]).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns keys from given list by its weight.
|
||||
/// </summary>
|
||||
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have any elements to choose from.</exception>
|
||||
public static int RandomKeyByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter)
|
||||
{
|
||||
var list = enumerable.ToList();
|
||||
if (!list.Any()) throw new NotEnoughElementsForRandomPickException("List need at least one element.");
|
||||
var maxWeight = list.Sum(weightGetter.Invoke);
|
||||
var randomWeight = RandomFloat(0, maxWeight);
|
||||
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
{
|
||||
randomWeight -= weightGetter.Invoke(list[i]);
|
||||
if (randomWeight <= 0) return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns element from given list by its weight.
|
||||
/// </summary>
|
||||
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have any elements to choose from.</exception>
|
||||
public static T RandomElementByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter)
|
||||
{
|
||||
return RandomElementsByWeight(enumerable, weightGetter, 1).First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns elements from given list by its weight.
|
||||
/// </summary>
|
||||
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have enough elements to choose from.</exception>
|
||||
public static List<T> RandomElementsByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter, int count, bool repeatable = true)
|
||||
{
|
||||
var list = enumerable.ToList();
|
||||
var listResult = new List<T>();
|
||||
var neededCount = repeatable ? 1 : list.Count;
|
||||
if (list.Count < neededCount) throw new NotEnoughElementsForRandomPickException($"Not enough elements in list. Need={neededCount}, Provided={list.Count}");
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var randomKey = RandomKeyByWeight(list, weightGetter);
|
||||
listResult.Add(list[randomKey]);
|
||||
if (!repeatable) list.RemoveAt(randomKey);
|
||||
}
|
||||
|
||||
return listResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Percentage chance by given float. Simply checks if random float is smaller than given one.
|
||||
/// </summary>
|
||||
/// <param name="chance">Chance to get true. <b>0.0f</b> = 0%, <b>1.0f</b> = 100%</param>
|
||||
public static bool Chance(float chance)
|
||||
{
|
||||
return RandomFloat() < Mathf.Clamp01(chance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd6d508bd1616174fb1350b8e4d3aebe
|
||||
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using RPGCoreCommon.Helpers.Exceptions;
|
||||
|
||||
namespace RPGCoreCommon.Helpers
|
||||
{
|
||||
public static class ReflectionExtensions
|
||||
{
|
||||
public const BindingFlags DefaultBindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
|
||||
|
||||
/// <summary>Works with any member (type, field, property, method)</summary>
|
||||
/// <returns>type of this member's value</returns>
|
||||
public static Type GetMemberValueType(this MemberInfo memberInfo)
|
||||
{
|
||||
return memberInfo switch
|
||||
{
|
||||
Type type => type,
|
||||
FieldInfo fieldInfo => fieldInfo.FieldType,
|
||||
PropertyInfo propertyInfo => propertyInfo.PropertyType,
|
||||
MethodInfo methodInfo => methodInfo.ReturnType,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Works with any member (type, field, property, method)</summary>
|
||||
/// <param name="memberInfo">this member</param>
|
||||
/// <param name="obj">Object to use for this reflection</param>
|
||||
/// <returns>member's value</returns>
|
||||
public static object GetMemberValue(this MemberInfo memberInfo, object obj)
|
||||
{
|
||||
return memberInfo switch
|
||||
{
|
||||
Type => obj,
|
||||
FieldInfo fi => fi.GetValue(obj),
|
||||
PropertyInfo pi => pi.GetValue(obj, null),
|
||||
MethodInfo mi => mi.Invoke(obj, null),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Works with any member (type, field, property, method)</summary>
|
||||
/// <param name="memberInfo">this member</param>
|
||||
/// <param name="path">path from current member with dot "." as delimiter</param>
|
||||
/// <param name="bindingFlags">Custom binding flags to use</param>
|
||||
/// <returns>TYPE of member's value from given path</returns>
|
||||
/// <exception cref="MemberNotFoundException">when member is not found</exception>
|
||||
public static Type GetMemberValueTypeByPath(this MemberInfo memberInfo, string path, BindingFlags bindingFlags = DefaultBindingFlags)
|
||||
{
|
||||
var pathParts = path.Split('.');
|
||||
var memberType = memberInfo.GetMemberValueType();
|
||||
|
||||
foreach (var pathPart in pathParts)
|
||||
{
|
||||
var member = memberType.GetMember(pathPart, bindingFlags).FirstOrDefault();
|
||||
if (member == null) throw new MemberNotFoundException(path, memberType.GetMemberValueType());
|
||||
memberType = member.GetMemberValueType();
|
||||
}
|
||||
|
||||
return memberType;
|
||||
}
|
||||
|
||||
/// <summary>This function supports all members: type, property, field, method</summary>
|
||||
/// <param name="memberInfo">Type (or any memberInfo) that will be used to get result value. When not provided memberInfo will be taken from obj type</param>
|
||||
/// <param name="obj">Object from which finding will be performed</param>
|
||||
/// <param name="path">path from current member with dot "." as delimiter</param>
|
||||
/// <param name="bindingFlags">Custom binding flags to use</param>
|
||||
/// <returns>member's value from given path</returns>
|
||||
/// <exception cref="MemberNotFoundException">When property at given path doesn't exist</exception>
|
||||
public static object GetMemberValueByPath(this MemberInfo memberInfo, object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
|
||||
{
|
||||
var pathParts = path.Split('.');
|
||||
var memberType = memberInfo.GetMemberValueType();
|
||||
var memberObj = obj;
|
||||
|
||||
foreach (var pathPart in pathParts)
|
||||
{
|
||||
var member = memberType.GetMember(pathPart, bindingFlags).FirstOrDefault();
|
||||
if (member == null) throw new MemberNotFoundException(path, memberType.GetMemberValueType());
|
||||
memberType = member.GetMemberValueType();
|
||||
memberObj = memberType.GetMemberValue(memberObj);
|
||||
}
|
||||
|
||||
return memberObj;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetMemberValueByPath(MemberInfo, object, string, BindingFlags)"/>
|
||||
/// <exception cref="MemberValueWrongTypeException">When result is not desired type</exception>
|
||||
/// <typeparam name="T">Result type</typeparam>
|
||||
public static T GetMemberValueByPath<T>(this MemberInfo memberInfo, object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
|
||||
{
|
||||
var result = GetMemberValueByPath(memberInfo, obj, path, bindingFlags);
|
||||
|
||||
if (result is T typedResult) return typedResult;
|
||||
|
||||
throw new MemberValueWrongTypeException(path, obj.GetType(), typeof(T), result.GetType());
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetMemberValueByPath(MemberInfo, object, string, BindingFlags)"/>
|
||||
public static object GetMemberValueByPath(object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
|
||||
{
|
||||
return GetMemberValueByPath(obj.GetType(), obj, path, bindingFlags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find specific generic type in currently implementation.
|
||||
/// </summary>
|
||||
/// <param name="type">This type</param>
|
||||
/// <param name="genericTypeDefinition">Generic type to find</param>
|
||||
/// <returns>Found generic type with parameters</returns>
|
||||
/// <exception cref="Exception">Generic type to find isn't generic.</exception>
|
||||
public static Type FindGenericDefinitionType(this Type type, Type genericTypeDefinition)
|
||||
{
|
||||
if (!genericTypeDefinition.IsGenericTypeDefinition) throw new Exception("The generic argument must be a generic type definition.");
|
||||
|
||||
var tempType = type;
|
||||
while (tempType != null && tempType != typeof(object))
|
||||
{
|
||||
if (tempType.IsGenericType && tempType.GetGenericTypeDefinition() == genericTypeDefinition) return tempType;
|
||||
tempType = tempType.BaseType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if specific generic type in currently implementation has type as argument at index.
|
||||
/// </summary>
|
||||
/// <param name="type">this type</param>
|
||||
/// <param name="genericTypeDefinition">Generic type to find</param>
|
||||
/// <param name="argIndex">index to check</param>
|
||||
/// <param name="argType">type in generic implementation</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsArgTypeInGenericDefinitionType(this Type type, Type genericTypeDefinition, int argIndex, Type argType)
|
||||
{
|
||||
return type.FindGenericDefinitionType(genericTypeDefinition).GenericTypeArguments.ElementAtOrDefault(argIndex) == argType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this member has public read accessor.
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">this member</param>
|
||||
/// <returns>TRUE if member exists and is public</returns>
|
||||
public static bool IsMemberReadPublic(this MemberInfo memberInfo)
|
||||
{
|
||||
return memberInfo switch
|
||||
{
|
||||
Type type => type.IsPublic,
|
||||
PropertyInfo pi => pi.GetGetMethod() != null,
|
||||
MethodInfo mi => mi.IsPublic,
|
||||
FieldInfo fi => fi.IsPublic,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> GetAllTypes(this Type forType, bool withObject = false)
|
||||
{
|
||||
var tempType = forType;
|
||||
while (tempType is not null)
|
||||
{
|
||||
yield return tempType;
|
||||
tempType = tempType.BaseType;
|
||||
if (!withObject && tempType == typeof(object)) yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8692e0796364209851d145aefa07e1c
|
||||
timeCreated: 1759334645
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5365711d18c94e37abd7c37755b59188
|
||||
timeCreated: 1761335420
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.UIElements
|
||||
{
|
||||
[UxmlElement]
|
||||
public partial class HelpBox : UnityEngine.UIElements.HelpBox
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f50a07c4f534d81a419229e92605e27
|
||||
timeCreated: 1761340341
|
||||
@@ -0,0 +1,9 @@
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.UIElements
|
||||
{
|
||||
[UxmlElement]
|
||||
public partial class TwoPaneSplitView : UnityEngine.UIElements.TwoPaneSplitView
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c45b936017280542bca4e473d23d685
|
||||
@@ -0,0 +1,78 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCoreCommon.Helpers
|
||||
{
|
||||
public static class VectorExtensions
|
||||
{
|
||||
public static Vector3 Scale(this Vector3 vector, float scale)
|
||||
{
|
||||
return new Vector3(vector.x * scale, vector.y * scale, vector.z * scale);
|
||||
}
|
||||
|
||||
public static Vector3 Scale(this Vector3 vector, float x, float y, float z)
|
||||
{
|
||||
return new Vector3(vector.x * x, vector.y * y, vector.z * z);
|
||||
}
|
||||
|
||||
public static Vector3 Add(this Vector3 vector, float x, float y, float z)
|
||||
{
|
||||
return vector.AddX(x).AddY(y).AddZ(z);
|
||||
}
|
||||
|
||||
public static Vector3 AddX(this Vector3 vector, float x)
|
||||
{
|
||||
vector.x += x;
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector3 AddY(this Vector3 vector, float y)
|
||||
{
|
||||
vector.y += y;
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector3 AddZ(this Vector3 vector, float z)
|
||||
{
|
||||
vector.z += z;
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector3 Set(this Vector3 vector, float x, float y, float z)
|
||||
{
|
||||
return vector.SetX(x).SetY(y).SetZ(z);
|
||||
}
|
||||
|
||||
public static Vector3 SetX(this Vector3 vector, float x)
|
||||
{
|
||||
vector.x = x;
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector3 SetY(this Vector3 vector, float y)
|
||||
{
|
||||
vector.y = y;
|
||||
return vector;
|
||||
}
|
||||
|
||||
public static Vector3 SetZ(this Vector3 vector, float z)
|
||||
{
|
||||
vector.z = z;
|
||||
return vector;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static Vector3 ToVector3_XZ(this Vector2 vector)
|
||||
{
|
||||
return new Vector3(vector.x, 0, vector.y);
|
||||
}
|
||||
|
||||
public static Vector3 ToVector3_XY(this Vector2 vector)
|
||||
{
|
||||
return new Vector3(vector.x, vector.y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b93c4334a254d6a9efa9ade68eb4572
|
||||
timeCreated: 1766008109
|
||||
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RPGCoreCommon.Helpers
|
||||
{
|
||||
public static class VisualElementExtensions
|
||||
{
|
||||
public static void Click(this VisualElement visualElement)
|
||||
{
|
||||
using var clickEvent = ClickEvent.GetPooled();
|
||||
clickEvent.target = visualElement;
|
||||
visualElement.panel.visualTree.SendEvent(clickEvent);
|
||||
if (visualElement is not Button button) return;
|
||||
using var ev = new NavigationSubmitEvent();
|
||||
ev.target = button;
|
||||
button.SendEvent(ev);
|
||||
}
|
||||
|
||||
public static void DisableDefaultAligning(this VisualElement visualElement)
|
||||
{
|
||||
const string class1 = "unity-base-field__inspector-field";
|
||||
const string class2 = "unity-base-field__aligned";
|
||||
visualElement.RegisterCallback<GeometryChangedEvent>(_ =>
|
||||
{
|
||||
visualElement.Query(className: class1).ForEach(el => el.RemoveFromClassList(class1));
|
||||
visualElement.Query(className: class2).ForEach(el => el.RemoveFromClassList(class2));
|
||||
}, TrickleDown.TrickleDown);
|
||||
}
|
||||
|
||||
public static void SetContextAligningHere(this VisualElement visualElement)
|
||||
{
|
||||
visualElement.AddToClassList("unity-inspector-element");
|
||||
visualElement.AddToClassList("unity-inspector-main-container");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Only for <see cref="PropertyField"/> that has array element as <see cref="SerializedProperty"/>.
|
||||
/// By default, those <see cref="PropertyField"/>s always draw inside <see cref="Foldout"/>.
|
||||
/// When this function is called only content of foldout will be displayed (foldout will be hidden).
|
||||
/// </summary>
|
||||
/// <param name="propertyField">Property field that will draw array element</param>
|
||||
public static void HideFoldoutAndLabel(this PropertyField propertyField)
|
||||
{
|
||||
propertyField.RegisterCallback<GeometryChangedEvent>(_ =>
|
||||
{
|
||||
propertyField.label = "";
|
||||
if (propertyField.Q<Foldout>(name: "unity-foldout-" + propertyField.bindingPath) is {} foldout2)
|
||||
{
|
||||
foldout2.Q<Toggle>().style.display = DisplayStyle.None;
|
||||
foldout2.Q<Toggle>().value = true;
|
||||
foldout2.Q(name: "unity-content").style.marginLeft = 0;
|
||||
}
|
||||
else if (propertyField.Q<Label>(name: "unity-input-" + propertyField.bindingPath) is {} label)
|
||||
{
|
||||
label.style.display = DisplayStyle.None;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void RemoveFoldoutAndLabel(this PropertyField propertyField)
|
||||
{
|
||||
propertyField.RegisterCallback<GeometryChangedEvent>(_ =>
|
||||
{
|
||||
if (propertyField.Q<Foldout>(name: "unity-foldout-" + propertyField.bindingPath) is {} foldout)
|
||||
{
|
||||
var children = foldout.Q(name: "unity-content").Children().ToList();
|
||||
children.ForEach(propertyField.Add);
|
||||
foldout.RemoveFromHierarchy();
|
||||
}
|
||||
else if (propertyField.Q<Label>(name: "unity-input-" + propertyField.bindingPath) is {} label)
|
||||
{
|
||||
label.RemoveFromHierarchy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void FadeOut(this VisualElement visualElement, float delay, float duration, bool remove)
|
||||
{
|
||||
if (visualElement.panel != null)
|
||||
FadeOutPrivate(visualElement, delay, duration, remove);
|
||||
else
|
||||
visualElement.RegisterCallback<AttachToPanelEvent>(_ => FadeOutPrivate(visualElement, delay, duration, remove));
|
||||
|
||||
}
|
||||
|
||||
private static void FadeOutPrivate(VisualElement visualElement, float delayMs, float durationMs, bool remove)
|
||||
{
|
||||
const int waitMs = 50;
|
||||
visualElement.style.opacity = 1f;
|
||||
visualElement.schedule
|
||||
.Execute(_ =>
|
||||
{
|
||||
visualElement.style.opacity = visualElement.style.opacity.value - waitMs / durationMs;
|
||||
if (visualElement.style.opacity.value > 0f) return;
|
||||
visualElement.style.opacity = 0f;
|
||||
if (remove) visualElement.RemoveFromHierarchy();
|
||||
})
|
||||
.Every(waitMs)
|
||||
.Until(() => visualElement.style.opacity.value <= 0f)
|
||||
.ExecuteLater(Convert.ToInt64(delayMs));
|
||||
}
|
||||
|
||||
public static void RemoveOnClick(this VisualElement visualElement, float delayMs = 0f, float durationMs = 250f)
|
||||
{
|
||||
visualElement.RegisterCallback<ClickEvent>(_ => FadeOutPrivate(visualElement, delayMs, durationMs, true));
|
||||
}
|
||||
|
||||
public static void InsertBefore(this VisualElement thisElement, VisualElement otherElement)
|
||||
{
|
||||
var index = thisElement.parent.IndexOf(thisElement);
|
||||
thisElement.parent.Insert(index, otherElement);
|
||||
}
|
||||
|
||||
public static void InsertAfter(this VisualElement thisElement, VisualElement otherElement)
|
||||
{
|
||||
var index = thisElement.parent.IndexOf(thisElement);
|
||||
if (index == thisElement.parent.childCount-1)
|
||||
thisElement.parent.Add(otherElement);
|
||||
else
|
||||
thisElement.parent.Insert(index, otherElement);
|
||||
}
|
||||
|
||||
public static bool IsAncestorOf(this VisualElement thisElement, VisualElement otherElement)
|
||||
{
|
||||
if (thisElement == null || otherElement == null) return false;
|
||||
|
||||
var ancestor = otherElement.parent;
|
||||
while (ancestor != null)
|
||||
{
|
||||
if (ancestor == thisElement) return true;
|
||||
ancestor = ancestor.parent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsDescendantOf(this VisualElement thisElement, VisualElement otherElement)
|
||||
{
|
||||
return otherElement.IsAncestorOf(thisElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b3f87689a16482f8bcb2e1869729299
|
||||
timeCreated: 1761508983
|
||||
Reference in New Issue
Block a user