コード例 #1
0
 /// <inheritdoc/>
 public virtual bool CreateJoint(ILinkSource source, ILinkTarget target)
 {
     if (isLinked)
     {
         HostedDebugLog.Error(
             this, "Cannot link the joint which is already linked to: {0}", linkTarget);
         return(false);
     }
     if (!CheckCoupled(source, target))
     {
         var errors = CheckConstraints(source, target);
         if (errors.Length > 0)
         {
             HostedDebugLog.Error(this, "Cannot create joint:\n{0}", DbgFormatter.C2S(errors));
             return(false);
         }
     }
     else
     {
         HostedDebugLog.Fine(this, "The parts are coupled. Skip the constraints check");
     }
     linkSource = source;
     linkTarget = target;
     if (!originalLength.HasValue)
     {
         SetOriginalLength(Vector3.Distance(
                               GetSourcePhysicalAnchor(source), GetTargetPhysicalAnchor(source, target)));
     }
     isLinked = true;
     // If the parts are already coupled at this moment, then the mode must be set as such.
     coupleOnLinkMode |= isCoupled;
     // Ensure the coupling can be done.
     coupleOnLinkMode &= linkSource.coupleNode != null && linkTarget.coupleNode != null;
     if (coupleOnLinkMode)
     {
         CoupleParts();
     }
     else
     {
         AttachParts();
     }
     return(true);
 }
コード例 #2
0
        /// <inheritdoc/>
        public virtual void OnJointBreak(float breakForce)
        {
            HostedDebugLog.Fine(this, "Joint is broken with force: {0}", breakForce);
            Part       parentPart = null;
            Vector3    relPos     = Vector3.zero;
            Quaternion relRot     = Quaternion.identity;

            if (isLinked && part.parent != linkTarget.part)
            {
                // Calculate relative position and rotation of the part to properly restore the coupling.
                parentPart = part.parent;
                var root          = vessel.rootPart.transform;
                var rootRotation  = root.rotation;
                var thisPartPos   = root.TransformPoint(part.orgPos);
                var thisPartRot   = rootRotation * part.orgRot;
                var parentPartPos = root.TransformPoint(parentPart.orgPos);
                var parentPartRot = rootRotation * parentPart.orgRot;
                relPos = parentPartRot.Inverse() * (thisPartPos - parentPartPos);
                relRot = parentPartRot.Inverse() * thisPartRot;
            }

            // The break event is sent for *any* joint on the game object that got broken. Even though it
            // may be KAS joint broken, the owner part will decouple from the vessel due to the KSP core
            // doesn't validate which joint has actually broke.
            AsyncCall.CallOnFixedUpdate(this, () => {
                if (isLinked && customJoints.Any(x => x == null))
                {
                    // It was KAS joint that broke. Restore the part attachment and break KAS link.
                    if (parentPart != null)
                    {
                        HostedDebugLog.Fine(this, "Restore coupling with: {0}", parentPart);
                        var parentPartTransform = parentPart.transform;
                        var parentPartRotation  = parentPartTransform.rotation;
                        var partTransform       = part.transform;
                        partTransform.position  = parentPartTransform.position + parentPartRotation * relPos;
                        partTransform.rotation  = parentPartRotation * relRot;
                        part.Couple(parentPart);
                    }
                    HostedDebugLog.Info(this, "KAS joint is broken, unlink the parts");
                    linkSource.BreakCurrentLink(LinkActorType.Physics);
                }
            });
        }
コード例 #3
0
        /// <inheritdoc/>
        public string[] CheckColliderHits(Transform source, Transform target)
        {
            if (!pipeColliderIsPhysical)
            {
                return(new string[0]); // No need to check, the meshes will never collide.
            }
            // HACK: Start the renderer before getting the pipes.
            var oldStartState    = isStarted;
            var oldPhysicalState = isPhysicalCollider;

            if (!isStarted)
            {
                isPhysicalCollider = false;
                StartRenderer(source, target);
            }
            else if (sourceTransform != source || targetTransform != target)
            {
                HostedDebugLog.Error(this, "Cannot verify hits on a started renderer");
            }
            var points = GetPipePath(source, target);

            if (!oldStartState)
            {
                StopRenderer();
                isPhysicalCollider = oldPhysicalState;
            }

            var hitParts = new HashSet <Part>();

            for (var i = 0; i < points.Length - 1; i++)
            {
                CheckHitsForCapsule(points[i + 1], points[i], pipeDiameter, target, hitParts);
            }
            var hitMessages = new List <string>();

            foreach (var hitPart in hitParts)
            {
                hitMessages.Add(hitPart != null
          ? LinkCollidesWithObjectMsg.Format(hitPart)
          : LinkCollidesWithSurfaceMsg.Format());
            }
            return(hitMessages.ToArray());
        }
コード例 #4
0
 /// <inheritdoc/>
 public virtual bool LinkToTarget(ILinkTarget target)
 {
     if (!linkStateMachine.CheckCanSwitchTo(LinkState.Linked))
     {
         if (linkActor == LinkActorType.Player)
         {
             ShowStatusMessage(SourceIsNotAvailableForLinkMsg, isError: true);
         }
         HostedDebugLog.Error(this, "Cannot link in state: {0}", linkState);
         return(false);
     }
     if (!CheckCanLinkTo(target, reportToGUI: linkActor == LinkActorType.Player))
     {
         return(false);
     }
     LogicalLink(target);
     PhysicaLink();
     return(true);
 }
コード例 #5
0
 /// <inheritdoc/>
 protected override void CheckSettingsConsistency()
 {
     base.CheckSettingsConsistency();
     if (!allowCoupling && coupleMode == CoupleMode.AlwaysCoupled)
     {
         allowCoupling = true;
         HostedDebugLog.Warning(
             this, "Inconsistent setting fixed: allowCoupling => true, due to coupleMode={0}",
             coupleMode);
     }
     if (!allowCoupling && linkJoint != null && linkJoint.coupleOnLinkMode)
     {
         // This check is needed for debug only.
         linkJoint.SetCoupleOnLinkMode(false);
         HostedDebugLog.Warning(
             this, "Inconsistent setting fixed: coupleOnLinkMode => false, due to allowCoupling={0}",
             allowCoupling);
     }
 }
コード例 #6
0
        /// <inheritdoc/>
        public virtual void StopRenderer()
        {
            // Stop meshes updates.
            if (_linkUpdateCoroutine != null)
            {
                HostedDebugLog.Fine(this, "Stopping renderer updates...");
                StopCoroutine(_linkUpdateCoroutine);
                _linkUpdateCoroutine = null;
            }

            // Sync the renderers settings to the source part to handle the highlights.
            if (isStarted)
            {
                sourceTransform.GetComponentsInChildren <Renderer>().ToList()
                .ForEach(r => r.SetPropertyBlock(part.mpb));
                targetTransform.GetComponentsInChildren <Renderer>().ToList()
                .ForEach(r => r.SetPropertyBlock(part.mpb));
            }

            DestroyPipeMesh();
            PartModel.UpdateHighlighters(part);

            // Update the target vessel relations (if any).
            if (targetPart != null)
            {
                PartModel.UpdateHighlighters(targetPart);
                if (targetPart.vessel != vessel)
                {
                    targetPart.vessel.parts
                    .Where(p => p != null) // It's a cleanup method.
                    .ToList()
                    .ForEach(p => SetCollisionIgnores(p, false));
                }
            }

            targetPart      = null;
            sourceTransform = null;
            targetTransform = null;

            GameEvents.onPartCoupleComplete.Remove(OnPartCoupleCompleteEvent);
            GameEvents.onPartDeCouple.Remove(OnPartDeCoupleEvent);
            GameEvents.onPartDeCoupleComplete.Remove(OnPartDeCoupleCompleteEvent);
        }
