Characters: 16:9 "Spielen" screen, autosave interval option

- Shared screen kit (mods/screen, docs/screens.md): a 1365x768 canvas
  centred in the render width on our own dark background, the game's
  frames and buttons; the main menu renders full width while it is shown.
- The load window (cUI_Savegame) is replaced by the Spielen screen:
  character list, hero panel, campaign x difficulty grid, save/autosave
  choice, rename/delete/reset/new character dialogs.
- Migrated characters without a shared hero load their hero's world
  first, then start the new world.
- Options, Allgemein: autosave interval (Aus/5/10/15/30 minutes).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
khallaandClaude Opus 5.5 committed 2026-10-20 23:20:45 +02:00
1 parent 732d9c2abb
commit 34a119433a
16 files changed
+1391 -468

No files matched your search

+105
View File
@@ -0,0 +1,105 @@
#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
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;
// Draws the screen of `window`: the background across the whole render
// target, then body(context) in canvas coordinates. While `window` is shown
// the main menu thread renders the full width (widescreen::SetWideMenuScreen).
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