/// <summary>
        /// Propagates failure through surrounding parts.
        /// </summary>
        /// <param name="part">The parent part.</param>
        /// <param name="failureChance">Chance of failure for the next part.</param>
        private void PropagateFailure(Part part, double failureChance)
        {
            // The list of potential parts that may be doomed.
            List <Part> potentialParts = new List <Part>();

            // Calculate the next propagation's failure chance.
            double nextFailureChance = (KLFSettings.Instance.PropagationChanceDecreases) ? failureChance * KLFSettings.Instance.FailurePropagateProbability : failureChance;

            // Parent
            if (part.parent && !doomedParts.Contains(part.parent))
            {
                potentialParts.Add(part.parent);
            }

            // Children
            foreach (Part childPart in part.children)
            {
                if (!doomedParts.Contains(childPart))
                {
                    potentialParts.Add(childPart);
                }
            }

            // For each potential part, see if it fails and then propagate it.
            foreach (Part potentialPart in potentialParts)
            {
                double thisFailureChance = (KLFUtils.PartIsExplosiveFuelTank(potentialPart)) ? 1 : failureChance;
                if (!doomedParts.Contains(potentialPart) && KLFUtils.RNG.NextDouble() < thisFailureChance)
                {
                    doomedParts.Add(potentialPart);
                    PropagateFailure(potentialPart, nextFailureChance);
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Constructor for Failure object.
        /// </summary>
        public Failure()
        {
            Instance = this;
            // Gather info about current active vessel.
            activeVessel     = FlightGlobals.ActiveVessel;
            activeVesselCost = CalcVesselCosts(activeVessel);
            //scienceAfterFailure = CalcScienceReward(activeVesselCost, activeVessel);

            currentCelestialBody = activeVessel.mainBody;

            // This plugin is only targeted at atmospheric failures.
            if (currentCelestialBody.atmosphere)
            {
                altitudeFailureOccurs = KLFUtils.RNG.Next(0, (int)(currentCelestialBody.atmosphereDepth * KLFSettings.Instance.MaxFailureAltitudePercentage));
#if DEBUG
                KLFUtils.LogDebugMessage("Failure will occur at an altitude of " + altitudeFailureOccurs);
                Log.Info("currentCelestialBody.atmosphereDepth: " + currentCelestialBody.atmosphereDepth.ToString());
                Log.Info("KLFSettings.Instance.MaxFailureAltitudePercentage: " + KLFSettings.Instance.MaxFailureAltitudePercentage.ToString());
#endif
            }
            else
            {
                altitudeFailureOccurs = 0;
            }
            //alarm = new KerbalLaunchFailure.Alarm();
        }
        /// <summary>
        /// Causes the starting part's failure.
        /// </summary>
        private void CauseStartingPartFailure()
        {
            // If a valid game tick.
            if (ticksSinceFailureStart % ticksBetweenPartExplosions == 0)
            {
                // Need to start overloading the thrust and increase the part's temperature.
                thrustOverload += (int)(startingPartEngineModule.maxThrust / 30.0);
                startingPartEngineModule.rigidbody.AddRelativeForce(Vector3.forward * thrustOverload);
                startingPart.temperature += startingPart.maxTemp / 20.0;
            }

            // When the part explodes to overheating.
            if (startingPart.temperature >= startingPart.maxTemp)
            {
                // Log flight data.
                KLFUtils.LogFlightData(activeVessel, "Random failure of " + startingPart.partInfo.title + ".");

                // If the auto abort sequence is on and this is the starting part, trigger the Abort action group.
                if (KLFSettings.Instance.AutoAbort)
                {
                    activeVessel.ActionGroups.SetGroup(KSPActionGroup.Abort, true);
                }

                // Gather the doomed parts at the time of failure.
                PrepareDoomedParts();

                // Nullify the starting part.
                startingPart = null;
            }
        }
Beispiel #4
0
 public void AddAndReportScience()
 {
     if (scienceAfterFailure > 0)
     {
         KLFUtils.LogFlightData(activeVessel, scienceAfterFailure.ToString() + "  Science gained due to lessons learned.");
         ResearchAndDevelopment.Instance.AddScience(scienceAfterFailure, TransactionReasons.VesselLoss);
     }
 }
Beispiel #5
0
        /// <summary>
        /// Determines if a failure will occur or not.
        /// </summary>
        /// <returns>True if yes, false if no.</returns>
        public static bool Occurs()
        {
            double r = KLFUtils.RNG.NextDouble();

            if (KLFUtils.ExperimentalPartsPresentAndActive())
            {
                Log.Info("Experimental Parts present");
                experimentalPartFailure = r < KLFSettings.Instance.ExpPartFailureProbability;
            }
            partFailure = KLFUtils.RNG.NextDouble() < KLFSettings.Instance.InitialFailureProbability;

            Log.Info("experimentalPartFailure: " + experimentalPartFailure.ToString() + "   partFailure: " + partFailure.ToString());

            return(experimentalPartFailure || partFailure);
        }
Beispiel #6
0
        /// <summary>
        /// Explodes the next doomed part.
        /// </summary>
        private void ExplodeNextDoomedPart()
        {
            // The next part to explode.
            Part nextDoomedPart = GetNextDoomedPart();

            // Tick was invalid here.
            if (nextDoomedPart == null)
            {
                return;
            }

            // Log flight data.
            KLFUtils.LogFlightData(activeVessel, nextDoomedPart.partInfo.title + " disassembly due to an earlier failure.");

            // The fun stuff...
            nextDoomedPart.explode();
        }
        /// <summary>
        /// Constructor for Failure object.
        /// </summary>
        public Failure()
        {
            // Gather info about current active vessel.
            activeVessel         = FlightGlobals.ActiveVessel;
            currentCelestialBody = activeVessel.mainBody;

            // This plugin is only targeted at atmospheric failures.
            if (currentCelestialBody.atmosphere)
            {
                altitudeFailureOccurs = KLFUtils.RNG.Next(0, (int)(currentCelestialBody.atmosphereDepth * KLFSettings.Instance.MaxFailureAltitudePercentage));
#if DEBUG
                KLFUtils.LogDebugMessage("Failure will occur at an altitude of " + altitudeFailureOccurs);
#endif
            }
            else
            {
                altitudeFailureOccurs = 0;
            }
        }
        /// <summary>
        /// Determines the starting part in the failure.
        /// </summary>
        private void PrepareStartingPart()
        {
            // Find engines
            List <Part> activeEngineParts = activeVessel.GetActiveParts().Where(o => KLFUtils.PartIsActiveEngine(o)).ToList();

            // If there are no active engines, skip this attempt.
            if (activeEngineParts.Count == 0)
            {
                return;
            }

            // Determine the starting part.
            int startingPartIndex = KLFUtils.RNG.Next(0, activeEngineParts.Count);

            startingPart = activeEngineParts[startingPartIndex];

            // Get the engine module for the part.
            startingPartEngineModule = startingPart.Modules.OfType <ModuleEngines>().Single();

            // Setup tick information for the part explosion loop.
            ticksBetweenPartExplosions = (int)(KLFUtils.GameTicksPerSecond * KLFSettings.Instance.DelayBetweenPartFailures);
            ticksSinceFailureStart     = 0;
            thrustOverload             = 0;
        }
Beispiel #9
0
        /// <summary>
        /// Causes the starting part's failure.
        /// </summary>
        private bool CauseStartingPartFailure()
        {
            if (activeVessel.Parts.Where(o => o == startingPart).Count() == 0)
            {
                ScreenMessages.PostScreenMessage("Failing " + startingPart.partInfo.title + " no longer attached to vessel", 5.0f, ScreenMessageStyle.UPPER_CENTER);
                // Log flight data.
                KLFUtils.LogFlightData(activeVessel, "Failing " + startingPart.partInfo.title + " no longer attached to vessel.");

                // Nullify the starting part.
                startingPart = null;
                doomedParts  = null;
                return(false);
            }

            if (preFailureTime + preFailureWarningTime > Planetarium.GetUniversalTime())
            {
                // This makes sure that one message a second is put to the screen
                if (lastWarningtime < Planetarium.GetUniversalTime())
                {
                    ScreenMessages.PostScreenMessage("Imminent Failure on " + startingPart.partInfo.title, 1.0f, ScreenMessageStyle.UPPER_CENTER);
                    HighlightFailingPart(startingPart);
                    lastWarningtime = Planetarium.GetUniversalTime() + 1;
                }
                return(true);
            }

            // If a valid game tick.
            if (ticksSinceFailureStart % ticksBetweenPartFailures == 0 && (startingPartEngineModule != null && startingPartEngineModule.finalThrust > 0))
            {
                // Need to start overloading the thrust and increase the part's temperature.

                // Calculate actual throttle, the lower of either the throttle setting or the current thrust / max thrust
                float throttle = startingPartEngineModule.finalThrust / startingPartEngineModule.maxThrust;


                if (overThrust)
                {
                    thrustOverload += (int)(startingPartEngineModule.maxThrust / 30.0 * throttle);

                    startingPartEngineModule.part.Rigidbody.AddRelativeForce(Vector3.up * thrustOverload);
                    //                startingPart.temperature += startingPart.maxTemp / 20.0;
                    startingPart.temperature += startingPart.maxTemp / 20.0 * throttle;
                }
                else
                {
                    // Underthrust will change every 1/2 second
                    if (underthrustTime < Planetarium.GetUniversalTime())
                    {
                        underThrustStart = underThrustEnd;
                        underThrustEnd   = (float)((0.9 - KLFUtils.RNG.NextDouble() / 2) * 100);



                        //if (startingPartEngineModule.thrustPercentage < 0)
                        //    startingPartEngineModule.thrustPercentage = KLFUtils.RNG.NextDouble() / 4;

                        //          startingPartEngineModule.part.Rigidbody.AddRelativeForce(Vector3.up * thrustOverload);
                        underthrustTime = Planetarium.GetUniversalTime() + 0.5;
                        // startingPart.temperature += startingPart.maxTemp / 50.0 * throttle;
                    }
                    startingPartEngineModule.thrustPercentage = Mathf.Lerp(underThrustStart, underThrustEnd, 1 - (float)(Planetarium.GetUniversalTime() - underthrustTime) / 0.5f);
                }

                //                startingPart.temperature += startingPart.maxTemp / 20.0 * throttle;
            }

            if (ticksSinceFailureStart % ticksBetweenPartFailures == 0 && failureType != FailureType.engine)
            {
                ++decouplerForceCnt;

                //Log.Info("decouplerForceCnt: " + decouplerForceCnt.ToString());
                if (decouplerForceCnt > 20)
                {
                    startingPart.decouple(startingPart.breakingForce + 1);
                }
            }

            // When the part explodes to overheating.
            if (startingPart.temperature >= startingPart.maxTemp || decouplerForceCnt > 20)
            {
                // Log flight data.
                if (failureType != FailureType.engine)
                {
                    KLFUtils.LogFlightData(activeVessel, "Random structural failure of " + startingPart.partInfo.title + ".");
                }
                else
                {
                    if (overThrust)
                    {
                        KLFUtils.LogFlightData(activeVessel, "Random failure of " + startingPart.partInfo.title + ".");
                    }
                    else
                    {
                        KLFUtils.LogFlightData(activeVessel, "Underthrust of " + startingPart.partInfo.title + ".");
                    }
                }

                // Gather the doomed parts at the time of failure.
                PrepareDoomedParts();
                failureTime = Double.MaxValue;

                // Nullify the starting part.
                startingPart = null;
            }
            else
            {
                if (lastWarningtime < Planetarium.GetUniversalTime() && (startingPart.temperature >= lastTemp || decouplerForceCnt > 20))
                {
                    HighlightFailingPart(startingPart);

                    if (failureType != FailureType.engine)
                    {
                        ScreenMessages.PostScreenMessage(startingPart.partInfo.title + " structural failure imminent ", 1.0f, ScreenMessageStyle.UPPER_CENTER);
                    }
                    else
                    {
                        if (overThrust)
                        {
                            ScreenMessages.PostScreenMessage(startingPart.partInfo.title + " temperature exceeding limits: " + Math.Round(startingPart.temperature, 1).ToString(), 1.0f, ScreenMessageStyle.UPPER_CENTER);
                        }
                        else
                        {
                            ScreenMessages.PostScreenMessage(startingPart.partInfo.title + " losing thrust", 1.0f, ScreenMessageStyle.UPPER_CENTER);
                        }
                    }
                    lastWarningtime = Planetarium.GetUniversalTime() + 1;
                    lastTemp        = startingPart.temperature;
                }
                // If the auto abort sequence is on and this is the starting part, trigger the Abort action group.
                if (KLFSettings.Instance.AutoAbort)
                {
                    if (failureTime == 0)
                    {
                        failureTime = Planetarium.GetUniversalTime();
                    }
                    else
                    {
                        Debug.Log("failureTime: " + failureTime.ToString() + "   AutoAbortDelay: " + KLFSettings.Instance.AutoAbortDelay.ToString() + "   Planetarium.GetUniversalTime: " + Planetarium.GetUniversalTime().ToString());
                        if (failureTime + KLFSettings.Instance.AutoAbortDelay < Planetarium.GetUniversalTime())
                        {
                            Debug.Log("Triggering autoabort");
                            activeVessel.ActionGroups.SetGroup(KSPActionGroup.Abort, true);
                            AddAndReportScience();
                            // Following just to be sure the abort isn't triggered more than once
                            failureTime = Double.MaxValue;
                        }
                    }
                }
            }

            return(true);
        }
Beispiel #10
0
        /// <summary>
        /// Determines the starting part in the failure.
        /// </summary>
        private void PrepareStartingPart()
        {
            Log.Info("PrepareStartingPart");
            // Find engines
            bool b = !experimentalPartFailure;

            List <Part>         activeEngineParts  = activeVessel.GetActiveParts().Where(o => KLFUtils.PartIsActiveEngine(o, b)).ToList();
            List <Part>         radialDecouplers   = activeVessel.Parts.Where(o => KLFUtils.PartIsRadialDecoupler(o, b)).ToList();
            List <Part>         controlSurfaces    = activeVessel.Parts.Where(o => KLFUtils.PartIsControlSurface(o, b)).ToList();
            List <CompoundPart> strutsAndFuelLines = activeVessel.Parts.OfType <CompoundPart>().Where(o => KLFUtils.PartIsStrutOrFuelLine(o, b)).ToList();

            int cnt = activeEngineParts.Count + radialDecouplers.Count + controlSurfaces.Count + strutsAndFuelLines.Count;

            // If there are no active engines or radial decouplers, skip this attempt.
            if (cnt == 0)
            {
                return;
            }

            // Determine the starting part.
            int startingPartIndex = KLFUtils.RNG.Next(0, cnt);

            Log.Info("activeEngineParts.Count: " + activeEngineParts.Count.ToString() + "    radialDecouplers.Count: " + radialDecouplers.Count.ToString() +
                     "   controlSurfaces.Count: " + controlSurfaces.Count.ToString() + "   strutsAndFuelLines.Count: " + strutsAndFuelLines.Count);
            Log.Info("startingPartIndex: " + startingPartIndex.ToString());

            // for debugging only: startingPartIndex = activeEngineParts.Count;
            int offset = 0;

            if (startingPartIndex < activeEngineParts.Count)
            {
                startingPart = activeEngineParts[startingPartIndex];
                failureType  = FailureType.engine;

                // Get the engine module for the part.
                foreach (ModuleEngines engineModule in startingPart.Modules.OfType <ModuleEngines>().ToList())
                {
                    if (engineModule.enabled && engineModule.currentThrottle > 0)
                    {
                        startingPartEngineModule = engineModule;
                    }
                }
            }
            offset += activeEngineParts.Count;
            if (startingPartIndex >= offset && startingPartIndex < offset + radialDecouplers.Count)
            {
                startingPart = radialDecouplers[startingPartIndex - offset];

                failureType       = FailureType.radialDecoupler;
                decouplerForceCnt = 0;
            }
            offset += radialDecouplers.Count;
            if (startingPartIndex >= offset && startingPartIndex < offset + controlSurfaces.Count)
            {
                startingPart = controlSurfaces[startingPartIndex - offset];

                failureType       = FailureType.controlSurface;
                decouplerForceCnt = 0;
            }
            offset += controlSurfaces.Count;
            if (startingPartIndex >= offset && startingPartIndex < offset + strutsAndFuelLines.Count)
            {
                startingPart = strutsAndFuelLines[startingPartIndex - offset];

                failureType       = FailureType.strutOrFuelLine;
                decouplerForceCnt = 0;
            }



#if false
            if (startingPartIndex >= activeEngineParts.Count)
            {
                startingPart = radialDecouplers[startingPartIndex - activeEngineParts.Count];

                failureType       = FailureType.radialDecoupler;
                decouplerForceCnt = 0;
            }
            else
            {
                startingPart = activeEngineParts[startingPartIndex];
                failureType  = FailureType.engine;

                // Get the engine module for the part.
                startingPartEngineModule = startingPart.Modules.OfType <ModuleEngines>().Single();
            }
#endif
            // Setup tick information for the part explosion loop.
            ticksBetweenPartFailures = (int)(KLFUtils.GameTicksPerSecond * KLFSettings.Instance.DelayBetweenPartFailures);
            if (ticksBetweenPartFailures == 0)
            {
                ticksBetweenPartFailures = (int)(KLFUtils.GameTicksPerSecond / 20f + 1f);
            }
            ticksSinceFailureStart = 0;
            thrustOverload         = 0;
            underThrustStart       = 100f;
            underThrustEnd         = 100f;
            scienceAfterFailure    = CalcScienceReward(activeVesselCost, activeVessel, startingPart);
            preFailureWarningTime  = KLFSettings.Instance.PreFailureWarningTime;
            if (KLFSettings.Instance.TimeRandomness > 0)
            {
                float f = UnityEngine.Random.Range(0, KLFSettings.Instance.TimeRandomness) - KLFSettings.Instance.TimeRandomness / 2f;
                preFailureWarningTime += f;
            }
        }
        /// <summary>
        /// Determines the starting part in the failure.
        /// </summary>
        private void PrepareStartingPart()
        {
            Log.Info("PrepareStartingPart");
            // Find engines
            List <Part>         activeEngineParts  = activeVessel.GetActiveParts().Where(o => KLFUtils.PartIsActiveEngine(o)).ToList();
            List <Part>         radialDecouplers   = activeVessel.Parts.Where(o => KLFUtils.PartIsRadialDecoupler(o)).ToList();
            List <Part>         controlSurfaces    = activeVessel.Parts.Where(o => KLFUtils.PartIsControlSurface(o)).ToList();
            List <CompoundPart> strutsAndFuelLines = activeVessel.Parts.OfType <CompoundPart>().Where(o => KLFUtils.PartIsStrutOrFuelLine(o)).ToList();

            int cnt = activeEngineParts.Count + radialDecouplers.Count + controlSurfaces.Count + strutsAndFuelLines.Count;

            // If there are no active engines or radial decouplers, skip this attempt.
            if (cnt == 0)
            {
                return;
            }

            // Determine the starting part.
            int startingPartIndex = KLFUtils.RNG.Next(0, cnt);

            Log.Info("activeEngineParts.Count: " + activeEngineParts.Count.ToString() + "    radialDecouplers.Count: " + radialDecouplers.Count.ToString() +
                     "   controlSurfaces.Count: " + controlSurfaces.Count.ToString() + "   strutsAndFuelLines.Count: " + strutsAndFuelLines.Count);
            Log.Info("startingPartIndex: " + startingPartIndex.ToString());

            // for debugging only: startingPartIndex = activeEngineParts.Count;
            int offset = 0;

            if (startingPartIndex < activeEngineParts.Count)
            {
                startingPart = activeEngineParts[startingPartIndex];
                failureType  = FailureType.engine;

                // Get the engine module for the part.
                startingPartEngineModule = startingPart.Modules.OfType <ModuleEngines>().Single();
            }
            offset += activeEngineParts.Count;
            if (startingPartIndex >= offset && startingPartIndex < offset + radialDecouplers.Count)
            {
                startingPart = radialDecouplers[startingPartIndex - offset];

                failureType       = FailureType.radialDecoupler;
                decouplerForceCnt = 0;
            }
            offset += radialDecouplers.Count;
            if (startingPartIndex >= offset && startingPartIndex < offset + controlSurfaces.Count)
            {
                startingPart = controlSurfaces[startingPartIndex - offset];

                failureType       = FailureType.controlSurface;
                decouplerForceCnt = 0;
            }
            offset += controlSurfaces.Count;
            if (startingPartIndex >= offset && startingPartIndex < offset + strutsAndFuelLines.Count)
            {
                startingPart = strutsAndFuelLines[startingPartIndex - offset];

                failureType       = FailureType.strutOrFuelLine;
                decouplerForceCnt = 0;
            }



#if false
            if (startingPartIndex >= activeEngineParts.Count)
            {
                startingPart = radialDecouplers[startingPartIndex - activeEngineParts.Count];

                failureType       = FailureType.radialDecoupler;
                decouplerForceCnt = 0;
            }
            else
            {
                startingPart = activeEngineParts[startingPartIndex];
                failureType  = FailureType.engine;

                // Get the engine module for the part.
                startingPartEngineModule = startingPart.Modules.OfType <ModuleEngines>().Single();
            }
#endif
            // Setup tick information for the part explosion loop.
            ticksBetweenPartFailures = (int)(KLFUtils.GameTicksPerSecond * KLFSettings.Instance.DelayBetweenPartFailures);
            ticksSinceFailureStart   = 0;
            thrustOverload           = 0;
            underThrustStart         = 100f;
            underThrustEnd           = 100f;
        }
        public static bool ExperimentalPartsPresentAndActive()
        {
            if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER)
            {
                return(false);
            }
            List <Part>         activeEngineParts  = FlightGlobals.ActiveVessel.GetActiveParts().Where(o => KLFUtils.PartIsActiveEngine(o, false)).ToList();
            List <Part>         radialDecouplers   = FlightGlobals.ActiveVessel.Parts.Where(o => KLFUtils.PartIsRadialDecoupler(o, false)).ToList();
            List <Part>         controlSurfaces    = FlightGlobals.ActiveVessel.Parts.Where(o => KLFUtils.PartIsControlSurface(o, false)).ToList();
            List <CompoundPart> strutsAndFuelLines = FlightGlobals.ActiveVessel.Parts.OfType <CompoundPart>().Where(o => KLFUtils.PartIsStrutOrFuelLine(o, false)).ToList();

            int i = activeEngineParts.Count + radialDecouplers.Count + controlSurfaces.Count + strutsAndFuelLines.Count;

            return(i > 0);
        }