using System; using System.Collections; using System.Collections.Generic; namespace Syntriax.Engine.Core; [System.Diagnostics.DebuggerDisplay("UniverseObject Count: {_universeObjects.Count}")] public class Universe : BaseEntity, IUniverse { public Event OnPreUpdate { get; } = new(); public Event OnUpdate { get; } = new(); public Event OnPostUpdate { get; } = new(); public Event OnPreDraw { get; } = new(); public Event OnDraw { get; } = new(); public Event OnPostDraw { get; } = new(); public Event OnUniverseObjectRegistered { get; } = new(); public Event OnUniverseObjectUnRegistered { get; } = new(); public Event OnTimeScaleChanged { get; } = new(); private readonly Event.EventHandler delegateOnUniverseObjectFinalize = null!; private readonly Event.EventHandler delegateOnUniverseObjectExitedUniverse = null!; private readonly List _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL); private float _timeScale = 1f; public Universe() { delegateOnUniverseObjectFinalize = OnUniverseObjectFinalize; delegateOnUniverseObjectExitedUniverse = OnUniverseObjectExitedUniverse; } public IReadOnlyList UniverseObjects => _universeObjects; public UniverseTime Time { get; private set; } = new(); public UniverseTime UnscaledTime { get; private set; } = new(); public float TimeScale { get => _timeScale; set { value = value.Max(0f); if (value == _timeScale) return; float previousTimeScale = _timeScale; _timeScale = value; OnTimeScaleChanged?.Invoke(this, new(previousTimeScale)); } } 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.AddListener(delegateOnUniverseObjectFinalize); universeObject.OnExitedUniverse.AddListener(delegateOnUniverseObjectExitedUniverse); 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?.Invoke(this, new(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.RemoveListener(delegateOnUniverseObjectFinalize); universeObject.OnExitedUniverse.RemoveListener(delegateOnUniverseObjectExitedUniverse); 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?.Invoke(this, new(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.Assert.AssertInitialized(this); UnscaledTime = engineTime; Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale))); OnPreUpdate?.Invoke(this, new(Time)); OnUpdate?.Invoke(this, new(Time)); OnPostUpdate?.Invoke(this, new(Time)); } public void Draw() { Debug.Assert.AssertInitialized(this); OnPreDraw?.Invoke(this); OnDraw?.Invoke(this); OnPostDraw?.Invoke(this); } private void OnUniverseObjectFinalize(IInitializable initializable) { if (initializable is IUniverseObject universeObject) Remove(universeObject); } private void OnUniverseObjectExitedUniverse(IUniverseObject sender, IUniverseObject.ExitedUniverseArguments args) { if (sender is IUniverseObject universeObject) Remove(universeObject); } public IEnumerator GetEnumerator() => _universeObjects.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _universeObjects.GetEnumerator(); }