diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc55ae2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc +nupkg/ + +# Visual Studio Code +.vscode/* +!.vscode/settings.json + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +msbuild.log +msbuild.err +msbuild.wrn \ No newline at end of file diff --git a/MechanicalDoor/Door.cs b/MechanicalDoor/Door.cs new file mode 100644 index 0000000..5a41af5 --- /dev/null +++ b/MechanicalDoor/Door.cs @@ -0,0 +1,57 @@ +using Sandbox.ModAPI.Ingame; +using System.Collections.Generic; + +namespace IngameScript +{ + public class Door + { + public Door() + { + Hinges = new List(); + } + + // Add a hinge to the door + public void AddHinge(IMyMotorStator hinge) + { + Hinges.Add(new DoorHinge(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; + } + + public bool Locked() + { + foreach (DoorHinge hinge in Hinges) + { + if (!hinge.Locked()) return false; + } + return true; + } + + private List Hinges; + } +} \ No newline at end of file diff --git a/MechanicalDoor/DoorHinge.cs b/MechanicalDoor/DoorHinge.cs new file mode 100644 index 0000000..f929948 --- /dev/null +++ b/MechanicalDoor/DoorHinge.cs @@ -0,0 +1,84 @@ +using Sandbox.ModAPI.Ingame; +using System.Collections.Generic; +using System; + +namespace IngameScript +{ + public class DoorHinge + { + public DoorHinge(IMyMotorStator hinge) + { + Hinge = hinge; + } + + // 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(); + } + + public bool Locked() + { + return Hinge.RotorLock; + } + + private IMyMotorStator Hinge { get; set; } + private float TargetAngle { get; set; } + private float LastAngle { get; set; } + private float OpenAngle { get; set; } = 90F; + private float ClosedAngle { get; set; } = 0F; + private float Velocity { get; set; } = 5F; + + private void ParseConfig() + { + string[] lines = Hinge.CustomData.Split('\n'); + foreach (string line in lines) + { + string[] tokens = line.Split('='); + if (tokens.Length != 2) continue; + switch (tokens[0]) + { + case "OpenAngle": + OpenAngle = float.Parse(tokens[1]); + break; + case "ClosedAngle": + ClosedAngle = float.Parse(tokens[1]); + break; + case "Velocity": + Velocity = float.Parse(tokens[1]); + break; + } + } + } + + // TODO: a utility class or function would be lovely... + // In general, the encapsulation feels a little screwy here. + 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 new file mode 100644 index 0000000..8434746 --- /dev/null +++ b/MechanicalDoor/MechanicalDoor.csproj @@ -0,0 +1,27 @@ + + + netframework48 + IngameScript + 6 + false + Release;Debug + x64 + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + \ No newline at end of file diff --git a/MechanicalDoor/MechanicalDoor.mdk.ini b/MechanicalDoor/MechanicalDoor.mdk.ini new file mode 100644 index 0000000..5add8f4 --- /dev/null +++ b/MechanicalDoor/MechanicalDoor.mdk.ini @@ -0,0 +1,22 @@ +; This file is project specific and should be checked in to source control. + +[mdk] +; This is a programmable block script project. +; You should not change this. +type=programmableblock + +; Toggle trace (on|off) (verbose output) +trace=off + +; What type of minification to use (none|trim|stripcomments|lite|full) +; none: No minification +; trim: Removes unused types (NOT members). +; stripcomments: trim + removes comments. +; lite: stripcomments + removes leading/trailing whitespace. +; full: lite + renames identifiers to shorter names. +minify=none + +; A list of files and folder to ignore when creating the script. +; This is a comma separated list of glob patterns. +; See https://code.visualstudio.com/docs/editor/glob-patterns +ignores=obj/**/*,MDK/**/*,**/*.debug.cs diff --git a/MechanicalDoor/MechanicalDoor.mdk.local.ini b/MechanicalDoor/MechanicalDoor.mdk.local.ini new file mode 100644 index 0000000..4b66820 --- /dev/null +++ b/MechanicalDoor/MechanicalDoor.mdk.local.ini @@ -0,0 +1,7 @@ +; This file is _local_ to your machine and should not be checked in to source control. + +[mdk] +; Where to output the script to (auto|specific path) +output=auto +; Override the default binary path (auto|specific path) +binarypath=auto \ No newline at end of file diff --git a/MechanicalDoor/Program.cs b/MechanicalDoor/Program.cs new file mode 100644 index 0000000..e2f22eb --- /dev/null +++ b/MechanicalDoor/Program.cs @@ -0,0 +1,165 @@ +using Sandbox.Game.EntityComponents; +using Sandbox.ModAPI.Ingame; +using Sandbox.ModAPI.Interfaces; +using SpaceEngineers.Game.ModAPI.Ingame; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using VRage; +using VRage.Collections; +using VRage.Game; +using VRage.Game.Components; +using VRage.Game.GUI.TextPanel; +using VRage.Game.ModAPI.Ingame; +using VRage.Game.ModAPI.Ingame.Utilities; +using VRage.Game.ObjectBuilders.Definitions; +using VRageMath; + +namespace IngameScript +{ + public partial class Program : MyGridProgram + { + MyCommandLine cli; + List> jobs; + Dictionary doors; + int tickCount; + + + public Program() + { + tickCount = 0; + cli = new MyCommandLine(); + jobs = new List>(); + + doors = new Dictionary(); + + List allHinges = new List(); + GridTerminalSystem.GetBlocksOfType(allHinges); + foreach (IMyMotorStator hinge in allHinges) + { + if (hinge.CustomName.StartsWith("Door")) + { + string doorName = hinge.CustomName.Split(' ')[0]; + if (!doors.ContainsKey(doorName)) + { + doors[doorName] = new Door(); + } + doors[doorName].AddHinge(hinge); + } + } + + Echo($"Found {doors.Keys.Count} doors."); + } + + public void Main(string argument, UpdateType updateSource) + { + Echo($"index: {tickCount++}"); + + if (updateSource == UpdateType.Trigger || updateSource == UpdateType.Terminal) + { + // Create a new job + cli.TryParse(argument); + List doorsToControl = new List(); + + if (cli.ArgumentCount == 0) + { + Echo("No arguments passed. Controlling all doors."); + foreach (Door door in doors.Values) + { + if (!door.Locked()) + { + continue; + } + doorsToControl.Add(door); + } + } + + for (int i = 0; i < cli.ArgumentCount; i++) + { + string key = "Door" + cli.Argument(i); + if (!doors.ContainsKey(key)) + { + Echo($"Door with identifier {key} not found. Skipping."); + continue; + } + if (!doors[key].Locked()) + { + Echo($"Door {key} already moving. Skipping."); + continue; + } + doorsToControl.Add(doors[key]); + } + + if (doorsToControl.Count == 0) + { + Echo("No doors found. Not creating new job."); + } + else + { + Echo("Creating new job."); + if (cli.Switch("close")) jobs.Add(CloseDoors(doorsToControl)); + else jobs.Add(OpenDoors(doorsToControl)); + Runtime.UpdateFrequency = UpdateFrequency.Update1; + } + } + + // Process running jobs + for (int i = 0; i < jobs.Count; i++) + { + if (!jobs[i].MoveNext()) + { + jobs[i].Dispose(); + jobs.Remove(jobs[i]); + i--; + Echo("Operation Complete."); + } + } + + if (jobs.Count == 0) + { + Runtime.UpdateFrequency = UpdateFrequency.None; + } + } + + private IEnumerator OpenDoors(List doorsToControl) + { + Echo("Opening doors."); + foreach (Door door in doorsToControl) + { + door.OpenDoor(); + } + return ActuateDoors(doorsToControl); + } + + private IEnumerator CloseDoors(List doorsToControl) + { + Echo("Closing doors."); + foreach (Door door in doorsToControl) + { + door.CloseDoor(); + } + return ActuateDoors(doorsToControl); + } + + private IEnumerator ActuateDoors(List doorsToControl) + { + while (true) + { + Echo("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/MechanicalDoor/instructions.readme b/MechanicalDoor/instructions.readme new file mode 100644 index 0000000..4a5d285 --- /dev/null +++ b/MechanicalDoor/instructions.readme @@ -0,0 +1,20 @@ +Script for controlling "mechanical" doors, aka custom doors using hinges. + +Future features: + * Rotor and piston support. + * "Multi-stage doors", e.g. actuate a hinge, then a piston. + +Currently this operates on hinges whose names start with "DoorX", where X is an arbitrary identifier. +Usage: + Script argument should be a space-separated list of identifiers and either + `-open` (default behavior) or `-close`. + Failing to pass any identifiers will cause the action to happen to all doors on the grid. + +By default "open" is assumed to be 90 degrees and "closed" is assumed to be 0 degrees. + However, each hinge can have this behavior configured via custom data. A sample config + (with the default values): + +OpenAngle=90 +ClosedAngle=0 +Velocity=5 + diff --git a/MechanicalDoor/thumb.png b/MechanicalDoor/thumb.png new file mode 100644 index 0000000..a8dc2ef Binary files /dev/null and b/MechanicalDoor/thumb.png differ diff --git a/mechanical_door_controller.cs b/mechanical_door_controller.cs deleted file mode 100644 index f361632..0000000 --- a/mechanical_door_controller.cs +++ /dev/null @@ -1,246 +0,0 @@ -// Script for controlling "mechanical" doors, aka custom doors using hinges. -// -// Future features: -// * Rotor and piston support. -// * "Multi-stage doors", e.g. actuate a hinge, then a piston. -// -// Currently this operates on hinges whose names start with "DoorX", where X is an arbitrary identifier. -// Usage: -// Script argument should be a space-separated list of identifiers and either -// `-open` (default behavior) or `-close`. -// Failing to pass any identifiers will cause the action to happen to all doors on the grid. -// -// By default "open" is assumed to be 90 degrees and "closed" is assumed to be 0 degrees. -// However, each hinge can have this behavior configured via custom data. A sample config -// (with the default values): -// -// OpenAngle=90 -// ClosedAngle=0 -// Velocity=5 - - - -MyCommandLine cli; -List> jobs; -Dictionary doors; -int tickCount; - -public class DoorHinge { - public DoorHinge(Program p, IMyMotorStator hinge) { - P = p; - Hinge = hinge; - } - - // 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(); - } - - public bool Locked() { - return Hinge.RotorLock; - } - - private Program P; // for access to Echo, etc. - private IMyMotorStator Hinge { get; set; } - private float TargetAngle { get; set; } - private float LastAngle {get; set; } - private float OpenAngle { get; set; } = 90F; - private float ClosedAngle { get; set; } = 0F; - private float Velocity { get; set; } = 5F; - - private void ParseConfig() { - string[] lines = Hinge.CustomData.Split('\n'); - foreach (string line in lines) { - string[] tokens = line.Split('='); - if (tokens.Length != 2) continue; - switch(tokens[0]) { - case "OpenAngle": - OpenAngle = float.Parse(tokens[1]); - break; - case "ClosedAngle": - ClosedAngle = float.Parse(tokens[1]); - break; - case "Velocity": - Velocity = float.Parse(tokens[1]); - break; - } - } - } - - // TODO: a utility class or function would be lovely... - // In general, the encapsulation feels a little screwy here. - private float DegToRad(float degrees) { - return degrees * ((float)Math.PI / 180F); - } -} - -public class Door { - public Door(Program p) { - P = p; - Hinges = new List(); - } - - // Add a hinge to the door - public void AddHinge(IMyMotorStator hinge) { - Hinges.Add(new DoorHinge(P, 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; - } - - public bool Locked() { - foreach (DoorHinge hinge in Hinges) { - if (!hinge.Locked()) return false; - } - return true; - } - - private List Hinges; - private Program P; -} - -public Program() { - tickCount = 0; - cli = new MyCommandLine(); - jobs = new List>(); - - doors = new Dictionary(); - - List allHinges = new List(); - GridTerminalSystem.GetBlocksOfType(allHinges); - foreach(IMyMotorStator hinge in allHinges) { - if (hinge.CustomName.StartsWith("Door")) { - string doorName = hinge.CustomName.Split(' ')[0]; - if (!doors.ContainsKey(doorName)) { - doors[doorName] = new Door(this); - } - doors[doorName].AddHinge(hinge); - } - } - - Echo($"Found {doors.Keys.Count} doors."); -} - -public void Main(string argument, UpdateType updateSource) { - Echo($"index: {tickCount++}"); - - if (updateSource == UpdateType.Trigger || updateSource == UpdateType.Terminal) { - // Create a new job - cli.TryParse(argument); - List doorsToControl = new List(); - - if (cli.ArgumentCount == 0) { - Echo("No arguments passed. Controlling all doors."); - foreach (Door door in doors.Values) { - if (!door.Locked()) { - continue; - } - doorsToControl.Add(door); - } - } - - for (int i=0; i < cli.ArgumentCount; i++) { - string key = "Door" + cli.Argument(i); - if (!doors.ContainsKey(key)) { - Echo($"Door with identifier {key} not found. Skipping."); - continue; - } - if (!doors[key].Locked()) { - Echo($"Door {key} already moving. Skipping."); - continue; - } - doorsToControl.Add(doors[key]); - } - - if (doorsToControl.Count == 0) { - Echo("No doors found. Not creating new job."); - } else { - Echo("Creating new job."); - if (cli.Switch("close")) jobs.Add(CloseDoors(doorsToControl)); - else jobs.Add(OpenDoors(doorsToControl)); - Runtime.UpdateFrequency = UpdateFrequency.Update1; - } - } - - // Process running jobs - for (int i=0; i < jobs.Count; i++) { - if (!jobs[i].MoveNext()) { - jobs[i].Dispose(); - jobs.Remove(jobs[i]); - i--; - Echo("Operation Complete."); - } - } - - if (jobs.Count == 0) { - Runtime.UpdateFrequency = UpdateFrequency.None; - } -} - -private IEnumerator OpenDoors(List doorsToControl) { - Echo("Opening doors."); - foreach (Door door in doorsToControl) { - door.OpenDoor(); - } - return ActuateDoors(doorsToControl); -} - -private IEnumerator CloseDoors(List doorsToControl) { - Echo("Closing doors."); - foreach (Door door in doorsToControl) { - door.CloseDoor(); - } - return ActuateDoors(doorsToControl); -} - -private IEnumerator ActuateDoors(List doorsToControl) { - while (true) { - Echo("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; - } -}