static IEnumerator WaitAndMakeLonePart(Part newPart, OnPartReady onPartReady) { Debug.Log("[ModuleMissileRearm]: Create lone part vessel for {0}" + newPart); string originatingVesselName = newPart.vessel.vesselName; newPart.physicalSignificance = Part.PhysicalSignificance.NONE; newPart.PromoteToPhysicalPart(); newPart.Unpack(); newPart.disconnect(true); Vessel newVessel = newPart.gameObject.AddComponent <Vessel>(); newVessel.id = Guid.NewGuid(); if (newVessel.Initialize(false)) { newVessel.vesselName = Vessel.AutoRename(newVessel, originatingVesselName); newVessel.IgnoreGForces(10); newVessel.currentStage = StageManager.RecalculateVesselStaging(newVessel); newPart.setParent(null); } yield return(new WaitWhile(() => !newPart.started && newPart.State != PartStates.DEAD)); Debug.Log("[ModuleMissileRearm]: Part {0} is in state {1}" + newPart + newPart.State); if (newPart.State == PartStates.DEAD) { Debug.Log("[ModuleMissileRearm]: Part {0} has died before fully instantiating" + newPart); yield break; } if (onPartReady != null) { onPartReady(newPart); } }
static IEnumerator WaitAndCouple(Part newPart, string srcAttachNodeId, AttachNode tgtAttachNode, OnPartReady onPartReady, bool createPhysicsless = false) { var tgtPart = newPart.parent; if (createPhysicsless) { newPart.PhysicsSignificance = 1; // Disable physics on the part. } // Create proper attach nodes. Debug.Log("[ModuleMissileRearm]: Attach new part {0} to {1}: srcNodeId={2}, tgtNode={3}" + newPart + newPart.vessel + srcAttachNodeId); var srcAttachNode = GetAttachNodeById(newPart, srcAttachNodeId); srcAttachNode.attachedPart = tgtPart; srcAttachNode.attachedPartId = tgtPart.flightID; if (tgtAttachNode != null) { tgtAttachNode.attachedPart = newPart; tgtAttachNode.attachedPartId = newPart.flightID; } // When target, source or both are docking ports force them into state PreAttached. It's the // most safe state that simulates behavior of parts attached in the editor. var srcDockingNode = GetDockingNode(newPart, attachNodeId: srcAttachNodeId); if (srcDockingNode != null) { // Source part is not yet started. It's functionality is very limited. srcDockingNode.state = "PreAttached"; srcDockingNode.dockedPartUId = 0; srcDockingNode.dockingNodeModuleIndex = 0; Debug.Log("[ModuleMissileRearm]: Force new node {0} to state {1}" + newPart + srcDockingNode.state); } var tgtDockingNode = GetDockingNode(tgtPart, attachNode: tgtAttachNode); if (tgtDockingNode != null) { CoupleDockingPortWithPart(tgtDockingNode); } // Wait until part is started. Keep it in position till it happen. Debug.Log("[ModuleMissileRearm]: Wait for part {0} to get alive...", newPart); newPart.transform.parent = tgtPart.transform; var relPos = newPart.transform.localPosition; var relRot = newPart.transform.localRotation; if (newPart.PhysicsSignificance != 1) { // Mangling with colliders on physicsless parts may result in camera effects. var childColliders = newPart.GetComponentsInChildren <Collider>(includeInactive: false); CollisionManager.IgnoreCollidersOnVessel(tgtPart.vessel, childColliders); } while (!newPart.started && newPart.State != PartStates.DEAD) { yield return(new WaitForFixedUpdate()); if (newPart.rb != null) { newPart.rb.position = newPart.parent.transform.TransformPoint(relPos); newPart.rb.rotation = newPart.parent.transform.rotation * relRot; newPart.rb.velocity = newPart.parent.Rigidbody.velocity; newPart.rb.angularVelocity = newPart.parent.Rigidbody.angularVelocity; } } newPart.transform.parent = newPart.transform; Debug.Log("[ModuleMissileRearm]: Part {0} is in state {1}" + newPart + newPart.State); if (newPart.State == PartStates.DEAD) { Debug.Log("[ModuleMissileRearm]: Part {0} has died before fully instantiating", newPart); yield break; } // Complete part initialization. newPart.Unpack(); newPart.InitializeModules(); // Notify game about a new part that has just "coupled". GameEvents.onPartCouple.Fire(new GameEvents.FromToAction <Part, Part>(newPart, tgtPart)); tgtPart.vessel.ClearStaging(); GameEvents.onVesselPartCountChanged.Fire(tgtPart.vessel); newPart.vessel.checkLanded(); newPart.vessel.currentStage = StageManager.RecalculateVesselStaging(tgtPart.vessel) + 1; GameEvents.onVesselWasModified.Fire(tgtPart.vessel); newPart.CheckBodyLiftAttachment(); if (onPartReady != null) { onPartReady(newPart); } }
/// <summary>Creates a new part from the config.</summary> /// <param name="partConfig">Config to read part from.</param> /// <param name="position">Initial position of the new part.</param> /// <param name="rotation">Initial rotation of the new part.</param> /// <param name="fromPart"></param> /// <param name="coupleToPart">Optional. Part to couple new part to.</param> /// <param name="srcAttachNodeId"> /// Optional. Attach node ID on the new part to use for coupling. It's required if coupling to /// part is requested. /// </param> /// <param name="tgtAttachNode"> /// Optional. Attach node on the target part to use for coupling. It's required if /// <paramref name="srcAttachNodeId"/> specifies a stack node. /// </param> /// <param name="onPartReady"> /// Callback to call when new part is fully operational and its joint is created (if any). It's /// undetermined how long it may take before the callback is called. The calling code must expect /// that there will be several frame updates and at least one fixed frame update. /// </param> /// <param name="createPhysicsless"> /// Tells if new part must be created without rigidbody and joint. It's only used to create /// equippable parts. Any other use-case is highly unlikely. /// </param> /// <returns></returns> public static Part CreatePart( ConfigNode partConfig, Vector3 position, Quaternion rotation, Part fromPart, Part coupleToPart = null, string srcAttachNodeId = null, AttachNode tgtAttachNode = null, OnPartReady onPartReady = null, bool createPhysicsless = false) { // Sanity checks for the parameters. if (coupleToPart != null) { if (srcAttachNodeId == null || srcAttachNodeId == "srfAttach" && tgtAttachNode != null || srcAttachNodeId != "srfAttach" && (tgtAttachNode == null || tgtAttachNode.id == "srfAttach")) { // Best we can do is falling back to surface attach. srcAttachNodeId = "srfAttach"; tgtAttachNode = null; } } var refVessel = coupleToPart != null ? coupleToPart.vessel : fromPart.vessel; var partNodeCopy = new ConfigNode(); partConfig.CopyTo(partNodeCopy); var snapshot = new ProtoPartSnapshot(partNodeCopy, refVessel.protoVessel, HighLogic.CurrentGame); if (HighLogic.CurrentGame.flightState.ContainsFlightID(snapshot.flightID) || snapshot.flightID == 0) { snapshot.flightID = ShipConstruction.GetUniqueFlightID(HighLogic.CurrentGame.flightState); } snapshot.parentIdx = coupleToPart != null?refVessel.parts.IndexOf(coupleToPart) : 0; snapshot.position = position; snapshot.rotation = rotation; snapshot.stageIndex = 0; snapshot.defaultInverseStage = 0; snapshot.seqOverride = -1; snapshot.inStageIndex = -1; snapshot.attachMode = srcAttachNodeId == "srfAttach" ? (int)AttachModes.SRF_ATTACH : (int)AttachModes.STACK; snapshot.attached = true; snapshot.flagURL = fromPart.flagURL; var newPart = snapshot.Load(refVessel, false); refVessel.Parts.Add(newPart); newPart.transform.position = position; newPart.transform.rotation = rotation; newPart.missionID = fromPart.missionID; newPart.UpdateOrgPosAndRot(newPart.vessel.rootPart); if (coupleToPart != null) { // Wait for part to initialize and then fire ready event. Debug.Log("[ModuleMissileRearm]: Ready to error" + newPart + srcAttachNodeId + tgtAttachNode); newPart.StartCoroutine( WaitAndCouple(newPart, srcAttachNodeId, tgtAttachNode, onPartReady, createPhysicsless: createPhysicsless)); } else { // Create new part as a separate vessel. newPart.StartCoroutine(WaitAndMakeLonePart(newPart, onPartReady)); } return(newPart); }