/// <inheritdoc/> public virtual void OnKASNodeBlockedState(ILinkPeer ownerPeer, bool isBlocked) { if (ownerPeer.cfgAttachNodeName == attachNodeName || cfgDependentNodeNames.Contains(ownerPeer.cfgAttachNodeName)) { // This is a notification handler, so don't use the isNodeBlocked property to not trigger // more notifications. //FIXME: fix the comment when migrated to the setter methods. linkState = isBlocked ? LinkState.NodeIsBlocked : LinkState.Available; } }
/// <summary>Helper method to create a part's context menu.</summary> /// <param name="peer">The peer module to create an action for.</param> /// <param name="guiName">The GUI name of the menu item.</param> /// <param name="action">The action to exectue when the menu item is triggered.</param> /// <returns>The new event. It's not automatically injected into the part.</returns> BaseEvent MakeEvent(ILinkPeer peer, Message guiName, Action <ILinkPeer> action) { var ev = new BaseEvent( peer.part.Events, "autoEventAttach" + peer.part.Modules.IndexOf(peer as PartModule), () => action.Invoke(peer), new KSPEvent()); ev.guiActive = true; ev.guiActiveUncommand = true; ev.guiActiveUnfocused = true; ev.guiName = guiName; return(ev); }
/// <inheritdoc/> public ILinkPeer FindLinkPeer(ILinkPeer srcPeer) { var host = srcPeer as PartModule; var tgtPart = FlightGlobals.FindPartByID(srcPeer.linkPartId); if (tgtPart == null) { HostedDebugLog.Warning(host, "Cannot find target part: partId=F{0}", srcPeer.linkPartId); return(null); } // In normal case we can lookup by the node name. ILinkPeer tgtPeer = null; if (!string.IsNullOrEmpty(srcPeer.linkNodeName)) { tgtPeer = tgtPart.Modules .OfType <ILinkPeer>() .FirstOrDefault(m => m.linkState == LinkState.Linked && m.linkPartId == srcPeer.part.flightID && m.linkNodeName == srcPeer.cfgAttachNodeName && m.cfgLinkType == srcPeer.cfgLinkType); } // Fallback case. Try guessing the target peer by less strict conditions. if (tgtPeer == null) { var candidates = tgtPart.Modules .OfType <ILinkPeer>() .Where(m => m.linkState == LinkState.Linked && m.linkPartId == srcPeer.part.flightID && m.cfgLinkType == srcPeer.cfgLinkType) .ToList(); if (candidates.Count == 1) { tgtPeer = candidates[0]; HostedDebugLog.Warning(host, "FALLBACK: Found a link: {0} => {1}", srcPeer, tgtPeer); } } if (tgtPeer == null) { HostedDebugLog.Warning( host, "Failed to find the link: targetPartId={0}, targetNode={1}", srcPeer.linkPartId, srcPeer.linkNodeName); } return(tgtPeer); }
bool CheckKASLink(ILinkPeer peer) { if (peer.isLinked && peer.otherPeer != null) { var thisVessel = peer.part.vessel; var otherVessel = peer.otherPeer.part.vessel; if (thisVessel != otherVessel && !visitedVessels.Contains(otherVessel.id)) { visitedVessels.Add(otherVessel.id); ProcessParts(otherVessel.parts[0].localRoot, AddModule(otherVessel.vesselName)); return(true); } } return(false); }
/// <summary>Sets the opposite point of the link.</summary> /// <param name="peer"> /// The other endpoint. It's <c>null</c> when the endpoint must be reset. /// </param> /// <seealso cref="otherPeer"/> protected virtual void SetOtherPeer(ILinkPeer peer) { var oldPeer = _otherPeer; _otherPeer = peer; if (_otherPeer != null) { persistedLinkPartId = _otherPeer != null ? _otherPeer.part.flightID : 0; persistedLinkNodeName = _otherPeer.cfgAttachNodeName; } else { persistedLinkPartId = 0; persistedLinkNodeName = ""; } OnPeerChange(oldPeer); }
bool CheckKASLink(ILinkPeer peer) { if (addVessels && peer.isLinked && peer.otherPeer != null) { var thisVessel = peer.part.vessel; var otherVessel = peer.otherPeer.part.vessel; if (!excludeParts.Contains(peer.otherPeer.part) && thisVessel != otherVessel && !visitedVessels.Contains(otherVessel.id)) { visitedVessels.Add(otherVessel.id); AddVessel(otherVessel.vesselName, otherVessel.parts, otherVessel.parts[0].localRoot); return(true); } } return(false); }
/// <summary> /// Removes the linked connector from the kerbal and links it to the target part. /// </summary> /// <param name="peer">The target part link module.</param> void LinkCarriedConnector(ILinkPeer peer) { var target = peer as ILinkTarget; if (target != null && isLinked && linkSource.CheckCanLinkTo(target, checkStates: false, reportToGUI: true)) { var source = linkSource; source.BreakCurrentLink(LinkActorType.API); if (!source.LinkToTarget(LinkActorType.Player, target)) { UISoundPlayer.instance.Play(KASAPI.CommonConfig.sndPathBipWrong); } } else { UISoundPlayer.instance.Play(KASAPI.CommonConfig.sndPathBipWrong); } }
/// <inheritdoc/> public ILinkPeer FindLinkPeer(ILinkPeer srcPeer) { if (srcPeer.linkPartId == 0 || srcPeer.linkModuleIndex == -1) { DebugEx.Error("Bad target part definition [Part:(id=F{0}#Module:{1}]", srcPeer.linkPartId, srcPeer.linkModuleIndex); return(null); } var tgtPart = FlightGlobals.FindPartByID(srcPeer.linkPartId); if (tgtPart == null) { DebugEx.Error("Cannot find [Part:(id=F{0})]", srcPeer.linkPartId); return(null); } if (srcPeer.linkModuleIndex >= tgtPart.Modules.Count) { DebugEx.Error("The target part {0} doesn't have a module at index {1}", tgtPart, srcPeer.linkModuleIndex); return(null); } var tgtPeer = tgtPart.Modules[srcPeer.linkModuleIndex] as ILinkPeer; if (tgtPeer == null) { DebugEx.Error("The target module {0} is not a link peer", tgtPart.Modules[srcPeer.linkModuleIndex]); return(null); } if (!tgtPeer.isLinked || tgtPeer.linkPartId != srcPeer.part.flightID || tgtPeer.linkModuleIndex != srcPeer.part.Modules.IndexOf(srcPeer as PartModule)) { DebugEx.Error("Source module {0} cannot be linked with the target module {1}", srcPeer.part.Modules[tgtPeer.linkModuleIndex], tgtPart.Modules[srcPeer.linkModuleIndex]); return(null); } return(tgtPeer); }
/// <inheritdoc/> protected override void OnPeerChange(ILinkPeer oldPeer) { base.OnPeerChange(oldPeer); linkState = linkSource != null ? LinkState.Linked : LinkState.Available; // Trigger events on the part. var oldSource = oldPeer as ILinkSource; if (linkStateMachine.currentState != null && oldSource != linkSource) { var linkInfo = new KasLinkEventImpl(linkSource ?? oldSource, this); if (linkSource != null) { part.Modules.OfType <ILinkStateEventListener>().ToList() .ForEach(x => x.OnKASLinkedState(linkInfo, isLinked: true)); } else { part.Modules.OfType <ILinkStateEventListener>().ToList() .ForEach(x => x.OnKASLinkedState(linkInfo, isLinked: false)); } } }
/// <inheritdoc/> public void OnKASNodeBlockedState(ILinkPeer ownerPeer, bool isBlocked) { throw new NotImplementedException(); // Obsolete. }
/// <summary>Triggers when a linked peers has been assigned with a value.</summary> /// <remarks> /// This method triggers even when the new peer doesn't differ from the old one. When it's /// important to catch the transition, check for the <paramref name="oldPeer"/>. /// </remarks> /// <param name="oldPeer">The peer prior to the change.</param> protected virtual void OnPeerChange(ILinkPeer oldPeer) { }
/// <summary>Checks if the peers are coupled via their attach nodes.</summary> /// <param name="source">The peer the link.</param> /// <param name="target">The peer the link.</param> /// <returns><c>true</c> if the peers are coupled.</returns> static bool CheckCoupled(ILinkPeer source, ILinkPeer target) { return(source.coupleNode != null && source.coupleNode.attachedPart == target.part && target.coupleNode != null && target.coupleNode.attachedPart == source.part); }
//FIXME rework for multiple connections ConnectedPartSet ConnectedParts(Part part) { var connectedParts = new ConnectedPartSet(); for (int i = part.Modules.Count; i-- > 0;) { var module = part.Modules[i]; // This covers radial and stack decouplers and separators, // launch clamps, and docking ports. var separator = module as IStageSeparator; if (separator != null) { GetOtherPart(separator, connectedParts); continue; } // The claw is on its own as it is never staged (I guess). var grapple = module as ModuleGrappleNode; if (grapple != null) { GetOtherPart(grapple, connectedParts); continue; } // EL's launchpad module is very much on its own. No need // to worry about survey stations as the built vessel is // never attached, nor disposable pads as they self // destruct. var pad = module as ELLaunchpad; if (pad != null) { GetOtherPart(pad, connectedParts); continue; } //FIXME need to add KAS connectors if (module.moduleName == "KASModuleStrut") { // legacy pipe connector var kasStrut = new KASModuleStrut(module); GetOtherPart(kasStrut, connectedParts); continue; } if (module.moduleName == "KASLinkResourceConnector") { // new resoruce connector. works when undocked! // however, can be used to dock the vessel, in which // case KASJointCableBase is to be checked, or even // just connected on the same vessel (just ignore) var peer = new ILinkPeer(module); if (CheckKASLink(peer)) { continue; } } if (module.moduleName == "KASLinkTargetBase") { // new resoruce connector. works when undocked! // however, can be used to dock the vessel, in which // case KASJointCableBase is to be checked, or even // just connected on the same vessel (just ignore) var peer = new ILinkPeer(module); if (peer.cfgLinkType == "MdHose" && CheckKASLink(peer)) { continue; } } if (module.moduleName == "KASJointCableBase") { var kasJoint = new KASJointCableBase(module); GetOtherPart(kasJoint, connectedParts); } } return(connectedParts); }
/// <summary>Checks if the peer parts are coupled in the vessel hierarchy.</summary> /// <param name="source">The first peer of the link.</param> /// <param name="target">The other peer of the link.</param> /// <returns><c>true</c> if the peers are coupled.</returns> static bool CheckCoupled(ILinkPeer source, ILinkPeer target) { return(source.part.parent == target.part || target.part.parent == source.part); }