Added missing changes to the change log.
Added missing changes to the change log.

1.0.17.1 1.0.18.0
  Added: Orbital readouts - "Speed at Periapsis" and "Speed at Apoapsis". (Padishar)
  Added: Manoeuvre readouts - "Post-burn Apoapsis" and "Post-burn Periapsis". (Padishar)
  Added: Orbital readout - "Time to Atmosphere".
Fixed: Synched the minimum simulation time sliders and stopped them from snapping back after 999ms. (saybur) Fixed: Synched the minimum simulation time sliders and stopped them from snapping back after 999ms. (saybur)
Fixed: Added workaround for the bug in Vessel.horizontalSrfSpeed (Padishar) Fixed: Added workaround for the bug in Vessel.horizontalSrfSpeed (Padishar)
  Fixed: Physically insignificant part mass was not being correctly cascaded down through multiple parent parts.
  Fixed: Intake air demand calculation not working.
  Fixed: Some build engineer settings labels do not scale with UI size.
   
1.0.17.0 1.0.17.0
Added: 'Mach Number' readout under the 'Surface' category and included it on the default surface HUD. Added: 'Mach Number' readout under the 'Surface' category and included it on the default surface HUD.
Added: Stock sections in the Flight Engineer can now become HUDs. Added: Stock sections in the Flight Engineer can now become HUDs.
Added 'Thermal' readouts category including: Added 'Thermal' readouts category including:
Internal Flux Internal Flux
Convection Flux Convection Flux
Radiation Flux Radiation Flux
Critical Part Name Critical Part Name
Critical Part Temperature Critical Part Temperature
Critical Part Skin Temperature Critical Part Skin Temperature
Critical Part Thermal Percentage of Max Temperature Critical Part Thermal Percentage of Max Temperature
Hottest Part Name Hottest Part Name
Hottest Part Temperature Hottest Part Temperature
Hottest Part Skin Temperature Hottest Part Skin Temperature
Coldest Part Name Coldest Part Name
Coldest Part Temperature Coldest Part Temperature
Coldest Part Skin Temperature Coldest Part Skin Temperature
   
Changed: Mach on the Build Engineer now accurate to 2 decimal places. Changed: Mach on the Build Engineer now accurate to 2 decimal places.
Changed: Max mach in the Build Engineer defaults to 1.00 even when no jet engines are present. Changed: Max mach in the Build Engineer defaults to 1.00 even when no jet engines are present.
Changed: Increased eccentricity readout to 5 decimal places. Changed: Increased eccentricity readout to 5 decimal places.
Changed: Implemented Sarbian's object pooling. Changed: Implemented Sarbian's object pooling.
Changed: The default selected body is now assigned via 'Planitarium.Home'. Changed: The default selected body is now assigned via 'Planitarium.Home'.
Changed: HUDs to clamp fully inside the screen instead of allowing them to run off the edge by a certain amount. Changed: HUDs to clamp fully inside the screen instead of allowing them to run off the edge by a certain amount.
Fixed: Physically insignificant part mass is now associated with the parent part. Fixed: Physically insignificant part mass is now associated with the parent part.
Fixed: Longitude and Latitude readouts now use a KER formatter instead of Squad's incorrect implementation. Fixed: Longitude and Latitude readouts now use a KER formatter instead of Squad's incorrect implementation.
Fixed: Possible null reference in the Rendezvous Processor. Fixed: Possible null reference in the Rendezvous Processor.
Fixed: Fairing mass issues introduced with regards to simulation changes. Fixed: Fairing mass issues introduced with regards to simulation changes.
Fixed: Use of per-propellant fuel flow mode override. Fixed: Use of per-propellant fuel flow mode override.
Fixed: Burn times calculated for jet engines. Fixed: Burn times calculated for jet engines.
Fixed: Thrust issues introduced with Sarbian's simulation alterations. Fixed: Thrust issues introduced with Sarbian's simulation alterations.
Fixed: Issue where HUDs positioned close to the top/bottom of the screen could be pushed out of position. Fixed: Issue where HUDs positioned close to the top/bottom of the screen could be pushed out of position.
   
1.0.16.6, 02-05-15 1.0.16.6, 02-05-15
Fixed: Separately staged fairing mass jettisons are now calculated in the editor. Fixed: Separately staged fairing mass jettisons are now calculated in the editor.
   
1.0.16.5, 02-05-2015 1.0.16.5, 02-05-2015
Fixed: Delta-V not being correctly calculated. Fixed: Delta-V not being correctly calculated.
Changed: Editor locking now uses the InputLockManager. Changed: Editor locking now uses the InputLockManager.
   
1.0.16.4, 01-05-2015 1.0.16.4, 01-05-2015
Fixed: Physically insignificant part mass is now accounted for. 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). Changed: Module mass accounted for as it now makes its way onto the launch pad (e.g. fairings).
   
Various optimisations: Various optimisations:
Object pooling. Object pooling.
Removed LINQ expressions. Removed LINQ expressions.
Converted foreach to for loops. Converted foreach to for loops.
   
1.0.16.3, 27-04-2015 1.0.16.3, 27-04-2015
Fixed issue with the toolbar icons not being created. Fixed issue with the toolbar icons not being created.
Removed superfluous 'm/s' on the mach slider in the build engineer. Removed superfluous 'm/s' on the mach slider in the build engineer.
   
1.0.16.2, 27-04-2015 1.0.16.2, 27-04-2015
Changed the atmospheric slider on the build engineer to default to 0km when changing bodies. 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 1.0.16.1, 26-04-2015, KSP Build #828
Merged Sarbian's mach adjustments. Merged Sarbian's mach adjustments.
Fixed bugs relating to thrust and atmosphere/velocity curves. 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 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 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. 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 1.0.16.0, 25-04-2015, KSP Build #821
Fixed errors relating to KSP 1.0 update. Fixed errors relating to KSP 1.0 update.
Fixed fuel simulation to account for new thrust system. Fixed fuel simulation to account for new thrust system.
Fixed atmospheric engines to use the new velocity curve. Fixed atmospheric engines to use the new velocity curve.
Fixed atmospheric readouts to work with the new atmospheric model. Fixed atmospheric readouts to work with the new atmospheric model.
   
1.0.15.2, 13-02-2015 1.0.15.2, 13-02-2015
Padishar's Fixes: Padishar's Fixes:
Fixed: Calculation of per-stage resource mass. 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.
   
