Changes to Module, Core, and throughout to support module filtering by game mode. Also tweaks to CareerStatus.formatDelta.
Changes to Module, Core, and throughout to support module filtering by game mode. Also tweaks to CareerStatus.formatDelta.

--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,13 @@
 * text=auto
 * eol=lf
 
+# These files are text and should be normalized (convert crlf => lf)
+*.cs      text diff=csharp
+*.cfg     text
+*.csproj  text
+*.sln     text
+
+# Images should be treated as binary
+# (binary is a macro for -text -diff)
+*.png     binary
+

--- /dev/null
+++ b/API/Attributes/AVOID_SaveValue.cs
@@ -1,1 +1,48 @@
+// VOID
+//
+// AVOID_SaveValue.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+using System;
 
+namespace VOID
+{
+	[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+	public class AVOID_SaveValue : Attribute
+	{
+		public string Name
+		{
+			get;
+			private set;
+		}
+
+		public AVOID_SaveValue(string fieldName)
+		{
+			this.Name = fieldName;
+		}
+	}
+}
+
+

--- /dev/null
+++ b/API/Attributes/VOID_GameModesAttribute.cs
@@ -1,1 +1,48 @@
+// VOID
+//
+// VOID_ScenesAttribute.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+using System;
 
+namespace VOID
+{
+	[AttributeUsage(AttributeTargets.Class)]
+	public class VOID_ScenesAttribute : Attribute
+	{
+		public GameScenes[] ValidScenes
+		{
+			get;
+			private set;
+		}
+
+		public VOID_ScenesAttribute(params GameScenes[] validScenes)
+		{
+			this.ValidScenes = validScenes;
+		}
+	}
+}
+
+

--- /dev/null
+++ b/API/Attributes/VOID_ScenesAttribute.cs
@@ -1,1 +1,48 @@
+// VOID
+//
+// VOID_ScenesAttribute.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+using System;
 
+namespace VOID
+{
+	[AttributeUsage(AttributeTargets.Class)]
+	public class VOID_GameModesAttribute : Attribute
+	{
+		public Game.Modes[] ValidModes
+		{
+			get;
+			private set;
+		}
+
+		public VOID_GameModesAttribute(params Game.Modes[] validModes)
+		{
+			this.ValidModes = validModes;
+		}
+	}
+}
+
+

--- /dev/null
+++ b/API/IVOID_DataValue.cs
@@ -1,1 +1,44 @@
+// VOID
+//
+// IVOID_DataValue.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+using System;
 
+namespace VOID
+{
+	public interface IVOID_DataValue
+	{
+		string Label { get; }
+		string Units { get; }
+		object Value { get; }
+
+		void Refresh();
+		string ValueUnitString();
+		void DoGUIHorizontal();
+	}
+}
+
+

--- /dev/null
+++ b/API/IVOID_Module.cs
@@ -1,1 +1,59 @@
+// VOID
+//
+// IVOID_Module.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+using System;
+
+namespace VOID
+{
+	public interface IVOID_Module
+	{
+		string Name { get; }
+		bool toggleActive { get; set; }
+		bool guiRunning { get; }
+		bool inValidScene { get; }
+		bool inValidGame { get; }
+
+		void DrawGUI();
+		void StartGUI();
+		void StopGUI();
+
+		void DrawConfigurables();
+
+		void LoadConfig();
+
+		void _SaveToConfig(KSP.IO.PluginConfiguration config);
+	}
+
+	public interface IVOID_BehaviorModule : IVOID_Module
+	{
+		void Update();
+		void FixedUpdate();
+		void OnDestroy();
+	}
+}
+

--- /dev/null
+++ b/API/IVOID_SaveValue.cs
@@ -1,1 +1,40 @@
+// VOID
+//
+// IVOID_SaveValue.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+using System;
 
+namespace VOID
+{
+	public interface IVOID_SaveValue
+	{
+		Type type { get; }
+		object value { get; }
+		void SetValue(object v);
+	}
+}
+
+

file:b/API/VOIDCore.cs (new)
--- /dev/null
+++ b/API/VOIDCore.cs
@@ -1,1 +1,97 @@
+// VOID
+//
+// IVOID_Core.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+using KerbalEngineer.VesselSimulator;
+using KSP;
+using System;
+using System.Collections.Generic;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	public abstract class VOIDCore : VOID_Module, IVOID_Module
+	{
+		public const double Constant_G = 6.674e-11;
+		public const int CONFIG_VERSION = 2;
+
+		public static bool useToolbarManager;
+
+		public abstract int configVersion { get; }
+		public virtual bool configNeedsUpdate { get; set; }
+
+		public abstract int windowID { get; }
+		public abstract bool configDirty { get; set; }
+		public abstract bool powerAvailable	{ get; protected set; }
+
+		public abstract List<IVOID_Module> Modules { get; }
+
+		public abstract float updateTimer { get; protected set; }
+		public abstract double updatePeriod { get; }
+
+		public virtual float saveTimer { get; protected set; }
+
+		public abstract GUISkin Skin { get; }
+
+		public abstract CelestialBody HomeBody { get; }
+		public abstract List<CelestialBody> allBodies { get; }
+		public abstract List<CelestialBody> sortedBodyList { get; protected set; }
+
+		public abstract List<VesselType> allVesselTypes { get; }
+
+		public abstract Stage LastStage { get; protected set; }
+		public abstract Stage[] Stages { get; protected set; }
+
+		public abstract event VOIDEventHandler onApplicationQuit;
+		public abstract event VOIDEventHandler onSkinChanged;
+
+		public virtual void OnGUI() {}
+
+		public override void LoadConfig()
+		{
+			var config = KSP.IO.PluginConfiguration.CreateForType<VOIDCore>(null);
+
+			useToolbarManager = config.GetValue("UseToolbarManager", useToolbarManager);
+
+			base.LoadConfig();
+		}
+
+		public abstract void SaveConfig();
+
+		public override void _SaveToConfig(KSP.IO.PluginConfiguration config)
+		{
+			config.SetValue("UseToolbarManager", useToolbarManager);
+
+			base._SaveToConfig(config);
+		}
+	}
+
+	public delegate void VOIDEventHandler(object sender);
+}
+
+

file:b/API/VOIDMaster.cs (new)
--- /dev/null
+++ b/API/VOIDMaster.cs
@@ -1,1 +1,160 @@
+// VOID
+//
+// VOIDMaster.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+//  Much, much credit to Younata, Adammada, Nivvydaskrl and to all the authors
+//  behind MechJeb, RemoteTech Relay Network, ISA MapSat, and Protractor for some
+//  invaluable functions and making your nicely written code available to learn from.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+//  This software uses VesselSimulator and Engineer.Extensions from Engineer Redux.
+//  Engineer Redux (c) 2013 cybutek
+//  Used by permission.
+//
+///////////////////////////////////////////////////////////////////////////////
 
+using System;
+using UnityEngine;
+using KerbalEngineer.VesselSimulator;
+using ToadicusTools;
+
+namespace VOID
+{
+	public abstract class VOIDMaster<T> : MonoBehaviour
+		where T : VOIDCore_Generic<T>, new()
+	{
+		protected T Core;
+
+		public abstract void Awake();
+
+		public virtual void Update()
+		{
+			if (this.Core != null && !this.InValidScene())
+			{
+				this.LogDebug("We have a Core but the scene is not valid for this master.  Saving and disposing.");
+
+				this.Core.SaveConfig ();
+				this.Core.Dispose();
+				this.Core = null;
+				return;
+			}
+
+			if (this.Core == null && this.InValidScene())
+			{
+				this.LogDebug("We have no Core and the scene is valid for this master; re-trying Awake.");
+				this.Awake();
+				return;
+			}
+
+			this.Core.Update ();
+
+			if (this.Core.factoryReset)
+			{
+				this.LogDebug("Factory reset is true; deleting config and disposing!");
+
+				KSP.IO.File.Delete<T>("config.xml");
+				this.Core.Dispose();
+				this.Core = null;
+			}
+		}
+
+		public virtual void FixedUpdate()
+		{
+			if (this.Core == null)
+			{
+				return;
+			}
+
+			this.Core.FixedUpdate ();
+		}
+
+		public virtual void OnGUI()
+		{
+			if (this.Core == null)
+			{
+				return;
+			}
+
+			this.Core.OnGUI();
+		}
+
+		public virtual void OnDestroy()
+		{
+			if (this.Core == null)
+			{
+				return;
+			}
+
+			this.Core.OnDestroy();
+		}
+
+		public virtual void OnApplicationQuit()
+		{
+			if (this.Core == null)
+			{
+				return;
+			}
+
+			this.Core.OnApplicationQuit();
+		}
+
+		protected virtual bool InValidScene()
+		{
+			foreach (var attr in this.GetType().GetCustomAttributes(true))
+			{
+				if (attr is KSPAddon)
+				{
+					KSPAddon addonAttr = (KSPAddon)attr;
+
+					switch (addonAttr.startup)
+					{
+						case KSPAddon.Startup.EveryScene:
+							return true;
+						case KSPAddon.Startup.EditorAny:
+							return HighLogic.LoadedSceneIsEditor;
+						case KSPAddon.Startup.Flight:
+							return HighLogic.LoadedSceneIsFlight;
+						case KSPAddon.Startup.MainMenu:
+							return HighLogic.LoadedScene == GameScenes.MAINMENU;
+						case KSPAddon.Startup.SpaceCentre:
+							return HighLogic.LoadedScene == GameScenes.SPACECENTER;
+						case KSPAddon.Startup.TrackingStation:
+							return HighLogic.LoadedScene == GameScenes.TRACKSTATION;
+						default:
+							return false;
+					}
+				}
+			}
+
+			return false;
+		}
+	}
+}
+

--- /dev/null
+++ b/API/VOID_HUDModule.cs
@@ -1,1 +1,219 @@
-
+// VOID
+//
+// VOID_HUDModule.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using KerbalEngineer.VesselSimulator;
+using KSP;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	public abstract class VOID_HUDModule : VOID_Module
+	{
+		[AVOID_SaveValue("colorIndex")]
+		protected VOID_SaveValue<int> _colorIndex;
+
+		protected List<Color> textColors;
+
+		[AVOID_SaveValue("positionsLocked")]
+		protected VOID_SaveValue<bool> positionsLocked;
+
+		public virtual int ColorIndex
+		{
+			get
+			{
+				return this._colorIndex;
+			}
+			set
+			{
+				if (this._colorIndex >= this.textColors.Count - 1)
+				{
+					this._colorIndex = 0;
+					return;
+				}
+
+				this._colorIndex = value;
+			}
+		}
+
+		public virtual List<HUDWindow> Windows
+		{
+			get;
+			protected set;
+		}
+
+		public VOID_HUDModule() : base()
+		{
+			this._colorIndex = 0;
+
+			this.textColors = new List<Color>();
+
+			this.textColors.Add(Color.green);
+			this.textColors.Add(Color.black);
+			this.textColors.Add(Color.white);
+			this.textColors.Add(Color.red);
+			this.textColors.Add(Color.blue);
+			this.textColors.Add(Color.yellow);
+			this.textColors.Add(Color.gray);
+			this.textColors.Add(Color.cyan);
+			this.textColors.Add(Color.magenta);
+
+			this.positionsLocked = true;
+
+			this.Windows = new List<HUDWindow>();
+		}
+
+		public override void DrawGUI()
+		{
+			VOID_Styles.labelHud.normal.textColor = textColors [ColorIndex];
+
+			GUI.skin = this.core.Skin;
+
+			if (HighLogic.LoadedSceneIsEditor ||
+				(TimeWarp.WarpMode == TimeWarp.Modes.LOW) || (TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate)
+			)
+			{
+				SimManager.RequestSimulation();
+			}
+
+			foreach (HUDWindow window in this.Windows)
+			{
+				window.WindowPos = GUILayout.Window(
+					this.core.windowID,
+					window.WindowPos,
+					VOID_Tools.GetWindowHandler(window.WindowFunction),
+					GUIContent.none,
+					GUIStyle.none
+				);
+			}
+		}
+
+		public override void DrawConfigurables()
+		{
+			base.DrawConfigurables();
+
+			if (GUILayout.Button (string.Intern("Change HUD color"), GUILayout.ExpandWidth (false)))
+			{
+				++this.ColorIndex;
+			}
+
+			if (GUILayout.Button(string.Intern("Reset HUD Positions"), GUILayout.ExpandWidth(false)))
+			{
+				foreach (HUDWindow window in this.Windows)
+				{
+					window.WindowPos = new Rect(window.defaultWindowPos);
+				}
+			}
+
+			this.positionsLocked = GUILayout.Toggle(this.positionsLocked,
+				string.Intern("Lock HUD Positions"),
+				GUILayout.ExpandWidth(false));
+		}
+
+		public override void LoadConfig()
+		{
+			base.LoadConfig();
+
+			var config = KSP.IO.PluginConfiguration.CreateForType<VOID_HUDModule>();
+			config.load();
+
+			foreach (HUDWindow window in this.Windows)
+			{
+				string saveName = string.Format("{0}_{1}", this.GetType().Name, window.WindowName);
+				Rect loadedPos = config.GetValue(saveName, window.defaultWindowPos);
+
+				window.WindowPos = loadedPos;
+			}
+		}
+
+		public override void _SaveToConfig(KSP.IO.PluginConfiguration config)
+		{
+			base._SaveToConfig(config);
+
+			foreach (HUDWindow window in this.Windows)
+			{
+				string saveName = string.Format("{0}_{1}", this.GetType().Name, window.WindowName);
+				config.SetValue(saveName, window.WindowPos);
+			}
+		}
+	}
+
+	public class HUDWindow
+	{
+		public readonly Rect defaultWindowPos;
+
+		private Rect _windowPos;
+
+		public Action<int> WindowFunction
+		{
+			get;
+			private set;
+		}
+
+		public Rect WindowPos
+		{
+			get
+			{
+				return this._windowPos;
+			}
+			set
+			{
+				if (value != this._windowPos)
+				{
+					this._windowPos = value;
+
+					if (VOID_Data.Core != null)
+					{
+						VOID_Data.Core.configDirty = true;
+					}
+				}
+			}
+		}
+
+		public string WindowName
+		{
+			get;
+			private set;
+		}
+
+		private HUDWindow() {}
+
+		public HUDWindow(string name, Action<int> windowFunc, Rect defaultPos)
+		{
+			this.WindowName = name;
+			this.WindowFunction = windowFunc;
+			this.defaultWindowPos = defaultPos;
+			this.WindowPos = new Rect(this.defaultWindowPos);
+		}
+	}
+}
+
+

--- /dev/null
+++ b/API/VOID_Module.cs
@@ -1,1 +1,526 @@
-
+// VOID
+//
+// VOID_Module.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	public abstract class VOID_Module : IVOID_Module
+	{
+		/*
+		 * Fields
+		 * */
+		[AVOID_SaveValue("Active")]
+		protected VOID_SaveValue<bool> _Active = false;
+		private GameScenes[] validScenes;
+		private Game.Modes[] validModes;
+
+		protected float lastUpdate = 0;
+
+		/*
+		 * Properties
+		 * */
+		protected virtual VOIDCore core
+		{
+			get
+			{
+				return VOID_Data.Core;
+			}
+		}
+
+		protected virtual bool timeToUpdate
+		{
+			get
+			{
+				return (
+					(this.core.updateTimer - this.lastUpdate) > this.core.updatePeriod ||
+					this.lastUpdate > this.core.updateTimer
+				);
+			}
+		}
+
+		public virtual bool toggleActive
+		{
+			get
+			{
+				return this._Active && this.inValidGame && this.inValidScene;
+			}
+			set
+			{
+				this._Active = value && this.inValidGame && this.inValidScene;
+			}
+		}
+
+		public virtual bool guiRunning
+		{
+			get
+			{
+				if (
+					RenderingManager.fetch == null ||
+					RenderingManager.fetch.postDrawQueue == null ||
+					RenderingManager.fetch.postDrawQueue.Length < 4
+				)
+				{
+					return false;
+				}
+				else
+				{
+					Delegate callback = RenderingManager.fetch.postDrawQueue[3];
+					if (callback == null)
+					{
+						return false;
+					}
+
+					return callback.GetInvocationList().Contains((Callback)this.DrawGUI);
+				}
+			}
+		}
+
+		public virtual GameScenes[] ValidScenes
+		{
+			get
+			{
+				if (this.validScenes == null)
+				{
+					Tools.PostDebugMessage(this, "validScenes is null when checking inValidScene; fetching attribute.");
+					foreach (var attr in this.GetType().GetCustomAttributes(false))
+					{
+						if (attr is VOID_ScenesAttribute)
+						{
+							VOID_ScenesAttribute addonAttr = (VOID_ScenesAttribute)attr;
+
+							this.validScenes = addonAttr.ValidScenes;
+
+							Tools.PostDebugMessage("Found VOID_ScenesAttribute; validScenes set.");
+
+							break;
+						}
+					}
+
+					if (this.validScenes == null)
+					{
+						this.validScenes = new GameScenes[] { GameScenes.FLIGHT };
+						Tools.PostDebugMessage("No VOID_ScenesAttribute found; validScenes defaulted to flight.");
+					}
+				}
+
+				return this.validScenes;
+			}
+		}
+
+		public virtual bool inValidScene
+		{
+			get
+			{
+
+				Tools.PostDebugMessage(
+					this,
+					"Checking if scene is valid: LoadedScene={0}, ValidScenes={1}, inValidScene={2}",
+					Enum.GetName(typeof(GameScenes), HighLogic.LoadedScene),
+					string.Join(", ", this.ValidScenes.Select(s => Enum.GetName(typeof(GameScenes), s)).ToArray()),
+					this.ValidScenes.Contains(HighLogic.LoadedScene)
+				);
+
+				return this.ValidScenes.Contains(HighLogic.LoadedScene);
+			}
+		}
+
+		public virtual Game.Modes[] ValidModes
+		{
+			get
+			{
+				if (this.validModes == null)
+				{
+					Tools.PostDebugMessage(this, "validModes is null when checking inValidGame; fetching attribute.");
+					foreach (var attr in this.GetType().GetCustomAttributes(false))
+					{
+						if (attr is VOID_GameModesAttribute)
+						{
+							VOID_GameModesAttribute addonAttr = (VOID_GameModesAttribute)attr;
+
+							this.validModes = addonAttr.ValidModes;
+
+							Tools.PostDebugMessage("Found VOID_GameModesAttribute; validScenes set.");
+
+							break;
+						}
+					}
+
+					if (this.validModes == null)
+					{
+						this.validModes = new Game.Modes[]
+						{
+							Game.Modes.CAREER,
+							Game.Modes.SANDBOX,
+							Game.Modes.SCENARIO,
+							Game.Modes.SCENARIO_NON_RESUMABLE,
+							Game.Modes.SCIENCE_SANDBOX
+						};
+
+						Tools.PostDebugMessage("No VOID_GameModesAttribute found; validScenes defaulted to flight.");
+					}
+				}
+
+				return this.validModes;
+			}
+		}
+
+		public virtual bool inValidGame
+		{
+			get
+			{
+
+				Tools.PostDebugMessage(
+					this,
+					"Checking if mode is valid: CurrentGame.Mode={0}, ValidModes={1}, inValidGame={2}",
+					Enum.GetName(typeof(Game.Modes), HighLogic.CurrentGame.Mode),
+					string.Join(", ", this.ValidModes.Select(m => Enum.GetName(typeof(Game.Modes), m)).ToArray()),
+					this.ValidModes.Contains(HighLogic.CurrentGame.Mode)
+				);
+
+				return this.ValidModes.Contains(HighLogic.CurrentGame.Mode);
+			}
+		}
+
+		public virtual string Name
+		{
+			get;
+			protected set;
+		}
+
+		public virtual Vessel vessel
+		{
+			get
+			{
+				return FlightGlobals.ActiveVessel;
+			}
+		}
+
+		/*
+		 * Methods
+		 * */
+		public void StartGUI()
+		{
+			if (!this.toggleActive || this.guiRunning)
+			{
+				return;
+			}
+
+			Tools.PostDebugMessage (string.Format("Adding {0} to the draw queue.", this.GetType().Name));
+			RenderingManager.AddToPostDrawQueue (3, this.DrawGUI);
+		}
+
+		public void StopGUI()
+		{
+			if (!this.guiRunning)
+			{
+				return;
+			}
+			Tools.PostDebugMessage (string.Format("Removing {0} from the draw queue.", this.GetType().Name));
+			RenderingManager.RemoveFromPostDrawQueue (3, this.DrawGUI);
+		}
+
+		public abstract void DrawGUI();
+
+		public virtual void DrawConfigurables() {}
+
+		public virtual void LoadConfig()
+		{
+			var config = KSP.IO.PluginConfiguration.CreateForType<VOID_Module> ();
+			config.load ();
+
+			if (this is VOIDCore)
+			{
+				int configVersion = config.GetValue("VOID_Core_configValue", 2);
+
+				if (configVersion < VOIDCore.CONFIG_VERSION)
+				{
+					((VOIDCore)this).configNeedsUpdate = true;
+				}
+			}
+
+			foreach (var field in this.GetType().GetMembers(
+				BindingFlags.NonPublic |
+				BindingFlags.Public |
+				BindingFlags.Instance |
+				BindingFlags.FlattenHierarchy
+			))
+			{
+				if (!(field is FieldInfo || field is PropertyInfo))
+				{
+					continue;
+				}
+
+				if (field is PropertyInfo && (field as PropertyInfo).GetIndexParameters().Length > 0)
+				{
+					continue;
+				}
+
+				object[] attrs = field.GetCustomAttributes(typeof(AVOID_SaveValue), false);
+
+				if (attrs.Length == 0) {
+					continue;
+				}
+
+				AVOID_SaveValue attr = attrs.FirstOrDefault () as AVOID_SaveValue;
+
+				string fieldName = string.Empty;
+
+				if (this is VOIDCore || this.core.configNeedsUpdate)
+				{
+					string typeName = this.GetType().Name;;
+
+					if (this is VOIDCore && ((VOIDCore)this).configNeedsUpdate)
+					{
+						if (this is VOIDCore_Flight)
+						{
+							typeName = "VOID_Core";
+						}
+						else if (this is VOIDCore_Editor)
+						{
+							typeName = "VOID_EditorCore";
+						}
+					}
+
+					fieldName = string.Format("{0}_{1}", typeName, attr.Name);
+				}
+				else
+				{
+					fieldName = string.Format(
+						"{0}_{1}_{2}",
+						this.GetType().Name,
+						Enum.GetName(typeof(GameScenes), HighLogic.LoadedScene),
+						attr.Name
+					);
+				}
+
+				Tools.PostDebugMessage(string.Format("{0}: Loading field {1}.", this.GetType().Name, fieldName));
+
+				object fieldValue;
+
+				if (field is FieldInfo)
+				{
+					fieldValue = (field as FieldInfo).GetValue(this);
+				}
+				else
+				{
+					fieldValue = (field as PropertyInfo).GetValue(this, null);
+				}
+
+				bool convertBack = false;
+				if (fieldValue is IVOID_SaveValue)
+				{
+					fieldValue = (fieldValue as IVOID_SaveValue).value;
+					convertBack = true;
+				}
+
+				fieldValue = config.GetValue(fieldName, fieldValue);
+
+				if (convertBack)
+				{
+					Type type = typeof(VOID_SaveValue<>).MakeGenericType (fieldValue.GetType ());
+					IVOID_SaveValue convertValue = Activator.CreateInstance (type) as IVOID_SaveValue;
+					convertValue.SetValue (fieldValue);
+					fieldValue = convertValue;
+				}
+
+				if (field is FieldInfo)
+				{
+					(field as FieldInfo).SetValue(this, fieldValue);
+				}
+				else
+				{
+					(field as PropertyInfo).SetValue(this, fieldValue, null);
+				}
+
+				Tools.PostDebugMessage(string.Format("{0}: Loaded field {1}.", this.GetType().Name, fieldName));
+			}
+		}
+
+		public virtual void _SaveToConfig(KSP.IO.PluginConfiguration config)
+		{
+			foreach (var field in this.GetType().GetMembers(
+				BindingFlags.Instance |
+				BindingFlags.NonPublic |
+				BindingFlags.Public |
+				BindingFlags.FlattenHierarchy
+				))
+			{
+				object[] attrs = field.GetCustomAttributes(typeof(AVOID_SaveValue), false);
+
+				if (attrs.Length == 0) {
+					continue;
+				}
+
+				AVOID_SaveValue attr = attrs.FirstOrDefault () as AVOID_SaveValue;
+
+				string fieldName;
+
+				if (this is VOIDCore)
+				{
+					fieldName = string.Format("{0}_{1}", this.GetType().Name, attr.Name);
+				}
+				else
+				{
+					fieldName = string.Format(
+						"{0}_{1}_{2}",
+						this.GetType().Name,
+						Enum.GetName(typeof(GameScenes), HighLogic.LoadedScene),
+						attr.Name
+					);
+				}
+
+				object fieldValue;
+
+				if (field is FieldInfo)
+				{
+					fieldValue = (field as FieldInfo).GetValue(this);
+				}
+				else
+				{
+					fieldValue = (field as PropertyInfo).GetValue(this, null);
+				}
+
+				if (fieldValue is IVOID_SaveValue)
+				{
+					fieldValue = (fieldValue as IVOID_SaveValue).value;
+				}
+
+				config.SetValue(fieldName, fieldValue);
+
+				Tools.PostDebugMessage(string.Format("{0}: Saved field {1}.", this.GetType().Name, fieldName));
+			}
+		}
+	}
+
+	public abstract class VOID_WindowModule : VOID_Module
+	{
+		[AVOID_SaveValue("WindowPos")]
+		protected Rect WindowPos;
+		protected float defWidth;
+		protected float defHeight;
+
+		protected string inputLockName;
+
+		public VOID_WindowModule() : base()
+		{
+			this.defWidth = 250f;
+			this.defHeight = 50f;
+
+			this.inputLockName = string.Concat(this.Name, "_edlock");
+
+			this.WindowPos = new Rect(Screen.width / 2, Screen.height / 2, this.defWidth, this.defHeight);
+		}
+
+		public abstract void ModuleWindow(int _);
+
+		public override void DrawGUI()
+		{
+			GUI.skin = this.core.Skin;
+
+			Rect _Pos = this.WindowPos;
+
+			_Pos = GUILayout.Window(
+				this.core.windowID,
+				_Pos,
+				VOID_Tools.GetWindowHandler(this.ModuleWindow),
+				this.Name,
+				GUILayout.Width(this.defWidth),
+				GUILayout.Height(this.defHeight),
+				GUILayout.ExpandWidth(true),
+				GUILayout.ExpandHeight(true)
+			);
+
+			bool cursorInWindow = _Pos.Contains(Mouse.screenPos);
+
+			switch (HighLogic.LoadedScene)
+			{
+				case GameScenes.EDITOR:
+					if (cursorInWindow)
+					{
+						InputLockManager.SetControlLock(
+							ControlTypes.EDITOR_ICON_HOVER | ControlTypes.EDITOR_ICON_PICK |
+							ControlTypes.EDITOR_PAD_PICK_COPY | ControlTypes.EDITOR_PAD_PICK_COPY,
+							this.inputLockName
+						);
+						EditorLogic.fetch.Lock(false, false, false, this.inputLockName);
+					}
+					else
+					{
+						EditorLogic.fetch.Unlock(this.inputLockName);
+					}
+					break;
+				case GameScenes.FLIGHT:
+					if (cursorInWindow)
+					{
+						InputLockManager.SetControlLock(ControlTypes.CAMERACONTROLS, this.inputLockName);
+					}
+					else if (InputLockManager.GetControlLock(this.inputLockName) != ControlTypes.None)
+					{
+						InputLockManager.RemoveControlLock(this.inputLockName);
+					}
+					break;
+				case GameScenes.SPACECENTER:
+					if (cursorInWindow)
+					{
+						InputLockManager.SetControlLock(
+							ControlTypes.KSC_FACILITIES | ControlTypes.CAMERACONTROLS,
+							this.inputLockName
+						);
+					}
+					else if (InputLockManager.GetControlLock(this.inputLockName) != ControlTypes.None)
+					{
+						InputLockManager.RemoveControlLock(this.inputLockName);
+					}
+					break;
+			}
+
+			if (HighLogic.LoadedSceneIsEditor)
+			{
+				_Pos = Tools.ClampRectToEditorPad(_Pos);
+			}
+			else
+			{
+				_Pos = Tools.ClampRectToScreen(_Pos);
+			}
+
+			if (_Pos != this.WindowPos)
+			{
+				this.WindowPos = _Pos;
+				this.core.configDirty = true;
+			}
+		}
+	}
+}
+
+

--- /dev/null
+++ b/API/VOID_SingletonModule.cs
@@ -1,1 +1,67 @@
+// VOID
+//
+// VOID_SingletonModule.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+using System;
 
+namespace VOID
+{
+	public abstract class VOID_SingletonModule<T> : VOIDCore, IVOID_Module
+		where T : VOID_Module, new()
+	{
+		#region Singleton Members
+		/*
+		 * Static Members
+		 * */
+		protected static bool _initialized = false;
+
+		public static bool Initialized
+		{
+			get
+			{
+				return _initialized;
+			}
+		}
+
+		protected static T _instance;
+
+		public static T Instance
+		{
+			get
+			{
+				if (_instance == null)
+				{
+					_instance = new T();
+					_initialized = true;
+				}
+				return _instance;
+			}
+		}
+		#endregion
+	}
+}
+
+

--- /dev/null
+++ b/GameData/VOID/Textures/ATM_VOID.cfg
@@ -1,1 +1,16 @@
+ACTIVE_TEXTURE_MANAGER_CONFIG
+{
+	folder = VOID
+	enabled  = true
+	OVERRIDES
+	{
+		VOID/Textures/.*
+		{
+			compress = false
+			mipmaps = false
+			scale = 1
+			max_size = 0
+		}
+	}
+}
 

 Binary files /dev/null and b/GameData/VOID/Textures/fundsgreen.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/fundsred.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/repgreen.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/repred.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/science.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/void_appIcon_dark.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/void_appIcon_dark_glow.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/void_appIcon_light.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/void_appIcon_light_glow.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/void_icon_dark.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/void_icon_dark_glow.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/void_icon_light.png differ
 Binary files /dev/null and b/GameData/VOID/Textures/void_icon_light_glow.png differ
file:a/IVOID_Module.cs (deleted)
--- a/IVOID_Module.cs
+++ /dev/null
@@ -1,59 +1,1 @@
-// VOID
-//
-// IVOID_Module.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using System;
-
-namespace VOID
-{
-	public interface IVOID_Module
-	{
-		string Name { get; }
-		bool toggleActive { get; set; }
-		bool guiRunning { get; }
-
-		void DrawGUI();
-		void StartGUI();
-		void StopGUI();
-
-		void DrawConfigurables();
-
-		void LoadConfig();
-
-		void _SaveToConfig(KSP.IO.PluginConfiguration config);
-	}
-
-	public interface IVOID_BehaviorModule : IVOID_Module
-	{
-		void Update();
-		void FixedUpdate();
-		void OnDestroy();
-	}
-
-	public interface IVOID_EditorModule : IVOID_Module {}
-}
-

--- a/Properties/AssemblyInfo.cs
+++ b/Properties/AssemblyInfo.cs
@@ -39,7 +39,7 @@
 // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
 // The form "{Major}.{Minor}.*" will automatically update the build and revision,
 // and "{Major}.{Minor}.{Build}.*" will update just the revision.
-[assembly: AssemblyVersion("0.15.*")]
+[assembly: AssemblyVersion("0.16.4.*")]
 // The following attributes are used to specify the signing key for the assembly,
 // if desired. See the Mono documentation for more information about signing.
 //[assembly: AssemblyDelaySign(false)]

