space_engineers/Airlock/Airlock.cs

305 lines
8.9 KiB
C#

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<IMyLightingBlock> 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<IMyDoor> onlyDoors = new List<IMyDoor>();
_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<IMyAirVent> onlyFans = new List<IMyAirVent>();
_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<IMyLightingBlock>();
List<IMyLightingBlock> allLights = new List<IMyLightingBlock>();
_p.GridTerminalSystem.GetBlocksOfType(allLights);
foreach (IMyLightingBlock light in allLights)
{
if (!light.CustomName.StartsWith(_name)) continue;
lights.Add(light);
}
}
private void initOxygen()
{
List<IMyGasTank> allTanks = new List<IMyGasTank>();
_p.GridTerminalSystem.GetBlocksOfType(allTanks);
foreach (IMyGasTank tank in allTanks)
{
if (!tank.CustomName.StartsWith(_name)) continue;
oxygenTank = tank;
return;
}
Functional = false;
}
public IEnumerator<bool> 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);
}
}
}