diff --git a/data/lang/ui-core/en.json b/data/lang/ui-core/en.json index 9847762efe..f1c1a79589 100644 --- a/data/lang/ui-core/en.json +++ b/data/lang/ui-core/en.json @@ -1159,6 +1159,18 @@ "description": "Tooltip: Current distance of the radar.", "message": "Active radar distance" }, + "HUD_RADAR_ZOOM_MODE_AUTOMATIC": { + "description": "Tooltip: Current radar zoom mode.", + "message": "Automatic zoom" + }, + "HUD_RADAR_ZOOM_MODE_MANUAL": { + "description": "Tooltip: Current radar zoom mode.", + "message": "Manual zoom" + }, + "HUD_RADAR_TOGGLE_MODE": { + "description": "Tooltip: Toggle radar mode.", + "message": "Toggle radar between 2D and 3D modes" + }, "HUD_REQUEST_TIME_ACCEL": { "description": "Tooltip: Request time acceleration {time}", "message": "Request time acceleration: {time}" diff --git a/data/pigui/modules/autopilot-window.lua b/data/pigui/modules/autopilot-window.lua index 4c6c49384b..9d99cd1cad 100644 --- a/data/pigui/modules/autopilot-window.lua +++ b/data/pigui/modules/autopilot-window.lua @@ -252,7 +252,8 @@ local function displayAutoPilotWindow() local current_view = Game.CurrentView() local window_h = mainButtonSize.y + smallButtonSize.y + ui.getWindowPadding().y * 2 local shift = smallButtonSize.y - local window_posx = ui.screenWidth/2 + ui.reticuleCircleRadius / 4 * 3 + -- X starting position is the edge of the scanner display. + local window_posx = ui.screenWidth/2 + ui.reticuleCircleRadius local window_posy = ui.screenHeight - window_h ui.setNextWindowPos(Vector2(window_posx, window_posy) , "Always") ui.window("AutoPilot", {"NoTitleBar", "NoResize", "NoFocusOnAppearing", "NoBringToFrontOnFocus", "NoSavedSettings", "AlwaysAutoResize"}, diff --git a/data/pigui/modules/radar.lua b/data/pigui/modules/radar.lua index eead59e9e4..a6f9ef965b 100644 --- a/data/pigui/modules/radar.lua +++ b/data/pigui/modules/radar.lua @@ -1,14 +1,11 @@ -- Copyright © 2008-2024 Pioneer Developers. See AUTHORS.txt for details -- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt -local Engine = require 'Engine' local Game = require 'Game' -local utils = require 'utils' local Event = require 'Event' local Input = require 'Input' local Lang = require 'Lang' -local lc = Lang.GetResource("core"); local lui = Lang.GetResource("ui-core"); local ui = require 'pigui' @@ -18,13 +15,13 @@ local pionillium = ui.fonts.pionillium local colors = ui.theme.colors local icons = ui.theme.icons +local SCREEN_BORDER = 4 + local MAX_RADAR_SIZE = 1000000000 local MIN_RADAR_SIZE = 1000 local DEFAULT_RADAR_SIZE = 10000 -local shouldDisplay2DRadar -local current_radar_size = DEFAULT_RADAR_SIZE -local manual_zoom = false +local shouldDisplay2DRadar = false local blobSize = 6.0 local keys = { @@ -55,31 +52,82 @@ local function getColorFor(item) return colors.radarUnknown end +local radar2d = { + icon = icons.radar_2d, + zoom = DEFAULT_RADAR_SIZE, + size = ui.reticuleCircleRadius * 0.66, + getRadius = function(self) return self.size end, + zoomIn = function(self) + self.zoom = math.max(self.zoom / 10, MIN_RADAR_SIZE) + end, + zoomOut = function(self) + self.zoom = math.min(self.zoom * 10, MAX_RADAR_SIZE) + end, + resetZoom = function(self) + self.zoom = DEFAULT_RADAR_SIZE + end, + getZoom = function(self) return self.zoom end, + isAutoZoom = function(self) return false end, +} + +local radar = require 'PiGui.Modules.RadarWidget'() +radar.minZoom = MIN_RADAR_SIZE +radar.maxZoom = MAX_RADAR_SIZE +radar.zoom = DEFAULT_RADAR_SIZE +local radar3d = { + icon = icons.radar_3d, + auto_zoom = true, + size = Vector2(ui.reticuleCircleRadius * 1.8, ui.reticuleCircleRadius * 1.4), + getRadius = function(self) return self.size.x end, + zoomIn = function(self) + if self.auto_zoom then + radar.zoom = 10 ^ math.floor(math.log(radar.zoom, 10)) + else + radar.zoom = math.max(radar.zoom / 10, MIN_RADAR_SIZE) + end + self.auto_zoom = false + end, + zoomOut = function(self) + if self.auto_zoom then + radar.zoom = 10 ^ math.ceil(math.log(radar.zoom, 10)) + else + radar.zoom = math.min(radar.zoom * 10, MAX_RADAR_SIZE) + end + self.auto_zoom = false + end, + resetZoom = function(self) + radar.zoom = DEFAULT_RADAR_SIZE + self.auto_zoom = true + end, + getZoom = function(self) return radar.zoom end, + isAutoZoom = function(self) return self.auto_zoom end, +} + -- display the 2D radar -local function display2DRadar(cntr, size) - local targets = ui.getTargetsNearby(current_radar_size) - local halfsize = size * 0.5 - local thirdsize = size * 0.3 - local twothirdsize = size * 0.7 +radar2d.draw = function(self, center) + local targets = ui.getTargetsNearby(self.zoom) + local halfsize = self.size * 0.5 + local thirdsize = self.size * 0.3 + local twothirdsize = self.size * 0.7 local fgColor = colors.uiPrimary local bgColor = colors.uiBackground:opacity(0.54) local lineThickness = 1.5 local function line(x,y) -- Uncomment to extend the radial line all the way to the outer circle - -- ui.addLine(cntr + Vector2(x, y) * halfsize, center + Vector2(x,y) * size, colors.uiPrimaryDark, lineThickness) - ui.addLine(cntr + Vector2(x, y) * thirdsize, cntr + Vector2(x,y) * twothirdsize, fgColor, lineThickness) + -- ui.addLine(center + Vector2(x, y) * halfsize, center + Vector2(x,y) * size, colors.uiPrimaryDark, lineThickness) + ui.addLine(center + Vector2(x, y) * thirdsize, center + Vector2(x,y) * twothirdsize, fgColor, lineThickness) end -- radar background and border - ui.addCircleFilled(cntr, size, bgColor, ui.circleSegments(size), 1) - ui.addCircle(cntr, size, fgColor, ui.circleSegments(size), lineThickness * 2) + ui.addCircleFilled(center, self.size, bgColor, ui.circleSegments(self.size), 1) + ui.addCircle(center, self.size, fgColor, ui.circleSegments(self.size), lineThickness * 2) -- inner circles -- Uncomment to add an additional circle dividing the 2D radar display - -- ui.addCircle(cntr, halfsize, fgColor, ui.circleSegments(halfsize), lineThickness) - ui.addCircle(cntr, thirdsize, fgColor, ui.circleSegments(thirdsize), lineThickness) - ui.addCircle(cntr, twothirdsize, fgColor, ui.circleSegments(twothirdsize), lineThickness) + -- ui.addCircle(center, halfsize, fgColor, ui.circleSegments(halfsize), lineThickness) + ui.addCircle(center, thirdsize, fgColor, ui.circleSegments(thirdsize), lineThickness) + ui.addCircle(center, twothirdsize, fgColor, ui.circleSegments(twothirdsize), lineThickness) local l = ui.oneOverSqrtTwo -- cross-lines line(-l, l) @@ -92,13 +140,13 @@ local function display2DRadar(cntr, size) local navTarget = player:GetNavTarget() local tooltip = {} for k,v in pairs(targets) do - if v.distance < current_radar_size then - local halfRadarSize = current_radar_size / 2 + if v.distance < self.zoom then + local halfRadarSize = self.zoom / 2 local alpha = 255 if v.distance > halfRadarSize then alpha = 255 * (1 - (v.distance - halfRadarSize) / halfRadarSize) end - local position = cntr + v.aep * size * 2 + local position = center + v.aep * self.size * 2 if v.body == navTarget then local color = Color(colors.navTarget.r, colors.navTarget.g, colors.navTarget.b, alpha) ui.addIcon(position, icons.square, color, Vector2(12, 12), ui.anchor.center, ui.anchor.center) @@ -118,9 +166,6 @@ local function display2DRadar(cntr, size) if #tooltip > 0 then ui.setTooltip(table.concat(tooltip, "\n")) end - -- local distance = ui.Format.Distance(current_radar_size) - -- local textcenter = cntr + Vector2((halfsize + twothirdsize) * 0.5, size) - -- local textsize = ui.addStyledText(textcenter, ui.anchor.left, ui.anchor.bottom, distance, colors.frame, pionillium.small, lui.HUD_RADAR_DISTANCE, colors.lightBlackBackground) end -- Return tooltip for target @@ -140,12 +185,7 @@ local function drawTarget(target, scale, center, color) return tooltip end -local radar = require 'PiGui.Modules.RadarWidget'() ---local currentZoomDist = MIN_RADAR_SIZE -radar.minZoom = MIN_RADAR_SIZE -radar.maxZoom = MAX_RADAR_SIZE - -local function display3DRadar(center, size) +radar3d.draw = function(self, center) local targets = ui.getTargetsNearby(MAX_RADAR_SIZE) local tooltip = {} @@ -155,176 +195,187 @@ local function display3DRadar(center, size) local maxShipDist = 0.0 local maxCargoDist = 0.0 - radar.size = size - radar.zoom = current_radar_size or DEFAULT_RADAR_SIZE - local radius = radar.radius + radar.size = self.size + radar.zoom = radar.zoom or DEFAULT_RADAR_SIZE local scale = radar.radius / radar.zoom - ui.setCursorPos(center - size / 2.0) + ui.setCursorPos(center - self.size / 2.0) -- draw targets below the plane for k, v in pairs(targets) do -- collect some values for zoom updates later maxBodyDist = math.max(maxBodyDist, v.distance) -- only snap to ships if they're less than 50km away (arbitrary constant based on crime range) - if v.body:IsShip() and v.distance < 50000 then maxShipDist = math.max(maxShipDist, v.distance) end + if v.body:IsShip() and v.distance < 50000 then + maxShipDist = math.max(maxShipDist, v.distance) + end -- only snap to cargo containers if they're less than 25km away (arbitrary) - if v.body:IsCargoContainer() and v.distance < 25000 then maxCargoDist = math.max(maxCargoDist, v.distance) end + if v.body:IsCargoContainer() and v.distance < 25000 then + maxCargoDist = math.max(maxCargoDist, v.distance) + end - if v.distance < current_radar_size and v.rel_position.y < 0.0 then - local color = (v.body == navTarget and colors.navTarget) or (v.body == combatTarget and colors.combatTarget) or getColorFor(v) + if v.distance < radar.zoom and v.rel_position.y < 0.0 then + local color = (v.body == navTarget and colors.navTarget) or + (v.body == combatTarget and colors.combatTarget) or + getColorFor(v) table.append(tooltip, drawTarget(v, scale, center, color)) end end + -- draw the radar plane itself ui.withStyleColors({ FrameBg = colors.radarBackground, FrameBgActive = colors.radarFrame, }, function() - -- draw the radar plane itself radar:Draw() end) - -- draw targets above the plane for k, v in pairs(targets) do - if v.distance < current_radar_size and v.rel_position.y >= 0.0 then - local color = (v.body == navTarget and colors.navTarget) or (v.body == combatTarget and colors.combatTarget) or getColorFor(v) + if v.distance < radar.zoom and v.rel_position.y >= 0.0 then + local color = (v.body == navTarget and colors.navTarget) or + (v.body == combatTarget and colors.combatTarget) or + getColorFor(v) table.append(tooltip, drawTarget(v, scale, center, color)) end end + -- return tooltip if mouse is over a target if #tooltip > 0 then ui.setTooltip(table.concat(tooltip, "\n")) end -- handle automatic radar zoom based on player surroundings - if not manual_zoom then + if self.auto_zoom then local maxDist = maxBodyDist if combatTarget then maxDist = combatTarget:GetPositionRelTo(player):length() * 1.4 elseif maxShipDist > 0 then maxDist = maxShipDist * 1.4 elseif maxCargoDist > 0 then - maxDist = maxCargoDist * 1.4 + maxDist = maxCargoDist * 1.4 elseif navTarget then local dist = navTarget:GetPositionRelTo(player):length() maxDist = dist > MAX_RADAR_SIZE and maxBodyDist or dist * 1.4 end - current_radar_size = math.clamp(radar.zoom + (maxDist - radar.zoom) * 0.03, - MIN_RADAR_SIZE, MAX_RADAR_SIZE) + radar.zoom = math.clamp(radar.zoom + (maxDist - radar.zoom) * 0.03, MIN_RADAR_SIZE, MAX_RADAR_SIZE) end - - -- local distance = ui.Format.Distance(current_radar_size) - -- local textwidth = ui.calcTextSize(distance).x - -- local textpos = center + Vector2(textwidth / -2, size.y * 0.42) - -- local textsize = ui.addStyledText(textpos, ui.anchor.left, ui.anchor.bottom, distance, colors.frame, pionillium.small, lui.HUD_RADAR_DISTANCE, colors.lightBlackBackground) end +-- This variable needs to be outside the function in order to capture state +-- between frames. We are trying to ensure that a mouse right-click started and +-- finished inside the radar area in order to trigger the popup. local click_on_radar = false + -- display either the 3D or the 2D radar, show a popup on right click to select local function displayRadar() if ui.optionsWindow.isOpen or Game.CurrentView() ~= "world" then return end - player = Game.player + player = player or Game.player -- only display if there actually *is* a radar installed local equipped_radar = player:GetComponent("EquipSet"):GetInstalledOfType("sensor.radar") - if #equipped_radar > 0 then - - local size = ui.reticuleCircleRadius * 0.66 - local center = Vector2(ui.screenWidth / 2, ui.screenHeight - size - 4) - local zoom = 0 - local toggle_radar = false - - -- Handle keyboard - -- TODO: Convert to axis? - if keys.radar_zoom_in:IsJustActive() then - zoom = 1 - elseif keys.radar_zoom_out:IsJustActive() then - zoom = -1 - end - if keys.radar_reset:IsJustActive() then - zoom = 0 - manual_zoom = false - current_radar_size = DEFAULT_RADAR_SIZE - end - if keys.radar_toggle_mode:IsJustActive() then - toggle_radar = true - end + -- TODO: get ship radar capability and determine functionality based on level of radar installed + if #equipped_radar == 0 then return end + + local instrument = shouldDisplay2DRadar and radar2d or radar3d + local center = Vector2(ui.screenWidth / 2, ui.screenHeight - radar2d.size - SCREEN_BORDER) + local zoom = 0 + local toggle_radar = false + + -- Handle keyboard + -- TODO: Convert to axis? + if keys.radar_zoom_in:IsJustActive() then + zoom = 1 + elseif keys.radar_zoom_out:IsJustActive() then + zoom = -1 + end + if keys.radar_reset:IsJustActive() then + zoom = 0 + instrument:resetZoom() + end + if keys.radar_toggle_mode:IsJustActive() then + toggle_radar = true + end - -- Handle mouse if it is in the radar area - local mp = ui.getMousePos() - local mouse_dist = shouldDisplay2DRadar and size or size * 1.8 - if (mp - center):length() > mouse_dist then - click_on_radar = false - end - if (mp - center):length() < mouse_dist then - if ui.isMouseClicked(1) then - click_on_radar = true - end - if not toggle_radar and click_on_radar and ui.isMouseReleased(1) then - ui.openPopup("radarselector") - end - -- TODO: figure out how to "capture" the mouse wheel to prevent - -- the game engine from using it to also zoom the viewport - if zoom == 0 then - zoom = ui.getMouseWheel() - end - end - if zoom > 0 then - -- Zoom in (decrease scanned area) - if not manual_zoom then - current_radar_size = 10 ^ math.floor(math.log(current_radar_size, 10)) - else - current_radar_size = math.max(current_radar_size / 10, MIN_RADAR_SIZE) - end - manual_zoom = true - elseif zoom < 0 then - -- Zoom out (increase scanned area) - if not manual_zoom then - current_radar_size = 10 ^ math.ceil(math.log(current_radar_size, 10)) - else - current_radar_size = math.min(current_radar_size * 10, MAX_RADAR_SIZE) - end - manual_zoom = true - end + -- Handle mouse if it is in the radar area + local mp = ui.getMousePos() + -- TODO: adjust properly for 3D radar; bit more challenging as its an ellipse + if (mp - center):length() < radar2d.getRadius(radar2d) then ui.popup("radarselector", function() if ui.selectable(lui.HUD_2D_RADAR, shouldDisplay2DRadar, {}) then - Event.Queue('onChangeMFD', 'radar') + toggle_radar = true end if ui.selectable(lui.HUD_3D_RADAR, not shouldDisplay2DRadar, {}) then - Event.Queue('onChangeMFD', 'scanner') + toggle_radar = true end end) - if toggle_radar then - shouldDisplay2DRadar = not shouldDisplay2DRadar - Event.Queue('onChangeMFD', shouldDisplay2DRadar and 'radar' or 'scanner') + if ui.isMouseClicked(1) then + click_on_radar = true end - -- Draw the actual radar - if shouldDisplay2DRadar then - display2DRadar(center, size) - else - display3DRadar(center, Vector2(ui.reticuleCircleRadius * 1.8, size * 2)) + if not toggle_radar and click_on_radar and ui.isMouseReleased(1) then + ui.openPopup("radarselector") + end + -- TODO: figure out how to "capture" the mouse wheel to prevent + -- the game engine from using it to also zoom the viewport + if zoom == 0 then + zoom = ui.getMouseWheel() end - -- Draw the range indicator - local distance = ui.Format.Distance(current_radar_size) - local textpos = Vector2(center.x + size, center.y + size) - local textsize = ui.addStyledText(textpos, ui.anchor.right, ui.anchor.bottom, distance, colors.frame, pionillium.small, lui.HUD_RADAR_DISTANCE, colors.lightBlackBackground) - -- Draw the radar mode in bottom-left corner - -- TODO: use an icon? - local mode = manual_zoom and '[M]' or '[A]' - textpos = Vector2(center.x - size, center.y + size) - ui.addStyledText(textpos, ui.anchor.left, ui.anchor.bottom, mode, colors.alertRed, pionillium.small, lui.HUD_RADAR_DISTANCE, colors.lightBlackBackground) end -end -Event.Register('onChangeMFD', function(selected) - shouldDisplay2DRadar = selected == "radar"; -end) + -- Update the zoom level + if zoom > 0 then + instrument:zoomIn() + elseif zoom < 0 then + instrument:zoomOut() + end + + -- Draw the actual radar - this can't be in a window or the 3D scanner background doesn't render + instrument:draw(center) + + -- Draw the radar buttons and info - this needs to be in a window, otherwise the buttons don't work. + local window_width = ui.reticuleCircleRadius * 1.8 + local window_height = radar2d.size / 3.5 + local window_pos = Vector2(center.x - window_width / 2, center.y + radar2d.size - window_height - SCREEN_BORDER) + local windowFlags = ui.WindowFlags {"NoTitleBar", "NoResize", "NoFocusOnAppearing", "NoBringToFrontOnFocus", "NoSavedSettings"} + ui.setNextWindowPos(window_pos, "Always") + ui.setNextWindowPadding(Vector2(0)) + ui.setNextWindowSize(Vector2(window_width, window_height), "Always") + + ui.window("radar_buttons", windowFlags, function() + + -- Draw radar mode toggle button + local icon = shouldDisplay2DRadar and radar3d.icon or radar2d.icon + local clicked = ui.mainMenuButton(icon, lui.HUD_RADAR_TOGGLE_MODE, false, Vector2(window_height)) + if toggle_radar or clicked then + shouldDisplay2DRadar = not shouldDisplay2DRadar + end + + -- Draw zoom mode indicator + if not shouldDisplay2DRadar then + local button_size = window_height / 1.5 + local tt = radar3d.auto_zoom and lui.HUD_RADAR_ZOOM_MODE_AUTOMATIC or lui.HUD_RADAR_ZOOM_MODE_MANUAL + ui.sameLine() + ui.addCursorPos(Vector2(0, window_height - button_size)) + icon = instrument:isAutoZoom() and icons.radar_automatic or icons.radar_manual + ui.mainMenuButton(icon, tt, ui.theme.buttonColors.disabled, Vector2(button_size)) + end + + -- Draw radar range + local distance = ui.Format.Distance(instrument:getZoom()) + local textpos = ui.getWindowPos() + Vector2(window_width, window_height) + ui.addStyledText(textpos, ui.anchor.right, ui.anchor.bottom, distance, colors.frame, pionillium.small, lui.HUD_RADAR_DISTANCE, colors.lightBlackBackground) + + end) -- window + +end -- function displayRadar() -- reset radar to default at game end -Event.Register("onGameEnd", function() shouldDisplay2DRadar = false end) +Event.Register("onGameEnd", function() + shouldDisplay2DRadar = false + radar2d:resetZoom() + radar3d:resetZoom() +end) -- save/load preference require 'Serializer':Register("PiguiRadar", @@ -335,7 +386,6 @@ ui.registerModule("game", { id = "game-view-radar-module", draw = displayRadar, debugReload = function() - print("Radar module reloading..") package.reimport() end }) diff --git a/data/pigui/modules/ship-internals-window.lua b/data/pigui/modules/ship-internals-window.lua index 36d896ec8a..fc3b57168d 100644 --- a/data/pigui/modules/ship-internals-window.lua +++ b/data/pigui/modules/ship-internals-window.lua @@ -106,7 +106,7 @@ local function displayShipFunctionWindow() assert(thrust_widget_size.y >= mainButtonSize.y) local window_width = ui.getWindowPadding().x * 2 + (mainButtonSize.x + ui.getItemSpacing().x) * buttons + thrust_widget_size.x local window_height = thrust_widget_size.y + ui.getWindowPadding().y * 2 - local window_posx = ui.screenWidth/2 - ui.reticuleCircleRadius - window_width + 12 -- manual move a little closer to the center + local window_posx = ui.screenWidth/2 - ui.reticuleCircleRadius - window_width local window_posy = ui.screenHeight - window_height ui.setNextWindowPos(Vector2(window_posx, window_posy), "Always") ui.window("ShipFunctions", windowFlags, function() @@ -128,6 +128,12 @@ local function displayShipFunctionWindow() end) end -ui.registerModule("game", { id = "ship-internals-window", draw = displayShipFunctionWindow }) +ui.registerModule("game", { + id = "ship-internals-window", + draw = displayShipFunctionWindow, + debugReload = function() + package.reimport() + end, +}) return {}