From 3236ccb05fb3f6fb4d409d673ec3aef4fd6cb327 Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Sun, 14 Sep 2025 17:19:16 +0200 Subject: [PATCH 1/4] New project to manage La Mimine frigate --- Mimine/AmmoManager.cs | 127 ++++++++++++++++++ Mimine/Components.cs | 37 +++++ Mimine/Constants.cs | 29 ++++ Mimine/Ingots.cs | 144 ++++++++++++++++++++ Mimine/Mimine.csproj | 34 +++++ Mimine/Mimine.mdk.ini | 22 +++ Mimine/Mimine.mdk.local.ini | 7 + Mimine/MissileManager.cs | 209 +++++++++++++++++++++++++++++ Mimine/Program.cs | 260 ++++++++++++++++++++++++++++++++++++ Mimine/packages.config | 4 + 10 files changed, 873 insertions(+) create mode 100644 Mimine/AmmoManager.cs create mode 100644 Mimine/Components.cs create mode 100644 Mimine/Constants.cs create mode 100644 Mimine/Ingots.cs create mode 100644 Mimine/Mimine.csproj create mode 100644 Mimine/Mimine.mdk.ini create mode 100644 Mimine/Mimine.mdk.local.ini create mode 100644 Mimine/MissileManager.cs create mode 100644 Mimine/Program.cs create mode 100644 Mimine/packages.config diff --git a/Mimine/AmmoManager.cs b/Mimine/AmmoManager.cs new file mode 100644 index 0000000..792b5a8 --- /dev/null +++ b/Mimine/AmmoManager.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Sandbox.ModAPI.Ingame; + +using VRage; +using VRage.Game; +using VRage.Game.ModAPI.Ingame; + +namespace IngameScript +{ + internal class AmmoManager + { + readonly IMyGridTerminalSystem gridTerminal; + readonly IMyCubeGrid grid; + readonly Output output; + readonly IList assemblers; + + public AmmoManager(IMyGridTerminalSystem gridTerminal, IMyCubeGrid grid, Output output, IList assemblers) + { + this.gridTerminal = gridTerminal; + this.grid = grid; + this.output = output; + this.assemblers = assemblers; + } + + public class AmmoStatus + { + public MyFixedPoint NbOfCannonShell { get; set; } + public MyFixedPoint NbOfArtilleryShell { get; set; } + public MyFixedPoint NbOfGatlingAmmo { get; set; } + + public AmmoStatus Copy() + { + return this.MemberwiseClone() as AmmoStatus; + } + } + + public void BuildAmmo(AmmoStatus status) + { + if (this.assemblers.Count == 0) + return; + + var mainAssembler = this.assemblers[0]; + + var statusWithAssemblerQueues = status.Copy(); + + // Add the current Queue. + foreach (var assembler in this.gridTerminal.GetBlocks(grid: this.grid)) + { + var queue = new List(); + assembler.GetQueue(queue); + + foreach (var item in queue) + { + if (item.BlueprintId == Constants.CANNON_SHELL_BLUEPRINT_ID) + statusWithAssemblerQueues.NbOfCannonShell += item.Amount; + else if (item.BlueprintId == Constants.ARTILLERY_SHELL_BLUEPRINT_ID) + statusWithAssemblerQueues.NbOfArtilleryShell += item.Amount; + else if (item.BlueprintId == Constants.GATLING_AMMO_BLUEPRINT_ID) + statusWithAssemblerQueues.NbOfGatlingAmmo += item.Amount; + } + } + + if (statusWithAssemblerQueues.NbOfCannonShell < Constants.NUMBER_OF_CANNON_SHELL) + { + var toBuild = Constants.NUMBER_OF_CANNON_SHELL - statusWithAssemblerQueues.NbOfCannonShell; + this.output.Print($"Requesting {toBuild} of {Constants.CANNON_SHELL_BLUEPRINT_ID.SubtypeName}"); + mainAssembler.AddQueueItem(Constants.CANNON_SHELL_BLUEPRINT_ID, toBuild); + } + + if (statusWithAssemblerQueues.NbOfArtilleryShell < Constants.NUMBER_OF_ARTILLERY_SHELL) + { + var toBuild = Constants.NUMBER_OF_ARTILLERY_SHELL - statusWithAssemblerQueues.NbOfArtilleryShell; + this.output.Print($"Requesting {toBuild} of {Constants.ARTILLERY_SHELL_BLUEPRINT_ID.SubtypeName}"); + mainAssembler.AddQueueItem(Constants.ARTILLERY_SHELL_BLUEPRINT_ID, toBuild); + } + + if (statusWithAssemblerQueues.NbOfGatlingAmmo < Constants.NUMBER_OF_GATLING_AMMO) + { + var toBuild = Constants.NUMBER_OF_GATLING_AMMO - statusWithAssemblerQueues.NbOfGatlingAmmo; + this.output.Print($"Requesting {toBuild} of {Constants.GATLING_AMMO_BLUEPRINT_ID.SubtypeName}"); + mainAssembler.AddQueueItem(Constants.GATLING_AMMO_BLUEPRINT_ID, toBuild); + } + } + + public void DisplayAmmoStatus(StringBuilder sb, AmmoStatus status) + { + var assaultConnonShell = "Assault Cannon Shell:"; + sb.Append(assaultConnonShell); + sb.AppendLine($"{status.NbOfCannonShell}".PadLeft(Constants.CONSOLE_LINE_LENGTH - assaultConnonShell.Length)); + + var artilleryShell = "Artillery Shell:"; + sb.Append(artilleryShell); + sb.AppendLine($"{status.NbOfArtilleryShell}".PadLeft(Constants.CONSOLE_LINE_LENGTH - artilleryShell.Length)); + + var gatlingAmmo = "Gatling Ammo Box"; + sb.Append(gatlingAmmo); + sb.AppendLine($"{status.NbOfGatlingAmmo}".PadLeft(Constants.CONSOLE_LINE_LENGTH - gatlingAmmo.Length)); + } + + public AmmoStatus GetAmmoStatus() + { + var status = new AmmoStatus(); + + foreach (var inventory in this.gridTerminal.GetAllInventories(grid)) + { + var items = new List(); + inventory.GetItems(items); + foreach (var item in items) + { + if (item.Type.SubtypeId == "MediumCalibreAmmo") + status.NbOfCannonShell += item.Amount; + else if (item.Type.SubtypeId == "LargeCalibreAmmo") + status.NbOfArtilleryShell += item.Amount; + else if (item.Type.SubtypeId == "NATO_25x184mm") + status.NbOfGatlingAmmo += item.Amount; + } + } + + return status; + } + } +} diff --git a/Mimine/Components.cs b/Mimine/Components.cs new file mode 100644 index 0000000..65a29e4 --- /dev/null +++ b/Mimine/Components.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using VRage; +using VRage.Game; +using VRage.Game.ModAPI.Ingame; + +namespace IngameScript +{ + class ComponentType + { + public readonly MyItemType ItemType; + public readonly MyDefinitionId BlueprintId; + + public ComponentType(MyItemType itemType, MyDefinitionId blueprintId) + { + this.ItemType = itemType; + this.BlueprintId = blueprintId; + } + } + + class ComponentQuantity + { + public readonly ComponentType ComponentType; + public readonly MyFixedPoint Quantity; + + public ComponentQuantity(MyItemType itemType, MyDefinitionId blueprintId, MyFixedPoint quantity) + { + + this.ComponentType = new ComponentType(itemType, blueprintId); + this.Quantity = quantity; + } + } +} diff --git a/Mimine/Constants.cs b/Mimine/Constants.cs new file mode 100644 index 0000000..3848d71 --- /dev/null +++ b/Mimine/Constants.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using VRage.Game; + +namespace IngameScript +{ + internal static class Constants + { + public const int NUMBER_OF_MISSILES = 3; // The quantity of components will be created to build this quantity of missiles. + + public const int NUMBER_OF_CANNON_SHELL = 200; + public const int NUMBER_OF_ARTILLERY_SHELL = 50; + public const int NUMBER_OF_GATLING_AMMO = 500; + + public const int CONSOLE_NB_LINES = 14; + public const int CONSOLE_LINE_LENGTH = 50; + public const string GRID_PREFIX = "[Mimine]"; + + public static readonly MyDefinitionId CANNON_SHELL_BLUEPRINT_ID = MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/Position0110_MediumCalibreAmmo"); + public static readonly MyDefinitionId ARTILLERY_SHELL_BLUEPRINT_ID = MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/Position0120_LargeCalibreAmmo"); + public static readonly MyDefinitionId GATLING_AMMO_BLUEPRINT_ID = MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/Position0080_NATO_25x184mmMagazine"); + + public const float EPSILON = 0.05f; + } +} diff --git a/Mimine/Ingots.cs b/Mimine/Ingots.cs new file mode 100644 index 0000000..879d505 --- /dev/null +++ b/Mimine/Ingots.cs @@ -0,0 +1,144 @@ +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +using Sandbox.ModAPI.Ingame; + +using VRage; +using VRage.Game; +using VRage.Game.ModAPI.Ingame; + +namespace IngameScript +{ + enum IngotsEnum + { + Iron = 0, + Nickel = 1, + Cobalt = 2, + Magnesium = 3, + Silicon = 4, + Silver = 5, + Gold = 6, + Platinum = 7, + Uranium = 8, + Gravel = 9, // Named 'Stone' in the API. + } + + class IngotException : Exception + { + public IngotException(string mess) : + base(mess) + { + } + } + + class Ingot + { + public static MyItemType FromEnum(IngotsEnum ingot) + { + var name = ""; + switch (ingot) + { + case IngotsEnum.Iron: name = "Iron"; break; + case IngotsEnum.Nickel: name = "Nickel"; break; + case IngotsEnum.Cobalt: name = "Cobalt"; break; + case IngotsEnum.Magnesium: name = "Magnesium"; break; + case IngotsEnum.Silicon: name = "Silicon"; break; + case IngotsEnum.Silver: name = "Silver"; break; + case IngotsEnum.Gold: name = "Gold"; break; + case IngotsEnum.Platinum: name = "Platinum"; break; + case IngotsEnum.Uranium: name = "Uranium"; break; + case IngotsEnum.Gravel: name = "Stone"; break; + } + return MyItemType.MakeIngot(name); + } + + public static IngotsEnum FromSubTypeId(string id) + { + IngotsEnum enumValue = IngotsEnum.Iron; + switch (id) + { + case "Iron": enumValue = IngotsEnum.Iron; break; + case "Nickel": enumValue = IngotsEnum.Nickel; break; + case "Cobalt": enumValue = IngotsEnum.Cobalt; break; + case "Magnesium": enumValue = IngotsEnum.Magnesium; break; + case "Silicon": enumValue = IngotsEnum.Silicon; break; + case "Silver": enumValue = IngotsEnum.Silver; break; + case "Gold": enumValue = IngotsEnum.Gold; break; + case "Platinum": enumValue = IngotsEnum.Platinum; break; + case "Uranium": enumValue = IngotsEnum.Uranium; break; + case "Stone": enumValue = IngotsEnum.Gravel; break; + default: throw new IngotException($"Unknown ingot id: {id}"); + } + return enumValue; + } + } + + internal class Ingots + { + readonly float[] ingots = new float[10]; + + public Ingots(float iron = 0, float nickel = 0, float cobalt = 0, float magnesium = 0, float silicon = 0, float silver = 0, float gold = 0, float platinum = 0, float uranium = 0, float gravel = 0) + { + ingots[(int)IngotsEnum.Iron] = iron; + ingots[(int)IngotsEnum.Nickel] = nickel; + ingots[(int)IngotsEnum.Cobalt] = cobalt; + ingots[(int)IngotsEnum.Magnesium] = magnesium; + ingots[(int)IngotsEnum.Silicon] = silicon; + ingots[(int)IngotsEnum.Silver] = silver; + ingots[(int)IngotsEnum.Gold] = gold; + ingots[(int)IngotsEnum.Platinum] = platinum; + ingots[(int)IngotsEnum.Uranium] = uranium; + ingots[(int)IngotsEnum.Gravel] = gravel; + } + + public float this[IngotsEnum ingot] + { + get { return ingots[(int)ingot]; } + set { ingots[(int)ingot] = value; } + } + + public static Ingots operator +(Ingots a, Ingots b) + { + var result = new Ingots(); + for (int i = 0; i < result.ingots.Length; i++) + result.ingots[i] = a.ingots[i] + b.ingots[i]; + return result; + } + + public override string ToString() + { + var sb = new StringBuilder(); + foreach (var ingot in Enum.GetValues(typeof(IngotsEnum)).Cast()) + { + sb.AppendLine($"{ingot}: {this[ingot]}"); + } + + return sb.ToString(); + } + } + + static class IngotsUtils + { + public static Ingots IngotsFromGridContainers(this IMyGridTerminalSystem gridTerminal, IMyCubeGrid grid) + { + var ingots = new Ingots(); + + foreach (var inventory in gridTerminal.GetAllInventories(grid)) + { + var items = new List(); + inventory.GetItems(items); + + foreach (var item in items) + if (item.Type.TypeId == "MyObjectBuilder_Ingot") + ingots[Ingot.FromSubTypeId(item.Type.SubtypeId)] += (float)item.Amount; + } + + return ingots; + } + } +} diff --git a/Mimine/Mimine.csproj b/Mimine/Mimine.csproj new file mode 100644 index 0000000..7718844 --- /dev/null +++ b/Mimine/Mimine.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/Mimine/Mimine.mdk.ini b/Mimine/Mimine.mdk.ini new file mode 100644 index 0000000..5add8f4 --- /dev/null +++ b/Mimine/Mimine.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/Mimine/Mimine.mdk.local.ini b/Mimine/Mimine.mdk.local.ini new file mode 100644 index 0000000..4b66820 --- /dev/null +++ b/Mimine/Mimine.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/Mimine/MissileManager.cs b/Mimine/MissileManager.cs new file mode 100644 index 0000000..c4d7079 --- /dev/null +++ b/Mimine/MissileManager.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Sandbox.ModAPI.Ingame; + +using VRage; +using VRage.Game; +using VRage.Game.ModAPI.Ingame; + +namespace IngameScript +{ + internal class MissileManager + { + readonly IMyGridTerminalSystem gridTerminal; + readonly IMyCubeGrid grid; + readonly Output output; + readonly IList assemblers; + + static readonly List MISSILE_COMPONENTS = new List{ + new ComponentQuantity(MyItemType.MakeComponent("SteelPlate"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/SteelPlate"), 254), + new ComponentQuantity(MyItemType.MakeComponent("Construction"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/ConstructionComponent"), 172), + new ComponentQuantity(MyItemType.MakeComponent("Motor"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/MotorComponent"), 18), + new ComponentQuantity(MyItemType.MakeComponent("InteriorPlate"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/InteriorPlate"), 43), + new ComponentQuantity(MyItemType.MakeComponent("Computer"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/ComputerComponent"), 53), + 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"), 27), + 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"), 14), + new ComponentQuantity(MyItemType.MakeComponent("RadioCommunication"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/RadioCommunicationComponent"), 22), + new ComponentQuantity(MyItemType.MakeComponent("Girder"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/GirderComponent"), 11), + new ComponentQuantity(MyItemType.MakeComponent("Explosives"), MyDefinitionId.Parse("MyObjectBuilder_BlueprintDefinition/ExplosivesComponent"), 22), + }; + + // In kg. + static readonly Dictionary INGOTS_PER_COMPONENT = new Dictionary + { + ["SteelPlate"] = new Ingots(iron: 21), + ["Construction"] = new Ingots(iron: 8), + ["Motor"] = new Ingots(iron: 20, nickel: 5), + ["InteriorPlate"] = new Ingots(iron: 3), + ["Computer"] = new Ingots(iron: 0.5f, silicon: 0.2f), + ["PowerCell"] = new Ingots(iron: 10, nickel: 2, silicon: 1), + ["Display"] = new Ingots(iron: 1, silicon: 5), + ["SmallTube"] = new Ingots(iron: 5), + ["MetalGrid"] = new Ingots(iron: 12, nickel: 5, cobalt: 3), + ["LargeTube"] = new Ingots(iron: 30), + ["Detector"] = new Ingots(iron: 5, nickel: 15), + ["RadioCommunication"] = new Ingots(iron: 8, silicon: 1), + ["Girder"] = new Ingots(iron: 6), + ["Explosives"] = new Ingots(silicon: 0.5f, magnesium: 2), + }; + + static readonly Ingots INGOTS_PER_MISSILE; + + static MissileManager() + { + INGOTS_PER_MISSILE = new Ingots(); + foreach (var component in MISSILE_COMPONENTS) + { + INGOTS_PER_MISSILE += INGOTS_PER_COMPONENT[component.ComponentType.ItemType.SubtypeId]; + } + } + + public MissileManager(IMyGridTerminalSystem gridTerminal, IMyCubeGrid grid, Output output, IList assemblers) + { + this.gridTerminal = gridTerminal; + this.grid = grid; + this.output = output; + this.assemblers = assemblers; + } + + public void BuildComponentForMissiles() + { + if (this.assemblers.Count == 0) + return; + + var existingAmounts = this.GetCurrentItemQuantity(MISSILE_COMPONENTS.Select(quantity => quantity.ComponentType)); + + int i = 0; + foreach (var missileComponent in MISSILE_COMPONENTS) + { + var existing = existingAmounts[i]; + //this.output.Print($"{missileComponent.ItemType}: {existing}"); + + var desired = missileComponent.Quantity * Constants.NUMBER_OF_MISSILES; + + if (existing < desired) + { + var toBuild = desired - existing; + this.output.Print($"Requesting {toBuild} of {missileComponent.ComponentType.BlueprintId.SubtypeName}"); + this.assemblers[0].AddQueueItem(missileComponent.ComponentType.BlueprintId, toBuild); + } + i += 1; + } + } + + struct IngotsMissileStatus + { + public string Name { get; set; } + + public float Amount { get; set; } + + public float NbOfMissiles { get; set; } + } + + struct IngotsStatus + { + public string Name { get; set; } + + public float Amount { get; set; } + } + + /// + /// Display how many missile can be build with the current ingot stock. + /// + /// + public void DisplayIngotsStatus(StringBuilder sb) + { + var ingots = this.gridTerminal.IngotsFromGridContainers(this.grid); + + var ingotsMissileStatus = new List(); + var ingotsStatusRest = new List(); // For ingots not needed by missiles. + + foreach (var ingot in Enum.GetValues(typeof(IngotsEnum)).Cast()) + { + var amount = ingots[ingot]; + var amountNeeded = INGOTS_PER_MISSILE[ingot]; + + if (amountNeeded != 0f) + ingotsMissileStatus.Add(new IngotsMissileStatus { Name = ingot.ToString(), Amount = amount, NbOfMissiles = amount / amountNeeded }); + else + ingotsStatusRest.Add(new IngotsStatus { Name = ingot.ToString(), Amount = amount }); + } + + ingotsMissileStatus.Sort((a, b) => b.NbOfMissiles.CompareTo(a.NbOfMissiles)); + + sb.AppendLine(); + sb.AppendLine($"Nb of missiles buildable: {(int)ingotsMissileStatus.Last().NbOfMissiles}"); + sb.AppendLine(new string('-', Constants.CONSOLE_LINE_LENGTH)); + + foreach (var ingotMissileStatus in ingotsMissileStatus) + { + var name = $"{ingotMissileStatus.Name} ({(int)ingotMissileStatus.NbOfMissiles}):"; + sb.Append(name); + var value = $"{ingotMissileStatus.Amount:N2} kg"; + sb.AppendLine(value.PadLeft(Constants.CONSOLE_LINE_LENGTH - name.Length)); + } + + sb.AppendLine(new string('-', Constants.CONSOLE_LINE_LENGTH)); + + foreach (var ingotStatus in ingotsStatusRest) + { + var name = $"{ingotStatus.Name}:"; + sb.Append(name); + var value = $"{ingotStatus.Amount:N2} kg"; + sb.AppendLine(value.PadLeft(Constants.CONSOLE_LINE_LENGTH - name.Length)); + } + } + + + /// + /// It counts elements in queue in assemblers too. + /// + /// + /// + List GetCurrentItemQuantity(IEnumerable componentTypes) + { + var total = new List(new MyFixedPoint[componentTypes.Count()]); + + foreach (var inventory in this.gridTerminal.GetAllInventories(grid)) + { + int i = 0; + foreach (var componentType in componentTypes) + { + var items = inventory.FindItem(componentType.ItemType); + if (items.HasValue) + total[i] += items.Value.Amount; + i += 1; + } + } + + // Special case for assemblers: check the current queue. + foreach (var assembler in this.gridTerminal.GetBlocks(grid: this.grid)) + { + var queue = new List(); + assembler.GetQueue(queue); + + int i = 0; + foreach (var componentType in componentTypes) + { + foreach (var item in queue) + { + if (item.BlueprintId == componentType.BlueprintId) + total[i] += item.Amount; + } + + i += 1; + } + } + + return total; + } + } +} diff --git a/Mimine/Program.cs b/Mimine/Program.cs new file mode 100644 index 0000000..9a8955e --- /dev/null +++ b/Mimine/Program.cs @@ -0,0 +1,260 @@ +using Sandbox.Game; +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.Game.VisualScripting; +using VRage.GameServices; +using VRage.ObjectBuilders; + +using VRageMath; + +namespace IngameScript +{ + partial class Program : MyGridProgram + { + enum State + { + IDLE, + BUILD_COMPONENTS_MISSILES, + } + + State currentState = State.IDLE; + + readonly IMyCubeGrid grid; + + readonly Output output; + + readonly Output leftPanel; + readonly Output rightPanel; + + readonly List assemblers = new List(); // First one is the main assembler. + + readonly MissileManager missileManager; + readonly AmmoManager ammoManager; + + public Program() + { + this.grid = this.Me.CubeGrid; + + var cockpitPilot = this.GridTerminalSystem.GetBlock("[Mimine] Control Seat Main"); + var cockpitCopilot = this.GridTerminalSystem.GetBlock("[Mimine] Control Seat Copilot"); + + var output = this.Me.GetSurface(0); + this.output = new Output(output, Constants.CONSOLE_NB_LINES); + + this.output.Print("Mimine system starting..."); + + this.leftPanel = new Output(new List { this.GridTerminalSystem.GetBlock("[Mimine] LCD Panel 01", this.grid), cockpitCopilot.GetSurface(1), cockpitPilot.GetSurface(1) }); + this.rightPanel = new Output(new List { this.GridTerminalSystem.GetBlock("[Mimine] LCD Panel 02", this.grid), cockpitCopilot.GetSurface(0), cockpitPilot.GetSurface(0) }); + + foreach (var assembler in this.GridTerminalSystem.GetBlocks(grid: this.grid)) + { + if (assembler.BlockDefinition.SubtypeName == "LargeAssembler") + { + if (assembler.CooperativeMode) + this.assemblers.Add(assembler); + else + this.assemblers.Insert(0, assembler); + } + else + { + // Not very useful to inform about ignored assemblers -> commented out. + //this.output.Print($"Assembler Ignored, not a large assembler:"); + //this.output.Print($" {assembler.CustomName}"); + } + } + + if (this.assemblers.Count == 0) + this.output.PrintError("No assembler found"); + else + this.output.Print($"Main assembler: {this.assemblers[0].CustomName}"); + + this.missileManager = new MissileManager(this.GridTerminalSystem, this.grid, this.output, this.assemblers); + this.ammoManager = new AmmoManager(this.GridTerminalSystem, this.grid, this.output, this.assemblers); + + 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 "BUILD": + this.currentState = State.BUILD_COMPONENTS_MISSILES; + break; + + case "STOP": + this.currentState = State.IDLE; + break; + + default: + this.output.Print($"Uknown command: {argument}"); + break; + } + } + } + + void UpdateState() + { + var ammoStatus = this.ammoManager.GetAmmoStatus(); + + if (this.currentState == State.BUILD_COMPONENTS_MISSILES) + { + this.missileManager.BuildComponentForMissiles(); + this.ammoManager.BuildAmmo(ammoStatus); + } + + this.DisplayStatus(); + + StringBuilder sb = new StringBuilder(); + this.missileManager.DisplayIngotsStatus(sb); + sb.AppendLine(new string('-', Constants.CONSOLE_LINE_LENGTH)); + this.ammoManager.DisplayAmmoStatus(sb, ammoStatus); + this.rightPanel.Display(sb.ToString()); + } + + void DisplayStatus() + { + var cargoSpace = this.GetCargoSpace(); + var energyState = this.GetEnergyState(); + var hydrogenVolume = this.GetHydrogenVolume(); + var oxygenVolume = this.GetOxygenVolume(); + + this.leftPanel.Display( + $@" +{cargoSpace.ToString("Cargo volume", "kL")} + +{energyState.ToString("Energy", "MWh")} + +{hydrogenVolume.ToString("Hydrogen", "L")} + +{oxygenVolume.ToString("Oxygen", "L")}" + ); + } + + 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 firstLine = new StringBuilder(); + firstLine.Append(name).Append(": "); + var values = $"{this.Current:N1} {unit} / {this.Total:N1} {unit}"; + firstLine.Append(values.PadLeft(Constants.CONSOLE_LINE_LENGTH - 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 = Constants.CONSOLE_LINE_LENGTH - 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(grid: 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(grid: 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(grid: this.grid, filter: tank => tank.CustomName.Contains("Hydrogen")); + + 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(grid: this.grid, filter: tank => tank.CustomName.Contains("Oxygen")); + + foreach (var tank in tanks) + { + currentVolume += (double)tank.Capacity * (double)tank.FilledRatio; + totalVolume += (double)tank.Capacity; + } + + return new Quantity(currentVolume, totalVolume); + } + } +} + diff --git a/Mimine/packages.config b/Mimine/packages.config new file mode 100644 index 0000000..0653ff6 --- /dev/null +++ b/Mimine/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From b1ac49118660b03eebd1261f4037dc964d1abc8c Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Sun, 14 Sep 2025 17:24:46 +0200 Subject: [PATCH 2/4] Enhance missile behavior and state management - Introduced new constants for missile control: `DISTANCE_BEFORE_ROTATING` and `ROTATION_SPEED`. - Expanded `State` enum to include `TURNING` and `ACCELERATING`. - Added variables for tracking missile position, speed, and acceleration. - Implemented logic for initializing backward thruster and gyroscope. - Added `CheckEnemies` method to switch to AI mode upon enemy detection. - Introduced `Detonate` method for centralized warhead detonation logic. - Updated `DistanceFromLaunch` property for accurate distance calculation. --- MissileController/Program.cs | 178 ++++++++++++++++++++++++++++------- 1 file changed, 143 insertions(+), 35 deletions(-) diff --git a/MissileController/Program.cs b/MissileController/Program.cs index 9a2ce30..5f3623e 100644 --- a/MissileController/Program.cs +++ b/MissileController/Program.cs @@ -33,26 +33,38 @@ namespace IngameScript { partial class Program : MyGridProgram { - //const string MISSILE_GRID_PREFIX = "[PM]"; - const float EPSILON = 0.05f; - const double DELAY_BEFORE_TRAVELLING_MODE = 5000; // [ms] (5 s). - const double AUTO_DESTRUCTION_AFTER = 120000; // [ms] (2 min). Or if the hydrogen tank is empty. + const double DISTANCE_BEFORE_ROTATING = 15; // [m]. + const float ROTATION_SPEED = 0.5f; // [rad/s]. enum State { NORMAL, LAUNCHING, + TURNING, + ACCELERATING, TRAVELLING, + AI, } - + State currentState = State.NORMAL; + int tickFromStart; + int begginningOfaccelerationTick; + bool launchPositionSet = false; + Vector3D launchPosition; + Vector3D directionBeforeTurning; + + Vector3D lastPosition; + double speed; + double acceleration; + readonly Output output; readonly IMyCubeGrid grid; - int tickFromStart; IMyThrust forwardThruster; + IMyThrust backwardThruster; + IMyGyro gyroscope; IEnumerable thrusters; IMyFlightMovementBlock aiMove; IMyOffensiveCombatBlock aiCombat; @@ -83,6 +95,22 @@ namespace IngameScript return false; } + if (this.backwardThruster == null) + this.backwardThruster = this.GridTerminalSystem.GetBlock("[PM] Hydrogen Thruster 06", this.grid); + if (this.backwardThruster == null) + { + this.output.Print("Error: Cannot find backward thruster"); + return false; + } + + if (this.gyroscope == null) + this.gyroscope = this.GridTerminalSystem.GetBlock("[PM] Gyroscope", this.grid); + if (this.gyroscope == null) + { + this.output.Print("Error: Cannot find gyroscope"); + return false; + } + if (this.thrusters == null) { this.thrusters = this.GridTerminalSystem.GetBlocksFromGroup("[PM] Thrusters", this.grid); @@ -141,15 +169,15 @@ namespace IngameScript return true; } - bool BlinkBeforeBeingAutodestructed() - { - return this.MsSinceLaunch > AUTO_DESTRUCTION_AFTER - 3000; - } + //bool BlinkBeforeBeingAutodestructed() + //{ + // return this.MsSinceLaunch > AUTO_DESTRUCTION_AFTER - 3000; + //} - bool MustBeAutodestructed() - { - return this.MsSinceLaunch > AUTO_DESTRUCTION_AFTER; - } + //bool MustBeAutodestructed() + //{ + // return this.MsSinceLaunch > AUTO_DESTRUCTION_AFTER; + //} bool EnemyAtRange() { @@ -160,6 +188,20 @@ namespace IngameScript { this.tickFromStart += 10; + if (this.forwardThruster != null) + { + var currentPosition = this.forwardThruster.GetPosition(); + if (this.lastPosition != new Vector3D()) + { + var d = (currentPosition - this.lastPosition).Length(); + var speed = d * 10; + this.acceleration = (speed - this.speed) * 10; + this.speed = speed; + //this.output.Print($"Spd: {this.speed:0.0}, Acc: {this.acceleration:0.0}"); + } + this.lastPosition = currentPosition; + } + switch (this.currentState) { case State.LAUNCHING: @@ -170,35 +212,69 @@ namespace IngameScript break; } - this.forwardThruster.Enabled = true; // Only one thruster is enabled when launching. - this.forwardThruster.ThrustOverridePercentage = 1; - if (this.MsSinceLaunch > DELAY_BEFORE_TRAVELLING_MODE && (this.EnemyAtRange() || this.BlinkBeforeBeingAutodestructed())) + if (!this.launchPositionSet) { - foreach (var thruster in this.thrusters) - { - if (thruster != this.forwardThruster) - thruster.Enabled = true; - } + this.launchPosition = this.forwardThruster.GetPosition(); + this.launchPositionSet = true; + } + //this.forwardThruster.Enabled = true; // Only one thruster is enabled when launching. Replaced by the loop below to avoid issues when gravity is present. + foreach (var thruster in this.thrusters) + { + if (thruster != this.backwardThruster) + thruster.Enabled = true; + } + + this.forwardThruster.ThrustOverridePercentage = 1f; // Can be lowered for low gravity but set to 1f for 1G planet gravity. + + //this.output.Print($"Distance from launch: {this.DistanceFromLaunch:0.0} m"); + + if (this.DistanceFromLaunch > DISTANCE_BEFORE_ROTATING) + { + this.directionBeforeTurning = this.forwardThruster.WorldMatrix.Forward.Normalized(); + this.gyroscope.GyroOverride = true; + this.gyroscope.Roll = ROTATION_SPEED; + + this.output.Print($"Turning mode"); + this.currentState = State.TURNING; + } + break; + + case State.TURNING: + var dotProduct = this.forwardThruster.WorldMatrix.Forward.Normalized().Dot(this.directionBeforeTurning); + //this.output.Print($"Dot product: {dotProduct:0.000}"); + if (dotProduct <= EPSILON) + { + this.gyroscope.GyroOverride = false; + this.gyroscope.Roll = 0; + this.forwardThruster.ThrustOverridePercentage = 1f; + + this.output.Print($"Accelerating mode"); + this.begginningOfaccelerationTick = this.tickFromStart; + this.currentState = State.ACCELERATING; + } + break; + + case State.ACCELERATING: + //this.CheckTank(); + this.CheckEnemies(); + + if (this.acceleration <= EPSILON) + { this.forwardThruster.ThrustOverridePercentage = 0; - this.aiMove.Enabled = true; - - foreach (var warhead in this.warheads) - warhead.IsArmed = true; - this.output.Print($"Travelling mode"); this.currentState = State.TRAVELLING; } break; case State.TRAVELLING: - if (this.BlinkBeforeBeingAutodestructed()) - this.light.BlinkIntervalSeconds = 0.5f; + //this.CheckTank(); + this.CheckEnemies(); + + break; - if (this.gasTank.FilledRatio <= EPSILON || this.MustBeAutodestructed()) - { - Detonate(); - } + case State.AI: + //this.CheckTank(); break; case State.NORMAL: @@ -206,6 +282,33 @@ namespace IngameScript } } + //void CheckTank() + //{ + // if (this.gasTank.FilledRatio <= EPSILON) + // { + // this.output.Print("Tank empty, detonating"); + // Detonate(); + // } + //} + + void CheckEnemies() + { + if (this.EnemyAtRange()) + { + this.forwardThruster.ThrustOverridePercentage = 0; + + foreach (var warhead in this.warheads) + warhead.IsArmed = true; + + this.backwardThruster.Enabled = true; + this.aiMove.Enabled = true; + + this.output.Print("Enemy in range, switching to AI mode"); + this.currentState = State.AI; + } + } + + void Detonate() { foreach (var warhead in this.warheads) @@ -216,9 +319,14 @@ namespace IngameScript { } - double MsSinceLaunch + //double MsSinceLaunch + //{ + // get { return (double)this.tickFromStart / 60 * 1000; } + //} + + double DistanceFromLaunch { - get { return (double)this.tickFromStart / 60 * 1000; } + get { return (this.forwardThruster.GetPosition() - this.launchPosition).Length(); } } public void Main(string argument, UpdateType updateSource) From 16040e5f5a2cdf9e84da0cf36ac2448c6c959fbd Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Sun, 14 Sep 2025 17:25:14 +0200 Subject: [PATCH 3/4] Refactor missile launch system and add abort functionality - Updated `State` enum to remove `FILLING_TANK` and add `ABORTING`. - Modified `Launcher` constructor to include `IMyProjector`. - Added `AbortLaunching` method to handle abort state. - Enhanced `UpdateState` to manage the new `ABORTING` state. - Introduced `CheckMissileBuilt` method for missile readiness checks. - Increased `HYDRO_TANK_FILLED_PERCENT` from 40 to 60. - Changed `GRID_PREFIX` from `[PML]` to `[Mimine]`. - Updated program's frequency from `Update10` to `Update100`. - Adjusted `ResetToNominal` to call `AbortLaunching` for all launchers. --- MissileLauncher/Program.cs | 164 +++++++++++++++++++++++-------------- 1 file changed, 102 insertions(+), 62 deletions(-) diff --git a/MissileLauncher/Program.cs b/MissileLauncher/Program.cs index 23ecdbe..2254d96 100644 --- a/MissileLauncher/Program.cs +++ b/MissileLauncher/Program.cs @@ -50,8 +50,8 @@ namespace IngameScript { NOMINAL, STARTING_SEQUENCE, - FILLING_TANK, LAUNCHING, + ABORTING, } readonly int number; @@ -59,6 +59,7 @@ namespace IngameScript readonly IMyGridTerminalSystem gridTerminal; readonly IMyShipConnector connector; + readonly IMyProjector projector; Missile missile; State currentState = State.NOMINAL; @@ -71,12 +72,13 @@ namespace IngameScript /// /// To output some text /// Connector of the launcher (not the missile) - public Launcher(int number, IMyGridTerminalSystem gridTerminal, Output output, IMyShipConnector connector) + public Launcher(int number, IMyGridTerminalSystem gridTerminal, Output output, IMyShipConnector connector, IMyProjector projector) { this.number = number; this.gridTerminal = gridTerminal; this.output = output; this.connector = connector; + this.projector = projector; } public void Launch() @@ -85,6 +87,11 @@ namespace IngameScript this.currentState = State.STARTING_SEQUENCE; } + public void AbortLaunching() + { + this.currentState = State.ABORTING; + } + void Print(string message) { this.output.Print(String.Format("{0:00}: {1}", this.number, message)); @@ -92,92 +99,119 @@ namespace IngameScript public void UpdateState() { + this.CheckMissileBuilt(); + switch (this.currentState) { case State.STARTING_SEQUENCE: - var missileConnector = this.connector.OtherConnector; - if (missileConnector == null) + if (!this.CheckMissileBuilt()) { - this.Print("Cannot find the missile connector"); + this.Print("Can't launch missile: Not built, waiting..."); break; } - - var missileGrid = missileConnector.CubeGrid; - if (missileGrid == null) - { - this.Print("Cannot find the missile grid"); - break; - } - - IMyGasTank tank = this.gridTerminal.GetBlock("[PM] Hydrogen Tank", missileGrid); - if (tank == null) - { - this.Print("Cannot find the missile hydrogen tank"); - break; - } - - IMyShipMergeBlock mergeBlock = this.gridTerminal.GetBlock("[PM] Merge Block", missileGrid); - if (mergeBlock == null) - { - this.Print("Cannot find the missile merge block"); - break; - } - - IMyProgrammableBlock programmableBlock = this.gridTerminal.GetBlock("[PM] Programmable Block", missileGrid); - if (programmableBlock == null) - { - this.Print("Cannot find the missile programmable block"); - break; - } - - IEnumerable thrusters = this.gridTerminal.GetBlocksFromGroup("[PM] Thrusters", missileGrid); - if (thrusters.Count() == 0) - { - this.Print("Cannot find missile thrusters"); - break; - } - - // When launched the missile will enabled itself its thrusters. - foreach (var thruster in thrusters) - thruster.Enabled = false; - - tank.Stockpile = true; - connector.Connect(); - - this.missile = new Missile(tank, connector, mergeBlock, programmableBlock); - - this.currentState = State.FILLING_TANK; - break; - - case State.FILLING_TANK: - this.Print($"Waiting missile tank filled... ({this.missile.Tank.FilledRatio * 100:.0}%)"); - + if (this.missile.Tank.FilledRatio >= Program.HYDRO_TANK_FILLED_PERCENT / 100) { this.missile.Tank.Stockpile = false; this.currentState = State.LAUNCHING; } + else + { + this.Print($"Waiting missile tank filled... ({this.missile.Tank.FilledRatio * 100:.0}%)"); + } break; case State.LAUNCHING: this.Print("Launching missile..."); + this.missile.MergeBlock.Enabled = false; + if (this.missile.ProgrammableBlock.TryRun("START")) this.Print("Missile launched!"); else this.Print("ERROR: Can't send START command to missile"); - this.missile.MergeBlock.Enabled = false; this.connector.Disconnect(); this.currentState = State.NOMINAL; this.missile = null; break; + case State.ABORTING: + this.missile = null; + this.currentState = State.NOMINAL; + break; + case State.NOMINAL: break; // Nothing; } } + + /// + /// Returns true if missile ready. + /// + /// + bool CheckMissileBuilt() + { + if (this.missile != null) + return true; + + var missileConnector = this.connector.OtherConnector; + if (missileConnector == null) + { + //this.Print("Cannot find the missile connector"); + return false; + } + + var missileGrid = missileConnector.CubeGrid; + if (missileGrid == null) + { + //this.Print("Cannot find the missile grid"); + return false; + } + + IMyGasTank tank = this.gridTerminal.GetBlock("[PM] Hydrogen Tank", missileGrid); + if (tank == null) + { + //this.Print("Cannot find the missile hydrogen tank"); + return false; + } + + IMyShipMergeBlock mergeBlock = this.gridTerminal.GetBlock("[PM] Merge Block", missileGrid); + if (mergeBlock == null) + { + //this.Print("Cannot find the missile merge block"); + return false; + } + + IMyProgrammableBlock programmableBlock = this.gridTerminal.GetBlock("[PM] Programmable Block", missileGrid); + if (programmableBlock == null) + { + //this.Print("Cannot find the missile programmable block"); + return false; + } + + IEnumerable thrusters = this.gridTerminal.GetBlocksFromGroup("[PM] Thrusters", missileGrid); + if (thrusters.Count() == 0) + { + //this.Print("Cannot find missile thrusters"); + return false; + } + + if (this.projector.RemainingBlocks > 0) + return false; + + // When launched the missile will enabled itself its thrusters. + foreach (var thruster in thrusters) + thruster.Enabled = false; + + tank.Stockpile = true; + connector.Connect(); + + this.missile = new Missile(tank, connector, mergeBlock, programmableBlock); + + return true; + } } partial class Program : MyGridProgram @@ -190,11 +224,12 @@ namespace IngameScript LAUNCHING_CONTINUOUS, } - public const string GRID_PREFIX = "[PML]"; + public const string GRID_PREFIX = "[Mimine]"; public const string MISSILE_GRID_PREFIX = "[PM]"; - public const double HYDRO_TANK_FILLED_PERCENT = 40; + public const double HYDRO_TANK_FILLED_PERCENT = 60; const string LAUNCHER_SMALL_CONNECTOR_NAME = "Connector Launcher"; // Following by a number: "01", "02", etc. + const string PROJECTOR_NAME = "Projector Missile"; // Following by a number: "01", "02", etc. const string DOORS_MISSILES_GROUP = "Doors Missiles"; const float EPSILON = 0.05f; @@ -234,10 +269,12 @@ namespace IngameScript foreach (var connector in launcherConnectors) { var n = int.Parse(connector.CustomName.Substring(connectorNamePrefix.Length)); - this.launchers.Add(new Launcher(n, this.GridTerminalSystem, this.output, connector)); + var projector = this.GridTerminalSystem.GetBlock($"{GRID_PREFIX} {PROJECTOR_NAME} {n:0,0}"); + + this.launchers.Add(new Launcher(n, this.GridTerminalSystem, this.output, connector, projector)); } - this.Runtime.UpdateFrequency = UpdateFrequency.Update10; + this.Runtime.UpdateFrequency = UpdateFrequency.Update100; this.output.Print($"Missile launcher system started ({this.launchers.Count} launcher(s))"); } @@ -264,6 +301,9 @@ namespace IngameScript void ResetToNominal() { + foreach (var launcher in this.launchers) + launcher.AbortLaunching(); + this.nbLaunched = 0; this.currentState = State.NOMINAL; } @@ -303,7 +343,7 @@ namespace IngameScript public void Main(string argument, UpdateType updateSource) { - if ((updateSource & UpdateType.Update10) != 0) + if ((updateSource & UpdateType.Update100) != 0) { this.UpdateState(); } From 169719ba4b3df289041ad1469c145c2c148eb166 Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Sun, 14 Sep 2025 17:26:17 +0200 Subject: [PATCH 4/4] - Add an extension method to get all inventories. - Update README and TODO --- README.md | 9 ++++++-- SECommon/Output.cs | 57 +++++++++++++++++++++++++++++++--------------- SECommon/Utils.cs | 47 ++++++++++++++++++++++++++++++++++++-- SEScripts.sln | 14 ++++++++++++ TODO.md | 36 ++++++++++++++++++++++++----- 5 files changed, 135 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 87366ae..6e95f5b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ # Modules AutoPilot: Automatic control of piloting on planet. -SSPifou: The main control for the spaceship SSPifou. +SSPifou: The main control for the spaceship SSPifou. (TODO) MissileController: Run in the missile computer to pilot it to its target. MissileLauncher: Can launch multiple missiles. @@ -14,4 +14,9 @@ MissileLauncher: Can launch multiple missiles. ## Elements -* \ No newline at end of file +* [PML] Connector Launcher 01 +* [PML] Merge Block Missile 01 +* [PML] Projector 01 +* Offsets: (0, -8, 2), pitch: 0° +* [PML] Projector 02 +* Offsets: (0, -8, -2), pitch: 180° \ No newline at end of file diff --git a/SECommon/Output.cs b/SECommon/Output.cs index 5573c61..1e5ed7c 100644 --- a/SECommon/Output.cs +++ b/SECommon/Output.cs @@ -26,8 +26,8 @@ namespace IngameScript { class Output { - IList outputs; - int maxNbLines; + readonly IList outputs; + readonly int maxNbLines; public Output(IList surfaces, int maxNbLines = 10) { @@ -58,29 +58,41 @@ namespace IngameScript : this(new List { surface }, maxNbLines) { } - public void Print(string text, int outputNumber = 0) + public void Print(string text, int outputNumber = -1) { - if (this.outputs.Count() <= outputNumber) + if (outputNumber >= 0 && this.outputs.Count() <= outputNumber) { throw new Exception($"Output number {outputNumber} doesn't exist (number of output: {this.outputs.Count()}"); } else { - var output = this.outputs[outputNumber]; - var currentText = output.GetText(); - var lines = currentText.Split('\n'); - if (lines.Count() >= this.maxNbLines) + if (outputNumber == -1) { - output.WriteText(lines.Skip(lines.Count() - this.maxNbLines + 1).Append(text).Aggregate((a, b) => a + Environment.NewLine + b)); - } - else if (lines.Count() == 0) - { - output.WriteText(text); + foreach (var output in this.outputs) + this.Print(text, output); } else { - output.WriteText(Environment.NewLine + text, true); - } + this.Print(text, this.outputs[outputNumber]); + } + } + } + + void Print(string text, IMyTextSurface surface) + { + var currentText = surface.GetText(); + var lines = currentText.Split('\n'); + if (lines.Count() >= this.maxNbLines) + { + surface.WriteText(lines.Skip(lines.Count() - this.maxNbLines + 1).Append(text).Aggregate((a, b) => a + Environment.NewLine + b)); + } + else if (lines.Count() == 0) + { + surface.WriteText(text); + } + else + { + surface.WriteText(Environment.NewLine + text, true); } } @@ -90,15 +102,24 @@ namespace IngameScript } - public void Display(string text, int outputNumber = 0) + public void Display(string text, int outputNumber = -1) { - if (this.outputs.Count() <= outputNumber) + if (outputNumber >= 0 && this.outputs.Count() <= outputNumber) { throw new Exception($"Output number {outputNumber} doesn't exist (number of output: {this.outputs.Count()}"); } else { - this.outputs[outputNumber].WriteText(text); + if (outputNumber == -1) + { + foreach (var output in this.outputs) + output.WriteText(text); + } + else + { + this.outputs[outputNumber].WriteText(text); + } + } } } diff --git a/SECommon/Utils.cs b/SECommon/Utils.cs index c083571..96d8ef2 100644 --- a/SECommon/Utils.cs +++ b/SECommon/Utils.cs @@ -1,8 +1,10 @@ using Sandbox.ModAPI.Ingame; +using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Security.Cryptography; using VRage.Game.ModAPI.Ingame; @@ -33,12 +35,12 @@ namespace IngameScript /// /// /// - public static IEnumerable GetBlocks(this IMyGridTerminalSystem gridTerminal, string name = null, IMyCubeGrid grid = null) + public static IEnumerable GetBlocks(this IMyGridTerminalSystem gridTerminal, string name = null, IMyCubeGrid grid = null, Func filter = null) where T : class, IMyTerminalBlock { var l = new List(); gridTerminal.GetBlocksOfType(l, (T block) => - (name == null || block.CustomName == name) && (grid == null || block.CubeGrid == grid) + (name == null || block.CustomName == name) && (grid == null || block.CubeGrid == grid) && (filter == null || filter(block)) ); return l; } @@ -90,5 +92,46 @@ namespace IngameScript return new List(); } + + public static IEnumerable GetAllInventories(this IMyGridTerminalSystem gridTerminal, IMyCubeGrid grid = null) + { + var inventories = new List(); + + // Containers. + foreach (var container in gridTerminal.GetBlocks(grid: grid)) + inventories.Add(container.GetInventory()); + + // Assemblers. + foreach (var assembler in gridTerminal.GetBlocks(grid: grid)) + { + inventories.Add(assembler.InputInventory); + inventories.Add(assembler.OutputInventory); + } + + // Refineries + foreach (var refinery in gridTerminal.GetBlocks(grid: grid)) + { + inventories.Add(refinery.InputInventory); + inventories.Add(refinery.OutputInventory); + } + + // Connectors. + foreach (var connector in gridTerminal.GetBlocks(grid: grid)) + inventories.Add(connector.GetInventory()); + + // Welders. + foreach (var welder in gridTerminal.GetBlocks(grid: grid)) + inventories.Add(welder.GetInventory()); + + // Turrets. + foreach (var turret in gridTerminal.GetBlocks(grid: grid)) + inventories.Add(turret.GetInventory()); + + // Artillery and missile launcher. + foreach (var missileLauncher in gridTerminal.GetBlocks(grid: grid)) + inventories.Add(missileLauncher.GetInventory()); + + return inventories; + } } } \ No newline at end of file diff --git a/SEScripts.sln b/SEScripts.sln index 2951aed..be758fb 100644 --- a/SEScripts.sln +++ b/SEScripts.sln @@ -22,6 +22,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MissileLauncher", "MissileL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BaseMiner", "BaseMiner\BaseMiner.csproj", "{1C2F7DB3-26AD-4C5E-B6C5-D6715B3CFB36}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PML", "PML\PML.csproj", "{F280FA02-7E9E-7390-1771-4B1F144AC88C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mimine", "Mimine\Mimine.csproj", "{1E123126-9FA8-67F1-5E48-3B8C400AAEDF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -48,6 +52,14 @@ Global {1C2F7DB3-26AD-4C5E-B6C5-D6715B3CFB36}.Debug|x64.Build.0 = Debug|x64 {1C2F7DB3-26AD-4C5E-B6C5-D6715B3CFB36}.Release|x64.ActiveCfg = Release|x64 {1C2F7DB3-26AD-4C5E-B6C5-D6715B3CFB36}.Release|x64.Build.0 = Release|x64 + {F280FA02-7E9E-7390-1771-4B1F144AC88C}.Debug|x64.ActiveCfg = Debug|x64 + {F280FA02-7E9E-7390-1771-4B1F144AC88C}.Debug|x64.Build.0 = Debug|x64 + {F280FA02-7E9E-7390-1771-4B1F144AC88C}.Release|x64.ActiveCfg = Release|x64 + {F280FA02-7E9E-7390-1771-4B1F144AC88C}.Release|x64.Build.0 = Release|x64 + {1E123126-9FA8-67F1-5E48-3B8C400AAEDF}.Debug|x64.ActiveCfg = Debug|x64 + {1E123126-9FA8-67F1-5E48-3B8C400AAEDF}.Debug|x64.Build.0 = Debug|x64 + {1E123126-9FA8-67F1-5E48-3B8C400AAEDF}.Release|x64.ActiveCfg = Release|x64 + {1E123126-9FA8-67F1-5E48-3B8C400AAEDF}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -58,9 +70,11 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution SECommon\SECommon.projitems*{141e1804-f644-48f8-a3d8-befeee66ecba}*SharedItemsImports = 5 SECommon\SECommon.projitems*{1c2f7db3-26ad-4c5e-b6c5-d6715b3cfb36}*SharedItemsImports = 5 + SECommon\SECommon.projitems*{1e123126-9fa8-67f1-5e48-3b8c400aaedf}*SharedItemsImports = 5 SECommon\SECommon.projitems*{761f968e-ce71-404b-a20a-7c1458d6c014}*SharedItemsImports = 5 SECommon\SECommon.projitems*{9e97399c-4fe6-495b-aa87-acc2213647cd}*SharedItemsImports = 13 SECommon\SECommon.projitems*{dbcd62fe-f7aa-4a03-9241-0a4be6952664}*SharedItemsImports = 5 + SECommon\SECommon.projitems*{f280fa02-7e9e-7390-1771-4b1f144ac88c}*SharedItemsImports = 5 SECommon\SECommon.projitems*{f902e413-8f1a-423d-98a5-f26b684e28ba}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/TODO.md b/TODO.md index 2dd8bc9..272cb8f 100644 --- a/TODO.md +++ b/TODO.md @@ -1,15 +1,39 @@ # Active + ## SSPifou -* Missile autodestruction (after 1min or when tank empty) -* Finish missile launchers -* Add external lights + commands -* Command to stop thrusters +* Remote control for the Bee +* Add external lights + commands * Add weapons rack * Embedded computer: - * Auto landing + auto stabilization * Display of ore, ingot, ammo status * Autocraft of some element: ammo + components + * Auto landing + auto stabilization +## Missile -# Finished \ No newline at end of file +* Missile autodestruction (after 1min or when tank empty) + +### Components: + Steel plate: 252 + Construction Comp: 167 + Motor: 18 + Interior Plate: 40 + Computer: 47 + Powercell: 2 + Display: 1 + Small Steel Tube: 27 + Metal Grid: 48 + Large Steel Tube: 16 + Detector Comp: 8 + Girder: 11 + Explosives: 22 + + Volume: 3'084 L + +# Finished + +* Cockpit commands to open/close hangar doors (3 commands) +* Command to stop thrusters +* Finish missile launchers +* Why the missile stops after ~100 m? \ No newline at end of file