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, LAUNCHING, ABORTING, } readonly int number; readonly Output output; readonly IMyGridTerminalSystem gridTerminal; readonly IMyShipConnector connector; readonly IMyProjector projector; 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, IMyProjector projector) { this.number = number; this.gridTerminal = gridTerminal; this.output = output; this.connector = connector; this.projector = projector; } public void Launch() { if (this.currentState == State.NOMINAL) 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)); } public void UpdateState() { this.CheckMissileBuilt(); switch (this.currentState) { case State.STARTING_SEQUENCE: if (!this.CheckMissileBuilt()) { this.Print("Can't launch missile: Not built, waiting..."); break; } 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.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 { enum State { NOMINAL, LAUNCHING_ONE, LAUNCHING_ALL, LAUNCHING_CONTINUOUS, } public const string GRID_PREFIX = "[Mimine]"; public const string MISSILE_GRID_PREFIX = "[PM]"; 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; readonly Output output; readonly List launchers = new List(); readonly IEnumerable missilesDoors; int nbLaunched = 0; int nextToLaunch = 0; State currentState = State.NOMINAL; public Program() { var output = this.Me.GetSurface(0); this.output = new Output(output, 14); this.output.Print("Missile launcher system starting..."); this.missilesDoors = Utils.GetBlocksFromGroup(this.GridTerminalSystem, String.Format("{0} {1}", GRID_PREFIX, DOORS_MISSILES_GROUP)); if (missilesDoors.Count() == 0) { this.output.Print("No missile doors found: Aborted"); return; } 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)); 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.Update100; this.output.Print($"Missile launcher system started ({this.launchers.Count} launcher(s))"); } void LaunchNext() { // Check if the hangar door is open. foreach (var missileDoor in this.missilesDoors) { if (missileDoor.Status != DoorStatus.Open) { this.output.Print("Can't launch when missile doors are closed: Aborted"); return; } } if (this.launchers.Count > 0) { this.launchers[this.nextToLaunch].Launch(); this.nextToLaunch = (this.nextToLaunch + 1) % this.launchers.Count; this.nbLaunched += 1; } } void ResetToNominal() { foreach (var launcher in this.launchers) launcher.AbortLaunching(); 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; } } } } }