public void Unequip()
 {
     if (!prefabModule)
     {
         return;
     }
     if (equipMode == EquipMode.Model)
     {
         UnityEngine.Object.Destroy(equippedGameObj);
         if (prefabModule.equipRemoveHelmet)
         {
             inventory.SetHelmet(true);
         }
     }
     if (equipMode == EquipMode.Part || equipMode == EquipMode.Physic)
     {
         Logger.logInfo("Update config node of equipped part: {0}", availablePart.title);
         partNode.ClearData();
         KIS_Shared.PartSnapshot(equippedPart).CopyTo(partNode);
         equippedPart.Die();
     }
     evaTransform    = null;
     equippedPart    = null;
     equippedGameObj = null;
     equipped        = false;
     PlaySound(prefabModule.moveSndPath);
     prefabModule.OnUnEquip(this);
 }
Beispiel #2
0
 /// <summary>Handles keyboard input.</summary>
 private void UpdateKey()
 {
     if (isRunning)
     {
         if (Input.GetKeyDown(KeyCode.Escape) ||
             Input.GetKeyDown(KeyCode.Return))
         {
             Logger.logInfo("Cancel key pressed, stop eva attach mode");
             StopPointer();
             SendPointerClick(PointerTarget.Nothing, Vector3.zero, Quaternion.identity, null, null);
         }
         if (GameSettings.Editor_toggleSymMethod.GetKeyDown()) // "R" by default.
         {
             if (pointerTarget != PointerTarget.PartMount && attachNodes.Count() > 1)
             {
                 attachNodeIndex++;
                 if (attachNodeIndex > (attachNodes.Count - 1))
                 {
                     attachNodeIndex = 0;
                 }
                 Logger.logInfo("Attach node index changed to: {0}", attachNodeIndex);
                 UpdatePointerAttachNode();
                 ResetMouseOver();
                 SendPointerState(pointerTarget, PointerState.OnChangeAttachNode, null, null);
             }
             else
             {
                 ScreenMessaging.ShowInfoScreenMessage("This part has only one attach node!");
                 audioBipWrong.Play();
             }
         }
     }
 }
Beispiel #3
0
        /// <summary>Makes a game object to represent currently dragging assembly.</summary>
        /// <remarks>It's a very expensive operation.</remarks>
        static void MakePointer()
        {
            DestroyPointer();

            // Make pointer node transformations.
            if (pointerNodeTransform)
            {
                pointerNodeTransform.gameObject.DestroyGameObject();
            }
            pointerNodeTransform = new GameObject("KISPointerPartNode").transform;

            // Deatch will decouple from the parent so, ask to ignore it when looking for the nodes.
            attachNodes =
                KIS_Shared.GetAvailableAttachNodes(partToAttach, ignoreAttachedPart: partToAttach.parent);
            if (!attachNodes.Any())
            {
                //TODO: When there are no nodes try finding ones in the parent or in the children.
                // Ideally, the caller should have checked if this part has free nodes. Now the only
                // way is to pick *any* node. The surface one always exists so, it's a good
                // candidate. Though, for many details it may result in a weird representation.
                Logger.logError("Part {0} has no free nodes, use {1}",
                                partToAttach, partToAttach.srfAttachNode);
                attachNodes.Add(partToAttach.srfAttachNode);
            }
            attachNodeIndex = 0; // Expect that first node is the best default.

            UpdatePointerAttachNode();

            // Make pointer renderer.
            var combines = new List <CombineInstance>();

            CollectMeshesFromAssembly(partToAttach, combines);

            // Create one filter per mesh in the hierarhcy. Simple combining all meshes into one
            // larger mesh may have weird representation artifacts on different video cards.
            pointer = new GameObject("KISPointer");
            foreach (var combine in combines)
            {
                var mesh = new Mesh();
                mesh.CombineMeshes(new[] { combine });
                var childObj = new GameObject("KISPointerChildMesh");

                var meshRenderer = childObj.AddComponent <MeshRenderer>();
                meshRenderer.shadowCastingMode = ShadowCastingMode.Off;
                meshRenderer.receiveShadows    = false;

                var filter = childObj.AddComponent <MeshFilter>();
                filter.sharedMesh = mesh;

                childObj.transform.parent = pointer.transform;
            }
            allModelMr = pointer.GetComponentsInChildren <MeshRenderer>().ToList();
            foreach (var mr in allModelMr)
            {
                mr.material = new Material(Shader.Find("Transparent/Diffuse"));
            }
            pointerNodeTransform.parent = pointer.transform;

            Logger.logInfo("New pointer created");
        }
