Ejemplo n.º 1
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);
     }
 }
Ejemplo n.º 2
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));
            }
        }
Ejemplo n.º 3
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);
        }
Ejemplo n.º 4
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);
        }
Ejemplo n.º 5
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.
 }
Ejemplo n.º 6
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);
        }
Ejemplo n.º 7
0
 void Destroy()
 {
     // The logging below will will identify the game object name by it's full hierarchy path.
     HostedDebugLog.Warning(part.transform.Find("model"), "Part's model is being destroyed");
 }
Ejemplo n.º 8
0
        /// <inheritdoc/>
        protected override void CheckCoupleNode()
        {
            base.CheckCoupleNode();
            if (coupleNode == null)
            {
                // If the part doesn't want to couple, then we should obey.
                if (parsedAttachNode.attachedPart != null)
                {
                    HostedDebugLog.Error(
                        this, "Cannot maintain coupling with: {0}", parsedAttachNode.attachedPart);
                    if (linkState == LinkState.Available)
                    {
                        AsyncCall.CallOnEndOfFrame(this, () => {
                            if (parsedAttachNode.attachedPart)
                            {
                                HostedDebugLog.Info(
                                    this, "Decoupling incompatible part: {0}", parsedAttachNode.attachedPart);
                                parsedAttachNode.attachedPart.decouple();
                            }
                        });
                    }
                    else if (linkState == LinkState.Linked && linkTarget != null)
                    {
                        HostedDebugLog.Warning(this, "Breaking the link to: {0}", linkTarget);
                        AsyncCall.CallOnEndOfFrame(this, () => BreakCurrentLink(LinkActorType.API));
                    }
                    else
                    {
                        AsyncCall.CallOnEndOfFrame(this, () => {
                            if (parsedAttachNode.attachedPart)
                            {
                                HostedDebugLog.Error(
                                    this, "Cannot pickup coupling in unexpected link state: {0}", linkState);
                                parsedAttachNode.attachedPart.decouple();
                            }
                        });
                    }
                }
                return;
            }
            if (linkState == LinkState.Available && coupleNode != null && coupleNode.attachedPart != null)
            {
                var target = coupleNode.attachedPart.Modules
                             .OfType <ILinkTarget>()
                             .FirstOrDefault(t => t.cfgLinkType == cfgLinkType && t.linkState == LinkState.Available &&
                                             t.coupleNode != null && t.coupleNode.attachedPart == part &&
                                             CheckCanLinkTo(t));
                if (target != null)
                {
                    HostedDebugLog.Fine(this, "Linking with the pre-attached part: {0}", target);
                    LinkToTarget(LinkActorType.API, target);
                }
                if (!isLinked)
                {
                    // Let the other part a chance to couple, and block if it didn't succeed.
                    HostedDebugLog.Fine(this, "Cannot link, wait for the other part: target={0}",
                                        coupleNode.attachedPart);
                    AsyncCall.CallOnEndOfFrame(this, () => {
                        if (linkState == LinkState.Available && coupleNode.attachedPart != null)
                        {
                            HostedDebugLog.Warning(this, "Cannot link to the pre-attached part: from={0}, to={1}",
                                                   KASAPI.AttachNodesUtils.NodeId(coupleNode),
                                                   KASAPI.AttachNodesUtils.NodeId(coupleNode.FindOpposingNode()));
                            SetLinkState(LinkState.NodeIsBlocked);
                        }
                    });
                }
            }
            else if (linkState == LinkState.NodeIsBlocked && parsedAttachNode.attachedPart == null)
            {
                SetLinkState(LinkState.Available);
            }

            // Restore the link state if not yet done.
            if (isLinked && !linkJoint.isLinked)
            {
                linkJoint.CreateJoint(this, linkTarget);
            }

            UpdateContextMenu(); // To update the dock/undock menu.
        }
Ejemplo n.º 9
0
        /// <summary>Updates or creates the collider.</summary>
        void UpdateOrCreateCollider()
        {
            var meshTransform = Hierarchy.FindPartModelByPath(part, meshPath);

            if (meshTransform == null)
            {
                HostedDebugLog.Error(this, "Cannot find mesh for path: {0}", meshPath);
                return;
            }
            var meshObj = meshTransform.gameObject;

            // Add or update the custom colldier.
            if (meshCollider)
            {
                UnityEngine.Object.DestroyImmediate(meshObj.GetComponent <Collider>());
                var collider = meshObj.AddComponent <MeshCollider>();
                collider.convex = true;
                HostedDebugLog.Info(this, "Added a mesh collider at {0}", collider.transform);
            }
            else
            {
                var meshBounds = default(Bounds);
                foreach (var filter in meshObj.GetComponents <MeshFilter>())
                {
                    meshBounds.Encapsulate(filter.sharedMesh.bounds);
                }
                HostedDebugLog.Fine(this, "Mesh bounds: {0}", meshBounds);
                if (meshBounds.extents.magnitude < float.Epsilon)
                {
                    HostedDebugLog.Warning(
                        this, "The mesh bounds are zero, not adding any collider: {0}", meshTransform);
                    return;
                }

                // Add collider basing on the requested type.
                if (primitiveShape == PrimitiveType.Cube)
                {
                    UnityEngine.Object.DestroyImmediate(meshObj.GetComponent <Collider>());
                    var collider = meshObj.AddComponent <BoxCollider>();
                    collider.center = meshBounds.center;
                    collider.size   = meshBounds.size;
                    HostedDebugLog.Info(this, "Added a cube collider at {0}: center={1}, size={2}",
                                        meshTransform, collider.center, collider.size);
                }
                else if (primitiveShape == PrimitiveType.Capsule)
                {
                    UnityEngine.Object.DestroyImmediate(meshObj.GetComponent <Collider>());
                    // TODO(ihsoft): Choose direction so that the volume is minimized.
                    var collider = meshObj.AddComponent <CapsuleCollider>();
                    collider.center    = meshBounds.center;
                    collider.direction = 2; // Z axis
                    collider.height    = meshBounds.size.z;
                    collider.radius    = Mathf.Min(meshBounds.extents.x, meshBounds.extents.y);
                    HostedDebugLog.Info(
                        this, "Added a capsule collider at {0}: center={1}, height={2}, radius={3}",
                        meshTransform, collider.center, collider.height, collider.radius);
                }
                else if (primitiveShape == PrimitiveType.Sphere)
                {
                    UnityEngine.Object.DestroyImmediate(meshObj.GetComponent <Collider>());
                    var collider = meshObj.AddComponent <SphereCollider>();
                    collider.center = meshBounds.center;
                    collider.radius = Mathf.Min(
                        meshBounds.extents.x, meshBounds.extents.y, meshBounds.extents.z);
                    HostedDebugLog.Info(
                        this, "Added a spehere collider at {0}: center={1}, radius={2}",
                        meshTransform, collider.center, collider.radius);
                }
                else
                {
                    DebugEx.Error("Unsupported collider: {0}. Ignoring", primitiveShape);
                }
            }
        }