Files
crtr/Assets/Cryville/Crtr/Event/EventBus.cs

205 lines
5.3 KiB
C#

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<EventBatch>, IDisposable {
public ContainerState RootState {
get;
private set;
}
Dictionary<EventContainer, ContainerState> states
= new();
HashSet<ContainerState> activeStates
= new();
HashSet<ContainerState> invalidatedStates
= new();
public int ActiveStateCount { get { return activeStates.Count; } }
public EventBus(ContainerState root, List<EventBatch> b) : base(b) {
RootState = root;
Expand();
AttachBus();
}
public EventBus Clone(byte ct, float offsetTime = 0) {
var r = (EventBus)MemberwiseClone();
r.states = new Dictionary<EventContainer, ContainerState>();
r.activeStates = new HashSet<ContainerState>();
r.invalidatedStates = new HashSet<ContainerState>();
r.tempEvents = new List<StampedEvent.Temporary>();
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<StampedEvent.Temporary> 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();
}
}
}