コード例 #7
0
        /// <inheritdoc/>
        protected override void SetupPhysXJoints()
        {
            if (isHeadStarted)
            {
                HostedDebugLog.Warning(this, "A physical head is running. Stop it before the link!");
                StopPhysicalHead();
            }
            var needStockJoint = isCoupled && isLockedWhenCoupled;

            if (needStockJoint && partJoint == null)
            {
                if (linkTarget.part.parent == linkSource.part)
                {
                    HostedDebugLog.Fine(this, "Create a stock joint: from={0}, to={1}", linkTarget, linkSource);
                    linkTarget.part.CreateAttachJoint(AttachModes.STACK);
                }
                else if (linkSource.part.parent == linkTarget.part)
                {
                    HostedDebugLog.Fine(this, "Create a stock joint: from={0}, to={1}", linkSource, linkTarget);
                    linkSource.part.CreateAttachJoint(AttachModes.STACK);
                }
                else
                {
                    HostedDebugLog.Error(
                        this, "Cannot create stock joint: {0} <=> {1}", linkSource, linkTarget);
                    needStockJoint = false;
                }
            }
            else if (!needStockJoint && partJoint != null)
            {
                HostedDebugLog.Fine(
                    this, "Drop stock joint: to={0}, isLockedWhenDocked={1}, isCoupled={2}",
                    partJoint.Child, isLockedWhenCoupled, isCoupled);
                partJoint.DestroyJoint();
                partJoint.Child.attachJoint = null;
            }
            if (!needStockJoint)
            {
                CreateDistanceJoint(
                    linkSource, linkTarget.part.Rigidbody, GetTargetPhysicalAnchor(linkSource, linkTarget));
            }
        }
コード例 #8
0
ファイル: KASLinkTargetBase.cs プロジェクト: dh9ts/KAS
        /// <inheritdoc/>
        protected override void SetupStateMachine()
        {
            base.SetupStateMachine();
            linkStateMachine.onAfterTransition += (start, end) => HostedDebugLog.Fine(
                this, "Target state changed at {0}: {1} => {2}", attachNodeName, start, end);
            linkStateMachine.SetTransitionConstraint(
                LinkState.Available,
                new[] { LinkState.AcceptingLinks, LinkState.RejectingLinks, LinkState.NodeIsBlocked });
            linkStateMachine.SetTransitionConstraint(
                LinkState.NodeIsBlocked,
                new[] { LinkState.Available });
            linkStateMachine.SetTransitionConstraint(
                LinkState.AcceptingLinks,
                new[] { LinkState.Available, LinkState.Linked, LinkState.Locked });
            linkStateMachine.SetTransitionConstraint(
                LinkState.Linked,
                new[] { LinkState.Available });
            linkStateMachine.SetTransitionConstraint(
                LinkState.Locked,
                new[] { LinkState.Available });
            linkStateMachine.SetTransitionConstraint(
                LinkState.RejectingLinks,
                new[] { LinkState.Available, LinkState.Locked });

            linkStateMachine.AddStateHandlers(
                LinkState.Available,
                enterHandler: x => KASAPI.KasEvents.OnStartLinking.Add(OnStartConnecting),
                leaveHandler: x => KASAPI.KasEvents.OnStartLinking.Remove(OnStartConnecting));
            linkStateMachine.AddStateHandlers(
                LinkState.AcceptingLinks,
                enterHandler: x => KASAPI.KasEvents.OnStopLinking.Add(OnStopConnecting),
                leaveHandler: x => KASAPI.KasEvents.OnStopLinking.Remove(OnStopConnecting));
            linkStateMachine.AddStateHandlers(
                LinkState.RejectingLinks,
                enterHandler: x => KASAPI.KasEvents.OnStopLinking.Add(OnStopConnecting),
                leaveHandler: x => KASAPI.KasEvents.OnStopLinking.Remove(OnStopConnecting));
            linkStateMachine.AddStateHandlers(
                LinkState.AcceptingLinks,
                enterHandler: x => SetEligiblePartHighlighting(true),
                leaveHandler: x => SetEligiblePartHighlighting(false),
                callOnShutdown: false);
        }
コード例 #9
0
ファイル: KISAddonConfig.cs プロジェクト: tinygrox/KIS
        /// <summary>Adds seat inventories to cover the maximum pod occupancy.</summary>
        /// <remarks>
        /// If the part already has seat inventories, they will be adjusted to have the unique seat
        /// indexes. This is usefull if the part's config provides the needed number of modules. If number
        /// of the existing modules is not enough to cover <c>CrewCapacity</c>, extra modules are added.
        /// </remarks>
        /// <param name="part">The part to add seat inventorties for.</param>
        public static void AddPodInventories(Part part)
        {
            // Check the fields that once had unexpected values.
            if (part.partInfo == null)
            {
                HostedDebugLog.Error(part, "Unexpected part configuration: partInfo=<NULL>");
                return;
            }
            if (part.partInfo.partConfig == null)
            {
                HostedDebugLog.Error(part, "Unexpected part configuration: partConfig=<NULL>");
                return;
            }

            var checkInventories = part.Modules.OfType <ModuleKISInventory>()
                                   .Where(m => m.invType == ModuleKISInventory.InventoryType.Pod)
                                   .ToArray();
            var seatIndex = 0;

            foreach (var inventory in checkInventories)
            {
                HostedDebugLog.Info(
                    inventory, "Assing seat to a pre-configured pod inventory: {0}", seatIndex);
                evaInventory.TryGetValue("slotsX", ref inventory.slotsX);
                evaInventory.TryGetValue("slotsY", ref inventory.slotsY);
                evaInventory.TryGetValue("maxVolume", ref inventory.maxVolume);
                inventory.podSeat = seatIndex++;
            }
            while (seatIndex < part.CrewCapacity)
            {
                var moduleNode = new ConfigNode("MODULE", "Dynamically created by KIS.");
                evaInventory.CopyTo(moduleNode);
                moduleNode.SetValue("name", typeof(ModuleKISInventory).Name, createIfNotFound: true);
                moduleNode.SetValue(
                    "invType", ModuleKISInventory.InventoryType.Pod.ToString(), createIfNotFound: true);
                moduleNode.SetValue("podSeat", seatIndex, createIfNotFound: true);
                part.partInfo.partConfig.AddNode(moduleNode);
                var inventory = part.AddModule(moduleNode, forceAwake: true);
                HostedDebugLog.Info(inventory, "Dynamically create pod inventory at seat: {0}", seatIndex);
                seatIndex++;
            }
        }
