using System; using System.Collections.Generic; using System.Linq; namespace Syntriax.Engine.Core; [System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")] public class BehaviourController : BaseEntity, IBehaviourController { public Event OnBehaviourAdded { get; } = new(); public Event OnBehaviourRemoved { get; } = new(); public Event OnUniverseObjectAssigned { get; } = new(); private readonly List behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL); private IUniverseObject _universeObject = null!; public IUniverseObject UniverseObject => _universeObject; public int Count => behaviours.Count; public IBehaviour this[Index index] => behaviours[index]; public T AddBehaviour(T behaviour) where T : class, IBehaviour { InsertBehaviourByPriority(behaviour); behaviour.Assign(this); if (IsInitialized) behaviour.Initialize(); behaviour.OnPriorityChanged.AddListener(OnPriorityChange); OnBehaviourAdded?.Invoke(this, new(behaviour)); return behaviour; } public T AddBehaviour(params object?[]? args) where T : class, IBehaviour { T behaviour = Factory.BehaviourFactory.Instantiate(args); return AddBehaviour(behaviour); } public T? GetBehaviour() { foreach (IBehaviour behaviourItem in behaviours) if (behaviourItem is T result) return result; return default; } public IReadOnlyList GetBehaviours() { List? behaviours = null; foreach (IBehaviour behaviourItem in this.behaviours) { if (behaviourItem is not T behaviour) continue; behaviours ??= []; behaviours.Add(behaviour); } return behaviours ?? Enumerable.Empty().ToList(); } public void GetBehaviours(IList results) { results.Clear(); foreach (IBehaviour behaviourItem in behaviours) { if (behaviourItem is not T behaviour) continue; results.Add(behaviour); } } public void RemoveBehaviour(bool removeAll = false) where T : class, IBehaviour { for (int i = behaviours.Count; i >= 0; i--) { if (behaviours[i] is not T behaviour) continue; RemoveBehaviour(behaviour); if (!removeAll) return; } } public void RemoveBehaviour(T behaviour) where T : class, IBehaviour { if (!behaviours.Contains(behaviour)) throw new Exception($"{behaviour.GetType().Name} does not exist in {UniverseObject.Name}'s {nameof(IBehaviourController)}."); behaviour.OnPriorityChanged.RemoveListener(OnPriorityChange); behaviour.Finalize(); behaviours.Remove(behaviour); OnBehaviourRemoved?.Invoke(this, new(behaviour)); } protected virtual void OnAssign(IUniverseObject universeObject) { } public bool Assign(IUniverseObject universeObject) { if (UniverseObject is not null && UniverseObject.IsInitialized) return false; _universeObject = universeObject; OnAssign(universeObject); OnUniverseObjectAssigned?.Invoke(this); return true; } protected override void InitializeInternal() { Debug.Assert.AssertUniverseObjectAssigned(this); foreach (IBehaviour behaviour in behaviours) behaviour.Initialize(); } protected override void FinalizeInternal() { foreach (IBehaviour behaviour in behaviours) behaviour.Finalize(); } public BehaviourController() { } public BehaviourController(IUniverseObject universeObject) => Assign(universeObject); private void InsertBehaviourByPriority(T behaviour) where T : class, IBehaviour { int i; for (i = 0; i < behaviours.Count; i++) { if (behaviours[i].Priority > behaviour.Priority) continue; behaviours.Insert(i, behaviour); return; } if (i == 0 || i == behaviours.Count) behaviours.Add(behaviour); } private void OnPriorityChange(IBehaviour sender, IBehaviour.PriorityChangedArguments arguments) { behaviours.Remove(sender); InsertBehaviourByPriority(sender); } }