// Update said predictions private void UpdateExitOrbit(Vessel nearObject, Vessel farObject) { if (!predictionsDrawn) { return; } if (!IsPatchedConicsAvailable) { HideExitOrbit(); return; } Vector3 mapCamPos = ScaledSpace.ScaledToLocalSpace(MapView.MapCamera.transform.position); MapObject farTarget = MapView.MapCamera.target; Vector3 farTarPos = ScaledSpace.ScaledToLocalSpace(farTarget.transform.position); float dirScalar = Vector3.Distance(mapCamPos, farTarPos); Vector3d exitTraj = ESLDBeacon.GetJumpVelOffset(nearObject, farObject); predictionOrbitRenderer.driver.referenceBody = farObject.mainBody; predictionOrbitRenderer.driver.orbit.referenceBody = farObject.mainBody; predictionOrbitRenderer.driver.pos = farObject.orbit.pos; predictionOrbitRenderer.celestialBody = farObject.mainBody; predictionOrbitRenderer.SetColor(Color.red); predictionOrbitDriver.orbit.UpdateFromStateVectors(farObject.orbit.pos, exitTraj, farObject.mainBody, Planetarium.GetUniversalTime()); /* Direction indicator is broken/not required * float baseWidth = 20.0f; * double baseStart = 10; * double baseEnd = 50; * oDirection.transform.position = ScaledSpace.LocalToScaledSpace(far.transform.position); * if (dirScalar / 325000 > baseWidth) baseWidth = dirScalar / 325000f; * oDirection.SetWidth(baseWidth, 0.01f); * if (dirScalar / 650000 > baseStart) baseStart = dirScalar / 650000; * if (dirScalar / 130000 > baseEnd) baseEnd = dirScalar / 130000; * // log.debug("Camera distance is " + dirScalar + " results: " + baseWidth + " " + baseStart + " " + baseEnd); * oDirection.SetPosition(0, Vector3d.zero + exitTraj.xzy.normalized * baseStart); * oDirection.SetPosition(1, exitTraj.xzy.normalized * baseEnd); * oDirection.transform.eulerAngles = Vector3d.zero;*/ }
// Show exit orbital predictions private void ShowExitOrbit(Vessel nearObject, Vessel farObject) { // Recenter map, save previous state. wasInMapView = MapView.MapIsEnabled; if (!MapView.MapIsEnabled) { MapView.EnterMapView(); } MapObject mapObject = FindVesselBody(farObject); if ((UnityEngine.Object)mapObject != (UnityEngine.Object)null) { MapView.MapCamera.SetTarget(mapObject); } Vector3 mapCamPos = ScaledSpace.ScaledToLocalSpace(MapView.MapCamera.transform.position); Vector3 farTarPos = ScaledSpace.ScaledToLocalSpace(mapObject.transform.position); float dirScalar = Vector3.Distance(mapCamPos, farTarPos); //log.Debug("Initializing, camera distance is " + dirScalar); if (!IsPatchedConicsAvailable) { HideExitOrbit(); } else { predictionsDrawn = true; Vector3d vel = ESLDBeacon.GetJumpVelOffset(nearObject, farObject); if (predictionGameObject != null) { Destroy(predictionGameObject); } predictionGameObject = new GameObject("OrbitRendererGameObject"); predictionOrbitDriver = predictionGameObject.AddComponent <OrbitDriver>(); predictionOrbitDriver.orbit.referenceBody = farObject.mainBody; predictionOrbitDriver.orbit = new Orbit(); predictionOrbitDriver.referenceBody = farObject.mainBody; predictionOrbitDriver.upperCamVsSmaRatio = 999999f; // Took forever to figure this out - this sets at what zoom level the orbit appears. Was causing it not to appear at small bodies. predictionOrbitDriver.lowerCamVsSmaRatio = 0.0001f; predictionOrbitDriver.orbit.UpdateFromStateVectors(farObject.orbit.pos, vel, farObject.mainBody, Planetarium.GetUniversalTime()); predictionOrbitDriver.orbit.Init(); Vector3d relativePositionAtUT = predictionOrbitDriver.orbit.getRelativePositionAtUT(Planetarium.GetUniversalTime()); Vector3d orbitalVelocityAtUT = predictionOrbitDriver.orbit.getOrbitalVelocityAtUT(Planetarium.GetUniversalTime()); predictionOrbitDriver.orbit.h = Vector3d.Cross(relativePositionAtUT, orbitalVelocityAtUT); predictionOrbitDriver.updateMode = OrbitDriver.UpdateMode.TRACK_Phys; predictionOrbitDriver.orbitColor = Color.red; predictionOrbitRenderer = predictionGameObject.AddComponent <OrbitRenderer>(); predictionOrbitRenderer.SetColor(Color.red); predictionOrbitRenderer.vessel = vessel; predictionOrbitDriver.vessel = vessel; predictionOrbitRenderer.upperCamVsSmaRatio = 999999f; predictionOrbitRenderer.lowerCamVsSmaRatio = 0.0001f; predictionOrbitRenderer.celestialBody = farObject.mainBody; predictionOrbitRenderer.driver = predictionOrbitDriver; predictionOrbitDriver.Renderer = predictionOrbitRenderer; if (true) { // This draws the full Patched Conics prediction. predictionPatchedConicSolver = predictionGameObject.AddComponent <PatchedConicSolver>(); predictionPatchedConicRenderer = predictionGameObject.AddComponent <PatchedConicRenderer>(); predictionOrbitRenderer.drawIcons = OrbitRendererBase.DrawIcons.NONE; predictionOrbitRenderer.drawMode = OrbitRendererBase.DrawMode.OFF; } #if false else { // This draws just the first patch, similar to a Level 1 tracking station. predictionOrbitRenderer.driver.drawOrbit = true; predictionOrbitRenderer.drawIcons = OrbitRenderer.DrawIcons.OBJ_PE_AP; predictionOrbitRenderer.drawMode = OrbitRenderer.DrawMode.REDRAW_AND_RECALCULATE; predictionOrbitRenderer.enabled = true; } #endif base.StartCoroutine(NullOrbitDriverVessels()); // Splash some color on it. // Directional indicator. /* * float baseWidth = 20.0f; * double baseStart = 10; * double baseEnd = 50; * oDirObj = new GameObject("Indicator"); * oDirObj.layer = 10; // Map layer! * oDirection = oDirObj.AddComponent<LineRenderer>(); * oDirection.useWorldSpace = false; * oOrigin = null; * foreach (Transform sstr in ScaledSpace.Instance.scaledSpaceTransforms) * { * if (sstr.name == far.mainBody.name) * { * oOrigin = sstr; * log.debug("Found origin: " + sstr.name); * break; * } * } * oDirection.transform.parent = oOrigin; * oDirection.transform.position = ScaledSpace.LocalToScaledSpace(far.transform.position); * oDirection.material = new Material(Shader.Find("Particles/Additive")); * oDirection.SetColors(Color.clear, Color.red); * if (dirScalar / 325000 > baseWidth) baseWidth = dirScalar / 325000f; * oDirection.SetWidth(baseWidth, 0.01f); * log.debug("Base Width set to " + baseWidth); * oDirection.SetVertexCount(2); * if (dirScalar / 650000 > baseStart) baseStart = dirScalar / 650000; * if (dirScalar / 130000 > baseEnd) baseEnd = dirScalar / 130000; * log.debug("Base Start set to " + baseStart); * log.debug("Base End set to " + baseEnd); * oDirection.SetPosition(0, Vector3d.zero + exitTraj.xzy.normalized * baseStart); * oDirection.SetPosition(1, exitTraj.xzy.normalized * baseEnd); * oDirection.enabled = true; */ } }
public void WindowInterface(int GuiId) { GUILayout.BeginVertical(); switch (displayMode) { case DisplayMode.Selection: if (farBeacons.Count <= 0 || nearBeacons.Count <= 0 || nearBeacon == null) { GUILayout.Label("No active beacons found."); } else { if (driftpenalty > 0f) { GUILayout.Label($"+{driftpenalty:F2}% due to Drift."); } if (nearBeacon.UnsafeTransfer) { if (vessel.GetCrewCount() > 0 || HCUPartFailures.Count > 0) { GUILayout.Label("WARNING: This beacon has no active Heisenkerb Compensator."); } if (vessel.GetCrewCount() > 0) { GUILayout.Label("Transfer will kill crew."); } if (HCUPartFailures.Count > 0) { GUILayout.Label("Some resources will destabilize."); } } int i; for (i = targetDetails.Count - 1; i >= 0; i--) { GUIStyle style = targetDetails[i].affordable ? (targetDetails[i].pathCheck.clear ? buttonHasFuel : buttonNoPath) : buttonNoFuel; if (GUILayout.Button($"{targetDetails[i].vesselName} ({targetDetails[i].targetSOI}, {targetDetails[i].targetAlt:F1}km) | {targetDetails[i].tripCost:F2}", style)) { if (targetDetails[i].affordable && targetDetails[i].pathCheck.clear) { selectedTarget = targetDetails[i]; if (nearBeacon.CarriesVelocity) { ShowExitOrbit(vessel, selectedTarget.targetVessel); } displayMode = DisplayMode.Confirmation; } else { string message = ""; if (!targetDetails[i].affordable) { int index = nearBeacon.JumpResources.FindIndex((ESLDJumpResource res) => (double)(res.ratio * targetDetails[i].tripCost) > res.fuelOnBoard); message = ((index >= 0) ? $"Cannot Warp: Origin beacon has {nearBeacon.JumpResources[index].fuelOnBoard:F2} of {nearBeacon.JumpResources[index].ratio * targetDetails[i].tripCost:F2} {nearBeacon.JumpResources[index].name} required to warp." : "Index error."); } else if (!targetDetails[i].pathCheck.clear) { string arg = (targetDetails[i].pathCheck.blockingBody.name == "Mun" || targetDetails[i].pathCheck.blockingBody.name == "Sun") ? "the " : ""; switch (targetDetails[i].pathCheck.blockReason) { case "Gravity": message = string.Format("Cannot Warp: Path of transfer intersects a high-gravity area around {1}{0}.", targetDetails[i].pathCheck.blockingBody.name, arg); break; case "Proximity": message = string.Format("Cannot Warp: Path of transfer passes too close to {1}{0}.", targetDetails[i].pathCheck.blockingBody.name, arg); break; case "Null": message = "Cannot Warp: No near beacon assigned. This is an error."; break; default: message = "Cannot Warp: Path is blocked."; break; } } ScreenMessages.PostScreenMessage(message, 5f, ScreenMessageStyle.UPPER_CENTER); } } } GUILayout.FlexibleSpace(); if (nearBeacons.Count > 1) { GUILayout.Label($"Current Beacon: {nearBeacon.Description} ({nearBeacon.Vessel.vesselName})"); if (GUILayout.Button($"Next Beacon ({currentBeaconIndex + 1} of {nearBeacons.Count})", buttonNeutral)) { if (currentBeaconIndex + 1 < nearBeacons.Count) { nearBeacon = nearBeacons[currentBeaconIndex + 1]; } else { nearBeacon = nearBeacons[0]; } } } if (GUILayout.Button("Close Beacon Interface", buttonNeutral)) { UnityEngine.Object.Destroy(this); } } break; case DisplayMode.Confirmation: { if (driftpenalty > 0f) { GUILayout.Label($"+{driftpenalty:F2}% due to Drift."); } if (nearBeacon.UnsafeTransfer) { if (vessel.GetCrewCount() > 0 || HCUPartFailures.Count > 0) { GUILayout.Label("WARNING: This beacon has no active Heisenkerb Compensator.", labelNoFuel); } if (vessel.GetCrewCount() > 0) { GUILayout.Label("Transfer will kill crew.", labelNoFuel); } if (HCUPartFailures.Count > 0) { GUILayout.Label("These resources will destabilize in transit:", labelNoFuel); for (int j = 0; j < HCUPartFailures.Count; j++) { GUILayout.Label(HCUPartFailures[j], labelNoFuel); } } } GUILayout.Label("Confirm Warp:"); string str = "Base cost: "; for (int k = 0; k < nearBeacon.JumpResources.Count; k++) { str += string.Format("{0:F2} {1}{2}", selectedTarget.tripCost * nearBeacon.JumpResources[k].ratio, nearBeacon.JumpResources[k].name, (k + 1 < nearBeacon.JumpResources.Count) ? ", " : ""); } GUILayout.Label(str + "."); List <string> costModifiers = nearBeacon.GetCostModifiers(vessel, selectedTarget.targetVessel, tonnage, HCUParts.Keys.ToList()); for (int l = 0; l < costModifiers.Count; l++) { GUILayout.Label(costModifiers[l]); } GUILayout.Label($"Destination: {selectedTarget.targetVessel.mainBody.name} at {selectedTarget.targetAlt:F1}km."); GUILayout.Label($"Transfer will emerge within {selectedTarget.precision:N0}m of destination beacon."); if (selectedTarget.targetVessel.altitude - (double)selectedTarget.precision <= selectedTarget.targetVessel.mainBody.Radius * 0.10000000149011612 || selectedTarget.targetVessel.altitude - (double)selectedTarget.precision <= selectedTarget.targetVessel.mainBody.atmosphereDepth) { GUILayout.Label($"Arrival area is very close to {selectedTarget.targetVessel.mainBody.name}.", labelNoFuel); } if (nearBeacon.CarriesVelocity) { GUILayout.Label($"Velocity relative to exit beacon will be {(ESLDBeacon.GetJumpVelOffset(vessel, selectedTarget.targetVessel) - selectedTarget.targetVessel.orbit.vel).magnitude:F0}m/s."); } if (selectedTarget.returnFuelCheck && selectedTarget.returnBeacon != null) { string str2 = (!selectedTarget.returnAffordable) ? "Destination beacon would need " : "Destination beacon can make return trip using "; string str3 = "Destination beacon has "; for (int m = 0; m < selectedTarget.returnBeacon.JumpResources.Count; m++) { str3 += string.Format("{0:F0} {1}{2}", selectedTarget.returnBeacon.JumpResources[m].fuelOnBoard, selectedTarget.returnBeacon.JumpResources[m].name, (m + 1 < selectedTarget.returnBeacon.JumpResources.Count) ? ", " : ""); } for (int n = 0; n < selectedTarget.returnBeacon.JumpResources.Count; n++) { str2 += string.Format("{0:F2} {1}{2}", selectedTarget.returnCost * selectedTarget.returnBeacon.JumpResources[n].ratio, selectedTarget.returnBeacon.JumpResources[n].name, (n + 1 < selectedTarget.returnBeacon.JumpResources.Count) ? ", " : ""); } GUILayout.Label(str3 + "."); GUILayout.Label(str2 + (selectedTarget.returnAffordable ? "." : " for return trip using active beacons."), selectedTarget.returnAffordable ? labelHasFuel : labelNoFuel); } else { GUILayout.Label("Destination beacon's fuel could not be checked."); } if (GUILayout.Button("Confirm and Warp", buttonNeutral)) { if (selectedTarget.pathCheck.clear) { HideExitOrbit(); nearBeacon.Warp(vessel, selectedTarget.targetVessel, selectedTarget.precision, HCUPartsList); UnityEngine.Object.Destroy(this); } else { ScreenMessages.PostScreenMessage("Jump Failed! Transfer path has become obstructed.", 5f, ScreenMessageStyle.UPPER_CENTER); } } if (GUILayout.Button("Back", buttonNeutral)) { displayMode = DisplayMode.Selection; window.height = 0f; } break; } } GUILayout.EndVertical(); GUI.DragWindow(/* new Rect(0f, 0f, 10000f, 20f) */); }
public void WindowInterface(int GuiId) { GUILayout.BeginVertical(); switch (displayMode) { case DisplayMode.Selection: if (farBeacons.Count <= 0 || nearBeacons.Count <= 0 || nearBeacon == null) { GUILayout.Label("No active beacons found."); } else { if (driftpenalty > 0) { GUILayout.Label(String.Format("+{0:F2}% due to Drift.", driftpenalty)); } if (nearBeacon.UnsafeTransfer) { if (vessel.GetCrewCount() > 0 || HCUPartFailures.Count > 0) { GUILayout.Label("WARNING: This beacon has no active Heisenkerb Compensator."); } if (vessel.GetCrewCount() > 0) { GUILayout.Label("Transfer will kill crew."); } if (HCUPartFailures.Count > 0) { GUILayout.Label("Some resources will destabilize."); } } for (int i = targetDetails.Count - 1; i >= 0; i--) { GUIStyle fuelstate = targetDetails[i].affordable ? (targetDetails[i].pathCheck.clear ? buttonHasFuel : buttonNoPath) : buttonNoFuel; if (GUILayout.Button(String.Format("{0} ({1}, {2:F1}km) | {3:F2}", targetDetails[i].vesselName, targetDetails[i].targetSOI, targetDetails[i].targetAlt, targetDetails[i].tripCost), fuelstate)) { if (targetDetails[i].affordable && targetDetails[i].pathCheck.clear) { selectedTarget = targetDetails[i]; if (nearBeacon.CarriesVelocity) { ShowExitOrbit(vessel, selectedTarget.targetVessel); } displayMode = DisplayMode.Confirmation; } else { string messageToPost = ""; if (!targetDetails[i].affordable) { int index = nearBeacon.JumpResources.FindIndex(res => res.ratio * targetDetails[i].tripCost > res.fuelOnBoard); if (index < 0) { messageToPost = "Index error."; } else { messageToPost = String.Format("Cannot Warp: Origin beacon has {0:F2} of {1:F2} {2} required to warp.", nearBeacon.JumpResources[index].fuelOnBoard, nearBeacon.JumpResources[index].ratio * targetDetails[i].tripCost, nearBeacon.JumpResources[index].name); } } else if (!targetDetails[i].pathCheck.clear) { string thevar = (targetDetails[i].pathCheck.blockingBody.name == "Mun" || targetDetails[i].pathCheck.blockingBody.name == "Sun") ? "the " : ""; switch (targetDetails[i].pathCheck.blockReason) { case "Gravity": messageToPost = String.Format("Cannot Warp: Path of transfer intersects a high-gravity area around {1}{0}.", targetDetails[i].pathCheck.blockingBody.name, thevar); break; case "Proximity": messageToPost = String.Format("Cannot Warp: Path of transfer passes too close to {1}{0}.", targetDetails[i].pathCheck.blockingBody.name, thevar); break; case "Null": messageToPost = "Cannot Warp: No near beacon assigned. This is an error."; break; default: messageToPost = "Cannot Warp: Path is blocked."; break; } } ScreenMessages.PostScreenMessage(messageToPost, 5.0f, ScreenMessageStyle.UPPER_CENTER); } } } GUILayout.FlexibleSpace(); if (nearBeacons.Count > 1) { GUILayout.Label(String.Format("Current Beacon: {0} ({1})", nearBeacon.Description, nearBeacon.Vessel.vesselName)); if (GUILayout.Button(String.Format("Next Beacon ({0} of {1})", (currentBeaconIndex + 1), nearBeacons.Count), buttonNeutral)) { if (currentBeaconIndex + 1 < nearBeacons.Count) { nearBeacon = nearBeacons[currentBeaconIndex + 1]; } else { nearBeacon = nearBeacons[0]; } } } if (GUILayout.Button("Close Beacon Interface", buttonNeutral)) { Destroy(this); } } break; case DisplayMode.Confirmation: if (driftpenalty > 0) { GUILayout.Label(String.Format("+{0:F2}% due to Drift.", driftpenalty)); } if (nearBeacon.UnsafeTransfer) { if (vessel.GetCrewCount() > 0 || HCUPartFailures.Count > 0) { GUILayout.Label("WARNING: This beacon has no active Heisenkerb Compensator.", labelNoFuel); } if (vessel.GetCrewCount() > 0) { GUILayout.Label("Transfer will kill crew.", labelNoFuel); } if (HCUPartFailures.Count > 0) { GUILayout.Label("These resources will destabilize in transit:", labelNoFuel); for (int i = 0; i < HCUPartFailures.Count; i++) { GUILayout.Label(HCUPartFailures[i], labelNoFuel); } } } GUILayout.Label("Confirm Warp:"); string costLabel = "Base cost: "; for (int i = 0; i < nearBeacon.JumpResources.Count; i++) { costLabel += String.Format("{0:F2} {1}{2}", selectedTarget.tripCost * nearBeacon.JumpResources[i].ratio, nearBeacon.JumpResources[i].name, i + 1 < nearBeacon.JumpResources.Count ? ", " : ""); } GUILayout.Label(costLabel + "."); List <string> modifiers = nearBeacon.GetCostModifiers(vessel, selectedTarget.targetVessel, tonnage, HCUParts.Keys.ToList()); for (int i = 0; i < modifiers.Count; i++) { GUILayout.Label(modifiers[i]); } GUILayout.Label(String.Format("Destination: {0} at {1:F1}km.", selectedTarget.targetVessel.mainBody.name, selectedTarget.targetAlt)); GUILayout.Label(String.Format("Transfer will emerge within {0:N0}m of destination beacon.", selectedTarget.precision)); if (selectedTarget.targetVessel.altitude - selectedTarget.precision <= selectedTarget.targetVessel.mainBody.Radius * 0.1f || selectedTarget.targetVessel.altitude - selectedTarget.precision <= selectedTarget.targetVessel.mainBody.atmosphereDepth) { GUILayout.Label(String.Format("Arrival area is very close to {0}.", selectedTarget.targetVessel.mainBody.name), labelNoFuel); } if (nearBeacon.CarriesVelocity) { Vector3d transferVelOffset = ESLDBeacon.GetJumpVelOffset(vessel, selectedTarget.targetVessel) - selectedTarget.targetVessel.orbit.vel; GUILayout.Label(String.Format("Velocity relative to exit beacon will be {0:F0}m/s.", transferVelOffset.magnitude)); } if (selectedTarget.returnFuelCheck && selectedTarget.returnBeacon != null) { string fuelMessage; if (selectedTarget.returnAffordable) { fuelMessage = "Destination beacon can make return trip using "; } else { fuelMessage = "Destination beacon would need "; } string fuelCount = "Destination beacon has "; for (int i = 0; i < selectedTarget.returnBeacon.JumpResources.Count; i++) { fuelCount += String.Format("{0:F0} {1}{2}", selectedTarget.returnBeacon.JumpResources[i].fuelOnBoard, selectedTarget.returnBeacon.JumpResources[i].name, i + 1 < selectedTarget.returnBeacon.JumpResources.Count ? ", " : ""); } for (int i = 0; i < selectedTarget.returnBeacon.JumpResources.Count; i++) { fuelMessage += String.Format("{0:F2} {1}{2}", selectedTarget.returnCost * selectedTarget.returnBeacon.JumpResources[i].ratio, selectedTarget.returnBeacon.JumpResources[i].name, i + 1 < selectedTarget.returnBeacon.JumpResources.Count ? ", " : ""); } GUILayout.Label(fuelCount + "."); GUILayout.Label(fuelMessage + (selectedTarget.returnAffordable ? "." : " for return trip using active beacons."), selectedTarget.returnAffordable ? labelHasFuel : labelNoFuel); } else { GUILayout.Label("Destination beacon's fuel could not be checked."); } if (GUILayout.Button("Confirm and Warp", buttonNeutral)) { if (selectedTarget.pathCheck.clear) { HideExitOrbit(); nearBeacon.Warp(vessel, selectedTarget.targetVessel, selectedTarget.precision, HCUPartsList); Destroy(this); } else { ScreenMessages.PostScreenMessage("Jump Failed! Transfer path has become obstructed.", 5.0f, ScreenMessageStyle.UPPER_CENTER); } } if (GUILayout.Button("Back", buttonNeutral)) { displayMode = DisplayMode.Selection; window.height = 0; } break; } GUILayout.EndVertical(); GUI.DragWindow(new Rect(0, 0, 10000, 20)); }