示例#1
0
 /// <summary>
 /// Goes thru the parts on the source and target vessels, and tries to restore the coupling
 /// between the parts.
 /// </summary>
 /// <remarks>
 /// Any linking module on the source or the target vessel, which is linked and in the docking
 /// mode, will be attempted to use to restore the vessels coupling. This work will be done at the
 /// end of frame to let the other logic to cleanup.
 /// </remarks>
 /// <param name="tgtPart">
 /// The former target part that was holding the coupling with this part.
 /// </param>
 void DelegateCouplingRole(Part tgtPart)
 {
     AsyncCall.CallOnEndOfFrame(this, () => {
         var candidates = new List <ILinkJoint>()
                          .Concat(vessel.parts
                                  .SelectMany(p => p.Modules.OfType <ILinkJoint>())
                                  .Where(j => !ReferenceEquals(j, this) && j.coupleOnLinkMode && j.isLinked &&
                                         j.linkTarget.part.vessel == tgtPart.vessel))
                          .Concat(tgtPart.vessel.parts
                                  .SelectMany(p => p.Modules.OfType <ILinkJoint>())
                                  .Where(j => j.coupleOnLinkMode && j.isLinked && j.linkTarget.part.vessel == vessel));
         foreach (var joint in candidates)
         {
             HostedDebugLog.Fine(this, "Trying to couple via: {0}", joint);
             if (joint.SetCoupleOnLinkMode(true))
             {
                 HostedDebugLog.Info(this, "The coupling role is delegated to: {0}", joint);
                 return;
             }
         }
         if (candidates.Any())
         {
             HostedDebugLog.Warning(this, "None of the found candidates took the coupling role");
         }
     });
 }
示例#2
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);
                if (closestSource.LinkToTarget(LinkActorType.Player, this))
                {
                    var cableJoint = closestSource.linkJoint as ILinkCableJoint;
                    if (cableJoint != null)
                    {
                        // By default, the cable joints set the length limit to the actual distance.
                        cableJoint.SetCableLength(float.PositiveInfinity);
                    }
                    var updatableMenu = closestSource as IHasContextMenu;
                    if (updatableMenu != null)
                    {
                        // Let the module know that we've changed the values.
                        updatableMenu.UpdateContextMenu();
                    }
                }
                else
                {
                    UISoundPlayer.instance.Play(KASAPI.CommonConfig.sndPathBipWrong);
                }
            }
        }
示例#3
0
 /// <inheritdoc/>
 public void OnJointBreak(float breakForce)
 {
     HostedDebugLog.Info(gameObject.transform, "Joint is broken with force {0}. Notifying part {1}",
                         breakForce, hostPart);
     hostPart.FindModulesImplementing <IKasJointEventsListener>()
     .ForEach(x => x.OnKASJointBreak(gameObject, breakForce));
 }
示例#4
0
        /// <inheritdoc/>
        protected override void CreatePartModel()
        {
            CreateLeverModels();
            CreatePistonModels();
            UpdateValuesFromModel();
            // Log basic part values to help part's designers.
            HostedDebugLog.Info(this,
                                "Procedural model: minLinkLength={0}, maxLinkLength={1}, attachNodePosition.Y={2},"
                                + " pistonLength={3}, outerPistonDiameter={4}",
                                minLinkLength, maxLinkLength,
                                Hierarchy.FindTransformInChildren(srcStrutJoint, PivotAxleTransformName).position.y,
                                pistonLength, outerPistonDiameter);

            // Init parked state. It must go after all the models are created.
            if (parkedOrientations.Count > 0)
            {
                persistedParkedOrientation = parkedOrientations[0].direction;
            }
            if (Mathf.Approximately(persistedParkedLength, 0))
            {
                persistedParkedLength = minLinkLength;
            }

            UpdateLinkLengthAndOrientation();
        }
示例#5
0
 /// <summary>Localizes the modules in the part and in all of its children parts.</summary>
 /// <param name="rootPart">The root part to start from.</param>
 static void UpdateLocalizationInPartHierarchy(Part rootPart)
 {
     HostedDebugLog.Info(rootPart, "EDITOR: Load localizations for the existing part from {0}",
                         LibraryLoader.assemblyVersionStr);
     UpdateLocalizationInPartModules(rootPart);
     rootPart.children.ForEach(UpdateLocalizationInPartHierarchy);
 }
