// Represents a single set of blocks to run in order, waiting for each group's completion before // moving on to the next one. using Sandbox.ModAPI.Ingame; using System.Collections.Generic; using System.Linq; namespace IngameScript { partial class Program { public class Sequencer { public bool Running { get; private set; } public string Name { get; private set; } private IConsole _console; private SortedDictionary> _sequence = new SortedDictionary>(); public Sequencer(Program program, string name) { Name = name; _console = new PrefixedConsole(program.Console, Name); } public void AddBlock(ISequenceable block) { if (!_sequence.ContainsKey(block.Step)) _sequence[block.Step] = new List(); _sequence[block.Step].Add(block); } // To activate the Sequencer, call this once, then call `MoveNext()` once per tick // on the returned object until it returns false. // (then be sure to call `Dispose() on that object`) public IEnumerator RunSequence(bool deploy = true) { if (Running) { _console.Print("Already running, ignoring invocation."); yield break; } // This is a bit convoluted, but we're extracting the iterator for use in the foreach directly. // This allows us to iterate in reverse when reversing/"closing" the sequence. IEnumerable>> steps = deploy ? _sequence : _sequence.Reverse(); foreach (KeyValuePair> kvp in steps) { List blocks = kvp.Value; List> subJobs = new List>(); Running = true; foreach (ISequenceable block in blocks) { // TODO: add some sort of handling for block.Running == true, maybe? // It *should* be an impossible case, but... subJobs.Add(block.Run(deploy)); } while (Running) { Running = false; foreach (IEnumerator subJob in subJobs) { if (subJob.MoveNext()) Running = true; } yield return true; } foreach (IEnumerator subJob in subJobs) subJob.Dispose(); // clean up after ourselves } } } } }