コード例 #10
0
ファイル: AttachNodesUtilsImpl.cs プロジェクト: tinygrox/KAS
        /// <inheritdoc/>
        public AttachNode ParseNodeFromString(Part ownerPart, string def, string nodeId)
        {
            ArgumentGuard.NotNull(ownerPart, "ownerPart");
            ArgumentGuard.NotNullOrEmpty(def, "def", context: ownerPart);
            ArgumentGuard.NotNullOrEmpty(nodeId, "nodeId", context: ownerPart);
            var array = def.Split(',');

            ArgumentGuard.InRange(array.Length, "def", 6, 10,
                                  message: "Unexpected number of components", context: ownerPart);
            try {
                // The logic is borrowed from PartLoader.ParsePart.
                var attachNode = new AttachNode();
                attachNode.owner = ownerPart;
                attachNode.id    = nodeId;
                var factor = ownerPart.rescaleFactor;
                attachNode.position = new Vector3(
                    float.Parse(array[0]), float.Parse(array[1]), float.Parse(array[2])) * factor;
                attachNode.orientation = new Vector3(
                    float.Parse(array[3]), float.Parse(array[4]), float.Parse(array[5])) * factor;
                attachNode.originalPosition    = attachNode.position;
                attachNode.originalOrientation = attachNode.orientation;
                attachNode.size         = array.Length >= 7 ? int.Parse(array[6]) : 1;
                attachNode.attachMethod = array.Length >= 8
          ? (AttachNodeMethod)int.Parse(array[7])
          : AttachNodeMethod.FIXED_JOINT;
                if (array.Length >= 9)
                {
                    attachNode.ResourceXFeed = int.Parse(array[8]) > 0;
                }
                if (array.Length >= 10)
                {
                    attachNode.rigid = int.Parse(array[9]) > 0;
                }
                attachNode.nodeType = AttachNode.NodeType.Stack;
                return(attachNode);
            }
            catch (Exception ex) {
                HostedDebugLog.Error(ownerPart, "Cannot parse node '{0}' from: {1}\nError: {2}",
                                     nodeId, def, ex.Message);
                return(null);
            }
        }
コード例 #11
0
        /// <summary>Gets the texture from either a KSP gamebase or the internal cache.</summary>
        /// <remarks>
        /// It's OK to call this method in the performance demanding code since once the texture is
        /// successfully returned it's cached internally. The subsequent calls won't issue expensive game
        /// database requests.
        /// </remarks>
        /// <param name="textureFileName">
        /// Filename of the texture file. The path is realtive to <c>GameData</c> folder. The name must
        /// not have the file extension.
        /// </param>
        /// <param name="asNormalMap">If <c>true</c> then the texture will be loaded as a bumpmap.</param>
        /// <returns>
        /// The texture. Note that it's a shared object. Don't execute actions on it which you don't want
        /// to affect the other meshes in the game.
        /// </returns>
        /// <seealso href="https://docs.unity3d.com/ScriptReference/Texture2D.html">
        /// Unity3D: Texture2D</seealso>
        protected Texture2D GetTexture(string textureFileName, bool asNormalMap = false)
        {
            Texture2D texture;

            if (!textures.TryGetValue(textureFileName, out texture))
            {
                texture = GameDatabase.Instance.GetTexture(textureFileName, asNormalMap);
                if (texture == null)
                {
                    // Use a "red" texture if no file found.
                    HostedDebugLog.Warning(this, "Cannot load texture: {0}", textureFileName);
                    texture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
                    texture.SetPixels(new[] { Color.red });
                    texture.Apply();
                    texture.Compress(highQuality: false);
                }
                textures[textureFileName] = texture;
            }
            return(texture);
        }
コード例 #12
0
        /// <inheritdoc/>
        public Transform GetTransformForNode(Part ownerPart, AttachNode an)
        {
            ArgumentGuard.NotNull(ownerPart, "ownerPart");
            ArgumentGuard.NotNull(an, "an", context: ownerPart);
            if (an.owner != ownerPart)
            {
                HostedDebugLog.Warning(
                    ownerPart, "Attach node doesn't belong to the part: {0}", NodeId(an));
            }
            var partModel     = Hierarchy.GetPartModelTransform(ownerPart);
            var objectName    = "attachNode-" + an.id;
            var nodeTransform = partModel.Find(objectName)
                                ?? new GameObject(objectName).transform;

            Hierarchy.MoveToParent(
                nodeTransform, partModel,
                newPosition: an.position / ownerPart.rescaleFactor,
                newRotation: Quaternion.LookRotation(an.orientation));
            return(nodeTransform);
        }
コード例 #13
0
ファイル: AbstractPipeRenderer.cs プロジェクト: tinygrox/KAS
 /// <summary>Reacts on a part coupling and adjusts its colliders as needed.</summary>
 /// <remarks>
 /// The pipe meshes should not collide with the target vessel. So track the part changes on the
 /// target vessel and disable collisions on the newly appeared parts.
 /// </remarks>
 /// <param name="action">The callback action.</param>
 void OnPartCoupleCompleteEvent(GameEvents.FromToAction <Part, Part> action)
 {
     if (targetPart != null && targetPart.vessel != vessel &&
         (action.from.vessel == targetPart.vessel || action.to.vessel == targetPart.vessel))
     {
         if (action.from == targetPart)
         {
             // The target part has couple to a new vessel.
             HostedDebugLog.Fine(this, "Set collision ignores on: {0}", action.to.vessel);
             action.to.vessel.parts
             .ForEach(p => SetCollisionIgnores(p, true));
         }
         else
         {
             // A part has joined the target vessel.
             HostedDebugLog.Fine(this, "Set collision ignores on: {0}", action.from);
             SetCollisionIgnores(action.from, true);
         }
     }
 }
コード例 #14
0
 /// <summary>
 /// Ensures that max setting of the FloatRange UI control is not less than the provided value.
 /// </summary>
 void SetupFloatUiControlMax(BaseField field, UI_Control control, float refValue)
 {
     if (control != null)
     {
         var uiFloat = control as UI_FloatRange;
         if (uiFloat == null)
         {
             HostedDebugLog.Error(
                 this, "Field is not of a FloatRange type: {0}", field.MemberInfo.Name);
             return;
         }
         if (uiFloat.maxValue < refValue)
         {
             HostedDebugLog.Fine(
                 this, "Adjust field max value: field={0}, oldMax={1}, newMax={2}",
                 field.MemberInfo.Name, uiFloat.maxValue, refValue);
             uiFloat.maxValue = refValue;
         }
     }
 }
コード例 #15
0
ファイル: AbstractJoint.cs プロジェクト: dh9ts/KAS
 /// <summary>Reacts on the vessel name change and updates the vessel infos.</summary>
 void OnVesselRename(GameEvents.HostedFromToAction <Vessel, string> action)
 {
     if (!isLinked || action.host != vessel)
     {
         return; // Nothing to do.
     }
     if (persistedSrcVesselInfo.rootPartUId == action.host.rootPart.flightID)
     {
         persistedSrcVesselInfo.name       = action.host.vesselName;
         persistedSrcVesselInfo.vesselType = action.host.vesselType;
         HostedDebugLog.Fine(this, "Update source vessel info to: name={0}, type={1}",
                             persistedSrcVesselInfo.name, persistedSrcVesselInfo.vesselType);
     }
     if (persistedTgtVesselInfo.rootPartUId == action.host.rootPart.flightID)
     {
         persistedTgtVesselInfo.name       = action.host.vesselName;
         persistedTgtVesselInfo.vesselType = action.host.vesselType;
         HostedDebugLog.Fine(this, "Update target vessel info to: name={0}, type={1}",
                             persistedTgtVesselInfo.name, persistedTgtVesselInfo.vesselType);
     }
 }