示例#6
0
 /// <inheritdoc/>
 public virtual void OnPartDie()
 {
     if (isLinked)
     {
         HostedDebugLog.Info(this, "Part has died. Drop the link to: {0}", linkSource);
         linkSource.BreakCurrentLink(LinkActorType.Physics);
     }
 }
 public override void OnAwake()
 {
     base.OnAwake();
     // The logging below will identify the owning part instance.
     HostedDebugLog.Info(part, "Part is being created");
     // The logging below will identify the part instance and the specific module in it.
     HostedDebugLog.Info(this, "Module created");
 }
示例#8
0
 /// <summary>Reacts on the vessel destruction and break the link if needed.</summary>
 /// <remarks>This event can get called from the physics callbacks.</remarks>
 /// <param name="targetVessel">The vessel that is being destroyed.</param>
 void OnVesselWillDestroyGameEvent(Vessel targetVessel)
 {
     if (isLinked && vessel != linkTarget.part.vessel &&
         (targetVessel == vessel || targetVessel == linkTarget.part.vessel))
     {
         HostedDebugLog.Info(
             this, "Drop the link due to the peer vessel destruction: {0}", targetVessel);
         BreakCurrentLink(LinkActorType.Physics);
     }
 }
示例#9
0
        public override void OnAwake()
        {
            base.OnAwake();

            // This will load the localized string and print it into the log.
            HostedDebugLog.Info(this, msg1.Format("Blah", 123));

            // The next example will only work if there is a localizable string defined. Otherwise, it will
            // print the tag.
            HostedDebugLog.Info(this, msg2.Format("Blah", 123));
        }
示例#10
0
 public virtual void ReturnConnectorEvent()
 {
     if (FlightGlobals.ActiveVessel.isEVA &&
         linkTarget != null && linkTarget.part.vessel == FlightGlobals.ActiveVessel)
     {
         BreakCurrentLink(LinkActorType.Player);
         SetConnectorState(ConnectorState.Locked);
         HostedDebugLog.Info(
             this, "{0} has returned the winch connector", FlightGlobals.ActiveVessel.vesselName);
     }
 }
        /// <summary>Replaces stock light dimming animation to properly adjust emissive color.</summary>
        /// <param name="animation">Animation object to fix.</param>
        void ReplaceLightOnOffAnimation(Animation animation)
        {
            HostedDebugLog.Info(this, "Replacing animation clip with {0}", animation.clip.name);
            var clip = animation.clip;

            clip.ClearCurves();
            clip.SetCurve(EmissiveLensModelPath, typeof(Material), "_EmissiveColor.a",
                          AnimationCurve.EaseInOut(0, 0, 1.0f, 1.0f));
            clip.SetCurve(lightName, typeof(Light), "m_Intensity",
                          AnimationCurve.EaseInOut(0, 0, 1.0f, 1.0f));
        }
示例#12
0
 public virtual void ReturnConnectorEvent()
 {
     if (FlightGlobals.ActiveVessel.isEVA &&
         linkTarget != null && linkTarget.part.vessel == FlightGlobals.ActiveVessel)
     {
         var kerbalTarget = FlightGlobals.ActiveVessel.rootPart.Modules.OfType <ILinkTarget>()
                            .FirstOrDefault(t => ReferenceEquals(t.linkSource, this));
         BreakCurrentLink(LinkActorType.Player);
         SetConnectorState(ConnectorState.Locked);
         HostedDebugLog.Info(
             this, "{0} has returned the winch connector", FlightGlobals.ActiveVessel.vesselName);
     }
 }
示例#13
0
        /// <summary>Logically links the source and the target, and starts the renderer.</summary>
        /// <remarks>It's always called <i>before</i> the physical link updates.</remarks>
        /// <param name="target">The target to link with.</param>
        protected virtual void LogicalLink(ILinkTarget target)
        {
            HostedDebugLog.Info(this, "Linking to target: {0}, actor={1}", target, linkActor);
            var linkInfo = new KasLinkEventImpl(this, target, linkActor);

            otherPeer             = target;
            linkTarget.linkSource = this;
            linkState             = LinkState.Linked;
            linkRenderer.StartRenderer(nodeTransform, linkTarget.nodeTransform);
            KASAPI.KasEvents.OnLinkCreated.Fire(linkInfo);
            part.Modules.OfType <ILinkStateEventListener>().ToList()
            .ForEach(x => x.OnKASLinkedState(linkInfo, isLinked: true));
        }
