Skip to content

Commit e8a9003

Browse files
committed
item: dress GameTooltip:GetItem() name with random suffix
GameTooltip:GetItem() resolved the CGItem and built the suffixed link via Item::Link::FromCGItem, but returned the base ItemStats name beside it -- so name and link disagreed ("Iridium Chain" vs "[Iridium Chain of the Owl]"). The old comment claimed the engine didn't expose the dressed name as a separate string; it does, via the same builder the link uses. Extract that builder into a shared Item::Link::NameFromCGItem helper (next to FromCGItem, wrapping FUN_ITEM_BUILD_INSTANCE_NAME) and use it in both GetItem and C_Item.GetItemName so the returned name matches the link and modern behavior. Verified in-game: GetItem() now returns "Iridium Chain of the Owl" alongside its matching link. Left the base-name reads that are correct as-is: itemID-only paths (mail, cursor-by-id, GetItemInfo, equipment-set missing items) have no instance, and the by-name *matching* in IsEquippedItem / bag lookups compares against a caller string (a separate semantics question).
1 parent aa0279a commit e8a9003

4 files changed

Lines changed: 43 additions & 37 deletions

File tree

src/item/Link.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ namespace {
2727

2828
using BuildItemLink_t = const char *(__fastcall *)(const void *cgItem);
2929

30+
using BuildInstanceName_t = void(__thiscall *)(const void *cgItem, char *out,
31+
unsigned outSize);
32+
3033
// Cached ItemStats record lookup. Same `_GetRecord` pattern the rest of
3134
// the codebase uses; passes a noop NULL callback so no SMSG fires for
3235
// uncached items — caller treats "not cached" as a clean failure.
@@ -50,6 +53,16 @@ const char *FromCGItem(const uint8_t *cgItem) {
5053
return fn(cgItem);
5154
}
5255

56+
bool NameFromCGItem(const uint8_t *cgItem, char *out, size_t outSize) {
57+
if (cgItem == nullptr || out == nullptr || outSize == 0)
58+
return false;
59+
out[0] = '\0';
60+
auto fn = reinterpret_cast<BuildInstanceName_t>(
61+
Offsets::FUN_ITEM_BUILD_INSTANCE_NAME);
62+
fn(cgItem, out, static_cast<unsigned>(outSize));
63+
return out[0] != '\0';
64+
}
65+
5366
bool BasicFromItemID(uint32_t itemID, char *out, size_t outSize) {
5467
if (out == nullptr || outSize == 0)
5568
return false;

src/item/Link.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ namespace Item::Link {
2929
// produce for the same slot.
3030
const char *FromCGItem(const uint8_t *cgItem);
3131

32+
// Writes the item's per-instance decorated *display name* into `out` —
33+
// the same `[Name]` the engine stamps into `FromCGItem`'s link, e.g.
34+
// "Iridium Chain of the Owl" (or the base name when the instance carries
35+
// no random suffix). Reads the suffix ID off the CGItem descriptor
36+
// (`+0x98`) and formats it through the engine's own name builder
37+
// (`FUN_ITEM_BUILD_INSTANCE_NAME`). Returns false (and leaves `out`
38+
// empty) for a null item or an empty build result, so callers can fall
39+
// back to the base ItemStats name. Use whenever a name is returned
40+
// alongside a `FromCGItem` link, so the two agree.
41+
bool NameFromCGItem(const uint8_t *cgItem, char *out, size_t outSize);
42+
3243
// Builds a basic itemID-only hyperlink (`"|cffRRGGBB|Hitem:N:0:0:0|h[Name]|h|r"`)
3344
// into `out` from the cached ItemStats record for `itemID`. No
3445
// enchant / random-suffix decoration — use this when the caller has

src/item/Name.cpp

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "item/Arg.h"
1717
#include "item/Data.h"
1818
#include "item/ID.h"
19+
#include "item/Link.h"
1920
#include "item/Location.h"
2021

2122
#include <cstdint>
@@ -56,27 +57,13 @@ int PushNameForItemID(void *L, int itemID) {
5657
return 1;
5758
}
5859

59-
// Engine's per-instance item-name builder: `__thiscall(CGItem *this,
60-
// char *outBuf, uint outSize)`. Reads the instance's random-suffix ID off
61-
// the descriptor (+0x98) and writes the decorated display name
62-
// ("Ethereum Torque of the Sorcerer"), or the base name when there's no
63-
// suffix. Same builder the item-link builder uses for the bracketed name,
64-
// so this matches `GetItemLink`'s name and modern `C_Item.GetItemName`.
65-
using BuildInstanceName_t = void(__thiscall *)(const void *cgItem, char *out,
66-
unsigned outSize);
67-
68-
// Pushes the suffix-decorated display name for a resolved `CGItem`.
69-
// Returns 0 (pushes nothing) for a null item or an empty build result,
70-
// so the caller can fall back to the base-name-by-itemID path.
60+
// Pushes the suffix-decorated display name for a resolved `CGItem` via
61+
// the shared engine name builder. Returns 0 (pushes nothing) for a null
62+
// item or an empty build result, so the caller can fall back to the
63+
// base-name-by-itemID path.
7164
int PushInstanceName(void *L, const uint8_t *cgItem) {
72-
if (cgItem == nullptr)
73-
return 0;
7465
char buf[128];
75-
buf[0] = '\0';
76-
auto fn = reinterpret_cast<BuildInstanceName_t>(
77-
Offsets::FUN_ITEM_BUILD_INSTANCE_NAME);
78-
fn(cgItem, buf, sizeof(buf));
79-
if (buf[0] == '\0')
66+
if (!Item::Link::NameFromCGItem(cgItem, buf, sizeof(buf)))
8067
return 0;
8168
Game::Lua::PushString(L, buf);
8269
return 1;

src/item/Tooltip.cpp

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -220,24 +220,19 @@ static int __fastcall Script_GameTooltipGetItem(void *L) {
220220
const char *link = Item::Link::FromCGItem(
221221
static_cast<const uint8_t *>(cgItem));
222222
if (link != nullptr && *link != '\0') {
223-
// Engine's link builder also writes the dressed
224-
// (random-suffixed) name into the link's `[Name]`
225-
// slot; pull it out by reading the cached base name
226-
// for the return value. The engine doesn't expose
227-
// the dressed name as a separate string, so we
228-
// return the base name — matches modern semantics
229-
// where (name, link) name is the cached display name
230-
// and the link is the full hyperlink.
231-
const uint8_t *record = PeekItemRecord(static_cast<uint32_t>(itemID));
232-
if (record != nullptr) {
233-
const char *name = *reinterpret_cast<const char *const *>(
234-
record + Offsets::OFF_ITEMSTATS_NAME);
235-
if (name != nullptr && *name != '\0') {
236-
Game::Lua::PushString(L, name);
237-
Game::Lua::PushString(L, link);
238-
Game::Lua::PushNumber(L, static_cast<double>(itemID));
239-
return 3;
240-
}
223+
// Return the dressed (random-suffixed) name the engine
224+
// wrote into the link's `[Name]` slot — built off the same
225+
// CGItem via Item::Link::NameFromCGItem — so the returned
226+
// name agrees with the link ("Iridium Chain of the Owl",
227+
// not the base "Iridium Chain") and matches modern
228+
// GameTooltip:GetItem() on suffixed items.
229+
char name[128];
230+
if (Item::Link::NameFromCGItem(
231+
static_cast<const uint8_t *>(cgItem), name, sizeof(name))) {
232+
Game::Lua::PushString(L, name);
233+
Game::Lua::PushString(L, link);
234+
Game::Lua::PushNumber(L, static_cast<double>(itemID));
235+
return 3;
241236
}
242237
}
243238
}

0 commit comments

Comments
 (0)