Initial SE scripts.

This commit is contained in:
Anna Rose 2025-02-06 19:02:13 -05:00
commit 44d161f5c0
3 changed files with 476 additions and 0 deletions

258
airlock.cs Normal file
View File

@ -0,0 +1,258 @@
enum AirlockState {
Off,
DoorsClosing,
Cycling,
DoorOpening,
Cooldown,
}
enum AirlockLightState {
Off,
Cycling,
Cooldown,
Error,
}
string airlockName;
IEnumerator<bool> state;
float targetOxygenLevel = 0.0F;
// Blocks we need to track
IMyDoor innerDoor;
IMyDoor outerDoor;
List<IMyLightingBlock> lights;
IMyAirVent airVent;
IMyAirVent airSensor; // TODO: we don't use this yet
IMyGasTank oxygenTank;
public Program()
{
airlockName = Me.CustomName.Split(' ')[0];
state = null;
// Find the correct objects
InitDoors();
InitVents();
InitLights();
InitOxygen();
}
private void InitDoors() {
List<IMyDoor> onlyDoors = new List<IMyDoor>();
GridTerminalSystem.GetBlocksOfType(onlyDoors);
foreach (IMyDoor door in onlyDoors) {
if (door.CustomName.StartsWith(airlockName)) {
if (door.CustomName.Contains("Inner")) {
innerDoor = door;
}
else if (door.CustomName.Contains("Outer")) {
outerDoor = door;
}
}
if (innerDoor != null && outerDoor != null) {
break;
}
}
}
private void InitVents() {
List<IMyAirVent> onlyFans = new List<IMyAirVent>();
GridTerminalSystem.GetBlocksOfType(onlyFans);
foreach (IMyAirVent vent in onlyFans) {
if (vent.CustomName.StartsWith(airlockName)) {
if (vent.CustomName.Contains("Main")) {
airVent = vent;
}
else if (vent.CustomName.Contains("Reference")) {
airSensor = vent;
}
}
// 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;
}
}
}
private void InitLights() {
lights = new List<IMyLightingBlock>();
List<IMyLightingBlock> allLights = new List<IMyLightingBlock>();
GridTerminalSystem.GetBlocksOfType(allLights);
foreach (IMyLightingBlock light in allLights) {
if (light.CustomName.StartsWith(airlockName)) {
lights.Add(light);
}
}
}
private void InitOxygen() {
List<IMyGasTank> allTanks = new List<IMyGasTank>();
GridTerminalSystem.GetBlocksOfType(allTanks);
foreach (IMyGasTank tank in allTanks) {
if (tank.CustomName.StartsWith(airlockName)) {
oxygenTank = tank;
break;
}
}
}
public void Main(string argument, UpdateType updateSource)
{
if (state == null) {
state = CycleAirlock();
Runtime.UpdateFrequency = UpdateFrequency.Update1;
return;
}
if (!state.MoveNext()) {
state.Dispose();
state = null;
Runtime.UpdateFrequency = UpdateFrequency.None;
}
}
private IEnumerator<bool> CycleAirlock() {
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
Runtime.UpdateFrequency = UpdateFrequency.Update100;
yield return true;
Runtime.UpdateFrequency = UpdateFrequency.Update1;
SetLights(AirlockLightState.Off);
}
private void CloseDoors() {
Echo("DEBUG: Closing Doors");
// close the doors
innerDoor.Enabled = true;
outerDoor.Enabled = true;
innerDoor.CloseDoor();
outerDoor.CloseDoor();
}
private bool DoorsClosed() {
return innerDoor.Status == DoorStatus.Closed && outerDoor.Status == DoorStatus.Closed;
}
private void PressurizeDepressurize() {
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();
Echo($"Set depressurization target to {targetOxygenLevel}");
}
}
// TODO: this should check for depressurizing to the *reference* amount
private bool PressureStabilized() {
return ((airVent.Depressurize && airVent.GetOxygenLevel() <= targetOxygenLevel) ||
(!airVent.Depressurize && airVent.Status == VentStatus.Pressurized));
}
// Open the appropriate door based on pressure state.
private void OpenDoor() {
Echo("DEBUG: Opening Door");
if (airVent.Status == VentStatus.Pressurized) {
innerDoor.Enabled = true;
innerDoor.OpenDoor();
} else {
outerDoor.Enabled = true;
outerDoor.OpenDoor();
}
}
private bool DoorOpened() {
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; }
Echo("DEBUG: Balancing Oxygen Tank");
// Configure the vent to suck in Oxygen.
airVent.Depressurize = true;
airVent.Enabled = true;
return true;
}
private bool OxygenBalanced() {
return oxygenTank.FilledRatio > 0.25;
}
// 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) {
Echo(error);
SetLights(AirlockLightState.Error);
}

88
docking.cs Normal file
View File