示例#14
0
        public override void OnAwake()
        {
            base.OnAwake();

            // This will load the localized string and print it into the log.
            HostedDebugLog.Info(this, msg1.Format());

            // The next example will only work if there is a localizable string defined. Otherwise, it will
            // print the tag.
            HostedDebugLog.Info(this, msg2.Format());

            // A simple message can be just casted to string to get the localized content.
            PrintString(msg1);
        }
示例#15
0
 /// <inheritdoc cref="IPartModule.OnLoad" />
 public override void OnLoad(ConfigNode node)
 {
     ConfigAccessor.ReadPartConfig(this, cfgNode: node);
     ConfigAccessor.ReadFieldsFromNode(node, GetType(), this, StdPersistentGroups.PartPersistant);
     base.OnLoad(node);
     if (vessel == null && PartLoader.Instance.IsReady())
     {
         HostedDebugLog.Info(this, "EVA construction part loaded");
         OnEvaPartLoaded();
     }
     if (!_moduleSettingsLoaded)
     {
         _moduleSettingsLoaded = true;
         InitModuleSettings();
     }
 }
示例#16
0
 /// <summary>Converts a physical connector back into the physicsless model.</summary>
 /// <remarks>It's a cleanup method that must always succeed.</remarks>
 void StopPhysicsOnConnector()
 {
     if (connectorObj == null || !linkRenderer.isStarted)
     {
         return; // Nothing to do.
     }
     HostedDebugLog.Info(this, "Make the cable connector non-physical");
     linkRenderer.StopRenderer();
     cableJoint.StopPhysicalHead();
     KASInternalPhysicalConnector.Demote(connectorObj.gameObject, false);
     Destroy(connectorObj.gameObject);
     connectorObj  = null;
     part.mass    += connectorMass;
     part.rb.mass += connectorMass;
     SaveConnectorModelPosAndRot();
 }
示例#17
0
        /// <summary>
        /// Checks if the cable connector can be locked without triggering significant physical forces.
        /// </summary>
        /// <param name="logCheckResult">
        /// If <c>true</c> then the result of the check will be logged.
        /// </param>
        /// <returns>
        /// <c>true</c> if projection of the position and direction of the connector, and whatever is
        /// attached to it, won't deal a significant disturbance to the system.
        /// </returns>
        bool CheckIsConnectorAligned(bool logCheckResult)
        {
            // Check the pre-conditions.
            if (cableJoint.deployedCableLength > Mathf.Epsilon || // Cable is not fully retracted.
                cableJoint.realCableLength > connectorLockMaxErrorDist) // Not close enough.
            {
                if (logCheckResult)
                {
                    HostedDebugLog.Info(this, "Connector cannot lock, the preconditions failed:"
                                        + " maxLength={0}, realLength={1}, isLinked={2}",
                                        cableJoint.deployedCableLength,
                                        cableJoint.realCableLength,
                                        isLinked);
                }
                return(false);
            }
            // The alignment doesn't matter if the connector is not attached to anything.
            if (!isLinked)
            {
                if (logCheckResult)
                {
                    HostedDebugLog.Info(this, "Unplugged connector is allowed to lock");
                }
                return(true);
            }
            // Check if the alignment error is small enough to not awake Kraken on dock.
            var fwdAngleErr =
                180 - Vector3.Angle(GetConnectorModelPipeAnchor().forward, nodeTransform.forward);

            if (fwdAngleErr > connectorLockMaxErrorDir)
            {
                if (logCheckResult)
                {
                    HostedDebugLog.Info(
                        this, "Plugged connector align error: yaw/pitch={0}",
                        fwdAngleErr);
                }
                return(false);
            }

            if (logCheckResult)
            {
                HostedDebugLog.Info(this, "Plugged connector is allowed to lock");
            }
            return(true);
        }