--- /dev/null
+++ b/Tools/VOID_DataValue.cs
@@ -1,1 +1,367 @@
-
+// VOID
+//
+// VOID_DataValue.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using System;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	public class VOID_DataValue<T> : IVOID_DataValue
+	{
+		/*
+		 * Static Members
+		 * */
+		public static implicit operator T(VOID_DataValue<T> v)
+		{
+			return (T)v.Value;
+		}
+
+		/*
+		 * Instance Members
+		 * */
+		/*
+		* Fields
+		 * */
+		protected T cache;
+		protected Func<T> ValueFunc;
+		protected float lastUpdate;
+
+		/* 
+		 * Properties
+		 * */
+		public string Label { get; protected set; }
+		public string Units { get; protected set; }
+
+		object IVOID_DataValue.Value
+		{
+			get
+			{
+				return this.Value;
+			}
+		}
+
+		public T Value
+		{
+			get
+			{
+				if (
+					(VOID_Data.Core.updateTimer - this.lastUpdate > VOID_Data.Core.updatePeriod) ||
+					(this.lastUpdate > VOID_Data.Core.updateTimer)
+				)
+				{
+					this.Refresh();
+				}
+				return (T)this.cache;
+			}
+		}
+
+		/*
+		 * Methods
+		 * */
+		public VOID_DataValue(string Label, Func<T> ValueFunc, string Units = "")
+		{
+			this.Label = Label;
+			this.Units = Units;
+			this.ValueFunc = ValueFunc;
+			this.lastUpdate = 0;
+
+			VOID_Data.DataValues[this.GetHashCode()] = this;
+		}
+
+		public void Refresh()
+		{
+			this.cache = this.ValueFunc.Invoke ();
+			this.lastUpdate = VOID_Data.Core.updateTimer;
+		}
+
+		public T GetFreshValue()
+		{
+			this.Refresh ();
+			return (T)this.cache;
+		}
+
+		public virtual string ValueUnitString() {
+			return this.Value.ToString() + this.Units;
+		}
+
+		public virtual void DoGUIHorizontal()
+		{
+			GUILayout.BeginHorizontal (GUILayout.ExpandWidth (true));
+			GUILayout.Label (this.Label + ":");
+			GUILayout.FlexibleSpace ();
+			GUILayout.Label (this.ValueUnitString(), GUILayout.ExpandWidth (false));
+			GUILayout.EndHorizontal ();
+		}
+
+		public override int GetHashCode()
+		{
+			int hash;
+			unchecked
+			{
+				hash = 79999;
+
+				hash = hash * 104399 + this.Label.GetHashCode();
+				hash = hash * 104399 + this.ValueFunc.GetHashCode();
+				hash = hash * 104399 + this.Units.GetHashCode();
+			}
+
+			return hash;
+		}
+
+		public override string ToString()
+		{
+			return string.Format (
+				"{0}: {1}{2}",
+				this.Label,
+				this.Value.ToString (),
+				this.Units
+			);
+		}
+	}
+
+	public abstract class VOID_NumValue<T> : VOID_DataValue<T>
+		where T : IFormattable, IConvertible, IComparable
+	{
+		public static implicit operator Double(VOID_NumValue<T> v)
+		{
+			return v.ToDouble();
+		}
+
+		public static implicit operator Int32(VOID_NumValue<T> v)
+		{
+			return v.ToInt32();
+		}
+
+
+		public static implicit operator Single(VOID_NumValue<T> v)
+		{
+			return v.ToSingle();
+		}
+
+		protected IFormatProvider formatProvider;
+
+		public VOID_NumValue(string Label, Func<T> ValueFunc, string Units = "") : base(Label, ValueFunc, Units)
+		{
+			this.formatProvider = System.Globalization.CultureInfo.CurrentUICulture;
+		}
+
+		public virtual double ToDouble(IFormatProvider provider)
+		{
+			return this.Value.ToDouble(provider);
+		}
+
+		public virtual double ToDouble()
+		{
+			return this.ToDouble(this.formatProvider);
+		}
+
+		public virtual int ToInt32(IFormatProvider provider)
+		{
+			return this.Value.ToInt32(provider);
+		}
+
+		public virtual int ToInt32()
+		{
+			return this.ToInt32(this.formatProvider);
+		}
+
+		public virtual float ToSingle(IFormatProvider provider)
+		{
+			return this.Value.ToSingle(provider);
+		}
+
+		public virtual float ToSingle()
+		{
+			return this.ToSingle(this.formatProvider);
+		}
+
+		public virtual string ToString(string Format)
+		{
+			return string.Format (
+				"{0}: {1}{2}",
+				this.Label,
+				this.Value.ToString(Format, this.formatProvider),
+				this.Units
+			);
+		}
+
+		public virtual string ToSIString(int digits = 3, int MinMagnitude = 0, int MaxMagnitude = int.MaxValue)
+		{
+			return string.Format (
+				"{0}{1}",
+				Tools.MuMech_ToSI (this, digits, MinMagnitude, MaxMagnitude),
+				this.Units
+			);
+		}
+
+		public virtual string ValueUnitString(string format)
+		{
+			return this.Value.ToString(format, this.formatProvider) + this.Units;
+		}
+		
+		public virtual string ValueUnitString(int digits) {
+			return Tools.MuMech_ToSI(this, digits) + this.Units;
+		}
+
+		public virtual string ValueUnitString(int digits, int MinMagnitude, int MaxMagnitude)
+		{
+			return Tools.MuMech_ToSI(this, digits, MinMagnitude, MaxMagnitude) + this.Units;
+		}
+
+		public virtual void DoGUIHorizontal(string format)
+		{
+			GUILayout.BeginHorizontal (GUILayout.ExpandWidth (true));
+			GUILayout.Label (this.Label + ":");
+			GUILayout.FlexibleSpace ();
+			GUILayout.Label (this.ValueUnitString(format), GUILayout.ExpandWidth (false));
+			GUILayout.EndHorizontal ();
+		}
+
+		public virtual int DoGUIHorizontal(int digits, bool precisionButton = true)
+		{
+			if (precisionButton)
+			{
+				return this.DoGUIHorizontalPrec(digits);
+			}
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label(this.Label + ":", GUILayout.ExpandWidth(true));
+			GUILayout.FlexibleSpace();
+			GUILayout.Label(this.ValueUnitString(digits), GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			return digits;
+		}
+
+		public virtual int DoGUIHorizontalPrec(int digits)
+		{
+			double magnitude;
+			double magLimit;
+
+			magnitude = Math.Log10(Math.Abs((double)this));
+
+			magLimit = Math.Max(Math.Abs(magnitude), 3d) + 3d;
+			magLimit = Math.Round(Math.Ceiling(magLimit / 3f)) * 3d;
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label(this.Label + "ⁱ:", GUILayout.ExpandWidth(true));
+			GUILayout.FlexibleSpace();
+
+			if (magnitude >= 0)
+			{
+				GUILayout.Label(this.ValueUnitString(3, int.MinValue, (int)magnitude - digits), GUILayout.ExpandWidth(false));
+			}
+			else
+			{
+				GUILayout.Label(this.ValueUnitString(3, (int)magnitude + digits, int.MaxValue), GUILayout.ExpandWidth(false));
+			}
+			GUILayout.EndHorizontal();
+
+			if (Event.current.type == EventType.mouseUp)
+			{
+				Rect lastRect = GUILayoutUtility.GetLastRect();
+				if (lastRect.Contains(Event.current.mousePosition))
+				{
+					Tools.PostDebugMessage(string.Format("{0}: Changing digits from {1} within magLimit {2}.",
+						this.GetType().Name,
+						digits,
+						magLimit));
+
+					if (Event.current.button == 0)
+					{
+						digits = (digits + 3) % (int)magLimit;
+					}
+					else if (Event.current.button == 1)
+					{
+						digits = (digits - 3) % (int)magLimit;
+					}
+
+					if (digits < 0)
+					{
+						digits += (int)magLimit;
+					}
+
+					Tools.PostDebugMessage(string.Format("{0}: Changed digits to {1}." +
+						"\n\tNew minMagnitude: {2}, maxMagnitude: {3}" +
+						"\n\tMagnitude: {4}",
+						this.GetType().Name,
+						digits,
+						magnitude >= 0 ? int.MinValue : (int)magnitude - 4 + digits,
+						magnitude >= 0 ? (int)magnitude - digits : int.MaxValue,
+						magnitude
+					));
+				}
+			}
+
+			return digits;
+		}
+	}
+
+	public class VOID_DoubleValue : VOID_NumValue<double>
+	{
+		public VOID_DoubleValue(string Label, Func<double> ValueFunc, string Units) : base(Label, ValueFunc, Units) {}
+	}
+
+	public class VOID_FloatValue : VOID_NumValue<float>
+	{
+		public VOID_FloatValue(string Label, Func<float> ValueFunc, string Units) : base(Label, ValueFunc, Units) {}
+	}
+
+	public class VOID_IntValue : VOID_NumValue<int>
+	{
+		public VOID_IntValue(string Label, Func<int> ValueFunc, string Units) : base(Label, ValueFunc, Units) {}
+	}
+
+	public class VOID_StrValue : VOID_DataValue<string>
+	{
+		public VOID_StrValue(string Label, Func<string> ValueFunc) : base(Label, ValueFunc, "") {}
+	}
+
+	public class VOID_Vector3dValue : VOID_DataValue<Vector3d>
+	{
+		public VOID_Vector3dValue(string Label, Func<Vector3d> ValueFunc, string Units)
+			: base(Label, ValueFunc, Units)
+		{}
+
+		public string ToString(string format)
+		{
+			return string.Format("{0}: {1}{2}",
+				this.Label,
+				this.Value.ToString(format),
+				this.Units
+			);
+		}
+
+		public string ValueUnitString(string format) {
+			return this.Value.ToString(format) + this.Units;
+		}
+	}
+}
+
+

--- /dev/null
+++ b/Tools/VOID_SaveValue.cs
@@ -1,1 +1,126 @@
+// VOID
+//
+// VOID_SaveValue.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+using KSP;
+using System;
+using System.Collections.Generic;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	public struct VOID_SaveValue<T> : IVOID_SaveValue
+	{
+		private T _value;
+		private Type _type;
+
+		private VOIDCore Core
+		{
+			get
+			{
+				return VOID_Data.Core;
+			}
+		}
+
+		object IVOID_SaveValue.value
+		{
+			get
+			{
+				return this.value;
+			}
+		}
+
+		public T value
+		{
+			get
+			{
+				return this._value;
+			}
+			set
+			{
+				if (this.Core != null && !System.Object.Equals(this._value, value))
+				{
+					Tools.PostDebugMessage (string.Format (
+						"VOID: Dirtying config for type {0} in method {1}." +
+						"\n\t Old Value: {2}, New Value: {3}" +
+						"\n\t Object.Equals(New, Old): {4}",
+						this._type,
+						new System.Diagnostics.StackTrace().GetFrame(1).GetMethod(),
+						this._value,
+						value,
+						System.Object.Equals(this._value, value)
+					));
+					this.Core.configDirty = true;
+				}
+				this._value = value;
+			}
+		}
+
+		public Type type
+		{
+			get
+			{
+				if (this._type == null && this._value != null)
+				{
+					this._type = this._value.GetType ();
+				}
+				return this._type;
+			}
+			set
+			{
+				this._type = value;
+			}
+		}
+
+		public void SetValue(object v)
+		{
+			this.value = (T)v;
+		}
+
+		public static implicit operator T(VOID_SaveValue<T> v)
+		{
+			return (T)v.value;
+		}
+
+		public static implicit operator VOID_SaveValue<T>(T v)
+		{
+			VOID_SaveValue<T> r = new VOID_SaveValue<T>();
+			r.type = v.GetType();
+			r.value = v;
+
+			return r;
+		}
+
+		public override string ToString()
+		{
+			return this.value.ToString();
+		}
+	}
+}
+
+

--- /dev/null
+++ b/Tools/VOID_Tools.cs
@@ -1,1 +1,1055 @@
-
+// VOID
+//
+// VOID_Tools.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using KSP;
+using System;
+using System.Collections.Generic;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	public static class VOID_Tools
+	{
+		#region CelestialBody Utilities
+		public static bool hasAncestor(this CelestialBody bodyA, CelestialBody bodyB)
+		{
+			if (bodyA == null || bodyB == null)
+			{
+				return false;
+			}
+
+			while (bodyA.orbitDriver != null)
+			{
+				if (bodyA.orbit.referenceBody == bodyB)
+				{
+					return true;
+				}
+
+				bodyA = bodyA.orbit.referenceBody;
+			} 
+
+			return false;
+		}
+
+		public static bool NearestRelatedParents(ref CelestialBody bodyA, ref CelestialBody bodyB)
+		{
+			if (bodyA == null || bodyB == null || bodyA.orbitDriver == null || bodyB.orbitDriver == null)
+			{
+				throw new ArgumentException(string.Concat(
+					"CelestialBody::FindRelatedParents: ",
+					"Neither body may be null, and both bodies must have orbits."
+				));
+			}
+
+			CelestialBody a, b;
+
+			a = bodyA;
+
+			while (bodyA.orbitDriver != null)
+			{
+				b = bodyB;
+
+				while (b.orbitDriver != null)
+				{
+					if (a.orbit.referenceBody == b.orbit.referenceBody)
+					{
+						bodyA = a;
+						bodyB = b;
+						return true;
+					}
+
+					b = b.orbit.referenceBody;
+				}
+
+				a = a.orbit.referenceBody;
+			}
+
+			return false;
+		}
+		#endregion
+
+		#region VESSEL_EXTENSIONS_SCIENCE
+		public static CBAttributeMapSO.MapAttribute GetBiome(this Vessel vessel)
+		{
+			CBAttributeMapSO.MapAttribute mapAttribute;
+
+			try
+			{
+				CBAttributeMapSO BiomeMap = vessel.mainBody.BiomeMap;
+
+				double lat = vessel.latitude * Math.PI / 180d;
+				double lon = vessel.longitude * Math.PI / 180d;
+
+				mapAttribute = BiomeMap.GetAtt(lat, lon);
+
+				/*
+				lon -= Math.PI / 2d;
+
+				if (lon < 0d)
+				{
+					lon += 2d * Math.PI;
+				}
+
+				float v = (float)(lat / Math.PI) + 0.5f;
+				float u = (float)(lon / (2d * Math.PI));
+
+				Color pixelBilinear = BiomeMap.Map.GetPixelBilinear(u, v);
+				mapAttribute = BiomeMap.defaultAttribute;
+
+				if (BiomeMap.Map != null)
+				{
+					if (BiomeMap.exactSearch)
+					{
+						for (int i = 0; i < BiomeMap.Attributes.Length; ++i)
+						{
+							if (pixelBilinear == BiomeMap.Attributes[i].mapColor)
+							{
+								mapAttribute = BiomeMap.Attributes[i];
+							}
+						}
+					}
+					else
+					{
+						float zero = 0;
+						float num = 1 / zero;
+						for (int j = 0; j < BiomeMap.Attributes.Length; ++j)
+						{
+							Color mapColor = BiomeMap.Attributes[j].mapColor;
+							float sqrMagnitude = ((Vector4)(mapColor - pixelBilinear)).sqrMagnitude;
+							if (sqrMagnitude < num)
+							{
+								bool testCase = true;
+								if (BiomeMap.nonExactThreshold != -1)
+								{
+									testCase = (sqrMagnitude < BiomeMap.nonExactThreshold);
+								}
+								if (testCase)
+								{
+									mapAttribute = BiomeMap.Attributes[j];
+									num = sqrMagnitude;
+								}
+							}
+						}
+					}
+				}
+				*/
+			}
+			catch (NullReferenceException)
+			{
+				mapAttribute = new CBAttributeMapSO.MapAttribute();
+				mapAttribute.name = "N/A";
+			}
+
+			return mapAttribute;
+		}
+
+		public static ExperimentSituations GetExperimentSituation(this Vessel vessel)
+		{
+			if (vessel == null)
+			{
+				return ExperimentSituations.SrfSplashed;
+			}
+
+			Vessel.Situations situation = vessel.situation;
+
+			switch (situation)
+			{
+				case Vessel.Situations.PRELAUNCH:
+				case Vessel.Situations.LANDED:
+					return ExperimentSituations.SrfLanded;
+				case Vessel.Situations.SPLASHED:
+					return ExperimentSituations.SrfSplashed;
+				case Vessel.Situations.FLYING:
+					if (vessel.altitude < (double)vessel.mainBody.scienceValues.flyingAltitudeThreshold)
+					{
+						return ExperimentSituations.FlyingLow;
+					}
+					else
+					{
+						return ExperimentSituations.FlyingHigh;
+					}
+			}
+
+			if (vessel.altitude < (double)vessel.mainBody.scienceValues.spaceAltitudeThreshold)
+			{
+				return ExperimentSituations.InSpaceLow;
+			}
+			else
+			{
+				return ExperimentSituations.InSpaceHigh;
+			}
+		}
+
+		public static string HumanString(this ExperimentSituations situation)
+		{
+			switch (situation)
+			{
+				case ExperimentSituations.FlyingHigh:
+					return "Upper Atmosphere";
+				case ExperimentSituations.FlyingLow:
+					return "Flying";
+				case ExperimentSituations.SrfLanded:
+					return "Surface";
+				case ExperimentSituations.InSpaceLow:
+					return "Near in Space";
+				case ExperimentSituations.InSpaceHigh:
+					return "High in Space";
+				case ExperimentSituations.SrfSplashed:
+					return "Splashed Down";
+				default:
+					return "Unknown";
+			}
+		}
+		#endregion
+
+		#region VESSEL_EXTENSIONS_LAT_LONG
+		public static string GetLongitudeString(this Vessel vessel, string format = "F4")
+		{
+			string dir_long = "W";
+			double v_long = vessel.longitude;
+
+			v_long = FixDegreeDomain(v_long);
+
+			if (v_long < -180d)
+			{
+				v_long += 360d;
+			}
+			if (v_long >= 180)
+			{
+				v_long -= 360d;
+			}
+
+			if (v_long > 0)
+				dir_long = "E";
+
+			return string.Format("{0}° {1}", Math.Abs(v_long).ToString(format), dir_long);
+		}
+
+		public static string GetLatitudeString(this Vessel vessel, string format = "F4")
+		{
+			string dir_lat = "S";
+			double v_lat = vessel.latitude;
+			if (v_lat > 0)
+				dir_lat = "N";
+
+			return string.Format("{0}° {1}", Math.Abs(v_lat).ToString(format), dir_lat);
+		}
+		#endregion
+
+		#region VESSEL_EXTENSIONS_GENERAL
+		public static double TrueAltitude(Vessel vessel)
+		{
+			double trueAltitude = vessel.orbit.altitude - vessel.terrainAltitude;
+
+			// HACK: This assumes that on worlds with oceans, all water is fixed at 0 m,
+			// and water covers the whole surface at 0 m.
+			if (vessel.terrainAltitude < 0 && vessel.mainBody.ocean)
+			{
+				trueAltitude = vessel.orbit.altitude;
+			}
+
+			return trueAltitude;
+		}
+
+		public static double Radius(this Vessel vessel)
+		{
+			double radius;
+
+			radius = vessel.altitude;
+
+			if (vessel.mainBody != null)
+			{
+				radius += vessel.mainBody.Radius;
+			}
+
+			return radius;
+		}
+		#endregion
+
+		#region GEOMETRY_UTILS
+		public static double FixAngleDomain(double Angle, bool Degrees = false)
+		{
+			double Extent = 2d * Math.PI;
+			if (Degrees)
+			{
+				Extent = 360d;
+			}
+
+			Angle = Angle % (Extent);
+			if (Angle < 0d)
+			{
+				Angle += Extent;
+			}
+
+			return Angle;
+		}
+
+		public static double FixDegreeDomain(double Angle)
+		{
+			return FixAngleDomain(Angle, true);
+		}
+		#endregion
+
+		private static Dictionary<int, GUI.WindowFunction> functionCache;
+		public static UnityEngine.GUI.WindowFunction GetWindowHandler(Action<int> func)
+		{
+			if (functionCache == null)
+			{
+				functionCache = new Dictionary<int, GUI.WindowFunction>();
+			}
+
+			int hashCode = func.GetHashCode();
+
+			if (!functionCache.ContainsKey(hashCode))
+			{
+				functionCache[hashCode] = delegate (int id)
+				{
+					try
+					{
+						func(id);
+					}
+					#if DEBUG
+					catch (ArgumentException)
+					#else
+					catch (ArgumentException)
+					#endif
+					{
+						Debug.LogWarning(
+							string.Format("[{0}]: ArgumentException caught during window call.  This is not a bug.",
+								func.Target.GetType().Name
+							));
+
+						/*#if DEBUG
+						Debug.LogException(ex);
+						#endif*/
+					}
+					catch (Exception ex)
+					{
+						Debug.LogError(
+							string.Format("[{0}]: {1} caught during window call.\nMessage:\n{2}\nStackTrace:\n{3}",
+								func.Target.GetType().Name,
+								ex.GetType().Name,
+								ex.Message,
+								ex.StackTrace
+							));
+					}
+				};
+			}
+
+			return functionCache[hashCode];
+		}
+
+		/// <summary>
+		/// Formats the interval given in seconds as a human-friendly
+		/// time period in [[[[years, ]days, ]hours, ]minutes, and ]seconds.
+		/// 
+		/// Uses sidereal days, since "6 hours per day" is the Kerbal standard.
+		/// </summary>
+		/// <returns>Human readable interval</returns>
+		/// <param name="seconds"></param>
+		public static string FormatInterval(double seconds)
+		{
+			return UnpackedTime.FromSeconds(seconds).FormatAsSpan();
+		}
+
+		/// <summary>
+		/// Formats the date given in seconds since epoch as a human-friendly
+		/// date in the format YY, DD, HH:MM:SS
+		/// </summary>
+		/// <returns>The date.</returns>
+		/// <param name="seconds">Seconds.</param>
+		public static string FormatDate(double seconds)
+		{
+			return UnpackedTime.FromSeconds(seconds).FormatAsDate();
+		}
+
+		public class UnpackedTime
+		{
+			public const double SecondsPerMinute = 60d;
+			public const double SecondsPerHour = 3600d;
+
+			public static double SecondsPerDay
+			{
+				get
+				{
+					if (GameSettings.KERBIN_TIME)
+					{
+						return 21600d;
+					}
+					else
+					{
+						return 86164.1d;
+					}
+				}
+			}
+
+			public static double SecondsPerYear
+			{
+				get
+				{
+					if (GameSettings.KERBIN_TIME)
+					{
+						return 9203545d;
+					}
+					else
+					{
+						return 31558149d;
+					}
+				}
+			}
+
+			public static UnpackedTime FromSeconds(double seconds)
+			{
+				UnpackedTime time = new UnpackedTime();
+
+				time.years = (int)(seconds / SecondsPerYear);
+
+				seconds %= SecondsPerYear;
+
+				time.days = (int)(seconds / SecondsPerDay);
+
+				seconds %= SecondsPerDay;
+
+				time.hours = (int)(seconds / SecondsPerHour);
+
+				seconds %= SecondsPerHour;
+
+				time.minutes = (int)(seconds / SecondsPerMinute);
+
+				seconds %= SecondsPerMinute;
+
+				time.seconds = seconds;
+
+				return time;
+			}
+
+			public static explicit operator UnpackedTime(double seconds)
+			{
+				return FromSeconds(seconds);
+			}
+
+			public static implicit operator double(UnpackedTime time)
+			{
+				return time.ToSeconds();
+			}
+
+			public static UnpackedTime operator+ (UnpackedTime lhs, UnpackedTime rhs)
+			{
+				return FromSeconds(lhs.ToSeconds() + rhs.ToSeconds());
+			}
+
+			public static UnpackedTime operator- (UnpackedTime lhs, UnpackedTime rhs)
+			{
+				return FromSeconds(lhs.ToSeconds() - rhs.ToSeconds());
+			}
+
+			public int years;
+			public int days;
+			public int hours;
+			public int minutes;
+			public double seconds;
+
+			public double ToSeconds()
+			{
+				return (double)years * SecondsPerYear +
+					(double)days * SecondsPerDay +
+					(double)hours * SecondsPerHour +
+					(double)minutes * SecondsPerMinute +
+					seconds;
+			}
+
+			public string FormatAsSpan()
+			{
+				string format_1 = "{0:D1}y {1:D1}d {2:D2}h {3:D2}m {4:00.0}s";
+				string format_2 = "{0:D1}d {1:D2}h {2:D2}m {3:00.0}s";
+				string format_3 = "{0:D2}h {1:D2}m {2:00.0}s";
+				string format_4 = "{0:D2}m {1:00.0}s";
+				string format_5 = "{0:00.0}s";
+
+				if (this.years > 0)
+				{
+					return string.Format(format_1, this.years, this.days, this.hours, this.minutes, this.seconds);
+				}
+				else if (this.days > 0)
+				{
+					return string.Format(format_2, this.days, this.hours, this.minutes, this.seconds);
+				}
+				else if (this.hours > 0)
+				{
+					return string.Format(format_3, this.hours, this.minutes, this.seconds);
+				}
+				else if (this.minutes > 0)
+				{
+					return string.Format(format_4, this.minutes, this.seconds);
+				}
+				else
+				{
+					return string.Format(format_5, this.seconds);
+				}
+			}
+
+			public string FormatAsDate()
+			{
+				string format = "Y{0:#0}, D{1:#0} {2:00}:{3:00}:{4:00.0}s";
+
+				return string.Format(format, years + 1, days, hours, minutes, seconds);
+			}
+
+			public UnpackedTime(int years, int days, int hours, int minutes, double seconds)
+			{
+				this.years = years;
+				this.days = days;
+				this.hours = hours;
+				this.minutes = minutes;
+				this.seconds = seconds;
+			}
+
+			public UnpackedTime() : this(0, 0, 0, 0, 0d) {}
+		}
+
+		public static string UppercaseFirst(string s)
+		{
+			if (string.IsNullOrEmpty(s))
+			{
+				return string.Empty;
+			}
+			char[] a = s.ToCharArray();
+			a[0] = char.ToUpper(a[0]);
+			return new string(a);
+		}
+
+		//transfer angles
+		public static double Nivvy_CalcTransferPhaseAngle(double r_current, double r_target, double grav_param)
+		{
+			r_target /= 1000;
+			r_current /= 1000;
+			grav_param /= 1000000000;
+
+			double midpoint = (r_target + r_current) / 2;
+
+			double T_target = (2 * Math.PI) * Math.Sqrt((r_target * r_target * r_target) / grav_param);
+			double T_transfer = (2 * Math.PI) * Math.Sqrt((midpoint * midpoint * midpoint) / grav_param);
+			return 360 * (0.5 - (T_transfer / (2 * T_target)));
+		}
+
+		public static double Younata_DeltaVToGetToOtherBody(double mu, double r1, double r2)
+		{
+			/*			
+            def deltaVToGetToOtherBody(mu, r1, r2):
+            # mu = gravity param of common orbiting body of r1 and r2
+            # (e.g. for mun to minmus, mu is kerbin's gravity param
+            # r1 = initial body's orbit radius
+            # r2 = target body's orbit radius
+		
+            # return value is km/s
+            sur1 = math.sqrt(mu / r1)
+            sr1r2 = math.sqrt(float(2*r2)/float(r1+r2))
+            mult = sr1r2 - 1
+            return sur1 * mult
+            */
+			double sur1, sr1r2, mult;
+			sur1 = Math.Sqrt(mu / r1);
+			sr1r2 = Math.Sqrt((2 * r2) / (r1 + r2));
+			mult = sr1r2 - 1;
+			return sur1 * mult;
+		}
+
+		public static double Younata_DeltaVToExitSOI(double mu, double r1, double r2, double v)
+		{
+			/*			
+            def deltaVToExitSOI(mu, r1, r2, v):
+            # mu = gravity param of current body
+            # r1 = current orbit radius
+            # r2 = SOI radius
+            # v = SOI exit velocity
+            foo = r2 * (v**2) - 2 * mu
+            bar = r1 * foo + (2 * r2 * mu)
+            r = r1*r2
+            return math.sqrt(bar / r)
+            */
+			double foo = r2 * (v * v) - 2 * mu;
+			double bar = r1 * foo + (2 * r2 * mu);
+			double r = r1 * r2;
+			return Math.Sqrt(bar / r);
+		}
+
+		public static double Younata_TransferBurnPoint(double r, double v, double angle, double mu)
+		{
+			/*			
+            def transferBurnPoint(r, v, angle, mu):
+            # r = parking orbit radius
+            # v = ejection velocity
+            # angle = phase angle (from function phaseAngle())
+            # mu = gravity param of current body.
+            epsilon = ((v**2)/2) - (mu / r)
+            h = r * v * math.sin(angle)
+            e = math.sqrt(1 + ((2 * epsilon * h**2)/(mu**2)))
+            theta = math.acos(1.0 / e)
+            degrees = theta * (180.0 / math.pi)
+            return 180 - degrees
+            */
+			double epsilon, h, ee, theta, degrees;
+			epsilon = ((v * v) / 2) - (mu / r);
+			h = r * v * Math.Sin(angle);
+			ee = Math.Sqrt(1 + ((2 * epsilon * (h * h)) / (mu * mu)));
+			theta = Math.Acos(1.0 / ee);
+			degrees = theta * (180.0 / Math.PI);
+			return 180 - degrees;
+			// returns the ejection angle
+		}
+
+		public static double Adammada_CurrrentPhaseAngle(
+			double body_LAN,
+			double body_orbitPct,
+			double origin_LAN,
+			double origin_orbitPct
+		)
+		{
+			double angle = (body_LAN / 360 + body_orbitPct) - (origin_LAN / 360 + origin_orbitPct);
+			if (angle > 1)
+				angle = angle - 1;
+			if (angle < 0)
+				angle = angle + 1;
+			if (angle > 0.5)
+				angle = angle - 1;
+			angle = angle * 360;
+			return angle;
+		}
+
+		public static double Adammada_CurrentEjectionAngle(
+			double vessel_long,
+			double origin_rotAngle,
+			double origin_LAN,
+			double origin_orbitPct
+		)
+		{
+			//double eangle = ((FlightGlobals.ActiveVOID.vessel.longitude + orbiting.rotationAngle) - (orbiting.orbit.LAN / 360 + orbiting.orbit.orbitPercent) * 360);
+			double eangle = ((vessel_long + origin_rotAngle) - (origin_LAN / 360 + origin_orbitPct) * 360);
+
+			while (eangle < 0)
+				eangle = eangle + 360;
+			while (eangle > 360)
+				eangle = eangle - 360;
+			if (eangle < 270)
+				eangle = 90 - eangle;
+			else
+				eangle = 450 - eangle;
+			return eangle;
+		}
+
+		public static double mrenigma03_calcphase(Vessel vessel, CelestialBody target)   //calculates phase angle between the current body and target body
+		{
+			Vector3d vecthis = new Vector3d();
+			Vector3d vectarget = new Vector3d();
+			vectarget = target.orbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());
+
+			if ((vessel.mainBody.name == "Sun") || (vessel.mainBody.referenceBody.referenceBody.name == "Sun"))
+			{
+				vecthis = vessel.orbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());
+			}
+			else
+			{
+				vecthis = vessel.mainBody.orbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());
+			}
+
+			vecthis = Vector3d.Project(new Vector3d(vecthis.x, 0, vecthis.z), vecthis);
+			vectarget = Vector3d.Project(new Vector3d(vectarget.x, 0, vectarget.z), vectarget);
+
+			Vector3d prograde = new Vector3d();
+			prograde = Quaternion.AngleAxis(90, Vector3d.forward) * vecthis;
+
+			double phase = Vector3d.Angle(vecthis, vectarget);
+
+			if (Vector3d.Angle(prograde, vectarget) > 90)
+				phase = 360 - phase;
+
+			return (phase + 360) % 360;
+		}
+
+		public static double adjustCurrPhaseAngle(double transfer_angle, double curr_phase)
+		{
+			if (transfer_angle < 0)
+			{
+				if (curr_phase > 0)
+					return (-1 * (360 - curr_phase));
+				else if (curr_phase < 0)
+					return curr_phase;
+			}
+			else if (transfer_angle > 0)
+			{
+				if (curr_phase > 0)
+					return curr_phase;
+				else if (curr_phase < 0)
+					return (360 + curr_phase);
+			}
+			return curr_phase;
+		}
+
+		public static double adjust_current_ejection_angle(double curr_ejection)
+		{
+			//curr_ejection WILL need to be adjusted once for all transfers as it returns values ranging -180 to 180
+			// need 0-360 instead
+			//
+			// ie i have -17 in the screenshot
+			// need it to show 343
+			//
+			// do this
+			//
+			// if < 0, add curr to 360  // 360 + (-17) = 343
+			// else its good as it is
+
+			if (curr_ejection < 0)
+				return 360 + curr_ejection;
+			else
+				return curr_ejection;
+
+		}
+
+		public static double adjust_transfer_ejection_angle(double trans_ejection, double trans_phase)
+		{
+			// if transfer_phase_angle < 0 its a lower transfer
+			//180 + curr_ejection
+			// else if transfer_phase_angle > 0 its good as it is
+
+			if (trans_phase < 0)
+				return 180 + trans_ejection;
+			else
+				return trans_ejection;
+
+		}
+
+		public static void display_transfer_angles_SUN2PLANET(CelestialBody body, Vessel vessel)
+		{
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Phase angle (curr/trans):");
+			GUILayout.Label(
+				VOID_Tools.mrenigma03_calcphase(vessel, body).ToString("F3") + "° / " + VOID_Tools.Nivvy_CalcTransferPhaseAngle(
+					vessel.orbit.semiMajorAxis,
+					body.orbit.semiMajorAxis,
+					vessel.mainBody.gravParameter
+				).ToString("F3") + "°",
+				GUILayout.ExpandWidth(false)
+			);
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Transfer velocity:");
+			GUILayout.Label(
+				(VOID_Tools.Younata_DeltaVToGetToOtherBody(
+					(vessel.mainBody.gravParameter / 1000000000),
+					(vessel.orbit.semiMajorAxis / 1000),
+					(body.orbit.semiMajorAxis / 1000)
+				) * 1000).ToString("F2") + "m/s",
+				GUILayout.ExpandWidth(false)
+			);
+			GUILayout.EndHorizontal();
+		}
+
+		public static void display_transfer_angles_PLANET2PLANET(CelestialBody body, Vessel vessel)
+		{
+			double dv1 = VOID_Tools.Younata_DeltaVToGetToOtherBody(
+				(vessel.mainBody.referenceBody.gravParameter / 1000000000),
+				(vessel.mainBody.orbit.semiMajorAxis / 1000),
+				(body.orbit.semiMajorAxis / 1000)
+			);
+			double dv2 = VOID_Tools.Younata_DeltaVToExitSOI(
+				(vessel.mainBody.gravParameter / 1000000000),
+				(vessel.orbit.semiMajorAxis / 1000),
+				(vessel.mainBody.sphereOfInfluence / 1000),
+				Math.Abs(dv1)
+			);
+
+			double trans_ejection_angle = VOID_Tools.Younata_TransferBurnPoint(
+				(vessel.orbit.semiMajorAxis / 1000),
+				dv2,
+				(Math.PI / 2.0),
+				(vessel.mainBody.gravParameter / 1000000000)
+			);
+			double curr_ejection_angle = VOID_Tools.Adammada_CurrentEjectionAngle(
+				FlightGlobals.ActiveVessel.longitude,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.rotationAngle,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.LAN,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.orbitPercent
+			);
+
+			double trans_phase_angle = VOID_Tools.Nivvy_CalcTransferPhaseAngle(
+				vessel.mainBody.orbit.semiMajorAxis,
+				body.orbit.semiMajorAxis,
+				vessel.mainBody.referenceBody.gravParameter
+			) % 360;
+			double curr_phase_angle = VOID_Tools.Adammada_CurrrentPhaseAngle(
+				body.orbit.LAN,
+				body.orbit.orbitPercent,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.LAN,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.orbitPercent
+			);
+
+			double adj_phase_angle = VOID_Tools.adjustCurrPhaseAngle(trans_phase_angle, curr_phase_angle);
+			double adj_trans_ejection_angle = VOID_Tools.adjust_transfer_ejection_angle(trans_ejection_angle, trans_phase_angle);
+			double adj_curr_ejection_angle = VOID_Tools.adjust_current_ejection_angle(curr_ejection_angle);
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Phase angle (curr/trans):");
+			GUILayout.Label(
+				adj_phase_angle.ToString("F3") + "° / " + trans_phase_angle.ToString("F3") + "°",
+				GUILayout.ExpandWidth(false)
+			);
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Ejection angle (curr/trans):");
+			GUILayout.Label(
+				adj_curr_ejection_angle.ToString("F3") + "° / " + adj_trans_ejection_angle.ToString("F3") + "°",
+				GUILayout.ExpandWidth(false)
+			);
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Transfer velocity:");
+			GUILayout.Label((dv2 * 1000).ToString("F2") + "m/s", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+		}
+
+		public static void display_transfer_angles_PLANET2MOON(CelestialBody body, Vessel vessel)
+		{
+			double dv1 = VOID_Tools.Younata_DeltaVToGetToOtherBody(
+				(vessel.mainBody.gravParameter / 1000000000),
+				(vessel.orbit.semiMajorAxis / 1000),
+				(body.orbit.semiMajorAxis / 1000)
+			);
+
+			double trans_phase_angle = VOID_Tools.Nivvy_CalcTransferPhaseAngle(
+				vessel.orbit.semiMajorAxis,
+				body.orbit.semiMajorAxis,
+				vessel.mainBody.gravParameter
+			);
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Phase angle (curr/trans):");
+			GUILayout.Label(
+				VOID_Tools.mrenigma03_calcphase(vessel, body).ToString("F3") + "° / " + trans_phase_angle.ToString("F3") + "°",
+				GUILayout.ExpandWidth(false)
+			);
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Transfer velocity:");
+			GUILayout.Label((dv1 * 1000).ToString("F2") + "m/s", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+		}
+
+		public static void display_transfer_angles_MOON2MOON(CelestialBody body, Vessel vessel)
+		{
+			double dv1 = VOID_Tools.Younata_DeltaVToGetToOtherBody(
+				(vessel.mainBody.referenceBody.gravParameter / 1000000000),
+				(vessel.mainBody.orbit.semiMajorAxis / 1000),
+				(body.orbit.semiMajorAxis / 1000)
+			);
+			double dv2 = VOID_Tools.Younata_DeltaVToExitSOI(
+				(vessel.mainBody.gravParameter / 1000000000),
+				(vessel.orbit.semiMajorAxis / 1000),
+				(vessel.mainBody.sphereOfInfluence / 1000),
+				Math.Abs(dv1)
+			);
+			double trans_ejection_angle = VOID_Tools.Younata_TransferBurnPoint(
+				(vessel.orbit.semiMajorAxis / 1000),
+				dv2,
+				(Math.PI / 2.0),
+				(vessel.mainBody.gravParameter / 1000000000)
+			);
+
+			double curr_phase_angle = VOID_Tools.Adammada_CurrrentPhaseAngle(
+				body.orbit.LAN,
+				body.orbit.orbitPercent,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.LAN,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.orbitPercent
+			);
+			double curr_ejection_angle = VOID_Tools.Adammada_CurrentEjectionAngle(
+				FlightGlobals.ActiveVessel.longitude,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.rotationAngle,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.LAN,
+				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.orbitPercent
+			);
+
+			double trans_phase_angle = VOID_Tools.Nivvy_CalcTransferPhaseAngle(
+				vessel.mainBody.orbit.semiMajorAxis,
+				body.orbit.semiMajorAxis,
+				vessel.mainBody.referenceBody.gravParameter
+			) % 360;
+
+			double adj_phase_angle = VOID_Tools.adjustCurrPhaseAngle(trans_phase_angle, curr_phase_angle);
+			//double adj_ejection_angle = adjustCurrEjectionAngle(trans_phase_angle, curr_ejection_angle);
+
+			//new stuff
+			//
+			double adj_trans_ejection_angle = VOID_Tools.adjust_transfer_ejection_angle(trans_ejection_angle, trans_phase_angle);
+			double adj_curr_ejection_angle = VOID_Tools.adjust_current_ejection_angle(curr_ejection_angle);
+			//
+			//
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Phase angle (curr/trans):");
+			GUILayout.Label(
+				adj_phase_angle.ToString("F3") + "° / " + trans_phase_angle.ToString("F3") + "°",
+				GUILayout.ExpandWidth(false)
+			);
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Ejection angle (curr/trans):");
+			GUILayout.Label(
+				adj_curr_ejection_angle.ToString("F3") + "° / " + adj_trans_ejection_angle.ToString("F3") + "°",
+				GUILayout.ExpandWidth(false)
+			);
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Transfer velocity:");
+			GUILayout.Label((dv2 * 1000).ToString("F2") + "m/s", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+		}
+
+		public static string get_heading_text(double heading)
+		{
+			if (heading > 348.75 || heading <= 11.25)
+				return "N";
+			else if (heading > 11.25 && heading <= 33.75)
+				return "NNE";
+			else if (heading > 33.75 && heading <= 56.25)
+				return "NE";
+			else if (heading > 56.25 && heading <= 78.75)
+				return "ENE";
+			else if (heading > 78.75 && heading <= 101.25)
+				return "E";
+			else if (heading > 101.25 && heading <= 123.75)
+				return "ESE";
+			else if (heading > 123.75 && heading <= 146.25)
+				return "SE";
+			else if (heading > 146.25 && heading <= 168.75)
+				return "SSE";
+			else if (heading > 168.75 && heading <= 191.25)
+				return "S";
+			else if (heading > 191.25 && heading <= 213.75)
+				return "SSW";
+			else if (heading > 213.75 && heading <= 236.25)
+				return "SW";
+			else if (heading > 236.25 && heading <= 258.75)
+				return "WSW";
+			else if (heading > 258.75 && heading <= 281.25)
+				return "W";
+			else if (heading > 281.25 && heading <= 303.75)
+				return "WNW";
+			else if (heading > 303.75 && heading <= 326.25)
+				return "NW";
+			else if (heading > 326.25 && heading <= 348.75)
+				return "NNW";
+			else
+				return "";
+		}
+	}
+
+	public class CBListComparer : IComparer<CelestialBody>
+	{
+		public int Compare(CelestialBody bodyA, CelestialBody bodyB)
+		{
+			Tools.PostDebugMessage(this, "got bodyA: {0} & bodyB: {1}", bodyA, bodyB);
+
+			if (bodyA == null && bodyB == null)
+			{
+				Tools.PostDebugMessage(this, "both bodies are null, returning 0");
+				return 0;
+			}
+			if (bodyA == null)
+			{
+				Tools.PostDebugMessage(this, "bodyA is null, returning -1");
+				return -1;
+			}
+			if (bodyB == null)
+			{
+				Tools.PostDebugMessage(this, "bodyB is null, returning 1");
+				return 1;
+			}
+
+			Tools.PostDebugMessage(this, "bodies are not null, carrying on");
+
+			if (object.ReferenceEquals(bodyA, bodyB))
+			{
+				Tools.PostDebugMessage(this, "bodies are equal, returning 0");
+				return 0;
+			}
+
+			Tools.PostDebugMessage(this, "bodies are not equal, carrying on");
+
+			if (bodyA.orbitDriver == null)
+			{
+				Tools.PostDebugMessage(this, "bodyA.orbit is null (bodyA is the sun, returning 1");
+				return 1;
+			}
+			if (bodyB.orbitDriver == null)
+			{
+				Tools.PostDebugMessage(this, "bodyB.orbit is null (bodyB is the sun, returning -1");
+				return -1;
+			}
+
+			Tools.PostDebugMessage(this, "orbits are not null, carrying on");
+
+			if (bodyA.orbit.referenceBody == bodyB.orbit.referenceBody)
+			{
+				Tools.PostDebugMessage(this, "bodies share a parent, comparing SMAs");
+				return -bodyA.orbit.semiMajorAxis.CompareTo(bodyB.orbit.semiMajorAxis);
+			}
+
+			Tools.PostDebugMessage(this, "orbits do not share a parent, carrying on");
+
+			if (bodyA.hasAncestor(bodyB))
+			{
+				Tools.PostDebugMessage(this, "bodyA is a moon or sub-moon of bodyB, returning -1");
+				return -1;
+			}
+			if (bodyB.hasAncestor(bodyA))
+			{
+				Tools.PostDebugMessage(this, "bodyA is a moon or sub-moon of bodyB, returning 1");
+				return 1;
+			}
+
+			Tools.PostDebugMessage(this, "bodies do not have an obvious relationship, searching for one");
+
+			if (VOID_Tools.NearestRelatedParents(ref bodyA, ref bodyB))
+			{
+				Tools.PostDebugMessage(this, "good relation {0} and {1}, comparing", bodyA.bodyName, bodyB.bodyName);
+				return this.Compare(bodyA, bodyB);
+			}
+
+			Tools.PostDebugMessage(this, "bad relation {0} and {1}, giving up", bodyA.bodyName, bodyB.bodyName);
+
+			return 0;
+		}
+	}
+}
+

--- a/VOID.csproj
+++ b/VOID.csproj
@@ -26,7 +26,7 @@
     <ConsolePause>false</ConsolePause>
     <CustomCommands>
       <CustomCommands>
-        <Command type="AfterBuild" command="xcopy /Y ${TargetFile} ..\..\..\Games\KSP_win\GameData\VOID\Plugins\" />
+        <Command type="AfterBuild" command="xcopy /Y ${TargetFile} ${ProjectDir}\GameData\VOID\Plugins\" />
       </CustomCommands>
     </CustomCommands>
   </PropertyGroup>
@@ -39,7 +39,7 @@
     <DefineConstants>TRACE</DefineConstants>
     <CustomCommands>
       <CustomCommands>
-        <Command type="AfterBuild" command="xcopy /Y ${TargetFile} ..\..\..\Games\KSP_win\GameData\VOID\Plugins\" />
+        <Command type="AfterBuild" command="xcopy /Y ${TargetFile} ${ProjectDir}\GameData\VOID\Plugins\" />
       </CustomCommands>
     </CustomCommands>
   </PropertyGroup>
@@ -54,7 +54,7 @@
     <ConsolePause>false</ConsolePause>
     <CustomCommands>
       <CustomCommands>
-        <Command type="AfterBuild" command="cp -afv ${TargetFile} /opt/games/KSP_linux/GameData/${ProjectName}/Plugins/" />
+        <Command type="AfterBuild" command="cp -afv ${TargetFile} ${ProjectDir}/GameData/${ProjectName}/Plugins/" />
       </CustomCommands>
     </CustomCommands>
   </PropertyGroup>
@@ -67,18 +67,13 @@
     <ConsolePause>false</ConsolePause>
     <CustomCommands>
       <CustomCommands>
-        <Command type="AfterBuild" command="cp -afv ${TargetFile} /opt/games/KSP_linux/GameData/${ProjectName}/Plugins/" />
+        <Command type="AfterBuild" command="cp -afv ${TargetFile} ${ProjectDir}/GameData/${ProjectName}/Plugins/" />
       </CustomCommands>
     </CustomCommands>
   </PropertyGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
-    <Compile Include="IVOID_Module.cs" />
-    <Compile Include="VOIDFlightMaster.cs" />
-    <Compile Include="VOID_Core.cs" />
-    <Compile Include="VOID_Module.cs" />
     <Compile Include="VOID_HUD.cs" />
-    <Compile Include="VOID_SaveValue.cs" />
     <Compile Include="VOID_Orbital.cs" />
     <Compile Include="VOID_SurfAtmo.cs" />
     <Compile Include="VOID_VesselInfo.cs" />
@@ -87,11 +82,7 @@
     <Compile Include="VOID_Rendezvous.cs" />
     <Compile Include="VOID_VesselRegister.cs" />
     <Compile Include="VOID_DataLogger.cs" />
-    <Compile Include="VOID_EditorCore.cs" />
     <Compile Include="VOID_EditorHUD.cs" />
-    <Compile Include="VOID_DataValue.cs" />
-    <Compile Include="VOIDEditorMaster.cs" />
-    <Compile Include="VOID_Tools.cs" />
     <Compile Include="VOID_Localization.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="VOID_HUDAdvanced.cs" />
@@ -100,6 +91,27 @@
     <Compile Include="VOID_StageInfo.cs" />
     <Compile Include="VOID_Styles.cs" />
     <Compile Include="VOID_Data.cs" />
+    <Compile Include="VOIDMaster_Flight.cs" />
+    <Compile Include="VOIDMaster_Editor.cs" />
+    <Compile Include="VOIDMaster_SpaceCentre.cs" />
+    <Compile Include="VOIDCore_SpaceCentre.cs" />
+    <Compile Include="VOIDCore_Flight.cs" />
+    <Compile Include="VOIDCore_Editor.cs" />
+    <Compile Include="VOIDCore_Generic.cs" />
+    <Compile Include="API\IVOID_Module.cs" />
+    <Compile Include="API\VOIDCore.cs" />
+    <Compile Include="API\IVOID_SaveValue.cs" />
+    <Compile Include="API\IVOID_DataValue.cs" />
+    <Compile Include="API\VOID_Module.cs" />
+    <Compile Include="Tools\VOID_Tools.cs" />
+    <Compile Include="Tools\VOID_DataValue.cs" />
+    <Compile Include="Tools\VOID_SaveValue.cs" />
+    <Compile Include="API\VOID_HUDModule.cs" />
+    <Compile Include="API\VOID_SingletonModule.cs" />
+    <Compile Include="API\Attributes\VOID_ScenesAttribute.cs" />
+    <Compile Include="API\Attributes\AVOID_SaveValue.cs" />
+    <Compile Include="API\VOIDMaster.cs" />
+    <Compile Include="API\Attributes\VOID_GameModesAttribute.cs" />
   </ItemGroup>
   <ProjectExtensions>
     <MonoDevelop>
@@ -131,5 +143,13 @@
       <Name>VesselSimulator</Name>
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup>
+    <None Include="GameData\VOID\Textures\ATM_VOID.cfg" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="API\" />
+    <Folder Include="Tools\" />
+    <Folder Include="API\Attributes\" />
+  </ItemGroup>
 </Project>
 

--- /dev/null
+++ b/VOIDCore_Editor.cs
@@ -1,1 +1,51 @@
+// VOID
+//
+// VOID_EditorCore.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+using KerbalEngineer.VesselSimulator;
+using KSP;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	[VOID_Scenes(GameScenes.EDITOR)]
+	public class VOIDCore_Editor : VOIDCore_Generic<VOIDCore_Editor>
+	{
+		public VOIDCore_Editor() : base()
+		{
+			this.Name = "VOID Core: Editor";
+		}
+
+		public override void FixedUpdate() {}
+	}
+}
+
+

