271 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			271 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System.Collections.Generic;
 | 
						|
using System.Diagnostics.CodeAnalysis;
 | 
						|
 | 
						|
using Engine.Core.Exceptions;
 | 
						|
 | 
						|
namespace Engine.Core;
 | 
						|
 | 
						|
public static class UniverseObjectExtensions
 | 
						|
{
 | 
						|
    public static T SetUniverseObject<T>(this T universeObject, string? name = "", IUniverseObject? parent = null) where T : IUniverseObject
 | 
						|
    {
 | 
						|
        if (!string.IsNullOrWhiteSpace(name))
 | 
						|
            universeObject.Name = name;
 | 
						|
        if (parent is not null)
 | 
						|
            universeObject.Parent = parent;
 | 
						|
        return universeObject;
 | 
						|
    }
 | 
						|
 | 
						|
    public static IEnumerator<IUniverseObject> TraverseChildren(this IUniverseObject universeObject)
 | 
						|
    {
 | 
						|
        static IEnumerable<IUniverseObject> Traverse(IUniverseObject obj)
 | 
						|
        {
 | 
						|
            foreach (IUniverseObject child in obj.Children)
 | 
						|
            {
 | 
						|
                yield return child;
 | 
						|
                foreach (IUniverseObject descendant in Traverse(child))
 | 
						|
                    yield return descendant;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return Traverse(universeObject).GetEnumerator();
 | 
						|
    }
 | 
						|
 | 
						|
    #region Universe Object Search
 | 
						|
    /// <summary>
 | 
						|
    /// Gets a <see cref="IUniverseObject"/> of the specified type.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="universeObjects">The <see cref="IUniverseObject"/>s to search.</param>
 | 
						|
    /// <returns>The first found <see cref="IUniverseObject"/> of the specified type; otherwise, null.</returns>
 | 
						|
    public static T? GetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
 | 
						|
    {
 | 
						|
        foreach (IUniverseObject universeObject in universeObjects)
 | 
						|
            if (universeObject is T @object)
 | 
						|
                return @object;
 | 
						|
 | 
						|
        return default;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Tries to get a <see cref="IUniverseObject"/> of the specified type.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="universeObjects">The <see cref="IUniverseObject"/>s to search.</param>
 | 
						|
    /// <returns><see cref="true"/> if a <see cref="IUniverseObject"/> of the specified type was found in the universe objects; otherwise, <see cref="false"/>.</returns>
 | 
						|
    public static bool TryGetUniverseObject<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? universeObject) where T : class
 | 
						|
    {
 | 
						|
        universeObject = GetUniverseObject<T>(universeObjects);
 | 
						|
        return universeObject is not null;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Searches through the provided <see cref="IUniverseObject"/>s to collect a list of <see cref="IUniverseObject"/>s of the specified type.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="universeObject">The <see cref="IUniverseObject"/> to search.</param>
 | 
						|
    /// <returns>The found <see cref="IUniverseObject"/>s of the specified types</returns>
 | 
						|
    public static void GetUniverseObjects<T>(this IEnumerable<IUniverseObject> universeObjects, IList<T> foundUniverseObjects) where T : class
 | 
						|
    {
 | 
						|
        foundUniverseObjects.Clear();
 | 
						|
 | 
						|
        foreach (IUniverseObject universeObject in universeObjects)
 | 
						|
            if (universeObject is T @object)
 | 
						|
                foundUniverseObjects.Add(@object);
 | 
						|
    }
 | 
						|
    #endregion
 | 
						|
 | 
						|
    #region Universe Object Search In Parent
 | 
						|
    /// <summary>
 | 
						|
    /// Tries to get a <see cref="IUniverseObject"/> of the specified type in it's parents recursively.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="behaviour">When this method returns, contains the <see cref="IUniverseObject"/> of the specified type, if found; otherwise, null.</param>
 | 
						|
    /// <returns><see cref="true"/> if a <see cref="IUniverseObject"/> of the specified type was found in the parent universe objects; otherwise, <see cref="false"/>.</returns>
 | 
						|
    public static bool TryGetUniverseObjectInParent<T>(this IUniverseObject universeObject, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
 | 
						|
    {
 | 
						|
        behaviour = GetUniverseObjectInParent<T>(universeObject);
 | 
						|
        return behaviour is not null;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Gets a <see cref="IUniverseObject"/> of the specified type in it's parents recursively.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="universeObject">The <see cref="IUniverseObject"/> to start searching from.</param>
 | 
						|
    /// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
 | 
						|
    public static T? GetUniverseObjectInParent<T>(this IUniverseObject universeObject) where T : class
 | 
						|
    {
 | 
						|
        if (universeObject.Children.GetUniverseObject<T>() is T localUniverseObject)
 | 
						|
            return localUniverseObject;
 | 
						|
 | 
						|
        IUniverseObject? parent = universeObject;
 | 
						|
 | 
						|
        while (parent is not null)
 | 
						|
        {
 | 
						|
            if (parent is T behaviour)
 | 
						|
                return behaviour;
 | 
						|
 | 
						|
            parent = universeObject.Parent;
 | 
						|
        }
 | 
						|
 | 
						|
        return default;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Gets a <see cref="IUniverseObject"/> of the specified type in the parents recursively. Throws an error if not found.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="universeObject">The <see cref="IUniverseObject"/> to start searching from.</param>
 | 
						|
    /// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, throws <see cref="UniverseObjectNotFoundException"/>.</returns>
 | 
						|
    public static T GetRequiredUniverseObjectInParent<T>(this IUniverseObject universeObject) where T : class
 | 
						|
        => universeObject.GetUniverseObjectInParent<T>() ?? throw new UniverseObjectNotFoundException($"{universeObject.Name}'s {nameof(IUniverseObject)} does not contain any {typeof(T).FullName} on any parent ");
 | 
						|
 | 
						|
    #endregion
 | 
						|
 | 
						|
    #region Universe Object Search In Children
 | 
						|
    /// <summary>
 | 
						|
    /// Tries to get a <see cref="IUniverseObject"/> of the specified type in it's children recursively.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="behaviour">When this method returns, contains the <see cref="IUniverseObject"/> of the specified type, if found; otherwise, null.</param>
 | 
						|
    /// <returns><see cref="true"/> if a <see cref="IUniverseObject"/> of the specified type was found in the child universe objects; otherwise, <see cref="false"/>.</returns>
 | 
						|
    public static bool TryGetUniverseObjectInChildren<T>(this IUniverseObject universeObject, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
 | 
						|
    {
 | 
						|
        behaviour = GetUniverseObjectInChildren<T>(universeObject);
 | 
						|
        return behaviour is not null;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Gets a <see cref="IUniverseObject"/> of the specified type in it's children recursively.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="universeObject">The <see cref="IUniverseObject"/> to start searching from.</param>
 | 
						|
    /// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, null.</returns>
 | 
						|
    public static T? GetUniverseObjectInChildren<T>(this IUniverseObject universeObject) where T : class
 | 
						|
    {
 | 
						|
        if (universeObject.Children.GetUniverseObject<T>() is T localUniverseObject)
 | 
						|
            return localUniverseObject;
 | 
						|
 | 
						|
        foreach (IUniverseObject child in universeObject.Children)
 | 
						|
            if (GetUniverseObjectInChildren<T>(child) is T behaviour)
 | 
						|
                return behaviour;
 | 
						|
 | 
						|
        return default;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Gets a <see cref="IUniverseObject"/> of the specified type in the children recursively. Throws an error if not found.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IUniverseObject"/> to get.</typeparam>
 | 
						|
    /// <param name="universeObject">The <see cref="IUniverseObject"/> to start searching from.</param>
 | 
						|
    /// <returns>The <see cref="IUniverseObject"/> of the specified type if found; otherwise, throws <see cref="UniverseObjectNotFoundException"/>.</returns>
 | 
						|
    public static T GetRequiredUniverseObjectInChildren<T>(this IUniverseObject universeObject) where T : class
 | 
						|
        => universeObject.GetUniverseObjectInChildren<T>() ?? throw new UniverseObjectNotFoundException($"{universeObject.Name}'s {nameof(IUniverseObject)} does not contain any {typeof(T).FullName} on any children ");
 | 
						|
    #endregion
 | 
						|
 | 
						|
    #region Behaviour Search
 | 
						|
    /// <summary>
 | 
						|
    /// Finds a <see cref="IBehaviour"/> of the specified type in the provided <see cref="IUniverseObject"/>s.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IBehaviour"/> to find.</typeparam>
 | 
						|
    /// <returns>The first found <see cref="IBehaviour"/> of the specified type; otherwise, null.</returns>
 | 
						|
    public static T? FindBehaviour<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
 | 
						|
    {
 | 
						|
        foreach (IUniverseObject universeObject in universeObjects)
 | 
						|
            if (universeObject.BehaviourController.GetBehaviour<T>() is T behaviour)
 | 
						|
                return behaviour;
 | 
						|
 | 
						|
        return default;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Tries to find a <see cref="IBehaviour"/> of the specified type in the provided <see cref="IUniverseObject"/>s.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IBehaviour"/> to find.</typeparam>
 | 
						|
    /// <param name="behaviour">When this method returns, contains the <see cref="IUniverseObject"/> of the specified type, if found; otherwise, null.</param>
 | 
						|
    /// <returns><see cref="true"/> if a <see cref="IBehaviour"/> of the specified type was found in the provided <see cref="IUniverseObject"/>s; otherwise, <see cref="false"/>.</returns>
 | 
						|
    public static bool TryFindBehaviour<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
 | 
						|
    {
 | 
						|
        behaviour = FindBehaviour<T>(universeObjects);
 | 
						|
        return behaviour is not null;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Searches through the provided <see cref="IUniverseObject"/>s to collect a list of <see cref="IBehaviour"/>s of the specified type.
 | 
						|
    /// </summary>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
 | 
						|
    /// <param name="universeObjects">The <see cref="IUniverseObject"/>s to search.</param>
 | 
						|
    public static void FindBehaviours<T>(this IEnumerable<IUniverseObject> universeObjects, IList<T> behaviours) where T : class
 | 
						|
    {
 | 
						|
        behaviours.Clear();
 | 
						|
        List<T> cache = [];
 | 
						|
 | 
						|
        foreach (IUniverseObject universeObject in universeObjects)
 | 
						|
        {
 | 
						|
            universeObject.BehaviourController.GetBehaviours(cache);
 | 
						|
            foreach (T behaviour in cache)
 | 
						|
                behaviours.Add(behaviour);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    #endregion
 | 
						|
 | 
						|
    #region General Search
 | 
						|
    /// <summary>
 | 
						|
    /// Finds an object of the specified type in the provided <see cref="IUniverseObject"/>s and their <see cref="IBehaviour"/>s.
 | 
						|
    /// </summary>
 | 
						|
    /// <remarks>
 | 
						|
    /// WARNING: This is more expensive compared to <see cref="GetUniverseObject{T}(IEnumerable{IUniverseObject})"/> or <see cref="FindBehaviour{T}(IEnumerable{IUniverseObject})"/> as it combines the two. If you know whether the type is either a type that gets implemented on an <see cref="IBehaviour"/> or <see cref="IUniverseObject"/> use the method appropriate for it for performance.
 | 
						|
    /// </remarks>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IBehaviour"/> to find.</typeparam>
 | 
						|
    /// <returns>The first found instance of the specified type; otherwise, null.</returns>
 | 
						|
    public static T? Find<T>(this IEnumerable<IUniverseObject> universeObjects) where T : class
 | 
						|
    {
 | 
						|
        if (universeObjects.GetUniverseObject<T>() is T foundUniverseObject)
 | 
						|
            return foundUniverseObject;
 | 
						|
 | 
						|
        if (universeObjects.FindBehaviour<T>() is T foundBehaviour)
 | 
						|
            return foundBehaviour;
 | 
						|
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Tries to find an object of the specified type in the provided <see cref="IUniverseObject"/>s and their <see cref="IBehaviour"/>s.
 | 
						|
    /// </summary>
 | 
						|
    /// <remarks>
 | 
						|
    /// WARNING: This is more expensive compared to <see cref="TryGetUniverseObject{T}(IEnumerable{IUniverseObject}, out T?)"/> or <see cref="TryFindBehaviour{T}(IEnumerable{IUniverseObject}, out T?)"/> as it combines the two. If you know whether the type is either a type that gets implemented on an <see cref="IBehaviour"/> or <see cref="IUniverseObject"/> use the method appropriate for it for performance.
 | 
						|
    /// </remarks>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IBehaviour"/> to find.</typeparam>
 | 
						|
    /// <param name="behaviour">When this method returns, contains the <see cref="IUniverseObject"/> of the specified type, if found; otherwise, null.</param>
 | 
						|
    /// <returns><see cref="true"/> if an object of the specified type was found in the provided <see cref="IUniverseObject"/>s; otherwise, <see cref="false"/>.</returns>
 | 
						|
    public static bool TryFind<T>(this IEnumerable<IUniverseObject> universeObjects, [NotNullWhen(returnValue: true)] out T? behaviour) where T : class
 | 
						|
    {
 | 
						|
        behaviour = Find<T>(universeObjects);
 | 
						|
        return behaviour is not null;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Searches through the provided <see cref="IUniverseObject"/>s and their <see cref="IBehaviour"/>s to collect a list of the specified type.
 | 
						|
    /// </summary>
 | 
						|
    /// <remarks>
 | 
						|
    /// WARNING: This is more expensive compared to <see cref="GetUniverseObjects{T}(IEnumerable{IUniverseObject}, IList{T})"/> or <see cref="FindBehaviours{T}(IEnumerable{IUniverseObject}, IList{T})"/> as it combines the two. If you know whether the type is either a type that gets implemented on an <see cref="IBehaviour"/> or <see cref="IUniverseObject"/> use the method appropriate for it for performance.
 | 
						|
    /// </remarks>
 | 
						|
    /// <typeparam name="T">The type of <see cref="IBehaviour"/> to get.</typeparam>
 | 
						|
    /// <param name="instances">List of objects found wit the specified type.</param>
 | 
						|
    /// <param name="universeObjects">The <see cref="IUniverseObject"/>s to search.</param>
 | 
						|
    public static void Find<T>(this IEnumerable<IUniverseObject> universeObjects, IList<T> instances) where T : class
 | 
						|
    {
 | 
						|
        instances.Clear();
 | 
						|
        List<T> cache = [];
 | 
						|
 | 
						|
        foreach (IUniverseObject universeObject in universeObjects)
 | 
						|
        {
 | 
						|
            universeObject.Children.Find(cache);
 | 
						|
            foreach (T behaviour in cache)
 | 
						|
                instances.Add(behaviour);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    #endregion
 | 
						|
}
 |