示例#18
0
 public void OnSave(ConfigNode node)
 {
     node.AddValue("partName", availablePart.name);
     node.AddValue("slot", slot);
     ConfigAccessor.WriteFieldsIntoNode(
         node, GetType(), this, group: StdPersistentGroups.PartPersistant);
     // Items in pod and container may have equipped status True but they are not actually equipped,
     // so there is no equipped part.
     if (equipped && equippedPart != null &&
         (equipMode == EquipMode.Part || equipMode == EquipMode.Physic))
     {
         HostedDebugLog.Info(
             inventory, "Update config node of equipped part: {0}", availablePart.name);
         partNode = KISAPI.PartNodeUtils.PartSnapshot(equippedPart);
     }
     partNode.CopyTo(node.AddNode("PART"));
 }
示例#19
0
 /// <inheritdoc/>
 public override void OnLoad(ConfigNode node)
 {
     base.OnLoad(node);
     if (part.Resources.Count == 0)
     {
         HostedDebugLog.Error(this, "No resources on the canister! This won't work.");
         return;
     }
     if (part.Resources.Count > 1)
     {
         HostedDebugLog.Error(this, "Too many resources on the canister! The first one will be used.");
     }
     _mainResourceName = part.Resources[0].resourceName;
     if (_mainResourceName != StockResourceNames.EvaPropellant)
     {
         HostedDebugLog.Info(this, "Using a customized resource type: {0}", _mainResourceName);
     }
 }
示例#20
0
        /// <summary>
        /// Logically un-links the source and the current target, and stops the renderer.
        /// </summary>
        /// <remarks>It's always called <i>after</i> the physical link updates.</remarks>
        /// <param name="actorType">The actor which has initiated the un-linking.</param>
        protected virtual void LogicalUnlink(LinkActorType actorType)
        {
            HostedDebugLog.Info(this, "Un-linking from target: {0}, actor={1}", linkTarget, actorType);
            linkActor = actorType;
            var linkInfo = new KasLinkEventImpl(this, linkTarget, actorType);

            linkRenderer.StopRenderer();
            SetLinkState(LinkState.Available);
            if (linkTarget != null)
            {
                linkTarget.linkSource = null;
                SetOtherPeer(null);
            }
            linkActor = LinkActorType.None;
            KASAPI.KasEvents.OnLinkBroken.Fire(linkInfo);
            part.Modules.OfType <ILinkStateEventListener>().ToList()
            .ForEach(x => x.OnKASLinkedState(linkInfo, isLinked: false));
        }
示例#21
0
        public void Unequip(ActorType actorType = ActorType.API)
        {
            if (!prefabModule)
            {
                return;
            }
            // This must be the first thing to happen to prevent the other handlers to trigger.
            equipped = false;
            if (equipMode == EquipMode.Model)
            {
                UnityEngine.Object.Destroy(equippedGameObj);
            }
            if (equipMode == EquipMode.Part || equipMode == EquipMode.Physic)
            {
                HostedDebugLog.Info(inventory, "Update config node of the equipped part: {0}", equippedPart);
                partNode = KISAPI.PartNodeUtils.PartSnapshot(equippedPart);
                equippedPart.Die();
            }
            evaTransform    = null;
            equippedPart    = null;
            equippedGameObj = null;
            if (actorType == ActorType.Player)
            {
                UISoundPlayer.instance.Play(prefabModule.moveSndPath);
            }
            prefabModule.OnUnEquip(this);

            // Return back the stock meshes if the custom helmet is unequipped.
            if (equipSlot == HelmetSlotName)
            {
                var kerbalModule = inventory.part.FindModuleImplementing <KerbalEVA>();
                if (kerbalModule.helmetTransform != null)
                {
                    for (var i = 0; i < kerbalModule.helmetTransform.childCount; i++)
                    {
                        kerbalModule.helmetTransform.GetChild(i).gameObject.SetActive(true);
                    }
                }
                else
                {
                    DebugEx.Warning("Kerbal model doesn't have helmet transform: {0}", inventory.part);
                }
            }
        }
示例#22
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);
                }
            });
        }
示例#23
0
        /// <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++;
            }
        }
示例#24
0
 /// <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;
     }
 }
示例#25
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 (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);
                }
            });
        }
示例#26
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();
        }
示例#27
0
 /// <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);
 }
示例#28
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);
        }
示例#29
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.
        }
示例#30
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);
            });

            // 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);
        }