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();
|
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_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_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))); }
|
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
|
// normalized [0,1] image space (top-left origin) so C# can forward
|
||||||
// them to the map camera with the same math the popout uses.
|
// them to the map camera with the same math the popout uses.
|
||||||
ImVec2 imgPos = ImGui::GetCursorScreenPos();
|
ImVec2 imgPos = ImGui::GetCursorScreenPos();
|
||||||
|
g_ovMapImgX.store(imgPos.x);
|
||||||
|
g_ovMapImgY.store(imgPos.y);
|
||||||
ImGui::InvisibleButton("##mapHit", sz, ImGuiButtonFlags_MouseButtonLeft);
|
ImGui::InvisibleButton("##mapHit", sz, ImGuiButtonFlags_MouseButtonLeft);
|
||||||
bool hov = ImGui::IsItemHovered();
|
bool hov = ImGui::IsItemHovered();
|
||||||
ImVec2 nrm = { (io.MousePos.x - imgPos.x) / sz.x,
|
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).
|
// aspect to the window shape (map fills the window, no letterbox bars).
|
||||||
void Overlay_GetMapView(float* outW, float* outH);
|
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.
|
// Show/hide the overlay UI.
|
||||||
void Overlay_SetVisible(bool visible);
|
void Overlay_SetVisible(bool visible);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,13 @@ void RRPOPOUT_GetOverlayMapView(float* outW, float* outH) {
|
||||||
Overlay_GetMapView(outW, 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,
|
// 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
|
// loco/location lists, rotation, follow flags, etc. to this handle with the same
|
||||||
// RRPOPOUT_Set* / RRPOPOUT_PollInputEvents functions the popout uses.
|
// 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).
|
// Intercept the map hotkey (GameInput calls Toggle() when M / the bound key is pressed).
|
||||||
// Open our overlay instead of the stock map panel.
|
// 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))]
|
[HarmonyPatch(typeof(MapWindow), nameof(MapWindow.Toggle))]
|
||||||
internal static class MapWindow_Toggle_Patch
|
internal static class MapWindow_Toggle_Patch
|
||||||
{
|
{
|
||||||
private static bool Prefix()
|
private static bool Prefix()
|
||||||
{
|
{
|
||||||
if (UiService.MapBypass) return true;
|
if (UiService.MapBypass) return true;
|
||||||
|
if (!PopoutModule.Settings.enabled) return true;
|
||||||
UiService.ToggleOverlay();
|
UiService.ToggleOverlay();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +105,7 @@ internal static class MapWindow_Show_Patch
|
||||||
private static bool Prefix()
|
private static bool Prefix()
|
||||||
{
|
{
|
||||||
if (UiService.MapBypass) return true;
|
if (UiService.MapBypass) return true;
|
||||||
|
if (!PopoutModule.Settings.enabled) return true;
|
||||||
UiService.OpenOverlay();
|
UiService.OpenOverlay();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -116,6 +119,7 @@ internal static class MapWindow_ShowPos_Patch
|
||||||
private static bool Prefix(Vector3 gamePosition)
|
private static bool Prefix(Vector3 gamePosition)
|
||||||
{
|
{
|
||||||
if (UiService.MapBypass) return true;
|
if (UiService.MapBypass) return true;
|
||||||
|
if (!PopoutModule.Settings.enabled) return true;
|
||||||
UiService.OpenOverlay();
|
UiService.OpenOverlay();
|
||||||
MapBuilder.Shared?.SetMapCenter(gamePosition);
|
MapBuilder.Shared?.SetMapCenter(gamePosition);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -255,10 +259,16 @@ internal sealed class UiHost : MonoBehaviour
|
||||||
// window, so the rest of the screen stays interactive while the map floats.
|
// window, so the rest of the screen stays interactive while the map floats.
|
||||||
MouseOverOverlay = _visible && Native.RRPOPOUT_OverlayWantsMouse() == 1;
|
MouseOverOverlay = _visible && Native.RRPOPOUT_OverlayWantsMouse() == 1;
|
||||||
|
|
||||||
// F10: switch the in-game map to the OS popout window. Only fires while the
|
// T key ("Jump to Mouse"): our IsMouseOverGameWindow patch blocks the game's
|
||||||
// in-game overlay is active (no-op if neither map surface is open).
|
// StrategyCameraController.TeleportToMouse() while the overlay is up, so we
|
||||||
if (_visible && Input.GetKeyDown(KeyCode.F10))
|
// handle it ourselves using the normalised cursor position within the map image.
|
||||||
PopoutModule.ScheduleExternalLaunch();
|
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()
|
private void LateUpdate()
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,14 @@ namespace S3.Modules.Popout {
|
||||||
[DllImport("user32.dll")] private static extern short GetAsyncKeyState(int vKey);
|
[DllImport("user32.dll")] private static extern short GetAsyncKeyState(int vKey);
|
||||||
|
|
||||||
private bool _prevHotkeyDown;
|
private bool _prevHotkeyDown;
|
||||||
|
private bool _prevTeleportDown;
|
||||||
public bool CloseRequested { get; private set; }
|
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.
|
// Status bar: only push to native when the string changes.
|
||||||
private string _lastStatusText = "";
|
private string _lastStatusText = "";
|
||||||
|
|
||||||
|
|
@ -169,6 +175,15 @@ namespace S3.Modules.Popout {
|
||||||
bool hotkeyNow = IsHotkeyDown();
|
bool hotkeyNow = IsHotkeyDown();
|
||||||
if (hotkeyNow && !_prevHotkeyDown) CloseRequested = true;
|
if (hotkeyNow && !_prevHotkeyDown) CloseRequested = true;
|
||||||
_prevHotkeyDown = hotkeyNow;
|
_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;
|
break;
|
||||||
|
|
||||||
case InputEventType.MouseMove:
|
case InputEventType.MouseMove:
|
||||||
|
_lastMouseX = e.x;
|
||||||
|
_lastMouseY = e.y;
|
||||||
if (!_isDragging) break;
|
if (!_isDragging) break;
|
||||||
if (!_didDrag &&
|
if (!_didDrag &&
|
||||||
(Mathf.Abs(e.x - _dragStartX) > kClickThreshold ||
|
(Mathf.Abs(e.x - _dragStartX) > kClickThreshold ||
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,11 @@ namespace S3.Modules.Popout {
|
||||||
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
[DllImport(Dll, CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void RRPOPOUT_GetOverlayMapView(out float outW, out float outH);
|
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
|
// 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
|
// (status/loco/location/rotation/follow) and poll it for toolbar commands
|
||||||
// with the same handle-based functions the popout uses.
|
// with the same handle-based functions the popout uses.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue