#pragma once // cUI_TabSheet (0x80 bytes, vtable 0x895258): a control with tab headers // (the inventory's "Rucksack" / "Kampfkünste" / "Combos"). Its cUI_TabControl // at +0x78 (0x98 bytes, constructor 0x7278f0) holds the tabs: a vector of // 0x18-byte entries at +0x78/+0x7c {text id, rect (+4 x, +8 y, +0xc w|h), // header control (+0x10), page (+0x14)}, the active index at +0x84 (u16). // Header controls: +0x24 x, +0x28 y, +0x2c w|h (relative to the sheet). // Layout (0x729210) puts the headers in a row above the sheet; a click on a // header reaches SetActive, which hides the old page and shows the new one // (page vf24(show)). Selected / hover / normal look is the game's own // (cUI_TabControl::Render 0x728090). #include #include #include "game/ui/text.h" namespace game { constexpr size_t kTabSheetSize = 0x80; constexpr size_t kTabSheetControl = 0x78; // cUI_TabControl* constexpr size_t kTabControlTabs = 0x78; // entries begin; +0x7c end constexpr size_t kTabEntrySize = 0x18; constexpr size_t kTabEntryRect = 0x4; constexpr size_t kTabEntryHeader = 0x10; constexpr size_t kControlRect = 0x24; // a control's rect (x, y, w|h), relative to its parent // The header art's height (left piece; static data filled when the UI loads). constexpr uintptr_t kTabHeaderArtHeight = 0x009ed17c; // The game's operator new (0x8485e2, cdecl): controls the UI deletes itself // must come from it. inline void* GameNew(size_t size) { return reinterpret_cast(0x008485e2)(size); } // cUI_TabSheet::cUI_TabSheet (0x728ed0, ret 0x18): (name, rect by value, // parent, 5); the inventory passes 5 as the last argument. inline void* NewTabSheet(const char* name, UiRect rect, void* parent) { void* sheet = GameNew(kTabSheetSize); using ConstructFn = void*(__fastcall*)(void*, void*, const char*, UiRect, void*, uint32_t); return sheet ? reinterpret_cast(0x00728ed0)(sheet, nullptr, name, rect, parent, 5) : nullptr; } // cUI_TabSheet::AddTab (0x7290d0, ret 8): (text id, page control). inline void AddTab(void* sheet, uint32_t textId, void* page) { using AddFn = char(__fastcall*)(void*, void*, uint32_t, void*); reinterpret_cast(0x007290d0)(sheet, nullptr, textId, page); } // cUI_TabSheet::SetActive (0x7291d0, ret 4). inline void SetActiveTab(void* sheet, uint16_t index) { using SetFn = void(__fastcall*)(void*, void*, uint32_t); reinterpret_cast(0x007291d0)(sheet, nullptr, index); } inline uint8_t* TabControl(void* sheet) { return *reinterpret_cast(static_cast(sheet) + kTabSheetControl); } constexpr size_t kTabControlActive = 0x84; // u16 inline uint16_t ActiveTab(void* sheet) { const uint8_t* control = TabControl(sheet); return control ? *reinterpret_cast(control + kTabControlActive) : 0; } // The tab entries of a cUI_TabControl. inline uint8_t* TabEntries(uint8_t* control) { return *reinterpret_cast(control + kTabControlTabs); } inline size_t TabCount(uint8_t* control) { uint8_t* begin = TabEntries(control); uint8_t* end = *reinterpret_cast(control + kTabControlTabs + 4); return begin ? static_cast(end - begin) / kTabEntrySize : 0; } } // namespace game namespace game { // cUI_Window2::AddControl (0x727120, ret 4): the window renders the controls // in its list (+0x78) and hands them its events (cUI_Window2::Render 0x727040, // HandleEvent 0x726880). A control's parent alone doesn't do that. inline void AddWindowControl(void* window, void* control) { using AddFn = void(__fastcall*)(void*, void*, void*); reinterpret_cast(0x00727120)(window, nullptr, control); } } // namespace game