mirror of
https://github.com/brues-code/ClassicAPI.git
synced 2026-11-10 20:07:37 +00:00
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).
This commit is contained in:
@@ -27,6 +27,9 @@ namespace {
|
||||
|
||||
using BuildItemLink_t = const char *(__fastcall *)(const void *cgItem);
|
||||
|
||||
using BuildInstanceName_t = void(__thiscall *)(const void *cgItem, char *out,
|
||||
unsigned outSize);
|
||||
|
||||
// Cached ItemStats record lookup. Same `_GetRecord` pattern the rest of
|
||||
// the codebase uses; passes a noop NULL callback so no SMSG fires for
|
||||
// uncached items — caller treats "not cached" as a clean failure.
|
||||
@@ -50,6 +53,16 @@ const char *FromCGItem(const uint8_t *cgItem) {
|
||||
return fn(cgItem);
|
||||
}
|
||||
|
||||
bool NameFromCGItem(const uint8_t *cgItem, char *out, size_t outSize) {
|
||||
if (cgItem == nullptr || out == nullptr || outSize == 0)
|
||||
return false;
|
||||
out[0] = '\0';
|
||||
auto fn = reinterpret_cast<BuildInstanceName_t>(
|
||||
Offsets::FUN_ITEM_BUILD_INSTANCE_NAME);
|
||||
fn(cgItem, out, static_cast<unsigned>(outSize));
|
||||
return out[0] != '\0';
|
||||
}
|
||||
|
||||
bool BasicFromItemID(uint32_t itemID, char *out, size_t outSize) {
|
||||
if (out == nullptr || outSize == 0)
|
||||
return false;
|
||||
|
||||
@@ -29,6 +29,17 @@ namespace Item::Link {
|
||||
// produce for the same slot.
|
||||
const char *FromCGItem(const uint8_t *cgItem);
|
||||
|
||||
// Writes the item's per-instance decorated *display name* into `out` —
|
||||
// the same `[Name]` the engine stamps into `FromCGItem`'s link, e.g.
|
||||
// "Iridium Chain of the Owl" (or the base name when the instance carries
|
||||
// no random suffix). Reads the suffix ID off the CGItem descriptor
|
||||
// (`+0x98`) and formats it through the engine's own name builder
|
||||
// (`FUN_ITEM_BUILD_INSTANCE_NAME`). Returns false (and leaves `out`
|
||||
// empty) for a null item or an empty build result, so callers can fall
|
||||
// back to the base ItemStats name. Use whenever a name is returned
|
||||
// alongside a `FromCGItem` link, so the two agree.
|
||||
bool NameFromCGItem(const uint8_t *cgItem, char *out, size_t outSize);
|
||||
|
||||
// Builds a basic itemID-only hyperlink (`"|cffRRGGBB|Hitem:N:0:0:0|h[Name]|h|r"`)
|
||||
// into `out` from the cached ItemStats record for `itemID`. No
|
||||
// enchant / random-suffix decoration — use this when the caller has
|
||||
|
||||
+6
-19
@@ -16,6 +16,7 @@
|
||||
#include "item/Arg.h"
|
||||
#include "item/Data.h"
|
||||
#include "item/ID.h"
|
||||
#include "item/Link.h"
|
||||
#include "item/Location.h"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -56,27 +57,13 @@ int PushNameForItemID(void *L, int itemID) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Engine's per-instance item-name builder: `__thiscall(CGItem *this,
|
||||
// char *outBuf, uint outSize)`. Reads the instance's random-suffix ID off
|
||||
// the descriptor (+0x98) and writes the decorated display name
|
||||
// ("Ethereum Torque of the Sorcerer"), or the base name when there's no
|
||||
// suffix. Same builder the item-link builder uses for the bracketed name,
|
||||
// so this matches `GetItemLink`'s name and modern `C_Item.GetItemName`.
|
||||
using BuildInstanceName_t = void(__thiscall *)(const void *cgItem, char *out,
|
||||
unsigned outSize);
|
||||
|
||||
// Pushes the suffix-decorated display name for a resolved `CGItem`.
|
||||
// Returns 0 (pushes nothing) for a null item or an empty build result,
|
||||
// so the caller can fall back to the base-name-by-itemID path.
|
||||
// Pushes the suffix-decorated display name for a resolved `CGItem` via
|
||||
// the shared engine name builder. Returns 0 (pushes nothing) for a null
|
||||
// item or an empty build result, so the caller can fall back to the
|
||||
// base-name-by-itemID path.
|
||||
int PushInstanceName(void *L, const uint8_t *cgItem) {
|
||||
if (cgItem == nullptr)
|
||||
return 0;
|
||||
char buf[128];
|
||||
buf[0] = '\0';
|
||||
auto fn = reinterpret_cast<BuildInstanceName_t>(
|
||||
Offsets::FUN_ITEM_BUILD_INSTANCE_NAME);
|
||||
fn(cgItem, buf, sizeof(buf));
|
||||
if (buf[0] == '\0')
|
||||
if (!Item::Link::NameFromCGItem(cgItem, buf, sizeof(buf)))
|
||||
return 0;
|
||||
Game::Lua::PushString(L, buf);
|
||||
return 1;
|
||||
|
||||
+13
-18
@@ -220,24 +220,19 @@ static int __fastcall Script_GameTooltipGetItem(void *L) {
|
||||
const char *link = Item::Link::FromCGItem(
|
||||
static_cast<const uint8_t *>(cgItem));
|
||||
if (link != nullptr && *link != '\0') {
|
||||
// Engine's link builder also writes the dressed
|
||||
// (random-suffixed) name into the link's `[Name]`
|
||||
// slot; pull it out by reading the cached base name
|
||||
// for the return value. The engine doesn't expose
|
||||
// the dressed name as a separate string, so we
|
||||
// return the base name — matches modern semantics
|
||||
// where (name, link) name is the cached display name
|
||||
// and the link is the full hyperlink.
|
||||
const uint8_t *record = PeekItemRecord(static_cast<uint32_t>(itemID));
|
||||
if (record != nullptr) {
|
||||
const char *name = *reinterpret_cast<const char *const *>(
|
||||
record + Offsets::OFF_ITEMSTATS_NAME);
|
||||
if (name != nullptr && *name != '\0') {
|
||||
Game::Lua::PushString(L, name);
|
||||
Game::Lua::PushString(L, link);
|
||||
Game::Lua::PushNumber(L, static_cast<double>(itemID));
|
||||
return 3;
|
||||
}
|
||||
// Return the dressed (random-suffixed) name the engine
|
||||
// wrote into the link's `[Name]` slot — built off the same
|
||||
// CGItem via Item::Link::NameFromCGItem — so the returned
|
||||
// name agrees with the link ("Iridium Chain of the Owl",
|
||||
// not the base "Iridium Chain") and matches modern
|
||||
// GameTooltip:GetItem() on suffixed items.
|
||||
char name[128];
|
||||
if (Item::Link::NameFromCGItem(
|
||||
static_cast<const uint8_t *>(cgItem), name, sizeof(name))) {
|
||||
Game::Lua::PushString(L, name);
|
||||
Game::Lua::PushString(L, link);
|
||||
Game::Lua::PushNumber(L, static_cast<double>(itemID));
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user