@ -0,0 +1,88 @@
// Docking controller script; handles docking and undocking.
// Specifically, when you activate the script it will:
// * Connect any Connectors whose names start with "Docking Port" that are ready to connect.
// * Turn off all engines.
// * Set all O2 and Hydrogen tanks to Stockpile.
// * Set all batteries to Recharge.
//
// When you activate the script with "-undock" it will reverse all of these actions.
//
// TODO: None of the below switches work yet.
// You can selectively disable some functionality with switches.
// * "-no-refuel" to disable refueling
// * "-no-recharge" to disable battery recharge
// (batteries will be left in whatever state they are already in)
MyCommandLine cli;
List<IMyShipConnector> dockingPorts;
List<IMyThrust> thrusters;
List<IMyBatteryBlock> batteries;
List<IMyGasTank> tanks;
public void Program() {
cli = new MyCommandLine();
dockingPorts = new List<IMyShipConnector>();
List<IMyShipConnector> allConnectors = new List<IMyShipConnector>();
foreach(IMyShipConnector connector in allConnectors) {
if (connector.CustomName.StartsWith("Docking Port")) {
dockingPorts.Add(connector);
}
}
thrusters = new List<IMyThrust>();
GridTerminalSystem.GetBlocksOfType(thrusters);
batteries = new List<IMyBatteryBlock>();
GridTerminalSystem.GetBlocksOfType(batteries);
tanks = new List<IMyGasTank>();
GridTerminalSystem.GetBlocksOfType(tanks);
}
public void Main(string argument, UpdateType updateSource) {
cli.TryParse(argument);
if (cli.Switch("undock")) Undock();
else Dock();
}
private void Dock() {
foreach (IMyShipConnector dockingPort in dockingPorts) {
if (dockingPort.Status == MyShipConnectorStatus.Connectable) {
dockingPort.Connect();
}
}
foreach (IMyThrust thruster in thrusters) {
thruster.Enabled = false;
}
foreach (IMyBatteryBlock battery in batteries) {
battery.ChargeMode = ChargeMode.Recharge;
}
foreach (IMyGasTank tank in tanks) {
tank.Stockpile = true;
}
}
private void Undock() {
foreach (IMyBatteryBlock battery in batteries) {
battery.ChargeMode = ChargeMode.Recharge;
}
foreach (IMyGasTank tank in tanks) {
tank.Stockpile = true;
}
foreach (IMyThrust thruster in thrusters) {
thruster.Enabled = true;
}
foreach (IMyShipConnector dockingPort in dockingPorts) {
if (dockingPort.Status == MyShipConnectorStatus.Connected) {
dockingPort.Disconnect();
}
}
}

130
maintenance_panels.cs Normal file
View File

@ -0,0 +1,130 @@
// Script for opening maintence panels on a ship.
//
// Currently this simply opens and closes *all* of the panels attached to hinges
// whose names start with "Maintenance". Default behavior is to open.
// Pass the argument -close to close the panels.
//
// By default "open" is assumed to be 90 degrees and "closed" is assumed to be 0 degrees.
// Each hinge can have its behavior configured via custom data. A sample config
// (with the default values):
//
// OpenAngle=90
// ClosedAngle=0
// Velocity=5
MyCommandLine cli;
IEnumerator<bool> state;
List<Panel> panels;
public class Panel {
public Panel(IMyMotorStator hinge) {
Hinge = hinge;
ParseConfig();
}
public void OpenPanel() {
Hinge.RotorLock = false;
TargetAngle = OpenAngle;
Hinge.RotateToAngle(MyRotationDirection.AUTO, TargetAngle, Velocity);
}
public void ClosePanel() {
Hinge.RotorLock = false;
TargetAngle = ClosedAngle;
Hinge.RotateToAngle(MyRotationDirection.AUTO, TargetAngle, Velocity);
}
// Call this function every tick after OpenPanel or ClosePanel.
// It will return true when the panel has finished moving.
public bool Monitor() {
if (Math.Abs(Hinge.Angle - TargetAngle) < 0.01) {
Hinge.RotorLock = true;
}
return Hinge.RotorLock;
}
private IMyMotorStator Hinge { get; set; }
private float TargetAngle { get; set; }
private float OpenAngle { get; set; } = 90F;
private float ClosedAngle { get; set; } = 0F;
private float Velocity { get; set; } = 5F;
private void ParseConfig() {
string[] lines = Hinge.CustomData.Split('\n');
foreach (string line in lines) {
string[] tokens = line.Split('=');
if (tokens.Length != 2) continue;
switch(tokens[0]) {
case "OpenAngle":
OpenAngle = float.Parse(tokens[1]);
break;
case "ClosedAngle":
ClosedAngle = float.Parse(tokens[1]);
break;
case "Velocity":
Velocity = float.Parse(tokens[1]);
break;
}
}
}
}
public Program() {
cli = new MyCommandLine();
state = null;
panels = new List<Panel>();
List<IMyMotorStator> allHinges = new List<IMyMotorStator>();
foreach(IMyMotorStator hinge in allHinges) {
if (hinge.CustomName.StartsWith("Maintenance")) {
panels.Add(new Panel(hinge));
}
}
Echo($"Found {panels.Length} panels.");
}
public void Main(string argument, UpdateType updateSource) {
if (state == null) {
cli.TryParse(argument);
if (cli.Switch("close")) state = ClosePanels();
else state = OpenPanels();
Runtime.UpdateFrequency = UpdateFrequency.Update1;
return;
}
if (!state.MoveNext()) {
state.Dispose();
state = null;
Runtime.UpdateFrequency = UpdateFrequency.None;
}
}
private IEnumerator<bool> OpenPanels() {
Echo("Opening panels.");
foreach (Panel panel in panels) {
panel.OpenPanel();
}
return MonitorPanels();
}
private IEnumerator<bool> ClosePanels() {
Echo("Closing panels.");
foreach (Panel panel in panels) {
panel.ClosePanel();
}
return MonitorPanels();
}
private IEnumerator<bool> MonitorPanels() {
while (true) {
Echo("Monitoring panels.");
bool done = true; // assume we've finished, then falsify it below
foreach (Panel panel in panels) {
if (!panel.Monitor()) done = false;
}
if (done) yield break;
yield return true;
}
}