Beispiel #4
0
 public static void StopPointer()
 {
     Logger.logInfo("StopPointer()");
     running = false;
     ResetMouseOver();
     InputLockManager.RemoveControlLock("KISpointer");
     DestroyPointer();
     allowedAttachmentParts = allowedAttachmentParts; // Clear selection.
 }
Beispiel #5
0
 void Awake()
 {
     audioGo = new GameObject();
     Logger.logInfo("Loading UI sounds for KIS...");
     InitSound(bipWrongSndPath, out audioBipWrong);
     InitSound(clickSndPath, out audioClick);
     InitSound(attachPartSndPath, out audioAttach);
     instance = this;
 }
        void InitConfig(AvailablePart availablePart, ModuleKISInventory inventory, float quantity)
        {
            this.inventory = inventory;
            this.quantity  = quantity;
            prefabModule   = availablePart.partPrefab.GetComponent <ModuleKISItem>();
            volume         = KIS_Shared.GetPartVolume(availablePart);
            cost           = GetCost();

            // Set launchID
            if (partNode.HasValue("launchID"))
            {
                if (int.Parse(this.partNode.GetValue("launchID")) == 0)
                {
                    partNode.SetValue("launchID", this.inventory.part.launchID.ToString(), true);
                }
            }
            else
            {
                partNode.SetValue("launchID", this.inventory.part.launchID.ToString(), true);
            }

            if (prefabModule)
            {
                equipable           = prefabModule.equipable;
                stackable           = prefabModule.stackable;
                equipSlot           = prefabModule.equipSlot;
                usableFromEva       = prefabModule.usableFromEva;
                usableFromContainer = prefabModule.usableFromContainer;
                usableFromPod       = prefabModule.usableFromPod;
                usableFromEditor    = prefabModule.usableFromEditor;
                carriable           = prefabModule.carriable;
            }
            int nonStackableModule = 0;

            foreach (PartModule pModule in availablePart.partPrefab.Modules)
            {
                if (!KISAddonConfig.stackableModules.Contains(pModule.moduleName))
                {
                    nonStackableModule++;
                }
            }
            if (nonStackableModule == 0 && GetResources().Count == 0)
            {
                Logger.logInfo(
                    "No non-stackable module or a resource found on the part, set the item as stackable");
                stackable = true;
            }
            if (KISAddonConfig.stackableList.Contains(availablePart.name) ||
                availablePart.name.IndexOf('.') != -1 &&
                KISAddonConfig.stackableList.Contains(availablePart.name.Replace('.', '_')))
            {
                Logger.logInfo("Part name present in settings.cfg (node StackableItemOverride),"
                               + " force item as stackable");
                stackable = true;
            }
        }
Beispiel #7
0
 public virtual void OnPartUnpack()
 {
     if (allowStaticAttach == ItemAttachMode.Disabled || useExternalStaticAttach)
     {
         return;
     }
     if (staticAttached)
     {
         Logger.logInfo("Re-attach static object (OnPartUnpack)");
         GroundAttach();
     }
 }
