public bool TrySetValidLinkedSet(Part linkPart1, Part linkPart2) { linkedSet.Clear(); tempPartList.Clear(); while (linkPart1 != null) { linkedSet.Add(linkPart1); linkPart1 = KJRJointUtils.IsJointAdjustmentAllowed(linkPart1) ? linkPart1.parent : null; } while (linkPart2 != null) { tempPartList.Add(linkPart2); linkPart2 = KJRJointUtils.IsJointAdjustmentAllowed(linkPart2) ? linkPart2.parent : null; } int i = linkedSet.Count - 1; int j = tempPartList.Count - 1; if (linkedSet[i] != tempPartList[j]) { return(false); // not same root, so they can never be in a valid set } while ((i >= 0) && (j >= 0) && (linkedSet[i] == tempPartList[j])) { --i; --j; } linkedSet.AddRange(tempPartList.GetRange(0, j + 1)); return(linkedSet.Count > 1); }
public void OnPartUnpack() { if (part.parent == null) { return; } neighbours.Add(part.parent); StringBuilder debugString = null; if (Log.debuglevel > 3) { debugString = new StringBuilder(); debugString.AppendLine("The following joints added by " + part.partInfo.title + " to increase stiffness:"); } if (part.parent.Rigidbody != null) { joints.Add(KJRJointUtils.BuildJoint(part, part.parent)); } if (Log.debuglevel > 3) { debugString.AppendLine(part.parent.partInfo.title + " connected to part " + part.partInfo.title); Log.dbg("{0}", debugString); } if (joints.Count > 0) { GameEvents.onVesselWasModified.Add(OnVesselWasModified); } }
public void Awake() { KJRJointUtils.LoadConstants(); updatedVessels = new List <Vessel>(); easingVessels = new HashSet <Vessel>(); multiJointManager = new KJRMultiJointManager(); updatingVessels = new List <Vessel>(); #if Compatible ModuleDecoupleType = Type.GetType("ModuleDecouple, Assembly-CSharp"); isDecoupledField = ModuleDecoupleType.GetField("isDecoupled"); // >= 1.7.1 KJRJointUtils.IRoboticServoType = Type.GetType("Expansions.Serenity.IRoboticServo, Assembly-CSharp"); // >= 1.7.1 // 1.7.1 detection onRoboticPartLockChangingField = typeof(GameEvents).GetField("onRoboticPartLockChanging"); // >= 1.7.2 if (onRoboticPartLockChangingField == null) { KJRJointUtils.BaseServoType = Type.GetType("Expansions.Serenity.BaseServo, Assembly-CSharp"); // 1.7.1 only } #endif _instance = this; }
public void OnEaseStop(Vessel v) { if (!easingVessels.Contains(v)) { return; // we expect, that in this case, we are in an OnDestroy and should not get this call at all } foreach (Part p in v.Parts) { if (KJRJointUtils.IsJointUnlockable(p)) { continue; // exclude those actions from joints that can be dynamically unlocked } p.crashTolerance = p.crashTolerance / 10000f; if (p.attachJoint) { p.attachJoint.SetUnbreakable(false, false); } } if (!updatingVessels.Contains(v)) { updatingVessels.Add(v); StartCoroutine(RunVesselJointUpdateFunctionDelayed(v)); } }
private void RunVesselJointUpdateFunction(Vessel v) { if (KJRJointUtils.debug) { Debug.Log("KJR: Processing vessel " + v.id + " (" + v.GetName() + "); root " + v.rootPart.partInfo.name + " (" + v.rootPart.flightID + ")"); } bool bReinforced = false; foreach (Part p in v.Parts) { if (KJRJointUtils.reinforceAttachNodes) { if ((p.parent != null) && (p.physicalSignificance == Part.PhysicalSignificance.FULL)) { bReinforced = true; UpdatePartJoint(p); } } if (KJRJointUtils.reinforceDecouplersFurther) { if ((p.Modules.Contains <ModuleDecouple>() || p.Modules.Contains <ModuleAnchoredDecoupler>()) && !p.Modules.Contains <KJRDecouplerReinforcementModule>()) { KJRJointUtils.AddDecouplerJointReinforcementModule(p); continue; } } if (KJRJointUtils.reinforceLaunchClampsFurther) { if ((p.parent != null) && p.Modules.Contains <LaunchClamp>() && !p.Modules.Contains <KJRLaunchClampReinforcementModule>()) { p.breakingForce = Mathf.Infinity; p.breakingTorque = Mathf.Infinity; p.mass = Mathf.Max(p.mass, (p.parent.mass + p.parent.GetResourceMass()) * 0.01f); //We do this to make sure that there is a mass ratio of 100:1 between the clamp and what it's connected to. This helps counteract some of the wobbliness simply, but also allows some give and springiness to absorb the initial physics kick if (KJRJointUtils.debug) { Debug.Log("KJR: Launch Clamp Break Force / Torque increased"); } KJRJointUtils.AddLaunchClampReinforcementModule(p); } } } if (bReinforced) { updatedVessels.Add(v); } if (KJRJointUtils.reinforceAttachNodes && KJRJointUtils.multiPartAttachNodeReinforcement) { MultiPartJointTreeChildren(v); } }
public void Awake() { KJRJointUtils.LoadConstants(); updatedVessels = new List <Vessel>(); vesselOffRails = new HashSet <Vessel>(); vesselJointStrengthened = new Dictionary <Vessel, List <Joint> >(); multiJointManager = new KJRMultiJointManager(); }
private void MultiPartJointBuildJoint(Part p, Part linkPart) { if (multiJointManager.CheckMultiJointBetweenParts(p, linkPart) || !multiJointManager.TrySetValidLinkedSet(p, linkPart)) { return; } multiJointManager.RegisterMultiJointBetweenParts(p, linkPart, KJRJointUtils.BuildJoint(p, linkPart)); }
public void Awake() { Log.dbg("Awake"); try { KJRJointUtils.LoadConstants(); } catch (Exception e) { Log.ex(this, e); Log.err("An error [{0}] was caught on initialization! The plugin WILL NOT work as expected!", e.Message); } updatedVessels = new List <Vessel>(); vesselOffRails = new HashSet <Vessel>(); vesselJointStrengthened = new Dictionary <Vessel, List <Joint> >(); multiJointManager = new KJRMultiJointManager(); }
private void MultiPartJointBuildJoint(Part part, Part linkPart, KJRMultiJointManager.Reason jointReason) { if (multiJointManager.CheckDirectJointBetweenParts(part, linkPart) || !multiJointManager.TrySetValidLinkedSet(part, linkPart)) { return; } ConfigurableJoint joint = KJRJointUtils.BuildJoint(part, linkPart); multiJointManager.RegisterMultiJoint(part, joint, true, jointReason); multiJointManager.RegisterMultiJoint(linkPart, joint, true, jointReason); foreach (Part p in multiJointManager.linkedSet) { multiJointManager.RegisterMultiJoint(p, joint, false, jointReason); } }
public bool TrySetValidLinkedSet(Part part1, Part part2) { linkedSet.Clear(); tempPartList.Clear(); while (part1 != null) { linkedSet.Add(part1); part1 = KJRJointUtils.IsJointAdjustmentAllowed(part1) ? part1.parent : null; } while (part2 != null) { tempPartList.Add(part2); part2 = KJRJointUtils.IsJointAdjustmentAllowed(part2) ? part2.parent : null; } int i = linkedSet.Count - 1; int j = tempPartList.Count - 1; if (linkedSet[i] != tempPartList[j]) { return(false); // not same root, so they can never be in a valid set } while ((i >= 0) && (j >= 0) && (linkedSet[i] == tempPartList[j])) { --i; --j; } if (linkedSet.Count > i + 2) { linkedSet.RemoveRange(i + 2, linkedSet.Count - i - 2); } linkedSet.RemoveAt(0); if ((tempPartList.Count > 1) && (j > 0)) { linkedSet.AddRange(tempPartList.GetRange(1, j)); } return(linkedSet.Count > 1); }
private void OnVesselOffRails(Vessel v) { if (v is null || v.isEVA) { return; } RunVesselJointUpdateFunction(v); if (!vesselOffRails.Contains(v) && v.precalc.isEasingGravity) { Log.info("KJR easing {0}", v.vesselName); vesselOffRails.Add(v); for (int i = 0; i < v.Parts.Count; ++i) { Part p = v.Parts[i]; p.crashTolerance = p.crashTolerance * 10000f; if (p.attachJoint) { p.attachJoint.SetUnbreakable(true, false); } Joint[] partJoints = p.GetComponents <Joint>(); if (p.Modules.Contains <LaunchClamp>()) { foreach (Joint j in partJoints) { if (j.connectedBody == null) { GameObject.Destroy(j); KJRJointUtils.ConnectLaunchClampToGround(p); break; } } } } } }
public void OnEaseStart(Vessel v) { if (KJRJointUtils.debug) { Debug.Log("KJR easing " + v.vesselName); } foreach (Part p in v.Parts) { if (KJRJointUtils.IsJointUnlockable(p)) { continue; // exclude those actions from joints that can be dynamically unlocked } p.crashTolerance = p.crashTolerance * 10000f; if (p.attachJoint) { p.attachJoint.SetUnbreakable(true, false); } Joint[] partJoints = p.GetComponents <Joint>(); if (p.Modules.Contains <LaunchClamp>()) { for (int j = 0; j < partJoints.Length; j++) { if (partJoints[j].connectedBody == null) { GameObject.Destroy(partJoints[j]); KJRJointUtils.ConnectLaunchClampToGround(p); break; } } } } easingVessels.Add(v); }
private void AddExtraJoints() { List <Part> childParts = new List <Part>(); List <Part> parentParts = new List <Part>(); parentParts = KJRJointUtils.DecouplerPartStiffeningList(part.parent, false, true); foreach (Part p in part.children) { childParts.AddRange(KJRJointUtils.DecouplerPartStiffeningList(p, true, true)); if (!childParts.Contains(p)) { childParts.Add(p); } } neighbours.Clear(); neighbours.AddRange(parentParts); neighbours.AddRange(childParts); neighbours.Remove(part); parentParts.Add(part); StringBuilder debugString = null; if (KJRJointUtils.settings.debug) { debugString = new StringBuilder(); debugString.AppendLine(parentParts.Count + " parts above decoupler to be connected to " + childParts.Count + " below decoupler."); debugString.AppendLine("The following joints added by " + part.partInfo.title + " to increase stiffness:"); } foreach (Part p in parentParts) { if (p == null || p.rb == null || p.Modules.Contains("ProceduralFairingDecoupler") || !KJRJointUtils.JointAdjustmentValid(p)) { continue; } foreach (Part q in childParts) { if (q == null || q.rb == null || q.Modules.Contains("ProceduralFairingDecoupler") || p == q || !KJRJointUtils.JointAdjustmentValid(q)) { continue; } StrutConnectParts(p, q); if (KJRJointUtils.settings.debug) { debugString.AppendLine(p.partInfo.title + " connected to part " + q.partInfo.title); } } } if (joints.Count > 0) { GameEvents.onVesselWasModified.Add(OnVesselWasModified); GameEvents.onVesselCreate.Add(OnVesselWasModified); } if (KJRJointUtils.settings.debug) { Debug.Log(debugString.ToString()); } }
private void OnVesselOffRails(Vessel v) { if ((object)v == null || v.isEVA) { return; } bool vesselHasLaunchClamps = false; RunVesselJointUpdateFunction(v); if (!vesselOffRails.Contains(v) && v.precalc.isEasingGravity) { Debug.Log("KJR easing " + v.vesselName); vesselOffRails.Add(v); List <Joint> jointList = new List <Joint>(); for (int i = 0; i < v.Parts.Count; ++i) { Part p = v.Parts[i]; p.crashTolerance = p.crashTolerance * 10000f; if (p.attachJoint) { p.attachJoint.SetUnbreakable(true, false); } Joint[] partJoints = p.GetComponents <Joint>(); if (p.Modules.Contains <LaunchClamp>()) { vesselHasLaunchClamps = true; foreach (Joint j in partJoints) { if (j.connectedBody == null) { jointList.Remove(j); GameObject.Destroy(j); KJRJointUtils.ConnectLaunchClampToGround(p); break; } } } } } else { for (int i = 0; i < v.Parts.Count; ++i) { Part p = v.Parts[i]; if (p.Modules.Contains <LaunchClamp>()) { vesselHasLaunchClamps = true; } } } // if we had launch clamps and our situation is not PRELAUNCH then assume some physics issue has bounced us into landed or flying situation. if (vesselHasLaunchClamps && v.situation != Vessel.Situations.PRELAUNCH) { Debug.Log("KJR: Vessel has launch clamps and is not PRELAUNCH: Moving back to PRELAUNCH"); v.situation = Vessel.Situations.PRELAUNCH; v.launchTime = 0; v.missionTime = 0; if (Versioning.version_minor > 3) { // The following field didn't exist pre KSP v1.4 var fInf = v.GetType().GetField("skipGroundPositioning"); fInf.SetValue(v, false); } } }
public void MultiPartJointTreeChildren(Vessel v) { if (v.Parts.Count <= 1) { return; } List <Part> childPartsToConnect = new List <Part>(); for (int i = 0; i < v.Parts.Count; ++i) { Part p = v.Parts[i]; if (p.children.Count == 0 && !p.Modules.Contains("LaunchClamp") && KJRJointUtils.MaximumPossiblePartMass(p) > KJRJointUtils.settings.massForAdjustment) { if (p.rb == null && p.Rigidbody != null) { p = p.RigidBodyPart; } childPartsToConnect.Add(p); } } Rigidbody rootRb = v.rootPart.Rigidbody; for (int i = 0; i < childPartsToConnect.Count; ++i) { Part p = childPartsToConnect[i]; Part linkPart = childPartsToConnect[i + 1 >= childPartsToConnect.Count ? 0 : i + 1]; Rigidbody rigidBody = linkPart.Rigidbody; if (!p.rb || !rigidBody || p.rb == rigidBody) { continue; } if (!multiJointManager.CheckMultiJointBetweenParts(p, linkPart) && multiJointManager.TrySetValidLinkedSet(p, linkPart)) { ConfigurableJoint betweenChildJoint; betweenChildJoint = p.gameObject.AddComponent <ConfigurableJoint>(); betweenChildJoint.connectedBody = rigidBody; betweenChildJoint.anchor = Vector3.zero; betweenChildJoint.axis = Vector3.right; betweenChildJoint.secondaryAxis = Vector3.forward; betweenChildJoint.breakForce = KJRJointUtils.settings.decouplerAndClampJointStrength; betweenChildJoint.breakTorque = KJRJointUtils.settings.decouplerAndClampJointStrength; betweenChildJoint.xMotion = betweenChildJoint.yMotion = betweenChildJoint.zMotion = ConfigurableJointMotion.Locked; betweenChildJoint.angularXMotion = betweenChildJoint.angularYMotion = betweenChildJoint.angularZMotion = ConfigurableJointMotion.Locked; multiJointManager.RegisterMultiJointBetweenParts(p, linkPart, betweenChildJoint); //multiJointManager.RegisterMultiJoint(p, betweenChildJoint); //multiJointManager.RegisterMultiJoint(linkPart, betweenChildJoint); } Part linkPart2; int part2Index = i + childPartsToConnect.Count / 2; if (part2Index >= childPartsToConnect.Count) { part2Index -= childPartsToConnect.Count; } linkPart2 = childPartsToConnect[part2Index]; rigidBody = linkPart2.Rigidbody; if (!p.rb || !rigidBody || p.rb == rigidBody) { continue; } if (!multiJointManager.CheckMultiJointBetweenParts(p, linkPart2) && multiJointManager.TrySetValidLinkedSet(p, linkPart2)) { ConfigurableJoint betweenChildJoint2; betweenChildJoint2 = p.gameObject.AddComponent <ConfigurableJoint>(); betweenChildJoint2.connectedBody = rigidBody; betweenChildJoint2.anchor = Vector3.zero; betweenChildJoint2.axis = Vector3.right; betweenChildJoint2.secondaryAxis = Vector3.forward; betweenChildJoint2.breakForce = KJRJointUtils.settings.decouplerAndClampJointStrength; betweenChildJoint2.breakTorque = KJRJointUtils.settings.decouplerAndClampJointStrength; betweenChildJoint2.xMotion = betweenChildJoint2.yMotion = betweenChildJoint2.zMotion = ConfigurableJointMotion.Locked; betweenChildJoint2.angularXMotion = betweenChildJoint2.angularYMotion = betweenChildJoint2.angularZMotion = ConfigurableJointMotion.Locked; multiJointManager.RegisterMultiJointBetweenParts(p, linkPart2, betweenChildJoint2); //multiJointManager.RegisterMultiJoint(p, betweenChildJoint2); //multiJointManager.RegisterMultiJoint(linkPart2, betweenChildJoint2); } if (!rootRb || p.rb == rootRb) { continue; } if (multiJointManager.CheckMultiJointBetweenParts(p, v.rootPart) && multiJointManager.TrySetValidLinkedSet(p, v.rootPart)) { ConfigurableJoint toRootJoint; toRootJoint = p.gameObject.AddComponent <ConfigurableJoint>(); toRootJoint.connectedBody = rootRb; toRootJoint.anchor = Vector3.zero; toRootJoint.axis = Vector3.right; toRootJoint.secondaryAxis = Vector3.forward; toRootJoint.breakForce = KJRJointUtils.settings.decouplerAndClampJointStrength; toRootJoint.breakTorque = KJRJointUtils.settings.decouplerAndClampJointStrength; toRootJoint.xMotion = toRootJoint.yMotion = toRootJoint.zMotion = ConfigurableJointMotion.Locked; toRootJoint.angularXMotion = toRootJoint.angularYMotion = toRootJoint.angularZMotion = ConfigurableJointMotion.Locked; multiJointManager.RegisterMultiJointBetweenParts(p, v.rootPart, toRootJoint); //multiJointManager.RegisterMultiJoint(p, toRootJoint); //multiJointManager.RegisterMultiJoint(v.rootPart, toRootJoint); } } }
private void UpdatePartJoint(Part p) { if (!KJRJointUtils.JointAdjustmentValid(p) || p.rb == null || p.attachJoint == null) { return; } if (p.attachMethod == AttachNodeMethod.LOCKED_JOINT) { if (KJRJointUtils.settings.debug) { Debug.Log("KJR: Already processed part before: " + p.partInfo.name + " (" + p.flightID + ") -> " + p.parent.partInfo.name + " (" + p.parent.flightID + ")"); } return; } List <ConfigurableJoint> jointList; if (p.Modules.Contains <CModuleStrut>()) { CModuleStrut s = p.Modules.GetModule <CModuleStrut>(); if (!(s.jointTarget == null || s.jointRoot == null)) { jointList = KJRJointUtils.GetJointListFromAttachJoint(s.strutJoint); if (jointList != null) { for (int i = 0; i < jointList.Count; i++) { ConfigurableJoint j = jointList[i]; if (j == null) { continue; } JointDrive strutDrive = j.angularXDrive; strutDrive.positionSpring = KJRJointUtils.settings.decouplerAndClampJointStrength; strutDrive.maximumForce = KJRJointUtils.settings.decouplerAndClampJointStrength; j.xDrive = j.yDrive = j.zDrive = j.angularXDrive = j.angularYZDrive = strutDrive; j.xMotion = j.yMotion = j.zMotion = ConfigurableJointMotion.Locked; j.angularXMotion = j.angularYMotion = j.angularZMotion = ConfigurableJointMotion.Locked; //float scalingFactor = (s.jointTarget.mass + s.jointTarget.GetResourceMass() + s.jointRoot.mass + s.jointRoot.GetResourceMass()) * 0.01f; j.breakForce = KJRJointUtils.settings.decouplerAndClampJointStrength; j.breakTorque = KJRJointUtils.settings.decouplerAndClampJointStrength; } p.attachMethod = AttachNodeMethod.LOCKED_JOINT; } } } jointList = KJRJointUtils.GetJointListFromAttachJoint(p.attachJoint); if (jointList == null) { return; } StringBuilder debugString = new StringBuilder(); bool addAdditionalJointToParent = KJRJointUtils.settings.multiPartAttachNodeReinforcement; //addAdditionalJointToParent &= !(p.Modules.Contains("LaunchClamp") || (p.parent.Modules.Contains("ModuleDecouple") || p.parent.Modules.Contains("ModuleAnchoredDecoupler"))); addAdditionalJointToParent &= !p.Modules.Contains <CModuleStrut>(); float partMass = p.mass + p.GetResourceMass(); for (int i = 0; i < jointList.Count; i++) { ConfigurableJoint j = jointList[i]; if (j == null) { continue; } String jointType = j.GetType().Name; Rigidbody connectedBody = j.connectedBody; Part connectedPart = connectedBody.GetComponent <Part>() ?? p.parent; float parentMass = connectedPart.mass + connectedPart.GetResourceMass(); if (partMass < KJRJointUtils.settings.massForAdjustment || parentMass < KJRJointUtils.settings.massForAdjustment) { if (KJRJointUtils.settings.debug) { Debug.Log("KJR: Part mass too low, skipping: " + p.partInfo.name + " (" + p.flightID + ")"); } continue; } // Check attachment nodes for better orientation data AttachNode attach = p.FindAttachNodeByPart(p.parent); AttachNode p_attach = p.parent.FindAttachNodeByPart(p); AttachNode node = attach ?? p_attach; if (node == null) { // Check if it's a pair of coupled docking ports var dock1 = p.Modules.GetModule <ModuleDockingNode>(); var dock2 = p.parent.Modules.GetModule <ModuleDockingNode>(); //Debug.Log(dock1 + " " + (dock1 ? ""+dock1.dockedPartUId : "?") + " " + dock2 + " " + (dock2 ? ""+dock2.dockedPartUId : "?")); if (dock1 && dock2 && (dock1.dockedPartUId == p.parent.flightID || dock2.dockedPartUId == p.flightID)) { attach = p.FindAttachNode(dock1.referenceAttachNode); p_attach = p.parent.FindAttachNode(dock2.referenceAttachNode); node = attach ?? p_attach; } } // If still no node and apparently surface attached, use the normal one if it's there if (node == null && p.attachMode == AttachModes.SRF_ATTACH) { node = attach = p.srfAttachNode; } if (KJRJointUtils.settings.debug) { debugString.AppendLine("Original joint from " + p.partInfo.title + " to " + p.parent.partInfo.title); debugString.AppendLine(" " + p.partInfo.name + " (" + p.flightID + ") -> " + p.parent.partInfo.name + " (" + p.parent.flightID + ")"); debugString.AppendLine(""); debugString.AppendLine(p.partInfo.title + " Inertia Tensor: " + p.rb.inertiaTensor + " " + p.parent.partInfo.name + " Inertia Tensor: " + connectedBody.inertiaTensor); debugString.AppendLine(""); debugString.AppendLine("Std. Joint Parameters"); debugString.AppendLine("Connected Body: " + p.attachJoint.Joint.connectedBody); debugString.AppendLine("Attach mode: " + p.attachMode + " (was " + jointType + ")"); if (attach != null) { debugString.AppendLine("Attach node: " + attach.id + " - " + attach.nodeType + " " + attach.size); } if (p_attach != null) { debugString.AppendLine("Parent node: " + p_attach.id + " - " + p_attach.nodeType + " " + p_attach.size); } debugString.AppendLine("Anchor: " + p.attachJoint.Joint.anchor); debugString.AppendLine("Axis: " + p.attachJoint.Joint.axis); debugString.AppendLine("Sec Axis: " + p.attachJoint.Joint.secondaryAxis); debugString.AppendLine("Break Force: " + p.attachJoint.Joint.breakForce); debugString.AppendLine("Break Torque: " + p.attachJoint.Joint.breakTorque); debugString.AppendLine(""); debugString.AppendLine("Joint Motion Locked: " + Convert.ToString(p.attachJoint.Joint.xMotion == ConfigurableJointMotion.Locked)); debugString.AppendLine("X Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.xDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.xDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.xDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Y Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.yDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.yDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.yDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Z Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.zDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.zDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.zDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Angular X Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.angularXDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.angularXDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.angularXDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Angular YZ Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.angularYZDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.angularYZDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.angularYZDrive.maximumForce); debugString.AppendLine(""); //Debug.Log(debugString.ToString()); } float breakForce = Math.Min(p.breakingForce, connectedPart.breakingForce) * KJRJointUtils.settings.breakForceMultiplier; float breakTorque = Math.Min(p.breakingTorque, connectedPart.breakingTorque) * KJRJointUtils.settings.breakTorqueMultiplier; Vector3 anchor = j.anchor; Vector3 connectedAnchor = j.connectedAnchor; Vector3 axis = j.axis; float radius = 0; float area = 0; float momentOfInertia = 0; if (node != null) { // Part that owns the node. For surface attachment, // this can only be parent if docking flips hierarchy. Part main = (node == attach) ? p : p.parent; // Orientation and position of the node in owner's local coords Vector3 ndir = node.orientation.normalized; Vector3 npos = node.position + node.offset; // And in the current part's local coords Vector3 dir = axis = p.transform.InverseTransformDirection(main.transform.TransformDirection(ndir)); if (node.nodeType == AttachNode.NodeType.Surface) { // Guessed main axis; for parts with stack nodes should be the axis of the stack Vector3 up = KJRJointUtils.GuessUpVector(main).normalized; // if guessed up direction is same as node direction, it's basically stack // for instance, consider a radially-attached docking port if (Mathf.Abs(Vector3.Dot(up, ndir)) > 0.9f) { radius = Mathf.Min(KJRJointUtils.CalculateRadius(main, ndir), KJRJointUtils.CalculateRadius(connectedPart, ndir)); if (radius <= 0.001) { radius = node.size * 1.25f; } area = Mathf.PI * radius * radius; //Area of cylinder momentOfInertia = area * radius * radius / 4; //Moment of Inertia of cylinder } else { // x along surface, y along ndir normal to surface, z along surface & main axis (up) var size1 = KJRJointUtils.CalculateExtents(main, ndir, up); var size2 = KJRJointUtils.CalculateExtents(connectedPart, ndir, up); // use average of the sides, since we don't know which one is used for attaching float width1 = (size1.x + size1.z) / 2; float width2 = (size2.x + size2.z) / 2; if (size1.y * width1 > size2.y * width2) { area = size1.y * width1; radius = Mathf.Max(size1.y, width1); } else { area = size2.y * width2; radius = Mathf.Max(size2.y, width2); } momentOfInertia = area * radius / 12; //Moment of Inertia of a rectangle bending along the longer length } } else { radius = Mathf.Min(KJRJointUtils.CalculateRadius(p, dir), KJRJointUtils.CalculateRadius(connectedPart, dir)); if (radius <= 0.001) { radius = node.size * 1.25f; } area = Mathf.PI * radius * radius; //Area of cylinder momentOfInertia = area * radius * radius / 4; //Moment of Inertia of cylinder } } //Assume part is attached along its "up" cross section; use a cylinder to approximate properties else if (p.attachMode == AttachModes.STACK) { radius = Mathf.Min(KJRJointUtils.CalculateRadius(p, Vector3.up), KJRJointUtils.CalculateRadius(connectedPart, Vector3.up)); if (radius <= 0.001) { radius = node.size * 1.25f; } area = Mathf.PI * radius * radius; //Area of cylinder momentOfInertia = area * radius * radius / 4; //Moment of Inertia of cylinder } else if (p.attachMode == AttachModes.SRF_ATTACH) { // x,z sides, y along main axis Vector3 up1 = KJRJointUtils.GuessUpVector(p); var size1 = KJRJointUtils.CalculateExtents(p, up1); Vector3 up2 = KJRJointUtils.GuessUpVector(connectedPart); var size2 = KJRJointUtils.CalculateExtents(connectedPart, up2); // use average of the sides, since we don't know which one is used for attaching float width1 = (size1.x + size1.z) / 2; float width2 = (size2.x + size2.z) / 2; if (size1.y * width1 > size2.y * width2) { area = size1.y * width1; radius = Mathf.Max(size1.y, width1); } else { area = size2.y * width2; radius = Mathf.Max(size2.y, width2); } momentOfInertia = area * radius / 12; //Moment of Inertia of a rectangle bending along the longer length } if (KJRJointUtils.settings.useVolumeNotArea) //If using volume, raise al stiffness-affecting parameters to the 1.5 power { area = Mathf.Pow(area, 1.5f); momentOfInertia = Mathf.Pow(momentOfInertia, 1.5f); } breakForce = Mathf.Max(KJRJointUtils.settings.breakStrengthPerArea * area, breakForce); breakTorque = Mathf.Max(KJRJointUtils.settings.breakTorquePerMOI * momentOfInertia, breakTorque); JointDrive angDrive = j.angularXDrive; angDrive.positionSpring = Mathf.Max(momentOfInertia * KJRJointUtils.settings.angularDriveSpring, angDrive.positionSpring); angDrive.positionDamper = Mathf.Max(momentOfInertia * KJRJointUtils.settings.angularDriveDamper * 0.1f, angDrive.positionDamper); angDrive.maximumForce = breakTorque; /*float moi_avg = p.rb.inertiaTensor.magnitude; * * moi_avg += (p.transform.localToWorldMatrix.MultiplyPoint(p.CoMOffset) - p.parent.transform.position).sqrMagnitude * p.rb.mass; * * if (moi_avg * 2f / drive.positionDamper < 0.08f) * { * drive.positionDamper = moi_avg / (0.04f); * * drive.positionSpring = drive.positionDamper * drive.positionDamper / moi_avg; * }*/ j.angularXDrive = j.angularYZDrive = j.slerpDrive = angDrive; JointDrive linDrive = j.xDrive; linDrive.maximumForce = breakForce; j.xDrive = j.yDrive = j.zDrive = linDrive; SoftJointLimit lim = new SoftJointLimit(); lim.limit = 0; lim.bounciness = 0; SoftJointLimitSpring limSpring = new SoftJointLimitSpring(); limSpring.spring = 0; limSpring.damper = 0; j.linearLimit = j.angularYLimit = j.angularZLimit = j.lowAngularXLimit = j.highAngularXLimit = lim; j.linearLimitSpring = j.angularYZLimitSpring = j.angularXLimitSpring = limSpring; j.targetAngularVelocity = Vector3.zero; j.targetVelocity = Vector3.zero; j.targetRotation = Quaternion.identity; j.targetPosition = Vector3.zero; j.breakForce = breakForce; j.breakTorque = breakTorque; p.attachJoint.SetBreakingForces(j.breakForce, j.breakTorque); p.attachMethod = AttachNodeMethod.LOCKED_JOINT; if (addAdditionalJointToParent && p.parent.parent != null) { addAdditionalJointToParent = false; if (!KJRJointUtils.JointAdjustmentValid(p.parent) || !KJRJointUtils.JointAdjustmentValid(p.parent.parent)) { continue; } /*if (ValidDecoupler(p) || ValidDecoupler(p.parent)) * continue;*/ Part newConnectedPart = p.parent.parent; bool massRatioBelowThreshold = false; int numPartsFurther = 0; float partMaxMass = KJRJointUtils.MaximumPossiblePartMass(p); List <Part> partsCrossed = new List <Part>(); List <Part> possiblePartsCrossed = new List <Part>(); partsCrossed.Add(p); partsCrossed.Add(p.parent); partsCrossed.Add(newConnectedPart); Rigidbody connectedRb = newConnectedPart.rb; do { float massRat1, massRat2; massRat1 = partMaxMass / newConnectedPart.mass; if (massRat1 < 1) { massRat1 = 1 / massRat1; } massRat2 = p.mass / KJRJointUtils.MaximumPossiblePartMass(newConnectedPart); if (massRat2 < 1) { massRat2 = 1 / massRat2; } if (massRat1 > KJRJointUtils.settings.stiffeningExtensionMassRatioThreshold || massRat2 > KJRJointUtils.settings.stiffeningExtensionMassRatioThreshold) { if (newConnectedPart.parent != null) { if (!KJRJointUtils.JointAdjustmentValid(newConnectedPart.parent)) { break; } newConnectedPart = newConnectedPart.parent; if (newConnectedPart.rb == null) { possiblePartsCrossed.Add(newConnectedPart); } else { connectedRb = newConnectedPart.rb; partsCrossed.AddRange(possiblePartsCrossed); partsCrossed.Add(newConnectedPart); possiblePartsCrossed.Clear(); } } else { break; } numPartsFurther++; } else { massRatioBelowThreshold = true; } } while (!massRatioBelowThreshold);// && numPartsFurther < 5); if (connectedRb != null && !multiJointManager.CheckMultiJointBetweenParts(p, newConnectedPart)) { ConfigurableJoint newJoint = p.gameObject.AddComponent <ConfigurableJoint>(); newJoint.connectedBody = connectedRb; newJoint.axis = Vector3.right; newJoint.secondaryAxis = Vector3.forward; newJoint.anchor = Vector3.zero; newJoint.connectedAnchor = p.transform.worldToLocalMatrix.MultiplyPoint(newConnectedPart.transform.position); //if(massRatioBelowThreshold) //{ newJoint.angularXDrive = newJoint.angularYZDrive = newJoint.slerpDrive = j.angularXDrive; newJoint.xDrive = j.xDrive; newJoint.yDrive = j.yDrive; newJoint.zDrive = j.zDrive; newJoint.linearLimit = newJoint.angularYLimit = newJoint.angularZLimit = newJoint.lowAngularXLimit = newJoint.highAngularXLimit = lim; /*newJoint.targetAngularVelocity = Vector3.zero; * newJoint.targetVelocity = Vector3.zero; * newJoint.targetRotation = Quaternion.identity; * newJoint.targetPosition = Vector3.zero;*/ /*} * else * { * newJoint.xMotion = newJoint.yMotion = newJoint.zMotion = ConfigurableJointMotion.Locked; * newJoint.angularXMotion = newJoint.angularYMotion = newJoint.angularZMotion = ConfigurableJointMotion.Locked; * }*/ newJoint.breakForce = breakForce; newJoint.breakTorque = breakTorque; //jointList.Add(newJoint); for (int k = 0; k < partsCrossed.Count; k++) { multiJointManager.RegisterMultiJoint(partsCrossed[k], newJoint); } } /*if(p.symmetryCounterparts != null && p.symmetryCounterparts.Count > 0) * { * Part linkPart = null; * Vector3 center = p.transform.position; * float cross = float.NegativeInfinity; * for(int k = 0; k < p.symmetryCounterparts.Count; k++) * { * center += p.symmetryCounterparts[k].transform.position; * } * center /= (p.symmetryCounterparts.Count + 1); * * for(int k = 0; k < p.symmetryCounterparts.Count; k++) * { * Part counterPart = p.symmetryCounterparts[k]; * if (counterPart.parent == p.parent && counterPart.rb != null) * { * float tmpCross = Vector3.Dot(Vector3.Cross(center - p.transform.position, counterPart.transform.position - p.transform.position), p.transform.up); * if(tmpCross > cross) * { * cross = tmpCross; * linkPart = counterPart; * } * } * } * if (linkPart) * { * Rigidbody rigidBody = linkPart.rb; * if (!linkPart.rb) * continue; * ConfigurableJoint newJoint; * * newJoint = p.gameObject.AddComponent<ConfigurableJoint>(); * * newJoint.connectedBody = rigidBody; * newJoint.anchor = Vector3.zero; * newJoint.axis = Vector3.right; * newJoint.secondaryAxis = Vector3.forward; * newJoint.breakForce = KJRJointUtils.decouplerAndClampJointStrength; * newJoint.breakTorque = KJRJointUtils.decouplerAndClampJointStrength; * * newJoint.xMotion = newJoint.yMotion = newJoint.zMotion = ConfigurableJointMotion.Locked; * newJoint.angularXMotion = newJoint.angularYMotion = newJoint.angularZMotion = ConfigurableJointMotion.Locked; * * multiJointManager.RegisterMultiJoint(p, newJoint); * multiJointManager.RegisterMultiJoint(linkPart, newJoint); * } * }*/ } if (KJRJointUtils.settings.debug) { debugString.AppendLine("Updated joint from " + p.partInfo.title + " to " + p.parent.partInfo.title); debugString.AppendLine(" " + p.partInfo.name + " (" + p.flightID + ") -> " + p.parent.partInfo.name + " (" + p.parent.flightID + ")"); debugString.AppendLine(""); debugString.AppendLine(p.partInfo.title + " Inertia Tensor: " + p.rb.inertiaTensor + " " + p.parent.partInfo.name + " Inertia Tensor: " + connectedBody.inertiaTensor); debugString.AppendLine(""); debugString.AppendLine("Std. Joint Parameters"); debugString.AppendLine("Connected Body: " + p.attachJoint.Joint.connectedBody); debugString.AppendLine("Attach mode: " + p.attachMode + " (was " + jointType + ")"); if (attach != null) { debugString.AppendLine("Attach node: " + attach.id + " - " + attach.nodeType + " " + attach.size); } if (p_attach != null) { debugString.AppendLine("Parent node: " + p_attach.id + " - " + p_attach.nodeType + " " + p_attach.size); } debugString.AppendLine("Anchor: " + p.attachJoint.Joint.anchor); debugString.AppendLine("Axis: " + p.attachJoint.Joint.axis); debugString.AppendLine("Sec Axis: " + p.attachJoint.Joint.secondaryAxis); debugString.AppendLine("Break Force: " + p.attachJoint.Joint.breakForce); debugString.AppendLine("Break Torque: " + p.attachJoint.Joint.breakTorque); debugString.AppendLine(""); debugString.AppendLine("Joint Motion Locked: " + Convert.ToString(p.attachJoint.Joint.xMotion == ConfigurableJointMotion.Locked)); debugString.AppendLine("Angular Drive"); debugString.AppendLine("Position Spring: " + angDrive.positionSpring); debugString.AppendLine("Position Damper: " + angDrive.positionDamper); debugString.AppendLine("Max Force: " + angDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Cross Section Properties"); debugString.AppendLine("Radius: " + radius); debugString.AppendLine("Area: " + area); debugString.AppendLine("Moment of Inertia: " + momentOfInertia); } } if (KJRJointUtils.settings.debug) { Debug.Log(debugString.ToString()); } }
public bool TrySetValidLinkedSet(Part linkPart1, Part linkPart2) { linkPart1List.Clear(); linkPart2List.Clear(); linkedSet.Clear(); if (!KJRJointUtils.JointAdjustmentValid(linkPart1) || !KJRJointUtils.JointAdjustmentValid(linkPart2)) { return(false); } //Add the parts we're connecting to their respective lists linkPart1List.Add(linkPart1); linkPart2List.Add(linkPart2); //iterate through each part's parents, making them the new linkPart and then adding them to their respective lists. LinkPart.parent will not be null until it reaches the root part while ((object)linkPart1.parent != null) { linkPart1 = linkPart1.parent; linkPart1List.Add(linkPart1); } while ((object)linkPart2.parent != null) { linkPart2 = linkPart2.parent; linkPart2List.Add(linkPart2); } int index1, index2; index1 = linkPart1List.Count - 1; index2 = linkPart2List.Count - 1; bool reachedMergeNode = false; //Start iterating through the lists, starting from the last indices. Due to the way the lists were created, index 0 = connected part, while index = Count - 1 = root part while (true) { Part p = null, q = null; //if the indices are valid, get those particular parts if (index1 >= 0) { p = linkPart1List[index1]; } if (index2 >= 0) { q = linkPart2List[index2]; } //decrement indices for next iteration index1--; index2--; //if p and q are the same, this is the merge we care about; set variable noting this now if (p == q) { reachedMergeNode = true; } //if we haven't reached the merge yet, go back to the beginning of the loop if (!reachedMergeNode) { continue; } //if we have reached the merge, all the remaining parts are parts of the line connecting these parts and should be set to be disconnected if ((object)p != null) { if (KJRJointUtils.JointAdjustmentValid(p)) { linkedSet.Add(p); } else { return(false); } } if ((object)q != null) { if (KJRJointUtils.JointAdjustmentValid(q)) { linkedSet.Add(q); } else { return(false); } } //if both p and q are null, that means we've reached the end of both lines and so all the parts have been added if ((object)p == null && (object)q == null) { break; } } return(true); }
// FEHLER, überarbeiten... ist das nicht etwas sehr viel was wir hier aufbauen??? private void ReinforceDecouplers(Part part) { List <Part> childParts = new List <Part>(); List <Part> parentParts = new List <Part>(); parentParts = KJRJointUtils.DecouplerPartStiffeningListParents(part.parent); foreach (Part p in part.children) { if (KJRJointUtils.IsJointAdjustmentAllowed(p)) { childParts.AddRange(KJRJointUtils.DecouplerPartStiffeningListChildren(p)); if (!childParts.Contains(p)) { childParts.Add(p); } } } parentParts.Add(part); StringBuilder debugString = null; if (KJRJointUtils.debug) { debugString = new StringBuilder(); debugString.AppendLine(parentParts.Count + " parts above decoupler to be connected to " + childParts.Count + " below decoupler."); debugString.AppendLine("The following joints added by " + part.partInfo.title + " to increase stiffness:"); } foreach (Part p in parentParts) { if (p == null || p.rb == null || p.Modules.Contains("ProceduralFairingDecoupler")) { continue; } foreach (Part q in childParts) { if (q == null || q.rb == null || p == q || q.Modules.Contains("ProceduralFairingDecoupler")) { continue; } if (p.vessel != q.vessel) { continue; } MultiPartJointBuildJoint(p, q, KJRMultiJointManager.Reason.ReinforceDecoupler); if (KJRJointUtils.debug) { debugString.AppendLine(p.partInfo.title + " connected to part " + q.partInfo.title); } } } if (KJRJointUtils.debug) { Debug.Log(debugString.ToString()); } }
/* public void LateUpdate() * { * if (!FlightDriver.Pause && FlightGlobals.fetch && FlightGlobals.Vessels != null) * { * foreach (Vessel v in FlightGlobals.Vessels) * { * if (!v.loaded) * continue; * * // int tick = 0; * // float scalingFactor = 1; * //This scales up the inertia tensor over a few frames rather than trying to initialize with massive inertia tensors * // if (vesselOffRailsTick.TryGetValue(v, out tick)) * // { * // scalingFactor = 1 - physicsEasingCurve.Evaluate(tick); * // } * * //ScreenMessages.PostScreenMessage("Scaling Factor: " + scalingFactor, TimeWarp.deltaTime, ScreenMessageStyle.UPPER_LEFT); * * foreach (Part p in v.Parts) * { * //if (p.Modules.Contains("LaunchClamp")) * // continue; * // This relies on KSP resetting the tensors to a constant in Update * if (p.started && p.State != PartStates.DEAD && p.rb) * { * float mass = p.rb.mass;// *scalingFactor; * * if (mass > 1f) * p.rb.inertiaTensor *= mass; * } * } * } * } * }*/ public void FixedUpdate() { if (FlightGlobals.ready && FlightGlobals.Vessels != null) { for (int i = 0; i < updatedVessels.Count; ++i) { Vessel v = updatedVessels[i]; if (v == null || !vesselOffRailsTick.ContainsKey(v)) { continue; } int tick = vesselOffRailsTick[v]; if (tick > 0) { float physicsScalingFactor = physicsEasingCurve.Evaluate(tick); if (tick >= numTicksForEasing) { List <Joint> jointList = new List <Joint>(); foreach (Part p in v.Parts) { p.crashTolerance = p.crashTolerance * 10000f; if (p.attachJoint) { p.attachJoint.SetUnbreakable(true); } Joint[] partJoints = p.GetComponents <Joint>(); /* foreach (Joint j in partJoints) * { * // j.breakForce *= 1000000000000000000; * // j.breakTorque *= 1000000000000000000; * jointList.Add(j); * Debug.Log("Part: " + p.partInfo.title + " BreakForce = " + j.breakForce + " BreakTorque = " + j.breakTorque); * }*/ if (p.Modules.Contains <LaunchClamp>()) { foreach (Joint j in partJoints) { if (j.connectedBody == null) { jointList.Remove(j); GameObject.Destroy(j); KJRJointUtils.ConnectLaunchClampToGround(p); break; } } } } //vesselJointStrengthened.Add(v, jointList); } bool easing = false; if (v.situation == Vessel.Situations.PRELAUNCH || v.situation == Vessel.Situations.LANDED || v.situation == Vessel.Situations.SPLASHED) { easing = true; } else { foreach (Part p in v.Parts) { if (p.Modules.Contains <LaunchClamp>()) { easing = true; break; } } } if (easing) { Vector3d vesselPos = v.GetWorldPos3D(); Vector3d vesselVel = v.obt_velocity; Vector3d vesselAcceleration = FlightGlobals.getGeeForceAtPosition(vesselPos) + FlightGlobals.getCentrifugalAcc(vesselPos, FlightGlobals.currentMainBody) + FlightGlobals.getCoriolisAcc(vesselVel, FlightGlobals.currentMainBody); vesselAcceleration *= physicsScalingFactor; foreach (Part p in v.Parts) { if (p.rb) { p.rb.AddForce(-vesselAcceleration * p.rb.mass); } } } if (v == FlightGlobals.ActiveVessel) { if (InputLockManager.GetControlLock("KJRLoadLock") != ControlTypes.ALL_SHIP_CONTROLS) { InputLockManager.SetControlLock(ControlTypes.ALL_SHIP_CONTROLS, "KJRLoadLock"); } ScreenMessages.PostScreenMessage("KJR stabilizing physics load...", TimeWarp.fixedDeltaTime, ScreenMessageStyle.UPPER_RIGHT); } else if (InputLockManager.GetControlLock("KJRLoadLock") == ControlTypes.ALL_SHIP_CONTROLS) { InputLockManager.RemoveControlLock("KJRLoadLock"); } tick--; vesselOffRailsTick[v] = tick; } else if (tick == 0) { foreach (Part p in v.Parts) { p.crashTolerance = p.crashTolerance / 10000f; if (p.attachJoint) { p.attachJoint.SetUnbreakable(false); } } vesselOffRailsTick.Remove(v); if (InputLockManager.GetControlLock("KJRLoadLock") == ControlTypes.ALL_SHIP_CONTROLS) { InputLockManager.RemoveControlLock("KJRLoadLock"); } } else { foreach (Part p in v.Parts) { p.crashTolerance = p.crashTolerance / 10000f; if (p.attachJoint) { p.attachJoint.SetUnbreakable(false); } } vesselOffRailsTick.Remove(v); if (InputLockManager.GetControlLock("KJRLoadLock") == ControlTypes.ALL_SHIP_CONTROLS) { InputLockManager.RemoveControlLock("KJRLoadLock"); } } } } }
// attachJoint's are always joints from a part to its parent private void ReinforceAttachJoints(Part p) { if (p.rb == null || p.attachJoint == null || !KJRJointUtils.IsJointAdjustmentAllowed(p)) { return; } if ((p.attachMethod == AttachNodeMethod.LOCKED_JOINT) && KJRJointUtils.debug) { Debug.Log("KJR: Already processed part before: " + p.partInfo.name + " (" + p.flightID + ") -> " + p.parent.partInfo.name + " (" + p.parent.flightID + ")"); } List <ConfigurableJoint> jointList; if (p.Modules.Contains <CModuleStrut>()) { CModuleStrut s = p.Modules.GetModule <CModuleStrut>(); if ((s.jointTarget != null) && (s.jointRoot != null)) { jointList = s.strutJoint.joints; if (jointList != null) { for (int i = 0; i < jointList.Count; i++) { ConfigurableJoint j = jointList[i]; if (j == null) { continue; } JointDrive strutDrive = j.angularXDrive; strutDrive.positionSpring = KJRJointUtils.decouplerAndClampJointStrength; strutDrive.maximumForce = KJRJointUtils.decouplerAndClampJointStrength; j.xDrive = j.yDrive = j.zDrive = j.angularXDrive = j.angularYZDrive = strutDrive; j.xMotion = j.yMotion = j.zMotion = ConfigurableJointMotion.Locked; j.angularXMotion = j.angularYMotion = j.angularZMotion = ConfigurableJointMotion.Locked; //float scalingFactor = (s.jointTarget.mass + s.jointTarget.GetResourceMass() + s.jointRoot.mass + s.jointRoot.GetResourceMass()) * 0.01f; j.breakForce = KJRJointUtils.decouplerAndClampJointStrength; j.breakTorque = KJRJointUtils.decouplerAndClampJointStrength; } p.attachMethod = AttachNodeMethod.LOCKED_JOINT; } } } jointList = p.attachJoint.joints; if (jointList == null) { return; } StringBuilder debugString = new StringBuilder(); bool addAdditionalJointToParent = KJRJointUtils.multiPartAttachNodeReinforcement; //addAdditionalJointToParent &= !(p.Modules.Contains("LaunchClamp") || (p.parent.Modules.Contains("ModuleDecouple") || p.parent.Modules.Contains("ModuleAnchoredDecoupler"))); addAdditionalJointToParent &= !p.Modules.Contains <CModuleStrut>(); if (!KJRJointUtils.IsJointUnlockable(p)) // exclude those actions from joints that can be dynamically unlocked { float partMass = p.mass + p.GetResourceMass(); for (int i = 0; i < jointList.Count; i++) { ConfigurableJoint j = jointList[i]; if (j == null) { continue; } String jointType = j.GetType().Name; Rigidbody connectedBody = j.connectedBody; Part connectedPart = connectedBody.GetComponent <Part>() ?? p.parent; float parentMass = connectedPart.mass + connectedPart.GetResourceMass(); if (partMass < KJRJointUtils.massForAdjustment || parentMass < KJRJointUtils.massForAdjustment) { if (KJRJointUtils.debug) { Debug.Log("KJR: Part mass too low, skipping: " + p.partInfo.name + " (" + p.flightID + ")"); } continue; } // Check attachment nodes for better orientation data AttachNode attach = p.FindAttachNodeByPart(p.parent); AttachNode p_attach = p.parent.FindAttachNodeByPart(p); AttachNode node = attach ?? p_attach; if (node == null) { // Check if it's a pair of coupled docking ports var dock1 = p.Modules.GetModule <ModuleDockingNode>(); var dock2 = p.parent.Modules.GetModule <ModuleDockingNode>(); //Debug.Log(dock1 + " " + (dock1 ? ""+dock1.dockedPartUId : "?") + " " + dock2 + " " + (dock2 ? ""+dock2.dockedPartUId : "?")); if (dock1 && dock2 && (dock1.dockedPartUId == p.parent.flightID || dock2.dockedPartUId == p.flightID)) { attach = p.FindAttachNode(dock1.referenceAttachNode); p_attach = p.parent.FindAttachNode(dock2.referenceAttachNode); node = attach ?? p_attach; } } // If still no node and apparently surface attached, use the normal one if it's there if (node == null && p.attachMode == AttachModes.SRF_ATTACH) { node = attach = p.srfAttachNode; } if (KJRJointUtils.debug) { debugString.AppendLine("Original joint from " + p.partInfo.title + " to " + p.parent.partInfo.title); debugString.AppendLine(" " + p.partInfo.name + " (" + p.flightID + ") -> " + p.parent.partInfo.name + " (" + p.parent.flightID + ")"); debugString.AppendLine(""); debugString.AppendLine(p.partInfo.title + " Inertia Tensor: " + p.rb.inertiaTensor + " " + p.parent.partInfo.name + " Inertia Tensor: " + connectedBody.inertiaTensor); debugString.AppendLine(""); debugString.AppendLine("Std. Joint Parameters"); debugString.AppendLine("Connected Body: " + p.attachJoint.Joint.connectedBody); debugString.AppendLine("Attach mode: " + p.attachMode + " (was " + jointType + ")"); if (attach != null) { debugString.AppendLine("Attach node: " + attach.id + " - " + attach.nodeType + " " + attach.size); } if (p_attach != null) { debugString.AppendLine("Parent node: " + p_attach.id + " - " + p_attach.nodeType + " " + p_attach.size); } debugString.AppendLine("Anchor: " + p.attachJoint.Joint.anchor); debugString.AppendLine("Axis: " + p.attachJoint.Joint.axis); debugString.AppendLine("Sec Axis: " + p.attachJoint.Joint.secondaryAxis); debugString.AppendLine("Break Force: " + p.attachJoint.Joint.breakForce); debugString.AppendLine("Break Torque: " + p.attachJoint.Joint.breakTorque); debugString.AppendLine(""); debugString.AppendLine("Joint Motion Locked: " + Convert.ToString(p.attachJoint.Joint.xMotion == ConfigurableJointMotion.Locked)); debugString.AppendLine("X Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.xDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.xDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.xDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Y Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.yDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.yDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.yDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Z Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.zDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.zDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.zDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Angular X Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.angularXDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.angularXDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.angularXDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Angular YZ Drive"); debugString.AppendLine("Position Spring: " + p.attachJoint.Joint.angularYZDrive.positionSpring); debugString.AppendLine("Position Damper: " + p.attachJoint.Joint.angularYZDrive.positionDamper); debugString.AppendLine("Max Force: " + p.attachJoint.Joint.angularYZDrive.maximumForce); debugString.AppendLine(""); //Debug.Log(debugString.ToString()); } float breakForce = Math.Min(p.breakingForce, connectedPart.breakingForce) * KJRJointUtils.breakForceMultiplier; float breakTorque = Math.Min(p.breakingTorque, connectedPart.breakingTorque) * KJRJointUtils.breakTorqueMultiplier; Vector3 anchor = j.anchor; Vector3 connectedAnchor = j.connectedAnchor; Vector3 axis = j.axis; float radius = 0; float area = 0; float momentOfInertia = 0; if (node != null) { // Part that owns the node. For surface attachment, // this can only be parent if docking flips hierarchy. Part main = (node == attach) ? p : p.parent; // Orientation and position of the node in owner's local coords Vector3 ndir = node.orientation.normalized; Vector3 npos = node.position + node.offset; // And in the current part's local coords Vector3 dir = axis = p.transform.InverseTransformDirection(main.transform.TransformDirection(ndir)); if (node.nodeType == AttachNode.NodeType.Surface) { // Guessed main axis; for parts with stack nodes should be the axis of the stack Vector3 up = KJRJointUtils.GuessUpVector(main).normalized; // if guessed up direction is same as node direction, it's basically stack // for instance, consider a radially-attached docking port if (Mathf.Abs(Vector3.Dot(up, ndir)) > 0.9f) { radius = Mathf.Min(KJRJointUtils.CalculateRadius(main, ndir), KJRJointUtils.CalculateRadius(connectedPart, ndir)); if (radius <= 0.001) { radius = node.size * 1.25f; } area = Mathf.PI * radius * radius; //Area of cylinder momentOfInertia = area * radius * radius / 4; //Moment of Inertia of cylinder } else { // x along surface, y along ndir normal to surface, z along surface & main axis (up) var size1 = KJRJointUtils.CalculateExtents(main, ndir, up); var size2 = KJRJointUtils.CalculateExtents(connectedPart, ndir, up); // use average of the sides, since we don't know which one is used for attaching float width1 = (size1.x + size1.z) / 2; float width2 = (size2.x + size2.z) / 2; if (size1.y * width1 > size2.y * width2) { area = size1.y * width1; radius = Mathf.Max(size1.y, width1); } else { area = size2.y * width2; radius = Mathf.Max(size2.y, width2); } momentOfInertia = area * radius / 12; //Moment of Inertia of a rectangle bending along the longer length } } else { radius = Mathf.Min(KJRJointUtils.CalculateRadius(p, dir), KJRJointUtils.CalculateRadius(connectedPart, dir)); if (radius <= 0.001) { radius = node.size * 1.25f; } area = Mathf.PI * radius * radius; //Area of cylinder momentOfInertia = area * radius * radius / 4; //Moment of Inertia of cylinder } } //Assume part is attached along its "up" cross section; use a cylinder to approximate properties else if (p.attachMode == AttachModes.STACK) { radius = Mathf.Min(KJRJointUtils.CalculateRadius(p, Vector3.up), KJRJointUtils.CalculateRadius(connectedPart, Vector3.up)); if (radius <= 0.001) { radius = 1.25f; // FEHLER, komisch, wieso setzen wir dann nicht alles < 1.25f auf 1.25f? -> zudem hatten wir hier sowieso einen Bug, das ist also sowieso zu hinterfragen } area = Mathf.PI * radius * radius; //Area of cylinder momentOfInertia = area * radius * radius / 4; //Moment of Inertia of cylinder } else if (p.attachMode == AttachModes.SRF_ATTACH) { // x,z sides, y along main axis Vector3 up1 = KJRJointUtils.GuessUpVector(p); var size1 = KJRJointUtils.CalculateExtents(p, up1); Vector3 up2 = KJRJointUtils.GuessUpVector(connectedPart); var size2 = KJRJointUtils.CalculateExtents(connectedPart, up2); // use average of the sides, since we don't know which one is used for attaching float width1 = (size1.x + size1.z) / 2; float width2 = (size2.x + size2.z) / 2; if (size1.y * width1 > size2.y * width2) { area = size1.y * width1; radius = Mathf.Max(size1.y, width1); } else { area = size2.y * width2; radius = Mathf.Max(size2.y, width2); } momentOfInertia = area * radius / 12; //Moment of Inertia of a rectangle bending along the longer length } if (KJRJointUtils.useVolumeNotArea) //If using volume, raise al stiffness-affecting parameters to the 1.5 power { area = Mathf.Pow(area, 1.5f); momentOfInertia = Mathf.Pow(momentOfInertia, 1.5f); } breakForce = Mathf.Max(KJRJointUtils.breakStrengthPerArea * area, breakForce); breakTorque = Mathf.Max(KJRJointUtils.breakTorquePerMOI * momentOfInertia, breakTorque); JointDrive angDrive = j.angularXDrive; angDrive.positionSpring = Mathf.Max(momentOfInertia * KJRJointUtils.angularDriveSpring, angDrive.positionSpring); angDrive.positionDamper = Mathf.Max(momentOfInertia * KJRJointUtils.angularDriveDamper * 0.1f, angDrive.positionDamper); angDrive.maximumForce = breakTorque; /*float moi_avg = p.rb.inertiaTensor.magnitude; * * moi_avg += (p.transform.localToWorldMatrix.MultiplyPoint(p.CoMOffset) - p.parent.transform.position).sqrMagnitude * p.rb.mass; * * if(moi_avg * 2f / drive.positionDamper < 0.08f) * { * drive.positionDamper = moi_avg / (0.04f); * * drive.positionSpring = drive.positionDamper * drive.positionDamper / moi_avg; * }*/ j.angularXDrive = j.angularYZDrive = j.slerpDrive = angDrive; JointDrive linDrive = j.xDrive; linDrive.maximumForce = breakForce; j.xDrive = j.yDrive = j.zDrive = linDrive; j.linearLimit = j.angularYLimit = j.angularZLimit = j.lowAngularXLimit = j.highAngularXLimit = new SoftJointLimit { limit = 0, bounciness = 0 }; j.linearLimitSpring = j.angularYZLimitSpring = j.angularXLimitSpring = new SoftJointLimitSpring { spring = 0, damper = 0 }; j.targetAngularVelocity = Vector3.zero; j.targetVelocity = Vector3.zero; j.targetRotation = Quaternion.identity; j.targetPosition = Vector3.zero; j.breakForce = breakForce; j.breakTorque = breakTorque; p.attachJoint.SetBreakingForces(j.breakForce, j.breakTorque); p.attachMethod = AttachNodeMethod.LOCKED_JOINT; if (KJRJointUtils.debug) { debugString.AppendLine("Updated joint from " + p.partInfo.title + " to " + p.parent.partInfo.title); debugString.AppendLine(" " + p.partInfo.name + " (" + p.flightID + ") -> " + p.parent.partInfo.name + " (" + p.parent.flightID + ")"); debugString.AppendLine(""); debugString.AppendLine(p.partInfo.title + " Inertia Tensor: " + p.rb.inertiaTensor + " " + p.parent.partInfo.name + " Inertia Tensor: " + connectedBody.inertiaTensor); debugString.AppendLine(""); debugString.AppendLine("Std. Joint Parameters"); debugString.AppendLine("Connected Body: " + p.attachJoint.Joint.connectedBody); debugString.AppendLine("Attach mode: " + p.attachMode + " (was " + jointType + ")"); if (attach != null) { debugString.AppendLine("Attach node: " + attach.id + " - " + attach.nodeType + " " + attach.size); } if (p_attach != null) { debugString.AppendLine("Parent node: " + p_attach.id + " - " + p_attach.nodeType + " " + p_attach.size); } debugString.AppendLine("Anchor: " + p.attachJoint.Joint.anchor); debugString.AppendLine("Axis: " + p.attachJoint.Joint.axis); debugString.AppendLine("Sec Axis: " + p.attachJoint.Joint.secondaryAxis); debugString.AppendLine("Break Force: " + p.attachJoint.Joint.breakForce); debugString.AppendLine("Break Torque: " + p.attachJoint.Joint.breakTorque); debugString.AppendLine(""); debugString.AppendLine("Joint Motion Locked: " + Convert.ToString(p.attachJoint.Joint.xMotion == ConfigurableJointMotion.Locked)); debugString.AppendLine("Angular Drive"); debugString.AppendLine("Position Spring: " + angDrive.positionSpring); debugString.AppendLine("Position Damper: " + angDrive.positionDamper); debugString.AppendLine("Max Force: " + angDrive.maximumForce); debugString.AppendLine(""); debugString.AppendLine("Cross Section Properties"); debugString.AppendLine("Radius: " + radius); debugString.AppendLine("Area: " + area); debugString.AppendLine("Moment of Inertia: " + momentOfInertia); } } } #if IncludeAnalyzer addAdditionalJointToParent &= WindowManager.Instance.BuildAdditionalJointToParent; #endif if (addAdditionalJointToParent && p.parent.parent != null && KJRJointUtils.IsJointAdjustmentAllowed(p.parent)) // verify that parent is not an excluded part -> we will skip this in our calculation, that's why we need to check it now { ConfigurableJoint j = p.attachJoint.Joint; // second steps uses the first/main joint as reference Part newConnectedPart = p.parent.parent; bool massRatioBelowThreshold = false; int numPartsFurther = 0; float partMaxMass = KJRJointUtils.MaximumPossiblePartMass(p); List <Part> partsCrossed = new List <Part>(); List <Part> possiblePartsCrossed = new List <Part>(); partsCrossed.Add(p.parent); Part connectedRbPart = newConnectedPart; // search the first part with an acceptable mass/mass ration to this part (joints work better then) do { float massRat1 = (partMaxMass < newConnectedPart.mass) ? (newConnectedPart.mass / partMaxMass) : (partMaxMass / newConnectedPart.mass); if (massRat1 <= KJRJointUtils.stiffeningExtensionMassRatioThreshold) { massRatioBelowThreshold = true; } else { float maxMass = KJRJointUtils.MaximumPossiblePartMass(newConnectedPart); float massRat2 = (p.mass < maxMass) ? (maxMass / p.mass) : (p.mass / maxMass); if (massRat2 <= KJRJointUtils.stiffeningExtensionMassRatioThreshold) { massRatioBelowThreshold = true; } else { if ((newConnectedPart.parent == null) || !KJRJointUtils.IsJointAdjustmentAllowed(newConnectedPart)) { break; } newConnectedPart = newConnectedPart.parent; if (newConnectedPart.rb == null) { possiblePartsCrossed.Add(newConnectedPart); } else { connectedRbPart = newConnectedPart; partsCrossed.AddRange(possiblePartsCrossed); partsCrossed.Add(newConnectedPart); possiblePartsCrossed.Clear(); } numPartsFurther++; } } } while(!massRatioBelowThreshold); // && numPartsFurther < 5); if (newConnectedPart.rb != null && !multiJointManager.CheckDirectJointBetweenParts(p, newConnectedPart)) { ConfigurableJoint newJoint; if ((p.mass >= newConnectedPart.mass) || (p.rb == null)) { newJoint = p.gameObject.AddComponent <ConfigurableJoint>(); newJoint.connectedBody = newConnectedPart.rb; newJoint.anchor = Vector3.zero; if (!KJRJointUtils.useOldJointCreation) { newJoint.autoConfigureConnectedAnchor = false; newJoint.connectedAnchor = Quaternion.Inverse(newConnectedPart.orgRot) * (p.orgPos - newConnectedPart.orgPos); Quaternion must = newConnectedPart.transform.rotation * (Quaternion.Inverse(newConnectedPart.orgRot) * p.orgRot); newJoint.SetTargetRotationLocal(Quaternion.Inverse(p.transform.rotation) * must, Quaternion.identity); // FEHLER, direkter machen } } else { newJoint = newConnectedPart.gameObject.AddComponent <ConfigurableJoint>(); newJoint.connectedBody = p.rb; newJoint.anchor = Vector3.zero; if (!KJRJointUtils.useOldJointCreation) { newJoint.autoConfigureConnectedAnchor = false; newJoint.connectedAnchor = Quaternion.Inverse(p.orgRot) * (newConnectedPart.orgPos - p.orgPos); Quaternion must = p.transform.rotation * (Quaternion.Inverse(p.orgRot) * newConnectedPart.orgRot); newJoint.SetTargetRotationLocal(Quaternion.Inverse(newConnectedPart.transform.rotation) * must, Quaternion.identity); // FEHLER, direkter machen } } // newJoint.linearLimit = newJoint.angularYLimit = newJoint.angularZLimit = newJoint.lowAngularXLimit = newJoint.highAngularXLimit // = new SoftJointLimit { limit = 0, bounciness = 0 }; // FEHLER, hab das mal rausgenommen -> evtl. später doch wieder Limited arbeiten?? newJoint.xMotion = newJoint.yMotion = newJoint.zMotion = ConfigurableJointMotion.Free; newJoint.angularYMotion = newJoint.angularZMotion = newJoint.angularXMotion = ConfigurableJointMotion.Free; newJoint.xDrive = j.xDrive; newJoint.yDrive = j.yDrive; newJoint.zDrive = j.zDrive; newJoint.angularXDrive = newJoint.angularYZDrive = newJoint.slerpDrive = j.angularXDrive; newJoint.breakForce = j.breakForce; newJoint.breakTorque = j.breakTorque; // register joint multiJointManager.RegisterMultiJoint(p, newJoint, true, KJRMultiJointManager.Reason.AdditionalJointToParent); multiJointManager.RegisterMultiJoint(newConnectedPart, newJoint, true, KJRMultiJointManager.Reason.AdditionalJointToParent); foreach (Part part in partsCrossed) { multiJointManager.RegisterMultiJoint(part, newJoint, false, KJRMultiJointManager.Reason.AdditionalJointToParent); } } } if (KJRJointUtils.debug) { Debug.Log(debugString.ToString()); } }
public void MultiPartJointTreeChildren(Vessel v) { if (v.Parts.Count <= 1) { return; } Dictionary <Part, List <Part> > childPartsToConnectByRoot = new Dictionary <Part, List <Part> >(); for (int i = 0; i < v.Parts.Count; ++i) { Part p = v.Parts[i]; bool bEndPoint = (p.children.Count == 0); if (!bEndPoint && !KJRJointUtils.IsJointAdjustmentAllowed(p) && p.parent && (p.parent.vessel == v)) { p = p.parent; bEndPoint = true; for (int j = 0; j < p.children.Count; j++) { if (KJRJointUtils.IsJointAdjustmentAllowed(p.children[j])) { bEndPoint = false; break; } } } if (bEndPoint && !p.Modules.Contains("LaunchClamp") && KJRJointUtils.MaximumPossiblePartMass(p) > KJRJointUtils.massForAdjustment) { if (p.rb == null && p.Rigidbody != null) { p = p.RigidBodyPart; } Part root = p; while (root.parent && (root.parent.vessel == v) && KJRJointUtils.IsJointAdjustmentAllowed(root)) { root = root.parent; } List <Part> childPartsToConnect; if (!childPartsToConnectByRoot.TryGetValue(root, out childPartsToConnect)) { childPartsToConnect = new List <Part>(); childPartsToConnectByRoot.Add(root, childPartsToConnect); } childPartsToConnect.Add(p); } } foreach (Part root in childPartsToConnectByRoot.Keys) { List <Part> childPartsToConnect = childPartsToConnectByRoot[root]; for (int i = 0; i < childPartsToConnect.Count; ++i) { Part p = childPartsToConnect[i]; if (!p.rb) { continue; } Part linkPart = childPartsToConnect[i + 1 >= childPartsToConnect.Count ? 0 : i + 1]; if (!linkPart.Rigidbody || p.rb == linkPart.Rigidbody) { continue; } #if IncludeAnalyzer if (WindowManager.Instance.BuildMultiPartJointTreeChildren) #endif MultiPartJointBuildJoint(p, linkPart, KJRMultiJointManager.Reason.MultiPartJointTreeChildren); int part2Index = i + childPartsToConnect.Count / 2; if (part2Index >= childPartsToConnect.Count) { part2Index -= childPartsToConnect.Count; } Part linkPart2 = childPartsToConnect[part2Index]; if (!linkPart2.Rigidbody || p.rb == linkPart2.Rigidbody) { continue; } #if IncludeAnalyzer if (WindowManager.Instance.BuildMultiPartJointTreeChildren) #endif MultiPartJointBuildJoint(p, linkPart2, KJRMultiJointManager.Reason.MultiPartJointTreeChildren); if (!root.Rigidbody || p.rb == root.Rigidbody) { continue; } #if IncludeAnalyzer if (WindowManager.Instance.BuildMultiPartJointTreeChildrenRoot) #endif MultiPartJointBuildJoint(p, root, KJRMultiJointManager.Reason.MultiPartJointTreeChildrenRoot); } } }
private void RunVesselJointUpdateFunction(Vessel v) { if (KJRJointUtils.debug) { Debug.Log("KJR: Processing vessel " + v.id + " (" + v.GetName() + "); root " + v.rootPart.partInfo.name + " (" + v.rootPart.flightID + ")"); } bool bReinforced = false; #if IncludeAnalyzer if (WindowManager.Instance.ReinforceExistingJoints) { #endif foreach (Part p in v.Parts) { if (KJRJointUtils.reinforceAttachNodes) { if ((p.parent != null) && (p.physicalSignificance == Part.PhysicalSignificance.FULL)) { bReinforced = true; ReinforceAttachJoints(p); } } if (KJRJointUtils.reinforceDecouplersFurther) { #if Compatible if (p.parent && (p.children.Count > 0)) { for (int i = 0; i < p.Modules.Count; i++) { if (KJRJointUtils.IsOfClass(p.Modules[i], ModuleDecoupleType)) { if (!(bool)isDecoupledField.GetValue(p.Modules[i])) { bReinforced = true; ReinforceDecouplers(p); goto continue_; } } } } #endif #if !Compatible ModuleDecouplerBase d = p.GetComponent <ModuleDecouplerBase>(); // FEHLER, wieso nicht auch ModuleDockingNode ?? if (p.parent && (p.children.Count > 0) && d && !d.isDecoupled) { bReinforced = true; ReinforceDecouplers(p); continue; } #endif } if (KJRJointUtils.reinforceLaunchClampsFurther) { if (p.parent && p.GetComponent <LaunchClamp>()) { ReinforceLaunchClamps(p); } } continue_ :; } #if IncludeAnalyzer } #endif if (bReinforced && !updatedVessels.Contains(v)) { updatedVessels.Add(v); } if (KJRJointUtils.reinforceAttachNodes && KJRJointUtils.multiPartAttachNodeReinforcement) { MultiPartJointTreeChildren(v); } }
private void AddExtraJoints() { List <Part> childParts = new List <Part>(); List <Part> parentParts = new List <Part>(); parentParts = KJRJointUtils.DecouplerPartStiffeningListParents(part.parent); foreach (Part p in part.children) { if (KJRJointUtils.IsJointAdjustmentAllowed(p)) { childParts.AddRange(KJRJointUtils.DecouplerPartStiffeningListChildren(p)); if (!childParts.Contains(p)) { childParts.Add(p); } } } neighbours.Clear(); neighbours.AddRange(parentParts); neighbours.AddRange(childParts); neighbours.Remove(part); parentParts.Add(part); StringBuilder debugString = null; if (Log.debuglevel > 3) { debugString = new StringBuilder(); debugString.AppendLine(parentParts.Count + " parts above decoupler to be connected to " + childParts.Count + " below decoupler."); debugString.AppendLine("The following joints added by " + part.partInfo.title + " to increase stiffness:"); } foreach (Part p in parentParts) { if (p == null || p.rb == null || p.Modules.Contains("ProceduralFairingDecoupler")) { continue; } foreach (Part q in childParts) { if (q == null || q.rb == null || p == q || q.Modules.Contains("ProceduralFairingDecoupler")) { continue; } if (p.vessel != q.vessel) { continue; } joints.Add(KJRJointUtils.BuildJoint(p, q)); if (Log.debuglevel > 3) { debugString.AppendLine(p.partInfo.title + " connected to part " + q.partInfo.title); } } } if (joints.Count > 0) { GameEvents.onVesselCreate.Add(OnVesselWasModified); GameEvents.onVesselWasModified.Add(OnVesselWasModified); } Log.dbg("{0}", debugString); }