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
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "game/engine/objects.h"
|
||||
#include "re/hook.h"
|
||||
|
||||
namespace game {
|
||||
@@ -29,7 +30,7 @@ void BeforeFxEvent(FxEventFn callback) {
|
||||
|
||||
uint32_t CreateObjectAt(uint32_t type, const void* position, uint32_t link) {
|
||||
using Fn = uint32_t(__fastcall*)(void* manager, void* edx, uint32_t, const void*, uint32_t, uint32_t, uint32_t);
|
||||
void* manager = *reinterpret_cast<void**>(0x00ad3bc0);
|
||||
void* manager = ObjectManager();
|
||||
return manager ? reinterpret_cast<Fn>(0x005fbaa0)(manager, nullptr, type, position, link, 0, 0) : 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -38,13 +38,27 @@ inline void* DynamicCast(void* object, uint32_t sourceType, uint32_t targetType)
|
||||
return object ? reinterpret_cast<Fn>(0x00849701)(object, 0, sourceType, targetType, 0) : nullptr;
|
||||
}
|
||||
|
||||
// cObjectManager::GetObject (0x5fe060) on the manager at *0xad3bc0.
|
||||
// The object manager (cObjectManager at *0xad3bc0), or null.
|
||||
inline void* ObjectManager() {
|
||||
return *reinterpret_cast<void**>(0x00ad3bc0);
|
||||
}
|
||||
|
||||
// cObjectManager::GetObject (0x5fe060).
|
||||
inline void* GetObjectById(uint32_t id) {
|
||||
using Fn = void*(__fastcall*)(void* manager, void* edx, uint32_t id);
|
||||
void* manager = *reinterpret_cast<void**>(0x00ad3bc0);
|
||||
void* manager = ObjectManager();
|
||||
return manager ? reinterpret_cast<Fn>(0x005fe060)(manager, nullptr, id) : nullptr;
|
||||
}
|
||||
|
||||
// cObjectManager::DestroyObject (0x5fbe10), with the arguments cCreature::UseItem
|
||||
// destroys a drunk potion with (id, 1, 0, 0).
|
||||
inline void DestroyObject(uint32_t id) {
|
||||
using Fn = uint32_t(__fastcall*)(void* manager, void* edx, uint32_t, uint32_t, uint32_t, uint32_t);
|
||||
if (void* manager = ObjectManager()) {
|
||||
reinterpret_cast<Fn>(0x005fbe10)(manager, nullptr, id, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// The object `id` as a cCreature, or null.
|
||||
inline void* GetCreature(uint32_t id) {
|
||||
return DynamicCast(GetObjectById(id), kTypeObject, kTypeCreature);
|
||||
@@ -54,7 +68,7 @@ inline void* GetCreature(uint32_t id) {
|
||||
// cCreature), or null.
|
||||
inline void* GetPlayerHero() {
|
||||
using Fn = void*(__fastcall*)(void* manager, void* edx);
|
||||
void* manager = *reinterpret_cast<void**>(0x00ad3bc0);
|
||||
void* manager = ObjectManager();
|
||||
return manager ? reinterpret_cast<Fn>(0x00603e90)(manager, nullptr) : nullptr;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user