using System.Collections.Generic; namespace Engine.Core; [System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")] public class UniverseObject : BaseEntity, IUniverseObject { public Event OnEnteredUniverse { get; } = new(); public Event OnExitedUniverse { get; } = new(); public Event OnParentChanged { get; } = new(); public Event OnChildrenAdded { get; } = new(); public Event OnChildrenRemoved { get; } = new(); public Event OnActiveChanged { get; } = new(); public Event OnNameChanged { get; } = new(); public Event OnBehaviourControllerAssigned { get; } = new(); private readonly FastList _children = []; public IBehaviourController BehaviourController { get; private set; } = null!; public IUniverse Universe { get; private set; } = null!; public bool IsActive { get; private set; } = false; public IReadOnlyList Children => _children; public bool IsInUniverse => Universe is not null; public string Name { get; set { if (value == field) return; string previousName = field; field = value; OnNameChanged?.Invoke(this, new(previousName)); } } = nameof(UniverseObject); public IUniverseObject? Parent { get; set { if (value == this) throw new Exceptions.AssignFailedException($"{Name} can not parent itself"); if (field == value) return; IUniverseObject? previousParent = Parent; if (previousParent is not null) { previousParent.RemoveChild(this); previousParent.OnActiveChanged.RemoveListener(OnParentActiveChanged); } field = value; if (value is not null) { if (value.IsInUniverse && !IsInUniverse) value.Universe.Register(this); value.AddChild(this); value.OnActiveChanged.AddListener(OnParentActiveChanged); } UpdateActive(); OnParentChanged?.Invoke(this, new(previousParent, value)); } } = null; protected virtual void OnEnteringUniverse(IUniverse universe) { } bool IUniverseObject.EnterUniverse(IUniverse universe) { if (IsInUniverse) return false; Universe = universe; UpdateActive(); OnEnteringUniverse(universe); OnEnteredUniverse?.Invoke(this, new(universe)); return true; } protected virtual void OnExitingUniverse(IUniverse universe) { } bool IUniverseObject.ExitUniverse() { if (!IsInUniverse || Universe is not IUniverse universe) return false; OnExitingUniverse(universe); Universe = null!; OnExitedUniverse?.Invoke(this, new(universe)); return true; } public void AddChild(IUniverseObject child) { if (_children.Contains(child)) return; _children.Add(child); child.Parent = this; OnChildrenAdded?.Invoke(this, new(child)); } public void RemoveChild(IUniverseObject child) { if (!_children.Remove(child)) return; child.Parent = null; OnChildrenRemoved?.Invoke(this, new(child)); } protected virtual void OnAssign(IBehaviourController behaviourController) { } public bool Assign(IBehaviourController behaviourController) { if (IsInitialized) return false; BehaviourController = behaviourController; OnAssign(behaviourController); OnBehaviourControllerAssigned?.Invoke(this); return true; } protected override void OnAssign(IStateEnable stateEnable) { base.OnAssign(stateEnable); stateEnable.OnEnabledChanged.AddListener(OnStateEnabledChanged); UpdateActive(); } private void OnParentActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive(); private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive(); private void UpdateActive() { bool previousActive = IsActive; IsActive = StateEnable.Enabled && (Parent?.IsActive ?? true); if (previousActive != IsActive) OnActiveChanged?.Invoke(this, new(previousActive)); } protected override void UnassignInternal() { base.UnassignInternal(); StateEnable.OnEnabledChanged.RemoveListener(OnStateEnabledChanged); } protected override void InitializeInternal() { base.InitializeInternal(); BehaviourController ??= Factory.BehaviourControllerFactory.Instantiate(this); BehaviourController.Initialize(); } public UniverseObject() { Name = GetType().Name; } }