--- /dev/null
+++ b/VOIDCore_Flight.cs
@@ -1,1 +1,55 @@
+// VOID
+//
+// VOIDCore_Flight.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+using KSP;
+using System;
+using UnityEngine;
+
+namespace VOID
+{
+	[VOID_Scenes(GameScenes.FLIGHT)]
+	public class VOIDCore_Flight : VOIDCore_Generic<VOIDCore_Flight>
+	{
+		public VOIDCore_Flight()
+		{
+			base.Name = "VOID Core: Flight";
+		}
+
+		public override void DrawConfigurables()
+		{
+			if (HighLogic.LoadedSceneIsFlight)
+			{
+				this.consumeResource.value = GUILayout.Toggle(this.consumeResource, "Consume Resources");
+			}
+
+			base.DrawConfigurables();
+		}
+	}
+}
+
+

--- /dev/null
+++ b/VOIDCore_Generic.cs
@@ -1,1 +1,1200 @@
-
+// VOID
+//
+// VOIDCore_Generic.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+using KerbalEngineer.VesselSimulator;
+using KSP;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	public abstract class VOIDCore_Generic<T> : VOID_SingletonModule<T>, IVOID_Module, IDisposable
+		where T : VOID_Module, new()
+	{
+		/*
+		 * Fields
+		 * */
+		protected string VoidName = "VOID";
+		protected string VoidVersion;
+
+		[AVOID_SaveValue("configValue")]
+		protected VOID_SaveValue<int> _configVersion = VOIDCore.CONFIG_VERSION;
+
+		protected List<IVOID_Module> _modules = new List<IVOID_Module>();
+		protected bool _modulesLoaded = false;
+
+		[AVOID_SaveValue("mainWindowPos")]
+		protected VOID_SaveValue<Rect> mainWindowPos = new Rect(475, 575, 10f, 10f);
+		[AVOID_SaveValue("mainGuiMinimized")]
+		protected VOID_SaveValue<bool> mainGuiMinimized = false;
+
+		[AVOID_SaveValue("configWindowPos")]
+		protected VOID_SaveValue<Rect> configWindowPos = new Rect(825, 625, 10f, 10f);
+		[AVOID_SaveValue("configWindowMinimized")]
+
+		protected VOID_SaveValue<bool> configWindowMinimized = true;
+
+		protected Texture2D VOIDIconTexture;
+		protected string VOIDIconOnActivePath;
+		protected string VOIDIconOnInactivePath;
+		protected string VOIDIconOffActivePath;
+		protected string VOIDIconOffInactivePath;
+
+		protected GUIStyle iconStyle;
+
+		protected int windowBaseID = -96518722;
+		protected int _windowID = 0;
+
+		protected bool GUIStylesLoaded = false;
+
+		protected CelestialBody _homeBody;
+
+		[AVOID_SaveValue("togglePower")]
+		public VOID_SaveValue<bool> togglePower = true;
+
+		public override bool powerAvailable { get; protected set; }
+
+		[AVOID_SaveValue("consumeResource")]
+		protected VOID_SaveValue<bool> consumeResource = false;
+
+		[AVOID_SaveValue("resourceName")]
+		protected VOID_SaveValue<string> resourceName = "ElectricCharge";
+
+		[AVOID_SaveValue("resourceRate")]
+		protected VOID_SaveValue<float> resourceRate = 0.2f;
+
+		[AVOID_SaveValue("updatePeriod")]
+		protected VOID_SaveValue<double> _updatePeriod = 1001f / 15000f;
+		protected string stringFrequency;
+
+		[AVOID_SaveValue("vesselSimActive")]
+		protected VOID_SaveValue<bool> vesselSimActive;
+
+		// Vessel Type Housekeeping
+		protected List<VesselType> _allVesselTypes = new List<VesselType>();
+		protected bool vesselTypesLoaded = false;
+
+		protected string defaultSkin = "KSP window 2";
+
+		[AVOID_SaveValue("defaultSkin")]
+		protected VOID_SaveValue<string> _skinName;
+		protected int _skinIdx;
+
+		protected Dictionary<string, GUISkin> validSkins;
+		protected string[] skinNames;
+		protected string[] forbiddenSkins =
+			{
+				"PlaqueDialogSkin",
+				"FlagBrowserSkin",
+				"SSUITextAreaDefault",
+				"ExperimentsDialogSkin",
+				"ExpRecoveryDialogSkin",
+				"KSP window 5",
+				"KSP window 6",
+				"PartTooltipSkin",
+				"KSCContextMenuSkin"
+			};
+		protected bool skinsLoaded = false;
+
+		public override bool configDirty { get; set; }
+
+		internal IButton ToolbarButton;
+
+		internal ApplicationLauncherButton AppLauncherButton;
+
+		/*
+		 * Properties
+		 * */
+		public override int configVersion
+		{
+			get
+			{
+				return this._configVersion;
+			}
+		}
+
+		public bool factoryReset
+		{
+			get;
+			protected set;
+		}
+
+		public override List<IVOID_Module> Modules
+		{
+			get
+			{
+				return this._modules;
+			}
+		}
+
+		public override GUISkin Skin
+		{
+			get
+			{
+				if (this.skinsLoaded)
+				{
+					try
+					{
+						return this.validSkins[this._skinName];
+					}
+					catch
+					{
+					}
+				}
+
+				return AssetBase.GetGUISkin(this.defaultSkin);
+			}
+		}
+
+		public override int windowID
+		{
+			get
+			{
+				if (this._windowID == 0)
+				{
+					this._windowID = this.windowBaseID;
+				}
+				return this._windowID++;
+			}
+		}
+
+		public override List<CelestialBody> allBodies
+		{
+			get
+			{
+				return FlightGlobals.Bodies;
+			}
+		}
+
+		public override List<CelestialBody> sortedBodyList
+		{
+			get;
+			protected set;
+		}
+
+		public override CelestialBody HomeBody
+		{
+			get
+			{
+				if (this._homeBody == null)
+				{
+					if (Planetarium.fetch != null)
+					{
+						this._homeBody = Planetarium.fetch.Home;
+					}
+				}
+
+				return this._homeBody;
+			}
+		}
+
+		public override List<VesselType> allVesselTypes
+		{
+			get
+			{
+				return this._allVesselTypes;
+			}
+		}
+
+		public override float updateTimer
+		{
+			get;
+			protected set;
+		}
+
+
+		public override double updatePeriod
+		{
+			get
+			{
+				return this._updatePeriod;
+			}
+		}
+
+		public override Stage[] Stages
+		{
+			get;
+			protected set;
+		}
+
+		public override Stage LastStage
+		{
+			get;
+			protected set;
+		}
+
+		protected IconState powerState
+		{
+			get
+			{
+				if (this.togglePower && this.powerAvailable)
+				{
+					return IconState.PowerOn;
+				}
+				else
+				{
+					return IconState.PowerOff;
+				}
+
+			}
+		}
+
+		protected IconState activeState
+		{
+			get
+			{
+				if (this.mainGuiMinimized)
+				{
+					return IconState.Inactive;
+				}
+				else
+				{
+					return IconState.Active;
+				}
+
+			}
+		}
+
+		protected bool UseToolbarManager
+		{
+			get
+			{
+				return useToolbarManager & ToolbarManager.ToolbarAvailable;
+			}
+			set
+			{
+				if (useToolbarManager == value)
+				{
+					return;
+				}
+
+				if (value == false && this.ToolbarButton != null)
+				{
+					this.ToolbarButton.Destroy();
+					this.ToolbarButton = null;
+				}
+				if (value == true)
+				{
+					if (this.AppLauncherButton != null)
+					{
+						ApplicationLauncher.Instance.RemoveModApplication(this.AppLauncherButton);
+						this.AppLauncherButton = null;
+					}
+
+					this.InitializeToolbarButton();
+				}
+
+				useToolbarManager = value;
+			}
+		}
+
+		protected virtual ApplicationLauncher.AppScenes appIconVisibleScenes
+		{
+			get
+			{
+				return HighLogic.LoadedScene.ToAppScenes();
+			}
+		}
+
+		/*
+		 * Events
+		 * */
+		public override event VOIDEventHandler onApplicationQuit;
+		public override event VOIDEventHandler onSkinChanged;
+
+		/*
+		 * Methods
+		 * */
+		public override void DrawGUI()
+		{
+			this._windowID = this.windowBaseID;
+
+			if (!this._modulesLoaded)
+			{
+				this.LoadModulesOfType<IVOID_Module>();
+			}
+
+			if (!this.skinsLoaded)
+			{
+				this.LoadSkins();
+			}
+
+			GUI.skin = this.Skin;
+
+			if (!this.GUIStylesLoaded)
+			{
+				this.LoadGUIStyles();
+
+				Tools.PostDebugMessage(
+					this,
+					"ToolbarAvailable: {0}, UseToobarManager: {1}",
+					ToolbarManager.ToolbarAvailable,
+					this.UseToolbarManager);
+			}
+
+			if (!this.UseToolbarManager)
+			{
+				if (this.AppLauncherButton == null)
+				{
+					Tools.PostDebugMessage(this,
+						"UseToolbarManager = false (ToolbarAvailable = {0}) and " +
+						"AppLauncherButton is null, making AppLauncher button.",
+						ToolbarManager.ToolbarAvailable
+					);
+					this.InitializeAppLauncherButton();
+				}
+			}
+			else if (this.ToolbarButton == null)
+			{
+				Tools.PostDebugMessage(this,
+					"UseToolbarManager = true (ToolbarAvailable = {0}) and " +
+					"ToolbarButton is null, making Toolbar button.",
+					ToolbarManager.ToolbarAvailable
+				);
+				this.InitializeToolbarButton();
+			}
+
+			if (!this.mainGuiMinimized)
+			{
+
+				Rect _mainWindowPos = this.mainWindowPos;
+
+				_mainWindowPos = GUILayout.Window(
+					this.windowID,
+					_mainWindowPos,
+					VOID_Tools.GetWindowHandler(this.VOIDMainWindow),
+					string.Join(" ", new string[] { this.VoidName, this.VoidVersion }),
+					GUILayout.Width(250),
+					GUILayout.Height(50)
+				);
+
+				if (HighLogic.LoadedSceneIsEditor)
+				{
+					_mainWindowPos = Tools.ClampRectToEditorPad(_mainWindowPos);
+				}
+				else
+				{
+					_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,
+					VOID_Tools.GetWindowHandler(this.VOIDConfigWindow),
+					string.Join(" ", new string[] { this.VoidName, "Configuration" }),
+					GUILayout.Width(250),
+					GUILayout.Height(50)
+				);
+
+				if (HighLogic.LoadedSceneIsEditor)
+				{
+					_configWindowPos = Tools.ClampRectToEditorPad(_configWindowPos);
+				}
+				else
+				{
+					_configWindowPos = Tools.ClampRectToScreen(_configWindowPos);
+				}
+
+				if (_configWindowPos != this.configWindowPos)
+				{
+					this.configWindowPos = _configWindowPos;
+				}
+			}
+		}
+
+		public virtual void Update()
+		{
+			this.LoadBeforeUpdate();
+
+			if (
+				this.vesselSimActive &&
+				(
+					this.vessel != null ||
+					(
+						HighLogic.LoadedSceneIsEditor &&
+						EditorLogic.RootPart != null &&
+						EditorLogic.SortedShipList.Count > 0
+					)
+				)
+			)
+			{
+				Tools.PostDebugMessage(this, "Updating SimManager.");
+				this.UpdateSimManager();
+			}
+
+			if (!this.guiRunning)
+			{
+				this.StartGUI();
+			}
+
+			foreach (IVOID_Module module in this.Modules)
+			{
+				Tools.PostDebugMessage(
+					this,
+					"\n\tmodule.Name={0}" +
+					"\n\tmodule.guiRunning={1}" +
+					"\n\tmodule.toggleActive={2}" +
+					"\n\tmodule.inValidScene={3}" +
+					"\n\tthis.togglePower={4}" +
+					"\n\tthis.factoryReset={5}" +
+					"\n\tHighLogic.LoadedSceneIsEditor={6}" +
+					"\n\t(EditorLogic.RootPart != null && EditorLogic.SortedShipList.Count > 0)={7}" +
+					"\n\t(EditorLogic.RootPart == null || EditorLogic.SortedShipList.Count == 0)={8}",
+					module.Name,
+					module.guiRunning,
+					module.toggleActive,
+					module.inValidScene,
+					this.togglePower,
+					this.factoryReset,
+					HighLogic.LoadedSceneIsEditor,
+					(EditorLogic.RootPart != null && EditorLogic.SortedShipList.Count > 0),
+					(EditorLogic.RootPart == null || EditorLogic.SortedShipList.Count == 0)
+				);
+				if (
+					!module.guiRunning &&
+					module.toggleActive &&
+					module.inValidScene &&
+					(
+						!HighLogic.LoadedSceneIsEditor ||
+						(EditorLogic.RootPart != null && EditorLogic.SortedShipList.Count > 0)
+					)
+				)
+				{
+					module.StartGUI();
+				}
+				if (
+					module.guiRunning &&
+					(
+						!module.toggleActive ||
+					    !this.togglePower ||
+						!module.inValidScene ||
+					    this.factoryReset ||
+						(
+							HighLogic.LoadedSceneIsEditor &&
+							(EditorLogic.RootPart == null || EditorLogic.SortedShipList.Count == 0)
+						)
+					)
+				)
+				{
+					module.StopGUI();
+				}
+
+				if (module is IVOID_BehaviorModule)
+				{
+					((IVOID_BehaviorModule)module).Update();
+				}
+			}
+
+			this.CheckAndSave();
+			this.updateTimer += Time.deltaTime;
+		}
+
+		public virtual 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_Module module in this.Modules)
+			{
+				if (module is IVOID_BehaviorModule)
+				{
+					((IVOID_BehaviorModule)module).FixedUpdate();
+				}
+			}
+		}
+
+		public void OnDestroy()
+		{
+			foreach (IVOID_Module module in this.Modules)
+			{
+				if (module is IVOID_BehaviorModule)
+				{
+					((IVOID_BehaviorModule)module).OnDestroy();
+				}
+			}
+
+			this.Dispose();
+		}
+
+		public virtual void OnApplicationQuit()
+		{
+			if (this.onApplicationQuit != null)
+			{
+				this.onApplicationQuit(this);
+			}
+
+			this.OnDestroy();
+		}
+
+		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.LoadedSceneIsFlight)
+			{
+				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.LoadedSceneIsFlight)
+				{
+					foreach (IVOID_Module module in this.Modules)
+					{
+						module.toggleActive = GUILayout.Toggle(module.toggleActive, module.Name);
+					}
+				}
+			}
+			else
+			{
+				GUILayout.Label("-- POWER LOST --", VOID_Styles.labelRed);
+			}
+
+			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;
+
+			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._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, VOID_Styles.labelCenter, GUILayout.ExpandWidth(true));
+
+			_content.text = "►";
+			_content.tooltip = "Select next skin";
+			if (GUILayout.Button(_content, GUILayout.ExpandWidth(true)))
+			{
+				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];
+				this.GUIStylesLoaded = false;
+			}
+
+			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));
+
+			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 UpdateSimManager()
+		{
+			if (SimManager.ResultsReady())
+			{
+				Tools.PostDebugMessage(this, "VesselSimulator results ready, setting Stages.");
+
+				this.Stages = SimManager.Stages;
+
+				if (this.Stages != null)
+				{
+					this.LastStage = this.Stages.Last();
+				}
+
+				if (HighLogic.LoadedSceneIsEditor)
+				{
+					SimManager.Gravity = VOID_Data.KerbinGee;
+				}
+				else
+				{
+					double radius = this.vessel.Radius();
+					SimManager.Gravity = this.vessel.mainBody.gravParameter / (radius * radius);
+				}
+
+				SimManager.minSimTime = new TimeSpan(0, 0, 0, 0, (int)(this.updatePeriod * 1000d));
+
+				SimManager.TryStartSimulation();
+			}
+			#if DEBUG
+			else
+			{
+				Tools.PostDebugMessage(this, "VesselSimulator results not ready.");
+			}
+			#endif
+		}
+
+		protected void LoadModulesOfType<U>()
+		{
+			Tools.DebugLogger sb = Tools.DebugLogger.New(this);
+			sb.AppendLine("Loading modules...");
+
+			foreach (AssemblyLoader.LoadedAssembly assy in AssemblyLoader.loadedAssemblies)
+			{
+				foreach (Type loadedType in assy.assembly.GetExportedTypes())
+				{
+					if (
+						loadedType.IsInterface ||
+						loadedType.IsAbstract ||
+						!typeof(U).IsAssignableFrom(loadedType) ||
+						typeof(VOIDCore).IsAssignableFrom(loadedType)
+					)
+					{
+						continue;
+					}
+
+					sb.AppendFormat("Checking IVOID_Module type {0}...", loadedType.Name);
+
+					GameScenes[] validScenes = null;
+
+					foreach (var attr in loadedType.GetCustomAttributes(true))
+					{
+						if (attr is VOID_ScenesAttribute)
+						{
+							validScenes = ((VOID_ScenesAttribute)attr).ValidScenes;
+
+							sb.Append("VOID_ScenesAttribute found;");
+
+							break;
+						}
+					}
+
+					if (validScenes == null)
+					{
+						validScenes = new GameScenes[] { GameScenes.FLIGHT };
+
+
+						sb.Append("VOID_ScenesAttribute not found;");
+
+					}
+
+					sb.AppendFormat(
+						" validScenes set to {0}.",
+						string.Join(
+							", ",
+							validScenes.Select(s => Enum.GetName(typeof(GameScenes), s)).ToArray()
+						)
+					);
+
+					if (!validScenes.Contains(HighLogic.LoadedScene))
+					{
+						sb.AppendFormat("  {0} not found in validScenes, skipping.",
+							Enum.GetName(typeof(GameScenes), HighLogic.LoadedScene));
+						continue;
+					}
+
+					sb.AppendFormat("Loading IVOID_Module type {0}...", loadedType.Name);
+
+					try
+					{
+						this.LoadModule(loadedType);
+						sb.AppendLine("Success.");
+					}
+					catch (Exception ex)
+					{
+						sb.AppendFormat("Failed, caught {0}\n", ex.GetType().Name);
+
+						#if DEBUG
+						Debug.LogException(ex);
+						#endif
+					}
+				}
+			}
+
+			this._modulesLoaded = true;
+
+			sb.AppendFormat("Loaded {0} modules.\n", this.Modules.Count);
+
+			sb.Print();
+		}
+
+		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;
+			}
+
+			var InstanceProperty = T.GetProperty(
+				"Instance",
+				System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public
+			);
+
+			object modInstance = null;
+			IVOID_Module module;
+
+			if (InstanceProperty != null)
+			{
+				modInstance = InstanceProperty.GetValue(null, null);
+			}
+
+			if (modInstance != null)
+			{
+				module = modInstance as IVOID_Module;
+			}
+			else
+			{
+				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()
+		{
+			VOID_Styles.OnSkinChanged();
+
+			if (this.onSkinChanged != null)
+			{
+				this.onSkinChanged(this);
+			}
+
+			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();
+			}
+
+			if (this.sortedBodyList == null && FlightGlobals.Bodies != null && FlightGlobals.Bodies.Count > 0)
+			{
+				this.sortedBodyList = new List<CelestialBody>(FlightGlobals.Bodies);
+				this.sortedBodyList.Sort(new CBListComparer());
+				this.sortedBodyList.Reverse();
+
+				Debug.Log(string.Format("sortedBodyList: {0}", string.Join("\n\t", this.sortedBodyList.Select(b => b.bodyName).ToArray())));
+			}
+
+		}
+
+		protected void InitializeToolbarButton()
+		{
+			// Do nothing if (the Toolbar is not available.
+			if (!ToolbarManager.ToolbarAvailable)
+			{
+				Tools.PostDebugMessage(this, "Refusing to make a ToolbarButton: ToolbarAvailable = false");
+				return;
+			}
+
+			this.ToolbarButton = ToolbarManager.Instance.add(this.VoidName, "coreToggle");
+			this.ToolbarButton.Text = this.VoidName;
+			this.SetIconTexture(this.powerState | this.activeState);
+
+			this.ToolbarButton.Visible = true;
+
+			this.ToolbarButton.OnClick += 
+				(e) =>
+			{
+				this.ToggleMainWindow();
+			};
+
+			Tools.PostDebugMessage(string.Format("{0}: Toolbar Button initialized.", this.GetType().Name));
+		}
+
+		protected void InitializeAppLauncherButton()
+		{
+			if (ApplicationLauncher.Ready)
+			{
+				this.AppLauncherButton = ApplicationLauncher.Instance.AddModApplication(
+					this.ToggleMainWindow, this.ToggleMainWindow,
+					this.appIconVisibleScenes,
+					this.VOIDIconTexture
+				);
+
+				Tools.PostDebugMessage(
+					this,
+					"AppLauncherButton initialized in {0}",
+					Enum.GetName(
+						typeof(GameScenes),
+						HighLogic.LoadedScene
+					)
+				);
+			}
+		}
+
+		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.Replace("icon", "appIcon"), false);
+
+			if (this.AppLauncherButton != null)
+			{
+				this.AppLauncherButton.SetTexture(VOIDIconTexture);
+			}
+		}
+
+		protected virtual 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 override void SaveConfig()
+		{
+			if (this.configNeedsUpdate && this is VOIDCore_Flight)
+			{
+				KSP.IO.File.Delete<T>("config.xml");
+			}
+
+			var config = KSP.IO.PluginConfiguration.CreateForType<T>();
+
+			config.load();
+
+			this._SaveToConfig(config);
+
+			foreach (IVOID_Module module in this.Modules)
+			{
+				module._SaveToConfig(config);
+			}
+
+			config.save();
+
+			this.configDirty = false;
+		}
+
+		public VOIDCore_Generic()
+		{
+			this.Name = "VOID Core";
+
+			System.Version version = this.GetType().Assembly.GetName().Version;
+
+			this.VoidVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.MajorRevision);
+
+			this.powerAvailable = true;
+
+			this.toggleActive = true;
+
+			this._skinName = this.defaultSkin;
+			this._skinIdx = int.MinValue;
+
+			this.VOIDIconOnActivePath = "VOID/Textures/void_icon_light_glow";
+			this.VOIDIconOnInactivePath = "VOID/Textures/void_icon_dark_glow";
+			this.VOIDIconOffActivePath = "VOID/Textures/void_icon_light";
+			this.VOIDIconOffInactivePath = "VOID/Textures/void_icon_dark";
+
+			this.saveTimer = 0f;
+			this.updateTimer = 0f;
+
+			this.vesselSimActive = true;
+
+			this.UseToolbarManager = ToolbarManager.ToolbarAvailable;
+
+			this.LoadConfig();
+
+			this._configVersion = VOIDCore.CONFIG_VERSION;
+			
+			this.SetIconTexture(this.powerState | this.activeState);
+
+			this.factoryReset = false;
+		}
+
+		public virtual void Dispose()
+		{
+			this.StopGUI();
+
+			if (this.AppLauncherButton != null)
+			{
+				ApplicationLauncher.Instance.RemoveModApplication(this.AppLauncherButton);
+				this.AppLauncherButton = null;
+			}
+			if (this.ToolbarButton != null)
+			{
+				this.ToolbarButton.Destroy();
+				this.ToolbarButton = null;
+			}
+
+			_instance = null;
+			_initialized = false;
+		}
+
+		protected enum IconState
+		{
+			PowerOff = 1,
+			PowerOn = 2,
+			Inactive = 4,
+			Active = 8
+		}
+	}
+}
+
+

