- 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>
88 lines
5.0 KiB
Markdown
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).
|