using System.Collections; using System.Collections.Generic; using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core; [System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")] public class HierarchyObject : BaseEntity, IHierarchyObject { public event IHierarchyObject.EnteredHierarchyEventHandler? OnEnteredHierarchy = null; public event IHierarchyObject.ExitedHierarchyEventHandler? OnExitedHierarchy = null; public event IHierarchyObject.ParentChangedEventHandler? OnParentChanged = null; public event IHierarchyObject.ChildrenAddedEventHandler? OnChildrenAdded = null; public event IHierarchyObject.ChildrenRemovedEventHandler? OnChildrenRemoved = null; public event IHasBehaviourController.BehaviourControllerAssignedEventHandler? OnBehaviourControllerAssigned = null; public event INameable.NameChangedEventHandler? OnNameChanged = null; private string _name = nameof(HierarchyObject); private IGameManager _gameManager = null!; private IBehaviourController _behaviourController = null!; private readonly List _children = []; public IHierarchyObject? Parent { get; private set; } = null; public IReadOnlyList Children => _children; public IGameManager GameManager => _gameManager; public bool IsInHierarchy => _gameManager is not null; public string Name { get => _name; set { if (value == _name) return; string previousName = _name; _name = value; OnNameChanged?.Invoke(this, previousName); } } public IBehaviourController BehaviourController => _behaviourController; protected virtual void OnEnteringHierarchy(IGameManager gameManager) { } bool IHierarchyObject.EnterHierarchy(IGameManager gameManager) { if (IsInHierarchy) return false; _gameManager = gameManager; OnEnteringHierarchy(gameManager); OnEnteredHierarchy?.Invoke(this, gameManager); return true; } protected virtual void OnExitingHierarchy(IGameManager gameManager) { } bool IHierarchyObject.ExitHierarchy() { if (!IsInHierarchy || _gameManager is not IGameManager gameManager) return false; _gameManager = null!; OnExitingHierarchy(gameManager); OnExitedHierarchy?.Invoke(this, gameManager); return true; } public void SetParent(IHierarchyObject? parent) { if (parent == this || Parent == parent) return; IHierarchyObject? previousParent = Parent; if (previousParent is not null) { previousParent.RemoveChild(this); previousParent.OnParentChanged -= NotifyChildrenOnParentChange; } Parent = parent; if (parent is not null) { parent.AddChild(this); parent.OnParentChanged += NotifyChildrenOnParentChange; } OnParentChanged?.Invoke(this, previousParent, parent); } public void AddChild(IHierarchyObject parent) { if (_children.Contains(parent)) return; _children.Add(parent); parent.SetParent(this); OnChildrenAdded?.Invoke(this, parent); } public void RemoveChild(IHierarchyObject parent) { if (!_children.Remove(parent)) return; parent.SetParent(null); OnChildrenRemoved?.Invoke(this, parent); } private void NotifyChildrenOnParentChange(IHierarchyObject sender, IHierarchyObject? previousParent, IHierarchyObject? newParent) { // TODO No idea how logical this is to propagate this to the children the way I'm doing right now. // I was originally gonna just call `child.OnParentChanged?.Invoke(child, child.parentTransform);` but seems an unnecessary call too? foreach (IHierarchyObject child in Children) // TODO CHECK ERRORS child.SetParent(this); } public bool Assign(IBehaviourController behaviourController) { if (IsInitialized) return false; _behaviourController = behaviourController; OnBehaviourControllerAssigned?.Invoke(this); return true; } protected override void InitializeInternal() { base.InitializeInternal(); _behaviourController ??= new Factory.BehaviourControllerFactory().Instantiate(this); } public IEnumerator GetEnumerator() => _children.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator(); }