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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +