using Cryville.Crtr.Ruleset; using Cryville.Crtr.Skin; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Cryville.Crtr.Event { public class EventBus : StateBase, IDisposable { public ContainerState RootState { get; private set; } Dictionary states = new(); HashSet activeStates = new(); HashSet invalidatedStates = new(); public int ActiveStateCount { get { return activeStates.Count; } } public EventBus(ContainerState root, List b) : base(b) { RootState = root; Expand(); AttachBus(); } public EventBus Clone(byte ct, float offsetTime = 0) { var r = (EventBus)MemberwiseClone(); r.states = new Dictionary(); r.activeStates = new HashSet(); r.invalidatedStates = new HashSet(); r.tempEvents = new List(); r.Time += offsetTime; r.RootState = RootState.Clone(ct); r.Expand(); r.AttachBus(); foreach (var s in r.states) r.invalidatedStates.Add(s.Value); r.ValidateStates(); return r; } public void CopyTo(EventBus dest) { base.CopyTo(dest); dest.activeStates.Clear(); dest.invalidatedStates.Clear(); RootState.CopyTo(dest.RootState); dest.ValidateStates(); } public void Dispose() { RootState.Dispose(); } public void DisposeAll() { RootState.DisposeAll(); } public void NotifyActiveChanged(ContainerState state) { if (!invalidatedStates.Contains(state)) invalidatedStates.Add(state); } void Expand(ContainerState s = null) { if (s == null) { states.Clear(); s = RootState; } AddState(s); foreach (var c in s.Children) Expand(c.Value); } public void AddState(ContainerState s) { states[s.Container] = s; s.Bus = this; } void AttachBus() { foreach (var s in states) s.Value.Bus = this; } public void AttachSystems(PdtSkin skin, Judge judge) { foreach (var s in states) s.Value.AttachSystems(skin, judge); } List tempEvents = new(); public void PushTempEvent(StampedEvent.Temporary ev) { var index = tempEvents.BinarySearch(ev); if (index < 0) index = ~index; tempEvents.Insert(index, ev); } readonly StampedEvent.Temporary _dummyEvent = new(); public void StripTempEvents() { _dummyEvent.Time = Time; var index = tempEvents.BinarySearch(_dummyEvent); if (index < 0) index = ~index; for (var i = index - 1; i >= 0; i--) { var ev = tempEvents[i]; tempEvents.RemoveAt(i); if (ev.CanDiscard) { if (ev.Container != null) { states[ev.Container].Discard(ev); } } else { ev.Time = Time; PushTempEvent(ev); } } } public override void ForwardOnceToTime(double toTime) { double time1 = EventId < Events.Count ? Events[EventId].Time : double.PositiveInfinity; double time2 = tempEvents.Count > 0 ? tempEvents[0].Time : double.PositiveInfinity; double time0 = Math.Min(time1, time2); if (time0 <= toTime && time0 != double.PositiveInfinity) { Time = time0; foreach (var s in activeStates) s.Handle(null); ValidateStates(); if (time1 == time0) { var batch = Events[EventId]; for (var i = 0; i < batch.Count; i++) { var ev = batch[i]; HandleTempEvents(time0, ev.Priority); if (ev is StampedEvent.ClipBehind) { var cevs = ev.Origin.Coevents; if (cevs != null) foreach (var cev in cevs) { if (cev.Container == null) continue; states[cev.Container].Handle(cev); } states[(EventContainer)ev.Origin.Unstamped].PhysicalActive = true; } else if (ev is StampedEvent.ClipAhead) { states[(EventContainer)ev.Origin.Unstamped].PhysicalActive = false; } else if (ev.Container != null) { states[ev.Container].Handle(ev); } } EventId++; } HandleTempEvents(time0); } else { Time = toTime; foreach (var s in activeStates) s.Handle(null); } ValidateStates(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] void HandleTempEvents(double time, int maxPriority = int.MaxValue) { while (tempEvents.Count > 0) { var ev = tempEvents[0]; if (ev.Time != time || ev.Priority >= maxPriority) break; if (ev.Container != null) { states[ev.Container].Handle(ev); } tempEvents.RemoveAt(0); } } private void ValidateStates() { foreach (var s in invalidatedStates) if (s.Active && !activeStates.Contains(s)) activeStates.Add(s); else if (!s.Active && activeStates.Contains(s)) activeStates.Remove(s); invalidatedStates.Clear(); } public void BroadcastPreInit() { RootState.BroadcastPreInit(); } public void BroadcastPostInit() { RootState.BroadcastPostInit(); } public void EndPreGraphicalUpdate() { RootState.EndPreGraphicalUpdate(); ClearTempEvents(); } public void EndGraphicalUpdate() { RootState.EndGraphicalUpdate(); ClearTempEvents(); } void ClearTempEvents() { foreach (var ev in tempEvents) { if (ev.Container != null) { states[ev.Container].Discard(ev); } } tempEvents.Clear(); } public void PreAnchor() { if (RootState.Handler.Alive) RootState.PreAnchor(); } public void Anchor() { if (RootState.Handler.Alive) RootState.Anchor(); } } }