- potioncounts.cpp: each potion slot shows how many potions of its kind the hero carries (stacks added up) - potionstack.cpp: potions of one type stack up to 20 per cell; every potion entering the hero's grid pours into stacks with room, the sort button stacks first, drinking takes one off a stack, counts drawn on the cells and saved in the JSON inventories - game/engine/items.h: potion kinds and grid helpers; ObjectManager and DestroyObject in objects.h - auto-attack: the projectile-weapon target radius is a design value - docs/potion-stacks.md: measured paths and the design Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3.1 KiB
3.1 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).