ξ€°Merith.TK Automatons 127306972040417497 CastShadows InScene 127306972040417497 0.100616686 -0.15431498 -0.4233862 0.8870216 MyModStorageComponent a8807ad4-524d-441a-a89a-0671fbfb1dd3 0 f5bad034-f449-4a0a-a1a5-190783244f3d System.Collections.Generic.List`1[System.Single] Small SmallShipSmallMergeBlock 102446621785045998 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Merge Block false false false true 38 true SmallBlockArmorSlope2Base CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorSlope2Base CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 111585299381378668 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 9 true 0.00605221232 true 0 false false 0 HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 103960001582271618 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 8 true 0.00604524044 true 0 false false 0 HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 73085564916457331 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 7 true 0.00605221651 true 0 false false 0 HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 124211572401377081 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 6 true 0.00604528934 true 0 false false 0 HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 127313373147323457 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 5 true 0.006050876 true 0 false false 0 HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockGyro 102930918334472131 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Balanced Gyroscope false false false true 6 false true HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 72704007175363914 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 4 true 0.00604436547 true 0 false false 0 HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 72676018575009114 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 3 true 0.00605095131 true 0 false false 0 HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 124926222140685907 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 2 true 0.00604701228 true 0 false false 0 HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorSlope2Base CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockSmallBatteryBlock 90649149436633627 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Battery false false false true 1 true 0.006048851 true 0 false false 0 HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallShipSmallMergeBlock 108631583032828834 CarbonFibre_Armor 144115188075855895 Faction [Loki] [Spine] Small Merge Block false false false true 37 true SmallBlockArmorBlock CarbonFibre_Armor CAP_HeloProp_6Blade 141746236322665562 CarbonFibre_Armor MyTimerComponent true 0 0 false false Frame100 0 100 false [Loki] [Spine] 6 Bladed Helicopter Rotor (30t) false false false true 3 false HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorHalfSlopedCornerBase CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorHalfSlopedCornerBase CarbonFibre_Armor SmallBlockArmorSlope2Tip CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockRemoteControl 133621291824677149 CarbonFibre_Armor 144115188075855895 Faction MyAutopilotComponent false 0 -1 false false 0 100 false 0 Forward true 25 false 0 0 0 0 0 [Loki] [Spine] Remote Control false true true true 1 false false Character 1 Run String toggle_manual Manual 133756617201896098 3 Run String toggle_landing Landing 133756617201896098 4 Run String toggle_standby Standby 133756617201896098 8 Run String toggle_shutdown Shutdown 133756617201896098 0 Run String toggle_manual Manual 133756617201896098 4 Run String toggle_landing Landing 133756617201896098 5 Run String toggle_standby Standby 133756617201896098 8 Run String toggle_shutdown Shutdown 133756617201896098 Character Character false false 0 0 -1 0 false false 0 true false true SmallBlockArmorSlope2Base CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor SmallBlockArmorHalfSlopeCorner CarbonFibre_Armor SmallBlockArmorSlope2Tip CarbonFibre_Armor SmallBlockArmorHalfSlopeCorner CarbonFibre_Armor SmallBlockArmorSlope2Tip CarbonFibre_Armor SmallBlockArmorSlope2Tip CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor SmallBlockArmorBlock CarbonFibre_Armor HalfArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorHalfSlopedCornerBase CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorHalfSlopedCornerBase CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor CAP_HeloProp_6Blade 98968291729232163 CarbonFibre_Armor MyTimerComponent true 0 0 false false Frame100 0 100 false [Loki] [Spine] 6 Bladed Helicopter Rotor (30t) false false false true 4 false SmallBlockArmorSlope2Base CarbonFibre_Armor SmallProgrammableBlockReskin 133756617201896098 CarbonFibre_Armor 144115188075855895 Faction MyMultiTextPanelComponent 0 1 NONE 4294967295 255 255 255 255 255 255 255 4278190080 0 0 0 0 0 0 255 0 SCRIPT TSS_ArtificialHorizon 2 true 4278190080 0 0 0 0 0 0 255 4281479935 255 50 50 255 50 50 255 0 0 1 NONE 4294967295 255 255 255 255 255 255 255 4278190080 0 0 0 0 0 0 255 0 SCRIPT TSS_EnergyHydrogen 2 true 4278190080 0 0 0 0 0 0 255 4281479935 255 50 50 255 50 50 255 0 MyModStorageComponent 74de02b3-27f9-4960-b1c4-27351f2b06d1 [main] block_group_name=[Loki] [Spine] Heli-Control start_mode=flight remember_mode=True max_pitch=30 max_roll=30 max_landing_pitch=10 max_landing_roll=10 precision=16 mouse_speed=0.5 [Loki] [Spine] Heli Assist Script false true true true 1 true /* * Heli Assist * ----------- * * Credits: * This script uses modified components from existing open source projects. You * can find the source code of these projects along with license information below. * * Project: Flight Assist (https://github.com/Naosyth/FlightAssist) * Copyright: Copyright (c) 2017 Brandon Worl * Licence: MIT Licence (https://github.com/Naosyth/FlightAssist/blob/master/LICENSE) * * Licence: * MIT License * * Copyright (c) 2019 Sean Campbell * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Installation: * 1. Create a group labeled 'heli assist' (or whatever you have configured the * Heli-Assist group to be called) containing the gyroscopes, thrusters and * cockpits you wish to use with Heli-Assist. * 2. Load the Heli-Assist script into a programmable block. * 3. (optionally) configure the Heli-Assist config as described in the configuration * section. * * Usage: * Once installed, Heli-Assist will align the craft with natural gravity. Using the * WASD keys will result in the craft pitching and rolling in the appropriate direction * as to result in forwarding or lateral motion. Additionally, it will regulate thrust * accordingly to maintain altitude. * * Using the mouse or up down left and right keys will orientate the craft accordingly * in addition to any pitch or roll already applied to move. Activation of the inertial * dampeners will result in the craft pitching and rolling in the appropriate direction * to slow the craft. * * Flight Modes: * For ease of use, Heli-Assist provides different flight modes for multiple situations. * * Flight mode will make the craft behave as described above and is activated by * running the program with the argument 'flight'. The 'toggle_flight' argument * will toggle between flight mode and manual mode. * * Landing mode behaves almost identically to flight mode, except that the maximum * pitch and roll (and thereby rate of acceleration) will be reduced to allow for more * precise movement. Additionally, the inertial dampeners will automatically activate. * Landing mode is activated by running the program with the argument 'landing'. The * 'toggle_landing' argument will toggle between landing mode and flight mode. * * Manual mode will disable the WASD keys and inertial dampening affecting pitch and roll * but will still regulate thrust in order to maintain altitude. Manual mode is useful for * aiming at targets or ignoring the limits of Heli-Assist's other flight modes, without the * Heli-Assist automatically correcting. Manual mode is activated by running the program with * the argument 'manual'. The 'toggle_manual' argument will toggle between manual mode and * flight mode. * * Standby mode will cause Heli-Assist to relinquish control of all gyroscopes and thrusters, * allowing the pilot or another script to control them. It is helpful for switching between * different craft configurations (such as shifting from hovering configuration to a high-speed * configuration like a modern VTOL aircraft such as a harrier) or allowing for another script * such as Vector Thrust to take control. Standby mode is activated by running the program with * the argument 'standby'. The 'toggle_standby' argument will toggle between standby mode and * flight mode. * * Shutdown mode will power off all thrusters and gyroscopes associated with Heli-Assist. * Shutdown mode is activated by running the program with the argument 'shutdown'. The * 'toggle_shutdown' argument will toggle between shutdown mode and flight mode. * * Parameters: * Heli-Assist provides multiple flight parameters in addition to its modes for customizability. * * The Precision Aim parameter can be used in flight, landing and manual mode, reducing the * sensitivity of the mouse/joystick to make aiming easier. It is toggled on/off by running * the program with the 'toggle_precision' argument. * * The Lateral Override parameter can be used in flight and landing mode and overrides inertial * dampening on the lateral axis, meaning that disabling the inertial dampeners will only * disable forward and backwards dampening, to make cruising easier. It is toggled on/off * by running the program with the 'toggle_lateral_override' argument ('toggle_lateral_dampening' * does the same and is retained for compatibility reasons). * * Configuration: * Heli Assist is configured by editing the Custom Data of the programmable block and is automatically * generated on first use. * * block_group_name - This allows for the setting of a custom group name for blocks associated * with Heli Assist. By default, it is 'Heli Assist' (without quotes). * * start_mode - This sets the mode that Heli Assist will be in when it is first started, as * described above. By default is 'flight'. * * remember_mode - This setting determines whether Heli Assist should attempt to remember the * mode it was last in when the script is restarted. By default, it is 'true'. * * max_pitch - This sets the maximum allowed pitch in degrees for both the WASD keys and the * motion dampeners. By default, it is 45. * * max_roll - This sets the maximum allowed roll in degrees for both the WASD keys and the motion * dampeners. By default, it is 45. * * max_landing_pitch - This sets the maximum allowed pitch in degrees for both the WASD keys and * the motion dampeners while in landing mode. By default, it is 10. * * max_landing_roll - This sets the maximum allowed roll in degrees for both the WASD keys and * the motion dampeners while in landing mode. By default, it is 10. * * precision - This sets the reduction in movement from gyroscopes when in precision aim mode. * By default, it is 16. * * mouse_speed - This sets the mouse sensitivity in all modes. By default, it is 10. * * Change Log: * v1.15: * * Fixed a bug where craft would gradually drift up due to incorrect craft mass being used. * v1.14: * * Fixed a bug where high pitch and roll config settings would cause craft to pitch and roll indefinitely. * * Fixed a bug where script config is not auto-generated when left blank. * * Added mouse sensitivity setting to config. * v1.13: * * Fixed a bug that prevented lateral dampening override from working. * * Added proportional dampening to inertial dampening. * * Changed lateral dampening to lateral override for less ambiguity * * Updated readme. * v1.12: * * Fixed a bug where control seats facing specific directions would make craft uncontrollable. * v1.11: * * Added automatic detection of new gyroscopes, thrusters and cockpits, updating every 10 seconds. * * Fixed a bug where specific gyroscope configurations would make craft uncontrollable. * v1.10: * * Added precision aim. * * Added lateral only dampening. * * Added standby mode. * * Changed landing mode so it will no longer force the use of dampeners. * v1.01: * * Fixed a bug where gyroscopes would fail to respond when placed in specific directions. */ IMyShipController controller; GyroController gyroController; ThrusterController thrustController; //Runtime Variables TimeSpan timeSinceLastUpdate; bool updateFinished = false; bool isFirstUpdate = true; //State Variables; string mode; bool enableLateralOverride; bool enablePrecisionAim; //Config Variables string blockGroupName; string start_mode; bool rememberLastMode; float maxFlightPitch; float maxFlightRoll; float maxLandingPitch; float maxLandingRoll; float precisionAimFactor; float mouseSpeed; //Cache Variables List<IMyShipController> controllerCache; List<IMyGyro> gyroCache; List<IMyThrust> thrustCache; string configCache; static Program program; public Program() { controllerCache = new List<IMyShipController>(); gyroCache = new List<IMyGyro>(); thrustCache = new List<IMyThrust>(); timeSinceLastUpdate = TimeSpan.FromSeconds(0); Runtime.UpdateFrequency = UpdateFrequency.Update1; try { this.Update(); } catch (Exception e) { Echo("Error: " + e.Message); } program = this; } public void Save() { Storage = mode; } public void Main(string argument, UpdateType updateSource) { timeSinceLastUpdate += Runtime.TimeSinceLastRun; if (isFirstUpdate || !updateFinished || timeSinceLastUpdate > TimeSpan.FromSeconds(10)) { try { this.Update(); } catch (Exception e) { Echo("Error: " + e.Message); } timeSinceLastUpdate = TimeSpan.FromSeconds(0); return; } Echo("Script running, next update: " + (10 - (uint)timeSinceLastUpdate.TotalSeconds).ToString()); Echo("Current Mode: " + mode); Echo("Precision Aim: " + (enablePrecisionAim ? "enabled" : "disabled")); Echo("Lateral Override: " + (enableLateralOverride ? "enabled" : "disabled")); if (IsValidMode(argument)) SwitchToMode(argument); else if (argument == "toggle_manual") SwitchToMode(mode == "manual" ? "flight" : "manual"); else if (argument == "toggle_landing") SwitchToMode(mode == "landing" ? "flight" : "landing"); else if (argument == "toggle_shutdown") SwitchToMode(mode == "shutdown" ? "flight" : "shutdown"); else if (argument == "toggle_standby") SwitchToMode(mode == "standby" ? "flight" : "standby"); else if (argument == "toggle_precision") enablePrecisionAim = !enablePrecisionAim; else if (argument == "toggle_lateral_dampening") enableLateralOverride = !enableLateralOverride; else if (argument == "toggle_lateral_override") enableLateralOverride = !enableLateralOverride; else if (argument == "update") { updateFinished = false; return; } var wasd = controller.MoveIndicator; var mouse = new Vector3(controller.RotationIndicator, controller.RollIndicator * 9); var dampeningRotation = gyroController.CalculatePitchRollToAchiveVelocity(Vector3.Zero); var autoStop = controller.DampenersOverride; if (enablePrecisionAim) mouse *= 1 / precisionAimFactor; else mouse *= mouseSpeed; switch (mode) { case "flight": { var pitch = wasd.Z * maxFlightPitch * degToRad; var roll = wasd.X * maxFlightRoll * degToRad; dampeningRotation = Vector2.Min(dampeningRotation, new Vector2(maxFlightRoll, maxFlightPitch) * degToRad); if ((autoStop || enableLateralOverride) && IsEqual(0, roll)) roll = MinAbs(dampeningRotation.X, maxFlightRoll * degToRad); if (autoStop && IsEqual(0, pitch)) pitch = MinAbs(dampeningRotation.Y, maxFlightPitch * degToRad); gyroController.SetAngularVelocity(gyroController.CalculateVelocityToAlign(pitch, roll) + mouse); thrustController.SetYAxisThrust(wasd.Y != 0 ? 0 : thrustController.CalculateThrustToHover()); break; } case "landing": { var pitch = wasd.Z * maxLandingPitch * degToRad; var roll = wasd.X * maxLandingRoll * degToRad; dampeningRotation = Vector2.Min(dampeningRotation, new Vector2(maxLandingRoll, maxLandingPitch) * degToRad); if ((autoStop || enableLateralOverride) && IsEqual(0, roll)) roll = MinAbs(dampeningRotation.X, maxLandingRoll); if (autoStop && IsEqual(0, pitch)) pitch = MinAbs(dampeningRotation.Y, maxLandingPitch); gyroController.SetAngularVelocity(gyroController.CalculateVelocityToAlign(pitch, roll) + mouse); thrustController.SetYAxisThrust(wasd.Y != 0 ? 0 : thrustController.CalculateThrustToHover()); break; } case "manual": gyroController.SetAngularVelocity(mouse); thrustController.SetYAxisThrust(wasd.Y != 0 ? 0 : thrustController.CalculateThrustToHover()); break; case "shutdown": break; case "standby": break; } } void SwitchToMode(string mode) { if (!IsValidMode(mode)) return; switch (mode) { case "flight": gyroController.SetEnabled(true); thrustController.SetEnabled(true); gyroController.SetOverride(true); break; case "landing": gyroController.SetEnabled(true); thrustController.SetEnabled(true); gyroController.SetOverride(true); controller.DampenersOverride = true; break; case "manual": gyroController.SetEnabled(true); thrustController.SetEnabled(true); gyroController.SetOverride(true); break; case "shutdown": gyroController.SetEnabled(false); thrustController.SetEnabled(false); break; case "standby": gyroController.SetEnabled(true); thrustController.SetEnabled(true); gyroController.SetOverride(false); thrustController.SetYAxisThrust(0); break; } this.mode = mode; enablePrecisionAim = false; enableLateralOverride = false; } bool IsValidMode(string mode) { return mode == "flight" || mode == "landing" || mode == "manual" || mode == "shutdown" || mode == "standby"; } public void Update() { if (isFirstUpdate || configCache != Me.CustomData || Me.CustomData == "") { var config = new ConfigSection("main"); config.Read(Me.CustomData); blockGroupName = config.Get<string>("block_group_name", "Heli Assist"); start_mode = config.Get<string>("start_mode", "flight"); rememberLastMode = config.Get<bool>("remember_mode", true); maxFlightPitch = config.Get<float>("max_pitch", 40.0f); maxFlightRoll = config.Get<float>("max_roll", 40.0f); maxLandingPitch = config.Get<float>("max_landing_pitch", 15.0f); maxLandingRoll = config.Get<float>("max_landing_roll", 15.0f); precisionAimFactor = config.Get<float>("precision", 16.0f); mouseSpeed = config.Get<float>("mouse_speed", 0.5f); if (Me.CustomData == "") Me.CustomData = configCache = config.write(); else configCache = Me.CustomData; } var blockGroup = GridTerminalSystem.GetBlockGroupWithName(blockGroupName); if (blockGroup == null) throw new Exception("Could not find block group with name '" + blockGroupName + "'"); controllerCache.Clear(); blockGroup.GetBlocksOfType<IMyShipController>(controllerCache); if (!controllerCache.Any()) throw new Exception("Ship must have at least one ship controller"); controller = null; foreach (var controller in controllerCache) { if (controller.IsUnderControl || (controller.IsMainCockpit && this.controller == null)) this.controller = controller; } if (this.controller == null) this.controller = controllerCache.First(); gyroCache.Clear(); blockGroup.GetBlocksOfType<IMyGyro>(gyroCache); if (!gyroCache.Any()) throw new Exception("Ship must have atleast one gyroscope"); thrustCache.Clear(); blockGroup.GetBlocksOfType<IMyThrust>(thrustCache); if (!thrustCache.Any()) throw new Exception("Ship must have atleast one thruster"); if (thrustController == null) thrustController = new ThrusterController(controller, thrustCache); else thrustController.Update(controller, thrustCache); if (gyroController == null) gyroController = new GyroController(controller, gyroCache); else gyroController.Update(controller, gyroCache); if (isFirstUpdate && rememberLastMode && IsValidMode(Storage)) SwitchToMode(Storage); else if (isFirstUpdate) SwitchToMode(start_mode); isFirstUpdate = false; updateFinished = true; } //The GyroController module is based on Flight Assist's GyroController and HoverModule, sharing code in places. public class GyroController { const float dampeningFactor = 25.0f; private IMyShipController controller; private List<IMyGyro> gyroscopes; public GyroController(IMyShipController controller, List<IMyGyro> gyroscopes) { this.controller = controller; this.gyroscopes = new List<IMyGyro>(gyroscopes); } public void Update(IMyShipController controller, List<IMyGyro> gyroscopes) { SetController(controller); AddGyroscopes(gyroscopes); } public void AddGyroscopes(List<IMyGyro> gyroscopes) { this.gyroscopes.AddList(gyroscopes); this.gyroscopes = this.gyroscopes.Distinct().ToList(); } public void SetController(IMyShipController controller) { this.controller = controller; } public void SetEnabled(bool setEnabled) { foreach (var gyroscope in gyroscopes) { gyroscope.Enabled = setEnabled; } } public void SetOverride(bool setOverride) { foreach (var gyroscope in gyroscopes) { gyroscope.GyroOverride = setOverride; } } public Vector2 CalculatePitchRollToAchiveVelocity(Vector3 targetVelocity) { Vector3 diffrence = Vector3.Normalize(controller.GetShipVelocities().LinearVelocity - targetVelocity); Vector3 gravity = -Vector3.Normalize(controller.GetNaturalGravity()); float velocity = (float)controller.GetShipSpeed(); float proportionalModifier = (float)Math.Pow(Math.Abs(diffrence.Length()), 2); float pitch = NotNaN(Vector3.Dot(diffrence, Vector3.Cross(gravity, controller.WorldMatrix.Right)) * velocity) * proportionalModifier / dampeningFactor; float roll = NotNaN(Vector3.Dot(diffrence, Vector3.Cross(gravity, controller.WorldMatrix.Forward)) * velocity) * proportionalModifier / dampeningFactor; pitch = MinAbs(pitch, 90.0f * degToRad); roll = MinAbs(roll, 90.0f * degToRad); return new Vector2(roll, pitch); } public Vector3 CalculateVelocityToAlign(float offsetPitch = 0.0f, float offsetRoll = 0.0f) { var gravity = -Vector3.Normalize(Vector3.TransformNormal(controller.GetNaturalGravity(), Matrix.Transpose(controller.WorldMatrix))); var target = Vector3.Normalize(Vector3.Transform(gravity, Matrix.CreateFromAxisAngle(Vector3.Right, offsetPitch) * Matrix.CreateFromAxisAngle(Vector3.Forward, offsetRoll))); var pitch = Vector3.Dot(Vector3.Forward, target); var roll = Vector3.Dot(Vector3.Right, target); return new Vector3(pitch, 0, roll); } public void SetAngularVelocity(Vector3 velocity) { var cockpitLocalVelocity = Vector3.TransformNormal(velocity, controller.WorldMatrix); foreach (var gyro in gyroscopes) { var gyroLocalVelocity = Vector3.TransformNormal(cockpitLocalVelocity, Matrix.Transpose(gyro.WorldMatrix)); gyro.Pitch = gyroLocalVelocity.X; gyro.Yaw = gyroLocalVelocity.Y; gyro.Roll = gyroLocalVelocity.Z; } } } public class ThrusterController { private IMyShipController controller; private List<IMyThrust> allThrusters; private List<IMyThrust> upThrusters, downThrusters, leftThrusters, rightThrusters, forwardThrusters, backwardThrusters; public ThrusterController(IMyShipController controller, List<IMyThrust> thrusters) { upThrusters = new List<IMyThrust>(); downThrusters = new List<IMyThrust>(); leftThrusters = new List<IMyThrust>(); rightThrusters = new List<IMyThrust>(); forwardThrusters = new List<IMyThrust>(); backwardThrusters = new List<IMyThrust>(); Update(controller, thrusters); } public void Update(IMyShipController controller, List<IMyThrust> thrusters) { this.controller = controller; this.allThrusters = thrusters.Distinct().ToList(); foreach (var thruster in thrusters) { if (thruster.GridThrustDirection.Z < 0) forwardThrusters.Add(thruster); if (thruster.GridThrustDirection.Z > 0) backwardThrusters.Add(thruster); if (thruster.GridThrustDirection.Y < 0) upThrusters.Add(thruster); if (thruster.GridThrustDirection.Y > 0) downThrusters.Add(thruster); if (thruster.GridThrustDirection.X < 0) leftThrusters.Add(thruster); if (thruster.GridThrustDirection.X > 0) rightThrusters.Add(thruster); thruster.ThrustOverride = 0; } forwardThrusters = forwardThrusters.Distinct().ToList(); backwardThrusters = backwardThrusters.Distinct().ToList(); upThrusters = upThrusters.Distinct().ToList(); downThrusters = downThrusters.Distinct().ToList(); leftThrusters = leftThrusters.Distinct().ToList(); rightThrusters = rightThrusters.Distinct().ToList(); } public void SetEnabled(bool enabled) { foreach (var thruster in allThrusters) { thruster.Enabled = enabled; } } public float SetZAxisThrust(float thrust) { return setAxisThrust(thrust, ref forwardThrusters, ref backwardThrusters); } public float SetYAxisThrust(float thrust) { return setAxisThrust(thrust, ref upThrusters, ref downThrusters); } public float SetXAxisThrust(float thrust) { return setAxisThrust(thrust, ref leftThrusters, ref rightThrusters); } public float CalculateMaxEffectiveForwardThrust() { return calculateMaxAxisThrust(ref forwardThrusters); } public float CalculateMaxEffectiveBackwardThrust() { return calculateMaxAxisThrust(ref backwardThrusters); } public float CalculateMaxEffectiveLeftThrust() { return calculateMaxAxisThrust(ref leftThrusters); } public float CalculateMaxEffectiveRightThrust() { return calculateMaxAxisThrust(ref rightThrusters); } public float CalculateMaxEffectiveUpThrust() { return calculateMaxAxisThrust(ref upThrusters); } public float CalculateMaxEffectiveDownThrust() { return calculateMaxAxisThrust(ref downThrusters); } public float CalculateThrustToHover() { var gravityDir = controller.GetNaturalGravity(); var weight = controller.CalculateShipMass().PhysicalMass * gravityDir.Length(); var velocity = controller.GetShipVelocities().LinearVelocity; gravityDir.Normalize(); var gravityMatrix = Matrix.Invert(Matrix.CreateFromDir(gravityDir)); velocity = Vector3D.Transform(velocity, gravityMatrix); if (Vector3.Transform(controller.WorldMatrix.GetOrientation().Down, gravityMatrix).Z < 0) return (float)(weight + weight * -velocity.Z); else return -(float)(weight + weight * -velocity.Z); } private float calculateMaxAxisThrust(ref List<IMyThrust> thrusters) { float thrust = 0; foreach (var thruster in thrusters) { thrust += thruster.MaxEffectiveThrust; } return thrust; } private float calculateEffectiveThustRatio(IMyThrust thruster) { return thruster.MaxThrust / thruster.MaxEffectiveThrust; } private float setAxisThrust(float thrust, ref List<IMyThrust> thrustersPos, ref List<IMyThrust> thrustersNeg) { List<IMyThrust> thrusters, backThrusters; if (thrust >= 0) { thrusters = thrustersPos; backThrusters = thrustersNeg; } else { thrusters = thrustersNeg; backThrusters = thrustersPos; } thrust = Math.Abs(thrust); foreach (var thruster in backThrusters) { thruster.ThrustOverride = 0.0f; } foreach (var thruster in thrusters) { //TODO: replace with smart thruster thrust allocation code. var localThrust = (thrust / thrusters.Count) * calculateEffectiveThustRatio(thruster); thruster.ThrustOverride = (float.IsNaN(localThrust) || float.IsInfinity(localThrust)) ? 0 : localThrust; } return 0.0f; } } const float degToRad = (float)Math.PI / 180; const float radToDeg = 180.0f / (float)Math.PI; public static bool IsEqual(float value1, float value2, float epsilon = 0.0001f) { return Math.Abs(NotNaN(value1 - value2)) <= epsilon; } public static float NotNaN(float value) { return float.IsNaN(value) ? 0 : value; } public static float MinAbs(float value1, float value2) { return Math.Min(Math.Abs(value1), Math.Abs(value2)) * (value1 < 0 ? -1 : 1); } class ConfigSection { Dictionary<string, string> config; string name; public ConfigSection(string name) { this.config = new Dictionary<string, string>(); this.name = name; } public void Read(string text) { config.Clear(); var ini = new MyIni(); MyIniParseResult parseResult; if (!ini.TryParse(text, out parseResult)) throw new Exception("Failed To Read Config: " + parseResult.Error + " on line" + parseResult.LineNo.ToString()); var keys = new List<MyIniKey>(); ini.GetKeys(name, keys); foreach (var key in keys) { config.Add(key.Name, ini.Get(key).ToString()); } } public string write() { MyIni ini = new MyIni(); ini.AddSection(name); foreach (var kv in config) { ini.Set(name, kv.Key, kv.Value); } return ini.ToString(); } public T Get<T>(string key, T value) { if (!config.ContainsKey(key)) { config.Add(key, value.ToString()); return value; } string result; config.TryGetValue(key, out result); return (T)Convert.ChangeType(result, typeof(T)); } } shutdown HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorSlope2Tip CarbonFibre_Armor SmallBlockArmorHalfSlopeCorner CarbonFibre_Armor SmallBlockArmorSlope2Tip CarbonFibre_Armor SmallBlockArmorHalfSlopeCorner CarbonFibre_Armor HalfSlopeArmorBlock CarbonFibre_Armor SmallBlockArmorCorner2Tip CarbonFibre_Armor SmallBlockArmorCorner2Tip CarbonFibre_Armor SmallBlockArmorCorner2Tip CarbonFibre_Armor SmallBlockArmorCorner2Tip CarbonFibre_Armor SmallBlockArmorCorner2Tip CarbonFibre_Armor SmallBlockArmorCorner2Tip CarbonFibre_Armor SmallBlockArmorCorner2Tip CarbonFibre_Armor SmallBlockArmorCorner2Tip CarbonFibre_Armor [Loki] [Spine] Batteries -6 16 -19 -6 16 -18 -6 16 -17 -6 16 -16 -6 16 -15 -6 16 -14 -6 16 -13 -6 16 -12 -6 16 -11 [Loki] [Spine] Merge Blocks -6 15 -11 -6 15 -2 [Loki] [Spine] Heli-Control -6 17 -4 -6 18 -21 -6 16 -7 -6 16 -21 [Loki] [Spine] Heli Module true false false false 0 None 0 76561198406231217 0