Improved itemlink logic (#7532)

Fixed tags and separators for various packet versions.

If itemlink is disabled it will output the item name with the refine as prefix and the slots as suffix (if equipment)
It will also append [0] if the item is an equipment and has 0 slots (to make it easier to tell from non-equipment items)

Disabled feature on packet versions older than 2015-11-04 until proven otherwise

Fixes #7441

Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
This commit is contained in:
Lemon 2023-01-23 06:49:18 -03:00 committed by GitHub
parent f4ad5045a5
commit 7e62670882
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 122 additions and 110 deletions

View File

@ -141,10 +141,10 @@ feature.dynamicnpc_rangey: 2
feature.dynamicnpc_direction: no
// Itemlink System on informational related commands (Note 1)
// Generates <ITEML> string for an item and can be used for npctalk, message,
// Generates an itemlink string for an item and can be used for npctalk, message,
// dispbottom, and broadcast commands. The result is clickable-item name just
// like from SHIFT+Click from player's inventory/cart/equipment window.
// Requires: 2010-00-00RagexeRE or later
// Requires: 2015-11-04Ragexe or later
feature.itemlink: on
// Stylist UI (Note 1)

View File

@ -1447,7 +1447,7 @@
// @iteminfo
1276: Please enter an item name/ID (usage: @ii/@iteminfo <item name/ID>).
1277: Item: '%s'/'%s'[%d] (%u) Type: %s | Extra Effect: %s
1277: Item: '%s'/'%s' (%u) Type: %s | Extra Effect: %s
1278: None
1279: With script
1280: NPC Buy:%dz, Sell:%dz | Weight: %.1f
@ -1457,7 +1457,7 @@
// @whodrops
1284: Please enter item name/ID (usage: @whodrops <item name/ID>).
1285: Item: '%s'[%d] (ID: %u)
1285: Item: '%s' (ID: %u)
1286: - Item is not dropped by mobs.
1287: - Common mobs with highest drop chance (only max %d are listed):

View File

@ -1192,7 +1192,7 @@
// @iteminfo
1276: 請輸入 物品名稱/ID (用法: @ii/@iteminfo <物品名稱/ID>).
1277: 物品: '%s'/'%s'[%d] (%u) 類型: %s | 額外效果: %s
1277: 物品: '%s'/'%s' (%u) 類型: %s | 額外效果: %s
1278: None
1279: With script
1280: NPC 買價:%dz, 賣價:%dz | 重量: %.1f
@ -1202,7 +1202,7 @@
// @whodrops
1284: Please enter 物品名稱/ID (用法: @whodrops <物品名稱/ID>).
1285: Item: '%s'[%d] (ID:%d)
1285: Item: '%s' (ID:%d)
1286: - Item is not dropped by mobs.
1287: - Common mobs with highest drop chance (only max %d are listed):

View File

@ -1205,7 +1205,7 @@
// @iteminfo
1276: Entrez un nom/ID d'objet (usage: @ii/@iteminfo <nom/ID>).
1277: Item: '%s'/'%s'[%d] (%u) Type: %s | Extra Effect: %s
1277: Item: '%s'/'%s' (%u) Type: %s | Extra Effect: %s
1278: Aucun
1279: Avec script
1280: NPC Acheté:%dz, Vendu:%dz | Poids: %.1f
@ -1215,7 +1215,7 @@
// @whodrops
1284: Entrez un nom/ID d'Objet (usage: @whodrops <nom/ID_objet>).
1285: Objet: '%s'[%d] (ID:%d)
1285: Objet: '%s' (ID:%d)
1286: - Cet Objet n'est lâché par aucun monstre.
1287: - Monstres communs avec la plus grande chance de drop (seuls %d max sont listés):

View File

@ -1293,7 +1293,7 @@
// @iteminfo
1276: Harap masukkan nama/ID item. (Penggunaan: @ii/@iteminfo <nama/ID item>).
1277: Item: '%s'/'%s'[%d] (%u) Jenis: %s | Efek tambahan: %s
1277: Item: '%s'/'%s' (%u) Jenis: %s | Efek tambahan: %s
1278: Tidak ada
1279: Dengan script.
1280: NPC Harga Beli:%dz, Harga jual:%dz | Berat: %.1f
@ -1303,7 +1303,7 @@
// @whodrops
1284: Harap masukkan nama/ID item. (Penggunaan: @whodrops <nama/ID item>).
1285: Item: '%s'[%d] (ID:%d)
1285: Item: '%s' (ID:%d)
1286: - Item ini tidak dijatuhkan dari monster.
1287: - Monster biasa dengan kemungkinan menjatuhkan barang tertinggi. (Maks. hanya %d yang ditampilkan):

View File

@ -1375,7 +1375,7 @@
// @iteminfo
1276: Digite o nome/ID de um item (uso: @ii/@iteminfo <nome do item/ID>).
1277: Item: '%s'/'%s'[%d] (%u) Tipo: %s | Efeito Extra: %s
1277: Item: '%s'/'%s' (%u) Tipo: %s | Efeito Extra: %s
1278: Nenhum
1279: Com script
1280: NPC Compra:%dz, Venda:%dz | Peso: %.1f
@ -1385,7 +1385,7 @@
// @whodrops
1284: Digite o nome/ID de um item (uso: @whodrops <nome do item/ID>).
1285: Item: '%s'[%d] (ID:%d)
1285: Item: '%s' (ID:%d)
1286: - Item não derrubado por monstros.
1287: - Monstros comuns com maiores chances de drop (somente máx %d são listados):

View File

@ -1205,7 +1205,7 @@
// @iteminfo
1276: Введите ID/название предмета (Использование: @ii/@iteminfo <ID/название предмета>).
1277: Предмет: '%s'/'%s'[%d] (%u) Тип: %s | Доп. эффект: %s
1277: Предмет: '%s'/'%s' (%u) Тип: %s | Доп. эффект: %s
1278: Пусто
1279: Скрипт
1280: НИП покупка:%d зени, продажа:%d зени | Вес: %.1f
@ -1215,7 +1215,7 @@
// @whodrops
1284: Введите ID/название предмета (Использование: @whodrops <ID/название предмета>).
1285: Предмет: '%s'[%d] (ID:%d)
1285: Предмет: '%s' (ID:%d)
1286: - Предмет не падает с монстров.
1287: - Обычные монстры с высоким шансом выпадения (перечислено только %d):

View File

@ -1344,7 +1344,7 @@
// @iteminfo
1276: Introduce el nombre/ID de un objeto (instrucciones: @ii/@iteminfo <nombre/ID del objeto>).
1277: Objeto: '%s'/'%s'[%d] (%u) Tipo: %s | Efecto: %s
1277: Objeto: '%s'/'%s' (%u) Tipo: %s | Efecto: %s
1278: Ninguno
1279: Contiene código
1280: Compra en NPC:%dz, Venta:%dz | Peso: %.1f
@ -1354,7 +1354,7 @@
// @whodrops
1284: Introduce el nombre/ID de un objeto (instrucciones: @whodrops <nombre/ID del objeto>). 
1285: Objeto: '%s'[%d] (ID:%d)
1285: Objeto: '%s' (ID:%d)
1286: - Ningún monstruo tiene ese objeto.
1287: - Estos monstruos tienen ese objeto (sólo se muestran un máximo de %d):

View File

@ -1198,7 +1198,7 @@
// @iteminfo
1276: â»Ã´Ãкت×èÍ/ID item (ÇÔ¸Õãªé: @ii/@iteminfo <ª×èÍ/ID item>).
1277: Item: '%s'/'%s'[%d] (%u) Type: %s | Extra Effect: %s
1277: Item: '%s'/'%s' (%u) Type: %s | Extra Effect: %s
1278: None
1279: With script
1280: NPC Buy:%dz, Sell:%dz | Weight: %.1f
@ -1208,7 +1208,7 @@
// @whodrops
1284: â»Ã´Ãкت×èÍ/ID item (ÇÔ¸Õãªé: @whodrops <ª×èÍ/ID item>).
1285: Item: '%s'[%d] (ID:%d)
1285: Item: '%s' (ID:%d)
1286: - Item ¹ÕéäÁèÁÕµ¡¨Ò¡ monster.
1287: - Monster ·ÑèÇä»·ÕèÁÕâÍ¡Òʵ¡ÊÙ§ÊØ´ (áÊ´§à¾Õ§ %d ÃÒ¡ÒÃ):

View File

@ -4008,7 +4008,7 @@ ACMD_FUNC(idsearch)
for(const auto &result : item_array) {
std::shared_ptr<item_data> id = result.second;
sprintf(atcmd_output, msg_txt(sd,78), item_db.create_item_link( id->nameid ).c_str(), id->nameid); // %s: %u
sprintf(atcmd_output, msg_txt(sd,78), item_db.create_item_link( id ).c_str(), id->nameid); // %s: %u
clif_displaymessage(fd, atcmd_output);
}
sprintf(atcmd_output, msg_txt(sd,79), match); // It is %d affair above.
@ -6656,7 +6656,7 @@ ACMD_FUNC(autolootitem)
return -1;
}
sd->state.autolootid[i] = item_data->nameid; // Autoloot Activated
sprintf(atcmd_output, msg_txt(sd,1192), item_data->name.c_str(), item_db.create_item_link( item_data->nameid ).c_str(), item_data->nameid); // Autolooting item: '%s'/'%s' {%u}
sprintf(atcmd_output, msg_txt(sd,1192), item_data->name.c_str(), item_db.create_item_link( item_data ).c_str(), item_data->nameid); // Autolooting item: '%s'/'%s' {%u}
clif_displaymessage(fd, atcmd_output);
sd->state.autolooting = 1;
break;
@ -6667,7 +6667,7 @@ ACMD_FUNC(autolootitem)
return -1;
}
sd->state.autolootid[i] = 0;
sprintf(atcmd_output, msg_txt(sd,1194), item_data->name.c_str(), item_db.create_item_link( item_data->nameid ).c_str(), item_data->nameid); // Removed item: '%s'/'%s' {%u} from your autolootitem list.
sprintf(atcmd_output, msg_txt(sd,1194), item_data->name.c_str(), item_db.create_item_link( item_data ).c_str(), item_data->nameid); // Removed item: '%s'/'%s' {%u} from your autolootitem list.
clif_displaymessage(fd, atcmd_output);
ARR_FIND(0, AUTOLOOTITEM_SIZE, i, sd->state.autolootid[i] != 0);
if (i == AUTOLOOTITEM_SIZE) {
@ -6695,7 +6695,7 @@ ACMD_FUNC(autolootitem)
continue;
}
sprintf(atcmd_output, "'%s'/'%s' {%u}", item_data->name.c_str(), item_db.create_item_link( item_data->nameid ).c_str(), item_data->nameid);
sprintf(atcmd_output, "'%s'/'%s' {%u}", item_data->name.c_str(), item_db.create_item_link( item_data ).c_str(), item_data->nameid);
clif_displaymessage(fd, atcmd_output);
}
}
@ -7761,10 +7761,7 @@ ACMD_FUNC(mobinfo)
int droprate = mob_getdroprate( &sd->bl, mob, mob->dropitem[i].rate, drop_modifier );
if (id->slots)
sprintf(atcmd_output2, " - %s[%d] %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), id->slots, (float)droprate / 100);
else
sprintf(atcmd_output2, " - %s %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), (float)droprate / 100);
sprintf(atcmd_output2, " - %s %02.02f%%", item_db.create_item_link( id ).c_str(), (float)droprate / 100);
strcat(atcmd_output, atcmd_output2);
if (++j % 3 == 0) {
clif_displaymessage(fd, atcmd_output);
@ -7801,15 +7798,9 @@ ACMD_FUNC(mobinfo)
if (mvppercent > 0) {
j++;
if (j == 1) {
if (id->slots)
sprintf(atcmd_output2, " %s[%d] %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), id->slots, mvppercent);
else
sprintf(atcmd_output2, " %s %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), mvppercent);
sprintf(atcmd_output2, " %s %02.02f%%", item_db.create_item_link( id ).c_str(), mvppercent);
} else {
if (id->slots)
sprintf(atcmd_output2, " - %s[%d] %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), id->slots, mvppercent);
else
sprintf(atcmd_output2, " - %s %02.02f%%", item_db.create_item_link( id->nameid ).c_str(), mvppercent);
sprintf(atcmd_output2, " - %s %02.02f%%", item_db.create_item_link( id ).c_str(), mvppercent);
}
strcat(atcmd_output, atcmd_output2);
}
@ -8243,8 +8234,8 @@ ACMD_FUNC(iteminfo)
for (const auto &result : item_array) {
std::shared_ptr<item_data> item_data = result.second;
sprintf(atcmd_output, msg_txt(sd,1277), // Item: '%s'/'%s'[%d] (%u) Type: %s | Extra Effect: %s
item_data->name.c_str(), item_db.create_item_link( item_data->nameid ).c_str(),item_data->slots,item_data->nameid,
sprintf(atcmd_output, msg_txt(sd,1277), // Item: '%s'/'%s' (%u) Type: %s | Extra Effect: %s
item_data->name.c_str(), item_db.create_item_link( item_data ).c_str(),item_data->nameid,
(item_data->type != IT_AMMO) ? itemdb_typename((enum item_types)item_data->type) : itemdb_typename_ammo((e_ammo_type)item_data->subtype),
(item_data->script==NULL)? msg_txt(sd,1278) : msg_txt(sd,1279) // None / With script
);
@ -8301,7 +8292,7 @@ ACMD_FUNC(whodrops)
for (const auto &result : item_array) {
std::shared_ptr<item_data> id = result.second;
sprintf(atcmd_output, msg_txt(sd,1285), item_db.create_item_link( id->nameid ).c_str(), id->slots, id->nameid); // Item: '%s'[%d] (ID:%u)
sprintf(atcmd_output, msg_txt(sd,1285), item_db.create_item_link( id ).c_str(), id->nameid); // Item: '%s' (ID:%u)
clif_displaymessage(fd, atcmd_output);
if (id->mob[0].chance == 0) {
@ -9249,7 +9240,7 @@ ACMD_FUNC(itemlist)
{
int i, j, count, counter;
const char* location;
const struct item* items;
struct item* items;
int size;
StringBuf buf;
@ -9277,7 +9268,7 @@ ACMD_FUNC(itemlist)
count = 0; // total slots occupied
counter = 0; // total items found
for( i = 0; i < size; ++i ) {
const struct item* it = &items[i];
struct item* it = &items[i];
if( it->nameid == 0 )
continue;
@ -9296,10 +9287,7 @@ ACMD_FUNC(itemlist)
StringBuf_Clear(&buf);
}
if( it->refine )
StringBuf_Printf(&buf, "%d %s %+d (%s, id: %u)", it->amount, item_db.create_item_link( it->nameid ).c_str(), it->refine, itd->name.c_str(), it->nameid);
else
StringBuf_Printf(&buf, "%d %s (%s, id: %u)", it->amount, item_db.create_item_link( it->nameid ).c_str(), itd->name.c_str(), it->nameid);
StringBuf_Printf(&buf, "%d %s (%s, id: %u)", it->amount, item_db.create_item_link( *it ).c_str(), itd->name.c_str(), it->nameid);
if( it->equip ) {
char equipstr[CHAT_SIZE_MAX];
@ -9402,7 +9390,7 @@ ACMD_FUNC(itemlist)
if( counter2 != 1 )
StringBuf_AppendStr(&buf, ", ");
StringBuf_Printf(&buf, "#%d %s (id: %u)", counter2, item_db.create_item_link( card->nameid ).c_str(), card->nameid);
StringBuf_Printf(&buf, "#%d %s (id: %u)", counter2, item_db.create_item_link( card ).c_str(), card->nameid);
}
if( counter2 > 0 )

View File

@ -1250,90 +1250,111 @@ std::shared_ptr<item_data> ItemDatabase::searchname( const char *name ){
* @return <ITEML> string for the item
* @author [Cydh]
**/
std::string ItemDatabase::create_item_link( struct item& item ){
std::shared_ptr<item_data> data = this->find( item.nameid );
std::string ItemDatabase::create_item_link(struct item& item, std::shared_ptr<item_data>& data){
if( data == nullptr ){
ShowError( "Tried to create itemlink for unknown item %u.\n", item.nameid );
return "Unknown item";
}
// All these dates are unconfirmed
#if PACKETVER >= 20100000
if( !battle_config.feature_itemlink ){
// Feature is disabled
return data->ename;
}
std::string itemstr;
struct item_data* id = data.get();
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
const std::string start_tag = "<ITEML>";
const std::string closing_tag = "</ITEML>";
#else // PACKETVER >= 20100000
const std::string start_tag = "<ITEMLINK>";
const std::string closing_tag = "</ITEMLINK>";
// All these dates are unconfirmed
#if PACKETVER >= 20151104
if( battle_config.feature_itemlink ) {
#if PACKETVER >= 20160113
const std::string start_tag = "<ITEML>";
const std::string closing_tag = "</ITEML>";
#else // PACKETVER >= 20151104
const std::string start_tag = "<ITEM>";
const std::string closing_tag = "</ITEM>";
#endif
std::string itemstr = start_tag;
itemstr += start_tag;
itemstr += util::string_left_pad(util::base62_encode(id->equip), '0', 5);
itemstr += itemdb_isequip2(id) ? "1" : "0";
itemstr += util::base62_encode(item.nameid);
if (item.refine > 0) {
itemstr += "%" + util::string_left_pad(util::base62_encode(item.refine), '0', 2);
}
if (itemdb_isequip2(id)) {
itemstr += "&" + util::string_left_pad(util::base62_encode(id->look), '0', 2);
}
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
itemstr += "'" + util::string_left_pad(util::base62_encode(item.enchantgrade), '0', 2);
#endif
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
const std::string card_sep = ")";
const std::string optid_sep = "+";
const std::string optpar_sep = ",";
const std::string optval_sep = "-";
#else
const std::string card_sep = "(";
const std::string optid_sep = "*";
const std::string optpar_sep = "+";
const std::string optval_sep = ",";
#endif
for (uint8 i = 0; i < MAX_SLOTS; ++i) {
itemstr += card_sep + util::string_left_pad(util::base62_encode(item.card[i]), '0', 2);
}
#if PACKETVER >= 20150225
for (uint8 i = 0; i < MAX_ITEM_RDM_OPT; ++i) {
if (item.option[i].id == 0) {
break; // ignore options including ones beyond this one since the client won't even display them
itemstr += util::string_left_pad(util::base62_encode(id->equip), '0', 5);
itemstr += itemdb_isequip2(id) ? "1" : "0";
itemstr += util::base62_encode(item.nameid);
if (item.refine > 0) {
itemstr += "%" + util::string_left_pad(util::base62_encode(item.refine), '0', 2);
}
// Option ID
itemstr += optid_sep + util::string_left_pad(util::base62_encode(item.option[i].id), '0', 2);
// Param
itemstr += optpar_sep + util::string_left_pad(util::base62_encode(item.option[i].param), '0', 2);
// Value
itemstr += optval_sep + util::string_left_pad(util::base62_encode(item.option[i].value), '0', 2);
#if PACKETVER >= 20161116
if (itemdb_isequip2(id)) {
itemstr += "&" + util::string_left_pad(util::base62_encode(id->look), '0', 2);
}
#endif
#if PACKETVER >= 20200724
itemstr += "'" + util::string_left_pad(util::base62_encode(item.enchantgrade), '0', 2);
#endif
#if PACKETVER >= 20200724
const std::string card_sep = ")";
const std::string optid_sep = "+";
const std::string optpar_sep = ",";
const std::string optval_sep = "-";
#elif PACKETVER >= 20161116
const std::string card_sep = "(";
const std::string optid_sep = "*";
const std::string optpar_sep = "+";
const std::string optval_sep = ",";
#else // PACKETVER >= 20151104
const std::string card_sep = "'";
const std::string optid_sep = ")";
const std::string optpar_sep = "*";
const std::string optval_sep = "+";
#endif
for (uint8 i = 0; i < MAX_SLOTS; ++i) {
itemstr += card_sep + util::string_left_pad(util::base62_encode(item.card[i]), '0', 2);
}
for (uint8 i = 0; i < MAX_ITEM_RDM_OPT; ++i) {
if (item.option[i].id == 0) {
break; // ignore options including ones beyond this one since the client won't even display them
}
// Option ID
itemstr += optid_sep + util::string_left_pad(util::base62_encode(item.option[i].id), '0', 2);
// Param
itemstr += optpar_sep + util::string_left_pad(util::base62_encode(item.option[i].param), '0', 2);
// Value
itemstr += optval_sep + util::string_left_pad(util::base62_encode(item.option[i].value), '0', 2);
}
itemstr += closing_tag;
if ((itemdb_isequip2(id)) && (data->slots == 0))
itemstr += " [" + std::to_string(data->slots) + "]";
return itemstr;
}
#endif
itemstr += closing_tag;
// This can be reached either because itemlinks are disabled via configuration or because the packet version does not support the feature
// If that's the case then we format the item prepending the refine and appending the slots
if (item.refine > 0)
itemstr += "+" + std::to_string(item.refine) + " ";
itemstr += data->ename;
if (itemdb_isequip2(id))
itemstr += "[" + std::to_string(data->slots) + "]";
return itemstr;
#else
// Did not exist before that
return data->ename;
#endif
}
std::string ItemDatabase::create_item_link( t_itemid id ){
std::string ItemDatabase::create_item_link( std::shared_ptr<item_data>& data ){
struct item it = {};
it.nameid = data->nameid;
it.nameid = id;
return this->create_item_link( it, data );
}
return this->create_item_link( it );
std::string ItemDatabase::create_item_link(struct item& item) {
std::shared_ptr<item_data> data = this->find(item.nameid);
return this->create_item_link(item, data);
}
ItemDatabase item_db;

View File

@ -2149,8 +2149,11 @@ public:
// Additional
std::shared_ptr<item_data> searchname( const char* name );
std::shared_ptr<item_data> search_aegisname( const char *name );
std::string create_item_link( struct item& data );
std::string create_item_link( t_itemid id );
std::string create_item_link(struct item& item);
std::string create_item_link( std::shared_ptr<item_data>& data );
private:
std::string create_item_link(struct item& item, std::shared_ptr<item_data>& data);
};
extern ItemDatabase item_db;