コード例 #16
0
ファイル: KASLinkSourcePhysical.cs プロジェクト: dh9ts/KAS
 /// <summary>
 /// Makes the winch connector an idependent physcal onbject or returns it into a part's model as
 /// a physicsless object.
 /// </summary>
 /// <remarks>
 /// Note, that physics objects on the connector don't die in this method call. They will be
 /// cleaned up at the frame end. The caller must consider it when dealing with the connector.
 /// </remarks>
 /// <param name="state">The physical state of the connector: <c>true</c> means "physical".</param>
 void TurnConnectorPhysics(bool state)
 {
     if (state && cableJoint.headRb == null)
     {
         HostedDebugLog.Info(this, "Make the cable connector physical");
         var connector = KASInternalPhysicalConnector.Promote(
             this, connectorModelObj.gameObject, connectorInteractDistance);
         cableJoint.StartPhysicalHead(this, connectorCableAnchor);
         connector.connectorRb.mass = connectorMass;
         part.mass    -= connectorMass;
         part.rb.mass -= connectorMass;
     }
     else if (!state && cableJoint.headRb != null)
     {
         HostedDebugLog.Info(this, "Make the cable connector non-physical");
         cableJoint.StopPhysicalHead();
         KASInternalPhysicalConnector.Demote(connectorModelObj.gameObject);
         part.mass    += connectorMass;
         part.rb.mass += connectorMass;
     }
 }
コード例 #17
0
        /// <summary>Loads the state that should be processed after all the modules are created.</summary>
        /// <remarks>
        /// This method can be called by the debug tool, so add some extra checks to not critically fail
        /// if the settings are not correct.
        /// </remarks>
        void InitStartState()
        {
            var oldLinkJoint = linkJoint;

            linkJoint = part.Modules.OfType <ILinkJoint>()
                        .FirstOrDefault(x => x.cfgJointName == jointName);
            if (linkJoint == null)
            {
                HostedDebugLog.Error(this, "Cannot find joint module: {0}", jointName);
            }
            linkJoint = linkJoint ?? oldLinkJoint;

            var oldLinkRenderer = linkRenderer;

            linkRenderer = part.Modules.OfType <ILinkRenderer>()
                           .FirstOrDefault(x => x.cfgRendererName == linkRendererName);
            if (linkRenderer == null)
            {
                HostedDebugLog.Error(this, "Cannot find renderer module: {0}", linkRendererName);
            }
            linkRenderer = linkRenderer ?? oldLinkRenderer;
        }
コード例 #18
0
 /// <summary>Reacts on a part de-coupling and adjusts the docking mode.</summary>
 /// <remarks>
 /// This is a cleanup method that verifies that all links in the DOCKED mode remained coupled
 /// after decoupling of the part. If it's not the case, the DOCKED mode is reset to ATTACHED. In
 /// the normal case the joint module takes care of restoring the affected couplings, and this
 /// method becomes NO-OP.
 /// </remarks>
 /// <param name="originator">The part that has decoupled.</param>
 void OnPartDeCoupleCompleteEvent(Part originator)
 {
     if (!isLinked || !linkJoint.coupleOnLinkMode || linkTarget.part.vessel == vessel)
     {
         return; // Not interested.
     }
     // Wait for one frame to allow joint logic to restore the coupling, and then check.
     AsyncCall.CallOnEndOfFrame(
         this,
         () => {
         if (isLinked && linkJoint.coupleOnLinkMode && linkTarget.part.vessel != vessel)
         {
             HostedDebugLog.Fine(
                 this,
                 "Coupling has not been restored, resetting the docking mode: {0} <=> {1}",
                 part, linkTarget.part);
             linkJoint.SetCoupleOnLinkMode(false);
             UpdateContextMenu();
         }
     },
         skipFrames: 1);
 }
コード例 #19
0
ファイル: AbstractJoint.cs プロジェクト: dh9ts/KAS
        /// <inheritdoc/>
        public virtual void OnJointBreak(float breakForce)
        {
            HostedDebugLog.Fine(this, "Joint is broken with force: {0}", breakForce);
            Part       parentPart = null;
            Vector3    relPos     = Vector3.zero;
            Quaternion relRot     = Quaternion.identity;

            if (part.parent != linkTarget.part)
            {
                // Calculate relative position and rotation of the part to properly restore the coupling.
                parentPart = part.parent;
                var root          = vessel.rootPart.transform;
                var thisPartPos   = root.TransformPoint(part.orgPos);
                var thisPartRot   = root.rotation * part.orgRot;
                var parentPartPos = root.TransformPoint(parentPart.orgPos);
                var parentPartRot = root.rotation * parentPart.orgRot;
                relPos = parentPartRot.Inverse() * (thisPartPos - parentPartPos);
                relRot = parentPartRot.Inverse() * thisPartRot;
            }

            // The break event is sent for *any* joint on the game object that got broken. However, it may
            // not be our link's joint. To figure it out, wait till the engine has cleared the object.
            AsyncCall.CallOnFixedUpdate(this, () => {
                if (isLinked && customJoints.Any(x => x == null))
                {
                    if (parentPart != null)
                    {
                        HostedDebugLog.Fine(this, "Restore coupling with: {0}", parentPart);
                        part.transform.position =
                            parentPart.transform.position + parentPart.transform.rotation * relPos;
                        part.transform.rotation = parentPart.transform.rotation * relRot;
                        part.Couple(parentPart);
                    }
                    HostedDebugLog.Info(this, "KAS joint is broken, unlink the parts");
                    linkSource.BreakCurrentLink(LinkActorType.Physics);
                }
            });
        }
コード例 #20
0
ファイル: AbstractJoint.cs プロジェクト: tinygrox/KAS
 /// <inheritdoc/>
 public virtual bool SetCoupleOnLinkMode(bool isCoupleOnLink)
 {
     if (!isLinked)
     {
         coupleOnLinkMode = isCoupleOnLink;
         HostedDebugLog.Fine(
             this, "Coupling mode updated in a non-linked module: {0}", isCoupleOnLink);
         return(true);
     }
     if (isCoupleOnLink && (linkSource.coupleNode == null || linkTarget.coupleNode == null))
     {
         HostedDebugLog.Error(this, "Cannot couple due to source or target doesn't support it");
         coupleOnLinkMode = false;
         return(false);
     }
     if (isCoupleOnLink && linkSource.part.vessel != linkTarget.part.vessel)
     {
         // Couple the parts, and drop the other link(s).
         HostedDebugLog.Info(this, "Change coupling mode: ATTACHED => COUPLED");
         DetachParts();
         coupleOnLinkMode = isCoupleOnLink;
         CoupleParts();
     }
     else if (!isCoupleOnLink && isCoupled)
     {
         // Decouple the parts, and make the non-coupling link(s).
         HostedDebugLog.Info(this, "Change coupling mode: COUPLED => ATTACHED");
         DecoupleParts();
         coupleOnLinkMode = isCoupleOnLink;
         AttachParts();
     }
     else
     {
         coupleOnLinkMode = isCoupleOnLink; // Simply change the mode.
     }
     return(true);
 }
