Skip to content

Instantly share code, notes, and snippets.

@leylaso
Last active March 27, 2026 15:18
Show Gist options
  • Select an option

  • Save leylaso/683d3fc602af753354c197c26d8ecb64 to your computer and use it in GitHub Desktop.

Select an option

Save leylaso/683d3fc602af753354c197c26d8ecb64 to your computer and use it in GitHub Desktop.
Simple airlock script for paired doors in Space Engineers
/*******************************************************************************
LEY LA'S AUTOMATIC AIRLOCKS
v 0.2
This script is intended to be run on pairs of doors with the naming
convention:
[prefix] LSAS Inner
or:
[prefix] LSAS Outer
[prefix] should be unique to each pair of doors and need not include square
brackets. The script will use LSAS Inner and LSAS Outer to find their
respective doors. These may be configured in Custom Data.
This script is configured to run every 10 ticks. It may still be possible
to lock the script by opening one of the automatic doors during a brief window
when they are enabled during processing.
So don't do that.
*******************************************************************************/
// LSAS Debug Class Definition
// DELETE THIS before publication
public static class LSASDebug
{
// Debug door closures
public static void CloseDoor(IMyDoor door, string append = "")
{
LSASLog.Line("Closing door: " + door.CustomName + append);
door.CloseDoor();
}
}
// LSAS Log Class Definition
public static class LSASLog
{
public static string TheLog = "";
public static bool Enabled = true;
public static bool Reset()
{
TheLog = "";
return Enabled;
}
public static bool Start()
{
if (Enabled)
TheLog = System.Environment.NewLine + DateTime.Now.ToString() + System.Environment.NewLine;
return Enabled;
}
public static bool Line(string logLine, string ind=" ")
{
if (Enabled)
TheLog += ind + logLine + System.Environment.NewLine;
return Enabled;
}
public static string Print()
{
return TheLog;
}
}
// LSASTimer Class Def
public class LSASTimer
{
public int Val;
private static int LockTime = 30;
private static int DisableTime = 29;
private static int HalfTime = 23;
private static int EnableTime = 17;
private static int TimeBuffer = 5;
// Constructor
public LSASTimer (int val = 0)
{
Val = val;
}
// Set lock time values
public static int SetLockTime (int val)
{
LockTime = val+TimeBuffer;
DisableTime = (val/5*4)+TimeBuffer;
HalfTime = (val/5*3)+TimeBuffer;
EnableTime = (val/5*2)+TimeBuffer;
return LockTime;
}
// Set the timer if it is valid
public int Timer (int val = -1)
{
if (val > -1)
Val = val;
return Val;
}
// Return LockTime
public static int Lock()
{
return LockTime;
}
// Return true if it is disable time
public bool Disable()
{
return (Val == DisableTime);
}
// Return true if we are before halftime
public bool FirstHalf()
{
return (Val > HalfTime);
}
// Return true if it is halfTime
public bool Half()
{
return (Val == HalfTime);
}
// Return true if it is before enable time
public bool BeforeEnable()
{
return (Val > EnableTime);
}
// Return true if it is enable time
public bool Enable()
{
return (Val == EnableTime);
}
// Return TimeBuffer
public int Buffer()
{
return TimeBuffer;
}
// Helper Function to get the timer status
public string TimerState()
{
string Out = "Timer is now " + Val;
Out += System.Environment.NewLine + " Half time at " + HalfTime;
Out += System.Environment.NewLine + " Enable time at " + EnableTime;
return Out;
}
// Tick the timer
public int Tick()
{
// First call on new timer
if (Val == 0)
{
Val = LockTime;
}
else
{
Val -= 1;
}
return Val;
}
// return the timer value when called as an int
public static implicit operator int (LSASTimer t)
{
return t.Val;
}
// Overload ToString method
public override string ToString()
{
return Val.ToString();
}
}
// LSAS Class Definition
public class LSAS
{
public static string InnerTerm = "LSAS Inner";
public static string OuterTerm = "LSAS Outer";
private LSASTimer LockTimer;
public IMyDoor Inner;
public IMyDoor Outer;
public string Group;
private IMyDoor Priority = null;
// Default Constructor
public LSAS ()
{
Group = null;
Inner = null;
Outer = null;
LockTimer = new LSASTimer();
}
// Constructor with group value
public LSAS (string grouped)
{
Group = grouped;
Inner = null;
Outer = null;
LockTimer = new LSASTimer();
}
// Safe way to set lock time respecting minimum value
public static int SetLockTime (int val = 30)
{
if (val < 30)
val = 30;
return LSASTimer.SetLockTime(val);
}
// Parser
public static string[] Parse (IMyDoor door, char sep = ' ')
{
return door.CustomName.Split(sep);
}
// Helper to check for inner or outer door
private static bool CheckDoor (IMyDoor door, string searchTerm)
{
return (door != null && door.CustomName.Contains(searchTerm));
}
// Helper to check for valid airlock before doing anything to it
private bool ValidSAS ()
{
if (Inner == null)
throw new Exception("No inner door");
else if (Outer == null)
throw new Exception("No outer door");
else if (Outer.Status != DoorStatus.Closed && Inner.Status != DoorStatus.Closed)
throw new Exception("Both doors are open in " + Group);
else
return true;
}
// Check for outer door
public static bool IsOuter (IMyDoor door)
{
return CheckDoor(door, OuterTerm);
}
// Check for inner door
public static bool IsInner (IMyDoor door)
{
return CheckDoor(door, InnerTerm);
}
// Get a valid group or null on an invalid group
public static string GetGroup (IMyDoor door)
{
string[] Parsed = Parse(door);
if (Parsed[0] == null || InnerTerm.Contains(Parsed[0]) || OuterTerm.Contains(Parsed[0]))
return null;
else
return Parsed[0];
}
// Safely add doors
public bool AddDoor (IMyDoor door, string grouped)
{
if (grouped != Group)
return false;
if (IsInner(door))
{
Inner = door;
return true;
}
if (IsOuter(door))
{
Outer = door;
return true;
}
return false;
}
// Helper method to never disable doors that are not fully open or closed
private bool SafeDisable (IMyDoor door, DoorStatus state = DoorStatus.Closed)
{
if (door.Status == state)
door.Enabled = false;
else
door.Enabled = true;
return door.Enabled;
}
// General paired door handling logic
private bool HandleDoorPair (IMyDoor observed, IMyDoor affected)
{
// no action is needed if the door is closed and timer has run out
if (observed.Status == DoorStatus.Closed && LockTimer == 0)
{
observed.Enabled = true;
affected.Enabled = true;
return false;
}
else
{
// Give the observed door priority if it is available
if (Priority == null)
Priority = observed;
// Otherwise check the observed door has priority
else if (Priority == observed)
{
LockTimer.Tick();
if (LockTimer == LSASTimer.Lock())
affected.CloseDoor(); // First close the opposite door if it is open
else if (LockTimer.FirstHalf()) // Disable both doors
{
SafeDisable(affected);
SafeDisable(observed, DoorStatus.Open);
}
else if (LockTimer.Half()) // Close the original door
{
observed.Enabled = true;
observed.CloseDoor();
}
else if (LockTimer.BeforeEnable()) // Disable both doors
{
SafeDisable(affected);
SafeDisable(observed);
}
else if (LockTimer.Enable()) // Open the opposite door
{
//LSASLog.Line("Timer is Enable");
affected.Enabled = true;
affected.OpenDoor();
}
else if (LockTimer > LockTimer.Buffer()) // Disable both doors
{
//LSASLog.Line("Timer > Buffer");
SafeDisable(affected, DoorStatus.Open);
SafeDisable(observed);
}
else if (LockTimer == LockTimer.Buffer())
{
affected.Enabled = true;
affected.CloseDoor();
}
else if (LockTimer > 0)
SafeDisable(affected);
else if (affected.Status != DoorStatus.Closed)
affected.CloseDoor();
else
{
affected.Enabled = true;
observed.Enabled = true;
Priority = null;
}
}
return true;
}
}
// Outer door handling
private bool HandleOuter ()
{
// LSASLog.Line("HandleOuter called");
return HandleDoorPair(Outer, Inner);
}
// Inner door handling
private bool HandleInner ()
{
// LSASLog.Line("HandleInner called");
return HandleDoorPair(Inner, Outer);
}
// Airlock Handling
public bool HandleLock (bool trigger = false)
{
if (!ValidSAS())
return false;
else if (trigger && Priority != null)
{
LSASLog.Line("Ignored triggered call");
return false; // Ignore the triggered call if a door has priority at the moment
}
else if (HandleOuter() | HandleInner())
{
LSASLog.Line(Status(Group));
return true;
}
else
{
LockTimer.Timer(0);
return false;
}
}
// Helper to print the status of a door
private static string DoorState(IMyDoor door, string ind = " ")
{
string Out = ind + door.CustomName + ":" + System.Environment.NewLine;
Out += ind + ind + "Enabled: " + door.Enabled.ToString() + System.Environment.NewLine;
Out += ind + ind + "Status: " + door.Status + System.Environment.NewLine;
return Out;
}
// Status of the airlock
public string Status(string grouped, string ind = " ")
{
string Out = null;
if (grouped != Group)
{
Out += System.Environment.NewLine + "Wrong key: " + grouped;
Out += System.Environment.NewLine + "Should be: " + Group;
}
else
{
Out += System.Environment.NewLine + Group + System.Environment.NewLine;
if (Inner == null)
Out += System.Environment.NewLine + ind + "No inner door" + System.Environment.NewLine;
else
Out += DoorState(Inner, ind);
if (Outer == null)
Out += System.Environment.NewLine + ind + "No outer door" + System.Environment.NewLine;
else
Out += DoorState(Outer, ind);
Out += "Airlock Timer: " + LockTimer.ToString() + System.Environment.NewLine;
if (Priority != null)
Out += Priority.CustomName + " has priority" + System.Environment.NewLine;
}
return Out;
}
}
// Dictionary of LSAS Definition
public class LSASDictionary : Dictionary<string, LSAS>
{
// Method to intelligently add a door to an existing SAS or create a new one
public bool AddDoor (IMyDoor door)
{
string grouped = LSAS.GetGroup(door);
if (grouped == null)
return false;
if (ContainsKey(grouped))
return this[grouped].AddDoor(door, grouped);
else
{
Add(grouped, new LSAS(grouped));
return this[grouped].AddDoor(door, grouped);
}
}
// Method to print full contents of dictionary
public string Status ()
{
string Output = null;
foreach (var SAS in this)
{
if (SAS.Value != null)
Output += SAS.Value.Status(SAS.Key);
}
return Output;
}
// Method to handle all the locks
public bool HandleLocks(bool trigger = false)
{
bool Out = false;
foreach (var SAS in this)
{
if (SAS.Value != null)
{
if (SAS.Value.HandleLock(trigger))
Out = true;
}
}
return Out;
}
}
// Instantiate a shared instance of the parser
MyIni _ini = new MyIni();
bool _firstRun = false;
// Instantiate a dict of Airlocks
LSASDictionary LeyLaSASPairs = new LSASDictionary();
// Initial setup
// Must be recompiled when doors are added or removed
public Program()
{
// Set the Update Frequency
Runtime.UpdateFrequency = UpdateFrequency.Update10;
// Check if this is a first run
_ini.TryParse(Storage);
_firstRun = _ini.Get("Two Door Airlock Config", "Reset").ToBoolean(true);
if (_firstRun)
{
_firstRun = false;
Echo(System.Environment.NewLine + "WARNING: First run of this script!");
// Set the initial custom data values
_ini.Clear();
_ini.Set("Two Door Airlock Config", "Inner Tag", LSAS.InnerTerm);
_ini.Set("Two Door Airlock Config", "Outer Tag", LSAS.OuterTerm);
_ini.Set("Two Door Airlock Config", "Airlock Timer", LSASTimer.Lock());
Me.CustomData = _ini.ToString();
// Store the first run variable so we don't do this again
_ini.Set("Two Door Airlock Config", "Reset", false);
Storage = _ini.ToString();
}
// Parse the contents of customdata
MyIniParseResult result;
if (!_ini.TryParse(Me.CustomData, out result))
throw new Exception(result.ToString());
// Set the search terms to find airlock doors
LSAS.InnerTerm = _ini.Get("Two Door Airlock Config", "Inner Tag").ToString(LSAS.InnerTerm);
LSAS.OuterTerm = _ini.Get("Two Door Airlock Config", "Outer Tag").ToString(LSAS.OuterTerm);
LSAS.SetLockTime(_ini.Get("Two Door Airlock Config", "Airlock Timer").ToInt32(LSASTimer.Lock()));
// Populate LeyLaSASPairs with relevant doors
GridTerminalSystem.GetBlocksOfType<IMyDoor>(null, (d) =>
{
var door = d as IMyDoor;
//string grouped = null;
if (LSAS.IsInner(door) || LSAS.IsOuter(door))
return LeyLaSASPairs.AddDoor (door);
return false;
});
}
public void Save()
{
_ini.Clear();
_ini.Set("Two Door Airlock Config", "Inner Tag", LSAS.InnerTerm);
_ini.Set("Two Door Airlock Config", "Outer Tag", LSAS.OuterTerm);
_ini.Set("Two Door Airlock Config", "Airlock Timer", LSASTimer.Lock());
// Store the first run variable so we don't do this again
_ini.Set("Two Door Airlock Config", "Reset", _firstRun);
Storage = _ini.ToString();
}
// Do the things
public void Main(string arg, UpdateType uptype)
{
LSASLog.Start();
if ((uptype & (UpdateType.Trigger | UpdateType.Terminal)) != 0)
{
LeyLaSASPairs.HandleLocks(true);
}
if ((uptype & (UpdateType.Update10 | UpdateType.Update100)) != 0)
LeyLaSASPairs.HandleLocks();
Echo (LSASLog.Print());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment