diff --git a/AirMonitor/AirMonitor.csproj b/AirMonitor/AirMonitor.csproj new file mode 100644 index 0000000..0f4379c --- /dev/null +++ b/AirMonitor/AirMonitor.csproj @@ -0,0 +1,28 @@ + + + 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/AirMonitor/AirMonitor.mdk.ini b/AirMonitor/AirMonitor.mdk.ini new file mode 100644 index 0000000..5add8f4 --- /dev/null +++ b/AirMonitor/AirMonitor.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/AirMonitor/AirMonitor.mdk.local.ini b/AirMonitor/AirMonitor.mdk.local.ini new file mode 100644 index 0000000..4b66820 --- /dev/null +++ b/AirMonitor/AirMonitor.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/AirMonitor/AirMonitor.sln b/AirMonitor/AirMonitor.sln new file mode 100644 index 0000000..8d91c25 --- /dev/null +++ b/AirMonitor/AirMonitor.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirMonitor", "AirMonitor.csproj", "{DAD911E6-670F-4AA0-8F00-5D239BC5F732}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DAD911E6-670F-4AA0-8F00-5D239BC5F732}.Debug|Any CPU.ActiveCfg = Debug|x64 + {DAD911E6-670F-4AA0-8F00-5D239BC5F732}.Debug|Any CPU.Build.0 = Debug|x64 + {DAD911E6-670F-4AA0-8F00-5D239BC5F732}.Release|Any CPU.ActiveCfg = Release|x64 + {DAD911E6-670F-4AA0-8F00-5D239BC5F732}.Release|Any CPU.Build.0 = Release|x64 + EndGlobalSection +EndGlobal diff --git a/AirMonitor/AirZone.cs b/AirMonitor/AirZone.cs new file mode 100644 index 0000000..29a8699 --- /dev/null +++ b/AirMonitor/AirZone.cs @@ -0,0 +1,73 @@ +using Sandbox.ModAPI.Ingame; +using SpaceEngineers.Game.ModAPI.Ingame; +using System.Collections.Generic; +using VRage.Game.ModAPI.Ingame.Utilities; + +namespace IngameScript { + partial class Program { + public class AirZone + { + public bool Triggered = false; + + private MyIni _ini; + private List _vents; + private PrefixedConsole _console; + private Sequencer _sequencer; + private const float TriggerLevel = 0.75F; + + public AirZone(string zoneName, MyIni ini, IConsole console) + { + _ini = ini; + _console = new PrefixedConsole(console, zoneName); + _sequencer = new Sequencer(zoneName, _console); + _vents = new List(); + } + + public void AddBlock(IMyTerminalBlock block) + { + if (block is IMyAirVent) + { + _vents.Add(block as IMyAirVent); + } else if (block is IMyDoor) { + SequenceableDoor wrapped = SequenceableFactory.MakeSequenceable(block as IMyDoor, _ini, "") as SequenceableDoor; + wrapped.Step = 0; + wrapped.DeployOpen = false; + wrapped.LockOpen = false; + wrapped.LockClosed = true; + _sequencer.AddBlock(wrapped); + } else { + _console.Print("Tried to add incompatible block '{block.CustomName}'"); + } + // TODO: support for arbitrary (sub-)sequences here could be powerful + } + + // This enumerator should run forever. + public IEnumerator Monitor() + { + while (true) + { + foreach (IMyAirVent vent in _vents) + { + if (vent.GetOxygenLevel() < TriggerLevel) Triggered = true; + } + + if (Triggered == true) + { + // close the doors + IEnumerator job = _sequencer.RunSequence(true); + while (job.MoveNext()) yield return true; + + // noop until manually reset + while (Triggered) yield return true; + + // open the doors + job = _sequencer.RunSequence(false); + while (job.MoveNext()) yield return true; + } + + yield return true; + } + } + } + } +} \ No newline at end of file diff --git a/AirMonitor/Instructions.readme b/AirMonitor/Instructions.readme new file mode 100644 index 0000000..ed30ab7 --- /dev/null +++ b/AirMonitor/Instructions.readme @@ -0,0 +1,5 @@ +R e a d m e +----------- + +In this file you can include any instructions or other comments you want to have injected onto the +top of your final script. You can safely delete this file if you do not want any such comments. diff --git a/AirMonitor/Program.cs b/AirMonitor/Program.cs new file mode 100644 index 0000000..78ada01 --- /dev/null +++ b/AirMonitor/Program.cs @@ -0,0 +1,96 @@ +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 + { + private MyIni _ini; + private Console _console; + private MyCommandLine _cli; + private Dictionary _zones; + private List> _jobs; + + public Program() + { + _cli = new MyCommandLine(); + _ini = new MyIni(); + _console = new Console(this, _ini); + _zones = new Dictionary(); + _jobs = new List>(); + + // Find all tagged objects and build out zones + List blocks = new List(); + GridTerminalSystem.GetBlocksOfType(blocks, block => MyIni.HasSection(block.CustomData, "airMonitor")); + foreach (IMyTerminalBlock block in blocks) + { + _ini.TryParse(block.CustomData); + string[] zones = new string[] { }; + if (_ini.Get("airMonitor", "zones").ToString() != "") + { + zones = _ini.Get("airMonitor", "zones").ToString().Split(','); + } + else if (_ini.Get("airMonitor", "zone").ToString() != "") + { + zones = new string[] { _ini.Get("airMonitor", "zone").ToString() }; + } + + foreach (string zone in zones) + { + if (!_zones.ContainsKey(zone)) + { + _zones[zone] = new AirZone(zone, _ini, _console); + } + _zones[zone].AddBlock(block); + } + } + + foreach (AirZone zone in _zones.Values) + { + _jobs.Add(zone.Monitor()); + } + Runtime.UpdateFrequency |= UpdateFrequency.Update100; + } + + public void Main(string argument, UpdateType updateSource) + { + if (argument != "") + { + _cli.TryParse(argument); + if (_cli.Switch("reset")) + { + for (int i = 0; i < _cli.ArgumentCount; i++) + { + if (_zones.ContainsKey(_cli.Argument(i))) + { + _zones[_cli.Argument(i)].Triggered = false; + } + } + } + } + + foreach (IEnumerator job in _jobs) + { + if (job.MoveNext()) continue; + _console.Print("WARNING: Monitoring job exited. Zone no longer being monitored."); + } + } + } +} diff --git a/AirMonitor/thumb.png b/AirMonitor/thumb.png new file mode 100644 index 0000000..a8dc2ef Binary files /dev/null and b/AirMonitor/thumb.png differ diff --git a/Mixins/Sequencer/SequenceableDoor.cs b/Mixins/Sequencer/SequenceableDoor.cs new file mode 100644 index 0000000..28fa0e6 --- /dev/null +++ b/Mixins/Sequencer/SequenceableDoor.cs @@ -0,0 +1,66 @@ +using Sandbox.ModAPI.Ingame; +using System.Collections.Generic; +using VRage.Game.ModAPI.Ingame.Utilities; + +namespace IngameScript +{ + partial class Program + { + public class SequenceableDoor : ISequenceable + { + public bool Running { get; } + public int Step { get; set; } + + private IMyDoor _door; + public bool DeployOpen { get; set; } + public bool LockOpen { get; set; } + public bool LockClosed { get; set; } + + public SequenceableDoor( + IMyDoor door, + MyIni ini, + string sectionName) + { + _door = door; + + ini.TryParse(door.CustomData); + DeployOpen = ini.Get(sectionName, "deployOpen").ToBoolean(true); + LockOpen = ini.Get(sectionName, "lockOpen").ToBoolean(true); + LockClosed = ini.Get(sectionName, "lockClosed").ToBoolean(true); + Step = ini.Get(sectionName, "step").ToInt32(0); + } + + public IEnumerator Run(bool deploy) + { + if (deploy && DeployOpen || !deploy && !DeployOpen) + { + foreach (bool tick in _openDoor()) yield return true; + } + else + { + foreach (bool tick in _closeDoor()) yield return true; + } + } + + public IEnumerable _openDoor() + { + _door.Enabled = true; + _door.OpenDoor(); + while (_door.Status != DoorStatus.Open) yield return true; + if (LockOpen) { + _door.Enabled = false; + } + } + + public IEnumerable _closeDoor() + { + _door.Enabled = true; + _door.CloseDoor(); + while (_door.Status != DoorStatus.Closed) yield return true; + if (LockClosed) { + _door.Enabled = false; + } + } + } + } +} \ No newline at end of file diff --git a/Mixins/Sequencer/SequenceableFactory.cs b/Mixins/Sequencer/SequenceableFactory.cs index 59e9bd3..1a45e17 100644 --- a/Mixins/Sequencer/SequenceableFactory.cs +++ b/Mixins/Sequencer/SequenceableFactory.cs @@ -27,6 +27,10 @@ namespace IngameScript { // return new SequenceableMergeBlock(block as IMyShipMergeBlock, step); } + if (block is IMyDoor) + { + return new SequenceableDoor(block as IMyDoor, ini, sectionName); + } return null; } }