using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; // shout outs to Chevy Ray for this namespace Otter { /// /// Class that manages Coroutines. /// public class Coroutine { #region Static Fields /// /// The reference to the main Coroutine object managed by Otter. /// public static Coroutine Instance; #endregion #region Static Methods static int nextRoutineId = -1; #endregion #region Private Fields List routines = new List(); Dictionary routineIds = new Dictionary(); Dictionary routineInvertedIds = new Dictionary(); Game game; List events = new List(); #endregion #region Constructors /// /// Create a new Coroutine manager. /// public Coroutine() { } #endregion #region Private Methods void Stop(IEnumerator routine) { if (routines.Contains(routine)) { routines.Remove(routine); } if (routineIds.ContainsValue(routine)) { var key = routineInvertedIds[routine]; routineIds.Remove(key); routineInvertedIds.Remove(routine); } } bool MoveNext(IEnumerator routine) { if (routine.Current is IEnumerator) if (MoveNext((IEnumerator)routine.Current)) return true; return routine.MoveNext(); } #endregion #region Public Methods /// /// Starts a new coroutine and returns an int id for that routine. /// /// The coroutine to start running. /// A unique int id for that routine. public int Start(IEnumerator routine) { routines.Add(routine); nextRoutineId++; routineIds.Add(nextRoutineId, routine); routineInvertedIds.Add(routine, nextRoutineId); return nextRoutineId; } /// /// Immediately clear and stop all Coroutines. /// public void StopAll() { routines.Clear(); } /// /// Stop a routine from running based off its int id. /// /// The id of the routine to stop. public void Stop(int routineId) { if (routineIds.ContainsKey(routineId)) { Stop(routineIds[routineId]); } } /// /// Updates all the routines. The coroutine in the Game automatically runs this. /// public void Update() { for (int i = 0; i < routines.Count; i++) { if (routines[i].Current is IEnumerator) if (MoveNext((IEnumerator)routines[i].Current)) continue; if (!routines[i].MoveNext()) { var key = routineInvertedIds[routines[i]]; routineIds.Remove(key); routineInvertedIds.Remove(routines[i]); routines.RemoveAt(i--); } } events.Clear(); } /// /// The current number of running routines. /// public int Count { get { return routines.Count; } } /// /// If any routines are currently running. /// public bool Running { get { return routines.Count > 0; } } /// /// Publishes an event to the coroutine manager. Used for WaitForEvent. /// Events are cleared on every update. /// /// The string id of the event. public void PublishEvent(string id) { events.Add(id); } /// /// Publishes an event to the coroutine manager. Used for WaitForEvent. /// Events are cleared on every update. /// /// The enum id of the event. public void PublishEvent(Enum id) { events.Add(Util.EnumValueToString(id)); } /// /// Waits until a specific event has been published. /// /// The string id of the event. /// public IEnumerator WaitForEvent(string id) { while (!events.Contains(id)) { yield return 0; } } /// /// Waits until a specific event has been published. /// /// The enum id of the event. /// public IEnumerator WaitForEvent(Enum id) { while (!events.Contains(Util.EnumValueToString(id))) { yield return 0; } } /// /// Check if an event has been published. /// /// The string id of the event. /// True if the event has been published. public bool HasEvent(string id) { return events.Contains(id); } /// /// Check if an event has been published. /// /// The enum id of the event. /// True if the event has been published. public bool HasEvent(Enum id) { return events.Contains(Util.EnumValueToString(id)); } /// /// Waits until an amount of time has passed. /// /// The number of seconds to wait. /// public IEnumerator WaitForSeconds(float seconds) { if (game != null) { // Using the game's delta time. float elapsed = 0; while (elapsed < seconds) { elapsed += game.RealDeltaTime * 0.001f; yield return 0; } } else { // Using a stopwatch. var watch = Stopwatch.StartNew(); while (watch.ElapsedMilliseconds / 1000f < seconds) { yield return 0; } watch.Stop(); } } /// /// Waits until an amount of frames have passed. Don't use this with non-fixed framerates. /// /// The number of frames to wait. /// public IEnumerator WaitForFrames(int frames) { int elapsed = 0; while (elapsed < frames) { elapsed++; yield return 0; } } /// /// Waits for a Tween to complete. /// /// The Tween to wait on. /// public IEnumerator WaitForTween(Tween tween) { while (tween.Completion != 1) yield return 0; } /// /// Waits for an anonymous method that returns true or false. /// /// The method to run until it returns true. /// public IEnumerator WaitForDelegate(Func func) { while (func() != true) yield return 0; } #endregion #region Internal internal Coroutine(Game game) { this.game = game; Instance = this; } #endregion } }