194 lines
5.4 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|