using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System; using Godot; using Rokojori.Extensions; namespace Rokojori; /** Manages time lines for events, callbacks tweens. Most time-based SequenceActions use a Timeline to define the clock it belongs to. */ [Tool] [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/TimelineManager.svg") ] public partial class TimelineManager:NetworkNode { [Export] public Timeline[] timelines = new Timeline[ 0 ]; [Export] public Timeline timeScaleTimeline; [Export] public Timeline realtimeTimeline; [Export] public bool computeRealtimeWithEngineDelta; List _runners = new List(); int _idCounter = 0; bool _initialized = false; public override void _Ready() { if ( _runners == null ) { GD.Print( "is null" ); } _runners = null; Initialize(); } public static TimelineManager Get() { return Unique.Get(); } float _lastUpdate = 0; //DateTime lastUpdated = DateTime.Now; float lastUpdated = TimeTool.Now(); float _estimatedDelta = 0; float _estimatedDeltaFilter = 0.9f; float _unscaledDelta = 0; double _lastRealtimePosition = 0; double _realtimePosition = 0; float _lastTimeScale = 1; QueueList _processCallbacks = new QueueList(); bool _inProcess = false; public bool inProcess => _inProcess; bool _paused = false; public bool paused => _paused; public override void _Process( double delta ) { _inProcess = true; _paused = GetTree().Paused; UpdateRealTime( delta ); if ( ! Engine.IsEditorHint() && timeScaleTimeline != null ) { Engine.TimeScale = GetRunnerByTimeline( timeScaleTimeline ).modulatedSpeed; } _runners.ForEach( r => { if ( r == null ) { return; } r.UpdateTimeLine( unscaledTimeDelta ); } ); _processCallbacks.IterateAndResolve( t => t() ); _inProcess = false; } public void AddProcessCallback( System.Action a ) { _processCallbacks.QueueInsert( a ); } public void RemoveProcessCallback( System.Action a ) { _processCallbacks.QueueRemoval( a ); } public int GetTimeLineIndex( Timeline timeLine ) { return timelines == null ? -1 : Arrays.IndexOf( timelines, timeLine ); } public void Modulate( Timeline timeline, AnimationCurve c, Action onReady ) { var runner = GetRunner( GetTimeLineIndex( timeline ) ); runner.Modulate( c, onReady ); } void UpdateRealTime( double engineDelta ) { var now = TimeTool.Now(); var unscaled = ( now - lastUpdated ); lastUpdated = now; if ( unscaled < 1f/20f ) { _estimatedDelta += _estimatedDeltaFilter * ( unscaled - _estimatedDelta ); } if ( computeRealtimeWithEngineDelta ) { _unscaledDelta = (float)( Engine.TimeScale == 0 ? _estimatedDelta : ( engineDelta / Engine.TimeScale ) ); } else { _unscaledDelta = _estimatedDelta; } _lastRealtimePosition = _realtimePosition; _realtimePosition += _unscaledDelta; } public float unscaledTimeDelta => _unscaledDelta; public double realtime => _realtimePosition; void Initialize() { if ( _initialized ) { return; } _initialized = true; _runners = ListExtensions.Map( timelines, tl => new TimelineRunner( tl, this ) ); // this.LogInfo( "Created runners:", _runners.Count ); } public static Timeline Ensure( Timeline timeline ) { if ( timeline != null ) { return timeline; } var tm = Get(); if ( tm == null ) { return new Timeline(); } return tm.timeScaleTimeline; } public TimelineRunner GetRunnerByTimeline( Timeline timeline ) { return GetRunner( _runners.FindIndex( r => r.timeLine == timeline ) ); } public TimelineRunner GetRunner( int index ) { if ( index < 0 || index >= _runners.Count ) { return null ; } return _runners[ index ]; } int _CreateID() { _idCounter ++; return _idCounter; } public static int CreateID() { var tm = Get(); return tm._CreateID(); } }