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
}
}