- Creatures were culled beyond 1024+200 x 768+200 screen pixels (cmp 4C8h / 3C8h in both object passes): invisible in the right part of a wide view, and the player too once the centre passed 1224. The bounds follow the render size now. - Centre immediates written as negatives (-200h / -180h), missed before: the row renderer's static object origin (0x62babb) and aiming at the cursor (0x4fb620). - HUD: moved windows no longer shift the shared mouse cursor object; the cursor accessor (0x6550f0) hands them a per-thread shifted copy and writes back what they changed. World input on other threads read the shifted position now and then, which made hold-to-move jerky. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3.0 KiB
HUD layout in widened views (src/mods/hud.cpp)
The UI is laid out for 1024x768. In a widened render target (e.g. 1835x768)
the windows are moved as a whole, without touching their own coordinates:
the bottom bar and all dialogs are centred, the party panel (portrait, health)
sits at the right edge. Main menu and loading screen keep the classic 1024x768
frame (see widescreen.md).
How a window is moved
A window's render and event methods are hooked (HUD_RENDER, HUD_RENDER3,
HUD_EVENT). While one of them runs on a thread, everything the window sees or
draws is translated by its offset dx (outermost window wins, children and
shared base methods are inside the scope):
| What | Where | Translation |
|---|---|---|
screen-space vertices (XYZRHW) |
IDirect3DDevice7::DrawPrimitive / DrawIndexedPrimitive (vtable 25 / 26) |
x + dx |
| 3D items (belt, equipment) | ortho projection centred on the screen | none needed |
| mouse events | cEvent_mouseMove x at +4 (previous) and +0xc; button down / up x at +8 |
x - dx, restored afterwards |
mouse cursor object (*0xcdbadc, accessor 0x6550f0) |
x at +4, previous x at +0xc (polled for hover, item pick-up 0x6559a0) |
the accessor returns a per-thread copy with x - dx; dwords the window changed are written back afterwards |
GetMouseClientPos (0x66e500) |
polled position | x - dx |
Calls outside a window's scope
- Tooltips (
cUI_Tooltip::vf140x6e7300) are separate windows that the hovered window positions in its own layout: they draw with the offset of the moved window under the cursor (noted while windows render, expires after 150 ms). - Hit tests (
vf1c:0x731d40, taskbar0x6dfb10) are also called by the UI manager with plain render coordinates, e.g. "is UI under the cursor" for world picking (0x75a370). They use the offset the top-level window (parent chain at +0x50) was last drawn with. - The item carried by the cursor (
0x6555e0, drawn by the render loop) and the manager's status line / message box (0x75bbf0,0x75c200, laid out around x = 512) draw with the centre offset.
UI events
vf04 of an event is its category: 1 mouse move, 2 button down, 3 button up,
4 key (code at +8), 5 UI command (cEventUI2: id at +4, vf08 subtype). Only
mouse events carry positions; category 5 events with subtype 0x10 test a point
at +0x48 in cUI_Window2::vf10, but commands such as "open ESC menu" (0x11)
keep unrelated data there.
Why the cursor is copied, not shifted
UI windows render on the render thread while world input (picking,
hold-to-move, 0x611f10) reads the same cursor object on another thread.
Shifting the shared object for the duration of a window's scope let the world
sample a position ~400 px off now and then (jerky hold-to-move). Every user of
the cursor goes through the accessor 0x6550f0, so inside a moved window it
returns a thread-local copy instead (with its own CRITICAL_SECTION at +0x24;
copying and writing back hold the real object's lock).