Index: conf/groups.conf
===================================================================
--- conf/groups.conf (revision 17294)
+++ conf/groups.conf (working copy)
@@ -266,6 +266,7 @@
log_commands: true
permissions: {
can_trade: true
+ can_trade_bounded: true
can_party: true
all_skill: false
all_equipment: false
Index: conf/msg_conf/map_msg.conf
===================================================================
--- conf/msg_conf/map_msg.conf (revision 17294)
+++ conf/msg_conf/map_msg.conf (working copy)
@@ -453,6 +453,13 @@
463: Message configuration has been reloaded.
464: ---- Available languages:
+// Account-Bound Items
+497: You cannot distribute this item - it is an account bounded item!
+
+// @itembound / @itembound2
+498: Cannot create bounded pet eggs or pet armors.
+499: Cannot create bounded stackable items.
+
// Messages of others (not for GM commands)
// ----------------------------------------
@@ -712,11 +719,11 @@
981: Please enter color and message (usage: @kamic <color> <message>).
982: Invalid color.
-// @item
-983: Please enter an item name or ID (usage: @item <item name/ID> <quantity>).
+// @item / @itembound
+983: Please enter an item name or ID (usage: @%s <item name/ID> <quantity>).
-// @item2
-984: Please enter all parameters (usage: @item2 <item name/ID> <quantity>
+// @item2 / @itembound2
+984: Please enter all parameters (usage: @%s <item name/ID> <quantity>
985: <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4>).
// @baselevelup
Index: doc/atcommands.txt
===================================================================
--- doc/atcommands.txt (revision 17294)
+++ doc/atcommands.txt (working copy)
@@ -629,6 +629,21 @@
---------------------------------------
+@itembound <item name/ID> {<amount>}
+
+Creates the specified item and bounds it to the account.
+If an amount is given for @itembound, that number will be created.
+
+---------------------------------------
+
+@itembound2 <item name/ID> <quantity> <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4>
+
+Creates an item with the given parameters (the 'cards' can be any item) and bounds it to the account.
+identify_flag: 0 = unidentified, 1 = identified
+attribute: 0 = not broken, 1 = broken
+
+---------------------------------------
+
@produce <equip name/ID> <element> <# of Very's>
Creates a weapon with the given parameters.
Index: doc/permissions.txt
===================================================================
--- doc/permissions.txt (revision 17294)
+++ doc/permissions.txt (working copy)
@@ -31,5 +31,6 @@
disable_pvm : Ability to disable Player vs. Monster.
disable_pvp : Ability to disable Player vs. Player.
disable_commands_when_dead : Ability to disable @command usage when dead.
+can_trade_bounded : Ability to trade or otherwise distribute bounded items (drop, storage, vending etc...).
channel_admin : Ability to modify channel settings regardless of ownership and join password-protected
channels without a password.
\ No newline at end of file
Index: doc/script_commands.txt
===================================================================
--- doc/script_commands.txt (revision 17294)
+++ doc/script_commands.txt (working copy)
@@ -2687,6 +2687,7 @@
craftsman.
@inventorylist_expire[] - expire time (Unix time stamp). 0 means never expires.
@inventorylist_count - the number of items in these lists.
+@inventorylist_bound - whether it is an account bounded item or not.
This could be handy to save/restore a character's inventory, since no other
command returns such a complete set of data, and could also be the only way to
@@ -4226,6 +4227,42 @@
---------------------------------------
+*getitembound <item id>,<amount>{,<account ID>};
+*getitembound "<item name>",<amount>{,<account ID>};
+
+This command will give an amount of specified items to the invoking character.
+If an optional account ID is specified, and the target character is currently
+online, items will be created in their inventory instead. If they are not
+online, nothing will happen.
+
+It works essentially the same as 'getitem', except that items created using
+this command will bound the item to the player's account. They will not be able
+to trade, sell, drop, nor auction the item.
+
+---------------------------------------
+
+*getitembound2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};
+*getitembound2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};
+
+This command will give an amount of specified items to the invoking character.
+If an optional account ID is specified, and the target character is currently
+online, items will be created in their inventory instead. If they are not
+online, nothing will happen.
+
+It works essentially the same as 'getitem2', except that items created using
+this command will bound the item to the player's account. They will not be able
+to trade, sell, drop, nor auction the item.
+
+---------------------------------------
+
+*equipisbounded <equipment slots>;
+
+This command will check if the item in the specified equipment slot is bounded
+to the player's account or not. It will return 1 if bounded, 0 otherwise.
+For a list of equipment slots see 'getequipid'.
+
+---------------------------------------
+
*getnameditem <item id>,<character name|character ID>;
*getnameditem "<item name>",<character name|character ID>;
Index: sql-files/main.sql
===================================================================
--- sql-files/main.sql (revision 17294)
+++ sql-files/main.sql (working copy)
@@ -43,6 +43,7 @@
`card2` smallint(11) NOT NULL default '0',
`card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0',
+ `bound` tinyint(1) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `char_id` (`char_id`)
@@ -404,6 +405,7 @@
`card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0',
`favorite` tinyint(3) unsigned NOT NULL default '0',
+ `bound` tinyint(1) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `char_id` (`char_id`)
@@ -665,6 +667,7 @@
`card2` smallint(11) NOT NULL default '0',
`card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0',
+ `bound` tinyint(1) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `account_id` (`account_id`)
Index: sql-files/upgrades/upgrade_svn17099_itembound.sql
===================================================================
--- sql-files/upgrades/upgrade_svn17099_itembound.sql (revision 0)
+++ sql-files/upgrades/upgrade_svn17099_itembound.sql (working copy)
@@ -0,0 +1,3 @@
+ALTER TABLE `inventory` ADD COLUMN `bound` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `favorite`;
+ALTER TABLE `cart_inventory` ADD COLUMN `bound` tinyint(1) UNSIGNED NOT NULL default '0' AFTER `expire_time`;
+ALTER TABLE `storage` ADD COLUMN `bound` tinyint(1) UNSIGNED NOT NULL default '0' AFTER `expire_time`;
Index: src/char/char.c
===================================================================
--- src/char/char.c (revision 17294)
+++ src/char/char.c (working copy)
@@ -778,6 +778,8 @@
StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`");
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j);
+ if( tableswitch != TABLE_GUILD_STORAGE )
+ StringBuf_AppendStr(&buf, ", `bound`");
StringBuf_Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id);
stmt = SqlStmt_Malloc(sql_handle);
@@ -800,6 +802,8 @@
SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
for( j = 0; j < MAX_SLOTS; ++j )
SqlStmt_BindColumn(stmt, 8+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
+ if( tableswitch != TABLE_GUILD_STORAGE )
+ SqlStmt_BindColumn(stmt, 8+MAX_SLOTS, SQLDT_CHAR, &item.bound, 0, NULL, NULL);
// bit array indicating which inventory items have already been matched
flag = (bool*) aCalloc(max, sizeof(bool));
@@ -826,7 +830,8 @@
items[i].identify == item.identify &&
items[i].refine == item.refine &&
items[i].attribute == item.attribute &&
- items[i].expire_time == item.expire_time )
+ items[i].expire_time == item.expire_time &&
+ (tableswitch != TABLE_GUILD_STORAGE && items[i].bound == item.bound) )
; //Do nothing.
else
{
@@ -836,6 +841,8 @@
tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time);
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
+ if( tableswitch != TABLE_GUILD_STORAGE )
+ StringBuf_Printf(&buf, ", `bound`=%d", items[i].bound);
StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) )
@@ -864,6 +871,8 @@
StringBuf_Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`", tablename, selectoption);
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j);
+ if( tableswitch != TABLE_GUILD_STORAGE )
+ StringBuf_AppendStr(&buf, ", `bound`");
StringBuf_AppendStr(&buf, ") VALUES ");
found = false;
@@ -883,8 +892,10 @@
id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].unique_id);
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", '%d'", items[i].card[j]);
+ if( tableswitch != TABLE_GUILD_STORAGE )
+ StringBuf_Printf(&buf, ", '%d'", items[i].bound);
StringBuf_AppendStr(&buf, ")");
-
+
updateLastUid(items[i].unique_id); // Unique Non Stackable Item ID
}
dbUpdateUid(sql_handle); // Unique Non Stackable Item ID
@@ -919,7 +930,7 @@
// it significantly reduces cpu load on the database server.
StringBuf_Init(&buf);
- StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`");
+ StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`");
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'", inventory_db, id);
@@ -943,8 +954,9 @@
SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &item.favorite, 0, NULL, NULL);
+ SqlStmt_BindColumn(stmt, 9, SQLDT_CHAR, &item.bound, 0, NULL, NULL);
for( j = 0; j < MAX_SLOTS; ++j )
- SqlStmt_BindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
+ SqlStmt_BindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
// bit array indicating which inventory items have already been matched
flag = (bool*) aCalloc(max, sizeof(bool));
@@ -970,13 +982,14 @@
items[i].refine == item.refine &&
items[i].attribute == item.attribute &&
items[i].expire_time == item.expire_time &&
- items[i].favorite == item.favorite )
+ items[i].favorite == item.favorite &&
+ items[i].bound == item.bound )
; //Do nothing.
else {
// update all fields.
StringBuf_Clear(&buf);
- StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%d'",
- inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite);
+ StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%d', `bound`='%d'",
+ inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound);
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
@@ -1001,7 +1014,7 @@
SqlStmt_Free(stmt);
StringBuf_Clear(&buf);
- StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`", inventory_db);
+ StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`", inventory_db);
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_AppendStr(&buf, ") VALUES ");
@@ -1018,8 +1031,8 @@
else
found = true;
- StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'",
- id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].unique_id);
+ StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%d', '%"PRIu64"'",
+ id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound, items[i].unique_id);
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", '%d'", items[i].card[j]);
StringBuf_AppendStr(&buf, ")");
@@ -1270,7 +1283,7 @@
//read inventory
//`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`)
StringBuf_Init(&buf);
- StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`");
+ StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`");
for( i = 0; i < MAX_SLOTS; ++i )
StringBuf_Printf(&buf, ", `card%d`", i);
StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY);
@@ -1287,10 +1300,11 @@
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 10, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_SLOTS; ++i )
- if( SQL_ERROR == SqlStmt_BindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
+ if( SQL_ERROR == SqlStmt_BindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
@@ -1299,9 +1313,9 @@
strcat(t_msg, " inventory");
//read cart
- //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `unique_id`)
+ //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `bound`, `unique_id`)
StringBuf_Clear(&buf);
- StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`");
+ StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`");
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART);
@@ -1317,10 +1331,11 @@
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL)
- || SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL)
+ || SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_SLOTS; ++i )
- if( SQL_ERROR == SqlStmt_BindColumn(stmt, 9+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
+ if( SQL_ERROR == SqlStmt_BindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_CART && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
Index: src/char/int_mail.c
===================================================================
--- src/char/int_mail.c (revision 17294)
+++ src/char/int_mail.c (working copy)
@@ -63,6 +63,7 @@
Sql_GetData(sql_handle,14, &data, NULL); item->identify = atoi(data);
Sql_GetData(sql_handle,15, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
item->expire_time = 0;
+ item->bound = 0;
for (j = 0; j < MAX_SLOTS; j++)
{
@@ -183,6 +184,7 @@
Sql_GetData(sql_handle,14, &data, NULL); msg->item.identify = atoi(data);
Sql_GetData(sql_handle,15, &data, NULL); msg->item.unique_id = strtoull(data, NULL, 10);
msg->item.expire_time = 0;
+ msg->item.bound = 0;
for( j = 0; j < MAX_SLOTS; j++ )
{
Index: src/char/int_storage.c
===================================================================
--- src/char/int_storage.c (revision 17294)
+++ src/char/int_storage.c (working copy)
@@ -38,7 +38,7 @@
// storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`}
StringBuf_Init(&buf);
- StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`unique_id`");
+ StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`bound`,`unique_id`");
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ",`card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id);
@@ -59,10 +59,11 @@
Sql_GetData(sql_handle, 5, &data, NULL); item->refine = atoi(data);
Sql_GetData(sql_handle, 6, &data, NULL); item->attribute = atoi(data);
Sql_GetData(sql_handle, 7, &data, NULL); item->expire_time = (unsigned int)atoi(data);
- Sql_GetData(sql_handle, 8, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
+ Sql_GetData(sql_handle, 8, &data, NULL); item->bound = atoi(data);
+ Sql_GetData(sql_handle, 9, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
for( j = 0; j < MAX_SLOTS; ++j )
{
- Sql_GetData(sql_handle, 9+j, &data, NULL); item->card[j] = atoi(data);
+ Sql_GetData(sql_handle, 10+j, &data, NULL); item->card[j] = atoi(data);
}
}
p->storage_amount = i;
@@ -117,6 +118,7 @@
Sql_GetData(sql_handle, 6, &data, NULL); item->attribute = atoi(data);
Sql_GetData(sql_handle, 7, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
item->expire_time = 0;
+ item->bound = 0;
for( j = 0; j < MAX_SLOTS; ++j )
{
Sql_GetData(sql_handle, 8+j, &data, NULL); item->card[j] = atoi(data);
Index: src/common/mmo.h
===================================================================
--- src/common/mmo.h (revision 17294)
+++ src/common/mmo.h (working copy)
@@ -204,6 +204,7 @@
short card[MAX_SLOTS];
unsigned int expire_time;
char favorite;
+ char bound;
uint64 unique_id;
};
Index: src/map/atcommand.c
===================================================================
--- src/map/atcommand.c (revision 17294)
+++ src/map/atcommand.c (working copy)
@@ -1113,23 +1113,26 @@
/*==========================================
* @item command (usage: @item <name/id_of_item> <quantity>) (modified by [Yor] for pet_egg)
+ * @itembound command (usage: @itembound <name/id_of_item> <quantity>)
*------------------------------------------*/
ACMD_FUNC(item)
{
char item_name[100];
- int number = 0, item_id, flag = 0;
+ int number = 0, item_id, flag = 0, bound = 0;
struct item item_tmp;
struct item_data *item_data;
int get_count, i;
nullpo_retr(-1, sd);
memset(item_name, '\0', sizeof(item_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!message || !*message || (
sscanf(message, "\"%99[^\"]\" %d", item_name, &number) < 1 &&
sscanf(message, "%99s %d", item_name, &number) < 1
)) {
- clif_displaymessage(fd, msg_txt(sd,983)); // Please enter an item name or ID (usage: @item <item name/ID> <quantity>).
+ sprintf(atcmd_output, msg_txt(sd,983), command+1); // Please enter an item name or ID (usage: @%s <item name/ID> <quantity>).
+ clif_displaymessage(fd, atcmd_output);
return -1;
}
@@ -1142,12 +1145,23 @@
clif_displaymessage(fd, msg_txt(sd,19)); // Invalid item ID or name.
return -1;
}
+
+ if( !strcmpi(command+1,"itembound") )
+ bound = 1;
item_id = item_data->nameid;
get_count = number;
//Check if it's stackable.
- if (!itemdb_isstackable2(item_data))
+ if (!itemdb_isstackable2(item_data)) {
+ if( bound && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) {
+ clif_displaymessage(fd, msg_txt(sd,498)); // Cannot create bounded pet eggs or pet armors.
+ return -1;
+ }
get_count = 1;
+ } else if( bound ) {
+ clif_displaymessage(fd, msg_txt(sd,499)); // Cannot create bounded stackable items.
+ return -1;
+ }
for (i = 0; i < number; i += get_count) {
// if not pet egg
@@ -1155,6 +1169,7 @@
memset(&item_tmp, 0, sizeof(item_tmp));
item_tmp.nameid = item_id;
item_tmp.identify = 1;
+ item_tmp.bound = bound;
if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
clif_additem(sd, 0, 0, flag);
@@ -1167,25 +1182,27 @@
}
/*==========================================
- *
+ * @item2 and @itembound2 command
*------------------------------------------*/
ACMD_FUNC(item2)
{
struct item item_tmp;
struct item_data *item_data;
char item_name[100];
- int item_id, number = 0;
+ int item_id, number = 0, bound = 0;
int identify = 0, refine = 0, attr = 0;
int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
nullpo_retr(-1, sd);
memset(item_name, '\0', sizeof(item_name));
+ memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!message || !*message || (
sscanf(message, "\"%99[^\"]\" %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9 &&
sscanf(message, "%99s %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9
)) {
- clif_displaymessage(fd, msg_txt(sd,984)); // Please enter all parameters (usage: @item2 <item name/ID> <quantity>
+ sprintf(atcmd_output, msg_txt(sd,984), command+1); // Please enter all parameters (usage: @%s <item name/ID> <quantity>
+ clif_displaymessage(fd, atcmd_output);
clif_displaymessage(fd, msg_txt(sd,985)); // <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4>).
return -1;
}
@@ -1203,8 +1220,13 @@
int loop, get_count, i;
loop = 1;
get_count = number;
- if (item_data->type == IT_WEAPON || item_data->type == IT_ARMOR ||
- item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) {
+ if( !strcmpi(command+1,"itembound2") )
+ bound = 1;
+ if( !itemdb_isstackable2(item_data) ) {
+ if( bound && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) {
+ clif_displaymessage(fd, msg_txt(sd,498)); // Cannot create bounded pet eggs or pet armors.
+ return -1;
+ }
loop = number;
get_count = 1;
if (item_data->type == IT_PETEGG) {
@@ -1216,6 +1238,10 @@
if (refine > MAX_REFINE)
refine = MAX_REFINE;
} else {
+ if( bound ) {
+ clif_displaymessage(fd, msg_txt(sd,499)); // Cannot create bounded stackable items.
+ return -1;
+ }
identify = 1;
refine = attr = 0;
}
@@ -1229,6 +1255,7 @@
item_tmp.card[1] = c2;
item_tmp.card[2] = c3;
item_tmp.card[3] = c4;
+ item_tmp.bound = bound;
if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
clif_additem(sd, 0, 0, flag);
}
@@ -9241,6 +9268,8 @@
ACMD_DEF(unloadnpcfile),
ACMD_DEF(cart),
ACMD_DEF(mount2),
+ ACMD_DEF2("itembound", item),
+ ACMD_DEF2("itembound2", item2),
ACMD_DEF(join),
ACMD_DEF(channel),
ACMD_DEF(fontcolor),
Index: src/map/buyingstore.c
===================================================================
--- src/map/buyingstore.c (revision 17294)
+++ src/map/buyingstore.c (working copy)
@@ -312,7 +312,7 @@
return;
}
- if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) )
+ if( sd->status.inventory[index].expire_time || (sd->status.inventory[index].bound && !pc_can_give_bounded_items(sd)) || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) )
{// non-tradable item
clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
return;
Index: src/map/clif.c
===================================================================
--- src/map/clif.c (revision 17294)
+++ src/map/clif.c (working copy)
@@ -1788,8 +1788,8 @@
if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) )
continue;
- if( sd->status.inventory[i].expire_time )
- continue; // Cannot Sell Rental Items
+ if( sd->status.inventory[i].expire_time || (sd->status.inventory[i].bound && !pc_can_give_bounded_items(sd)) )
+ continue; // Cannot Sell Rental Items or Account Bounded Items
val=sd->inventory_data[i]->value_sell;
if( val < 0 )
@@ -2180,7 +2180,7 @@
WFIFOL(fd,23)=sd->status.inventory[n].expire_time;
#endif
#if PACKETVER >= 20071002
- WFIFOW(fd,27)=0; // unknown
+ WFIFOW(fd,27)=sd->status.inventory[n].bound ? 2 : 0;
#endif
}
@@ -2300,7 +2300,7 @@
clif_addcards(WBUFP(bufe, ne*se+16), &sd->status.inventory[i]);
#if PACKETVER >= 20071002
WBUFL(bufe,ne*se+24)=sd->status.inventory[i].expire_time;
- WBUFW(bufe,ne*se+28)=0; //Unknown
+ WBUFW(bufe,ne*se+28)=sd->status.inventory[i].bound ? 2 : 0;
#endif
#if PACKETVER >= 20100629
if (sd->inventory_data[i]->equip&EQP_VISIBLE)
@@ -2392,7 +2392,7 @@
clif_addcards(WBUFP(buf, n*cmd+16), &sd->status.inventory[i]);
#if PACKETVER >= 20071002
WBUFL(buf,n*cmd+24)=sd->status.inventory[i].expire_time;
- WBUFW(buf,n*cmd+28)=0; //Unknown
+ WBUFW(buf,n*cmd+28)=sd->status.inventory[i].bound ? 2 : 0;
#endif
#if PACKETVER >= 20100629
if (sd->inventory_data[i]->equip&EQP_VISIBLE)
@@ -2449,7 +2449,7 @@
clif_addcards(WBUFP(bufe, ne*cmd+16), &items[i]);
#if PACKETVER >= 20071002
WBUFL(bufe,ne*cmd+24)=items[i].expire_time;
- WBUFW(bufe,ne*cmd+28)=0; //Unknown
+ WBUFW(bufe,ne*cmd+28)=items[i].bound ? 2 : 0;
#endif
ne++;
}
@@ -2529,7 +2529,7 @@
clif_addcards(WBUFP(bufe, ne*cmd+16), &sd->status.cart[i]);
#if PACKETVER >= 20071002
WBUFL(bufe,ne*cmd+24)=sd->status.cart[i].expire_time;
- WBUFW(bufe,ne*cmd+28)=0; //Unknown
+ WBUFW(bufe,ne*cmd+28)=sd->status.cart[i].bound ? 2 : 0;
#endif
ne++;
}
@@ -8743,7 +8743,7 @@
clif_addcards(WBUFP(buf, n*s+55), &tsd->status.inventory[i]);
// Expiration date stuff, if all of those are set to 0 then the client doesn't show anything related (6 bytes)
WBUFL(buf, n*s+63) = tsd->status.inventory[i].expire_time;
- WBUFW(buf, n*s+67) = 0;
+ WBUFW(buf, n*s+67) = tsd->status.inventory[i].bound ? 2 : 0;
#if PACKETVER >= 20100629
if (tsd->inventory_data[i]->equip&EQP_VISIBLE)
WBUFW(buf, n*s+69) = tsd->inventory_data[i]->look;
@@ -14059,10 +14059,12 @@
clif_Auction_setitem(sd->fd, idx, true);
return;
}
-
- if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time ||
- !sd->status.inventory[idx].identify ||
- !itemdb_canauction(&sd->status.inventory[idx],pc_get_group_level(sd)) ) { // Quest Item or something else
+
+ if( !pc_can_give_items(sd) ||
+ sd->status.inventory[idx].expire_time ||
+ !sd->status.inventory[idx].identify ||
+ !itemdb_canauction(&sd->status.inventory[idx],pc_get_group_level(sd)) ||
+ (sd->status.inventory[idx].bound && !pc_can_give_bounded_items(sd)) ) { // Quest Item or something else
clif_Auction_setitem(sd->fd, idx, true);
return;
}
Index: src/map/mail.c
===================================================================
--- src/map/mail.c (revision 17294)
+++ src/map/mail.c (working copy)
@@ -78,8 +78,10 @@
return 1;
if( amount < 0 || amount > sd->status.inventory[idx].amount )
return 1;
- if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time ||
- !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) )
+ if( !pc_can_give_items(sd) ||
+ sd->status.inventory[idx].expire_time ||
+ !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) ||
+ (sd->status.inventory[idx].bound && !pc_can_give_bounded_items(sd)) )
return 1;
sd->mail.index = idx;
Index: src/map/pc.c
===================================================================
--- src/map/pc.c (revision 17294)
+++ src/map/pc.c (working copy)
@@ -509,6 +509,14 @@
return pc_has_permission(sd, PC_PERM_TRADE);
}
+/**
+ * Determines if player can give / drop / trade / vend bounded items
+ */
+bool pc_can_give_bounded_items(struct map_session_data *sd)
+{
+ return pc_has_permission(sd, PC_PERM_TRADE_BOUNDED);
+}
+
/*==========================================
* prepares character for saving.
*------------------------------------------*/
@@ -3883,7 +3891,7 @@
{ // Stackable | Non Rental
for( i = 0; i < MAX_INVENTORY; i++ )
{
- if( sd->status.inventory[i].nameid == item_data->nameid && memcmp(&sd->status.inventory[i].card, &item_data->card, sizeof(item_data->card)) == 0 )
+ if( sd->status.inventory[i].nameid == item_data->nameid && sd->status.inventory[i].bound == item_data->bound && memcmp(&sd->status.inventory[i].card, &item_data->card, sizeof(item_data->card)) == 0 )
{
if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->status.inventory[i].amount ) )
return 5;
@@ -4430,6 +4438,7 @@
{
ARR_FIND( 0, MAX_CART, i,
sd->status.cart[i].nameid == item_data->nameid &&
+ sd->status.cart[i].bound == item_data->bound &&
sd->status.cart[i].card[0] == item_data->card[0] && sd->status.cart[i].card[1] == item_data->card[1] &&
sd->status.cart[i].card[2] == item_data->card[2] && sd->status.cart[i].card[3] == item_data->card[3] );
};
@@ -7865,7 +7874,7 @@
*------------------------------------------*/
int pc_candrop(struct map_session_data *sd, struct item *item)
{
- if( item && item->expire_time )
+ if( item && (item->expire_time || (item->bound && !pc_can_give_bounded_items(sd))) )
return 0;
if( !pc_can_give_items(sd) ) //check if this GM level can drop items
return 0;
Index: src/map/pc.h
===================================================================
--- src/map/pc.h (revision 17294)
+++ src/map/pc.h (working copy)
@@ -708,6 +708,7 @@
int pc_get_group_id(struct map_session_data *sd);
int pc_getrefinebonus(int lv,int type);
bool pc_can_give_items(struct map_session_data *sd);
+bool pc_can_give_bounded_items(struct map_session_data *sd);
bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type);
#define pc_has_permission(sd, permission) ( ((sd)->permissions&permission) != 0 )
Index: src/map/pc_groups.h
===================================================================
--- src/map/pc_groups.h (revision 17294)
+++ src/map/pc_groups.h (working copy)
@@ -44,6 +44,7 @@
PC_PERM_DISABLE_PVP = 0x080000,
PC_PERM_DISABLE_CMD_DEAD = 0x100000,
PC_PERM_CHANNEL_ADMIN = 0x200000,
+ PC_PERM_TRADE_BOUNDED = 0x400000,
};
static const struct {
Index: src/map/script.c
===================================================================
--- src/map/script.c (revision 17294)
+++ src/map/script.c (working copy)
@@ -6554,6 +6554,7 @@
it.nameid = nameid;
it.identify = 1;
it.expire_time = (unsigned int)(time(NULL) + seconds);
+ it.bound = 0;
if( (flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT)) )
{
@@ -11475,6 +11476,7 @@
item_tmp.refine = sd->status.inventory[i].refine;
item_tmp.attribute = sd->status.inventory[i].attribute;
item_tmp.expire_time = sd->status.inventory[i].expire_time;
+ item_tmp.bound = sd->status.inventory[i].bound;
for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
item_tmp.card[j]=sd->status.inventory[i].card[j];
@@ -11548,6 +11550,7 @@
item_tmp.refine = sd->status.inventory[i].refine;
item_tmp.attribute = sd->status.inventory[i].attribute;
item_tmp.expire_time = sd->status.inventory[i].expire_time;
+ item_tmp.bound = sd->status.inventory[i].bound;
for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
item_tmp.card[j]=sd->status.inventory[i].card[j];
@@ -12211,6 +12214,7 @@
pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]);
}
pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_bound"), j),sd->status.inventory[i].bound);
j++;
}
}
@@ -17575,6 +17579,172 @@
return 0;
}
+/*==========================================
+ * getitembound <item id>,<amount>{,<account ID>};
+ * getitembound "<item name>",<amount>{,<account ID>};
+ *------------------------------------------*/
+BUILDIN_FUNC(getitembound)
+{
+ int nameid, amount, i, flag;
+ struct item it;
+ struct script_data *data;
+ TBL_PC *sd;
+
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ) { // "<item name>"
+ const char *name = conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data == NULL ) {
+ ShowError("buildin_getitembound: Nonexistant item %s requested.\n", name);
+ return 1; //No item created.
+ }
+ nameid = item_data->nameid;
+ } else if( data_isint(data) ) { // <item id>
+ nameid = conv_num(st,data);
+ if( nameid <= 0 || !itemdb_exists(nameid) ) {
+ ShowError("buildin_getitembound: Nonexistant item %d requested.\n", nameid);
+ return 1; //No item created.
+ }
+ } else {
+ ShowError("buildin_getitembound: invalid data type for argument #1 (%d).", data->type);
+ return 1;
+ }
+
+ if( itemdb_isstackable(nameid) || itemdb_type(nameid) == IT_PETEGG ) {
+ ShowError("buildin_getitembound: invalid item type. Bound only work for non stackeable items (Item %d).", nameid);
+ return 1;
+ }
+
+ if( (amount = script_getnum(st,3)) <= 0)
+ return 0; //return if amount <=0, skip the useles iteration
+
+ memset(&it,0,sizeof(it));
+ it.nameid = nameid;
+ it.bound = 1;
+ it.identify = 1;
+
+ if( script_hasdata(st,4) )
+ sd = map_id2sd(script_getnum(st,4)); // Account ID
+ else
+ sd = script_rid2sd(st); // Attached player
+
+ if( sd == NULL ) // no target
+ return 0;
+
+ for( i = 0; i < amount; i++ ) {
+ if( (flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT)) ) {
+ clif_additem(sd, 0, 0, flag);
+ if( pc_candrop(sd,&it) )
+ map_addflooritem(&it,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * getitembound2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};
+ * getitembound2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};
+ *------------------------------------------*/
+BUILDIN_FUNC(getitembound2)
+{
+ int nameid, amount, i, flag;
+ int iden, ref, attr, c1, c2, c3, c4;
+ struct item_data *item_data;
+ struct item item_tmp;
+ struct script_data *data;
+ TBL_PC *sd;
+
+ if( script_hasdata(st,11) )
+ sd = map_id2sd(script_getnum(st,11)); // <Account ID>
+ else
+ sd = script_rid2sd(st); // Attached player
+
+ if( sd == NULL ) // no target
+ return 0;
+
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ) {
+ const char *name = conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ nameid = item_data->nameid;
+ else
+ nameid = UNKNOWN_ITEM_ID;
+ } else
+ nameid = conv_num(st,data);
+
+ amount = script_getnum(st,3);
+ iden = script_getnum(st,4);
+ ref = script_getnum(st,5);
+ attr = script_getnum(st,6);
+ c1 = (short)script_getnum(st,7);
+ c2 = (short)script_getnum(st,8);
+ c3 = (short)script_getnum(st,9);
+ c4 = (short)script_getnum(st,10);
+
+ if( nameid < 0 || (item_data = itemdb_exists(nameid)) == NULL || itemdb_isstackable2(item_data) )
+ return 0;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ if( item_data->type == IT_WEAPON || item_data->type == IT_ARMOR )
+ ref = cap_value(ref,0,MAX_REFINE);
+ else if( item_data->type == IT_PETEGG ) {
+ ShowError("getitembound2: invalid item type. Pet Egg cannot be set as rental items.\n");
+ return 1;
+ } else { // Should not happen
+ iden = 1;
+ ref = attr = 0;
+ }
+
+ item_tmp.nameid = nameid;
+ item_tmp.identify = iden;
+ item_tmp.refine = ref;
+ item_tmp.attribute = attr;
+ item_tmp.card[0] = (short)c1;
+ item_tmp.card[1] = (short)c2;
+ item_tmp.card[2] = (short)c3;
+ item_tmp.card[3] = (short)c4;
+ item_tmp.bound = 1;
+
+ for( i = 0; i < amount; i++ ) {
+ if( (flag = pc_additem(sd, &item_tmp, 1, LOG_TYPE_SCRIPT)) ) {
+ clif_additem(sd, 0, 0, flag);
+ if( pc_candrop(sd,&item_tmp) )
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * equipisbounded <equip location>;
+ *------------------------------------------*/
+BUILDIN_FUNC(equipisbounded)
+{
+ int i = -1, num;
+ TBL_PC *sd;
+
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+
+ num = script_getnum(st,2);
+ if( num > 0 && num <= ARRAYLENGTH(equip) )
+ i = pc_checkequip(sd,equip[num-1]);
+
+ if( i >= 0 && sd->status.inventory[i].bound )
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+
// declarations that were supposed to be exported from npc_chat.c
#ifdef PCRE_SUPPORT
BUILDIN_FUNC(defpattern);
@@ -18031,6 +18201,12 @@
BUILDIN_DEF(bindatcmd, "ss??"),
BUILDIN_DEF(unbindatcmd, "s"),
BUILDIN_DEF(useatcmd, "s"),
+ /**
+ * Item bound
+ **/
+ BUILDIN_DEF(getitembound,"vi?"),
+ BUILDIN_DEF(getitembound2,"viiiiiiii?"),
+ BUILDIN_DEF(equipisbounded,"i"),
//Quest Log System [Inkfish]
BUILDIN_DEF(setquest, "i"),
Index: src/map/storage.c
===================================================================
--- src/map/storage.c (revision 17294)
+++ src/map/storage.c (working copy)
@@ -120,7 +120,8 @@
a->identify == b->identify &&
a->refine == b->refine &&
a->attribute == b->attribute &&
- a->expire_time == b->expire_time )
+ a->expire_time == b->expire_time &&
+ a->bound == b->bound )
{
int i;
for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);
@@ -442,7 +443,7 @@
return 1;
}
- if( !itemdb_canguildstore(item_data, pc_get_group_level(sd)) || item_data->expire_time )
+ if( !itemdb_canguildstore(item_data, pc_get_group_level(sd)) || item_data->expire_time || (item_data->bound && !pc_can_give_bounded_items(sd)) )
{ //Check if item is storable. [Skotlex]
clif_displaymessage (sd->fd, msg_txt(sd,264));
return 1;
Index: src/map/trade.c
===================================================================
--- src/map/trade.c (revision 17294)
+++ src/map/trade.c (working copy)
@@ -368,6 +368,12 @@
return;
}
+ if( item->bound && !pc_can_give_bounded_items(sd) ) { // Account Bound
+ clif_displaymessage(sd->fd, msg_txt(sd, 497)); // Can't distribute an account bounded item
+ clif_tradeitemok(sd, index+2, 1);
+ return;
+ }
+
//Locate a trade position
ARR_FIND( 0, 10, trade_i, sd->deal.item[trade_i].index == index || sd->deal.item[trade_i].amount == 0 );
if( trade_i == 10 ) //No space left
Index: src/map/vending.c
===================================================================
--- src/map/vending.c (revision 17294)
+++ src/map/vending.c (working copy)
@@ -275,6 +275,7 @@
|| !sd->status.cart[index].identify // unidentified item
|| sd->status.cart[index].attribute == 1 // broken item
|| sd->status.cart[index].expire_time // It should not be in the cart but just in case
+ || (sd->status.cart[index].bound && !pc_can_give_bounded_items(sd)) // can't trade account bound items and has no permission
|| !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item
continue;