Beispiel #8
0
 public void GroundDetach()
 {
     if (staticAttached)
     {
         Logger.logInfo("Removing static rigidbody and fixed joint on: {0}", this.part.partInfo.title);
         if (staticAttachJoint)
         {
             Destroy(staticAttachJoint);
         }
         staticAttachJoint = null;
         staticAttached    = false;
     }
 }
Beispiel #9
0
 /// <summary>Sets current pointer visible state.</summary>
 /// <remarks>
 /// Method expects all or none of the objects in the pointer to be visible: pointer
 /// visiblity state is determined by checking the first <c>MeshRenderer</c> only.
 /// </remarks>
 /// <param name="isVisible">New state.</param>
 /// <exception cref="InvalidOperationException">If pointer doesn't exist.</exception>
 private static void SetPointerVisible(bool isVisible)
 {
     foreach (var mr in pointer.GetComponentsInChildren <MeshRenderer>())
     {
         if (mr.enabled == isVisible &&
             mr.material.renderQueue == KIS_Shared.HighlighedPartRenderQueue)
         {
             return; // Abort if current state is already up to date.
         }
         mr.enabled = isVisible;
         mr.material.renderQueue = KIS_Shared.HighlighedPartRenderQueue;
     }
     Logger.logInfo("Pointer state set to: visibility={0}", isVisible);
 }
Beispiel #10
0
        public void Awake()
        {
            ConfigAccessor.ReadFieldsInType(GetType(), this);
            ConfigAccessor.ReadFieldsInType(typeof(ModuleKISInventory), instance: null);

            // Set inventory module for every eva kerbal
            Logger.logInfo("Set KIS config...");
            ConfigNode nodeSettings = GameDatabase.Instance.GetConfigNode("KIS/settings/KISConfig");

            if (nodeSettings == null)
            {
                Logger.logError("KIS settings.cfg not found or invalid !");
                return;
            }

            // Male Kerbal.
            UpdateEvaPrefab(PartLoader.getPartInfoByName(MaleKerbalEva), nodeSettings);
            // Female Kerbal.
            UpdateEvaPrefab(PartLoader.getPartInfoByName(FemaleKerbalEva), nodeSettings);

            // Set inventory module for every pod with crew capacity.
            Logger.logInfo("Loading pod inventories...");
            foreach (AvailablePart avPart in PartLoader.LoadedPartsList)
            {
                if (avPart.name == MaleKerbalEva || avPart.name == FemaleKerbalEva ||
                    avPart.name == RdKerbalEva ||
                    !avPart.partPrefab || avPart.partPrefab.CrewCapacity < 1)
                {
                    continue;
                }

                Logger.logInfo("Found part with CrewCapacity: {0}", avPart.name);
                for (int i = 0; i < avPart.partPrefab.CrewCapacity; i++)
                {
                    try {
                        var moduleInventory =
                            avPart.partPrefab.AddModule(typeof(ModuleKISInventory).Name) as ModuleKISInventory;
                        KIS_Shared.AwakePartModule(moduleInventory);
                        SetInventoryConfig(moduleInventory, nodeSettings);
                        moduleInventory.podSeat = i;
                        moduleInventory.invType = ModuleKISInventory.InventoryType.Pod;
                        Logger.logInfo("Pod inventory module(s) for seat {0} loaded successfully", i);
                    } catch {
                        Logger.logError("Pod inventory module(s) for seat {0} can't be loaded!", i);
                    }
                }
            }
        }
