﻿
// Isy's Solar Alignment Script
// ============================
// Version: 4.3.5
// Date: 2021-10-24

// =======================================================================================
//                                                                            --- Configuration ---
// =======================================================================================

// --- Essential Configuration ---
// =======================================================================================

// Name of the group with all the solar related rotors (not needed in gyro mode)
string rotorGroupName = "Solar Rotors";

// By enabling gyro mode, the script will no longer use rotors but all gyroscopes on the grid instead.
// This mode only makes sense when used on a SHIP in SPACE. Gyro mode deactivates the following
// features: night mode, rotate to sunrise, time calculation and triggering external timer blocks.
bool useGyroMode = false;

// Name of the reference group for gyro mode. Put your main cockpit, flight seat or remote control in this group!
string referenceGroupName = "Solar Reference";


// --- Rotate to sunrise ---
// =======================================================================================

// Rotate the panels towards the sunrise during the night? (Possible values: true | false, default: true)
// The angle is figured out automatically based on the first lock of the day.
// If you want to set the angles yourself, set manualAngle to true and adjust the angles to your likings.
bool rotateToSunrise = true;
bool manualAngle = false;
int manualAngleVertical = 0;
int manualAngleHorizontal = 0;


// --- Power fallback ---
// =======================================================================================

// With this option, you can enable your reactors and hydrogen engines as a safety fallback, if not enough power is available
// to power all your machines or if the battery charge gets low. By default, all reactors and hydrogen engines
// on the same grid will be used. If you only want to use specific ones, put their names or group in the list.
// Example: string[] fallbackDevices = { "Small Reactor 1", "Base reactor group", "Hydrogen Engine" };
bool useReactorFallback = false;
bool useHydrogenEngineFallback = false;
string[] fallbackDevices = { };

// Activation order
// By default, the hydrogen engine will be turned on first and the reactors after that if still not enough power is available.
// Set this value to false, and the reactors will be used first.
bool activateHydrogenEngineFirst = true;

// Activation on low battery?
// The fallback devices will be active until 'turnOffAtPercent' of the max battery charge after it was turned on at 'turnOnAtPercent'.
bool activateOnLowBattery = true;
double turnOnAtPercent = 10;
double turnOffAtPercent = 15;

// Activate on overload?
// If the combined output of batteries, solar panels and wind turbines is more than 'overloadPercentage' of their max output, the fallback devices will be turned on.
bool activateOnOverload = true;
double overloadPercentage = 90;


// --- Base Light Management ---
// =======================================================================================

// Enable base light management? (Possible values: true | false, default: false)
// Lights will be turned on/off based on daytime.
bool baseLightManagement = false;

// Simple mode: toggle lights based on max. solar output (percentage). Time based toggle will be deactivated.
bool simpleMode = false;
int simpleThreshold = 50;

// Define the times when your lights should be turned on or off. If simple mode is active, this does nothing.
int lightOffHour = 8;
int lightOnHour = 18;

// To only toggle specific lights, declare groups for them.
// Example: string[] baseLightGroups = { "Interior Lights", "Spotlights", "Hangar Lights" };
string[] baseLightGroups = { };


// --- LCD panels ---
// =======================================================================================

// To display the main script informations, add the following keyword to any LCD name (default: !ISA-main).
// You can enable or disable specific informations on the LCD by editing its custom data.
string mainLcdKeyword = "ISA-main";

// To display compact stats (made for small screens, add the following keyword to any LCD name (default: !ISA-compact).
string compactLcdKeyword = "ISA-compact";

// To display all current warnings and problems, add the following keyword to any LCD name (default: !ISA-warnings).
string warningsLcdKeyword = "ISA-warnings";

// To display the script performance, add the following keyword to any LCD name (default: !ISA-performance).
string performanceLcdKeyword = "ISA-performance";

// Default screen font, fontsize and padding, when a screen is first initialized. Fonts: "Debug" or "Monospace"
string defaultFont = "Debug";
float defaultFontSize = 0.6f;
float defaultPadding = 2f;


// --- Terminal statistics ---
// =======================================================================================

// The script can display informations in the names of the used blocks. The shown information is a percentage of
// the current efficiency (solar panels and oxygen farms) or the fill level (batteries and tanks).
// You can enable or disable single statistics or disable all using the master switch below.
bool enableTerminalStatistics = true;

bool showSolarStats = true;
bool showWindTurbineStats = true;
bool showBatteryStats = true;
bool showOxygenFarmStats = true;
bool showOxygenTankStats = true;


// --- External timer blocks ---
// =======================================================================================

// Trigger external timer blocks at specific events? (action "Start" is used with a delay > 1; with a delay of 1 "Trigger Now")
// Events can be:
// - any commandline argument, like "pause", "resume", "realign"...
// - "battery##" (a battery charge percentage - ## stands for a battery charge percentage, like "battery30" for 30 percent charge)
// - "badweather" and "goodweather" which will trigger at the beginning and end of bad weather
// - "sunrise" and "sunset" which will trigger at the beginning and end of the day
// - a time like "15:00"
// - a number for every X seconds
// Every event needs a timer block name in the exact same order as the events.
// Calling the same timer block with multiple events requires it's name multiple times in the timers list!
// Example:
// string[] events = { "sunrise", "sunset", "15:00", "battery30" };
// string[] timers = { "Timer 1", "Timer 1", "Timer 2", "Timer 3" };
// This will trigger "Timer 1" at sunrise and sunset, "Timer 2" at 15:00 and "Timer 3" at 30% battery charge.
string[] events = { };
string[] timers = { };


// --- Settings for enthusiasts ---
// =======================================================================================

// Use weather detection? This needs solar panels and wind turbines to work and will pause the script in certain weather
// conditions, like fog or storms. The variable 'maxBadWeatherMinutes' declares the max duration, the alignment is slowed down
// in bad weather conditions.
bool useWeatherDetection = true;
double maxBadWeatherMinutes = 5;

// Check the integrity and general condition of all used blocks?
bool checkIntegrity = true;

// Output difference in percent between the last locked output and a new alignment: lower values = more frequent alignment (defaults: rotor: 1, gyro: 2)
double realignPercentageRotor = 1;
double realignPercentageGyro = 2;

// Percentage of the max detected output where the script starts night mode (default: 10)
double nightPercentage = 10;

// Percentage of the max detected output where the script detects night for time calculation (default: 50)
double nightTimePercentage = 50;

// Rotor speeds (speeds are automatically scaled between these values based on the output)
float rotorMinSpeed = 0.05f;
float rotorMaxSpeed = 1.0f;

// Rotor options
float rotorTorqueLarge = 33600000f;
float rotorTorqueSmall = 448000f;
bool setInertiaTensor = true;
bool setRotorLockWhenStopped = false;

// Min gyro RPM, max gyro RPM and gyro power for gyro mode
const double minGyroRPM = 0.1;
const double maxGyroRPM = 1;
const float gyroPower = 1f;

// Automatically pause aligning while using gyro mode and sitting in the cockpit?
bool autoPauseGyroMode = true;


// =======================================================================================
//                                                                      --- End of Configuration ---
//                                                        Don't change anything beyond this point!
// =======================================================================================


