private void StageRecoverySuccessEvent(Vessel v, float[] infoArray, string reason) { if (!KCT_GameStates.settings.enabledForSave) { return; } KCTDebug.Log("Recovery Success Event triggered."); float damage = 0; if (infoArray.Length == 3) { damage = infoArray[0]; } else { KCTDebug.Log("Malformed infoArray received!"); } System.Random rand = new System.Random(); Dictionary <string, int> destroyed = new Dictionary <string, int>(); foreach (ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots) { float random = (float)rand.NextDouble(); string name = part.partInfo.name + KCT_Utilities.GetTweakScaleSize(part); if (random < damage) { KCT_Utilities.AddPartToInventory(name); } else { string commonName = part.partInfo.title + KCT_Utilities.GetTweakScaleSize(part); Debug.Log("[KCT] Part " + commonName + " was too damaged to be used anymore and was scrapped! Chance: " + damage); if (!destroyed.ContainsKey(commonName)) { destroyed.Add(commonName, 1); } else { ++destroyed[commonName]; } } } if (destroyed.Count > 0 && !KCT_GameStates.settings.DisableAllMessages) { StringBuilder msg = new StringBuilder(); msg.AppendLine("The following parts were too damaged to be reused and were scrapped:"); foreach (KeyValuePair <string, int> entry in destroyed) { msg.AppendLine(entry.Value + " x " + entry.Key); } msg.AppendLine("\nChance of failure: " + Math.Round(100 * damage) + "%"); KCT_Utilities.DisplayMessage("KCT: Parts Scrapped", msg, MessageSystemButton.MessageButtonColor.ORANGE, MessageSystemButton.ButtonIcons.ALERT); } }
public void vesselDestroyEvent(Vessel v) { if (!KCT_GameStates.settings.enabledForSave) { return; } Dictionary <string, int> PartsRecovered = new Dictionary <string, int>(); float FundsRecovered = 0, KSCDistance = 0, RecoveryPercent = 0; StringBuilder Message = new StringBuilder(); if (FlightGlobals.fetch == null) { return; } if (v != null && !(HighLogic.LoadedSceneIsFlight && v.isActiveVessel) && v.mainBody.bodyName == "Kerbin" && (!v.loaded || v.packed) && v.altitude < 35000 && (v.situation == Vessel.Situations.FLYING || v.situation == Vessel.Situations.SUB_ORBITAL) && !v.isEVA) { double totalMass = 0; double dragCoeff = 0; bool realChuteInUse = false; float RCParameter = 0; if (!v.packed) //adopted from mission controller. { foreach (Part p in v.Parts) { p.Pack(); } } if (v.protoVessel == null) { return; } KCTDebug.Log("Attempting to recover vessel."); try { foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { //KCTDebug.Log("Has part " + p.partName + ", mass " + p.mass); List <string> ModuleNames = new List <string>(); foreach (ProtoPartModuleSnapshot ppms in p.modules) { //Debug.Log(ppms.moduleName); ModuleNames.Add(ppms.moduleName); } totalMass += p.mass; totalMass += GetResourceMass(p.resources); bool isParachute = false; if (ModuleNames.Contains("ModuleParachute")) { KCTDebug.Log("Found parachute module on " + p.partInfo.name); //Find the ModuleParachute (find it in the module list by checking for a module with the name ModuleParachute) ProtoPartModuleSnapshot ppms = p.modules.First(mod => mod.moduleName == "ModuleParachute"); float drag = 500; if (ppms.moduleRef != null) { ModuleParachute mp = (ModuleParachute)ppms.moduleRef; mp.Load(ppms.moduleValues); drag = mp.fullyDeployedDrag; } //Add the part mass times the fully deployed drag (typically 500) to the dragCoeff variable (you'll see why later) dragCoeff += p.mass * drag; //This is most definitely a parachute part isParachute = true; } if (ModuleNames.Contains("RealChuteModule")) { KCTDebug.Log("Found realchute module on " + p.partInfo.name); ProtoPartModuleSnapshot realChute = p.modules.First(mod => mod.moduleName == "RealChuteModule"); if ((object)realChute != null) //Some of this was adopted from DebRefund, as Vendan's method of handling multiple parachutes is better than what I had. { Type matLibraryType = AssemblyLoader.loadedAssemblies .SelectMany(a => a.assembly.GetExportedTypes()) .SingleOrDefault(t => t.FullName == "RealChute.Libraries.MaterialsLibrary"); ConfigNode[] parchutes = realChute.moduleValues.GetNodes("PARACHUTE"); foreach (ConfigNode chute in parchutes) { float diameter = float.Parse(chute.GetValue("deployedDiameter")); string mat = chute.GetValue("material"); System.Reflection.MethodInfo matMethod = matLibraryType.GetMethod("GetMaterial", new Type[] { mat.GetType() }); object MatLibraryInstance = matLibraryType.GetProperty("instance").GetValue(null, null); object materialObject = matMethod.Invoke(MatLibraryInstance, new object[] { mat }); float dragC = (float)KCT_Utilities.GetMemberInfoValue(materialObject.GetType().GetMember("dragCoefficient")[0], materialObject); RCParameter += dragC * (float)Math.Pow(diameter, 2); } isParachute = true; realChuteInUse = true; } } if (!isParachute) { if (p.partRef != null) { dragCoeff += p.mass * p.partRef.maximum_drag; } else { dragCoeff += p.mass * 0.2; } } } } catch (Exception e) { Debug.LogError("[KCT] Error while attempting to recover vessel."); Debug.LogException(e); } double Vt = double.MaxValue; if (!realChuteInUse) { dragCoeff = dragCoeff / (totalMass); Vt = Math.Sqrt((250 * 6.674E-11 * 5.2915793E22) / (3.6E11 * 1.22309485 * dragCoeff)); KCTDebug.Log("Using Stock Module! Drag: " + dragCoeff + " Vt: " + Vt); } else { Vt = Math.Sqrt((8000 * totalMass * 9.8) / (1.223 * Math.PI) * Math.Pow(RCParameter, -1)); //This should work perfect for multiple identical chutes and gives an approximation for multiple differing chutes KCTDebug.Log("Using RealChute Module! Vt: " + Vt); } if (Vt < 10.0) { KCTDebug.Log("Recovered parts from " + v.vesselName); foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { KCT_Utilities.AddPartToInventory(p.partInfo.name + KCT_Utilities.GetTweakScaleSize(p)); if (!PartsRecovered.ContainsKey(p.partInfo.title)) { PartsRecovered.Add(p.partInfo.title, 1); } else { ++PartsRecovered[p.partInfo.title]; } } Message.AppendLine("Vessel name: " + v.vesselName); Message.AppendLine("Parts recovered: "); for (int i = 0; i < PartsRecovered.Count; i++) { Message.AppendLine(PartsRecovered.Values.ElementAt(i) + "x " + PartsRecovered.Keys.ElementAt(i)); } if (KCT_Utilities.CurrentGameIsCareer()) { if (KCT_Utilities.StageRecoveryAddonActive || KCT_Utilities.DebRefundAddonActive) //Delegate funds handling to Stage Recovery or DebRefund if it's present { KCTDebug.Log("Delegating Funds recovery to another addon."); } else //Otherwise do it ourselves { bool probeCoreAttached = false; foreach (ProtoPartSnapshot pps in v.protoVessel.protoPartSnapshots) { if (pps.modules.Find(module => (module.moduleName == "ModuleCommand" && ((ModuleCommand)module.moduleRef).minimumCrew == 0)) != null) { KCTDebug.Log("Probe Core found!"); probeCoreAttached = true; break; } } float RecoveryMod = probeCoreAttached ? 1.0f : KCT_GameStates.settings.RecoveryModifier; KSCDistance = (float)SpaceCenter.Instance.GreatCircleDistance(SpaceCenter.Instance.cb.GetRelSurfaceNVector(v.protoVessel.latitude, v.protoVessel.longitude)); double maxDist = SpaceCenter.Instance.cb.Radius * Math.PI; RecoveryPercent = RecoveryMod * Mathf.Lerp(0.98f, 0.1f, (float)(KSCDistance / maxDist)); float totalReturn = 0; foreach (ProtoPartSnapshot pps in v.protoVessel.protoPartSnapshots) { float dryCost, fuelCost; totalReturn += Math.Max(ShipConstruction.GetPartCosts(pps, pps.partInfo, out dryCost, out fuelCost), 0); } float totalBeforeModifier = totalReturn; totalReturn *= RecoveryPercent; FundsRecovered = totalReturn; KCTDebug.Log("Vessel being recovered by KCT. Percent returned: " + 100 * RecoveryPercent + "%. Distance from KSC: " + Math.Round(KSCDistance / 1000, 2) + " km"); KCTDebug.Log("Funds being returned: " + Math.Round(totalReturn, 2) + "/" + Math.Round(totalBeforeModifier, 2)); Message.AppendLine("Funds recovered: " + FundsRecovered + "(" + Math.Round(RecoveryPercent * 100, 1) + "%)"); KCT_Utilities.AddFunds(FundsRecovered); } } Message.AppendLine("\nAdditional information:"); Message.AppendLine("Distance from KSC: " + Math.Round(KSCDistance / 1000, 2) + " km"); if (!realChuteInUse) { Message.AppendLine("Stock module used. Terminal velocity (less than 10 needed): " + Math.Round(Vt, 2)); } else { Message.AppendLine("RealChute module used. Terminal velocity (less than 10 needed): " + Math.Round(Vt, 2)); } if (!(KCT_Utilities.StageRecoveryAddonActive || KCT_Utilities.DebRefundAddonActive) && (KCT_Utilities.CurrentGameIsCareer() || !KCT_GUI.PrimarilyDisabled) && !(KCT_GameStates.settings.DisableAllMessages || KCT_GameStates.settings.DisableRecoveryMessages)) { KCT_Utilities.DisplayMessage("Stage Recovered", Message, MessageSystemButton.MessageButtonColor.BLUE, MessageSystemButton.ButtonIcons.MESSAGE); } } } }