Beispiel #11
0
    void InitSound(string clipPath, out AudioSource source)
    {
        Logger.logInfo("Loading clip: {0}", clipPath);
        source              = audioGo.AddComponent <AudioSource>();
        source.volume       = GameSettings.UI_VOLUME;
        source.spatialBlend = 0; //set as 2D audiosource

        if (GameDatabase.Instance.ExistsAudioClip(clipPath))
        {
            source.clip = GameDatabase.Instance.GetAudioClip(clipPath);
        }
        else
        {
            Logger.logError("Cannot locate clip: {0}", clipPath);
        }
    }
        /// <summary>Fixes all structural links to another vessel(s).</summary>
        /// <remarks>
        /// Normally compound parts should handle decoupling themselves but sometimes they do it
        /// horribly wrong. For instance, stock strut connector tries to restore connection when
        /// part is re-attached to the former vessel which may produce a collision. This method
        /// deletes all compound parts with target pointing to a different vessel.
        /// </remarks>
        /// <param name="vessel">Vessel to fix links for.</param>
        // TODO: Break the link instead of destroying the part.
        // TODO: Handle KAS and other popular plugins connectors.
        public static void CleanupExternalLinks(Vessel vessel)
        {
            var parts = vessel.parts.FindAll(p => p is CompoundPart);

            Logger.logInfo("Check {0} compound part(s) in vessel: {1}", parts.Count(), vessel);
            foreach (var part in parts)
            {
                var compoundPart = part as CompoundPart;
                if (compoundPart.target && compoundPart.target.vessel != vessel)
                {
                    Logger.logInfo("Destroy compound part '{0}' which links '{1}' to '{2}'",
                                   compoundPart, compoundPart.parent, compoundPart.target);
                    compoundPart.Die();
                }
            }
        }
Beispiel #13
0
        /// <summary>Destroyes object(s) allocated to represent a pointer.</summary>
        /// <remarks>When making pointer for a complex hierarchy a lot of different resources may be
        /// allocated/dropped. Destroying each one of them can be too slow so, cleanup is done in
        /// one call to <c>UnloadUnusedAssets()</c>.
        /// <para>This method also destroys <see cref="pointerNodeTransform"/>.</para>
        /// </remarks>
        private static void DestroyPointer()
        {
            if (!pointer)
            {
                return; // Nothing to do.
            }
            pointer.DestroyGameObject();
            pointer = null;
            pointerNodeTransform.gameObject.DestroyGameObject();
            pointerNodeTransform = null;
            allModelMr.Clear();

            // On large assemblies memory consumption can be significant. Reclaim it.
            Resources.UnloadUnusedAssets();
            Logger.logInfo("Pointer destroyed");
        }
Beispiel #14
0
        /// <summary>Goes thru part assembly and collects all meshes in the hierarchy.</summary>
        /// <remarks>
        /// Returns shared meshes with the right transformations. No new objects are created.
        /// </remarks>
        /// <param name="assembly">An assembly to collect meshes from.</param>
        /// <param name="meshCombines">[out] Collected meshes.</param>
        /// <param name="worldTransform">A world transformation matrix to apply to every mesh after
        ///     it's translated into world's coordinates. If <c>null</c> then coordinates will be
        ///     calculated relative to the root part of the assembly.</param>
        private static void CollectMeshesFromAssembly(Part assembly,
                                                      ICollection <CombineInstance> meshCombines,
                                                      Matrix4x4?worldTransform = null)
        {
            // Always use world transformation from the root.
            var rootWorldTransform = worldTransform ?? assembly.transform.localToWorldMatrix.inverse;

            // Get all meshes from the part's model.
            var meshFilters = assembly.FindModelComponents <MeshFilter>();

            if (meshFilters.Count > 0)
            {
                Logger.logInfo("Found {0} children meshes in: {1}", meshFilters.Count, assembly);
                foreach (var meshFilter in meshFilters)
                {
                    var combine = new CombineInstance();
                    combine.mesh      = meshFilter.sharedMesh;
                    combine.transform = rootWorldTransform * meshFilter.transform.localToWorldMatrix;
                    meshCombines.Add(combine);
                }
            }

            // Skinned meshes are baked on every frame before rendering. Bake them to get current mesh
            // state.
            var skinnedMeshRenderers = assembly.FindModelComponents <SkinnedMeshRenderer>();

            if (skinnedMeshRenderers.Count > 0)
            {
                Logger.logInfo("Found {0} skinned meshes in: {1}", skinnedMeshRenderers.Count, assembly);
                foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
                {
                    var combine = new CombineInstance();
                    combine.mesh = new Mesh();
                    skinnedMeshRenderer.BakeMesh(combine.mesh);
                    combine.transform = rootWorldTransform * skinnedMeshRenderer.transform.localToWorldMatrix;
                    meshCombines.Add(combine);
                }
            }

            // Collect meshes from the children parts.
            foreach (Part child in assembly.children)
            {
                CollectMeshesFromAssembly(child, meshCombines, worldTransform: rootWorldTransform);
            }
        }