コード例 #21
0
        /// <summary>Converts a physicsless connector model into a physical object.</summary>
        void StartPhysicsOnConnector()
        {
            HostedDebugLog.Info(this, "Make the cable connector physical");
            var connectorPosAndRot =
                gameObject.transform.TransformPosAndRot(persistedConnectorPosAndRot);
            var connectorModel = GetConnectorModel();
            var pipeAttach     = GetConnectorModelPipeAnchor();
            var partAttach     = GetConnectorModelPartAnchor();

            // Make a physical object and attach renderer to it. This will make connector following physics.
            // Adjust pipe and part transforms the same way as in the connector.
            connectorObj = new GameObject(
                "physicalConnectorObj" + part.launchID + "-" + linkRendererName).transform;
            connectorObj.SetPositionAndRotation(connectorModel.position, connectorModel.rotation);
            var physPartAttach = new GameObject(partAttach.name + "-reverseAnchor").transform;

            physPartAttach.SetPositionAndRotation(
                partAttach.position, Quaternion.LookRotation(-partAttach.forward, -partAttach.up));
            physPartAttach.parent = connectorObj;
            var physPipeAttachObj = new GameObject(pipeAttach.name + "-Anchor").transform;

            physPipeAttachObj.parent = connectorObj;
            physPipeAttachObj.SetPositionAndRotation(pipeAttach.position, pipeAttach.rotation);
            connectorObj.SetPositionAndRotation(connectorPosAndRot.pos, connectorPosAndRot.rot);

            var connector = KASInternalPhysicalConnector.Promote(
                this, connectorObj.gameObject, connectorInteractDistance);

            connector.connectorRb.mass = connectorMass;
            part.mass    -= connectorMass;
            part.rb.mass -= connectorMass;

            linkRenderer.StartRenderer(nodeTransform, physPartAttach);
            Colliders.UpdateColliders(connectorModel.gameObject);
            cableJoint.StartPhysicalHead(this, physPipeAttachObj);
            SaveConnectorModelPosAndRot();
        }
コード例 #22
0
 /// <inheritdoc/>
 protected override void InitModuleSettings()
 {
     base.InitModuleSettings();
     if (isAutoAttachNode && parsedAttachNode != null)
     {
         KASAPI.AttachNodesUtils.DropNode(part, parsedAttachNode);
     }
     parsedAttachNode = part.FindAttachNode(attachNodeName);
     isAutoAttachNode = parsedAttachNode == null;
     if (isAutoAttachNode)
     {
         parsedAttachNode = KASAPI.AttachNodesUtils.ParseNodeFromString(
             part, attachNodeDef, attachNodeName);
         if (parsedAttachNode != null)
         {
             HostedDebugLog.Fine(
                 this, "Created auto node: {0}", KASAPI.AttachNodesUtils.NodeId(parsedAttachNode));
             if (coupleNode != null && (HighLogic.LoadedSceneIsFlight || HighLogic.LoadedSceneIsEditor))
             {
                 // Only pre-add the node in the scenes that assume restoring a vessel state.
                 // We'll drop it in the OnStartFinished if not used.
                 KASAPI.AttachNodesUtils.AddNode(part, coupleNode);
             }
         }
         else
         {
             HostedDebugLog.Error(this, "Cannot create auto node from: {0}", attachNodeDef);
         }
     }
     if (parsedAttachNode != null)
     {
         // HACK: Handle a KIS issue which causes the nodes to be owned by the prefab part.
         parsedAttachNode.owner = part;
         nodeTransform          = KASAPI.AttachNodesUtils.GetTransformForNode(part, parsedAttachNode);
     }
 }
コード例 #23
0
        public void PickupConnectorEvent()
        {
            var connector = closestConnector;

            if (connector != null)
            {
                var closestSource = connector.ownerModule as ILinkSource;
                HostedDebugLog.Info(this, "Try picking up a physical connector of: {0}...", closestSource);
                System.Diagnostics.Debug.Assert(closestSource != null, nameof(closestSource) + " != null");
                if (closestSource.LinkToTarget(LinkActorType.Player, this))
                {
                    // By default, the cable joints set the length limit to the actual distance.
                    var cableJoint = closestSource.linkJoint as ILinkCableJoint;
                    cableJoint?.SetCableLength(float.PositiveInfinity);
                    // Let the module know that we've changed the values.
                    var updateableMenu = closestSource as IHasContextMenu;
                    updateableMenu?.UpdateContextMenu();
                }
                else
                {
                    UISoundPlayer.instance.Play(KASAPI.CommonConfig.sndPathBipWrong);
                }
            }
        }
コード例 #24
0
 /// <inheritdoc/>
 public override void OnDebugAdjustablesUpdated()
 {
     base.OnDebugAdjustablesUpdated();
     AsyncCall.CallOnEndOfFrame(
         this,
         () => {
         HostedDebugLog.Warning(this, "Reloading settings...");
         InitModuleSettings();
         InitStartState();
         UpdateContextMenu();
         if (_dbgOldTarget != null)
         {
             HostedDebugLog.Warning(this, "Relinking to target: {0}", _dbgOldTarget);
             LinkToTarget(LinkActorType.Player, _dbgOldTarget);
             var cableJoint = linkJoint as ILinkCableJoint;
             if (cableJoint != null)
             {
                 HostedDebugLog.Warning(this, "Restoring cable length: {0}", _dbgOldCableLength);
                 cableJoint.SetCableLength(_dbgOldCableLength);
             }
         }
     },
         skipFrames: 1); // The link's logic is asynchronous.
 }
コード例 #25
0
ファイル: KASLinkTargetKerbal.cs プロジェクト: dh9ts/KAS
        /// <inheritdoc/>
        public override void OnStart(PartModule.StartState state)
        {
            // The EVA parts don't get the load method called. So, to complete the initalization, pretend
            // the method was called with no config provided. This is needed to make the parent working.
            Load(new ConfigNode());

            base.OnStart(state);

            if (equipMeshName != "")
            {
                attachBoneTransform = Hierarchy.FindTransformByPath(part.transform, equipBoneName);
                if (attachBoneTransform != null)
                {
                    boneAttachNodePosition = attachBoneTransform.InverseTransformPoint(nodeTransform.position);
                    boneAttachNodeRotation =
                        Quaternion.Inverse(attachBoneTransform.rotation) * nodeTransform.rotation;
                }
                else
                {
                    HostedDebugLog.Error(this, "Cannot find bone for: mesh name={0}, bone name={1}",
                                         equipMeshName, equipBoneName);
                }
            }
        }
