/// <summary>
        /// Get the vessel's acceleration ability, in m/s2
        /// </summary>
        /// <param name="propellantsConsumed">All the propellants consumed, in tons/second for each one</param>
        /// <param name="totalThrust">The total thrust produced, in kilonewtons</param>
        private void GetThrustInfo(
            Tally propellantsConsumed,
            out double totalThrust)
        {
            // Add up all the thrust for all the active engines on the vessel.
            // We do this as a vector because the engines might not be parallel to each other.
            Vector3 totalThrustVector = Vector3.zero;

            totalThrust = 0.0F;
            propellantsConsumed.Zero();
            Tally availableResources = vessel.AvailableResources;
            int   engineCount        = 0;

            for (int engineIndex = 0; engineIndex < vessel.ActiveEngines.Count; ++engineIndex)
            {
                ModuleEngines engine = vessel.ActiveEngines[engineIndex];
                if (engine.thrustPercentage > 0)
                {
                    double engineKilonewtons = engine.ThrustLimit();
                    if (!CheatOptions.InfinitePropellant)
                    {
                        // Possible future consideraiton:
                        // Get the vacuum Isp from engine.atmosphereCurve.Evaluate(0), rather than ask
                        // for engine.realIsp, because there may be mods that tinker with the atmosphere
                        // curve, which changes the actual Isp that the game uses for vacuum without
                        // updating engine.realIsp.
                        double engineIsp = engine.realIsp;

                        double engineTotalFuelConsumption = engineKilonewtons / (KERBIN_GRAVITY * engineIsp); // tons/sec
                        double ratioSum  = 0.0;
                        bool   isStarved = false;
                        for (int propellantIndex = 0; propellantIndex < engine.propellants.Count; ++propellantIndex)
                        {
                            Propellant propellant = engine.propellants[propellantIndex];
                            if (!ShouldIgnore(propellant.name))
                            {
                                if (!availableResources.Has(propellant.name))
                                {
                                    isStarved = true;
                                    break;
                                }
                                ratioSum += propellant.ratio;
                            }
                        }
                        if (isStarved)
                        {
                            continue;
                        }
                        if (ratioSum > 0)
                        {
                            double ratio = 1.0 / ratioSum;
                            for (int propellantIndex = 0; propellantIndex < engine.propellants.Count; ++propellantIndex)
                            {
                                Propellant propellant = engine.propellants[propellantIndex];
                                if (!ShouldIgnore(propellant.name))
                                {
                                    double consumptionRate = ratio * propellant.ratio * engineTotalFuelConsumption; // tons/sec
                                    propellantsConsumed.Add(propellant.name, consumptionRate);
                                }
                            }
                        }
                    } // if we need to worry about fuel
                    ++engineCount;
                    totalThrustVector += engine.Forward() * (float)engineKilonewtons;
                } // if the engine is operational
            }     // for each engine module on the part
            totalThrust = totalThrustVector.magnitude;
            if (engineCount != lastEngineCount)
            {
                lastEngineCount = engineCount;
                Logging.Log(engineCount.ToString("Active engines: ##0"));
            }
        }
        public void LateUpdate()
        {
            logFuelCheatActivation();
            try
            {
                if (!BurnInfo.IsInitialized) return; // can't do anything

                string customDescription = null;
                double dVrequired = ImpactTracker.ImpactSpeed;
                int timeUntil = -1;
                if (double.IsNaN(dVrequired))
                {
                    // No impact info is available. Do we have closest-approach info?
                    dVrequired = ClosestApproachTracker.Velocity;
                    if (double.IsNaN(dVrequired))
                    {
                        // No closest-approach info available either, use the maneuver dV remaining.
                        dVrequired = BurnInfo.DvRemaining;
                        timeUntil = SecondsUntilNode();
                    }
                    else
                    {
                        // We have closest-approach info, use the description from that.
                        customDescription = ClosestApproachTracker.Description;
                        timeUntil = ClosestApproachTracker.TimeUntil;
                    }
                }
                else
                {
                    // We have impact info, use the description from that.
                    customDescription = ImpactTracker.Description;
                    // TODO: enable countdown to retro-burn, not doing it now 'coz it needs more math & logic
                    // timeUntil = ImpactTracker.TimeUntil;
                }

                // At this point, either we have a dVrequired or not. If we have one, we might
                // have a description (meaning it's one of our custom trackers from this mod)
                // or we might not (meaning "leave it alone at let the stock game decide what to say").

                if (double.IsNaN(dVrequired)) return;
                if (FlightGlobals.ActiveVessel == null) return;

                if (FlightGlobals.ActiveVessel.IsEvaKerbal())
                {
                    // it's a kerbal on EVA
                    BurnInfo.Duration = EVA_KERBAL_LABEL;
                    BurnInfo.Countdown = string.Empty;
                }
                else
                {
                    // it's a ship, not an EVA kerbal
                    vessel.Refresh();
                    propellantsConsumed = new Tally();
                    bool isInsufficientFuel;
                    double floatBurnSeconds = GetBurnTime(dVrequired, out isInsufficientFuel);
                    int burnSeconds = double.IsInfinity(floatBurnSeconds) ? -1 : (int)(0.5 + floatBurnSeconds);
                    if (burnSeconds != lastBurnTime)
                    {
                        lastBurnTime = burnSeconds;
                        if (isInsufficientFuel)
                        {
                            lastUpdateText = ESTIMATED_BURN_LABEL + TimeFormatter.Default.warn(burnSeconds);
                        }
                        else
                        {
                            lastUpdateText = ESTIMATED_BURN_LABEL + TimeFormatter.Default.format(burnSeconds);
                        }
                    }
                    BurnInfo.Duration = lastUpdateText;
                    int timeUntilBurnStart = timeUntil - burnSeconds / 2;
                    if (timeUntilBurnStart != lastTimeUntilBurnStart)
                    {
                        lastTimeUntilBurnStart = timeUntilBurnStart;
                        BurnInfo.Countdown = Countdown.ForSeconds(timeUntilBurnStart);
                    }
                }

                if (customDescription == null)
                {
                    // No custom description available, turn off the alternate display
                    BurnInfo.AlternateDisplayEnabled = false;
                }
                else
                {
                    // We have alternate info to show
                    BurnInfo.TimeUntil = customDescription;
                    BurnInfo.AlternateDisplayEnabled = true;
                }
            }
            catch (Exception e)
            {
                Logging.Exception(e);
                BurnInfo.Duration = e.GetType().Name + ": " + e.Message + " -> " + e.StackTrace;
            }
        }
        /// <summary>
        /// Get the vessel's acceleration ability, in m/s2
        /// </summary>
        /// <param name="propellantsConsumed">All the propellants consumed, in tons/second for each one</param>
        /// <param name="totalThrust">The total thrust produced, in kilonewtons</param>
        private void GetThrustInfo(
            Tally propellantsConsumed,
            out double totalThrust)
        {
            // Add up all the thrust for all the active engines on the vessel.
            // We do this as a vector because the engines might not be parallel to each other.
            Vector3 totalThrustVector = Vector3.zero;
            totalThrust = 0.0F;
            propellantsConsumed.Zero();
            Tally availableResources = vessel.AvailableResources;
            int engineCount = 0;
            for (int engineIndex = 0; engineIndex < vessel.ActiveEngines.Count; ++engineIndex)
            {
                ModuleEngines engine = vessel.ActiveEngines[engineIndex];
                if (engine.thrustPercentage > 0)
                {
                    double engineKilonewtons = engine.ThrustLimit();
                    if (!CheatOptions.InfinitePropellant)
                    {
                        // Possible future consideraiton:
                        // Get the vacuum Isp from engine.atmosphereCurve.Evaluate(0), rather than ask
                        // for engine.realIsp, because there may be mods that tinker with the atmosphere
                        // curve, which changes the actual Isp that the game uses for vacuum without
                        // updating engine.realIsp.
                        double engineIsp = engine.realIsp;

                        double engineTotalFuelConsumption = engineKilonewtons / (KERBIN_GRAVITY * engineIsp); // tons/sec
                        double ratioSum = 0.0;
                        bool isStarved = false;
                        for (int propellantIndex = 0; propellantIndex < engine.propellants.Count; ++propellantIndex)
                        {
                            Propellant propellant = engine.propellants[propellantIndex];
                            if (!ShouldIgnore(propellant.name))
                            {
                                if (!availableResources.Has(propellant.name))
                                {
                                    isStarved = true;
                                    break;
                                }
                                ratioSum += propellant.ratio;
                            }
                        }
                        if (isStarved) continue;
                        if (ratioSum > 0)
                        {
                            double ratio = 1.0 / ratioSum;
                            for (int propellantIndex = 0; propellantIndex < engine.propellants.Count; ++propellantIndex)
                            {
                                Propellant propellant = engine.propellants[propellantIndex];
                                if (!ShouldIgnore(propellant.name))
                                {
                                    double consumptionRate = ratio * propellant.ratio * engineTotalFuelConsumption; // tons/sec
                                    propellantsConsumed.Add(propellant.name, consumptionRate);
                                }
                            }
                        }
                    } // if we need to worry about fuel
                    ++engineCount;
                    totalThrustVector += engine.Forward() * (float)engineKilonewtons;
                } // if the engine is operational
            } // for each engine module on the part
            totalThrust = totalThrustVector.magnitude;
            if (engineCount != lastEngineCount)
            {
                lastEngineCount = engineCount;
                Logging.Log(engineCount.ToString("Active engines: ##0"));
            }
        }
 /// <summary>
 /// Calculate how long we can burn at full throttle until something important runs out.
 /// </summary>
 /// <param name="vessel"></param>
 /// <param name="propellantsConsumed"></param>
 /// <param name="propellantsAvailable"></param>
 /// <param name="maxBurnTime"></param>
 private static double CalculateMaxBurnTime(ShipState vessel, Tally propellantsConsumed)
 {
     double maxBurnTime = double.PositiveInfinity;
     Tally availableResources = vessel.AvailableResources;
     foreach (string resourceName in propellantsConsumed.Keys)
     {
         if (ShouldIgnore(resourceName))
         {
             // ignore this for burn time, it's replenishable
             continue;
         }
         if (!availableResources.Has(resourceName))
         {
             // we're all out!
             return 0.0;
         }
         double availableAmount = availableResources[resourceName];
         double rate = propellantsConsumed[resourceName];
         double burnTime = availableAmount / rate;
         if (burnTime < maxBurnTime) maxBurnTime = burnTime;
     }
     return maxBurnTime;
 }
