using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using LiteNetLib; using MessagePack; using Unity.Mathematics; using static Unity.Mathematics.math; using Random = Unity.Mathematics.Random; using Unity.Tiny; public static class Extensions { //public static IDatabaseEntry Get(this Guid entry) => Database.Get(entry); //public static T Get(this Guid entry) where T : class, IDatabaseEntry => Database.Get(entry) as T; public static string ToJson(this DatabaseEntry entry) => MessagePackSerializer.SerializeToJson(entry); public static byte[] Serialize(this DatabaseEntry entry) => MessagePackSerializer.Serialize(entry); public static bool IsImplementationOf(this Type baseType, Type interfaceType) { return baseType.GetInterfaces().Any(interfaceType.Equals); } private static readonly Dictionary Quality = new Dictionary(); public static float CompoundQuality(this CraftedItemInstance item) { if (Quality.ContainsKey(item)) return Quality[item]; var quality = item.Quality; var craftedIngredients = item.Ingredients.Where(i => i is CraftedItemInstance).ToArray(); if (craftedIngredients.Length > 0) { quality *= craftedIngredients.Cast().Average(CompoundQuality); } Quality[item] = quality; return Quality[item]; } public static void Send(this NetPeer peer, T message, DeliveryMethod method = DeliveryMethod.ReliableOrdered) where T : Message { peer.Send(MessagePackSerializer.Serialize(message as Message), method); } public static float Performance(this EquippableItemData itemData, float temperature) { return saturate(itemData.HeatPerformanceCurve.Evaluate(saturate( (temperature - itemData.MinimumTemperature) / (itemData.MaximumTemperature - itemData.MinimumTemperature)))); } public static string SplitCamelCase( this string str ) { return Regex.Replace( Regex.Replace( str, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2" ), @"(\p{Ll})(\P{Ll})", "$1 $2" ); } private static Random? _random; private static Random Random => (Random) (_random ?? (_random = new Random((uint) (DateTime.Now.Ticks%uint.MaxValue)))); // public static T RandomElement(this IEnumerable enumerable) => enumerable.ElementAt(Random.NextInt(0, enumerable.Count())); public static float NextUnbounded(this ref Random random) => 1 / (1 - random.NextFloat()) - 1; public static float NextUnbounded(this ref Random random, float bias, float power, float ceiling) => 1 / (1 - pow(min(random.NextFloat(), ceiling), 1 - pow(clamp(bias,0,.99f), 1 / power))) - 1; private static Dictionary InterfaceClasses = new Dictionary(); public static Type[] GetAllInterfaceClasses(this Type type) { if (InterfaceClasses.ContainsKey(type)) return InterfaceClasses[type]; return InterfaceClasses[type] = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(ass => ass.GetTypes()).Where(t => t.IsClass && t.GetInterfaces().Contains(type)).ToArray(); } private static Dictionary ParentTypes = new Dictionary(); public static Type[] GetParentTypes(this Type type) { if (ParentTypes.ContainsKey(type)) return ParentTypes[type]; return ParentTypes[type] = type.GetParents().ToArray(); } private static IEnumerable GetParents(this Type type) { // is there any base type? if (type == null) { yield break; } yield return type; // return all implemented or inherited interfaces foreach (var i in type.GetInterfaces()) { yield return i; } // return all inherited types var currentBaseType = type.BaseType; while (currentBaseType != null) { yield return currentBaseType; currentBaseType= currentBaseType.BaseType; } } private static Dictionary ChildClasses = new Dictionary(); public static Type[] GetAllChildClasses(this Type type) { if (ChildClasses.ContainsKey(type)) return ChildClasses[type]; return ChildClasses[type] = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(ass => ass.GetTypes()).Where(type.IsAssignableFrom).ToArray(); } public static bool IsDefault(this T value) where T : struct { bool isDefault = value.Equals(default(T)); return isDefault; } public static bool IsNull(this KeyValuePair pair) { return pair.Equals(new KeyValuePair()); } private static Dictionary _cachedIndices = new Dictionary(); public static float Evaluate(this float4[] curve, float time) { // Clamp time time = math.clamp(time, curve[0].x, curve[curve.Length - 1].x); FindSurroundingKeyframes(time, curve); return HermiteInterpolate(time, curve[_cachedIndices[curve].x], curve[_cachedIndices[curve].y]); } static void FindSurroundingKeyframes(float time, float4[] curve) { // Check that time is within cached keyframe time if (_cachedIndices.ContainsKey(curve) && _cachedIndices[curve].x != _cachedIndices[curve].y && time >= curve[_cachedIndices[curve].x].x && time <= curve[_cachedIndices[curve].y].x) { return; } // Fall back to using dichotomic search. var length = curve.Length; int half; int middle; int first = 0; while (length > 0) { half = length >> 1; middle = first + half; if (time < curve[middle].x) { length = half; } else { first = middle + 1; length = length - half - 1; } } // If not within range, we pick the last element twice. var indices = int2(first - 1, math.min(curve.Length - 1, first)); _cachedIndices[curve] = indices; } static float HermiteInterpolate(float time, in float4 leftKeyframe, in float4 rightKeyframe) { // Handle stepped curve. if (math.isinf(leftKeyframe.w) || math.isinf(rightKeyframe.z)) { return leftKeyframe.y; } float dx = rightKeyframe.x - leftKeyframe.x; float m0; float m1; float t; if (dx != 0.0f) { t = (time - leftKeyframe.x) / dx; m0 = leftKeyframe.w * dx; m1 = rightKeyframe.z * dx; } else { t = 0.0f; m0 = 0; m1 = 0; } return HermiteInterpolate(t, leftKeyframe.y, m0, m1, rightKeyframe.y); } static float HermiteInterpolate(float t, float p0, float m0, float m1, float p1) { // Unrolled the equations to avoid precision issue. // (2 * t^3 -3 * t^2 +1) * p0 + (t^3 - 2 * t^2 + t) * m0 + (-2 * t^3 + 3 * t^2) * p1 + (t^3 - t^2) * m1 var a = 2.0f * p0 + m0 - 2.0f * p1 + m1; var b = -3.0f * p0 - 2.0f * m0 + 3.0f * p1 - m1; var c = m0; var d = p0; return t * (t * (a * t + b) + c) + d; } public static float AngleDiff(this float2 a, float2 b) { var directionAngle = atan2(b.y, b.x); var currentAngle = atan2(a.y, a.x); var d1 = directionAngle - currentAngle; var d2 = directionAngle - 2 * PI - currentAngle; var d3 = directionAngle + 2 * PI - currentAngle; return abs(d1) < abs(d2) ? abs(d1) < abs(d3) ? d1 : d3 : abs(d2) < abs(d3) ? d2 : d3; } } // https://github.com/Burtsev-Alexey/net-object-deep-copy public static class ObjectExtensions { private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); public static bool IsPrimitive(this Type type) { if (type == typeof(String)) return true; return (type.IsValueType & type.IsPrimitive); } public static Object Copy(this Object originalObject) { return InternalCopy(originalObject, new Dictionary(new ReferenceEqualityComparer())); } private static Object InternalCopy(Object originalObject, IDictionary visited) { if (originalObject == null) return null; var typeToReflect = originalObject.GetType(); if (IsPrimitive(typeToReflect)) return originalObject; if (visited.ContainsKey(originalObject)) return visited[originalObject]; if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null; var cloneObject = CloneMethod.Invoke(originalObject, null); if (typeToReflect.IsArray) { var arrayType = typeToReflect.GetElementType(); if (IsPrimitive(arrayType) == false) { Array clonedArray = (Array)cloneObject; clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices)); } } visited.Add(originalObject, cloneObject); CopyFields(originalObject, visited, cloneObject, typeToReflect); RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect); return cloneObject; } private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect) { if (typeToReflect.BaseType != null) { RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType); CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate); } } private static void CopyFields(object originalObject, IDictionary visited, object cloneObject, Type typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func filter = null) { foreach (FieldInfo fieldInfo in typeToReflect.GetFields(bindingFlags)) { if (filter != null && filter(fieldInfo) == false) continue; if (IsPrimitive(fieldInfo.FieldType)) continue; var originalFieldValue = fieldInfo.GetValue(originalObject); var clonedFieldValue = InternalCopy(originalFieldValue, visited); fieldInfo.SetValue(cloneObject, clonedFieldValue); } } public static T Copy(this T original) { return (T)Copy((Object)original); } public static bool IntersectsWith(this Rect r1, Rect r2) { return (r2.x + r2.width >= r1.x && r2.x <= r1.x + r1.width) && (r2.y + r2.height >= r1.y && r2.y <= r1.y + r1.height); } // public static IEnumerable FindPath(this IEnumerable> data, T source, T target, Func costFunction, bool bestFirst = true) where T : class // { // List> members = new List>{new DijkstraVertex{Vertex = source}}; // List searched = new List(); // while (true) // { // var v = members.Where(m=>!searched.Contains(m.Vertex)).OrderBy(m=>bestFirst?m.Heuristic:m.Cost).FirstOrDefault(); // if (v == null) return null; // No vertices left unsearched // if (v.Vertex == target) // { // Stack> path = new Stack>(); // path.Push(v); // while(path.Peek().Parent!=null) // path.Push(path.Peek().Parent); // return path.Select(dv => dv.Vertex).ToList(); // } // members.AddRange(data.Where(l => l.Source == v.Vertex).Select(l => l.Target).Concat(data.Where(l => l.Target == v.Vertex).Select(l => l.Source)) // .Where(n => members.All(m => m.Vertex != n)).Select(n => // { // var c = new DijkstraVertex {Parent = v, Vertex = n, Cost = v.Cost + costFunction(v.Vertex,n)}; // if (bestFirst) // c.Heuristic = c.Cost + costFunction(c.Vertex,target); // return c; // })); // searched.Add(v.Vertex); // } // } class DijkstraVertex { public double Cost; public double Heuristic; public DijkstraVertex Parent; public T Vertex; } } public class ReferenceEqualityComparer : EqualityComparer { public override bool Equals(object x, object y) { return ReferenceEquals(x, y); } public override int GetHashCode(object obj) { if (obj == null) return 0; return obj.GetHashCode(); } } public static class ArrayExtensions { public static void ForEach(this Array array, Action action) { if (array.LongLength == 0) return; ArrayTraverse walker = new ArrayTraverse(array); do action(array, walker.Position); while (walker.Step()); } } internal class ArrayTraverse { public int[] Position; private int[] maxLengths; public ArrayTraverse(Array array) { maxLengths = new int[array.Rank]; for (int i = 0; i < array.Rank; ++i) { maxLengths[i] = array.GetLength(i) - 1; } Position = new int[array.Rank]; } public bool Step() { for (int i = 0; i < Position.Length; ++i) { if (Position[i] < maxLengths[i]) { Position[i]++; for (int j = 0; j < i; j++) { Position[j] = 0; } return true; } } return false; } }