Files
sacred_k/src/game/ui/button.h
T
khallaandClaude Opus 5.5 5bccb0734e Characters: one file per character with a world per difficulty
- CharacterNN.sacredK holds the shared hero and one world per difficulty,
  plus an autosave file; the game works in save\characters\_work
- Load window: one row per character, a difficulty dialog on Laden
  (worlds, newer autosaves, "(neu)" starts a new game with the shared
  hero), Umbenennen, delete moves the character to characters\deleted
- The world keeps its own portals and hero position when the shared hero
  is put on
- K and the Esc menu's "Speichern" save directly (quicksave path)
- One-time migration of the old save rows and exported heroes, after a
  backup
- Startup no longer hangs in the game's foreground loop (0x810af0)
- Text overrides apply to our own text ids too; shared cUI_Button2
  helpers in game/ui/button.h

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 01:41:42 +02:00

67 lines
2.8 KiB
C++

#pragma once
// cUI_Button2 (vtable 0x895308, 0xbc bytes, constructor 0x72a180, plain ret).
// Windows set their buttons up as cUI_Options 0x7184e0 does: images
// (+0x54..+0x87), sounds (0x72acf0 -> +0x90..+0x9f), caption (0x72ad60 (text
// id, font 1) -> +0xa4 / +0xa0), caption offset (0x72ad40 -> +0xb0), name
// (0x732090).
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include "game/ui/tabs.h"
#include "game/ui/text.h"
namespace game {
constexpr size_t kButtonSize = 0xbc;
constexpr size_t kButtonLook = 0x54;
constexpr size_t kButtonLookEnd = 0x88;
constexpr size_t kButtonSounds = 0x90;
constexpr size_t kButtonSoundsEnd = 0xa0;
constexpr size_t kButtonCaptionOffset = 0xb0; // x, y (int16)
constexpr uint32_t kButtonFlags = 5; // 0x731e70, as 0x7184e0 passes
// Control vtable entries: vf10 handles an event (ret 4), vf14 renders (ret 4).
constexpr size_t kVfHandleEvent = 0x10 / 4;
constexpr size_t kVfRender = 0x14 / 4;
// A new button with `model`'s look and sounds at `rect` (relative to the
// parent it is added to, or the screen frame without one), captioned with text
// id `caption`.
inline uint8_t* NewButtonLike(const uint8_t* model, const UiRect& rect, uint32_t caption, const char* name) {
void* memory = GameNew(kButtonSize);
if (!memory) {
return nullptr;
}
using ConstructFn = uint8_t*(__fastcall*)(void*, void*);
uint8_t* button = reinterpret_cast<ConstructFn>(0x0072a180)(memory, nullptr);
using FlagsFn = void(__fastcall*)(void*, void*, uint32_t);
reinterpret_cast<FlagsFn>(0x00731e70)(button, nullptr, kButtonFlags);
std::copy(model + kButtonLook, model + kButtonLookEnd, button + kButtonLook);
std::copy(model + kButtonSounds, model + kButtonSoundsEnd, button + kButtonSounds);
std::copy(model + kButtonCaptionOffset, model + kButtonCaptionOffset + 4, button + kButtonCaptionOffset);
*reinterpret_cast<UiRect*>(button + kControlRect) = rect;
using CaptionFn = void(__fastcall*)(void*, void*, uint32_t, uint32_t);
reinterpret_cast<CaptionFn>(0x0072ad60)(button, nullptr, caption, 1);
using NameFn = void(__fastcall*)(void*, void*, const char*);
reinterpret_cast<NameFn>(0x00732090)(button, nullptr, name);
return button;
}
inline int ControlHandleEvent(void* control, void* event) {
using Fn = int(__fastcall*)(void*, void*, void*);
return reinterpret_cast<Fn>((*static_cast<void***>(control))[kVfHandleEvent])(control, nullptr, event);
}
inline int ControlRender(void* control, void* device) {
using Fn = int(__fastcall*)(void*, void*, void*);
return reinterpret_cast<Fn>((*static_cast<void***>(control))[kVfRender])(control, nullptr, device);
}
inline bool Inside(const UiRect& rect, int x, int y) {
return x >= rect.x && x < rect.x + rect.w && y >= rect.y && y < rect.y + rect.h;
}
} // namespace game