using Sandbox.ModAPI.Ingame; using SpaceEngineers.Game.ModAPI.Ingame; using System.Collections.Generic; using VRageMath; namespace IngameScript { public class Airlock { enum AirlockLightState { Off, Cycling, Cooldown, Error, } public bool DoorsClosed { get { return innerDoor.Status == DoorStatus.Closed && outerDoor.Status == DoorStatus.Closed; } } // TODO: this should check for depressurizing to the *reference* amount public bool PressureStabilized { get { return ((airVent.Depressurize && airVent.GetOxygenLevel() <= targetOxygenLevel) || (!airVent.Depressurize && airVent.Status == VentStatus.Pressurized)); } } public bool OxygenBalanced { get { return oxygenTank.FilledRatio > 0.25; } } public bool Functional { get; private set; } = true; public bool Cycling { get; private set; } = false; private bool DoorOpened { get { return innerDoor.Status == DoorStatus.Open || outerDoor.Status == DoorStatus.Open; } } // Returns false if we are in a state where we can't or don't need to balance private bool balanceOxygen() { if (innerDoor.Status == DoorStatus.Closed || outerDoor.Status == DoorStatus.Open || OxygenBalanced) { return false; } _p.Echo("DEBUG: Balancing Oxygen Tank"); // Configure the vent to suck in Oxygen. airVent.Depressurize = true; airVent.Enabled = true; return true; } private string _name; private MyGridProgram _p; private float targetOxygenLevel = 0.0F; private IMyDoor innerDoor; private IMyDoor outerDoor; private List lights; private IMyGasTank oxygenTank; private IMyAirVent airVent; private IMyAirVent airSensor; private const int CooldownTicks = 120; public Airlock(MyGridProgram p, string name) { _p = p; _name = name; // Find the appropriate blocks given the airlock name initDoors(); initLights(); initVents(); initOxygen(); } private void initDoors() { List onlyDoors = new List(); _p.GridTerminalSystem.GetBlocksOfType(onlyDoors); foreach (IMyDoor door in onlyDoors) { if (!door.CustomName.StartsWith(_name)) continue; if (door.CustomName.Contains("Inner")) { innerDoor = door; } else if (door.CustomName.Contains("Outer")) { outerDoor = door; } if (innerDoor != null && outerDoor != null) { return; } } Functional = false; } private void initVents() { List onlyFans = new List(); _p.GridTerminalSystem.GetBlocksOfType(onlyFans); foreach (IMyAirVent vent in onlyFans) { if (!vent.CustomName.StartsWith(_name)) continue; if (vent.CustomName.StartsWith(_name)) { if (vent.CustomName.Contains("Main")) { airVent = vent; continue; } else if (vent.CustomName.Contains("Reference")) { airSensor = vent; continue; } } // A global reference vent will be used if we don't have one specific to our airlock. // A specific vent found later will overwrite this assignment. if (vent.CustomName.StartsWith("Airlock Reference") && airSensor == null) { airSensor = vent; } } if (airVent == null) Functional = false; } private void initLights() { lights = new List(); List allLights = new List(); _p.GridTerminalSystem.GetBlocksOfType(allLights); foreach (IMyLightingBlock light in allLights) { if (!light.CustomName.StartsWith(_name)) continue; lights.Add(light); } } private void initOxygen() { List allTanks = new List(); _p.GridTerminalSystem.GetBlocksOfType(allTanks); foreach (IMyGasTank tank in allTanks) { if (!tank.CustomName.StartsWith(_name)) continue; oxygenTank = tank; return; } Functional = false; } public IEnumerator CycleAirlock() { Cycling = true; setLights(AirlockLightState.Cycling); closeDoors(); while (!DoorsClosed) { yield return true; } lockDoors(); if (!airVent.CanPressurize) { error("Airlock is not airtight."); yield return false; } pressurizeDepressurize(); while (!PressureStabilized) { yield return true; } airVent.Enabled = false; openDoor(); while (!DoorOpened) { yield return true; } lockDoors(); // Balance oxygen storage setLights(AirlockLightState.Cooldown); if (balanceOxygen()) { while (!OxygenBalanced) { yield return true; } } airVent.Enabled = false; // Cooldown period int cooldown = 0; while(cooldown < CooldownTicks) { cooldown++; yield return true; } setLights(AirlockLightState.Off); } private void closeDoors() { _p.Echo("DEBUG: Closing Doors"); // close the doors innerDoor.Enabled = true; outerDoor.Enabled = true; innerDoor.CloseDoor(); outerDoor.CloseDoor(); } private void pressurizeDepressurize() { _p.Echo("DEBUG: Cycling"); // toggle the current state airVent.Depressurize = !airVent.Depressurize; airVent.Enabled = true; // When depressurizing, check the external pressure and only depressurize to that value. // TODO: test this for floating point errors if (airVent.Depressurize && airSensor != null) { targetOxygenLevel = airSensor.GetOxygenLevel(); _p.Echo($"Set depressurization target to {targetOxygenLevel}"); } } // Open the appropriate door based on pressure state. private void openDoor() { _p.Echo("DEBUG: Opening Door"); if (airVent.Status == VentStatus.Pressurized) { innerDoor.Enabled = true; innerDoor.OpenDoor(); } else { outerDoor.Enabled = true; outerDoor.OpenDoor(); } } // TODO: blinkenlights are unsatisfying right now... private void setLights(AirlockLightState lightState) { float blinkLength = 1.0F; float blinkInterval = 0.0F; Color color = Color.Red; switch (lightState) { case AirlockLightState.Off: color = Color.Green; break; case AirlockLightState.Cycling: blinkInterval = 1.0F; blinkLength = 0.75F; break; case AirlockLightState.Cooldown: color = Color.Yellow; break; case AirlockLightState.Error: // the defaults already set this correctly break; } foreach (IMyLightingBlock light in lights) { light.Enabled = true; light.BlinkIntervalSeconds = blinkInterval; light.BlinkLength = blinkLength; light.Color = color; } } private void lockDoors() { innerDoor.Enabled = false; outerDoor.Enabled = false; } private void error(string error) { _p.Echo(error); setLights(AirlockLightState.Error); } } }