Beispiel #15
0
        IEnumerator WaitAndStaticAttach()
        {
            // Wait for part to become active in case of it came from inventory.
            while (!part.started && part.State != PartStates.DEAD)
            {
                yield return(new WaitForFixedUpdate());
            }
            part.vessel.Landed = true;

            Logger.logInfo("Create fixed joint attached to the world");
            if (staticAttachJoint)
            {
                Destroy(staticAttachJoint);
            }
            staticAttachJoint             = part.gameObject.AddComponent <FixedJoint>();
            staticAttachJoint.breakForce  = staticAttachBreakForce;
            staticAttachJoint.breakTorque = staticAttachBreakForce;
        }
        public static void CouplePart(Part srcPart, Part tgtPart, string srcAttachNodeID = null,
                                      AttachNode tgtAttachNode = null)
        {
            // Node links
            if (srcAttachNodeID != null)
            {
                if (srcAttachNodeID == "srfAttach")
                {
                    Logger.logInfo("Attach type: {0} | ID : {1}",
                                   srcPart.srfAttachNode.nodeType, srcPart.srfAttachNode.id);
                    srcPart.attachMode = AttachModes.SRF_ATTACH;
                    srcPart.srfAttachNode.attachedPart = tgtPart;
                }
                else
                {
                    AttachNode srcAttachNode = srcPart.FindAttachNode(srcAttachNodeID);
                    if (srcAttachNode != null)
                    {
                        Logger.logInfo("Attach type : {0} | ID : {1}",
                                       srcPart.srfAttachNode.nodeType, srcAttachNode.id);
                        srcPart.attachMode         = AttachModes.STACK;
                        srcAttachNode.attachedPart = tgtPart;
                        if (tgtAttachNode != null)
                        {
                            tgtAttachNode.attachedPart = srcPart;
                        }
                        else
                        {
                            Logger.logWarning("Target node is null");
                        }
                    }
                    else
                    {
                        Logger.logError("Source attach node not found !");
                    }
                }
            }
            else
            {
                Logger.logWarning("Missing source attach node !");
            }

            srcPart.Couple(tgtPart);
        }
 /// <summary>Adds default items to the pod's seats.</summary>
 /// <remarks>Items are only added to a part created in the editor. Thus, reacting on the editor
 /// event.</remarks>
 /// <param name="type">Unused.</param>
 /// <param name="p">A target part.</param>
 void OnEditPartCreate(ConstructionEventType type, Part p) {
   if (type != ConstructionEventType.PartCreated && type != ConstructionEventType.PartCopied) {
     return;
   }
   var inventories = p.GetComponents<ModuleKISInventory>();
   foreach (var inventory in inventories) {
     if (inventory.podSeat != -1 && ModuleKISInventory.defaultItemsForAllSeats.Count > 0) {
       Logger.logInfo(
           "Adding default item(s) into seat's {0} inventory of part {1}: {2}",
           inventory.podSeat, p.name, Logger.C2S(ModuleKISInventory.defaultItemsForAllSeats));
       AddItems(inventory, ModuleKISInventory.defaultItemsForAllSeats);
     }
     if (inventory.podSeat == 0 && ModuleKISInventory.defaultItemsForTheFirstSeat.Count > 0) {
       Logger.logInfo("Adding default item(s) into the first seat of part {0}: {1}",
                      p.name, Logger.C2S(ModuleKISInventory.defaultItemsForTheFirstSeat));
       AddItems(inventory, ModuleKISInventory.defaultItemsForTheFirstSeat);
     }
   }
 }
