Files
crtr/Assets/Cryville/Crtr/Event/EventBatcher.cs
2023-01-08 16:27:02 +08:00

137 lines
3.7 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;
}
ContainerState RootState;
/// <summary>
/// event => the container state that handles the event
/// </summary>
readonly Dictionary<ChartEvent, ContainerState> table
= new Dictionary<ChartEvent, ContainerState>();
public List<StampedEvent> stampedEvents = new List<StampedEvent>();
readonly List<EventBatch> batches = new List<EventBatch>();
double beat;
float tempo;
public EventBatcher(Chart c) : base(c, new List<ChartEvent>()) {
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(chart, c, parent);
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.IsLong) {
events.Add(ev.ReleaseEvent);
table.Add(ev.ReleaseEvent, cs);
}
events.Add(ev);
table.Add(ev, cs);
if (ev is EventContainer)
AddEventContainer((EventContainer)ev, 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 (table.ContainsKey(ev)) con = table[ev].Container;
var sev = new StampedEvent() {
Time = etime,
Unstamped = ev,
Container = con
};
if (ev is InstantEvent) {
var tev = (InstantEvent)ev;
var pev = stampedEvents.First(tpev => tpev.Unstamped == tev.Original);
pev.Subevents.Add(sev);
sev.Origin = pev;
}
stampedEvents.Add(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((a, b) => {
int u = a.CompareTo(b);
if (u != 0) return u;
if (a.Unstamped != null && b.Unstamped != null)
if (table.ContainsKey(a.Unstamped) && table.ContainsKey(b.Unstamped)) {
u = table[a.Unstamped].Depth.CompareTo(table[b.Unstamped].Depth);
if (u != 0) return u;
}
return a.GetHashCode().CompareTo(b.GetHashCode());
});
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);
}
batches.Add(cb);
Bus = new EventBus(chart, RootState, batches);
return Bus;
}
}
}