// //
// Copyright (C) 2015 CYBUTEK // Copyright (C) 2015 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 namespace KerbalEngineer
{ {
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
   
public static class EngineerGlobals public static class EngineerGlobals
{ {
/// <summary> /// <summary>
/// Current version of the Kerbal Engineer assembly. /// Current version of the Kerbal Engineer assembly.
/// </summary> /// </summary>
public const string ASSEMBLY_VERSION = "1.0.17.0"; public const string ASSEMBLY_VERSION = "1.0.18.0";
   
private static string assemblyFile; private static string assemblyFile;
private static string assemblyName; private static string assemblyName;
private static string assemblyPath; private static string assemblyPath;
   
/// <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 get
{ {
return assemblyFile ?? (assemblyFile = Assembly.GetExecutingAssembly().Location); 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 get
{ {
return assemblyName ?? (assemblyName = new FileInfo(AssemblyFile).Name); 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 get
{ {
return assemblyPath ?? (assemblyPath = AssemblyFile.Replace(new FileInfo(AssemblyFile).Name, "")); return assemblyPath ?? (assemblyPath = AssemblyFile.Replace(new FileInfo(AssemblyFile).Name, ""));
} }
} }
} }
} }
// //
// 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.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
   
using UnityEngine; using UnityEngine;
   
#endregion #endregion
   
namespace KerbalEngineer.VesselSimulator namespace KerbalEngineer.VesselSimulator
{ {
  using System.ComponentModel;
using CompoundParts; using CompoundParts;
using Extensions; using Extensions;
using Helpers; using Helpers;
   
public class Simulation public class Simulation
{ {
private const double SECONDS_PER_DAY = 86400; private const double SECONDS_PER_DAY = 86400;
private readonly Stopwatch _timer = new Stopwatch(); private readonly Stopwatch _timer = new Stopwatch();
private List<EngineSim> activeEngines = new List<EngineSim>(); private List<EngineSim> activeEngines = new List<EngineSim>();
private List<EngineSim> allEngines = new List<EngineSim>(); private List<EngineSim> allEngines = new List<EngineSim>();
private List<PartSim> allFuelLines = new List<PartSim>(); private List<PartSim> allFuelLines = new List<PartSim>();
private List<PartSim> allParts = new List<PartSim>(); private List<PartSim> allParts = new List<PartSim>();
private double atmosphere; private double atmosphere;
private int currentStage; private int currentStage;
private double currentisp; private double currentisp;
private HashSet<PartSim> decoupledParts = new HashSet<PartSim>(); private HashSet<PartSim> decoupledParts = new HashSet<PartSim>();
private bool doingCurrent; private bool doingCurrent;
private List<PartSim> dontStageParts; private List<PartSim> dontStageParts;
private List<List<PartSim>> dontStagePartsLists = new List<List<PartSim>>(); private List<List<PartSim>> dontStagePartsLists = new List<List<PartSim>>();
private HashSet<PartSim> drainingParts; private HashSet<PartSim> drainingParts;
private HashSet<int> drainingResources; private HashSet<int> drainingResources;
private double gravity; private double gravity;
private Dictionary<Part, PartSim> partSimLookup; private Dictionary<Part, PartSim> partSimLookup;
   
private int lastStage; private int lastStage;
private List<Part> partList = new List<Part>(); private List<Part> partList = new List<Part>();
private double simpleTotalThrust; private double simpleTotalThrust;
private double stageStartMass; private double stageStartMass;
private Vector3d stageStartCom; private Vector3d stageStartCom;
private double stageTime; private double stageTime;
private double stepEndMass; private double stepEndMass;
private double stepStartMass; private double stepStartMass;
private double totalStageActualThrust; private double totalStageActualThrust;
private double totalStageFlowRate; private double totalStageFlowRate;
private double totalStageIspFlowRate; private double totalStageIspFlowRate;
private double totalStageThrust; private double totalStageThrust;
private ForceAccumulator totalStageThrustForce = new ForceAccumulator(); private ForceAccumulator totalStageThrustForce = new ForceAccumulator();
private Vector3 vecActualThrust; private Vector3 vecActualThrust;
private Vector3 vecStageDeltaV; private Vector3 vecStageDeltaV;
private Vector3 vecThrust; private Vector3 vecThrust;
private double mach; private double mach;
private float maxMach; private float maxMach;
public String vesselName; public String vesselName;
public VesselType vesselType; public VesselType vesselType;
private WeightedVectorAverager vectorAverager = new WeightedVectorAverager(); private WeightedVectorAverager vectorAverager = new WeightedVectorAverager();
   
public Simulation() public Simulation()
{ {
this.allParts = new List<PartSim>(); this.allParts = new List<PartSim>();
this.allFuelLines = new List<PartSim>(); this.allFuelLines = new List<PartSim>();
this.drainingParts = new HashSet<PartSim>(); this.drainingParts = new HashSet<PartSim>();
this.allEngines = new List<EngineSim>(); this.allEngines = new List<EngineSim>();
this.activeEngines = new List<EngineSim>(); this.activeEngines = new List<EngineSim>();
this.drainingResources = new HashSet<int>(); this.drainingResources = new HashSet<int>();
this.totalStageThrustForce = new ForceAccumulator(); this.totalStageThrustForce = new ForceAccumulator();
   
// A dictionary for fast lookup of Part->PartSim during the preparation phase // A dictionary for fast lookup of Part->PartSim during the preparation phase
partSimLookup = new Dictionary<Part, PartSim>(); partSimLookup = new Dictionary<Part, PartSim>();
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("Simulation created"); MonoBehaviour.print("Simulation created");
} }
} }
   
private double ShipMass private double ShipMass
{ {
get get
{ {
double mass = 0d; double mass = 0d;
   
for (int i = 0; i < allParts.Count; ++i) { for (int i = 0; i < allParts.Count; ++i) {
mass += allParts[i].GetMass(currentStage); mass += allParts[i].GetMass(currentStage);
} }
   
return mass; return mass;
} }
} }
   
private Vector3d ShipCom private Vector3d ShipCom
{ {
get get
{ {
vectorAverager.Reset(); vectorAverager.Reset();
   
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[i]; PartSim partSim = allParts[i];
vectorAverager.Add(partSim.centerOfMass, partSim.GetMass(currentStage, true)); vectorAverager.Add(partSim.centerOfMass, partSim.GetMass(currentStage, true));
} }
   
return vectorAverager.Get(); return vectorAverager.Get();
} }
} }
   
// This function prepares the simulation by creating all the necessary data structures it will // This function prepares the simulation by creating all the necessary data structures it will
// need during the simulation. All required data is copied from the core game data structures // need during the simulation. All required data is copied from the core game data structures
// so that the simulation itself can be run in a background thread without having issues with // so that the simulation itself can be run in a background thread without having issues with
// the core game changing the data while the simulation is running. // the core game changing the data while the simulation is running.
public bool PrepareSimulation(List<Part> parts, double theGravity, double theAtmosphere = 0, double theMach = 0, bool dumpTree = false, bool vectoredThrust = false, bool fullThrust = false) public bool PrepareSimulation(List<Part> parts, double theGravity, double theAtmosphere = 0, double theMach = 0, bool dumpTree = false, bool vectoredThrust = false, bool fullThrust = false)
{ {
LogMsg log = null; LogMsg log = null;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
log.buf.AppendLine("PrepareSimulation started"); log.buf.AppendLine("PrepareSimulation started");
dumpTree = true; dumpTree = true;
} }
this._timer.Reset(); this._timer.Reset();
this._timer.Start(); this._timer.Start();
   
// Store the parameters in members for ease of access in other functions // Store the parameters in members for ease of access in other functions
this.partList = parts; this.partList = parts;
this.gravity = theGravity; this.gravity = theGravity;
this.atmosphere = theAtmosphere; this.atmosphere = theAtmosphere;
this.mach = theMach; this.mach = theMach;
this.lastStage = Staging.lastStage; this.lastStage = Staging.lastStage;
this.maxMach = 1.0f; this.maxMach = 1.0f;
//MonoBehaviour.print("lastStage = " + lastStage); //MonoBehaviour.print("lastStage = " + lastStage);
   
// Clear the lists for our simulation parts // Clear the lists for our simulation parts
allParts.Clear(); allParts.Clear();
allFuelLines.Clear(); allFuelLines.Clear();
drainingParts.Clear(); drainingParts.Clear();
allEngines.Clear(); allEngines.Clear();
activeEngines.Clear(); activeEngines.Clear();
drainingResources.Clear(); drainingResources.Clear();
   
// A dictionary for fast lookup of Part->PartSim during the preparation phase // A dictionary for fast lookup of Part->PartSim during the preparation phase
partSimLookup.Clear(); partSimLookup.Clear();
   
if (this.partList.Count > 0 && this.partList[0].vessel != null) if (this.partList.Count > 0 && this.partList[0].vessel != null)
{ {
this.vesselName = this.partList[0].vessel.vesselName; this.vesselName = this.partList[0].vessel.vesselName;
this.vesselType = this.partList[0].vessel.vesselType; this.vesselType = this.partList[0].vessel.vesselType;
} }
//MonoBehaviour.print("PrepareSimulation pool size = " + PartSim.pool.Count()); //MonoBehaviour.print("PrepareSimulation pool size = " + PartSim.pool.Count());
// First we create a PartSim for each Part (giving each a unique id) // First we create a PartSim for each Part (giving each a unique id)
int partId = 1; int partId = 1;
for (int i = 0; i < partList.Count; ++i) for (int i = 0; i < partList.Count; ++i)
{ {
Part part = partList[i]; Part part = partList[i];
   
// If the part is already in the lookup dictionary then log it and skip to the next part // If the part is already in the lookup dictionary then log it and skip to the next part
if (partSimLookup.ContainsKey(part)) if (partSimLookup.ContainsKey(part))
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Part " + part.name + " appears in vessel list more than once"); log.buf.AppendLine("Part " + part.name + " appears in vessel list more than once");
} }
continue; continue;
} }
   
