Added new single nested 'for' loop to the core, which can be subscribed to by modules and data members.
Added new single nested 'for' loop to the core, which can be subscribed to by modules and data members.

--- a/API/VOIDCore.cs
+++ b/API/VOIDCore.cs
@@ -40,6 +40,25 @@
 		public const double Constant_G = 6.674e-11;
 		public const int CONFIG_VERSION = 2;
 
+		public static event VOIDEventHandler onModulesLoaded;
+		public static event VOIDEventHandler onModulesDestroyed;
+
+		protected static void FireOnModulesLoaded(object sender)
+		{
+			if (onModulesLoaded != null)
+			{
+				onModulesLoaded(sender);
+			}
+		}
+
+		protected static void FireOnModulesDestroyed(object sender)
+		{
+			if (onModulesDestroyed != null)
+			{
+				onModulesDestroyed(sender);
+			}
+		}
+
 		public abstract int ConfigVersion { get; }
 		public virtual bool configNeedsUpdate { get; set; }
 
@@ -73,6 +92,12 @@
 		public abstract event VOIDEventHandler onApplicationQuit;
 		public abstract event VOIDEventHandler onSkinChanged;
 		public abstract event VOIDEventHandler onUpdate;
+
+		public abstract event VOIDEventHandler onPreForEach;
+		public abstract event VOIDForEachPartHandler onForEachPart;
+		public abstract event VOIDForEachPartModuleHandler onForEachModule;
+		public abstract event VOIDEventHandler onPostForEach;
+
 		public virtual event VOIDEventHandler onPreRender;
 		public virtual event VOIDEventHandler onPostRender;
 
@@ -155,10 +180,32 @@
 		public override void Save(KSP.IO.PluginConfiguration config, string sceneKey)
 		{
 			base.Save(config, sceneKey);
+
 		}
 	}
 
 	public delegate void VOIDEventHandler(object sender);
+	public delegate void VOIDForEachPartHandler(object sender, VOIDForEachPartArgs args);
+	public delegate void VOIDForEachPartModuleHandler(object sender, VOIDForEachPartModuleArgs args);
+
+	public abstract class VOIDForEachEventArgs<T> : EventArgs where T : class
+	{
+		public T Data;
+
+		public VOIDForEachEventArgs(T data)
+		{
+			this.Data = data;
+		}
+	}
+
+	public class VOIDForEachPartArgs : VOIDForEachEventArgs<Part>
+	{
+		public VOIDForEachPartArgs(Part data) : base(data) {}
+	}
+	public class VOIDForEachPartModuleArgs : VOIDForEachEventArgs<PartModule>
+	{
+		public VOIDForEachPartModuleArgs(PartModule data) : base(data) {}
+	}
 }
 
 

--- a/VOIDCore_Generic.cs
+++ b/VOIDCore_Generic.cs
@@ -321,6 +321,11 @@
 		public override event VOIDEventHandler onSkinChanged;
 		public override event VOIDEventHandler onUpdate;
 
+		public override event VOIDEventHandler onPreForEach;
+		public override event VOIDForEachPartHandler onForEachPart;
+		public override event VOIDForEachPartModuleHandler onForEachModule;
+		public override event VOIDEventHandler onPostForEach;
+
 		/*
 		 * Methods
 		 * */
@@ -331,6 +336,8 @@
 			if (!this.modulesLoaded)
 			{
 				this.LoadModulesOfType<IVOID_Module>();
+
+				FireOnModulesLoaded(this);
 			}
 
 			if (!this.skinsLoaded)
@@ -385,9 +392,61 @@
 			{
 				Logging.PostDebugMessage(this, "Updating SimManager.");
 				this.UpdateSimManager();
-			}
-
-			if (!this.GUIRunning)
+
+				VOIDForEachPartArgs partArgs;
+				VOIDForEachPartModuleArgs moduleArgs;
+
+				Part part;
+				PartModule partModule;
+
+				bool doForEachPart = this.onForEachPart != null;
+				bool doForEachModule = this.onForEachModule != null;
+
+				if (
+					(doForEachPart || doForEachModule) &&
+					(this.Vessel != null) &&
+					(this.Vessel.parts != null) &&
+					this.timeToUpdate
+				)
+				{
+					if (this.onPreForEach != null)
+					{
+						this.onPreForEach(this);
+					}
+
+					for (int pIdx = 0; pIdx < this.Vessel.parts.Count; pIdx++)
+					{
+						part = this.Vessel.parts[pIdx];
+						partArgs = new VOIDForEachPartArgs(part);
+
+						if (doForEachPart)
+						{
+							this.onForEachPart(this, partArgs);
+						}
+
+						if (doForEachModule && part.Modules != null)
+						{
+							for (int mIdx = 0; mIdx < part.Modules.Count; mIdx++)
+							{
+								partModule = part.Modules[mIdx];
+								moduleArgs = new VOIDForEachPartModuleArgs(partModule);
+
+								if (doForEachModule)
+								{
+									this.onForEachModule(this, moduleArgs);
+								}
+							}
+						}
+					}
+
+					if (this.onPostForEach!= null)
+					{
+						this.onPostForEach(this);
+					}
+				}
+			}
+
+			if (!this.GUIRunning && !this.gameUIHidden)
 			{
 				this.StartGUI();
 			}
