diff --git a/.gitignore b/.gitignore
index 87fcdba..95dcbcf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -135,3 +135,4 @@ $RECYCLE.BIN/
.DS_Store
_NCrunch*
+/PML/.$pml.drawio.bkp
diff --git a/PML/PML.csproj b/PML/PML.csproj
new file mode 100644
index 0000000..7718844
--- /dev/null
+++ b/PML/PML.csproj
@@ -0,0 +1,34 @@
+
+
+
+ netframework48
+ IngameScript
+ 6
+ false
+ Release;Debug
+ x64
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PML/PML.mdk.ini b/PML/PML.mdk.ini
new file mode 100644
index 0000000..5add8f4
--- /dev/null
+++ b/PML/PML.mdk.ini
@@ -0,0 +1,22 @@
+; This file is project specific and should be checked in to source control.
+
+[mdk]
+; This is a programmable block script project.
+; You should not change this.
+type=programmableblock
+
+; Toggle trace (on|off) (verbose output)
+trace=off
+
+; What type of minification to use (none|trim|stripcomments|lite|full)
+; none: No minification
+; trim: Removes unused types (NOT members).
+; stripcomments: trim + removes comments.
+; lite: stripcomments + removes leading/trailing whitespace.
+; full: lite + renames identifiers to shorter names.
+minify=none
+
+; A list of files and folder to ignore when creating the script.
+; This is a comma separated list of glob patterns.
+; See https://code.visualstudio.com/docs/editor/glob-patterns
+ignores=obj/**/*,MDK/**/*,**/*.debug.cs
diff --git a/PML/PML.mdk.local.ini b/PML/PML.mdk.local.ini
new file mode 100644
index 0000000..4b66820
--- /dev/null
+++ b/PML/PML.mdk.local.ini
@@ -0,0 +1,7 @@
+; This file is _local_ to your machine and should not be checked in to source control.
+
+[mdk]
+; Where to output the script to (auto|specific path)
+output=auto
+; Override the default binary path (auto|specific path)
+binarypath=auto
\ No newline at end of file
diff --git a/PML/Program.cs b/PML/Program.cs
new file mode 100644
index 0000000..a19bc9b
--- /dev/null
+++ b/PML/Program.cs
@@ -0,0 +1,402 @@
+using Sandbox.Game.EntityComponents;
+using Sandbox.ModAPI.Ingame;
+using Sandbox.ModAPI.Interfaces;
+
+using SpaceEngineers.Game.ModAPI.Ingame;
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+
+using VRage;
+using VRage.Collections;
+using VRage.Game;
+using VRage.Game.Components;
+using VRage.Game.GUI.TextPanel;
+using VRage.Game.ModAPI.Ingame;
+using VRage.Game.ModAPI.Ingame.Utilities;
+using VRage.Game.ObjectBuilders.Definitions;
+using VRage.ObjectBuilders;
+
+using VRageMath;
+
+namespace IngameScript
+{
+ partial class Program : MyGridProgram
+ {
+ const int NUMBER_OF_MISSILES = 52; // The quantity of components will be created to build this quantity of missiles.
+ const int NUMBER_OF_CANNON_SHELL = 160;
+ const int NUMBER_OF_ARTILLERY_SHELL = 40;
+
+ const int CONSOLE_NB_LINES = 14;
+ const string GRID_PREFIX = "[PML]";
+
+ const float EPSILON = 0.05f;
+
+ readonly IMyCubeGrid grid;
+
+ readonly Output output;
+
+ readonly Output topPanel;
+ readonly Output bottomPanel;
+
+ readonly IMyAssembler assembler;
+
+ readonly List missileComponents = new List{
+ new ComponentQuantity(MyItemType.MakeComponent("SteelPlate"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/SteelPlate"), 256),
+ new ComponentQuantity(MyItemType.MakeComponent("Construction"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/ConstructionComponent"), 166),
+ new ComponentQuantity(MyItemType.MakeComponent("Motor"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/MotorComponent"), 18),
+ new ComponentQuantity(MyItemType.MakeComponent("InteriorPlate"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/InteriorPlate"), 40),
+ new ComponentQuantity(MyItemType.MakeComponent("Computer"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/ComputerComponent"), 48),
+ new ComponentQuantity(MyItemType.MakeComponent("PowerCell"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/PowerCell"), 2),
+ new ComponentQuantity(MyItemType.MakeComponent("Display"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/Display"), 1),
+ new ComponentQuantity(MyItemType.MakeComponent("SmallTube"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/SmallTube"), 29),
+ new ComponentQuantity(MyItemType.MakeComponent("MetalGrid"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/MetalGrid"), 48),
+ new ComponentQuantity(MyItemType.MakeComponent("LargeTube"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/LargeTube"), 16),
+ new ComponentQuantity(MyItemType.MakeComponent("Detector"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/DetectorComponent"), 8),
+ new ComponentQuantity(MyItemType.MakeComponent("Girder"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/GirderComponent"), 12),
+ new ComponentQuantity(MyItemType.MakeComponent("Explosives"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/ExplosivesComponent"), 24),
+ };
+
+ public Program()
+ {
+ this.grid = this.Me.CubeGrid;
+
+ var output = this.Me.GetSurface(0);
+ this.output = new Output(output, CONSOLE_NB_LINES);
+
+ this.output.Print("PML system starting...");
+
+ this.topPanel = new Output(this.GridTerminalSystem.GetBlock("LCD Panel - Main 02", this.grid));
+
+ this.assembler = this.GridTerminalSystem.GetBlock("Assembler Main", this.grid);
+
+ Runtime.UpdateFrequency = UpdateFrequency.Update100;
+ }
+
+ public void Save()
+ {
+ }
+
+ public void Main(string argument, UpdateType updateSource)
+ {
+ if ((updateSource & UpdateType.Update100) != 0)
+ {
+ this.UpdateState();
+ }
+ else if ((updateSource & (UpdateType.Terminal | UpdateType.Trigger)) != 0)
+ {
+ switch (argument)
+ {
+ case "COMMAND":
+ this.output.Print("BLA...");
+ break;
+
+ case "START_MINING":
+
+ default:
+ this.output.Print($"Uknown command: {argument}");
+ break;
+ }
+ }
+ }
+
+ void UpdateState()
+ {
+ this.BuildComponentForMissiles();
+ this.BuildAmmo();
+ this.DisplayStatus();
+ }
+
+ void BuildComponentForMissiles()
+ {
+ var existingAmounts = this.GetCurrentItemQuantity(missileComponents.Select(quantity => quantity.ItemType));
+
+ int i = 0;
+ foreach (var missileComponent in this.missileComponents)
+ {
+ var existing = existingAmounts[i];
+
+ var queue = new List();
+ this.assembler.GetQueue(queue);
+ foreach (var item in queue)
+ {
+ if (item.BlueprintId == missileComponent.blueprintId)
+ existing += item.Amount;
+ }
+
+ var desired = missileComponent.Quantity * NUMBER_OF_MISSILES;
+
+ if (existing < desired)
+ {
+ var toBuild = desired - existing;
+ this.output.Print($"Requesting {toBuild} of {missileComponent.blueprintId.SubtypeName}");
+ this.assembler.AddQueueItem(missileComponent.blueprintId, toBuild);
+ }
+ i += 1;
+ }
+ }
+
+ void BuildAmmo()
+ {
+ //var ammoDefinitionIds = new List
+ //{
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/Missile200mm"),
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/Missile400mm"),
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/Missile800mm"),
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/Missile1200mm"),
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/Projectile200mm"),
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/Projectile400mm"),
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/Projectile800mm"),
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/Projectile1200mm"),
+ // MyDefinitionId.Parse("MyObjectBuilder_AmmoMagazine/ArtilleryShell"),
+ //};
+ //var existingAmounts = this.GetCurrentItemQuantity(ammoDefinitionIds);
+ //var desiredAmounts = new List
+ //{
+ // (MyFixedPoint)(NUMBER_OF_MISSILES * 4), // Missile200mm
+ // (MyFixedPoint)(NUMBER_OF_MISSILES * 4), // Missile400mm
+ // (MyFixedPoint)(NUMBER_OF_MISSILES * 2), // Missile800mm
+ // (MyFixedPoint)(NUMBER_OF_MISSILES * 2), // Missile1200mm
+ // (MyFixedPoint)(NUMBER_OF_CANNON_SHELL / 4), // Projectile200mm
+ // (MyFixedPoint)(NUMBER_OF_CANNON_SHELL / 4), // Projectile400mm
+ // (MyFixedPoint)(NUMBER_OF_CANNON_SHELL / 4), // Projectile800mm
+ // (MyFixedPoint)(NUMBER_OF_CANNON_SHELL / 4), // Projectile1200mm
+ // (MyFixedPoint)(NUMBER_OF_ARTILLERY_SHELL), // ArtilleryShell
+ //};
+ //foreach (var tuple in ammoDefinitionIds.Zip(existingAmounts, desiredAmounts, (id, existing, desired) => (id, existing, desired)))
+ //{
+ // if (tuple.existing < tuple.desired - EPSILON)
+ // {
+ // var toBuild = tuple.desired - tuple.existing;
+ // this.assembler.AddQueueItem(tuple.id, toBuild);
+ // this.output.Print($"Requesting {toBuild} of {tuple.id.SubtypeName}");
+ // }
+ //}
+ }
+
+ void DisplayStatus()
+ {
+ var cargoSpace = this.GetCargoSpace();
+ var energyState = this.GetEnergyState();
+ var hydrogenVolume = this.GetHydrogenVolume();
+ var oxygenVolume = this.GetOxygenVolume();
+
+ this.topPanel.Display(
+ $@"
+{cargoSpace.ToString("Cargo volume", "kL")}
+
+{energyState.ToString("Energy", "MWh")}
+
+{hydrogenVolume.ToString("Hydrogen", "L")}
+
+{oxygenVolume.ToString("Oxygen", "L")}"
+ );
+ }
+
+ List GetCurrentItemQuantity(IEnumerable itemTypes)
+ {
+ var total = new List(new MyFixedPoint[itemTypes.Count()]);
+
+ foreach (var container in this.GridTerminalSystem.GetBlocks(null, this.grid))
+ {
+ //this.output.Print($"Container: {container.CustomName}");
+ int i = 0;
+ foreach (var itemType in itemTypes)
+ {
+ var items = container.GetInventory().FindItem(itemType);
+ //this.output.Print($"{itemType}: {items}");
+ if (items.HasValue)
+ total[i] += items.Value.Amount;
+ i += 1;
+ }
+ }
+
+ foreach (var assembler in this.GridTerminalSystem.GetBlocks(null, this.grid))
+ {
+ int i = 0;
+ foreach (var itemType in itemTypes)
+ {
+ var items = assembler.OutputInventory.FindItem(itemType);
+ if (items.HasValue)
+ total[i] += items.Value.Amount;
+ i += 1;
+ }
+ }
+
+ foreach (var sorter in this.GridTerminalSystem.GetBlocks(null, this.grid))
+ {
+ int i = 0;
+ foreach (var itemType in itemTypes)
+ {
+ var items = sorter.GetInventory().FindItem(itemType);
+ if (items.HasValue)
+ total[i] += items.Value.Amount;
+ i += 1;
+ }
+ }
+
+ foreach (var welder in this.GridTerminalSystem.GetBlocks(null, this.grid))
+ {
+ int i = 0;
+ foreach (var itemType in itemTypes)
+ {
+ var items = welder.GetInventory().FindItem(itemType);
+ if (items.HasValue)
+ total[i] += items.Value.Amount;
+ i += 1;
+ }
+ }
+
+ foreach (var turret in this.GridTerminalSystem.GetBlocks(null, this.grid))
+ {
+ int i = 0;
+ foreach (var itemType in itemTypes)
+ {
+ var items = turret.GetInventory().FindItem(itemType);
+ if (items.HasValue)
+ total[i] += items.Value.Amount;
+ i += 1;
+ }
+ }
+
+ return total;
+ }
+
+ class ComponentQuantity
+ {
+ public ComponentQuantity(MyItemType itemType, MyDefinitionId blueprintId, MyFixedPoint quantity)
+ {
+ this.ItemType = itemType;
+ this.blueprintId = blueprintId;
+
+ //if (!MyDefinitionId.TryParse("MyObjectBuilder_BlueprintDefinition", this.ItemType.SubtypeId, out this.Id))
+ // throw new ArgumentException($"Cannot parse {this.ItemType} into a MyDefinitionId");
+
+ this.Quantity = quantity;
+ }
+
+ public readonly MyItemType ItemType;
+ public readonly MyDefinitionId blueprintId;
+ public readonly MyFixedPoint Quantity;
+ }
+
+ struct Quantity
+ {
+ public Quantity(double current, double total)
+ {
+ this.Current = current;
+ this.Total = total;
+ }
+
+ public readonly double Current;
+ public readonly double Total;
+
+ public string ToString(string name, string unit)
+ {
+ var firstLineLength = 50;
+ var secondLineLength = 50;
+
+ var firstLine = new StringBuilder();
+ firstLine.Append(name).Append(": ");
+ var values = $"{this.Current:N1} {unit} / {this.Total:N1} {unit}";
+ firstLine.Append(values.PadLeft(firstLineLength - firstLine.Length));
+
+ var secondLine = new StringBuilder();
+ secondLine.Append("[");
+ var ratio = this.Current / this.Total;
+ var percentStr = $"{ratio * 100.0,8:N1}%";
+ // "-2" because of the starting and ending characters: '[', ']'.
+ var gaugeSize = secondLineLength - percentStr.Length - 2;
+ var n = (int)((double)gaugeSize * ratio);
+ secondLine.Append(new string('|', n));
+ secondLine.Append(new string(' ', gaugeSize - n)).Append("]");
+ secondLine.Append(percentStr);
+
+ return $"{firstLine}\n{secondLine}";
+ }
+ }
+
+ Quantity GetCargoSpace()
+ {
+ var containers = this.GridTerminalSystem.GetBlocks(null, this.grid);
+
+ double currentVolume = 0;
+ double totalVolume = 0;
+
+ foreach (var container in containers)
+ {
+ var inventory = container.GetInventory();
+ currentVolume += (double)inventory.CurrentVolume;
+ totalVolume += (double)inventory.MaxVolume;
+ }
+
+ return new Quantity(currentVolume, totalVolume);
+ }
+
+ Quantity GetEnergyState()
+ {
+ double currentkWh = 0;
+ double maxkWh = 0;
+ var batteries = this.GridTerminalSystem.GetBlocks(null, this.grid);
+
+ foreach (var battery in batteries)
+ {
+ currentkWh += (double)battery.CurrentStoredPower;
+ maxkWh += (double)battery.MaxStoredPower;
+ }
+
+ return new Quantity(currentkWh, maxkWh);
+ }
+
+ Quantity GetHydrogenVolume()
+ {
+ double currentVolume = 0;
+ double totalVolume = 0;
+ var tanks = this.GridTerminalSystem.GetBlocks(null, this.grid, tank => tank.CustomName.Contains("Hydrogen"));
+
+ //var tanks = new List();
+ //GridTerminalSystem.GetBlocksOfType(tanks,
+ // (IMyGasTank tank) =>
+ // {
+ // return tank.CustomName.Contains("Mammouth") && tank.CustomName.Contains("hydro");
+ // }
+ //);
+
+ foreach (var tank in tanks)
+ {
+ currentVolume += (double)tank.Capacity * (double)tank.FilledRatio;
+ totalVolume += (double)tank.Capacity;
+ }
+
+ return new Quantity(currentVolume, totalVolume);
+ }
+
+ Quantity GetOxygenVolume()
+ {
+ double currentVolume = 0;
+ double totalVolume = 0;
+ var tanks = this.GridTerminalSystem.GetBlocks(null, this.grid, tank => tank.CustomName.Contains("Oxygen"));
+ //var tanks = new List();
+ //GridTerminalSystem.GetBlocksOfType(tanks,
+ // (IMyGasTank tank) =>
+ // {
+ // return tank.CustomName.Contains("Mammouth") && tank.CustomName.Contains("oxygène");
+ // }
+ //);
+
+ foreach (var tank in tanks)
+ {
+ currentVolume += (double)tank.Capacity * (double)tank.FilledRatio;
+ totalVolume += (double)tank.Capacity;
+ }
+
+ return new Quantity(currentVolume, totalVolume);
+ }
+ }
+}
+
diff --git a/PML/packages.config b/PML/packages.config
new file mode 100644
index 0000000..0653ff6
--- /dev/null
+++ b/PML/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/PML/pml.drawio b/PML/pml.drawio
new file mode 100644
index 0000000..5e69641
--- /dev/null
+++ b/PML/pml.drawio
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+