コード例 #26
0
        /// <summary>
        /// Makes the list of all fuels and mixtures that can be moved between the linked vessels.
        /// </summary>
        /// <remarks>This is a very expensive operation.</remarks>
        void MaybeUpdateResourceOptionList()
        {
            if (!resourceListNeedsUpdate)
            {
                return; // Nothing to do.
            }
            resourceListNeedsUpdate = false;
            HostedDebugLog.Fine(this, "Refreshing resources...");

            // Gather all the resources that *both* vessel have.
            var leftResources = new HashSet <int>(
                vessel.parts
                .SelectMany(p => p.Resources)
                .Select(r => r.info.id));
            var rightResources = new HashSet <int>(
                linkTarget.part.vessel.parts
                .SelectMany(p => p.Resources)
                .Select(r => r.info.id));
            var availableResources = leftResources
                                     .Union(rightResources)
                                     .Distinct()
                                     .ToList();

            // Find the predefined resources that the part can pump between the vessels.
            var allowedResourceIds = allowedResource
                                     .Select(x => StockResourceNames.GetId(x))
                                     .ToArray();

            if (allowedResourceIds.Length == 0)
            {
                // If no specific resources set, then allow all the vessel resources that are material and
                // not restricted for pumping. Allow overriding to include/exclude a specific resource.
                var overrideEnabled = resourceOverride
                                      .Where(x => x.Length > 0 && x[0] == '+')
                                      .Select(x => StockResourceNames.GetId(x.Substring(1)))
                                      .ToArray();
                var overrideDisabled = resourceOverride
                                       .Where(x => x.Length > 0 && x[0] == '-')
                                       .Select(x => StockResourceNames.GetId(x.Substring(1)));
                var nonMovableIds = PartResourceLibrary.Instance.resourceDefinitions
                                    .Cast <PartResourceDefinition>()
                                    .Where(d => overrideEnabled.IndexOf(d.id) == -1 &&
                                           (d.unitCost < float.Epsilon ||
                                            d.volume < float.Epsilon ||
                                            d.resourceTransferMode == ResourceTransferMode.NONE))
                                    .Select(d => d.id)
                                    .Union(overrideDisabled)
                                    .ToArray();
                allowedResourceIds = availableResources
                                     .Where(x => nonMovableIds.IndexOf(x) == -1)
                                     .ToArray();
            }
            var movableResources = availableResources
                                   .Where(id => allowedResourceIds.IndexOf(id) != -1)
                                   // The GUI function will render the list in the reversed order.
                                   .OrderByDescending(id => id)
                                   .Select(id => new ResourceTransferOption(new[] { id }, new[] { 1.0 }))
                                   .ToList();

            // Add the mixtures.
            var availableMixtures = fuelMixtures
                                    .Where(m =>
                                           m.components.All(c => availableResources.Contains(StockResourceNames.GetId(c.name))));

            foreach (var mixture in availableMixtures)
            {
                movableResources.Insert(0, new ResourceTransferOption(
                                            mixture.components.Select(x => StockResourceNames.GetId(x.name)).ToArray(),
                                            mixture.components.Select(x => x.ratio).ToArray()));
            }

            resourceRows = movableResources
                           .Select(resource => resourceRowsHash.ContainsKey(resource.GetHashCode())
            ? resourceRowsHash[resource.GetHashCode()]
            : resource)
                           .ToArray();
            resourceRowsHash = resourceRows.ToDictionary(r => r.GetHashCode());
        }
コード例 #27
0
        /// <inheritdoc/>
        protected override void SetupStateMachine()
        {
            base.SetupStateMachine();
            linkStateMachine.onAfterTransition += (start, end) => UpdateContextMenu();
            linkStateMachine.AddStateHandlers(
                LinkState.Linked,
                enterHandler: oldState => {
                var module = linkTarget as PartModule;
                PartModuleUtils.InjectEvent(this, DetachConnectorEvent, module);
                PartModuleUtils.AddEvent(module, _grabConnectorEventInject);
            },
                leaveHandler: newState => {
                var module = linkTarget as PartModule;
                PartModuleUtils.WithdrawEvent(this, DetachConnectorEvent, module);
                PartModuleUtils.DropEvent(module, _grabConnectorEventInject);
            });
            linkStateMachine.AddStateHandlers(
                LinkState.NodeIsBlocked,
                enterHandler: oldState => {
                if (decoupleIncompatibleTargets &&
                    coupleNode != null && coupleNode.attachedPart != null)
                {
                    HostedDebugLog.Warning(this, "Decouple incompatible part from the node: {0}",
                                           coupleNode.FindOpposingNode().attachedPart);
                    UISoundPlayer.instance.Play(KASAPI.CommonConfig.sndPathBipWrong);
                    ShowStatusMessage(
                        CannotLinkToPreAttached.Format(coupleNode.attachedPart), isError: true);
                    KASAPI.LinkUtils.DecoupleParts(part, coupleNode.attachedPart);
                }
            },
                callOnShutdown: false);

            // The default state is "Locked". All the enter state handlers rely on it, and all the exit
            // state handlers reset the state back to the default.
            connectorStateMachine = new SimpleStateMachine <ConnectorState>();
            connectorStateMachine.onAfterTransition += (start, end) => {
                if (end != null) // Do nothing on state machine shutdown.
                {
                    persistedIsConnectorLocked = isConnectorLocked;
                    if (end == ConnectorState.Locked)
                    {
                        KASAPI.AttachNodesUtils.AddNode(part, coupleNode);
                    }
                    else if (coupleNode.attachedPart == null)
                    {
                        KASAPI.AttachNodesUtils.DropNode(part, coupleNode);
                    }
                    UpdateContextMenu();
                }
                HostedDebugLog.Info(this, "Connector state changed: {0} => {1}", start, end);
            };
            connectorStateMachine.SetTransitionConstraint(
                ConnectorState.Docked,
                new[] {
                ConnectorState.Plugged,
                ConnectorState.Locked, // External detach.
            });
            connectorStateMachine.SetTransitionConstraint(
                ConnectorState.Locked,
                new[] {
                ConnectorState.Deployed,
                ConnectorState.Plugged,
                ConnectorState.Docked, // External attach.
            });
            connectorStateMachine.SetTransitionConstraint(
                ConnectorState.Deployed,
                new[] {
                ConnectorState.Locked,
                ConnectorState.Plugged,
            });
            connectorStateMachine.SetTransitionConstraint(
                ConnectorState.Plugged,
                new[] {
                ConnectorState.Deployed,
                ConnectorState.Docked,
            });
            connectorStateMachine.AddStateHandlers(
                ConnectorState.Locked,
                enterHandler: oldState => {
                SaveConnectorModelPosAndRot();
                if (oldState.HasValue) // Skip when restoring state.
                {
                    UISoundPlayer.instance.Play(sndPathLockConnector);
                }
            },
                leaveHandler: newState =>
                SaveConnectorModelPosAndRot(saveNonPhysical: newState == ConnectorState.Deployed),
                callOnShutdown: false);
            connectorStateMachine.AddStateHandlers(
                ConnectorState.Docked,
                enterHandler: oldState => {
                SaveConnectorModelPosAndRot();
                cableJoint.SetLockedOnCouple(true);

                // Align the docking part to the nodes if it's a separate vessel.
                if (oldState.HasValue && linkTarget.part.vessel != vessel)
                {
                    AlignTransforms.SnapAlignNodes(linkTarget.coupleNode, coupleNode);
                    linkJoint.SetCoupleOnLinkMode(true);
                    UISoundPlayer.instance.Play(sndPathDockConnector);
                }
            },
                leaveHandler: newState => {
                cableJoint.SetLockedOnCouple(false);
                SaveConnectorModelPosAndRot(saveNonPhysical: newState == ConnectorState.Deployed);
                linkJoint.SetCoupleOnLinkMode(false);
            },
                callOnShutdown: false);
            connectorStateMachine.AddStateHandlers(
                ConnectorState.Plugged,
                enterHandler: oldState => SaveConnectorModelPosAndRot(),
                leaveHandler: newState =>
                SaveConnectorModelPosAndRot(saveNonPhysical: newState == ConnectorState.Deployed),
                callOnShutdown: false);
            connectorStateMachine.AddStateHandlers(
                ConnectorState.Deployed,
                enterHandler: oldState => StartPhysicsOnConnector(),
                leaveHandler: newState => StopPhysicsOnConnector(),
                callOnShutdown: false);
        }