@@ -487,6 +546,11 @@
 				}
 			}
 
+			if (this.onUpdate != null)
+			{
+				this.onUpdate(this);
+			}
+
 			this.saveTimer += Time.deltaTime;
 
 			if (this.modulesLoaded && this.saveTimer > 2f)
@@ -506,11 +570,6 @@
 			}
 
 			this.UpdateTimer += Time.deltaTime;
-
-			if (this.onUpdate != null)
-			{
-				this.onUpdate(this);
-			}
 		}
 
 		public virtual void FixedUpdate()
@@ -565,6 +624,8 @@
 					((IVOID_BehaviorModule)module).OnDestroy();
 				}
 			}
+
+			FireOnModulesDestroyed(this);
 
 			this.Dispose();
 		}

--- a/VOID_Data.cs
+++ b/VOID_Data.cs
@@ -486,101 +486,95 @@
 				""
 			);
 
+		private static Vector3 thrustPos;
+		private static Vector3 thrustDir;
+		private static float thrust;
+
+		private static void thrustOffsetPreForEach(object sender)
+		{
+			thrustPos = Vector3.zero;
+			thrustDir = Vector3.zero;
+			thrust = 0;
+		}
+
+		private static void thrustOffSetPerModule(object sender, VOIDForEachPartModuleArgs args)
+		{
+			PartModule module = args.Data;
+
+			float moduleThrust = 0;
+
+			switch (module.moduleName)
+			{
+				case "ModuleEngines":
+				case "ModuleEnginesFX":
+					break;
+				default:
+					return;
+			}
+
+			if (!module.isEnabled)
+			{
+				return;
+			}
+
+			CenterOfThrustQuery cotQuery = new CenterOfThrustQuery();
+
+			if (module is ModuleEngines)
+			{
+				ModuleEngines engineModule = module as ModuleEngines;
+
+				moduleThrust = engineModule.finalThrust;
+
+				engineModule.OnCenterOfThrustQuery(cotQuery);
+			}
+			else // engine is ModuleEnginesFX
+			{
+				ModuleEnginesFX engineFXModule = module as ModuleEnginesFX;
+
+				moduleThrust = engineFXModule.finalThrust;
+
+				engineFXModule.OnCenterOfThrustQuery(cotQuery);
+			}
+
+			if (moduleThrust != 0d)
+			{
+				cotQuery.thrust = moduleThrust;
+			}
+
+			thrustPos += cotQuery.pos * cotQuery.thrust;
+			thrustDir += cotQuery.dir * cotQuery.thrust;
+			thrust += cotQuery.thrust;
+		}
+
 		public static readonly VOID_Vector3Value vesselThrustOffset =
 			new VOID_Vector3Value(
 				"Thrust Offset",
 				delegate()
 				{
-					if (Core.Vessel == null)
-					{
-						return Vector3.zero;
-					}
-
-					Vector3 thrustPos = Vector3.zero;
-					Vector3 thrustDir = Vector3.zero;
-					float thrust = 0;
-
-					Part part;
-					for (int partIdx = 0; partIdx < Core.Vessel.parts.Count; partIdx++)
-					{
-						part = Core.Vessel.parts[partIdx];
-
-						if (part == null || part.Modules == null)
-						{
-							continue;
-						}
-
-						PartModule module;
-						for (int idx = 0; idx < part.Modules.Count; idx++)
-						{
-							module = part.Modules[idx];
-
-							float moduleThrust = 0;
-
-							switch (module.moduleName)
-							{
-								case "ModuleEngines":
-								case "ModuleEnginesFX":
-									break;
-								default:
-									continue;
-							}
-
-							if (!module.isEnabled)
-							{
-								continue;
-							}
-
-							CenterOfThrustQuery cotQuery = new CenterOfThrustQuery();
-
-							if (module is ModuleEngines)
-							{
-								ModuleEngines engineModule = module as ModuleEngines;
-
-								moduleThrust = engineModule.finalThrust;
-
-								engineModule.OnCenterOfThrustQuery(cotQuery);
-							}
-							else // engine is ModuleEnginesFX
-							{
-								ModuleEnginesFX engineFXModule = module as ModuleEnginesFX;
-
-								moduleThrust = engineFXModule.finalThrust;
-
-								engineFXModule.OnCenterOfThrustQuery(cotQuery);
-							}
-
-							if (moduleThrust != 0d)
-							{
-								cotQuery.thrust = moduleThrust;
-							}
-
-							thrustPos += cotQuery.pos * cotQuery.thrust;
-							thrustDir += cotQuery.dir * cotQuery.thrust;
-							thrust += cotQuery.thrust;
-						}
-					}
+					Vector3 pos = thrustPos;
+					Vector3 dir = thrustDir;
+
 					if (thrust != 0)
 					{
-						thrustPos /= thrust;
-						thrustDir /= thrust;
+						pos /= thrust;
+						dir /= thrust;
 					}
 
 					Transform vesselTransform = Core.Vessel.transform;
 
-					thrustPos = vesselTransform.InverseTransformPoint(thrustPos);
-					thrustDir = vesselTransform.InverseTransformDirection(thrustDir);
+					pos = vesselTransform.InverseTransformPoint(pos);
+					dir = vesselTransform.InverseTransformDirection(dir);
 
 					Vector3 thrustOffset = VectorTools.PointDistanceToLine(
-						                        thrustPos, thrustDir.normalized, Core.Vessel.findLocalCenterOfMass());
+						pos, dir.normalized, Core.Vessel.findLocalCenterOfMass());
 
 					Logging.PostDebugMessage(typeof(VOID_Data), "vesselThrustOffset:\n" +
 					"\tthrustPos: {0}\n" +
 					"\tthrustDir: {1}\n" +
 					"\tthrustOffset: {2}\n" +
 					"\tvessel.CoM: {3}",
-						thrustPos,
-						thrustDir.normalized,
+						pos,
+						dir.normalized,
 						thrustOffset,
 						Core.Vessel.findWorldCenterOfMass()
 					);
@@ -594,90 +588,109 @@
 
 		#region Air Breathing
 
+		private static double airFlowCurrent;
+		private static double airFlowRequired;
+		private static string intakeAirString;
+
+		private static void intakeAirPreForEach(object sender)
+		{
+			airFlowCurrent = 0d;
+			airFlowRequired = 0d;
+			intakeAirString = string.Empty;
+		}
+
+		private static void intakeAirForEachModule(object sender, VOIDForEachPartModuleArgs args)
+		{
+			PartModule module = args.Data;
+			List<Propellant> propellantList = null;
+
+			if (!module.part.enabled)
+			{
+				return;
+			}
+
+			if (module is ModuleEngines)
+			{
+				propellantList = ((ModuleEngines)module).propellants;
+			}
+			else if (module is ModuleEnginesFX)
+			{
+				propellantList = ((ModuleEnginesFX)module).propellants;
+			}
+			else if (module is ModuleResourceIntake)
+			{
+				ModuleResourceIntake intakeModule = (ModuleResourceIntake)module;
+
+				if (intakeModule.resourceName == "IntakeAir")
+				{
+					airFlowCurrent += intakeModule.airFlow;
+				}
+			}
+
+			if (propellantList != null)
+			{
+				Propellant propellant;
+				for (int propIdx = 0; propIdx < propellantList.Count; propIdx++)
+				{
+					propellant = propellantList[propIdx];
+
+					if (propellant.name == "IntakeAir")
+					{
+						airFlowRequired += propellant.currentRequirement / TimeWarp.fixedDeltaTime;
+						break;
+					}
+				}
+			}
+		}
+
+		private static void intakeAirPostForEach(object sender)
+		{
+			if (airFlowCurrent == 0 && airFlowRequired == 0)
+			{
+				intakeAirString = "N/A";
+			}
+			else
+			{
+				intakeAirString = string.Format("{0:F3} / {1:F3}", airFlowCurrent, airFlowRequired);
+			}
+		}
+
 		public static readonly VOID_StrValue intakeAirStatus =
 			new VOID_StrValue(
 				"Intake Air (Curr / Req)",
 				delegate()
 				{
-					double currentAmount;
-					double currentRequirement;
-
-					currentAmount = 0d;
-					currentRequirement = 0d;
-
-					Part part;
-					for (int idx = 0; idx < Core.Vessel.Parts.Count; idx++)
-					{
-						part = Core.Vessel.Parts[idx];
-
-						if (part.enabled)
-						{
-							ModuleEngines engineModule;
-							ModuleEnginesFX enginesFXModule;
-							List<Propellant> propellantList = null;
-
-							if (part.tryGetFirstModuleOfType<ModuleEngines>(out engineModule))
-							{
-								propellantList = engineModule.propellants;
-							}
-							else if (part.tryGetFirstModuleOfType<ModuleEnginesFX>(out enginesFXModule))
-							{
-								propellantList = enginesFXModule.propellants;
-							}
-
-							if (propellantList != null)
-							{
-								Propellant propellant;
-								for (int propIdx = 0; propIdx < propellantList.Count; propIdx++)
-								{
-									propellant = propellantList[propIdx];
-
-									if (propellant.name == "IntakeAir")
-									{
-										currentRequirement += propellant.currentRequirement / TimeWarp.fixedDeltaTime;
-										break;
-									}
-								}
-							}
-						}
-
-						ModuleResourceIntake intakeModule;
-
-						if (part.enabled && part.tryGetFirstModuleOfType<ModuleResourceIntake>(out intakeModule))
-						{
-							if (intakeModule.resourceName == "IntakeAir")
-							{
-								currentAmount += intakeModule.airFlow;
-							}
-						}
-					}
-
-					if (currentAmount == 0 && currentRequirement == 0)
-					{
-						return "N/A";
-					}
-
-					return string.Format("{0:F3} / {1:F3}", currentAmount, currentRequirement);
+					return intakeAirString;
 				}
 			);
 
 		#endregion
 
 		#region Crew
+
+		private static int crewCount;
+		private static int crewCapacity;
+
+		private static void crewCountPreForEach(object sender)
+		{
+			crewCount = 0;
+			crewCapacity = 0;
+		}
+
+		private static void crewCountPerPart(object sender, VOIDForEachPartArgs args)
+		{
+			Part part = args.Data;
+
+			crewCount += part.protoModuleCrew.Count;
+			crewCapacity += part.CrewCapacity;
+		}
 
 		public static readonly VOID_IntValue vesselCrewCount =
 			new VOID_IntValue(
 				"Crew Onboard",
 				delegate()
 				{
-					if (Core.Vessel != null)
-					{
-						return Core.Vessel.GetCrewCount();
-					}
-					else
-					{
-						return 0;
-					}
+					return crewCount;
 				},
 				""
 			);
@@ -687,14 +700,7 @@
 				"Crew Capacity",
 				delegate()
 				{
-					if (Core.Vessel != null)
-					{
-						return Core.Vessel.GetCrewCapacity();
-					}
-					else
-					{
-						return 0;
-					}
+					return crewCapacity;
 				},
 				""
 			);
