Skip to content

Instantly share code, notes, and snippets.

@Zemnmez
Created May 26, 2021 21:47
Show Gist options
  • Save Zemnmez/05dbcb60c37b64243b5d7f49fa77b896 to your computer and use it in GitHub Desktop.
Save Zemnmez/05dbcb60c37b64243b5d7f49fa77b896 to your computer and use it in GitHub Desktop.
/*
* R e a d m e
* -----------
*
* In this file you can include any instructions or other comments you want to have injected onto the
* top of your final script. You can safely delete this file if you do not want any such comments.
*/
// This file contains your actual script.
//
// You can either keep all your code here, or you can create separate
// code files to make your program easier to navigate while coding.
//
// In order to add a new utility class, right-click on your project,
// select 'New' then 'Add Item...'. Now find the 'Space Engineers'
// category under 'Visual C# Items' on the left hand side, and select
// 'Utility Class' in the main area. Name it in the box below, and
// press OK. This utility class will be merged in with your code when
// deploying your final script.
//
// You can also simply create a new utility class manually, you don't
// have to use the template if you don't want to. Just do so the first
// time to see what a utility class looks like.
//
// Go to:
// https://github.com/malware-dev/MDK-SE/wiki/Quick-Introduction-to-Space-Engineers-Ingame-Scripts
//
// to learn more about ingame scripts.
float FontSize = .5F;
public Program()
{
// The constructor, called only once every session and
// always before any other method is called. Use it to
// initialize your script.
//
// The constructor is optional and can be removed if not
// needed.
//
// It's recommended to set Runtime.UpdateFrequency
// here, which will allow your script to run itself without a
// timer block.
Clear();
Screen().FontSize = FontSize;
Runtime.UpdateFrequency = UpdateFrequency.Update1;
}
public void Save()
{
// Called when the program needs to save its state. Use
// this method to save your state to the Storage field
// or some other means.
//
// This method is optional and can be removed if not
// needed.
}
List<string> TextLines = new List<string>();
StringBuilder letterM = new StringBuilder("M");
private IMyTextSurface Screen()
{
return Me.GetSurface(0);
}
private int NumLines()
{
var screenSize = Screen().SurfaceSize;
var MLetterSize = Screen().MeasureStringInPixels(letterM, Screen().Font, FontSize);
return (int)((screenSize.X / 2) / MLetterSize.X) - 4;
}
private void Clear()
{
Screen().WriteText("", false);
}
private void Write(string what)
{
Screen().WriteText(what);
Screen().ContentType = ContentType.TEXT_AND_IMAGE;
}
private void Println(params string[] what)
{
TextLines.Add(String.Join("", what));
while (TextLines.Count > NumLines())
{
TextLines.RemoveAt(0);
}
Write(String.Join("\n", TextLines));
}
private void Log(params string[] what)
{
Println(/*"[", DateTime.Now.ToString(), "]",*/ String.Join(" ", what));
}
private List<T> BlocksOfType<T>() where T : class
{
var l = new List<T>();
GridTerminalSystem.GetBlocksOfType<T>(l);
return l;
}
private List<IMyDoor> Doors()
{
return BlocksOfType<IMyDoor>();
}
private List<IMyAirVent> Vents()
{
return BlocksOfType<IMyAirVent>();
}
enum MainStates
{
InitDetectRooms,
DetectingRooms,
DoneDetectingRooms
}
class Step
{
public static Step Wait(int n)
{
var s = new Step();
s.waitFor = n;
return s;
}
public static Step Next(IEnumerator<Step> next)
{
var s = new Step();
s.next = next;
return s;
}
public int waitFor = 0;
public IEnumerator<Step> next;
}
public void Main(string argument, UpdateType updateSource)
{
// Make sure that `MyMethod()` is not already running
if (null == stateMachine)
{
Log("Starting...");
Runtime.UpdateFrequency = UpdateFrequency.Update10;
shouldContinue = true;
remainingWaitCount = 0;
var enumerator = Enumerator();
if (enumerator == null) { Log("WARNING!! enumerator is missing!"); }
stateMachine = EnumeratorUnroller(Enumerator());
if (stateMachine == null) { Log("WARNING!! state machine is missing!!"); }
}
if (argument.Length > 0)
{
Log("Got signal:", argument);
if (argument.Equals("reboot", StringComparison.OrdinalIgnoreCase))
{
stateMachine.Dispose();
stateMachine = EnumeratorUnroller(Enumerator());
shouldContinue = true;
remainingWaitCount = 0;
}
if (argument.Equals("stop", StringComparison.OrdinalIgnoreCase))
{
// Can only do a stop, when `MyMethod()` previously have been started
if (null != stateMachine)
{
// Instruct `MyMethod` that it should stop, the next time it executes its `while(...)` statement
shouldContinue = false;
}
}
}
// Only when PB is called with `Update10` in `updateSource`
if (0 == (updateSource & UpdateType.Update10))
{
Log("Exited, incorrect update source");
return;
}
// Make sure there actually is a state-machine running
if (null == stateMachine)
{
Log("Exited, no state machine.");
return;
}
// Reduce the 'remaining counter' - e.g. the "sleep clock"
remainingWaitCount -= 1;
// When 'remaining counter' has reached zero (or below), then we are ready to continue the next part of `MyMethod()`
if (!(remainingWaitCount <= 0))
{
return;
}
// `MoveNext()` executes next part of `MyMethod()` until the next `yield ...` statement
// But `MoveNext()` will also return `false` when `MyMethod()` has nothing more to do
if (false == stateMachine.MoveNext())
{
Log("Script finished.");
stateMachine.Dispose(); // Important to clean up!
stateMachine = null; // Important to clean up!
Runtime.UpdateFrequency = UpdateFrequency.Update100;
}
else
{
// Take the `yield return ...`-value and store it in our 'remaining counter'
remainingWaitCount = stateMachine.Current;
}
}
int remainingWaitCount;
bool shouldContinue;
IEnumerator<int> stateMachine = null;
IEnumerator<int> EnumeratorUnroller(IEnumerator<Step> e)
{
while (e.MoveNext())
{
if (e.Current.next != null)
{
var unroller = EnumeratorUnroller(e.Current.next);
while (unroller.MoveNext())
{
yield return unroller.Current;
}
unroller.Dispose();
}
else if (e.Current.waitFor != 0)
{
yield return e.Current.waitFor;
}
else { throw new Exception("???"); }
}
e.Dispose();
}
IEnumerator<Step> Enumerator()
{
var s2 = Do();
while (shouldContinue)
{
s2.MoveNext();
yield return s2.Current;
}
s2.Dispose();
Log("Stopping Enumerator, due to shouldContinue=false (or a break statement).");
}
IEnumerator<Step> Do()
{
Log(" == Room Management System == ");
yield return Step.Next(AutoCloseDoors(10));
}
IEnumerator<Step> CloseAllDoors()
{
Log("Closing all doors...");
Doors().ForEach(door => door.CloseDoor());
Log("Waiting for doors to be closed...");
while (Doors().Exists(v => v.Status != DoorStatus.Closed))
{
var openDoors = Doors().Where(d => d.Status == DoorStatus.Opening);
foreach (IMyDoor door in openDoors) { door.CloseDoor(); Log("Still waiting for", door.CustomName); }
yield return Step.Wait(1);
}
Log("All doors closed.");
}
IEnumerator<Step> OpenDoor(IMyDoor door)
{
Log("Opening door", door.CustomName);
door.OpenDoor();
while (door.Status != DoorStatus.Open)
{
yield return Step.Wait(1);
}
Log("Door", door.CustomName, "is now open");
}
IEnumerator<Step> TestLines()
{
int end = NumLines();
Log("Detected", NumLines().ToString(), "rows:");
for (int i = 0; i < end; i++)
{
Log("row:", i.ToString());
yield return Step.Wait(1);
}
Log("Row calibration complete.");
}
IEnumerator<Step> AutoCloseDoors(int nTicks)
{
Log("== Auto door closer ==");
Log("Doors close after", nTicks.ToString(), "ticks.");
var openDoors = new Dictionary<long, int>();
while (true)
{
foreach (IMyDoor door in Doors())
{
if (openDoors.ContainsKey(door.EntityId))
{
var entry = openDoors[door.EntityId];
Log("Door", door.CustomName, "has", entry.ToString(), "ticks left.");
if (entry <= 0)
{
Log("Closing door", door.CustomName, "after", nTicks.ToString(), "ticks.");
door.CloseDoor();
openDoors.Remove(door.EntityId);
} else
{
openDoors[door.EntityId]--;
}
continue;
}
if(door.Status == DoorStatus.Open)
{
Log("Door", door.CustomName, "is open. Closing after", nTicks.ToString(), "ticks.");
openDoors[door.EntityId] = nTicks;
}
}
yield return Step.Wait(1);
}
}
IEnumerator<Step> FindRooms()
{
yield return Step.Next(TestLines());
Log("Finding rooms...");
foreach (IMyAirVent vent in Vents())
{
yield return Step.Next(CloseAllDoors());
Log("Found vent", vent.CustomName);
if (!vent.CanPressurize)
{
Log("Vent", vent.CustomName, "is not in a valid room --");
Log("Vent", vent.CustomName, "cannot pressurise even with all doors closed.");
continue;
}
Log("Testing doors for", vent.CustomName);
foreach (IMyDoor door in Doors())
{
yield return Step.Next(CloseAllDoors());
Log("Ok doors closed, let's test door: ", door.CustomName);
Log("Opening the door", door.CustomName, "to see if it affects anything...");
yield return Step.Next(OpenDoor(door));
if (vent.CanPressurize == false)
{
Log("Vent", vent.CustomName, "is part of the same room as door", door.CustomName);
} else
{
Log("Vent", vent.CustomName, "is not part of the same room as", door.CustomName);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment