- 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>
118 lines
4.4 KiB
C++
118 lines
4.4 KiB
C++
#pragma once
|
|
// 16:9 menu screens (request of 2026-10-20, docs/screens.md): a screen of our
|
|
// own replaces a full-screen window of the game's (its render and mouse
|
|
// handling), laid out on a 1365x768 canvas (1920x1080 in UI units), centred in
|
|
// the render width; wider screens only get more background at the sides. The
|
|
// look is the game's: its frames and buttons on a dark background of ours.
|
|
//
|
|
// A screen hooks its window's render and event methods and calls Render /
|
|
// Handle with a body that draws / handles in canvas coordinates with the
|
|
// helpers below.
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
#include "game/ui/text.h"
|
|
#include "mods/hud.h"
|
|
|
|
namespace mods::screen {
|
|
|
|
using game::UiRect;
|
|
|
|
constexpr int kWidth = hud::kCanvasWidth;
|
|
constexpr int kHeight = hud::kCanvasHeight;
|
|
|
|
// Spacing: the game's frames reach about 0x20 beyond their rects, buttons
|
|
// about 0x21 (measured on the options window).
|
|
constexpr int kFrameOrnament = 0x20;
|
|
// The frames are made of pieces of 64: other sizes leave gaps (the game's
|
|
// frames are 384x128, 384x64; screenshot 2026-10-20).
|
|
constexpr int kFrameStep = 64;
|
|
constexpr int kButtonOrnament = 0x21;
|
|
constexpr int kMargin = kFrameOrnament + 16; // canvas edge to a panel's rect
|
|
constexpr int kPanelGap = 96; // between two panels' rects, as the options window's columns
|
|
constexpr int kPanelPadding = 16; // panel rect to its content
|
|
|
|
// The logo in the background's top left corner (render target coordinates;
|
|
// screens may overlap it).
|
|
constexpr int kLogoMargin = 48;
|
|
constexpr int kLogoHeight = 124;
|
|
constexpr int kLogoWidth = kLogoHeight * 74 / 100; // the emblem's aspect (screen.cpp)
|
|
|
|
constexpr uint16_t kFont = 1;
|
|
constexpr uint32_t kTextColour = 0xffe6d6aa; // as the options' texts
|
|
constexpr uint32_t kHeadingColour = 0xfff0c060; // as the options' headings
|
|
constexpr uint32_t kDimColour = 0xff8c8474;
|
|
constexpr uint32_t kRowColour = 0x40302418;
|
|
constexpr uint32_t kRowHoverColour = 0x80403020;
|
|
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);
|
|
// 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.
|
|
int Render(void* window, void* device, int (*body)(void* context), void* context);
|
|
// Runs body(context) with the event's mouse position in canvas coordinates.
|
|
int Handle(void* event, int (*body)(void* context), void* context);
|
|
|
|
void Fill(void* device, const UiRect& rect, uint32_t colour);
|
|
// One line of text in `rect`, centred vertically (game::kTextCenter /
|
|
// kTextRight for horizontal alignment).
|
|
void Text(void* device, const UiRect& rect, const std::wstring& text, uint32_t colour, uint32_t flags = 0);
|
|
|
|
UiRect Inset(const UiRect& rect, int by);
|
|
|
|
// A panel: a dark backdrop with the game's frame around `rect` (made anew when
|
|
// the rect changes) and an optional heading at its top.
|
|
class Panel {
|
|
public:
|
|
explicit Panel(const char* name) : name_(name) {}
|
|
// The content area below the heading.
|
|
UiRect Draw(void* device, const UiRect& rect, const std::wstring& heading = {});
|
|
|
|
private:
|
|
const char* name_;
|
|
UiRect rect_ = {};
|
|
uint8_t* frame_ = nullptr;
|
|
};
|
|
|
|
// A button of the game's (look and sounds of `model`, a button of the
|
|
// window), made on first use.
|
|
class Button {
|
|
public:
|
|
Button(const char* name, std::wstring caption) : name_(name), caption_(std::move(caption)) {}
|
|
void Draw(void* device, const uint8_t* model, const UiRect& rect);
|
|
// Passes the event to the button (pressed look, sound); true for a left
|
|
// click on it. Only for buttons drawn this frame.
|
|
bool Clicked(void* event);
|
|
void SetCaption(const std::wstring& caption);
|
|
|
|
private:
|
|
const char* name_;
|
|
std::wstring caption_;
|
|
uint32_t captionId_ = 0;
|
|
uint8_t* control_ = nullptr;
|
|
UiRect rect_ = {};
|
|
unsigned drawnFrame_ = 0;
|
|
};
|
|
|
|
// Height of the model's buttons (as the window made them).
|
|
int16_t ButtonHeight(const uint8_t* model);
|
|
|
|
// Two clicks on the same thing within the system's double-click time.
|
|
class DoubleClick {
|
|
public:
|
|
bool Click(int target);
|
|
|
|
private:
|
|
int last_ = -1;
|
|
unsigned long at_ = 0;
|
|
};
|
|
|
|
} // namespace mods::screen
|