Added "Atmo" button to compact build engineer
Added "Atmo" button to compact build engineer
Removed title addition and doesn't show sliders

 Binary files a/Assets/CurseLogo.png and b/Assets/CurseLogo.png differ
 Binary files a/Assets/CurseLogo.psd and b/Assets/CurseLogo.psd differ
  1.0.16.6, 02-05-15
  Fixed: Separately staged fairing mass jettisons are now calculated in the editor.
   
  1.0.16.5, 02-05-2015
  Fixed: Delta-V not being correctly calculated.
  Changed: Editor locking now uses the InputLockManager.
   
  1.0.16.4, 01-05-2015
  Fixed: Physically insignificant part mass is now accounted for.
  Changed: Module mass accounted for as it now makes its way onto the launch pad (e.g. fairings).
   
  Various optimisations:
  Object pooling.
  Removed LINQ expressions.
  Converted foreach to for loops.
   
  1.0.16.3, 27-04-2015
  Fixed issue with the toolbar icons not being created.
  Removed superfluous 'm/s' on the mach slider in the build engineer.
   
  1.0.16.2, 27-04-2015
  Changed the atmospheric slider on the build engineer to default to 0km when changing bodies.
   
  1.0.16.1, 26-04-2015, KSP Build #828
  Merged Sarbian's mach adjustments.
  Fixed bugs relating to thrust and atmosphere/velocity curves.
  Changed the atmospheric slider on the Build Engineer to work based on altitude.
  Changed the atmospheric slider to clamp to the maximum altitude for the selected body.
  Changed the velocity slider to clamp to the maximum usable mach value for the current vessel.
   
  1.0.16.0, 25-04-2015, KSP Build #821
  Fixed errors relating to KSP 1.0 update.
  Fixed fuel simulation to account for new thrust system.
  Fixed atmospheric engines to use the new velocity curve.
  Fixed atmospheric readouts to work with the new atmospheric model.
   
  1.0.15.2, 13-02-2015
  Padishar's Fixes:
  Fixed: Calculation of per-stage resource mass.
   
1.0.15.1, 13-02-2015 1.0.15.1, 13-02-2015
Rebuild Rebuild
1.0.15.0, 08-02-2015 1.0.15.0, 08-02-2015
Padishar's Fixes: Padishar's Fixes:
Added: Support KIDS ISP thrust correction. Added: Support KIDS ISP thrust correction.
Fixed: Log spam for stage priority mode. Fixed: Log spam for stage priority mode.
Fixed: Locked tanks preventing simulation from staging. Fixed: Locked tanks preventing simulation from staging.
Fixed: No flow and all vessel modes to respect flow states. Fixed: No flow and all vessel modes to respect flow states.
   
1.0.14.1, 28-12-2014 1.0.14.1, 28-12-2014
Fixed: Missing texture on the ER-7500 model. Fixed: Missing texture on the ER-7500 model.
1.0.14.0, 28-12-2014 1.0.14.0, 28-12-2014
Added: Career mode that limits the Flight Engineer by: Added: Career mode that limits the Flight Engineer by:
- Requiring an Engineer Kerbal of any level, or placement of an Engineer Chip or ER-7500 part. - Requiring an Engineer Kerbal of any level, or placement of an Engineer Chip or ER-7500 part.
- Tracking station level 3 enables Flight Engineer everywhere. - Tracking station level 3 enables Flight Engineer everywhere.
   
Added: New readouts to the orbital category: Added: New readouts to the orbital category:
- Mean Anomaly at Epoc - Mean Anomaly at Epoc
   
Added: New readouts to the miscellaneous category: Added: New readouts to the miscellaneous category:
- System Time - System Time
   
Added: Editor Overlay Tab's X position is now changable in the BuildOverlay.xml settings file. Added: Editor Overlay Tab's X position is now changable in the BuildOverlay.xml settings file.
Changed: Editor Overlay Tabs start position moved over as to not overlap the parts menu. Changed: Editor Overlay Tabs start position moved over as to not overlap the parts menu.
Fixed: Bug where STAGE_PRIORITY_FLOW resources would not be corrently disabled/enabled. Fixed: Bug where STAGE_PRIORITY_FLOW resources would not be corrently disabled/enabled.
Fixed: Issue with the formatting large Mass and Cost values. Fixed: Issue with the formatting large Mass and Cost values.
Fixed: Error when loading the Engineer7500 part model. Fixed: Error when loading the Engineer7500 part model.
   
1.0.13.1, 16-12-2014 1.0.13.1, 16-12-2014
Fixed: Issue with manoeuvre node readouts and low tier tracking station. Fixed: Issue with manoeuvre node readouts and low tier tracking station.
   
1.0.13.0, 16-12-2014 1.0.13.0, 16-12-2014
Updated for KSP version 0.90 Updated for KSP version 0.90
   
Added: New readouts to the vessel category: Added: New readouts to the vessel category:
- Heading Rate - Heading Rate
- Pitch Rate - Pitch Rate
- Roll Rate - Roll Rate
   
Changed: Simulation to look for fuel lines that use CModuleFuelLine module. Changed: Simulation to look for fuel lines that use CModuleFuelLine module.
Fixed: Editor Overlay now loads the saved visibility value properly. Fixed: Editor Overlay now loads the saved visibility value properly.
Fixed: Altitude (Terrain) will no longer give a reading below sea level. Fixed: Altitude (Terrain) will no longer give a reading below sea level.
Fixed: Suicide burn now uses radar altitude that clamps to sea level. Fixed: Suicide burn now uses radar altitude that clamps to sea level.
   
1.0.12.0, 01-12-2014 1.0.12.0, 01-12-2014
Added: Setting in Build Engineer to enable/disable vectored thrust calculations. Added: Setting in Build Engineer to enable/disable vectored thrust calculations.
Added: Thrust torque field in Build Engineer (courtesy of mic_e). Added: Thrust torque field in Build Engineer (courtesy of mic_e).
Added: New readouts to the vessel category: Added: New readouts to the vessel category:
- Thrust Offset Angle (courtesy of mic_e) - Thrust Offset Angle (courtesy of mic_e)
- Thrust Torque (courtesy of mic_e) - Thrust Torque (courtesy of mic_e)
- Part Count: stage/total - Part Count: stage/total
- Heading - Heading
- Pitch - Pitch
- Roll - Roll
   
Added: New readouts to the surface category: Added: New readouts to the surface category:
- Situation - Situation
   
Added: New readouts to the miscellaneous category: Added: New readouts to the miscellaneous category:
- Vectored Thrust Toggle - Vectored Thrust Toggle
   
Fixed: The category selection within the section editors now do not always reset back to 'Orbital'. Fixed: The category selection within the section editors now do not always reset back to 'Orbital'.
Fixed: Issue where the vessel simulation can sometimes permanently freeze. Fixed: Issue where the vessel simulation can sometimes permanently freeze.
Fixed: Issue where the vessel simulation would not show updates when the delay was set lower than the frame rate. Fixed: Issue where the vessel simulation would not show updates when the delay was set lower than the frame rate.
   
1.0.11.3, 11-11-2014 1.0.11.3, 11-11-2014
Changed: Gravity measurements for Isp to 9.82. Changed: Gravity measurements for Isp to 9.82.
   
1.0.11.2, 10-11-2014 1.0.11.2, 10-11-2014
Changed: Gravity measurements for Isp calculations from 9.81 to 9.8066 for accuracy. Changed: Gravity measurements for Isp calculations from 9.81 to 9.8066 for accuracy.
Changed: Manoeuvre node burn times are now more accurate. Changed: Manoeuvre node burn times are now more accurate.
Fixed: Bug in the manoeuvre node burn time calculations where it was not averaging acceleration correctly. Fixed: Bug in the manoeuvre node burn time calculations where it was not averaging acceleration correctly.
   
1.0.11.1, 07-11-2014 1.0.11.1, 07-11-2014
Changed: Build Engineer now shows stage part count as well as total. Changed: Build Engineer now shows stage part count as well as total.
Changed: Build Overlay Vessel tab data: Changed: Build Overlay Vessel tab data:
DeltaV: stage / total DeltaV: stage / total
Mass: stage / total Mass: stage / total
TWR: start (max) <- shows for bottom stage only. TWR: start (max) <- shows for bottom stage only.
Parts: stage / total Parts: stage / total
   
Fixed: Issue with the vessel tab vanishing from the editor. Fixed: Issue with the vessel tab vanishing from the editor.
   
1.0.11.0, 06-11-2014 1.0.11.0, 06-11-2014
Added: New readouts to the orbital category: Added: New readouts to the orbital category:
- Current SOI - Current SOI
- Manoeuvre Node DeltaV (Prograde) - Manoeuvre Node DeltaV (Prograde)
- Manoeuvre Node DeltaV (Normal) - Manoeuvre Node DeltaV (Normal)
- Manoeuvre Node DeltaV (Radial) - Manoeuvre Node DeltaV (Radial)
- Manoeuvre Node DeltaV (Total) - Manoeuvre Node DeltaV (Total)
- Manoeuvre Node Burn Time - Manoeuvre Node Burn Time
- Manoeuvre Node Half Burn Time - Manoeuvre Node Half Burn Time
- Manoeuvre Node Angle to Prograde - Manoeuvre Node Angle to Prograde
- Manoeuvre Node Angle to Retrograde - Manoeuvre Node Angle to Retrograde
- Time to Manoeuvre Node - Time to Manoeuvre Node
- Time to Manoeuvre Burn - Time to Manoeuvre Burn
   
Added: Readout help strings by ClassyJakey. Added: Readout help strings by ClassyJakey.
   
Fixed: Issue with separators in HUDs. Fixed: Issue with separators in HUDs.
Fixed: Issue with HUDs with backgrounds that have no displayed lines. Fixed: Issue with HUDs with backgrounds that have no displayed lines.
   
Padishar's Fixes: Padishar's Fixes:
Fixed: Issue with multicouplers when attached to parent by bottom node. Fixed: Issue with multicouplers when attached to parent by bottom node.
Fixed: Issue with sepratrons on solid rocket boosters. Fixed: Issue with sepratrons on solid rocket boosters.
   
1.0.10.0, 19-10-2014 1.0.10.0, 19-10-2014
UPDATE NOTICE: If you are updating from a previous version of Kerbal Engineer 1.0, please UPDATE NOTICE: If you are updating from a previous version of Kerbal Engineer 1.0, please
delete the 'Settings/SectionLibrary.xml' file, or remove the old install first. This will delete the 'Settings/SectionLibrary.xml' file, or remove the old install first. This will
reset the Flight Engineer sections to their default values and enable the new HUD functionality. reset the Flight Engineer sections to their default values and enable the new HUD functionality.
   
Added: New reaouts to the vessel category: Added: New reaouts to the vessel category:
- Suicide Burn Altitude (height above terrain to start burn) - Suicide Burn Altitude (height above terrain to start burn)
- Suicide Burn Distance (distance to suicide burn altitude) - Suicide Burn Distance (distance to suicide burn altitude)
- Suicide Burn DeltaV (velocity change required to zero vertical speed) - Suicide Burn DeltaV (velocity change required to zero vertical speed)
*** F5 for safety and use at your own risk! *** *** F5 for safety and use at your own risk! ***
   
Added: HUD type sections to the Flight Engineer. Added: HUD type sections to the Flight Engineer.
Added: HUD sections can have a smoked background for easy visibility. Added: HUD sections can have a smoked background for easy visibility.
Added: 'Switch to Target' button on the Target Selector readout. Added: 'Switch to Target' button on the Target Selector readout.
Changed: The default installed readouts to reduce new user brain melt. Changed: The default installed readouts to reduce new user brain melt.
Fixed: Flight Engineer not saving its hidden state. Fixed: Flight Engineer not saving its hidden state.
Fixed: Bug in the phase angle calculations. Fixed: Bug in the phase angle calculations.
Fixed: Bug where the Build Engineer would stay locked after hiding with the shortcut key. Fixed: Bug where the Build Engineer would stay locked after hiding with the shortcut key.
   
1.0.9.3, 08-10-2014 1.0.9.3, 08-10-2014
Added: Title of the build engineer in compact mode now shows if you are using atmospheric data. Added: Title of the build engineer in compact mode now shows if you are using atmospheric data.
Added: New readout to the surface category: Added: New readout to the surface category:
- Vertical Acceleration - Vertical Acceleration
- Horizontal Acceleration - Horizontal Acceleration
Changed: Atmospheric efficiency readout now shows as a percentage. Changed: Atmospheric efficiency readout now shows as a percentage.
Changed: Atmospheric settings (pressure/velocity) in the editor condensed onto a single line. Changed: Atmospheric settings (pressure/velocity) in the editor condensed onto a single line.
Fixed: Bug where the overlays in the editor would stay open outside of parts screen. Fixed: Bug where the overlays in the editor would stay open outside of parts screen.
   
1.0.9.2, 07-10-2014 1.0.9.2, 07-10-2014
Updated for KSP v0.25.0 Updated for KSP v0.25.0
Changed: Prettyfied Latitude and Longitude readouts. Changed: Prettyfied Latitude and Longitude readouts.
Changed: ModuleLandingGear now uses the physical significance flag. Changed: ModuleLandingGear now uses the physical significance flag.
Changed: Updated MiniAVC to 1.0.2.4. Changed: Updated MiniAVC to 1.0.2.4.
   
1.0.9.1, 17-09-2014 1.0.9.1, 17-09-2014
Fixed: Part size bug caused by TweakScale's cost calculator. Fixed: Part size bug caused by TweakScale's cost calculator.
   
1.0.9.0, 15-09-2014 1.0.9.0, 15-09-2014
Added: Build Engineer now also implements the '\' backslash show/hide shortcut. Added: Build Engineer now also implements the '\' backslash show/hide shortcut.
Added: New readouts to the vessel category: Added: New readouts to the vessel category:
- Current Stage DeltaV - Current Stage DeltaV
- Surface Thrust to Weight Ratio - Surface Thrust to Weight Ratio
   
Added: New editor overlay system. Added: New editor overlay system.
- Sleeker design. - Sleeker design.
- Hover over part information options: - Hover over part information options:
- Name only - Name only
- Middle click to show - Middle click to show
- Always show - Always show
- Slide out overlay displays: - Slide out overlay displays:
- Vessel information - Vessel information
- Resources list - Resources list
   
Fixed: Cost calculation now works with mods implementing IPartCostModifier. Fixed: Cost calculation now works with mods implementing IPartCostModifier.
   
1.0.8.1, 06-09-2014 1.0.8.1, 06-09-2014
Fixed: Bug which caused rendezvous readouts to freeze the game or show all zeros. Fixed: Bug which caused rendezvous readouts to freeze the game or show all zeros.
   
1.0.8.0, 06-09-2014 1.0.8.0, 06-09-2014
Added: New readouts to the vessel category: Added: New readouts to the vessel category:
- Intake Air (Usage) - Intake Air (Usage)
   
Added: New readouts to the rendezvous category: Added: New readouts to the rendezvous category:
- Relative Velocity - Relative Velocity
- Relative Speed - Relative Speed
   
Fixed: An issue where deltaV would not be calculated whilst flying. Fixed: An issue where deltaV would not be calculated whilst flying.
Fixed: NullRef whilst loading the in flight Action Menu. Fixed: NullRef whilst loading the in flight Action Menu.
   
1.0.7.1, 02-09-2014 1.0.7.1, 02-09-2014
Changed: Reversed Intake Air readout from 'S/D' to 'D/S' for easier reading. Changed: Reversed Intake Air readout from 'S/D' to 'D/S' for easier reading.
Changed: Increased Intake Air readout precision to 4 decimal places. Changed: Increased Intake Air readout precision to 4 decimal places.
Fixed: Issue where Intake Air supply was not representative of total supply. Fixed: Issue where Intake Air supply was not representative of total supply.
Fixed: Bug where actual thrust does not reset to zero on deactivated engines. Fixed: Bug where actual thrust does not reset to zero on deactivated engines.
Fixed: Thrust now scales with velocity for atmospheric engines. (Padishar's fix) Fixed: Thrust now scales with velocity for atmospheric engines. (Padishar's fix)
   
1.0.7.0, 01-09-2014 1.0.7.0, 01-09-2014
Added: Part count information to the Build Engineer. Added: Part count information to the Build Engineer.
Added: Reset button to the G-Force readout. Added: Reset button to the G-Force readout.
Added: Preset system to the Flight Engineer. Added: Preset system to the Flight Engineer.
Added: New stock presets: Added: New stock presets:
- Orbital - Orbital
- Surface - Surface
- Vessel - Vessel
- Rendezvous - Rendezvous
   
Added: New readouts to the orbital category: Added: New readouts to the orbital category:
- True Anomaly - True Anomaly
- Eccentric Anomaly - Eccentric Anomaly
- Mean Anomaly - Mean Anomaly
- Argument of Periapsis - Argument of Periapsis
- Angle to Prograde - Angle to Prograde
- Angle to Retrograde - Angle to Retrograde
   
Added: New readouts to the vessel category: Added: New readouts to the vessel category:
- Intake Air (Demand) - Intake Air (Demand)
- Intake Air (Supply) - Intake Air (Supply)
- Intake Air (Supply/Demand) - Intake Air (Supply/Demand)
   
Added: New readouts to the rendezvous category. Added: New readouts to the rendezvous category.
- Semi-major Axis - Semi-major Axis
- Semi-minor Axis - Semi-minor Axis
   
Added: Time formatter which can show time as referenced by any celestial body. Added: Time formatter which can show time as referenced by any celestial body.
Added: New readouts to the miscellaneous category: Added: New readouts to the miscellaneous category:
- Time Reference Adjuster - Time Reference Adjuster
   
Changed: Moved Sim Delay readout into the Miscellaneous category. Changed: Moved Sim Delay readout into the Miscellaneous category.
Changed: Updated MiniAVC to v1.0.2.3. Changed: Updated MiniAVC to v1.0.2.3.
Fixed: Issues with large value wrap around in the Flight Engineer. Fixed: Issues with large value wrap around in the Flight Engineer.
Fixed: Bug in the phase angle calculation. Fixed: Bug in the phase angle calculation.
   
1.0.6.0, 23-08-2014 1.0.6.0, 23-08-2014
Added: Time and Angle to equatorial ascending/descending nodes in the orbital display. Added: Time and Angle to equatorial ascending/descending nodes in the orbital display.
Added: Time and Angle to relative ascending/descending nodes in the rendezvous display. Added: Time and Angle to relative ascending/descending nodes in the rendezvous display.
Added: Overlay tooltip information delay adjustment slider to the Build Engineer settings. Added: Overlay tooltip information delay adjustment slider to the Build Engineer settings.
Added: Ability to rename the stock displays in the Flight Engineer. Added: Ability to rename the stock displays in the Flight Engineer.
Changed: Build Engineer is now hidden when not in parts view. Changed: Build Engineer is now hidden when not in parts view.
Changed: Custom display panels will only show in the control bar if an abbreviation is set. Changed: Custom display panels will only show in the control bar if an abbreviation is set.
Changed: Licensing and readme structures are now more verbose to satisfy the new add-on rules. Changed: Licensing and readme structures are now more verbose to satisfy the new add-on rules.
Fixed: Updated MiniAVC to v1.0.2.1 (fixes remote check bug as well as other minor bugs). Fixed: Updated MiniAVC to v1.0.2.1 (fixes remote check bug as well as other minor bugs).
   
1.0.5.0, 13-08-2014 1.0.5.0, 13-08-2014
Added: Acceleration readout to the Vessel category (current / maximum). Added: Acceleration readout to the Vessel category (current / maximum).
Added: Category library system for the Flight Engineer readouts. Added: Category library system for the Flight Engineer readouts.
Added: Drop-down category selection to better support the new system. Added: Drop-down category selection to better support the new system.
Changed: Misc category now called Miscellaneous (this will cause previously added readouts from this category to vanish). Changed: Misc category now called Miscellaneous (this will cause previously added readouts from this category to vanish).
Fixed: Bug with the Build Engineer toolbar button. Fixed: Bug with the Build Engineer toolbar button.
Fixed: Some buggyness when trying to close the bodies drop-down in the Build Engineer via the button. Fixed: Some buggyness when trying to close the bodies drop-down in the Build Engineer via the button.
Fixed: Flight Engineer toolbar menu now hides when hiding the GUI with F2. Fixed: Flight Engineer toolbar menu now hides when hiding the GUI with F2.
Fixed: Flight Engineer toolbar button now disables when in module mode and no engineer is running. Fixed: Flight Engineer toolbar button now disables when in module mode and no engineer is running.
   
1.0.4.0, 12-08-2014 1.0.4.0, 12-08-2014
Added: Better stock toolbar support in the flight engineer. Added: Better stock toolbar support in the flight engineer.
Added: Dynamically generated celestial body library for supporting add-ons that modify the star system. Added: Dynamically generated celestial body library for supporting add-ons that modify the star system.
Changed: Reference bodies are now listed with a nestable menu system. Changed: Reference bodies are now listed with a nestable menu system.
Changed: Extended logging system has been improved. Changed: Extended logging system has been improved.
Changed: Swapped out integrated MiniAVC in place of the official bundle version. Changed: Swapped out integrated MiniAVC in place of the official bundle version.
Changed: Increased general distance precision to 1 decimal place. Changed: Increased general distance precision to 1 decimal place.
Changed: Increased Semi-major/minor axis precision to 3 decimal places. Changed: Increased Semi-major/minor axis precision to 3 decimal places.
Fixed: Impact altitude was mistakenly formatted as an angle, it is now formatted correctly as a distance. Fixed: Impact altitude was mistakenly formatted as an angle, it is now formatted correctly as a distance.
   
1.0.3.0, 30-07-2014 1.0.3.0, 30-07-2014
Added: Integrated KSP-AVC support with MiniAVC. Added: Integrated KSP-AVC support with MiniAVC.
Added: Setting to change the simulation delay in the Build Engineer. Added: Setting to change the simulation delay in the Build Engineer.
Added: Setting to enable and disable the build overlay system. Added: Setting to enable and disable the build overlay system.
Added: Burn time to Delta-V readouts. Added: Burn time to Delta-V readouts.
Added: Atmospheric readouts fully support FAR. Added: Atmospheric readouts fully support FAR.
Added: Atmospheric readouts are disabled with NEAR. Added: Atmospheric readouts are disabled with NEAR.
Changed: Force formatting inversely scales decimal precision with value. Changed: Force formatting inversely scales decimal precision with value.
Fixed: Flickering in VAB and Vessel display. Fixed: Flickering in VAB and Vessel display.
Fixed: Bug saving the GUI display size. Fixed: Bug saving the GUI display size.
   
1.0.2.0, 27-07-2014 1.0.2.0, 27-07-2014
Added: Separator readout module under Misc in the Flight Engineer. Added: Separator readout module under Misc in the Flight Engineer.
Added: Adjustable GUI display size. Added: Adjustable GUI display size.
Added: Display size can be adjusted in the Build Engineer settings. Added: Display size can be adjusted in the Build Engineer settings.
Added: Misc readout for adjusting display size in the Flight Engineer. Added: Misc readout for adjusting display size in the Flight Engineer.
Changed: The rendezvous readout for the target's Orbital Period has higher precision. Changed: The rendezvous readout for the target's Orbital Period has higher precision.
Fixed: White toolbar icon by manually importing the texture if it cannot be found in the game database. Fixed: White toolbar icon by manually importing the texture if it cannot be found in the game database.
Fixed: Engines that have a minimum thrust are now calculated properly. (Thanks to nosscire.) Fixed: Engines that have a minimum thrust are now calculated properly. (Thanks to nosscire.)
Fixed: Compact collapse mode is now saved in the Build Engineer. Fixed: Compact collapse mode is now saved in the Build Engineer.
   
1.0.1.0, 26-07-2014 1.0.1.0, 26-07-2014
Added: Part-less Flight Engineer. Added: Part-less Flight Engineer.
Added: Ability to collapse the Build Engineer into compact mode from left or right. Added: Ability to collapse the Build Engineer into compact mode from left or right.
Added: Settings in Build Engineer for compact collapse mode and partless/module Flight Engineer. Added: Settings in Build Engineer for compact collapse mode and partless/module Flight Engineer.
Added: Biome, Impact Biome and Slope readouts. Added: Biome, Impact Biome and Slope readouts.
Added: Extra logging and exception handling. Added: Extra logging and exception handling.
Added: The original Engineer Chip part. Added: The original Engineer Chip part.
Added: "Show Engineer" toggle on the Flight Engineer toolbar. Added: "Show Engineer" toggle on the Flight Engineer toolbar.
Changed: Extended logging system now also writes to the standard KSP logs. Changed: Extended logging system now also writes to the standard KSP logs.
Changed: Extended logging saves next to the .dll file. Changed: Extended logging saves next to the .dll file.
Changed: ER7500 part has no physical significance. Changed: ER7500 part has no physical significance.
Fixed: ActionMenu and DisplayStack destruction bug. Fixed: ActionMenu and DisplayStack destruction bug.
   
1.0.0.1, 24-07-2014 1.0.0.1, 24-07-2014
Added: Stock toolbar support in the Flight Engineer. Added: Stock toolbar support in the Flight Engineer.
Changed: Orbital Period has higher precision. Changed: Orbital Period has higher precision.
Fixed: Various NullRefs in editor window and overlay. Fixed: Various NullRefs in editor window and overlay.
1.0.0.0, 24-07-2014 1.0.0.0, 24-07-2014
Initial release for public testing. Initial release for public testing.
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives  
   
using System;  
using System.Collections.Generic;  
using System.Linq;  
   
#endregion  
   