Beispiel #5
0
        public void LateUpdate()
        {
            logFuelCheatActivation();
            try
            {
                BetterBurnTimeData api = BetterBurnTimeData.Current;
                if (api != null)
                {
                    api.Reset();
                }
                if (!BurnInfo.IsInitialized)
                {
                    return;                          // can't do anything
                }
                BetterBurnTimeData.BurnType burnType = BetterBurnTimeData.BurnType.None;
                string customDescription             = null;
                double dVrequired             = ImpactTracker.ImpactSpeed;
                double timeUntil              = double.NaN;
                bool   shouldDisplayCountdown = false;
                if (double.IsNaN(dVrequired))
                {
                    // No impact info is available. Do we have closest-approach info?
                    dVrequired = ClosestApproachTracker.Velocity;
                    if (double.IsNaN(dVrequired))
                    {
                        // No closest-approach info is available either.  At this point, we've exhausted all
                        // of our special-tracking-burn-information options (impact, closest approach), so
                        // we won't show any burn information or countdown.  (Before KSP 1.5, we would have
                        // checked for a maneuver node, and if present, we would have shown a countdown
                        // and our revised burn time estimate.  KSP 1.5, however, introduced drastically improved
                        // burn-time indication in stock, so there's no point in trying to compete with that--
                        // it's good enough, so leave it alone.
                        shouldDisplayCountdown = false;

                        // Do we have a maneuver node?
                        dVrequired = BurnInfo.DvRemaining;
                        if (double.IsNaN(dVrequired))
                        {
                            // No, there's no maneuver node.
                            burnType = BetterBurnTimeData.BurnType.None;
                            // If we've got an upcoming atmosphere transition, include that info.
                            customDescription = AtmosphereTracker.Description;
                            if (customDescription == null)
                            {
                                customDescription = GeosyncTracker.Description;
                                timeUntil         = 0;
                            }
                            else
                            {
                                timeUntil = AtmosphereTracker.TimeUntil;
                            }
                        }
                        else
                        {
                            // Yep, there's a maneuver node.
                            timeUntil = SecondsUntilNode();
                            burnType  = BetterBurnTimeData.BurnType.Maneuver;
                        }
                    }
                    else
                    {
                        // We have closest-approach info, use the description from that.
                        customDescription      = ClosestApproachTracker.Description;
                        timeUntil              = ClosestApproachTracker.TimeUntil;
                        shouldDisplayCountdown = true;
                        burnType = BetterBurnTimeData.BurnType.Rendezvous;
                    }
                }
                else
                {
                    // We have impact info, use the description from that.
                    customDescription = ImpactTracker.Description;
                    timeUntil         = ImpactTracker.TimeUntil;
                    // TODO: enable countdown to retro-burn, not doing it now 'coz it needs more math & logic
                    // shouldDisplayCountdown = true
                    burnType = BetterBurnTimeData.BurnType.Impact;
                }

                if (FlightGlobals.ActiveVessel == null)
                {
                    BurnInfo.Countdown = string.Empty;
                    if (customDescription == null)
                    {
                        BurnInfo.AlternateDisplayEnabled = false;
                    }
                    return;
                }

                if (double.IsNaN(dVrequired) && !double.IsNaN(timeUntil))
                {
                    // Special case of knowing a time until an event, but there's no dV associated
                    // with it and therefore no burn display.
                    BurnInfo.Duration  = string.Empty;
                    BurnInfo.TimeUntil = customDescription;
                    BurnInfo.AlternateDisplayEnabled = true;
                    BurnInfo.Countdown = string.Empty;
                    return;
                }

                // At this point, either we have a dVrequired or not. If we have one, we might
                // have a description (meaning it's one of our custom trackers from this mod)
                // or we might not (meaning "leave it alone at let the stock game decide what to say").

                if ((burnType == BetterBurnTimeData.BurnType.Maneuver) ||
                    double.IsNaN(dVrequired) ||
                    (FlightGlobals.ActiveVessel == null))
                {
                    BurnInfo.Countdown = string.Empty;
                    if (customDescription == null)
                    {
                        BurnInfo.AlternateDisplayEnabled = false;
                    }
                    return;
                }

                if (FlightGlobals.ActiveVessel.IsEvaKerbal())
                {
                    // it's a kerbal on EVA
                    BurnInfo.Duration  = EVA_KERBAL_LABEL;
                    BurnInfo.Countdown = string.Empty;
                }
                else
                {
                    // it's a ship, not an EVA kerbal
                    vessel.Refresh();
                    propellantsConsumed = new Tally();
                    bool   isInsufficientFuel;
                    double floatBurnSeconds = GetBurnTime(dVrequired, out isInsufficientFuel);
                    int    burnSeconds      = double.IsInfinity(floatBurnSeconds) ? -1 : (int)(0.5 + floatBurnSeconds);
                    if (burnSeconds != lastBurnTime)
                    {
                        lastBurnTime = burnSeconds;
                        if (isInsufficientFuel)
                        {
                            lastUpdateText = ESTIMATED_BURN_LABEL + TimeFormatter.Default.warn(burnSeconds);
                        }
                        else
                        {
                            lastUpdateText = ESTIMATED_BURN_LABEL + TimeFormatter.Default.format(burnSeconds);
                        }
                    }
                    BurnInfo.Duration = lastUpdateText;
                    int intTimeUntil       = (double.IsNaN(timeUntil) || !shouldDisplayCountdown) ? -1 : (int)timeUntil;
                    int timeUntilBurnStart = intTimeUntil - burnSeconds / 2;
                    if (timeUntilBurnStart != lastTimeUntilBurnStart)
                    {
                        lastTimeUntilBurnStart = timeUntilBurnStart;
                        BurnInfo.Countdown     = Countdown.ForSeconds(timeUntilBurnStart);
                    }
                    if (api != null)
                    {
                        api.burnType           = burnType;
                        api.burnTime           = floatBurnSeconds;
                        api.dV                 = dVrequired;
                        api.timeUntil          = timeUntil;
                        api.isInsufficientFuel = isInsufficientFuel;
                    }
                }

                if (customDescription == null)
                {
                    // No custom description available, turn off the alternate display
                    BurnInfo.AlternateDisplayEnabled = false;
                }
                else
                {
                    // We have alternate info to show
                    BurnInfo.TimeUntil = customDescription;
                    BurnInfo.AlternateDisplayEnabled = true;
                }
            }
            catch (Exception e)
            {
                Logging.Exception(e);
                BurnInfo.Duration = e.GetType().Name + ": " + e.Message + " -> " + e.StackTrace;
            }
        }
        /// <summary>
        /// Refresh cached information.
        /// </summary>
        public void Refresh()
        {
            if (!NeedUpdate())
            {
                return;
            }
            lastUpdateTime = DateTime.Now;
            Vessel vessel = FlightGlobals.ActiveVessel;

            if (vessel == null)
            {
                return;
            }
            vesselId        = vessel.id;
            vesselPartCount = vessel.parts.Count;
            totalMass       = vessel.GetTotalMass();
            activeEngines.Clear();
            for (int engineIndex = 0; engineIndex < Propulsion.Engines.Count; ++engineIndex)
            {
                Part part = Propulsion.Engines[engineIndex];
                for (int moduleIndex = 0; moduleIndex < part.Modules.Count; ++moduleIndex)
                {
                    ModuleEngines engine = part.Modules[moduleIndex] as ModuleEngines;
                    if (engine == null)
                    {
                        continue;
                    }
                    if (!engine.isOperational)
                    {
                        continue;
                    }
                    if (!CheatOptions.InfinitePropellant)
                    {
                        bool isDeprived = false;
                        for (int propellantIndex = 0; propellantIndex < engine.propellants.Count; ++propellantIndex)
                        {
                            Propellant propellant = engine.propellants[propellantIndex];
                            if (propellant.isDeprived && !propellant.ignoreForIsp)
                            {
                                isDeprived = true; // out of fuel!
                                break;
                            }
                        }
                        if (isDeprived)
                        {
                            continue;             // skip this engine
                        }
                    }
                    activeEngines.Add(engine);
                }
            }

            availableResources = new Tally();
            for (int tankIndex = 0; tankIndex < Propulsion.Tanks.Count; ++tankIndex)
            {
                Part part = Propulsion.Tanks[tankIndex];
                for (int resourceIndex = 0; resourceIndex < part.Resources.Count; ++resourceIndex)
                {
                    PartResource resource = part.Resources[resourceIndex];
                    if (resource.flowState)
                    {
                        availableResources.Add(resource.resourceName, resource.amount * resource.info.density);
                    }
                }
            }
        }