Beispiel #18
0
        public static void StartPointer(Part partToMoveAndAttach, OnPointerClick pClick,
                                        OnPointerState pState, Transform from = null)
        {
            if (!running)
            {
                Logger.logInfo("StartPointer()");
                customRot        = Vector3.zero;
                aboveDistance    = 0;
                partToAttach     = partToMoveAndAttach;
                sourceTransform  = from;
                running          = true;
                SendPointerClick = pClick;
                SendPointerState = pState;

                MakePointer();

                InputLockManager.SetControlLock(ControlTypes.ALLBUTCAMERAS, "KISpointer");
                allowedAttachmentParts = allowedAttachmentParts; // Apply selection.
            }
        }
 public void OnSave(ConfigNode node)
 {
     node.AddValue("partName", this.availablePart.name);
     node.AddValue("slot", slot);
     node.AddValue("quantity", quantity);
     node.AddValue("equipped", equipped);
     node.AddValue("resourceMass", resourceMass);
     node.AddValue("contentMass", contentMass);
     node.AddValue("contentCost", contentCost);
     if (inventoryName != "")
     {
         node.AddValue("inventoryName", inventoryName);
     }
     if (equipped && (equipMode == EquipMode.Part || equipMode == EquipMode.Physic))
     {
         Logger.logInfo("Update config node of equipped part: {0}", this.availablePart.title);
         partNode.ClearData();
         KIS_Shared.PartSnapshot(equippedPart).CopyTo(partNode);
     }
     partNode.CopyTo(node.AddNode("PART"));
 }