// Create the PartSim // Create the PartSim
PartSim partSim = PartSim.New(part, partId, this.atmosphere, log); PartSim partSim = PartSim.New(part, partId, this.atmosphere, log);
   
// Add it to the Part lookup dictionary and the necessary lists // Add it to the Part lookup dictionary and the necessary lists
partSimLookup.Add(part, partSim); partSimLookup.Add(part, partSim);
this.allParts.Add(partSim); this.allParts.Add(partSim);
if (partSim.isFuelLine) if (partSim.isFuelLine)
{ {
this.allFuelLines.Add(partSim); this.allFuelLines.Add(partSim);
} }
if (partSim.isEngine) if (partSim.isEngine)
{ {
partSim.CreateEngineSims(this.allEngines, this.atmosphere, this.mach, vectoredThrust, fullThrust, log); partSim.CreateEngineSims(this.allEngines, this.atmosphere, this.mach, vectoredThrust, fullThrust, log);
} }
   
partId++; partId++;
} }
   
for (int i = 0; i < allEngines.Count; ++i) for (int i = 0; i < allEngines.Count; ++i)
{ {
maxMach = Mathf.Max(maxMach, allEngines[i].maxMach); maxMach = Mathf.Max(maxMach, allEngines[i].maxMach);
} }
   
this.UpdateActiveEngines(); this.UpdateActiveEngines();
   
// Now that all the PartSims have been created we can do any set up that needs access to other parts // Now that all the PartSims have been created we can do any set up that needs access to other parts
// First we set up all the parent links // First we set up all the parent links
for (int i = 0; i < this.allParts.Count; i++) for (int i = 0; i < this.allParts.Count; i++)
{ {
PartSim partSim = this.allParts[i]; PartSim partSim = this.allParts[i];
partSim.SetupParent(partSimLookup, log); partSim.SetupParent(partSimLookup, log);
} }
   
// Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets // Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets
if (HighLogic.LoadedSceneIsEditor) if (HighLogic.LoadedSceneIsEditor)
{ {
for (int i = 0; i < allFuelLines.Count; ++i) for (int i = 0; i < allFuelLines.Count; ++i)
{ {
PartSim partSim = allFuelLines[i]; PartSim partSim = allFuelLines[i];
   
CModuleFuelLine fuelLine = partSim.part.GetModule<CModuleFuelLine>(); CModuleFuelLine fuelLine = partSim.part.GetModule<CModuleFuelLine>();
if (fuelLine.target != null) if (fuelLine.target != null)
{ {
PartSim targetSim; PartSim targetSim;
if (partSimLookup.TryGetValue(fuelLine.target, out targetSim)) if (partSimLookup.TryGetValue(fuelLine.target, out targetSim))
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Fuel line target is " + targetSim.name + ":" + targetSim.partId); log.buf.AppendLine("Fuel line target is " + targetSim.name + ":" + targetSim.partId);
} }
   
targetSim.fuelTargets.Add(partSim.parent); targetSim.fuelTargets.Add(partSim.parent);
} }
else else
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("No PartSim for fuel line target (" + partSim.part.partInfo.name + ")"); log.buf.AppendLine("No PartSim for fuel line target (" + partSim.part.partInfo.name + ")");
} }
} }
} }
else else
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Fuel line target is null"); log.buf.AppendLine("Fuel line target is null");
} }
} }
} }
} }
   
//MonoBehaviour.print("SetupAttachNodes and count stages"); //MonoBehaviour.print("SetupAttachNodes and count stages");
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[i]; PartSim partSim = allParts[i];
   
partSim.SetupAttachNodes(partSimLookup, log); partSim.SetupAttachNodes(partSimLookup, log);
if (partSim.decoupledInStage >= this.lastStage) if (partSim.decoupledInStage >= this.lastStage)
{ {
this.lastStage = partSim.decoupledInStage + 1; this.lastStage = partSim.decoupledInStage + 1;
} }
} }
   
// And finally release the Part references from all the PartSims // And finally release the Part references from all the PartSims
//MonoBehaviour.print("ReleaseParts"); //MonoBehaviour.print("ReleaseParts");
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
allParts[i].ReleasePart(); allParts[i].ReleasePart();
} }
   
// And dereference the core's part list // And dereference the core's part list
this.partList = null; this.partList = null;
   
this._timer.Stop(); this._timer.Stop();
if (log != null) if (log != null)
{ {
log.buf.AppendLine("PrepareSimulation: " + this._timer.ElapsedMilliseconds + "ms"); log.buf.AppendLine("PrepareSimulation: " + this._timer.ElapsedMilliseconds + "ms");
log.Flush(); log.Flush();
} }
   
if (dumpTree) if (dumpTree)
{ {
this.Dump(); this.Dump();
} }
   
return true; return true;
} }
// This function runs the simulation and returns a newly created array of Stage objects // This function runs the simulation and returns a newly created array of Stage objects
public Stage[] RunSimulation() public Stage[] RunSimulation()
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
MonoBehaviour.print("RunSimulation started"); MonoBehaviour.print("RunSimulation started");
} }
   
this._timer.Reset(); this._timer.Reset();
this._timer.Start(); this._timer.Start();
   
LogMsg log = null; LogMsg log = null;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
log = new LogMsg(); log = new LogMsg();
} }
   
// Start with the last stage to simulate // Start with the last stage to simulate
// (this is in a member variable so it can be accessed by AllowedToStage and ActivateStage) // (this is in a member variable so it can be accessed by AllowedToStage and ActivateStage)
this.currentStage = this.lastStage; this.currentStage = this.lastStage;
// Work out which engines would be active if just doing the staging and if this is different to the // Work out which engines would be active if just doing the staging and if this is different to the
// currently active engines then generate an extra stage // currently active engines then generate an extra stage
// Loop through all the engines // Loop through all the engines
bool anyActive = false; bool anyActive = false;
for (int i = 0; i < allEngines.Count; ++i) for (int i = 0; i < allEngines.Count; ++i)
{ {
EngineSim engine = allEngines[i]; EngineSim engine = allEngines[i];
   
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Testing engine mod of " + engine.partSim.name + ":" + engine.partSim.partId); log.buf.AppendLine("Testing engine mod of " + engine.partSim.name + ":" + engine.partSim.partId);
} }
bool bActive = engine.isActive; bool bActive = engine.isActive;
bool bStage = (engine.partSim.inverseStage >= this.currentStage); bool bStage = (engine.partSim.inverseStage >= this.currentStage);
if (log != null) if (log != null)
{ {
log.buf.AppendLine("bActive = " + bActive + " bStage = " + bStage); log.buf.AppendLine("bActive = " + bActive + " bStage = " + bStage);
} }
if (HighLogic.LoadedSceneIsFlight) if (HighLogic.LoadedSceneIsFlight)
{ {
if (bActive) if (bActive)
{ {
anyActive = true; anyActive = true;
} }
if (bActive != bStage) if (bActive != bStage)
{ {
// If the active state is different to the state due to staging // If the active state is different to the state due to staging
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Need to do current active engines first"); log.buf.AppendLine("Need to do current active engines first");
} }
   
this.doingCurrent = true; this.doingCurrent = true;
} }
} }
else else
{ {
if (bStage) if (bStage)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Marking as active"); log.buf.AppendLine("Marking as active");
} }
   
