using Sandbox.Game.Entities.Cube; using Sandbox.Game.EntityComponents; using Sandbox.Game.GameSystems; using Sandbox.ModAPI.Ingame; using Sandbox.ModAPI.Interfaces; using SpaceEngineers.Game.Entities.Blocks; using SpaceEngineers.Game.ModAPI.Ingame; using System; using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; 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.GameServices; using VRageMath; namespace IngameScript { class Missile { public IMyGasTank Tank { get; } public IMyShipConnector Connector { get; } public IMyShipMergeBlock MergeBlock { get; } public IMyProgrammableBlock ProgrammableBlock { get; } public Missile(IMyGasTank tank, IMyShipConnector connector, IMyShipMergeBlock mergeBlock, IMyProgrammableBlock programmableBlock) { this.Tank = tank; this.Connector = connector; this.MergeBlock = mergeBlock; this.ProgrammableBlock = programmableBlock; } } class Launcher { enum State { NOMINAL, STARTING_SEQUENCE, FILLING_TANK, LAUNCHING, } readonly int number; readonly Output output; readonly IMyGridTerminalSystem gridTerminal; readonly IMyShipConnector connector; Missile missile; State currentState = State.NOMINAL; /// /// /// /// /// /// /// To output some text /// Connector of the launcher (not the missile) public Launcher(int number, IMyGridTerminalSystem gridTerminal, Output output, IMyShipConnector connector) { this.number = number; this.gridTerminal = gridTerminal; this.output = output; this.connector = connector; } public void Launch() { if (this.currentState == State.NOMINAL) this.currentState = State.STARTING_SEQUENCE; } void Print(string message) { this.output.Print(String.Format("{0:00}: {1}", this.number, message)); } public void UpdateState() { switch (this.currentState) { case State.STARTING_SEQUENCE: var missileConnector = this.connector.OtherConnector; if (missileConnector == null) { this.Print("Cannot find the missile connector"); 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; } 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; } break; case State.LAUNCHING: this.Print("Launching missile..."); 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.NOMINAL: break; // Nothing; } } } partial class Program : MyGridProgram { enum State { NOMINAL, LAUNCHING_ONE, LAUNCHING_ALL, LAUNCHING_CONTINUOUS, } public const string GRID_PREFIX = "[PML]"; public const string MISSILE_GRID_PREFIX = "[PM]"; public const double HYDRO_TANK_FILLED_PERCENT = 20; const string LAUNCHER_SMALL_CONNECTOR_NAME = "Connector Launcher"; // Following by a number: "01", "02", etc. const float EPSILON = 0.05f; readonly Output output; readonly List launchers = new List(); int nbLaunched = 0; int nextToLaunch = 0; State currentState = State.NOMINAL; public Program() { var output = this.Me.GetSurface(0); this.output = new Output(output); this.output.Print("Missile launcher system starting..."); var connectorNamePrefix = String.Format("{0} {1} ", GRID_PREFIX, LAUNCHER_SMALL_CONNECTOR_NAME); // Find all launcher sub-grid and create the associated launcher. var launcherConnectors = new List(); this.GridTerminalSystem.GetBlocksOfType( launcherConnectors, (IMyShipConnector connector) => connector.CustomName.StartsWith(connectorNamePrefix) ); 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)); } this.Runtime.UpdateFrequency = UpdateFrequency.Update100; this.output.Print($"Missile launcher system started ({this.launchers.Count} launcher(s))"); } void LaunchNext() { if (this.launchers.Count > 0) { this.launchers[this.nextToLaunch].Launch(); this.nextToLaunch = (this.nextToLaunch + 1) % this.launchers.Count; this.nbLaunched += 1; } } void ResetToNominal() { this.nbLaunched = 0; this.currentState = State.NOMINAL; } void UpdateState() { switch (this.currentState) { case State.LAUNCHING_ONE: this.LaunchNext(); this.ResetToNominal(); break; case State.LAUNCHING_ALL: if (this.nbLaunched >= launchers.Count) { this.ResetToNominal(); } else { this.LaunchNext(); } break; case State.LAUNCHING_CONTINUOUS: this.LaunchNext(); break; } foreach (var launcher in this.launchers) launcher.UpdateState(); } 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 "LAUNCH ONE": this.output.Print("Launching one missile..."); this.currentState = State.LAUNCHING_ONE; break; case "LAUNCH ALL": this.output.Print("Launching all missiles..."); this.currentState = State.LAUNCHING_ALL; break; case "LAUNCH CONTINUOUS": this.output.Print("Launching missiles in continous..."); this.currentState = State.LAUNCHING_CONTINUOUS; break; case "STOP": this.output.Print("Stopping lauching..."); this.ResetToNominal(); break; default: this.output.Print($"Uknown command: {argument}"); break; } } } } }