using System; using System.Collections; using System.Collections.Generic; using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core; [System.Diagnostics.DebuggerDisplay("UniverseObject Count: {_universeObjects.Count}")] public class Universe : BaseEntity, IUniverse { public event IUniverse.UpdateEventHandler? OnPreUpdate = null; public event IUniverse.UpdateEventHandler? OnUpdate = null; public event IUniverse.PreDrawEventHandler? OnPreDraw = null; public event IUniverse.UniverseObjectRegisteredEventHandler? OnUniverseObjectRegistered = null; public event IUniverse.UniverseObjectUnRegisteredEventHandler? OnUniverseObjectUnRegistered = null; private readonly List _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL); private float _timeScale = 1f; public IReadOnlyList UniverseObjects => _universeObjects; public UniverseTime Time { get; private set; } = new(); public UniverseTime UnscaledTime { get; private set; } = new(); public float TimeScale { get => _timeScale; set => _timeScale = value.Max(0f); } public void Register(IUniverseObject universeObject) { if (_universeObjects.Contains(universeObject)) throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is already registered to the {nameof(Universe)}."); universeObject.OnFinalized += OnUniverseObjectFinalize; universeObject.OnExitedUniverse += OnUniverseObjectExitedUniverse; if (!universeObject.Initialize()) throw new Exception($"{universeObject.Name} can't be initialized"); for (int i = 0; i < universeObject.Children.Count; i++) Register(universeObject.Children[i]); _universeObjects.Add(universeObject); if (!universeObject.EnterUniverse(this)) throw new Exception($"{universeObject.Name} can't enter the universe"); OnUniverseObjectRegistered?.InvokeSafe(this, universeObject); } public T InstantiateUniverseObject(params object?[]? args) where T : class, IUniverseObject { T universeObject = Factory.UniverseObjectFactory.Instantiate(args); Register(universeObject); return universeObject; } public void Remove(IUniverseObject universeObject) { universeObject.SetParent(null); RemoveIncursive(universeObject); } private void RemoveIncursive(IUniverseObject universeObject) { if (!_universeObjects.Contains(universeObject)) throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is not registered to the {nameof(Universe)}."); universeObject.OnFinalized -= OnUniverseObjectFinalize; universeObject.OnExitedUniverse -= OnUniverseObjectExitedUniverse; for (int i = universeObject.Children.Count - 1; i >= 0; i--) Remove(universeObject.Children[i]); _universeObjects.Remove(universeObject); if (!universeObject.ExitUniverse()) throw new Exception($"{universeObject.Name} can't exit the universe"); if (!universeObject.Finalize()) throw new Exception($"{universeObject.Name} can't be finalized"); OnUniverseObjectUnRegistered?.InvokeSafe(this, universeObject); } protected override void InitializeInternal() { foreach (IUniverseObject universeObject in UniverseObjects) universeObject.Initialize(); } protected override void FinalizeInternal() { base.FinalizeInternal(); for (int i = UniverseObjects.Count; i >= 0; i--) UniverseObjects[i].Finalize(); } public void Update(UniverseTime engineTime) { Debug.AssertHelpers.AssertInitialized(this); UnscaledTime = engineTime; Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale))); OnPreUpdate?.InvokeSafe(this, Time); for (int i = 0; i < UniverseObjects.Count; i++) UniverseObjects[i].BehaviourController.Update(); OnUpdate?.InvokeSafe(this, Time); } public void PreDraw() { Debug.AssertHelpers.AssertInitialized(this); for (int i = 0; i < UniverseObjects.Count; i++) UniverseObjects[i].BehaviourController.UpdatePreDraw(); OnPreDraw?.InvokeSafe(this); } private void OnUniverseObjectFinalize(IInitializable initializable) { if (initializable is IUniverseObject universeObject) Remove(universeObject); } private void OnUniverseObjectExitedUniverse(IUniverseObject sender, IUniverse universe) { if (sender is IUniverseObject universeObject) Remove(universeObject); } public IEnumerator GetEnumerator() => _universeObjects.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _universeObjects.GetEnumerator(); }