Map: fix T key teleport in overlay and popout; pass M through when module disabled
Overlay: our IsMouseOverGameWindow patch was blocking StrategyCameraController.TeleportToMouse() as a side effect. Added native export RRPOPOUT_GetOverlayMouseMapPos (stores imgPos atomically each render frame, returns normalized map-image cursor coords) and handle the T key directly in UiHost.Update(), invoking MapDrag.OnTeleport with the correct viewport position. MapDrag.Update() stays gated behind _pointerOver which never fires on our collapsed canvas, so there is no double-teleport. Popout: T key does not fire through Unity Input System when the game window lacks focus. Added GetAsyncKeyState(VK_T) rising-edge detection in DetachedPanel.Update(), using the last MouseMove event position (tracked before the drag-only guard) as the viewport coordinate. Module disabled: all three MapWindow_Toggle/Show/ShowPos Harmony prefixes now pass through to the game's native map when PopoutModule.Settings.enabled is false. Previously UiService.Install() always applied the intercepts regardless of module state. Also removed F10 as a shortcut to switch to the popout - F9 already does this and F10 conflicts with the Shift+F10 UMM hotkey.
This commit is contained in:
parent
c5e75ad54a
commit
fcfdc6fba0
6 changed files with 63 additions and 4 deletions
|
|
@ -1457,6 +1457,20 @@ void Overlay_GetMapView(float* outW, float* outH) {
|
|||
if (outH) *outH = g_ovViewH.load();
|
||||
}
|
||||
|
||||
// Screen-space top-left corner (pixels, top-left origin) of the map image area, stored
|
||||
// each frame so C# can compute a normalized in-image mouse position for teleport.
|
||||
// -1 when the map is not currently drawn (no save loaded, or wrong frame).
|
||||
static std::atomic<float> g_ovMapImgX {-1.f}, g_ovMapImgY {-1.f};
|
||||
void Overlay_GetMouseMapPos(float* outX, float* outY) {
|
||||
float imgX = g_ovMapImgX.load(), imgY = g_ovMapImgY.load();
|
||||
float vw = g_ovViewW.load(), vh = g_ovViewH.load();
|
||||
if (imgX < 0.f || vw <= 0.f || vh <= 0.f) {
|
||||
if (outX) *outX = -1.f; if (outY) *outY = -1.f; return;
|
||||
}
|
||||
if (outX) *outX = (g_ovMouseX.load() - imgX) / vw;
|
||||
if (outY) *outY = (g_ovMouseY.load() - imgY) / vh;
|
||||
}
|
||||
|
||||
void Overlay_SetAlpha(float alpha) { g_ovAlpha.store(std::max(0.1f, std::min(1.0f, alpha))); }
|
||||
void Overlay_SetMapAlpha(float alpha) { g_mapAlpha.store(std::max(0.0f, std::min(1.0f, alpha))); }
|
||||
void Overlay_SetMapBgAlpha(float alpha) { g_mapBgAlpha.store(std::max(0.0f, std::min(1.0f, alpha))); }
|
||||
|
|
@ -1596,6 +1610,8 @@ void Renderer_PresentOverlay() {
|
|||
// normalized [0,1] image space (top-left origin) so C# can forward
|
||||
// them to the map camera with the same math the popout uses.
|
||||
ImVec2 imgPos = ImGui::GetCursorScreenPos();
|
||||
g_ovMapImgX.store(imgPos.x);
|
||||
g_ovMapImgY.store(imgPos.y);
|
||||
ImGui::InvisibleButton("##mapHit", sz, ImGuiButtonFlags_MouseButtonLeft);
|
||||
bool hov = ImGui::IsItemHovered();
|
||||
ImVec2 nrm = { (io.MousePos.x - imgPos.x) / sz.x,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ int Overlay_PollInput(InputEvent* out, int maxEvents);
|
|||
// aspect to the window shape (map fills the window, no letterbox bars).
|
||||
void Overlay_GetMapView(float* outW, float* outH);
|
||||
|
||||
// Current mouse position normalised within the map image (top-left origin, [0,1]).
|
||||
// Returns (-1, -1) when the map is not currently rendered (no save or map inactive).
|
||||
void Overlay_GetMouseMapPos(float* outX, float* outY);
|
||||
|
||||
// Show/hide the overlay UI.
|
||||
void Overlay_SetVisible(bool visible);
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,13 @@ void RRPOPOUT_GetOverlayMapView(float* outW, float* outH) {
|
|||
Overlay_GetMapView(outW, outH);
|
||||
}
|
||||
|
||||
// Current mouse position normalised within the map image (top-left origin, [0,1]).
|
||||
// Returns (-1, -1) when the map image is not visible this frame.
|
||||
extern "C" __declspec(dllexport)
|
||||
void RRPOPOUT_GetOverlayMouseMapPos(float* outX, float* outY) {
|
||||
Overlay_GetMouseMapPos(outX, outY);
|
||||
}
|
||||
|
||||
// Handle of the in-game overlay's shared UI-state holder. C# pushes status text,
|
||||
// loco/location lists, rotation, follow flags, etc. to this handle with the same
|
||||
// RRPOPOUT_Set* / RRPOPOUT_PollInputEvents functions the popout uses.
|
||||
|
|
|
|||
|
|
@ -85,12 +85,14 @@ internal static class GameInput_IsMouseOverGameWindow_Patch
|
|||
|
||||
// Intercept the map hotkey (GameInput calls Toggle() when M / the bound key is pressed).
|
||||
// Open our overlay instead of the stock map panel.
|
||||
// Pass through when the Map Module is disabled so the game's native map opens normally.
|
||||
[HarmonyPatch(typeof(MapWindow), nameof(MapWindow.Toggle))]
|
||||
internal static class MapWindow_Toggle_Patch
|
||||
{
|
||||
private static bool Prefix()
|
||||
{
|
||||
if (UiService.MapBypass) return true;
|
||||
if (!PopoutModule.Settings.enabled) return true;
|
||||
UiService.ToggleOverlay();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -103,6 +105,7 @@ internal static class MapWindow_Show_Patch
|
|||
private static bool Prefix()
|
||||
{
|
||||
if (UiService.MapBypass) return true;
|
||||
if (!PopoutModule.Settings.enabled) return true;
|
||||
UiService.OpenOverlay();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -116,6 +119,7 @@ internal static class MapWindow_ShowPos_Patch
|
|||
private static bool Prefix(Vector3 gamePosition)
|
||||
{
|
||||
if (UiService.MapBypass) return true;
|
||||
if (!PopoutModule.Settings.enabled) return true;
|
||||
UiService.OpenOverlay();
|
||||
MapBuilder.Shared?.SetMapCenter(gamePosition);
|
||||
return false;
|
||||
|
|
@ -255,10 +259,16 @@ internal sealed class UiHost : MonoBehaviour
|
|||
// window, so the rest of the screen stays interactive while the map floats.
|
||||
MouseOverOverlay = _visible && Native.RRPOPOUT_OverlayWantsMouse() == 1;
|
||||
|
||||
// F10: switch the in-game map to the OS popout window. Only fires while the
|
||||
// in-game overlay is active (no-op if neither map surface is open).
|
||||
if (_visible && Input.GetKeyDown(KeyCode.F10))
|
||||
PopoutModule.ScheduleExternalLaunch();
|
||||
// T key ("Jump to Mouse"): our IsMouseOverGameWindow patch blocks the game's
|
||||
// StrategyCameraController.TeleportToMouse() while the overlay is up, so we
|
||||
// handle it ourselves using the normalised cursor position within the map image.
|
||||
if (_visible && _mapActive && GameInput.shared.Teleport)
|
||||
{
|
||||
Native.RRPOPOUT_GetOverlayMouseMapPos(out float mx, out float my);
|
||||
// mx/my are top-left origin [0,1]; Unity viewport is bottom-left origin.
|
||||
if (mx >= 0f && mx <= 1f && my >= 0f && my <= 1f)
|
||||
PanelFinder.GetMapDrag()?.OnTeleport?.Invoke(new Vector2(mx, 1f - my));
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
|
|
|
|||
|
|
@ -36,8 +36,14 @@ namespace S3.Modules.Popout {
|
|||
[DllImport("user32.dll")] private static extern short GetAsyncKeyState(int vKey);
|
||||
|
||||
private bool _prevHotkeyDown;
|
||||
private bool _prevTeleportDown;
|
||||
public bool CloseRequested { get; private set; }
|
||||
|
||||
// Last mouse position (normalised [0,1], top-left origin) received via MouseMove.
|
||||
// Updated by ForwardInputEvent; used for popout teleport key handling.
|
||||
private float _lastMouseX = -1f;
|
||||
private float _lastMouseY = -1f;
|
||||
|
||||
// Status bar: only push to native when the string changes.
|
||||
private string _lastStatusText = "";
|
||||
|
||||
|
|
@ -169,6 +175,15 @@ namespace S3.Modules.Popout {
|
|||
bool hotkeyNow = IsHotkeyDown();
|
||||
if (hotkeyNow && !_prevHotkeyDown) CloseRequested = true;
|
||||
_prevHotkeyDown = hotkeyNow;
|
||||
|
||||
// T key ("Jump to Mouse"): teleport the player to the last-known cursor position
|
||||
// on the map. We use GetAsyncKeyState so this fires even when the game window
|
||||
// doesn't have focus. Unity's Input System won't fire Teleport from the popout
|
||||
// window, so we poll here instead. VK_T = 0x54 (matches the game's default binding).
|
||||
bool teleportNow = IsDown(0x54);
|
||||
if (teleportNow && !_prevTeleportDown && _lastMouseX >= 0f)
|
||||
PanelFinder.GetMapDrag()?.OnTeleport?.Invoke(new Vector2(_lastMouseX, 1f - _lastMouseY));
|
||||
_prevTeleportDown = teleportNow;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -321,6 +336,8 @@ namespace S3.Modules.Popout {
|
|||
break;
|
||||
|
||||
case InputEventType.MouseMove:
|
||||
_lastMouseX = e.x;
|
||||
_lastMouseY = e.y;
|
||||
if (!_isDragging) break;
|
||||
if (!_didDrag &&
|
||||
(Mathf.Abs(e.x - _dragStartX) > kClickThreshold ||
|
||||
|
|
|
|||
|
|
@ -223,6 +223,11 @@ namespace S3.Modules.Popout {
|
|||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void RRPOPOUT_GetOverlayMapView(out float outW, out float outH);
|
||||
|
||||
// Current mouse position normalised within the map image (top-left origin, [0,1]).
|
||||
// Returns (-1, -1) when the map is not visible this frame.
|
||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void RRPOPOUT_GetOverlayMouseMapPos(out float outX, out float outY);
|
||||
|
||||
// Handle of the in-game overlay's shared UI-state holder. Push state to it
|
||||
// (status/loco/location/rotation/follow) and poll it for toolbar commands
|
||||
// with the same handle-based functions the popout uses.
|
||||
|
|
|
|||
Loading…
Reference in a new issue