/// <summary> /// Find the node before a given node /// </summary> /// <param name="vessel">The currently active vessel</param> /// <param name="cur">Node from which to start searching</param> /// <returns> /// Another node if any /// </returns> public PlanningNodeModel PrevNode(Vessel vessel, PlanningNodeModel cur) { var nodes = NodesFor(vessel, true); var curIdx = nodes.IndexOf(cur); return(curIdx >= 0 ? nodes[(curIdx + nodes.Count - 1) % nodes.Count] : cur); }
private void openDialog(PlanningNodeModel toEdit) { if (editDialog == null) { editDialog = new PlanningNodeEditDialog(toEdit, CanEdit); editDialog.CloseDialog += () => launcher.SetFalse(true); editDialog.NewNode += () => { var nd = new PlanningNodeModel( renderer.vessel?.mainBody ?? FlightGlobals.GetHomeBody(), renderer.vessel); PlanningNodesManager.Instance.nodes.Add(nd); editNode(nd); }; editDialog.DeleteNode += () => { PlanningNodesManager.Instance.nodes.Remove(editDialog.editingNode); OnNodeDeleted(); }; editDialog.PrevNode += () => editNode(PlanningNodesManager.Instance.PrevNode(renderer.vessel, editDialog.editingNode)); editDialog.NextNode += () => editNode(PlanningNodesManager.Instance.NextNode(renderer.vessel, editDialog.editingNode)); editDialog.BodyChanged += OnBodyChanged; editDialog.WarpTo += WarpTo; editDialog.Show(launcher.GetAnchor()); } else { // Already open, just switch to this node editDialog.editingNode = toEdit; } }
private void FixedUpdate() { if (solver != null && solver.targetBody != FlightGlobals.ActiveVessel?.targetObject as CelestialBody) { solver.targetBody = FlightGlobals.ActiveVessel?.targetObject as CelestialBody; editingNode.color = PlanningNodeModel.GetBodyColor(solver.targetBody); targeter.SetTarget(solver.targetBody?.orbitDriver); solver.UpdateFlightPlan(); } }
private void WarpTo(PlanningNodeModel node) { if (TimeWarp.CurrentRate > 1) { TimeWarp.fetch.CancelAutoWarp(); TimeWarp.SetRate(0, false); } else { TimeWarp.fetch.WarpTo(node.burnTime - WarpBuffer(node.origin, (float?)renderer.vessel?.orbit.ApA ?? 100000f)); } }
private void Update() { if (node != null && solver != null && !solver.maneuverNodes.Contains(node)) { // User deleted the node! Remove it from everything. PlanningNodesManager.Instance.nodes.Remove(editingNode); editingNode = null; node = null; // Tell listeners to return to non-editing state DeleteMe?.Invoke(); } }
/// <summary> /// Return the nodes for the given vessel /// </summary> /// <param name="vessel">The vessel for which to return nodes</param> /// <param name="includeGlobal">True to include non-vessel-specific nodes, false to omit</param> /// <returns> /// List of nodes /// </returns> public List <PlanningNodeModel> NodesFor(Vessel vessel, bool includeGlobal) { var matched = new List <PlanningNodeModel>(); for (int i = 0; i < nodes.Count; ++i) { PlanningNodeModel nd = nodes[i]; if (nd.vessel == vessel || (nd.vessel == null && includeGlobal)) { matched.Add(nd); } } return(matched); }
private void editNode(PlanningNodeModel toEdit) { if (editor != null) { StartCoroutine(editor.SwitchTo(toEdit)); } if (editor == null) { editor = gameObject.AddComponent <PlanningNodeEditor>(); editor.canEdit = CanEdit; editor.DeleteMe += OnNodeDeleted; editor.CloseMe += OnClose; editor.editingNode = toEdit; } openDialog(toEdit); }
private void FixedUpdate() { if (solver != null && solver.targetBody != FlightGlobals.ActiveVessel?.targetObject as CelestialBody) { solver.targetBody = FlightGlobals.ActiveVessel?.targetObject as CelestialBody; editingNode.color = PlanningNodeModel.GetBodyColor(solver.targetBody); if (canEdit) { targeter.SetTarget(solver.targetBody?.orbitDriver); } else { // targeter will call this for us if needed solver.UpdateFlightPlan(); } } }
/// <summary> /// Start editing a different node /// </summary> /// <param name="newNode">The node to edit</param> /// <returns> /// This is a coroutine because we want to give the stock objects one /// frame to update themselves before we create a new maneuver node /// </returns> public IEnumerator SwitchTo(PlanningNodeModel newNode) { DestroyNode(); editingNode = newNode; driver.orbit.SetOrbit( editingNode.origin.orbit.inclination, editingNode.origin.orbit.eccentricity, editingNode.origin.orbit.semiMajorAxis, editingNode.origin.orbit.LAN, editingNode.origin.orbit.argumentOfPeriapsis, editingNode.origin.orbit.meanAnomalyAtEpoch, editingNode.origin.orbit.epoch, editingNode.origin.orbit.referenceBody ); yield return(new WaitForEndOfFrame()); node = solver.AddManeuverNode(editingNode.burnTime); // Don't need to update plan for zero dV (new) node if (editingNode.deltaV != node.DeltaV) { node.DeltaV = editingNode.deltaV; solver.UpdateFlightPlan(); } if (canEdit) { node.AttachGizmo(MapView.ManeuverNodePrefab, conicRenderer); node.attachedGizmo.OnGizmoUpdated += OnGizmoUpdated; } // Hide the first orbit because it's already drawn with the original planet // and if drawn again it will be gold and distracting because PatchedConicRenderer // assumes that any orbit with a vessel that isn't the active vessel is the target var pr = conicRenderer.patchRenders[0]; pr.lineWidth = 0; pr.MakeVector(); ZoomTo(); }
/// <summary> /// Find the planning node for the given vessel and body /// that is the closest to the needed excess V /// </summary> /// <param name="vessel">The vessel we're piloting</param> /// <param name="body">The body we're escaping</param> /// <returns> /// Which node is the closest, if any /// </returns> public PlanningNodeModel ClosestExcessV(Vessel vessel, CelestialBody body) { var nodes = NodesFor(vessel, true); PlanningNodeModel best = null; double? bestDiff = null; for (int i = 0; i < nodes.Count; ++i) { var nd = nodes[i]; if (nd.origin == body) { var escPat = nd.escapePatch(vessel); var excessV = escPat.getOrbitalVelocityAtUT(escPat.EndUT).xzy; var diff = (nd.BurnExcessV() - excessV).sqrMagnitude; if (!bestDiff.HasValue || diff < bestDiff.Value) { best = nd; bestDiff = diff; } } } return(best); }
/// <summary> /// Initialize the dialog /// </summary> /// <param name="nodeToEdit">The node we'll be editing</param> /// <param name="canEdit">True if we can open the maneuver node editing tools, false otherwise</param> public PlanningNodeEditDialog(PlanningNodeModel nodeToEdit, bool canEdit) : base(-1, -1, pad, new RectOffset(4, 4, 4, 4), TextAnchor.UpperLeft) { editingNode = nodeToEdit; var toprow = new List <DialogGUIBase>() { TooltipExtensions.DeferTooltip(new DialogGUIButton( "PlanningNode_DeleteButtonCaption", () => DeleteNode?.Invoke(), buttonWidth, buttonHeight, false ) { tooltipText = "PlanningNode_DeleteButtonTooltip" }), new DialogGUIFlexibleSpace(), TooltipExtensions.DeferTooltip(new DialogGUIButton( "PlanningNode_CloseButtonCaption", () => CloseDialog?.Invoke(), buttonWidth, buttonHeight, false ) { tooltipText = "PlanningNode_CloseButtonTooltip" }) }; if (canEdit) { toprow.Insert(0, TooltipExtensions.DeferTooltip(new DialogGUIButton( "PlanningNode_NewButtonCaption", () => NewNode?.Invoke(), buttonWidth, buttonHeight, false ) { tooltipText = "PlanningNode_NewButtonTooltip" })); } AddChild(new DialogGUIHorizontalLayout( -1, -1, 8, new RectOffset(0, 0, 0, 0), TextAnchor.MiddleLeft, toprow.ToArray() )); AddChild(new DialogGUIHorizontalLayout( -1, -1, pad, new RectOffset(0, 0, 0, 0), TextAnchor.MiddleLeft, new DialogGUILabel("PlanningNode_NameLabelCaption", buttonWidth / 2), NotifyOnFocus(new DialogGUITextInput( editingNode.name, false, 24, s => { return(editingNode.name = s); }, () => { return(editingNode.name); }, TMP_InputField.ContentType.Standard, buttonHeight ), // Don't trigger other parts of the game while they're typing a name in the text field v => InputLockManager.SetControlLock(MyLocks, "PlanningNodeEditDialogName"), v => InputLockManager.RemoveControlLock("PlanningNodeEditDialogName") ), TooltipExtensions.DeferTooltip(new DialogGUIButton( "PlanningNode_PrevNodeCaption", () => PrevNode?.Invoke(), smallBtnWidth, buttonHeight, false ) { tooltipText = canEdit ? "PlanningNode_PrevNodeTooltip" : "PlanningNode_PrevNodeViewOnlyTooltip" }), TooltipExtensions.DeferTooltip(new DialogGUIButton( "PlanningNode_NextNodeCaption", () => NextNode?.Invoke(), smallBtnWidth, buttonHeight, false ) { tooltipText = canEdit ? "PlanningNode_NextNodeTooltip" : "PlanningNode_NextNodeViewOnlyTooltip" }) )); AddChild(new DialogGUIHorizontalLayout( -1, -1, pad, new RectOffset(0, 0, 0, 0), TextAnchor.MiddleLeft, new DialogGUILabel("PlanningNode_HueLabelCaption", buttonWidth / 2), new DialogGUISlider( () => { if (editingNode != null) { Color.RGBToHSV(editingNode.color, out float hue, out float _, out float _); return(hue); } return(0f); }, 0f, 1f, false, -1, buttonHeight, v => { if (editingNode != null) { editingNode.color = Color.HSVToRGB(v, 0.5f, 0.75f); } } ) )); if (canEdit) { AddChild(new DialogGUIHorizontalLayout( -1, -1, pad, new RectOffset(0, 0, 0, 0), TextAnchor.MiddleLeft, new DialogGUILabel("PlanningNode_BodyLabelCaption", buttonWidth / 2), new DialogGUILabel( () => editingNode.origin.bodyName, -1 ), TooltipExtensions.DeferTooltip(new DialogGUIButton( "PlanningNode_PrevBodyCaption", () => BodyChanged?.Invoke(prevBody(editingNode.origin)), smallBtnWidth, buttonHeight, false ) { tooltipText = "PlanningNode_PrevBodyTooltip" }), TooltipExtensions.DeferTooltip(new DialogGUIButton( "PlanningNode_NextBodyCaption", () => BodyChanged?.Invoke(nextBody(editingNode.origin)), smallBtnWidth, buttonHeight, false ) { tooltipText = "PlanningNode_NextBodyTooltip" }) )); AddChild(TooltipExtensions.DeferTooltip(new DialogGUIToggle( () => editingNode.vessel == null, "PlanningNode_ShowForAllCheckboxCaption", b => { editingNode.vessel = b ? null : FlightGlobals.ActiveVessel; } ) { tooltipText = "PlanningNode_ShowForAllCheckboxTooltip" })); } AddChild(TooltipExtensions.DeferTooltip(new DialogGUIButton( "PlanningNode_WarpToCaption", () => WarpTo?.Invoke(editingNode), buttonWidth, buttonHeight, false ) { tooltipText = "PlanningNode_WarpToTooltip" })); // Don't try to plot a maneuver from the Sun for (int i = 0; i < FlightGlobals.Bodies.Count; ++i) { var b = FlightGlobals.Bodies[i]; if (b.referenceBody != null && b.referenceBody != b) { allowedBodies.Add(b); } } }
private void MarkerClicked(PlanningNodeModel whichNode) { EditNode?.Invoke(whichNode); }