engine.isActive = true; engine.isActive = true;
} }
} }
} }
   
// If we need to do current because of difference in engine activation and there actually are active engines // If we need to do current because of difference in engine activation and there actually are active engines
// then we do the extra stage otherwise activate the next stage and don't treat it as current // then we do the extra stage otherwise activate the next stage and don't treat it as current
if (this.doingCurrent && anyActive) if (this.doingCurrent && anyActive)
{ {
this.currentStage++; this.currentStage++;
} }
else else
{ {
this.ActivateStage(); this.ActivateStage();
this.doingCurrent = false; this.doingCurrent = false;
} }
   
// Create a list of lists of PartSims that prevent decoupling // Create a list of lists of PartSims that prevent decoupling
BuildDontStageLists(log); BuildDontStageLists(log);
   
if (log != null) if (log != null)
{ {
log.Flush(); log.Flush();
} }
   
// Create the array of stages that will be returned // Create the array of stages that will be returned
Stage[] stages = new Stage[this.currentStage + 1]; Stage[] stages = new Stage[this.currentStage + 1];
   
int startStage = currentStage; int startStage = currentStage;
   
// Loop through the stages // Loop through the stages
while (this.currentStage >= 0) while (this.currentStage >= 0)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Simulating stage " + this.currentStage); log.buf.AppendLine("Simulating stage " + this.currentStage);
log.Flush(); log.Flush();
this._timer.Reset(); this._timer.Reset();
this._timer.Start(); this._timer.Start();
} }
   
// Update active engines and resource drains // Update active engines and resource drains
this.UpdateResourceDrains(); this.UpdateResourceDrains();
   
// Update the masses of the parts to correctly handle "no physics" parts // Update the masses of the parts to correctly handle "no physics" parts
this.stageStartMass = this.UpdatePartMasses(); this.stageStartMass = this.UpdatePartMasses();
   
if (log != null) if (log != null)
this.allParts[0].DumpPartToBuffer(log.buf, "", this.allParts); this.allParts[0].DumpPartToBuffer(log.buf, "", this.allParts);
   
// Create the Stage object for this stage // Create the Stage object for this stage
Stage stage = new Stage(); Stage stage = new Stage();
   
this.stageTime = 0d; this.stageTime = 0d;
this.vecStageDeltaV = Vector3.zero; this.vecStageDeltaV = Vector3.zero;
   
this.stageStartCom = this.ShipCom; this.stageStartCom = this.ShipCom;
   
this.stepStartMass = this.stageStartMass; this.stepStartMass = this.stageStartMass;
this.stepEndMass = 0; this.stepEndMass = 0;
   
this.CalculateThrustAndISP(); this.CalculateThrustAndISP();
   
// Store various things in the Stage object // Store various things in the Stage object
stage.thrust = this.totalStageThrust; stage.thrust = this.totalStageThrust;
if (log != null) log.buf.AppendLine("stage.thrust = " + stage.thrust); if (log != null) log.buf.AppendLine("stage.thrust = " + stage.thrust);
stage.thrustToWeight = this.totalStageThrust / (this.stageStartMass * this.gravity); stage.thrustToWeight = this.totalStageThrust / (this.stageStartMass * this.gravity);
stage.maxThrustToWeight = stage.thrustToWeight; stage.maxThrustToWeight = stage.thrustToWeight;
if (log != null) log.buf.AppendLine("StageMass = " + stageStartMass); if (log != null) log.buf.AppendLine("StageMass = " + stageStartMass);
if (log != null) log.buf.AppendLine("Initial maxTWR = " + stage.maxThrustToWeight); if (log != null) log.buf.AppendLine("Initial maxTWR = " + stage.maxThrustToWeight);
stage.actualThrust = this.totalStageActualThrust; stage.actualThrust = this.totalStageActualThrust;
stage.actualThrustToWeight = this.totalStageActualThrust / (this.stageStartMass * this.gravity); stage.actualThrustToWeight = this.totalStageActualThrust / (this.stageStartMass * this.gravity);
   
// calculate torque and associates // calculate torque and associates
stage.maxThrustTorque = this.totalStageThrustForce.TorqueAt(this.stageStartCom).magnitude; stage.maxThrustTorque = this.totalStageThrustForce.TorqueAt(this.stageStartCom).magnitude;
   
// torque divided by thrust. imagine that all engines are at the end of a lever that tries to turn the ship. // torque divided by thrust. imagine that all engines are at the end of a lever that tries to turn the ship.
// this numerical value, in meters, would represent the length of that lever. // this numerical value, in meters, would represent the length of that lever.
double torqueLeverArmLength = (stage.thrust <= 0) ? 0 : stage.maxThrustTorque / stage.thrust; double torqueLeverArmLength = (stage.thrust <= 0) ? 0 : stage.maxThrustTorque / stage.thrust;
   
// how far away are the engines from the CoM, actually? // how far away are the engines from the CoM, actually?
double thrustDistance = (this.stageStartCom - this.totalStageThrustForce.GetAverageForceApplicationPoint()).magnitude; double thrustDistance = (this.stageStartCom - this.totalStageThrustForce.GetAverageForceApplicationPoint()).magnitude;
   
// the combination of the above two values gives an approximation of the offset angle. // the combination of the above two values gives an approximation of the offset angle.
double sinThrustOffsetAngle = 0; double sinThrustOffsetAngle = 0;
if (thrustDistance > 1e-7) { if (thrustDistance > 1e-7) {
sinThrustOffsetAngle = torqueLeverArmLength / thrustDistance; sinThrustOffsetAngle = torqueLeverArmLength / thrustDistance;
if (sinThrustOffsetAngle > 1) { if (sinThrustOffsetAngle > 1) {
sinThrustOffsetAngle = 1; sinThrustOffsetAngle = 1;
} }
} }
   
stage.thrustOffsetAngle = Math.Asin(sinThrustOffsetAngle) * 180 / Math.PI; stage.thrustOffsetAngle = Math.Asin(sinThrustOffsetAngle) * 180 / Math.PI;
   
// Calculate the total cost of the vessel at this point // Calculate the total cost of the vessel at this point
stage.totalCost = 0d; stage.totalCost = 0d;
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
if (this.currentStage > allParts[i].decoupledInStage) if (this.currentStage > allParts[i].decoupledInStage)
stage.totalCost += allParts[i].GetCost(currentStage); stage.totalCost += allParts[i].GetCost(currentStage);
} }
   
// The total mass is simply the mass at the start of the stage // The total mass is simply the mass at the start of the stage
stage.totalMass = this.stageStartMass; stage.totalMass = this.stageStartMass;
   
// If we have done a previous stage // If we have done a previous stage
if (currentStage < startStage) if (currentStage < startStage)
{ {
// Calculate what the previous stage's mass and cost were by subtraction // Calculate what the previous stage's mass and cost were by subtraction
Stage prev = stages[currentStage + 1]; Stage prev = stages[currentStage + 1];
prev.cost = prev.totalCost - stage.totalCost; prev.cost = prev.totalCost - stage.totalCost;
prev.mass = prev.totalMass - stage.totalMass; prev.mass = prev.totalMass - stage.totalMass;
} }
   
// The above code will never run for the last stage so set those directly // The above code will never run for the last stage so set those directly
if (currentStage == 0) if (currentStage == 0)
{ {
stage.cost = stage.totalCost; stage.cost = stage.totalCost;
stage.mass = stage.totalMass; stage.mass = stage.totalMass;
} }
   
this.dontStageParts = dontStagePartsLists[this.currentStage]; this.dontStageParts = dontStagePartsLists[this.currentStage];
   
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Stage setup took " + this._timer.ElapsedMilliseconds + "ms"); log.buf.AppendLine("Stage setup took " + this._timer.ElapsedMilliseconds + "ms");
   
