Files
khallaandClaude Opus 5.5 0ef3cf4bbd Shared chest, menu after a game, dialogs over 16:9 screens
- 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>
2026-10-21 00:41:12 +02:00

88 lines
5.0 KiB
Markdown

# Shared chest
## Request (2026-10-17)
The chest gets a second tab that belongs to no single hero: a chest shared by
all characters. Decided: stored in one file in the save folder
(`shared_chest.json` in the save folder, `Documents\Sacred\save`, see
`user-folders.md`), two tabs ("Truhe" / "Gemeinsam"), same size as the
chest (20 x 10). Potions stack in it like in every player inventory
(docs/item-stacks.md). Follow-up (same day): text tabs were barely visible;
use the inventory's Rucksack / Kampfkünste / Combos header look with its
selected state, stacked as a column from the grid's top-left. The shared chest
was empty after loading another character (in-game saves don't call saveHero).
## Measured / read
- The chest window `cUI_Chest` (constructor ends `0x6ac899`): +0x158 =
inventory 0x11 (the hero's chest), grid 20 x 10 from (window x, 20). Render
`0x6ac8b0` (vf14) draws +0x158 with `DrawItems`; HandleEvent `0x6acc70`
(vf10) does hit test, fit, place, swap and take via +0x158. Only Shift+click
uses the index: `cInventoryManager::MoveItem` `0x5dec80` (from, to, id, ?,
ret 0x10) from 0x11 to the hero.
- Chest saving: inventory 0x11 is chunk 0xc6 of the hero file and part of
save games, both through the JSON inventories already (diagnostic log).
- Items in hero files (`ItemDataMgr::saveHero` `0x6046f0`, chunk 0xc2): the
ids of both inventories, equipment, weapon sets; per item its type (+0x10)
and the object's serializer `vf1c(stream, 1, 0)` (ret 0xc).
- Loading (`loadHero` `0x605900`, chunk 0xc2): `cObjectManager::Create(type,
0, 0, 0)` `0x5fb590`, `vf1c(stream, 0, version)` (version = header byte,
0x1b), then the saved id (read into +0xc) is replaced by the new one
(`ReplaceItemId`), flags +0x14: clear 0x200000, 0x30 if bit 2, 0x10; +0x39
= owner hero id.
- `cEngine::~cEngine` `0x60d5e0` (plain ret) runs before the object manager is
destroyed: `game::OnGameEnd`.
## Mod (`src/mods/sharedchest.cpp`)
- An inventory of our own (`cInventoryEntry::Construct` `0x5d7a10`, owner
0x11 so it counts as a player inventory).
- Tabs: a `cUI_TabSheet` (constructor `0x728ed0`, `AddTab` `0x7290d0`) built
right after the chest window (`cUI_Chest::cUI_Chest` `0x6ac7f0`), the same
control as the inventory's Rucksack / Kampfkünste / Combos headers, so the
selected / hover look is the game's. Its layout (`0x729210`, headers in a row
above the sheet) is followed by ours: a column left of the grid's top-left
corner. Labels "Truhe" / "Gemeinsam" through `game::RegisterText`
(`src/game/ui/texts.cpp`, an override of `GetText` `0x672f70`). Switching
(`cUI_TabControl::SetActive` `0x728af0`) sets window +0x158 to inventory
0x11 or the shared one (origin and size copied from the chest). Pages are
empty controls.
- The tab sheet is added to the window's control list (`cUI_Window2::AddControl`
`0x727120`); only those controls are drawn and get events (a parent alone
isn't enough).
- Grid place: the constructor sets inventory 0x11 to (window x, 20), 20 x 10
once, but loads overwrite it (the game's inventory read `0x5df09b` resets
some inventories to 0/0; the JSON inventories restore the saved place), so
the chest drew at the wrong spot after some loads. Both tabs' grids are put
back to the constructor's values every tick and on each tab switch.
- `MoveItem` from/to 0x11 while the shared tab is open moves from/to it.
- File: `{"format": "sacredK.shared_chest", "version": 1, "items": [{cell,
w, h, type, data (hex of vf1c), count}]}`; written after each successful
`saveHero` and after each save-game write (in-game saves don't call saveHero;
via a temp file), read on the first engine tick with a hero after a session
start (`game::OnGameStart`, cEngine::cEngine `0x60ac70`) or a save-game read
with a hero; the grid is cleared when the session ends.
## Open / to check
- Whether save games (PAK) also store the shared chest's item objects (they
live in the object manager outside the 32 inventories).
- loadHero's extra fix-up for some item classes (`0x5beaa0` / `0x5d3a20`,
item classes 5, 6, 8, 0xd, 0x11..0x19, 0x1b, 0x1d) is not repeated.
- Multiplayer: not supported.
## Items replaced by a world's (fixed 2026-10-21)
Reported: the shared chest's items were gone, other items (potions, gear) in
their cells. The grid holds object ids. A save game loaded into the running
engine (Esc "Laden", cEngine::Load from the render loop 0x60e68d) replaces
all objects, so until the chest reloads on the next tick its ids name the new
world's objects. A hero saved inside that load (characters.cpp Engine_Load: a
migrated character's first load makes its world hero the shared hero with
SaveHero) wrote the shared chest, i.e. those objects, into
shared_chest.json; the next tick loaded them as the chest. Now the chest is
emptied and marked unloaded as soon as a save game is read or a game starts
(RequestLoad), so no save writes it before it is loaded again. The user's
chest was restored from the 2026-10-19 backup (the broken file kept as
backup/shared_chest.broken-2026-10-21.json).