--- /dev/null
+++ b/VOIDCore_SpaceCentre.cs
@@ -1,1 +1,44 @@
+// VOID
+//
+// VOIDCore_SpaceCentre.cs
+//
+// Copyright © 2015, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+using System;
 
+namespace VOID
+{
+	[VOID_Scenes(GameScenes.SPACECENTER)]
+	public class VOIDCore_SpaceCentre : VOIDCore_Generic<VOIDCore_SpaceCentre>
+	{
+		public VOIDCore_SpaceCentre() : base()
+		{
+			base.Name = "VOID Core: SpaceCentre";
+		}
+
+		public override void FixedUpdate() {}
+	}
+}
+
+

file:a/VOIDEditorMaster.cs (deleted)
--- a/VOIDEditorMaster.cs
+++ /dev/null
@@ -1,110 +1,1 @@
-// VOID
-//
-// VOIDEditorMaster.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-///////////////////////////////////////////////////////////////////////////////
-//
-//  Much, much credit to Younata, Adammada, Nivvydaskrl and to all the authors
-//  behind MechJeb, RemoteTech Relay Network, ISA MapSat, and Protractor for some
-//  invaluable functions and making your nicely written code available to learn from.
-//
-///////////////////////////////////////////////////////////////////////////////
-//
-//  This software uses VesselSimulator and Engineer.Extensions from Engineer Redux.
-//  Engineer Redux (c) 2013 cybutek
-//  Used by permission.
-//
-///////////////////////////////////////////////////////////////////////////////
 
-using Engineer.VesselSimulator;
-using KSP;
-using System;
-using ToadicusTools;
-using UnityEngine;
-
-namespace VOID
-{
-	[KSPAddon(KSPAddon.Startup.EditorAny, false)]
-	public class VOIDEditorMaster : MonoBehaviour
-	{
-		protected VOID_EditorCore Core;
-
-		public void Awake()
-		{
-			Tools.PostDebugMessage ("VOIDEditorMaster: Waking up.");
-			this.Core = VOID_EditorCore.Instance;
-			this.Core.ResetGUI ();
-			Tools.PostDebugMessage ("VOIDEditorMaster: Awake.");
-		}
-
-		public void Update()
-		{
-			if (!HighLogic.LoadedSceneIsEditor && this.Core != null)
-			{
-				this.Core.SaveConfig ();
-				this.Core = null;
-				VOID_EditorCore.Reset();
-				return;
-			}
-
-			if (this.Core == null)
-			{
-				this.Awake();
-			}
-
-			this.Core.Update ();
-
-			if (this.Core.factoryReset)
-			{
-				KSP.IO.File.Delete<VOID_EditorCore>("config.xml");
-				this.Core = null;
-				VOID_EditorCore.Reset();
-			}
-		}
-
-		public void FixedUpdate()
-		{
-			if (this.Core == null || !HighLogic.LoadedSceneIsEditor)
-			{
-				return;
-			}
-
-			this.Core.FixedUpdate ();
-		}
-
-		public void OnGUI()
-		{
-			if (this.Core == null)
-			{
-				return;
-			}
-
-			this.Core.OnGUI();
-		}
-	}
-}
-

file:a/VOIDFlightMaster.cs (deleted)
--- a/VOIDFlightMaster.cs
+++ /dev/null
@@ -1,109 +1,1 @@
-// VOID
-//
-// VOIDFlightMaster.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-///////////////////////////////////////////////////////////////////////////////
-//
-//  Much, much credit to Younata, Adammada, Nivvydaskrl and to all the authors
-//  behind MechJeb, RemoteTech Relay Network, ISA MapSat, and Protractor for some
-//  invaluable functions and making your nicely written code available to learn from.
-//
-///////////////////////////////////////////////////////////////////////////////
-//
-//  This software uses VesselSimulator and Engineer.Extensions from Engineer Redux.
-//  Engineer Redux (c) 2013 cybutek
-//  Used by permission.
-//
-///////////////////////////////////////////////////////////////////////////////
 
-using System;
-using UnityEngine;
-using Engineer.VesselSimulator;
-using ToadicusTools;
-
-namespace VOID
-{
-	[KSPAddon(KSPAddon.Startup.Flight, false)]
-	public class VOIDFlightMaster : MonoBehaviour
-	{
-		protected VOID_Core Core;
-
-		public void Awake()
-		{
-			Tools.PostDebugMessage ("VOIDFlightMaster: Waking up.");
-			this.Core = (VOID_Core)VOID_Core.Instance;
-			this.Core.ResetGUI ();
-			Tools.PostDebugMessage ("VOIDFlightMaster: Awake.");
-		}
-
-		public void Update()
-		{
-			if (!HighLogic.LoadedSceneIsFlight && this.Core != null)
-			{
-				this.Core.SaveConfig ();
-				this.Core = null;
-				VOID_Core.Reset();
-				return;
-			}
-
-			if (this.Core == null)
-			{
-				this.Awake();
-			}
-
-			this.Core.Update ();
-
-			if (this.Core.factoryReset)
-			{
-				KSP.IO.File.Delete<VOID_Core>("config.xml");
-				this.Core = null;
-				VOID_Core.Reset();
-			}
-		}
-
-		public void FixedUpdate()
-		{
-			if (this.Core == null || !HighLogic.LoadedSceneIsFlight)
-			{
-				return;
-			}
-
-			this.Core.FixedUpdate ();
-		}
-
-		public void OnGUI()
-		{
-			if (this.Core == null)
-			{
-				return;
-			}
-
-			this.Core.OnGUI();
-		}
-    }
-}
-

--- /dev/null
+++ b/VOIDMaster_Editor.cs
@@ -1,1 +1,63 @@
+// VOID
+//
+// VOIDEditorMaster.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+//  Much, much credit to Younata, Adammada, Nivvydaskrl and to all the authors
+//  behind MechJeb, RemoteTech Relay Network, ISA MapSat, and Protractor for some
+//  invaluable functions and making your nicely written code available to learn from.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+//  This software uses VesselSimulator and Engineer.Extensions from Engineer Redux.
+//  Engineer Redux (c) 2013 cybutek
+//  Used by permission.
+//
+///////////////////////////////////////////////////////////////////////////////
 
+using KerbalEngineer.VesselSimulator;
+using KSP;
+using System;
+using ToadicusTools;
+using UnityEngine;
+
+namespace VOID
+{
+	[KSPAddon(KSPAddon.Startup.EditorAny, false)]
+	public class VOIDMaster_Editor : VOIDMaster<VOIDCore_Editor>
+	{
+		public override void Awake()
+		{
+			Tools.PostDebugMessage ("VOIDEditorMaster: Waking up.");
+			this.Core = VOIDCore_Editor.Instance;
+			this.Core.ResetGUI ();
+			Tools.PostDebugMessage ("VOIDEditorMaster: Awake.");
+		}
+	}
+}
+

--- /dev/null
+++ b/VOIDMaster_Flight.cs
@@ -1,1 +1,62 @@
+// VOID
+//
+// VOIDFlightMaster.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+//  Much, much credit to Younata, Adammada, Nivvydaskrl and to all the authors
+//  behind MechJeb, RemoteTech Relay Network, ISA MapSat, and Protractor for some
+//  invaluable functions and making your nicely written code available to learn from.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+//  This software uses VesselSimulator and Engineer.Extensions from Engineer Redux.
+//  Engineer Redux (c) 2013 cybutek
+//  Used by permission.
+//
+///////////////////////////////////////////////////////////////////////////////
 
+using System;
+using UnityEngine;
+using KerbalEngineer.VesselSimulator;
+using ToadicusTools;
+
+namespace VOID
+{
+	[KSPAddon(KSPAddon.Startup.Flight, false)]
+	public class VOIDMaster_Flight : VOIDMaster<VOIDCore_Flight>
+	{
+		public override void Awake()
+		{
+				this.LogDebug("Waking up.");
+				this.Core = VOIDCore_Flight.Instance;
+				this.Core.ResetGUI ();
+				this.LogDebug("Awake.");
+		}
+	}
+}
+

--- /dev/null
+++ b/VOIDMaster_SpaceCentre.cs
@@ -1,1 +1,62 @@
+// VOID
+//
+// VOIDFlightMaster.cs
+//
+// Copyright © 2014, toadicus
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation and/or other
+//    materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be used
+//    to endorse or promote products derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// 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
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+//  Much, much credit to Younata, Adammada, Nivvydaskrl and to all the authors
+//  behind MechJeb, RemoteTech Relay Network, ISA MapSat, and Protractor for some
+//  invaluable functions and making your nicely written code available to learn from.
+//
+///////////////////////////////////////////////////////////////////////////////
+//
+//  This software uses VesselSimulator and Engineer.Extensions from Engineer Redux.
+//  Engineer Redux (c) 2013 cybutek
+//  Used by permission.
+//
+///////////////////////////////////////////////////////////////////////////////
 
+using System;
+using UnityEngine;
+using KerbalEngineer.VesselSimulator;
+using ToadicusTools;
+
+namespace VOID
+{
+	[KSPAddon(KSPAddon.Startup.SpaceCentre, false)]
+	public class VOIDMaster_SpaceCentre : VOIDMaster<VOIDCore_SpaceCentre>
+	{
+		public override void Awake()
+		{
+				this.LogDebug("Waking up.");
+				this.Core = VOIDCore_SpaceCentre.Instance;
+				this.Core.ResetGUI ();
+				this.LogDebug("Awake.");
+		}
+	}
+}
+

