using Ghost.Entities;
using Ghost.Engine.Components;
namespace Ghost.Engine.Systems;
///
/// Provides utility methods for working with entity hierarchies.
///
public static class HierarchyUtility
{
///
/// Sets the parent of an entity, updating the Hierarchy component accordingly.
///
/// The world containing the entities.
/// The child entity.
/// The parent entity, or Entity.Invalid to make the entity a root.
public static void SetParent(World world, Entity child, Entity parent)
{
if (!world.EntityManager.HasComponent(child))
{
world.EntityManager.AddComponent(child, Hierarchy.Root);
}
ref var childHierarchy = ref world.EntityManager.GetComponent(child);
// Remove from old parent's children list
if (childHierarchy.parent.IsValid)
{
RemoveFromSiblingList(world, child, childHierarchy.parent);
}
// Set new parent
childHierarchy.parent = parent;
if (parent.IsValid)
{
if (!world.EntityManager.HasComponent(parent))
{
world.EntityManager.AddComponent(parent, Hierarchy.Root);
}
ref var parentHierarchy = ref world.EntityManager.GetComponent(parent);
// Add to parent's children list
childHierarchy.nextSibling = parentHierarchy.firstChild;
parentHierarchy.firstChild = child;
}
else
{
childHierarchy.nextSibling = Entity.Invalid;
}
}
///
/// Gets the parent of an entity.
///
/// The world containing the entity.
/// The entity to get the parent of.
/// The parent entity, or Entity.Invalid if the entity has no parent.
public static Entity GetParent(World world, Entity entity)
{
if (!world.EntityManager.HasComponent(entity))
{
return Entity.Invalid;
}
ref var hierarchy = ref world.EntityManager.GetComponent(entity);
return hierarchy.parent;
}
///
/// Gets all children of an entity.
///
/// The world containing the entity.
/// The parent entity.
/// Span to store the children.
/// The number of children written to the span.
public static int GetChildren(World world, Entity parent, Span children)
{
if (!world.EntityManager.HasComponent(parent))
{
return 0;
}
ref var hierarchy = ref world.EntityManager.GetComponent(parent);
var currentChild = hierarchy.firstChild;
var count = 0;
while (currentChild.IsValid && count < children.Length)
{
children[count++] = currentChild;
if (world.EntityManager.HasComponent(currentChild))
{
ref var childHierarchy = ref world.EntityManager.GetComponent(currentChild);
currentChild = childHierarchy.nextSibling;
}
else
{
break;
}
}
return count;
}
///
/// Gets all descendants of an entity (children, grandchildren, etc.) in depth-first order.
///
/// The world containing the entity.
/// The root entity.
/// List to store the descendants.
public static void GetDescendants(World world, Entity root, List descendants)
{
Span children = stackalloc Entity[32];
var childCount = GetChildren(world, root, children);
for (int i = 0; i < childCount; i++)
{
var child = children[i];
descendants.Add(child);
GetDescendants(world, child, descendants);
}
}
///
/// Removes a child from its parent's sibling list.
///
private static void RemoveFromSiblingList(World world, Entity child, Entity parent)
{
if (!world.EntityManager.HasComponent(parent))
{
return;
}
ref var parentHierarchy = ref world.EntityManager.GetComponent(parent);
ref var childHierarchy = ref world.EntityManager.GetComponent(child);
// If child is the first child
if (parentHierarchy.firstChild.Equals(child))
{
parentHierarchy.firstChild = childHierarchy.nextSibling;
childHierarchy.nextSibling = Entity.Invalid;
return;
}
// Find the previous sibling
var currentSibling = parentHierarchy.firstChild;
while (currentSibling.IsValid)
{
if (!world.EntityManager.HasComponent(currentSibling))
{
break;
}
ref var siblingHierarchy = ref world.EntityManager.GetComponent(currentSibling);
if (siblingHierarchy.nextSibling.Equals(child))
{
siblingHierarchy.nextSibling = childHierarchy.nextSibling;
childHierarchy.nextSibling = Entity.Invalid;
return;
}
currentSibling = siblingHierarchy.nextSibling;
}
}
///
/// Checks if an entity is an ancestor of another entity.
///
/// The world containing the entities.
/// The potential ancestor entity.
/// The descendant entity.
/// True if potentialAncestor is an ancestor of descendant, false otherwise.
public static bool IsAncestor(World world, Entity potentialAncestor, Entity descendant)
{
var current = GetParent(world, descendant);
while (current.IsValid)
{
if (current.Equals(potentialAncestor))
{
return true;
}
current = GetParent(world, current);
}
return false;
}
}