if (this.dontStageParts.Count > 0) if (this.dontStageParts.Count > 0)
{ {
log.buf.AppendLine("Parts preventing staging:"); log.buf.AppendLine("Parts preventing staging:");
for (int i = 0; i < this.dontStageParts.Count; i++) for (int i = 0; i < this.dontStageParts.Count; i++)
{ {
PartSim partSim = this.dontStageParts[i]; PartSim partSim = this.dontStageParts[i];
partSim.DumpPartToBuffer(log.buf, ""); partSim.DumpPartToBuffer(log.buf, "");
} }
} }
else else
{ {
log.buf.AppendLine("No parts preventing staging"); log.buf.AppendLine("No parts preventing staging");
} }
   
log.Flush(); log.Flush();
} }
   
   
// Now we will loop until we are allowed to stage // Now we will loop until we are allowed to stage
int loopCounter = 0; int loopCounter = 0;
while (!this.AllowedToStage()) while (!this.AllowedToStage())
{ {
loopCounter++; loopCounter++;
//MonoBehaviour.print("loop = " + loopCounter); //MonoBehaviour.print("loop = " + loopCounter);
// Calculate how long each draining tank will take to drain and run for the minimum time // Calculate how long each draining tank will take to drain and run for the minimum time
double resourceDrainTime = double.MaxValue; double resourceDrainTime = double.MaxValue;
PartSim partMinDrain = null; PartSim partMinDrain = null;
foreach (PartSim partSim in this.drainingParts) foreach (PartSim partSim in this.drainingParts)
{ {
double time = partSim.TimeToDrainResource(); double time = partSim.TimeToDrainResource();
if (time < resourceDrainTime) if (time < resourceDrainTime)
{ {
resourceDrainTime = time; resourceDrainTime = time;
partMinDrain = partSim; partMinDrain = partSim;
} }
} }
   
if (log != null) if (log != null)
{ {
MonoBehaviour.print("Drain time = " + resourceDrainTime + " (" + partMinDrain.name + ":" + partMinDrain.partId + ")"); MonoBehaviour.print("Drain time = " + resourceDrainTime + " (" + partMinDrain.name + ":" + partMinDrain.partId + ")");
} }
foreach (PartSim partSim in this.drainingParts) foreach (PartSim partSim in this.drainingParts)
{ {
partSim.DrainResources(resourceDrainTime); partSim.DrainResources(resourceDrainTime);
} }
   
// Get the mass after draining // Get the mass after draining
this.stepEndMass = this.ShipMass; this.stepEndMass = this.ShipMass;
this.stageTime += resourceDrainTime; this.stageTime += resourceDrainTime;
   
double stepEndTWR = this.totalStageThrust / (this.stepEndMass * this.gravity); double stepEndTWR = this.totalStageThrust / (this.stepEndMass * this.gravity);
//MonoBehaviour.print("After drain mass = " + stepEndMass); //MonoBehaviour.print("After drain mass = " + stepEndMass);
//MonoBehaviour.print("currentThrust = " + totalStageThrust); //MonoBehaviour.print("currentThrust = " + totalStageThrust);
//MonoBehaviour.print("currentTWR = " + stepEndTWR); //MonoBehaviour.print("currentTWR = " + stepEndTWR);
if (stepEndTWR > stage.maxThrustToWeight) if (stepEndTWR > stage.maxThrustToWeight)
{ {
stage.maxThrustToWeight = stepEndTWR; stage.maxThrustToWeight = stepEndTWR;
} }
   
//MonoBehaviour.print("newMaxTWR = " + stage.maxThrustToWeight); //MonoBehaviour.print("newMaxTWR = " + stage.maxThrustToWeight);
   
// If we have drained anything and the masses make sense then add this step's deltaV to the stage total // If we have drained anything and the masses make sense then add this step's deltaV to the stage total
if (resourceDrainTime > 0d && this.stepStartMass > this.stepEndMass && this.stepStartMass > 0d && this.stepEndMass > 0d) if (resourceDrainTime > 0d && this.stepStartMass > this.stepEndMass && this.stepStartMass > 0d && this.stepEndMass > 0d)
{ {
this.vecStageDeltaV += this.vecThrust * (float)((this.currentisp * Units.GRAVITY * Math.Log(this.stepStartMass / this.stepEndMass)) / this.simpleTotalThrust); this.vecStageDeltaV += this.vecThrust * (float)((this.currentisp * Units.GRAVITY * Math.Log(this.stepStartMass / this.stepEndMass)) / this.simpleTotalThrust);
} }
   
// Update the active engines and resource drains for the next step // Update the active engines and resource drains for the next step
this.UpdateResourceDrains(); this.UpdateResourceDrains();
   
// Recalculate the current thrust and isp for the next step // Recalculate the current thrust and isp for the next step
this.CalculateThrustAndISP(); this.CalculateThrustAndISP();
// Check if we actually changed anything // Check if we actually changed anything
if (this.stepStartMass == this.stepEndMass) if (this.stepStartMass == this.stepEndMass)
{ {
//MonoBehaviour.print("No change in mass"); //MonoBehaviour.print("No change in mass");
break; break;
} }
   
// Check to stop rampant looping // Check to stop rampant looping
if (loopCounter == 1000) if (loopCounter == 1000)
{ {
MonoBehaviour.print("exceeded loop count"); MonoBehaviour.print("exceeded loop count");
MonoBehaviour.print("stageStartMass = " + this.stageStartMass); MonoBehaviour.print("stageStartMass = " + this.stageStartMass);
MonoBehaviour.print("stepStartMass = " + this.stepStartMass); MonoBehaviour.print("stepStartMass = " + this.stepStartMass);
MonoBehaviour.print("StepEndMass = " + this.stepEndMass); MonoBehaviour.print("StepEndMass = " + this.stepEndMass);
Logger.Log("exceeded loop count"); Logger.Log("exceeded loop count");
Logger.Log("stageStartMass = " + this.stageStartMass); Logger.Log("stageStartMass = " + this.stageStartMass);
Logger.Log("stepStartMass = " + this.stepStartMass); Logger.Log("stepStartMass = " + this.stepStartMass);
Logger.Log("StepEndMass = " + this.stepEndMass); Logger.Log("StepEndMass = " + this.stepEndMass);
break; break;
} }
   
// The next step starts at the mass this one ended at // The next step starts at the mass this one ended at
this.stepStartMass = this.stepEndMass; this.stepStartMass = this.stepEndMass;
} }
   
   
// Store more values in the Stage object and stick it in the array // Store more values in the Stage object and stick it in the array
   
// Store the magnitude of the deltaV vector // Store the magnitude of the deltaV vector
stage.deltaV = this.vecStageDeltaV.magnitude; stage.deltaV = this.vecStageDeltaV.magnitude;
stage.resourceMass = this.stageStartMass - this.stepEndMass; stage.resourceMass = this.stageStartMass - this.stepEndMass;
   
// Recalculate effective stage isp from the stage deltaV (flip the standard deltaV calculation around) // Recalculate effective stage isp from the stage deltaV (flip the standard deltaV calculation around)
// Note: If the mass doesn't change then this is a divide by zero // Note: If the mass doesn't change then this is a divide by zero
if (this.stageStartMass != this.stepStartMass) if (this.stageStartMass != this.stepStartMass)
{ {
stage.isp = stage.deltaV / (Units.GRAVITY * Math.Log(this.stageStartMass / this.stepStartMass)); stage.isp = stage.deltaV / (Units.GRAVITY * Math.Log(this.stageStartMass / this.stepStartMass));
} }
else else
{ {
stage.isp = 0; stage.isp = 0;
} }
   