コード例 #28
0
ファイル: KASLinkSourcePhysical.cs プロジェクト: dh9ts/KAS
        /// <inheritdoc/>
        protected override void SetupStateMachine()
        {
            base.SetupStateMachine();
            linkStateMachine.onAfterTransition += (start, end) => UpdateContextMenu();
            linkStateMachine.AddStateHandlers(
                LinkState.Linked,
                enterHandler: oldState => {
                var module = linkTarget as PartModule;
                PartModuleUtils.InjectEvent(this, DetachConnectorEvent, module);
                PartModuleUtils.AddEvent(module, GrabConnectorEventInject);
            },
                leaveHandler: newState => {
                var module = linkTarget as PartModule;
                PartModuleUtils.WithdrawEvent(this, DetachConnectorEvent, module);
                PartModuleUtils.DropEvent(module, GrabConnectorEventInject);
            });

            // The default state is "Locked". All the enter state handlers rely on it, and all the exit
            // state handlers reset the state back to the default.
            connectorStateMachine = new SimpleStateMachine <ConnectorState>(strict: true);
            connectorStateMachine.onAfterTransition += (start, end) => {
                if (end != null) // Do nothing on state machine shutdown.
                {
                    persistedIsConnectorLocked = isConnectorLocked;
                    if (end == ConnectorState.Locked)
                    {
                        KASAPI.AttachNodesUtils.AddNode(part, coupleNode);
                    }
                    else if (coupleNode.attachedPart == null)
                    {
                        KASAPI.AttachNodesUtils.DropNode(part, coupleNode);
                    }
                    UpdateContextMenu();
                }
                HostedDebugLog.Info(this, "Connector state changed: {0} => {1}", start, end);
            };
            connectorStateMachine.SetTransitionConstraint(
                ConnectorState.Docked,
                new[] {
                ConnectorState.Plugged,
                ConnectorState.Locked, // External detach.
            });
            connectorStateMachine.SetTransitionConstraint(
                ConnectorState.Locked,
                new[] {
                ConnectorState.Deployed,
                ConnectorState.Plugged,
                ConnectorState.Docked, // External attach.
            });
            connectorStateMachine.SetTransitionConstraint(
                ConnectorState.Deployed,
                new[] {
                ConnectorState.Locked,
                ConnectorState.Plugged,
            });
            connectorStateMachine.SetTransitionConstraint(
                ConnectorState.Plugged,
                new[] {
                ConnectorState.Deployed,
                ConnectorState.Docked,
            });
            connectorStateMachine.AddStateHandlers(
                ConnectorState.Locked,
                enterHandler: oldState => {
                connectorModelObj.parent = nodeTransform;
                PartModel.UpdateHighlighters(part);
                connectorModelObj.GetComponentsInChildren <Renderer>().ToList()
                .ForEach(r => r.SetPropertyBlock(part.mpb));
                AlignTransforms.SnapAlign(connectorModelObj, connectorCableAnchor, partCableAnchor);
                SetCableLength(0);
                if (oldState.HasValue) // Skip when restoring state.
                {
                    UISoundPlayer.instance.Play(sndPathLockConnector);
                }
            },
                callOnShutdown: false);
            connectorStateMachine.AddStateHandlers(
                ConnectorState.Docked,
                enterHandler: oldState => {
                connectorModelObj.parent = nodeTransform;
                AlignTransforms.SnapAlign(connectorModelObj, connectorCableAnchor, partCableAnchor);
                SetCableLength(0);

                // Align the docking part to the nodes if it's a separate vessel.
                if (oldState.HasValue && linkTarget.part.vessel != vessel)
                {
                    AlignTransforms.SnapAlignNodes(linkTarget.coupleNode, coupleNode);
                    linkJoint.SetCoupleOnLinkMode(true);
                    UISoundPlayer.instance.Play(sndPathDockConnector);
                }
            },
                leaveHandler: newState => linkJoint.SetCoupleOnLinkMode(false),
                callOnShutdown: false);
            connectorStateMachine.AddStateHandlers(
                ConnectorState.Deployed,
                enterHandler: oldState => {
                TurnConnectorPhysics(true);
                connectorModelObj.parent = connectorModelObj;
                PartModel.UpdateHighlighters(part);
                linkRenderer.StartRenderer(partCableAnchor, connectorCableAnchor);
            },
                leaveHandler: newState => {
                TurnConnectorPhysics(false);
                linkRenderer.StopRenderer();
            },
                callOnShutdown: false);
            connectorStateMachine.AddStateHandlers(
                ConnectorState.Plugged,
                enterHandler: oldState => {
                // Destroy the previous highlighter if any, since it would interfere with the new owner.
                DestroyImmediate(connectorModelObj.GetComponent <Highlighter>());
                connectorModelObj.parent = linkTarget.nodeTransform;
                PartModel.UpdateHighlighters(part);
                PartModel.UpdateHighlighters(linkTarget.part);
                connectorModelObj.GetComponentsInChildren <Renderer>().ToList()
                .ForEach(r => r.SetPropertyBlock(linkTarget.part.mpb));
                AlignTransforms.SnapAlign(
                    connectorModelObj, connectorPartAnchor, linkTarget.nodeTransform);
                linkRenderer.StartRenderer(partCableAnchor, connectorCableAnchor);
            },
                leaveHandler: newState => {
                var oldParent = connectorModelObj.GetComponentInParent <Part>();
                var oldHigh   = oldParent.HighlightActive;
                if (oldHigh)
                {
                    // Disable the part highlight to restore the connector's renderer materials.
                    oldParent.SetHighlight(false, false);
                }
                connectorModelObj.parent = nodeTransform; // Back to the model.
                PartModel.UpdateHighlighters(part);
                PartModel.UpdateHighlighters(oldParent);
                if (oldHigh)
                {
                    oldParent.SetHighlight(true, false);
                }
                linkRenderer.StopRenderer();
            },
                callOnShutdown: false);
        }