float Ǿ=0;float ǿ=0;float Ȁ=0;float Ȃ=0;float Ȍ=0;float ȃ=0;float Ȅ=0;List<IMyMotorStator>ȅ=new List<IMyMotorStator>();
List<IMyMotorStator>Ȇ=new List<IMyMotorStator>();List<IMyMotorStator>ȇ=new List<IMyMotorStator>();List<IMyGyro>Ȉ=new List<
IMyGyro>();List<IMyTerminalBlock>ȉ=new List<IMyTerminalBlock>();List<IMyTerminalBlock>Ȋ=new List<IMyTerminalBlock>();List<
IMyTerminalBlock>ȋ=new List<IMyTerminalBlock>();List<IMyTerminalBlock>ȍ=new List<IMyTerminalBlock>();List<IMyInteriorLight>ȁ=new List<
IMyInteriorLight>();List<IMyReflectorLight>ǽ=new List<IMyReflectorLight>();List<IMyPowerProducer>ǰ=new List<IMyPowerProducer>();int ǧ=0;
int Ǩ=0;int ǩ=0;int Ǫ=0;List<IMySolarPanel>ǫ=new List<IMySolarPanel>();int Ǭ=0;bool ǭ=false;bool Ǯ=false;int ǯ=30;int Ǳ=90;
int ǻ=10;bool ǲ=true;List<string>ǳ=new List<string>{"output=0","outputLast=0","outputLocked=0","outputMax=0",
"outputMaxAngle=0","outputMaxDayBefore=0","outputBestPanel=0","direction=1","directionChanged=0","directionTimer=0","allowRotation=1",
"rotationDone=1","timeSinceRotation=0","firstLockOfDay=0","sunriseAngle=0"};List<IMyShipController>Ǵ=new List<IMyShipController>();
double ǵ=0;double Ƕ=0;double Ƿ=0;double Ǹ=1;double ǹ=1;double Ǻ=1;bool Ǽ=false;bool Ǧ=false;bool Ƥ=false;double Ƽ=0;double ƥ=0
;double Ʀ=0;bool Ƨ=true;bool ƨ=true;bool Ʃ=true;double ƪ=0;double ƫ=0;double Ƭ=0;List<IMyBatteryBlock>ƭ=new List<
IMyBatteryBlock>();float Ư=0;float ƹ=0;float ư=0;float Ʊ=0;float Ʋ=0;float Ƴ=0;List<IMyOxygenFarm>ƴ=new List<IMyOxygenFarm>();List<
IMyGasTank>Ƶ=new List<IMyGasTank>();double ƶ=0;double Ʒ=0;double Ƹ=0;int ƺ=0;List<IMyPowerProducer>ƣ=new List<IMyPowerProducer>();
int ƍ=0;float ơ=0;float Ǝ=0;float Ə=0;bool Ɛ=false;bool Ƒ=false;int ƒ=0;float Ɠ=-1;DateTime Ɣ;string ƕ="0 kW";string Ɩ=
"0 kW";string Ɨ="0 kW";string Ƙ="0 kW";string ƙ="0 kW";string ƚ="0 kW";string ƛ="0 kW";string Ɯ="0 kW";string Ɲ="0 kW";string
ƞ="0 kW";string Ɵ="0 kW";string Ơ="0 kW";string Ʈ="0 L";string Ƣ="0 L";string ƻ="Checking setup...";string ǐ;string Ǒ;
string[]ǒ={"/","-","\\","|"};int Ǔ=0;int ǔ=0;int Ǖ=270;const int ǖ=7200;int Ǘ=ǖ;const int ǘ=ǖ/2;int Ǚ=ǘ;string[]š={
"showHeading=true","showWarnings=true","showCurrentOperation=true","showSolarStats=true","showTurbineStats=true","showBatteryStats=true",
"showOxygenStats=true","showLocationTime=true","scrollTextIfNeeded=true"};string[]Ǥ={"showSolarStats=false","showTurbineStats=false",
"showBatteryStats=false","showOxygenStats=false","showLocationTime=false","showRealTime=false","scrollTextIfNeeded=false"};string[]Ǜ={
"showHeading=true","scrollTextIfNeeded=true"};bool ǜ=false;bool ǝ=false;bool Ǟ=false;int ǟ=0;int Ǡ=0;double ǡ=0;string Ǣ="";bool ǣ=false;
string ǥ="";string ǚ="";string Ĕ;int ǆ=0;int ƽ=0;bool ƾ=false;HashSet<string>ƿ=new HashSet<string>();HashSet<string>ǀ=new
HashSet<string>();string ǁ="";bool ǂ=false;string ǃ="both";bool Ǆ=false;double ǅ=0;double Ǉ=0;int Ǐ=0;int ǈ=0;int ǉ=1;bool Ǌ=
true;bool ǋ=true;int ǌ=0;string[]Ǎ={"Get blocks","Get block stats","Time Calculation","Rotation Logic","Reactor Fallback"};
Program(){Ú();realignPercentageRotor=(realignPercentageRotor%100)/100;realignPercentageGyro=(realignPercentageGyro%100)/100;
nightPercentage=(nightPercentage%100)/100;nightTimePercentage=(nightTimePercentage%100)/100;Runtime.UpdateFrequency=UpdateFrequency.
Update10;}void Main(string ǎ){if(ƽ>=10){throw new Exception("Too many errors. Please recompile!\n\nScript stoppped!\n");}try{if(
ǎ!=""){if(!ɖ(ǎ.ToLower()))R("Unknown argument!");ǥ=ǎ;ǉ=0;}if(Ǌ){Ȏ();ȶ(false);h();Ǌ=false;}if(ǈ<Ǐ){ǈ++;return;}if(ǋ){if(ǌ
==0)Ė();if(ǌ==1)ė();if(ǌ==2)Ē();if(ǌ==3)ê();if(ǌ>3)ǌ=0;ǋ=false;return;}if(ǉ==0||ƾ){Ȏ();ƾ=false;if(ǉ==0){Ų(Ǎ[ǉ]);e();ǉ++;}
return;}ǈ=0;ǋ=true;if(ǉ==1){ɛ();if(checkIntegrity)ɟ();if(!useGyroMode&&useWeatherDetection)ɚ();}if(ǉ==2){if(!useGyroMode)f();
if(!useGyroMode&&baseLightManagement)H();if(ǣ)M();}if(ǉ==3){if(!ɖ(ǁ)){if(useGyroMode){ɞ();}else{Ƞ();}}}if(ǉ==4){if(
useReactorFallback||useHydrogenEngineFallback)C();foreach(var Đ in ȅ){double ý=ɤ(Đ,"output");ɥ(Đ,"outputLast",ý);}Ȃ=Ȁ;Save();}Ų(Ǎ[ǉ]);e();
if(ǉ>=4){ǉ=0;if(ǆ>0){ƿ=new HashSet<string>(ǀ);ǀ.Clear();if(ƽ>0)ƽ--;if(ƿ.Count==0)Ĕ=null;ǆ=0;}else{ǆ++;}Ǒ=ǐ;ǐ="";}else{ǉ++;
}Ǔ=Ǔ>=3?0:Ǔ+1;}catch(NullReferenceException){ƽ++;ƾ=true;R("Execution of script step aborted:\n"+Ǎ[ǉ]+" (ID: "+ǉ+
")\n\nCached block not available..");}catch(Exception e){ƽ++;ƾ=true;R("Critical error in script step:\n"+Ǎ[ǉ]+" (ID: "+ǉ+")\n\n"+e);}}void Ȏ(){if(Ŵ==null){
Ŷ(Me.CubeGrid);}if(!useGyroMode){var Ɍ=GridTerminalSystem.GetBlockGroupWithName(rotorGroupName);if(Ɍ!=null){Ɍ.
GetBlocksOfType<IMyMotorStator>(ȅ);if(ȅ.Count==0){R("There are no rotors in the rotor group:\n'"+rotorGroupName+"'");}}else{R(
"Rotor group not found:\n'"+rotorGroupName+"'");}HashSet<IMyCubeGrid>ɍ=new HashSet<IMyCubeGrid>();foreach(var Đ in ȅ){if(!Đ.IsFunctional)R("'"+Đ.
CustomName+"' is broken!\nRepair it to use it for aligning!");if(!Đ.Enabled)R("'"+Đ.CustomName+
"' is turned off!\nTurn it on to use it for aligning!");if(!Đ.IsAttached)R("'"+Đ.CustomName+"' has no rotor head!\nAdd one to use it for aligning!");ɍ.Add(Đ.CubeGrid);if(Đ.
CubeGrid.GridSize==0.5){Đ.Torque=rotorTorqueSmall;}else{Đ.Torque=rotorTorqueLarge;}if(Đ.GetOwnerFactionTag()!=Me.
GetOwnerFactionTag()){R("'"+Đ.CustomName+"' has a different owner / faction!\nAll blocks should have the same owner / faction!");}}Ȇ.Clear
();ȇ.Clear();foreach(var Đ in ȅ){if(ɍ.Contains(Đ.TopGrid)){Ȇ.Add(Đ);if(Đ.CubeGrid==Ŵ&&setInertiaTensor){try{Đ.
SetValueBool("ShareInertiaTensor",false);}catch(Exception){}}}else{ȇ.Add(Đ);if(Đ.CubeGrid!=Ŵ&&setInertiaTensor){try{Đ.SetValueBool(
"ShareInertiaTensor",true);}catch(Exception){}}}}List<IMyMotorStator>Ɏ=new List<IMyMotorStator>();Ɏ.AddRange(ȇ);ȇ.Clear();bool ɏ;foreach(var
ɐ in Ɏ){ɏ=true;foreach(var Đ in ȇ){if(Đ.TopGrid==ɐ.TopGrid){ɐ.RotorLock=false;ɐ.TargetVelocityRPM=0f;ɐ.Torque=0f;ɐ.
BrakingTorque=0f;ɏ=false;break;}}if(ɏ)ȇ.Add(ɐ);}ǫ.Clear();ƴ.Clear();foreach(var Ț in ȇ){Ǿ=0;ǿ=0;Ŗ(Ț.TopGrid,true);var ɑ=new List<
IMySolarPanel>();GridTerminalSystem.GetBlocksOfType<IMySolarPanel>(ɑ,ɒ=>Ş.Contains(ɒ.CubeGrid)&&ɒ.Enabled);var ɕ=new List<
IMyOxygenFarm>();GridTerminalSystem.GetBlocksOfType<IMyOxygenFarm>(ɕ,ɓ=>Ş.Contains(ɓ.CubeGrid));foreach(var U in ɑ){ǫ.Add(U);Ǿ+=U.
MaxOutput;if(U.MaxOutput>ǿ)ǿ=U.MaxOutput;}foreach(var Ø in ɕ){ƴ.Add(Ø);Ǿ+=Ø.GetOutput();if(Ø.GetOutput()>ǿ)ǿ=Ø.GetOutput();}if(ɑ.
Count==0&&ɕ.Count==0){R("'"+Ț.CustomName+"' can't see the sun!\nAdd a solar panel or oxygen farm to it!");}ɥ(Ț,"output",Ǿ);ɥ(
Ț,"outputBestPanel",ǿ);if(Ǿ>ɤ(Ț,"outputMax")){ɥ(Ț,"outputMax",Ǿ);ɥ(Ț,"outputMaxAngle",Ď(Ț));}}foreach(var û in Ȇ){double
ü=0;ǿ=float.MaxValue;foreach(var Ț in ȇ){if(Ț.CubeGrid==û.TopGrid){ü+=ɤ(Ț,"output");if(ɤ(Ț,"outputBestPanel")<ǿ)ǿ=(float)
ɤ(Ț,"outputBestPanel");}}ɥ(û,"output",ü);ɥ(û,"outputBestPanel",ǿ);if(ü>ɤ(û,"outputMax")){ɥ(û,"outputMax",ü);ɥ(û,
"outputMaxAngle",Ď(û));}}}if(useGyroMode){if(Me.CubeGrid.IsStatic){R("The grid is stationary!\nConvert it to a ship in the Info tab!");}
var ɔ=GridTerminalSystem.GetBlockGroupWithName(referenceGroupName);if(ɔ!=null){ɔ.GetBlocksOfType<IMyShipController>(Ǵ);if(Ǵ
.Count==0){R("There are no cockpits, flight seats or remote controls in the reference group:\n'"+referenceGroupName+"'");
}}else{R("Reference group not found!\nPut your main cockpit, flight seat or remote control in a group called '"+
referenceGroupName+"'!");}GridTerminalSystem.GetBlocksOfType<IMyGyro>(Ȉ,ɀ=>ɀ.IsSameConstructAs(Me)&&ɀ.Enabled);if(Ȉ.Count==0){R(
"No gyroscopes found!\nAre they enabled and completely built?");}GridTerminalSystem.GetBlocksOfType<IMySolarPanel>(ǫ,ɒ=>ɒ.IsSameConstructAs(Me)&&ɒ.Enabled);GridTerminalSystem.
GetBlocksOfType<IMyOxygenFarm>(ƴ,ɓ=>ɓ.IsSameConstructAs(Me));}if(Ǭ!=ǫ.Count){ɉ("solar panels");}else if(ƺ!=ƴ.Count){ɉ("oxygen farms");}
if(ǫ.Count==0&&ƴ.Count==0){R("No solar panels or oxygen farms found!\nHow should I see the sun now?");}ƭ.Clear();
GridTerminalSystem.GetBlocksOfType<IMyBatteryBlock>(ƭ,ŧ=>ŧ.IsSameConstructAs(Me)&&ŧ.Enabled);if(ƭ.Count==0){R(
"No batteries found!\nDon't you want to store your Power?");}ƣ.Clear();GridTerminalSystem.GetBlocksOfType<IMyPowerProducer>(ƣ,ɂ=>ɂ.BlockDefinition.TypeIdString.Contains(
"WindTurbine")&&ɂ.IsSameConstructAs(Me)&&ɂ.Enabled);if(ƍ!=ƣ.Count){ƍ=ƣ.Count;ɉ("wind turbines");}Ƶ.Clear();GridTerminalSystem.
GetBlocksOfType<IMyGasTank>(Ƶ,ɂ=>!ɂ.BlockDefinition.SubtypeId.Contains("Hydrogen")&&ɂ.IsSameConstructAs(Me)&&ɂ.Enabled);if(
useReactorFallback||useHydrogenEngineFallback){ǰ.Clear();foreach(var ų in fallbackDevices){var Ⱦ=GridTerminalSystem.GetBlockGroupWithName(
ų);if(Ⱦ!=null){var ȿ=new List<IMyPowerProducer>();Ⱦ.GetBlocksOfType<IMyPowerProducer>(ȿ,ɀ=>ɀ.BlockDefinition.TypeIdString
.Contains("Reactor")||ɀ.BlockDefinition.TypeIdString.Contains("HydrogenEngine"));ǰ.AddRange(ȿ);}else{IMyPowerProducer Ɂ=
GridTerminalSystem.GetBlockWithName(ų)as IMyPowerProducer;if(Ɂ!=null){ǰ.Add(Ɂ);}else{R("Power fallback device not found:\n'"+ų+"'");}}}if(
ǰ.Count==0){GridTerminalSystem.GetBlocksOfType<IMyPowerProducer>(ǰ,ɀ=>(ɀ.BlockDefinition.TypeIdString.Contains("Reactor")
||ɀ.BlockDefinition.TypeIdString.Contains("HydrogenEngine"))&&ɀ.IsSameConstructAs(Me));}if(!useReactorFallback)ǰ.RemoveAll
(ɀ=>ɀ.BlockDefinition.TypeIdString.Contains("Reactor"));if(!useHydrogenEngineFallback)ǰ.RemoveAll(ɀ=>ɀ.BlockDefinition.
TypeIdString.Contains("HydrogenEngine"));ǟ=ǰ.Count(ɀ=>ɀ.BlockDefinition.TypeIdString.Contains("Reactor"));Ǡ=ǰ.Count(ɀ=>ɀ.
BlockDefinition.TypeIdString.Contains("HydrogenEngine"));}foreach(var Q in timers){ǣ=true;var Ƀ=GridTerminalSystem.GetBlockWithName(Q)
as IMyTimerBlock;if(Ƀ==null){R("Timer block not found:\n'"+Q+"'\nTimer functions disabled!");ǣ=false;break;}}if(
baseLightManagement){ȁ.Clear();ǽ.Clear();if(baseLightGroups.Length>0){var Ʉ=new List<IMyInteriorLight>();var Ʌ=new List<IMyReflectorLight>(
);foreach(var Ɇ in baseLightGroups){var ɇ=GridTerminalSystem.GetBlockGroupWithName(Ɇ);if(ɇ!=null){ɇ.GetBlocksOfType<
IMyInteriorLight>(Ʉ);ȁ.AddRange(Ʉ);ɇ.GetBlocksOfType<IMyReflectorLight>(Ʌ);ǽ.AddRange(Ʌ);}else{R("Light group not found:\n'"+Ɇ+"'");}}}
else{GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(ȁ,Ɉ=>Ɉ.IsSameConstructAs(Me));GridTerminalSystem.GetBlocksOfType<
IMyReflectorLight>(ǽ,Ɉ=>Ɉ.IsSameConstructAs(Me));}}ȉ=Š(mainLcdKeyword,š,defaultFont,defaultFontSize,defaultPadding);Ȋ=Š(compactLcdKeyword
,Ǥ,defaultFont,defaultFontSize,defaultPadding);ȋ=Š(warningsLcdKeyword,Ǜ,defaultFont,defaultFontSize,defaultPadding);ȍ=Š(
performanceLcdKeyword,Ǜ,defaultFont,defaultFontSize,defaultPadding);}void ɉ(string Ɋ,bool Ƚ=true){foreach(var Đ in ȅ){ɥ(Đ,"outputMax",0);}Ȍ=0
;ȃ=0;Ǭ=ǫ.Count;ƺ=ƴ.Count;if(Ƚ)R("Amount of "+Ɋ+" has changed!\nResetting stored max values..");}bool ɖ(string ɠ){if(ɠ==
"pause"){ȶ();if(ǂ){ǁ="";ǂ=false;ƻ="Resuming..";}else{ǁ="paused";ǂ=true;}}else if(ɠ=="stop"){ȶ();ǂ=true;ǁ="paused";}else if(ɠ==
"resume"){ǂ=false;ǁ="";ƻ="Resuming..";}else if(ɠ=="paused"){}else if(ɠ=="realign"&&!useGyroMode){Ȭ();ƻ=
"Forced realign by user.\n";ƻ+="Searching highest output for "+Ǳ+" more seconds.";if(Ǳ==0){ǁ="resume";Ǳ=90;}else{ǁ="realign";Ǳ-=1;}}else if(ɠ==
"realign"&&useGyroMode){ƻ="Forced realign by user.\n";ƻ+="Resetted stored values and starting to pitch.";ȵ();ǵ=0;ƪ=999;Ƨ=true;Ƿ=0
;Ƕ=0;Ȍ=0;ǁ="";}else if(ɠ=="reset"&&!useGyroMode){ǔ=0;Ǖ=270;Ǚ=ǘ;Ǘ=ǖ;R("Calculated time resetted.");}else if(ɠ.Contains(
"rotate")&&!useGyroMode){String[]ɡ=ɠ.Split(' ');bool ɢ=false;ǃ="both";Ǆ=false;if(ɡ[0].Contains("pause"))Ǆ=true;if(ɡ.Length==2){
if(ɡ[1].Contains("h")){ɢ=Double.TryParse(ɡ[1].Replace("h",""),out ǅ);ǃ="horizontalOnly";}else if(ɡ[1].Contains("v")){ɢ=
Double.TryParse(ɡ[1].Replace("v",""),out Ǉ);ǃ="verticalOnly";}if(ɢ){ƻ="Checking rotation parameters...";ǁ="rotNormal";}else{ȶ(
);R("Wrong format!\n\nShould be:\nrotate h90 OR\nrotate v90");}}else if(ɡ.Length==3){string ɣ="rotNormal";if(ɡ[1].
Contains("v")){ɢ=Double.TryParse(ɡ[1].Replace("v",""),out Ǉ);if(ɢ)ɢ=Double.TryParse(ɡ[2].Replace("h",""),out ǅ);ɣ="rotVH1";}else
{ɢ=Double.TryParse(ɡ[1].Replace("h",""),out ǅ);if(ɢ)ɢ=Double.TryParse(ɡ[2].Replace("v",""),out Ǉ);}if(ɢ){ƻ=
"Checking rotation parameters...";ǁ=ɣ;}else{ȶ();R("Wrong format!\n\nShould be:\nrotate h90 v90 OR\nrotate v90 h90");}}else{ȶ();R(
"Not enough parameters!\n\nShould be 2 or 3:\nrotate h90 OR\nrotate h90 v90");}}else if(ɠ=="rotNormal"){ƻ="Rotating to user defined values...";bool ȭ=Ⱥ(ǃ,ǅ,Ǉ);if(ȭ&&Ǆ){ǁ="pause";}else if(ȭ&&!Ǆ){ǁ=
"resume";}}else if(ɠ=="rotVH1"){ƻ="Rotating to user defined values...";bool ȭ=Ⱥ("verticalOnly",ǅ,Ǉ);if(ȭ)ǁ="rotVH2";}else if(ɠ==
"rotVH2"){ƻ="Rotating to user defined values...";bool ȭ=Ⱥ("horizontalOnly",ǅ,Ǉ);if(ȭ&&Ǆ){ǁ="pause";}else if(ȭ&&!Ǆ){ǁ="resume";}}
else{return false;}if(ǁ=="paused"){ƻ="Automatic alignment paused.\n";ƻ+="Run 'pause' again or 'resume' to continue..";}
return true;}double ɤ(IMyTerminalBlock ć,string ŉ){ɦ(ć);var Ħ=ć.CustomData.Split('\n').ToList();int ĥ=Ħ.FindIndex(P=>P.
StartsWith(ŉ+"="));if(ĥ>-1){return Convert.ToDouble(Ħ[ĥ].Replace(ŉ+"=",""));}return 0;}void ɥ(IMyTerminalBlock ć,string ŉ,double è
){ɦ(ć);var Ħ=ć.CustomData.Split('\n').ToList();int ĥ=Ħ.FindIndex(P=>P.StartsWith(ŉ+"="));if(ĥ>-1){Ħ[ĥ]=ŉ+"="+è;ć.
CustomData=String.Join("\n",Ħ);}}void ɦ(IMyTerminalBlock ć){var Ħ=ć.CustomData.Split('\n').ToList();if(Ħ.Count!=ǳ.Count){ć.
CustomData=String.Join("\n",ǳ);}}void ɟ(){foreach(var U in ǫ){if(!U.CubeGrid.GetCubeBlock(U.Position).IsFullIntegrity){R(U.
CustomName+" is damaged! Repair it for maximum solar power!");}}foreach(var ɗ in ƣ){if(!ɗ.CubeGrid.GetCubeBlock(ɗ.Position).
IsFullIntegrity){R(ɗ.CustomName+" is damaged! Repair it for maximum wind power!");}}foreach(var Ø in ƴ){if(!Ø.CubeGrid.GetCubeBlock(Ø.
Position).IsFullIntegrity){R(Ø.CustomName+" is damaged! Repair it for maximum oxygen production!");}}foreach(var º in ƭ){if(!º.
CubeGrid.GetCubeBlock(º.Position).IsFullIntegrity){R(º.CustomName+" is damaged! Repair it for maximum power storage!");}if(º.
CurrentStoredPower==0){R(º.CustomName+" is completely empty! Build more solar panels or wind turbines to keep it filled!");}}if(!
useGyroMode){foreach(var Đ in ȅ){if(!Đ.CubeGrid.GetCubeBlock(Đ.Position).IsFullIntegrity){R(Đ.CustomName+
" is damaged! Repair it to align your towers!");}try{if(Đ.CubeGrid==Ŵ&&Đ.GetValueBool("ShareInertiaTensor")==true){R(Đ.CustomName+" has intertia tensor enabled! Rotors directly attached to the base should never have it enabled because they are too weak to move the solar tower that way!"
);}}catch{}}}if(useGyroMode){foreach(var Ȳ in Ȉ){if(!Ȳ.CubeGrid.GetCubeBlock(Ȳ.Position).IsFullIntegrity){R(Ȳ.CustomName+
" is damaged! Repair it for maximum alignment force!");}}}if(useReactorFallback||useHydrogenEngineFallback){foreach(var ɘ in ǰ){if(!ɘ.CubeGrid.GetCubeBlock(ɘ.Position).
IsFullIntegrity){R(ɘ.CustomName+" is damaged! Repair it for maximum emergency power!");}if(ɘ is IMyReactor){if(ɘ.GetInventory(0).
CurrentVolume==0){R(ɘ.CustomName+" has no fuel! Fill it for maximum emergency power!");}}else{if(ɘ.DetailedInfo.Contains(
"Filled: 0.0%")){R(ɘ.CustomName+" has no fuel! Fill it for maximum emergency power!");}}}}if(baseLightManagement){foreach(var K in ȁ){
if(!K.CubeGrid.GetCubeBlock(K.Position).IsFullIntegrity){R(K.CustomName+" is damaged! Repair it for maximum illumination!"
);}}foreach(var ə in ǽ){if(!ə.CubeGrid.GetCubeBlock(ə.Position).IsFullIntegrity){R(ə.CustomName+
" is damaged! Repair it for maximum illumination!");}}}}void ɚ(){if(ƣ.Count==0)return;if(Ȁ==0){if(Ɛ){ǁ="resume";Ɛ=false;ƒ=0;ǥ="goodweather";}return;}Ƒ=false;if(Ɛ){if((
DateTime.Now-Ɣ).TotalMinutes>maxBadWeatherMinutes){Ƒ=true;}}if(Ǝ>ȃ*1.4){R("There's a storm. Solar panel alignment slowed down.")
;Ɛ=true;}else if(Ǝ>=ȃ*1.05&&Ȁ<=Ȍ*0.9){R("It's raining. Solar panel alignment slowed down.");Ɛ=true;}else if(Ǝ<=ȃ*0.55){R(
"It's foggy or snowing. Solar panel alignment slowed down.");Ɛ=true;}else if(Ɛ){Ƒ=true;}if(Ƒ){ɉ("",false);Ɛ=false;ƒ=3;rotorMaxSpeed=Ɠ;Ɠ=-1;ǥ="goodweather";}if(Ɛ&&Ɠ==-1){Ɣ=DateTime
.Now;Ɠ=rotorMaxSpeed;rotorMaxSpeed=rotorMinSpeed;ǥ="badweather";}}void ɛ(){Ȁ=0;Ȅ=0;foreach(var U in ǫ){Ȁ+=U.MaxOutput;Ȅ+=
U.CurrentOutput;if(showSolarStats&&enableTerminalStatistics){double ɜ;double.TryParse(U.CustomData,out ɜ);if(ɜ<U.
MaxOutput){ɜ=U.MaxOutput;U.CustomData=ɜ.ToString();}Ć(U,true,"",U.MaxOutput,ɜ);}}foreach(var Ø in ƴ){Ȁ+=Ø.GetOutput()/1000000;}ơ=
0;Ǝ=0;if(ƣ.Count>0)Ə=ƣ.Max(ɂ=>ɂ.MaxOutput);foreach(var ɝ in ƣ){ơ+=ɝ.CurrentOutput;Ǝ+=ɝ.MaxOutput;if(showWindTurbineStats
&&enableTerminalStatistics){Ć(ɝ,true,"",ɝ.MaxOutput,Ə);}}if(Ȁ>Ȍ){Ȍ=Ȁ;ȃ=Ǝ;}Ɨ=Ȅ.Ń();ƕ=Ȁ.Ń();Ɩ=Ȍ.Ń();ƞ=ơ.Ń();Ɵ=Ǝ.Ń();Ơ=ȃ.Ń();
Ư=0;ƹ=0;ư=0;Ʊ=0;Ʋ=0;Ƴ=0;foreach(var º in ƭ){Ư+=º.CurrentInput;ƹ+=º.MaxInput;ư+=º.CurrentOutput;Ʊ+=º.MaxOutput;Ʋ+=º.
CurrentStoredPower;Ƴ+=º.MaxStoredPower;if(showBatteryStats&&enableTerminalStatistics){string ĉ="";if(º.CurrentStoredPower<º.MaxStoredPower
*0.99){ĉ="Draining";if(º.CurrentInput>º.CurrentOutput)ĉ="Recharging";}Ć(º,true,ĉ,º.CurrentStoredPower,º.MaxStoredPower);}
}Ƙ=Ư.Ń();ƙ=ƹ.Ń();ƚ=ư.Ń();ƛ=Ʊ.Ń();Ɯ=Ʋ.Ń(true);Ɲ=Ƴ.Ń(true);ƶ=0;Ʒ=0;Ƹ=0;foreach(var Ø in ƴ){ƶ+=Ø.GetOutput();if(
showOxygenFarmStats&&enableTerminalStatistics){Ć(Ø,true,"",Ø.GetOutput(),1);}}ƶ=Math.Round(ƶ/ƴ.Count*100,2);foreach(var Ù in Ƶ){Ʒ+=Ù.
Capacity;Ƹ+=Ù.Capacity*Ù.FilledRatio;if(showOxygenTankStats&&enableTerminalStatistics){Ć(Ù,true,"",Ù.FilledRatio,1);}}Ʈ=Ʒ.Ň();Ƣ=
Ƹ.Ň();}void ɞ(){if(Ȉ.Count==0)return;if(autoPauseGyroMode&&Ǵ[0].IsUnderControl){ȶ();ƻ="Automatic alignment paused.\n";ƻ+=
"Ship is currently controlled by a player.";return;}int ǻ=10;bool ɋ=false;bool ȼ=false;bool ȝ=false;string Ā="";double ü=Ȁ;double þ=Ȍ==0?1:Ȍ;double ý=Ȃ;bool ȟ=(ü==
0||ü<=Ȍ*0.1);double Ȗ=maxGyroRPM-(maxGyroRPM-minGyroRPM)*(ü/þ);Ȗ=Ȗ/(Math.PI*3);if((!ü.ŋ(ǵ-ǵ*realignPercentageGyro,ǵ+ǵ*
realignPercentageGyro)||ȟ)&&Ƨ&&ƪ>=ǻ){ƨ=false;Ʃ=false;ǵ=0;if(ü<ý&&Ƽ==3&&!Ǽ){Ǹ=-Ǹ;Ƽ=0;Ǽ=true;}Ȝ((float)(Ǹ*Ȗ),0,0);if(Ǹ==-1){Ā="down";}else{Ā=
"up";}if((ü<ý&&Ƽ>=4)||(ȟ&&Ƽ>60)){ȵ();ƨ=true;Ʃ=true;ǵ=ü;Ƕ=ü;Ǽ=false;Ƽ=0;ƪ=0;}else{ɋ=true;Ƽ++;}}else if(Ƨ){ȵ();ƨ=true;Ʃ=true;Ǽ
=false;Ƽ=0;ƪ++;}else{ƪ++;}if((!ü.ŋ(Ƿ-Ƿ*realignPercentageGyro,Ƿ+Ƿ*realignPercentageGyro)||ȟ)&&Ʃ&&Ƭ>=ǻ){Ƨ=false;ƨ=false;Ƿ=0
;if(ü<ý&&Ʀ==3&&!Ƥ){Ǻ=-Ǻ;Ʀ=0;Ƥ=true;}Ȝ(0,0,(float)(Ǻ*Ȗ));if(Ǻ==-1){Ā="left";}else{Ā="right";}if(ü<ý&&Ʀ>=4){ȵ();Ƨ=true;ƨ=
true;Ƿ=ü;Ƥ=false;Ʀ=0;Ƭ=0;}else{ȝ=true;Ʀ++;}}else if(Ʃ){ȵ();Ƨ=true;ƨ=true;Ƥ=false;Ʀ=0;Ƭ++;}else{Ƭ++;}if(!ü.ŋ(Ƕ-Ƕ*
realignPercentageGyro,Ƕ+Ƕ*realignPercentageGyro)&&ƨ&&ƫ>=ǻ){Ƨ=false;Ʃ=false;Ƕ=0;if(ü<ý&&ƥ==3&&!Ǧ){ǹ=-ǹ;ƥ=0;Ǧ=true;}Ȝ(0,(float)(ǹ*Ȗ),0);if(ǹ==-
1){Ā="left";}else{Ā="right";}if(ü<ý&&ƥ>=4){ȵ();Ƨ=true;Ʃ=true;Ƕ=ü;ǵ=ü;Ǧ=false;ƥ=0;ƫ=0;}else{ȼ=true;ƥ++;}}else if(ƨ){ȵ();Ƨ=
true;Ʃ=true;Ǧ=false;ƥ=0;ƫ++;}else{ƫ++;}if(!ɋ&&!ȼ&&!ȝ){if(ȟ){ƻ="No sun in sight! Realigning in "+(ǻ-ƪ)+"..";}else{ƻ=
"Aligned.";}}else if(ɋ){ƻ="Aligning by pitching the ship "+Ā+"..";}else if(ȼ){ƻ="Aligning by yawing the ship "+Ā+"..";}else if(ȝ){
ƻ="Aligning by rolling the ship "+Ā+"..";}}void Ƞ(){if(Ȁ<Ȍ*nightPercentage&&ǯ>=30){ƻ="Night Mode.";ǭ=true;if(
rotateToSunrise&&!Ǯ){if(manualAngle){Ǯ=Ⱥ("both",manualAngleHorizontal,manualAngleVertical);}else{Ǯ=Ⱥ("sunrise");}if(Ǯ){foreach(var Đ in
ȅ){ɥ(Đ,"firstLockOfDay",1);ɥ(Đ,"rotationDone",0);}}}else{ȶ();}return;}if(ǭ){ǭ=false;ǯ=0;foreach(var Đ in ȅ){ɥ(Đ,
"outputMaxDayBefore",ɤ(Đ,"outputMax"));ɥ(Đ,"outputMax",0);}}else if(ǯ>172800){ǯ=0;}else{ǯ++;}Ǯ=false;ǲ=true;ǻ=Ȁ<Ȍ*0.5?30:10;int ȡ=Ȣ(Ȇ,true);
int ȧ=Ȣ(ȇ);if(ȡ==0&&ȧ==0){ƻ="Aligned.";}else if(ȡ==0){ƻ="Aligning "+ȧ+" horizontal rotors..";}else if(ȧ==0){ƻ="Aligning "+ȡ
+" vertical rotors..";}else{ƻ="Aligning "+ȧ+" horizontal and "+ȡ+" vertical rotors..";}}int Ȣ(List<IMyMotorStator>ȣ,bool
Ȥ=false){int ȥ=0;foreach(var Đ in ȣ){double ü=ɤ(Đ,"output");double ý=ɤ(Đ,"outputLast");double Ȧ=ɤ(Đ,"outputLocked");
double þ=ɤ(Đ,"outputMax");double Ā=ɤ(Đ,"direction");double ā=ɤ(Đ,"directionChanged");double Ă=ɤ(Đ,"directionTimer");double Ș=ɤ
(Đ,"allowRotation");double Ȑ=ɤ(Đ,"timeSinceRotation");bool ȑ=false;if(Ș==0||Ȑ<ǻ){ȴ(Đ);ɥ(Đ,"allowRotation",1);ɥ(Đ,
"timeSinceRotation",Ȑ+1);continue;}if(!ü.ŋ(Ȧ-Ȧ*realignPercentageRotor,Ȧ+Ȧ*realignPercentageRotor)){if(Ȥ){ș(Đ,false);}else{ȗ(Đ,false);}Ȧ=0;
if(ü<ý&&Ă==2&&ā==0){Ā=-Ā;Ă=0;ā=1;}if((Đ.LowerLimitDeg!=float.MinValue||Đ.UpperLimitDeg!=float.MaxValue)&&Ă>=5){double Ȓ=Ď(
Đ);float ȓ=(float)Math.Round(Đ.LowerLimitDeg);float Ȕ=(float)Math.Round(Đ.UpperLimitDeg);if(Ȓ==ȓ||Ȓ==360+ȓ||Ȓ==Ȕ||Ȓ==360+
Ȕ){if(ü<ý&&ā==0){Ā=-Ā;Ă=0;ā=1;}else{ȑ=true;}}}bool ȕ=ü.ŋ(þ*0.998,þ*1.002);float Ȗ=(float)(rotorMaxSpeed-rotorMaxSpeed*ü.ƅ
(þ)+rotorMinSpeed);if(!ȕ)Ȗ+=rotorMinSpeed;ț(Đ,Ā,Ȗ);if((ü<ý&&!ȕ&&Ă>=3)||ü==0||ȑ){ȴ(Đ);if(ɤ(Đ,"firstLockOfDay")==1){if(ü>ɤ(
Đ,"outputMaxDayBefore")*0.9){ɥ(Đ,"firstLockOfDay",0);ɥ(Đ,"sunriseAngle",Ď(Đ));}}Ȧ=ü;ā=0;Ă=0;Ȑ=0;}else{ȥ++;Ă++;}ɥ(Đ,
"outputLocked",Ȧ);ɥ(Đ,"direction",Ā);ɥ(Đ,"directionChanged",ā);ɥ(Đ,"directionTimer",Ă);ɥ(Đ,"timeSinceRotation",Ȑ);}else{ȴ(Đ);}}return
ȥ;}void ȗ(IMyMotorStator Đ,bool è){foreach(var û in Ȇ){if(Đ.CubeGrid==û.TopGrid){if(è){ɥ(û,"allowRotation",1);}else{ȴ(û);
ɥ(û,"allowRotation",0);}}}}void ș(IMyMotorStator Đ,bool è){foreach(var Ț in ȇ){if(Đ.TopGrid==Ț.CubeGrid){if(è){ɥ(Ț,
"allowRotation",1);}else{ȴ(Ț);ɥ(Ț,"allowRotation",0);}}}}void ț(IMyMotorStator Đ,double Ā,float Ȗ){Đ.RotorLock=false;Đ.
TargetVelocityRPM=Ȗ*(float)Ā;}void Ȝ(double ȏ,double Ȩ,double ȩ){Vector3D Ȱ=new Vector3D(-ȏ,Ȩ,ȩ);Vector3D ȱ=Vector3D.TransformNormal(Ȱ,Ǵ[
0].WorldMatrix);foreach(var Ȳ in Ȉ){Vector3D ȳ=Vector3D.TransformNormal(ȱ,Matrix.Transpose(Ȳ.WorldMatrix));Ȳ.GyroOverride
=true;Ȳ.GyroPower=gyroPower;Ȳ.Pitch=(float)ȳ.X;Ȳ.Yaw=(float)ȳ.Y;Ȳ.Roll=(float)ȳ.Z;}}void ȴ(IMyMotorStator Đ,bool ȭ=true){
Đ.TargetVelocityRPM=0f;if(ȭ){ɥ(Đ,"rotationDone",1);}else{ɥ(Đ,"rotationDone",0);}if(setRotorLockWhenStopped){Đ.RotorLock=
true;}}void ȵ(){foreach(var Ȳ in Ȉ){Ȳ.Pitch=0;Ȳ.Yaw=0;Ȳ.Roll=0;Ȳ.GyroOverride=false;}}void ȶ(bool ȭ=true){foreach(var Đ in ȅ
){ȴ(Đ,ȭ);ɥ(Đ,"timeSinceRotation",0);}ȵ();ƪ=0;ƫ=0;Ƭ=0;}bool ȸ(IMyMotorStator Đ,double ȫ,bool Ȫ=true){double Ȓ=Ď(Đ);bool ȹ=
false;if(Ȫ){if(Đ.CustomName.IndexOf("[90]")>=0){ȫ+=90;}else if(Đ.CustomName.IndexOf("[180]")>=0){ȫ+=180;}else if(Đ.CustomName
.IndexOf("[270]")>=0){ȫ+=270;}if(ȫ>=360)ȫ-=360;if(Đ.Orientation.Up.ToString()=="Down"){ȹ=true;}else if(Đ.Orientation.Up.
ToString()=="Backward"){ȹ=true;}else if(Đ.Orientation.Up.ToString()=="Left"){ȹ=true;}}if(Đ.LowerLimitDeg!=float.MinValue||Đ.
UpperLimitDeg!=float.MaxValue){if(ȹ)ȫ=-ȫ;if(ȫ>Đ.UpperLimitDeg){ȫ=Math.Floor(Đ.UpperLimitDeg);}if(ȫ<Đ.LowerLimitDeg){ȫ=Math.Ceiling(Đ.
LowerLimitDeg);}}else{if(ȹ)ȫ=360-ȫ;}if(Ȓ.ŋ(ȫ-1,ȫ+1)||Ȓ.ŋ(360+ȫ-1,360+ȫ+1)){ȴ(Đ);return true;}else{int Ā=Ȓ<ȫ?1:-1;if(Ȓ<=90&&ȫ>=270){Ā=
-1;}if(Ȓ>=270&&ȫ<=90){Ā=1;}Single Ȗ=Math.Abs(Ȓ-ȫ)>15?1f:0.2f;if(Math.Abs(Ȓ-ȫ)<3)Ȗ=0.1f;ț(Đ,Ā,Ȗ);return false;}}bool Ⱥ(
string Ȼ,double ȷ=0,double ȯ=0){bool ȭ=true;int ȡ=0;int ȧ=0;if(ǲ){ǲ=false;ȶ(false);}if(Ȼ!="verticalOnly"){foreach(var Ț in ȇ){
if(ɤ(Ț,"rotationDone")==1)continue;bool Ȫ=true;double ȫ=ȷ;if(Ȼ=="sunrise"){ȫ=ɤ(Ț,"sunriseAngle");Ȫ=false;}if(!ȸ(Ț,ȫ,Ȫ)){ȭ=
false;ȧ++;ǐ=ȧ+" horizontal rotors are set to "+ȷ+"°";if(Ȼ=="sunrise")ǐ=ȧ+" horizontal rotors are set to sunrise position";}}}
if(!ȭ)return false;if(Ȼ!="horizontalOnly"){foreach(var û in Ȇ){if(ɤ(û,"rotationDone")==1)continue;bool Ȫ=true;double ȫ=ȯ;
if(Ȼ=="sunrise"){ȫ=ɤ(û,"sunriseAngle");Ȫ=false;}if(!ȸ(û,ȫ,Ȫ)){ȭ=false;ȡ++;ǐ=ȡ+" vertical rotors are set to "+ȯ+"°";if(Ȼ==
"sunrise")ǐ=ȡ+" vertical rotors are set to sunrise position";}}}if(ȭ)ǲ=true;return ȭ;}void Ȭ(){int ȧ=0;int ȡ=0;if(Ǳ==90){foreach(
var Đ in ȅ){ȴ(Đ,false);double Ȯ=1;if(Đ.Orientation.Up.ToString()=="Up"){Ȯ=-1;}else if(Đ.Orientation.Up.ToString()==
"Forward"){Ȯ=-1;}else if(Đ.Orientation.Up.ToString()=="Right"){Ȯ=-1;}ɥ(Đ,"outputMax",ɤ(Đ,"output"));ɥ(Đ,"direction",Ȯ);ɥ(Đ,
"directionChanged",0);ɥ(Đ,"directionTimer",0);Ȍ=0;}}foreach(var Ț in ȇ){if(ɤ(Ț,"rotationDone")==1)continue;double ü=ɤ(Ț,"output");double ý
=ɤ(Ț,"outputLast");double þ=ɤ(Ț,"outputMax");double ÿ=ɤ(Ț,"outputMaxAngle");double Ā=ɤ(Ț,"direction");double ā=ɤ(Ț,
"directionChanged");double Ă=ɤ(Ț,"directionTimer");if(þ==0)þ=1;if(ā!=2){ȧ++;if(ü<ý&&Ă>=7&&ā==0){ɥ(Ț,"direction",-Ā);ɥ(Ț,"directionChanged"
,1);Ă=0;}if((Ț.LowerLimitDeg!=float.MinValue||Ț.UpperLimitDeg!=float.MaxValue)&&Ă>=3&&ā==0){double Ȟ=Ď(Ț);float ƌ=(float)
Math.Round(Ț.LowerLimitDeg);float Ĭ=(float)Math.Round(Ț.UpperLimitDeg);if(Ȟ==ƌ||Ȟ==360+ƌ||Ȟ==Ĭ||Ȟ==360+Ĭ){ɥ(Ț,"direction",-Ā
);ɥ(Ț,"directionChanged",1);Ă=0;}}ț(Ț,Ā,(float)(2.75-ü.ƅ(þ)*2));if(ü<ý&&Ă>=7&&ā==1){ȴ(Ț,false);ɥ(Ț,"directionChanged",2);
}else{ɥ(Ț,"directionTimer",Ă+1);}}else{if(!ȸ(Ț,ÿ,false))ȧ++;}}if(ȧ!=0)return;foreach(var û in Ȇ){if(ɤ(û,"rotationDone")==
1)continue;double ü=ɤ(û,"output");double ý=ɤ(û,"outputLast");double þ=ɤ(û,"outputMax");double ÿ=ɤ(û,"outputMaxAngle");
double Ā=ɤ(û,"direction");double ā=ɤ(û,"directionChanged");double Ă=ɤ(û,"directionTimer");if(þ==0)þ=1;if(ā!=2){ȡ++;if(ü<ý&&Ă>=
7&&ā==0){ɥ(û,"direction",-Ā);ɥ(û,"directionChanged",1);Ă=0;}if((û.LowerLimitDeg!=float.MinValue||û.UpperLimitDeg!=float.
MaxValue)&&Ă>=3&&ā==0){double ă=Ď(û);float ą=(float)Math.Round(û.LowerLimitDeg);float ď=(float)Math.Round(û.UpperLimitDeg);if(ă
==ą||ă==360+ą||ă==ď||ă==360+ď){ɥ(û,"direction",-Ā);ɥ(û,"directionChanged",1);Ă=0;}}ț(û,Ā,(float)(2.75-ü.ƅ(þ)*2));if(ü<ý&&Ă
>=7&&ā==1){ȴ(û,false);ɥ(û,"directionChanged",2);}else{ɥ(û,"directionTimer",Ă+1);}}else{if(!ȸ(û,ÿ,false))ȡ++;}}if(ȧ==0&&ȡ==
0){Ǳ=0;}}void Ć(IMyTerminalBlock ć,bool Ĉ=true,string ĉ="",double Ċ=0,double ċ=0){string Č=ć.CustomName;string č=System.
Text.RegularExpressions.Regex.Match(ć.CustomName,@" *\(\d+\.*\d*%.*\)").Value;if(č!=String.Empty){Č=ć.CustomName.Replace(č,
"");}if(Ĉ){Č+=" ("+Ċ.Ŀ(ċ);if(ĉ!=""){Č+=", "+ĉ;}Č+=")";}if(Č!=ć.CustomName){ć.CustomName=Č;}}double Ď(IMyMotorStator Đ){
return Math.Round(Đ.Angle*180/Math.PI);}StringBuilder Ą(IMyTextSurface É,bool Z=true,bool ì=true,bool í=true,bool î=true,bool
ï=true,bool ð=true,bool ñ=true,bool ë=true){bool ò=false;StringBuilder d=new StringBuilder();if(Z){d.Append(
"Isy's Solar Alignment "+ǒ[Ǔ]+"\n");d.Append(É.ĭ('=',É.Ź(d))).Append("\n\n");}if(ì&&Ĕ!=null){d.Append("Warning!\n"+Ĕ+"\n\n");ò=true;}if(í){
string ó=ƻ+"\n"+Ǒ;d.Append(ó);d.Append('\n'.Ő(3-ó.Count(ù=>ù=='\n')));ò=true;}if(î){d.Append("Statistics for "+ǫ.Count+
" Solar Panels:\n");d.Append(Ý(É,"Efficiency",Ȁ,Ȍ,ƕ,Ɩ));d.Append(Ý(É,"Output",Ȅ,Ȁ,Ɨ,ƕ)+"\n\n");ò=true;}if(ï&&ƣ.Count>0){d.Append(
"Statistics for "+ƣ.Count+" Wind Turbines:\n");d.Append(Ý(É,"Efficiency",Ǝ,ȃ,Ɵ,Ơ));d.Append(Ý(É,"Output",ơ,Ǝ,ƞ,Ɵ)+"\n\n");ò=true;}if(ð&&ƭ
.Count>0){d.Append("Statistics for "+ƭ.Count+" Batteries:\n");d.Append(Ý(É,"Input",Ư,ƹ,Ƙ,ƙ));d.Append(Ý(É,"Output",ư,Ʊ,ƚ,
ƛ));d.Append(Ý(É,"Charge",Ʋ,Ƴ,Ɯ,Ɲ)+"\n\n");ò=true;}if(ñ&&(ƴ.Count>0||Ƶ.Count>0)){d.Append("Statistics for Oxygen:\n");if(
ƴ.Count>0){d.Append(Ý(É,ƴ.Count+" Farms",ƶ,100));}if(Ƶ.Count>0){d.Append(Ý(É,Ƶ.Count+" Tanks",Ƹ,Ʒ,Ƣ,Ʈ));}d.Append("\n\n")
;ò=true;}if(ë&&!useGyroMode){string ô="";string õ="";string ö="";if(Ǘ<ǔ){õ=" inaccurate";ô="*";}else if(Ǘ==ǖ||Ǚ==ǘ){õ=
" inaccurate, still calculating";ô="*";}if(ǔ<Ǚ&&ô==""){ö=" / Dusk in: "+V(Ǚ-ǔ);}else if(ǔ>Ǚ&&ô==""){ö=" / Dawn in: "+V(Ǘ-ǔ);}d.Append(
"Time of your location:\n");d.Append("Time: "+ª(ǔ)+ö+ô+"\n");d.Append("Dawn: "+ª(Ǘ)+" / Daylength: "+V(Ǚ)+ô+"\n");d.Append("Dusk: "+ª(Ǚ)+
" / Nightlength: "+V(Ǘ-Ǚ)+ô+"\n");if(ô!=""){d.Append(ô+õ);}ò=true;}if(!ò){d.Append("-- No informations to show --");}return d;}
StringBuilder ø(IMyTextSurface É,bool î=false,bool ï=false,bool ð=false,bool ñ=false,bool ë=false,bool đ=false){bool ò=false;
StringBuilder d=new StringBuilder();if(î){d.Append("Statistics for "+ǫ.Count+" Solar Panels:\n");d.Append(Ý(É,"Efficiency",Ȁ,Ȍ,ƕ,Ɩ,ä:
true));d.Append(Ý(É,"Output",Ȅ,Ȁ,Ɨ,ƕ,ä:true));ò=true;}if(ï&&ƣ.Count>0){if(ò)d.Append("\n");d.Append("Statistics for "+ƣ.
Count+" Wind Turbines:\n");d.Append(Ý(É,"Output",ơ,Ǝ,ƞ,Ɵ,ä:true));ò=true;}if(ð&&ƭ.Count>0){if(ò)d.Append("\n");d.Append(
"Statistics for "+ƭ.Count+" Batteries:\n");d.Append(Ý(É,"Input",Ư,ƹ,Ƙ,ƙ,ä:true));d.Append(Ý(É,"Output",ư,Ʊ,ƚ,ƛ,ä:true));d.Append(Ý(É,
"Charge",Ʋ,Ƴ,Ɯ,Ɲ,ä:true));ò=true;}if(ñ&&(ƴ.Count>0||Ƶ.Count>0)){if(ò)d.Append("\n");d.Append("Statistics for Oxygen:\n");if(ƴ.
Count>0){d.Append(Ý(É,ƴ.Count+" Farms",ƶ,100,ä:true));}if(Ƶ.Count>0){d.Append(Ý(É,Ƶ.Count+" Tanks",Ƹ,Ʒ,Ƣ,Ʈ,ä:true));}ò=true;}
if(ë){if(ò)d.Append("\n");if(useGyroMode){d.Append("Location time is not available in gyro mode!");}else{string w=ª(ǔ);d.
Append(É.ĭ(' ',(É.ƀ()-É.Ź(w))/2)).Append(w+"\n");}ò=true;}if(đ){if(ò)d.Append("\n");string w=DateTime.Now.ToString(@"HH:mm:ss"
);d.Append(É.ĭ(' ',(É.ƀ()-É.Ź(w))/2)).Append(w+"\n");ò=true;}if(!ò){d.Append(
"Edit the custom data and set,\nwhat should be shown here!");}return d;}void Ė(string S=null){if(ȉ.Count==0){ǌ++;return;}for(int P=ǧ;P<ȉ.Count;P++){if(ş())return;ǧ++;var À=ȉ[P].ğ(
mainLcdKeyword);foreach(var W in À){var X=W.Key;var Y=W.Value;bool Z=Y.Ľ("showHeading");bool ì=Y.Ľ("showWarnings");bool í=Y.Ľ(
"showCurrentOperation");bool î=Y.Ľ("showSolarStats");bool ï=Y.Ľ("showTurbineStats");bool ð=Y.Ľ("showBatteryStats");bool ñ=Y.Ľ(
"showOxygenStats");bool ë=Y.Ľ("showLocationTime");bool a=Y.Ľ("scrollTextIfNeeded");StringBuilder d=new StringBuilder();if(S!=null){d.
Append(S);}else{d=Ą(X,Z,ì,í,î,ï,ð,ñ,ë);}d=X.ĳ(d,Z?3:0,a);X.WriteText(d);}}ǌ++;ǧ=0;}void ė(){if(Ȋ.Count==0){ǌ++;return;}for(int
P=Ǩ;P<Ȋ.Count;P++){if(ş())return;Ǩ++;var À=Ȋ[P].ğ(compactLcdKeyword);foreach(var W in À){var X=W.Key;var Y=W.Value;bool î
=Y.Ľ("showSolarStats");bool ï=Y.Ľ("showTurbineStats");bool ð=Y.Ľ("showBatteryStats");bool ñ=Y.Ľ("showOxygenStats");bool ë
=Y.Ľ("showLocationTime");bool đ=Y.Ľ("showRealTime");bool a=Y.Ľ("scrollTextIfNeeded");StringBuilder d=new StringBuilder();
d=ø(X,î,ï,ð,ñ,ë,đ);d=X.ĳ(d,0,a);X.WriteText(d);}}ǌ++;Ǩ=0;}void Ē(){if(ȋ.Count==0){ǌ++;return;}StringBuilder ĕ=new
StringBuilder();if(ƿ.Count==0){ĕ.Append("- No problems detected -");}else{int ē=1;foreach(var Ĕ in ƿ){ĕ.Append(ē+". "+Ĕ.Replace("\n",
" ")+"\n");ē++;}}for(int P=ǩ;P<ȋ.Count;P++){if(ş())return;ǩ++;var À=ȋ[P].ğ(warningsLcdKeyword);foreach(var W in À){var X=W.
Key;var Y=W.Value;bool Z=Y.Ľ("showHeading");bool a=Y.Ľ("scrollTextIfNeeded");StringBuilder d=new StringBuilder();if(Z){d.
Append("Isy's Solar Alignment Warnings\n");d.Append(X.ĭ('=',X.Ź(d))).Append("\n\n");}d.Append(ĕ);d=X.ĳ(d,Z?3:0,a);X.WriteText(
d);}}ǌ++;ǩ=0;}void ê(){if(ȍ.Count==0){ǌ++;return;}for(int P=Ǫ;P<ȍ.Count;P++){if(ş())return;Ǫ++;var À=ȍ[P].ğ(
performanceLcdKeyword);foreach(var W in À){var X=W.Key;var Y=W.Value;bool Z=Y.Ľ("showHeading");bool a=Y.Ľ("scrollTextIfNeeded");StringBuilder
d=new StringBuilder();if(Z){d.Append("Isy's Solar Alignment Performance\n");d.Append(X.ĭ('=',X.Ź(d))).Append("\n\n");}d.
Append(Ę);d=X.ĳ(d,Z?3:0,a);X.WriteText(d);}}ǌ++;Ǫ=0;}void e(){if(ű==99){ű=0;}else{ű++;}Echo("Isy's Solar Alignment "+ǒ[Ǔ]+
"\n========================\n");if(Ĕ!=null){Echo("Warning!\n"+Ĕ+"\n");}StringBuilder d=new StringBuilder();d.Append(ƻ+"\n"+Ǒ+"\n\n");d.Append(
"Script is running in "+(useGyroMode?"gyro":"rotor")+" mode\n\n");d.Append("Task: "+Ǎ[ǉ]+"\n");d.Append("Script step: "+ǉ+" / "+(Ǎ.Length-1)+
"\n\n");d.Append(Ę);d.Append("Main Grid: "+Ŵ.CustomName+"\n");if(ȅ.Count>0)d.Append("Rotors: "+ȅ.Count+"\n");if(Ȉ.Count>0)d.
Append("Gyros: "+Ȉ.Count+"\n");if(ǫ.Count>0)d.Append("Solar Panels: "+ǫ.Count+"\n");if(ƣ.Count>0)d.Append("Wind Turbines: "+ƣ.
Count+"\n");if(ƴ.Count>0)d.Append("Oxygen Farms: "+ƴ.Count+"\n");if(Ƶ.Count>0)d.Append("Oxygen Tanks: "+Ƶ.Count+"\n");if(ƭ.
Count>0)d.Append("Batteries: "+ƭ.Count+"\n");if(ǟ>0)d.Append("Reactors: "+ǟ+"\n");if(Ǡ>0)d.Append("Hydrogen Engines: "+Ǡ+"\n"
);if(ȉ.Count>0)d.Append("Main LCDs: "+ȉ.Count+"\n");if(Ȋ.Count>0)d.Append("Compact LCDs: "+Ȋ.Count+"\n");if(ȋ.Count>0)d.
Append("Warning LCDs: "+ȋ.Count+"\n");if(ȍ.Count>0)d.Append("Performance LCDs: "+ȍ.Count+"\n");if(ȁ.Count>0)d.Append(
"Lights: "+ȁ.Count+"\n");if(ǽ.Count>0)d.Append("Spotlights: "+ǽ.Count+"\n");if(timers.Length>0)d.Append("Timer Blocks: "+timers.
Length+"\n");Ę=d;Echo(d.ToString());if(ȉ.Count==0){Echo("Hint:\nBuild a LCD and add the main LCD\nkeyword '"+mainLcdKeyword+
"' to its name to get\nmore informations about your base\nand the current script actions.\n");}}void f(){ǔ+=1;Ǖ+=1;if(ǔ>172800){ǔ=0;Ǖ=0;}if(Ɛ)return;if(ƒ>0){ƒ--;return;}double j=Ȍ*nightTimePercentage;if(Ȁ<j&&Ȃ>=j
&&Ǖ>300){Ǚ=ǔ;Ǖ=0;}if(Ȁ>j&&Ȃ<=j&&Ǖ>300){if(Ǚ!=ǘ){Ǘ=ǔ;}ǔ=0;Ǖ=0;}if(Ǚ>Ǘ){Ǘ=Ǚ*2;}}string ª(double k,bool m=false){string q="";
k=k%Ǘ;double u=Ǚ+(Ǘ-Ǚ)/2D;double v=Ǘ/24D;double w;if(k<u){w=(k+(Ǘ-u))/v;}else{w=(k-u)/v;}double x=Math.Floor(w);double y=
Math.Floor((w%1*100)*0.6);string z=x.ToString("00");string µ=y.ToString("00");q=z+":"+µ;if(m){return x.ToString();}else{
return q;}}string V(int A){string T="";TimeSpan B=TimeSpan.FromSeconds(A);T=B.ToString(@"hh\:mm\:ss");return T;}void C(){if(ǰ.
Count==0)return;double D=turnOnAtPercent%100/100;double E=turnOffAtPercent%100/100;double F=overloadPercentage%100/100;if(Ǣ==
"lowBat"||Ǣ==""){if(activateOnLowBattery&&Ʋ<Ƴ*D){ǜ=true;Ǣ="lowBat";}else if(activateOnLowBattery&&Ʋ>Ƴ*E){ǜ=false;Ǣ="";}}if(Ǣ==
"overload"||Ǣ==""){if(activateOnOverload&&ư+Ȅ+ơ>(Ʊ+Ȁ+Ǝ)*F){ǜ=true;Ǣ="overload";}else{ǜ=false;Ǣ="";}}if(Ʋ<ǡ||(ǜ&&ǝ&&Ǟ)){ǝ=true;Ǟ=
true;}else{if(activateHydrogenEngineFirst&&Ǡ>0){Ǟ=true;ǝ=false;}else if(!activateHydrogenEngineFirst&&ǟ>0){Ǟ=false;ǝ=true;}
else{Ǟ=true;ǝ=true;}}ǡ=Ʋ;foreach(var G in ǰ){if(ǜ){if(ǝ&&G.BlockDefinition.TypeIdString.Contains("Reactor")){G.Enabled=true;
}else if(Ǟ&&G.BlockDefinition.TypeIdString.Contains("HydrogenEngine")){G.Enabled=true;}else{G.Enabled=false;}}else{G.
Enabled=false;}}if(Ǣ=="lowBat")ǐ="Power fallback active: Low battery charge!";if(Ǣ=="overload")ǐ=
"Power fallback active: Overload!";}void H(){if(ȁ.Count==0&&ǽ.Count==0)return;int I=0;int.TryParse(ª(ǔ,true),out I);bool J=true;if(!simpleMode){if(ǔ!=Ǘ&&I
>=lightOffHour&&I<lightOnHour){J=false;}else if(ǔ==Ǘ&&Ȁ>Ȍ*nightTimePercentage){J=false;}}else{if(Ȁ>Ȍ*(simpleThreshold%100)
/100)J=false;}foreach(var K in ȁ){K.Enabled=J;}foreach(var L in ǽ){L.Enabled=J;}}void M(){if(events.Length==0){R(
"No events for triggering specified!");return;}if(timers.Length==0){R("No timers for triggering specified!");return;}if(events.Length!=timers.Length){R(
"Every event needs a timer block name!\nFound "+events.Length+" events and "+timers.Length+" timers.");return;}int N=-1;string O="";int A;for(int P=0;P<=events.Length-
1;P++){if(events[P]==ǥ){ǥ="";O=events[P];}else if(events[P]=="sunrise"&&ǔ==0){O=events[P];}else if(events[P]=="sunset"&&ǔ
==Ǚ){O=events[P];}else if(events[P]!=ǚ&&events[P].StartsWith("battery")&&System.Text.RegularExpressions.Regex.Match(events
[P],@"\d+").Value==Math.Floor(Ʋ/Ƴ*100).ToString()){O=events[P];ǚ=O;}else if(int.TryParse(events[P],out A)==true&&ǔ%A==0){
O=A+" seconds";}else if(ª(ǔ)==events[P]){O=events[P];}if(O!=""){N=P;break;}}if(N>=0){var Q=GridTerminalSystem.
GetBlockWithName(timers[N])as IMyTimerBlock;if(Q!=null){Q.Enabled=true;if(Q.TriggerDelay==1){Q.Trigger();}else{Q.StartCountdown();}R(
"External timer triggered! Reason: "+O);}}}void R(string S){ƿ.Add(S);ǀ.Add(S);Ĕ=ƿ.Last();}void h(){foreach(var U in ǫ){U.CustomData="";Ć(U,false);}foreach(
var º in ƭ){Ć(º,false);}foreach(var Ø in ƴ){Ć(Ø,false);}foreach(var Ù in Ƶ){Ć(Ù,false);}}void Ú(){if(Storage.Length>0){var
Y=Storage.Split('\n');foreach(var Û in Y){var Ü=Û.Split('=');if(Ü.Length!=2)continue;if(Ü[0]=="dayTimer"){int.TryParse(Ü[
1],out ǔ);}else if(Ü[0]=="dayLength"){int.TryParse(Ü[1],out Ǘ);}else if(Ü[0]=="sunSet"){int.TryParse(Ü[1],out Ǚ);}else if
(Ü[0]=="outputLast"){float.TryParse(Ü[1],out Ȃ);}else if(Ü[0]=="maxDetectedOutput"){float.TryParse(Ü[1],out Ȍ);}else if(Ü
[0]=="maxDetectedWind"){float.TryParse(Ü[1],out ȃ);}else if(Ü[0]=="solarPanelsCount"){int.TryParse(Ü[1],out Ǭ);}else if(Ü
[0]=="oxygenFarmsCount"){int.TryParse(Ü[1],out ƺ);}else if(Ü[0]=="turbinesCount"){int.TryParse(Ü[1],out ƍ);}else if(Ü[0]
=="action"){ǁ=Ü[1];}}if(ǁ=="paused")ǂ=true;}}void Save(){string Y="";Y+="dayTimer="+ǔ+"\n";Y+="dayLength="+Ǘ+"\n";Y+=
"sunSet="+Ǚ+"\n";Y+="outputLast="+Ȃ+"\n";Y+="maxDetectedOutput="+Ȍ+"\n";Y+="maxDetectedWind="+ȃ+"\n";Y+="solarPanelsCount="+ǫ.
Count+"\n";Y+="oxygenFarmsCount="+ƴ.Count+"\n";Y+="turbinesCount="+ƣ.Count+"\n";Y+="action="+ǁ;Storage=Y;}StringBuilder Ý(
IMyTextSurface É,string ß,double è,double à,string á=null,string â=null,bool ã=false,bool ä=false,string Ì=""){string å=è.ToString();
string æ=à.ToString();if(á!=null){å=á;}if(â!=null){æ=â;}float ç=É.FontSize;float é=0.61f;float Þ=1.01f;if(É.Font=="Monospace")
{é=0.41f;Þ=0.81f;}float Ö=É.ƀ();char Ê=' ';float Á=É.ſ(Ê);StringBuilder Â=new StringBuilder(" "+è.Ŀ(à));Â=É.ĭ(Ê,É.Ź(
"9999.9%")-É.Ź(Â)).Append(Â);StringBuilder Ã=new StringBuilder(å+" / "+æ);StringBuilder Ä=new StringBuilder();StringBuilder Å=new
StringBuilder();StringBuilder Æ;if(à==0){Ä.Append(Ì+ß+" ");Æ=É.ĭ(Ê,Ö-É.Ź(Ä)-É.Ź(å));Ä.Append(Æ).Append(å);return Ä.Append("\n");}
double Ç=0;if(à>0)Ç=è/à>=1?1:è/à;if(ä&&!ã){if(ç<é||(ç<Þ&&Ö>512)){Ä.Append(È(É,Ö*0.25f,Ç,Ì)+" "+ß+" ");Æ=É.ĭ(Ê,Ö*0.75-É.Ź(Ä)-É.
Ź(å+" /"));Ä.Append(Æ).Append(Ã);Æ=É.ĭ(Ê,Ö-É.Ź(Ä)-É.Ź(Â));Ä.Append(Æ);Ä.Append(Â);}else{Ä.Append(È(É,Ö*0.3f,Ç,Ì)+" "+ß+
" ");Æ=É.ĭ(Ê,Ö-É.Ź(Ä)-É.Ź(Â));Ä.Append(Æ);Ä.Append(Â);}}else{Ä.Append(Ì+ß+" ");if(ç<é||(ç<Þ&&Ö>512)){Æ=É.ĭ(Ê,Ö*0.5-É.Ź(Ä)-É
.Ź(å+" /"));Ä.Append(Æ).Append(Ã);Æ=É.ĭ(Ê,Ö-É.Ź(Ä)-É.Ź(Â));Ä.Append(Æ).Append(Â);if(!ã){Å=È(É,Ö,Ç,Ì).Append("\n");}}else{
Æ=É.ĭ(Ê,Ö-É.Ź(Ä)-É.Ź(Ã));Ä.Append(Æ).Append(Ã);if(!ã){Å=È(É,Ö-É.Ź(Â),Ç,Ì);Å.Append(Â).Append("\n");}}}return Ä.Append(
"\n").Append(Å);}StringBuilder È(IMyTextSurface É,float Ë,double Ç,string Ì){StringBuilder Í,Î;char Ï='[';char Ð=']';char Ñ=
'I';char Ò='∙';float Ó=É.ſ(Ï);float Ô=É.ſ(Ð);float ú=0;if(Ì!="")ú=É.Ź(Ì);float Õ=Ë-Ó-Ô-ú;Í=É.ĭ(Ñ,Õ*Ç);Î=É.ĭ(Ò,Õ-É.Ź(Í));
return new StringBuilder().Append(Ì).Append(Ï).Append(Í).Append(Î).Append(Ð);}StringBuilder Ę=new StringBuilder(
"No performance Information available!");Dictionary<string,int>Ũ=new Dictionary<string,int>();List<int>ũ=new List<int>(new int[600]);List<double>Ū=new List<
double>(new double[600]);double ū,Ŭ,ŭ,Ů,ů;int Ű,ű=0;void Ų(string ŷ){ű=ű>=599?0:ű+1;Ű=Runtime.CurrentInstructionCount;if(Ű>Ŭ)Ŭ
=Ű;ũ[ű]=Ű;Ů=ũ.Sum()/ũ.Count;Ę.Clear();Ę.Append("Instructions: "+Ű+" / "+Runtime.MaxInstructionCount+"\n");Ę.Append(
"Max. Instructions: "+Ŭ+" / "+Runtime.MaxInstructionCount+"\n");Ę.Append("Avg. Instructions: "+Math.Floor(Ů)+" / "+Runtime.
MaxInstructionCount+"\n\n");ū=Runtime.LastRunTimeMs;if(ū>ŭ&&Ũ.ContainsKey(ŷ))ŭ=ū;Ū[ű]=ū;ů=Ū.Sum()/Ū.Count;Ę.Append("Last runtime: "+Math.
Round(ū,4)+" ms\n");Ę.Append("Max. runtime: "+Math.Round(ŭ,4)+" ms\n");Ę.Append("Avg. runtime: "+Math.Round(ů,4)+" ms\n\n");Ę
.Append("Instructions per Method:\n");Ũ[ŷ]=Ű;foreach(var ų in Ũ.OrderByDescending(P=>P.Value)){Ę.Append("- "+ų.Key+": "+ų
.Value+"\n");}Ę.Append("\n");}IMyCubeGrid Ŵ=null;HashSet<IMyCubeGrid>ŵ=new HashSet<IMyCubeGrid>();void Ŷ(IMyCubeGrid ŗ){ŵ
.Add(ŗ);List<IMyMotorStator>ř=new List<IMyMotorStator>();List<IMyPistonBase>Ś=new List<IMyPistonBase>();
GridTerminalSystem.GetBlocksOfType<IMyMotorStator>(ř,ś=>ś.IsAttached&&ś.TopGrid==ŗ&&!ŵ.Contains(ś.CubeGrid));GridTerminalSystem.
GetBlocksOfType<IMyPistonBase>(Ś,Ŝ=>Ŝ.IsAttached&&Ŝ.TopGrid==ŗ&&!ŵ.Contains(Ŝ.CubeGrid));if(ř.Count==0&&Ś.Count==0){Ŵ=ŗ;return;}else{
foreach(var Đ in ř){Ŷ(Đ.CubeGrid);}foreach(var ŝ in Ś){Ŷ(ŝ.CubeGrid);}}}HashSet<IMyCubeGrid>Ş=new HashSet<IMyCubeGrid>();void Ŗ
(IMyCubeGrid ŗ,bool Ř=false){if(Ř)Ş.Clear();Ş.Add(ŗ);List<IMyMotorStator>ř=new List<IMyMotorStator>();List<IMyPistonBase>
Ś=new List<IMyPistonBase>();GridTerminalSystem.GetBlocksOfType<IMyMotorStator>(ř,ś=>ś.CubeGrid==ŗ&&ś.IsAttached&&!Ş.
Contains(ś.TopGrid));GridTerminalSystem.GetBlocksOfType<IMyPistonBase>(Ś,Ŝ=>Ŝ.CubeGrid==ŗ&&Ŝ.IsAttached&&!Ş.Contains(Ŝ.TopGrid))
;foreach(var Đ in ř){Ŗ(Đ.TopGrid);}foreach(var ŝ in Ś){Ŗ(ŝ.TopGrid);}}bool ş(double è=10){return Runtime.
CurrentInstructionCount>è*1000;}List<IMyTerminalBlock>Š(string ĩ,string[]š=null,string Ţ="Debug",float ţ=0.6f,float Ť=2f){string ť="[IsyLCD]";
var Ŧ=new List<IMyTerminalBlock>();GridTerminalSystem.GetBlocksOfType<IMyTextSurfaceProvider>(Ŧ,ŧ=>ŧ.IsSameConstructAs(Me)
&&(ŧ.CustomName.Contains(ĩ)||(ŧ.CustomName.Contains(ť)&&ŧ.CustomData.Contains(ĩ))));var ŕ=Ŧ.FindAll(ŧ=>ŧ.CustomName.
Contains(ĩ));foreach(var É in ŕ){É.CustomName=É.CustomName.Replace(ĩ,"").Replace(" "+ĩ,"").TrimEnd(' ');bool Ɓ=false;bool Ƃ=
false;int ƃ=0;if(É is IMyTextSurface){if(!É.CustomName.Contains(ť))Ɓ=true;if(!É.CustomData.Contains(ĩ)){Ƃ=true;É.CustomData=
"@0 "+ĩ+(š!=null?"\n"+String.Join("\n",š):"");}}else if(É is IMyTextSurfaceProvider){if(!É.CustomName.Contains(ť))Ɓ=true;int
Ƅ=(É as IMyTextSurfaceProvider).SurfaceCount;for(int P=0;P<Ƅ;P++){if(!É.CustomData.Contains("@"+P)){Ƃ=true;ƃ=P;É.
CustomData+=(É.CustomData==""?"":"\n\n")+"@"+P+" "+ĩ+(š!=null?"\n"+String.Join("\n",š):"");break;}}}else{Ŧ.Remove(É);}if(Ɓ)É.
CustomName+=" "+ť;if(Ƃ){var X=(É as IMyTextSurfaceProvider).GetSurface(ƃ);X.Font=Ţ;X.FontSize=ţ;X.TextPadding=Ť;X.Alignment=
TextAlignment.LEFT;X.ContentType=ContentType.TEXT_AND_IMAGE;}}return Ŧ;}
}public static partial class ł{public static float ƅ(this float ŀ,float Ł){return Ł==0?1:ŀ/Ł;}public static double ƅ(this
double ŀ,double Ł){return Ł==0?1:ŀ/Ł;}}public static partial class ł{private static Dictionary<char,float>Ɔ=new Dictionary<
char,float>();public static void Ƈ(string ƈ,float Ɖ){foreach(char Ÿ in ƈ){Ɔ[Ÿ]=Ɖ;}}public static void Ɗ(){if(Ɔ.Count>0)
return;Ƈ("3FKTabdeghknopqsuy£µÝàáâãäåèéêëðñòóôõöøùúûüýþÿāăąďđēĕėęěĝğġģĥħĶķńņňŉōŏőśŝşšŢŤŦũūŭůűųŶŷŸșȚЎЗКЛбдекруцяёђћўџ",18);Ƈ(
"ABDNOQRSÀÁÂÃÄÅÐÑÒÓÔÕÖØĂĄĎĐŃŅŇŌŎŐŔŖŘŚŜŞŠȘЅЊЖф□",22);Ƈ("#0245689CXZ¤¥ÇßĆĈĊČŹŻŽƒЁЌАБВДИЙПРСТУХЬ€",20);Ƈ("￥$&GHPUVY§ÙÚÛÜÞĀĜĞĠĢĤĦŨŪŬŮŰŲОФЦЪЯжы†‡",21);Ƈ(
"！ !I`ijl ¡¨¯´¸ÌÍÎÏìíîïĨĩĪīĮįİıĵĺļľłˆˇ˘˙˚˛˜˝ІЇії‹›∙",9);Ƈ("？7?Jcz¢¿çćĉċčĴźżžЃЈЧавийнопсъьѓѕќ",17);Ƈ("（）：《》，。、；【】(),.1:;[]ft{}·ţťŧț",10);Ƈ("+<=>E^~¬±¶ÈÉÊË×÷ĒĔĖĘĚЄЏЕНЭ−",19);
Ƈ("L_vx«»ĹĻĽĿŁГгзлхчҐ–•",16);Ƈ("\"-rª­ºŀŕŗř",11);Ƈ("WÆŒŴ—…‰",32);Ƈ("'|¦ˉ‘’‚",7);Ƈ("@©®мшњ",26);Ƈ("mw¼ŵЮщ",28);Ƈ("/ĳтэє",
15);Ƈ("\\°“”„",13);Ƈ("*²³¹",12);Ƈ("¾æœЉ",29);Ƈ("%ĲЫ",25);Ƈ("MМШ",27);Ƈ("½Щ",30);Ƈ("ю",24);Ƈ("ј",8);Ƈ("љ",23);Ƈ("ґ",14);Ƈ(
"™",31);}public static Vector2 Ƌ(this IMyTextSurface X,StringBuilder S){Ɗ();Vector2 Ë=new Vector2();if(X.Font=="Monospace")
{float ç=X.FontSize;Ë.X=(float)(S.Length*19.4*ç);Ë.Y=(float)(28.8*ç);return Ë;}else{float ç=(float)(X.FontSize*0.779);
foreach(char Ÿ in S.ToString()){try{Ë.X+=Ɔ[Ÿ]*ç;}catch{}}Ë.Y=(float)(28.8*X.FontSize);return Ë;}}public static float Ź(this
IMyTextSurface É,StringBuilder S){Vector2 ź=É.Ƌ(S);return ź.X;}public static float Ź(this IMyTextSurface É,string S){Vector2 ź=É.Ƌ(new
StringBuilder(S));return ź.X;}public static float ſ(this IMyTextSurface É,char Ż){float ż=Ź(É,new string(Ż,1));return ż;}public
static int Ž(this IMyTextSurface É){Vector2 Ŕ=É.SurfaceSize;float Ī=É.TextureSize.Y;if(Ŕ.X<512||Ī!=Ŕ.Y)Ŕ.Y*=512/Ī;float ž=Ŕ.Y*
(100-É.TextPadding*2)/100;Vector2 ź=É.Ƌ(new StringBuilder("T"));return(int)(ž/ź.Y);}public static float ƀ(this
IMyTextSurface É){Vector2 Ŕ=É.SurfaceSize;float Ī=É.TextureSize.Y;if(Ŕ.X<512||Ī!=Ŕ.Y)Ŕ.X*=512/Ī;return Ŕ.X*(100-É.TextPadding*2)/100;}
public static StringBuilder ĭ(this IMyTextSurface É,char Į,double į){int İ=(int)(į/ſ(É,Į));if(İ<0)İ=0;return new StringBuilder
().Append(Į,İ);}private static DateTime ı=DateTime.Now;private static Dictionary<int,List<int>>Ĳ=new Dictionary<int,List<
int>>();public static StringBuilder ĳ(this IMyTextSurface É,StringBuilder S,int ļ=3,bool a=true,int Ĵ=0){int ĵ=É.
GetHashCode();if(!Ĳ.ContainsKey(ĵ)){Ĳ[ĵ]=new List<int>{1,3,ļ,0};}int Ķ=Ĳ[ĵ][0];int ķ=Ĳ[ĵ][1];int ĸ=Ĳ[ĵ][2];int Ĺ=Ĳ[ĵ][3];var ĺ=S.
ToString().TrimEnd('\n').Split('\n');List<string>Ļ=new List<string>();if(Ĵ==0)Ĵ=É.Ž();float Ö=É.ƀ();StringBuilder Ì,ī=new
StringBuilder();for(int P=0;P<ĺ.Length;P++){if(P<ļ||P<ĸ||Ļ.Count-ĸ>Ĵ||É.Ź(ĺ[P])<=Ö){Ļ.Add(ĺ[P]);}else{try{ī.Clear();float ę,Ě;var ě=ĺ
[P].Split(' ');string Ĝ=System.Text.RegularExpressions.Regex.Match(ĺ[P],@"\d+(\.|\:)\ ").Value;Ì=É.ĭ(' ',É.Ź(Ĝ));foreach(
var ĝ in ě){ę=É.Ź(ī);Ě=É.Ź(ĝ);if(ę+Ě>Ö){Ļ.Add(ī.ToString());ī=new StringBuilder(Ì+ĝ+" ");}else{ī.Append(ĝ+" ");}}Ļ.Add(ī.
ToString());}catch{Ļ.Add(ĺ[P]);}}}if(a){if(Ļ.Count>Ĵ){if(DateTime.Now.Second!=Ĺ){Ĺ=DateTime.Now.Second;if(ķ>0)ķ--;if(ķ<=0)ĸ+=Ķ;
if(ĸ+Ĵ-ļ>=Ļ.Count&&ķ<=0){Ķ=-1;ķ=3;}if(ĸ<=ļ&&ķ<=0){Ķ=1;ķ=3;}}}else{ĸ=ļ;Ķ=1;ķ=3;}Ĳ[ĵ][0]=Ķ;Ĳ[ĵ][1]=ķ;Ĳ[ĵ][2]=ĸ;Ĳ[ĵ][3]=Ĺ;}
else{ĸ=ļ;}StringBuilder Ğ=new StringBuilder();for(var Û=0;Û<ļ;Û++){Ğ.Append(Ļ[Û]+"\n");}for(var Û=ĸ;Û<Ļ.Count;Û++){Ğ.Append(
Ļ[Û]+"\n");}return Ğ;}public static Dictionary<IMyTextSurface,string>ğ(this IMyTerminalBlock ć,string ĩ,Dictionary<string
,string>Ġ=null){var ġ=new Dictionary<IMyTextSurface,string>();if(ć is IMyTextSurface){ġ[ć as IMyTextSurface]=ć.CustomData
;}else if(ć is IMyTextSurfaceProvider){var Ģ=System.Text.RegularExpressions.Regex.Matches(ć.CustomData,@"@(\d).*("+ĩ+@")"
);int ģ=(ć as IMyTextSurfaceProvider).SurfaceCount;foreach(System.Text.RegularExpressions.Match Ĥ in Ģ){int ĥ=-1;if(int.
TryParse(Ĥ.Groups[1].Value,out ĥ)){if(ĥ>=ģ)continue;string Ħ=ć.CustomData;int ħ=Ħ.IndexOf("@"+ĥ);int Ĩ=Ħ.IndexOf("@",ħ+1)-ħ;
string Y=Ĩ<=0?Ħ.Substring(ħ):Ħ.Substring(ħ,Ĩ);ġ[(ć as IMyTextSurfaceProvider).GetSurface(ĥ)]=Y;}}}return ġ;}public static bool
Ľ(this string Y,string ŉ){var Ħ=Y.Replace(" ","").Split('\n');foreach(var Û in Ħ){if(Û.StartsWith(ŉ+"=")){try{return
Convert.ToBoolean(Û.Replace(ŉ+"=",""));}catch{return true;}}}return true;}public static string Ŋ(this string Y,string ŉ){var Ħ=
Y.Replace(" ","").Split('\n');foreach(var Û in Ħ){if(Û.StartsWith(ŉ+"=")){return Û.Replace(ŉ+"=","");}}return"";}}public
static partial class ł{public static bool ŋ(this double è,double œ,double æ,bool Ō=false,bool ō=false){bool Ŏ=è>=œ;bool ŏ=è<=æ
;if(ō)Ŏ=è>œ;if(Ō)ŏ=è<æ;return Ŏ&&ŏ;}}public static partial class ł{public static string Ő(this char ő,int Œ){if(Œ<=0){
return"";}return new string(ő,Œ);}}public static partial class ł{public static string Ŀ(this double ŀ,double Ł){double ľ=Math.
Round(ŀ/Ł*100,1);if(Ł==0){return"0%";}else{return ľ+"%";}}public static string Ŀ(this float ŀ,float Ł){double ľ=Math.Round(ŀ/
Ł*100,1);if(Ł==0){return"0%";}else{return ľ+"%";}}}public static partial class ł{public static string Ń(this float è,bool
ń=false){string Ņ="MW";string ň=è<0?"-":"";è=Math.Abs(è);if(è<1){è*=1000;Ņ="kW";}else if(è>=1000&&è<1000000){è/=1000;Ņ=
"GW";}else if(è>=1000000&&è<1000000000){è/=1000000;Ņ="TW";}else if(è>=1000000000){è/=1000000000;Ņ="PW";}if(ń)Ņ+="h";return ň
+Math.Round(è,1)+" "+Ņ;}public static string Ń(this double è,bool ń=false){float ņ=(float)è;return ņ.Ń(ń);}}public static
partial class ł{public static string Ň(this float è){string Ņ="L";if(è>=1000&&è<1000000){è/=1000;Ņ="KL";}else if(è>=1000000&&è<
1000000000){è/=1000000;Ņ="ML";}else if(è>=1000000000&&è<1000000000000){è/=1000000000;Ņ="BL";}else if(è>=1000000000000){è/=
1000000000000;Ņ="TL";}return Math.Round(è,1)+" "+Ņ;}public static string Ň(this double è){float ņ=(float)è;return ņ.Ň();}