using System; using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Cryville.Crtr.Event { public class EventBatcher : StateBase { public EventBus Bus { get; private set; } ContainerState RootState; /// /// event => the container state that handles the event /// readonly Dictionary table = new Dictionary(); public List stampedEvents = new List(); readonly List batches = new List(); double beat; float tempo; public EventBatcher(Chart c) : base(c, new List()) { 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, Action callback) { 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; bool flag = false; 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.Priority >= 0) { if (callback != null) callback(ev); flag = true; } if (ev is Chart.Signature) { var tev = (Chart.Signature)ev; if (tev.tempo != null) tempo = (float)tev.tempo; } EventId++; } if (callback != null && !flag) callback(batch.First()); return; return_ahead: Time = toTime; beat = toBeat; if (callback != null) callback(null); } IOrderedEnumerable 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; } } }