コード例 #29
0
        /// <summary>Creates the joins to make a physical link.</summary>
        protected override void SetupPhysXJoints()
        {
            // The stock joint is rigid, drop it.
            if (partJoint != null)
            {
                HostedDebugLog.Fine(this, "Dropping the stock joint to: {0}", partJoint.Child);
                partJoint.DestroyJoint();
                partJoint.Child.attachJoint = null;
            }

            HostedDebugLog.Fine(this, "Creating a 2-joints assembly");
            var srcAnchorPos   = GetSourcePhysicalAnchor(linkSource);
            var tgtAnchorPos   = GetTargetPhysicalAnchor(linkSource, linkTarget);
            var pipeHalfLength = (tgtAnchorPos - srcAnchorPos).magnitude / 2;

            middleObj = new GameObject("ConnectorObj");
            middleObj.AddComponent <KASInternalBrokenJointListener>().hostPart = part;
            var middleRb = middleObj.AddComponent <Rigidbody>();

            // PhysX behaves weird if the linked rigidbodies are too different in mass, so make the
            // connector object "somewhat" the same in mass as the both ends of the link.
            middleRb.mass            = (linkSource.part.rb.mass + linkTarget.part.rb.mass) / 2;
            middleRb.useGravity      = false;
            middleRb.velocity        = linkSource.part.rb.velocity;
            middleRb.angularVelocity = linkSource.part.rb.angularVelocity;

            // Build all joints aligned to the source node direction to have the angle limits set correctly.
            middleObj.transform.position = srcAnchorPos + linkSource.nodeTransform.forward * pipeHalfLength;
            middleObj.transform.rotation =
                Quaternion.LookRotation(linkSource.nodeTransform.forward, linkSource.nodeTransform.up);
            srcJoint = middleObj.AddComponent <ConfigurableJoint>();
            KASAPI.JointUtils.ResetJoint(srcJoint);
            KASAPI.JointUtils.SetupSphericalJoint(srcJoint, angleLimit: sourceLinkAngleLimit);
            srcJoint.autoConfigureConnectedAnchor = false;
            srcJoint.anchor          = middleRb.transform.InverseTransformPoint(srcAnchorPos);
            srcJoint.connectedBody   = linkSource.part.rb;
            srcJoint.connectedAnchor = srcJoint.connectedBody.transform.InverseTransformPoint(srcAnchorPos);
            SetBreakForces(srcJoint, linkBreakForce, linkBreakTorque);

            middleObj.transform.position = tgtAnchorPos + linkTarget.nodeTransform.forward * pipeHalfLength;
            middleObj.transform.rotation =
                Quaternion.LookRotation(-linkTarget.nodeTransform.forward, linkTarget.nodeTransform.up);
            trgJoint = middleObj.AddComponent <ConfigurableJoint>();
            KASAPI.JointUtils.ResetJoint(trgJoint);
            KASAPI.JointUtils.SetupSphericalJoint(trgJoint, angleLimit: targetLinkAngleLimit);
            trgJoint.autoConfigureConnectedAnchor = false;
            trgJoint.anchor          = middleRb.transform.InverseTransformPoint(tgtAnchorPos);
            trgJoint.connectedBody   = linkTarget.part.rb;
            trgJoint.connectedAnchor = trgJoint.connectedBody.transform.InverseTransformPoint(tgtAnchorPos);
            SetBreakForces(trgJoint, linkBreakForce, linkBreakTorque);

            middleObj.transform.position = (tgtAnchorPos + srcAnchorPos) / 2;
            middleObj.transform.rotation = Quaternion.LookRotation(
                tgtAnchorPos - middleObj.transform.position, linkSource.nodeTransform.up);

            // This "joint" is only needed to disable the collisions between the parts.
            var collisionJoint = linkSource.part.gameObject.AddComponent <ConfigurableJoint>();

            KASAPI.JointUtils.ResetJoint(collisionJoint);
            KASAPI.JointUtils.SetupDistanceJoint(collisionJoint);
            collisionJoint.xMotion       = ConfigurableJointMotion.Free;
            collisionJoint.yMotion       = ConfigurableJointMotion.Free;
            collisionJoint.zMotion       = ConfigurableJointMotion.Free;
            collisionJoint.connectedBody = linkTarget.part.rb;

            SetCustomJoints(new[] { srcJoint, trgJoint, collisionJoint }, extraObjects: new[] { middleObj });
        }
コード例 #30
0
ファイル: KASLinkSourcePhysical.cs プロジェクト: dh9ts/KAS
        /// <summary>Intializes the connector model object and its anchors.</summary>
        /// <remarks>
        /// <para>
        /// If the connector model is not found then a stub object will be created. There will be no visual
        /// representation but the overall functionality of the winch should keep working.
        /// </para>
        /// <para>
        /// If the connector doesn't have the anchors then the missed ones will be created basing on the
        /// provided position/rotation. If the config file doesn't provide anything then the anchors will
        /// have a zero position and a random rotation.
        /// </para>
        /// </remarks>
        void LoadOrCreateConnectorModel()
        {
            var          ConnectorModelName      = "ConnectorModel" + part.Modules.IndexOf(this);
            var          ConnectorParkAnchorName = "ConnectorParkAnchor" + part.Modules.IndexOf(this);
            const string CableAnchorName         = "CableAnchor";
            const string PartAnchorName          = "PartAnchor";

            if (!PartLoader.Instance.IsReady())
            {
                // Make the missing models and set the proper hierarchy.
                connectorModelObj    = Hierarchy.FindPartModelByPath(part, connectorModel);
                connectorCableAnchor = connectorCableAttachAt != ""
          ? Hierarchy.FindPartModelByPath(part, connectorCableAttachAt) : null;
                connectorPartAnchor = connectorPartAttachAt != ""
          ? Hierarchy.FindPartModelByPath(part, connectorPartAttachAt) : null;

                if (connectorModelObj == null)
                {
                    HostedDebugLog.Error(this, "Cannot find a connector model: {0}", connectorModel);
                    // Fallback to not have the whole code to crash.
                    connectorModelObj = new GameObject().transform;
                }
                connectorModelObj.name   = ConnectorModelName;
                connectorModelObj.parent = nodeTransform;


                if (connectorCableAnchor == null)
                {
                    if (connectorCableAttachAt != "")
                    {
                        HostedDebugLog.Error(
                            this, "Cannot find cable anchor transform: {0}", connectorCableAttachAt);
                    }
                    connectorCableAnchor = new GameObject().transform;
                    var posAndRot = PosAndRot.FromString(connectorCableAttachAtPosAndRot);
                    Hierarchy.MoveToParent(connectorCableAnchor, connectorModelObj,
                                           newPosition: posAndRot.pos, newRotation: posAndRot.rot);
                }
                connectorCableAnchor.name   = CableAnchorName;
                connectorCableAnchor.parent = connectorModelObj;

                if (connectorPartAnchor == null)
                {
                    if (connectorPartAttachAt != "")
                    {
                        HostedDebugLog.Error(
                            this, "Cannot find part anchor transform: {0}", connectorPartAttachAt);
                    }
                    connectorPartAnchor = new GameObject().transform;
                    var posAndRot = PosAndRot.FromString(connectorPartAttachAtPosAndRot);
                    Hierarchy.MoveToParent(connectorPartAnchor, connectorModelObj,
                                           newPosition: posAndRot.pos, newRotation: posAndRot.rot);
                }
                connectorPartAnchor.name   = PartAnchorName;
                connectorPartAnchor.parent = connectorModelObj;

                partCableAnchor = new GameObject(ConnectorParkAnchorName).transform;
                Hierarchy.MoveToParent(
                    partCableAnchor, nodeTransform, newPosition: connectorParkPositionOffset);
            }
            else
            {
                connectorModelObj    = nodeTransform.Find(ConnectorModelName);
                connectorCableAnchor = connectorModelObj.Find(CableAnchorName);
                connectorPartAnchor  = connectorModelObj.Find(PartAnchorName);
                partCableAnchor      = nodeTransform.Find(ConnectorParkAnchorName);
            }
            AlignTransforms.SnapAlign(connectorModelObj, connectorCableAnchor, partCableAnchor);
        }