Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/radar improvements #5984

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

mwerle
Copy link
Contributor

@mwerle mwerle commented Nov 25, 2024

NOTE: The code could probably do with some tidying up...


  1. ensure targeted object remains visible when switching from automatic to manual zoom (feat(radar): capture mouse wheel for zooming radar #5978 (review))
  2. add intermediate zoom steps for manual zoom (feat(radar): capture mouse wheel for zooming radar #5978 (review))
  3. fix popup disappearing when mouse moves outside of radar area but still over popup
  4. improve checking when mouse is over radar area (especially for the 3D scanner mode display)
  5. add left-click to set navigation target when clicking on a target "pip". This will show a popup if there are multiple targets on a "pip".
  6. add double-click to clear the navigation target

Additionally fix an issue introduced in 678d46e where a couple of parameters are swapped when calling "PlayMusic" (found due to compiler warning).

TODO:

  • code review
  • clean up commits
  • merge

Commit 678d46e added crossfading, but
accidentally swapped two parameters when invoking "PlayMusic()".
Ensure that a targeted object will always remain visible on the radar
when switching from automatic to manual zoom.
Instead of increasing/decreasing the manual zoom level by powers of 10,
use a semi-logarithmic scale of 1,2,5,10... when manually zooming in or
out.
The 2D radar is trivial as it is a circle, but the 3D scanner is an
ellipse. For now we check if the mouse is in a rectangle surrounding
the scanner, which is an improvement but not a complete fix.

TODO: if the mouse leaves the radar area while the popup for selecting
between the radar modes is displayed, the popup disappears. The
mouse-area-check should be disabled while the popup is displayed.
When the radar selection popup is open and the mouse leaves the radar
area, the popup automatically closes even if the mouse is over the popup.

Detect when the popup is open and only close it after it has been clicked
on.
Left-clicking on a "pip" sets it as the navigation target.

TODO: When there are multiple targets represented by a single "pip", the
first is automatically chosen. If there are multiple targets in a single
"pip", a popup should be displayed allowing the player to select which
target to set.
Display a popup to allow the player to select a target if there are
multiple targets over a "pip".

Also fix issue if the player clicks outside a popup which could get
mouse handling confused.
Comment on lines 404 to 418
if radar_popup_displayed or isMouseOverRadar() then
ui.popup("radarselector", function()
if ui.selectable(lui.HUD_2D_RADAR, shouldDisplay2DRadar, {}) then
toggle_radar = true
if not shouldDisplay2DRadar then
toggle_radar = true
end
radar_popup_displayed = false
end
if ui.selectable(lui.HUD_3D_RADAR, not shouldDisplay2DRadar, {}) then
toggle_radar = true
if shouldDisplay2DRadar then
toggle_radar = true
end
radar_popup_displayed = false
end
end)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Now that we have the icon button to switch the radar between 2d and 3d modes, do we still want to support the right-click popup? The code is a bit messy and I don't really see the utility.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend "no". The only reason to keep the right-click menu is to not break existing muscle memory / tutorials, which is kind of a moot point with the significant UI additions to the radar widget.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Muscle-memory for right-click popups is pretty poor anyway..

Thanks, so unless someone else is going to step in with a strong argument "for" I'll remove it.

Double-clicking on the radar will clear the current navigation target.
@mwerle mwerle marked this pull request as ready for review November 26, 2024 00:12
Comment on lines +86 to +133
-- Generate the next in a sequence of 1 2 5 10 20 ...
local function nextZoomSeq(currZoom, maxZoom)
local newZoom = 0
if getDigits(currZoom) == 2 then
newZoom = currZoom * 2.5
else
newZoom = currZoom * 2
end
return math.min(newZoom, maxZoom)
end

-- Generate the previous in a sequence of 1 2 5 10 20 ...
local function prevZoomSeq(currZoom, minZoom)
local newZoom = 0
if getDigits(currZoom) == 5 then
newZoom = currZoom / 2.5
else
newZoom = currZoom / 2
end
return math.max(newZoom, minZoom)
end

-- Normalize a number into the zoom sequence (1, 2, 5, 10, 20, ...), rounding
-- it up or down to the next item in the sequence (default: down).
local function normalizeToZoomSeq(number, up)
if up == nil then
up = false
end

local firstDigits = getDigits(number, 1, 2)
if firstDigits > 50 then
firstDigits = up and 10 or 5
elseif firstDigits == 50 then
firstDigits = 5
elseif firstDigits > 20 then
firstDigits = up and 5 or 2
elseif firstDigits == 20 then
firstDigits = 2
elseif firstDigits > 10 then
firstDigits = up and 2 or 1
else
firstDigits = 1
end

local numDigits = #tostring(number)
return firstDigits * 10^(numDigits-1)
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions are possibly of general use and could be placed into a utility library?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the normalizeToZoomSeq function can be generalized by taking the log10 of the zoom and snapping the fractional part to one of 0, ~0.3, or ~0.7 (for 1, 2, 5 respectively). The particular values should likely be precomputed as math.log10(2) etc. as they have a significant number of digits.

I'll leave the particulars of the implementation to you, but that's how I'd approach the problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have a look, although I daresay floating-point arithmetic is going to result in rather imprecise results. I guess performance is moot since it's not going to be calculated every frame..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That said, the core question here was whether these functions should be part of a utility library, and if so where, instead of being in "radar.lua"..

Comment on lines +428 to +431
Vector2(center.x - ui.reticuleCircleRadius * 0.9,
center.y - ui.reticuleCircleRadius * 0.7),
Vector2(center.x + ui.reticuleCircleRadius * 0.9,
center.y + ui.reticuleCircleRadius * 0.7))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably move the window size definitions (below) to the top, and re-use the sizes here.

local function onTargetClicked(target)
-- TODO: Should ships be set as nav or combat targets?
Game.player:SetNavTarget(target.body)
end

-- display either the 3D or the 2D radar, show a popup on right click to select
local function displayRadar()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally I'd like to split this function into multiple functions, but that would require moving a LOT of variables into the file scope. Is there another way?

What is the best practise in Lua for doing this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends - what are you trying to split out of the function? You have a few options:

  1. Pass read-only parameters via function arguments, granularize function actions such that they can return "changed values" as needed. This allows one "master function" to own the local functions and pass them around. If needed, the master function can pass a table value wrapping multiple variables into sub-functions, but that's basically option 2:
  2. Convert the radar display into a module (see data/pigui/libs/module.lua, and how it's used in e.g. the save/load window) and implement most of your drawing functions as methods on the "radar module". If you do this route, I'd strongly recommend attempting to make your "display" functions non-mutating and use the built-in message() facilities to handle state changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking input handling and actually drawing the radar are two rather different logical tasks which should be split.. so yeah, I guess I can put the entire state of the radar module into a table and pass that around (I forgot about tables..).

It's a shame the 3d radar is partially in C++ otherwise it would be much neater to refactor this thing. If/when we upgrade to the next version of ImGUi we could move this into Lua as well since the next version of ImGui has native ellipse drawing primitives.

I'm thinking the next step will be to actually define multiple pieces of radar equipment and then have functionality depending on what equipment is installed - range, target identification, etc. So any refactoring here should take this into account. Or possibly require further refactoring, in which case, might as well leave this as-is for now and leave the refactoring to when the next step is done.

Thoughts?

Copy link
Member

@Web-eWorks Web-eWorks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add double-click to clear the navigation target

General thought - it might be better to have a "reject/unlock target" keybinding which unsets combat then navigation targets in that order. Nav/combat target is generally a state which is managed (by the player) at a higher level than the radar widget.

local click_on_radar = false
local function onTargetClicked(target)
-- TODO: Should ships be set as nav or combat targets?
Game.player:SetNavTarget(target.body)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ships are always combat targets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ships are always combat targets.

Not really - rescue missions, for example.

radar_popup_displayed = false
click_on_radar = false
end
elseif isMouseOverRadar() then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that instead of calling isMouseOverRadar(), you can instead potentially submit a ui.invisibleButton with the appropriate button flags (might need to expose a few more in src/lua/LuaPiGui.cpp) before drawing the radars and test ui.isItemClicked() / ui.isItemHovered() etc.

This would allow for each radar to define its own "click area" and potentially unify some of the "clicked on radar" handling.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you draw circular buttons? And how will this invisible button affect the other buttons because I don't think I'll be able to make an elliptical button!

@@ -50,7 +50,7 @@ namespace Sound {
current = &m_eventTwo;
next = &m_eventOne;
}
next->PlayMusic(name.c_str(), m_volume, repeat, fadeDelta, current);
next->PlayMusic(name.c_str(), m_volume, fadeDelta, repeat, current);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thank you!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thank you!

It helps to compile with BOTH clang and gcc... :)

We -should- set "-Wall, -Weverything, -Werror", at least for release builds, except the "contrib" folder.

No excuse to have a warning in our code. If the code can really not be modified to fix a warning (compilers aren't infallible either), can always set a #pragma with documentation explaining why.

@mwerle
Copy link
Contributor Author

mwerle commented Nov 26, 2024

add double-click to clear the navigation target

General thought - it might be better to have a "reject/unlock target" keybinding which unsets combat then navigation targets in that order. Nav/combat target is generally a state which is managed (by the player) at a higher level than the radar widget.

Sure - I just thought it poor that being able to click on a target to set nav-target (can use RMB to set combat target now that we're getting rid of the RMB popup) there was no way to undo that.

Pressing "Y" already resets the nav target, not sure about combat..

@impaktor
Copy link
Member

I suspect most players don't even know about the 2D vs 3D radar.

@bszlrd
Copy link
Contributor

bszlrd commented Nov 26, 2024

Yeah, that button will definitely help with the discoverability

@mwerle
Copy link
Contributor Author

mwerle commented Nov 26, 2024

I suspect most players don't even know about the 2D vs 3D radar.

I didn't.. (but then again I'm newly back to Pioneer..)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants