Lots and lots of changes that have brought the modular system into basic functionality. Configurable values still need to be implemented.
[VOID.git] / VOID_Core.cs
blob:a/VOID_Core.cs -> blob:b/VOID_Core.cs
// // VOID
// VOID_Core.cs //
// // VOID_Core.cs
// Author: //
// toadicus <> // Copyright © 2014, toadicus
// // All rights reserved.
// Copyright (c) 2013 toadicus //
// // Redistribution and use in source and binary forms, with or without modification,
// This program is free software: you can redistribute it and/or modify // are permitted provided that the following conditions are met:
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation, either version 3 of the License, or // 1. Redistributions of source code must retain the above copyright notice,
// (at your option) any later version. // this list of conditions and the following disclaimer.
// //
// This program is distributed in the hope that it will be useful, // 2. Redistributions in binary form must reproduce the above copyright notice,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // this list of conditions and the following disclaimer in the documentation and/or other
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // materials provided with the distribution.
// GNU General Public License for more details. //
// // 3. Neither the name of the copyright holder nor the names of its contributors may be used
// You should have received a copy of the GNU General Public License // to endorse or promote products derived from this software without specific prior written permission.
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
using System; // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
using System.Collections.Generic; // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
using System.Linq; // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
using KSP; // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
using UnityEngine; // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
namespace VOID // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{  
public class VOID_Core : VOID_Module, IVOID_Module using Engineer.VesselSimulator;
{ using KSP;
/* using System;
* Static Members using System.Collections.Generic;
* */ using System.Linq;
protected static VOID_Core _instance; using ToadicusTools;
public static VOID_Core Instance using UnityEngine;
{  
get namespace VOID
{ {
if (_instance == null) public class VOID_Core : VOID_Module, IVOID_Module
{ {
_instance = new VOID_Core(); #region Singleton Members
} /*
return _instance; * Static Members
} * */
} protected static bool _initialized = false;
   
/* public static bool Initialized
* Fields {
* */ get
protected string VoidName = "VOID"; {
protected string VoidVersion = "0.9.9"; return _initialized;
protected int configVersion = 1; }
  }
protected List<VOID_Module> _modules = new List<VOID_Module>();  
  protected static VOID_Core _instance;
protected Rect _mainWindowPos = new Rect(Screen.width / 2, Screen.height / 2, 10f, 10f);  
protected bool mainGuiMinimized = false; public static VOID_Core Instance
  {
protected Rect _configWindowPos = new Rect(Screen.width / 2, Screen.height /2, 10f, 10f); get
protected bool configWindowMinimized = true; {
  if (_instance == null)
protected Rect VOIDIconPos = new Rect(Screen.width / 2 - 200, Screen.height - 30, 30f, 30f); {
protected Texture2D VOIDIconOff = new Texture2D(30, 30, TextureFormat.ARGB32, false); _instance = new VOID_Core();
protected Texture2D VOIDIconOn = new Texture2D(30, 30, TextureFormat.ARGB32, false); _initialized = true;
protected Texture2D VOIDIconTexture; }
protected string VOIDIconOnPath = "VOID/Textures/void_icon_on"; return _instance;
protected string VOIDIconOffPath = "VOID/Textures/void_icon_off"; }
  }
protected int windowBaseID = -96518722;  
  public static void Reset()
public bool togglePower = true; {
public bool powerAvailable = true; _instance.StopGUI();
protected bool consumeResource = false; _instance = null;
protected string resourceName = "ElectricCharge"; _initialized = false;
protected float resourceRate = 0.2f; }
  #endregion
public float saveTimer = 0;  
  public static double Constant_G = 6.674e-11;
protected string defaultSkin = "KSP window 2";  
protected string _skin; /*
  * Fields
/* * */
* Properties protected string VoidName = "VOID";
* */ protected string VoidVersion = "0.11.0";
public List<VOID_Module> Modules  
{ protected bool _factoryReset = false;
get  
{ [AVOID_SaveValue("configValue")]
return this._modules; protected VOID_SaveValue<int> configVersion = 1;
}  
} protected List<IVOID_Module> _modules = new List<IVOID_Module>();
  protected bool _modulesLoaded = false;
public GUISkin Skin  
{ [AVOID_SaveValue("mainWindowPos")]
get protected VOID_SaveValue<Rect> mainWindowPos = new Rect(475, 575, 10f, 10f);
{ [AVOID_SaveValue("mainGuiMinimized")]
if (this._skin == null) protected VOID_SaveValue<bool> mainGuiMinimized = false;
{  
this._skin = this.defaultSkin; [AVOID_SaveValue("configWindowPos")]
} protected VOID_SaveValue<Rect> configWindowPos = new Rect(825, 625, 10f, 10f);
return AssetBase.GetGUISkin(this._skin); [AVOID_SaveValue("configWindowMinimized")]
}  
} protected VOID_SaveValue<bool> configWindowMinimized = true;
  [AVOID_SaveValue("VOIDIconPos")]
public Vessel vessel protected VOID_SaveValue<Rect> VOIDIconPos = new Rect(Screen.width / 2 - 200, Screen.height - 32, 32f, 32f);
{  
get protected Texture2D VOIDIconTexture;
{ protected string VOIDIconOnActivePath;
return FlightGlobals.ActiveVessel; protected string VOIDIconOnInactivePath;
} protected string VOIDIconOffActivePath;
} protected string VOIDIconOffInactivePath;
   
protected Rect mainWindowPos protected bool VOIDIconLocked = true;
{  
get protected GUIStyle iconStyle;
{  
return this._mainWindowPos; protected int windowBaseID = -96518722;
} protected int _windowID = 0;
set  
{ protected bool GUIStylesLoaded = false;
this._mainWindowPos = value; protected Dictionary<string, GUIStyle> _LabelStyles = new Dictionary<string, GUIStyle>();
this.configDirty = true;  
} protected CelestialBody _Kerbin;
}  
  [AVOID_SaveValue("togglePower")]
protected Rect configWindowPos public VOID_SaveValue<bool> togglePower = true;
{ public bool powerAvailable = true;
get  
{ [AVOID_SaveValue("consumeResource")]
return this._configWindowPos; protected VOID_SaveValue<bool> consumeResource = false;
}  
set [AVOID_SaveValue("resourceName")]
{ protected VOID_SaveValue<string> resourceName = "ElectricCharge";
this._configWindowPos = value;  
this.configDirty = true; [AVOID_SaveValue("resourceRate")]
} protected VOID_SaveValue<float> resourceRate = 0.2f;
}  
/* [AVOID_SaveValue("updatePeriod")]
* Methods protected VOID_SaveValue<double> _updatePeriod = 1001f / 15000f;
* */ protected float _updateTimer = 0f;
protected VOID_Core() protected string stringFrequency;
{  
this._Name = "VOID Core"; [AVOID_SaveValue("vesselSimActive")]
  protected VOID_SaveValue<bool> vesselSimActive;
this.VOIDIconOn = GameDatabase.Instance.GetTexture (this.VOIDIconOnPath, false);  
this.VOIDIconOff = GameDatabase.Instance.GetTexture (this.VOIDIconOffPath, false); // Vessel Type Housekeeping
} protected List<VesselType> _allVesselTypes = new List<VesselType>();
  protected bool vesselTypesLoaded = false;
public void LoadModule(Type T) public float saveTimer = 0;
{  
this._modules.Add (Activator.CreateInstance (T) as VOID_Module); protected string defaultSkin = "KSP window 2";
}  
  [AVOID_SaveValue("defaultSkin")]
public void Update() protected VOID_SaveValue<string> _skinName;
{ protected int _skinIdx;
this.saveTimer += Time.deltaTime;  
  protected Dictionary<string, GUISkin> validSkins;
if (!this.guiRunning) protected string[] skinNames;
{ protected string[] forbiddenSkins =
this.StartGUI (); {
} "PlaqueDialogSkin",
  "FlagBrowserSkin",
foreach (VOID_Module module in this.Modules) "SSUITextAreaDefault",
{ "ExperimentsDialogSkin",
if (!module.guiRunning && module.toggleActive) "ExpRecoveryDialogSkin",
{ "KSP window 5",
module.StartGUI (); "KSP window 6",
} "PartTooltipSkin"
if (module.guiRunning && !module.toggleActive || !this.togglePower) };
{ protected bool skinsLoaded = false;
module.StopGUI();  
} public bool configDirty;
}  
  [AVOID_SaveValue("UseBlizzyToolbar")]
if (this.saveTimer > 15f) protected VOID_SaveValue<bool> _UseToolbarManager;
{ internal IButton ToolbarButton;
this.SaveConfig ();  
this.saveTimer = 0; /*
} * Properties
} * */
  public bool factoryReset
public void FixedUpdate() {
{ get
if (this.consumeResource && {
this.vessel.vesselType != VesselType.EVA && return this._factoryReset;
TimeWarp.deltaTime != 0 }
) }
{  
float powerReceived = this.vessel.rootPart.RequestResource(this.resourceName, public List<IVOID_Module> Modules
this.resourceRate * TimeWarp.fixedDeltaTime); {
if (powerReceived > 0) get
{ {
this.powerAvailable = true; return this._modules;
} }
else }
{  
this.powerAvailable = false; public GUISkin Skin
} {
} get
} {
  if (this.skinsLoaded)
public void VOIDMainWindow(int _) {
{ try
GUILayout.BeginVertical(); {
  return this.validSkins[this._skinName];
if (this.powerAvailable) }
{ catch
string str = "ON"; {
if (togglePower) str = "OFF"; }
if (GUILayout.Button("Power " + str)) togglePower = !togglePower; }
if (togglePower)  
{ return AssetBase.GetGUISkin(this.defaultSkin);
foreach (VOID_Module module in this.Modules) }
{ }
module.toggleActive = GUILayout.Toggle (module.toggleActive, module.Name);  
} public int windowID
} {
} get
else {
{ if (this._windowID == 0)
GUIStyle label_txt_red = new GUIStyle(GUI.skin.label); {
label_txt_red.normal.textColor = Color.red; this._windowID = this.windowBaseID;
label_txt_red.alignment = TextAnchor.MiddleCenter; }
GUILayout.Label("-- POWER LOST --", label_txt_red); return this._windowID++;
} }
  }
this.configWindowMinimized = !GUILayout.Toggle (!this.configWindowMinimized, "Configuration");  
  public Dictionary<string, GUIStyle> LabelStyles
GUILayout.EndVertical(); {
GUI.DragWindow(); get
} {
  return this._LabelStyles;
public void VOIDConfigWindow(int _) }
{ }
GUILayout.BeginVertical ();  
  public List<CelestialBody> allBodies
this.consumeResource = GUILayout.Toggle (this.consumeResource, "Consume Resources"); {
  get
GUILayout.EndVertical (); {
GUI.DragWindow (); return FlightGlobals.Bodies;
} }
  }
public override void DrawGUI()  
{ public CelestialBody Kerbin
GUI.skin = this.Skin; {
  get
int windowID = this.windowBaseID; {
  if (this._Kerbin == null)
this.VOIDIconTexture = this.VOIDIconOff; //icon off default {
if (this.togglePower) this.VOIDIconTexture = this.VOIDIconOn; //or on if power_toggle==true if (FlightGlobals.Bodies != null)
if (GUI.Button(new Rect(VOIDIconPos), VOIDIconTexture, new GUIStyle())) {
{ this._Kerbin = FlightGlobals.Bodies.First(b => b.name == "Kerbin");
this.mainGuiMinimized = !this.mainGuiMinimized; }
} }
   
if (!this.mainGuiMinimized) return this._Kerbin;
{ }
this.mainWindowPos = GUILayout.Window ( }
++windowID,  
this.mainWindowPos, public List<VesselType> allVesselTypes
this.VOIDMainWindow, {
string.Join (" ", this.VoidName, this.VoidVersion), get
GUILayout.Width (250), {
GUILayout.Height (50) return this._allVesselTypes;
); }
} }
   
if (!this.configWindowMinimized) public float updateTimer
{ {
this.configWindowPos = GUILayout.Window ( get
++windowID, {
this.configWindowPos, return this._updateTimer;
this.VOIDConfigWindow, }
string.Join (" ", this.VoidName, "Configuration"), }
GUILayout.Width (250),  
GUILayout.Height (50) public double updatePeriod
); {
} get
} {
  return this._updatePeriod;
public new void StartGUI() }
{ }
base.StartGUI ();  
foreach (var module in this._modules) protected IconState powerState
{ {
if (module.toggleActive) get
{ {
module.StartGUI (); if (this.togglePower && this.powerAvailable)
} {
} return IconState.PowerOn;
  }
this._Running = true; else
} {
  return IconState.PowerOff;
public new void StopGUI() }
{  
base.StopGUI (); }
foreach (var module in this._modules) }
{  
if (module.guiRunning) protected IconState activeState
{ {
module.StopGUI (); get
} {
} if (this.mainGuiMinimized)
  {
this._Running = false; return IconState.Inactive;
} }
  else
public override void LoadConfig() {
{ return IconState.Active;
var config = KSP.IO.PluginConfiguration.CreateForType<VOID_Core>(); }
config.load();  
  }
if (this.configVersion > config.GetValue("configVersion", 0)) }
{  
// TODO: Config update stuff. protected bool UseToolbarManager
} {
  get
this.mainWindowPos = config.GetValue("main_window_pos", this.mainWindowPos); {
this.VOIDIconPos = config.GetValue("VOIDIconPos", this.VOIDIconPos); return _UseToolbarManager & ToolbarManager.ToolbarAvailable;
this._skin = config.GetValue ("void_skin", this.defaultSkin); }
this.togglePower = config.GetValue ("togglePower", this.togglePower); set
this.consumeResource = config.GetValue ("consumePower", this.consumeResource); {
  if (this._UseToolbarManager == value)
foreach (VOID_Module module in this.Modules) {
{ return;
module.LoadConfig (); }
}  
} if (value == false && this.ToolbarButton != null)
  {
public override void SaveConfig() this.ToolbarButton.Destroy();
{ this.ToolbarButton = null;
var config = KSP.IO.PluginConfiguration.CreateForType<VOID_Core> (); }
config.load (); if (value == true)
  {
config.SetValue ("main_window_pos", this.mainWindowPos); this.InitializeToolbarButton();
config.SetValue ("VOIDIconPos", this.VOIDIconPos); }
config.SetValue ("void_skin", this.Skin.name);  
config.SetValue ("togglePower", this.togglePower); _UseToolbarManager.value = value;
config.SetValue ("configVersion", this.configVersion); }
config.SetValue ("consumePower", this.consumeResource); }
   
config.save (); /*
  * Methods
foreach (VOID_Module module in this.Modules) * */
{ public override void DrawGUI()
module.SaveConfig (); {
} this._windowID = this.windowBaseID;
   
this.configDirty = false; if (!this._modulesLoaded)
} {
} this.LoadModulesOfType<IVOID_Module>();
} }
   
  if (!this.skinsLoaded)
  {
  this.LoadSkins();
  }
   
  GUI.skin = this.Skin;
   
  if (!this.GUIStylesLoaded)
  {
  this.LoadGUIStyles();
  }
   
  if (!this.UseToolbarManager)
  {
  if (GUI.Button(VOIDIconPos, VOIDIconTexture, this.iconStyle) && this.VOIDIconLocked)
  {
  this.ToggleMainWindow();
  }
  }
  else if (this.ToolbarButton == null)
  {
  this.InitializeToolbarButton();
  }
   
  if (!this.mainGuiMinimized)
  {
   
  Rect _mainWindowPos = this.mainWindowPos;
   
  _mainWindowPos = GUILayout.Window(
  this.windowID,
  _mainWindowPos,
  this.VOIDMainWindow,
  string.Join(" ", new string[] { this.VoidName, this.VoidVersion }),
  GUILayout.Width(250),
  GUILayout.Height(50)
  );
   
  _mainWindowPos = Tools.ClampRectToScreen(_mainWindowPos);
   
  if (_mainWindowPos != this.mainWindowPos)
  {
  this.mainWindowPos = _mainWindowPos;
  }
  }
   
  if (!this.configWindowMinimized && !this.mainGuiMinimized)
  {
  Rect _configWindowPos = this.configWindowPos;
   
  _configWindowPos = GUILayout.Window(
  this.windowID,
  _configWindowPos,
  this.VOIDConfigWindow,
  string.Join(" ", new string[] { this.VoidName, "Configuration" }),
  GUILayout.Width(250),
  GUILayout.Height(50)
  );
   
  _configWindowPos = Tools.ClampRectToScreen(_configWindowPos);
   
  if (_configWindowPos != this.configWindowPos)
  {
  this.configWindowPos = _configWindowPos;
  }
  }
  }
   
  public void OnGUI()
  {
  if (Event.current.type == EventType.Repaint)
  {
  return;
  }
   
  /*
  Tools.PostDebugMessage(string.Format(
  "Event.current.type: {0}" +
  "\nthis.VOIDIconLocked: {1}" +
  "\nEvent.current.mousePosition: {2}" +
  "\nVOIDIconPos: ({3}, {4}),({5}, {6})",
  Event.current.type,
  this.VOIDIconLocked,
  Event.current.mousePosition,
  this.VOIDIconPos.value.xMin,
  this.VOIDIconPos.value.yMin,
  this.VOIDIconPos.value.xMax,
  this.VOIDIconPos.value.yMax
  ));
  */
   
  if (!this.VOIDIconLocked &&
  VOIDIconPos.value.Contains(Event.current.mousePosition)
  && Event.current.type == EventType.mouseDrag)
  {
  Tools.PostDebugMessage(string.Format(
  "Event.current.type: {0}" +
  "\ndelta.x: {1}; delta.y: {2}",
  Event.current.type,
  Event.current.delta.x,
  Event.current.delta.y
  ));
   
  Rect tmp = new Rect(VOIDIconPos);
   
  tmp.x = Event.current.mousePosition.x - tmp.width / 2;
  tmp.y = Event.current.mousePosition.y - tmp.height / 2;
   
  if (tmp.x > Screen.width - tmp.width)
  {
  tmp.x = Screen.width - tmp.width;
  }
   
  if (tmp.y > Screen.height - tmp.height)
  {
  tmp.y = Screen.height - tmp.height;
  }
   
  VOIDIconPos = tmp;
  }
  }
   
  public void Update()
  {
  this.LoadBeforeUpdate();
   
  if (this.vessel != null && this.vesselSimActive)
  {
  SimManager.Gravity = VOID_Core.Instance.vessel.mainBody.gravParameter /
  Math.Pow(VOID_Core.Instance.vessel.Radius(), 2);
  SimManager.TryStartSimulation();
  }
  else if (!this.vesselSimActive)
  {
  SimManager.ClearResults();
  }
   
  if (!this.guiRunning)
  {
  this.StartGUI();
  }
   
  if (!HighLogic.LoadedSceneIsFlight && this.guiRunning)
  {
  this.StopGUI();
  }
   
  foreach (IVOID_Module module in this.Modules)
  {
  if (!module.guiRunning && module.toggleActive)
  {
  module.StartGUI();
  }
  if (module.guiRunning && !module.toggleActive ||
  !this.togglePower ||
  !HighLogic.LoadedSceneIsFlight ||
  this.factoryReset)
  {
  module.StopGUI();
  }
   
  if (module is IVOID_BehaviorModule)
  {
  ((IVOID_BehaviorModule)module).Update();
  }
  }
   
  this.CheckAndSave();
  this._updateTimer += Time.deltaTime;
  }
   
  public void FixedUpdate()
  {
  bool newPowerState = this.powerAvailable;
   
  if (this.togglePower && this.consumeResource &&
  this.vessel.vesselType != VesselType.EVA &&
  TimeWarp.deltaTime != 0)
  {
  float powerReceived = this.vessel.rootPart.RequestResource(
  this.resourceName,
  this.resourceRate * TimeWarp.fixedDeltaTime
  );
   
  if (powerReceived > 0)
  {
  newPowerState = true;
  }
  else
  {
  newPowerState = false;
  }
   
  if (this.powerAvailable != newPowerState)
  {
  this.powerAvailable = newPowerState;
  this.SetIconTexture(this.powerState | this.activeState);
  }
  }
   
  foreach (IVOID_BehaviorModule module in
  this._modules.OfType<IVOID_BehaviorModule>().Where(m => !m.GetType().IsAbstract))
  {
  module.FixedUpdate();
  }
  }
   
  public void ResetGUI()
  {
  this.StopGUI();
   
  foreach (IVOID_Module module in this.Modules)
  {
  module.StopGUI();
  module.StartGUI();
  }
   
  this.StartGUI();
  }
   
  public void VOIDMainWindow(int _)
  {
  GUILayout.BeginVertical();
   
  if (this.powerAvailable || HighLogic.LoadedSceneIsEditor)
  {
  if (!HighLogic.LoadedSceneIsEditor)
  {
  string str = string.Intern("ON");
  if (togglePower)
  str = string.Intern("OFF");
  if (GUILayout.Button("Power " + str))
  {
  togglePower.value = !togglePower;
  this.SetIconTexture(this.powerState | this.activeState);
  }
  }
   
  if (togglePower || HighLogic.LoadedSceneIsEditor)
  {
  foreach (IVOID_Module module in this.Modules)
  {
  module.toggleActive = GUILayout.Toggle(module.toggleActive, module.Name);
  }
  }
  }
  else
  {
  GUILayout.Label("-- POWER LOST --", this.LabelStyles["red"]);
  }
   
  this.configWindowMinimized.value = !GUILayout.Toggle(!this.configWindowMinimized, "Configuration");
   
  GUILayout.EndVertical();
  GUI.DragWindow();
  }
   
  public void VOIDConfigWindow(int _)
  {
  GUILayout.BeginVertical();
   
  this.DrawConfigurables();
   
  GUILayout.EndVertical();
  GUI.DragWindow();
  }
   
  public override void DrawConfigurables()
  {
  GUIContent _content;
   
  if (HighLogic.LoadedSceneIsFlight)
  {
  this.consumeResource.value = GUILayout.Toggle(this.consumeResource, "Consume Resources");
   
  this.VOIDIconLocked = GUILayout.Toggle(this.VOIDIconLocked, "Lock Icon Position");
  }
   
  this.UseToolbarManager = GUILayout.Toggle(this.UseToolbarManager, "Use Blizzy's Toolbar If Available");
   
  this.vesselSimActive.value = GUILayout.Toggle(this.vesselSimActive.value,
  "Enable Engineering Calculations");
   
  GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
   
  GUILayout.Label("Skin:", GUILayout.ExpandWidth(false));
   
  _content = new GUIContent();
   
  _content.text = "◄";
  _content.tooltip = "Select previous skin";
  if (GUILayout.Button(_content, GUILayout.ExpandWidth(true)))
  {
  this.GUIStylesLoaded = false;
  this._skinIdx--;
  Tools.PostDebugMessage(string.Format(
  "{0}: new this._skinIdx = {1} :: skin_list.Count = {2}",
  this.GetType().Name,
  this._skinName,
  this.validSkins.Count
  ));
  }
   
  _content.text = this.Skin.name;
  _content.tooltip = "Current skin";
  GUILayout.Label(_content, this.LabelStyles["center"], GUILayout.ExpandWidth(true));
   
  _content.text = "►";
  _content.tooltip = "Select next skin";
  if (GUILayout.Button(_content, GUILayout.ExpandWidth(true)))
  {
  this.GUIStylesLoaded = false;
  this._skinIdx++;
  Tools.PostDebugMessage(string.Format(
  "{0}: new this._skinIdx = {1} :: skin_list.Count = {2}",
  this.GetType().Name,
  this._skinName,
  this.validSkins.Count
  ));
  }
   
  this._skinIdx %= this.skinNames.Length;
  if (this._skinIdx < 0)
  {
  this._skinIdx += this.skinNames.Length;
  }
   
  if (this._skinName != skinNames[this._skinIdx])
  {
  this._skinName.value = skinNames[this._skinIdx];
  }
   
  GUILayout.EndHorizontal();
   
  GUILayout.BeginHorizontal();
  GUILayout.Label("Update Rate (Hz):");
  if (this.stringFrequency == null)
  {
  this.stringFrequency = (1f / this.updatePeriod).ToString();
  }
  this.stringFrequency = GUILayout.TextField(this.stringFrequency.ToString(), 5, GUILayout.ExpandWidth(true));
  // GUILayout.FlexibleSpace();
  if (GUILayout.Button("Apply"))
  {
  double updateFreq = 1f / this.updatePeriod;
  double.TryParse(stringFrequency, out updateFreq);
  this._updatePeriod = 1 / updateFreq;
  }
  GUILayout.EndHorizontal();
   
  foreach (IVOID_Module mod in this.Modules)
  {
  mod.DrawConfigurables();
  }
   
  this._factoryReset = GUILayout.Toggle(this._factoryReset, "Factory Reset");
  }
   
  protected void LoadModulesOfType<T>()
  {
  var types = AssemblyLoader.loadedAssemblies
  .Select(a => a.assembly.GetExportedTypes())
  .SelectMany(t => t)
  .Where(v => typeof(T).IsAssignableFrom(v)
  && !(v.IsInterface || v.IsAbstract) &&
  !typeof(VOID_Core).IsAssignableFrom(v)
  );
   
  Tools.PostDebugMessage(string.Format(
  "{0}: Found {1} modules to check.",
  this.GetType().Name,
  types.Count()
  ));
  foreach (var voidType in types)
  {
  if (!HighLogic.LoadedSceneIsEditor &&
  typeof(IVOID_EditorModule).IsAssignableFrom(voidType))
  {
  continue;
  }
   
  Tools.PostDebugMessage(string.Format(
  "{0}: found Type {1}",
  this.GetType().Name,
  voidType.Name
  ));
   
  this.LoadModule(voidType);
  }
   
  this._modulesLoaded = true;
   
  Tools.PostDebugMessage(string.Format(
  "{0}: Loaded {1} modules.",
  this.GetType().Name,
  this.Modules.Count
  ));
  }
   
  protected void LoadModule(Type T)
  {
  var existingModules = this._modules.Where(mod => mod.GetType().Name == T.Name);
  if (existingModules.Any())
  {
  Tools.PostDebugMessage(string.Format(
  "{0}: refusing to load {1}: already loaded",
  this.GetType().Name,
  T.Name
  ));
  return;
  }
  IVOID_Module module = Activator.CreateInstance(T) as IVOID_Module;
  module.LoadConfig();
  this._modules.Add(module);
   
  Tools.PostDebugMessage(string.Format(
  "{0}: loaded module {1}.",
  this.GetType().Name,
  T.Name
  ));
  }
   
  protected void LoadSkins()
  {
  Tools.PostDebugMessage("AssetBase has skins: \n" +
  string.Join("\n\t",
  Resources.FindObjectsOfTypeAll(typeof(GUISkin))
  .Select(s => s.ToString())
  .ToArray()
  )
  );
   
  this.validSkins = Resources.FindObjectsOfTypeAll(typeof(GUISkin))
  .Where(s => !this.forbiddenSkins.Contains(s.name))
  .Select(s => s as GUISkin)
  .GroupBy(s => s.name)
  .Select(g => g.First())
  .ToDictionary(s => s.name);
   
  Tools.PostDebugMessage(string.Format(
  "{0}: loaded {1} GUISkins.",
  this.GetType().Name,
  this.validSkins.Count
  ));
   
  this.skinNames = this.validSkins.Keys.ToArray();
  Array.Sort(this.skinNames);
   
  int defaultIdx = int.MinValue;
   
  for (int i = 0; i < this.skinNames.Length; i++)
  {
  if (this.skinNames[i] == this._skinName)
  {
  this._skinIdx = i;
  }
  if (this.skinNames[i] == this.defaultSkin)
  {
  defaultIdx = i;
  }
  if (this._skinIdx != int.MinValue && defaultIdx != int.MinValue)
  {
  break;
  }
  }
   
  if (this._skinIdx == int.MinValue)
  {
  this._skinIdx = defaultIdx;
  }
   
  Tools.PostDebugMessage(string.Format(
  "{0}: _skinIdx = {1}.",
  this.GetType().Name,
  this._skinName.ToString()
  ));
   
  this.skinsLoaded = true;
  }
   
  protected void LoadGUIStyles()
  {
  this.LabelStyles["link"] = new GUIStyle(GUI.skin.label);
  this.LabelStyles["link"].fontStyle = FontStyle.Bold;
   
  this.LabelStyles["center"] = new GUIStyle(GUI.skin.label);
  this.LabelStyles["center"].normal.textColor = Color.white;
  this.LabelStyles["center"].alignment = TextAnchor.UpperCenter;
   
  this.LabelStyles["center_bold"] = new GUIStyle(GUI.skin.label);
  this.LabelStyles["center_bold"].normal.textColor = Color.white;
  this.LabelStyles["center_bold"].alignment = TextAnchor.UpperCenter;
  this.LabelStyles["center_bold"].fontStyle = FontStyle.Bold;
   
  this.LabelStyles["right"] = new GUIStyle(GUI.skin.label);
  this.LabelStyles["right"].normal.textColor = Color.white;
  this.LabelStyles["right"].alignment = TextAnchor.UpperRight;
   
  this.LabelStyles["red"] = new GUIStyle(GUI.skin.label);
  this.LabelStyles["red"].normal.textColor = Color.red;
  this.LabelStyles["red"].alignment = TextAnchor.MiddleCenter;
   
  this.iconStyle = new GUIStyle(GUI.skin.button);
  this.iconStyle.padding = new RectOffset(0, 0, 0, 0);
  // this.iconStyle.margin = new RectOffset(0, 0, 0, 0);
  // this.iconStyle.contentOffset = new Vector2(0, 0);
  this.iconStyle.overflow = new RectOffset(0, 0, 0, 0);
  // this.iconStyle.border = new RectOffset(0, 0, 0, 0);
   
  this.GUIStylesLoaded = true;
  }
   
  protected void LoadVesselTypes()
  {
  this._allVesselTypes = Enum.GetValues(typeof(VesselType)).OfType<VesselType>().ToList();
  this.vesselTypesLoaded = true;
  }
   
  protected void LoadBeforeUpdate()
  {
  if (!this.vesselTypesLoaded)
  {
  this.LoadVesselTypes();
  }
  }
   
  protected void InitializeToolbarButton()
  {
  // Do nothing if the Toolbar is not available.
  if (!ToolbarManager.ToolbarAvailable)
  {
  return;
  }
   
  this.ToolbarButton = ToolbarManager.Instance.add(this.VoidName, "coreToggle");
  this.ToolbarButton.Text = this.VoidName;
  this.SetIconTexture(this.powerState | this.activeState);
   
  this.ToolbarButton.Visibility = new GameScenesVisibility(GameScenes.EDITOR, GameScenes.FLIGHT, GameScenes.SPH);
   
  this.ToolbarButton.OnClick +=
  (e) =>
  {
  this.ToggleMainWindow();
  };
   
  Tools.PostDebugMessage(string.Format("{0}: Toolbar Button initialized.", this.GetType().Name));
  }
   
  protected void ToggleMainWindow()
  {
  this.mainGuiMinimized = !this.mainGuiMinimized;
  this.SetIconTexture(this.powerState | this.activeState);
  }
   
  protected void SetIconTexture(IconState state)
  {
  switch (state)
  {
  case (IconState.PowerOff | IconState.Inactive):
  this.SetIconTexture(this.VOIDIconOffInactivePath);
  break;
  case (IconState.PowerOff | IconState.Active):
  this.SetIconTexture(this.VOIDIconOffActivePath);
  break;
  case (IconState.PowerOn | IconState.Inactive):
  this.SetIconTexture(this.VOIDIconOnInactivePath);
  break;
  case (IconState.PowerOn | IconState.Active):
  this.SetIconTexture(this.VOIDIconOnActivePath);
  break;
  default:
  throw new NotImplementedException();
  }
  }
   
  protected void SetIconTexture(string texturePath)
  {
  if (this.ToolbarButton != null)
  {
  this.ToolbarButton.TexturePath = texturePath;
  }
   
  this.VOIDIconTexture = GameDatabase.Instance.GetTexture(texturePath, false);
  }
   
  protected void CheckAndSave()
  {
  this.saveTimer += Time.deltaTime;
   
  if (this.saveTimer > 2f)
  {
  if (!this.configDirty)
  {
  return;
  }
   
  Tools.PostDebugMessage(string.Format(
  "{0}: Time to save, checking if configDirty: {1}",
  this.GetType().Name,
  this.configDirty
  ));
   
  this.SaveConfig();
  this.saveTimer = 0;
  }
  }
   
  public override void LoadConfig()
  {
  base.LoadConfig();
   
  foreach (IVOID_Module module in this.Modules)
  {
  module.LoadConfig();
  }
  }
   
  public void SaveConfig()
  {
  var config = KSP.IO.PluginConfiguration.CreateForType<VOID_Core>();
  config.load();
   
  this._SaveToConfig(config);
   
  foreach (IVOID_Module module in this.Modules)
  {
  module._SaveToConfig(config);
  }
   
  config.save();
   
  this.configDirty = false;
  }
   
  protected VOID_Core()
  {
  this._Name = "VOID Core";
   
  this._Active.value = true;
   
  this._skinName = this.defaultSkin;
  this._skinIdx = int.MinValue;
   
  this.VOIDIconOnInactivePath = "VOID/Textures/void_icon_light_glow";
  this.VOIDIconOnActivePath = "VOID/Textures/void_icon_dark_glow";
  this.VOIDIconOffInactivePath = "VOID/Textures/void_icon_light";
  this.VOIDIconOffActivePath = "VOID/Textures/void_icon_dark";
   
  this.vesselSimActive = true;
   
  this.UseToolbarManager = false;
   
  this.LoadConfig();
   
  this.SetIconTexture(this.powerState | this.activeState);
  }
   
  protected enum IconState
  {
  PowerOff = 1,
  PowerOn = 2,
  Inactive = 4,
  Active = 8
  }
  }
   
  public static partial class VOID_Data
  {
  public static VOID_Core core
  {
  get
  {
  return VOID_Core.Instance;
  }
  }
   
  public static double KerbinGee
  {
  get
  {
  return core.Kerbin.gravParameter / (core.Kerbin.Radius * core.Kerbin.Radius);
  }
  }
  }
  }