Shared chest, menu after a game, dialogs over 16:9 screens
- Shared chest: emptied and not saved from the moment a save game is read until it reloads; a hero saved inside a load wrote the new world's objects (stale ids) as the chest's items. - Main menu after a game: shown 16:9 screens are tracked on the menu thread only and expire after 2 frames; a destroyed in-game window kept the menu unframed (old pictures, frame at the left). - Dialogs over a 16:9 screen in the main menu are centred, their dimming covers the screen. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c4aacbcdd2
commit
0ef3cf4bbd
6 files changed
+68
-25
No files matched your search
@@ -75,3 +75,16 @@ window's own pictures removed.
|
||||
|
||||
- Spielen (the load window, cUI_Savegame): docs/characters.md, "Spielen
|
||||
screen".
|
||||
|
||||
## Fixes (2026-10-21)
|
||||
|
||||
- The main menu came back unframed (old pictures, frame at the left) after a
|
||||
game: the set of shown 16:9 screens kept a window of the in-game UI that
|
||||
was destroyed while shown. Now only screens drawn on the menu thread count,
|
||||
and an entry expires after 2 menu frames without being drawn
|
||||
(`screen::BeginMenuFrame`).
|
||||
- Dialogs over a 16:9 screen in the main menu (e.g. "Spielstand laden?") sat
|
||||
at the left with the dimming 1024 wide: `hud::OffsetFor` gave every window
|
||||
offset 0 while the menu showed. Now only while the menu frame is drawn
|
||||
centred (it shifts the windows itself); otherwise dialogs are centred and
|
||||
their full-layout dimming stretched, as in a game.
|
||||
@@ -70,3 +70,18 @@ was empty after loading another character (in-game saves don't call saveHero).
|
||||
- loadHero's extra fix-up for some item classes (`0x5beaa0` / `0x5d3a20`,
|
||||
item classes 5, 6, 8, 0xd, 0x11..0x19, 0x1b, 0x1d) is not repeated.
|
||||
- Multiplayer: not supported.
|
||||
|
||||
## Items replaced by a world's (fixed 2026-10-21)
|
||||
|
||||
Reported: the shared chest's items were gone, other items (potions, gear) in
|
||||
their cells. The grid holds object ids. A save game loaded into the running
|
||||
engine (Esc "Laden", cEngine::Load from the render loop 0x60e68d) replaces
|
||||
all objects, so until the chest reloads on the next tick its ids name the new
|
||||
world's objects. A hero saved inside that load (characters.cpp Engine_Load: a
|
||||
migrated character's first load makes its world hero the shared hero with
|
||||
SaveHero) wrote the shared chest, i.e. those objects, into
|
||||
shared_chest.json; the next tick loaded them as the chest. Now the chest is
|
||||
emptied and marked unloaded as soon as a save game is read or a game starts
|
||||
(RequestLoad), so no save writes it before it is loaded again. The user's
|
||||
chest was restored from the 2026-10-19 backup (the broken file kept as
|
||||
backup/shared_chest.broken-2026-10-21.json).
|
||||
+9
-6
@@ -53,14 +53,21 @@ thread_local bool g_eventShifted = false;
|
||||
// Canvas: a 16:9 screen (kCanvasWidth), centred.
|
||||
enum class Anchor { Center, Right, Screen, Canvas };
|
||||
|
||||
// Offset of the main menu's 1024 frame while it is drawn centred (set each
|
||||
// menu frame by RenderMenuFrame; 0 while a 16:9 screen draws itself).
|
||||
std::atomic<int> g_menuFrameDx = 0;
|
||||
|
||||
int OffsetFor(Anchor anchor) {
|
||||
static const bool enabled = re::ConfigInt("hud", "Enabled", 1) != 0;
|
||||
const int width = mods::widescreen::RenderWidth();
|
||||
if (anchor == Anchor::Canvas) {
|
||||
return width > kCanvasWidth ? (width - kCanvasWidth) / 2 : 0;
|
||||
}
|
||||
if (!enabled || width <= kLayoutWidth || mods::widescreen::IsClassicFrame() ||
|
||||
mods::widescreen::MenuIsShowing() || anchor == Anchor::Screen) {
|
||||
// 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 ||
|
||||
anchor == Anchor::Screen) {
|
||||
return 0;
|
||||
}
|
||||
return anchor == Anchor::Center ? (width - kLayoutWidth) / 2 : width - kLayoutWidth;
|
||||
@@ -868,10 +875,6 @@ int HandleCentred(void* event, int (*body)(void* context), void* context) {
|
||||
return body(context);
|
||||
}
|
||||
|
||||
// Offset of the main menu's 1024 frame while it is drawn centred (set each
|
||||
// menu frame by RenderMenuFrame; 0 while a 16:9 screen draws itself).
|
||||
std::atomic<int> g_menuFrameDx = 0;
|
||||
|
||||
void SetHiddenTextures(void* const* surfaces, int count) {
|
||||
for (int i = 0; i < kHiddenTextureMax; ++i) {
|
||||
g_hiddenTextures[i].store(i < count ? surfaces[i] : nullptr, std::memory_order_relaxed);
|
||||
|
||||
+19
-16
@@ -4,8 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
@@ -46,15 +45,22 @@ constexpr int kHeadingHeight = 30;
|
||||
// frames).
|
||||
std::atomic<unsigned> g_frame{1};
|
||||
|
||||
// The windows whose screen is shown: the main menu's frame is not drawn
|
||||
// centred while any is (the screen draws itself across the width).
|
||||
std::mutex g_shownLock;
|
||||
std::set<const void*> g_shown;
|
||||
// The windows whose screen is shown on the menu thread, with the menu frame
|
||||
// they were last drawn in: the main menu's frame is not drawn centred while
|
||||
// any is (the screen draws itself across the width). Entries not drawn for
|
||||
// kShownFrames frames are dropped: the UI is rebuilt when a game starts or
|
||||
// ends, and a window destroyed while shown kept the menu from being framed
|
||||
// (user, 2026-10-21: "the old main menu is rendering again"). Menu thread only.
|
||||
constexpr unsigned kShownFrames = 2;
|
||||
unsigned g_menuFrame = 0;
|
||||
std::map<const void*, unsigned> g_shown;
|
||||
|
||||
void NoteShown(const void* window, bool shown) {
|
||||
std::lock_guard guard(g_shownLock);
|
||||
if (!widescreen::IsMenuThread()) {
|
||||
return;
|
||||
}
|
||||
if (shown) {
|
||||
g_shown.insert(window);
|
||||
g_shown[window] = g_menuFrame;
|
||||
} else {
|
||||
g_shown.erase(window);
|
||||
}
|
||||
@@ -91,11 +97,6 @@ void HidePictures() {
|
||||
hud::SetHiddenTextures(surfaces.data(), static_cast<int>(surfaces.size()));
|
||||
}
|
||||
|
||||
bool ShownAny() {
|
||||
std::lock_guard guard(g_shownLock);
|
||||
return !g_shown.empty();
|
||||
}
|
||||
|
||||
// The game's logo top left (user, 2026-10-21: "the bright blueish one"),
|
||||
// kLogoMargin from the edges: FX_SACREDLOGO.TGA, the emblem (horns around a
|
||||
// ring of runes) glowing blue on black, no alpha, added onto the background as
|
||||
@@ -134,8 +135,10 @@ void DrawRockWall(void* device, float width, float height) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool AnyShown() {
|
||||
return ShownAny();
|
||||
bool BeginMenuFrame() {
|
||||
++g_menuFrame;
|
||||
std::erase_if(g_shown, [](const auto& entry) { return g_menuFrame - entry.second > kShownFrames; });
|
||||
return !g_shown.empty();
|
||||
}
|
||||
|
||||
void DrawBackground(void* device) {
|
||||
@@ -261,7 +264,7 @@ uint32_t __fastcall UiManager_Render(void* self, void* edx, uint32_t argument) {
|
||||
}
|
||||
auto* driver = *reinterpret_cast<DxDriver7**>(addr::g_dxDriver);
|
||||
void* device = driver ? driver->device : nullptr;
|
||||
const bool framed = !screen::AnyShown();
|
||||
const bool framed = !screen::BeginMenuFrame();
|
||||
if (framed && device) {
|
||||
screen::DrawBackground(device);
|
||||
}
|
||||
|
||||
+3
-2
@@ -50,8 +50,9 @@ constexpr uint32_t kRowSelectedColour = 0xc0604818;
|
||||
// The background of the menu screens across the whole render target (also
|
||||
// beside the main menu's centred frame and full-screen windows in a game).
|
||||
void DrawBackground(void* device);
|
||||
// Whether a screen is shown (the main menu then does not draw centred).
|
||||
bool AnyShown();
|
||||
// Called once per main menu frame (menu thread): whether a screen is shown
|
||||
// (the main menu then does not draw centred).
|
||||
bool BeginMenuFrame();
|
||||
|
||||
// Draws the screen of `window`: the background across the whole render
|
||||
// target, then body(context) in canvas coordinates.
|
||||
|
||||
@@ -166,9 +166,17 @@ void Load() {
|
||||
}
|
||||
|
||||
// A session started or a save game was loaded into it (its objects replace
|
||||
// the old ones): (re)load on the next tick with a hero.
|
||||
// the old ones): (re)load on the next tick with a hero. Until then the grid's
|
||||
// ids are stale (they name the new world's objects) and nothing is saved: a
|
||||
// hero saved inside the load (characters.cpp, Engine_Load, a migrated
|
||||
// character's first load) wrote them into the file, the chest's items
|
||||
// replaced by the world's (user, 2026-10-21).
|
||||
void RequestLoad() {
|
||||
g_loaded = false;
|
||||
g_loadPending = true;
|
||||
if (g_constructed) {
|
||||
ClearGrid();
|
||||
}
|
||||
}
|
||||
|
||||
// The chest grid's place and size, as the cUI_Chest constructor sets them for
|
||||
|
||||
Reference in new issue
Block a user