public static double GetChuteArea(List <Part> parts) { double RCParameter = 0; bool realChuteInUse = false; try { foreach (Part p in parts) { //Make a list of all the Module Names for easy checking later. This can be avoided, but is convenient. List <string> ModuleNames = new List <string>(); foreach (PartModule pm in p.Modules) { ModuleNames.Add(pm.moduleName); } if (ModuleNames.Contains("RealChuteModule")) { if (!realChuteInUse) { RCParameter = 0; } //First off, get the PPMS since we'll need that PartModule realChute = p.Modules["RealChuteModule"]; //Assuming that's not somehow null, then we continue if (realChute != null) //Some of this was adopted from DebRefund, as Vendan's method of handling multiple parachutes is better than what I had. { //We make a list of ConfigNodes containing the parachutes (usually 1, but now there can be any number of them) //We get that from the PPMS ConfigNode rcNode = new ConfigNode(); realChute.Save(rcNode); //It's existence means that RealChute is installed and in use on the craft (you could have it installed and use stock chutes, so we only check if it's on the craft) realChuteInUse = true; RCParameter += ProcessRealchute(rcNode); } } else if (ModuleNames.Contains("RealChuteFAR")) //RealChute Lite for FAR { if (!realChuteInUse) { RCParameter = 0; } PartModule realChute = p.Modules["RealChuteFAR"]; float diameter = 0.0F; //realChute.moduleValues.GetValue("deployedDiameter") if (realChute != null) { try { diameter = realChute.Fields.GetValue <float>("deployedDiameter"); Log.Info($"[SR] Diameter is {diameter}."); } catch (Exception e) { Debug.LogError("[SR] Exception while finding deployedDiameter for RealChuteFAR module on module."); Debug.LogException(e); } } else { Log.Info("[SR] moduleRef is null, attempting workaround to find diameter."); object dDefault = p.partInfo.partPrefab.Modules["RealChuteFAR"]?.Fields?.GetValue("deployedDiameter"); //requires C# 6 if (dDefault != null) { diameter = Convert.ToSingle(dDefault); Log.Info($"[SR] Workaround gave a diameter of {diameter}."); } else { Log.Info("[SR] Couldn't get default value, setting to 0 and calling it a day."); diameter = 0.0F; } } float dragC = 1.0f; //float.Parse(realChute.moduleValues.GetValue("staticCd")); RCParameter += (dragC * Mathf.Pow(diameter, 2) * Math.PI / 4.0); realChuteInUse = true; } else if (!realChuteInUse && ModuleNames.Contains("ModuleParachute")) { //Credit to m4v and RCSBuildAid: https://github.com/m4v/RCSBuildAid/blob/master/Plugin/CoDMarker.cs Part part = p ?? p.partInfo.partPrefab; //the part, or the part prefab DragCubeList dragCubes = part.DragCubes; dragCubes.SetCubeWeight("DEPLOYED", 1); dragCubes.SetCubeWeight("SEMIDEPLOYED", 0); dragCubes.SetCubeWeight("PACKED", 0); dragCubes.SetOcclusionMultiplier(0); Quaternion rotation = Quaternion.LookRotation(Vector3d.up); try { rotation = Quaternion.LookRotation(part.partTransform?.InverseTransformDirection(Vector3d.up) ?? Vector3d.up); } catch (Exception e) { Debug.LogException(e); } dragCubes.SetDragVectorRotation(rotation); } if (!realChuteInUse) { Part part = p ?? p.partInfo.partPrefab; //the part reference, or the part prefab DragCubeList dragCubes = part.DragCubes; dragCubes.ForceUpdate(false, true); dragCubes.SetDragWeights(); dragCubes.SetPartOcclusion(); Vector3 dir = Vector3d.up; try { dir = -part.partTransform?.InverseTransformDirection(Vector3d.down) ?? Vector3d.up; } catch (Exception e) { //Debug.LogException(e); Log.Info("[SR] The expected excpetion is still present. " + e.Message); } dragCubes.SetDrag(dir, 0.03f); //mach 0.03, or about 10m/s double dragCoeff = dragCubes.AreaDrag * PhysicsGlobals.DragCubeMultiplier; RCParameter += (dragCoeff * PhysicsGlobals.DragMultiplier); } } } catch (Exception e) { Debug.LogError("[SR] Error occured while trying to determine total chute area."); Debug.LogException(e); } return(RCParameter); }