Replace the bare-bones stock map with a full Dear ImGui in-game overlay (M key). The overlay shares the same toolbar and compass as the OS popout window: Follow, Pop Out, Gear, rotation compass. Pop Out button is now in the bottom toolbar where it is actually visible. Pressing it (or F10) closes the overlay, waits 0.5 s for the camera to release, then opens the OS popout window. Closing the popout restores the overlay if it was running when the launch was triggered. Pressing M while the popout is open closes the popout and reopens the overlay. Harmony patches intercept MapWindow.Toggle and Show so base-game "Show on map" links and the map hotkey both route through the S3 overlay. MapBypass lets internal S3 calls through without triggering the patch recursively. Camera ownership is enforced in LateUpdate so no base-game script can reset targetTexture before the camera renders. Object.Destroy replaces Release() on both RT teardown paths so the D3D address stays live until end-of-frame and cannot be recycled into a new RT mid-frame.
91 lines
4 KiB
C++
91 lines
4 KiB
C++
#pragma once
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <Windows.h>
|
|
#include <d3d11.h>
|
|
#include <atomic>
|
|
#include <mutex>
|
|
#include <cstring>
|
|
#include "input_queue.h"
|
|
|
|
struct PopoutWindow {
|
|
HWND hwnd = nullptr;
|
|
HWND contentHwnd = nullptr; // D3D11 swapchain target
|
|
IDXGISwapChain* swapChain = nullptr;
|
|
ID3D11RenderTargetView* rtv = nullptr;
|
|
|
|
// Message-pump thread that owns hwnd. Kept open for the window's lifetime so
|
|
// DestroyPopoutWindow can join it before freeing this struct (the pump thread
|
|
// touches `this` while processing WM_DESTROY).
|
|
HANDLE pumpThread = nullptr;
|
|
|
|
// Swapchain dimensions — content area only (no status bar gap any more;
|
|
// the ImGui toolbar is an overlay rendered into the same swapchain).
|
|
std::atomic<int> width {800};
|
|
std::atomic<int> height {600};
|
|
|
|
// Texture + UV rect written by RRPOPOUT_SetFrameTexture on the main thread,
|
|
// read by Renderer_Present on the render thread.
|
|
std::atomic<void*> pendingTexture {nullptr};
|
|
std::atomic<float> srcU0 {0.f}, srcV0 {0.f};
|
|
std::atomic<float> srcU1 {1.f}, srcV1 {1.f};
|
|
|
|
std::atomic<bool> resizing {false};
|
|
std::atomic<bool> destroyRequested {false};
|
|
std::atomic<bool> alive {true};
|
|
|
|
InputQueue inputQueue;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// ImGui input state
|
|
// Written by the Win32 message pump thread; consumed by the render thread.
|
|
// -----------------------------------------------------------------------
|
|
std::atomic<float> imMouseX {-1.f};
|
|
std::atomic<float> imMouseY {-1.f};
|
|
std::atomic<bool> imLButton {false};
|
|
std::atomic<bool> imRButton {false};
|
|
// Wheel accumulator in raw WHEEL_DELTA units (120 per notch).
|
|
// fetch_add on message pump; exchange(0) on render thread.
|
|
std::atomic<int> imWheelRaw {0};
|
|
// Written by render thread after each ImGui frame.
|
|
// When true, PollInputEvents suppresses mouse events so they don't reach Unity.
|
|
std::atomic<bool> imWantMouse {false};
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Status bar label (UTF-8), shown in the ImGui toolbar.
|
|
// Written by RRPOPOUT_SetStatusText (main thread), read by render thread.
|
|
// -----------------------------------------------------------------------
|
|
char imStatusText[256];
|
|
std::mutex imStatusMutex;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Dynamic menu lists (UTF-8 labels), set by RRPOPOUT_SetLocoList /
|
|
// RRPOPOUT_SetLocationList from the C# side every few seconds.
|
|
// Read by the render thread inside the ImGui settings menu.
|
|
// -----------------------------------------------------------------------
|
|
struct ImMenuItem { char label[128]; };
|
|
std::vector<ImMenuItem> imLocoList;
|
|
std::mutex imLocoMutex;
|
|
std::vector<ImMenuItem> imLocationList;
|
|
std::mutex imLocationMutex;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Map rotation + ME flag (C# → render thread via exports)
|
|
// -----------------------------------------------------------------------
|
|
std::atomic<float> imMapRotationDeg {0.f};
|
|
std::atomic<float> imMapZoom {500.f};
|
|
std::atomic<bool> imMapSyncPlayer {false};
|
|
std::atomic<bool> imMapEnhancerInstalled{false};
|
|
std::atomic<bool> imFollowPlayer {false};
|
|
std::atomic<bool> imAlwaysOnTop {false};
|
|
|
|
// Loaded geometry from the save file — consumed by MessagePumpThread before CreateWindowExW.
|
|
std::atomic<int> lastWinX {-1}, lastWinY {-1};
|
|
std::atomic<int> lastWinW {900}, lastWinH {700};
|
|
|
|
PopoutWindow() {
|
|
// Status starts empty (filled live with zoom/coords). Avoids a default label
|
|
// that would be redundant with the window title bar and would render any
|
|
// non-ASCII glyph as '?' in ImGui's default font.
|
|
imStatusText[0] = '\0';
|
|
}
|
|
};
|