Refactor doors and lights to be slightly less coupled to the Zones. This allows them to be shared by multiple zones.

This commit is contained in:
Anna Rose 2025-02-12 22:32:00 -05:00
parent 4be70bdfdb
commit 8b740dde71
3 changed files with 110 additions and 70 deletions

19
AirMonitor/AirDoor.cs Normal file
View File

@ -0,0 +1,19 @@
using Sandbox.ModAPI.Ingame;
using System.Collections.Generic;
namespace IngameScript
{
partial class Program
{
public class AirDoor
{
public bool Triggered { get; set; } = false;
public IMyDoor Door { get; private set; }
public AirDoor(Program program, IMyDoor door)
{
Door = door;
}
}
}
}

View File

@ -10,13 +10,14 @@ namespace IngameScript
public class AirZone public class AirZone
{ {
public string Name { get; private set; } public string Name { get; private set; }
public bool Triggered public bool Triggered
{ {
get get
{ {
foreach (IMyAirVent vent in Vents) foreach (IMyAirVent vent in Vents)
{ {
if (vent.GetOxygenLevel() < TriggerLevel) return true; if (vent.GetOxygenLevel() < TriggerLevel || vent.Status == VentStatus.Depressurized) return true;
} }
return false; return false;
} }
@ -24,9 +25,10 @@ namespace IngameScript
public List<IMyAirVent> Vents { get; } = new List<IMyAirVent>(); public List<IMyAirVent> Vents { get; } = new List<IMyAirVent>();
private Program _program; private Program _program;
private List<IMyDoor> _doors = new List<IMyDoor>();
private List<IMyTextSurface> _displays = new List<IMyTextSurface>(); // private List<IMyTextSurface> _displays = new List<IMyTextSurface>(); // TODO: add single-zone displays
private List<IMyLightingBlock> _lights = new List<IMyLightingBlock>(); private List<IMyLightingBlock> _lights = new List<IMyLightingBlock>();
private List<AirDoor> _doors = new List<AirDoor>();
private PrefixedConsole _console; private PrefixedConsole _console;
private Sequencer _sequencer; private Sequencer _sequencer;
@ -40,25 +42,27 @@ namespace IngameScript
_sequencer = new Sequencer(_program, Name); _sequencer = new Sequencer(_program, Name);
} }
public void AddDoor(AirDoor door)
{
_doors.Add(door);
IMyDoor doorBlock = door.Door;
SequenceableDoor wrapped = SequenceableFactory.MakeSequenceable(
_program,
doorBlock as IMyDoor,
"airMonitor") as SequenceableDoor;
wrapped.Step = 0;
wrapped.DeployOpen = false;
wrapped.LockOpen = false;
wrapped.LockClosed = true;
_sequencer.AddBlock(wrapped);
}
public void AddBlock(IMyTerminalBlock block) public void AddBlock(IMyTerminalBlock block)
{ {
if (block is IMyAirVent) if (block is IMyAirVent)
{ {
Vents.Add(block as IMyAirVent); Vents.Add(block as IMyAirVent);
} }
else if (block is IMyDoor)
{
_doors.Add(block as IMyDoor);
SequenceableDoor wrapped = SequenceableFactory.MakeSequenceable(
_program,
block as IMyDoor,
"airMonitor") as SequenceableDoor;
wrapped.Step = 0;
wrapped.DeployOpen = false;
wrapped.LockOpen = false;
wrapped.LockClosed = true;
_sequencer.AddBlock(wrapped);
}
else if (block is IMyLightingBlock) else if (block is IMyLightingBlock)
{ {
_lights.Add(block as IMyLightingBlock); _lights.Add(block as IMyLightingBlock);
@ -78,54 +82,34 @@ namespace IngameScript
if (Triggered == true) if (Triggered == true)
{ {
_console.Print($"Low pressure alarm triggered."); _console.Print($"Low pressure alarm triggered.");
// close the doors // close the doors
IEnumerator<bool> job = _sequencer.RunSequence(true); IEnumerator<bool> job = _sequencer.RunSequence(true);
while (job.MoveNext()) while (job.MoveNext())
{ {
// It would be nice if the API had UpdateFrequency.Once10, e.g. "re-run in 10 ticks and then clear the flag" // It would be nice if the API had UpdateFrequency.Once10, e.g. "re-run in 10 ticks and then clear the flag"
// We'll just monitor every tick instead. // We'll just monitor every tick instead.
setTriggeredStates();
_program.Runtime.UpdateFrequency |= UpdateFrequency.Once; _program.Runtime.UpdateFrequency |= UpdateFrequency.Once;
yield return true; yield return true;
} }
job.Dispose(); job.Dispose();
while (Triggered) { yield return true; } while (Triggered)
// unlock the doors, but we'll leave them closed.
foreach (IMyDoor door in _doors) { door.Enabled = true; }
foreach (IMyLightingBlock light in _lights)
{ {
light.Enabled = true; setTriggeredStates();
light.Color = Color.White; yield return true;
} }
_console.Print("Air pressure returned to safe levels.");
} }
yield return true; yield return true;
} }
} }
public void SetLights() private void setTriggeredStates()
{ {
bool warning = false; foreach (IMyLightingBlock light in _lights) light.Color = Color.Red;
foreach (IMyAirVent vent in Vents) foreach (AirDoor door in _doors) door.Triggered = true;
{
if (vent.GetOxygenLevel() < TriggerLevel)
{
warning = true;
break;
}
}
foreach (IMyLightingBlock light in _lights)
{
if (warning)
{
light.Enabled = true;
light.Color = Color.Red;
}
else light.Color = Color.White;
}
} }
} }
} }

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using VRage.Game.ModAPI.Ingame.Utilities; using VRage.Game.ModAPI.Ingame.Utilities;
using VRageMath;
namespace IngameScript namespace IngameScript
{ {
@ -13,12 +14,17 @@ namespace IngameScript
public MyIni Ini { get; } = new MyIni(); public MyIni Ini { get; } = new MyIni();
public IConsole Console { get; private set; } public IConsole Console { get; private set; }
private Dictionary<string, AirZone> _zones = new Dictionary<string, AirZone>();
private List<IEnumerator<bool>> _jobs = new List<IEnumerator<bool>>(); private List<IEnumerator<bool>> _jobs = new List<IEnumerator<bool>>();
private Dictionary<string, AirZone> _zones = new Dictionary<string, AirZone>();
private List<AirDoor> _doors = new List<AirDoor>();
private List<IMyLightingBlock> _lights = new List<IMyLightingBlock>();
private List<IMyTextSurface> _displays = new List<IMyTextSurface>(); private List<IMyTextSurface> _displays = new List<IMyTextSurface>();
private StringBuilder _displayBuffer = new StringBuilder();
private List<IMyGasTank> _oxygenTanks = new List<IMyGasTank>(); private List<IMyGasTank> _oxygenTanks = new List<IMyGasTank>();
private StringBuilder _displayBuffer = new StringBuilder();
public Program() public Program()
{ {
Console = new MainConsole(this, "Air Pressure Monitor"); Console = new MainConsole(this, "Air Pressure Monitor");
@ -31,13 +37,34 @@ namespace IngameScript
Ini.TryParse(block.CustomData); Ini.TryParse(block.CustomData);
string[] zones = new string[] { }; string[] zones = new string[] { };
if (block is IMyTextSurface) 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(this, zone);
}
if (block is IMyGasTank)
{
_oxygenTanks.Add(block as IMyGasTank);
}
else if (block is IMyTextSurface)
{
// TODO: behave differently if zone is defined
Console.Print($"Adding monitoring display '{block.CustomName}'"); Console.Print($"Adding monitoring display '{block.CustomName}'");
_displays.Add(block as IMyTextSurface); _displays.Add(block as IMyTextSurface);
} }
else if (Ini.Get("airMonitor", "display").ToString() != "") else if (Ini.Get("airMonitor", "display").ToString() != "")
{ {
// TODO: behave differently if zone is defined
int displayIndex = Ini.Get("airMonitor", "display").ToInt32(); int displayIndex = Ini.Get("airMonitor", "display").ToInt32();
IMyTextSurfaceProvider provider = block as IMyTextSurfaceProvider; IMyTextSurfaceProvider provider = block as IMyTextSurfaceProvider;
if (provider.SurfaceCount <= displayIndex) if (provider.SurfaceCount <= displayIndex)
@ -49,29 +76,26 @@ namespace IngameScript
_displays.Add(provider.GetSurface(displayIndex)); _displays.Add(provider.GetSurface(displayIndex));
} }
} }
else if (block is IMyGasTank)
else if (block is IMyDoor)
{ {
_oxygenTanks.Add(block as IMyGasTank); AirDoor door = new AirDoor(this, block as IMyDoor);
} _doors.Add(door);
else if (Ini.Get("airMonitor", "zones").ToString() != "") foreach (string zone in zones) _zones[zone].AddDoor(door);
{
zones = Ini.Get("airMonitor", "zones").ToString().Split(',');
}
else if (Ini.Get("airMonitor", "zone").ToString() != "")
{
zones = new string[] { Ini.Get("airMonitor", "zone").ToString() };
}
else {
Console.Print($"Failed to add block {block.CustomName}");
} }
foreach (string zone in zones) else if (block is IMyLightingBlock)
{ {
if (!_zones.ContainsKey(zone)) _lights.Add(block as IMyLightingBlock);
foreach (string zone in zones) _zones[zone].AddBlock(block);
}
else
{
foreach (string zone in zones)
{ {
_zones[zone] = new AirZone(this, zone); _zones[zone].AddBlock(block);
} }
_zones[zone].AddBlock(block);
} }
} }
@ -82,6 +106,7 @@ namespace IngameScript
Console.Print(kvp.Key); Console.Print(kvp.Key);
_jobs.Add(zone.Monitor()); _jobs.Add(zone.Monitor());
} }
Runtime.UpdateFrequency = UpdateFrequency.Update100; Runtime.UpdateFrequency = UpdateFrequency.Update100;
Console.UpdateTickCount(); Console.UpdateTickCount();
} }
@ -90,20 +115,32 @@ namespace IngameScript
{ {
Console.UpdateTickCount(); Console.UpdateTickCount();
// Light indicators should be set/unset independent of the triggered states and actions.
foreach (IMyLightingBlock light in _lights) light.Color = Color.White;
foreach (AirDoor door in _doors) door.Triggered = false;
foreach (IEnumerator job in _jobs) foreach (IEnumerator job in _jobs)
{ {
if (job.MoveNext()) continue; // Run the monitoring jobs, which will update the lights and door states
Console.Print("WARNING: Monitoring job exited. Zone no longer being monitored."); if (!job.MoveNext()) Console.Print("WARNING: Monitoring job exited. Zone no longer being monitored.");
} }
// Light indicators should be set/unset independent of the triggered states and actions. // Unlock all safe doors, but leave them closed.
foreach (AirZone zone in _zones.Values) zone.SetLights(); // Note that this will make it difficult to manually lock/disable these doors.
foreach (AirDoor door in _doors)
{
if (door.Door.Enabled == false && !door.Triggered)
{
Console.Print($"DEBUG: {door.Door.CustomName} re-enabled");
door.Door.Enabled = true;
}
}
_updateDisplays(); updateDisplays();
} }
// write diagnostics to any configured display screens // write diagnostics to any configured display screens
private void _updateDisplays() private void updateDisplays()
{ {
if (_displays.Count == 0) return; if (_displays.Count == 0) return;