First-pass reintegration of VesselSimulator and VesselInfo.
First-pass reintegration of VesselSimulator and VesselInfo.

--- a/IVOID_Module.cs
+++ b/IVOID_Module.cs
@@ -36,7 +36,7 @@
 
 		void LoadConfig();
 
-		void SaveConfig();
+		void _SaveToConfig(KSP.IO.PluginConfiguration config);
 	}
 }
 

--- a/VOIDFlightMaster.cs
+++ b/VOIDFlightMaster.cs
@@ -60,6 +60,12 @@
 			}
 
 			this.Core.Update ();
+
+			if (this.Core.vessel != null)
+			{
+				SimManager.Instance.Gravity = VOID_Core.Instance.vessel.mainBody.gravParameter / Math.Pow(VOID_Core.Instance.vessel.mainBody.Radius, 2);
+				SimManager.Instance.TryStartSimulation();
+			}
 		}
 
 		public void FixedUpdate()

--- a/VOID_Core.cs
+++ b/VOID_Core.cs
@@ -69,8 +69,6 @@
 		protected List<IVOID_Module> _modules = new List<IVOID_Module>();
 		protected bool _modulesLoaded = false;
 
-		protected List<Callback> _configurableCallbacks = new List<Callback>();
-
 		[AVOID_SaveValue("mainWindowPos")]
 		protected VOID_SaveValue<Rect> mainWindowPos = new Rect(Screen.width / 2, Screen.height / 2, 10f, 10f);
 
@@ -255,7 +253,7 @@
 				}
 			}
 
-			if (this.saveTimer > 15f)
+			if (this.saveTimer > 2f)
 			{
 				this.SaveConfig ();
 				this.saveTimer = 0;
@@ -423,19 +421,24 @@
 			}
 		}
 
-		public override void SaveConfig()
+		public void SaveConfig()
 		{
 			if (!this.configDirty)
 			{
 				return;
 			}
 
-			base.SaveConfig ();
+			var config = KSP.IO.PluginConfiguration.CreateForType<VOID_Core> ();
+			config.load ();
+
+			this._SaveToConfig(config);
 
 			foreach (IVOID_Module module in this.Modules)
 			{
-				module.SaveConfig ();
-			}
+				module._SaveToConfig (config);
+			}
+
+			config.save();
 
 			this.configDirty = false;
 		}

--- a/VOID_HUD.cs
+++ b/VOID_HUD.cs
@@ -32,14 +32,14 @@
 		/*
 		 * Fields
 		 * */
-		protected new bool _hasConfigurables = true;
-
 		[AVOID_SaveValue("colorIndex")]
 		protected VOID_SaveValue<int> _colorIndex = 0;
 
 		protected List<Color> textColors = new List<Color>();
 
 		protected GUIStyle labelStyle;