--- a/VOID_CBInfoBrowser.cs
+++ b/VOID_CBInfoBrowser.cs
@@ -56,7 +56,7 @@
 
 		public VOID_CBInfoBrowser()
 		{
-			this._Name = "Celestial Body Information Browser";
+			this.Name = "Celestial Body Information Browser";
 
 			this.WindowPos.x = 10;
 			this.WindowPos.y = 85;
@@ -368,7 +368,7 @@
 
 			GUILayout.Label(num_art_sats.ToString(), VOID_Styles.labelRight, GUILayout.ExpandWidth(true));
 
-			double g_ASL = (VOID_Core.Constant_G * body.Mass) / (body.Radius * body.Radius);
+			double g_ASL = (VOIDCore.Constant_G * body.Mass) / (body.Radius * body.Radius);
 
 			GUILayout.Label(Tools.MuMech_ToSI(g_ASL) + "m/s²", VOID_Styles.labelRight, GUILayout.ExpandWidth(true));
 

--- a/VOID_CareerStatus.cs
+++ b/VOID_CareerStatus.cs
@@ -34,6 +34,8 @@
 
 namespace VOID
 {
+	[VOID_Scenes(GameScenes.FLIGHT, GameScenes.EDITOR, GameScenes.SPACECENTER)]
+	[VOID_GameModes(Game.Modes.CAREER, Game.Modes.SCIENCE_SANDBOX)]
 	public class VOID_CareerStatus : VOID_WindowModule
 	{
 		public static VOID_CareerStatus Instance
@@ -42,20 +44,25 @@
 			private set;
 		}
 
+		public static string formatDelta(double delta, string numberFormat)
+		{
+			if (delta > 0)
+			{
+				return string.Format("<color='lime'>{0}↑</color>", delta.ToString(numberFormat, Tools.mySIFormatter));
+			}
+			else if (delta < 0)
+			{
+				return string.Format("<color='red'>{0}↓</color>", delta.ToString(numberFormat, Tools.mySIFormatter));
+			}
+			else
+			{
+				return "0";
+			}
+		}
+
 		public static string formatDelta(double delta)
 		{
-			if (delta > 0)
-			{
-				return string.Format("<color='green'>{0:#,#.##}↑</color>", delta);
-			}
-			else if (delta < 0)
-			{
-				return string.Format("<color='red'>{0:#,#.##}↓</color>", delta);
-			}
-			else
-			{
-				return string.Intern("0");
-			}
+			return formatDelta(delta, "#,##0.##");
 		}
 
 		public static string formatDelta(float delta)
@@ -67,73 +74,81 @@
 		private GUIContent repContent;
 		private GUIContent scienceContent;
 
+		#pragma warning disable 0414
 		private Texture2D fundsIconGreen;
 		private Texture2D fundsIconRed;
 		private Texture2D reputationIconGreen;
 		private Texture2D reputationIconRed;
 		private Texture2D scienceIcon;
-
-		public override bool toggleActive
+		#pragma warning restore 0414
+
+		public double lastFundsChange
+		{
+			get;
+			private set;
+		}
+
+		public float lastRepChange
+		{
+			get;
+			private set;
+		}
+
+		public float lastScienceChange
+		{
+			get;
+			private set;
+		}
+
+		public double currentFunds
+		{
+			get;
+			private set;
+		}
+
+		public float currentReputation
+		{
+			get;
+			private set;
+		}
+
+		public float currentScience
+		{
+			get;
+			private set;
+		}
+
+		private bool currenciesInitialized
 		{
 			get
 			{
-				switch (HighLogic.CurrentGame.Mode)
-				{
-					case Game.Modes.CAREER:
-					case Game.Modes.SCIENCE_SANDBOX:
-						return base.toggleActive;
-					default:
-						return false;
-				}
-			}
-			set
-			{
-				switch (HighLogic.CurrentGame.Mode)
-				{
-					case Game.Modes.CAREER:
-					case Game.Modes.SCIENCE_SANDBOX:
-						base.toggleActive = value;
-						break;
-					default:
-						return;
-				}
-			}
-		}
-
-		public double lastFundsChange
-		{
-			get;
-			private set;
-		}
-
-		public float lastRepChange
-		{
-			get;
-			private set;
-		}
-
-		public float lastScienceChange
-		{
-			get;
-			private set;
-		}
-
-		public double currentFunds
-		{
-			get;
-			private set;
-		}
-
-		public float currentReputation
-		{
-			get;
-			private set;
-		}
-
-		public float currentScience
-		{
-			get;
-			private set;
+				Tools.PostDebugMessage(
+					this,
+					"Checking init state:" +
+					"\n\tcurrentFunds={0}" +
+					"\n\tcurrentScience={1}" +
+					"\n\tcurrentReputation={2}",
+					this.currentFunds,
+					this.currentScience,
+					this.currentReputation
+				);
+
+				return !(
+					double.IsNaN(this.currentFunds) ||
+					float.IsNaN(this.currentScience) ||
+					float.IsNaN(this.currentReputation)
+				);
+			}
+		}
+
+		public override void DrawGUI()
+		{
+			if (Event.current.type != EventType.Layout && !this.currenciesInitialized)
+			{
+				this.initCurrencies();
+			}
+
+			base.DrawGUI();
 		}
 
 		public override void ModuleWindow(int _)
@@ -144,21 +159,21 @@
 			GUILayout.Label(VOID_Data.fundingStatus.Label);
 			GUILayout.FlexibleSpace();
 			this.fundsContent.text = VOID_Data.fundingStatus.Value;
-			GUILayout.Label(this.fundsContent, GUILayout.ExpandWidth(false));
+			GUILayout.Label(this.fundsContent, GUILayout.ExpandWidth(true));
 			GUILayout.EndHorizontal();
 
 			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
 			GUILayout.Label(VOID_Data.reputationStatus.Label);
 			GUILayout.FlexibleSpace();
 			this.repContent.text = VOID_Data.reputationStatus.Value;
-			GUILayout.Label(this.repContent, GUILayout.ExpandWidth(false));
+			GUILayout.Label(this.repContent, GUILayout.ExpandWidth(true));
 			GUILayout.EndHorizontal();
 
 			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
 			GUILayout.Label(VOID_Data.scienceStatus.Label);
 			GUILayout.FlexibleSpace();
 			this.scienceContent.text = VOID_Data.scienceStatus.Value;
-			GUILayout.Label(this.scienceContent, GUILayout.ExpandWidth(false));
+			GUILayout.Label(this.scienceContent, GUILayout.ExpandWidth(true));
 			GUILayout.EndHorizontal();
 
 			GUILayout.EndVertical();
@@ -183,6 +198,30 @@
 		{
 			this.lastScienceChange = newValue - this.currentScience;
 			this.currentScience = newValue;
+		}
+
+		private void onGameStateLoad(ConfigNode node)
+		{
+			this.initCurrencies();
+		}
+
+		private void initCurrencies()
+		{
+			Tools.PostDebugMessage(
+				this,
+				"Initializing currencies." +
+				"\n\tFunding.Instance={0}" +
+				"ResearchAndDevelopment.Instance={1}" +
+				"Reputation.Instance={2}",
+				Funding.Instance == null ? "NULL" : Funding.Instance.ToString(),
+				ResearchAndDevelopment.Instance == null ? "NULL" : ResearchAndDevelopment.Instance.ToString(),
+				Reputation.Instance == null ? "NULL" : Reputation.Instance.ToString()
+			);
+
+			this.currentFunds = Funding.Instance != null ? Funding.Instance.Funds : double.NaN;
+			this.currentReputation = Reputation.Instance != null ? Reputation.Instance.reputation : float.NaN;
+			this.currentScience = ResearchAndDevelopment.Instance != null ?
+				ResearchAndDevelopment.Instance.Science : float.NaN;
 		}
 
 		/*
@@ -196,11 +235,12 @@
 		{
 			VOID_CareerStatus.Instance = this;
 
-			this._Name = "Career Status";
+			this.Name = "Career Status";
 
 			GameEvents.OnFundsChanged.Add(this.onFundsChange);
 			GameEvents.OnReputationChanged.Add(this.onRepChange);
 			GameEvents.OnScienceChanged.Add(this.onScienceChange);
+			GameEvents.onGameStateLoad.Add(this.onGameStateLoad);
 
 			bool texturesLoaded;
 
@@ -221,10 +261,9 @@
 				this.scienceContent.image = this.scienceIcon;
 			}
 
-			this.currentFunds = Funding.Instance != null ? Funding.Instance.Funds : double.NaN;
-			this.currentReputation = Reputation.Instance != null ? Reputation.Instance.reputation : float.NaN;
-			this.currentScience = ResearchAndDevelopment.Instance != null ?
-				ResearchAndDevelopment.Instance.Science : float.NaN;
+			this.currentFunds = double.NaN;
+			this.currentScience = float.NaN;
+			this.currentReputation = float.NaN;
 		}
 
 		~VOID_CareerStatus()
@@ -232,6 +271,7 @@
 			GameEvents.OnFundsChanged.Remove(this.onFundsChange);
 			GameEvents.OnReputationChanged.Remove(this.onRepChange);
 			GameEvents.OnScienceChanged.Remove(this.onScienceChange);
+			GameEvents.onGameStateLoad.Remove(this.onGameStateLoad);
 
 			VOID_CareerStatus.Instance = null;
 		}

file:a/VOID_Core.cs (deleted)
--- a/VOID_Core.cs
+++ /dev/null
@@ -1,1162 +1,1 @@
-// VOID
-//
-// VOID_Core.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using Engineer.VesselSimulator;
-using KSP;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using ToadicusTools;
-using UnityEngine;
-
-namespace VOID
-{
-	public class VOID_Core : VOID_Module, IVOID_Module
-	{
-		#region Singleton Members
-		/*
-		 * Static Members
-		 * */
-		protected static bool _initialized = false;
-
-		public static bool Initialized
-		{
-			get
-			{
-				return _initialized;
-			}
-		}
-
-		protected static VOID_Core _instance;
-
-		public static VOID_Core Instance
-		{
-			get
-			{
-				if (_instance == null)
-				{
-					_instance = new VOID_Core();
-					_initialized = true;
-				}
-				return _instance;
-			}
-		}
-
-		public static void Reset()
-		{
-			_instance.StopGUI();
-			_instance.Dispose();
-			_instance = null;
-			_initialized = false;
-		}
-		#endregion
-
-		public static readonly double Constant_G = 6.674e-11;
-
-		/*
-		 * Fields
-		 * */
-		protected string VoidName = "VOID";
-		protected string VoidVersion;
-
-		protected bool _factoryReset = false;
-
-		[AVOID_SaveValue("configValue")]
-		protected VOID_SaveValue<int> configVersion = 1;
-
-		protected List<IVOID_Module> _modules = new List<IVOID_Module>();
-		protected bool _modulesLoaded = false;
-
-		[AVOID_SaveValue("mainWindowPos")]
-		protected VOID_SaveValue<Rect> mainWindowPos = new Rect(475, 575, 10f, 10f);
-		[AVOID_SaveValue("mainGuiMinimized")]
-		protected VOID_SaveValue<bool> mainGuiMinimized = false;
-
-		[AVOID_SaveValue("configWindowPos")]
-		protected VOID_SaveValue<Rect> configWindowPos = new Rect(825, 625, 10f, 10f);
-		[AVOID_SaveValue("configWindowMinimized")]
-
-		protected VOID_SaveValue<bool> configWindowMinimized = true;
-		[AVOID_SaveValue("VOIDIconPos")]
-		protected VOID_SaveValue<Rect> VOIDIconPos = new Rect(Screen.width / 2 - 200, Screen.height - 32, 32f, 32f);
-
-		protected Texture2D VOIDIconTexture;
-		protected string VOIDIconOnActivePath;
-		protected string VOIDIconOnInactivePath;
-		protected string VOIDIconOffActivePath;
-		protected string VOIDIconOffInactivePath;
-
-		protected bool VOIDIconLocked = true;
-
-		protected GUIStyle iconStyle;
-
-		protected int windowBaseID = -96518722;
-		protected int _windowID = 0;
-
-		protected bool GUIStylesLoaded = false;
-
-		protected CelestialBody _homeBody;
-
-		[AVOID_SaveValue("togglePower")]
-		public VOID_SaveValue<bool> togglePower = true;
-		public bool powerAvailable = true;
-
-		[AVOID_SaveValue("consumeResource")]
-		protected VOID_SaveValue<bool> consumeResource = false;
-
-		[AVOID_SaveValue("resourceName")]
-		protected VOID_SaveValue<string> resourceName = "ElectricCharge";
-
-		[AVOID_SaveValue("resourceRate")]
-		protected VOID_SaveValue<float> resourceRate = 0.2f;
-
-		[AVOID_SaveValue("updatePeriod")]
-		protected VOID_SaveValue<double> _updatePeriod = 1001f / 15000f;
-		protected float _updateTimer = 0f;
-		protected string stringFrequency;
-
-		[AVOID_SaveValue("vesselSimActive")]
-		protected VOID_SaveValue<bool> vesselSimActive;
-
-		// Vessel Type Housekeeping
-		protected List<VesselType> _allVesselTypes = new List<VesselType>();
-		protected bool vesselTypesLoaded = false;
-		public float saveTimer = 0;
-
-		protected string defaultSkin = "KSP window 2";
-
-		[AVOID_SaveValue("defaultSkin")]
-		protected VOID_SaveValue<string> _skinName;
-		protected int _skinIdx;
-
-		protected Dictionary<string, GUISkin> validSkins;
-		protected string[] skinNames;
-		protected string[] forbiddenSkins =
-			{
-				"PlaqueDialogSkin",
-				"FlagBrowserSkin",
-				"SSUITextAreaDefault",
-				"ExperimentsDialogSkin",
-				"ExpRecoveryDialogSkin",
-				"KSP window 5",
-				"KSP window 6",
-				"PartTooltipSkin",
-				"KSCContextMenuSkin"
-			};
-		protected bool skinsLoaded = false;
-
-		public bool configDirty;
-
-		[AVOID_SaveValue("UseBlizzyToolbar")]
-		protected VOID_SaveValue<bool> _UseToolbarManager;
-		internal IButton ToolbarButton;
-
-		internal ApplicationLauncherButton AppLauncherButton;
-
-		/*
-		 * Properties
-		 * */
-		public bool factoryReset
-		{
-			get
-			{
-				return this._factoryReset;
-			}
-		}
-
-		public List<IVOID_Module> Modules
-		{
-			get
-			{
-				return this._modules;
-			}
-		}
-
-		public GUISkin Skin
-		{
-			get
-			{
-				if (this.skinsLoaded)
-				{
-					try
-					{
-						return this.validSkins[this._skinName];
-					}
-					catch
-					{
-					}
-				}
-
-				return AssetBase.GetGUISkin(this.defaultSkin);
-			}
-		}
-
-		public int windowID
-		{
-			get
-			{
-				if (this._windowID == 0)
-				{
-					this._windowID = this.windowBaseID;
-				}
-				return this._windowID++;
-			}
-		}
-
-		public List<CelestialBody> allBodies
-		{
-			get
-			{
-				return FlightGlobals.Bodies;
-			}
-		}
-
-		public List<CelestialBody> sortedBodyList
-		{
-			get;
-			private set;
-		}
-
-		public CelestialBody HomeBody
-		{
-			get
-			{
-				if (this._homeBody == null)
-				{
-					if (Planetarium.fetch != null)
-					{
-						this._homeBody = Planetarium.fetch.Home;
-					}
-				}
-
-				return this._homeBody;
-			}
-		}
-
-		public List<VesselType> allVesselTypes
-		{
-			get
-			{
-				return this._allVesselTypes;
-			}
-		}
-
-		public float updateTimer
-		{
-			get
-			{
-				return this._updateTimer;
-			}
-		}
-
-		public double updatePeriod
-		{
-			get
-			{
-				return this._updatePeriod;
-			}
-		}
-
-		public Stage[] Stages
-		{
-			get;
-			protected set;
-		}
-
-		public Stage LastStage
-		{
-			get;
-			protected set;
-		}
-
-		protected IconState powerState
-		{
-			get
-			{
-				if (this.togglePower && this.powerAvailable)
-				{
-					return IconState.PowerOn;
-				}
-				else
-				{
-					return IconState.PowerOff;
-				}
-
-			}
-		}
-
-		protected IconState activeState
-		{
-			get
-			{
-				if (this.mainGuiMinimized)
-				{
-					return IconState.Inactive;
-				}
-				else
-				{
-					return IconState.Active;
-				}
-
-			}
-		}
-
-		protected bool UseToolbarManager
-		{
-			get
-			{
-				return _UseToolbarManager & ToolbarManager.ToolbarAvailable;
-			}
-			set
-			{
-				if (this._UseToolbarManager == value)
-				{
-					return;
-				}
-
-				if (value == false && this.ToolbarButton != null)
-				{
-					this.ToolbarButton.Destroy();
-					this.ToolbarButton = null;
-				}
-				if (value == true)
-				{
-					if (this.AppLauncherButton != null)
-					{
-						ApplicationLauncher.Instance.RemoveModApplication(this.AppLauncherButton);
-						this.AppLauncherButton = null;
-					}
-
-					this.InitializeToolbarButton();
-				}
-
-				_UseToolbarManager.value = value;
-			}
-		}
-
-		protected virtual ApplicationLauncher.AppScenes appIconVisibleScenes
-		{
-			get
-			{
-				return ApplicationLauncher.AppScenes.FLIGHT;
-			}
-		}
-
-		/*
-		 * Methods
-		 * */
-		public override void DrawGUI()
-		{
-			this._windowID = this.windowBaseID;
-
-			if (!this._modulesLoaded)
-			{
-				this.LoadModulesOfType<IVOID_Module>();
-			}
-
-			if (!this.skinsLoaded)
-			{
-				this.LoadSkins();
-			}
-
-			GUI.skin = this.Skin;
-
-			if (!this.GUIStylesLoaded)
-			{
-				this.LoadGUIStyles();
-
-				Tools.PostDebugMessage(
-					this,
-					"ToolbarAvailable: {0}, UseToobarManager: {1}",
-					ToolbarManager.ToolbarAvailable,
-					this.UseToolbarManager);
-			}
-
-			if (!this.UseToolbarManager)
-			{
-				if (this.AppLauncherButton == null)
-				{
-					Tools.PostDebugMessage(this,
-						"UseToolbarManager = false (ToolbarAvailable = {0}) and " +
-						"AppLauncherButton is null, making AppLauncher button.",
-						ToolbarManager.ToolbarAvailable
-					);
-					this.InitializeAppLauncherButton();
-				}
-			}
-			else if (this.ToolbarButton == null)
-			{
-				Tools.PostDebugMessage(this,
-					"UseToolbarManager = true (ToolbarAvailable = {0}) and " +
-					"ToolbarButton is null, making Toolbar button.",
-					ToolbarManager.ToolbarAvailable
-				);
-				this.InitializeToolbarButton();
-			}
-
-			if (!this.mainGuiMinimized)
-			{
-
-				Rect _mainWindowPos = this.mainWindowPos;
-
-				_mainWindowPos = GUILayout.Window(
-					this.windowID,
-					_mainWindowPos,
-					VOID_Tools.GetWindowHandler(this.VOIDMainWindow),
-					string.Join(" ", new string[] { this.VoidName, this.VoidVersion }),
-					GUILayout.Width(250),
-					GUILayout.Height(50)
-				);
-
-				if (HighLogic.LoadedSceneIsEditor)
-				{
-					_mainWindowPos = Tools.ClampRectToEditorPad(_mainWindowPos);
-				}
-				else
-				{
-					_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,
-					VOID_Tools.GetWindowHandler(this.VOIDConfigWindow),
-					string.Join(" ", new string[] { this.VoidName, "Configuration" }),
-					GUILayout.Width(250),
-					GUILayout.Height(50)
-				);
-
-				if (HighLogic.LoadedSceneIsEditor)
-				{
-					_configWindowPos = Tools.ClampRectToEditorPad(_configWindowPos);
-				}
-				else
-				{
-					_configWindowPos = Tools.ClampRectToScreen(_configWindowPos);
-				}
-
-				if (_configWindowPos != this.configWindowPos)
-				{
-					this.configWindowPos = _configWindowPos;
-				}
-			}
-		}
-
-		public virtual 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 virtual void Update()
-		{
-			this.LoadBeforeUpdate();
-
-			if (this.vessel != null && this.vesselSimActive)
-			{
-				Tools.PostDebugMessage(this, "Updating SimManager.");
-				this.UpdateSimManager();
-			}
-
-			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 virtual 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_Module module in this.Modules)
-			{
-				if (module is IVOID_BehaviorModule)
-				{
-					((IVOID_BehaviorModule)module).FixedUpdate();
-				}
-			}
-		}
-
-		public void OnDestroy()
-		{
-			foreach (IVOID_Module module in this.Modules)
-			{
-				if (module is IVOID_BehaviorModule)
-				{
-					((IVOID_BehaviorModule)module).OnDestroy();
-				}
-			}
-		}
-
-		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 --", VOID_Styles.labelRed);
-			}
-
-			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._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, VOID_Styles.labelCenter, GUILayout.ExpandWidth(true));
-
-			_content.text = "►";
-			_content.tooltip = "Select next skin";
-			if (GUILayout.Button(_content, GUILayout.ExpandWidth(true)))
-			{
-				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];
-				this.GUIStylesLoaded = false;
-			}
-
-			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));
-
-			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 UpdateSimManager()
-		{
-			if (SimManager.ResultsReady())
-			{
-				Tools.PostDebugMessage(this, "VesselSimulator results ready, setting Stages.");
-
-				this.Stages = SimManager.Stages;
-
-				if (this.Stages != null)
-				{
-					this.LastStage = this.Stages.Last();
-				}
-
-				if (HighLogic.LoadedSceneIsEditor)
-				{
-					SimManager.Gravity = VOID_Data.KerbinGee;
-				}
-				else
-				{
-					double radius = this.vessel.Radius();
-					SimManager.Gravity = this.vessel.mainBody.gravParameter / (radius * radius);
-				}
-
-				SimManager.minSimTime = (long)(this.updatePeriod * 1000);
-
-				SimManager.TryStartSimulation();
-			}
-			#if DEBUG
-			else
-			{
-				Tools.PostDebugMessage(this, "VesselSimulator results not ready.");
-			}
-			#endif
-		}
-
-		protected void LoadModulesOfType<T>()
-		{
-			StringBuilder sb = new StringBuilder("Loading modules...");
-			sb.AppendLine();
-
-			foreach (AssemblyLoader.LoadedAssembly assy in AssemblyLoader.loadedAssemblies)
-			{
-				foreach (Type loadedType in assy.assembly.GetExportedTypes())
-				{
-					if (
-						loadedType.IsInterface ||
-						loadedType.IsAbstract ||
-						!typeof(T).IsAssignableFrom(loadedType) ||
-						this.GetType().IsAssignableFrom(loadedType)
-					)
-					{
-						continue;
-					}
-
-					// HACK: This stops editor modules from loading in flight.  It is a dirty hack and should be fixed.
-					if (!HighLogic.LoadedSceneIsEditor && typeof(IVOID_EditorModule).IsAssignableFrom(loadedType))
-					{
-						continue;
-					}
-
-					sb.AppendFormat("Loading IVOID_Module type {0}...", loadedType.Name);
-
-					try
-					{
-						this.LoadModule(loadedType);
-						sb.AppendLine("Success.");
-					}
-					catch (Exception ex)
-					{
-						sb.AppendFormat("Failed, caught {0}", ex.GetType().Name);
-						sb.AppendLine();
-
-						#if DEBUG
-						Debug.LogException(ex);
-						#endif
-					}
-				}
-			}
-
-			this._modulesLoaded = true;
-
-			sb.AppendFormat("Loaded {0} modules.", this.Modules.Count);
-			sb.AppendLine();
-		}
-
-		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()
-		{
-			VOID_Styles.OnSkinChanged();
-
-			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();
-			}
-
-			if (this.sortedBodyList == null && FlightGlobals.Bodies != null && FlightGlobals.Bodies.Count > 0)
-			{
-				this.sortedBodyList = new List<CelestialBody>(FlightGlobals.Bodies);
-				this.sortedBodyList.Sort(new CBListComparer());
-				this.sortedBodyList.Reverse();
-
-				Debug.Log(string.Format("sortedBodyList: {0}", string.Join("\n\t", this.sortedBodyList.Select(b => b.bodyName).ToArray())));
-			}
-
-		}
-
-		protected void InitializeToolbarButton()
-		{
-			// Do nothing if (the Toolbar is not available.
-			if (!ToolbarManager.ToolbarAvailable)
-			{
-				Tools.PostDebugMessage(this, "Refusing to make a ToolbarButton: ToolbarAvailable = false");
-				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 InitializeAppLauncherButton()
-		{
-			if (ApplicationLauncher.Ready)
-			{
-				this.AppLauncherButton = ApplicationLauncher.Instance.AddModApplication(
-					this.ToggleMainWindow, this.ToggleMainWindow,
-					this.appIconVisibleScenes,
-					this.VOIDIconTexture
-				);
-
-				Tools.PostDebugMessage(
-					this,
-					"AppLauncherButton initialized in {0}",
-					Enum.GetName(
-						typeof(GameScenes),
-						HighLogic.LoadedScene
-					)
-				);
-			}
-		}
-
-		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.Replace("icon", "appIcon"), false);
-
-			if (this.AppLauncherButton != null)
-			{
-				this.AppLauncherButton.SetTexture(VOIDIconTexture);
-			}
-		}
-
-		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";
-
-			System.Version version = this.GetType().Assembly.GetName().Version;
-
-			this.VoidVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.MajorRevision);
-
-			this.toggleActive = 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);
-		}
-
-		public virtual void Dispose()
-		{
-			if (this.AppLauncherButton != null)
-			{
-				ApplicationLauncher.Instance.RemoveModApplication(this.AppLauncherButton);
-				this.AppLauncherButton = null;
-			}
-			if (this.ToolbarButton != null)
-			{
-				this.ToolbarButton.Destroy();
-				this.ToolbarButton = null;
-			}
-		}
-
-		protected enum IconState
-		{
-			PowerOff = 1,
-			PowerOn = 2,
-			Inactive = 4,
-			Active = 8
-		}
-	}
-}
-
-

--- a/VOID_Data.cs
+++ b/VOID_Data.cs
@@ -26,7 +26,7 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using Engineer.VesselSimulator;
+using KerbalEngineer.VesselSimulator;
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -56,7 +56,7 @@
 			{
 				if (kerbinGee == default(double))
 				{
-					kerbinGee = core.HomeBody.gravParameter / (core.HomeBody.Radius * core.HomeBody.Radius);
+					kerbinGee = Core.HomeBody.gravParameter / (Core.HomeBody.Radius * Core.HomeBody.Radius);
 				}
 
 				return kerbinGee;
@@ -67,21 +67,47 @@
 
 		#region Core Data
 
-		public static VOID_Core core
+		public static VOIDCore Core
 		{
 			get
 			{
-				if (HighLogic.LoadedSceneIsEditor)
-				{
-					return VOID_EditorCore.Instance;
-				}
-				else
-				{
-					return VOID_Core.Instance;
+				if (!CoreInitialized)
+				{
+					return null;
+				}
+
+				switch (HighLogic.LoadedScene)
+				{
+					case GameScenes.EDITOR:
+						return (VOIDCore)VOIDCore_Editor.Instance;
+					case GameScenes.FLIGHT:
+						return (VOIDCore)VOIDCore_Flight.Instance;
+					case GameScenes.SPACECENTER:
+						return (VOIDCore)VOIDCore_SpaceCentre.Instance;
+					default:
+						return null;
 				}
 			}
 		}
 
+		public static bool CoreInitialized
+		{
+			get
+			{
+				switch (HighLogic.LoadedScene)
+				{
+					case GameScenes.EDITOR:
+						return VOIDCore_Editor.Initialized;
+					case GameScenes.FLIGHT:
+						return VOIDCore_Flight.Initialized;
+					case GameScenes.SPACECENTER:
+						return VOIDCore_SpaceCentre.Initialized;
+					default:
+						return false;
+				}
+			}
+		}
+
 		#endregion
 
 		#region Atmosphere
@@ -89,28 +115,28 @@
 		public static readonly VOID_DoubleValue atmDensity =
 			new VOID_DoubleValue(
 				"Atmosphere Density",
-				new Func<double>(() => core.vessel.atmDensity * 1000f),
+				new Func<double>(() => Core.vessel.atmDensity * 1000f),
 				"g/m³"
 			);
 
 		public static readonly VOID_FloatValue atmLimit =
 			new VOID_FloatValue(
 				"Atmosphere Limit",
-				new Func<float>(() => core.vessel.mainBody.maxAtmosphereAltitude),
+				new Func<float>(() => Core.vessel.mainBody.maxAtmosphereAltitude),
 				"m"
 			);
 
 		public static readonly VOID_DoubleValue atmPressure =
 			new VOID_DoubleValue(
 				"Pressure",
-				new Func<double>(() => core.vessel.staticPressure),
+				new Func<double>(() => Core.vessel.staticPressure),
 				"atm"
 			);
 
 		public static readonly VOID_FloatValue temperature =
 			new VOID_FloatValue(
 				"Temperature",
-				new Func<float>(() => core.vessel.flightIntegrator.getExternalTemperature()),
+				new Func<float>(() => Core.vessel.flightIntegrator.getExternalTemperature()),
 				"°C"
 			);
 
@@ -123,7 +149,7 @@
 				"Heading",
 				delegate()
 				{
-					double heading = core.vessel.getSurfaceHeading();
+					double heading = Core.vessel.getSurfaceHeading();
 					string cardinal = VOID_Tools.get_heading_text(heading);
 
 					return string.Format(
@@ -137,7 +163,7 @@
 		public static readonly VOID_DoubleValue vesselPitch =
 			new VOID_DoubleValue(
 				"Pitch",
-				() => core.vessel.getSurfacePitch(),
+				() => Core.vessel.getSurfacePitch(),
 				"°"
 			);
 
@@ -203,7 +229,7 @@
 		public static readonly VOID_FloatValue mainThrottle =
 			new VOID_FloatValue(
 				"Throttle",
-				new Func<float>(() => core.vessel.ctrlState.mainThrottle * 100f),
+				new Func<float>(() => Core.vessel.ctrlState.mainThrottle * 100f),
 				"%"
 			);
 
@@ -214,7 +240,7 @@
 		public static readonly VOID_IntValue partCount =
 			new VOID_IntValue(
 				"Parts",
-				new Func<int>(() => core.vessel.Parts.Count),
+				new Func<int>(() => Core.vessel.Parts.Count),
 				""
 			);
 
@@ -237,12 +263,12 @@
 				"Resource Mass",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.totalMass - core.LastStage.totalBaseMass;
+					if (Core.Stages == null || Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.resourceMass;
 				},
 				"tons"
 			);
@@ -252,12 +278,12 @@
 				"Resource Mass (Stage)",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.mass - core.LastStage.baseMass;
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.resourceMass;
 				},
 				"tons"
 			);
@@ -267,12 +293,12 @@
 				"Total Mass",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.totalMass;
+					if (Core.Stages == null || Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.totalMass;
 				},
 				"tons"
 			);
@@ -286,9 +312,9 @@
 				"DeltaV (Current Stage)",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-						return double.NaN;
-					return core.LastStage.deltaV;
+					if (Core.Stages == null || Core.LastStage == null)
+						return double.NaN;
+					return Core.LastStage.deltaV;
 				},
 				"m/s"
 			);
@@ -298,9 +324,9 @@
 				"DeltaV (Total)",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-						return double.NaN;
-					return core.LastStage.totalDeltaV;
+					if (Core.Stages == null || Core.LastStage == null)
+						return double.NaN;
+					return Core.LastStage.totalDeltaV;
 				},
 				"m/s"
 			);
@@ -314,7 +340,7 @@
 				"T:W (curr/max)",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
+					if (Core.Stages == null || Core.LastStage == null)
 						return "N/A";
 
 					return string.Format(
@@ -330,11 +356,11 @@
 				"Thrust (curr/max)",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
+					if (Core.Stages == null || Core.LastStage == null)
 						return "N/A";
 
-					double currThrust = core.LastStage.actualThrust;
-					double maxThrust = core.LastStage.thrust;
+					double currThrust = Core.LastStage.actualThrust;
+					double maxThrust = Core.LastStage.thrust;
 
 					return string.Format(
 						"{0} / {1}",
@@ -349,12 +375,12 @@
 				"Stage Mass Flow",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					double stageIsp = core.LastStage.isp;
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					double stageIsp = Core.LastStage.isp;
 					double stageThrust = stageNominalThrust;
 
 					Tools.PostDebugMessage(typeof(VOID_Data), "calculating stageMassFlow from:\n" +
@@ -376,18 +402,18 @@
 				"Nominal Stage Thrust",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					if (core.LastStage.actualThrust == 0d)
-					{
-						return core.LastStage.thrust;
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					if (Core.LastStage.actualThrust == 0d)
+					{
+						return Core.LastStage.thrust;
 					}
 					else
 					{
-						return core.LastStage.actualThrust;
+						return Core.LastStage.actualThrust;
 					}
 				},
 				"kN"
@@ -402,12 +428,12 @@
 				"T:W Ratio",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.actualThrustToWeight;
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.actualThrustToWeight;
 				},
 				""
 			);
@@ -419,12 +445,12 @@
 				"T:W Ratio",
 				delegate()
 				{
-					if (core.LastStage == null)
-					{
-						return double.NaN;
-					}
-
-					return core.LastStage.thrustToWeight;
+					if (Core.LastStage == null)
+					{
+						return double.NaN;
+					}
+
+					return Core.LastStage.thrustToWeight;
 				},
 				""
 			);
@@ -449,13 +475,13 @@
 				"Max T:W @ surface",
 				delegate()
 				{
-					if (core.Stages == null || core.LastStage == null)
-						return double.NaN;
-
-					double maxThrust = core.LastStage.thrust;
-					double mass = core.LastStage.totalMass;
-					double gravity = (VOID_Core.Constant_G * core.vessel.mainBody.Mass) /
-					               (core.vessel.mainBody.Radius * core.vessel.mainBody.Radius);
+					if (Core.Stages == null || Core.LastStage == null)
+						return double.NaN;
+
+					double maxThrust = Core.LastStage.thrust;
+					double mass = Core.LastStage.totalMass;
+					double gravity = (VOIDCore.Constant_G * Core.vessel.mainBody.Mass) /
+					               (Core.vessel.mainBody.Radius * Core.vessel.mainBody.Radius);
 					double weight = mass * gravity;
 
 					return maxThrust / weight;
@@ -468,12 +494,12 @@
 				"Thrust Offset",
 				delegate()
 				{
-					if (core.vessel == null)
+					if (Core.vessel == null)
 					{
 						return Vector3d.zero;
 					}
 
-					List<PartModule> engineModules = core.vessel.getModulesOfType<PartModule>();
+					List<PartModule> engineModules = Core.vessel.getModulesOfType<PartModule>();
 
 					Vector3d thrustPos = Vector3d.zero;
 					Vector3d thrustDir = Vector3d.zero;
@@ -532,13 +558,13 @@
 						thrustDir /= thrust;
 					}
 
-					Transform vesselTransform = core.vessel.transform;
+					Transform vesselTransform = Core.vessel.transform;
 
 					thrustPos = vesselTransform.InverseTransformPoint(thrustPos);
 					thrustDir = vesselTransform.InverseTransformDirection(thrustDir);
 
 					Vector3d thrustOffset = VectorTools.PointDistanceToLine(
-						                      thrustPos, thrustDir.normalized, core.vessel.findLocalCenterOfMass());
+						                      thrustPos, thrustDir.normalized, Core.vessel.findLocalCenterOfMass());
 
 					Tools.PostDebugMessage(typeof(VOID_Data), "vesselThrustOffset:\n" +
 						"\tthrustPos: {0}\n" +
@@ -548,7 +574,7 @@
 						thrustPos,
 						thrustDir.normalized,
 						thrustOffset,
-						core.vessel.findWorldCenterOfMass()
+						Core.vessel.findWorldCenterOfMass()
 					);
 
 					return thrustOffset;
@@ -571,7 +597,7 @@
 					currentAmount = 0d;
 					currentRequirement = 0d;
 
