/// <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); } } }
/// <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; } }
public void AddAndReportScience() { if (scienceAfterFailure > 0) { KLFUtils.LogFlightData(activeVessel, scienceAfterFailure.ToString() + " Science gained due to lessons learned."); ResearchAndDevelopment.Instance.AddScience(scienceAfterFailure, TransactionReasons.VesselLoss); } }
/// <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); }
/// <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; }
/// <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); }
/// <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); }