using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System; using Godot; namespace Rokojori { public class TimeLineRunner { public TimeLine timeLine; public TimeLineManager manager; public float lastPosition = 0; public float position = 0; public bool playing = false; public float deltaScale = 1; public float speed = 1; List events = new List(); List spans = new List(); List callbacks = new List(); AnimationCurve modulatorCurve; float modulatorTime; Vector3 modulatorRandomization; Action modulatorOnReady = null; float _lastModulation = 1; float _currentDelta = 1; public float modulatedSpeed => (float)( speed * _lastModulation * deltaScale ); public float currentDelta => _currentDelta; public TimeLineRunner( TimeLine timeLine, TimeLineManager manager ) { this.timeLine = timeLine; this.manager = manager; playing = timeLine.autoStart; } public void Modulate( AnimationCurve curve, Action onReady ) { if ( modulatorOnReady != null ) { modulatorOnReady( false ); } modulatorCurve = curve; modulatorTime = 0; modulatorRandomization = curve.Randomize(); modulatorOnReady = onReady; } public void UpdateTimeLine( float realtimeDelta ) { if ( ! playing ) { return; } var modulation = 1f; if ( modulatorCurve != null ) { modulation = modulatorCurve.Sample( modulatorTime, modulatorRandomization ); _lastModulation = modulation; modulatorTime += realtimeDelta; if ( modulatorTime >= modulatorCurve.GetRandomizedEndTime( modulatorRandomization ) ) { var mr = modulatorOnReady; modulatorOnReady = null; modulatorCurve = null; modulatorTime = 0; modulatorRandomization = Vector3.Zero; if ( mr != null ) { mr( true ); } } } else { _lastModulation = 1; } _currentDelta = realtimeDelta * deltaScale * speed * modulation; lastPosition = position; position += _currentDelta; var isForward = speed >= 0; if ( isForward ) { ProcessForward(); } } void ProcessForward() { ProcessEvents(); ProcessSpans(); ProcessCallbacks(); } List _callbackAdditions = new List(); void ProcessCallbacks() { if ( _callbackAdditions.Count > 0 ) { callbacks.AddRange( _callbackAdditions ); _callbackAdditions.Clear(); } callbacks.ForEach( c => c.callback( c ) ); } void ProcessEvents() { if ( requestedRemovals.Count > 0 ) { requestedRemovals.Sort(); Lists.RemoveIncreasingSortedIndices( events, requestedRemovals ); requestedRemovals.Clear(); } List eventRemovals = null; for ( int i = 0; i < events.Count; i++ ) { var eventPosition = events[ i ].position; if ( events[ i ].looping ) { var next = events[ i ].GetNextLoopPosition( lastPosition ); var previous = events[ i ].GetPreviousLoopPosition( position ); if ( next != previous ) { continue; } eventPosition = next; } if ( ! RangeDouble.ContainsExclusiveMax( lastPosition, position, eventPosition ) ) { if ( events[ i ].wasInside ) { eventRemovals = AddRemoval( events[ i ].persistent, i, eventRemovals ); } continue; } events[ i ].callback( events[ i ] ); events[ i ].wasInside = true; } if ( eventRemovals != null ) { Lists.RemoveIncreasingSortedIndices( events, eventRemovals ); } } void ProcessSpans() { if ( spans.Count == 0 ) { return; } List spanRemovals = null; var timelineSpan = new RangeDouble( lastPosition, position ); var span = new RangeDouble( 0, 1 ); var isForward = lastPosition < position; for ( int i = 0; i < spans.Count; i++ ) { span.min = spans[ i ].start; span.max = spans[ i ].end; var overlaps = timelineSpan.Overlaps( span ); if ( ! overlaps ) { if ( spans[ i ].wasInside ) { spanRemovals = AddRemoval( spans[ i ].persistent, i, spanRemovals ); } continue; } var isStart = timelineSpan.Contains( spans[ i ].start ); var isEnd = timelineSpan.Contains( spans[ i ].end ); var spanType = TimeLineSpanUpdateType.InSpan; if ( isStart && isEnd ) { spanType = TimeLineSpanUpdateType.CompletelyInside; } else if ( isStart ) { spanType = TimeLineSpanUpdateType.Start; } else if ( isEnd ) { spanType = TimeLineSpanUpdateType.End; } spans[ i ].wasInside = true; spans[ i ].callback( spans[ i ], spanType ); } if ( spanRemovals != null ) { Lists.RemoveIncreasingSortedIndices( spans, spanRemovals ); } } List AddRemoval( bool isPersistent, int index, List list ) { if ( isPersistent ) { return list; } if ( list == null ) { list = new List(); } list.Add( index ); return list; } List requestedRemovals = new List(); public void RemoveEvent( int eventID ) { requestedRemovals.Add( eventID ); } public TimeLineCallback _ScheduleCallback( Action callback, int eventID ) { var tle = new TimeLineCallback(); tle.id = eventID; tle.callback = callback; tle.timeLine = timeLine; _callbackAdditions.Add( tle ); return tle; } public TimeLineEvent _ScheduleEvent( float position, int eventID, bool isPersistent, Action callback ) { var tle = new TimeLineEvent(); tle.position = position; tle.id = eventID; tle.persistent = isPersistent; tle.callback = callback; tle.timeLine = timeLine; events.Add( tle ); return tle; } public TimeLineEvent _ScheduleLoopEvent( float loopDuration, float loopOffset, int eventID, bool isPersistent, Action callback ) { var tle = new TimeLineEvent(); tle.position = loopOffset; tle.looping = true; tle.loopDuration = loopDuration; tle.id = eventID; tle.persistent = isPersistent; tle.callback = callback; tle.timeLine = timeLine; events.Add( tle ); return tle; } public TimeLineSpan _ScheduleSpan( float start, float end, int eventID, bool isPersistent, Action callback ) { var tse = new TimeLineSpan(); tse.start = start; tse.end = end; tse.id = eventID; tse.persistent = isPersistent; tse.wasInside = false; tse.callback = callback; tse.timeLine = timeLine; spans.Add( tse ); return tse; } } }