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

194 lines
5.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Cryville.Crtr.Event {
public class EventBatcher : StateBase<ChartEvent> {
public EventBus Bus {
get;
private set;
}
readonly Chart chart;
ContainerState rootState;
readonly Dictionary<ChartEvent, ContainerState> containerMap
= new();
readonly Dictionary<EventContainer, ContainerState> stateMap
= new();
readonly Dictionary<ChartEvent, StampedEvent> map
= new();
readonly Dictionary<EventContainer, List<StampedEvent>> coeventMap
= new();
readonly HashSet<ChartEvent> coevents = new();
readonly List<StampedEvent> stampedEvents = new();
readonly List<EventBatch> batches = new();
double beat;
float tempo;
public EventBatcher(Chart c) : base(new List<ChartEvent>()) {
chart = c;
beat = chart.BeatPosition;
tempo = (float)c.sigs[0].tempo;
Events.Add(c);
Events.Add(c.ReleaseEvent);
AddEventContainer(c);
Events.Sort((a, b) => a.BeatPosition.CompareTo(b.BeatPosition));
}
void AddEventContainer(EventContainer c, ContainerState parent = null) {
var cs = new ContainerState(c, parent);
stateMap.Add(c, cs);
if (parent == null) {
cs.Depth = 0;
rootState = cs;
}
else {
cs.Depth = (ushort)(parent.Depth + 1);
}
foreach (var ev in c.Events) {
if (ev.time == null) {
ev.time = c.time;
if (ev is EventContainer) ev.endtime = c.endtime;
if ((ev.CoeventMode & CoeventMode.Coevent) != 0) coevents.Add(ev);
}
if (ev.IsLong) {
Events.Add(ev.ReleaseEvent);
containerMap.Add(ev.ReleaseEvent, cs);
}
Events.Add(ev);
containerMap.Add(ev, cs);
if (ev is EventContainer container)
AddEventContainer(container, cs);
}
}
public override void ForwardOnceToTime(double toTime) {
double toBeat = Math.Round(beat + (toTime - Time) * tempo / 60f, 6);
if (EventId >= Events.Count)
goto return_ahead;
double ebeat = Events[EventId].BeatPosition;
double etime = Math.Round((ebeat - beat) / tempo * 60f + Time, 6);
if (etime > toTime)
goto return_ahead;
var batch = GetEventBatch();
Time = etime;
beat = ebeat;
foreach (var ev in batch) {
EventContainer con = null;
if (containerMap.ContainsKey(ev)) con = containerMap[ev].Container;
var sev = new StampedEvent() {
Time = etime,
Unstamped = ev,
Container = con
};
if (ev is EventContainer) {
var tev = (EventContainer)ev;
stateMap[tev].StampedContainer = sev;
stampedEvents.Add(new StampedEvent.ClipBehind {
Container = con,
Origin = sev,
Time = etime + tev.Clip.Behind,
});
if (!ev.IsLong) {
stampedEvents.Add(new StampedEvent.ClipAhead {
Container = con,
Origin = sev,
Time = etime + tev.Clip.Ahead,
});
}
}
if (ev is ReleaseEvent) {
var tev = (ReleaseEvent)ev;
var oev = tev.Original;
var pev = map[oev];
pev.ReleaseEvent = sev;
sev.Origin = pev;
if (oev is EventContainer container) {
stampedEvents.Add(new StampedEvent.ClipAhead {
Container = con,
Origin = pev,
Time = etime + container.Clip.Ahead,
});
}
}
if (con != null && coevents.Contains(ev)) {
if (!coeventMap.TryGetValue(con, out List<StampedEvent> cevs)) {
coeventMap.Add(con, cevs = new List<StampedEvent>());
}
cevs.Add(sev);
if ((ev.CoeventMode & CoeventMode.Standalone) != 0) {
stampedEvents.Add(sev);
}
}
else stampedEvents.Add(sev);
map.Add(ev, sev);
if (ev is Chart.Signature) {
var tev = (Chart.Signature)ev;
if (tev.tempo != null) tempo = (float)tev.tempo;
}
EventId++;
}
return;
return_ahead:
Time = toTime;
beat = toBeat;
}
IOrderedEnumerable<ChartEvent> GetEventBatch() {
float cbeat = Events[EventId].BeatPosition;
int b = EventId;
while (Mathf.Approximately(Events[b].BeatPosition, cbeat)) {
b--;
if (b == -1) break;
}
int a = EventId;
while (Mathf.Approximately(Events[a].BeatPosition, cbeat)) {
a++;
if (a == Events.Count) break;
}
return from ev in Events.GetRange(b + 1, a - b - 1) orderby ev.Priority select ev;
}
public EventBus Batch() {
stampedEvents.Sort(CompareStampedEvents);
var cb = new EventBatch(stampedEvents[0].Time);
foreach (var ev in stampedEvents) {
if (ev.Time != cb.Time) {
batches.Add(cb);
cb = new EventBatch(ev.Time);
}
cb.Enqueue(ev);
BatchCoevents(ev);
}
batches.Add(cb);
Bus = new EventBus(rootState, batches);
return Bus;
}
void BatchCoevents(StampedEvent ev, List<StampedEvent> ocevs = null) {
if (ev.Unstamped is not EventContainer container) return;
if (coeventMap.TryGetValue(container, out List<StampedEvent> cevs)) {
var rootFlag = ocevs == null;
if (rootFlag) ev.Coevents = ocevs = new List<StampedEvent>();
foreach (var cev in cevs) {
ocevs.Add(cev);
BatchCoevents(cev, ocevs);
}
if (rootFlag) ocevs.Sort(CompareStampedEvents);
else ocevs.Add(ev);
}
}
int CompareStampedEvents(StampedEvent a, StampedEvent b) {
int u = a.CompareTo(b);
if (u != 0) return u;
if (a.Unstamped != null && b.Unstamped != null && containerMap.ContainsKey(a.Unstamped) && containerMap.ContainsKey(b.Unstamped)) {
u = containerMap[a.Unstamped].Depth.CompareTo(containerMap[b.Unstamped].Depth);
if (u != 0) return u;
}
return 0;
}
}
}