Beispiel #20
0
        void UpdateEvaPrefab(AvailablePart avPart, ConfigNode nodeSettings)
        {
            var prefab = avPart.partPrefab;

            // Adding module to EVA may cause an NPE but module update will still work.
            try {
                prefab.AddModule(typeof(ModuleKISInventory).Name);
            } catch (Exception ex) {
                Logger.logInfo(
                    "NOT A BUG! Ignoring error while adding ModuleKISInventory to {0}: {1}", prefab, ex);
            }
            try {
                prefab.AddModule(typeof(ModuleKISPickup).Name);
            } catch (Exception ex) {
                Logger.logInfo("NOT A BUG! Ignoring error adding ModuleKISPickup to {0}: {1}", prefab, ex);
            }

            // Setup inventory module for eva.
            var evaInventory = prefab.GetComponent <ModuleKISInventory>();

            KIS_Shared.AwakePartModule(evaInventory);
            if (evaInventory)
            {
                SetInventoryConfig(evaInventory, nodeSettings);
                evaInventory.invType = ModuleKISInventory.InventoryType.Eva;
                Logger.logInfo("Eva inventory module loaded successfully");
            }

            // Load KSP fields for ModuleKISPickup module.
            var nodeEvaPickup = nodeSettings.GetNode("EvaPickup");
            var evaPickup     = prefab.GetComponent <ModuleKISPickup>();

            KIS_Shared.AwakePartModule(evaPickup);
            if (evaPickup && nodeEvaPickup != null)
            {
                var fields = new BaseFieldList(evaPickup);
                fields.Load(nodeEvaPickup);
                Logger.logInfo("Eva pickup module loaded successfully");
            }
        }
        public void Drop(Part fromPart = null)
        {
            Logger.logInfo("Drop item");
            if (fromPart == null)
            {
                fromPart = inventory.part;
            }
            Quaternion rot;
            Vector3    pos;

            if (prefabModule)
            {
                rot = evaTransform.rotation * Quaternion.Euler(prefabModule.equipDir);
                pos = evaTransform.TransformPoint(prefabModule.equipPos);
            }
            else
            {
                rot = inventory.part.transform.rotation;
                pos = inventory.part.transform.position + new Vector3(0, 1, 0);
            }
            KIS_Shared.CreatePart(partNode, pos, rot, fromPart);
            StackRemove(1);
        }
        static IEnumerator WaitAndCouple(Part newPart, Part tgtPart  = null,
                                         string srcAttachNodeID      = null,
                                         AttachNode tgtAttachNode    = null,
                                         OnPartCoupled onPartCoupled = null)
        {
            // Get relative position & rotation
            Vector3    toPartLocalPos = Vector3.zero;
            Quaternion toPartLocalRot = Quaternion.identity;

            if (tgtPart)
            {
                if (tgtAttachNode == null)
                {
                    // Local position & rotation from part
                    toPartLocalPos = tgtPart.transform.InverseTransformPoint(newPart.transform.position);
                    toPartLocalRot =
                        Quaternion.Inverse(tgtPart.transform.rotation) * newPart.transform.rotation;
                }
                else
                {
                    // Local position & rotation from node (KAS winch connector)
                    toPartLocalPos =
                        tgtAttachNode.nodeTransform.InverseTransformPoint(newPart.transform.position);
                    toPartLocalRot =
                        Quaternion.Inverse(tgtAttachNode.nodeTransform.rotation) * newPart.transform.rotation;
                }
            }

            // Wait part to initialize
            while (!newPart.started && newPart.State != PartStates.DEAD)
            {
                Logger.logInfo("CreatePart - Waiting initialization of the part...");
                if (tgtPart)
                {
                    // Part stay in position
                    if (tgtAttachNode == null)
                    {
                        newPart.transform.position = tgtPart.transform.TransformPoint(toPartLocalPos);
                        newPart.transform.rotation = tgtPart.transform.rotation * toPartLocalRot;
                    }
                    else
                    {
                        newPart.transform.position = tgtAttachNode.nodeTransform.TransformPoint(toPartLocalPos);
                        newPart.transform.rotation = tgtAttachNode.nodeTransform.rotation * toPartLocalRot;
                    }
                }
                yield return(null);
            }
            // Part stay in position
            if (tgtAttachNode == null)
            {
                newPart.transform.position = tgtPart.transform.TransformPoint(toPartLocalPos);
                newPart.transform.rotation = tgtPart.transform.rotation * toPartLocalRot;
            }
            else
            {
                newPart.transform.position = tgtAttachNode.nodeTransform.TransformPoint(toPartLocalPos);
                newPart.transform.rotation = tgtAttachNode.nodeTransform.rotation * toPartLocalRot;
            }
            Logger.logInfo("CreatePart - Coupling part...");
            CouplePart(newPart, tgtPart, srcAttachNodeID, tgtAttachNode);

            if (onPartCoupled != null)
            {
                onPartCoupled(newPart, tgtPart, tgtAttachNode);
            }
        }
        public void Equip()
        {
            // Only equip EVA kerbals.
            if (!prefabModule || !inventory.vessel.isEVA)
            {
                return;
            }
            Logger.logInfo("Equip item {0}", this.availablePart.name);

            // Check skill if needed. Skip the check in sandbox modes.
            if (HighLogic.CurrentGame.Mode != Game.Modes.SANDBOX &&
                HighLogic.CurrentGame.Mode != Game.Modes.SCIENCE_SANDBOX &&
                !String.IsNullOrEmpty(prefabModule.equipSkill))
            {
                bool skillFound = false;
                List <ProtoCrewMember> protoCrewMembers = inventory.vessel.GetVesselCrew();
                foreach (var expEffect in protoCrewMembers[0].experienceTrait.Effects)
                {
                    if (expEffect.ToString().Replace("Experience.Effects.", "") == prefabModule.equipSkill)
                    {
                        skillFound = true;
                        break;
                    }
                }
                if (!skillFound)
                {
                    ScreenMessaging.ShowPriorityScreenMessage(
                        "This item can only be used by a kerbal with the skill : {0}",
                        prefabModule.equipSkill);
                    PlaySound(KIS_Shared.bipWrongSndPath);
                    return;
                }
            }

            // Check if already carried
            if (equipSlot != null)
            {
                KIS_Item equippedItem = inventory.GetEquipedItem(equipSlot);
                if (equippedItem != null)
                {
                    if (equippedItem.carriable)
                    {
                        ScreenMessaging.ShowPriorityScreenMessage(
                            "Cannot equip item, slot <{0}> already used for carrying {1}",
                            equipSlot, equippedItem.availablePart.title);
                        PlaySound(KIS_Shared.bipWrongSndPath);
                        return;
                    }
                    equippedItem.Unequip();
                }
            }

            if (equipMode == EquipMode.Model)
            {
                GameObject modelGo = availablePart.partPrefab.FindModelTransform("model").gameObject;
                equippedGameObj = UnityEngine.Object.Instantiate(modelGo);
                foreach (Collider col in equippedGameObj.GetComponentsInChildren <Collider>())
                {
                    UnityEngine.Object.DestroyImmediate(col);
                }
                evaTransform = null;
                var skmrs = new List <SkinnedMeshRenderer>(
                    inventory.part.GetComponentsInChildren <SkinnedMeshRenderer>());
                foreach (SkinnedMeshRenderer skmr in skmrs)
                {
                    if (skmr.name != prefabModule.equipMeshName)
                    {
                        continue;
                    }
                    foreach (Transform bone in skmr.bones)
                    {
                        if (bone.name == prefabModule.equipBoneName)
                        {
                            evaTransform = bone.transform;
                            break;
                        }
                    }
                }

                if (!evaTransform)
                {
                    Logger.logError("evaTransform not found ! ");
                    UnityEngine.Object.Destroy(equippedGameObj);
                    return;
                }
            }
            if (equipMode == EquipMode.Part || equipMode == EquipMode.Physic)
            {
                evaTransform = null;
                var skmrs = new List <SkinnedMeshRenderer>(
                    inventory.part.GetComponentsInChildren <SkinnedMeshRenderer>());
                foreach (SkinnedMeshRenderer skmr in skmrs)
                {
                    if (skmr.name != prefabModule.equipMeshName)
                    {
                        continue;
                    }
                    foreach (Transform bone in skmr.bones)
                    {
                        if (bone.name == prefabModule.equipBoneName)
                        {
                            evaTransform = bone.transform;
                            break;
                        }
                    }
                }

                if (!evaTransform)
                {
                    Logger.logError("evaTransform not found ! ");
                    return;
                }

                Part alreadyEquippedPart =
                    this.inventory.part.vessel.Parts.Find(p => p.partInfo.name == this.availablePart.name);
                if (alreadyEquippedPart)
                {
                    Logger.logInfo("Part: {0} already found on eva", availablePart.name);
                    equippedPart = alreadyEquippedPart;
                    OnEquippedPartCoupled(equippedPart);
                }
                else
                {
                    Vector3    equipPos = evaTransform.TransformPoint(prefabModule.equipPos);
                    Quaternion equipRot = evaTransform.rotation * Quaternion.Euler(prefabModule.equipDir);
                    equippedPart = KIS_Shared.CreatePart(
                        partNode, equipPos, equipRot, this.inventory.part, this.inventory.part, null, null,
                        OnEquippedPartCoupled);
                }
                if (equipMode == EquipMode.Part)
                {
                    equippedGameObj = equippedPart.gameObject;
                }
            }

            if (prefabModule.equipRemoveHelmet)
            {
                inventory.SetHelmet(false);
            }
            PlaySound(prefabModule.moveSndPath);
            equipped = true;
            prefabModule.OnEquip(this);
        }