Map: zoomed-view symbols centred, 10x reveal and enemy range; progress screen on the menu background
- Megamap / TAB map zoomed view: heroes' markers and the other symbols (integer 200h centre immediates) follow the view's centre. - Map reveal: the hero's reveal stamp scaled 10x (mods/mapreveal.cpp, docs/map-reveal.md); creatures on the zoomed map shown within 10x the sight range. - Load / save progress screen drawn on the rock background across the full width, the picture blended over it (mods/loadingscreen.cpp); the classic 1024x768 frame is gone. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3f03228c29
commit
b452558122
18 files changed
+435
-40
No files matched your search
+1
-11
@@ -56,14 +56,12 @@ struct Viewport {
|
||||
HWND window = nullptr;
|
||||
bool runInBackground = false;
|
||||
Layout full; // the whole back buffer
|
||||
Layout classic; // its top-left 1024x768 (menus, loading screen; widescreen mod)
|
||||
};
|
||||
|
||||
Viewport g_viewport;
|
||||
std::atomic<bool> g_classicShown = false; // which layout the last presented frame used
|
||||
|
||||
const Layout& CurrentLayout() {
|
||||
return g_classicShown ? g_viewport.classic : g_viewport.full;
|
||||
return g_viewport.full;
|
||||
}
|
||||
|
||||
bool Enabled() {
|
||||
@@ -116,10 +114,8 @@ void ApplyBorderless(game::DxDriver7* driver) {
|
||||
viewport.window = window;
|
||||
viewport.runInBackground = re::ConfigInt("borderless", "RunInBackground", 1) != 0;
|
||||
viewport.full = makeLayout(driver->mode.width, driver->mode.height);
|
||||
viewport.classic = makeLayout(std::min<int>(driver->mode.width, 1024), std::min<int>(driver->mode.height, 768));
|
||||
viewport.active = true;
|
||||
g_viewport = viewport;
|
||||
g_classicShown = false;
|
||||
|
||||
// Paint the bars: the window class background brush is black.
|
||||
InvalidateRect(window, nullptr, TRUE);
|
||||
@@ -188,12 +184,6 @@ int __fastcall DxDriver7_Flip(DxDriver7* self, void* edx) {
|
||||
// The original computes the blit rectangles once, for a centred window of
|
||||
// exactly the game's size; override them on every present.
|
||||
if (g_viewport.active && self->window == g_viewport.window) {
|
||||
// Menus and the loading screen only fill the top-left 1024x768 of a
|
||||
// widened back buffer; show just that part, scaled like before.
|
||||
const bool classic = mods::widescreen::IsClassicFrame();
|
||||
if (g_classicShown.exchange(classic) != classic) {
|
||||
InvalidateRect(self->window, nullptr, TRUE); // repaint the bars for the new layout
|
||||
}
|
||||
const Layout& layout = CurrentLayout();
|
||||
self->blitSource = {0, 0, layout.gameWidth, layout.gameHeight};
|
||||
self->blitDest = layout.screen;
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ int OffsetFor(Anchor anchor) {
|
||||
// The main menu's frame shifts the windows in it; over a 16:9 screen
|
||||
// (no frame) dialogs are centred like in a game (user, 2026-10-21).
|
||||
const bool inMenuFrame = mods::widescreen::MenuIsShowing() && g_menuFrameDx != 0;
|
||||
if (!enabled || width <= kLayoutWidth || mods::widescreen::IsClassicFrame() || inMenuFrame ||
|
||||
if (!enabled || width <= kLayoutWidth || mods::widescreen::IsLoadingScreen() || inMenuFrame ||
|
||||
anchor == Anchor::Screen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
// The load / save progress screen on the menu screens' background (request of
|
||||
// 2026-10-25).
|
||||
//
|
||||
// The game's progress screen (DxDriver7::LoadingScreen 0x6469d0, thiscall
|
||||
// (progress, text)) draws with GDI into the back buffer's DC (surface +0xbc,
|
||||
// GetDC vf 0x44 / ReleaseDC vf 0x68): the 1024x768 picture (HBITMAP +0x200)
|
||||
// at 0, 0, the progress bar (+0x204 / +0x208) and the text at y 700, then
|
||||
// Flip. It only redraws when the bar moves.
|
||||
//
|
||||
// The mod: first the menu background across the whole render width in a
|
||||
// Direct3D scene (screen::DrawBackground), then the game's drawing with the
|
||||
// DC's origin moved to the centred 1024 frame. The game asks for a memory DC
|
||||
// compatible with the back buffer's DC right after getting it
|
||||
// (CreateCompatibleDC(hdc)), which is where the origin is set.
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
#include "game/addresses.h"
|
||||
#include "game/render/dx_driver7.h"
|
||||
#include "mods/loadingscreen.h"
|
||||
#include "mods/screen.h"
|
||||
#include "mods/widescreen.h"
|
||||
#include "re/hook.h"
|
||||
|
||||
namespace mods::loadingscreen {
|
||||
namespace {
|
||||
|
||||
constexpr int kLayoutWidth = 1024;
|
||||
|
||||
using BeginSceneFn = char(__fastcall*)(game::DxDriver7* self, void* edx);
|
||||
using EndSceneFn = void(__fastcall*)(game::DxDriver7* self, void* edx);
|
||||
|
||||
} // namespace
|
||||
|
||||
// While the progress screen draws on this thread: its frame's x.
|
||||
thread_local int g_offsetX = 0;
|
||||
|
||||
void Begin(game::DxDriver7* driver) {
|
||||
g_offsetX = 0;
|
||||
const int width = widescreen::RenderWidth();
|
||||
if (width <= kLayoutWidth || !driver || !driver->device || !driver->backBuffer) {
|
||||
return;
|
||||
}
|
||||
if (reinterpret_cast<BeginSceneFn>(static_cast<uintptr_t>(game::addr::DxDriver7_BeginScene))(driver, nullptr)) {
|
||||
screen::DrawBackground(driver->device);
|
||||
reinterpret_cast<EndSceneFn>(static_cast<uintptr_t>(game::addr::DxDriver7_EndScene))(driver, nullptr);
|
||||
}
|
||||
g_offsetX = (width - kLayoutWidth) / 2;
|
||||
}
|
||||
|
||||
void End() {
|
||||
g_offsetX = 0;
|
||||
}
|
||||
|
||||
} // namespace mods::loadingscreen
|
||||
|
||||
namespace mods::loadingscreen {
|
||||
namespace {
|
||||
|
||||
constexpr int kLayoutHeight = 768;
|
||||
|
||||
// The game's pictures are artwork on black (PAK\loading0.bmp, loadgame.bmp,
|
||||
// ...). Blended instead of copied: each pixel's alpha is its brightest
|
||||
// channel, colours stay as they are (premultiplied), so black shows the
|
||||
// background and the glow fades into it.
|
||||
bool BlendPicture(HDC target, HDC source) {
|
||||
const int offsetX = g_offsetX;
|
||||
g_offsetX = 0; // our own GDI calls below pass the hooks unchanged
|
||||
BITMAPINFO info = {};
|
||||
info.bmiHeader.biSize = sizeof(info.bmiHeader);
|
||||
info.bmiHeader.biWidth = kLayoutWidth;
|
||||
info.bmiHeader.biHeight = -kLayoutHeight;
|
||||
info.bmiHeader.biPlanes = 1;
|
||||
info.bmiHeader.biBitCount = 32;
|
||||
info.bmiHeader.biCompression = BI_RGB;
|
||||
void* bits = nullptr;
|
||||
HBITMAP picture = CreateDIBSection(source, &info, DIB_RGB_COLORS, &bits, nullptr, 0);
|
||||
if (!picture) {
|
||||
g_offsetX = offsetX;
|
||||
return false;
|
||||
}
|
||||
HDC copy = CreateCompatibleDC(source);
|
||||
HGDIOBJ previous = SelectObject(copy, picture);
|
||||
BitBlt(copy, 0, 0, kLayoutWidth, kLayoutHeight, source, 0, 0, SRCCOPY);
|
||||
GdiFlush();
|
||||
auto* pixel = static_cast<uint8_t*>(bits);
|
||||
for (int i = 0; i < kLayoutWidth * kLayoutHeight; ++i, pixel += 4) {
|
||||
pixel[3] = std::max({pixel[0], pixel[1], pixel[2]});
|
||||
}
|
||||
const BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
|
||||
const bool done =
|
||||
GdiAlphaBlend(target, 0, 0, kLayoutWidth, kLayoutHeight, copy, 0, 0, kLayoutWidth, kLayoutHeight, blend) != 0;
|
||||
SelectObject(copy, previous);
|
||||
DeleteDC(copy);
|
||||
DeleteObject(picture);
|
||||
g_offsetX = offsetX;
|
||||
return done;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mods::loadingscreen
|
||||
|
||||
// The picture: BitBlt(hdc, 0, 0, 1024, 768, picture DC, 0, 0, SRCCOPY), or
|
||||
// BLACKNESS without a picture (then nothing is drawn over the background).
|
||||
BOOL WINAPI BitBltForLoadingScreen(HDC target, int x, int y, int width, int height, HDC source, int sx, int sy,
|
||||
DWORD rop);
|
||||
RE_HOOK_API(BitBlt, BitBltForLoadingScreen);
|
||||
|
||||
BOOL WINAPI BitBltForLoadingScreen(HDC target, int x, int y, int width, int height, HDC source, int sx, int sy,
|
||||
DWORD rop) {
|
||||
using namespace mods::loadingscreen;
|
||||
if (g_offsetX != 0 && x == 0 && y == 0 && width == 1024 && height == 768) {
|
||||
if (rop == BLACKNESS) {
|
||||
return TRUE;
|
||||
}
|
||||
if (rop == SRCCOPY && source && BlendPicture(target, source)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return BitBltForLoadingScreen_original(target, x, y, width, height, source, sx, sy, rop);
|
||||
}
|
||||
|
||||
HDC WINAPI CreateCompatibleDCForLoadingScreen(HDC hdc);
|
||||
RE_HOOK_API(CreateCompatibleDC, CreateCompatibleDCForLoadingScreen);
|
||||
|
||||
HDC WINAPI CreateCompatibleDCForLoadingScreen(HDC hdc) {
|
||||
if (hdc && mods::loadingscreen::g_offsetX != 0) {
|
||||
SetViewportOrgEx(hdc, mods::loadingscreen::g_offsetX, 0, nullptr);
|
||||
}
|
||||
return CreateCompatibleDCForLoadingScreen_original(hdc);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
// The load / save progress screen on the menu background (loadingscreen.cpp).
|
||||
|
||||
#include "game/render/dx_driver7.h"
|
||||
|
||||
namespace mods::loadingscreen {
|
||||
|
||||
// Around DxDriver7::LoadingScreen (widescreen.cpp's hook): Begin draws the
|
||||
// background and centres the game's GDI drawing, End stops centring.
|
||||
void Begin(game::DxDriver7* driver);
|
||||
void End();
|
||||
|
||||
} // namespace mods::loadingscreen
|
||||
@@ -0,0 +1,108 @@
|
||||
// A larger vision range for the explored map (request of 2026-10-25: "increase
|
||||
// the base vision range for the map's fog of war reveal by x10 or so").
|
||||
//
|
||||
// Measured (game/engine/explored.h): RevealAroundHero stamps a fixed pattern
|
||||
// around the hero into the sectors' explored masks whenever the hero's tile
|
||||
// changes, a 14-tile circle by day, smaller ones at night.
|
||||
//
|
||||
// The mod: after the original revealed at a new tile, the same pattern scaled
|
||||
// up kScale times is revealed too (by day a 140-tile circle), one cell (2x2
|
||||
// tiles) at a time through the game's own World_SetExplored. Only loaded
|
||||
// sectors can be marked; they reach further than the scaled stamp.
|
||||
//
|
||||
// Creatures on the zoomed map views (large map, TAB map; 0x6cc950): one off
|
||||
// explored ground, or a hostile one, is only drawn within a sight range of the
|
||||
// hero (|dx| and |dy| in world units, about 53.7 per tile): 650 (about 12
|
||||
// tiles), 300 where 0x63d280 holds for the hero's area, 350 when the weather
|
||||
// object's 0x41ba40 is false; plus bonuses from a skill (0x355) and the hero's
|
||||
// equipment values. The three base values are scaled by kScale as well.
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "game/engine/explored.h"
|
||||
#include "re/hook.h"
|
||||
#include "re/log.h"
|
||||
#include "re/patch.h"
|
||||
|
||||
namespace mods::mapreveal {
|
||||
namespace {
|
||||
|
||||
constexpr int kScale = 10;
|
||||
constexpr int kCellTiles = 2; // one mask bit per 2x2 tiles
|
||||
|
||||
// The stamp cell a tile offset falls into: cell c covers offsets
|
||||
// c * kScale - kScale / 2 .. c * kScale + kScale / 2 - 1.
|
||||
int StampOffset(int tiles) {
|
||||
const int shifted = tiles + kScale / 2;
|
||||
return (shifted >= 0 ? shifted : shifted - kScale + 1) / kScale;
|
||||
}
|
||||
|
||||
// mov dword ptr [esp+14h], <float>: the map's creature sight range.
|
||||
struct SightRange {
|
||||
uint32_t instruction;
|
||||
float value;
|
||||
};
|
||||
constexpr SightRange kCreatureSightRanges[] = {
|
||||
{0x006ce677, 650.0f},
|
||||
{0x006ce6b5, 300.0f},
|
||||
{0x006ce6cf, 350.0f},
|
||||
};
|
||||
|
||||
uint32_t FloatBits(float value) {
|
||||
uint32_t bits;
|
||||
std::memcpy(&bits, &value, sizeof bits);
|
||||
return bits;
|
||||
}
|
||||
|
||||
void Startup() {
|
||||
int failed = 0;
|
||||
for (const SightRange& range : kCreatureSightRanges) {
|
||||
failed += !re::PatchInstructionDword(range.instruction, FloatBits(range.value), FloatBits(range.value * kScale));
|
||||
}
|
||||
if (failed) {
|
||||
re::Log("mapreveal: %d creature sight range patches FAILED", failed);
|
||||
}
|
||||
}
|
||||
|
||||
void RevealScaled(int tileX, int tileY, int sight) {
|
||||
const game::RevealStamp stamp = game::RevealStampFor(sight);
|
||||
const int centre = stamp.size / 2;
|
||||
const int radius = centre * kScale + kScale / 2;
|
||||
// Cells start at even tiles (sector origins are multiples of 64).
|
||||
const int left = (tileX - radius) & ~(kCellTiles - 1);
|
||||
const int top = (tileY - radius) & ~(kCellTiles - 1);
|
||||
for (int y = top; y <= tileY + radius; y += kCellTiles) {
|
||||
const int row = centre + StampOffset(y + kCellTiles / 2 - tileY);
|
||||
if (row < 0 || row >= stamp.size) {
|
||||
continue;
|
||||
}
|
||||
for (int x = left; x <= tileX + radius; x += kCellTiles) {
|
||||
const int column = centre + StampOffset(x + kCellTiles / 2 - tileX);
|
||||
if (column >= 0 && column < stamp.size && stamp.cells[row * stamp.size + column]) {
|
||||
game::SetExplored(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RE_STARTUP(MapRevealStartup, Startup);
|
||||
|
||||
} // namespace mods::mapreveal
|
||||
|
||||
void __fastcall RevealAroundHero(void* creature, void* edx, int tileX, int tileY, int sight);
|
||||
RE_HOOK(game::kRevealAroundHero, RevealAroundHero);
|
||||
void __fastcall RevealAroundHero(void* creature, void* edx, int tileX, int tileY, int sight) {
|
||||
const int lastX = game::LastRevealTileX();
|
||||
const int lastY = game::LastRevealTileY();
|
||||
RevealAroundHero_original(creature, edx, tileX, tileY, sight);
|
||||
// The original revealed (local hero, new tile) iff it moved its last tile
|
||||
// here; it ignores tile 0, 0.
|
||||
const bool revealed = (tileX != 0 || tileY != 0) && (lastX != tileX || lastY != tileY) &&
|
||||
game::LastRevealTileX() == tileX && game::LastRevealTileY() == tileY;
|
||||
if (revealed && sight >= 0 && sight <= 3) {
|
||||
mods::mapreveal::RevealScaled(tileX, tileY, sight);
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,20 @@ constexpr uint32_t kZoomView = 0x006cc950;
|
||||
constexpr uint32_t kZoomCentreUses[] = {0x006ccb7f, 0x006cf782};
|
||||
constexpr uint32_t kZoomMarkerX = 0x006ce156;
|
||||
constexpr float kZoomMarkerFromCentre = 256.0f;
|
||||
// The heroes' markers in the zoomed view: a 6 px quad at x = d + 509 / 515
|
||||
// (fadd [509.0] 0x6cf1bd, fadd [515.0] 0x6cf1dd), i.e. the centre 512 +- 3
|
||||
// (2026-10-25: they stayed at the 1024 layout's centre).
|
||||
constexpr uint32_t kZoomHeroMarkerLeft = 0x006cf1bd;
|
||||
constexpr uint32_t kZoomHeroMarkerRight = 0x006cf1dd;
|
||||
constexpr uintptr_t kZoomHeroMarkerLeftF = 0x00894780; // 509.0
|
||||
constexpr uintptr_t kZoomHeroMarkerRightF = 0x00894778; // 515.0
|
||||
constexpr float kZoomHeroMarkerHalf = 3.0f;
|
||||
// The other symbols in the zoomed view take the centre as an integer
|
||||
// immediate 200h (mov reg / add reg, 200h): a quad x = 200h - w / 200h + w
|
||||
// (0x6ced82, 0x6cedb0), icons (0x6cf47a, 0x6cf4ac) and labels centred on
|
||||
// their x (0x6cf5e4) (2026-10-25: they stayed at the 1024 layout's centre).
|
||||
// Rewritten whenever the centre changes.
|
||||
constexpr uint32_t kZoomCentreImmediates[] = {0x006ced82, 0x006cedb0, 0x006cf47a, 0x006cf4ac, 0x006cf5e4};
|
||||
// The dark band around the view (10 vertices, mov [esp+..], imm32): its right
|
||||
// outer edge x 1024 and inner edge x 954.
|
||||
constexpr uint32_t kBandRightOuter[] = {0x006ca774, 0x006ca808};
|
||||
@@ -60,6 +74,9 @@ constexpr int kBandWidth = 70;
|
||||
float g_halfWidth = 512.0f;
|
||||
float g_zoomCentre = 512.0f;
|
||||
float g_zoomMarkerX = 768.0f;
|
||||
float g_zoomHeroMarkerLeft = 509.0f;
|
||||
float g_zoomHeroMarkerRight = 515.0f;
|
||||
uint32_t g_zoomCentreImmediate = kLayoutWidth / 2;
|
||||
|
||||
uint32_t FloatBits(float value) {
|
||||
uint32_t bits;
|
||||
@@ -183,6 +200,8 @@ void PatchLayout(int width) {
|
||||
failed += !re::RedirectOperand(instruction, game::addr::kScreenHalfWidthF, &g_zoomCentre);
|
||||
}
|
||||
failed += !re::RedirectOperand(kZoomMarkerX, game::addr::kScreenHeightF, &g_zoomMarkerX);
|
||||
failed += !re::RedirectOperand(kZoomHeroMarkerLeft, kZoomHeroMarkerLeftF, &g_zoomHeroMarkerLeft);
|
||||
failed += !re::RedirectOperand(kZoomHeroMarkerRight, kZoomHeroMarkerRightF, &g_zoomHeroMarkerRight);
|
||||
failed += re::PatchAll(kBandRightOuter, FloatBits(1024.0f), FloatBits(static_cast<float>(width)));
|
||||
failed += re::PatchAll(kBandRightInner, FloatBits(1024.0f - kBandWidth),
|
||||
FloatBits(static_cast<float>(width - kBandWidth)));
|
||||
@@ -196,6 +215,13 @@ void __cdecl SetZoomCentre() {
|
||||
const int width = std::max(mods::widescreen::RenderWidth(), kLayoutWidth);
|
||||
g_zoomCentre = width / 2.0f - static_cast<float>(mods::hud::DrawOffsetX());
|
||||
g_zoomMarkerX = g_zoomCentre + kZoomMarkerFromCentre;
|
||||
g_zoomHeroMarkerLeft = g_zoomCentre - kZoomHeroMarkerHalf;
|
||||
g_zoomHeroMarkerRight = g_zoomCentre + kZoomHeroMarkerHalf;
|
||||
const auto immediate = static_cast<uint32_t>(width / 2 - mods::hud::DrawOffsetX());
|
||||
if (immediate != g_zoomCentreImmediate &&
|
||||
re::PatchAll(kZoomCentreImmediates, g_zoomCentreImmediate, immediate) == 0) {
|
||||
g_zoomCentreImmediate = immediate;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mods::megamap
|
||||
|
||||
+1
-1
@@ -259,7 +259,7 @@ uint32_t __fastcall UiManager_Render(void* self, void* edx, uint32_t argument);
|
||||
RE_HOOK(0x007587b0, UiManager_Render);
|
||||
uint32_t __fastcall UiManager_Render(void* self, void* edx, uint32_t argument) {
|
||||
using namespace mods;
|
||||
if (!widescreen::IsMenuThread() || widescreen::IsClassicFrame() || widescreen::RenderWidth() == 0) {
|
||||
if (!widescreen::IsMenuThread() || widescreen::IsLoadingScreen() || widescreen::RenderWidth() == 0) {
|
||||
return UiManager_Render_original(self, edx, argument);
|
||||
}
|
||||
auto* driver = *reinterpret_cast<DxDriver7**>(addr::g_dxDriver);
|
||||
|
||||
+11
-11
@@ -24,6 +24,7 @@
|
||||
#include "mods/widescreen.h"
|
||||
|
||||
#include "mods/hud.h"
|
||||
#include "mods/loadingscreen.h"
|
||||
#include "mods/megamap.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -63,9 +64,9 @@ bool g_pushesPatched = false;
|
||||
int g_renderWidth = 0;
|
||||
int g_renderHeight = 0;
|
||||
|
||||
// The loading screen is laid out for 1024x768 ("classic"); the main menu
|
||||
// thread renders the full width too (its windows drawn centred by
|
||||
// mods/screen.cpp), the in-game view is widened.
|
||||
// Every frame uses the full render width: the main menu thread (its windows
|
||||
// drawn centred by mods/screen.cpp), the loading screen (centred on the menu
|
||||
// background, mods/loadingscreen.cpp), the widened in-game view.
|
||||
std::atomic<DWORD> g_menuThread = 0;
|
||||
// Whether the most recent scene (on any thread) was the main menu's or the
|
||||
// loading screen's. UI input is processed outside the menu thread.
|
||||
@@ -343,7 +344,7 @@ bool MenuIsShowing() {
|
||||
return g_lastSceneMenu;
|
||||
}
|
||||
|
||||
bool IsClassicFrame() {
|
||||
bool IsLoadingScreen() {
|
||||
return g_renderWidth != 0 && g_loadingScreenDepth > 0;
|
||||
}
|
||||
|
||||
@@ -369,22 +370,21 @@ void __fastcall UiManager_ThreadLoop(void* self, void* edx) {
|
||||
|
||||
void __fastcall DxDriver7_LoadingScreen(DxDriver7* self, void* edx, uint32_t progress, const char* text) {
|
||||
++g_loadingScreenDepth;
|
||||
mods::loadingscreen::Begin(self);
|
||||
DxDriver7_LoadingScreen_original(self, edx, progress, text);
|
||||
mods::loadingscreen::End();
|
||||
--g_loadingScreenDepth;
|
||||
}
|
||||
|
||||
// The loading screen draws for a 1024x768 screen: give it a 1024x768
|
||||
// viewport (the borderless mod then presents just that area, scaled), and
|
||||
// everything else the full render size.
|
||||
// Every frame gets the full render size as its viewport.
|
||||
int __fastcall DxDriver7_BeginScene(DxDriver7* self, void* edx) {
|
||||
mods::hud::OnDevice(self->device);
|
||||
if (self->sceneDepth == 0) {
|
||||
g_lastSceneMenu = IsClassicFrame() || IsMenuThread();
|
||||
g_lastSceneMenu = IsLoadingScreen() || IsMenuThread();
|
||||
}
|
||||
if (g_renderWidth != 0 && self->sceneDepth == 0 && self->device) {
|
||||
const bool classic = IsClassicFrame();
|
||||
Viewport7 viewport = {0, 0, classic ? 1024u : static_cast<DWORD>(g_renderWidth),
|
||||
classic ? 768u : static_cast<DWORD>(g_renderHeight), 0.0f, 1.0f};
|
||||
Viewport7 viewport = {0, 0, static_cast<DWORD>(g_renderWidth), static_cast<DWORD>(g_renderHeight), 0.0f,
|
||||
1.0f};
|
||||
using SetViewportFn = HRESULT(__stdcall*)(void* device, Viewport7* viewport);
|
||||
auto setViewport = reinterpret_cast<SetViewportFn>((*static_cast<void***>(self->device))[13]);
|
||||
setViewport(self->device, &viewport);
|
||||
|
||||
@@ -11,9 +11,8 @@ namespace mods::widescreen {
|
||||
// the world view to match. No-op when disabled.
|
||||
void ApplyRenderSize(game::DxDisplayMode& mode, HWND window);
|
||||
|
||||
// True while the current thread renders a frame laid out for 1024x768 (the
|
||||
// loading screen); the borderless mod then presents only that area.
|
||||
bool IsClassicFrame();
|
||||
// True while the current thread draws the load / save progress screen.
|
||||
bool IsLoadingScreen();
|
||||
|
||||
// Applied render width (0 while the original 1024x768 is in use).
|
||||
int RenderWidth();
|
||||
|
||||
Reference in new issue
Block a user