Potion counts on the bottom bar and potion stacks in the inventory
- 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>
This commit is contained in:
1 parent
b2e886c7f5
commit
13b91cdd3d
13 files changed
+612
-10
No files matched your search
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
// Items and the inventory grid (cInventoryEntry, docs/skill-slots.md).
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "game/engine/objects.h"
|
||||
|
||||
namespace game {
|
||||
|
||||
// Item objects (cItemBase and subclasses such as cPotion3D).
|
||||
constexpr size_t kItemType = 0x10;
|
||||
constexpr uint32_t kObjectUsedUp = 0x8000000; // kObjectFlags: set by cPotion3D::vf18 when drunk
|
||||
|
||||
// The item type table at *0xaa9564 (ecx of ItemPotionKind).
|
||||
constexpr uint32_t kItemTypeTable = 0x00aa9564;
|
||||
|
||||
// ItemPotionKind (0x43f630): the potion kind (1..5, the bottom bar's potion
|
||||
// slots; 1 = healing) of item type `type`, 0 if it is no potion.
|
||||
constexpr int kPotionKinds = 5;
|
||||
inline int ItemPotionKind(uint32_t type) {
|
||||
using Fn = int(__fastcall*)(void* table, void* edx, uint32_t type);
|
||||
void* table = *reinterpret_cast<void**>(kItemTypeTable);
|
||||
return table ? reinterpret_cast<Fn>(0x0043f630)(table, nullptr, type) : 0;
|
||||
}
|
||||
|
||||
inline uint32_t ItemType(const void* item) {
|
||||
return *reinterpret_cast<const uint32_t*>(static_cast<const uint8_t*>(item) + kItemType);
|
||||
}
|
||||
|
||||
// The potion kind of object `id`, 0 if it is no potion (or gone).
|
||||
inline int PotionKindOf(uint32_t id) {
|
||||
const void* item = id ? GetObjectById(id) : nullptr;
|
||||
return item ? ItemPotionKind(ItemType(item)) : 0;
|
||||
}
|
||||
|
||||
// cInventoryEntry item grid: cells of {u32 item id, u8 flags, u8, u8, u8,
|
||||
// u16 type, u16}. An item's id is in its first cell only; the other cells it
|
||||
// covers have id 0, flags bit 0 and the first cell's x, y at +5 / +6
|
||||
// (cInventoryEntry::GetItemAt 0x5da290 resolves them).
|
||||
constexpr size_t kInventoryGrid = 0x18;
|
||||
constexpr size_t kGridCellSize = 0xc;
|
||||
constexpr size_t kGridWidth = 0x115c; // u16, 0x14
|
||||
constexpr size_t kGridHeight = 0x115e; // u16, 8
|
||||
constexpr size_t kInventoryOwner = 0x1160; // u16: player index (heroes < 0x12)
|
||||
|
||||
inline unsigned GridCells(const uint8_t* inventory) {
|
||||
return *reinterpret_cast<const uint16_t*>(inventory + kGridWidth) *
|
||||
*reinterpret_cast<const uint16_t*>(inventory + kGridHeight);
|
||||
}
|
||||
|
||||
inline uint32_t GridItem(const uint8_t* inventory, unsigned cell) {
|
||||
return *reinterpret_cast<const uint32_t*>(inventory + kInventoryGrid + cell * kGridCellSize);
|
||||
}
|
||||
|
||||
// Calls `fn(id)` for each item in `inventory`'s grid.
|
||||
template <typename Fn>
|
||||
void ForEachGridItem(const uint8_t* inventory, Fn fn) {
|
||||
const unsigned cells = GridCells(inventory);
|
||||
for (unsigned cell = 0; cell < cells; ++cell) {
|
||||
if (const uint32_t id = GridItem(inventory, cell)) {
|
||||
fn(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
Reference in new issue
Block a user