The chest is inventory 0x11 and already saved as JSON; stacking now applies to all inventories below 0x12. Traders keep selling single potions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3.6 KiB
3.6 KiB
Potion counts and potion stacks
Requests (2026-10-17)
- The potion selector on the bottom bar always shows how many potions of each kind the hero carries (only when there are any).
- Potions of the same type stack in one inventory cell, up to 20 per stack. Every potion entering the inventory tries to stack. Drag, sell and drop move a whole stack. The inventory's sort button stacks first, then sorts. A stack dropped on the ground splits into single potions (agreed for now).
Measured (diagnostic log, 2026-10-17)
| Action | Calls |
|---|---|
| Drink (potion key) | cEngine::UseActionSlot 0x613040 → cCreature::UseItem 0x54a8f0 → RemoveItem (from 0x54aa20) → DestroyObject (from 0x54aa30); then UseActionSlot calls RemoveItem again (0x6130d8, not found) |
| Drag in the inventory | RemoveAt from 0x6c06fb (cell → cursor), PlaceItem from 0x6c0583 (cursor → cell) |
| Buy | trader grid (inventory 22) RemoveItem 0x6d96f5 → hero grid PlaceItem 0x6d98e8 |
| Pick up from the ground | PlaceItem from 0x558db6 |
| Trader restock | RemoveItem 0x6d9973 + DestroyObject 0x6d99cc, new items PlaceItem 0x6d9bdf |
| Sort | cInventoryEntry::Sort 0x5db670: all items removed (0x5db8a5) and placed again (0x5dbb4c, notify 0) |
Not seen yet: a sale, a right-click drink.
Game side
- Potion kinds:
ItemPotionKind0x43f630(ecx = item type table*0xaa9564) maps an item type to kind 1..5 (the bottom bar's potion slots);GetPotionRef0x5da880picks the potion a slot drinks. - Bottom bar:
cUI_Taskbar2::RenderPotionSlots0x6e39e0draws the 5 slot controls (embedded at +0x258, stride 0xbc). - Inventory grid (
cInventoryEntry, docs/skill-slots.md): cells of 0xc bytes at +0x18; an item's id only in its first cell, covered cells flagged.CellRect0x5dd530gives a cell's screen rect (32x32 from the origin +0x1164 / +0x1166, which the window sets beforeDrawItems0x5dc3f0). - Drinking:
cPotion3D::vf180x5ca9d0applies the effect and sets object flag +0x14 bit0x8000000(used up);UseItemthen removes and destroys. - Sort button:
cUI_Inventory3::HandleEvent0x6c5f40, button control at +0x158, mode from bit 4 of +0x168. - Hero files map saved item ids to new objects with
ReplaceItemId0x5d80e0.
Mods
src/mods/potioncounts.cpp: counts per kind (stacks added up), drawn bottom-right on each potion slot afterRenderPotionSlots.src/mods/potionstack.cpp: stack counts by item id (with the item type), saved as"count"on the grid cell in the JSON inventories (inventory_save.cpp); entry pours into stacks with room on the next tick;Sortstacks the hero's potions first; a drink from a stack of more than one takes one off the count and keeps the object; the count is drawn on the cell afterDrawItems.
Open
- Selling a stack: the price is for one potion so far (sale path not measured).
- Dropping a stack on the ground: it should split into single potions; for now the count stays with the object while the game runs, but a save keeps only the object (one potion).
Chest and other inventories (2026-10-17)
Asked: potions stack in every kind of inventory; traders only sell single
potions. Measured: the chest is inventory 0x11 (cUI_Chest draws window
+0x158), saved with the hero as chunk 0xc6 and in save games, both through the
JSON path already (stack sizes survived a reload). Stacking now applies to all
inventories below 0x12 (players and the chest); trader stock (0x12 and up)
never stacks. Selling a stack to a trader still has to be handled (pay
count x price, the trader keeps singles).