Problem The bug is on the server side in CShop::BroadcastUpdateItem (source/server/game/src/shop.cpp): TPacketGCShopUpdateItem pack2; // <- not initialized!
When an item is purchased from a private shop (including the search glass), the server first sets r_item.pkItem = NULL and then calls BroadcastUpdateItem(pos). The first branch of this function is: if (m_pkPC && !m_itemVector[pos].pkItem) pack2.item.vnum = 0; // only vnum is zeroed out
Because the pack2 structure on the stack is not zeroed out, other fields including dwTransmutationVnum retain garbage memory values. The client copies this packet via SetItemData, and when shop.GetItemChangeLookVnum(idx) != 0 in uishop.py, it renders the ingame_convert_Mark.tga icon. Closing and reopening the window sends the SHOP_SUBHEADER_GC_START packet with the correct value (0), causing the icon to disappear—exactly matching the described behavior.
Solution Zero out pack2 in shop.cpp:
TPacketGCShopUpdateItem pack2;
memset(&pack2, 0, sizeof(pack2));
The Start() function in the same file already uses this exact approach. This clears dwTransmutationVnum and all other uninitialized fields in the sold item's slot. Since both regular shop purchases and search glass purchases route through this function, both are resolved. Recompiling the game project is all that is required.
example:
void CShop::BroadcastUpdateItem(BYTE pos)
{
TPacketGCShop pack;
TPacketGCShopUpdateItem pack2;
#if defined(ENABLE_TRANSMUTATION)
memset(&pack2, 0, sizeof(pack2));
#endif
TEMP_BUFFER buf;
pack.header = HEADER_GC_SHOP;
pack.subheader = SHOP_SUBHEADER_GC_UPDATE_ITEM;
pack.size = sizeof(pack) + sizeof(pack2);
pack2.pos = pos;
if (m_pkPC && !m_itemVector[pos].pkItem)
pack2.item.vnum = 0;
else
{
pack2.item.vnum = m_itemVector[pos].vnum;
if (m_itemVector[pos].pkItem)
{
thecore_memcpy(pack2.item.alSockets, m_itemVector[pos].pkItem->GetSockets(), sizeof(pack2.item.alSockets));
thecore_memcpy(pack2.item.aAttr, m_itemVector[pos].pkItem->GetAttributes(), sizeof(pack2.item.aAttr));
#if defined(ENABLE_TRANSMUTATION)
pack2.item.dwTransmutationVnum = m_itemVector[pos].pkItem->GetTransmutationVnum();
#endif
}
else
{
memset(pack2.item.alSockets, 0, sizeof(pack2.item.alSockets));
memset(pack2.item.aAttr, 0, sizeof(pack2.item.aAttr));
#if defined(ENABLE_TRANSMUTATION)
pack2.item.dwTransmutationVnum = 0;
#endif
}
}
pack2.item.price = m_itemVector[pos].price;
#ifdef ENABLE_CHEQUE_SYSTEM
pack2.item.cheque = m_itemVector[pos].cheque;
#endif
pack2.item.count = m_itemVector[pos].count;
buf.write(&pack, sizeof(pack));
buf.write(&pack2, sizeof(pack2));
Broadcast(buf.read_peek(), buf.size());
}