+
+		protected Vessel vessel = null;
 
 		/*
 		 * Properties
@@ -68,7 +68,6 @@
 		public VOID_HUD() : base()
 		{
 			this._Name = "Heads-Up Display";
-
 			this.textColors.Add(Color.green);
 			this.textColors.Add(Color.black);
 			this.textColors.Add(Color.white);
@@ -87,13 +86,16 @@
 
 		public override void DrawGUI()
 		{
-			Tools.PostDebugMessage ("VOID_HUD: Drawing GUI.");
-
 			GUI.skin = VOID_Core.Instance.Skin;
 
-			if (this.vessel == null)
+			if (vessel == null)
 			{
-				return;
+				vessel = FlightGlobals.ActiveVessel;
+			}
+
+			if (vessel != FlightGlobals.ActiveVessel)
+			{
+				vessel = FlightGlobals.ActiveVessel;
 			}
 
 			if (VOID_Core.Instance.powerAvailable)
@@ -102,26 +104,26 @@
 
 				GUI.Label (
 					new Rect ((Screen.width * .2083f), 0, 300f, 70f),
-					"Obt Alt: " + Tools.MuMech_ToSI (this.vessel.orbit.altitude) + "m" +
-					" Obt Vel: " + Tools.MuMech_ToSI (this.vessel.orbit.vel.magnitude) + "m/s" +
-					"\nAp: " + Tools.MuMech_ToSI (this.vessel.orbit.ApA) + "m" +
-					" ETA " + Tools.ConvertInterval (this.vessel.orbit.timeToAp) +
-					"\nPe: " + Tools.MuMech_ToSI (this.vessel.orbit.PeA) + "m" +
-					" ETA " + Tools.ConvertInterval (this.vessel.orbit.timeToPe) +
-					"\nInc: " + this.vessel.orbit.inclination.ToString ("F3") + "°",
+					"Obt Alt: " + Tools.MuMech_ToSI (vessel.orbit.altitude) + "m" +
+					" Obt Vel: " + Tools.MuMech_ToSI (vessel.orbit.vel.magnitude) + "m/s" +
+					"\nAp: " + Tools.MuMech_ToSI (vessel.orbit.ApA) + "m" +
+					" ETA " + Tools.ConvertInterval (vessel.orbit.timeToAp) +
+					"\nPe: " + Tools.MuMech_ToSI (vessel.orbit.PeA) + "m" +
+					" ETA " + Tools.ConvertInterval (vessel.orbit.timeToPe) +
+					"\nInc: " + vessel.orbit.inclination.ToString ("F3") + "°",
 					labelStyle);
 				// Toadicus edit: Added "Biome: " line to surf/atmo HUD
 				GUI.Label (
 					new Rect ((Screen.width * .625f), 0, 300f, 90f),
-					"Srf Alt: " + Tools.MuMech_ToSI (Tools.TrueAltitude (this.vessel)) + "m" +
-					" Srf Vel: " + Tools.MuMech_ToSI (this.vessel.srf_velocity.magnitude) + "m/s" +
-					"\nVer: " + Tools.MuMech_ToSI (this.vessel.verticalSpeed) + "m/s" +
-					" Hor: " + Tools.MuMech_ToSI (this.vessel.horizontalSrfSpeed) + "m/s" +
-					"\nLat: " + Tools.GetLatitudeString (this.vessel, "F3") +
-					" Lon: " + Tools.GetLongitudeString (this.vessel, "F3") +
-					"\nHdg: " + Tools.MuMech_get_heading (this.vessel).ToString ("F2") + "° " +
-					Tools.get_heading_text (Tools.MuMech_get_heading (this.vessel)) +
-					"\nBiome: " + Tools.Toadicus_GetAtt (this.vessel).name,
+					"Srf Alt: " + Tools.MuMech_ToSI (Tools.TrueAltitude (vessel)) + "m" +
+					" Srf Vel: " + Tools.MuMech_ToSI (vessel.srf_velocity.magnitude) + "m/s" +
+					"\nVer: " + Tools.MuMech_ToSI (vessel.verticalSpeed) + "m/s" +
+					" Hor: " + Tools.MuMech_ToSI (vessel.horizontalSrfSpeed) + "m/s" +
+					"\nLat: " + Tools.GetLatitudeString (vessel, "F3") +
+					" Lon: " + Tools.GetLongitudeString (vessel, "F3") +
+					"\nHdg: " + Tools.MuMech_get_heading (vessel).ToString ("F2") + "° " +
+					Tools.get_heading_text (Tools.MuMech_get_heading (vessel)) +
+					"\nBiome: " + Tools.Toadicus_GetAtt (vessel).name,
 					labelStyle);
 			}
 			else

--- a/VOID_Module.cs
+++ b/VOID_Module.cs
@@ -147,26 +147,18 @@
 			}
 		}
 
-		public virtual void SaveConfig()
+		public virtual void _SaveToConfig(KSP.IO.PluginConfiguration config)
 		{
-			if (!VOID_Core.Instance.configDirty)
-			{
-				return;
-			}
-
-			var config = KSP.IO.PluginConfiguration.CreateForType<VOID_Core> ();
-			config.load ();
-
 			foreach (var field in this.GetType().GetFields(
+				BindingFlags.Instance |
 				BindingFlags.NonPublic |
-				BindingFlags.Public |
-				BindingFlags.Instance
+				BindingFlags.Public
 				))
 			{
 				object[] attrs = field.GetCustomAttributes(typeof(AVOID_SaveValue), false);
 
 				if (attrs.Length == 0) {
-					return;
+					continue;
 				}
 
 				AVOID_SaveValue attr = attrs.FirstOrDefault () as AVOID_SaveValue;
@@ -184,8 +176,6 @@
 
 				Tools.PostDebugMessage(string.Format("{0}: Saved field {1}.", this.GetType().Name, fieldName));
 			}
-
-			config.save ();
 		}
 	}
 }

--- a/VOID_Orbital.cs
+++ b/VOID_Orbital.cs
@@ -26,18 +26,18 @@
 {
 	public class VOID_Orbital : VOID_Module
 	{
-		[AVOID_SaveValue("OrbitalWindowPos")]
-		protected Rect OrbitalWindowPos = new Rect(Screen.width / 2, Screen.height / 2, 10f, 10f);
+		[AVOID_SaveValue("WindowPos")]
+		protected Rect WindowPos = new Rect(Screen.width / 2, Screen.height / 2, 10f, 10f);
 
-		[AVOID_SaveValue("toggleExtendedOribtal")]
-		protected bool toggleExtendedOribtal = false;
+		[AVOID_SaveValue("toggleExtended")]
+		protected VOID_SaveValue<bool> toggleExtended = false;
 
 		public VOID_Orbital()
 		{
 			this._Name = "Orbital Information";
 		}
 
-		public void OrbitalWindow(int _)
+		public void ModuleWindow(int _)
 		{
 			// Toadicus edit: added local sidereal longitude.
 			double LSL = vessel.longitude + vessel.orbit.referenceBody.rotationAngle;
@@ -92,9 +92,9 @@
 			GUILayout.Label(Tools.MuMech_ToSI(g_vessel) + "m/s²", GUILayout.ExpandWidth(false));
             GUILayout.EndHorizontal();
 
-            toggleExtendedOribtal = GUILayout.Toggle(toggleExtendedOribtal, "Extended info");
+			this.toggleExtended = GUILayout.Toggle(this.toggleExtended, "Extended info");
 
-            if (toggleExtendedOribtal)
+			if (this.toggleExtended)
             {
                 GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
                 GUILayout.Label("Period:");
@@ -151,12 +151,20 @@
 
 		public override void DrawGUI()
 		{
-			OrbitalWindowPos = GUILayout.Window(
+			Rect _Pos = this.WindowPos;
+
+			_Pos = GUILayout.Window(
 				VOID_Core.Instance.windowID,
-				OrbitalWindowPos,
-				this.OrbitalWindow,
+				_Pos,
+				this.ModuleWindow,
 				this.Name, GUILayout.Width(250),
 				GUILayout.Height(50));
+
+			if (_Pos != this.WindowPos)
+			{
+				this.WindowPos = _Pos;
+				VOID_Core.Instance.configDirty = true;
+			}
 		}
 	}
 }

file:b/VOID_SurfAtmo.cs (new)
--- /dev/null
+++ b/VOID_SurfAtmo.cs
@@ -1,1 +1,132 @@
+//
+//  VOID_Orbital.cs
+//
+//  Author:
+//       toadicus <>
+//
+//  Copyright (c) 2013 toadicus
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+using KSP;
+using System;
+using UnityEngine;
 
+namespace VOID
+{
+	public class VOID_SurfAtmo : VOID_Module
+	{
+		[AVOID_SaveValue("WindowPos")]
+		protected Rect WindowPos = new Rect(Screen.width / 2, Screen.height / 2, 10f, 10f);
+
+		public VOID_SurfAtmo()
+		{
+			this._Name = "Surface & Atmospheric Information";
+		}
+
+		public void ModuleWindow(int _)
+		{
+			GUILayout.BeginVertical();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Altitude (true):");
+			double alt_true = 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 ) alt_true = vessel.orbit.altitude;
+			GUILayout.Label(Tools.MuMech_ToSI(alt_true) + "m", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal ();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Latitude:");
+			GUILayout.Label(Tools.GetLatitudeString(vessel), GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Longitude:");
+			GUILayout.Label(Tools.GetLongitudeString(vessel), GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Heading:");
+			GUILayout.Label(Tools.MuMech_get_heading(vessel).ToString("F2") + "° " + Tools.get_heading_text(Tools.MuMech_get_heading(vessel)), GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Terrain elevation:");
+			GUILayout.Label(Tools.MuMech_ToSI(vessel.terrainAltitude) + "m", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Surface velocity:");
+			GUILayout.Label(Tools.MuMech_ToSI(vessel.srf_velocity.magnitude) + "m/s", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Vertical speed:");
+			GUILayout.Label(Tools.MuMech_ToSI(vessel.verticalSpeed) + "m/s", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Horizontal speed:");
+			GUILayout.Label(Tools.MuMech_ToSI(vessel.horizontalSrfSpeed) + "m/s", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Temperature:");
+			GUILayout.Label(vessel.flightIntegrator.getExternalTemperature().ToString("F2") + "° C", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Atmosphere density:");
+			GUILayout.Label(Tools.MuMech_ToSI(vessel.atmDensity * 1000) + "g/m³", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Pressure:");
+			GUILayout.Label(vessel.staticPressure.ToString("F2") + " atms", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Atmosphere limit:");
+			GUILayout.Label("≈ " + Tools.MuMech_ToSI(vessel.mainBody.maxAtmosphereAltitude) + "m", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			// Toadicus edit: added Biome
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Biome:");
+			GUILayout.Label(Tools.Toadicus_GetAtt(vessel).name, VOID_Core.Instance.LabelStyles["txt_right"]);
+			GUILayout.EndHorizontal();
+
+			GUILayout.EndVertical();
+			GUI.DragWindow();
+		}
+
+		public override void DrawGUI()
+		{
+			Rect _Pos = this.WindowPos;
+
+			_Pos = GUILayout.Window(
+				VOID_Core.Instance.windowID,
+				_Pos,
+				this.ModuleWindow,
+				this.Name, GUILayout.Width(250),
+				GUILayout.Height(50));
+
+			if (_Pos != this.WindowPos)
+			{
+				this.WindowPos = _Pos;
+				VOID_Core.Instance.configDirty = true;
+			}
+		}
+	}
+}

--- /dev/null
+++ b/VOID_VesselInfo.cs
@@ -1,1 +1,163 @@
+//
+//  VOID_Orbital.cs
+//
+//  Author:
+//       toadicus <>
+//
+//  Copyright (c) 2013 toadicus
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+using KSP;
+using System;
+using UnityEngine;
 
+namespace VOID
+{
+	public class VOID_VesselInfo : VOID_Module
+	{
+		[AVOID_SaveValue("WindowPos")]
+		protected Rect WindowPos = new Rect(Screen.width / 2, Screen.height / 2, 10f, 10f);
+
+		[AVOID_SaveValue("toggleExtended")]
+		protected VOID_SaveValue<bool> toggleExtended = false;
+
+		public VOID_VesselInfo()
+		{
+			this._Name = "Vessel Information";
+		}
+
+		public void ModuleWindow(int _)
+		{
+			if ((TimeWarp.WarpMode == TimeWarp.Modes.LOW) || (TimeWarp.CurrentRate <= TimeWarp.MaxPhysicsRate))
+			{
+				Engineer.VesselSimulator.SimManager.Instance.RequestSimulation();
+			}
+
+			Engineer.VesselSimulator.Stage[] stages = Engineer.VesselSimulator.SimManager.Instance.Stages;
+
+			GUILayout.BeginVertical();
+
+			GUILayout.Label(vessel.vesselName, VOID_Core.Instance.LabelStyles["center_bold"], GUILayout.ExpandWidth(true));
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("G-force:");
+			GUILayout.Label(vessel.geeForce.ToString("F2") + " gees", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			int num_parts = 0;
+			double total_mass = vessel.GetTotalMass();
+			double resource_mass = 0;
+			double max_thrust = 0;
+			double final_thrust = 0;
+
+			foreach (Part p in vessel.parts)
+			{
+			    num_parts++;
+			    resource_mass += p.GetResourceMass();
+
+			    foreach (PartModule pm in p.Modules)
+			    {
+			        if ((pm.moduleName == "ModuleEngines") && ((p.State == PartStates.ACTIVE) || ((Staging.CurrentStage > Staging.lastStage) && (p.inverseStage == Staging.lastStage))))
+			        {
+			            max_thrust += ((ModuleEngines)pm).maxThrust;
+			            final_thrust += ((ModuleEngines)pm).finalThrust;
+			        }
+			    }
+			}
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Parts:");
+			GUILayout.Label(num_parts.ToString("F0"), GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Total mass:");
+			GUILayout.Label(total_mass.ToString("F1") + " tons", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Resource mass:");
+			GUILayout.Label(resource_mass.ToString("F1") + " tons", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			if (stages.Length > Staging.lastStage)
+			{
+				GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+				GUILayout.Label("DeltaV (Current Stage):");
+				GUILayout.Label(Tools.MuMech_ToSI(stages[Staging.lastStage].deltaV).ToString() + "m/s", GUILayout.ExpandWidth(false));
+				GUILayout.EndHorizontal();
+			}
+
+			if (stages.Length > 0)
+			{
+				double totalDeltaV = 0d;
+
+				for (int i = 0; i < stages.Length; ++i)
+				{
+					totalDeltaV += stages [i].deltaV;
+				}
+
+				GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+				GUILayout.Label("DeltaV (Total):");
+				GUILayout.Label(Tools.MuMech_ToSI(totalDeltaV).ToString() + "m/s", GUILayout.ExpandWidth(false));
+				GUILayout.EndHorizontal();
+			}
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Throttle:");
+			GUILayout.Label((vessel.ctrlState.mainThrottle * 100f).ToString("F0") + "%", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Thrust (curr/max):");
+			GUILayout.Label(final_thrust.ToString("F1") + " / " + max_thrust.ToString("F1") + " kN", GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			double gravity = vessel.mainBody.gravParameter / Math.Pow(vessel.mainBody.Radius + vessel.altitude, 2);
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("T:W (curr/max):");
+			GUILayout.Label((final_thrust / (total_mass * gravity)).ToString("F2") + " / " + (max_thrust / (total_mass * gravity)).ToString("F2"), GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			double g_ASL = (VOID_Core.Constant_G * vessel.mainBody.Mass) / Math.Pow(vessel.mainBody.Radius, 2);
+			GUILayout.BeginHorizontal(GUILayout.ExpandWidth(true));
+			GUILayout.Label("Max T:W @ surface:");
+			GUILayout.Label((max_thrust / (total_mass * g_ASL)).ToString("F2"), GUILayout.ExpandWidth(false));
+			GUILayout.EndHorizontal();
+
+			GUILayout.EndVertical();
+			GUI.DragWindow();
+		}
+
+		public override void DrawGUI()
+		{
+			Rect _Pos = this.WindowPos;
+
+			_Pos = GUILayout.Window(
+				VOID_Core.Instance.windowID,
+				_Pos,
+				this.ModuleWindow,
+				this.Name, GUILayout.Width(250),
+				GUILayout.Height(50));
+
+			if (_Pos != this.WindowPos)
+			{
+				this.WindowPos = _Pos;
+				VOID_Core.Instance.configDirty = true;
+			}
+		}
+	}
+}
+
+