305 lines
10 KiB
C#
305 lines
10 KiB
C#
// Airlock logic.
|
|
//
|
|
// We don't use a Sequencer for Airlocks for a number of reasons. Most notably:
|
|
//
|
|
// * One door in each cycle needs to be both closed and then opened.
|
|
// * The lights (and in the future, displays) need to emit signal information at several stages.
|
|
//
|
|
// We could add functionality to the Sequencer for these use cases, but right now we'll keep the logic separate,
|
|
// even though *for the most part* it follows the same "Enumerator that waits for various bits of state" logic.
|
|
|
|
using Sandbox.ModAPI.Ingame;
|
|
using SpaceEngineers.Game.ModAPI.Ingame;
|
|
using System.Collections.Generic;
|
|
using VRage.Game.ModAPI.Ingame.Utilities;
|
|
using VRageMath;
|
|
|
|
namespace IngameScript
|
|
{
|
|
partial class Program
|
|
{
|
|
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
|
|
{
|
|
return (_innerDoor != null && _outerDoor != null && _airVent != null && _oxygenTank != null);
|
|
}
|
|
}
|
|
|
|
public bool Cycling { get; private set; } = false;
|
|
|
|
public IMyAirVent ReferenceVent { get; set; }
|
|
|
|
private bool DoorOpened
|
|
{
|
|
get
|
|
{
|
|
return _innerDoor.Status == DoorStatus.Open || _outerDoor.Status == DoorStatus.Open;
|
|
}
|
|
}
|
|
|
|
private string _name;
|
|
private PrefixedConsole _console;
|
|
private MyIni _ini;
|
|
|
|
private List<IMyLightingBlock> _lights = new List<IMyLightingBlock>();
|
|
private IMyDoor _innerDoor;
|
|
private IMyDoor _outerDoor;
|
|
private IMyGasTank _oxygenTank;
|
|
private IMyAirVent _airVent;
|
|
|
|
private float _targetOxygenLevel = 0.0F;
|
|
|
|
private const int CooldownTicks = 12;
|
|
private const int SealTimeoutTicks = 30;
|
|
|
|
public Airlock(string name, Program _program)
|
|
{
|
|
_ini = _program.Ini;
|
|
_name = name;
|
|
_console = new PrefixedConsole(_program.Console, _name);
|
|
}
|
|
|
|
public void AddBlock(IMyTerminalBlock block)
|
|
{
|
|
if (block is IMyDoor) addDoor(block as IMyDoor);
|
|
else if (block is IMyLightingBlock) _lights.Add(block as IMyLightingBlock);
|
|
else if (block is IMyAirVent) addVent(block as IMyAirVent);
|
|
else if (block is IMyGasTank) addOxygenTank(block as IMyGasTank);
|
|
else _console.Print($"Tried to add invalid block '{block.CustomName}'");
|
|
}
|
|
|
|
// Precondition: _ini.TryParse() should be called on the block before calling this function.
|
|
private void addDoor(IMyDoor door)
|
|
{
|
|
if (_ini.Get("airlock", "doorPosition").ToString() == "inner" && _innerDoor == null)
|
|
{
|
|
_innerDoor = door;
|
|
return;
|
|
}
|
|
if (_ini.Get("airlock", "doorPosition").ToString() == "outer" && _outerDoor == null)
|
|
{
|
|
_outerDoor = door;
|
|
return;
|
|
}
|
|
_console.Print($"Couldn't add door '{door.CustomName}'");
|
|
}
|
|
|
|
// Precondition: _ini.TryParse() should be called on the block before calling this function.
|
|
private void addVent(IMyAirVent vent)
|
|
{
|
|
if (_airVent == null)
|
|
{
|
|
_airVent = vent;
|
|
return;
|
|
}
|
|
_console.Print($"Couldn't add air vent '{vent.CustomName}'");
|
|
}
|
|
|
|
private void addOxygenTank(IMyGasTank tank)
|
|
{
|
|
if (_oxygenTank == null)
|
|
{
|
|
_oxygenTank = tank;
|
|
return;
|
|
}
|
|
_console.Print($"Couldn't add oxygen tank '{tank.CustomName}'");
|
|
}
|
|
|
|
public IEnumerator<bool> CycleAirlock()
|
|
{
|
|
if (Cycling) yield break;
|
|
Cycling = true;
|
|
setLights(AirlockLightState.Cycling);
|
|
|
|
closeDoors();
|
|
while (!DoorsClosed) { yield return true; }
|
|
lockDoors();
|
|
|
|
int ticks = 0;
|
|
while (!_airVent.CanPressurize && ticks < SealTimeoutTicks)
|
|
{
|
|
ticks++;
|
|
yield return true;
|
|
}
|
|
|
|
if (!_airVent.CanPressurize)
|
|
{
|
|
error("Airlock is not airtight.");
|
|
Cycling = false;
|
|
yield break;
|
|
}
|
|
|
|
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);
|
|
_console.Print("Cycling Complete.");
|
|
Cycling = false;
|
|
}
|
|
|
|
private void closeDoors()
|
|
{
|
|
_console.Print("Closing Doors");
|
|
|
|
// close the doors
|
|
_innerDoor.Enabled = true;
|
|
_outerDoor.Enabled = true;
|
|
_innerDoor.CloseDoor();
|
|
_outerDoor.CloseDoor();
|
|
}
|
|
|
|
private void pressurizeDepressurize()
|
|
{
|
|
_console.Print("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 && ReferenceVent != null)
|
|
{
|
|
_targetOxygenLevel = ReferenceVent.GetOxygenLevel();
|
|
_console.Print($"Set depressurization target to {_targetOxygenLevel}");
|
|
}
|
|
}
|
|
|
|
// Open the appropriate door based on pressure state.
|
|
private void openDoor()
|
|
{
|
|
_console.Print($"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 setDisplays(string text) {
|
|
// foreach (IMyTextSurface display in _displays) {
|
|
// display.WriteText(text);
|
|
// }
|
|
// }
|
|
|
|
// 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; }
|
|
|
|
_console.Print($"{_name}: Balancing Oxygen Tank");
|
|
|
|
// Configure the vent to suck in Oxygen.
|
|
_airVent.Depressurize = true;
|
|
_airVent.Enabled = true;
|
|
return true;
|
|
}
|
|
|
|
private void lockDoors()
|
|
{
|
|
_innerDoor.Enabled = false;
|
|
_outerDoor.Enabled = false;
|
|
}
|
|
|
|
private void error(string error)
|
|
{
|
|
_console.Print(error);
|
|
setLights(AirlockLightState.Error);
|
|
}
|
|
}
|
|
}
|
|
} |