// Zero stage time if more than a day (this should be moved into the window code) // Zero stage time if more than a day (this should be moved into the window code)
stage.time = (this.stageTime < SECONDS_PER_DAY) ? this.stageTime : 0d; stage.time = (this.stageTime < SECONDS_PER_DAY) ? this.stageTime : 0d;
stage.number = this.doingCurrent ? -1 : this.currentStage; // Set the stage number to -1 if doing current engines stage.number = this.doingCurrent ? -1 : this.currentStage; // Set the stage number to -1 if doing current engines
stage.totalPartCount = this.allParts.Count; stage.totalPartCount = this.allParts.Count;
stage.maxMach = maxMach; stage.maxMach = maxMach;
stages[this.currentStage] = stage; stages[this.currentStage] = stage;
   
// Now activate the next stage // Now activate the next stage
this.currentStage--; this.currentStage--;
this.doingCurrent = false; this.doingCurrent = false;
   
if (log != null) if (log != null)
{ {
// Log how long the stage took // Log how long the stage took
this._timer.Stop(); this._timer.Stop();
MonoBehaviour.print("Simulating stage took " + this._timer.ElapsedMilliseconds + "ms"); MonoBehaviour.print("Simulating stage took " + this._timer.ElapsedMilliseconds + "ms");
stage.Dump(); stage.Dump();
this._timer.Reset(); this._timer.Reset();
this._timer.Start(); this._timer.Start();
} }
   
// Activate the next stage // Activate the next stage
this.ActivateStage(); this.ActivateStage();
   
if (log != null) if (log != null)
{ {
// Log how long it took to activate // Log how long it took to activate
this._timer.Stop(); this._timer.Stop();
MonoBehaviour.print("ActivateStage took " + this._timer.ElapsedMilliseconds + "ms"); MonoBehaviour.print("ActivateStage took " + this._timer.ElapsedMilliseconds + "ms");
} }
} }
   
// Now we add up the various total fields in the stages // Now we add up the various total fields in the stages
for (int i = 0; i < stages.Length; i++) for (int i = 0; i < stages.Length; i++)
{ {
// For each stage we total up the cost, mass, deltaV and time for this stage and all the stages above // For each stage we total up the cost, mass, deltaV and time for this stage and all the stages above
for (int j = i; j >= 0; j--) for (int j = i; j >= 0; j--)
{ {
stages[i].totalDeltaV += stages[j].deltaV; stages[i].totalDeltaV += stages[j].deltaV;
stages[i].totalTime += stages[j].time; stages[i].totalTime += stages[j].time;
stages[i].partCount = i > 0 ? stages[i].totalPartCount - stages[i - 1].totalPartCount : stages[i].totalPartCount; stages[i].partCount = i > 0 ? stages[i].totalPartCount - stages[i - 1].totalPartCount : stages[i].totalPartCount;
} }
// We also total up the deltaV for stage and all stages below // We also total up the deltaV for stage and all stages below
for (int j = i; j < stages.Length; j++) for (int j = i; j < stages.Length; j++)
{ {
stages[i].inverseTotalDeltaV += stages[j].deltaV; stages[i].inverseTotalDeltaV += stages[j].deltaV;
} }
   
// Zero the total time if the value will be huge (24 hours?) to avoid the display going weird // Zero the total time if the value will be huge (24 hours?) to avoid the display going weird
// (this should be moved into the window code) // (this should be moved into the window code)
if (stages[i].totalTime > SECONDS_PER_DAY) if (stages[i].totalTime > SECONDS_PER_DAY)
{ {
stages[i].totalTime = 0d; stages[i].totalTime = 0d;
} }
} }
   
if (log != null) if (log != null)
{ {
this._timer.Stop(); this._timer.Stop();
MonoBehaviour.print("RunSimulation: " + this._timer.ElapsedMilliseconds + "ms"); MonoBehaviour.print("RunSimulation: " + this._timer.ElapsedMilliseconds + "ms");
} }
FreePooledObject(); FreePooledObject();
return stages; return stages;
} }
   
public double UpdatePartMasses() public double UpdatePartMasses()
{ {
for (int i = 0; i < this.allParts.Count; i++) for (int i = 0; i < this.allParts.Count; i++)
{ {
this.allParts[i].baseMass = this.allParts[i].realMass; this.allParts[i].baseMass = this.allParts[i].realMass;
this.allParts[i].baseMassForCoM = this.allParts[i].realMass; this.allParts[i].baseMassForCoM = this.allParts[i].realMass;
} }
   
for (int i = 0; i < this.allParts.Count; i++) for (int i = 0; i < this.allParts.Count; i++)
{ {
PartSim part = this.allParts[i]; PartSim part = this.allParts[i];
// If the part has a parent  
if (part.parent != null) // Check if part should pass it's mass onto its parent.
{ if (part.isNoPhysics && part.parent != null)
if (part.isNoPhysics) {
{ PartSim partParent = part.parent;
if (part.parent.isNoPhysics && part.parent.parent != null)  
{ // Loop through all parents until a physically significant parent is found.
part.baseMass = 0d; while (partParent != null)
part.baseMassForCoM = 0d; {
} // Check if parent is physically significant.
else if (partParent.isNoPhysics == false)
{ {
part.parent.baseMassForCoM += part.baseMassForCoM; // Apply the mass to the parent and remove it from the originating part.
part.baseMassForCoM = 0d; partParent.baseMassForCoM += part.baseMassForCoM;
} part.baseMassForCoM = 0.0;
   
  // Break out of the recursive loop.
  break;
  }
   
  // Recursively loop through the parent parts.
  partParent = partParent.parent;
} }
} }
} }
   
double totalMass = 0d; double totalMass = 0d;
for (int i = 0; i < this.allParts.Count; i++) for (int i = 0; i < this.allParts.Count; i++)
{ {
totalMass += this.allParts[i].startMass = this.allParts[i].GetMass(currentStage); totalMass += this.allParts[i].startMass = this.allParts[i].GetMass(currentStage);
} }
   
return totalMass; return totalMass;
} }
   
// Make sure we free them all, even if they should all be free already at this point // Make sure we free them all, even if they should all be free already at this point
public void FreePooledObject() public void FreePooledObject()
{ {
//MonoBehaviour.print("FreePooledObject pool size before = " + PartSim.pool.Count() + " for " + allParts.Count + " parts"); //MonoBehaviour.print("FreePooledObject pool size before = " + PartSim.pool.Count() + " for " + allParts.Count + " parts");
foreach (PartSim part in allParts) foreach (PartSim part in allParts)
{ {
part.Release(); part.Release();
} }
//MonoBehaviour.print("FreePooledObject pool size after = " + PartSim.pool.Count()); //MonoBehaviour.print("FreePooledObject pool size after = " + PartSim.pool.Count());
   
//MonoBehaviour.print("FreePooledObject pool size before = " + EngineSim.pool.Count() + " for " + allEngines.Count + " engines"); //MonoBehaviour.print("FreePooledObject pool size before = " + EngineSim.pool.Count() + " for " + allEngines.Count + " engines");
foreach (EngineSim engine in allEngines) foreach (EngineSim engine in allEngines)
{ {
engine.Release(); engine.Release();
} }
//MonoBehaviour.print("FreePooledObject pool size after = " + EngineSim.pool.Count()); //MonoBehaviour.print("FreePooledObject pool size after = " + EngineSim.pool.Count());
} }
   
private void BuildDontStageLists(LogMsg log) private void BuildDontStageLists(LogMsg log)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("Creating list with capacity of " + (this.currentStage + 1)); log.buf.AppendLine("Creating list with capacity of " + (this.currentStage + 1));
} }
   
dontStagePartsLists.Clear(); dontStagePartsLists.Clear();
for (int i = 0; i <= this.currentStage; i++) for (int i = 0; i <= this.currentStage; i++)
{ {
if (i < dontStagePartsLists.Count) if (i < dontStagePartsLists.Count)
{ {
dontStagePartsLists[i].Clear(); dontStagePartsLists[i].Clear();
} }
else else
{ {
dontStagePartsLists.Add(new List<PartSim>()); dontStagePartsLists.Add(new List<PartSim>());
} }
} }
   
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[i]; PartSim partSim = allParts[i];
   
