public void UpdateContextMenu() { PartModuleUtils.SetupEvent( this, TogglePlayStateEvent, x => x.guiName = sndMainTune == null || !sndMainTune.isPlaying ? PlayMenuTxt : StopMenuTxt); }
/// <inheritdoc/> public virtual void UpdateContextMenu() { PartModuleUtils.SetupEvent(this, ToggleVesselsDockModeEvent, e => { if (linkJoint != null) { e.guiName = linkJoint.coupleOnLinkMode ? DockedModeMenuTxt : UndockedModeMenuTxt; if (coupleMode == CoupleMode.SetViaGUI) { e.active = coupleNode != null && (linkTarget == null || linkTarget.coupleNode != null); } else if (isLinked) { // Just in case show GUI if the link is established, and its couple mode contradicts the // joint setting. GUI will allow fixing it manually. e.active = coupleMode == CoupleMode.NeverCouple && linkJoint.coupleOnLinkMode || coupleMode == CoupleMode.AlwaysCoupled && !linkJoint.coupleOnLinkMode; } else { e.active = false; } } else { e.active = false; } }); }
/// <inheritdoc/> public override void UpdateContextMenu() { base.UpdateContextMenu(); deployedCableLengthMenuInfo = DistanceType.Format( cableJoint != null ? cableJoint.deployedCableLength : 0); PartModuleUtils.SetupEvent(this, ToggleExtendCableEvent, e => { e.active = linkState != LinkState.NodeIsBlocked; e.guiName = motorTargetSpeed > float.Epsilon ? StopExtendingMenuTxt : ExtendCableMenuTxt; }); PartModuleUtils.SetupEvent(this, ToggleRetractCableEvent, e => { e.active = linkState != LinkState.NodeIsBlocked; e.guiName = motorTargetSpeed < -float.Epsilon ? StopRetractingMenuTxt : RetractCableMenuTxt; }); PartModuleUtils.SetupEvent(this, InstantStretchEvent, e => { e.active = !isConnectorLocked && linkState != LinkState.NodeIsBlocked; }); PartModuleUtils.SetupEvent(this, ReleaseCableEvent, e => { e.active = linkState != LinkState.NodeIsBlocked; }); }
/// <inheritdoc/> public override void UpdateContextMenu() { base.UpdateContextMenu(); connectorStateMenuInfo = ConnectorStatesMsgLookup.Lookup(connectorState); PartModuleUtils.SetupEvent(this, ToggleVesselsDockModeEvent, e => { e.active &= !isConnectorLocked && linkState != LinkState.NodeIsBlocked; }); PartModuleUtils.SetupEvent(this, GrabConnectorEvent, e => { e.active = connectorState == ConnectorState.Locked && linkState != LinkState.NodeIsBlocked; if (_grabConnectorEventInject != null) { _grabConnectorEventInject.guiName = e.guiName; } }); PartModuleUtils.SetupEvent(this, ReturnConnectorEvent, e => { e.active = IsActiveEvaHoldingConnector(); }); PartModuleUtils.SetupEvent(this, DetachConnectorEvent, e => { e.active = isLinked; }); PartModuleUtils.SetupEvent(this, InstantLockConnectorEvent, e => { e.active = connectorState == ConnectorState.Deployed; }); if (_grabConnectorEventInject != null) { _grabConnectorEventInject.active = linkTarget != null && connectorState == ConnectorState.Plugged && FlightGlobals.ActiveVessel != linkTarget.part.vessel; } }
/// <inheritdoc/> public override void OnUpdate() { base.OnUpdate(); if (showSetup) { var distToPart = Vector3.Distance( FlightGlobals.ActiveVessel.transform.position, part.transform.position); var setupEvent = PartModuleUtils.GetEvent(this, SetupEvent); if (setupEvent == null || distToPart > setupEvent.unfocusedRange) { showSetup = false; } } if (activated) { delay -= TimeWarp.deltaTime; if (delay < 1 && !sndTimeEnd.isPlaying) { sndTimeEnd.Play(); } if (delay < 0) { sndTimeStart.Stop(); sndTimeLoop.Stop(); Explode(part.transform.position); } } }
/// <inheritdoc/> public void UpdateContextMenu() { PartModuleUtils.SetupEvent( this, PickupConnectorEvent, x => x.guiActive = FlightGlobals.fetch != null && FlightGlobals.ActiveVessel == vessel && !isLinked && closestConnector != null); }
/// <inheritdoc/> public override void UpdateContextMenu() { base.UpdateContextMenu(); PartModuleUtils.SetupEvent(this, OpenGUIEvent, e => { e.active = linkTarget != null && linkTarget.part != null && !linkTarget.part.vessel.isEVA; }); }
/// <summary>Cleans up the internal cache of the injected menu items.</summary> /// <param name="menuOwnerPart">The part for which the UI is being destroyed.</param> /// <seealso cref="OnPartGUIStart"/> void OnPartGUIStop(Part menuOwnerPart) { List <InjectedEvent> injects; if (_targetCandidates.TryGetValue(menuOwnerPart.flightID, out injects)) { injects .Where(ie => ie.baseEvent != null).ToList() .ForEach(ie => PartModuleUtils.DropEvent(ie.module as PartModule, ie.baseEvent)); _targetCandidates.Remove(menuOwnerPart.flightID); } }
/// <inheritdoc/> public override void UpdateContextMenu() { base.UpdateContextMenu(); PartModuleUtils.SetupEvent(this, StartLinkContextMenuAction, e => { e.guiName = startLinkMenu; e.active = linkState == LinkState.Available; }); PartModuleUtils.SetupEvent(this, BreakLinkContextMenuAction, e => { e.guiName = breakLinkMenu; e.active = linkState == LinkState.Linked; }); }
public void ActivateEvent() { if (!activated) { activated = true; sndTimeStart.Play(); sndTimeLoop.Play(); PartModuleUtils.SetupEvent(this, ActivateEvent, x => x.active = false); PartModuleUtils.SetupEvent(this, SetupEvent, x => x.active = false); ScreenMessaging.ShowPriorityScreenMessage(TimeToEscapeMsg.Format((int)delay)); } }
/// <inheritdoc/> protected override void SetupStateMachine() { base.SetupStateMachine(); linkStateMachine.onAfterTransition += (start, end) => HostedDebugLog.Fine( this, "Source state changed at {0}: {1} => {2}", attachNodeName, start, end); linkStateMachine.SetTransitionConstraint( LinkState.Available, new[] { LinkState.Linking, LinkState.RejectingLinks, LinkState.NodeIsBlocked }); linkStateMachine.SetTransitionConstraint( LinkState.NodeIsBlocked, new[] { LinkState.Available }); linkStateMachine.SetTransitionConstraint( LinkState.Linking, new[] { LinkState.Available, LinkState.Linked }); linkStateMachine.SetTransitionConstraint( LinkState.Linked, new[] { LinkState.Available }); linkStateMachine.SetTransitionConstraint( LinkState.Locked, new[] { LinkState.Available }); linkStateMachine.SetTransitionConstraint( LinkState.RejectingLinks, new[] { LinkState.Available, LinkState.Locked }); linkStateMachine.AddStateHandlers( LinkState.Available, enterHandler: x => KASAPI.KasEvents.OnStartLinking.Add(OnStartLinkingKASEvent), leaveHandler: x => KASAPI.KasEvents.OnStartLinking.Remove(OnStartLinkingKASEvent)); linkStateMachine.AddStateHandlers( LinkState.RejectingLinks, enterHandler: x => KASAPI.KasEvents.OnStopLinking.Add(OnStopLinkingKASEvent), leaveHandler: x => KASAPI.KasEvents.OnStopLinking.Remove(OnStopLinkingKASEvent)); linkStateMachine.AddStateHandlers( LinkState.Linked, enterHandler: x => { GameEvents.onVesselWillDestroy.Add(OnVesselWillDestroyGameEvent); var module = linkTarget as PartModule; PartModuleUtils.InjectEvent(this, ToggleVesselsDockModeEvent, module); }, leaveHandler: x => { GameEvents.onVesselWillDestroy.Remove(OnVesselWillDestroyGameEvent); var module = linkTarget as PartModule; PartModuleUtils.WithdrawEvent(this, ToggleVesselsDockModeEvent, module); }); linkStateMachine.AddStateHandlers( LinkState.Linking, enterHandler: x => KASAPI.KasEvents.OnStartLinking.Fire(this), leaveHandler: x => KASAPI.KasEvents.OnStopLinking.Fire(this)); }
/// <inheritdoc/> protected override void SetupStateMachine() { base.SetupStateMachine(); linkStateMachine.onAfterTransition += (start, end) => UpdateContextMenu(); linkStateMachine.AddStateHandlers( LinkState.Linking, enterHandler: x => { InputLockManager.SetControlLock( ControlTypes.All & ~ControlTypes.CAMERACONTROLS, TotalControlLock); canAutoSaveState = HighLogic.CurrentGame.Parameters.Flight.CanAutoSave; HighLogic.CurrentGame.Parameters.Flight.CanAutoSave = false; linkRenderer.shaderNameOverride = InteractiveShaderName; linkRenderer.colorOverride = BadLinkColor; linkRenderer.isPhysicalCollider = false; }, leaveHandler: x => { linkRenderer.StopRenderer(); // This resets the pipe state. linkRenderer.shaderNameOverride = null; linkRenderer.colorOverride = null; linkRenderer.isPhysicalCollider = true; ScreenMessages.RemoveMessage(statusScreenMessage); InputLockManager.RemoveControlLock(TotalControlLock); HighLogic.CurrentGame.Parameters.Flight.CanAutoSave = canAutoSaveState; lastHoveredPart = null; }); linkStateMachine.AddStateHandlers( LinkState.Linked, enterHandler: x => { if (linkActor == LinkActorType.Player || linkActor == LinkActorType.Physics) { UISoundPlayer.instance.Play(linkJoint.coupleOnLinkMode ? sndPathDock : sndPathPlug); } var module = linkTarget as PartModule; PartModuleUtils.InjectEvent(this, BreakLinkContextMenuAction, module); }, leaveHandler: x => { if (linkActor == LinkActorType.Player) { UISoundPlayer.instance.Play(linkJoint.coupleOnLinkMode ? sndPathUndock : sndPathUnplug); } else if (linkActor == LinkActorType.Physics) { UISoundPlayer.instance.Play(sndPathBroke); } var module = linkTarget as PartModule; PartModuleUtils.WithdrawEvent(this, BreakLinkContextMenuAction, module); }); }
/// <summary>Updates the GUI items when a part's context menu is opened.</summary> /// <remarks> /// <para> /// The goal of this method is to intercept the action of opening a context menu on the other /// part. The method checks if the target part can be a target for the link of the connector that /// is being carried by the kerbal. If this is the case, then a special menu item is injected to /// allow player to complete the link. /// </para> /// <para> /// This event is called once for every part with an opened menu in every frame. For this reason /// it must be very efficient, or else the performance will suffer. To not impact the performance, /// this method caches all the opened menus. /// </para> /// </remarks> /// <param name="menuOwnerPart">The part for which the UI is created.</param> /// <seealso cref="OnPartGUIStop"/> void OnPartGUIStart(Part menuOwnerPart) { if (FlightGlobals.ActiveVessel != vessel) { // If the EVA part has lost the focus, then cleanup all the caches. if (_targetCandidates.Count > 0) { _targetCandidates .SelectMany(t => t.Value) .Where(ie => ie.baseEvent != null) .ToList() .ForEach(ie => PartModuleUtils.DropEvent(ie.module as PartModule, ie.baseEvent)); _targetCandidates.Clear(); } return; } // Check if the menu injects need to be added/removed on the monitored parts. List <InjectedEvent> injects; if (!_targetCandidates.TryGetValue(menuOwnerPart.flightID, out injects)) { injects = menuOwnerPart.Modules.OfType <ILinkTarget>() .Where(t => t.cfgLinkType == cfgLinkType) .Select(t => new InjectedEvent() { module = t, baseEvent = null }) .ToList(); _targetCandidates.Add(menuOwnerPart.flightID, injects); } foreach (var inject in injects) { var target = inject.module; var canLink = inject.module.linkState == LinkState.Available && isLinked; if (!canLink && inject.baseEvent != null) { PartModuleUtils.DropEvent(target as PartModule, inject.baseEvent); inject.baseEvent = null; } else if (canLink && inject.baseEvent == null) { inject.baseEvent = MakeEvent(target, AttachConnectorMenu, LinkCarriedConnector); PartModuleUtils.AddEvent(target as PartModule, inject.baseEvent); } } }
/// <inheritdoc/> public override void UpdateContextMenu() { base.UpdateContextMenu(); deployedCableLengthMenuInfo = DistanceType.Format( cableJoint != null ? cableJoint.deployedCableLength : 0); PartModuleUtils.SetupEvent(this, ToggleExtendCableEvent, e => { e.guiName = motorTargetSpeed > float.Epsilon ? StopExtendingMenuTxt : ExtendCableMenuTxt; }); PartModuleUtils.SetupEvent(this, ToggleRetractCableEvent, e => { e.guiName = motorTargetSpeed < -float.Epsilon ? StopRetractingMenuTxt : RetractCableMenuTxt; }); }
/// <summary>Creates the context menu events from the orientation descriptions.</summary> /// <seealso cref="parkedOrientations"/> void InjectOrientationMenuItems() { foreach (var orientation in parkedOrientations) { var eventInject = new BaseEvent( Events, "autoEventOrientation" + part.Modules.IndexOf(this), () => { persistedParkedOrientation = orientation.direction; UpdateLinkLengthAndOrientation(); }, new KSPEvent()); eventInject.guiName = orientation.title; eventInject.guiActive = false; eventInject.guiActiveEditor = true; eventInject.guiActiveUnfocused = true; PartModuleUtils.AddEvent(this, eventInject); injectedOrientationEvents.Add(eventInject); } }
/// <inheritdoc/> public virtual void UpdateContextMenu() { PartModuleUtils.SetupEvent(this, ToggleVesselsDockModeEvent, e => { if (linkJoint != null) { if (linkJoint.coupleOnLinkMode) { e.active = true; e.guiName = DockedModeMenuTxt; } else { e.active = showCouplingUi && allowCoupling && (linkTarget == null || linkTarget.coupleNode != null); e.guiName = UndockedModeMenuTxt; } } else { e.active = false; } }); }
/// <inheritdoc/> public void UpdateContextMenu() { Fields["lockStatus"].guiActive = isLinked; Fields["steeringStatus"].guiActive = isLinked; Fields["steeringInvert"].guiActive = isLinked && persistedActiveSteeringEnabled; Fields["steeringSensitivity"].guiActive = isLinked && persistedActiveSteeringEnabled; PartModuleUtils.SetupEvent( this, StartLockLockingAction, e => e.active = isLinked && persistedLockingMode == LockMode.Disabled); PartModuleUtils.SetupEvent( this, UnlockAction, e => e.active = isLinked && persistedLockingMode != LockMode.Disabled); PartModuleUtils.SetupEvent( this, DeactivateSteeringAction, e => e.active = isLinked && persistedActiveSteeringEnabled); PartModuleUtils.SetupEvent( this, ActiveSteeringAction, e => e.active = isLinked && !persistedActiveSteeringEnabled); lockStatus = LockStatusMsgLookup.Lookup(persistedLockingMode); steeringStatus = SteeringStatusMsgLookup.Lookup( persistedActiveSteeringEnabled ? SteeringStatus.Active : SteeringStatus.Disabled); }
/// <inheritdoc/> public void UpdateContextMenu() { // FIXME: Consider the mounted state. PartModuleUtils.SetupEvent(this, ReleaseEvent, x => x.active = allowRelease); PartModuleUtils.SetupAction(this, ActionGroupRelease, x => x.active = allowRelease); }
/// <inheritdoc/> public void UpdateContextMenu() { _injectedOrientationEvents.ForEach(e => e.active = !isLinked); PartModuleUtils.SetupEvent(this, ExtendAtMaxMenuAction, x => x.active = !isLinked); PartModuleUtils.SetupEvent(this, RetractToMinMenuAction, x => x.active = !isLinked); }
void SetupEvents() { // This call will activate the event. PartModuleUtils.SetupEvent(this, TestEvent, x => x.active = true); }
void SetupEvents() { var e = PartModuleUtils.GetEvent(this, TestEvent); e.active = true; // Activates the event. }
/// <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); }
/// <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); }