diff --git a/MechanicalDoor/Door.cs b/MechanicalDoor/Door.cs deleted file mode 100644 index 1449b1d..0000000 --- a/MechanicalDoor/Door.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Sandbox.ModAPI.Ingame; -using System.Collections.Generic; - -namespace IngameScript -{ - public class Door - { - private PrefixedConsole _console; - private List _hinges; - - public bool Locked - { - get - { - foreach (DoorHinge hinge in _hinges) - { - if (!hinge.Locked) return false; - } - return true; - } - } - - public Door(Console console, string name) - { - _console = new PrefixedConsole(console, name); - _hinges = new List(); - } - - // Add a hinge to the door - public void AddHinge(IMyMotorStator hinge) - { - _hinges.Add(new DoorHinge(_console, hinge)); - } - - public void OpenDoor() - { - foreach (DoorHinge hinge in _hinges) - { - hinge.OpenDoorHinge(); - } - } - - public void CloseDoor() - { - foreach (DoorHinge hinge in _hinges) - { - hinge.CloseDoorHinge(); - } - } - - // Process the door's movement. This will return true when the door is done moving. - public bool Actuate() - { - bool done = true; - foreach (DoorHinge hinge in _hinges) - { - if (!hinge.Actuate()) done = false; - } - return done; - } - } -} \ No newline at end of file diff --git a/MechanicalDoor/DoorHinge.cs b/MechanicalDoor/DoorHinge.cs deleted file mode 100644 index 9950136..0000000 --- a/MechanicalDoor/DoorHinge.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Sandbox.ModAPI.Ingame; -using System.Collections.Generic; -using System; - -namespace IngameScript -{ - public class DoorHinge - { - public bool Locked { get { return _hinge.RotorLock; } } - - private PrefixedConsole _console; - private IMyMotorStator _hinge; - private float _targetAngle; - private float _lastAngle; - private float _openAngle = 90F; - private float _closedAngle = 0F; - private float _velocity = 5F; - - public DoorHinge(PrefixedConsole console, IMyMotorStator hinge) - { - _hinge = hinge; - _console = console; - - ConfigParser config = new ConfigParser(_hinge); - _openAngle = config.GetValue("OpenAngle", 90F); - _closedAngle = config.GetValue("ClosedAngle", 0F); - _velocity = config.GetValue("Velocity", 5F); - } - - // For these two functions, IMyMotorStator.Angle reports radians, but - // IMyMotorStator.RotateToAngle() expects degrees... - public void OpenDoorHinge() - { - _hinge.RotorLock = false; - _targetAngle = degToRad(_openAngle); - _hinge.RotateToAngle(MyRotationDirection.AUTO, _openAngle, _velocity); - } - - public void CloseDoorHinge() - { - _hinge.RotorLock = false; - _targetAngle = degToRad(_closedAngle); - _hinge.RotateToAngle(MyRotationDirection.AUTO, _closedAngle, _velocity); - } - - // Process the hinge's movement. - // It will return true when the panel has finished moving. - // TODO: Add a mechanism to determine when a door gets stuck or can't reach the target angle. - public bool Actuate() - { - if (Math.Abs(_hinge.Angle - _targetAngle) < 0.1 && _hinge.Angle == _lastAngle) - { - _hinge.RotorLock = true; - } - _lastAngle = _hinge.Angle; - return Locked; - } - - private float degToRad(float degrees) - { - return degrees * ((float)Math.PI / 180F); - } - } -} \ No newline at end of file diff --git a/MechanicalDoor/MechanicalDoor.csproj b/MechanicalDoor/MechanicalDoor.csproj index 7b595e0..a0c8b60 100644 --- a/MechanicalDoor/MechanicalDoor.csproj +++ b/MechanicalDoor/MechanicalDoor.csproj @@ -25,4 +25,6 @@ + + \ No newline at end of file diff --git a/MechanicalDoor/Program.cs b/MechanicalDoor/Program.cs index 696745f..b7a68fa 100644 --- a/MechanicalDoor/Program.cs +++ b/MechanicalDoor/Program.cs @@ -1,4 +1,5 @@ using Sandbox.ModAPI.Ingame; +using SpaceEngineers.Game.ModAPI.Ingame; using System.Collections.Generic; using VRage.Game.ModAPI.Ingame.Utilities; @@ -9,7 +10,7 @@ namespace IngameScript private MyCommandLine _cli; private Console _console; private List> _jobs; - private Dictionary _doors; + private Dictionary _doors; private int _tickCount; public Program() @@ -19,21 +20,25 @@ namespace IngameScript _console = new Console(this); _jobs = new List>(); - _doors = new Dictionary(); + _doors = new Dictionary(); - List allHinges = new List(); - GridTerminalSystem.GetBlocksOfType(allHinges); - foreach (IMyMotorStator hinge in allHinges) + List doorParts = Utils.GetBlocksNameContains(GridTerminalSystem, "!Door"); + foreach (IMyTerminalBlock block in doorParts) { - if (hinge.CustomName.StartsWith("Door")) + string doorName = Utils.ExtractTag(block, "!Door"); + + // Create the door if this is a new tag + if (!_doors.ContainsKey(doorName)) { - string doorName = hinge.CustomName.Split(' ')[0]; - if (!_doors.ContainsKey(doorName)) - { - _doors[doorName] = new Door(_console, doorName); - } - _doors[doorName].AddHinge(hinge); + _doors[doorName] = new Sequencer(doorName, new PrefixedConsole(_console, doorName)); } + + // Add the part; the Door object handles typing and sequencing. + int defaultStep = 1; + if (block is IMyShipMergeBlock) defaultStep = 0; + ISequenceable wrapped = SequenceableFactory.MakeSequenceable(block, defaultStep); + if (wrapped == null) { _console.Print($"Tried to add incompatible block '{block.CustomName}'"); continue; } + _doors[doorName].AddBlock(wrapped); } _console.Print($"Found {_doors.Keys.Count} doors."); @@ -47,17 +52,14 @@ namespace IngameScript { // Create a new job _cli.TryParse(argument); - List doorsToControl = new List(); + List doorsToControl = new List(); if (_cli.ArgumentCount == 0) { _console.Print("No arguments passed. Controlling all doors."); - foreach (Door door in _doors.Values) + foreach (Sequencer door in _doors.Values) { - if (!door.Locked) - { - continue; - } + if (door.Running) continue; doorsToControl.Add(door); } } @@ -67,12 +69,12 @@ namespace IngameScript string key = "Door" + _cli.Argument(i); if (!_doors.ContainsKey(key)) { - _console.Print($"Door with identifier {key} not found. Skipping."); + _console.Print($"Door '{key}' not found. Skipping."); continue; } - if (!_doors[key].Locked) + if (_doors[key].Running) { - _console.Print($"Door {key} already moving. Skipping."); + _console.Print($"Door '{key}' already moving. Skipping."); continue; } doorsToControl.Add(_doors[key]); @@ -84,10 +86,13 @@ namespace IngameScript } else { - _console.Print("Creating new job."); - if (_cli.Switch("close")) _jobs.Add(closeDoors(doorsToControl)); - else _jobs.Add(openDoors(doorsToControl)); - Runtime.UpdateFrequency = UpdateFrequency.Update1; + _console.Print("Creating new job(s)."); + bool close = _cli.Switch("close"); + foreach (Sequencer door in doorsToControl) + { + _jobs.Add(door.RunSequence(close)); + } + Runtime.UpdateFrequency |= UpdateFrequency.Update1; } } @@ -108,43 +113,5 @@ namespace IngameScript Runtime.UpdateFrequency = UpdateFrequency.None; } } - - private IEnumerator openDoors(List doorsToControl) - { - _console.Print("Opening doors."); - foreach (Door door in doorsToControl) - { - door.OpenDoor(); - } - return actuateDoors(doorsToControl); - } - - private IEnumerator closeDoors(List doorsToControl) - { - _console.Print("Closing doors."); - foreach (Door door in doorsToControl) - { - door.CloseDoor(); - } - return actuateDoors(doorsToControl); - } - - private IEnumerator actuateDoors(List doorsToControl) - { - while (true) - { - _console.Print("Actuating doors."); - bool done = true; // assume we've finished, then falsify it below - foreach (Door door in doorsToControl) - { - if (!door.Actuate()) - { - done = false; - } - } - if (done) yield break; - yield return true; - } - } } } diff --git a/Mixins/Sequencer/ISequenceable.cs b/Mixins/Sequencer/ISequenceable.cs new file mode 100644 index 0000000..defd8cc --- /dev/null +++ b/Mixins/Sequencer/ISequenceable.cs @@ -0,0 +1,14 @@ +namespace IngameScript +{ + partial class Program + { + public interface ISequenceable + { + bool Running { get; } + int Step { get; } + void Start(bool reverse = true); + bool Check(); + void Finish(); + } + } +} \ No newline at end of file diff --git a/Mixins/Sequencer/SequenceableFactory.cs b/Mixins/Sequencer/SequenceableFactory.cs new file mode 100644 index 0000000..829bebc --- /dev/null +++ b/Mixins/Sequencer/SequenceableFactory.cs @@ -0,0 +1,30 @@ +// I hate Factories, but when the shoe fits... + +using Sandbox.ModAPI.Ingame; +using SpaceEngineers.Game.ModAPI.Ingame; + +namespace IngameScript +{ + partial class Program + { + public class SequenceableFactory + { + public static ISequenceable MakeSequenceable(IMyTerminalBlock block, int step = 0) + { + if (block is IMyMotorStator) + { + return new SequenceableRotor(block as IMyMotorStator, step); + } + if (block is IMyPistonBase) + { + // return new SequenceablePiston(block as IMyPistonBase, step); + } + if (block is IMyShipMergeBlock) + { + // return new SequenceableMergeBlock(block as IMyShipMergeBlock, step); + } + return null; + } + } + } +} \ No newline at end of file diff --git a/Mixins/Sequencer/SequenceableRotor.cs b/Mixins/Sequencer/SequenceableRotor.cs new file mode 100644 index 0000000..b106fc3 --- /dev/null +++ b/Mixins/Sequencer/SequenceableRotor.cs @@ -0,0 +1,64 @@ +using Sandbox.ModAPI.Ingame; +using System.Collections.Generic; +using System; + +namespace IngameScript +{ + partial class Program + { + public class SequenceableRotor : ISequenceable + { + public bool Running { get; private set; } = false; + public int Step { get; private set; } + + private float _targetAngle; + private float _lastAngle; + + private float _velocity; + private float _openAngle; + private float _closedAngle; + private IMyMotorStator _rotor; + + public SequenceableRotor(IMyMotorStator rotor, int defaultStep = 0) + { + _rotor = rotor; + + ConfigParser config = new ConfigParser(_rotor); + _openAngle = config.GetValue("OpenAngle", 90F); + _closedAngle = config.GetValue("ClosedAngle", 0F); + _velocity = config.GetValue("Velocity", 5F); + Step = config.GetValue("Step", defaultStep); + } + + public void Start(bool reverse) + { + float degAngle = reverse ? _closedAngle : _openAngle; + _targetAngle = degToRad(degAngle); + _rotor.RotorLock = false; + _rotor.RotateToAngle(MyRotationDirection.AUTO, degAngle, _velocity); + Running = true; + } + + public bool Check() + { + if (Math.Abs(_rotor.Angle - _targetAngle) < 0.1 && _rotor.Angle == _lastAngle) + { + return false; + } + _lastAngle = _rotor.Angle; + return true; + } + + public void Finish() + { + _rotor.RotorLock = true; + Running = false; + } + + private float degToRad(float degrees) + { + return degrees * ((float)Math.PI / 180F); + } + } + } +} \ No newline at end of file diff --git a/Mixins/Sequencer/Sequencer.cs b/Mixins/Sequencer/Sequencer.cs new file mode 100644 index 0000000..3f4168f --- /dev/null +++ b/Mixins/Sequencer/Sequencer.cs @@ -0,0 +1,80 @@ +// 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; } + + private IConsole _console; + private string _name; + + private SortedDictionary> _sequence; + + public Sequencer(string name, IConsole console) + { + _name = name; + _console = console; + _sequence = new SortedDictionary>(); + } + + 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 reverse = false) + { + 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 = reverse ? _sequence.Reverse() : _sequence; + foreach (KeyValuePair> kvp in steps) + { + List blocks = kvp.Value; + Running = true; + + foreach (ISequenceable block in blocks) + { + // TODO: add some sort of handling for block.Running == true + block.Start(reverse); + } + yield return true; + + while (Running) + { + Running = false; + foreach (ISequenceable block in blocks) + { + if (block.Check()) Running = true; + } + yield return true; + } + + foreach (ISequenceable block in blocks) + { + block.Finish(); + } + } + + yield break; + } + } + } +} \ No newline at end of file diff --git a/Mixins/Sequencer/Sequencer.projitems b/Mixins/Sequencer/Sequencer.projitems new file mode 100644 index 0000000..141cea5 --- /dev/null +++ b/Mixins/Sequencer/Sequencer.projitems @@ -0,0 +1,11 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 8a3cdcc5-4b55-4d87-a415-698a0e1ff06f + + + + + \ No newline at end of file diff --git a/Mixins/Sequencer/Sequencer.shproj b/Mixins/Sequencer/Sequencer.shproj new file mode 100644 index 0000000..83aa175 --- /dev/null +++ b/Mixins/Sequencer/Sequencer.shproj @@ -0,0 +1,19 @@ + + + + 8a3cdcc5-4b55-4d87-a415-698a0e1ff06f + 14.0 + + + + + + + bin\Debug\ + + + bin\Release\ + + + +