if (partSim.isEngine || !partSim.Resources.Empty) if (partSim.isEngine || !partSim.Resources.Empty)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine( log.buf.AppendLine(
partSim.name + ":" + partSim.partId + " is engine or tank, decoupled = " + partSim.decoupledInStage); partSim.name + ":" + partSim.partId + " is engine or tank, decoupled = " + partSim.decoupledInStage);
} }
   
if (partSim.decoupledInStage < -1 || partSim.decoupledInStage > this.currentStage - 1) if (partSim.decoupledInStage < -1 || partSim.decoupledInStage > this.currentStage - 1)
{ {
if (log != null) if (log != null)
{ {
log.buf.AppendLine("decoupledInStage out of range"); log.buf.AppendLine("decoupledInStage out of range");
} }
} }
else else
{ {
dontStagePartsLists[partSim.decoupledInStage + 1].Add(partSim); dontStagePartsLists[partSim.decoupledInStage + 1].Add(partSim);
} }
} }
} }
   
for (int i = 1; i <= this.lastStage; i++) for (int i = 1; i <= this.lastStage; i++)
{ {
if (dontStagePartsLists[i].Count == 0) if (dontStagePartsLists[i].Count == 0)
{ {
dontStagePartsLists[i] = dontStagePartsLists[i - 1]; dontStagePartsLists[i] = dontStagePartsLists[i - 1];
} }
} }
} }
   
// This function simply rebuilds the active engines by testing the isActive flag of all the engines // This function simply rebuilds the active engines by testing the isActive flag of all the engines
private void UpdateActiveEngines() private void UpdateActiveEngines()
{ {
this.activeEngines.Clear(); this.activeEngines.Clear();
for (int i = 0; i < allEngines.Count; ++i) for (int i = 0; i < allEngines.Count; ++i)
{ {
EngineSim engine = allEngines[i]; EngineSim engine = allEngines[i];
if (engine.isActive) if (engine.isActive)
{ {
this.activeEngines.Add(engine); this.activeEngines.Add(engine);
} }
} }
} }
   
private void CalculateThrustAndISP() private void CalculateThrustAndISP()
{ {
// Reset all the values // Reset all the values
this.vecThrust = Vector3.zero; this.vecThrust = Vector3.zero;
this.vecActualThrust = Vector3.zero; this.vecActualThrust = Vector3.zero;
this.simpleTotalThrust = 0d; this.simpleTotalThrust = 0d;
this.totalStageThrust = 0d; this.totalStageThrust = 0d;
this.totalStageActualThrust = 0d; this.totalStageActualThrust = 0d;
this.totalStageFlowRate = 0d; this.totalStageFlowRate = 0d;
this.totalStageIspFlowRate = 0d; this.totalStageIspFlowRate = 0d;
this.totalStageThrustForce.Reset(); this.totalStageThrustForce.Reset();
   
// Loop through all the active engines totalling the thrust, actual thrust and mass flow rates // Loop through all the active engines totalling the thrust, actual thrust and mass flow rates
// The thrust is totalled as vectors // The thrust is totalled as vectors
for (int i = 0; i < activeEngines.Count; ++i) for (int i = 0; i < activeEngines.Count; ++i)
{ {
EngineSim engine = activeEngines[i]; EngineSim engine = activeEngines[i];
   
this.simpleTotalThrust += engine.thrust; this.simpleTotalThrust += engine.thrust;
this.vecThrust += ((float)engine.thrust * engine.thrustVec); this.vecThrust += ((float)engine.thrust * engine.thrustVec);
this.vecActualThrust += ((float)engine.actualThrust * engine.thrustVec); this.vecActualThrust += ((float)engine.actualThrust * engine.thrustVec);
   
this.totalStageFlowRate += engine.ResourceConsumptions.Mass; this.totalStageFlowRate += engine.ResourceConsumptions.Mass;
this.totalStageIspFlowRate += engine.ResourceConsumptions.Mass * engine.isp; this.totalStageIspFlowRate += engine.ResourceConsumptions.Mass * engine.isp;
   
for (int j = 0; j < engine.appliedForces.Count; ++j) for (int j = 0; j < engine.appliedForces.Count; ++j)
{ {
this.totalStageThrustForce.AddForce(engine.appliedForces[j]); this.totalStageThrustForce.AddForce(engine.appliedForces[j]);
} }
} }
//MonoBehaviour.print("vecThrust = " + vecThrust.ToString() + " magnitude = " + vecThrust.magnitude); //MonoBehaviour.print("vecThrust = " + vecThrust.ToString() + " magnitude = " + vecThrust.magnitude);
this.totalStageThrust = this.vecThrust.magnitude; this.totalStageThrust = this.vecThrust.magnitude;
this.totalStageActualThrust = this.vecActualThrust.magnitude; this.totalStageActualThrust = this.vecActualThrust.magnitude;
   
// Calculate the effective isp at this point // Calculate the effective isp at this point
if (this.totalStageFlowRate > 0d && this.totalStageIspFlowRate > 0d) if (this.totalStageFlowRate > 0d && this.totalStageIspFlowRate > 0d)
{ {
this.currentisp = this.totalStageIspFlowRate / this.totalStageFlowRate; this.currentisp = this.totalStageIspFlowRate / this.totalStageFlowRate;
} }
else else
{ {
this.currentisp = 0; this.currentisp = 0;
} }
} }
   
// This function does all the hard work of working out which engines are burning, which tanks are being drained // This function does all the hard work of working out which engines are burning, which tanks are being drained
// and setting the drain rates // and setting the drain rates
private void UpdateResourceDrains() private void UpdateResourceDrains()
{ {
// Update the active engines // Update the active engines
this.UpdateActiveEngines(); this.UpdateActiveEngines();
   
// Empty the draining resources set // Empty the draining resources set
this.drainingResources.Clear(); this.drainingResources.Clear();
   
// Reset the resource drains of all draining parts // Reset the resource drains of all draining parts
foreach (PartSim partSim in this.drainingParts) foreach (PartSim partSim in this.drainingParts)
{ {
partSim.ResourceDrains.Reset(); partSim.ResourceDrains.Reset();
} }
   
// Empty the draining parts set // Empty the draining parts set
this.drainingParts.Clear(); this.drainingParts.Clear();
   
// Loop through all the active engine modules // Loop through all the active engine modules
for (int i = 0; i < activeEngines.Count; ++i) for (int i = 0; i < activeEngines.Count; ++i)
{ {
EngineSim engine = activeEngines[i]; EngineSim engine = activeEngines[i];
   
// Set the resource drains for this engine // Set the resource drains for this engine
if (engine.SetResourceDrains(this.allParts, this.allFuelLines, this.drainingParts)) if (engine.SetResourceDrains(this.allParts, this.allFuelLines, this.drainingParts))
{ {
// If it is active then add the consumed resource types to the set // If it is active then add the consumed resource types to the set
for (int j = 0; j < engine.ResourceConsumptions.Types.Count; ++j) for (int j = 0; j < engine.ResourceConsumptions.Types.Count; ++j)
{ {
drainingResources.Add(engine.ResourceConsumptions.Types[j]); drainingResources.Add(engine.ResourceConsumptions.Types[j]);
} }
} }
} }
   
// Update the active engines again to remove any engines that have no fuel supply // Update the active engines again to remove any engines that have no fuel supply
this.UpdateActiveEngines(); this.UpdateActiveEngines();
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
StringBuilder buffer = new StringBuilder(1024); StringBuilder buffer = new StringBuilder(1024);
buffer.AppendFormat("Active engines = {0:d}\n", this.activeEngines.Count); buffer.AppendFormat("Active engines = {0:d}\n", this.activeEngines.Count);
int i = 0; int i = 0;
for (int j = 0; j < this.activeEngines.Count; j++) for (int j = 0; j < this.activeEngines.Count; j++)
{ {
EngineSim engine = this.activeEngines[j]; EngineSim engine = this.activeEngines[j];
engine.DumpEngineToBuffer(buffer, "Engine " + (i++) + ":"); engine.DumpEngineToBuffer(buffer, "Engine " + (i++) + ":");
} }
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
} }
   
