# Options window: tabs and key bindings Request (2026-10-18): the settings screen gets three tabs: *Allgemein*, *Beute* (loot filter; first iteration: the game's loot options moved there from the common page) and *Tastenbelegung* (key bindings, so far only settable in `sacredK.ini`). Code: `src/mods/options.cpp`; bindings in `src/mods/input.cpp`. ## The window (cUI_Options) - vtable `0x89505c`, constructor `0x716570` (name, rect, parent, flags; `ret 0x18`), destructor `0x7165f0`; controls built by `0x7184e0`. - vf44 `0x716640` (renderer, ?, ?; `ret 0xc`): window render (`0x727040`), then drives the sliders while the mouse is held. vf10 `0x716b30`: events. - `0x717ea0` loads the settings into the controls when the window is shown; `0x717460` (thiscall, plain `ret`) writes them back — called for OK. - Controls: vector at `+0x90` (begin) / `+0x94` (end); the 16 background tiles are in the window's own list `+0x78`. Buttons: `+0x158` Cancel, `+0x15c` OK. Pickup: radio buttons `+0x16c/+0x170/+0x174` (`PICKUPAUTO` 0/1/2), checkbox `+0x178` (`PICKUPANIM`). - Layout (1024x768): frames at x `0x40` / `0x220`, y `0x40` / `0x120` / `0x200`, borders `0x14` outside, labels above (y `0x20` / `0x100` / `0x1e0`). Left: graphics, auto-pickup (*AUTOCOLLECT*), general; right: sound volume, 3D sound, internet. OK / Cancel at y `0x284`. - cUI_Frame (vtable `0x8955ac`): its render `0x73a540` does not test the visible flag, only `+0xdc` (set to 1 by its constructors). ## How the mod works - After the constructor: every control in `+0x90` is put on a page by its position — left column middle row → *Beute*, everything else → *Allgemein*, OK / Cancel on all pages. The auto-pickup group moves up one row (to the top of its page), the general group below it moves up into its place. - A cUI_TabSheet (`game/ui/tabs.h`) at the top frames' border; its headers (default row layout) sit above the frame labels. - Each frame (vf44) the controls not on the active page are hidden (visible flag, frames also `+0xdc`); controls hidden by the mod are shown again on their page. The game re-shows controls when the window opens, so this runs every frame. - *Tastenbelegung* is drawn by the mod: rows per binding, grouped (Kampfkünste, Waffensets, Bewegung, Aktionen), a key box per row. A click on a box captures the next key press (`input::SetKeyCapture`, swallowed before the game sees it): Esc cancels, Backspace unbinds, a bindable key is taken (and removed from any other action). The page works on a copy loaded when the window opens; OK (`0x717460`) saves it to `[keys]` in `sacredK.ini`, Cancel drops it. - Movement keys (W/A/S/D) are bindings now (`MoveUp/Left/Down/Right`); `mods/wasd.cpp` polls the bound keys. ## Follow-ups (2026-10-18) - Pages moved down 24 px (`kContentShift`) for room below the tab headers. - *Alle zurücksetzen* (left of OK / Cancel) sets every binding back to its default; saved with OK like any other change. - Potion bindings *Trank 1–5* and *Trank 1 für Gefährten* (default: no key). They call `cEngine::UseActionSlot` (`0x613040`, thiscall (kind, on ally), `ret 8`) on the engine tick. The game's own potion hotkeys are off: its engine hotkey switch (`cEngine::ReceiveEvent`, index bytes `0x619adc` per upper-case character − 0x20, targets `0x619ac0`) had Space → kind 1 (Shift: 5), B → kind 1 on the weakest ally, Q 2, W 3, E 4, R 5; those six entries now point at index 6 (no hotkey). `ActionSlot3` (W's action on G) is gone. - The window is drawn like the save game / export screens: black beside the 1024 frame (`hud::RenderFramed`). - Key bindings page: a backdrop (`0x724ed0`) and a frame (`0x739f90`) per column, created with the window's controls and shown only on that page. (Drawing the window's existing ones at other rects did not fit: a frame's border pieces keep the place they were made at.) - Pages move down 24 px under the tab headers. A slider (cUI_Scrollbar, vtable `0x895664`) moves with its parts: minus, plus, track and knob are embedded controls (+0x94 / +0x110 / +0x18c / +0x208) with rects in window coordinates, and the knob is placed from +0x78 / +0x7c each frame. - Key page layout (2026-10-19): the two frames span the other pages' frame area (top row's top to the bottom of *Internet-Optionen*); the hints sit in the bottom row next to *Alle zurücksetzen*, since they hold for the whole page. - Potion kinds are named by the bottom bar's tooltip texts: `PotionTooltipText` `0x43fa90` (kind) → text id by key, e.g. `Heiltrank [SPC]` (kinds 1–5: Heiltrank, Sog der Hölle, Trank des Mentors, Antidot der Viper, Trank der Konzentration / Teleportationstrank; read in `scripts/de/SRglbl.res`). Shown, the tags are colour codes (backslash, letter, 8 hex digits: `\dFFFFFF80Heiltrank \wFFFFFFFF[SPC]`); `game::PotionKindName` strips them and the key; the tooltips get the bound key in the brackets, or none (`game::SetTextOverride`, mods/potioncounts.cpp). - No posted game keys any more: *Spiel speichern* calls what the UI manager's S case (`0x757775`) does: save window built (manager slot `0x2b`), `CanOpenWindow` `0x75a690`, engine event (vtable `0x88eb38`, 0x13, 5, 1), then window command *show* (0x11) for `UI_WND_SAVEGAME` (`game/ui/windows.h`, shared with escape.cpp's *hide* 0x12). ## Karte, Minikarte, Beschriftungen (2026-10-19) Request: M (map), Tab (minimap) and Alt (text overlay) bindable too. - M: the UI manager's letter switch (`cUI_Manager::ReceiveEvent` 0x757120, upper-case character − 'C', index bytes 0x75868c, targets 0x758660) shows `UI_WND_MEGAMAP` (slot 0x25) unless a blocking window is open; while the map is open its own branch (cmp 'M' at 0x757f86) hides it. Switched off (M → index 10, the compare → -1); the *Karte* binding does the same checks and sends the window command. - Tab: the in-game window procedure 0x8110d0 shows `UI_WND_OVERVIEWMAP` on key down (switch on vk − 9: index bytes 0x812124, case 0x81146d) and hides it on key up (0x812270, case 0x8117f8). Switched off (Tab → the "other key" indices 4 / 3); *Minikarte (halten)* shows / hides it on the bound key's down / up. - Alt: no message, the game polls `GetAsyncKeyState(VK_MENU)` for its name overlay in seven places (DrawObjectLabels 0x627260, PickObjectAt 0x626d30, cEngine 0x60f1d0 / 0x611f10 / 0x617360, cCreature 0x599880, the map's debug teleport 0x6c7ec0). Their `call [GetAsyncKeyState]` read our pointer instead: `LabelsKeyState` returns the bound key's state. - Alt and F10 come as WM_SYSKEYDOWN: the key capture takes those too. - Key page: 20 lines per column (line 25 px) to fit the three rows. ## Beute: loot filter (2026-10-19) The page holds the loot filter (`docs/loot-filter.md`): the game's pickup choice (PICKUPAUTO radio buttons, their labels, the separator; matched by pointer and control name at +0x30) is never shown, a framed table below the auto-pickup frame sets a mode per category. Its frame is made like the key page's (`AddFrame`). ## Allgemein: autosave interval (2026-10-20) Request: the silent autosave's interval (docs/characters.md, Save) settable in the options, not only in `sacredK.ini`. The general frame moved up into the auto-pickup frame's place; it is taller than the frames beside it, so the new frame goes below the lowest frame of the left column. It copies the right column's lowest frame (Internet-Optionen): its height and the gap the right column leaves between its frames, measured from the window's frames when it is made (a lower frame broke its side ornaments, a smaller gap joined the borders: screenshots 2026-10-20). One row *Autosave (Min.)*: Aus / 5 / 10 / 15 / 30 (the key page's box colour is not seen on the dark backdrop: a lighter one). A click selects, OK writes `[characters] AutosaveMinutes` (`characters::SetAutosaveMinutes`, the timer uses it at once), Cancel drops it.