@@ -1180,7 +1186,7 @@
 		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 * 180d / Math.PI),
 				"°"
 			);
 
@@ -1350,6 +1356,48 @@
 
 			return burntime;
 		}
+
+		private static void onFlightModulesLoaded(object sender)
+		{
+			if (sender is VOIDCore_Flight)
+			{
+				VOIDCore_Flight flightCore = sender as VOIDCore_Flight;
+
+				flightCore.onPreForEach += thrustOffsetPreForEach;
+				flightCore.onForEachModule += thrustOffSetPerModule;
+
+				flightCore.onPreForEach += intakeAirPreForEach;
+				flightCore.onForEachModule += intakeAirForEachModule;
+				flightCore.onPostForEach += intakeAirPostForEach;
+
+				flightCore.onPreForEach += crewCountPreForEach;
+				flightCore.onForEachPart += crewCountPerPart;
+			}
+		}
+
+		private static void onFlightModulesDestroyed(object sender)
+		{
+			if (sender is VOIDCore_Flight)
+			{
+				VOIDCore_Flight flightCore = sender as VOIDCore_Flight;
+
+				flightCore.onPreForEach -= thrustOffsetPreForEach;
+				flightCore.onForEachModule -= thrustOffSetPerModule;
+
+				flightCore.onPreForEach -= intakeAirPreForEach;
+				flightCore.onForEachModule -= intakeAirForEachModule;
+				flightCore.onPostForEach -= intakeAirPostForEach;
+
+				flightCore.onPreForEach -= crewCountPreForEach;
+				flightCore.onForEachPart -= crewCountPerPart;
+			}
+		}
+
+		static VOID_Data()
+		{
+			VOIDCore_Flight.onModulesLoaded += onFlightModulesLoaded;
+			VOIDCore_Flight.onModulesDestroyed += onFlightModulesDestroyed;
+		}
 	}
 }