Files
sacred_k/src/game/engine/items.h
T
khallaandClaude Opus 5.5 46a08e97e9 Teleportationstrank: yellow potions open the portal window
- Every yellow potion (potion kind 5, all stages) is a Teleportationstrank:
  drinking it posts the teleporter's event to the busy dialog with the
  hero's known portals; the potion is used up only when the hero teleports
  from that window (closing it keeps the potion)
- Names replaced in every game text (ReplaceInTexts), so item names and the
  bottom bar's quick-select tooltip read Teleportationstrank; GameText reads
  the game's own text
- HeroIndex / HeroInventory moved to skillslots for shared use
- docs/teleport-potion.md; walking animation after loading noted in todo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 04:04:21 +02:00

131 lines
5.3 KiB
C++

#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;
constexpr uint32_t kItemTypeCount = 0x7e60; // types 1..0x7e5f (bounds check in 0x428700)
// 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;
}
// The item class of item type `type` (type table entry: 0x80 bytes, class u8
// at +0x2e; read by ItemPotionKind). Usable from the inventory
// (cCreature::UseItem 0x54a8f0): 9 (potions), 0x10, 0x1c (runes).
constexpr size_t kItemTypeEntrySize = 0x80;
constexpr size_t kItemTypeClass = 0x2e;
inline uint8_t ItemClass(uint32_t type) {
const auto* table = *reinterpret_cast<const uint8_t* const*>(kItemTypeTable);
return table ? table[type * kItemTypeEntrySize + kItemTypeClass] : 0;
}
// Item classes (type table +0x2e), measured: 5 weapons / armour, 7, 9 potions,
// 0x1c combat-art runes (1x1).
constexpr uint8_t kItemClassPotion = 9;
constexpr uint8_t kItemClassRune = 0x1c;
// 0x43f5f0 (stdcall, ret 4): the item type is a potion or a rune (class 9 or
// 0x1c). Items of one such type are alike apart from their object state
// (measured: two runes of one type serialize the same past the object part).
inline bool ItemIsConsumable(uint32_t type) {
return reinterpret_cast<bool(__stdcall*)(uint32_t)>(0x0043f5f0)(type);
}
// 0x428700 (thiscall on the type table, (type), ret 4): the text id of the
// item type's name (entry +0x20), which cItemBase::GetName (0x44a420) shows
// unless the object has its own (+0x3c).
inline uint32_t ItemNameText(uint32_t type) {
using Fn = uint32_t(__fastcall*)(void* table, void* edx, uint32_t type);
void* table = *reinterpret_cast<void**>(kItemTypeTable);
return table ? reinterpret_cast<Fn>(0x00428700)(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: = the inventory's index (measured)
// Inventories below 0x12 belong to the players (index = hero id) and the
// chest (0x11, saved with the hero as chunk 0xc6); PlaceItem / RemoveAt only
// update the hero below that. Traders use higher ones (22 measured).
constexpr uint16_t kPlayerInventoryEnd = 0x12;
constexpr uint16_t kChestInventory = 0x11;
inline bool IsPlayerInventory(const uint8_t* inventory) {
return *reinterpret_cast<const uint16_t*>(inventory + kInventoryOwner) < kPlayerInventoryEnd;
}
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
#include <vector>
namespace game {
// Item objects as hero files store them (ItemDataMgr::saveHero / loadHero,
// docs/shared-chest.md): the type plus the object's own serializer
// vf1c(stream, write, version), ret 0xc.
constexpr uint32_t kItemFileVersion = 0x1b; // the hero file version handed to the item readers
constexpr size_t kItemOwner = 0x39; // u8: the owner hero's id
std::vector<uint8_t> SerializeItem(void* item);
// A new item object of `type` read from `data`, as loadHero recreates one
// (cObjectManager::Create, the serializer, its new id, flags, the owner).
// Returns its id, 0 on failure.
uint32_t CreateItem(uint32_t type, const std::vector<uint8_t>& data, uint32_t ownerHeroId);
// A copy of item `id` as a new object (same type and data, same owner).
uint32_t CloneItem(uint32_t id);
} // namespace game