public override List <string> GetTestFlightInfo() { List <string> infoStrings = new List <string>(); infoStrings.Add("<b>Engine Cycle</b>"); infoStrings.Add(String.Format("<b>Rated Burn Time</b>: {0}", TestFlightUtil.FormatTime(ratedBurnTime, TestFlightUtil.TIMEFORMAT.SHORT_IDENTIFIER, true))); if (idleDecayRate > 0) { infoStrings.Add(String.Format("Cooling. Burn time decays {0:F2} per sec second engine is off", idleDecayRate)); } float minThrust, maxThrust; thrustModifier.FindMinMaxValue(out minThrust, out maxThrust); if (minThrust != maxThrust) { infoStrings.Add(String.Format("Engine thrust affects burn time")); infoStrings.Add(String.Format("<b>Min Thrust/b> {0:F2}kn, {1:F2}x", thrustModifier.minTime, minThrust)); infoStrings.Add(String.Format("<b>Max Thrust/b> {0:F2}kn, {1:F2}x", thrustModifier.maxTime, maxThrust)); } return(infoStrings); }
protected void UpdateCycle() { double currentTime = Planetarium.GetUniversalTime(); double deltaTime = (currentTime - previousOperatingTime) / 1d; // Log(String.Format("TestFlightReliability_EngineCycle: previous time: {0:F4}, current time: {1:F4}, delta time: {2:F4}", previousOperatingTime, currentTime, deltaTime)); if (deltaTime >= 1d) { previousOperatingTime = currentTime; // Is our engine running? if (!core.IsPartOperating()) { // If not then we optionally cool down the engine, which decresses the burn time used engineOperatingTime = Math.Max(engineOperatingTime - (idleDecayRate * deltaTime), 0d); } else { // If so then we add burn time based on time passed, optionally modified by the thrust float actualThrustModifier = thrustModifier.Evaluate(engine.finalThrust); if (verboseDebugging) { Log(String.Format("TestFlightReliability_EngineCycle: Engine Thrust {0:F4}", engine.finalThrust)); Log(String.Format("TestFlightReliability_EngineCycle: delta time: {0:F4}, operating time :{1:F4}, thrustModifier: {2:F4}", deltaTime, engineOperatingTime, actualThrustModifier)); } engineOperatingTime = engineOperatingTime + (deltaTime * actualThrustModifier); // Check for failure float minValue, maxValue = -1f; cycle.FindMinMaxValue(out minValue, out maxValue); float penalty = cycle.Evaluate((float)engineOperatingTime); if (verboseDebugging) { Log(String.Format("TestFlightReliability_EngineCycle: Cycle Curve, Min Value {0:F2}:{1:F6}, Max Value {2:F2}:{3:F6}", cycle.minTime, minValue, cycle.maxTime, maxValue)); Log(String.Format("TestFlightReliability_EngineCycle: Applying modifier {0:F4} at cycle time {1:F4}", penalty, engineOperatingTime)); } core.SetTriggerMomentaryFailureModifier("EngineCycle", penalty, this); } } }
public override string GetInfo() { if (wheelRef == null) { return(""); } if (HighLogic.LoadedSceneIsEditor && string.IsNullOrEmpty(info)) { saturationLimit = (wheelRef.PitchTorque + wheelRef.YawTorque + wheelRef.RollTorque) * saturationScale / 3; // Base info info = string.Format("<b>Pitch Torque:</b> {0:F1} kNm\r\n<b>Yaw Torque:</b> {1:F1} kNm\r\n<b>Roll Torque:</b> {2:F1} kNm\r\n\r\n<b>Capacity:</b> {3:F1} kNms", wheelRef.PitchTorque, wheelRef.YawTorque, wheelRef.RollTorque, saturationLimit); // display min/max bleed rate if there is a difference, otherwise just one value float min, max; bleedRate.FindMinMaxValue(out min, out max); if (min == max) { info += string.Format("\r\n<b>Bleed Rate:</b> {0:F1}%", max * 100); } else { info += string.Format("\r\n<b>Bleed Rate:\r\n\tMin:</b> {0:0.#%}\r\n\t<b>Max:</b> {1:0.#%}", min, max); } // resource consumption info += "\r\n\r\n<b><color=#99ff00ff>Requires:</color></b>"; //foreach (ModuleResource res in wheelRef.GetConsumedResources()) //{ // if (res.rate <= 1) // info += string.Format("\r\n - <b>{0}:</b> {1:F1} /min", res.name, res.rate * 60); // else // info += string.Format("\r\n - <b>{0}:</b> {1:F1} /s", res.name, res.rate); //} } return(info); }
public override void OnUpdate() { if (!TestFlightEnabled) { return; } // For each engine we are tracking, compare its current ignition state to our last known ignition state for (int i = 0; i < engines.Count; i++) { EngineHandler engine = engines[i]; EngineModuleWrapper.EngineIgnitionState currentIgnitionState = engine.engine.IgnitionState; // If we are transitioning from not ignited to ignited, we do our check // The ignitionFailureRate defines the failure rate per flight data if (currentIgnitionState == EngineModuleWrapper.EngineIgnitionState.IGNITED) { if (engine.ignitionState == EngineModuleWrapper.EngineIgnitionState.NOT_IGNITED || engine.ignitionState == EngineModuleWrapper.EngineIgnitionState.UNKNOWN) { double failureRoll = 0d; if (verboseDebugging) { Log(String.Format("IgnitionFail: Engine {0} transitioning to INGITED state", engine.engine.Module.GetInstanceID())); Log(String.Format("IgnitionFail: Checking curves...")); } numIgnitions++; double initialFlightData = core.GetInitialFlightData(); float ignitionChance = 1f; float multiplier = 1f; // Check to see if the vessel has not launched and if the player disabled pad failures if (this.vessel.situation == Vessel.Situations.PRELAUNCH && !preLaunchFailures) { ignitionChance = 1.0f; } else { ignitionChance = baseIgnitionChance.Evaluate((float)initialFlightData); if (ignitionChance <= 0) { ignitionChance = 1f; } } if (dynPressurePenalties) { multiplier = pressureCurve.Evaluate((float)(part.dynamicPressurekPa * 1000d)); if (multiplier <= 0f) { multiplier = 1f; } } float minValue, maxValue = -1f; baseIgnitionChance.FindMinMaxValue(out minValue, out maxValue); if (verboseDebugging) { Log(String.Format("TestFlightFailure_IgnitionFail: IgnitionChance Curve, Min Value {0:F2}:{1:F6}, Max Value {2:F2}:{3:F6}", baseIgnitionChance.minTime, minValue, baseIgnitionChance.maxTime, maxValue)); } if (this.vessel.situation != Vessel.Situations.PRELAUNCH) { ignitionChance = ignitionChance * multiplier * ignitionUseMultiplier.Evaluate(numIgnitions); } failureRoll = core.RandomGenerator.NextDouble(); if (verboseDebugging) { Log(String.Format("IgnitionFail: Engine {0} ignition chance {1:F4}, roll {2:F4}", engine.engine.Module.GetInstanceID(), ignitionChance, failureRoll)); } if (failureRoll > ignitionChance) { engine.failEngine = true; core.TriggerNamedFailure("TestFlightFailure_IgnitionFail"); failureRoll = core.RandomGenerator.NextDouble(); if (failureRoll < additionalFailureChance) { core.TriggerFailure(); } } } } engine.ignitionState = currentIgnitionState; } }
/// <summary> /// Load the engine config /// </summary> /// <param name="node">The config node to load from</param> public void Load(ConfigNode node) { if (node.name.Equals("MODE")) { ConfigNode.LoadObjectFromConfig(this, node); //load the propellants ConfigNode[] propellantNodes = node.GetNodes("PROPELLANT"); propellants = new List <Propellant>(); for (int i = 0; i < propellantNodes.Length; i++) { Propellant p = new Propellant(); p.Load(propellantNodes[i]); propellants.Add(p); } //load the name of the mode if (node.HasValue("name")) { name = node.GetValue("name"); } //load the curve for atmosphere ISP or Thrust if (node.HasNode("atmosphereISPCurve")) { atmosphereISPCurve = new FloatCurve(); atmosphereISPCurve.Load(node.GetNode("atmosphereISPCurve")); float tmpMin; atmosphereISPCurve.FindMinMaxValue(out tmpMin, out maxISP); } else if (node.HasNode("atmosphereThrustCurve")) { atmosphereThrustCurve = new FloatCurve(); atmosphereThrustCurve.Load(node.GetNode("atmosphereThrustCurve")); } else { atmosphereISPCurve = new FloatCurve(); atmosphereISPCurve.Add(0, 0); atmosphereISPCurve.Add(1, 100); maxISP = 100; } //load the consumption curve if (node.HasNode("consumptionCurve")) { consumptionCurve = new FloatCurve(); consumptionCurve.Load(node.GetNode("consumptionCurve")); } //load the velocity curve if (node.HasNode("velocityCurve")) { velocityCurve = new FloatCurve(); velocityCurve.Load(node.GetNode("velocityCurve")); } //wheter the engine needs an atmosphere to work if (node.HasValue("needsAtmosphere")) { needsAtmosphere = bool.Parse(node.GetValue("needsAtmosphere")); } //wheter the engine needs oxygen to operate if (node.HasValue("needsOxygen")) { needsOxygen = bool.Parse(node.GetValue("needsOxygen")); } //wheter the engine needs to be in water to operate if (node.HasValue("needsWater")) { needsWater = bool.Parse(node.GetValue("needsWater")); } //wheter the engine needs to be in water to operate if (node.HasValue("maxThrust")) { try { maxThrust = float.Parse(node.GetValue("maxThrust")); } catch (Exception e) { maxThrust = 0; Debug.LogError("[LYNX] Cannot load maxThrust for engine: " + e.Message); } } //wheter the engine needs to be in water to operate if (node.HasValue("flameoutThreshold")) { try { flameoutThreshold = float.Parse(node.GetValue("flameoutThreshold")); } catch (Exception e) { flameoutThreshold = 0.1f; Debug.LogError("[LYNX] Cannot load flameoutThreshold for engine: " + e.Message); } } //calculate the fuel flow modifier if (atmosphereISPCurve != null) { float fuelDensity = 0.0f; for (int i = 0; i < propellants.Count; i++) { fuelDensity += PartResourceLibrary.Instance.resourceDefinitions[propellants[i].name].density; } fuelFlow = (fuelDensity > 0.0f) ? maxThrust / (9.80665f * maxISP * fuelDensity) : 1; } else { fuelFlow = 1.0f; } } }