-					foreach (Part part in core.vessel.Parts)
+					foreach (Part part in Core.vessel.Parts)
 					{
 						if (part.enabled)
 						{
@@ -630,9 +656,9 @@
 				"Crew Onboard",
 				delegate()
 				{
-					if (core.vessel != null)
-					{
-						return core.vessel.GetCrewCount();
+					if (Core.vessel != null)
+					{
+						return Core.vessel.GetCrewCount();
 					}
 					else
 					{
@@ -647,9 +673,9 @@
 				"Crew Capacity",
 				delegate()
 				{
-					if (core.vessel != null)
-					{
-						return core.vessel.GetCrewCapacity();
+					if (Core.vessel != null)
+					{
+						return Core.vessel.GetCrewCapacity();
 					}
 					else
 					{
@@ -665,24 +691,24 @@
 
 		#region Location
 
+		public const double kscLongitude = 285.442323427289 * Math.PI / 180d;
+		public const double kscLatitude = -0.0972112860655246 * Math.PI / 180d;
+
 		public static readonly VOID_DoubleValue downrangeDistance =
 			new VOID_DoubleValue(
 				"Downrange Distance",
 				delegate()
 				{
 
-					if (core.vessel == null ||
+					if (Core.vessel == null ||
 					  Planetarium.fetch == null ||
-					  core.vessel.mainBody != Planetarium.fetch.Home)
-					{
-						return double.NaN;
-					}
-
-					double vesselLongitude = core.vessel.longitude * Math.PI / 180d;
-					double vesselLatitude = core.vessel.latitude * Math.PI / 180d;
-
-					const double kscLongitude = 285.442323427289 * Math.PI / 180d;
-					const double kscLatitude = -0.0972112860655246 * Math.PI / 180d;
+					  Core.vessel.mainBody != Planetarium.fetch.Home)
+					{
+						return double.NaN;
+					}
+
+					double vesselLongitude = Core.vessel.longitude * Math.PI / 180d;
+					double vesselLatitude = Core.vessel.latitude * Math.PI / 180d;
 
 					double diffLon = vesselLongitude - kscLongitude;
 					double diffLat = vesselLatitude - kscLatitude;
@@ -699,7 +725,7 @@
 
 					double arc = 2d * Math.Atan2(Math.Sqrt(haversine), Math.Sqrt(1d - haversine));
 
-					return core.vessel.mainBody.Radius * arc;
+					return Core.vessel.mainBody.Radius * arc;
 				},
 				"m"
 			);
@@ -707,13 +733,13 @@
 		public static readonly VOID_StrValue surfLatitude =
 			new VOID_StrValue(
 				"Latitude",
-				new Func<string>(() => VOID_Tools.GetLatitudeString(core.vessel))
+				new Func<string>(() => VOID_Tools.GetLatitudeString(Core.vessel))
 			);
 
 		public static readonly VOID_StrValue surfLongitude =
 			new VOID_StrValue(
 				"Longitude",
-				new Func<string>(() => VOID_Tools.GetLongitudeString(core.vessel))
+				new Func<string>(() => VOID_Tools.GetLongitudeString(Core.vessel))
 			);
 
 		public static readonly VOID_DoubleValue trueAltitude =
@@ -721,11 +747,11 @@
 				"Altitude (true)",
 				delegate()
 				{
-					double alt_true = core.vessel.orbit.altitude - core.vessel.terrainAltitude;
+					double alt_true = Core.vessel.orbit.altitude - Core.vessel.terrainAltitude;
 					// HACK: This assumes that on worlds with oceans, all water is fixed at 0 m,
 					// and water covers the whole surface at 0 m.
-					if (core.vessel.terrainAltitude < 0 && core.vessel.mainBody.ocean)
-						alt_true = core.vessel.orbit.altitude;
+					if (Core.vessel.terrainAltitude < 0 && Core.vessel.mainBody.ocean)
+						alt_true = Core.vessel.orbit.altitude;
 					return alt_true;
 				},
 				"m"
@@ -738,28 +764,28 @@
 		public static readonly VOID_DoubleValue geeForce =
 			new VOID_DoubleValue(
 				"G-force",
-				new Func<double>(() => core.vessel.geeForce),
+				new Func<double>(() => Core.vessel.geeForce),
 				"gees"
 			);
 
 		public static readonly VOID_DoubleValue horzVelocity =
 			new VOID_DoubleValue(
 				"Horizontal speed",
-				new Func<double>(() => core.vessel.horizontalSrfSpeed),
+				new Func<double>(() => Core.vessel.horizontalSrfSpeed),
 				"m/s"
 			);
 
 		public static readonly VOID_DoubleValue surfVelocity =
 			new VOID_DoubleValue(
 				"Surface velocity",
-				new Func<double>(() => core.vessel.srf_velocity.magnitude),
+				new Func<double>(() => Core.vessel.srf_velocity.magnitude),
 				"m/s"
 			);
 
 		public static readonly VOID_DoubleValue vertVelocity =
 			new VOID_DoubleValue(
 				"Vertical speed",
-				new Func<double>(() => core.vessel.verticalSpeed),
+				new Func<double>(() => Core.vessel.verticalSpeed),
 				"m/s"
 			);
 
@@ -775,9 +801,9 @@
 				"Angular Velocity",
 				delegate()
 				{
-					if (core.vessel != null)
-					{
-						return core.vessel.angularVelocity.magnitude;
+					if (Core.vessel != null)
+					{
+						return Core.vessel.angularVelocity.magnitude;
 					}
 					else
 					{
@@ -795,14 +821,14 @@
 		{
 			get
 			{
-				if (core.vessel == null ||
-				    core.vessel.patchedConicSolver == null ||
-				    core.vessel.patchedConicSolver.maneuverNodes == null)
+				if (Core.vessel == null ||
+				    Core.vessel.patchedConicSolver == null ||
+				    Core.vessel.patchedConicSolver.maneuverNodes == null)
 				{
 					return 0;
 				}
 
-				return core.vessel.patchedConicSolver.maneuverNodes.Count;
+				return Core.vessel.patchedConicSolver.maneuverNodes.Count;
 			}
 		}
 
@@ -811,12 +837,12 @@
 				"Full burn time to be half done at node",
 				delegate()
 				{
-					if (core.LastStage == null && upcomingManeuverNodes < 1)
+					if (Core.LastStage == null && upcomingManeuverNodes < 1)
 					{
 						return "N/A";
 					}
 
-					ManeuverNode node = core.vessel.patchedConicSolver.maneuverNodes[0];
+					ManeuverNode node = Core.vessel.patchedConicSolver.maneuverNodes[0];
 
 					if ((node.UT - Planetarium.GetUniversalTime()) < 0)
 					{
@@ -853,12 +879,12 @@
 				"Full burn time to be half done at node",
 				delegate()
 				{
-					if (core.LastStage == null && upcomingManeuverNodes < 1)
+					if (Core.LastStage == null && upcomingManeuverNodes < 1)
 					{
 						return "N/A";
 					}
 
-					ManeuverNode node = core.vessel.patchedConicSolver.maneuverNodes[0];
+					ManeuverNode node = Core.vessel.patchedConicSolver.maneuverNodes[0];
 
 					if ((node.UT - Planetarium.GetUniversalTime()) < 0)
 					{
@@ -897,7 +923,7 @@
 				{
 					if (upcomingManeuverNodes > 0)
 					{
-						return core.vessel.patchedConicSolver.maneuverNodes[0].DeltaV.magnitude;
+						return Core.vessel.patchedConicSolver.maneuverNodes[0].DeltaV.magnitude;
 					}
 					else
 					{
@@ -914,7 +940,7 @@
 				{
 					if (upcomingManeuverNodes > 0)
 					{
-						return core.vessel.patchedConicSolver.maneuverNodes[0].GetBurnVector(core.vessel.orbit).magnitude;
+						return Core.vessel.patchedConicSolver.maneuverNodes[0].GetBurnVector(Core.vessel.orbit).magnitude;
 					}
 					else
 					{
@@ -929,7 +955,7 @@
 				"Total Burn Time",
 				delegate()
 				{
-					if (core.LastStage == null || currManeuverDeltaV.Value == double.NaN)
+					if (Core.LastStage == null || currManeuverDeltaV.Value == double.NaN)
 					{
 						return double.NaN;
 					}
@@ -946,7 +972,7 @@
 				"Burn Time Remaining",
 				delegate()
 				{
-					if (core.LastStage == null || currManeuverDVRemaining == double.NaN)
+					if (Core.LastStage == null || currManeuverDVRemaining == double.NaN)
 					{
 						return double.NaN;
 					}
@@ -963,7 +989,7 @@
 				"Half Burn Time",
 				delegate()
 				{
-					if (core.LastStage == null || currManeuverDeltaV.Value == double.NaN)
+					if (Core.LastStage == null || currManeuverDeltaV.Value == double.NaN)
 					{
 						return double.NaN;
 					}
@@ -982,7 +1008,7 @@
 				{
 					if (upcomingManeuverNodes > 1)
 					{
-						return core.vessel.patchedConicSolver.maneuverNodes[1].DeltaV.magnitude;
+						return Core.vessel.patchedConicSolver.maneuverNodes[1].DeltaV.magnitude;
 					}
 					else
 					{
@@ -1001,58 +1027,58 @@
 				VOID_Localization.void_primary,
 				delegate()
 				{
-					if (core.vessel == null)
+					if (Core.vessel == null)
 					{
 						return string.Empty;
 					}
-					return core.vessel.mainBody.name;
+					return Core.vessel.mainBody.name;
 				}
 			);
 
 		public static readonly VOID_DoubleValue orbitAltitude =
 			new VOID_DoubleValue(
 				"Altitude (ASL)",
-				new Func<double>(() => core.vessel.orbit.altitude),
+				new Func<double>(() => Core.vessel.orbit.altitude),
 				"m"
 			);
 
 		public static readonly VOID_DoubleValue orbitVelocity =
 			new VOID_DoubleValue(
 				VOID_Localization.void_velocity,
-				new Func<double>(() => core.vessel.orbit.vel.magnitude),
+				new Func<double>(() => Core.vessel.orbit.vel.magnitude),
 				"m/s"
 			);
 
 		public static readonly VOID_DoubleValue orbitApoAlt =
 			new VOID_DoubleValue(
 				VOID_Localization.void_apoapsis,
-				new Func<double>(() => core.vessel.orbit.ApA),
+				new Func<double>(() => Core.vessel.orbit.ApA),
 				"m"
 			);
 
 		public static readonly VOID_DoubleValue oribtPeriAlt =
 			new VOID_DoubleValue(
 				VOID_Localization.void_periapsis,
-				new Func<double>(() => core.vessel.orbit.PeA),
+				new Func<double>(() => Core.vessel.orbit.PeA),
 				"m"
 			);
 
 		public static readonly VOID_StrValue timeToApo =
 			new VOID_StrValue(
 				"Time to Apoapsis",
-				new Func<string>(() => VOID_Tools.FormatInterval(core.vessel.orbit.timeToAp))
+				new Func<string>(() => VOID_Tools.FormatInterval(Core.vessel.orbit.timeToAp))
 			);
 
 		public static readonly VOID_StrValue timeToPeri =
 			new VOID_StrValue(
 				"Time to Periapsis",
-				new Func<string>(() => VOID_Tools.FormatInterval(core.vessel.orbit.timeToPe))
+				new Func<string>(() => VOID_Tools.FormatInterval(Core.vessel.orbit.timeToPe))
 			);
 
 		public static readonly VOID_DoubleValue orbitInclination =
 			new VOID_DoubleValue(
 				"Inclination",
-				new Func<double>(() => core.vessel.orbit.inclination),
+				new Func<double>(() => Core.vessel.orbit.inclination),
 				"°"
 			);
 
@@ -1061,9 +1087,9 @@
 				"Gravity",
 				delegate()
 				{
-					double orbitRadius = core.vessel.mainBody.Radius +
-					                   core.vessel.mainBody.GetAltitude(core.vessel.findWorldCenterOfMass());
-					return (VOID_Core.Constant_G * core.vessel.mainBody.Mass) /
+					double orbitRadius = Core.vessel.mainBody.Radius +
+					                   Core.vessel.mainBody.GetAltitude(Core.vessel.findWorldCenterOfMass());
+					return (VOIDCore.Constant_G * Core.vessel.mainBody.Mass) /
 					(orbitRadius * orbitRadius);
 				},
 				"m/s²"
@@ -1072,55 +1098,55 @@
 		public static readonly VOID_StrValue orbitPeriod =
 			new VOID_StrValue(
 				"Period",
-				new Func<string>(() => VOID_Tools.FormatInterval(core.vessel.orbit.period))
+				new Func<string>(() => VOID_Tools.FormatInterval(Core.vessel.orbit.period))
 			);
 
 		public static readonly VOID_DoubleValue semiMajorAxis =
 			new VOID_DoubleValue(
 				"Semi-Major Axis",
-				new Func<double>(() => core.vessel.orbit.semiMajorAxis),
+				new Func<double>(() => Core.vessel.orbit.semiMajorAxis),
 				"m"
 			);
 
 		public static readonly VOID_DoubleValue eccentricity =
 			new VOID_DoubleValue(
 				"Eccentricity",
-				new Func<double>(() => core.vessel.orbit.eccentricity),
+				new Func<double>(() => Core.vessel.orbit.eccentricity),
 				""
 			);
 
 		public static readonly VOID_DoubleValue meanAnomaly =
 			new VOID_DoubleValue(
 				"Mean Anomaly",
-				new Func<double>(() => core.vessel.orbit.meanAnomaly * 180d / Math.PI),
+				new Func<double>(() => Core.vessel.orbit.meanAnomaly * 180d / Math.PI),
 				"°"
 			);
 
 		public static readonly VOID_DoubleValue trueAnomaly = 
 			new VOID_DoubleValue(
 				"True Anomaly",
-				new Func<double>(() => core.vessel.orbit.trueAnomaly),
+				new Func<double>(() => Core.vessel.orbit.trueAnomaly),
 				"°"
 			);
 
 		public static readonly VOID_DoubleValue eccAnomaly =
 			new VOID_DoubleValue(
 				"Eccentric Anomaly",
-				new Func<double>(() => core.vessel.orbit.eccentricAnomaly * 180d / Math.PI),
+				new Func<double>(() => Core.vessel.orbit.eccentricAnomaly * 180d / Math.PI),
 				"°"
 			);
 
 		public static readonly VOID_DoubleValue longitudeAscNode =
 			new VOID_DoubleValue(
 				"Long. Ascending Node",
-				new Func<double>(() => core.vessel.orbit.LAN),
+				new Func<double>(() => Core.vessel.orbit.LAN),
 				"°"
 			);
 
 		public static readonly VOID_DoubleValue argumentPeriapsis =
 			new VOID_DoubleValue(
 				"Argument of Periapsis",
-				new Func<double>(() => core.vessel.orbit.argumentOfPeriapsis),
+				new Func<double>(() => Core.vessel.orbit.argumentOfPeriapsis),
 				"°"
 			);
 
@@ -1128,7 +1154,7 @@
 			new VOID_DoubleValue(
 				"Local Sidereal Longitude",
 				new Func<double>(() => VOID_Tools.FixDegreeDomain(
-						core.vessel.longitude + core.vessel.orbit.referenceBody.rotationAngle)),
+						Core.vessel.longitude + Core.vessel.orbit.referenceBody.rotationAngle)),
 				"°"
 			);
 
@@ -1139,13 +1165,13 @@
 		public static readonly VOID_StrValue expSituation =
 			new VOID_StrValue(
 				"Situation",
-				new Func<string>(() => core.vessel.GetExperimentSituation().HumanString())
+				new Func<string>(() => Core.vessel.GetExperimentSituation().HumanString())
 			);
 
 		public static readonly VOID_StrValue currBiome =
 			new VOID_StrValue(
 				"Biome",
-				new Func<string>(() => VOID_Tools.GetBiome(core.vessel).name)
+				new Func<string>(() => VOID_Tools.GetBiome(Core.vessel).name)
 			);
 
 		#endregion
@@ -1155,7 +1181,7 @@
 		public static readonly VOID_DoubleValue terrainElevation =
 			new VOID_DoubleValue(
 				"Terrain elevation",
-				new Func<double>(() => core.vessel.terrainAltitude),
+				new Func<double>(() => Core.vessel.terrainAltitude),
 				"m"
 			);
 
@@ -1178,4 +1204,3 @@
 	}
 }
 
-

--- a/VOID_DataLogger.cs
+++ b/VOID_DataLogger.cs
@@ -54,6 +54,7 @@
 
 		protected List<byte> csvBytes;
 
+		protected string _fileName;
 		protected FileStream _outputFile;
 
 		protected uint outstandingWrites;
@@ -97,15 +98,20 @@
 		{
 			get
 			{
-				return KSP.IO.IOUtils.GetFilePathFor(
-					typeof(VOID_Core),
-					string.Format(
-						"{0}_{1}",
-						this.vessel.vesselName,
-						"data.csv"
-					),
-					null
-				);
+				if (this._fileName == null || this._fileName == string.Empty)
+				{
+					this._fileName = KSP.IO.IOUtils.GetFilePathFor(
+						typeof(VOIDCore),
+						string.Format(
+							"{0}_{1}",
+							this.vessel.vesselName,
+							"data.csv"
+						),
+						null
+					);
+				}
+
+				return this._fileName;
 			}
 		}
 
@@ -145,10 +151,13 @@
 						byte[] byteOrderMark = utf8Encoding.GetPreamble();
 
 						logger.Append(" and writing preamble");
-						outputFile.Write(byteOrderMark, 0, byteOrderMark.Length);
+						this._outputFile.Write(byteOrderMark, 0, byteOrderMark.Length);
 					}
 
 					logger.Append('.');
+
+					logger.AppendFormat("  File is {0}opened asynchronously.", this._outputFile.IsAsync ? "" : "not ");
+
 					logger.Print();
 				}
 
@@ -211,7 +220,7 @@
 			this.CloseFileIfOpen();
 
 			logger.Append(" Done.");
-			logger.Print();
+			logger.Print(false);
 		}
 
 		#endregion
@@ -222,7 +231,7 @@
 		{
 			base.LoadConfig();
 
-			this.logIntervalStr = this.logInterval.value.ToString("{0:0.0##}");
+			this.logIntervalStr = this.logInterval.value.ToString("#.0##");
 		}
 
 		public override void ModuleWindow(int _)
@@ -266,6 +275,7 @@
 			if (float.TryParse(logIntervalStr, out newLogInterval))
 			{
 				logInterval.value = newLogInterval;
+				this.logIntervalStr = this.logInterval.value.ToString("#.0##");
 			}
 
 			GUILayout.EndVertical();
@@ -369,7 +379,7 @@
 			line.Append(',');
 
 			//atm density
-			line.Append(Tools.MuMech_ToSI(VOID_Data.atmDensity.Value, 3));
+			line.Append(VOID_Data.atmDensity.Value.ToString("G3"));
 			line.Append(',');
 
 			// Downrange Distance
@@ -411,13 +421,20 @@
 
 		private void CloseFileIfOpen()
 		{
-			if (this.csvBytes.Count > 0)
-			{
+			Tools.DebugLogger logger = Tools.DebugLogger.New(this);
+
+			logger.AppendFormat("Cleaning up file {0}...", this.fileName);
+
+			if (this.csvBytes != null && this.csvBytes.Count > 0)
+			{
+				logger.Append(" Writing remaining data...");
 				this.AsyncWriteData();
 			}
 
+			logger.Append(" Waiting for writes to finish.");
 			while (this.outstandingWrites > 0)
 			{
+				logger.Append('.');
 				System.Threading.Thread.Sleep(10);
 			}
 
@@ -425,15 +442,19 @@
 			{
 				this._outputFile.Close();
 				this._outputFile = null;
-			}
-		}
+				logger.Append(" File closed.");
+			}
+
+			logger.Print(false);
+		}
+
 		#endregion
 
 		#region Constructors & Destructors
 
 		public VOID_DataLogger()
 		{
-			this._Name = "CSV Data Logger";
+			this.Name = "CSV Data Logger";
 
 			this.loggingActive = false;
 			this.firstWrite = true;
@@ -445,6 +466,11 @@
 
 			this.WindowPos.x = Screen.width - 520f;
 			this.WindowPos.y = 85f;
+
+			this.core.onApplicationQuit += delegate(object sender)
+			{
+				this.CloseFileIfOpen();
+			};
 		}
 
 		~VOID_DataLogger()

file:a/VOID_DataValue.cs (deleted)
--- a/VOID_DataValue.cs
+++ /dev/null
@@ -1,379 +1,1 @@
-// VOID
-//
-// VOID_DataValue.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using System;
-using ToadicusTools;
-using UnityEngine;
-
-namespace VOID
-{
-	public interface IVOID_DataValue
-	{
-		string Label { get; }
-		string Units { get; }
-		object Value { get; }
-
-		void Refresh();
-		string ValueUnitString();
-		void DoGUIHorizontal();
-	}
-
-	public class VOID_DataValue<T> : IVOID_DataValue
-	{
-		/*
-		 * Static Members
-		 * */
-		public static implicit operator T(VOID_DataValue<T> v)
-		{
-			return (T)v.Value;
-		}
-
-		/*
-		 * Instance Members
-		 * */
-		/*
-		* Fields
-		 * */
-		protected T cache;
-		protected Func<T> ValueFunc;
-		protected float lastUpdate;
-
-		/* 
-		 * Properties
-		 * */
-		public string Label { get; protected set; }
-		public string Units { get; protected set; }
-
-		object IVOID_DataValue.Value
-		{
-			get
-			{
-				return (object)this.Value;
-			}
-		}
-
-		public T Value
-		{
-			get
-			{
-				if (
-					HighLogic.LoadedSceneIsEditor ||
-					(VOID_Core.Instance.updateTimer - this.lastUpdate > VOID_Core.Instance.updatePeriod) ||
-					(this.lastUpdate > VOID_Core.Instance.updateTimer)
-				)
-				{
-					this.Refresh();
-				}
-				return (T)this.cache;
-			}
-		}
-
-		/*
-		 * Methods
-		 * */
-		public VOID_DataValue(string Label, Func<T> ValueFunc, string Units = "")
-		{
-			this.Label = Label;
-			this.Units = Units;
-			this.ValueFunc = ValueFunc;
-			this.lastUpdate = 0;
-
-			VOID_Data.DataValues[this.GetHashCode()] = this;
-		}
-
-		public void Refresh()
-		{
-			this.cache = this.ValueFunc.Invoke ();
-			this.lastUpdate = VOID_Core.Instance.updateTimer;
-		}
-
-		public T GetFreshValue()
-		{
-			this.Refresh ();
-			return (T)this.cache;
-		}
-
-		public virtual string ValueUnitString() {
-			return this.Value.ToString() + this.Units;
-		}
-
-		public virtual void DoGUIHorizontal()
-		{
-			GUILayout.BeginHorizontal (GUILayout.ExpandWidth (true));
-			GUILayout.Label (this.Label + ":");
-			GUILayout.FlexibleSpace ();
-			GUILayout.Label (this.ValueUnitString(), GUILayout.ExpandWidth (false));
-			GUILayout.EndHorizontal ();
-		}
-
-		public override int GetHashCode()
-		{
-			int hash;
-			unchecked
-			{
-				hash = 79999;
-
-				hash = hash * 104399 + this.Label.GetHashCode();
-				hash = hash * 104399 + this.ValueFunc.GetHashCode();
-				hash = hash * 104399 + this.Units.GetHashCode();
-			}
-
-			return hash;
-		}
-
-		public override string ToString()
-		{
-			return string.Format (
-				"{0}: {1}{2}",
-				this.Label,
-				this.Value.ToString (),
-				this.Units
-			);
-		}
-	}
-
-	public abstract class VOID_NumValue<T> : VOID_DataValue<T>
-		where T : IFormattable, IConvertible, IComparable
-	{
-		public static implicit operator Double(VOID_NumValue<T> v)
-		{
-			return v.ToDouble();
-		}
-
-		public static implicit operator Int32(VOID_NumValue<T> v)
-		{
-			return v.ToInt32();
-		}
-
-
-		public static implicit operator Single(VOID_NumValue<T> v)
-		{
-			return v.ToSingle();
-		}
-
-		protected IFormatProvider formatProvider;
-
-		public VOID_NumValue(string Label, Func<T> ValueFunc, string Units = "") : base(Label, ValueFunc, Units)
-		{
-			this.formatProvider = System.Globalization.CultureInfo.CurrentUICulture;
-		}
-
-		public virtual double ToDouble(IFormatProvider provider)
-		{
-			return this.Value.ToDouble(provider);
-		}
-
-		public virtual double ToDouble()
-		{
-			return this.ToDouble(this.formatProvider);
-		}
-
-		public virtual int ToInt32(IFormatProvider provider)
-		{
-			return this.Value.ToInt32(provider);
-		}
-
-		public virtual int ToInt32()
-		{
-			return this.ToInt32(this.formatProvider);
-		}
-
-		public virtual float ToSingle(IFormatProvider provider)
-		{
-			return this.Value.ToSingle(provider);
-		}
-
-		public virtual float ToSingle()
-		{
-			return this.ToSingle(this.formatProvider);
-		}
-
-		public virtual string ToString(string Format)
-		{
-			return string.Format (
-				"{0}: {1}{2}",
-				this.Label,
-				this.Value.ToString(Format, this.formatProvider),
-				this.Units
-			);
-		}
-
-		public virtual string ToSIString(int digits = 3, int MinMagnitude = 0, int MaxMagnitude = int.MaxValue)
-		{
-			return string.Format (
-				"{0}{1}",
-				Tools.MuMech_ToSI (this, digits, MinMagnitude, MaxMagnitude),
-				this.Units
-			);
-		}
-
-		public virtual string ValueUnitString(string format)
-		{
-			return this.Value.ToString(format, this.formatProvider) + this.Units;
-		}
-		
-		public virtual string ValueUnitString(int digits) {
-			return Tools.MuMech_ToSI(this, digits) + this.Units;
-		}
-
-		public virtual string ValueUnitString(int digits, int MinMagnitude, int MaxMagnitude)
-		{
-			return Tools.MuMech_ToSI(this, digits, MinMagnitude, MaxMagnitude) + this.Units;
-		}
-
-		public virtual void DoGUIHorizontal(string format)
-		{
-			GUILayout.BeginHorizontal (GUILayout.ExpandWidth (true));
-			GUILayout.Label (this.Label + ":");
-			GUILayout.FlexibleSpace ();
-			GUILayout.Label (this.ValueUnitString(format), GUILayout.ExpandWidth (false));
-			GUILayout.EndHorizontal ();
-		}
-
-		public virtual int DoGUIHorizontal(int digits, bool precisionButton = true)
-		{
-			if (precisionButton)
-			{
-				return this.DoGUIHorizontalPrec(digits);
-			}
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label(this.Label + ":", GUILayout.ExpandWidth(true));
-			GUILayout.FlexibleSpace();
-			GUILayout.Label(this.ValueUnitString(digits), GUILayout.ExpandWidth(false));
-			GUILayout.EndHorizontal();
-
-			return digits;
-		}
-
-		public virtual int DoGUIHorizontalPrec(int digits)
-		{
-			double magnitude;
-			double magLimit;
-
-			magnitude = Math.Log10(Math.Abs((double)this));
-
-			magLimit = Math.Max(Math.Abs(magnitude), 3d) + 3d;
-			magLimit = Math.Round(Math.Ceiling(magLimit / 3f)) * 3d;
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label(this.Label + "ⁱ:", GUILayout.ExpandWidth(true));
-			GUILayout.FlexibleSpace();
-
-			if (magnitude >= 0)
-			{
-				GUILayout.Label(this.ValueUnitString(3, int.MinValue, (int)magnitude - digits), GUILayout.ExpandWidth(false));
-			}
-			else
-			{
-				GUILayout.Label(this.ValueUnitString(3, (int)magnitude + digits, int.MaxValue), GUILayout.ExpandWidth(false));
-			}
-			GUILayout.EndHorizontal();
-
-			if (Event.current.type == EventType.mouseUp)
-			{
-				Rect lastRect = GUILayoutUtility.GetLastRect();
-				if (lastRect.Contains(Event.current.mousePosition))
-				{
-					Tools.PostDebugMessage(string.Format("{0}: Changing digits from {1} within magLimit {2}.",
-						this.GetType().Name,
-						digits,
-						magLimit));
-
-					if (Event.current.button == 0)
-					{
-						digits = (digits + 3) % (int)magLimit;
-					}
-					else if (Event.current.button == 1)
-					{
-						digits = (digits - 3) % (int)magLimit;
-					}
-
-					if (digits < 0)
-					{
-						digits += (int)magLimit;
-					}
-
-					Tools.PostDebugMessage(string.Format("{0}: Changed digits to {1}." +
-						"\n\tNew minMagnitude: {2}, maxMagnitude: {3}" +
-						"\n\tMagnitude: {4}",
-						this.GetType().Name,
-						digits,
-						magnitude >= 0 ? int.MinValue : (int)magnitude - 4 + digits,
-						magnitude >= 0 ? (int)magnitude - digits : int.MaxValue,
-						magnitude
-					));
-				}
-			}
-
-			return digits;
-		}
-	}
-
-	public class VOID_DoubleValue : VOID_NumValue<double>
-	{
-		public VOID_DoubleValue(string Label, Func<double> ValueFunc, string Units) : base(Label, ValueFunc, Units) {}
-	}
-
-	public class VOID_FloatValue : VOID_NumValue<float>
-	{
-		public VOID_FloatValue(string Label, Func<float> ValueFunc, string Units) : base(Label, ValueFunc, Units) {}
-	}
-
-	public class VOID_IntValue : VOID_NumValue<int>
-	{
-		public VOID_IntValue(string Label, Func<int> ValueFunc, string Units) : base(Label, ValueFunc, Units) {}
-	}
-
-	public class VOID_StrValue : VOID_DataValue<string>
-	{
-		public VOID_StrValue(string Label, Func<string> ValueFunc) : base(Label, ValueFunc, "") {}
-	}
-
-	public class VOID_Vector3dValue : VOID_DataValue<Vector3d>
-	{
-		public VOID_Vector3dValue(string Label, Func<Vector3d> ValueFunc, string Units)
-			: base(Label, ValueFunc, Units)
-		{}
-
-		public string ToString(string format)
-		{
-			return string.Format("{0}: {1}{2}",
-				this.Label,
-				this.Value.ToString(format),
-				this.Units
-			);
-		}
-
-		public string ValueUnitString(string format) {
-			return this.Value.ToString(format) + this.Units;
-		}
-	}
-}
-
-

file:a/VOID_EditorCore.cs (deleted)
--- a/VOID_EditorCore.cs
+++ /dev/null
@@ -1,156 +1,1 @@
-// VOID
-//
-// VOID_EditorCore.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using Engineer.VesselSimulator;
-using KSP;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using ToadicusTools;
-using UnityEngine;
-
-namespace VOID
-{
-	public class VOID_EditorCore : VOID_Core
-	{
-		/*
-		 * Static Members
-		 * */
-		protected new static bool _initialized = false;
-		public new static bool Initialized
-		{
-			get 
-			{
-				return _initialized;
-			}
-		}
-
-		protected new static VOID_EditorCore _instance;
-		public new static VOID_EditorCore Instance
-		{
-			get
-			{
-				if (_instance == null)
-				{
-					_instance = new VOID_EditorCore();
-					_initialized = true;
-				}
-				return _instance;
-			}
-		}
-
-		public new static void Reset()
-		{
-			if (_initialized)
-			{
-				_instance.StopGUI();
-				_instance.Dispose();
-				_instance = null;
-				_initialized = false;
-			}
-		}
-
-		protected override ApplicationLauncher.AppScenes appIconVisibleScenes
-		{
-			get
-			{
-				return ApplicationLauncher.AppScenes.VAB | ApplicationLauncher.AppScenes.SPH;
-			}
-		}
-
-		public VOID_EditorCore() : base()
-		{
-			this._Name = "VOID Editor Core";
-		}
-
-		public override void OnGUI() {}
-
-		public override void DrawGUI()
-		{
-			if (!this._modulesLoaded)
-			{
-				this.LoadModulesOfType<IVOID_EditorModule>();
-			}
-
-			Rect _iconPos = Tools.DockToWindow (this.VOIDIconPos, this.mainWindowPos);
-
-			_iconPos = Tools.ClampRectToEditorPad (_iconPos);
-
-			if (_iconPos != this.VOIDIconPos)
-			{
-				this.VOIDIconPos = _iconPos;
-			}
-
-			base.DrawGUI();
-		}
-
-		public override void Update()
-		{
-			this.LoadBeforeUpdate();
-
-			foreach (IVOID_EditorModule module in this.Modules)
-			{
-				if (EditorLogic.startPod == null)
-				{
-					module.StopGUI();
-					continue;
-				}
-				if (HighLogic.LoadedSceneIsEditor && module.toggleActive && EditorLogic.SortedShipList.Any())
-				{
-					module.StartGUI();
-				}
-				if (!HighLogic.LoadedSceneIsEditor || !module.toggleActive || !EditorLogic.SortedShipList.Any())
-				{
-					module.StopGUI();
-				}
-			}
-
-			if (EditorLogic.startPod == null || !HighLogic.LoadedSceneIsEditor)
-			{
-				this.StopGUI();
-				return;
-			}
-			else if (!this.guiRunning && HighLogic.LoadedSceneIsEditor)
-			{
-				this.StartGUI();
-			}
-
-			if (EditorLogic.SortedShipList.Count > 0 && this.vesselSimActive)
-			{
-				Tools.PostDebugMessage(this, "Updating SimManager.");
-				this.UpdateSimManager();
-			}
-
-			this.CheckAndSave ();
-		}
-
-		public override void FixedUpdate() {}
-	}
-}
-
-

--- a/VOID_EditorHUD.cs
+++ b/VOID_EditorHUD.cs
@@ -26,7 +26,7 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using Engineer.VesselSimulator;
+using KerbalEngineer.VesselSimulator;
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -37,41 +37,21 @@
 
 namespace VOID
 {
-	public class VOID_EditorHUD : VOID_Module, IVOID_EditorModule
+	[VOID_Scenes(GameScenes.EDITOR)]
+	public class VOID_EditorHUD : VOID_HUDModule
 	{
 		/*
 		 * Fields
 		 * */
-		[AVOID_SaveValue("colorIndex")]
-		protected VOID_SaveValue<int> _colorIndex = 0;
-
-		protected List<Color> textColors = new List<Color>();
-
-		protected GUIStyle labelStyle;
-
+		protected HUDWindow ehudWindow;
 		protected EditorVesselOverlays _vesselOverlays;
+
+		[AVOID_SaveValue("snapToLeft")]
+		protected VOID_SaveValue<bool> snapToLeft;
 
 		/*
 		 * Properties
 		 * */
-		public int ColorIndex
-		{
-			get
-			{
-				return this._colorIndex;
-			}
-			set
-			{
-				if (this._colorIndex >= this.textColors.Count - 1)
-				{
-					this._colorIndex = 0;
-					return;
-				}
-
-				this._colorIndex = value;
-			}
-		}
-
 		protected EditorVesselOverlays vesselOverlays
 		{
 			get
@@ -118,61 +98,32 @@
 		 * */
 		public VOID_EditorHUD() : base()
 		{
-			this._Name = "Heads-Up Display";
+			this.Name = "Heads-Up Display";
 
 			this.toggleActive = true;
 
-			this.textColors.Add(Color.green);
-			this.textColors.Add(Color.black);
-			this.textColors.Add(Color.white);
-			this.textColors.Add(Color.red);
-			this.textColors.Add(Color.blue);
-			this.textColors.Add(Color.yellow);
-			this.textColors.Add(Color.gray);
-			this.textColors.Add(Color.cyan);
-			this.textColors.Add(Color.magenta);
-
-			this.labelStyle = new GUIStyle ();
-			// this.labelStyle.alignment = TextAnchor.UpperRight;
-			this.labelStyle.normal.textColor = this.textColors [this.ColorIndex];
+			this.snapToLeft.value = true;
+
+			this.ehudWindow = new HUDWindow(
+				"editorHUD",
+				this.ehudWindowFunc,
+				new Rect(EditorPanels.Instance.partsPanelWidth + 10f, 125f, 300f, 64f)
+			);
+			this.Windows.Add(this.ehudWindow);
 
 			Tools.PostDebugMessage (this.GetType().Name + ": Constructed.");
 		}
 
-		public override void DrawGUI()
-		{
-			SimManager.RequestSimulation();
+		public void ehudWindowFunc(int id)
+		{
+			StringBuilder hudString = new StringBuilder();
 
 			if (this.core.LastStage == null)
 			{
 				return;
 			}
 
-			float hudLeft;
-			StringBuilder hudString;
-
-			if (EditorLogic.fetch.editorScreen == EditorLogic.EditorScreen.Parts)
-			{
-				hudLeft = EditorPanels.Instance.partsPanelWidth + 10;
-			}
-			else if (EditorLogic.fetch.editorScreen == EditorLogic.EditorScreen.Actions)
-			{
-				hudLeft = EditorPanels.Instance.actionsPanelWidth + 10;
-			}
-			else
-			{
-				return;
-			}
-
-			GUI.skin = this.core.Skin;
-
-			Rect hudPos = new Rect (hudLeft, 48, 300, 32);
-
-			hudString = new StringBuilder();
-
-			// GUI.skin = AssetBase.GetGUISkin("KSP window 2");
-
-			labelStyle.normal.textColor = textColors [ColorIndex];
+			VOID_Styles.labelHud.alignment = TextAnchor.UpperLeft;
 
 			hudString.Append("Total Mass: ");
 			hudString.Append(this.core.LastStage.totalMass.ToString("F3"));
@@ -200,8 +151,16 @@
 			hudString.Append("Bottom Stage T/W Ratio: ");
 			hudString.Append(this.core.LastStage.thrustToWeight.ToString("F3"));
 
+			Tools.PostDebugMessage(this,
+				"CoMmarker.gameObject.activeInHierarchy: {0};" +
+				"CoTmarker.gameObject.activeInHierarchy: {1}",
+				this.CoMmarker.gameObject.activeInHierarchy,
+				this.CoTmarker.gameObject.activeInHierarchy
+			);
+
 			if (this.CoMmarker.gameObject.activeInHierarchy && this.CoTmarker.gameObject.activeInHierarchy)
 			{
+				Tools.PostDebugMessage(this, "CoM and CoT markers are active, doing thrust offset.");
 				hudString.Append('\n');
 
 				hudString.Append("Thrust Offset: ");
@@ -211,19 +170,77 @@
 						this.CoMmarker.posMarkerObject.transform.position - this.CoTmarker.posMarkerObject.transform.position
 					).ToString("F3"));
 			}
-
-			GUI.Label (
-				hudPos,
+			#if DEBUG
+			else
+			{
+				Tools.PostDebugMessage(this, "CoM and CoT markers are not active, thrust offset skipped.");
+			}
+			#endif
+
+			GUILayout.Label(
 				hudString.ToString(),
-				labelStyle);
-		}
-
-		public override void DrawConfigurables()
-		{
-			if (GUILayout.Button ("Change HUD color", GUILayout.ExpandWidth (false)))
-			{
-				++this.ColorIndex;
-			}
+				VOID_Styles.labelHud,
+				GUILayout.ExpandWidth(true),
+				GUILayout.ExpandHeight(true)
+			);
+
+			if (!this.positionsLocked)
+			{
+				GUI.DragWindow();
+			}
+
+			GUI.BringWindowToBack(id);
+		}
+
+		public override void DrawGUI()
+		{
+			float hudLeft;
+
+			if (EditorLogic.fetch.editorScreen == EditorScreen.Parts)
+			{
+				hudLeft = EditorPanels.Instance.partsPanelWidth + 10f;
+				hudLeft += EditorPartList.Instance.transformTopLeft.position.x -
+					EditorPartList.Instance.transformTopLeft.parent.parent.position.x -
+					72f;
+			}
+			else if (EditorLogic.fetch.editorScreen == EditorScreen.Actions)
+			{
+				hudLeft = EditorPanels.Instance.actionsPanelWidth + 10f;
+			}
+			else
+			{
+				return;
+			}
+
+			Tools.PostDebugMessage(this,
+				"EditorPartList topLeft.parent.parent.position: {0}\n" +
+				"EditorPartList topLeft.parent.position: {1}\n" +
+				"EditorPartList topLeft.position: {2}\n" +
+				"snapToEdge: {3} (pos.Xmin: {4}; hudLeft: {5})",
+				EditorPartList.Instance.transformTopLeft.parent.parent.position,
+				EditorPartList.Instance.transformTopLeft.parent.position,
+				EditorPartList.Instance.transformTopLeft.position,
+				this.snapToLeft, this.ehudWindow.WindowPos.xMin, hudLeft
+			);
+
+			base.DrawGUI();
+
+			Rect hudPos = this.ehudWindow.WindowPos;
+
+			if (this.snapToLeft && this.positionsLocked)
+			{
+				hudPos.xMin = hudLeft;
+			}
+			else
+			{
+				hudPos.xMin = Mathf.Max(hudLeft, hudPos.xMin);
+			}
+
+			hudPos.width = this.ehudWindow.defaultWindowPos.width;
+
+			this.ehudWindow.WindowPos = hudPos;
+
+			this.snapToLeft = Mathf.Abs(this.ehudWindow.WindowPos.xMin - hudLeft) < 15f;
 		}
 	}
 }

--- a/VOID_HUD.cs
+++ b/VOID_HUD.cs
@@ -26,7 +26,7 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using Engineer.VesselSimulator;
+using KerbalEngineer.VesselSimulator;
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -36,78 +36,32 @@
 
 namespace VOID
 {
-	public class VOID_HUD : VOID_Module, IVOID_Module
+	public class VOID_HUD : VOID_HUDModule, IVOID_Module
 	{
 		/*
 		 * Fields
 		 * */
-		[AVOID_SaveValue("colorIndex")]
-		protected VOID_SaveValue<int> _colorIndex;
-
-		protected List<Color> textColors;
-
-		protected Rect leftHUDdefaultPos;
-		protected Rect rightHUDdefaultPos;
-
-		[AVOID_SaveValue("leftHUDPos")]
-		protected VOID_SaveValue<Rect> leftHUDPos;
-		[AVOID_SaveValue("rightHUDPos")]
-		protected VOID_SaveValue<Rect> rightHUDPos;
-
-		[AVOID_SaveValue("positionsLocked")]
-		protected VOID_SaveValue<bool> positionsLocked;
+		protected HUDWindow leftHUD;
+		protected HUDWindow rightHUD;
 
 		/*
 		 * Properties
 		 * */
-		public int ColorIndex
-		{
-			get
-			{
-				return this._colorIndex;
-			}
-			set
-			{
-				if (this._colorIndex >= this.textColors.Count - 1)
-				{
-					this._colorIndex = 0;
-					return;
-				}
-
-				this._colorIndex = value;
-			}
-		}
 
 		/* 
 		 * Methods
 		 * */
 		public VOID_HUD() : base()
 		{
-			this._Name = "Heads-Up Display";
+			this.Name = "Heads-Up Display";
 
 			this.toggleActive = true;
 
-			this._colorIndex = 0;
+			this.leftHUD = new HUDWindow("leftHUD", this.leftHUDWindow, new Rect(Screen.width * .375f - 300f, 0f, 300f, 90f));
+			this.Windows.Add(this.leftHUD);
 
-			this.textColors = new List<Color>();
-
-			this.textColors.Add(Color.green);
-			this.textColors.Add(Color.black);
-			this.textColors.Add(Color.white);
-			this.textColors.Add(Color.red);
-			this.textColors.Add(Color.blue);
-			this.textColors.Add(Color.yellow);
-			this.textColors.Add(Color.gray);
-			this.textColors.Add(Color.cyan);
-			this.textColors.Add(Color.magenta);
-
-			this.leftHUDdefaultPos = new Rect(Screen.width * .375f - 300f, 0f, 300f, 90f);
-			this.leftHUDPos = new Rect(this.leftHUDdefaultPos);
-
-			this.rightHUDdefaultPos = new Rect(Screen.width * .625f, 0f, 300f, 90f);
-			this.rightHUDPos = new Rect(this.rightHUDdefaultPos);
-
-			this.positionsLocked = true;
+			this.rightHUD = new HUDWindow("rightHUD", this.rightHUDWindow, new Rect(Screen.width * .625f, 0f, 300f, 90f));
+			this.Windows.Add(this.rightHUD);
 
 			Tools.PostDebugMessage ("VOID_HUD: Constructed.");
 		}
@@ -149,7 +103,12 @@
 				leftHUD.Append(string.Intern("-- POWER LOST --"));
 			}
 
-			GUILayout.Label(leftHUD.ToString(), VOID_Styles.labelHud, GUILayout.ExpandWidth(true));
+			GUILayout.Label(
+				leftHUD.ToString(),
+				VOID_Styles.labelHud,
+				GUILayout.ExpandWidth(true),
+				GUILayout.ExpandHeight(true)
+			);
 
 			if (!this.positionsLocked)
 			{
@@ -210,7 +169,12 @@
 			}
 
 
-			GUILayout.Label(rightHUD.ToString(), VOID_Styles.labelHud, GUILayout.ExpandWidth(true));
+			GUILayout.Label(
+				rightHUD.ToString(),
+				VOID_Styles.labelHud,
+				GUILayout.ExpandWidth(true),
+				GUILayout.ExpandHeight(true)
+			);
 
 			if (!this.positionsLocked)
 			{
@@ -219,52 +183,6 @@
 
 			GUI.BringWindowToBack(id);
 		}
-
-		public override void DrawGUI()
-		{
-			VOID_Styles.labelHud.normal.textColor = textColors [ColorIndex];
-
-			GUI.skin = this.core.Skin;
-
-			if ((TimeWarp.WarpMode == TimeWarp.Modes.LOW) || (TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate))
-			{
-				SimManager.RequestSimulation();
-			}
-
-			this.leftHUDPos.value = GUI.Window(
-				this.core.windowID,
-				this.leftHUDPos,
-				VOID_Tools.GetWindowHandler(this.leftHUDWindow),
-				GUIContent.none,
-				GUIStyle.none
-			);
-
-			this.rightHUDPos.value = GUI.Window(
-				this.core.windowID,
-				this.rightHUDPos,
-				VOID_Tools.GetWindowHandler(this.rightHUDWindow),
-				GUIContent.none,
-				GUIStyle.none
-			);
-		}
-
-		public override void DrawConfigurables()
-		{
-			if (GUILayout.Button (string.Intern("Change HUD color"), GUILayout.ExpandWidth (false)))
-			{
-				++this.ColorIndex;
-			}
-
-			if (GUILayout.Button(string.Intern("Reset HUD Positions"), GUILayout.ExpandWidth(false)))
-			{
-				this.leftHUDPos = new Rect(this.leftHUDdefaultPos);
-				this.rightHUDPos = new Rect(this.rightHUDdefaultPos);
-			}
-
-			this.positionsLocked = GUILayout.Toggle(this.positionsLocked,
-				string.Intern("Lock HUD Positions"),
-				GUILayout.ExpandWidth(false));
-		}
 	}
 }
 

--- a/VOID_HUDAdvanced.cs
+++ b/VOID_HUDAdvanced.cs
@@ -26,7 +26,7 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using Engineer.VesselSimulator;
+using KerbalEngineer.VesselSimulator;
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -37,37 +37,33 @@
 
 namespace VOID
 {
-	public class VOID_HUDAdvanced : VOID_Module, IVOID_Module
+	public class VOID_HUDAdvanced : VOID_HUDModule, IVOID_Module
 	{
 		/*
 		 * Fields
 		 * */
 		protected VOID_HUD primaryHUD;
 
-		protected Rect leftHUDdefaultPos;
-		protected Rect rightHUDdefaultPos;
-
-		[AVOID_SaveValue("leftHUDPos")]
-		protected VOID_SaveValue<Rect> leftHUDPos;
-		[AVOID_SaveValue("rightHUDPos")]
-		protected VOID_SaveValue<Rect> rightHUDPos;
-
-		[AVOID_SaveValue("positionsLocked")]
-		protected VOID_SaveValue<bool> positionsLocked;
+		protected HUDWindow leftHUD;
+		protected HUDWindow rightHUD;
 
 		/*
 		 * Properties
 		 * */
-		public int ColorIndex
+		public override int ColorIndex
 		{
 			get
 			{
-				if (this.primaryHUD == null)
-				{
-					return 0;
-				}
-
-				return this.primaryHUD.ColorIndex;
+				if (this.primaryHUD != null)
+				{
+					return this.primaryHUD.ColorIndex;
+				}
+
+				return base.ColorIndex;
+			}
+			set
+			{
+				base.ColorIndex = value;
 			}
 		}
 
@@ -76,23 +72,28 @@
 		 * */
 		public VOID_HUDAdvanced() : base()
 		{
-			this._Name = "Advanced Heads-Up Display";
+			this.Name = "Advanced Heads-Up Display";
 
 			this.toggleActive = true;
 
-			this.leftHUDdefaultPos = new Rect(
-				Screen.width * .5f - (float)GameSettings.UI_SIZE * .25f - 300f,
-				Screen.height - 200f,
-				300f, 90f
-			);
-			this.leftHUDPos = new Rect(this.leftHUDdefaultPos);
-
-			this.rightHUDdefaultPos = new Rect(
-				Screen.width * .5f + (float)GameSettings.UI_SIZE * .25f,
-				Screen.height - 200f,
-				300f, 90f
-			);
-			this.rightHUDPos = new Rect(this.rightHUDdefaultPos);
+			this.leftHUD = new HUDWindow("leftHUD",
+				this.leftHUDWindow,
+				new Rect(
+					Screen.width * .5f - (float)GameSettings.UI_SIZE * .25f - 300f,
+					Screen.height - 200f,
+					300f, 90f)
+			);
+			this.Windows.Add(this.leftHUD);
+
+			this.rightHUD = new HUDWindow(
+				"rightHUD",
+				this.rightHUDWindow,
+				new Rect(
+					Screen.width * .5f + (float)GameSettings.UI_SIZE * .25f,
+					Screen.height - 200f,
+					300f, 90f)
+			);
+			this.Windows.Add(this.rightHUD);
 
 			this.positionsLocked = true;
 
@@ -148,7 +149,12 @@
 				leftHUD.Append(string.Intern("-- POWER LOST --"));
 			}
 
-			GUILayout.Label(leftHUD.ToString(), VOID_Styles.labelHud, GUILayout.ExpandWidth(true));
+			GUILayout.Label(
+				leftHUD.ToString(),
+				VOID_Styles.labelHud,
+				GUILayout.ExpandWidth(true),
+				GUILayout.ExpandHeight(true)
+			);
 
 			if (!this.positionsLocked)
 			{
@@ -207,7 +213,12 @@
 				rightHUD.Append(string.Intern("-- POWER LOST --"));
 			}
 
-			GUILayout.Label(rightHUD.ToString(), VOID_Styles.labelHud, GUILayout.ExpandWidth(true));
+			GUILayout.Label(
+				rightHUD.ToString(),
+				VOID_Styles.labelHud,
+				GUILayout.ExpandWidth(true),
+				GUILayout.ExpandHeight(true)
+			);
 
 			if (!this.positionsLocked)
 			{
@@ -229,36 +240,31 @@
 					}
 				}
 			}
-			else
-			{
-				if ((TimeWarp.WarpMode == TimeWarp.Modes.LOW) || (TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate))
-				{
-					SimManager.RequestSimulation();
-				}
-
-				this.leftHUDPos.value = GUI.Window(
-					this.core.windowID,
-					this.leftHUDPos,
-					VOID_Tools.GetWindowHandler(this.leftHUDWindow),
-					GUIContent.none,
-					GUIStyle.none
-				);
-
-				if (VOID_Data.upcomingManeuverNodes > 0)
-				{
-					this.rightHUDPos.value = GUI.Window(
-						this.core.windowID,
-						this.rightHUDPos,
-						VOID_Tools.GetWindowHandler(this.rightHUDWindow),
-						GUIContent.none,
-						GUIStyle.none
-					);
-				}
-			}
+
+			if (VOID_Data.upcomingManeuverNodes < 1 && this.Windows.Contains(this.rightHUD))
+			{
+				this.Windows.Remove(this.rightHUD);
+			}
+			else if (VOID_Data.upcomingManeuverNodes > 0 && !this.Windows.Contains(this.rightHUD))
+			{
+				this.Windows.Add(this.rightHUD);
+			}
+
+			base.DrawGUI();
 		}
 
 		public override void DrawConfigurables()
 		{
+			base.DrawConfigurables();
+
+			if (GUILayout.Button(string.Intern("Reset Advanced HUD Positions"), GUILayout.ExpandWidth(false)))
+			{
+				foreach (HUDWindow window in this.Windows)
+				{
+					window.WindowPos = new Rect(window.defaultWindowPos);
+				}
+			}
+
 			this.positionsLocked = GUILayout.Toggle(this.positionsLocked,
 				string.Intern("Lock Advanced HUD Positions"),
 				GUILayout.ExpandWidth(false));

file:a/VOID_Module.cs (deleted)
--- a/VOID_Module.cs
+++ /dev/null
@@ -1,311 +1,1 @@
-// VOID
-//
-// VOID_Module.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using ToadicusTools;
-using UnityEngine;
-
-namespace VOID
-{
-	public abstract class VOID_Module : IVOID_Module
-	{
-		/*
-		 * Fields
-		 * */
-		[AVOID_SaveValue("Active")]
-		protected VOID_SaveValue<bool> _Active = false;
-		protected bool _Running = false;
-
-		protected string _Name;
-
-		protected float lastUpdate = 0;
-
-		/*
-		 * Properties
-		 * */
-		protected virtual VOID_Core core
-		{
-			get
-			{
-				if (HighLogic.LoadedSceneIsEditor)
-				{
-					return VOID_EditorCore.Instance as VOID_Core;
-				}
-
-				return VOID_Core.Instance;
-			}
-		}
-
-		public virtual bool toggleActive
-		{
-			get
-			{
-				return this._Active;
-			}
-			set
-			{
-				this._Active.value = value;
-			}
-		}
-
-		public virtual bool guiRunning
-		{
-			get
-			{
-				return this._Running;
-			}
-		}
-
-		public virtual string Name
-		{
-			get
-			{
-				return this._Name;
-			}
-		}
-
-		public virtual Vessel vessel
-		{
-			get
-			{
-				return FlightGlobals.ActiveVessel;
-			}
-		}
-
-		/*
-		 * Methods
-		 * */
-		public void StartGUI()
-		{
-			if (!this.toggleActive || this.guiRunning)
-			{
-				return;
-			}
-
-			Tools.PostDebugMessage (string.Format("Adding {0} to the draw queue.", this.GetType().Name));
-			RenderingManager.AddToPostDrawQueue (3, this.DrawGUI);
-			this._Running = true;
-		}
-
-		public void StopGUI()
-		{
-			if (!this.guiRunning)
-			{
-				return;
-			}
-			Tools.PostDebugMessage (string.Format("Removing {0} from the draw queue.", this.GetType().Name));
-			RenderingManager.RemoveFromPostDrawQueue (3, this.DrawGUI);
-			this._Running = false;
-		}
-
-		public abstract void DrawGUI();
-
-		public virtual void DrawConfigurables() {}
-
-		public virtual void LoadConfig()
-		{
-			var config = KSP.IO.PluginConfiguration.CreateForType<VOID_Core> ();
-			config.load ();
-
-			foreach (var field in this.GetType().GetFields(
-				BindingFlags.NonPublic |
-				BindingFlags.Public |
-				BindingFlags.Instance |
-				BindingFlags.FlattenHierarchy
-				))
-			{
-				object[] attrs = field.GetCustomAttributes(typeof(AVOID_SaveValue), false);
-
-				if (attrs.Length == 0) {
-					continue;
-				}
-
-				AVOID_SaveValue attr = attrs.FirstOrDefault () as AVOID_SaveValue;
-
-				string fieldName = string.Format("{0}_{1}", this.GetType().Name, attr.Name);
-
-				Tools.PostDebugMessage(string.Format("{0}: Loading field {1}.", this.GetType().Name, fieldName));
-
-				object fieldValue = field.GetValue(this);
-
-				bool convertBack = false;
-				if (fieldValue is IVOID_SaveValue)
-				{
-					fieldValue = (fieldValue as IVOID_SaveValue).AsType;
-					convertBack = true;
-				}
-
-				fieldValue = config.GetValue(fieldName, fieldValue);
-
-				if (convertBack)
-				{
-					Type type = typeof(VOID_SaveValue<>).MakeGenericType (fieldValue.GetType ());
-					IVOID_SaveValue convertValue = Activator.CreateInstance (type) as IVOID_SaveValue;
-					convertValue.SetValue (fieldValue);
-					fieldValue = convertValue;
-				}
-
-				field.SetValue (this, fieldValue);
-
-				Tools.PostDebugMessage(string.Format("{0}: Loaded field {1}.", this.GetType().Name, fieldName));
-			}
-		}
-
-		public virtual void _SaveToConfig(KSP.IO.PluginConfiguration config)
-		{
-			foreach (var field in this.GetType().GetFields(
-				BindingFlags.Instance |
-				BindingFlags.NonPublic |
-				BindingFlags.Public |
-				BindingFlags.FlattenHierarchy
-				))
-			{
-				object[] attrs = field.GetCustomAttributes(typeof(AVOID_SaveValue), false);
-
-				if (attrs.Length == 0) {
-					continue;
-				}
-
-				AVOID_SaveValue attr = attrs.FirstOrDefault () as AVOID_SaveValue;
-
-				string fieldName = string.Format("{0}_{1}", this.GetType().Name, attr.Name);
-
-				object fieldValue = field.GetValue(this);
-
-				if (fieldValue is IVOID_SaveValue)
-				{
-					fieldValue = (fieldValue as IVOID_SaveValue).AsType;
-				}
-
-				config.SetValue(fieldName, fieldValue);
-
-				Tools.PostDebugMessage(string.Format("{0}: Saved field {1}.", this.GetType().Name, fieldName));
-			}
-		}
-	}
-
-	public abstract class VOID_WindowModule : VOID_Module
-	{
-		[AVOID_SaveValue("WindowPos")]
-		protected Rect WindowPos;
-		protected float defWidth;
-		protected float defHeight;
-
-		protected string inputLockName;
-
-		public VOID_WindowModule() : base()
-		{
-			this.defWidth = 250f;
-			this.defHeight = 50f;
-
-			this.inputLockName = string.Concat(this.Name, "_edlock");
-
-			this.WindowPos = new Rect(Screen.width / 2, Screen.height / 2, this.defWidth, this.defHeight);
-		}
-
-		public abstract void ModuleWindow(int _);
-
-		public override void DrawGUI()
-		{
-			GUI.skin = this.core.Skin;
-
-			Rect _Pos = this.WindowPos;
-
-			_Pos = GUILayout.Window(
-				this.core.windowID,
-				_Pos,
-				VOID_Tools.GetWindowHandler(this.ModuleWindow),
-				this.Name,
-				GUILayout.Width(this.defWidth),
-				GUILayout.Height(this.defHeight)
-			);
-
-			bool cursorInWindow = _Pos.Contains(Mouse.screenPos);
-
-			switch (HighLogic.LoadedScene)
-			{
-				case GameScenes.EDITOR:
-				case GameScenes.SPH:
-					if (cursorInWindow)
-					{
-						InputLockManager.SetControlLock(
-							ControlTypes.EDITOR_ICON_HOVER | ControlTypes.EDITOR_ICON_PICK |
-							ControlTypes.EDITOR_PAD_PICK_COPY | ControlTypes.EDITOR_PAD_PICK_COPY,
-							this.inputLockName
-						);
-						EditorLogic.fetch.Lock(false, false, false, this.inputLockName);
-					}
-					else
-					{
-						EditorLogic.fetch.Unlock(this.inputLockName);
-					}
-					break;
-				case GameScenes.FLIGHT:
-					if (cursorInWindow)
-					{
-						InputLockManager.SetControlLock(ControlTypes.CAMERACONTROLS, this.inputLockName);
-					}
-					else if (InputLockManager.GetControlLock(this.inputLockName) != ControlTypes.None)
-					{
-						InputLockManager.RemoveControlLock(this.inputLockName);
-					}
-					break;
-				case GameScenes.SPACECENTER:
-					if (cursorInWindow)
-					{
-						InputLockManager.SetControlLock(ControlTypes.KSC_FACILITIES, this.inputLockName);
-					}
-					else if (InputLockManager.GetControlLock(this.inputLockName) != ControlTypes.None)
-					{
-						InputLockManager.RemoveControlLock(this.inputLockName);
-					}
-					break;
-			}
-
-			if (HighLogic.LoadedSceneIsEditor)
-			{
-				_Pos = Tools.ClampRectToEditorPad(_Pos);
-			}
-			else
-			{
-				_Pos = Tools.ClampRectToScreen(_Pos);
-			}
-
-			if (_Pos != this.WindowPos)
-			{
-				this.WindowPos = _Pos;
-				this.core.configDirty = true;
-			}
-		}
-	}
-}
-
-

--- a/VOID_Orbital.cs
+++ b/VOID_Orbital.cs
@@ -44,7 +44,7 @@
 
 		public VOID_Orbital()
 		{
-			this._Name = "Orbital Information";
+			this.Name = "Orbital Information";
 
 			this.WindowPos.x = Screen.width - 520f;
 			this.WindowPos.y = 250f;

--- a/VOID_Rendezvous.cs
+++ b/VOID_Rendezvous.cs
@@ -47,7 +47,7 @@
 
 		public VOID_Rendezvous()
 		{
-			this._Name = "Rendezvous Information";
+			this.Name = "Rendezvous Information";
 
 			this.WindowPos.x = 845;
 			this.WindowPos.y = 85;

file:a/VOID_SaveValue.cs (deleted)
--- a/VOID_SaveValue.cs
+++ /dev/null
@@ -1,166 +1,1 @@
-// VOID
-//
-// VOID_SaveValue.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using KSP;
-using System;
-using System.Collections.Generic;
-using ToadicusTools;
-using UnityEngine;
-
-namespace VOID
-{
-	public struct VOID_SaveValue<T> : IVOID_SaveValue
-	{
-		private T _value;
-		private Type _type;
-
-		private VOID_Core Core
-		{
-			get
-			{
-				if (HighLogic.LoadedSceneIsEditor)
-				{
-					if (VOID_EditorCore.Initialized)
-					{
-						return VOID_EditorCore.Instance;
-					}
-				}
-				else if (HighLogic.LoadedSceneIsFlight)
-				{
-					if (VOID_Core.Initialized)
-					{
-						return VOID_Core.Instance;
-					}
-				}
-				return null;
-			}
-		}
-
-		public T value
-		{
-			get
-			{
-				return this._value;
-			}
-			set
-			{
-				if (this.Core != null && !System.Object.Equals(this._value, value))
-				{
-					Tools.PostDebugMessage (string.Format (
-						"VOID: Dirtying config for type {0} in method {1}." +
-						"\n\t Old Value: {2}, New Value: {3}" +
-						"\n\t Object.Equals(New, Old): {4}",
-						this._type,
-						new System.Diagnostics.StackTrace().GetFrame(1).GetMethod(),
-						this._value,
-						value,
-						System.Object.Equals(this._value, value)
-					));
-					this.Core.configDirty = true;
-				}
-				this._value = value;
-			}
-		}
-
-		public Type type
-		{
-			get
-			{
-				if (this._type == null && this._value != null)
-				{
-					this._type = this._value.GetType ();
-				}
-				return this._type;
-			}
-			set
-			{
-				this._type = value;
-			}
-		}
-
-		public object AsType
-		{
-			get
-			{
-				return (T)this._value;
-			}
-		}
-
-		public void SetValue(object v)
-		{
-			this.value = (T)v;
-		}
-
-		public static implicit operator T(VOID_SaveValue<T> v)
-		{
-			return (T)v.value;
-		}
-
-		public static implicit operator VOID_SaveValue<T>(T v)
-		{
-			VOID_SaveValue<T> r = new VOID_SaveValue<T>();
-			r.type = v.GetType();
-			r.value = v;
-
-			return r;
-		}
-
-		public override string ToString()
-		{
-			return this.value.ToString();
-		}
-	}
-
-	public interface IVOID_SaveValue
-	{
-		Type type { get; }
-		object AsType { get; }
-		void SetValue(object v);
-	}
-
-	[AttributeUsage(AttributeTargets.Field)]
-	public class AVOID_SaveValue : Attribute
-	{
-		protected string _name;
-
-		public string Name
-		{
-			get
-			{
-				return this._name;
-			}
-		}
-
-		public AVOID_SaveValue(string fieldName)
-		{
-			this._name = fieldName;
-		}
-	}
-}
-
-

--- a/VOID_StageInfo.cs
+++ b/VOID_StageInfo.cs
@@ -3,7 +3,7 @@
 // This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a
 // copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/
 
-using Engineer.VesselSimulator;
+using KerbalEngineer.VesselSimulator;
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -13,6 +13,7 @@
 
 namespace VOID
 {
+	[VOID_Scenes(GameScenes.EDITOR, GameScenes.FLIGHT)]
 	public class VOID_StageInfo : VOID_WindowModule
 	{
 		private Table stageTable;
@@ -38,7 +39,7 @@
 
 		public VOID_StageInfo() : base()
 		{
-			this._Name = "Stage Information";
+			this.Name = "Stage Information";
 			this.defWidth = 200f;
 			this.bodyIdx = 4;
 
@@ -94,12 +95,12 @@
 		public override void ModuleWindow(int _)
 		{
 			if (
-				HighLogic.LoadedSceneIsEditor ||
+				!HighLogic.LoadedSceneIsFlight ||
 				(TimeWarp.WarpMode == TimeWarp.Modes.LOW) ||
 				(TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate)
 			)
 			{
-				Engineer.VesselSimulator.SimManager.RequestSimulation();
+				KerbalEngineer.VesselSimulator.SimManager.RequestSimulation();
 			}
 
 			if (!this.stylesApplied)
@@ -211,8 +212,6 @@
 			}
 		}
 	}
-
-	public class VOID_StageInfoEditor : VOID_StageInfo, IVOID_EditorModule {}
 }
 
 

--- a/VOID_Styles.cs
+++ b/VOID_Styles.cs
@@ -121,6 +121,8 @@
 
 		static VOID_Styles()
 		{
+			OnSkinChanged();
+
 			Ready = false;
 		}
 	}

--- a/VOID_SurfAtmo.cs
+++ b/VOID_SurfAtmo.cs
@@ -41,7 +41,7 @@
 
 		public VOID_SurfAtmo()
 		{
-			this._Name = "Surface & Atmospheric Information";
+			this.Name = "Surface & Atmospheric Information";
 
 			this.WindowPos.x = Screen.width - 260f;
 			this.WindowPos.y = 85;

--- a/VOID_TWR.cs
+++ b/VOID_TWR.cs
@@ -12,22 +12,23 @@
 
 namespace VOID
 {
+	[VOID_Scenes(GameScenes.EDITOR, GameScenes.FLIGHT)]
 	public class VOID_TWR : VOID_WindowModule
 	{
 		public VOID_TWR() : base()
 		{
-			this._Name = "IP Thrust-to-Weight Ratios";
+			this.Name = "IP Thrust-to-Weight Ratios";
 		}
 
 		public override void ModuleWindow(int _)
 		{
 			if (
-				HighLogic.LoadedSceneIsEditor ||
+				!HighLogic.LoadedSceneIsFlight ||
 				(TimeWarp.WarpMode == TimeWarp.Modes.LOW) ||
 				(TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate)
 			)
 			{
-				Engineer.VesselSimulator.SimManager.RequestSimulation();
+				KerbalEngineer.VesselSimulator.SimManager.RequestSimulation();
 			}
 
 			GUILayout.BeginVertical();
@@ -62,8 +63,6 @@
 			GUI.DragWindow();
 		}
 	}
-
-	public class VOID_EditorTWR : VOID_TWR, IVOID_EditorModule {}
 }
 
 

file:a/VOID_Tools.cs (deleted)
--- a/VOID_Tools.cs
+++ /dev/null
@@ -1,1055 +1,1 @@
-// VOID
-//
-// VOID_Tools.cs
-//
-// Copyright © 2014, toadicus
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification,
-// are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-//    this list of conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-//    this list of conditions and the following disclaimer in the documentation and/or other
-//    materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be used
-//    to endorse or promote products derived from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-// 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
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using KSP;
-using System;
-using System.Collections.Generic;
-using ToadicusTools;
-using UnityEngine;
-
-namespace VOID
-{
-	public static partial class VOID_Tools
-	{
-		#region CelestialBody Utilities
-		public static bool hasAncestor(this CelestialBody bodyA, CelestialBody bodyB)
-		{
-			if (bodyA == null || bodyB == null)
-			{
-				return false;
-			}
-
-			while (bodyA.orbitDriver != null)
-			{
-				if (bodyA.orbit.referenceBody == bodyB)
-				{
-					return true;
-				}
-
-				bodyA = bodyA.orbit.referenceBody;
-			} 
-
-			return false;
-		}
-
-		public static bool NearestRelatedParents(ref CelestialBody bodyA, ref CelestialBody bodyB)
-		{
-			if (bodyA == null || bodyB == null || bodyA.orbitDriver == null || bodyB.orbitDriver == null)
-			{
-				throw new ArgumentException(string.Concat(
-					"CelestialBody::FindRelatedParents: ",
-					"Neither body may be null, and both bodies must have orbits."
-				));
-			}
-
-			CelestialBody a, b;
-
-			a = bodyA;
-
-			while (bodyA.orbitDriver != null)
-			{
-				b = bodyB;
-
-				while (b.orbitDriver != null)
-				{
-					if (a.orbit.referenceBody == b.orbit.referenceBody)
-					{
-						bodyA = a;
-						bodyB = b;
-						return true;
-					}
-
-					b = b.orbit.referenceBody;
-				}
-
-				a = a.orbit.referenceBody;
-			}
-
-			return false;
-		}
-		#endregion
-
-		#region VESSEL_EXTENSIONS_SCIENCE
-		public static CBAttributeMap.MapAttribute GetBiome(this Vessel vessel)
-		{
-			CBAttributeMap.MapAttribute mapAttribute;
-
-			try
-			{
-				CBAttributeMap BiomeMap = vessel.mainBody.BiomeMap;
-
-				double lat = vessel.latitude * Math.PI / 180d;
-				double lon = vessel.longitude * Math.PI / 180d;
-
-				mapAttribute = BiomeMap.GetAtt(lat, lon);
-
-				/*
-				lon -= Math.PI / 2d;
-
-				if (lon < 0d)
-				{
-					lon += 2d * Math.PI;
-				}
-
-				float v = (float)(lat / Math.PI) + 0.5f;
-				float u = (float)(lon / (2d * Math.PI));
-
-				Color pixelBilinear = BiomeMap.Map.GetPixelBilinear(u, v);
-				mapAttribute = BiomeMap.defaultAttribute;
-
-				if (BiomeMap.Map != null)
-				{
-					if (BiomeMap.exactSearch)
-					{
-						for (int i = 0; i < BiomeMap.Attributes.Length; ++i)
-						{
-							if (pixelBilinear == BiomeMap.Attributes[i].mapColor)
-							{
-								mapAttribute = BiomeMap.Attributes[i];
-							}
-						}
-					}
-					else
-					{
-						float zero = 0;
-						float num = 1 / zero;
-						for (int j = 0; j < BiomeMap.Attributes.Length; ++j)
-						{
-							Color mapColor = BiomeMap.Attributes[j].mapColor;
-							float sqrMagnitude = ((Vector4)(mapColor - pixelBilinear)).sqrMagnitude;
-							if (sqrMagnitude < num)
-							{
-								bool testCase = true;
-								if (BiomeMap.nonExactThreshold != -1)
-								{
-									testCase = (sqrMagnitude < BiomeMap.nonExactThreshold);
-								}
-								if (testCase)
-								{
-									mapAttribute = BiomeMap.Attributes[j];
-									num = sqrMagnitude;
-								}
-							}
-						}
-					}
-				}
-				*/
-			}
-			catch (NullReferenceException)
-			{
-				mapAttribute = new CBAttributeMap.MapAttribute();
-				mapAttribute.name = "N/A";
-			}
-
-			return mapAttribute;
-		}
-
-		public static ExperimentSituations GetExperimentSituation(this Vessel vessel)
-		{
-			if (vessel == null)
-			{
-				return ExperimentSituations.SrfSplashed;
-			}
-
-			Vessel.Situations situation = vessel.situation;
-
-			switch (situation)
-			{
-				case Vessel.Situations.PRELAUNCH:
-				case Vessel.Situations.LANDED:
-					return ExperimentSituations.SrfLanded;
-				case Vessel.Situations.SPLASHED:
-					return ExperimentSituations.SrfSplashed;
-				case Vessel.Situations.FLYING:
-					if (vessel.altitude < (double)vessel.mainBody.scienceValues.flyingAltitudeThreshold)
-					{
-						return ExperimentSituations.FlyingLow;
-					}
-					else
-					{
-						return ExperimentSituations.FlyingHigh;
-					}
-			}
-
-			if (vessel.altitude < (double)vessel.mainBody.scienceValues.spaceAltitudeThreshold)
-			{
-				return ExperimentSituations.InSpaceLow;
-			}
-			else
-			{
-				return ExperimentSituations.InSpaceHigh;
-			}
-		}
-
-		public static string HumanString(this ExperimentSituations situation)
-		{
-			switch (situation)
-			{
-				case ExperimentSituations.FlyingHigh:
-					return "Upper Atmosphere";
-				case ExperimentSituations.FlyingLow:
-					return "Flying";
-				case ExperimentSituations.SrfLanded:
-					return "Surface";
-				case ExperimentSituations.InSpaceLow:
-					return "Near in Space";
-				case ExperimentSituations.InSpaceHigh:
-					return "High in Space";
-				case ExperimentSituations.SrfSplashed:
-					return "Splashed Down";
-				default:
-					return "Unknown";
-			}
-		}
-		#endregion
-
-		#region VESSEL_EXTENSIONS_LAT_LONG
-		public static string GetLongitudeString(this Vessel vessel, string format = "F4")
-		{
-			string dir_long = "W";
-			double v_long = vessel.longitude;
-
-			v_long = FixDegreeDomain(v_long);
-
-			if (v_long < -180d)
-			{
-				v_long += 360d;
-			}
-			if (v_long >= 180)
-			{
-				v_long -= 360d;
-			}
-
-			if (v_long > 0)
-				dir_long = "E";
-
-			return string.Format("{0}° {1}", Math.Abs(v_long).ToString(format), dir_long);
-		}
-
-		public static string GetLatitudeString(this Vessel vessel, string format = "F4")
-		{
-			string dir_lat = "S";
-			double v_lat = vessel.latitude;
-			if (v_lat > 0)
-				dir_lat = "N";
-
-			return string.Format("{0}° {1}", Math.Abs(v_lat).ToString(format), dir_lat);
-		}
-		#endregion
-
-		#region VESSEL_EXTENSIONS_GENERAL
-		public static double TrueAltitude(Vessel vessel)
-		{
-			double trueAltitude = vessel.orbit.altitude - vessel.terrainAltitude;
-
-			// HACK: This assumes that on worlds with oceans, all water is fixed at 0 m,
-			// and water covers the whole surface at 0 m.
-			if (vessel.terrainAltitude < 0 && vessel.mainBody.ocean)
-			{
-				trueAltitude = vessel.orbit.altitude;
-			}
-
-			return trueAltitude;
-		}
-
-		public static double Radius(this Vessel vessel)
-		{
-			double radius;
-
-			radius = vessel.altitude;
-
-			if (vessel.mainBody != null)
-			{
-				radius += vessel.mainBody.Radius;
-			}
-
-			return radius;
-		}
-		#endregion
-
-		#region GEOMETRY_UTILS
-		public static double FixAngleDomain(double Angle, bool Degrees = false)
-		{
-			double Extent = 2d * Math.PI;
-			if (Degrees)
-			{
-				Extent = 360d;
-			}
-
-			Angle = Angle % (Extent);
-			if (Angle < 0d)
-			{
-				Angle += Extent;
-			}
-
-			return Angle;
-		}
-
-		public static double FixDegreeDomain(double Angle)
-		{
-			return FixAngleDomain(Angle, true);
-		}
-		#endregion
-
-		private static Dictionary<int, GUI.WindowFunction> functionCache;
-		public static UnityEngine.GUI.WindowFunction GetWindowHandler(Action<int> func)
-		{
-			if (functionCache == null)
-			{
-				functionCache = new Dictionary<int, GUI.WindowFunction>();
-			}
-
-			int hashCode = func.GetHashCode();
-
-			if (!functionCache.ContainsKey(hashCode))
-			{
-				functionCache[hashCode] = delegate (int id)
-				{
-					try
-					{
-						func(id);
-					}
-					#if DEBUG
-					catch (ArgumentException ex)
-					#else
-					catch (ArgumentException)
-					#endif
-					{
-						Debug.LogWarning(
-							string.Format("[{0}]: ArgumentException caught during window call.  This is not a bug.",
-								func.Target.GetType().Name
-							));
-
-						/*#if DEBUG
-						Debug.LogException(ex);
-						#endif*/
-					}
-					catch (Exception ex)
-					{
-						Debug.LogError(
-							string.Format("[{0}]: {1} caught during window call.\nMessage:\n{2}\nStackTrace:\n{3}",
-								func.Target.GetType().Name,
-								ex.GetType().Name,
-								ex.Message,
-								ex.StackTrace
-							));
-					}
-				};
-			}
-
-			return functionCache[hashCode];
-		}
-
-		/// <summary>
-		/// Formats the interval given in seconds as a human-friendly
-		/// time period in [[[[years, ]days, ]hours, ]minutes, and ]seconds.
-		/// 
-		/// Uses sidereal days, since "6 hours per day" is the Kerbal standard.
-		/// </summary>
-		/// <returns>Human readable interval</returns>
-		/// <param name="seconds"></param>
-		public static string FormatInterval(double seconds)
-		{
-			return UnpackedTime.FromSeconds(seconds).FormatAsSpan();
-		}
-
-		/// <summary>
-		/// Formats the date given in seconds since epoch as a human-friendly
-		/// date in the format YY, DD, HH:MM:SS
-		/// </summary>
-		/// <returns>The date.</returns>
-		/// <param name="seconds">Seconds.</param>
-		public static string FormatDate(double seconds)
-		{
-			return UnpackedTime.FromSeconds(seconds).FormatAsDate();
-		}
-
-		public class UnpackedTime
-		{
-			public const double SecondsPerMinute = 60d;
-			public const double SecondsPerHour = 3600d;
-
-			public static double SecondsPerDay
-			{
-				get
-				{
-					if (GameSettings.KERBIN_TIME)
-					{
-						return 21600d;
-					}
-					else
-					{
-						return 86164.1d;
-					}
-				}
-			}
-
-			public static double SecondsPerYear
-			{
-				get
-				{
-					if (GameSettings.KERBIN_TIME)
-					{
-						return 9203545d;
-					}
-					else
-					{
-						return 31558149d;
-					}
-				}
-			}
-
-			public static UnpackedTime FromSeconds(double seconds)
-			{
-				UnpackedTime time = new UnpackedTime();
-
-				time.years = (int)(seconds / SecondsPerYear);
-
-				seconds %= SecondsPerYear;
-
-				time.days = (int)(seconds / SecondsPerDay);
-
-				seconds %= SecondsPerDay;
-
-				time.hours = (int)(seconds / SecondsPerHour);
-
-				seconds %= SecondsPerHour;
-
-				time.minutes = (int)(seconds / SecondsPerMinute);
-
-				seconds %= SecondsPerMinute;
-
-				time.seconds = seconds;
-
-				return time;
-			}
-
-			public static explicit operator UnpackedTime(double seconds)
-			{
-				return FromSeconds(seconds);
-			}
-
-			public static implicit operator double(UnpackedTime time)
-			{
-				return time.ToSeconds();
-			}
-
-			public static UnpackedTime operator+ (UnpackedTime lhs, UnpackedTime rhs)
-			{
-				return FromSeconds(lhs.ToSeconds() + rhs.ToSeconds());
-			}
-
-			public static UnpackedTime operator- (UnpackedTime lhs, UnpackedTime rhs)
-			{
-				return FromSeconds(lhs.ToSeconds() - rhs.ToSeconds());
-			}
-
-			public int years;
-			public int days;
-			public int hours;
-			public int minutes;
-			public double seconds;
-
-			public double ToSeconds()
-			{
-				return (double)years * SecondsPerYear +
-					(double)days * SecondsPerDay +
-					(double)hours * SecondsPerHour +
-					(double)minutes * SecondsPerMinute +
-					seconds;
-			}
-
-			public string FormatAsSpan()
-			{
-				string format_1 = "{0:D1}y {1:D1}d {2:D2}h {3:D2}m {4:00.0}s";
-				string format_2 = "{0:D1}d {1:D2}h {2:D2}m {3:00.0}s";
-				string format_3 = "{0:D2}h {1:D2}m {2:00.0}s";
-				string format_4 = "{0:D2}m {1:00.0}s";
-				string format_5 = "{0:00.0}s";
-
-				if (this.years > 0)
-				{
-					return string.Format(format_1, this.years, this.days, this.hours, this.minutes, this.seconds);
-				}
-				else if (this.days > 0)
-				{
-					return string.Format(format_2, this.days, this.hours, this.minutes, this.seconds);
-				}
-				else if (this.hours > 0)
-				{
-					return string.Format(format_3, this.hours, this.minutes, this.seconds);
-				}
-				else if (this.minutes > 0)
-				{
-					return string.Format(format_4, this.minutes, this.seconds);
-				}
-				else
-				{
-					return string.Format(format_5, this.seconds);
-				}
-			}
-
-			public string FormatAsDate()
-			{
-				string format = "Y{0:D1}, D{1:D1} {2:D2}:{3:D2}:{4:00.0}s";
-
-				return string.Format(format, years, days, hours, minutes, seconds);
-			}
-
-			public UnpackedTime(int years, int days, int hours, int minutes, double seconds)
-			{
-				this.years = years;
-				this.days = days;
-				this.hours = hours;
-				this.minutes = minutes;
-				this.seconds = seconds;
-			}
-
-			public UnpackedTime() : this(0, 0, 0, 0, 0d) {}
-		}
-
-		public static string UppercaseFirst(string s)
-		{
-			if (string.IsNullOrEmpty(s))
-			{
-				return string.Empty;
-			}
-			char[] a = s.ToCharArray();
-			a[0] = char.ToUpper(a[0]);
-			return new string(a);
-		}
-
-		//transfer angles
-		public static double Nivvy_CalcTransferPhaseAngle(double r_current, double r_target, double grav_param)
-		{
-			r_target /= 1000;
-			r_current /= 1000;
-			grav_param /= 1000000000;
-
-			double midpoint = (r_target + r_current) / 2;
-
-			double T_target = (2 * Math.PI) * Math.Sqrt((r_target * r_target * r_target) / grav_param);
-			double T_transfer = (2 * Math.PI) * Math.Sqrt((midpoint * midpoint * midpoint) / grav_param);
-			return 360 * (0.5 - (T_transfer / (2 * T_target)));
-		}
-
-		public static double Younata_DeltaVToGetToOtherBody(double mu, double r1, double r2)
-		{
-			/*			
-            def deltaVToGetToOtherBody(mu, r1, r2):
-            # mu = gravity param of common orbiting body of r1 and r2
-            # (e.g. for mun to minmus, mu is kerbin's gravity param
-            # r1 = initial body's orbit radius
-            # r2 = target body's orbit radius
-		
-            # return value is km/s
-            sur1 = math.sqrt(mu / r1)
-            sr1r2 = math.sqrt(float(2*r2)/float(r1+r2))
-            mult = sr1r2 - 1
-            return sur1 * mult
-            */
-			double sur1, sr1r2, mult;
-			sur1 = Math.Sqrt(mu / r1);
-			sr1r2 = Math.Sqrt((2 * r2) / (r1 + r2));
-			mult = sr1r2 - 1;
-			return sur1 * mult;
-		}
-
-		public static double Younata_DeltaVToExitSOI(double mu, double r1, double r2, double v)
-		{
-			/*			
-            def deltaVToExitSOI(mu, r1, r2, v):
-            # mu = gravity param of current body
-            # r1 = current orbit radius
-            # r2 = SOI radius
-            # v = SOI exit velocity
-            foo = r2 * (v**2) - 2 * mu
-            bar = r1 * foo + (2 * r2 * mu)
-            r = r1*r2
-            return math.sqrt(bar / r)
-            */
-			double foo = r2 * (v * v) - 2 * mu;
-			double bar = r1 * foo + (2 * r2 * mu);
-			double r = r1 * r2;
-			return Math.Sqrt(bar / r);
-		}
-
-		public static double Younata_TransferBurnPoint(double r, double v, double angle, double mu)
-		{
-			/*			
-            def transferBurnPoint(r, v, angle, mu):
-            # r = parking orbit radius
-            # v = ejection velocity
-            # angle = phase angle (from function phaseAngle())
-            # mu = gravity param of current body.
-            epsilon = ((v**2)/2) - (mu / r)
-            h = r * v * math.sin(angle)
-            e = math.sqrt(1 + ((2 * epsilon * h**2)/(mu**2)))
-            theta = math.acos(1.0 / e)
-            degrees = theta * (180.0 / math.pi)
-            return 180 - degrees
-            */
-			double epsilon, h, ee, theta, degrees;
-			epsilon = ((v * v) / 2) - (mu / r);
-			h = r * v * Math.Sin(angle);
-			ee = Math.Sqrt(1 + ((2 * epsilon * (h * h)) / (mu * mu)));
-			theta = Math.Acos(1.0 / ee);
-			degrees = theta * (180.0 / Math.PI);
-			return 180 - degrees;
-			// returns the ejection angle
-		}
-
-		public static double Adammada_CurrrentPhaseAngle(
-			double body_LAN,
-			double body_orbitPct,
-			double origin_LAN,
-			double origin_orbitPct
-		)
-		{
-			double angle = (body_LAN / 360 + body_orbitPct) - (origin_LAN / 360 + origin_orbitPct);
-			if (angle > 1)
-				angle = angle - 1;
-			if (angle < 0)
-				angle = angle + 1;
-			if (angle > 0.5)
-				angle = angle - 1;
-			angle = angle * 360;
-			return angle;
-		}
-
-		public static double Adammada_CurrentEjectionAngle(
-			double vessel_long,
-			double origin_rotAngle,
-			double origin_LAN,
-			double origin_orbitPct
-		)
-		{
-			//double eangle = ((FlightGlobals.ActiveVOID.vessel.longitude + orbiting.rotationAngle) - (orbiting.orbit.LAN / 360 + orbiting.orbit.orbitPercent) * 360);
-			double eangle = ((vessel_long + origin_rotAngle) - (origin_LAN / 360 + origin_orbitPct) * 360);
-
-			while (eangle < 0)
-				eangle = eangle + 360;
-			while (eangle > 360)
-				eangle = eangle - 360;
-			if (eangle < 270)
-				eangle = 90 - eangle;
-			else
-				eangle = 450 - eangle;
-			return eangle;
-		}
-
-		public static double mrenigma03_calcphase(Vessel vessel, CelestialBody target)   //calculates phase angle between the current body and target body
-		{
-			Vector3d vecthis = new Vector3d();
-			Vector3d vectarget = new Vector3d();
-			vectarget = target.orbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());
-
-			if ((vessel.mainBody.name == "Sun") || (vessel.mainBody.referenceBody.referenceBody.name == "Sun"))
-			{
-				vecthis = vessel.orbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());
-			}
-			else
-			{
-				vecthis = vessel.mainBody.orbit.getRelativePositionAtUT(Planetarium.GetUniversalTime());
-			}
-
-			vecthis = Vector3d.Project(new Vector3d(vecthis.x, 0, vecthis.z), vecthis);
-			vectarget = Vector3d.Project(new Vector3d(vectarget.x, 0, vectarget.z), vectarget);
-
-			Vector3d prograde = new Vector3d();
-			prograde = Quaternion.AngleAxis(90, Vector3d.forward) * vecthis;
-
-			double phase = Vector3d.Angle(vecthis, vectarget);
-
-			if (Vector3d.Angle(prograde, vectarget) > 90)
-				phase = 360 - phase;
-
-			return (phase + 360) % 360;
-		}
-
-		public static double adjustCurrPhaseAngle(double transfer_angle, double curr_phase)
-		{
-			if (transfer_angle < 0)
-			{
-				if (curr_phase > 0)
-					return (-1 * (360 - curr_phase));
-				else if (curr_phase < 0)
-					return curr_phase;
-			}
-			else if (transfer_angle > 0)
-			{
-				if (curr_phase > 0)
-					return curr_phase;
-				else if (curr_phase < 0)
-					return (360 + curr_phase);
-			}
-			return curr_phase;
-		}
-
-		public static double adjust_current_ejection_angle(double curr_ejection)
-		{
-			//curr_ejection WILL need to be adjusted once for all transfers as it returns values ranging -180 to 180
-			// need 0-360 instead
-			//
-			// ie i have -17 in the screenshot
-			// need it to show 343
-			//
-			// do this
-			//
-			// if < 0, add curr to 360  // 360 + (-17) = 343
-			// else its good as it is
-
-			if (curr_ejection < 0)
-				return 360 + curr_ejection;
-			else
-				return curr_ejection;
-
-		}
-
-		public static double adjust_transfer_ejection_angle(double trans_ejection, double trans_phase)
-		{
-			// if transfer_phase_angle < 0 its a lower transfer
-			//180 + curr_ejection
-			// else if transfer_phase_angle > 0 its good as it is
-
-			if (trans_phase < 0)
-				return 180 + trans_ejection;
-			else
-				return trans_ejection;
-
-		}
-
-		public static void display_transfer_angles_SUN2PLANET(CelestialBody body, Vessel vessel)
-		{
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Phase angle (curr/trans):");
-			GUILayout.Label(
-				VOID_Tools.mrenigma03_calcphase(vessel, body).ToString("F3") + "° / " + VOID_Tools.Nivvy_CalcTransferPhaseAngle(
-					vessel.orbit.semiMajorAxis,
-					body.orbit.semiMajorAxis,
-					vessel.mainBody.gravParameter
-				).ToString("F3") + "°",
-				GUILayout.ExpandWidth(false)
-			);
-			GUILayout.EndHorizontal();
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Transfer velocity:");
-			GUILayout.Label(
-				(VOID_Tools.Younata_DeltaVToGetToOtherBody(
-					(vessel.mainBody.gravParameter / 1000000000),
-					(vessel.orbit.semiMajorAxis / 1000),
-					(body.orbit.semiMajorAxis / 1000)
-				) * 1000).ToString("F2") + "m/s",
-				GUILayout.ExpandWidth(false)
-			);
-			GUILayout.EndHorizontal();
-		}
-
-		public static void display_transfer_angles_PLANET2PLANET(CelestialBody body, Vessel vessel)
-		{
-			double dv1 = VOID_Tools.Younata_DeltaVToGetToOtherBody(
-				(vessel.mainBody.referenceBody.gravParameter / 1000000000),
-				(vessel.mainBody.orbit.semiMajorAxis / 1000),
-				(body.orbit.semiMajorAxis / 1000)
-			);
-			double dv2 = VOID_Tools.Younata_DeltaVToExitSOI(
-				(vessel.mainBody.gravParameter / 1000000000),
-				(vessel.orbit.semiMajorAxis / 1000),
-				(vessel.mainBody.sphereOfInfluence / 1000),
-				Math.Abs(dv1)
-			);
-
-			double trans_ejection_angle = VOID_Tools.Younata_TransferBurnPoint(
-				(vessel.orbit.semiMajorAxis / 1000),
-				dv2,
-				(Math.PI / 2.0),
-				(vessel.mainBody.gravParameter / 1000000000)
-			);
-			double curr_ejection_angle = VOID_Tools.Adammada_CurrentEjectionAngle(
-				FlightGlobals.ActiveVessel.longitude,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.rotationAngle,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.LAN,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.orbitPercent
-			);
-
-			double trans_phase_angle = VOID_Tools.Nivvy_CalcTransferPhaseAngle(
-				vessel.mainBody.orbit.semiMajorAxis,
-				body.orbit.semiMajorAxis,
-				vessel.mainBody.referenceBody.gravParameter
-			) % 360;
-			double curr_phase_angle = VOID_Tools.Adammada_CurrrentPhaseAngle(
-				body.orbit.LAN,
-				body.orbit.orbitPercent,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.LAN,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.orbitPercent
-			);
-
-			double adj_phase_angle = VOID_Tools.adjustCurrPhaseAngle(trans_phase_angle, curr_phase_angle);
-			double adj_trans_ejection_angle = VOID_Tools.adjust_transfer_ejection_angle(trans_ejection_angle, trans_phase_angle);
-			double adj_curr_ejection_angle = VOID_Tools.adjust_current_ejection_angle(curr_ejection_angle);
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Phase angle (curr/trans):");
-			GUILayout.Label(
-				adj_phase_angle.ToString("F3") + "° / " + trans_phase_angle.ToString("F3") + "°",
-				GUILayout.ExpandWidth(false)
-			);
-			GUILayout.EndHorizontal();
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Ejection angle (curr/trans):");
-			GUILayout.Label(
-				adj_curr_ejection_angle.ToString("F3") + "° / " + adj_trans_ejection_angle.ToString("F3") + "°",
-				GUILayout.ExpandWidth(false)
-			);
-			GUILayout.EndHorizontal();
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Transfer velocity:");
-			GUILayout.Label((dv2 * 1000).ToString("F2") + "m/s", GUILayout.ExpandWidth(false));
-			GUILayout.EndHorizontal();
-		}
-
-		public static void display_transfer_angles_PLANET2MOON(CelestialBody body, Vessel vessel)
-		{
-			double dv1 = VOID_Tools.Younata_DeltaVToGetToOtherBody(
-				(vessel.mainBody.gravParameter / 1000000000),
-				(vessel.orbit.semiMajorAxis / 1000),
-				(body.orbit.semiMajorAxis / 1000)
-			);
-
-			double trans_phase_angle = VOID_Tools.Nivvy_CalcTransferPhaseAngle(
-				vessel.orbit.semiMajorAxis,
-				body.orbit.semiMajorAxis,
-				vessel.mainBody.gravParameter
-			);
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Phase angle (curr/trans):");
-			GUILayout.Label(
-				VOID_Tools.mrenigma03_calcphase(vessel, body).ToString("F3") + "° / " + trans_phase_angle.ToString("F3") + "°",
-				GUILayout.ExpandWidth(false)
-			);
-			GUILayout.EndHorizontal();
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Transfer velocity:");
-			GUILayout.Label((dv1 * 1000).ToString("F2") + "m/s", GUILayout.ExpandWidth(false));
-			GUILayout.EndHorizontal();
-		}
-
-		public static void display_transfer_angles_MOON2MOON(CelestialBody body, Vessel vessel)
-		{
-			double dv1 = VOID_Tools.Younata_DeltaVToGetToOtherBody(
-				(vessel.mainBody.referenceBody.gravParameter / 1000000000),
-				(vessel.mainBody.orbit.semiMajorAxis / 1000),
-				(body.orbit.semiMajorAxis / 1000)
-			);
-			double dv2 = VOID_Tools.Younata_DeltaVToExitSOI(
-				(vessel.mainBody.gravParameter / 1000000000),
-				(vessel.orbit.semiMajorAxis / 1000),
-				(vessel.mainBody.sphereOfInfluence / 1000),
-				Math.Abs(dv1)
-			);
-			double trans_ejection_angle = VOID_Tools.Younata_TransferBurnPoint(
-				(vessel.orbit.semiMajorAxis / 1000),
-				dv2,
-				(Math.PI / 2.0),
-				(vessel.mainBody.gravParameter / 1000000000)
-			);
-
-			double curr_phase_angle = VOID_Tools.Adammada_CurrrentPhaseAngle(
-				body.orbit.LAN,
-				body.orbit.orbitPercent,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.LAN,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.orbitPercent
-			);
-			double curr_ejection_angle = VOID_Tools.Adammada_CurrentEjectionAngle(
-				FlightGlobals.ActiveVessel.longitude,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.rotationAngle,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.LAN,
-				FlightGlobals.ActiveVessel.orbit.referenceBody.orbit.orbitPercent
-			);
-
-			double trans_phase_angle = VOID_Tools.Nivvy_CalcTransferPhaseAngle(
-				vessel.mainBody.orbit.semiMajorAxis,
-				body.orbit.semiMajorAxis,
-				vessel.mainBody.referenceBody.gravParameter
-			) % 360;
-
-			double adj_phase_angle = VOID_Tools.adjustCurrPhaseAngle(trans_phase_angle, curr_phase_angle);
-			//double adj_ejection_angle = adjustCurrEjectionAngle(trans_phase_angle, curr_ejection_angle);
-
-			//new stuff
-			//
-			double adj_trans_ejection_angle = VOID_Tools.adjust_transfer_ejection_angle(trans_ejection_angle, trans_phase_angle);
-			double adj_curr_ejection_angle = VOID_Tools.adjust_current_ejection_angle(curr_ejection_angle);
-			//
-			//
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Phase angle (curr/trans):");
-			GUILayout.Label(
-				adj_phase_angle.ToString("F3") + "° / " + trans_phase_angle.ToString("F3") + "°",
-				GUILayout.ExpandWidth(false)
-			);
-			GUILayout.EndHorizontal();
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Ejection angle (curr/trans):");
-			GUILayout.Label(
-				adj_curr_ejection_angle.ToString("F3") + "° / " + adj_trans_ejection_angle.ToString("F3") + "°",
-				GUILayout.ExpandWidth(false)
-			);
-			GUILayout.EndHorizontal();
-
-			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
-			GUILayout.Label("Transfer velocity:");
-			GUILayout.Label((dv2 * 1000).ToString("F2") + "m/s", GUILayout.ExpandWidth(false));
-			GUILayout.EndHorizontal();
-		}
-
-		public static string get_heading_text(double heading)
-		{
-			if (heading > 348.75 || heading <= 11.25)
-				return "N";
-			else if (heading > 11.25 && heading <= 33.75)
-				return "NNE";
-			else if (heading > 33.75 && heading <= 56.25)
-				return "NE";
-			else if (heading > 56.25 && heading <= 78.75)
-				return "ENE";
-			else if (heading > 78.75 && heading <= 101.25)
-				return "E";
-			else if (heading > 101.25 && heading <= 123.75)
-				return "ESE";
-			else if (heading > 123.75 && heading <= 146.25)
-				return "SE";
-			else if (heading > 146.25 && heading <= 168.75)
-				return "SSE";
-			else if (heading > 168.75 && heading <= 191.25)
-				return "S";
-			else if (heading > 191.25 && heading <= 213.75)
-				return "SSW";
-			else if (heading > 213.75 && heading <= 236.25)
-				return "SW";
-			else if (heading > 236.25 && heading <= 258.75)
-				return "WSW";
-			else if (heading > 258.75 && heading <= 281.25)
-				return "W";
-			else if (heading > 281.25 && heading <= 303.75)
-				return "WNW";
-			else if (heading > 303.75 && heading <= 326.25)
-				return "NW";
-			else if (heading > 326.25 && heading <= 348.75)
-				return "NNW";
-			else
-				return "";
-		}
-	}
-
-	public class CBListComparer : IComparer<CelestialBody>
-	{
-		public int Compare(CelestialBody bodyA, CelestialBody bodyB)
-		{
-			Tools.PostDebugMessage(this, "got bodyA: {0} & bodyB: {1}", bodyA, bodyB);
-
-			if (bodyA == null && bodyB == null)
-			{
-				Tools.PostDebugMessage(this, "both bodies are null, returning 0");
-				return 0;
-			}
-			if (bodyA == null)
-			{
-				Tools.PostDebugMessage(this, "bodyA is null, returning -1");
-				return -1;
-			}
-			if (bodyB == null)
-			{
-				Tools.PostDebugMessage(this, "bodyB is null, returning 1");
-				return 1;
-			}
-
-			Tools.PostDebugMessage(this, "bodies are not null, carrying on");
-
-			if (object.ReferenceEquals(bodyA, bodyB))
-			{
-				Tools.PostDebugMessage(this, "bodies are equal, returning 0");
-				return 0;
-			}
-
-			Tools.PostDebugMessage(this, "bodies are not equal, carrying on");
-
-			if (bodyA.orbitDriver == null)
-			{
-				Tools.PostDebugMessage(this, "bodyA.orbit is null (bodyA is the sun, returning 1");
-				return 1;
-			}
-			if (bodyB.orbitDriver == null)
-			{
-				Tools.PostDebugMessage(this, "bodyB.orbit is null (bodyB is the sun, returning -1");
-				return -1;
-			}
-
-			Tools.PostDebugMessage(this, "orbits are not null, carrying on");
-
-			if (bodyA.orbit.referenceBody == bodyB.orbit.referenceBody)
-			{
-				Tools.PostDebugMessage(this, "bodies share a parent, comparing SMAs");
-				return -bodyA.orbit.semiMajorAxis.CompareTo(bodyB.orbit.semiMajorAxis);
-			}
-
-			Tools.PostDebugMessage(this, "orbits do not share a parent, carrying on");
-
-			if (bodyA.hasAncestor(bodyB))
-			{
-				Tools.PostDebugMessage(this, "bodyA is a moon or sub-moon of bodyB, returning -1");
-				return -1;
-			}
-			if (bodyB.hasAncestor(bodyA))
-			{
-				Tools.PostDebugMessage(this, "bodyA is a moon or sub-moon of bodyB, returning 1");
-				return 1;
-			}
-
-			Tools.PostDebugMessage(this, "bodies do not have an obvious relationship, searching for one");
-
-			if (VOID_Tools.NearestRelatedParents(ref bodyA, ref bodyB))
-			{
-				Tools.PostDebugMessage(this, "good relation {0} and {1}, comparing", bodyA.bodyName, bodyB.bodyName);
-				return this.Compare(bodyA, bodyB);
-			}
-
-			Tools.PostDebugMessage(this, "bad relation {0} and {1}, giving up", bodyA.bodyName, bodyB.bodyName);
-
-			return 0;
-		}
-	}
-}
-

--- a/VOID_Transfer.cs
+++ b/VOID_Transfer.cs
@@ -41,7 +41,7 @@
 
 		public VOID_Transfer() : base()
 		{
-			this._Name = "Transfer Angle Information";
+			this.Name = "Transfer Angle Information";
 
 			this.WindowPos.x = 475;
 			this.WindowPos.y = 85;

--- a/VOID_VesselInfo.cs
+++ b/VOID_VesselInfo.cs
@@ -26,8 +26,8 @@
 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-using Engineer.VesselSimulator;
-using Engineer.Extensions;
+using KerbalEngineer.VesselSimulator;
+using KerbalEngineer.Extensions;
 using KSP;
 using System;
 using System.Collections.Generic;
@@ -40,7 +40,7 @@
 	{
 		public VOID_VesselInfo() : base()
 		{
-			this._Name = "Vessel Information";
+			this.Name = "Vessel Information";
 
 			this.WindowPos.x = Screen.width - 260;
 			this.WindowPos.y = 450;

--- a/VOID_VesselRegister.cs
+++ b/VOID_VesselRegister.cs
@@ -60,7 +60,7 @@
 
 		public VOID_VesselRegister() : base()
 		{
-			this._Name = "Vessel Register";
+			this.Name = "Vessel Register";
 
 			this.WindowPos.x = 845;
 			this.WindowPos.y = 275;