// This function works out if it is time to stage // This function works out if it is time to stage
private bool AllowedToStage() private bool AllowedToStage()
{ {
StringBuilder buffer = null; StringBuilder buffer = null;
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer = new StringBuilder(1024); buffer = new StringBuilder(1024);
buffer.AppendLine("AllowedToStage"); buffer.AppendLine("AllowedToStage");
buffer.AppendFormat("currentStage = {0:d}\n", this.currentStage); buffer.AppendFormat("currentStage = {0:d}\n", this.currentStage);
} }
   
if (this.activeEngines.Count > 0) if (this.activeEngines.Count > 0)
{ {
for (int i = 0; i < dontStageParts.Count; ++i) for (int i = 0; i < dontStageParts.Count; ++i)
{ {
PartSim partSim = dontStageParts[i]; PartSim partSim = dontStageParts[i];
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
partSim.DumpPartToBuffer(buffer, "Testing: "); partSim.DumpPartToBuffer(buffer, "Testing: ");
} }
//buffer.AppendFormat("isSepratron = {0}\n", partSim.isSepratron ? "true" : "false"); //buffer.AppendFormat("isSepratron = {0}\n", partSim.isSepratron ? "true" : "false");
   
if (!partSim.isSepratron && !partSim.EmptyOf(this.drainingResources)) if (!partSim.isSepratron && !partSim.EmptyOf(this.drainingResources))
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
partSim.DumpPartToBuffer(buffer, "Decoupled part not empty => false: "); partSim.DumpPartToBuffer(buffer, "Decoupled part not empty => false: ");
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
return false; return false;
} }
   
if (partSim.isEngine) if (partSim.isEngine)
{ {
for (int j = 0; j < activeEngines.Count; ++j) for (int j = 0; j < activeEngines.Count; ++j)
{ {
EngineSim engine = activeEngines[j]; EngineSim engine = activeEngines[j];
   
if (engine.partSim == partSim) if (engine.partSim == partSim)
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
partSim.DumpPartToBuffer(buffer, "Decoupled part is active engine => false: "); partSim.DumpPartToBuffer(buffer, "Decoupled part is active engine => false: ");
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
return false; return false;
} }
} }
} }
} }
} }
   
if (this.currentStage == 0 && this.doingCurrent) if (this.currentStage == 0 && this.doingCurrent)
{ {
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer.AppendLine("Current stage == 0 && doingCurrent => false"); buffer.AppendLine("Current stage == 0 && doingCurrent => false");
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
return false; return false;
} }
   
if (SimManager.logOutput) if (SimManager.logOutput)
{ {
buffer.AppendLine("Returning true"); buffer.AppendLine("Returning true");
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
return true; return true;
} }
   
// This function activates the next stage // This function activates the next stage
// currentStage must be updated before calling this function // currentStage must be updated before calling this function
private void ActivateStage() private void ActivateStage()
{ {
// Build a set of all the parts that will be decoupled // Build a set of all the parts that will be decoupled
decoupledParts.Clear(); decoupledParts.Clear();
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
PartSim partSim = allParts[i]; PartSim partSim = allParts[i];
   
if (partSim.decoupledInStage >= this.currentStage) if (partSim.decoupledInStage >= this.currentStage)
{ {
decoupledParts.Add(partSim); decoupledParts.Add(partSim);
} }
} }
   
foreach (PartSim partSim in decoupledParts) foreach (PartSim partSim in decoupledParts)
{ {
// Remove it from the all parts list // Remove it from the all parts list
this.allParts.Remove(partSim); this.allParts.Remove(partSim);
partSim.Release(); partSim.Release();
if (partSim.isEngine) if (partSim.isEngine)
{ {
// If it is an engine then loop through all the engine modules and remove all the ones from this engine part // If it is an engine then loop through all the engine modules and remove all the ones from this engine part
for (int i = this.allEngines.Count - 1; i >= 0; i--) for (int i = this.allEngines.Count - 1; i >= 0; i--)
{ {
EngineSim engine = this.allEngines[i]; EngineSim engine = this.allEngines[i];
if (engine.partSim == partSim) if (engine.partSim == partSim)
{ {
this.allEngines.RemoveAt(i); this.allEngines.RemoveAt(i);
engine.Release(); engine.Release();
} }
} }
} }
// If it is a fuel line then remove it from the list of all fuel lines // If it is a fuel line then remove it from the list of all fuel lines
if (partSim.isFuelLine) if (partSim.isFuelLine)
{ {
this.allFuelLines.Remove(partSim); this.allFuelLines.Remove(partSim);
} }
} }
   
// Loop through all the (remaining) parts // Loop through all the (remaining) parts
for (int i = 0; i < allParts.Count; ++i) for (int i = 0; i < allParts.Count; ++i)
{ {
// Ask the part to remove all the parts that are decoupled // Ask the part to remove all the parts that are decoupled
allParts[i].RemoveAttachedParts(decoupledParts); allParts[i].RemoveAttachedParts(decoupledParts);
} }
   
// Now we loop through all the engines and activate those that are ignited in this stage // Now we loop through all the engines and activate those that are ignited in this stage
for (int i = 0; i < allEngines.Count; ++i) for (int i = 0; i < allEngines.Count; ++i)
{ {
EngineSim engine = allEngines[i]; EngineSim engine = allEngines[i];
if (engine.partSim.inverseStage == this.currentStage) if (engine.partSim.inverseStage == this.currentStage)
{ {
engine.isActive = true; engine.isActive = true;
} }
} }
} }
   
public void Dump() public void Dump()
{ {
StringBuilder buffer = new StringBuilder(1024); StringBuilder buffer = new StringBuilder(1024);
buffer.AppendFormat("Part count = {0:d}\n", this.allParts.Count); buffer.AppendFormat("Part count = {0:d}\n", this.allParts.Count);
   
// Output a nice tree view of the rocket // Output a nice tree view of the rocket
if (this.allParts.Count > 0) if (this.allParts.Count > 0)
{ {
PartSim root = this.allParts[0]; PartSim root = this.allParts[0];
while (root.parent != null) while (root.parent != null)
{ {
root = root.parent; root = root.parent;
} }
   
if (root.hasVessel) if (root.hasVessel)
{ {
buffer.AppendFormat("vesselName = '{0}' vesselType = {1}\n", this.vesselName, SimManager.GetVesselTypeString(this.vesselType)); buffer.AppendFormat("vesselName = '{0}' vesselType = {1}\n", this.vesselName, SimManager.GetVesselTypeString(this.vesselType));
} }
   
root.DumpPartToBuffer(buffer, "", this.allParts); root.DumpPartToBuffer(buffer, "", this.allParts);
} }
   
MonoBehaviour.print(buffer); MonoBehaviour.print(buffer);
} }
} }
} }
   
 Binary files a/Output/KerbalEngineer/KerbalEngineer.dll and b/Output/KerbalEngineer/KerbalEngineer.dll differ
{ {
"NAME":"Kerbal Engineer Redux 1.0", "NAME":"Kerbal Engineer Redux",
"URL":"http://ksp-avc.cybutek.net/version.php?id=6", "URL":"http://ksp-avc.cybutek.net/version.php?id=6",
"VERSION": "VERSION":
{ {
"MAJOR":1, "MAJOR":1,
"MINOR":0, "MINOR":0,
"PATCH":17, "PATCH":18,
"BUILD":0 "BUILD":0
}, },
"KSP_VERSION": "KSP_VERSION":
{ {
"MAJOR":1, "MAJOR":1,
"MINOR":0, "MINOR":0,
"PATCH":4 "PATCH":4
} }
} }