namespace KerbalEngineer namespace KerbalEngineer
{ {
  using System;
  using System.Collections.Generic;
  using System.Linq;
   
/* /*
* *
* With thanks to Nathaniel R. Lewis (aka. Teknoman117) (linux.robotdude@gmail.com) for working out * With thanks to Nathaniel R. Lewis (aka. Teknoman117) (linux.robotdude@gmail.com) for working out
* the best way of getting the celestial body information dynamically using PSystemManager. * the best way of getting the celestial body information dynamically using PSystemManager.
* *
*/ */
   
public static class CelestialBodies public static class CelestialBodies
{ {
#region Constructors  
   
static CelestialBodies() static CelestialBodies()
{ {
try try
{ {
SystemBody = new BodyInfo(PSystemManager.Instance.localBodies.Find(b => b.referenceBody == null || b.referenceBody == b)); SystemBody = new BodyInfo(PSystemManager.Instance.localBodies.Find(b => b.referenceBody == null || b.referenceBody == b));
if (!SetSelectedBody("Kerbin")) if (!SetSelectedBody("Kerbin"))
{ {
SelectedBody = SystemBody; SelectedBody = SystemBody;
SelectedBody.SetSelected(true); SelectedBody.SetSelected(true);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion  
   
#region Properties  
   
public static BodyInfo SelectedBody { get; private set; } public static BodyInfo SelectedBody { get; private set; }
public static BodyInfo SystemBody { get; private set; } public static BodyInfo SystemBody { get; private set; }
   
#endregion  
   
#region Public Methods  
   
/// <summary> /// <summary>
/// Gets a body given a supplied body name. /// Gets a body given a supplied body name.
/// </summary> /// </summary>
public static BodyInfo GetBodyInfo(string bodyName) public static BodyInfo GetBodyInfo(string bodyName)
{ {
try try
{ {
return SystemBody.GetBodyInfo(bodyName); return SystemBody.GetBodyInfo(bodyName);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
return null; return null;
} }
   
/// <summary> /// <summary>
/// Sets the selected body to one matching the supplied body name. Returns true if successful. /// Sets the selected body to one matching the supplied body name. Returns true if successful.
/// </summary> /// </summary>
public static bool SetSelectedBody(string bodyName) public static bool SetSelectedBody(string bodyName)
{ {
try try
{ {
var body = GetBodyInfo(bodyName); BodyInfo body = GetBodyInfo(bodyName);
if (body != null) if (body != null)
{ {
if (SelectedBody != null) if (SelectedBody != null)
{ {
SelectedBody.SetSelected(false); SelectedBody.SetSelected(false);
} }
SelectedBody = body; SelectedBody = body;
SelectedBody.SetSelected(true); SelectedBody.SetSelected(true);
return true; return true;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
return false; return false;
} }
   
#endregion  
   
#region Nested type: BodyInfo  
   
public class BodyInfo public class BodyInfo
{ {
#region Constructors  
   
public BodyInfo(CelestialBody body, BodyInfo parent = null) public BodyInfo(CelestialBody body, BodyInfo parent = null)
{ {
try try
{ {
// Set the body information. // Set the body information.
this.CelestialBody = body; CelestialBody = body;
this.Name = body.bodyName; Name = body.bodyName;
this.Gravity = 9.81 * body.GeeASL; Gravity = 9.81 * body.GeeASL;
this.Atmosphere = body.atmosphere ? 101.325 * body.atmosphereMultiplier : 0; Parent = parent;
this.Parent = parent;  
   
// Set orbiting bodies information. // Set orbiting bodies information.
this.Children = new List<BodyInfo>(); Children = new List<BodyInfo>();
foreach (var orbitingBody in body.orbitingBodies) foreach (CelestialBody orbitingBody in body.orbitingBodies)
{ {
this.Children.Add(new BodyInfo(orbitingBody, this)); Children.Add(new BodyInfo(orbitingBody, this));
} }
   
this.SelectedDepth = 0; SelectedDepth = 0;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion public CelestialBody CelestialBody { get; private set; }
  public List<BodyInfo> Children { get; private set; }
#region Properties public double Gravity { get; private set; }
   
public string Name { get; private set; } public string Name { get; private set; }
public double Gravity { get; private set; }  
public double Atmosphere { get; private set; }  
public BodyInfo Parent { get; private set; } public BodyInfo Parent { get; private set; }
public List<BodyInfo> Children { get; private set; }  
public CelestialBody CelestialBody { get; private set; }  
public bool Selected { get; private set; } public bool Selected { get; private set; }
public int SelectedDepth { get; private set; } public int SelectedDepth { get; private set; }
   
#endregion  
   
#region Public Methods  
   
public BodyInfo GetBodyInfo(string bodyName) public BodyInfo GetBodyInfo(string bodyName)
{ {
try try
{ {
// This is the searched body. // This is the searched body.
if (String.Equals(this.Name, bodyName, StringComparison.CurrentCultureIgnoreCase)) if (String.Equals(Name, bodyName, StringComparison.CurrentCultureIgnoreCase))
{ {
return this; return this;
} }
   
// Check to see if any of this bodies children are the searched body. // Check to see if any of this bodies children are the searched body.
foreach (var child in this.Children) foreach (BodyInfo child in Children)
{ {
var body = child.GetBodyInfo(bodyName); BodyInfo body = child.GetBodyInfo(bodyName);
if (body != null) if (body != null)
{ {
return body; return body;
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
   
// A body with the specified name was not found. // A body with the specified name was not found.
return null; return null;
} }
   
  public double GetDensity(double altitude)
  {
  return CelestialBody.GetDensity(GetPressure(altitude), GetTemperature(altitude));
  }
   
  public double GetPressure(double altitude)
  {
  return CelestialBody.GetPressure(altitude);
  }
   
  public double GetTemperature(double altitude)
  {
  return CelestialBody.GetTemperature(altitude);
  }
   
  public double GetAtmospheres(double altitude)
  {
  return GetPressure(altitude) * PhysicsGlobals.KpaToAtmospheres;
  }
   
public void SetSelected(bool state, int depth = 0) public void SetSelected(bool state, int depth = 0)
{ {
this.Selected = state; Selected = state;
this.SelectedDepth = depth; SelectedDepth = depth;
if (this.Parent != null) if (Parent != null)
{ {
this.Parent.SetSelected(state, depth + 1); Parent.SetSelected(state, depth + 1);
} }
} }
   
#endregion  
   
#region Debugging  
   
public override string ToString() public override string ToString()
{ {
var log = "\n" + this.Name + string log = "\n" + Name +
"\n\tGravity: " + this.Gravity + "\n\tGravity: " + Gravity +
"\n\tAtmosphere: " + this.Atmosphere + "\n\tSelected: " + Selected;
"\n\tSelected: " + this.Selected;  
  return Children.Aggregate(log, (current, child) => current + "\n" + child);
return this.Children.Aggregate(log, (current, child) => current + "\n" + child); }
} }
   
#endregion  
}  
   
#endregion  
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
#region Using Directives #region Using Directives
   
using System; using System;
using System.Linq;  
using Extensions; using Extensions;
using Flight; using Flight;
using Helpers; using Helpers;
using Settings; using Settings;
using UIControls; using UIControls;
using UnityEngine; using UnityEngine;
using VesselSimulator; using VesselSimulator;
   
#endregion #endregion
   
[KSPAddon(KSPAddon.Startup.EditorAny, false)] [KSPAddon(KSPAddon.Startup.EditorAny, false)]
public class BuildAdvanced : MonoBehaviour public class BuildAdvanced : MonoBehaviour
{ {
#region Fields #region Fields
  public static float Altitude = 0.0f;
   
private GUIStyle areaSettingStyle; private GUIStyle areaSettingStyle;
private GUIStyle areaStyle; private GUIStyle areaStyle;
private float atmosphericPercentage = 1.0f; private float atmosphericMach;
private float atmosphericVelocity;  
private GUIStyle bodiesButtonActiveStyle; private GUIStyle bodiesButtonActiveStyle;
private GUIStyle bodiesButtonStyle; private GUIStyle bodiesButtonStyle;
private DropDown bodiesList; private DropDown bodiesList;
private Rect bodiesListPosition; private Rect bodiesListPosition;
private GUIStyle buttonStyle; private GUIStyle buttonStyle;
private int compactCheck; private int compactCheck;
private bool compactCollapseRight; private bool compactCollapseRight;
private bool compactMode; private bool compactMode;
private float compactRight; private float compactRight;
private bool hasChanged; private bool hasChanged;
private GUIStyle infoStyle; private GUIStyle infoStyle;
private bool isEditorLocked; private bool isEditorLocked;
  private float maxMach;
private int numberOfStages; private int numberOfStages;
private Rect position = new Rect(265.0f, 45.0f, 0, 0); private Rect position = new Rect(265.0f, 45.0f, 0, 0);
private GUIStyle settingAtmoStyle; private GUIStyle settingAtmoStyle;
private GUIStyle settingStyle; private GUIStyle settingStyle;
private bool showAllStages; private bool showAllStages;
private bool showAtmosphericDetails; private bool showAtmosphericDetails;
private bool showSettings; private bool showSettings;
private Stage[] stages; private Stage[] stages;
private GUIStyle titleStyle; private GUIStyle titleStyle;
private bool visible = true; private bool visible = true;
private GUIStyle windowStyle; private GUIStyle windowStyle;
   
#endregion #endregion
   
#region Properties #region Properties
   
/// <summary> /// <summary>
/// Gets the current instance if started or returns null. /// Gets the current instance if started or returns null.
/// </summary> /// </summary>
public static BuildAdvanced Instance { get; private set; } public static BuildAdvanced Instance { get; private set; }
   
/// <summary> /// <summary>
/// Gets and sets whether to show in compact mode. /// Gets and sets whether to show in compact mode.
/// </summary> /// </summary>
public bool CompactMode public bool CompactMode
{ {
get { return this.compactMode; } get
set { this.compactMode = value; } {
  return compactMode;
  }
  set
  {
  compactMode = value;
  }
} }
   
/// <summary> /// <summary>
/// Gets and sets whether to show all stages. /// Gets and sets whether to show all stages.
/// </summary> /// </summary>
public bool ShowAllStages public bool ShowAllStages
{ {
get { return this.showAllStages; } get
set { this.showAllStages = value; } {
  return showAllStages;
  }
  set
  {
  showAllStages = value;
  }
} }
   
/// <summary> /// <summary>
/// Gets and sets whether to use atmospheric details. /// Gets and sets whether to use atmospheric details.
/// </summary> /// </summary>
public bool ShowAtmosphericDetails public bool ShowAtmosphericDetails
{ {
get { return this.showAtmosphericDetails; } get
set { this.showAtmosphericDetails = value; } {
  return showAtmosphericDetails;
  }
  set
  {
  showAtmosphericDetails = value;
  }
} }
   
/// <summary> /// <summary>
/// Gets and sets whether to show the settings display. /// Gets and sets whether to show the settings display.
/// </summary> /// </summary>
public bool ShowSettings public bool ShowSettings
{ {
get { return this.showSettings; } get
set { this.showSettings = value; } {
  return showSettings;
  }
  set
  {
  showSettings = value;
  }
} }
   
/// <summary> /// <summary>
/// Gets and sets whether the display is enabled. /// Gets and sets whether the display is enabled.
/// </summary> /// </summary>
public bool Visible public bool Visible
{ {
get { return this.visible; } get
set { this.visible = value; } {
} return visible;
  }
  set
  {
  visible = value;
  }
  }
#endregion #endregion
   
#region Methods #region Methods
  private static Rect compactModeRect = new Rect(0.0f, 5.0f, 0.0f, 20.0f);
  private static Stage stage;
  private static int stagesCount;
  private static int stagesLength;
  private static string title;
   
protected void Awake() protected void Awake()
{ {
try try
{ {
Instance = this; Instance = this;
this.bodiesList = this.gameObject.AddComponent<DropDown>(); bodiesList = gameObject.AddComponent<DropDown>();
this.bodiesList.DrawCallback = this.DrawBodiesList; bodiesList.DrawCallback = DrawBodiesList;
this.Load(); Load();
   
SimManager.UpdateModSettings(); SimManager.UpdateModSettings();
SimManager.OnReady -= this.GetStageInfo; SimManager.OnReady -= GetStageInfo;
SimManager.OnReady += this.GetStageInfo; SimManager.OnReady += GetStageInfo;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex, "BuildAdvanced.Awake()");
} }
} }
   
/// <summary> /// <summary>
/// Saves the settings when this object is destroyed. /// Saves the settings when this object is destroyed.
/// </summary> /// </summary>
protected void OnDestroy() protected void OnDestroy()
{ {
try try
{ {
var handler = new SettingHandler(); SettingHandler handler = new SettingHandler();
handler.Set("visible", this.visible); handler.Set("visible", visible);
handler.Set("windowPositionX", this.position.x); handler.Set("windowPositionX", position.x);
handler.Set("windowPositionY", this.position.y); handler.Set("windowPositionY", position.y);
handler.Set("compactMode", this.compactMode); handler.Set("compactMode", compactMode);
handler.Set("compactCollapseRight", this.compactCollapseRight); handler.Set("compactCollapseRight", compactCollapseRight);
handler.Set("showAllStages", this.showAllStages); handler.Set("showAllStages", showAllStages);
handler.Set("showAtmosphericDetails", this.showAtmosphericDetails); handler.Set("showAtmosphericDetails", showAtmosphericDetails);
handler.Set("showSettings", this.showSettings); handler.Set("showSettings", showSettings);
handler.Set("selectedBodyName", CelestialBodies.SelectedBody.Name); handler.Set("selectedBodyName", CelestialBodies.SelectedBody.Name);
handler.Save("BuildAdvanced.xml"); handler.Save("BuildAdvanced.xml");
GuiDisplaySize.OnSizeChanged -= this.OnSizeChanged; GuiDisplaySize.OnSizeChanged -= OnSizeChanged;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex, "BuildAdvanced.OnDestroy()");
} }
} }
   
protected void OnGUI() protected void OnGUI()
{ {
try try
{ {
if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorScreen.Parts) if (!visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0 || EditorLogic.fetch.editorScreen != EditorScreen.Parts)
{ {
return; return;
} }
   
if (this.stages == null) if (stages == null)
{ {
return; return;
} }
   
// Change the window title based on whether in compact mode or not. // Change the window title based on whether in compact mode or not.
var title = !this.compactMode ? "KERBAL ENGINEER REDUX " + EngineerGlobals.AssemblyVersion : "K.E.R. " + EngineerGlobals.AssemblyVersion + (this.showAtmosphericDetails ? " (ATMOS.)" : String.Empty); title = !compactMode ? "KERBAL ENGINEER REDUX " + EngineerGlobals.AssemblyVersion : "K.E.R. " + EngineerGlobals.AssemblyVersion;
   
// Reset the window size when the staging or something else has changed. // Reset the window size when the staging or something else has changed.
var stageCount = this.stages.Count(stage => this.showAllStages || stage.deltaV > 0); stagesLength = stages.Length;
if (this.hasChanged || stageCount != this.numberOfStages) if (showAllStages)
{ {
this.hasChanged = false; stagesCount = stagesLength;
this.numberOfStages = stageCount; }
  if (showAllStages == false)
this.position.width = 0; {
this.position.height = 0; stagesCount = 0;
  for (int i = 0; i < stagesLength; ++i)
  {
  if (stages[i].deltaV > 0.0f)
  {
  stagesCount = stagesCount + 1;
  }
  }
  }
   
  if (hasChanged || stagesCount != numberOfStages)
  {
  hasChanged = false;
  numberOfStages = stagesCount;
   
  position.width = 0;
  position.height = 0;
} }
   
GUI.skin = null; GUI.skin = null;
this.position = GUILayout.Window(this.GetInstanceID(), this.position, this.Window, title, this.windowStyle).ClampToScreen(); position = GUILayout.Window(GetInstanceID(), position, Window, title, windowStyle).ClampToScreen();
   
if (this.compactCheck > 0 && this.compactCollapseRight) if (compactCheck > 0 && compactCollapseRight)
{ {
this.position.x = this.compactRight - this.position.width; position.x = compactRight - position.width;
this.compactCheck--; compactCheck--;
} }
else if (this.compactCheck > 0) else if (compactCheck > 0)
{ {
this.compactCheck = 0; compactCheck = 0;
} }
   
// Check editor lock to manage click-through. // Check editor lock to manage click-through.
this.CheckEditorLock(); CheckEditorLock();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex, "BuildAdvanced.OnGUI()");
} }
} }
   
protected void Start() protected void Start()
{ {
try try
{ {
this.InitialiseStyles(); InitialiseStyles();
GuiDisplaySize.OnSizeChanged += this.OnSizeChanged; GuiDisplaySize.OnSizeChanged += OnSizeChanged;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex, "BuildAdvanced.Start()");
} }
} }
   
protected void Update() protected void Update()
{ {
try try
{ {
if (Input.GetKeyDown(KeyBinder.EditorShowHide)) if (Input.GetKeyDown(KeyBinder.EditorShowHide))
{ {
this.visible = !this.visible; visible = !visible;
if (!this.visible) if (!visible)
{ {
this.EditorLock(false); EditorLock(false);
} }
} }
   
if (!this.visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0) if (!visible || EditorLogic.fetch == null || EditorLogic.fetch.ship.parts.Count == 0)
{ {
this.bodiesList.enabled = false; bodiesList.enabled = false;
return; return;
} }
   
// Configure the simulation parameters based on the selected reference body. // Configure the simulation parameters based on the selected reference body.
SimManager.Gravity = CelestialBodies.SelectedBody.Gravity; SimManager.Gravity = CelestialBodies.SelectedBody.Gravity;
   
if (this.showAtmosphericDetails) if (showAtmosphericDetails)
{ {
SimManager.Atmosphere = CelestialBodies.SelectedBody.Atmosphere * 0.01d * this.atmosphericPercentage; SimManager.Atmosphere = CelestialBodies.SelectedBody.GetAtmospheres(Altitude);
} }
else else
{ {
SimManager.Atmosphere = 0; SimManager.Atmosphere = 0;
} }
   
SimManager.Velocity = this.atmosphericVelocity; SimManager.Mach = atmosphericMach;
   
SimManager.RequestSimulation(); SimManager.RequestSimulation();
SimManager.TryStartSimulation(); SimManager.TryStartSimulation();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced->Update"); Logger.Exception(ex, "BuildAdvanced.Update()");
} }
} }
   
/// <summary> /// <summary>
/// Checks whether the editor should be locked to stop click-through. /// Checks whether the editor should be locked to stop click-through.
/// </summary> /// </summary>
private void CheckEditorLock() private void CheckEditorLock()
{ {
if ((this.position.MouseIsOver() || this.bodiesList.Position.MouseIsOver()) && !this.isEditorLocked) if ((position.MouseIsOver() || bodiesList.Position.MouseIsOver()) && !isEditorLocked)
{ {
this.EditorLock(true); EditorLock(true);
} }
else if (!this.position.MouseIsOver() && !this.bodiesList.Position.MouseIsOver() && this.isEditorLocked) else if (!position.MouseIsOver() && !bodiesList.Position.MouseIsOver() && isEditorLocked)
{ {
this.EditorLock(false); EditorLock(false);
} }
} }
   
/// <summary> /// <summary>
/// Draws the atmospheric settings. /// Draws the atmospheric settings.
/// </summary> /// </summary>
private void DrawAtmosphericDetails() private void DrawAtmosphericDetails()
{ {
GUILayout.BeginHorizontal(); try
GUILayout.BeginVertical(); {
GUILayout.Label("Pressure: " + (this.atmosphericPercentage * 100.0f).ToString("F1") + "%", this.settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset)); GUILayout.BeginHorizontal();
GUI.skin = HighLogic.Skin; GUILayout.BeginVertical();
this.atmosphericPercentage = GUILayout.HorizontalSlider(this.atmosphericPercentage, 0, 1.0f); GUILayout.Label("Altitude: " + (Altitude * 0.001f).ToString("F1") + "km", settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset));
GUI.skin = null; GUI.skin = HighLogic.Skin;
GUILayout.EndVertical(); Altitude = GUILayout.HorizontalSlider(Altitude, 0.0f, (float)(CelestialBodies.SelectedBody.CelestialBody.atmosphereDepth));
  GUI.skin = null;
GUILayout.Space(5.0f); GUILayout.EndVertical();
   
GUILayout.BeginVertical(); GUILayout.Space(5.0f);
GUILayout.Label("Velocity: " + this.atmosphericVelocity.ToString("F1") + "m/s", this.settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset));  
GUI.skin = HighLogic.Skin; GUILayout.BeginVertical();
this.atmosphericVelocity = GUILayout.HorizontalSlider(this.atmosphericVelocity, 0, 2500f); GUILayout.Label("Mach: " + atmosphericMach.ToString("F2"), settingAtmoStyle, GUILayout.Width(125.0f * GuiDisplaySize.Offset));
GUI.skin = null; GUI.skin = HighLogic.Skin;
GUILayout.EndVertical(); atmosphericMach = GUILayout.HorizontalSlider(Mathf.Clamp(atmosphericMach, 0.0f, maxMach), 0.0f, maxMach);
GUILayout.EndHorizontal(); GUI.skin = null;
  GUILayout.EndVertical();
  GUILayout.EndHorizontal();
  }
  catch (Exception ex)
  {
  Logger.Exception(ex, "BuildAdvanced.DrawAtmosphericDetails()");
  }
} }
   
private void DrawBodiesList() private void DrawBodiesList()
{ {
if (CelestialBodies.SystemBody == CelestialBodies.SelectedBody) if (CelestialBodies.SystemBody == CelestialBodies.SelectedBody)
{ {
this.DrawBody(CelestialBodies.SystemBody); DrawBody(CelestialBodies.SystemBody);
} }
else else
{ {
foreach (var body in CelestialBodies.SystemBody.Children) foreach (CelestialBodies.BodyInfo body in CelestialBodies.SystemBody.Children)
{ {
this.DrawBody(body); DrawBody(body);
} }
} }
} }
   
private void DrawBody(CelestialBodies.BodyInfo bodyInfo, int depth = 0) private void DrawBody(CelestialBodies.BodyInfo bodyInfo, int depth = 0)
{ {
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Space(20.0f * depth); GUILayout.Space(20.0f * depth);
if (GUILayout.Button(bodyInfo.Children.Count > 0 ? bodyInfo.Name + " [" + bodyInfo.Children.Count + "]" : bodyInfo.Name, bodyInfo.Selected && bodyInfo.SelectedDepth == 0 ? this.bodiesButtonActiveStyle : this.bodiesButtonStyle)) if (GUILayout.Button(bodyInfo.Children.Count > 0 ? bodyInfo.Name + " [" + bodyInfo.Children.Count + "]" : bodyInfo.Name, bodyInfo.Selected && bodyInfo.SelectedDepth == 0 ? bodiesButtonActiveStyle : bodiesButtonStyle))
{ {
CelestialBodies.SetSelectedBody(bodyInfo.Name); CelestialBodies.SetSelectedBody(bodyInfo.Name);
this.bodiesList.Resize = true; Altitude = 0.0f;
  bodiesList.Resize = true;
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
if (bodyInfo.Selected) if (bodyInfo.Selected)
{ {
foreach (var body in bodyInfo.Children) for (int i = 0; i < bodyInfo.Children.Count; ++i)
{ {
this.DrawBody(body, depth + 1); DrawBody(bodyInfo.Children[i], depth + 1);
} }
} }
} }
   
/// <summary> /// <summary>
/// Draws the burn time column. /// Draws the burn time column.
/// </summary> /// </summary>
private void DrawBurnTime() private void DrawBurnTime()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset));
GUILayout.Label("BURN", this.titleStyle); GUILayout.Label("BURN", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(TimeFormatter.ConvertToString(stage.time), this.infoStyle); {
  GUILayout.Label(TimeFormatter.ConvertToString(stage.time), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the cost column. /// Draws the cost column.
/// </summary> /// </summary>
private void DrawCost() private void DrawCost()
{ {
GUILayout.BeginVertical(GUILayout.Width(110.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(110.0f * GuiDisplaySize.Offset));
GUILayout.Label("COST", this.titleStyle); GUILayout.Label("COST", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(Units.Cost(stage.cost, stage.totalCost), this.infoStyle); {
  GUILayout.Label(Units.Cost(stage.cost, stage.totalCost), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the deltaV column. /// Draws the deltaV column.
/// </summary> /// </summary>
private void DrawDeltaV() private void DrawDeltaV()
{ {
GUILayout.BeginVertical(GUILayout.Width(100.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.Label("DELTA-V", this.titleStyle); GUILayout.Label("DELTA-V", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(stage.deltaV.ToString("N0") + " / " + stage.inverseTotalDeltaV.ToString("N0") + "m/s", this.infoStyle); {
  GUILayout.Label(stage.deltaV.ToString("N0") + " / " + stage.inverseTotalDeltaV.ToString("N0") + "m/s", infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the specific impluse column. /// Draws the specific impluse column.
/// </summary> /// </summary>
private void DrawIsp() private void DrawIsp()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset));
GUILayout.Label("ISP", this.titleStyle); GUILayout.Label("ISP", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(stage.isp.ToString("F1") + "s", this.infoStyle); {
  GUILayout.Label(stage.isp.ToString("F1") + "s", infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the mass column. /// Draws the mass column.
/// </summary> /// </summary>
private void DrawMass() private void DrawMass()
{ {
GUILayout.BeginVertical(GUILayout.Width(110.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(110.0f * GuiDisplaySize.Offset));
GUILayout.Label("MASS", this.titleStyle); GUILayout.Label("MASS", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(Units.ToMass(stage.mass, stage.totalMass), this.infoStyle); {
  GUILayout.Label(Units.ToMass(stage.mass, stage.totalMass), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the part count column. /// Draws the part count column.
/// </summary> /// </summary>
private void DrawPartCount() private void DrawPartCount()
{ {
GUILayout.BeginVertical(GUILayout.Width(50.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(50.0f * GuiDisplaySize.Offset));
GUILayout.Label("PARTS", this.titleStyle); GUILayout.Label("PARTS", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(stage.partCount + " / " + stage.totalPartCount, this.infoStyle); {
  GUILayout.Label(stage.partCount + " / " + stage.totalPartCount, infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the settings panel. /// Draws the settings panel.
/// </summary> /// </summary>
private void DrawSettings() private void DrawSettings()
{ {
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Compact mode collapses to the:", this.settingStyle); GUILayout.Label("Compact mode collapses to the:", settingStyle);
this.compactCollapseRight = !GUILayout.Toggle(!this.compactCollapseRight, "LEFT", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); compactCollapseRight = !GUILayout.Toggle(!compactCollapseRight, "LEFT", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
this.compactCollapseRight = GUILayout.Toggle(this.compactCollapseRight, "RIGHT", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); compactCollapseRight = GUILayout.Toggle(compactCollapseRight, "RIGHT", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Simulate using vectored thrust values:"); GUILayout.Label("Simulate using vectored thrust values:");
SimManager.vectoredThrust = GUILayout.Toggle(SimManager.vectoredThrust, "ENABLED", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); SimManager.vectoredThrust = GUILayout.Toggle(SimManager.vectoredThrust, "ENABLED", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Build Engineer Overlay:", this.settingStyle); GUILayout.Label("Build Engineer Overlay:", settingStyle);
BuildOverlay.Visible = GUILayout.Toggle(BuildOverlay.Visible, "VISIBLE", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlay.Visible = GUILayout.Toggle(BuildOverlay.Visible, "VISIBLE", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
BuildOverlayPartInfo.NamesOnly = GUILayout.Toggle(BuildOverlayPartInfo.NamesOnly, "NAMES ONLY", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlayPartInfo.NamesOnly = GUILayout.Toggle(BuildOverlayPartInfo.NamesOnly, "NAMES ONLY", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
BuildOverlayPartInfo.ClickToOpen = GUILayout.Toggle(BuildOverlayPartInfo.ClickToOpen, "CLICK TO OPEN", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); BuildOverlayPartInfo.ClickToOpen = GUILayout.Toggle(BuildOverlayPartInfo.ClickToOpen, "CLICK TO OPEN", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Flight Engineer activation mode:", this.settingStyle); GUILayout.Label("Flight Engineer activation mode:", settingStyle);
FlightEngineerCore.IsCareerMode = GUILayout.Toggle(FlightEngineerCore.IsCareerMode, "CAREER", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsCareerMode = GUILayout.Toggle(FlightEngineerCore.IsCareerMode, "CAREER", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
FlightEngineerCore.IsCareerMode = !GUILayout.Toggle(!FlightEngineerCore.IsCareerMode, "PARTLESS", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsCareerMode = !GUILayout.Toggle(!FlightEngineerCore.IsCareerMode, "PARTLESS", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("Flight Engineer Career Limitations:", this.settingStyle); GUILayout.Label("Flight Engineer Career Limitations:", settingStyle);
FlightEngineerCore.IsKerbalLimited = GUILayout.Toggle(FlightEngineerCore.IsKerbalLimited, "KERBAL", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsKerbalLimited = GUILayout.Toggle(FlightEngineerCore.IsKerbalLimited, "KERBAL", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
FlightEngineerCore.IsTrackingStationLimited = GUILayout.Toggle(FlightEngineerCore.IsTrackingStationLimited, "TRACKING", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)); FlightEngineerCore.IsTrackingStationLimited = GUILayout.Toggle(FlightEngineerCore.IsTrackingStationLimited, "TRACKING", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.Label("GUI Size: " + GuiDisplaySize.Increment, this.settingStyle); GUILayout.Label("GUI Size: " + GuiDisplaySize.Increment, settingStyle);
if (GUILayout.Button("<", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset))) if (GUILayout.Button("<", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)))
{ {
GuiDisplaySize.Increment--; GuiDisplaySize.Increment--;
} }
if (GUILayout.Button(">", this.buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset))) if (GUILayout.Button(">", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)))
{ {
GuiDisplaySize.Increment++; GuiDisplaySize.Increment++;
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
GUILayout.Label("Minimum delay between simulations: " + SimManager.minSimTime.Milliseconds + "ms", this.settingStyle); GUILayout.Label("Minimum delay between simulations: " + SimManager.minSimTime.Milliseconds + "ms", settingStyle);
GUI.skin = HighLogic.Skin; GUI.skin = HighLogic.Skin;
SimManager.minSimTime = new TimeSpan(0, 0, 0, 0, (int)GUILayout.HorizontalSlider(SimManager.minSimTime.Milliseconds, 0, 2000.0f)); SimManager.minSimTime = new TimeSpan(0, 0, 0, 0, (int)GUILayout.HorizontalSlider(SimManager.minSimTime.Milliseconds, 0, 2000.0f));
GUI.skin = null; GUI.skin = null;
} }
   
/// <summary> /// <summary>
/// Draws the stage number column. /// Draws the stage number column.
/// </summary> /// </summary>
private void DrawStageNumbers() private void DrawStageNumbers()
{ {
GUILayout.BeginVertical(GUILayout.Width(30.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(30.0f * GuiDisplaySize.Offset));
GUILayout.Label(string.Empty, this.titleStyle); GUILayout.Label(string.Empty, titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label("S" + stage.number, this.titleStyle); {
  GUILayout.Label("S" + stage.number, titleStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the thrust column. /// Draws the thrust column.
/// </summary> /// </summary>
private void DrawThrust() private void DrawThrust()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset));
GUILayout.Label("THRUST", this.titleStyle); GUILayout.Label("THRUST", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(stage.thrust.ToForce(), this.infoStyle); {
  GUILayout.Label(stage.thrust.ToForce(), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Draws the torque column. /// Draws the torque column.
/// </summary> /// </summary>
private void DrawTorque() private void DrawTorque()
{ {
GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(75.0f * GuiDisplaySize.Offset));
GUILayout.Label("TORQUE", this.titleStyle); GUILayout.Label("TORQUE", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(stage.maxThrustTorque.ToTorque(), this.infoStyle); {
  GUILayout.Label(stage.maxThrustTorque.ToTorque(), infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
/// <summary> /// <summary>
/// Drwas the thrust to weight ratio column. /// Drwas the thrust to weight ratio column.
/// </summary> /// </summary>
private void DrawTwr() private void DrawTwr()
{ {
GUILayout.BeginVertical(GUILayout.Width(100.0f * GuiDisplaySize.Offset)); GUILayout.BeginVertical(GUILayout.Width(100.0f * GuiDisplaySize.Offset));
GUILayout.Label("TWR (MAX)", this.titleStyle); GUILayout.Label("TWR (MAX)", titleStyle);
foreach (var stage in this.stages) for (int i = 0; i < stagesLength; ++i)
{ {
if (this.showAllStages || stage.deltaV > 0) stage = stages[i];
{ if (showAllStages || stage.deltaV > 0.0)
GUILayout.Label(stage.thrustToWeight.ToString("F2") + " (" + stage.maxThrustToWeight.ToString("F2") + ")", this.infoStyle); {
  GUILayout.Label(stage.thrustToWeight.ToString("F2") + " (" + stage.maxThrustToWeight.ToString("F2") + ")", infoStyle);
} }
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
private void EditorLock(bool state) private void EditorLock(bool state)
{ {
if (state) if (state)
{ {
EditorLogic.fetch.Lock(true, true, true, "KER_BuildAdvanced"); InputLockManager.SetControlLock(ControlTypes.All, "KER_BuildAdvanced");
BuildOverlayPartInfo.Hidden = true; BuildOverlayPartInfo.Hidden = true;
this.isEditorLocked = true; isEditorLocked = true;
} }
else else
{ {
EditorLogic.fetch.Unlock("KER_BuildAdvanced"); InputLockManager.SetControlLock(ControlTypes.None, "KER_BuildAdvanced");
BuildOverlayPartInfo.Hidden = false; BuildOverlayPartInfo.Hidden = false;
this.isEditorLocked = false; isEditorLocked = false;
} }
} }
   
private void GetStageInfo() private void GetStageInfo()
{ {
this.stages = SimManager.Stages; stages = SimManager.Stages;
  if (stages != null && stages.Length > 0)
  {
  maxMach = stages[stages.Length - 1].maxMach;
  }
} }
   
/// <summary> /// <summary>
/// Initialises all the styles that are required. /// Initialises all the styles that are required.
/// </summary> /// </summary>
private void InitialiseStyles() private void InitialiseStyles()
{ {
this.windowStyle = new GUIStyle(HighLogic.Skin.window) windowStyle = new GUIStyle(HighLogic.Skin.window)
{ {
alignment = TextAnchor.UpperLeft alignment = TextAnchor.UpperLeft
}; };
   
this.areaStyle = new GUIStyle(HighLogic.Skin.box) areaStyle = new GUIStyle(HighLogic.Skin.box)
{ {
padding = new RectOffset(0, 0, 9, 0) padding = new RectOffset(0, 0, 9, 0)
}; };
   
this.areaSettingStyle = new GUIStyle(HighLogic.Skin.box) areaSettingStyle = new GUIStyle(HighLogic.Skin.box)
{ {
padding = new RectOffset(10, 10, 10, 10) padding = new RectOffset(10, 10, 10, 10)
}; };
   
this.buttonStyle = new GUIStyle(HighLogic.Skin.button) buttonStyle = new GUIStyle(HighLogic.Skin.button)
{ {
normal = normal =
{ {
textColor = Color.white textColor = Color.white
}, },
fontSize = (int)(11 * GuiDisplaySize.Offset), fontSize = (int)(11 * GuiDisplaySize.Offset),
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter alignment = TextAnchor.MiddleCenter
}; };
   
this.titleStyle = new GUIStyle(HighLogic.Skin.label) titleStyle = new GUIStyle(HighLogic.Skin.label)
{ {
normal = normal =
{ {
textColor = Color.white textColor = Color.white
}, },
fontSize = (int)(11 * GuiDisplaySize.Offset), fontSize = (int)(11 * GuiDisplaySize.Offset),
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter, alignment = TextAnchor.MiddleCenter,
stretchWidth = true, stretchWidth = true,
}; };
   
this.infoStyle = new GUIStyle(HighLogic.Skin.label) infoStyle = new GUIStyle(HighLogic.Skin.label)
{ {
fontSize = (int)(11 * GuiDisplaySize.Offset), fontSize = (int)(11 * GuiDisplaySize.Offset),
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter, alignment = TextAnchor.MiddleCenter,
stretchWidth = true stretchWidth = true
}; };
   
this.settingStyle = new GUIStyle(this.titleStyle) settingStyle = new GUIStyle(titleStyle)
{ {
alignment = TextAnchor.MiddleLeft, alignment = TextAnchor.MiddleLeft,
stretchWidth = true, stretchWidth = true,
stretchHeight = true stretchHeight = true
}; };
   
this.settingAtmoStyle = new GUIStyle(this.titleStyle) settingAtmoStyle = new GUIStyle(titleStyle)
{ {
margin = new RectOffset(), margin = new RectOffset(),
padding = new RectOffset(), padding = new RectOffset(),
alignment = TextAnchor.UpperLeft alignment = TextAnchor.UpperLeft
}; };
   
this.bodiesButtonStyle = new GUIStyle(HighLogic.Skin.button) bodiesButtonStyle = new GUIStyle(HighLogic.Skin.button)
{ {
margin = new RectOffset(0, 0, 2, 0), margin = new RectOffset(0, 0, 2, 0),
padding = new RectOffset(5, 5, 5, 5), padding = new RectOffset(5, 5, 5, 5),
normal = normal =
{ {
textColor = Color.white textColor = Color.white
}, },
active = active =
{ {
textColor = Color.white textColor = Color.white
}, },
fontSize = (int)(11 * GuiDisplaySize.Offset), fontSize = (int)(11 * GuiDisplaySize.Offset),
fontStyle = FontStyle.Bold, fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter, alignment = TextAnchor.MiddleCenter,
fixedHeight = 20.0f fixedHeight = 20.0f
}; };
   
this.bodiesButtonActiveStyle = new GUIStyle(this.bodiesButtonStyle) bodiesButtonActiveStyle = new GUIStyle(bodiesButtonStyle)
{ {
normal = this.bodiesButtonStyle.onNormal, normal = bodiesButtonStyle.onNormal,
hover = this.bodiesButtonStyle.onHover hover = bodiesButtonStyle.onHover
}; };
} }
   
/// <summary> /// <summary>
/// Loads the settings when this object is created. /// Loads the settings when this object is created.
/// </summary> /// </summary>
private void Load() private void Load()
{ {
try try
{ {
var handler = SettingHandler.Load("BuildAdvanced.xml"); SettingHandler handler = SettingHandler.Load("BuildAdvanced.xml");
handler.Get("visible", ref this.visible); handler.Get("visible", ref visible);
this.position.x = handler.Get("windowPositionX", this.position.x); position.x = handler.Get("windowPositionX", position.x);
this.position.y = handler.Get("windowPositionY", this.position.y); position.y = handler.Get("windowPositionY", position.y);
handler.Get("compactMode", ref this.compactMode); handler.Get("compactMode", ref compactMode);
handler.Get("compactCollapseRight", ref this.compactCollapseRight); handler.Get("compactCollapseRight", ref compactCollapseRight);
handler.Get("showAllStages", ref this.showAllStages); handler.Get("showAllStages", ref showAllStages);
handler.Get("showAtmosphericDetails", ref this.showAtmosphericDetails); handler.Get("showAtmosphericDetails", ref showAtmosphericDetails);
handler.Get("showSettings", ref this.showSettings); handler.Get("showSettings", ref showSettings);
CelestialBodies.SetSelectedBody(handler.Get("selectedBodyName", CelestialBodies.SelectedBody.Name)); CelestialBodies.SetSelectedBody(handler.Get("selectedBodyName", CelestialBodies.SelectedBody.Name));
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildAdvanced->Load"); Logger.Exception(ex, "BuildAdvanced.Load()");
} }
} }
   
private void OnSizeChanged() private void OnSizeChanged()
{ {
this.InitialiseStyles(); InitialiseStyles();
this.hasChanged = true; hasChanged = true;
} }
   
/// <summary> /// <summary>
/// Draws the OnGUI window. /// Draws the OnGUI window.
/// </summary> /// </summary>
private void Window(int windowId) private void Window(int windowId)
{ {
try try
{ {
  compactModeRect = new Rect(position.width - 70.0f * GuiDisplaySize.Offset, 5.0f, 65.0f * GuiDisplaySize.Offset, 20.0f);
   
// Draw the compact mode toggle. // Draw the compact mode toggle.
if (GUI.Toggle(new Rect(this.position.width - 70.0f * GuiDisplaySize.Offset, 5.0f, 65.0f * GuiDisplaySize.Offset, 20.0f), this.compactMode, "COMPACT", this.buttonStyle) != this.compactMode) if (GUI.Toggle(compactModeRect, compactMode, "COMPACT", buttonStyle) != compactMode)
{ {
this.hasChanged = true; hasChanged = true;
this.compactCheck = 2; compactCheck = 2;
this.compactRight = this.position.xMax; compactRight = position.xMax;
this.compactMode = !this.compactMode; compactMode = !compactMode;
} }
   
// When not in compact mode draw the 'All Stages' and 'Atmospheric' toggles. // When not in compact mode draw the 'All Stages' and 'Atmospheric' toggles.
if (!this.compactMode) if (!compactMode)
{ {
if (GUI.Toggle(new Rect(this.position.width - 143.0f * GuiDisplaySize.Offset, 5.0f, 70.0f * GuiDisplaySize.Offset, 20.0f), this.showSettings, "SETTINGS", this.buttonStyle) != this.showSettings) if (GUI.Toggle(new Rect(position.width - 143.0f * GuiDisplaySize.Offset, 5.0f, 70.0f * GuiDisplaySize.Offset, 20.0f), showSettings, "SETTINGS", buttonStyle) != showSettings)
{ {
this.hasChanged = true; hasChanged = true;
this.showSettings = !this.showSettings; showSettings = !showSettings;
} }
   
if (GUI.Toggle(new Rect(this.position.width - 226.0f * GuiDisplaySize.Offset, 5.0f, 80.0f * GuiDisplaySize.Offset, 20.0f), this.showAllStages, "ALL STAGES", this.buttonStyle) != this.showAllStages) if (GUI.Toggle(new Rect(position.width - 226.0f * GuiDisplaySize.Offset, 5.0f, 80.0f * GuiDisplaySize.Offset, 20.0f), showAllStages, "ALL STAGES", buttonStyle) != showAllStages)
{ {
this.hasChanged = true; hasChanged = true;
this.showAllStages = !this.showAllStages; showAllStages = !showAllStages;
} }
   
if (GUI.Toggle(new Rect(this.position.width - 324.0f * GuiDisplaySize.Offset, 5.0f, 95.0f * GuiDisplaySize.Offset, 20.0f), this.showAtmosphericDetails, "ATMOSPHERIC", this.buttonStyle) != this.showAtmosphericDetails) if (GUI.Toggle(new Rect(position.width - 324.0f * GuiDisplaySize.Offset, 5.0f, 95.0f * GuiDisplaySize.Offset, 20.0f), showAtmosphericDetails, "ATMOSPHERIC", buttonStyle) != showAtmosphericDetails)
{ {
this.hasChanged = true; hasChanged = true;
this.showAtmosphericDetails = !this.showAtmosphericDetails; showAtmosphericDetails = !showAtmosphericDetails;
} }
   
this.bodiesListPosition = new Rect(this.position.width - 452.0f * GuiDisplaySize.Offset, 5.0f, 125.0f * GuiDisplaySize.Offset, 20.0f); bodiesListPosition = new Rect(position.width - 452.0f * GuiDisplaySize.Offset, 5.0f, 125.0f * GuiDisplaySize.Offset, 20.0f);
this.bodiesList.enabled = GUI.Toggle(this.bodiesListPosition, this.bodiesList.enabled, "BODY: " + CelestialBodies.SelectedBody.Name.ToUpper(), this.buttonStyle); bodiesList.enabled = GUI.Toggle(bodiesListPosition, bodiesList.enabled, "BODY: " + CelestialBodies.SelectedBody.Name.ToUpper(), buttonStyle);
this.bodiesList.SetPosition(this.bodiesListPosition.Translate(this.position)); bodiesList.SetPosition(bodiesListPosition.Translate(position));
  }
  else
  {
  if (GUI.Toggle(new Rect(position.width - 133.0f * GuiDisplaySize.Offset, 5.0f, 60.0f * GuiDisplaySize.Offset, 20.0f), showAtmosphericDetails, "ATMO", buttonStyle) != showAtmosphericDetails)
  {
  hasChanged = true;
  showAtmosphericDetails = !showAtmosphericDetails;
  }
} }
   
// Draw the main informational display box. // Draw the main informational display box.
if (!this.compactMode) if (!compactMode)
{ {
GUILayout.BeginHorizontal(this.areaStyle); GUILayout.BeginHorizontal(areaStyle);
this.DrawStageNumbers(); DrawStageNumbers();
this.DrawPartCount(); DrawPartCount();
this.DrawCost(); DrawCost();
this.DrawMass(); DrawMass();
this.DrawIsp(); DrawIsp();
this.DrawThrust(); DrawThrust();
this.DrawTorque(); DrawTorque();
this.DrawTwr(); DrawTwr();
this.DrawDeltaV(); DrawDeltaV();
this.DrawBurnTime(); DrawBurnTime();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
   
if (this.showAtmosphericDetails) if (showAtmosphericDetails && !compactMode)
{ {
GUILayout.BeginVertical(this.areaSettingStyle); GUILayout.BeginVertical(areaSettingStyle);
this.DrawAtmosphericDetails(); DrawAtmosphericDetails();
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
   
if (this.showSettings) if (showSettings)
{ {
GUILayout.BeginVertical(this.areaSettingStyle); GUILayout.BeginVertical(areaSettingStyle);
this.DrawSettings(); DrawSettings();
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
} }
else // Draw only a few details when in compact mode. else // Draw only a few details when in compact mode.
{ {
GUILayout.BeginHorizontal(this.areaStyle); GUILayout.BeginHorizontal(areaStyle);
this.DrawStageNumbers(); DrawStageNumbers();
this.DrawTwr(); DrawTwr();
this.DrawDeltaV(); DrawDeltaV();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
   
GUI.DragWindow(); GUI.DragWindow();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex, "BuildAdvanced.Window()");
} }
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives  
   
using System;  
using System.Collections.Generic;  
using System.Linq;  
   
using KerbalEngineer.Extensions;  
using KerbalEngineer.Helpers;  
   
using UnityEngine;  
   
#endregion  
   
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
  using System;
  using System.Collections.Generic;
  using Extensions;
  using Helpers;
  using UnityEngine;
   
public class BuildOverlayPartInfo : MonoBehaviour public class BuildOverlayPartInfo : MonoBehaviour
{ {
#region Fields  
   
private static bool clickToOpen = true; private static bool clickToOpen = true;
  private static ModuleGenerator.GeneratorResource generatorResource;
  private static ModuleAlternator moduleAlternator;
  private static ModuleDataTransmitter moduleDataTransmitter;
  private static ModuleDeployableSolarPanel moduleDeployableSolarPanel;
  private static ModuleGenerator moduleGenerator;
  private static ModuleGimbal moduleGimbal;
  private static ModuleParachute moduleParachute;
  private static ModuleRCS moduleRcs;
  private static ModuleReactionWheel moduleReactionWheel;
  private static ModuleResource moduleResource;
  private static ModuleScienceExperiment moduleScienceExperiment;
private static bool namesOnly; private static bool namesOnly;
  private static Part part;
  private static PartInfoItem partInfoItem;
  private static PartResource partResource;
  private static Propellant propellant;
  private static PartExtensions.ProtoModuleDecoupler protoModuleDecoupler;
  private static PartExtensions.ProtoModuleEngine protoModuleEngine;
private static bool visible = true; private static bool visible = true;
   
private readonly List<PartInfoItem> infoItems = new List<PartInfoItem>(); private readonly List<PartInfoItem> infoItems = new List<PartInfoItem>();
   
private Rect position; private Rect position;
private Part selectedPart; private Part selectedPart;
private bool showInfo; private bool showInfo;
private bool skipFrame; private bool skipFrame;
   
#endregion  
   
#region Properties  
   
public static bool ClickToOpen public static bool ClickToOpen
{ {
get { return clickToOpen; } get
set { clickToOpen = value; } {
  return clickToOpen;
  }
  set
  {
  clickToOpen = value;
  }
} }
   
public static bool Hidden { get; set; } public static bool Hidden { get; set; }
   
public static bool NamesOnly public static bool NamesOnly
{ {
get { return namesOnly; } get
set { namesOnly = value; } {
  return namesOnly;
  }
  set
  {
  namesOnly = value;
  }
} }
   
public static bool Visible public static bool Visible
{ {
get { return visible; } get
set { visible = value; } {
} return visible;
  }
#endregion set
  {
#region Methods: protected visible = value;
  }
  }
   
protected void OnGUI() protected void OnGUI()
{ {
try try
{ {
if (!Visible || Hidden || this.selectedPart == null) if (!Visible || Hidden || selectedPart == null)
{ {
return; return;
} }
   
this.position = GUILayout.Window(this.GetInstanceID(), this.position, this.Window, String.Empty, BuildOverlay.WindowStyle); position = GUILayout.Window(GetInstanceID(), position, Window, String.Empty, BuildOverlay.WindowStyle);
} }
catch (Exception ex) catch (Exception ex)
   
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
protected void Update() protected void Update()
{ {
try try
{ {
if (!Visible || Hidden || EditorLogic.RootPart == null || EditorLogic.fetch.editorScreen != EditorScreen.Parts) if (!Visible || Hidden || EditorLogic.RootPart == null || EditorLogic.fetch.editorScreen != EditorScreen.Parts)
{ {
return; return;
} }
   
this.position.x = Mathf.Clamp(Input.mousePosition.x + 16.0f, 0.0f, Screen.width - this.position.width); position.x = Mathf.Clamp(Input.mousePosition.x + 16.0f, 0.0f, Screen.width - position.width);
this.position.y = Mathf.Clamp(Screen.height - Input.mousePosition.y, 0.0f, Screen.height - this.position.height); position.y = Mathf.Clamp(Screen.height - Input.mousePosition.y, 0.0f, Screen.height - position.height);
if (this.position.x < Input.mousePosition.x + 20.0f) if (position.x < Input.mousePosition.x + 20.0f)
{ {
this.position.y = Mathf.Clamp(this.position.y + 20.0f, 0.0f, Screen.height - this.position.height); position.y = Mathf.Clamp(position.y + 20.0f, 0.0f, Screen.height - position.height);
} }
if (this.position.x < Input.mousePosition.x + 16.0f && this.position.y < Screen.height - Input.mousePosition.y) if (position.x < Input.mousePosition.x + 16.0f && position.y < Screen.height - Input.mousePosition.y)
{ {
this.position.x = Input.mousePosition.x - 3 - this.position.width; position.x = Input.mousePosition.x - 3 - position.width;
} }
   
this.infoItems.Clear(); part = EditorLogic.fetch.ship.parts.Find(p => p.stackIcon.highlightIcon) ?? EditorLogic.SelectedPart;
var part = EditorLogic.fetch.ship.parts.Find(p => p.stackIcon.highlightIcon) ?? EditorLogic.SelectedPart;  
if (part != null) if (part != null)
{ {
if (!part.Equals(this.selectedPart)) if (!part.Equals(selectedPart))
{ {
this.selectedPart = part; selectedPart = part;
this.ResetInfo(); ResetInfo();
} }
if (NamesOnly || this.skipFrame) if (NamesOnly || skipFrame)
{ {
this.skipFrame = false; skipFrame = false;
return; return;
} }
   
this.SetCostInfo(); if (!showInfo && Input.GetMouseButtonDown(2))
this.SetMassItems(); {
this.SetResourceItems(); showInfo = true;
this.SetEngineInfo(); }
this.SetAlternatorInfo(); else if (ClickToOpen && showInfo && Input.GetMouseButtonDown(2))
this.SetGimbalInfo(); {
this.SetRcsInfo(); ResetInfo();
this.SetParachuteInfo(); }
this.SetSasInfo();  
this.SetReactionWheelInfo(); if (showInfo)
this.SetSolarPanelInfo(); {
this.SetGeneratorInfo(); PartInfoItem.Release(infoItems);
this.SetDecouplerInfo(); infoItems.Clear();
this.SetTransmitterInfo(); SetCostInfo();
this.SetScienceExperimentInfo(); SetMassItems();
this.SetScienceContainerInfo(); SetResourceItems();
this.SetSingleActivationInfo(); SetEngineInfo();
  SetAlternatorInfo();
if (!this.showInfo && Input.GetMouseButtonDown(2)) SetGimbalInfo();
{ SetRcsInfo();
this.showInfo = true; SetParachuteInfo();
} SetSasInfo();
else if (ClickToOpen && this.showInfo && Input.GetMouseButtonDown(2)) SetReactionWheelInfo();
{ SetSolarPanelInfo();
this.ResetInfo(); SetGeneratorInfo();
  SetDecouplerInfo();
  SetTransmitterInfo();
  SetScienceExperimentInfo();
  SetScienceContainerInfo();
  SetSingleActivationInfo();
} }
} }
else else
{ {
this.selectedPart = null; selectedPart = null;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion  
   
#region Methods: private  
   
private void ResetInfo() private void ResetInfo()
{ {
this.showInfo = !clickToOpen; showInfo = !clickToOpen;
this.skipFrame = true; skipFrame = true;
this.position.width = namesOnly || clickToOpen ? 0.0f : 200.0f; position.width = namesOnly || clickToOpen ? 0.0f : 200.0f;
this.position.height = 0.0f; position.height = 0.0f;
} }
   
private void SetAlternatorInfo() private void SetAlternatorInfo()
{ {
if (!this.selectedPart.HasModule<ModuleAlternator>()) moduleAlternator = selectedPart.GetModule<ModuleAlternator>();
{ if (moduleAlternator != null)
return; {
} infoItems.Add(PartInfoItem.Create("Alternator"));
  for (int i = 0; i < moduleAlternator.outputResources.Count; ++i)
var alternator = this.selectedPart.GetModule<ModuleAlternator>(); {
this.infoItems.Add(new PartInfoItem("Alternator")); moduleResource = moduleAlternator.outputResources[i];
foreach (var resource in alternator.outputResources) infoItems.Add(PartInfoItem.Create("\t" + moduleResource.name, moduleResource.rate.ToRate()));
{ }
this.infoItems.Add(new PartInfoItem("\t" + resource.name, resource.rate.ToRate()));  
} }
} }
   
private void SetCostInfo() private void SetCostInfo()
{ {
this.infoItems.Add(new PartInfoItem("Cost", Units.ConcatF(this.selectedPart.GetCostDry(), this.selectedPart.GetCostWet()))); infoItems.Add(PartInfoItem.Create("Cost", Units.ConcatF(selectedPart.GetCostDry(), selectedPart.GetCostWet())));
} }
   
private void SetDecouplerInfo() private void SetDecouplerInfo()
{ {
if (!this.selectedPart.IsDecoupler()) protoModuleDecoupler = selectedPart.GetProtoModuleDecoupler();
{ if (protoModuleDecoupler != null)
return; {
} infoItems.Add(PartInfoItem.Create("Ejection Force", protoModuleDecoupler.EjectionForce.ToForce()));
  if (protoModuleDecoupler.IsOmniDecoupler)
var decoupler = this.selectedPart.GetProtoModuleDecoupler(); {
this.infoItems.Add(new PartInfoItem("Ejection Force", decoupler.EjectionForce.ToForce())); infoItems.Add(PartInfoItem.Create("Omni-directional"));
if (decoupler.IsOmniDecoupler) }
{  
this.infoItems.Add(new PartInfoItem("Omni-directional"));  
} }
} }
   
private void SetEngineInfo() private void SetEngineInfo()
{ {
if (!this.selectedPart.IsEngine()) protoModuleEngine = selectedPart.GetProtoModuleEngine();
{ if (protoModuleEngine != null)
return; {
} infoItems.Add(PartInfoItem.Create("Thrust", Units.ToForce(protoModuleEngine.MinimumThrust, protoModuleEngine.MaximumThrust)));
  infoItems.Add(PartInfoItem.Create("Isp", Units.ConcatF(protoModuleEngine.GetSpecificImpulse(1.0f), protoModuleEngine.GetSpecificImpulse(0.0f)) + "s"));
var engine = this.selectedPart.GetProtoModuleEngine(); if (protoModuleEngine.Propellants.Count > 0)
this.infoItems.Add(new PartInfoItem("Thrust", Units.ToForce(engine.MinimumThrust, engine.MaximumThrust))); {
this.infoItems.Add(new PartInfoItem("Isp", Units.ConcatF(engine.GetSpecificImpulse(1.0f), engine.GetSpecificImpulse(0.0f)) + "s")); infoItems.Add(PartInfoItem.Create("Propellants"));
if (engine.Propellants.Count > 0)  
{ float totalRatio = 0.0f;
this.infoItems.Add(new PartInfoItem("Propellants")); for (int i = 0; i < protoModuleEngine.Propellants.Count; ++i)
var totalRatio = engine.Propellants.Sum(p => p.ratio); {
foreach (var propellant in engine.Propellants) totalRatio = totalRatio + protoModuleEngine.Propellants[i].ratio;
{ }
this.infoItems.Add(new PartInfoItem("\t" + propellant.name, (propellant.ratio / totalRatio).ToPercent()));  
  for (int i = 0; i < protoModuleEngine.Propellants.Count; ++i)
  {
  propellant = protoModuleEngine.Propellants[i];
  infoItems.Add(PartInfoItem.Create("\t" + propellant.name, (propellant.ratio / totalRatio).ToPercent()));
  }
} }
} }
} }
   
private void SetGeneratorInfo() private void SetGeneratorInfo()
{ {
if (!this.selectedPart.HasModule<ModuleGenerator>()) moduleGenerator = selectedPart.GetModule<ModuleGenerator>();
{ if (moduleGenerator != null)
return; {
} if (moduleGenerator.inputList.Count > 0)
  {
var generator = this.selectedPart.GetModule<ModuleGenerator>(); infoItems.Add(PartInfoItem.Create("Generator Input"));
if (generator.inputList.Count > 0) for (int i = 0; i < moduleGenerator.inputList.Count; ++i)
{ {
this.infoItems.Add(new PartInfoItem("Generator Input")); generatorResource = moduleGenerator.inputList[i];
foreach (var resource in generator.inputList) infoItems.Add(PartInfoItem.Create("\t" + generatorResource.name, generatorResource.rate.ToRate()));
{ }
this.infoItems.Add(new PartInfoItem("\t" + resource.name, resource.rate.ToRate())); }
} if (moduleGenerator.outputList.Count > 0)
} {
if (generator.outputList.Count > 0) infoItems.Add(PartInfoItem.Create("Generator Output"));
{ for (int i = 0; i < moduleGenerator.outputList.Count; ++i)
this.infoItems.Add(new PartInfoItem("Generator Output")); {
foreach (var resource in generator.outputList) generatorResource = moduleGenerator.outputList[i];
{ infoItems.Add(PartInfoItem.Create("\t" + generatorResource.name, generatorResource.rate.ToRate()));
this.infoItems.Add(new PartInfoItem("\t" + resource.name, resource.rate.ToRate())); }
} }
} if (moduleGenerator.isAlwaysActive)
if (generator.isAlwaysActive) {
{ infoItems.Add(PartInfoItem.Create("Generator is Always Active"));
this.infoItems.Add(new PartInfoItem("Generator is Always Active")); }
} }
} }
   
private void SetGimbalInfo() private void SetGimbalInfo()
{ {
if (!this.selectedPart.HasModule<ModuleGimbal>()) moduleGimbal = selectedPart.GetModule<ModuleGimbal>();
{ if (moduleGimbal != null)
return; {
} infoItems.Add(PartInfoItem.Create("Thrust Vectoring", moduleGimbal.gimbalRange.ToString("F2")));
  }
var gimbal = this.selectedPart.GetModule<ModuleGimbal>();  
this.infoItems.Add(new PartInfoItem("Thrust Vectoring", gimbal.gimbalRange.ToString("F2")));  
} }
   
private void SetMassItems() private void SetMassItems()
{ {
if (this.selectedPart.physicalSignificance == Part.PhysicalSignificance.FULL) if (selectedPart.physicalSignificance == Part.PhysicalSignificance.FULL)
{ {
this.infoItems.Add(new PartInfoItem("Mass", Units.ToMass(this.selectedPart.GetDryMass(), this.selectedPart.GetWetMass()))); infoItems.Add(PartInfoItem.Create("Mass", Units.ToMass(selectedPart.GetDryMass(), selectedPart.GetWetMass())));
} }
} }
   
private void SetParachuteInfo() private void SetParachuteInfo()
{ {
if (!this.selectedPart.HasModule<ModuleParachute>()) moduleParachute = selectedPart.GetModule<ModuleParachute>();
{ if (moduleParachute != null)
return; {
} infoItems.Add(PartInfoItem.Create("Deployed Drag", Units.ConcatF(moduleParachute.semiDeployedDrag, moduleParachute.fullyDeployedDrag)));
  infoItems.Add(PartInfoItem.Create("Deployment Altitude", moduleParachute.deployAltitude.ToDistance()));
var parachute = this.selectedPart.GetModule<ModuleParachute>(); infoItems.Add(PartInfoItem.Create("Deployment Pressure", moduleParachute.minAirPressureToOpen.ToString("F2")));
this.infoItems.Add(new PartInfoItem("Deployed Drag", Units.ConcatF(parachute.semiDeployedDrag, parachute.fullyDeployedDrag))); }
this.infoItems.Add(new PartInfoItem("Deployment Altitude", parachute.deployAltitude.ToDistance()));  
this.infoItems.Add(new PartInfoItem("Deployment Pressure", parachute.minAirPressureToOpen.ToString("F2")));  
} }
   
private void SetRcsInfo() private void SetRcsInfo()
{ {
if (!this.selectedPart.HasModule<ModuleRCS>()) moduleRcs = selectedPart.GetModule<ModuleRCS>();
{ if (moduleRcs != null)
return; {
} infoItems.Add(PartInfoItem.Create("Thruster Power", moduleRcs.thrusterPower.ToForce()));
  infoItems.Add(PartInfoItem.Create("Specific Impulse", Units.ConcatF(moduleRcs.atmosphereCurve.Evaluate(1.0f), moduleRcs.atmosphereCurve.Evaluate(0.0f)) + "s"));
var rcs = this.selectedPart.GetModule<ModuleRCS>(); }
this.infoItems.Add(new PartInfoItem("Thruster Power", rcs.thrusterPower.ToForce()));  
this.infoItems.Add(new PartInfoItem("Specific Impulse", Units.ConcatF(rcs.atmosphereCurve.Evaluate(1.0f), rcs.atmosphereCurve.Evaluate(0.0f)) + "s"));  
} }
   
private void SetReactionWheelInfo() private void SetReactionWheelInfo()
{ {
if (!this.selectedPart.HasModule<ModuleReactionWheel>()) moduleReactionWheel = selectedPart.GetModule<ModuleReactionWheel>();
{ if (moduleReactionWheel != null)
return; {
} infoItems.Add(PartInfoItem.Create("Reaction Wheel Torque"));
  infoItems.Add(PartInfoItem.Create("\tPitch", moduleReactionWheel.PitchTorque.ToTorque()));
var reactionWheel = this.selectedPart.GetModule<ModuleReactionWheel>(); infoItems.Add(PartInfoItem.Create("\tRoll", moduleReactionWheel.RollTorque.ToTorque()));
this.infoItems.Add(new PartInfoItem("Reaction Wheel Torque")); infoItems.Add(PartInfoItem.Create("\tYaw", moduleReactionWheel.YawTorque.ToTorque()));
this.infoItems.Add(new PartInfoItem("\tPitch", reactionWheel.PitchTorque.ToTorque())); for (int i = 0; i < moduleReactionWheel.inputResources.Count; ++i)
this.infoItems.Add(new PartInfoItem("\tRoll", reactionWheel.RollTorque.ToTorque())); {
this.infoItems.Add(new PartInfoItem("\tYaw", reactionWheel.YawTorque.ToTorque())); moduleResource = moduleReactionWheel.inputResources[i];
foreach (var resource in reactionWheel.inputResources) infoItems.Add(PartInfoItem.Create("\t" + moduleResource.name, moduleResource.rate.ToRate()));
{ }
this.infoItems.Add(new PartInfoItem("\t" + resource.name, resource.rate.ToRate()));  
} }
} }
   
private void SetResourceItems() private void SetResourceItems()
{ {
if (this.selectedPart.Resources.list.Any(r => !r.hideFlow)) bool visibleResources = false;
{ for (int i = 0; i < selectedPart.Resources.list.Count; ++i)
this.infoItems.Add(new PartInfoItem("Resources")); {
foreach (var resource in this.selectedPart.Resources.list.Where(r => !r.hideFlow)) if (selectedPart.Resources.list[i].hideFlow == false)
{ {
this.infoItems.Add(resource.GetDensity() > 0 visibleResources = true;
? new PartInfoItem("\t" + resource.info.name, "(" + resource.GetMass().ToMass() + ") " + resource.amount.ToString("F1")) break;
: new PartInfoItem("\t" + resource.info.name, resource.amount.ToString("F1"))); }
  }
  if (visibleResources)
  {
  infoItems.Add(PartInfoItem.Create("Resources"));
  for (int i = 0; i < selectedPart.Resources.list.Count; ++i)
  {
  partResource = selectedPart.Resources.list[i];
   
  if (partResource.hideFlow == false)
  {
  infoItems.Add(partResource.GetDensity() > 0
  ? PartInfoItem.Create("\t" + partResource.info.name, "(" + partResource.GetMass().ToMass() + ") " + partResource.amount.ToString("F1"))
  : PartInfoItem.Create("\t" + partResource.info.name, partResource.amount.ToString("F1")));
  }
} }
} }
} }
   
private void SetSasInfo() private void SetSasInfo()
{ {
if (this.selectedPart.HasModule<ModuleSAS>()) if (selectedPart.HasModule<ModuleSAS>())
{ {
this.infoItems.Add(new PartInfoItem("SAS Equiped")); infoItems.Add(PartInfoItem.Create("SAS Equiped"));
} }
} }
   
private void SetScienceContainerInfo() private void SetScienceContainerInfo()
{ {
if (this.selectedPart.HasModule<ModuleScienceContainer>()) if (selectedPart.HasModule<ModuleScienceContainer>())
{ {
this.infoItems.Add(new PartInfoItem("Science Container")); infoItems.Add(PartInfoItem.Create("Science Container"));
} }
} }
   
private void SetScienceExperimentInfo() private void SetScienceExperimentInfo()
{ {
if (!this.selectedPart.HasModule<ModuleScienceExperiment>()) moduleScienceExperiment = selectedPart.GetModule<ModuleScienceExperiment>();
{ if (moduleScienceExperiment != null)
return; {
} infoItems.Add(PartInfoItem.Create("Science Experiment", moduleScienceExperiment.experimentActionName));
  infoItems.Add(PartInfoItem.Create("\tTransmit Efficiency", moduleScienceExperiment.xmitDataScalar.ToPercent()));
var experiment = this.selectedPart.GetModule<ModuleScienceExperiment>(); if (moduleScienceExperiment.rerunnable == false)
this.infoItems.Add(new PartInfoItem("Science Experiment", experiment.experimentActionName)); {
this.infoItems.Add(new PartInfoItem("\tTransmit Efficiency", experiment.xmitDataScalar.ToPercent())); infoItems.Add(PartInfoItem.Create("\tSingle Usage"));
if (!experiment.rerunnable) }
{  
this.infoItems.Add(new PartInfoItem("\tSingle Usage"));  
} }
} }
   
private void SetSingleActivationInfo() private void SetSingleActivationInfo()
{ {
if (this.selectedPart.HasModule<ModuleAnimateGeneric>(m => m.isOneShot)) if (selectedPart.HasModule<ModuleAnimateGeneric>(m => m.isOneShot))
{ {
this.infoItems.Add(new PartInfoItem("Single Activation")); infoItems.Add(PartInfoItem.Create("Single Activation"));
} }
} }
   
private void SetSolarPanelInfo() private void SetSolarPanelInfo()
{ {
if (!this.selectedPart.HasModule<ModuleDeployableSolarPanel>()) moduleDeployableSolarPanel = selectedPart.GetModule<ModuleDeployableSolarPanel>();
{ if (moduleDeployableSolarPanel != null)
return; {
} infoItems.Add(PartInfoItem.Create("Charge Rate", moduleDeployableSolarPanel.chargeRate.ToRate()));
  if (moduleDeployableSolarPanel.isBreakable)
var solarPanel = this.selectedPart.GetModule<ModuleDeployableSolarPanel>(); {
this.infoItems.Add(new PartInfoItem("Charge Rate", solarPanel.chargeRate.ToRate())); infoItems.Add(PartInfoItem.Create("Breakable"));
if (solarPanel.isBreakable) }
{ if (moduleDeployableSolarPanel.sunTracking)
this.infoItems.Add(new PartInfoItem("Breakable")); {
} infoItems.Add(PartInfoItem.Create("Sun Tracking"));
if (solarPanel.sunTracking) }
{  
this.infoItems.Add(new PartInfoItem("Sun Tracking"));  
} }
} }
   
private void SetTransmitterInfo() private void SetTransmitterInfo()
{ {
if (!this.selectedPart.HasModule<ModuleDataTransmitter>()) moduleDataTransmitter = selectedPart.GetModule<ModuleDataTransmitter>();
{ if (moduleDataTransmitter != null)
return; {
} infoItems.Add(PartInfoItem.Create("Packet Size", moduleDataTransmitter.packetSize.ToString("F2") + " Mits"));
  infoItems.Add(PartInfoItem.Create("Bandwidth", (moduleDataTransmitter.packetInterval * moduleDataTransmitter.packetSize).ToString("F2") + "Mits/sec"));
var transmitter = this.selectedPart.GetModule<ModuleDataTransmitter>(); infoItems.Add(PartInfoItem.Create(moduleDataTransmitter.requiredResource, moduleDataTransmitter.packetResourceCost.ToString("F2") + "/Packet"));
this.infoItems.Add(new PartInfoItem("Packet Size", transmitter.packetSize.ToString("F2") + " Mits")); }
this.infoItems.Add(new PartInfoItem("Bandwidth", (transmitter.packetInterval * transmitter.packetSize).ToString("F2") + "Mits/sec"));  
this.infoItems.Add(new PartInfoItem(transmitter.requiredResource, transmitter.packetResourceCost.ToString("F2") + "/Packet"));  
} }
   
private void Window(int windowId) private void Window(int windowId)
{ {
try try
{ {
GUILayout.Label(this.selectedPart.partInfo.title, BuildOverlay.TitleStyle); GUILayout.Label(selectedPart.partInfo.title, BuildOverlay.TitleStyle);
if (this.showInfo) if (showInfo)
{ {
foreach (var item in this.infoItems) for (int i = 0; i < infoItems.Count; ++i)
{ {
  partInfoItem = infoItems[i];
GUILayout.Space(2.0f); GUILayout.Space(2.0f);
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
if (item.Value != null) if (partInfoItem.Value != null)
{ {
GUILayout.Label(item.Name + ":", BuildOverlay.NameStyle); GUILayout.Label(partInfoItem.Name + ":", BuildOverlay.NameStyle);
GUILayout.Space(25.0f); GUILayout.Space(25.0f);
GUILayout.Label(item.Value, BuildOverlay.ValueStyle); GUILayout.Label(partInfoItem.Value, BuildOverlay.ValueStyle);
} }
else else
{ {
GUILayout.Label(item.Name, BuildOverlay.NameStyle); GUILayout.Label(partInfoItem.Name, BuildOverlay.NameStyle);
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
} }
else if (this.infoItems.Count > 0) else if (clickToOpen && namesOnly == false)
{ {
GUILayout.Space(2.0f); GUILayout.Space(2.0f);
GUILayout.Label("Click middle mouse to show more info...", BuildOverlay.NameStyle); GUILayout.Label("Click middle mouse to show more info...", BuildOverlay.NameStyle);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion  
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System;  
using System.Collections.Generic;  
using System.Linq;  
   
using KerbalEngineer.Extensions;  
   
using UnityEngine;  
   
#endregion #endregion
   
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
  using System;
  using System.Collections.Generic;
  using Extensions;
  using UnityEngine;
   
public class BuildOverlayResources : MonoBehaviour public class BuildOverlayResources : MonoBehaviour
{ {
#region Fields #region Fields
   
private static bool visible = true; private static bool visible = true;
   
private readonly Dictionary<int, ResourceInfoItem> resources = new Dictionary<int, ResourceInfoItem>(); private readonly Dictionary<int, ResourceInfoItem> resources = new Dictionary<int, ResourceInfoItem>();
   
private bool open = true; private bool open = true;
private float openPercent; private float openPercent;
private GUIContent tabContent; private GUIContent tabContent;
private Rect tabPosition; private Rect tabPosition;
private Vector2 tabSize; private Vector2 tabSize;
private Rect windowPosition = new Rect(0.0f, 0.0f, BuildOverlay.MinimumWidth, 0.0f); private Rect windowPosition = new Rect(0.0f, 0.0f, BuildOverlay.MinimumWidth, 0.0f);
   
#endregion #endregion
   
#region Properties #region Properties
   
public static bool Visible public static bool Visible
{ {
get { return visible; } get
set { visible = value; } {
  return visible;
  }
  set
  {
  visible = value;
  }
} }
   
public bool Open public bool Open
{ {
get { return this.open; } get
set { this.open = value; } {
} return open;
  }
  set
  {
  open = value;
  }
  }
#endregion #endregion
   
#region Methods: protected #region Methods: protected
   
protected void OnGUI() protected void OnGUI()
{ {
try try
{ {
if (!Visible || this.resources.Count == 0 || EditorLogic.fetch.editorScreen != EditorScreen.Parts) if (!Visible || resources.Count == 0 || EditorLogic.fetch.editorScreen != EditorScreen.Parts)
{ {
return; return;
} }
   
this.open = GUI.Toggle(this.tabPosition, this.open, this.tabContent, BuildOverlay.TabStyle); open = GUI.Toggle(tabPosition, open, tabContent, BuildOverlay.TabStyle);
if (this.openPercent > 0.0) if (openPercent > 0.0)
{ {
this.windowPosition = GUILayout.Window(this.GetInstanceID(), this.windowPosition, this.Window, String.Empty, BuildOverlay.WindowStyle); windowPosition = GUILayout.Window(GetInstanceID(), windowPosition, Window, String.Empty, BuildOverlay.WindowStyle);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
protected void Start() protected void Start()
{ {
try try
{ {
this.tabContent = new GUIContent("RESOURCES"); tabContent = new GUIContent("RESOURCES");
this.tabSize = BuildOverlay.TabStyle.CalcSize(this.tabContent); tabSize = BuildOverlay.TabStyle.CalcSize(tabContent);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
protected void Update() protected void Update()
{ {
try try
{ {
if (!Visible) if (!Visible)
{ {
return; return;
} }
   
this.SetResources(); SetResources();
this.SetSlidePosition(); SetSlidePosition();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion #endregion
   
#region Methods: private #region Methods: private
  private static Part part;
  private static PartResource partResource;
   
private void SetResources() private void SetResources()
{ {
var previousCount = this.resources.Count; int previousCount = resources.Count;
this.resources.Clear(); resources.Clear();
foreach (var resource in EditorLogic.fetch.ship.parts.SelectMany(p => p.Resources.list).Where(r => r.amount > 0.0))  
{ for (int i = 0; i < EditorLogic.fetch.ship.parts.Count; ++i)
if (this.resources.ContainsKey(resource.info.id)) {
{ part = EditorLogic.fetch.ship.parts[i];
this.resources[resource.info.id].Amount += resource.amount; for (int j = 0; j < part.Resources.list.Count; ++j)
} {
else partResource = part.Resources.list[j];
{  
this.resources.Add(resource.info.id, new ResourceInfoItem(resource)); if (resources.ContainsKey(partResource.info.id))
} {
} resources[partResource.info.id].Amount += partResource.amount;
  }
if (this.resources.Count < previousCount) else
{ {
this.windowPosition.height = 0; resources.Add(partResource.info.id, new ResourceInfoItem(partResource));
  }
  }
  }
   
  if (resources.Count < previousCount)
  {
  windowPosition.height = 0;
} }
} }
   
private void SetSlidePosition() private void SetSlidePosition()
{ {
if (this.open && this.openPercent < 1.0f) if (open && openPercent < 1.0f)
{ {
this.openPercent = Mathf.Clamp(this.openPercent + Time.deltaTime * BuildOverlay.TabSpeed, 0.0f, 1.0f); openPercent = Mathf.Clamp(openPercent + Time.deltaTime * BuildOverlay.TabSpeed, 0.0f, 1.0f);
} }
else if (!this.open && this.openPercent > 0.0f) else if (!open && openPercent > 0.0f)
{ {
this.openPercent = Mathf.Clamp(this.openPercent - Time.deltaTime * BuildOverlay.TabSpeed, 0.0f, 1.0f); openPercent = Mathf.Clamp(openPercent - Time.deltaTime * BuildOverlay.TabSpeed, 0.0f, 1.0f);
} }
   
this.windowPosition.x = BuildOverlay.BuildOverlayVessel.WindowPosition.xMax + 5.0f; windowPosition.x = BuildOverlay.BuildOverlayVessel.WindowPosition.xMax + 5.0f;
this.windowPosition.y = Mathf.Lerp(Screen.height, Screen.height - this.windowPosition.height, this.openPercent); windowPosition.y = Mathf.Lerp(Screen.height, Screen.height - windowPosition.height, openPercent);
this.tabPosition.width = this.tabSize.x; tabPosition.width = tabSize.x;
this.tabPosition.height = this.tabSize.y; tabPosition.height = tabSize.y;
this.tabPosition.x = this.windowPosition.x; tabPosition.x = windowPosition.x;
this.tabPosition.y = this.windowPosition.y - this.tabPosition.height; tabPosition.y = windowPosition.y - tabPosition.height;
} }
   
private void Window(int windowId) private void Window(int windowId)
{ {
try try
{ {
var firstItem = true; bool firstItem = true;
foreach (var resource in this.resources) foreach (KeyValuePair<int, ResourceInfoItem> resource in resources)
{ {
if (!firstItem) if (!firstItem)
{ {
GUILayout.Space(2.0f); GUILayout.Space(2.0f);
} }
firstItem = false; firstItem = false;
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
   
GUILayout.Label(resource.Value.Name + ":", BuildOverlay.NameStyle); GUILayout.Label(resource.Value.Name + ":", BuildOverlay.NameStyle);
GUILayout.Space(50.0f); GUILayout.Space(50.0f);
if (resource.Value.Mass > 0.0) if (resource.Value.Mass > 0.0)
{ {
GUILayout.Label("(" + resource.Value.Mass.ToMass() + ") " + resource.Value.Amount.ToString("N1"), BuildOverlay.ValueStyle); GUILayout.Label("(" + resource.Value.Mass.ToMass() + ") " + resource.Value.Amount.ToString("N1"), BuildOverlay.ValueStyle);
} }
else else
{ {
GUILayout.Label(resource.Value.Amount.ToString("N1"), BuildOverlay.ValueStyle); GUILayout.Label(resource.Value.Amount.ToString("N1"), BuildOverlay.ValueStyle);
} }
   
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
#endregion #endregion
   
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
#region Using Directives #region Using Directives
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Helpers; using Helpers;
using UnityEngine; using UnityEngine;
using VesselSimulator; using VesselSimulator;
   
#endregion #endregion
   
public class BuildOverlayVessel : MonoBehaviour public class BuildOverlayVessel : MonoBehaviour
{ {
#region Constants #region Constants
   
private const float Width = 175.0f; private const float Width = 175.0f;
   
#endregion #endregion
   
#region Fields #region Fields
   
private static bool visible = true; private static bool visible = true;
   
private readonly List<PartInfoItem> infoItems = new List<PartInfoItem>(); private readonly List<PartInfoItem> infoItems = new List<PartInfoItem>();
   
private Stage lastStage; private Stage lastStage;
private bool open = true; private bool open = true;
private float openPercent; private float openPercent;
private GUIContent tabContent; private GUIContent tabContent;
private Rect tabPosition; private Rect tabPosition;
private Vector2 tabSize; private Vector2 tabSize;
private Rect windowPosition = new Rect(330.0f, 0.0f, Width, 0.0f); private Rect windowPosition = new Rect(330.0f, 0.0f, Width, 0.0f);
   
#endregion #endregion
   
#region Properties #region Properties
   
public static bool Visible public static bool Visible
{ {
get { return visible; } get
set { visible = value; } {
  return visible;
  }
  set
  {
  visible = value;
  }
} }
   
public bool Open public bool Open
{ {
get { return this.open; } get
set { this.open = value; } {
  return open;
  }
  set
  {
  open = value;
  }
} }
   
public Rect WindowPosition public Rect WindowPosition
{ {
get { return this.windowPosition; } get
  {
  return windowPosition;
  }
} }
   
public float WindowX public float WindowX
{ {
get { return this.windowPosition.x; } get
set { this.windowPosition.x = value; } {
} return windowPosition.x;
  }
  set
  {
  windowPosition.x = value;
  }
  }
#endregion #endregion
   
#region Methods #region Methods
   
protected void Awake() protected void Awake()
{ {
try try
{ {
SimManager.OnReady -= this.GetStageInfo; SimManager.OnReady -= GetStageInfo;
SimManager.OnReady += this.GetStageInfo; SimManager.OnReady += GetStageInfo;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
protected void OnGUI() protected void OnGUI()
{ {
try try
{ {
if (!Visible || EditorLogic.RootPart == null || EditorLogic.fetch.editorScreen != EditorScreen.Parts) if (!Visible || EditorLogic.RootPart == null || EditorLogic.fetch.editorScreen != EditorScreen.Parts)
{ {
return; return;
} }
   
this.open = GUI.Toggle(this.tabPosition, this.open, this.tabContent, BuildOverlay.TabStyle); open = GUI.Toggle(tabPosition, open, tabContent, BuildOverlay.TabStyle);
if (this.openPercent > 0.0) if (openPercent > 0.0)
{ {
this.windowPosition = GUILayout.Window(this.GetInstanceID(), this.windowPosition, this.VesselWindow, String.Empty, BuildOverlay.WindowStyle); windowPosition = GUILayout.Window(GetInstanceID(), windowPosition, VesselWindow, String.Empty, BuildOverlay.WindowStyle);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
protected void Start() protected void Start()
{ {
try try
{ {
this.tabContent = new GUIContent("VESSEL"); tabContent = new GUIContent("VESSEL");
this.tabSize = BuildOverlay.TabStyle.CalcSize(this.tabContent); tabSize = BuildOverlay.TabStyle.CalcSize(tabContent);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
protected void Update() protected void Update()
{ {
try try
{ {
if (!Visible || EditorLogic.RootPart == null) if (!Visible || EditorLogic.RootPart == null)
{ {
return; return;
} }
   
if (this.openPercent > 0.0) if (openPercent > 0.0)
{ {
this.SetVesselInfo(); SetVesselInfo();
} }
   
this.SetSlidePosition(); SetSlidePosition();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
private void GetStageInfo() private void GetStageInfo()
{ {
this.lastStage = SimManager.LastStage; lastStage = SimManager.LastStage;
} }
   
private void SetSlidePosition() private void SetSlidePosition()
{ {
if (this.open && this.openPercent < 1.0f) if (open && openPercent < 1.0f)
{ {
this.openPercent = Mathf.Clamp(this.openPercent + Time.deltaTime * BuildOverlay.TabSpeed, 0.0f, 1.0f); openPercent = Mathf.Clamp(openPercent + Time.deltaTime * BuildOverlay.TabSpeed, 0.0f, 1.0f);
} }
else if (!this.open && this.openPercent > 0.0f) else if (!open && openPercent > 0.0f)
{ {
this.openPercent = Mathf.Clamp(this.openPercent - Time.deltaTime * BuildOverlay.TabSpeed, 0.0f, 1.0f); openPercent = Mathf.Clamp(openPercent - Time.deltaTime * BuildOverlay.TabSpeed, 0.0f, 1.0f);
} }
   
this.windowPosition.y = Mathf.Lerp(Screen.height, Screen.height - this.windowPosition.height, this.openPercent); windowPosition.y = Mathf.Lerp(Screen.height, Screen.height - windowPosition.height, openPercent);
if (this.windowPosition.width < Width) if (windowPosition.width < Width)
{ {
this.windowPosition.width = Width; windowPosition.width = Width;
} }
this.tabPosition.width = this.tabSize.x; tabPosition.width = tabSize.x;
this.tabPosition.height = this.tabSize.y; tabPosition.height = tabSize.y;
this.tabPosition.x = this.windowPosition.x; tabPosition.x = windowPosition.x;
this.tabPosition.y = this.windowPosition.y - this.tabPosition.height; tabPosition.y = windowPosition.y - tabPosition.height;
} }
   
private void SetVesselInfo() private void SetVesselInfo()
{ {
SimManager.Gravity = CelestialBodies.SelectedBody.Gravity; SimManager.Gravity = CelestialBodies.SelectedBody.Gravity;
   
if (BuildAdvanced.Instance.ShowAtmosphericDetails) if (BuildAdvanced.Instance.ShowAtmosphericDetails)
{ {
SimManager.Atmosphere = CelestialBodies.SelectedBody.Atmosphere * 0.01; SimManager.Atmosphere = CelestialBodies.SelectedBody.GetAtmospheres(BuildAdvanced.Altitude);
} }
else else
{ {
SimManager.Atmosphere = 0.0; SimManager.Atmosphere = 0.0;
} }
   
SimManager.RequestSimulation(); SimManager.RequestSimulation();
SimManager.TryStartSimulation(); SimManager.TryStartSimulation();
   
if (this.lastStage != null) if (lastStage != null)
{ {
this.infoItems.Clear(); PartInfoItem.Release(infoItems);
this.infoItems.Add(new PartInfoItem("Delta-V", this.lastStage.deltaV.ToString("N0") + " / " + this.lastStage.totalDeltaV.ToString("N0") + "m/s")); infoItems.Clear();
this.infoItems.Add(new PartInfoItem("Mass", Units.ToMass(this.lastStage.mass, this.lastStage.totalMass))); infoItems.Add(PartInfoItem.Create("Delta-V", lastStage.deltaV.ToString("N0") + " / " + lastStage.totalDeltaV.ToString("N0") + "m/s"));
this.infoItems.Add(new PartInfoItem("TWR", this.lastStage.thrustToWeight.ToString("F2") + " (" + this.lastStage.maxThrustToWeight.ToString("F2") + ")")); infoItems.Add(PartInfoItem.Create("Mass", Units.ToMass(lastStage.mass, lastStage.totalMass)));
this.infoItems.Add(new PartInfoItem("Parts", this.lastStage.partCount + " / " + this.lastStage.totalPartCount)); infoItems.Add(PartInfoItem.Create("TWR", lastStage.thrustToWeight.ToString("F2") + " (" + lastStage.maxThrustToWeight.ToString("F2") + ")"));
  infoItems.Add(PartInfoItem.Create("Parts", lastStage.partCount + " / " + lastStage.totalPartCount));
} }
} }
   
private void VesselWindow(int windowId) private void VesselWindow(int windowId)
{ {
try try
{ {
var firstItem = true; bool firstItem = true;
foreach (var item in this.infoItems) foreach (PartInfoItem item in infoItems)
{ {
if (!firstItem) if (!firstItem)
{ {
GUILayout.Space(2.0f); GUILayout.Space(2.0f);
} }
firstItem = false; firstItem = false;
   
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
if (item.Value != null) if (item.Value != null)
{ {
GUILayout.Label(item.Name + ":", BuildOverlay.NameStyle); GUILayout.Label(item.Name + ":", BuildOverlay.NameStyle);
GUILayout.FlexibleSpace(); GUILayout.FlexibleSpace();
GUILayout.Label(item.Value, BuildOverlay.ValueStyle); GUILayout.Label(item.Value, BuildOverlay.ValueStyle);
} }
else else
{ {
GUILayout.Label(item.Name, BuildOverlay.NameStyle); GUILayout.Label(item.Name, BuildOverlay.NameStyle);
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System; using System;
   
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
[KSPAddon(KSPAddon.Startup.EditorAny, false)] [KSPAddon(KSPAddon.Startup.EditorAny, false)]
public class BuildToolbar : MonoBehaviour public class BuildToolbar : MonoBehaviour
{ {
#region Fields #region Fields
   
private ApplicationLauncherButton button; private ApplicationLauncherButton button;
   
#endregion #endregion
   
#region Methods: private #region Methods: private
   
private void Awake() private void Awake()
{ {
GameEvents.onGUIApplicationLauncherReady.Add(this.OnGuiAppLauncherReady); GameEvents.onGUIApplicationLauncherReady.Add(this.OnGuiAppLauncherReady);
Logger.Log("BuildToolbar->Awake"); Logger.Log("BuildToolbar->Awake");
  }
   
  private void Start()
  {
  if (button == null)
  {
  OnGuiAppLauncherReady();
  }
} }
   
private void OnDestroy() private void OnDestroy()
{ {
GameEvents.onGUIApplicationLauncherReady.Remove(this.OnGuiAppLauncherReady); GameEvents.onGUIApplicationLauncherReady.Remove(this.OnGuiAppLauncherReady);
if (this.button != null) if (this.button != null)
{ {
ApplicationLauncher.Instance.RemoveModApplication(this.button); ApplicationLauncher.Instance.RemoveModApplication(this.button);
} }
Logger.Log("BuildToolbar->OnDestroy"); Logger.Log("BuildToolbar->OnDestroy");
} }
   
private void OnGuiAppLauncherReady() private void OnGuiAppLauncherReady()
{ {
try try
{ {
this.button = ApplicationLauncher.Instance.AddModApplication( this.button = ApplicationLauncher.Instance.AddModApplication(
() => BuildAdvanced.Instance.Visible = true, () => BuildAdvanced.Instance.Visible = true,
() => BuildAdvanced.Instance.Visible = false, () => BuildAdvanced.Instance.Visible = false,
null, null,
null, null,
null, null,
null, null,
ApplicationLauncher.AppScenes.ALWAYS, ApplicationLauncher.AppScenes.ALWAYS,
GameDatabase.Instance.GetTexture("KerbalEngineer/Textures/ToolbarIcon", false) GameDatabase.Instance.GetTexture("KerbalEngineer/Textures/ToolbarIcon", false)
); );
Logger.Log("BuildToolbar->OnGuiAppLauncherReady"); Logger.Log("BuildToolbar->OnGuiAppLauncherReady");
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildToolbar->OnGuiAppLauncherReady"); Logger.Exception(ex, "BuildToolbar->OnGuiAppLauncherReady");
} }
} }
   
private void Update() private void Update()
{ {
try try
{ {
if (this.button == null) if (this.button == null)
{ {
return; return;
} }
   
if (EditorLogic.fetch != null && EditorLogic.fetch.ship.parts.Count > 0) if (EditorLogic.fetch != null && EditorLogic.fetch.ship.parts.Count > 0)
{ {
if (BuildAdvanced.Instance.Visible && this.button.State != RUIToggleButton.ButtonState.TRUE) if (BuildAdvanced.Instance.Visible && this.button.State != RUIToggleButton.ButtonState.TRUE)
{ {
this.button.SetTrue(); this.button.SetTrue();
} }
else if (!BuildAdvanced.Instance.Visible && this.button.State != RUIToggleButton.ButtonState.FALSE) else if (!BuildAdvanced.Instance.Visible && this.button.State != RUIToggleButton.ButtonState.FALSE)
{ {
this.button.SetFalse(); this.button.SetFalse();
} }
} }
else if (this.button.State != RUIToggleButton.ButtonState.DISABLED) else if (this.button.State != RUIToggleButton.ButtonState.DISABLED)
{ {
this.button.Disable(); this.button.Disable();
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "BuildToolbar->Update"); Logger.Exception(ex, "BuildToolbar->Update");
} }
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
namespace KerbalEngineer.Editor namespace KerbalEngineer.Editor
{ {
  using System.Collections.Generic;
  using VesselSimulator;
   
public class PartInfoItem public class PartInfoItem
{ {
#region Constructors private static readonly Pool<PartInfoItem> pool = new Pool<PartInfoItem>(Create, Reset);
   
public PartInfoItem(string name)  
{  
this.Name = name;  
}  
   
public PartInfoItem(string name, string value)  
{  
this.Name = name;  
this.Value = value;  
}  
   
#endregion  
   
#region Properties  
   
public string Name { get; set; } public string Name { get; set; }
   
public string Value { get; set; } public string Value { get; set; }
   
#endregion private static PartInfoItem Create()
  {
  return new PartInfoItem();
  }
   
  public void Release()
  {
  pool.Release(this);
  }
   
  public static void Release(List<PartInfoItem> objList)
  {
  for (int i = 0; i < objList.Count; ++i)
  {
  objList[i].Release();
  }
  }
   
  private static void Reset(PartInfoItem obj)
  {
  obj.Name = string.Empty;
  obj.Value = string.Empty;
  }
   
  public static PartInfoItem Create(string name)
  {
  return New(name);
  }
   
  public static PartInfoItem Create(string name, string value)
  {
  return New(name, value);
  }
   
  public static PartInfoItem New(string name)
  {
  PartInfoItem obj = pool.Borrow();
   
  obj.Name = name;
  obj.Value = string.Empty;
   
  return obj;
  }
   
  public static PartInfoItem New(string name, string value)
  {
  PartInfoItem obj = pool.Borrow();
   
  obj.Name = name;
  obj.Value = value;
   
  return obj;
  }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
   
#endregion #endregion
   
namespace KerbalEngineer namespace KerbalEngineer
{ {
public class EngineerGlobals public class EngineerGlobals
{ {
#region Constants #region Constants
   
/// <summary> /// <summary>
/// Current version of the Kerbal Engineer assembly. /// Current version of the Kerbal Engineer assembly.
/// </summary> /// </summary>
public const string AssemblyVersion = "1.0.15.1"; public const string AssemblyVersion = "1.0.16.6";
   
#endregion #endregion
   
#region Fields #region Fields
   
private static string assemblyFile; private static string assemblyFile;
private static string assemblyName; private static string assemblyName;
private static string assemblyPath; private static string assemblyPath;
   
#endregion #endregion
   
#region Properties #region Properties
   
/// <summary> /// <summary>
/// Gets the Kerbal Engineer assembly's path including the file name. /// Gets the Kerbal Engineer assembly's path including the file name.
/// </summary> /// </summary>
public static string AssemblyFile public static string AssemblyFile
{ {
get { return assemblyFile ?? (assemblyFile = Assembly.GetExecutingAssembly().Location); } get { return assemblyFile ?? (assemblyFile = Assembly.GetExecutingAssembly().Location); }
} }
   
/// <summary> /// <summary>
/// Gets the Kerbal Engineer assembly's file name. /// Gets the Kerbal Engineer assembly's file name.
/// </summary> /// </summary>
public static string AssemblyName public static string AssemblyName
{ {
get { return assemblyName ?? (assemblyName = new FileInfo(AssemblyFile).Name); } get { return assemblyName ?? (assemblyName = new FileInfo(AssemblyFile).Name); }
} }
   
/// <summary> /// <summary>
/// Gets the Kerbal Engineer assembly's path excluding the file name. /// Gets the Kerbal Engineer assembly's path excluding the file name.
/// </summary> /// </summary>
public static string AssemblyPath public static string AssemblyPath
{ {
get { return assemblyPath ?? (assemblyPath = AssemblyFile.Replace(new FileInfo(AssemblyFile).Name, "")); } get { return assemblyPath ?? (assemblyPath = AssemblyFile.Replace(new FileInfo(AssemblyFile).Name, "")); }
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives  
   
using System;  
using System.Collections.Generic;  
using System.Linq;  
   
#endregion  
   
namespace KerbalEngineer.Extensions namespace KerbalEngineer.Extensions
{ {
  using System;
  using System.Collections.Generic;
using CompoundParts; using CompoundParts;
   
public static class PartExtensions public static class PartExtensions
{ {
#region Methods: public //private static Part cachePart;
  //private static PartModule cachePartModule;
  //private static PartResource cachePartResource;
   
/// <summary> /// <summary>
/// Gets whether the part contains a specific resource. /// Gets whether the part contains a specific resource.
/// </summary> /// </summary>
public static bool ContainsResource(this Part part, int resourceId) public static bool ContainsResource(this Part part, int resourceId)
{ {
return part.Resources.Contains(resourceId); return part.Resources.Contains(resourceId);
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains resources. /// Gets whether the part contains resources.
/// </summary> /// </summary>
public static bool ContainsResources(this Part part) public static bool ContainsResources(this Part part)
{ {
return part.Resources.list.Count(p => p.amount > 0d) > 0; for (int i = 0; i < part.Resources.list.Count; ++i)
  {
  if (part.Resources.list[i].amount > 0.0)
  {
  return true;
  }
  }
  return false;
} }
   
/// <summary> /// <summary>
/// Gets whether the part has fuel. /// Gets whether the part has fuel.
/// </summary> /// </summary>
public static bool EngineHasFuel(this Part part) public static bool EngineHasFuel(this Part part)
{ {
if (part.HasModule<ModuleEngines>()) PartModule cachePartModule = GetModule<ModuleEngines>(part);
{ if (cachePartModule != null)
return part.GetModuleEngines().getFlameoutState; {
} return (cachePartModule as ModuleEngines).getFlameoutState;
if (part.HasModule<MultiModeEngine>()) }
{  
return part.GetModuleMultiModeEngine().getFlameoutState; cachePartModule = GetModuleMultiModeEngine(part);
  if (cachePartModule != null)
  {
  return (cachePartModule as ModuleEnginesFX).getFlameoutState;
} }
   
return false; return false;
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part excluding resources. /// Gets the cost of the part excluding resources.
/// </summary> /// </summary>
public static double GetCostDry(this Part part) public static double GetCostDry(this Part part)
{ {
return part.partInfo.cost - GetResourceCostMax(part) + part.GetModuleCosts(0.0f); return part.partInfo.cost - GetResourceCostMax(part) + part.GetModuleCosts(0.0f);
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part including maximum resources. /// Gets the cost of the part including maximum resources.
/// </summary> /// </summary>
public static double GetCostMax(this Part part) public static double GetCostMax(this Part part)
{ {
return part.partInfo.cost + part.GetModuleCosts(0.0f); return part.partInfo.cost + part.GetModuleCosts(0.0f);
} }
   
/// <summary> /// <summary>
  /// Gets the cost of the part modules
  /// Same as stock but without mem allocation
  /// </summary>
  public static double GetModuleCostsNoAlloc(this Part part, float defaultCost)
  {
  float cost = 0f;
  for (int i = 0; i < part.Modules.Count; i++)
  {
  PartModule pm = part.Modules[i];
  if (pm is IPartCostModifier)
  cost += (pm as IPartCostModifier).GetModuleCost(defaultCost);
  }
  return cost;
  }
   
  /// <summary>
/// Gets the cost of the part including resources. /// Gets the cost of the part including resources.
/// </summary> /// </summary>
public static double GetCostWet(this Part part) public static double GetCostWet(this Part part)
{ {
return part.partInfo.cost - GetResourceCostInverted(part) + part.GetModuleCosts(0.0f); return part.partInfo.cost - GetResourceCostInverted(part) + part.GetModuleCostsNoAlloc(0.0f); // part.GetModuleCosts allocate 44B per call.
} }
   
/// <summary> /// <summary>
/// Gets the dry mass of the part. /// Gets the dry mass of the part.
/// </summary> /// </summary>
public static double GetDryMass(this Part part) public static double GetDryMass(this Part part)
{ {
return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass : 0d; return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass : 0d;
} }
   
/// <summary> /// <summary>
/// Gets the maximum thrust of the part if it's an engine. /// Gets the maximum thrust of the part if it's an engine.
/// </summary> /// </summary>
public static double GetMaxThrust(this Part part) public static double GetMaxThrust(this Part part)
{ {
if (part.HasModule<ModuleEngines>()) PartModule cachePartModule = GetModule<ModuleEngines>(part);
{ if (cachePartModule != null)
return part.GetModuleEngines().maxThrust; {
} return (cachePartModule as ModuleEngines).maxThrust;
if (part.HasModule<MultiModeEngine>()) }
{  
return part.GetModuleMultiModeEngine().maxThrust; cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part);
} if (cachePartModule != null)
if (part.HasModule<ModuleEnginesFX>()) {
{ return (cachePartModule as ModuleEnginesFX).maxThrust;
return part.GetModuleEnginesFx().maxThrust; }
}  
  return 0.0;
return 0d;  
} }
   
/// <summary> /// <summary>
/// Gets the first typed PartModule in the part's module list. /// Gets the first typed PartModule in the part's module list.
/// </summary> /// </summary>
public static T GetModule<T>(this Part part) where T : PartModule public static T GetModule<T>(this Part part) where T : PartModule
{ {
return part.Modules.OfType<T>().FirstOrDefault(); for (int i = 0; i < part.Modules.Count; i++)
  {
  PartModule pm = part.Modules[i];
  if (pm is T)
  return (T)pm;
  }
  return null;
} }
   
/// <summary> /// <summary>
/// Gets a typed PartModule. /// Gets a typed PartModule.
/// </summary> /// </summary>
public static T GetModule<T>(this Part part, string className) where T : PartModule public static T GetModule<T>(this Part part, string className) where T : PartModule
{ {
return (T)Convert.ChangeType(part.Modules[className], typeof(T)); return part.Modules[className] as T;
} }
   
/// <summary> /// <summary>
/// Gets a typed PartModule. /// Gets a typed PartModule.
/// </summary> /// </summary>
public static T GetModule<T>(this Part part, int classId) where T : PartModule public static T GetModule<T>(this Part part, int classId) where T : PartModule
{ {
return (T)Convert.ChangeType(part.Modules[classId], typeof(T)); return part.Modules[classId] as T;
} }
   
/// <summary> /// <summary>
/// Gets a ModuleAlternator typed PartModule. /// Gets a ModuleAlternator typed PartModule.
/// </summary> /// </summary>
public static ModuleAlternator GetModuleAlternator(this Part part) public static ModuleAlternator GetModuleAlternator(this Part part)
{ {
return part.GetModule<ModuleAlternator>(); return GetModule<ModuleAlternator>(part);
} }
   
/// <summary> /// <summary>
/// Gets a ModuleDeployableSolarPanel typed PartModule. /// Gets a ModuleDeployableSolarPanel typed PartModule.
/// </summary> /// </summary>
public static ModuleDeployableSolarPanel GetModuleDeployableSolarPanel(this Part part) public static ModuleDeployableSolarPanel GetModuleDeployableSolarPanel(this Part part)
{ {
return part.GetModule<ModuleDeployableSolarPanel>(); return GetModule<ModuleDeployableSolarPanel>(part);
} }
   
/// <summary> /// <summary>
/// Gets a ModuleEngines typed PartModule. /// Gets a ModuleEngines typed PartModule.
/// </summary> /// </summary>
public static ModuleEngines GetModuleEngines(this Part part) public static ModuleEngines GetModuleEngines(this Part part)
{ {
return part.GetModule<ModuleEngines>(); return GetModule<ModuleEngines>(part);
} }
   
public static ModuleEnginesFX GetModuleEnginesFx(this Part part) public static ModuleEnginesFX GetModuleEnginesFx(this Part part)
{ {
return part.GetModule<ModuleEnginesFX>(); return GetModule<ModuleEnginesFX>(part);
} }
   
/// <summary> /// <summary>
/// Gets a ModuleGenerator typed PartModule. /// Gets a ModuleGenerator typed PartModule.
/// </summary> /// </summary>
public static ModuleGenerator GetModuleGenerator(this Part part) public static ModuleGenerator GetModuleGenerator(this Part part)
{ {
return part.GetModule<ModuleGenerator>(); return GetModule<ModuleGenerator>(part);
} }
   
/// <summary> /// <summary>
/// Gets a ModuleGimbal typed PartModule. /// Gets a ModuleGimbal typed PartModule.
/// </summary> /// </summary>
public static ModuleGimbal GetModuleGimbal(this Part part) public static ModuleGimbal GetModuleGimbal(this Part part)
{ {
return part.GetModule<ModuleGimbal>(); return GetModule<ModuleGimbal>(part);
} }
   
/// <summary> /// <summary>
/// Gets the current selected ModuleEnginesFX. /// Gets the current selected ModuleEnginesFX.
/// </summary> /// </summary>
public static ModuleEnginesFX GetModuleMultiModeEngine(this Part part) public static ModuleEnginesFX GetModuleMultiModeEngine(this Part part)
{ {
var mode = part.GetModule<MultiModeEngine>().mode; ModuleEnginesFX moduleEngineFx;
return part.Modules.OfType<ModuleEnginesFX>().FirstOrDefault(engine => engine.engineID == mode); string mode = GetModule<MultiModeEngine>(part).mode;
  for (int i = 0; i < part.Modules.Count; ++i)
  {
  moduleEngineFx = part.Modules[i] as ModuleEnginesFX;
  if (moduleEngineFx != null && moduleEngineFx.engineID == mode)
  {
  return moduleEngineFx;
  }
  }
  return null;
} }
   
/// <summary> /// <summary>
/// Gets a ModuleParachute typed PartModule. /// Gets a ModuleParachute typed PartModule.
/// </summary> /// </summary>
public static ModuleParachute GetModuleParachute(this Part part) public static ModuleParachute GetModuleParachute(this Part part)
{ {
return part.GetModule<ModuleParachute>(); return GetModule<ModuleParachute>(part);
} }
   
public static ModuleRCS GetModuleRcs(this Part part) public static ModuleRCS GetModuleRcs(this Part part)
{ {
return part.GetModule<ModuleRCS>(); return GetModule<ModuleRCS>(part);
} }
   
/// <summary> /// <summary>
/// Gets a typed list of PartModules. /// Gets a typed list of PartModules.
/// </summary> /// </summary>
public static List<T> GetModules<T>(this Part part) where T : PartModule public static List<T> GetModules<T>(this Part part) where T : PartModule
{ {
return part.Modules.OfType<T>().ToList(); List<T> list = new List<T>();
  for (int i = 0; i < part.Modules.Count; ++i)
  {
  T module = part.Modules[i] as T;
  if (module != null)
  {
  list.Add(module);
  }
  }
  return list;
} }
   
public static ProtoModuleDecoupler GetProtoModuleDecoupler(this Part part) public static ProtoModuleDecoupler GetProtoModuleDecoupler(this Part part)
{ {
if (HasModule<ModuleDecouple>(part)) PartModule cachePartModule = GetModule<ModuleDecouple>(part);
{ if (cachePartModule == null)
return new ProtoModuleDecoupler(GetModule<ModuleDecouple>(part)); {
} cachePartModule = GetModule<ModuleAnchoredDecoupler>(part);
if (HasModule<ModuleAnchoredDecoupler>(part)) }
{ if (cachePartModule != null)
return new ProtoModuleDecoupler(GetModule<ModuleAnchoredDecoupler>(part)); {
} return new ProtoModuleDecoupler(cachePartModule);
  }
   
return null; return null;
} }
   
/// <summary> /// <summary>
/// Gets a generic proto engine for the current engine module attached to the part. /// Gets a generic proto engine for the current engine module attached to the part.
/// </summary> /// </summary>
public static ProtoModuleEngine GetProtoModuleEngine(this Part part) public static ProtoModuleEngine GetProtoModuleEngine(this Part part)
{ {
if (HasModule<ModuleEngines>(part)) PartModule cachePartModule = GetModule<ModuleEngines>(part);
{ if (cachePartModule != null)
return new ProtoModuleEngine(GetModule<ModuleEngines>(part)); {
} return new ProtoModuleEngine(cachePartModule);
if (HasModule<MultiModeEngine>(part)) }
{  
return new ProtoModuleEngine(GetModuleMultiModeEngine(part)); cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part);
} if (cachePartModule != null)
if (HasModule<ModuleEnginesFX>(part)) {
{ return new ProtoModuleEngine(cachePartModule);
return new ProtoModuleEngine(GetModule<ModuleEnginesFX>(part)); }
}  
return null; return null;
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part's contained resources. /// Gets the cost of the part's contained resources.
/// </summary> /// </summary>
public static double GetResourceCost(this Part part) public static double GetResourceCost(this Part part)
{ {
return part.Resources.list.Sum(r => r.amount * r.info.unitCost); double cost = 0.0;
  for (int i = 0; i < part.Resources.list.Count; ++i)
  {
  PartResource cachePartResource = part.Resources.list[i];
  cost = cost + (cachePartResource.amount * cachePartResource.info.unitCost);
  }
  return cost;
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part's contained resources, inverted. /// Gets the cost of the part's contained resources, inverted.
/// </summary> /// </summary>
public static double GetResourceCostInverted(this Part part) public static double GetResourceCostInverted(this Part part)
{ {
return part.Resources.list.Sum(r => (r.maxAmount - r.amount) * r.info.unitCost); double sum = 0;
  for (int i = 0; i < part.Resources.list.Count; i++)
  {
  PartResource r = part.Resources.list[i];
  sum += (r.maxAmount - r.amount) * r.info.unitCost;
  }
  return sum;
} }
   
/// <summary> /// <summary>
/// Gets the cost of the part's maximum contained resources. /// Gets the cost of the part's maximum contained resources.
/// </summary> /// </summary>
public static double GetResourceCostMax(this Part part) public static double GetResourceCostMax(this Part part)
{ {
return part.Resources.list.Sum(r => r.maxAmount * r.info.unitCost); double cost = 0.0;
  for (int i = 0; i < part.Resources.list.Count; ++i)
  {
  PartResource cachePartResource = part.Resources.list[i];
  cost = cost + (cachePartResource.maxAmount * cachePartResource.info.unitCost);
  }
  return cost;
} }
   
/// <summary> /// <summary>
/// Gets the current specific impulse for the engine. /// Gets the current specific impulse for the engine.
/// </summary> /// </summary>
public static double GetSpecificImpulse(this Part part, float atmosphere) public static double GetSpecificImpulse(this Part part, float atmosphere)
{ {
if (part.HasModule<ModuleEngines>()) PartModule cachePartModule = GetModule<ModuleEngines>(part);
{ if (cachePartModule != null)
return part.GetModuleEngines().atmosphereCurve.Evaluate(atmosphere); {
} return (cachePartModule as ModuleEngines).atmosphereCurve.Evaluate(atmosphere);
if (part.HasModule<MultiModeEngine>()) }
{  
return part.GetModuleMultiModeEngine().atmosphereCurve.Evaluate(atmosphere); cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule<ModuleEnginesFX>(part);
} if (cachePartModule != null)
if (part.HasModule<ModuleEnginesFX>()) {
{ return (cachePartModule as ModuleEnginesFX).atmosphereCurve.Evaluate(atmosphere);
return part.GetModuleEnginesFx().atmosphereCurve.Evaluate(atmosphere); }
}  
  return 0.0;
return 0d;  
} }
   
/// <summary> /// <summary>
/// Gets the total mass of the part including resources. /// Gets the total mass of the part including resources.
/// </summary> /// </summary>
public static double GetWetMass(this Part part) public static double GetWetMass(this Part part)
{ {
return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass + part.GetResourceMass() : part.GetResourceMass(); return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass + part.GetResourceMass() : part.GetResourceMass();
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains a PartModule. /// Gets whether the part contains a PartModule.
/// </summary> /// </summary>
public static bool HasModule<T>(this Part part) where T : PartModule public static bool HasModule<T>(this Part part) where T : PartModule
{ {
return part.Modules.OfType<T>().Any(); for (int i = 0; i < part.Modules.Count; i++)
  {
  if (part.Modules[i] is T)
  return true;
  }
  return false;
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains a PartModule conforming to the supplied predicate. /// Gets whether the part contains a PartModule conforming to the supplied predicate.
/// </summary> /// </summary>
public static bool HasModule<T>(this Part part, Func<T, bool> predicate) where T : PartModule public static bool HasModule<T>(this Part part, Func<T, bool> predicate) where T : PartModule
{ {
return part.Modules.OfType<T>().Any(predicate); for (int i = 0; i < part.Modules.Count; i++)
  {
  PartModule pm = part.Modules[i];
  if (pm is T && predicate(pm as T))
  return true;
  }
  return false;
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains a PartModule. /// Gets whether the part contains a PartModule.
/// </summary> /// </summary>
public static bool HasModule(this Part part, string className) public static bool HasModule(this Part part, string className)
{ {
return part.Modules.Contains(className); return part.Modules.Contains(className);
} }
   
/// <summary> /// <summary>
/// Gets whether the part contains a PartModule. /// Gets whether the part contains a PartModule.
/// </summary> /// </summary>
public static bool HasModule(this Part part, int moduleId) public static bool HasModule(this Part part, int moduleId)
{ {
return part.Modules.Contains(moduleId); return part.Modules.Contains(moduleId);
} }
   
/// <summary> /// <summary>
/// Gets whether the part has a one shot animation. /// Gets whether the part has a one shot animation.
/// </summary> /// </summary>
public static bool HasOneShotAnimation(this Part part) public static bool HasOneShotAnimation(this Part part)
{ {
return part.HasModule<ModuleAnimateGeneric>() && part.GetModule<ModuleAnimateGeneric>().isOneShot; PartModule cachePartModule = GetModule<ModuleAnimateGeneric>(part);
  return cachePartModule != null && (cachePartModule as ModuleAnimateGeneric).isOneShot;
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a command module. /// Gets whether the part is a command module.
/// </summary> /// </summary>
public static bool IsCommandModule(this Part part) public static bool IsCommandModule(this Part part)
{ {
return part.HasModule<ModuleCommand>(); return HasModule<ModuleCommand>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is decoupled in a specified stage. /// Gets whether the part is decoupled in a specified stage.
/// </summary> /// </summary>
public static bool IsDecoupledInStage(this Part part, int stage) public static bool IsDecoupledInStage(this Part part, int stage)
{ {
if ((part.IsDecoupler() || part.IsLaunchClamp()) && part.inverseStage == stage) if ((IsDecoupler(part) || IsLaunchClamp(part)) && part.inverseStage == stage)
{ {
return true; return true;
} }
if (part.parent == null) if (part.parent == null)
{ {
return false; return false;
} }
return part.parent.IsDecoupledInStage(stage); return IsDecoupledInStage(part.parent, stage);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a decoupler. /// Gets whether the part is a decoupler.
/// </summary> /// </summary>
public static bool IsDecoupler(this Part part) public static bool IsDecoupler(this Part part)
{ {
return part.HasModule<ModuleDecouple>() || part.HasModule<ModuleAnchoredDecoupler>(); return HasModule<ModuleDecouple>(part) || HasModule<ModuleAnchoredDecoupler>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is an active engine. /// Gets whether the part is an active engine.
/// </summary> /// </summary>
public static bool IsEngine(this Part part) public static bool IsEngine(this Part part)
{ {
return part.HasModule<ModuleEngines>() || part.HasModule<ModuleEnginesFX>(); return HasModule<ModuleEngines>(part) || HasModule<ModuleEnginesFX>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a fuel line. /// Gets whether the part is a fuel line.
/// </summary> /// </summary>
public static bool IsFuelLine(this Part part) public static bool IsFuelLine(this Part part)
{ {
return (HasModule<CModuleFuelLine>(part)); return HasModule<CModuleFuelLine>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a generator. /// Gets whether the part is a generator.
/// </summary> /// </summary>
public static bool IsGenerator(this Part part) public static bool IsGenerator(this Part part)
{ {
return part.HasModule<ModuleGenerator>(); return HasModule<ModuleGenerator>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a launch clamp. /// Gets whether the part is a launch clamp.
/// </summary> /// </summary>
public static bool IsLaunchClamp(this Part part) public static bool IsLaunchClamp(this Part part)
{ {
return part.HasModule<LaunchClamp>(); return HasModule<LaunchClamp>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a parachute. /// Gets whether the part is a parachute.
/// </summary> /// </summary>
public static bool IsParachute(this Part part) public static bool IsParachute(this Part part)
{ {
return part.HasModule<ModuleParachute>(); return HasModule<ModuleParachute>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is considered a primary part on the vessel. /// Gets whether the part is considered a primary part on the vessel.
/// </summary> /// </summary>
public static bool IsPrimary(this Part part, List<Part> partsList, PartModule module) public static bool IsPrimary(this Part part, List<Part> partsList, PartModule module)
{ {
foreach (var vesselPart in partsList) for (int i = 0; i < partsList.Count; i++)
{ {
  var vesselPart = partsList[i];
if (!vesselPart.HasModule(module.ClassID)) if (!vesselPart.HasModule(module.ClassID))
{ {
continue; continue;
} }
   
if (vesselPart == part) if (vesselPart == part)
{ {
return true; return true;
} }
break; break;
} }
   
return false; return false;
} }
   
public static bool IsRcsModule(this Part part) public static bool IsRcsModule(this Part part)
{ {
return part.HasModule<ModuleRCS>(); return HasModule<ModuleRCS>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a sepratron. /// Gets whether the part is a sepratron.
/// </summary> /// </summary>
public static bool IsSepratron(this Part part) public static bool IsSepratron(this Part part)
{ {
return (part.IsSolidRocket() && part.ActivatesEvenIfDisconnected && part.IsDecoupledInStage(part.inverseStage)); return IsSolidRocket(part) && part.ActivatesEvenIfDisconnected && IsDecoupledInStage(part, part.inverseStage);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a deployable solar panel. /// Gets whether the part is a deployable solar panel.
/// </summary> /// </summary>
public static bool IsSolarPanel(this Part part) public static bool IsSolarPanel(this Part part)
{ {
return part.HasModule<ModuleDeployableSolarPanel>(); return HasModule<ModuleDeployableSolarPanel>(part);
} }
   
/// <summary> /// <summary>
/// Gets whether the part is a solid rocket motor. /// Gets whether the part is a solid rocket motor.
/// </summary> /// </summary>
public static bool IsSolidRocket(this Part part) public static bool IsSolidRocket(this Part part)
{ {
return part.HasModule<ModuleEngines>() && part.GetModuleEngines().throttleLocked; return (part.HasModule<ModuleEngines>() && part.GetModuleEngines().throttleLocked) || (part.HasModule<ModuleEnginesFX>() && part.GetModuleEnginesFx().throttleLocked);
} }
   
#endregion  
   
#region Nested Type: ProtoModuleDecoupler  
   
public class ProtoModuleDecoupler public class ProtoModuleDecoupler
{ {
#region Fields  
   
private readonly PartModule module; private readonly PartModule module;
   
#endregion  
   
#region Constructors  
   
public ProtoModuleDecoupler(PartModule module) public ProtoModuleDecoupler(PartModule module)
{ {
this.module = module; this.module = module;
   
if (this.module is ModuleDecouple) if (this.module is ModuleDecouple)
{ {
this.SetModuleDecouple(); SetModuleDecouple();
} }
else if (this.module is ModuleAnchoredDecoupler) else if (this.module is ModuleAnchoredDecoupler)
{ {
this.SetModuleAnchoredDecoupler(); SetModuleAnchoredDecoupler();
} }
} }
   
#endregion  
   
#region Properties  
   
public double EjectionForce { get; private set; } public double EjectionForce { get; private set; }
public bool IsOmniDecoupler { get; private set; } public bool IsOmniDecoupler { get; private set; }
   
#endregion  
   
#region Methods: private  
   
private void SetModuleAnchoredDecoupler() private void SetModuleAnchoredDecoupler()
{ {
var decoupler = this.module as ModuleAnchoredDecoupler; ModuleAnchoredDecoupler decoupler = module as ModuleAnchoredDecoupler;
if (decoupler == null) if (decoupler == null)
{ {
return; return;
} }
   
this.EjectionForce = decoupler.ejectionForce; EjectionForce = decoupler.ejectionForce;
} }
   
private void SetModuleDecouple() private void SetModuleDecouple()
{ {
var decoupler = this.module as ModuleDecouple; ModuleDecouple decoupler = module as ModuleDecouple;
if (decoupler == null) if (decoupler == null)
{ {
return; return;
} }
   
this.EjectionForce = decoupler.ejectionForce; EjectionForce = decoupler.ejectionForce;
this.IsOmniDecoupler = decoupler.isOmniDecoupler; IsOmniDecoupler = decoupler.isOmniDecoupler;
} }
  }
#endregion  
}  
   
#endregion  
   
#region Nested Type: ProtoModuleEngine  
   
public class ProtoModuleEngine public class ProtoModuleEngine
{ {
#region Fields  
   
private readonly PartModule module; private readonly PartModule module;
   
#endregion  
   
#region Constructors  
   
public ProtoModuleEngine(PartModule module) public ProtoModuleEngine(PartModule module)
{ {
this.module = module; this.module = module;
   
if (module is ModuleEngines) if (module is ModuleEngines)
{ {
this.SetModuleEngines(); SetModuleEngines();
} }
else if (module is ModuleEnginesFX) }
{  
this.SetModuleEnginesFx();  
}  
}  
   
#endregion  
   
#region Properties  
   
public double MaximumThrust { get; private set; } public double MaximumThrust { get; private set; }
public double MinimumThrust { get; private set; } public double MinimumThrust { get; private set; }
public List<Propellant> Propellants { get; private set; } public List<Propellant> Propellants { get; private set; }
   
#endregion  
   
#region Methods: public  
   
public float GetSpecificImpulse(float atmosphere) public float GetSpecificImpulse(float atmosphere)
{ {
if (this.module is ModuleEngines) if (module is ModuleEngines)
{ {
return (this.module as ModuleEngines).atmosphereCurve.Evaluate(atmosphere); return (module as ModuleEngines).atmosphereCurve.Evaluate(atmosphere);
}  
if (this.module is ModuleEnginesFX)  
{  
return (this.module as ModuleEnginesFX).atmosphereCurve.Evaluate(atmosphere);  
} }
return 0.0f; return 0.0f;
} }
   
#endregion  
   
#region Methods: private  
   
private void SetModuleEngines() private void SetModuleEngines()
{ {
var engine = this.module as ModuleEngines; ModuleEngines engine = module as ModuleEngines;
if (engine == null) if (engine == null)
{ {
return; return;
} }
   
this.MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01); MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01);
this.MinimumThrust = engine.minThrust; MinimumThrust = engine.minThrust;
this.Propellants = engine.propellants; Propellants = engine.propellants;
} }
   
private void SetModuleEnginesFx() private void SetModuleEnginesFx()
{ {
var engine = this.module as ModuleEnginesFX; ModuleEnginesFX engine = module as ModuleEnginesFX;
if (engine == null) if (engine == null)
{ {
return; return;
} }
   
this.MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01); MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01);
this.MinimumThrust = engine.minThrust; MinimumThrust = engine.minThrust;
this.Propellants = engine.propellants; Propellants = engine.propellants;
} }
  }
#endregion  
}  
   
#endregion  
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System; using System;
   
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
namespace KerbalEngineer.Flight namespace KerbalEngineer.Flight
{ {
/// <summary> /// <summary>
/// Graphical controller for section interaction in the form of a menu system. /// Graphical controller for section interaction in the form of a menu system.
/// </summary> /// </summary>
[KSPAddon(KSPAddon.Startup.Flight, false)] [KSPAddon(KSPAddon.Startup.Flight, false)]
public class ActionMenu : MonoBehaviour public class ActionMenu : MonoBehaviour
{ {
#region Fields #region Fields
   
private ActionMenuGui actionMenuGui; private ActionMenuGui actionMenuGui;
private ApplicationLauncherButton button; private ApplicationLauncherButton button;
   
#endregion #endregion
   
#region Methods: protected #region Methods: protected
   
protected void Awake() protected void Awake()
{ {
try try
{ {
GameEvents.onGUIApplicationLauncherReady.Add(this.OnGuiAppLauncherReady); GameEvents.onGUIApplicationLauncherReady.Add(this.OnGuiAppLauncherReady);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
Logger.Log("ActionMenu was created."); Logger.Log("ActionMenu was created.");
} }
   
  protected void Start()
  {
  if (button == null)
  {
  OnGuiAppLauncherReady();
  }
  }
   
protected void OnDestroy() protected void OnDestroy()
{ {
try try
{ {
GameEvents.onGUIApplicationLauncherReady.Remove(this.OnGuiAppLauncherReady); GameEvents.onGUIApplicationLauncherReady.Remove(this.OnGuiAppLauncherReady);
GameEvents.onHideUI.Remove(this.OnHide); GameEvents.onHideUI.Remove(this.OnHide);
GameEvents.onShowUI.Remove(this.OnShow); GameEvents.onShowUI.Remove(this.OnShow);
ApplicationLauncher.Instance.RemoveModApplication(this.button); if (button != null)
  {
  ApplicationLauncher.Instance.RemoveModApplication(this.button);
  }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
Logger.Log("ActionMenu was destroyed."); Logger.Log("ActionMenu was destroyed.");
} }
   
protected void Update() protected void Update()
{ {
try try
{ {
if (this.button == null) if (this.button == null)
{ {
return; return;
} }
if (FlightEngineerCore.IsDisplayable && this.button.State == RUIToggleButton.ButtonState.DISABLED) if (FlightEngineerCore.IsDisplayable && this.button.State == RUIToggleButton.ButtonState.DISABLED)
{ {
this.button.Enable(); this.button.Enable();
} }
else if (!FlightEngineerCore.IsDisplayable && this.button.State != RUIToggleButton.ButtonState.DISABLED) else if (!FlightEngineerCore.IsDisplayable && this.button.State != RUIToggleButton.ButtonState.DISABLED)
{ {
this.button.Disable(); this.button.Disable();
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion #endregion
   
#region Methods: private #region Methods: private
   
private void OnFalse() private void OnFalse()
{ {
try try
{ {
this.actionMenuGui.enabled = false; this.actionMenuGui.enabled = false;
this.actionMenuGui.StayOpen = false; this.actionMenuGui.StayOpen = false;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
private void OnGuiAppLauncherReady() private void OnGuiAppLauncherReady()
{ {
try try
{ {
this.button = ApplicationLauncher.Instance.AddModApplication( this.button = ApplicationLauncher.Instance.AddModApplication(
this.OnTrue, this.OnTrue,
this.OnFalse, this.OnFalse,
this.OnHover, this.OnHover,
this.OnHoverOut, this.OnHoverOut,
null, null,
null, null,
ApplicationLauncher.AppScenes.ALWAYS, ApplicationLauncher.AppScenes.ALWAYS,
GameDatabase.Instance.GetTexture("KerbalEngineer/Textures/ToolbarIcon", false)); GameDatabase.Instance.GetTexture("KerbalEngineer/Textures/ToolbarIcon", false));
this.actionMenuGui = this.button.gameObject.AddComponent<ActionMenuGui>(); this.actionMenuGui = this.button.gameObject.AddComponent<ActionMenuGui>();
this.actionMenuGui.transform.parent = this.button.transform; this.actionMenuGui.transform.parent = this.button.transform;
ApplicationLauncher.Instance.EnableMutuallyExclusive(this.button); ApplicationLauncher.Instance.EnableMutuallyExclusive(this.button);
GameEvents.onHideUI.Add(this.OnHide); GameEvents.onHideUI.Add(this.OnHide);
GameEvents.onShowUI.Add(this.OnShow); GameEvents.onShowUI.Add(this.OnShow);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
private void OnHide() private void OnHide()
{ {
try try
{ {
this.actionMenuGui.Hidden = true; this.actionMenuGui.Hidden = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
private void OnHover() private void OnHover()
{ {
try try
{ {
this.actionMenuGui.enabled = true; this.actionMenuGui.enabled = true;
this.actionMenuGui.Hovering = true; this.actionMenuGui.Hovering = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
private void OnHoverOut() private void OnHoverOut()
{ {
try try
{ {
this.actionMenuGui.Hovering = false; this.actionMenuGui.Hovering = false;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
private void OnShow() private void OnShow()
{ {
try try
{ {
this.actionMenuGui.Hidden = false; this.actionMenuGui.Hidden = false;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
private void OnTrue() private void OnTrue()
{ {
try try
{ {
this.actionMenuGui.enabled = true; this.actionMenuGui.enabled = true;
this.actionMenuGui.StayOpen = true; this.actionMenuGui.StayOpen = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex); Logger.Exception(ex);
} }
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
   
using KerbalEngineer.Extensions; using KerbalEngineer.Extensions;
   
#endregion #endregion
   
namespace KerbalEngineer.Flight.Readouts.Surface namespace KerbalEngineer.Flight.Readouts.Surface
{ {
  using UnityEngine;
   
public class AtmosphericProcessor : IUpdatable, IUpdateRequest public class AtmosphericProcessor : IUpdatable, IUpdateRequest
{ {
#region Instance #region Instance
   
#region Fields #region Fields
   
private static readonly AtmosphericProcessor instance = new AtmosphericProcessor(); private static readonly AtmosphericProcessor instance = new AtmosphericProcessor();
   
#endregion #endregion
   
#region Properties #region Properties
   
/// <summary> /// <summary>
/// Gets the current instance of the atmospheric processor. /// Gets the current instance of the atmospheric processor.
/// </summary> /// </summary>
public static AtmosphericProcessor Instance public static AtmosphericProcessor Instance
{ {
get { return instance; } get
  {
  return instance;
  }
} }
   
#endregion #endregion
   
#endregion #endregion
   
#region Fields #region Fields
   
private MethodInfo farTerminalVelocity; private MethodInfo farTerminalVelocity;
private bool hasCheckedAeroMods; private bool hasCheckedAeroMods;
   
#endregion #endregion
   
#region Properties #region Properties
   
/// <summary> /// <summary>
/// Gets the deceleration caused by drag. /// Gets the deceleration caused by drag.
/// </summary> /// </summary>
public static double Deceleration { get; private set; } public static double Deceleration { get; private set; }
   
/// <summary> /// <summary>
/// Gets the difference between current velocity and terminal velocity. /// Gets the difference between current velocity and terminal velocity.
/// </summary> /// </summary>
public static double Efficiency { get; private set; } public static double Efficiency { get; private set; }
   
/// <summary> /// <summary>
/// Gets whether FAR is installed. /// Gets whether FAR is installed.
/// </summary> /// </summary>
public static bool FarInstalled { get; private set; } public static bool FarInstalled { get; private set; }
   
/// <summary> /// <summary>
/// Gets whether NEAR is installed. /// Gets whether NEAR is installed.
/// </summary> /// </summary>
public static bool NearInstalled { get; private set; } public static bool NearInstalled { get; private set; }
   
/// <summary> /// <summary>
/// Gets whether the details are ready to be shown. /// Gets whether the details are ready to be shown.
/// </summary> /// </summary>
public static bool ShowDetails { get; private set; } public static bool ShowDetails { get; private set; }
   
/// <summary> /// <summary>
/// Gets the terminal velocity of the active vessel. /// Gets the terminal velocity of the active vessel.
/// </summary> /// </summary>
public static double TerminalVelocity { get; private set; } public static double TerminalVelocity { get; private set; }
   
#endregion #endregion
   
#region IUpdatable Members #region IUpdatable Members
   
/// <summary> /// <summary>
/// Updates the details by recalculating if requested. /// Updates the details by recalculating if requested.
/// </summary> /// </summary>
public void Update() public void Update()
{ {
try try
{ {
if (!this.hasCheckedAeroMods) if (!this.hasCheckedAeroMods)
{ {
this.CheckAeroMods(); this.CheckAeroMods();
} }
   
if (FlightGlobals.ActiveVessel.atmDensity < double.Epsilon || NearInstalled) if (FlightGlobals.ActiveVessel.atmDensity < double.Epsilon || NearInstalled)
{ {
ShowDetails = false; ShowDetails = false;
return; return;
} }
   
ShowDetails = true; ShowDetails = true;
   
if (FarInstalled) if (FarInstalled)
{ {
TerminalVelocity = (double)this.farTerminalVelocity.Invoke(null, null); TerminalVelocity = (double)this.farTerminalVelocity.Invoke(null, null);
} }
else else
{ {
var mass = FlightGlobals.ActiveVessel.parts.Sum(p => p.GetWetMass()); var m = FlightGlobals.ActiveVessel.parts.Sum(part => part.GetWetMass()) * 1000.0;
var drag = FlightGlobals.ActiveVessel.parts.Sum(p => p.GetWetMass() * p.maximum_drag); var g = FlightGlobals.getGeeForceAtPosition(FlightGlobals.ship_position).magnitude;
var grav = FlightGlobals.getGeeForceAtPosition(FlightGlobals.ship_position).magnitude; var a = FlightGlobals.ActiveVessel.parts.Sum(part => part.DragCubes.AreaDrag) * PhysicsGlobals.DragCubeMultiplier;
var atmo = FlightGlobals.ActiveVessel.atmDensity; var p = FlightGlobals.ActiveVessel.atmDensity;
var coef = FlightGlobals.DragMultiplier; var c = PhysicsGlobals.DragMultiplier;
   
TerminalVelocity = Math.Sqrt((2 * mass * grav) / (atmo * drag * coef)); TerminalVelocity = Math.Sqrt((2.0 * m * g) / (p * a * c));
} }
   
Efficiency = FlightGlobals.ship_srfSpeed / TerminalVelocity; Efficiency = FlightGlobals.ship_srfSpeed / TerminalVelocity;
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "AtmosphericProcessor->Update"); Logger.Exception(ex, "AtmosphericProcessor->Update");
} }
} }
   
#endregion #endregion
   
#region IUpdateRequest Members #region IUpdateRequest Members
   
/// <summary> /// <summary>
/// Gets and sets whether the updatable object should be updated. /// Gets and sets whether the updatable object should be updated.
/// </summary> /// </summary>
public bool UpdateRequested { get; set; } public bool UpdateRequested { get; set; }
   
#endregion #endregion
   
#region Methods: public #region Methods: public
   
/// <summary> /// <summary>
/// Request an update to calculate the details. /// Request an update to calculate the details.
/// </summary> /// </summary>
public static void RequestUpdate() public static void RequestUpdate()
{ {
instance.UpdateRequested = true; instance.UpdateRequested = true;
} }
   
#endregion #endregion
   
#region Private Methods #region Private Methods
   
private void CheckAeroMods() private void CheckAeroMods()
{ {
try try
{ {
this.hasCheckedAeroMods = true; this.hasCheckedAeroMods = true;
   
foreach (var loadedAssembly in AssemblyLoader.loadedAssemblies) foreach (var loadedAssembly in AssemblyLoader.loadedAssemblies)
{ {
switch (loadedAssembly.name) switch (loadedAssembly.name)
{ {
case "FerramAerospaceResearch": case "FerramAerospaceResearch":
this.farTerminalVelocity = loadedAssembly.assembly.GetType("ferram4.FARAPI").GetMethod("GetActiveControlSys_TermVel"); this.farTerminalVelocity = loadedAssembly.assembly.GetType("ferram4.FARAPI").GetMethod("GetActiveControlSys_TermVel");
FarInstalled = true; FarInstalled = true;
Logger.Log("FAR detected!"); Logger.Log("FAR detected!");
break; break;
   
case "NEAR": case "NEAR":
NearInstalled = true; NearInstalled = true;
Logger.Log("NEAR detected! Turning off atmospheric details!"); Logger.Log("NEAR detected! Turning off atmospheric details!");
break; break;
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.Exception(ex, "AtmosphericProcessor->CheckAeroMods"); Logger.Exception(ex, "AtmosphericProcessor->CheckAeroMods");
} }
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
namespace KerbalEngineer.Flight.Readouts.Vessel namespace KerbalEngineer.Flight.Readouts.Vessel
{ {
#region Using Directives #region Using Directives
   
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
public class AttitudeProcessor : IUpdatable, IUpdateRequest public class AttitudeProcessor : IUpdatable, IUpdateRequest
{ {
#region Fields #region Fields
   
private static readonly AttitudeProcessor instance = new AttitudeProcessor(); private static readonly AttitudeProcessor instance = new AttitudeProcessor();
   
private Vector3 centreOfMass = Vector3.zero; private Vector3 centreOfMass = Vector3.zero;
   
private double heading; private double heading;
private double headingRate; private double headingRate;
private Vector3 north = Vector3.zero; private Vector3 north = Vector3.zero;
private double pitch; private double pitch;
private double pitchRate; private double pitchRate;
private double previousHeading; private double previousHeading;
private double previousPitch; private double previousPitch;
private double previousRoll; private double previousRoll;
private double roll; private double roll;
private double rollRate; private double rollRate;
private Quaternion surfaceRotation; private Quaternion surfaceRotation;
private Vector3 up = Vector3.zero; private Vector3 up = Vector3.zero;
   
#endregion #endregion
   
#region Properties #region Properties
   
public static double Heading public static double Heading
{ {
get { return instance.heading; } get { return instance.heading; }
} }
   
public static double HeadingRate public static double HeadingRate
{ {
get { return instance.headingRate; } get { return instance.headingRate; }
} }
   
public static AttitudeProcessor Instance public static AttitudeProcessor Instance
{ {
get { return instance; } get { return instance; }
} }
   
public static double Pitch public static double Pitch
{ {
get { return instance.pitch; } get { return instance.pitch; }
} }
   
public static double PitchRate public static double PitchRate
{ {
get { return instance.pitchRate; } get { return instance.pitchRate; }
} }
   
public static double Roll public static double Roll
{ {
get { return instance.roll; } get { return instance.roll; }
} }
   
public static double RollRate public static double RollRate
{ {
get { return instance.rollRate; } get { return instance.rollRate; }
} }
   
public bool UpdateRequested { get; set; } public bool UpdateRequested { get; set; }
   
#endregion #endregion
   
#region Methods #region Methods
   
public static void RequestUpdate() public static void RequestUpdate()
{ {
instance.UpdateRequested = true; instance.UpdateRequested = true;
} }
   
public void Update() public void Update()
{ {
this.surfaceRotation = this.GetSurfaceRotation(); this.surfaceRotation = this.GetSurfaceRotation();
   
this.previousHeading = this.heading; this.previousHeading = this.heading;
this.previousPitch = this.pitch; this.previousPitch = this.pitch;
this.previousRoll = this.roll; this.previousRoll = this.roll;
   
// This code was derived from MechJeb2's implementation for getting the vessel's surface relative rotation. // This code was derived from MechJeb2's implementation for getting the vessel's surface relative rotation.
this.heading = this.surfaceRotation.eulerAngles.y; this.heading = this.surfaceRotation.eulerAngles.y;
this.pitch = this.surfaceRotation.eulerAngles.x > 180.0f this.pitch = this.surfaceRotation.eulerAngles.x > 180.0f
? 360.0f - this.surfaceRotation.eulerAngles.x ? 360.0f - this.surfaceRotation.eulerAngles.x
: -this.surfaceRotation.eulerAngles.x; : -this.surfaceRotation.eulerAngles.x;
this.roll = this.surfaceRotation.eulerAngles.z > 180.0f this.roll = this.surfaceRotation.eulerAngles.z > 180.0f
? this.surfaceRotation.eulerAngles.z - 360.0f ? this.surfaceRotation.eulerAngles.z - 360.0f
: this.surfaceRotation.eulerAngles.z; : this.surfaceRotation.eulerAngles.z;
   
this.headingRate = this.heading - this.previousHeading; this.headingRate = this.heading - this.previousHeading;
this.pitchRate = this.pitch - this.previousPitch; this.pitchRate = this.pitch - this.previousPitch;
this.rollRate = this.roll - this.previousRoll; this.rollRate = this.roll - this.previousRoll;
} }
   
private Quaternion GetSurfaceRotation() private Quaternion GetSurfaceRotation()
{ {
// This code was derived from MechJeb2's implementation for getting the vessel's surface relative rotation. // This code was derived from MechJeb2's implementation for getting the vessel's surface relative rotation.
this.centreOfMass = FlightGlobals.ActiveVessel.findWorldCenterOfMass(); this.centreOfMass = FlightGlobals.ActiveVessel.findWorldCenterOfMass();
this.up = (this.centreOfMass - FlightGlobals.ActiveVessel.mainBody.position).normalized; this.up = (this.centreOfMass - FlightGlobals.ActiveVessel.mainBody.position).normalized;
this.north = Vector3.Exclude(this.up, (FlightGlobals.ActiveVessel.mainBody.position + FlightGlobals.ActiveVessel.mainBody.transform.up * (float)FlightGlobals.ActiveVessel.mainBody.Radius) - this.centreOfMass).normalized; this.north = Vector3.ProjectOnPlane((FlightGlobals.ActiveVessel.mainBody.position + FlightGlobals.ActiveVessel.mainBody.transform.up * (float)FlightGlobals.ActiveVessel.mainBody.Radius) - this.centreOfMass, this.up).normalized;
   
return Quaternion.Inverse(Quaternion.Euler(90.0f, 0.0f, 0.0f) * Quaternion.Inverse(FlightGlobals.ActiveVessel.transform.rotation) * Quaternion.LookRotation(this.north, this.up)); return Quaternion.Inverse(Quaternion.Euler(90.0f, 0.0f, 0.0f) * Quaternion.Inverse(FlightGlobals.ActiveVessel.transform.rotation) * Quaternion.LookRotation(this.north, this.up));
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
namespace KerbalEngineer.Flight.Readouts.Vessel namespace KerbalEngineer.Flight.Readouts.Vessel
{ {
#region Using Directives #region Using Directives
   
using Helpers; using Helpers;
using Sections; using Sections;
   
#endregion #endregion
   
public class PartCount : ReadoutModule public class PartCount : ReadoutModule
{ {
#region Constructors #region Constructors
   
public PartCount() public PartCount()
{ {
this.Name = "Part Count"; this.Name = "Part Count";
this.Category = ReadoutCategory.GetCategory("Vessel"); this.Category = ReadoutCategory.GetCategory("Vessel");
this.HelpString = string.Empty; this.HelpString = string.Empty;
this.IsDefault = true; this.IsDefault = true;
} }
   
#endregion #endregion
   
#region Methods #region Methods
   
public override void Draw(SectionModule section) public override void Draw(SectionModule section)
{ {
if (SimulationProcessor.ShowDetails) if (SimulationProcessor.ShowDetails)
{ {
this.DrawLine(Units.ConcatF(SimulationProcessor.LastStage.partCount, SimulationProcessor.LastStage.totalPartCount), section.IsHud); this.DrawLine(Units.ConcatF(SimulationProcessor.LastStage.partCount, SimulationProcessor.LastStage.totalPartCount, 0), section.IsHud);
} }
} }
   
public override void Reset() public override void Reset()
{ {
FlightEngineerCore.Instance.AddUpdatable(SimulationProcessor.Instance); FlightEngineerCore.Instance.AddUpdatable(SimulationProcessor.Instance);
} }
   
public override void Update() public override void Update()
{ {
SimulationProcessor.RequestUpdate(); SimulationProcessor.RequestUpdate();
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
   
   
#endregion #endregion
   
namespace KerbalEngineer.Flight.Readouts.Vessel namespace KerbalEngineer.Flight.Readouts.Vessel
{ {
#region Using Directives #region Using Directives
   
using System; using System;
using VesselSimulator; using VesselSimulator;
   
#endregion #endregion
   
public class SimulationProcessor : IUpdatable, IUpdateRequest public class SimulationProcessor : IUpdatable, IUpdateRequest
{ {
#region Instance #region Instance
   
#region Fields #region Fields
   
private static readonly SimulationProcessor instance = new SimulationProcessor(); private static readonly SimulationProcessor instance = new SimulationProcessor();
   
#endregion #endregion
   
#region Constructors #region Constructors
   
static SimulationProcessor() static SimulationProcessor()
{ {
SimManager.OnReady += GetStageInfo; SimManager.OnReady += GetStageInfo;
} }
   
#endregion #endregion
   
#region Properties #region Properties
   
/// <summary> /// <summary>
/// Gets the current instance of the simulation processor. /// Gets the current instance of the simulation processor.
/// </summary> /// </summary>
public static SimulationProcessor Instance public static SimulationProcessor Instance
{ {
get { return instance; } get { return instance; }
} }
   
#endregion #endregion
   
#endregion #endregion
   
#region Properties #region Properties
   
/// <summary> /// <summary>
/// Gets the currently active vessel stage. /// Gets the currently active vessel stage.
/// </summary> /// </summary>
public static Stage LastStage { get; private set; } public static Stage LastStage { get; private set; }
   
/// <summary> /// <summary>
/// Gets whether the details are ready to be shown. /// Gets whether the details are ready to be shown.
/// </summary> /// </summary>
public static bool ShowDetails { get; private set; } public static bool ShowDetails { get; private set; }
   
/// <summary> /// <summary>
/// Gets an array of the vessel stages. /// Gets an array of the vessel stages.
/// </summary> /// </summary>
public static Stage[] Stages { get; private set; } public static Stage[] Stages { get; private set; }
   
public bool UpdateRequested { get; set; } public bool UpdateRequested { get; set; }
   
#endregion #endregion
   
#region Methods #region Methods
   
private static void GetStageInfo() private static void GetStageInfo()
{ {
Stages = SimManager.Stages; Stages = SimManager.Stages;
LastStage = SimManager.LastStage; LastStage = SimManager.LastStage;
} }
   
public static void RequestUpdate() public static void RequestUpdate()
{ {
instance.UpdateRequested = true; instance.UpdateRequested = true;
} }
   
public void Update() public void Update()
{ {
SimManager.RequestSimulation(); SimManager.RequestSimulation();
SimManager.TryStartSimulation(); SimManager.TryStartSimulation();
   
if (!SimManager.ResultsReady()) if (!SimManager.ResultsReady())
{ {
return; return;
} }
   
if (Stages != null && LastStage != null) if (Stages != null && LastStage != null)
{ {
ShowDetails = true; ShowDetails = true;
} }
   
if (FlightGlobals.ActiveVessel != null) if (FlightGlobals.ActiveVessel != null)
{ {
SimManager.Gravity = FlightGlobals.ActiveVessel.mainBody.gravParameter / SimManager.Gravity = FlightGlobals.ActiveVessel.mainBody.gravParameter /
Math.Pow(FlightGlobals.ActiveVessel.mainBody.Radius + Math.Pow(FlightGlobals.ActiveVessel.mainBody.Radius +
FlightGlobals.ActiveVessel.mainBody.GetAltitude(FlightGlobals.ActiveVessel.CoM), 2); FlightGlobals.ActiveVessel.mainBody.GetAltitude(FlightGlobals.ActiveVessel.CoM), 2);
SimManager.Velocity = FlightGlobals.ActiveVessel.srfSpeed; SimManager.Mach = FlightGlobals.ActiveVessel.mach;
} }
} }
   
#endregion #endregion
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
using System; using System;
   
namespace KerbalEngineer namespace KerbalEngineer
{ {
public class VectorAverager public class VectorAverager
{ {
private Vector3d sum = Vector3d.zero; private Vector3d sum = Vector3d.zero;
private uint count = 0; private uint count = 0;
   
public void Add(Vector3d v) { public void Add(Vector3d v) {
sum += v; sum += v;
count += 1; count += 1;
} }
   
public Vector3d Get() { public Vector3d Get() {
if (count > 0) { if (count > 0) {
return sum / count; return sum / count;
} else { } else {
return Vector3d.zero; return Vector3d.zero;
} }
} }
   
  public void Reset()
  {
  sum = Vector3d.zero;
  count = 0;
  }
} }
   
public class WeightedVectorAverager public class WeightedVectorAverager
{ {
private Vector3d sum = Vector3d.zero; private Vector3d sum = Vector3d.zero;
private double totalweight = 0; private double totalweight = 0;
   
public void Add(Vector3d v, double weight) { public void Add(Vector3d v, double weight) {
sum += v * weight; sum += v * weight;
totalweight += weight; totalweight += weight;
} }
   
public Vector3d Get() { public Vector3d Get() {
if (totalweight > 0) { if (totalweight > 0) {
return sum / totalweight; return sum / totalweight;
} else { } else {
return Vector3d.zero; return Vector3d.zero;
} }
} }
   
public double GetTotalWeight() { public double GetTotalWeight() {
return totalweight; return totalweight;
} }
   
  public void Reset()
  {
  sum = Vector3d.zero;
  totalweight = 0.0;
  }
} }
} }
   
   
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
  using KerbalEngineer.VesselSimulator;
   
namespace KerbalEngineer namespace KerbalEngineer
{ {
// a (force, application point) tuple // a (force, application point) tuple
public class AppliedForce public class AppliedForce
{ {
  private static readonly Pool<AppliedForce> pool = new Pool<AppliedForce>(Create, Reset);
   
public Vector3d vector; public Vector3d vector;
public Vector3d applicationPoint; public Vector3d applicationPoint;
   
public AppliedForce(Vector3d vector, Vector3d applicationPoint) { static private AppliedForce Create()
this.vector = vector; {
this.applicationPoint = applicationPoint; return new AppliedForce();
} }
   
  static private void Reset(AppliedForce appliedForce) { }
   
  static public AppliedForce New(Vector3d vector, Vector3d applicationPoint)
  {
  AppliedForce force = pool.Borrow();
  force.vector = vector;
  force.applicationPoint = applicationPoint;
  return force;
  }
   
  public void Release()
  {
  pool.Release(this);
  }
   
   
} }
   
// This class was mostly adapted from FARCenterQuery, part of FAR, by ferram4, GPLv3 // This class was mostly adapted from FARCenterQuery, part of FAR, by ferram4, GPLv3
// https://github.com/ferram4/Ferram-Aerospace-Research/blob/master/FerramAerospaceResearch/FARCenterQuery.cs // https://github.com/ferram4/Ferram-Aerospace-Research/blob/master/FerramAerospaceResearch/FARCenterQuery.cs
// Also see https://en.wikipedia.org/wiki/Resultant_force // Also see https://en.wikipedia.org/wiki/Resultant_force
   
// It accumulates forces and their points of applications, and provides methods for // It accumulates forces and their points of applications, and provides methods for
// calculating the effective torque at any position, as well as the minimum-torque net force application point. // calculating the effective torque at any position, as well as the minimum-torque net force application point.
// //
// The latter is a non-trivial issue; there is a 1-dimensional line of physically-equivalent solutions parallel // The latter is a non-trivial issue; there is a 1-dimensional line of physically-equivalent solutions parallel
// to the resulting force vector; the solution closest to the weighted average of force positions is chosen. // to the resulting force vector; the solution closest to the weighted average of force positions is chosen.
// In the case of non-parallel forces, there usually is an infinite number of such lines, all of which have // In the case of non-parallel forces, there usually is an infinite number of such lines, all of which have
// some amount of residual torque. The line with the least amount of residual torque is chosen. // some amount of residual torque. The line with the least amount of residual torque is chosen.
public class ForceAccumulator public class ForceAccumulator
{ {
// Total force. // Total force.
private Vector3d totalForce = Vector3d.zero; private Vector3d totalForce = Vector3d.zero;
// Torque needed to compensate if force were applied at origin. // Torque needed to compensate if force were applied at origin.
private Vector3d totalZeroOriginTorque = Vector3d.zero; private Vector3d totalZeroOriginTorque = Vector3d.zero;
   
// Weighted average of force application points. // Weighted average of force application points.
private WeightedVectorAverager avgApplicationPoint = new WeightedVectorAverager(); private WeightedVectorAverager avgApplicationPoint = new WeightedVectorAverager();
   
// Feed an force to the accumulator. // Feed an force to the accumulator.
public void AddForce(Vector3d applicationPoint, Vector3d force) public void AddForce(Vector3d applicationPoint, Vector3d force)
{ {
totalForce += force; totalForce += force;
totalZeroOriginTorque += Vector3d.Cross(applicationPoint, force); totalZeroOriginTorque += Vector3d.Cross(applicationPoint, force);
avgApplicationPoint.Add(applicationPoint, force.magnitude); avgApplicationPoint.Add(applicationPoint, force.magnitude);
} }
   
public Vector3d GetAverageForceApplicationPoint() { public Vector3d GetAverageForceApplicationPoint() {
return avgApplicationPoint.Get(); return avgApplicationPoint.Get();
} }
   
public void AddForce(AppliedForce force) { public void AddForce(AppliedForce force) {
AddForce(force.applicationPoint, force.vector); AddForce(force.applicationPoint, force.vector);
} }
   
// Residual torque for given force application point. // Residual torque for given force application point.
public Vector3d TorqueAt(Vector3d origin) public Vector3d TorqueAt(Vector3d origin)
{ {
return totalZeroOriginTorque - Vector3d.Cross(origin, totalForce); return totalZeroOriginTorque - Vector3d.Cross(origin, totalForce);
} }
   
// Total force vector. // Total force vector.
public Vector3d GetTotalForce() public Vector3d GetTotalForce()
{ {
return totalForce; return totalForce;
} }
   
// Returns the minimum-residual-torque force application point that is closest to origin. // Returns the minimum-residual-torque force application point that is closest to origin.
// Note that TorqueAt(GetMinTorquePos()) is always parallel to totalForce. // Note that TorqueAt(GetMinTorquePos()) is always parallel to totalForce.
public Vector3d GetMinTorqueForceApplicationPoint(Vector3d origin) public Vector3d GetMinTorqueForceApplicationPoint(Vector3d origin)
{ {
double fmag = totalForce.sqrMagnitude; double fmag = totalForce.sqrMagnitude;
if (fmag <= 0) { if (fmag <= 0) {
return origin; return origin;
} }
   
return origin + Vector3d.Cross(totalForce, TorqueAt(origin)) / fmag; return origin + Vector3d.Cross(totalForce, TorqueAt(origin)) / fmag;
} }
   
public Vector3d GetMinTorqueForceApplicationPoint() public Vector3d GetMinTorqueForceApplicationPoint()
{ {
return GetMinTorqueForceApplicationPoint(avgApplicationPoint.Get()); return GetMinTorqueForceApplicationPoint(avgApplicationPoint.Get());
} }
   
  public void Reset()
  {
  totalForce = Vector3d.zero;
  totalZeroOriginTorque = Vector3d.zero;
  avgApplicationPoint.Reset();
  }
} }
} }
  using System.Collections.Generic;
 
  namespace KerbalEngineer
  {
  /// <summary>
  /// Pool of object
  /// </summary>
  public class Pool<T> {
 
  private readonly Stack<T> values = new Stack<T>();
 
  private readonly CreateDelegate<T> create;
  private readonly ResetDelegate<T> reset;
 
  public delegate R CreateDelegate<out R>();
  public delegate void ResetDelegate<in T1>(T1 a);
 
  /// <summary>
  /// Creates an empty pool with the specified object creation and reset delegates.
  /// </summary>
  public Pool(CreateDelegate<T> create, ResetDelegate<T> reset) {
  this.create = create;
  this.reset = reset;
  }
 
  /// <summary>
  /// Borrows an object from the pool.
  /// </summary>
  public T Borrow() {
  lock (values) {
  return values.Count > 0 ? values.Pop() : create();
  }
  }
 
  /// <summary>
  /// Release an object, reset it and returns it to the pool.
  /// </summary>
  public void Release(T value) {
  reset(value);
  lock (values) {
  values.Push(value);
  }
  }
 
  /// <summary>
  /// Current size of the pool.
  /// </summary>
  public int Count()
  {
  return values.Count;
  }
  }
  }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
namespace KerbalEngineer.Helpers namespace KerbalEngineer.Helpers
{ {
#region Using Directives #region Using Directives
   
using System; using System;
   
#endregion #endregion
   
public static class Units public static class Units
{ {
#region Methods #region Methods
  public const double GRAVITY = 9.80665;
   
public static string Concat(int value1, int value2) public static string Concat(int value1, int value2)
{ {
return value1 + " / " + value2; return value1 + " / " + value2;
} }
   
public static string ConcatF(double value1, double value2, int decimals = 1) public static string ConcatF(double value1, double value2, int decimals = 1)
{ {
return value1.ToString("F" + decimals) + " / " + value2.ToString("F" + decimals); return value1.ToString("F" + decimals) + " / " + value2.ToString("F" + decimals);
} }
   
public static string ConcatF(double value1, double value2, double value3, int decimals = 1) public static string ConcatF(double value1, double value2, double value3, int decimals = 1)
{ {
return value1.ToString("F" + decimals) + " / " + value2.ToString("F" + decimals) + " / " + value3.ToString("F" + decimals); return value1.ToString("F" + decimals) + " / " + value2.ToString("F" + decimals) + " / " + value3.ToString("F" + decimals);
} }
   
public static string ConcatN(double value1, double value2, int decimals = 1) public static string ConcatN(double value1, double value2, int decimals = 1)
{ {
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals); return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals);
} }
   
public static string ConcatN(double value1, double value2, double value3, int decimals = 1) public static string ConcatN(double value1, double value2, double value3, int decimals = 1)
{ {
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + " / " + value3.ToString("N" + decimals); return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + " / " + value3.ToString("N" + decimals);
} }
   
public static string Cost(double value, int decimals = 1) public static string Cost(double value, int decimals = 1)
{ {
if (value >= 1000000.0) if (value >= 1000000.0)
{ {
return (value / 1000.0).ToString("N" + decimals) + "K"; return (value / 1000.0).ToString("N" + decimals) + "K";
} }
return value.ToString("N" + decimals); return value.ToString("N" + decimals);
} }
   
public static string Cost(double value1, double value2, int decimals = 1) public static string Cost(double value1, double value2, int decimals = 1)
{ {
if (value1 >= 1000000.0 || value2 >= 1000000.0) if (value1 >= 1000000.0 || value2 >= 1000000.0)
{ {
return (value1 / 1000.0).ToString("N" + decimals) + " / " + (value2 / 1000.0).ToString("N" + decimals) + "K"; return (value1 / 1000.0).ToString("N" + decimals) + " / " + (value2 / 1000.0).ToString("N" + decimals) + "K";
} }
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals); return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals);
} }
   
public static string ToAcceleration(double value, int decimals = 2) public static string ToAcceleration(double value, int decimals = 2)
{ {
return value.ToString("N" + decimals) + "m/s²"; return value.ToString("N" + decimals) + "m/s²";
} }
   
public static string ToAcceleration(double value1, double value2, int decimals = 2) public static string ToAcceleration(double value1, double value2, int decimals = 2)
{ {
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + "m/s²"; return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + "m/s²";
} }
   
public static string ToAngle(double value, int decimals = 5) public static string ToAngle(double value, int decimals = 5)
{ {
return value.ToString("F" + decimals) + "°"; return value.ToString("F" + decimals) + "°";
} }
   
public static string ToDistance(double value, int decimals = 1) public static string ToDistance(double value, int decimals = 1)
{ {
if (Math.Abs(value) < 1000000.0) if (Math.Abs(value) < 1000000.0)
{ {
if (Math.Abs(value) >= 10.0) if (Math.Abs(value) >= 10.0)
{ {
return value.ToString("N" + decimals) + "m"; return value.ToString("N" + decimals) + "m";
} }
   
value *= 100.0; value *= 100.0;
if (Math.Abs(value) >= 100.0) if (Math.Abs(value) >= 100.0)
{ {
return value.ToString("N" + decimals) + "cm"; return value.ToString("N" + decimals) + "cm";
} }
   
value *= 10.0; value *= 10.0;
return value.ToString("N" + decimals) + "mm"; return value.ToString("N" + decimals) + "mm";
} }
   
value /= 1000.0; value /= 1000.0;
if (Math.Abs(value) < 1000000.0) if (Math.Abs(value) < 1000000.0)
{ {
return value.ToString("N" + decimals) + "km"; return value.ToString("N" + decimals) + "km";
} }
   
value /= 1000.0; value /= 1000.0;
return value.ToString("N" + decimals) + "Mm"; return value.ToString("N" + decimals) + "Mm";
} }
   
public static string ToForce(double value) public static string ToForce(double value)
{ {
return value.ToString((value < 100000.0) ? (value < 10000.0) ? (value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0") + "kN"; return value.ToString((value < 100000.0) ? (value < 10000.0) ? (value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0") + "kN";
} }
   
public static string ToForce(double value1, double value2) public static string ToForce(double value1, double value2)
{ {
var format1 = (value1 < 100000.0) ? (value1 < 10000.0) ? (value1 < 100.0) ? (Math.Abs(value1) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0"; string format1 = (value1 < 100000.0) ? (value1 < 10000.0) ? (value1 < 100.0) ? (Math.Abs(value1) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0";
var format2 = (value2 < 100000.0) ? (value2 < 10000.0) ? (value2 < 100.0) ? (Math.Abs(value2) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0"; string format2 = (value2 < 100000.0) ? (value2 < 10000.0) ? (value2 < 100.0) ? (Math.Abs(value2) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0";
return value1.ToString(format1) + " / " + value2.ToString(format2) + "kN"; return value1.ToString(format1) + " / " + value2.ToString(format2) + "kN";
} }
   
public static string ToMass(double value, int decimals = 0) public static string ToMass(double value, int decimals = 0)
{ {
if (value >= 1000.0) if (value >= 1000.0)
{ {
return value.ToString("N" + decimals + 2) + "t"; return value.ToString("N" + decimals + 2) + "t";
} }
   
value *= 1000.0; value *= 1000.0;
return value.ToString("N" + decimals) + "kg"; return value.ToString("N" + decimals) + "kg";
} }
   
public static string ToMass(double value1, double value2, int decimals = 0) public static string ToMass(double value1, double value2, int decimals = 0)
{ {
if (value1 >= 1000.0f || value2 >= 1000.0f) if (value1 >= 1000.0f || value2 >= 1000.0f)
{ {
return value1.ToString("N" + decimals + 2) + " / " + value2.ToString("N" + decimals + 2) + "t"; return value1.ToString("N" + decimals + 2) + " / " + value2.ToString("N" + decimals + 2) + "t";
} }
   
value1 *= 1000.0; value1 *= 1000.0;
value2 *= 1000.0; value2 *= 1000.0;
return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + "kg"; return value1.ToString("N" + decimals) + " / " + value2.ToString("N" + decimals) + "kg";
} }
   
public static string ToPercent(double value, int decimals = 2) public static string ToPercent(double value, int decimals = 2)
{ {
value *= 100.0; value *= 100.0;
return value.ToString("F" + decimals) + "%"; return value.ToString("F" + decimals) + "%";
} }
   
public static string ToRate(double value, int decimals = 1) public static string ToRate(double value, int decimals = 1)
{ {
return value < 1.0 ? (value * 60.0).ToString("F" + decimals) + "/min" : value.ToString("F" + decimals) + "/sec"; return value < 1.0 ? (value * 60.0).ToString("F" + decimals) + "/min" : value.ToString("F" + decimals) + "/sec";
} }
   
public static string ToSpeed(double value, int decimals = 2) public static string ToSpeed(double value, int decimals = 2)
{ {
if (Math.Abs(value) < 1.0) if (Math.Abs(value) < 1.0)
{ {
return (value * 1000.0).ToString("N" + decimals) + "mm/s"; return (value * 1000.0).ToString("N" + decimals) + "mm/s";
} }
return value.ToString("N" + decimals) + "m/s"; return value.ToString("N" + decimals) + "m/s";
} }
   
public static string ToTime(double value) public static string ToTime(double value)
{ {
return TimeFormatter.ConvertToString(value); return TimeFormatter.ConvertToString(value);
} }
   
public static string ToTorque(double value) public static string ToTorque(double value)
{ {
return value.ToString((value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N1" : "N0") + "kNm"; return value.ToString((value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N1" : "N0") + "kNm";
} }
   
#endregion #endregion
} }
} }
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{39806613-E0B7-46E0-89A6-A569EC538CBB}</ProjectGuid> <ProjectGuid>{39806613-E0B7-46E0-89A6-A569EC538CBB}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>KerbalEngineer</RootNamespace> <RootNamespace>KerbalEngineer</RootNamespace>
<AssemblyName>KerbalEngineer</AssemblyName> <AssemblyName>KerbalEngineer</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<DebugType>none</DebugType> <DebugType>none</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>..\Output\KerbalEngineer\</OutputPath> <OutputPath>..\Output\KerbalEngineer\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>..\Output\KerbalEngineer\</OutputPath> <OutputPath>..\Output\KerbalEngineer\</OutputPath>
<DefineConstants> <DefineConstants>
</DefineConstants> </DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<UseVSHostingProcess>false</UseVSHostingProcess> <UseVSHostingProcess>false</UseVSHostingProcess>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks> <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Control\IControlPanel.cs" /> <Compile Include="Control\IControlPanel.cs" />
<Compile Include="Control\Panels\BuildOverlayPanel.cs" /> <Compile Include="Control\Panels\BuildOverlayPanel.cs" />
<Compile Include="Control\Panels\BuildEngineerPanel.cs" /> <Compile Include="Control\Panels\BuildEngineerPanel.cs" />
<Compile Include="Editor\BuildAdvanced.cs" /> <Compile Include="Editor\BuildAdvanced.cs" />
<Compile Include="Editor\BuildOverlay.cs" /> <Compile Include="Editor\BuildOverlay.cs" />
<Compile Include="CelestialBodies.cs" /> <Compile Include="CelestialBodies.cs" />
<Compile Include="Editor\BuildOverlayPartInfo.cs" /> <Compile Include="Editor\BuildOverlayPartInfo.cs" />
<Compile Include="Editor\BuildOverlayResources.cs" /> <Compile Include="Editor\BuildOverlayResources.cs" />
<Compile Include="Editor\BuildOverlayVessel.cs" /> <Compile Include="Editor\BuildOverlayVessel.cs" />
<Compile Include="Editor\BuildToolbar.cs" /> <Compile Include="Editor\BuildToolbar.cs" />
<Compile Include="Editor\PartInfoItem.cs" /> <Compile Include="Editor\PartInfoItem.cs" />
<Compile Include="Editor\ResourceInfoItem.cs" /> <Compile Include="Editor\ResourceInfoItem.cs" />
<Compile Include="Extensions\FloatExtensions.cs" /> <Compile Include="Extensions\FloatExtensions.cs" />
<Compile Include="Extensions\OrbitExtensions.cs" /> <Compile Include="Extensions\OrbitExtensions.cs" />
<Compile Include="Flight\ActionMenuGui.cs" /> <Compile Include="Flight\ActionMenuGui.cs" />
<Compile Include="Flight\Presets\Preset.cs" /> <Compile Include="Flight\Presets\Preset.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\SystemTime.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\SystemTime.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\VectoredThrustToggle.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\VectoredThrustToggle.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\TimeReference.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\TimeReference.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\Separator.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\Separator.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\GuiSizeAdjustor.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\GuiSizeAdjustor.cs" />
<Compile Include="Flight\Readouts\Orbital\AngleToEquatorialDescendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\AngleToEquatorialDescendingNode.cs" />
<Compile Include="Flight\Readouts\Orbital\AngleToEquatorialAscendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\AngleToEquatorialAscendingNode.cs" />
<Compile Include="Flight\Readouts\Orbital\AngleToRetrograde.cs" /> <Compile Include="Flight\Readouts\Orbital\AngleToRetrograde.cs" />
<Compile Include="Flight\Readouts\Orbital\AngleToPrograde.cs" /> <Compile Include="Flight\Readouts\Orbital\AngleToPrograde.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeRadialDeltaV.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeRadialDeltaV.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\ManoeuvreProcessor.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\ManoeuvreProcessor.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTimeToHalfBurn.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTimeToHalfBurn.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTimeToManoeuvre.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTimeToManoeuvre.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeHalfBurnTime.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeHalfBurnTime.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeBurnTime.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeBurnTime.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeAngleToRetrograde.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeAngleToRetrograde.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeNormalDeltaV.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeNormalDeltaV.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeAngleToPrograde.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeAngleToPrograde.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTotalDeltaV.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeTotalDeltaV.cs" />
<Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeProgradeDeltaV.cs" /> <Compile Include="Flight\Readouts\Orbital\ManoeuvreNode\NodeProgradeDeltaV.cs" />
<Compile Include="Flight\Readouts\Orbital\MeanAnomalyAtEpoc.cs" /> <Compile Include="Flight\Readouts\Orbital\MeanAnomalyAtEpoc.cs" />
<Compile Include="Flight\Readouts\Orbital\MeanAnomaly.cs" /> <Compile Include="Flight\Readouts\Orbital\MeanAnomaly.cs" />
<Compile Include="Flight\Readouts\Orbital\EccentricAnomaly.cs" /> <Compile Include="Flight\Readouts\Orbital\EccentricAnomaly.cs" />
<Compile Include="Flight\Readouts\Orbital\ArgumentOfPeriapsis.cs" /> <Compile Include="Flight\Readouts\Orbital\ArgumentOfPeriapsis.cs" />
<Compile Include="Flight\Readouts\Orbital\CurrentSoi.cs" /> <Compile Include="Flight\Readouts\Orbital\CurrentSoi.cs" />
<Compile Include="Flight\Readouts\Orbital\TrueAnomaly.cs" /> <Compile Include="Flight\Readouts\Orbital\TrueAnomaly.cs" />
<Compile Include="Flight\Readouts\Orbital\TimeToEquatorialAscendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\TimeToEquatorialAscendingNode.cs" />
<Compile Include="Flight\Readouts\Orbital\TimeToEquatorialDescendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\TimeToEquatorialDescendingNode.cs" />
<Compile Include="Flight\Readouts\Rendezvous\RelativeSpeed.cs" /> <Compile Include="Flight\Readouts\Rendezvous\RelativeSpeed.cs" />
<Compile Include="Flight\Readouts\Rendezvous\RelativeVelocity.cs" /> <Compile Include="Flight\Readouts\Rendezvous\RelativeVelocity.cs" />
<Compile Include="Flight\Readouts\Rendezvous\SemiMinorAxis.cs" /> <Compile Include="Flight\Readouts\Rendezvous\SemiMinorAxis.cs" />
<Compile Include="Flight\Readouts\Rendezvous\SemiMajorAxis.cs" /> <Compile Include="Flight\Readouts\Rendezvous\SemiMajorAxis.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TimeToRelativeDescendingNode.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TimeToRelativeDescendingNode.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TimeToRelativeAscendingNode.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TimeToRelativeAscendingNode.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactBiome.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactBiome.cs" />
<Compile Include="Flight\Readouts\Surface\Slope.cs" /> <Compile Include="Flight\Readouts\Surface\Slope.cs" />
<Compile Include="Flight\Readouts\Surface\Biome.cs" /> <Compile Include="Flight\Readouts\Surface\Biome.cs" />
<Compile Include="Flight\Readouts\Surface\HorizontalAcceleration.cs" /> <Compile Include="Flight\Readouts\Surface\HorizontalAcceleration.cs" />
<Compile Include="Flight\Readouts\Surface\VerticalAcceleration.cs" /> <Compile Include="Flight\Readouts\Surface\VerticalAcceleration.cs" />
<Compile Include="Flight\Readouts\Vessel\AttitudeProcessor.cs" /> <Compile Include="Flight\Readouts\Vessel\AttitudeProcessor.cs" />
<Compile Include="Flight\Readouts\Vessel\DeltaVCurrentTotal.cs" /> <Compile Include="Flight\Readouts\Vessel\DeltaVCurrentTotal.cs" />
<Compile Include="Flight\Readouts\Vessel\PitchRate.cs" /> <Compile Include="Flight\Readouts\Vessel\PitchRate.cs" />
<Compile Include="Flight\Readouts\Vessel\HeadingRate.cs" /> <Compile Include="Flight\Readouts\Vessel\HeadingRate.cs" />
<Compile Include="Flight\Readouts\Vessel\RollRate.cs" /> <Compile Include="Flight\Readouts\Vessel\RollRate.cs" />
<Compile Include="Flight\Readouts\Vessel\Roll.cs" /> <Compile Include="Flight\Readouts\Vessel\Roll.cs" />
<Compile Include="Flight\Readouts\Vessel\Pitch.cs" /> <Compile Include="Flight\Readouts\Vessel\Pitch.cs" />
<Compile Include="Flight\Readouts\Vessel\Heading.cs" /> <Compile Include="Flight\Readouts\Vessel\Heading.cs" />
<Compile Include="Flight\Readouts\Vessel\PartCount.cs" /> <Compile Include="Flight\Readouts\Vessel\PartCount.cs" />
<Compile Include="Flight\Readouts\Vessel\SuicideBurnDeltaV.cs" /> <Compile Include="Flight\Readouts\Vessel\SuicideBurnDeltaV.cs" />
<Compile Include="Flight\Readouts\Vessel\SuicideBurnAltitude.cs" /> <Compile Include="Flight\Readouts\Vessel\SuicideBurnAltitude.cs" />
<Compile Include="Flight\Readouts\Vessel\SuicideBurnDistance.cs" /> <Compile Include="Flight\Readouts\Vessel\SuicideBurnDistance.cs" />
<Compile Include="Flight\Readouts\Vessel\DeltaVCurrent.cs" /> <Compile Include="Flight\Readouts\Vessel\DeltaVCurrent.cs" />
<Compile Include="Flight\Readouts\Vessel\IntakeAirUsage.cs" /> <Compile Include="Flight\Readouts\Vessel\IntakeAirUsage.cs" />
<Compile Include="Flight\Readouts\Vessel\IntakeAirDemandSupply.cs" /> <Compile Include="Flight\Readouts\Vessel\IntakeAirDemandSupply.cs" />
<Compile Include="Flight\Readouts\Vessel\IntakeAirSupply.cs" /> <Compile Include="Flight\Readouts\Vessel\IntakeAirSupply.cs" />
<Compile Include="Flight\Readouts\Vessel\IntakeAirDemand.cs" /> <Compile Include="Flight\Readouts\Vessel\IntakeAirDemand.cs" />
<Compile Include="Flight\Readouts\Miscellaneous\SimulationDelay.cs" /> <Compile Include="Flight\Readouts\Miscellaneous\SimulationDelay.cs" />
<Compile Include="Flight\Readouts\Vessel\SimulationProcessor.cs" /> <Compile Include="Flight\Readouts\Vessel\SimulationProcessor.cs" />
<Compile Include="Flight\Readouts\Vessel\Acceleration.cs" /> <Compile Include="Flight\Readouts\Vessel\Acceleration.cs" />
<Compile Include="Flight\Presets\PresetLibrary.cs" /> <Compile Include="Flight\Presets\PresetLibrary.cs" />
<Compile Include="Flight\Readouts\Vessel\SuicideBurnProcessor.cs" /> <Compile Include="Flight\Readouts\Vessel\SuicideBurnProcessor.cs" />
<Compile Include="Flight\Readouts\Vessel\SurfaceThrustToWeight.cs" /> <Compile Include="Flight\Readouts\Vessel\SurfaceThrustToWeight.cs" />
<Compile Include="Flight\Readouts\Surface\Situation.cs" /> <Compile Include="Flight\Readouts\Surface\Situation.cs" />
<Compile Include="Flight\Readouts\Vessel\ThrustOffsetAngle.cs" /> <Compile Include="Flight\Readouts\Vessel\ThrustOffsetAngle.cs" />
<Compile Include="Flight\Readouts\Vessel\ThrustTorque.cs" /> <Compile Include="Flight\Readouts\Vessel\ThrustTorque.cs" />
<Compile Include="GuiDisplaySize.cs" /> <Compile Include="GuiDisplaySize.cs" />
<Compile Include="Helpers\AngleHelper.cs" /> <Compile Include="Helpers\AngleHelper.cs" />
<Compile Include="Helpers\Averager.cs" /> <Compile Include="Helpers\Averager.cs" />
<Compile Include="Helpers\ForceAccumulator.cs" /> <Compile Include="Helpers\ForceAccumulator.cs" />
<Compile Include="Helpers\TextureHelper.cs" /> <Compile Include="Helpers\TextureHelper.cs" />
<Compile Include="Helpers\Units.cs" /> <Compile Include="Helpers\Units.cs" />
<Compile Include="Helpers\TimeFormatter.cs" /> <Compile Include="Helpers\TimeFormatter.cs" />
<Compile Include="KeyBinder.cs" /> <Compile Include="KeyBinder.cs" />
<Compile Include="Control\ControlCentre.cs" /> <Compile Include="Control\ControlCentre.cs" />
<Compile Include="UIControls\DropDown.cs" /> <Compile Include="UIControls\DropDown.cs" />
<Compile Include="Logger.cs" /> <Compile Include="Logger.cs" />
<Compile Include="EngineerGlobals.cs" /> <Compile Include="EngineerGlobals.cs" />
<Compile Include="Extensions\DoubleExtensions.cs" /> <Compile Include="Extensions\DoubleExtensions.cs" />
<Compile Include="Extensions\PartExtensions.cs" /> <Compile Include="Extensions\PartExtensions.cs" />
<Compile Include="Extensions\PartResourceExtensions.cs" /> <Compile Include="Extensions\PartResourceExtensions.cs" />
<Compile Include="Extensions\RectExtensions.cs" /> <Compile Include="Extensions\RectExtensions.cs" />
<Compile Include="Flight\ActionMenu.cs" /> <Compile Include="Flight\ActionMenu.cs" />
<Compile Include="Flight\DisplayStack.cs" /> <Compile Include="Flight\DisplayStack.cs" />
<Compile Include="Flight\FlightEngineerCore.cs" /> <Compile Include="Flight\FlightEngineerCore.cs" />
<Compile Include="Flight\FlightEngineerModule.cs" /> <Compile Include="Flight\FlightEngineerModule.cs" />
<Compile Include="Flight\IUpdatable.cs" /> <Compile Include="Flight\IUpdatable.cs" />
<Compile Include="Flight\IUpdateRequest.cs" /> <Compile Include="Flight\IUpdateRequest.cs" />
<Compile Include="Flight\Readouts\Orbital\ApoapsisHeight.cs" /> <Compile Include="Flight\Readouts\Orbital\ApoapsisHeight.cs" />
<Compile Include="Flight\Readouts\Orbital\Eccentricity.cs" /> <Compile Include="Flight\Readouts\Orbital\Eccentricity.cs" />
<Compile Include="Flight\Readouts\Orbital\Inclination.cs" /> <Compile Include="Flight\Readouts\Orbital\Inclination.cs" />
<Compile Include="Flight\Readouts\Orbital\LongitudeOfAscendingNode.cs" /> <Compile Include="Flight\Readouts\Orbital\LongitudeOfAscendingNode.cs" />
<Compile Include="Flight\Readouts\Orbital\LongitudeOfPeriapsis.cs" /> <Compile Include="Flight\Readouts\Orbital\LongitudeOfPeriapsis.cs" />
<Compile Include="Flight\Readouts\Orbital\OrbitalPeriod.cs" /> <Compile Include="Flight\Readouts\Orbital\OrbitalPeriod.cs" />
<Compile Include="Flight\Readouts\Orbital\OrbitalSpeed.cs" /> <Compile Include="Flight\Readouts\Orbital\OrbitalSpeed.cs" />
<Compile Include="Flight\Readouts\Orbital\PeriapsisHeight.cs" /> <Compile Include="Flight\Readouts\Orbital\PeriapsisHeight.cs" />
<Compile Include="Flight\Readouts\Orbital\SemiMajorAxis.cs" /> <Compile Include="Flight\Readouts\Orbital\SemiMajorAxis.cs" />
<Compile Include="Flight\Readouts\Orbital\SemiMinorAxis.cs" /> <Compile Include="Flight\Readouts\Orbital\SemiMinorAxis.cs" />
<Compile Include="Flight\Readouts\Orbital\TimeToApoapsis.cs" /> <Compile Include="Flight\Readouts\Orbital\TimeToApoapsis.cs" />
<Compile Include="Flight\Readouts\Orbital\TimeToPeriapsis.cs" /> <Compile Include="Flight\Readouts\Orbital\TimeToPeriapsis.cs" />
<Compile Include="Flight\Readouts\ReadoutCategory.cs" /> <Compile Include="Flight\Readouts\ReadoutCategory.cs" />
<Compile Include="Flight\Readouts\ReadoutLibrary.cs" /> <Compile Include="Flight\Readouts\ReadoutLibrary.cs" />
<Compile Include="Flight\Readouts\ReadoutModule.cs" /> <Compile Include="Flight\Readouts\ReadoutModule.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TimeToPeriapsis.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TimeToPeriapsis.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TimeToApoapsis.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TimeToApoapsis.cs" />
<Compile Include="Flight\Readouts\Rendezvous\PeriapsisHeight.cs" /> <Compile Include="Flight\Readouts\Rendezvous\PeriapsisHeight.cs" />
<Compile Include="Flight\Readouts\Rendezvous\ApoapsisHeight.cs" /> <Compile Include="Flight\Readouts\Rendezvous\ApoapsisHeight.cs" />
<Compile Include="Flight\Readouts\Rendezvous\InterceptAngle.cs" /> <Compile Include="Flight\Readouts\Rendezvous\InterceptAngle.cs" />
<Compile Include="Flight\Readouts\Rendezvous\OrbitalPeriod.cs" /> <Compile Include="Flight\Readouts\Rendezvous\OrbitalPeriod.cs" />
<Compile Include="Flight\Readouts\Rendezvous\Distance.cs" /> <Compile Include="Flight\Readouts\Rendezvous\Distance.cs" />
<Compile Include="Flight\Readouts\Rendezvous\AltitudeSeaLevel.cs" /> <Compile Include="Flight\Readouts\Rendezvous\AltitudeSeaLevel.cs" />
<Compile Include="Flight\Readouts\Rendezvous\AngleToRelativeDescendingNode.cs" /> <Compile Include="Flight\Readouts\Rendezvous\AngleToRelativeDescendingNode.cs" />
<Compile Include="Flight\Readouts\Rendezvous\AngleToRelativeAscendingNode.cs" /> <Compile Include="Flight\Readouts\Rendezvous\AngleToRelativeAscendingNode.cs" />
<Compile Include="Flight\Readouts\Rendezvous\PhaseAngle.cs" /> <Compile Include="Flight\Readouts\Rendezvous\PhaseAngle.cs" />
<Compile Include="Flight\Readouts\Rendezvous\RelativeInclination.cs" /> <Compile Include="Flight\Readouts\Rendezvous\RelativeInclination.cs" />
<Compile Include="Flight\Readouts\Rendezvous\RendezvousProcessor.cs" /> <Compile Include="Flight\Readouts\Rendezvous\RendezvousProcessor.cs" />
<Compile Include="Flight\Readouts\Rendezvous\TargetSelector.cs" /> <Compile Include="Flight\Readouts\Rendezvous\TargetSelector.cs" />
<Compile Include="Flight\Readouts\Surface\AltitudeSeaLevel.cs" /> <Compile Include="Flight\Readouts\Surface\AltitudeSeaLevel.cs" />
<Compile Include="Flight\Readouts\Surface\AltitudeTerrain.cs" /> <Compile Include="Flight\Readouts\Surface\AltitudeTerrain.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactLatitude.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactLatitude.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactAltitude.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactAltitude.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactLongitude.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactLongitude.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactTime.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactTime.cs" />
<Compile Include="Flight\Readouts\Surface\AtmosphericProcessor.cs" /> <Compile Include="Flight\Readouts\Surface\AtmosphericProcessor.cs" />
<Compile Include="Flight\Readouts\Surface\AtmosphericEfficiency.cs" /> <Compile Include="Flight\Readouts\Surface\AtmosphericEfficiency.cs" />
<Compile Include="Flight\Readouts\Surface\GeeForce.cs" /> <Compile Include="Flight\Readouts\Surface\GeeForce.cs" />
<Compile Include="Flight\Readouts\Surface\HorizontalSpeed.cs" /> <Compile Include="Flight\Readouts\Surface\HorizontalSpeed.cs" />
<Compile Include="Flight\Readouts\Surface\ImpactProcessor.cs" /> <Compile Include="Flight\Readouts\Surface\ImpactProcessor.cs" />
<Compile Include="Flight\Readouts\Surface\Latitude.cs" /> <Compile Include="Flight\Readouts\Surface\Latitude.cs" />
<Compile Include="Flight\Readouts\Surface\Longitude.cs" /> <Compile Include="Flight\Readouts\Surface\Longitude.cs" />
<Compile Include="Flight\Readouts\Surface\TerminalVelocity.cs" /> <Compile Include="Flight\Readouts\Surface\TerminalVelocity.cs" />
<Compile Include="Flight\Readouts\Surface\VerticalSpeed.cs" /> <Compile Include="Flight\Readouts\Surface\VerticalSpeed.cs" />
<Compile Include="Flight\Readouts\Vessel\DeltaVStaged.cs" /> <Compile Include="Flight\Readouts\Vessel\DeltaVStaged.cs" />
<Compile Include="Flight\Readouts\Vessel\DeltaVTotal.cs" /> <Compile Include="Flight\Readouts\Vessel\DeltaVTotal.cs" />
<Compile Include="Flight\Readouts\Vessel\Mass.cs" /> <Compile Include="Flight\Readouts\Vessel\Mass.cs" />
<Compile Include="Flight\Readouts\Vessel\Thrust.cs" /> <Compile Include="Flight\Readouts\Vessel\Thrust.cs" />
<Compile Include="Flight\Readouts\Vessel\SpecificImpulse.cs" /> <Compile Include="Flight\Readouts\Vessel\SpecificImpulse.cs" />
<Compile Include="Flight\Readouts\Vessel\ThrustToWeight.cs" /> <Compile Include="Flight\Readouts\Vessel\ThrustToWeight.cs" />
<Compile Include="Flight\Sections\SectionEditor.cs" /> <Compile Include="Flight\Sections\SectionEditor.cs" />
<Compile Include="Flight\Sections\SectionLibrary.cs" /> <Compile Include="Flight\Sections\SectionLibrary.cs" />
<Compile Include="Flight\Sections\SectionModule.cs" /> <Compile Include="Flight\Sections\SectionModule.cs" />
<Compile Include="Flight\Sections\SectionWindow.cs" /> <Compile Include="Flight\Sections\SectionWindow.cs" />
<Compile Include="LogMsg.cs" /> <Compile Include="LogMsg.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\SettingHandler.cs" /> <Compile Include="Settings\SettingHandler.cs" />
<Compile Include="Settings\SettingItem.cs" /> <Compile Include="Settings\SettingItem.cs" />
<Compile Include="TapeDriveAnimator.cs" /> <Compile Include="TapeDriveAnimator.cs" />
<Compile Include="UIControls\WindowObject.cs" /> <Compile Include="UIControls\WindowObject.cs" />
<Compile Include="VesselSimulator\AttachNodeSim.cs" /> <Compile Include="VesselSimulator\AttachNodeSim.cs" />
<Compile Include="VesselSimulator\EngineSim.cs" /> <Compile Include="VesselSimulator\EngineSim.cs" />
  <Compile Include="Helpers\Pool.cs" />
<Compile Include="VesselSimulator\PartSim.cs" /> <Compile Include="VesselSimulator\PartSim.cs" />
<Compile Include="VesselSimulator\ResourceContainer.cs" /> <Compile Include="VesselSimulator\ResourceContainer.cs" />
<Compile Include="VesselSimulator\SimManager.cs" /> <Compile Include="VesselSimulator\SimManager.cs" />
<Compile Include="VesselSimulator\Simulation.cs" /> <Compile Include="VesselSimulator\Simulation.cs" />
<Compile Include="VesselSimulator\Stage.cs" /> <Compile Include="VesselSimulator\Stage.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>..\Game\KSP_Data\Managed\Assembly-CSharp.dll</HintPath> <HintPath>..\..\..\..\..\..\Program Files (x86)\Steam\SteamApps\common\Kerbal Space Program\KSP_Data\Managed\Assembly-CSharp.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="System"> <Reference Include="System">
<HintPath>..\Game\KSP_Data\Managed\System.dll</HintPath> <HintPath>..\Game\KSP_Data\Managed\System.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="System.Xml"> <Reference Include="System.Xml">
<HintPath>..\Game\KSP_Data\Managed\System.Xml.dll</HintPath> <HintPath>..\Game\KSP_Data\Managed\System.Xml.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>..\Game\KSP_Data\Managed\UnityEngine.dll</HintPath> <HintPath>..\..\..\..\..\..\Program Files (x86)\Steam\SteamApps\common\Kerbal Space Program\KSP_Data\Managed\UnityEngine.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
  <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="PostBuildMacros"> <Target Name="PostBuildMacros">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)"> <GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="Targets" /> <Output TaskParameter="Assemblies" ItemName="Targets" />
</GetAssemblyIdentity> </GetAssemblyIdentity>
<ItemGroup> <ItemGroup>
<VersionNumber Include="@(Targets->'%(Version)')" /> <VersionNumber Include="@(Targets->'%(Version)')" />
</ItemGroup> </ItemGroup>
</Target> </Target>
<PropertyGroup> <PropertyGroup>
<PostBuildEventDependsOn> <PostBuildEventDependsOn>
$(PostBuildEventDependsOn); $(PostBuildEventDependsOn);
PostBuildMacros; PostBuildMacros;
</PostBuildEventDependsOn> </PostBuildEventDependsOn>
<PostBuildEvent>xcopy "$(SolutionDir)Output\*" "$(SolutionDir)Game\GameData\*" /E /Y <PostBuildEvent>xcopy "$(SolutionDir)Output\*" "$(SolutionDir)Game\GameData\*" /E /Y
del "$(SolutionDir)Release\*" /Q del "$(SolutionDir)Release\*" /Q
xcopy "$(SolutionDir)Documents\*" "$(SolutionDir)Release\Documents\*" /E /Y xcopy "$(SolutionDir)Documents\*" "$(SolutionDir)Release\Documents\*" /E /Y
7z.exe a -tzip -mx3 "$(SolutionDir)Release\$(ProjectName)-@(VersionNumber).zip" "$(SolutionDir)Output\*" 7z.exe a -tzip -mx3 "$(SolutionDir)Release\$(ProjectName)-@(VersionNumber).zip" "$(SolutionDir)Output\*"
7z.exe a -tzip -mx3 "$(SolutionDir)Release\$(ProjectName)-@(VersionNumber).zip" "$(SolutionDir)Documents\*"</PostBuildEvent> 7z.exe a -tzip -mx3 "$(SolutionDir)Release\$(ProjectName)-@(VersionNumber).zip" "$(SolutionDir)Documents\*"</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
</Target> </Target>
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives #region Using Directives
   
using System;  
using System.Text;  
   
#endregion #endregion
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
  using System;
  using System.Text;
   
internal class AttachNodeSim internal class AttachNodeSim
{ {
   
  private static readonly Pool<AttachNodeSim> pool = new Pool<AttachNodeSim>(Create, Reset);
   
public PartSim attachedPartSim; public PartSim attachedPartSim;
public String id; public String id;
public AttachNode.NodeType nodeType; public AttachNode.NodeType nodeType;
   
public AttachNodeSim(PartSim partSim, String newId, AttachNode.NodeType newNodeType) private static AttachNodeSim Create()
{ {
this.attachedPartSim = partSim; return new AttachNodeSim();
this.nodeType = newNodeType; }
this.id = newId;  
  public static AttachNodeSim New(PartSim partSim, String newId, AttachNode.NodeType newNodeType)
  {
  AttachNodeSim nodeSim = pool.Borrow();
   
  nodeSim.attachedPartSim = partSim;
  nodeSim.nodeType = newNodeType;
  nodeSim.id = newId;
   
  return nodeSim;
  }
   
  static private void Reset(AttachNodeSim attachNodeSim) { }
   
   
  public void Release()
  {
  pool.Release(this);
} }
   
public void DumpToBuffer(StringBuilder buffer) public void DumpToBuffer(StringBuilder buffer)
{ {
if (this.attachedPartSim == null) if (attachedPartSim == null)
{ {
buffer.Append("<staged>:<n>"); buffer.Append("<staged>:<n>");
} }
else else
{ {
buffer.Append(this.attachedPartSim.name); buffer.Append(attachedPartSim.name);
buffer.Append(":"); buffer.Append(":");
buffer.Append(this.attachedPartSim.partId); buffer.Append(attachedPartSim.partId);
} }
buffer.Append("#"); buffer.Append("#");
buffer.Append(this.nodeType); buffer.Append(nodeType);
buffer.Append(":"); buffer.Append(":");
buffer.Append(this.id); buffer.Append(id);
} }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives  
   
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
   
using UnityEngine;  
   
#endregion  
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
  using System;
  using System.Collections.Generic;
  using System.Text;
  using Editor;
  using Helpers;
  using UnityEngine;
   
public class EngineSim public class EngineSim
{ {
  private static readonly Pool<EngineSim> pool = new Pool<EngineSim>(Create, Reset);
   
private readonly ResourceContainer resourceConsumptions = new ResourceContainer(); private readonly ResourceContainer resourceConsumptions = new ResourceContainer();
   
public double actualThrust = 0; public double actualThrust = 0;
public bool isActive = false; public bool isActive = false;
public double isp = 0; public double isp = 0;
public PartSim partSim; public PartSim partSim;
public List<AppliedForce> appliedForces; public List<AppliedForce> appliedForces = new List<AppliedForce>();
  public float maxMach;
   
public double thrust = 0; public double thrust = 0;
   
// Add thrust vector to account for directional losses // Add thrust vector to account for directional losses
public Vector3 thrustVec; public Vector3 thrustVec;
   
public EngineSim(PartSim theEngine, private static EngineSim Create()
  {
  return new EngineSim();
  }
   
  private static void Reset(EngineSim engineSim)
  {
  engineSim.resourceConsumptions.Reset();
  engineSim.actualThrust = 0;
  engineSim.isActive = false;
  engineSim.isp = 0;
  for (int i = 0; i < engineSim.appliedForces.Count; i++)
  {
  engineSim.appliedForces[i].Release();
  }
  engineSim.appliedForces.Clear();
  engineSim.thrust = 0;
  engineSim.maxMach = 0f;
  }
   
  public void Release()
  {
  pool.Release(this);
  }
   
  public static EngineSim New(PartSim theEngine,
double atmosphere, double atmosphere,
double velocity, float machNumber,
float maxThrust, float maxFuelFlow,
float minThrust, float minFuelFlow,
float thrustPercentage, float thrustPercentage,
float requestedThrust,  
Vector3 vecThrust, Vector3 vecThrust,
float realIsp,  
FloatCurve atmosphereCurve, FloatCurve atmosphereCurve,
FloatCurve velocityCurve, bool atmChangeFlow,
  FloatCurve atmCurve,
  FloatCurve velCurve,
  float currentThrottle,
  float IspG,
bool throttleLocked, bool throttleLocked,
List<Propellant> propellants, List<Propellant> propellants,
bool active, bool active,
bool correctThrust, float resultingThrust,
List<Transform> thrustTransforms) List<Transform> thrustTransforms)
{ {
  EngineSim engineSim = pool.Borrow();
   
StringBuilder buffer = null; StringBuilder buffer = null;
//MonoBehaviour.print("Create EngineSim for " + theEngine.name);  
//MonoBehaviour.print("maxThrust = " + maxThrust); engineSim.isp = 0.0;
//MonoBehaviour.print("minThrust = " + minThrust); engineSim.maxMach = 0.0f;
//MonoBehaviour.print("thrustPercentage = " + thrustPercentage); engineSim.actualThrust = 0.0;
//MonoBehaviour.print("requestedThrust = " + requestedThrust); engineSim.partSim = theEngine;
//MonoBehaviour.print("velocity = " + velocity); engineSim.isActive = active;
  engineSim.thrustVec = vecThrust;
this.partSim = theEngine; engineSim.resourceConsumptions.Reset();
  engineSim.appliedForces.Clear();
this.isActive = active;  
this.thrust = (maxThrust - minThrust) * (thrustPercentage / 100f) + minThrust; double flowRate = 0.0;
//MonoBehaviour.print("thrust = " + thrust); if (engineSim.partSim.hasVessel)
  {
this.thrustVec = vecThrust; float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, engineSim.partSim.part.atmDensity, velCurve, machNumber, ref engineSim.maxMach);
  engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere);
double flowRate = 0d; engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp);
if (this.partSim.hasVessel) engineSim.actualThrust = engineSim.isActive ? resultingThrust : 0.0;
{  
//MonoBehaviour.print("hasVessel is true"); if (throttleLocked)
this.actualThrust = isActive ? requestedThrust : 0.0; {
if (velocityCurve != null) flowRate = GetFlowRate(engineSim.thrust, engineSim.isp);
{ }
this.actualThrust *= velocityCurve.Evaluate((float)velocity); else
//MonoBehaviour.print("actualThrust at velocity = " + actualThrust); {
} if (currentThrottle > 0.0f && engineSim.partSim.isLanded == false)
   
this.isp = atmosphereCurve.Evaluate((float)this.partSim.part.staticPressureAtm);  
if (this.isp == 0d)  
{  
MonoBehaviour.print("Isp at " + this.partSim.part.staticPressureAtm + " is zero. Flow rate will be NaN");  
}  
   
if (correctThrust && realIsp == 0)  
{  
float ispsl = atmosphereCurve.Evaluate(0);  
if (ispsl != 0)  
{ {
this.thrust = this.thrust * this.isp / ispsl; flowRate = GetFlowRate(engineSim.actualThrust, engineSim.isp);
} }
else else
{ {
MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust."); flowRate = GetFlowRate(engineSim.thrust, engineSim.isp);
} }
//MonoBehaviour.print("corrected thrust = " + thrust);  
}  
   
if (velocityCurve != null)  
{  
this.thrust *= velocityCurve.Evaluate((float)velocity);  
//MonoBehaviour.print("thrust at velocity = " + thrust);  
}  
   
if (throttleLocked)  
{  
//MonoBehaviour.print("throttleLocked is true");  
flowRate = this.thrust / (this.isp * 9.82);  
}  
else  
{  
if (this.partSim.isLanded)  
{  
//MonoBehaviour.print("partSim.isLanded is true, mainThrottle = " + FlightInputHandler.state.mainThrottle);  
flowRate = Math.Max(0.000001d, this.thrust * FlightInputHandler.state.mainThrottle) / (this.isp * 9.82);  
}  
else  
{  
if (requestedThrust > 0)  
{  
if (velocityCurve != null)  
{  
requestedThrust *= velocityCurve.Evaluate((float)velocity);  
//MonoBehaviour.print("requestedThrust at velocity = " + requestedThrust);  
}  
   
//MonoBehaviour.print("requestedThrust > 0");  
flowRate = requestedThrust / (this.isp * 9.82);  
}  
else  
{  
//MonoBehaviour.print("requestedThrust <= 0");  
flowRate = this.thrust / (this.isp * 9.82);  
}  
}  
} }
} }
else else
{ {
//MonoBehaviour.print("hasVessel is false"); float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, CelestialBodies.SelectedBody.GetDensity(BuildAdvanced.Altitude), velCurve, machNumber, ref engineSim.maxMach);
this.isp = atmosphereCurve.Evaluate((float)atmosphere); engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere);
if (this.isp == 0d) engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp);
{ flowRate = GetFlowRate(engineSim.thrust, engineSim.isp);
MonoBehaviour.print("Isp at " + atmosphere + " is zero. Flow rate will be NaN");  
}  
if (correctThrust)  
{  
float ispsl = atmosphereCurve.Evaluate(0);  
if (ispsl != 0)  
{  
this.thrust = this.thrust * this.isp / ispsl;  
}  
else  
{  
MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust.");  
}  
//MonoBehaviour.print("corrected thrust = " + thrust);  
}  
   
if (velocityCurve != null)  
{  
this.thrust *= velocityCurve.Evaluate((float)velocity);  
//MonoBehaviour.print("thrust at velocity = " + thrust);  
}  
   
flowRate = this.thrust / (this.isp * 9.82);  
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer = new StringBuilder(1024); buffer = new StringBuilder(1024);
buffer.AppendFormat("flowRate = {0:g6}\n", flowRate); buffer.AppendFormat("flowRate = {0:g6}\n", flowRate);
} }
   
  engineSim.thrust = flowRate * (engineSim.isp * IspG);
  // I did not look into the diff between those 2 so I made them equal...
  engineSim.actualThrust = engineSim.thrust;
   
float flowMass = 0f; float flowMass = 0f;
foreach (Propellant propellant in propellants) for (int i = 0; i < propellants.Count; ++i)
{ {
  Propellant propellant = propellants[i];
flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id); flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer.AppendFormat("flowMass = {0:g6}\n", flowMass); buffer.AppendFormat("flowMass = {0:g6}\n", flowMass);
} }
   
foreach (Propellant propellant in propellants) for (int i = 0; i < propellants.Count; ++i)
{ {
  Propellant propellant = propellants[i];
   
if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir") if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")
{ {
continue; continue;
} }
   
double consumptionRate = propellant.ratio * flowRate / flowMass; double consumptionRate = propellant.ratio * flowRate / flowMass;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer.AppendFormat("Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate); buffer.AppendFormat(
} "Add consumption({0}, {1}:{2:d}) = {3:g6}\n",
this.resourceConsumptions.Add(propellant.id, consumptionRate); ResourceContainer.GetResourceName(propellant.id),
  theEngine.name,
  theEngine.partId,
  consumptionRate);
  }
  engineSim.resourceConsumptions.Add(propellant.id, consumptionRate);
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
   
appliedForces = new List<AppliedForce>(); double thrustPerThrustTransform = engineSim.thrust / thrustTransforms.Count;
double thrustPerThrustTransform = thrust / thrustTransforms.Count; for (int i = 0; i < thrustTransforms.Count; i++)
foreach (Transform thrustTransform in thrustTransforms) { {
  Transform thrustTransform = thrustTransforms[i];
Vector3d direction = thrustTransform.forward.normalized; Vector3d direction = thrustTransform.forward.normalized;
Vector3d position = thrustTransform.position; Vector3d position = thrustTransform.position;
appliedForces.Add(new AppliedForce(direction * thrustPerThrustTransform, position));  
} AppliedForce appliedForce = AppliedForce.New(direction * thrustPerThrustTransform, position);
  engineSim.appliedForces.Add(appliedForce);
  }
   
  return engineSim;
} }
   
public ResourceContainer ResourceConsumptions public ResourceContainer ResourceConsumptions
{ {
get { return this.resourceConsumptions; } get
} {
  return resourceConsumptions;
  }
  }
   
  public static double GetExhaustVelocity(double isp)
  {
  return isp * Units.GRAVITY;
  }
   
  public static float GetFlowModifier(bool atmChangeFlow, FloatCurve atmCurve, double atmDensity, FloatCurve velCurve, float machNumber, ref float maxMach)
  {
  float flowModifier = 1.0f;
  if (atmChangeFlow)
  {
  flowModifier = (float)(atmDensity / 1.225);
  if (atmCurve != null)
  {
  flowModifier = atmCurve.Evaluate(flowModifier);
  }
  }
  if (velCurve != null)
  {
  flowModifier = flowModifier * velCurve.Evaluate(machNumber);
  maxMach = velCurve.maxTime;
  }
  if (flowModifier < float.Epsilon)
  {
  flowModifier = float.Epsilon;
  }
  return flowModifier;
  }
   
  public static double GetFlowRate(double thrust, double isp)
  {
  return thrust / GetExhaustVelocity(isp);
  }
   
  public static float GetThrottlePercent(float currentThrottle, float thrustPercentage)
  {
  return currentThrottle * GetThrustPercent(thrustPercentage);
  }
   
  public static double GetThrust(double flowRate, double isp)
  {
  return flowRate * GetExhaustVelocity(isp);
  }
   
  public static float GetThrustPercent(float thrustPercentage)
  {
  return thrustPercentage * 0.01f;
  }
   
  public void DumpEngineToBuffer(StringBuilder buffer, String prefix)
  {
  buffer.Append(prefix);
  buffer.AppendFormat("[thrust = {0:g6}, actual = {1:g6}, isp = {2:g6}\n", thrust, actualThrust, isp);
  }
   
  // A dictionary to hold a set of parts for each resource
  Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>();
   
  Dictionary<int, HashSet<PartSim>> stagePartSets = new Dictionary<int, HashSet<PartSim>>();
   
  HashSet<PartSim> visited = new HashSet<PartSim>();
   
public bool SetResourceDrains(List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts) public bool SetResourceDrains(List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts)
{ {
LogMsg log = null; LogMsg log = null;
   
// A dictionary to hold a set of parts for each resource foreach (HashSet<PartSim> sourcePartSet in sourcePartSets.Values)
Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>(); {
  sourcePartSet.Clear();
foreach (int type in this.resourceConsumptions.Types) }
{  
HashSet<PartSim> sourcePartSet = null; for (int index = 0; index < this.resourceConsumptions.Types.Count; index++)
  {
  int type = this.resourceConsumptions.Types[index];
   
  HashSet<PartSim> sourcePartSet;
  if (!sourcePartSets.TryGetValue(type, out sourcePartSet))
  {
  sourcePartSet = new HashSet<PartSim>();
  sourcePartSets.Add(type, sourcePartSet);
  }
   
switch (ResourceContainer.GetResourceFlowMode(type)) switch (ResourceContainer.GetResourceFlowMode(type))
{ {
case ResourceFlowMode.NO_FLOW: case ResourceFlowMode.NO_FLOW:
if (this.partSim.resources[type] > SimManager.RESOURCE_MIN && this.partSim.resourceFlowStates[type] != 0) if (partSim.resources[type] > SimManager.RESOURCE_MIN && partSim.resourceFlowStates[type] != 0)
{ {
sourcePartSet = new HashSet<PartSim>(); //sourcePartSet = new HashSet<PartSim>();
//MonoBehaviour.print("SetResourceDrains(" + name + ":" + partId + ") setting sources to just this"); //MonoBehaviour.print("SetResourceDrains(" + name + ":" + partId + ") setting sources to just this");
sourcePartSet.Add(this.partSim); sourcePartSet.Add(partSim);
} }
break; break;
   
case ResourceFlowMode.ALL_VESSEL: case ResourceFlowMode.ALL_VESSEL:
foreach (PartSim aPartSim in allParts) for (int i = 0; i < allParts.Count; i++)
{ {
  PartSim aPartSim = allParts[i];
if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0) if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0)
{ {
if (sourcePartSet == null)  
{  
sourcePartSet = new HashSet<PartSim>();  
}  
   
sourcePartSet.Add(aPartSim); sourcePartSet.Add(aPartSim);
} }
} }
break; break;
   
case ResourceFlowMode.STAGE_PRIORITY_FLOW: case ResourceFlowMode.STAGE_PRIORITY_FLOW:
var stagePartSets = new Dictionary<int, HashSet<PartSim>>();  
  foreach (HashSet<PartSim> stagePartSet in stagePartSets.Values)
  {
  stagePartSet.Clear();
  }
var maxStage = -1; var maxStage = -1;
   
//Logger.Log(type); //Logger.Log(type);
foreach (var aPartSim in allParts) for (int i = 0; i < allParts.Count; i++)
{ {
if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0) continue; var aPartSim = allParts[i];
  if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0)
var stage = aPartSim.DecouplerCount(); {
  continue;
  }
   
  int stage = aPartSim.DecouplerCount();
if (stage > maxStage) if (stage > maxStage)
{ {
maxStage = stage; maxStage = stage;
} }
   
if (!stagePartSets.TryGetValue(stage, out sourcePartSet)) if (!stagePartSets.TryGetValue(stage, out sourcePartSet))
{ {
sourcePartSet = new HashSet<PartSim>(); sourcePartSet = new HashSet<PartSim>();
stagePartSets.Add(stage, sourcePartSet); stagePartSets.Add(stage, sourcePartSet);
} }
sourcePartSet.Add(aPartSim); sourcePartSet.Add(aPartSim);
} }
   
for (var i = 0; i <= maxStage; i++) for (int j = 0; j <= maxStage; j++)
{ {
HashSet<PartSim> stagePartSet; HashSet<PartSim> stagePartSet;
if (stagePartSets.TryGetValue(i, out stagePartSet) && stagePartSet.Count > 0) if (stagePartSets.TryGetValue(j, out stagePartSet) && stagePartSet.Count > 0)
{ {
sourcePartSet = stagePartSet; sourcePartSet = stagePartSet;
} }
} }
break; break;
   
case ResourceFlowMode.STACK_PRIORITY_SEARCH: case ResourceFlowMode.STACK_PRIORITY_SEARCH:
HashSet<PartSim> visited = new HashSet<PartSim>(); visited.Clear();
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
log.buf.AppendLine("Find " + ResourceContainer.GetResourceName(type) + " sources for " + this.partSim.name + ":" + this.partSim.partId); log.buf.AppendLine("Find " + ResourceContainer.GetResourceName(type) + " sources for " + partSim.name + ":" + partSim.partId);
} }
sourcePartSet = this.partSim.GetSourceSet(type, allParts, visited, log, ""); partSim.GetSourceSet(type, allParts, visited, sourcePartSet, log, "");
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print(log.buf); MonoBehaviour.print(log.buf);
} }
break; break;
   
default: default:
MonoBehaviour.print("SetResourceDrains(" + this.partSim.name + ":" + this.partSim.partId + ") Unexpected flow type for " + ResourceContainer.GetResourceName(type) + ")"); MonoBehaviour.print("SetResourceDrains(" + partSim.name + ":" + partSim.partId + ") Unexpected flow type for " + ResourceContainer.GetResourceName(type) + ")");
break; break;
} }
   
if (sourcePartSet != null && sourcePartSet.Count > 0)  
  if (sourcePartSet.Count > 0)
{ {
sourcePartSets[type] = sourcePartSet; sourcePartSets[type] = sourcePartSet;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
log.buf.AppendLine("Source parts for " + ResourceContainer.GetResourceName(type) + ":"); log.buf.AppendLine("Source parts for " + ResourceContainer.GetResourceName(type) + ":");
foreach (PartSim partSim in sourcePartSet) foreach (PartSim partSim in sourcePartSet)
{ {
log.buf.AppendLine(partSim.name + ":" + partSim.partId); log.buf.AppendLine(partSim.name + ":" + partSim.partId);
} }
MonoBehaviour.print(log.buf); MonoBehaviour.print(log.buf);
} }
} }
} }
   
// If we don't have sources for all the needed resources then return false without setting up any drains // If we don't have sources for all the needed resources then return false without setting up any drains
foreach (int type in this.resourceConsumptions.Types) for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
{ {
if (!sourcePartSets.ContainsKey(type)) int type = this.resourceConsumptions.Types[i];
  HashSet<PartSim> sourcePartSet;
  if (!sourcePartSets.TryGetValue(type, out sourcePartSet) || sourcePartSet.Count == 0)
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("No source of " + ResourceContainer.GetResourceName(type)); MonoBehaviour.print("No source of " + ResourceContainer.GetResourceName(type));
} }
   
this.isActive = false; isActive = false;
return false; return false;
} }
} }
   
// Now we set the drains on the members of the sets and update the draining parts set // Now we set the drains on the members of the sets and update the draining parts set
foreach (int type in this.resourceConsumptions.Types) for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
{ {
  int type = this.resourceConsumptions.Types[i];
HashSet<PartSim> sourcePartSet = sourcePartSets[type]; HashSet<PartSim> sourcePartSet = sourcePartSets[type];
// Loop through the members of the set // Loop through the members of the set
double amount = this.resourceConsumptions[type] / sourcePartSet.Count; double amount = resourceConsumptions[type] / sourcePartSet.Count;
foreach (PartSim partSim in sourcePartSet) foreach (PartSim partSim in sourcePartSet)
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" + partSim.partId); MonoBehaviour.print(
  "Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" +
  partSim.partId);
} }
   
partSim.resourceDrains.Add(type, amount); partSim.resourceDrains.Add(type, amount);
drainingParts.Add(partSim); drainingParts.Add(partSim);
} }
} }
   
return true; return true;
}  
   
public void DumpEngineToBuffer(StringBuilder buffer, String prefix)  
{  
buffer.Append(prefix);  
buffer.AppendFormat("[thrust = {0:g6}, actual = {1:g6}, isp = {2:g6}\n", this.thrust, this.actualThrust, this.isp);  
} }
} }
} }
// //
// Kerbal Engineer Redux // Kerbal Engineer Redux
// //
// Copyright (C) 2014 CYBUTEK // Copyright (C) 2014 CYBUTEK
// //
// This program is free software: you can redistribute it and/or modify // 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 // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
   
#region Using Directives  
   
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
   
using KerbalEngineer.Extensions;  
   
using UnityEngine;  
   
#endregion  
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
using CompoundParts; using CompoundParts;
  using Extensions;
  using UnityEngine;
   
public class PartSim public class PartSim
{ {
  private static readonly Pool<PartSim> pool = new Pool<PartSim>(Create, Reset);
   
private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>(); private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>();
   
  public double baseMass;
public Vector3d centerOfMass; public Vector3d centerOfMass;
public double baseMass = 0d;  
public double cost; public double cost;
public int decoupledInStage; public int decoupledInStage;
public bool fuelCrossFeed; public bool fuelCrossFeed;
public List<PartSim> fuelTargets = new List<PartSim>(); public List<PartSim> fuelTargets = new List<PartSim>();
public bool hasModuleEngines; public bool hasModuleEngines;
public bool hasModuleEnginesFX; public bool hasModuleEnginesFX;
public bool hasMultiModeEngine; public bool hasMultiModeEngine;
   
public bool hasVessel; public bool hasVessel;
public String initialVesselName; public String initialVesselName;
public int inverseStage; public int inverseStage;
public bool isDecoupler; public bool isDecoupler;
public bool isEngine; public bool isEngine;
public bool isFuelLine; public bool isFuelLine;
public bool isFuelTank; public bool isFuelTank;
public bool isLanded; public bool isLanded;
public bool isNoPhysics; public bool isNoPhysics;
public bool isSepratron; public bool isSepratron;
  public bool isFairing;
public bool localCorrectThrust; public bool localCorrectThrust;
  public float moduleMass;
  public int stageIndex;
public String name; public String name;
public String noCrossFeedNodeKey; public String noCrossFeedNodeKey;
public PartSim parent; public PartSim parent;
public AttachModes parentAttach; public AttachModes parentAttach;
public Part part; // This is only set while the data structures are being initialised public Part part; // This is only set while the data structures are being initialised
public int partId = 0; public int partId = 0;
public ResourceContainer resourceDrains = new ResourceContainer(); public ResourceContainer resourceDrains = new ResourceContainer();
public ResourceContainer resourceFlowStates = new ResourceContainer(); public ResourceContainer resourceFlowStates = new ResourceContainer();
public ResourceContainer resources = new ResourceContainer(); public ResourceContainer resources = new ResourceContainer();
public double startMass = 0d; public double startMass = 0d;
public String vesselName; public String vesselName;
public VesselType vesselType; public VesselType vesselType;
   
public PartSim(Part thePart, int id, double atmosphere, LogMsg log)  
{ private static PartSim Create()
this.part = thePart; {
this.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset); return new PartSim();
this.partId = id; }
this.name = this.part.partInfo.name;  
  private static void Reset(PartSim partSim)
  {
  for (int i = 0; i < partSim.attachNodes.Count; i++)
  {
  partSim.attachNodes[i].Release();
  }
  partSim.attachNodes.Clear();
  partSim.fuelTargets.Clear();
  partSim.resourceDrains.Reset();
  partSim.resourceFlowStates.Reset();
  partSim.resources.Reset();
  partSim.baseMass = 0d;
  partSim.startMass = 0d;
  }
   
  public void Release()
  {
  pool.Release(this);
  }
   
  public static PartSim New(Part thePart, int id, double atmosphere, LogMsg log)
  {
  PartSim partSim = pool.Borrow();
   
   
  partSim.part = thePart;
  partSim.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset);
  partSim.partId = id;
  partSim.name = partSim.part.partInfo.name;
   
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Create PartSim for " + this.name); log.buf.AppendLine("Create PartSim for " + partSim.name);
} }
   
this.parent = null; partSim.parent = null;
this.parentAttach = part.attachMode; partSim.parentAttach = partSim.part.attachMode;
this.fuelCrossFeed = this.part.fuelCrossFeed; partSim.fuelCrossFeed = partSim.part.fuelCrossFeed;
this.noCrossFeedNodeKey = this.part.NoCrossFeedNodeKey; partSim.noCrossFeedNodeKey = partSim.part.NoCrossFeedNodeKey;
this.decoupledInStage = this.DecoupledInStage(this.part); partSim.decoupledInStage = partSim.DecoupledInStage(partSim.part);
this.isFuelLine = this.part.HasModule<CModuleFuelLine>(); partSim.isFuelLine = partSim.part.HasModule<CModuleFuelLine>();
this.isFuelTank = this.part is FuelTank; partSim.isFuelTank = partSim.part is FuelTank;
this.isSepratron = this.IsSepratron(); partSim.isSepratron = partSim.IsSepratron();
this.inverseStage = this.part.inverseStage; partSim.inverseStage = partSim.part.inverseStage;
//MonoBehaviour.print("inverseStage = " + inverseStage); //MonoBehaviour.print("inverseStage = " + inverseStage);
   
this.cost = this.part.GetCostWet(); partSim.cost = partSim.part.GetCostWet();
   
// Work out if the part should have no physical significance // Work out if the part should have no physical significance
this.isNoPhysics = this.part.HasModule<LaunchClamp>() || partSim.isNoPhysics = partSim.part.HasModule<LaunchClamp>() ||
this.part.physicalSignificance == Part.PhysicalSignificance.NONE || partSim.part.physicalSignificance == Part.PhysicalSignificance.NONE ||
this.part.PhysicsSignificance == 1; partSim.part.PhysicsSignificance == 1;
   
if (!this.isNoPhysics) if (!partSim.isNoPhysics)
{ {
this.baseMass = this.part.mass; partSim.baseMass = partSim.part.mass;
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print((this.isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + this.part.mass); MonoBehaviour.print((partSim.isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + partSim.part.mass);
} }
   
foreach (PartResource resource in this.part.Resources) for (int i = 0; i < partSim.part.Resources.Count; i++)
{ {
  PartResource resource = partSim.part.Resources[i];
   
// Make sure it isn't NaN as this messes up the part mass and hence most of the values // Make sure it isn't NaN as this messes up the part mass and hence most of the values
// This can happen if a resource capacity is 0 and tweakable // This can happen if a resource capacity is 0 and tweakable
if (!Double.IsNaN(resource.amount)) if (!Double.IsNaN(resource.amount))
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print(resource.resourceName + " = " + resource.amount); MonoBehaviour.print(resource.resourceName + " = " + resource.amount);
} }
   
this.resources.Add(resource.info.id, resource.amount); partSim.resources.Add(resource.info.id, resource.amount);
this.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0); partSim.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0);
} }
else else
{ {
MonoBehaviour.print(resource.resourceName + " is NaN. Skipping."); MonoBehaviour.print(resource.resourceName + " is NaN. Skipping.");
} }
} }
   
this.startMass = this.GetMass(); partSim.startMass = partSim.GetMass(-1);
   
this.hasVessel = (this.part.vessel != null); partSim.hasVessel = (partSim.part.vessel != null);
this.isLanded = this.hasVessel && this.part.vessel.Landed; partSim.isLanded = partSim.hasVessel && partSim.part.vessel.Landed;
if (this.hasVessel) if (partSim.hasVessel)
{ {
this.vesselName = this.part.vessel.vesselName; partSim.vesselName = partSim.part.vessel.vesselName;
this.vesselType = this.part.vesselType; partSim.vesselType = partSim.part.vesselType;
} }
this.initialVesselName = this.part.initialVesselName; partSim.initialVesselName = partSim.part.initialVesselName;
   
this.hasMultiModeEngine = this.part.HasModule<MultiModeEngine>(); partSim.hasMultiModeEngine = partSim.part.HasModule<MultiModeEngine>();
this.hasModuleEnginesFX = this.part.HasModule<ModuleEnginesFX>(); partSim.hasModuleEnginesFX = partSim.part.HasModule<ModuleEnginesFX>();
this.hasModuleEngines = this.part.HasModule<ModuleEngines>(); partSim.hasModuleEngines = partSim.part.HasModule<ModuleEngines>();
   
this.isEngine = this.hasMultiModeEngine || this.hasModuleEnginesFX || this.hasModuleEngines; partSim.isEngine = partSim.hasMultiModeEngine || partSim.hasModuleEnginesFX || partSim.hasModuleEngines;
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("Created " + this.name + ". Decoupled in stage " + this.decoupledInStage); MonoBehaviour.print("Created " + partSim.name + ". Decoupled in stage " + partSim.decoupledInStage);
  }
  return partSim;
  }
   
  public ResourceContainer ResourceDrains
  {
  get
  {
  return resourceDrains;
} }
} }
   
public ResourceContainer Resources public ResourceContainer Resources
{ {
get { return this.resources; } get
} {
  return resources;
public ResourceContainer ResourceDrains }
{ }
get { return this.resourceDrains; }  
} public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log)
  {
public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double velocity, bool vectoredThrust, LogMsg log) bool correctThrust = SimManager.DoesEngineUseCorrectedThrust(part);
{  
bool correctThrust = SimManager.DoesEngineUseCorrectedThrust(this.part);  
if (log != null) if (log != null)
{ {
log.buf.AppendLine("CreateEngineSims for " + this.name); log.buf.AppendLine("CreateEngineSims for " + this.name);
  for (int i = 0; i < this.part.Modules.Count; i++)
foreach (PartModule partMod in this.part.Modules) {
{ PartModule partMod = this.part.Modules[i];
log.buf.AppendLine("Module: " + partMod.moduleName); log.buf.AppendLine("Module: " + partMod.moduleName);
} }
   
log.buf.AppendLine("correctThrust = " + correctThrust); log.buf.AppendLine("correctThrust = " + correctThrust);
} }
   
if (this.hasMultiModeEngine) if (hasMultiModeEngine)
{ {
// A multi-mode engine has multiple ModuleEnginesFX but only one is active at any point // A multi-mode engine has multiple ModuleEnginesFX but only one is active at any point
// The mode of the engine is the engineID of the ModuleEnginesFX that is active // The mode of the engine is the engineID of the ModuleEnginesFX that is active
string mode = this.part.GetModule<MultiModeEngine>().mode; string mode = part.GetModule<MultiModeEngine>().mode;
   
foreach (ModuleEnginesFX engine in this.part.GetModules<ModuleEnginesFX>()) List<ModuleEnginesFX> engines = part.GetModules<ModuleEnginesFX>();
{ for (int i = 0; i < engines.Count; ++i)
  {
  ModuleEnginesFX engine = engines[i];
if (engine.engineID == mode) if (engine.engineID == mode)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Module: " + engine.moduleName); log.buf.AppendLine("Module: " + engine.moduleName);
} }
   
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);
   
EngineSim engineSim = new EngineSim(this, EngineSim engineSim = EngineSim.New(
atmosphere, this,
velocity, atmosphere,
engine.maxThrust, (float)mach,
engine.minThrust, engine.maxFuelFlow,
engine.thrustPercentage, engine.minFuelFlow,
engine.requestedThrust, engine.thrustPercentage,
thrustvec, thrustvec,
engine.realIsp, engine.atmosphereCurve,
engine.atmosphereCurve, engine.atmChangeFlow,
engine.useVelocityCurve ? engine.velocityCurve : null, engine.useAtmCurve ? engine.atmCurve : null,
engine.throttleLocked, engine.useVelCurve ? engine.velCurve : null,
engine.propellants, engine.currentThrottle,
engine.isOperational, engine.g,
correctThrust, engine.throttleLocked || fullThrust,
engine.thrustTransforms); engine.propellants,
  engine.isOperational,
  engine.resultingThrust,
  engine.thrustTransforms);
allEngines.Add(engineSim); allEngines.Add(engineSim);
} }
} }
} }
else else
{ {
if (this.hasModuleEnginesFX) if (hasModuleEngines)
{ {
foreach (ModuleEnginesFX engine in this.part.GetModules<ModuleEnginesFX>()) List<ModuleEngines> engines = part.GetModules<ModuleEngines>();
{ for (int i = 0; i < engines.Count; ++i)
  {
  ModuleEngines engine = engines[i];
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Module: " + engine.moduleName); log.buf.AppendLine("Module: " + engine.moduleName);
} }
   
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);
   
EngineSim engineSim = new EngineSim(this, EngineSim engineSim = EngineSim.New(
atmosphere, this,
velocity, atmosphere,
engine.maxThrust, (float)mach,
engine.minThrust, engine.maxFuelFlow,
engine.thrustPercentage, engine.minFuelFlow,
engine.requestedThrust, engine.thrustPercentage,
thrustvec, thrustvec,
engine.realIsp, engine.atmosphereCurve,
engine.atmosphereCurve, engine.atmChangeFlow,
engine.useVelocityCurve ? engine.velocityCurve : null, engine.useAtmCurve ? engine.atmCurve : null,
engine.throttleLocked, engine.useVelCurve ? engine.velCurve : null,
engine.propellants, engine.currentThrottle,
engine.isOperational, engine.g,
correctThrust, engine.throttleLocked || fullThrust,
engine.thrustTransforms); engine.propellants,
  engine.isOperational,
  engine.resultingThrust,
  engine.thrustTransforms);
allEngines.Add(engineSim); allEngines.Add(engineSim);
} }
} }
   
if (this.hasModuleEngines)  
{  
foreach (ModuleEngines engine in this.part.GetModules<ModuleEngines>())  
{  
if (log != null)  
{  
log.buf.AppendLine("Module: " + engine.moduleName);  
}  
   
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);  
   
EngineSim engineSim = new EngineSim(this,  
atmosphere,  
velocity,  
engine.maxThrust,  
engine.minThrust,  
engine.thrustPercentage,  
engine.requestedThrust,  
thrustvec,  
engine.realIsp,  
engine.atmosphereCurve,  
engine.useVelocityCurve ? engine.velocityCurve : null,  
engine.throttleLocked,  
engine.propellants,  
engine.isOperational,  
correctThrust,  
engine.thrustTransforms);  
allEngines.Add(engineSim);  
}  
}  
} }
   
if (log != null) if (log != null)
{ {
log.Flush(); log.Flush();
} }
} }
   
private Vector3 CalculateThrustVector(List<Transform> thrustTransforms, LogMsg log) public int DecouplerCount()
{ {
if (thrustTransforms == null) int count = 0;
{ PartSim partSim = this;
return Vector3.forward; while (partSim != null)
} {
  if (partSim.isDecoupler)
Vector3 thrustvec = Vector3.zero; {
foreach (Transform trans in thrustTransforms) count++;
{ }
if (log != null)  
{ partSim = partSim.parent;
log.buf.AppendFormat("Transform = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", trans.forward.x, trans.forward.y, trans.forward.z, trans.forward.magnitude); }
} return count;
  }
thrustvec -= trans.forward;  
} public void DrainResources(double time)
  {
if (log != null) //MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")");
{ for (int i = 0; i < resourceDrains.Types.Count; ++i)
log.buf.AppendFormat("ThrustVec = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude); {
} int type = resourceDrains.Types[i];
   
thrustvec.Normalize(); //MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type));
  resources.Add(type, -time * resourceDrains[type]);
if (log != null) //MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]);
{ }
log.buf.AppendFormat("ThrustVecN = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude); }
}  
  public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix)
return thrustvec; {
} if (parent != null)
  {
public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log) prefix = parent.DumpPartAndParentsToBuffer(buffer, prefix) + " ";
{ }
if (this.part.parent != null)  
{ DumpPartToBuffer(buffer, prefix);
this.parent = null;  
if (partSimLookup.TryGetValue(this.part.parent, out this.parent)) return prefix;
{ }
if (log != null)  
{ public void DumpPartToBuffer(StringBuilder buffer, String prefix, List<PartSim> allParts = null)
log.buf.AppendLine("Parent part is " + this.parent.name + ":" + this.parent.partId); {
} buffer.Append(prefix);
} buffer.Append(name);
else buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", partId, decoupledInStage, inverseStage);
{  
if (log != null) buffer.AppendFormat(", vesselName = '{0}'", vesselName);
{ buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(vesselType));
log.buf.AppendLine("No PartSim for parent part (" + this.part.parent.partInfo.name + ")"); buffer.AppendFormat(", initialVesselName = '{0}'", initialVesselName);
}  
} buffer.AppendFormat(", fuelCF = {0}", fuelCrossFeed);
} buffer.AppendFormat(", noCFNKey = '{0}'", noCrossFeedNodeKey);
}  
  buffer.AppendFormat(", isSep = {0}", isSepratron);
public void SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup, LogMsg log)  
{ foreach (int type in resources.Types)
if (log != null) {
{ buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), resources[type]);
log.buf.AppendLine("SetupAttachNodes for " + this.name + ":" + this.partId + ""); }
}  
  if (attachNodes.Count > 0)
this.attachNodes.Clear(); {
foreach (AttachNode attachNode in this.part.attachNodes) buffer.Append(", attached = <");
{ attachNodes[0].DumpToBuffer(buffer);
if (log != null) for (int i = 1; i < attachNodes.Count; i++)
{ {
log.buf.AppendLine("AttachNode " + attachNode.id + " = " + (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null")); buffer.Append(", ");
} attachNodes[i].DumpToBuffer(buffer);
  }
if (attachNode.attachedPart != null && attachNode.id != "Strut") buffer.Append(">");
{ }
PartSim attachedSim;  
if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim)) // Add more info here
{  
if (log != null) buffer.Append("]\n");
{  
log.buf.AppendLine("Adding attached node " + attachedSim.name + ":" + attachedSim.partId + ""); if (allParts != null)
} {
  String newPrefix = prefix + " ";
this.attachNodes.Add(new AttachNodeSim(attachedSim, attachNode.id, attachNode.nodeType)); foreach (PartSim partSim in allParts)
} {
else if (partSim.parent == this)
{ {
if (log != null) partSim.DumpPartToBuffer(buffer, newPrefix, allParts);
{ }
log.buf.AppendLine("No PartSim for attached part (" + attachNode.attachedPart.partInfo.name + ")"); }
} }
} }
}  
} public bool EmptyOf(HashSet<int> types)
  {
foreach (Part p in this.part.fuelLookupTargets) foreach (int type in types)
{ {
if (p != null) if (resources.HasType(type) && resourceFlowStates[type] != 0 && resources[type] > SimManager.RESOURCE_MIN)
{ {
PartSim targetSim; return false;
if (partSimLookup.TryGetValue(p, out targetSim)) }
{ }
if (log != null)  
{ return true;
log.buf.AppendLine("Fuel target: " + targetSim.name + ":" + targetSim.partId); }
}  
  public double GetMass(int currentStage)
this.fuelTargets.Add(targetSim); {
} double mass = baseMass;
else  
{ for (int i = 0; i < resources.Types.Count; ++i)
if (log != null) {
{ mass += resources.GetResourceMass(resources.Types[i]);
log.buf.AppendLine("No PartSim for fuel target (" + p.name + ")"); }
}  
} if (hasVessel == false && isFairing && inverseStage < currentStage)
} {
} mass = mass + moduleMass;
} }
   
private int DecoupledInStage(Part thePart, int stage = -1) return mass;
{ }
if (this.IsDecoupler(thePart))  
{  
if (thePart.inverseStage > stage)  
{  
stage = thePart.inverseStage;  
}  
}  
   
if (thePart.parent != null)  
{  
stage = this.DecoupledInStage(thePart.parent, stage);  
}