Export screen: widescreen frame, black sides, slots can be cleared
- cUI_Character (export) and cUI_Savegame draw inside the centred 1024x768 frame (viewport) with the render target beside it cleared black; the export screen's 3D heroes were stretched across the wide viewport, away from their labels and hit areas. - Export screen shows the game's erase button for an occupied slot and reloads the heroes after a delete. Overwriting was already the game's own path once clicks hit the right slot. - game/ui/control.h: shared show/hide for cUI_Control2. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c875d7d6b9
commit
72525cd5ba
9 files changed
+272
-13
No files matched your search
+66
-2
@@ -430,6 +430,49 @@ HRESULT __stdcall ClearShifted(void* device, DWORD count, Rect7* rects, DWORD fl
|
||||
return g_clear(device, count, shifted.data(), flags, color, z, stencil);
|
||||
}
|
||||
|
||||
using GetViewportFn = HRESULT(__stdcall*)(void* device, Viewport7* viewport);
|
||||
constexpr int kVtableGetViewport = 15; // IDirect3DDevice7 (13 SetViewport, 14 MultiplyTransform)
|
||||
constexpr DWORD kClearTarget = 1; // D3DCLEAR_TARGET
|
||||
|
||||
// RAII for full-screen windows (character export, save games) that fill the
|
||||
// 1024 frame with a picture: the render target left and right of the frame is
|
||||
// cleared black, and the window draws with a viewport on the frame. The
|
||||
// export screen's 3D scene (heroes, background) uses a projection for a
|
||||
// 1024x768 viewport; across the widened one it was stretched 1.8x wide, away
|
||||
// from the slot labels and hit areas (measured on a screenshot, 2026-10-18).
|
||||
// Clear clips to the viewport, so the sides are cleared before it is set.
|
||||
class FrameScope {
|
||||
public:
|
||||
FrameScope(void* window, void* device, int dx) {
|
||||
const auto* w = static_cast<const uint8_t*>(window);
|
||||
if (dx == 0 || g_drawScoped || !device || !g_setViewport || !g_clear || (w[kControlFlags] & 1) == 0) {
|
||||
return;
|
||||
}
|
||||
auto getViewport = reinterpret_cast<GetViewportFn>((*static_cast<void***>(device))[kVtableGetViewport]);
|
||||
if (FAILED(getViewport(device, &previous_))) {
|
||||
return;
|
||||
}
|
||||
const LONG height = static_cast<LONG>(previous_.height);
|
||||
Rect7 sides[] = {{0, 0, dx, height},
|
||||
{dx + kLayoutWidth, 0, mods::widescreen::RenderWidth(), height}};
|
||||
g_clear(device, 2, sides, kClearTarget, 0, 1.0f, 0);
|
||||
Viewport7 frame = {static_cast<DWORD>(dx), 0, kLayoutWidth, 768, previous_.minZ, previous_.maxZ};
|
||||
device_ = device;
|
||||
g_setViewport(device, &frame);
|
||||
}
|
||||
~FrameScope() {
|
||||
if (device_) {
|
||||
g_setViewport(device_, &previous_);
|
||||
}
|
||||
}
|
||||
FrameScope(const FrameScope&) = delete;
|
||||
FrameScope& operator=(const FrameScope&) = delete;
|
||||
|
||||
private:
|
||||
void* device_ = nullptr;
|
||||
Viewport7 previous_ = {};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int CursorOffsetX() {
|
||||
@@ -571,6 +614,27 @@ int TooltipOffset(void* tooltip) {
|
||||
return name##_original(self, edx, device, a, b); \
|
||||
}
|
||||
|
||||
// Centred full-screen windows: black beside the frame, viewport on the frame.
|
||||
#define HUD_RENDER_FRAMED(address, name) \
|
||||
int __fastcall name(void* self, void* edx, void* device); \
|
||||
RE_HOOK(address, name); \
|
||||
int __fastcall name(void* self, void* edx, void* device) { \
|
||||
const int dx = mods::hud::OffsetFor(mods::hud::Anchor::Center); \
|
||||
mods::hud::FrameScope frame(self, device, dx); \
|
||||
mods::hud::DrawOffset offset(self, dx); \
|
||||
return name##_original(self, edx, device); \
|
||||
}
|
||||
|
||||
#define HUD_RENDER3_FRAMED(address, name) \
|
||||
int __fastcall name(void* self, void* edx, void* device, int a, int b); \
|
||||
RE_HOOK(address, name); \
|
||||
int __fastcall name(void* self, void* edx, void* device, int a, int b) { \
|
||||
const int dx = mods::hud::OffsetFor(mods::hud::Anchor::Center); \
|
||||
mods::hud::FrameScope frame(self, device, dx); \
|
||||
mods::hud::DrawOffset offset(self, dx); \
|
||||
return name##_original(self, edx, device, a, b); \
|
||||
}
|
||||
|
||||
namespace game {
|
||||
|
||||
using mods::hud::Anchor;
|
||||
@@ -629,11 +693,11 @@ HUD_RENDER(0x006ea750, UiTrade_Render, Anchor::Center)
|
||||
HUD_EVENT(0x006f8960, UiNetworkInfo_HandleEvent, Anchor::Center)
|
||||
HUD_RENDER(0x006f73f0, UiNetworkInfo_Render, Anchor::Center)
|
||||
HUD_EVENT(0x0070e860, UiCharacter_HandleEvent, Anchor::Center)
|
||||
HUD_RENDER3(0x0070f160, UiCharacter_Render, Anchor::Center)
|
||||
HUD_RENDER3_FRAMED(0x0070f160, UiCharacter_Render) // export screen in-game
|
||||
HUD_EVENT(0x00716b30, UiOptions_HandleEvent, Anchor::Center)
|
||||
HUD_RENDER3(0x00716640, UiOptions_Render, Anchor::Center)
|
||||
HUD_EVENT(0x0071d130, UiSavegame_HandleEvent, Anchor::Center)
|
||||
HUD_RENDER(0x0071dd80, UiSavegame_Render, Anchor::Center)
|
||||
HUD_RENDER_FRAMED(0x0071dd80, UiSavegame_Render)
|
||||
HUD_EVENT(0x00720c10, UiBusyDlg_HandleEvent, Anchor::Center)
|
||||
HUD_RENDER(0x007213a0, UiBusyDlg_Render, Anchor::Center)
|
||||
|
||||
|
||||
Reference in new issue
Block a user