From e15015be4a7aa925e16b7418302e408bce3dc0fb Mon Sep 17 00:00:00 2001 From: Mhalicot Date: Fri, 18 Oct 2013 14:05:01 +0700 Subject: [PATCH] Item Bind ~ Original: by Xantara ~ Revised by Mhalicot ~ Topic: http://hercules.ws/board/topic/1198-please-implement-accound-bound-items/ Signed-off-by: Mhalicot --- conf/groups.conf | 1 + conf/messages.conf | 17 ++- doc/atcommands.txt | 15 +++ doc/permissions.txt | 1 + doc/script_commands.txt | 37 ++++++ sql-files/main.sql | 6 +- sql-files/upgrades/accountbound.sql | 3 + src/char/char.c | 51 +++++--- src/char/int_mail.c | 2 + src/char/int_storage.c | 8 +- src/common/mmo.h | 5 +- src/map/atcommand.c | 49 ++++++-- src/map/buyingstore.c | 2 +- src/map/clif.c | 21 ++-- src/map/mail.c | 3 +- src/map/pc.c | 14 ++- src/map/pc.h | 1 + src/map/pc_groups.c | 1 + src/map/pc_groups.h | 3 +- src/map/script.c | 176 ++++++++++++++++++++++++++++ src/map/storage.c | 5 +- src/map/trade.c | 6 + src/map/vending.c | 1 + 23 files changed, 372 insertions(+), 56 deletions(-) create mode 100644 sql-files/upgrades/accountbound.sql diff --git a/conf/groups.conf b/conf/groups.conf index 7c97352dcda..9a4c5712641 100644 --- a/conf/groups.conf +++ b/conf/groups.conf @@ -260,6 +260,7 @@ groups: ( log_commands: true permissions: { can_trade: true + can_trade_bounded: false can_party: true all_skill: false all_equipment: false diff --git a/conf/messages.conf b/conf/messages.conf index 3a16d8054a9..0493ad59998 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -455,6 +455,13 @@ // Messages of others (not for GM commands) // ---------------------------------------- +// 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. + //500: FREE 501: Your account time limit is: %d-%m-%Y %H:%M:%S. 502: Day Mode is activated @@ -711,11 +718,13 @@ 981: Please enter color and message (usage: @kamic ). 982: Invalid color. -// @item -983: Please enter an item name or ID (usage: @item ). -// @item2 -984: Please enter all parameters (usage: @item2 +// @item / @itembound +983: Please enter an item name or ID (usage: @%s ). + + +// @item2 / @itembound2 +984: Please enter all parameters (usage: @%s ). 985: ). // @baselevelup diff --git a/doc/atcommands.txt b/doc/atcommands.txt index 11e7cd6bd92..db6584ab215 100644 --- a/doc/atcommands.txt +++ b/doc/atcommands.txt @@ -633,6 +633,21 @@ attribute: 0 = not broken, 1 = broken --------------------------------------- +@itembound {} + +Creates the specified item and bounds it to the account. +If an amount is given for @itembound, that number will be created. + +--------------------------------------- + +@itembound2 + +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 <# of Very's> Creates a weapon with the given parameters. diff --git a/doc/permissions.txt b/doc/permissions.txt index 9760f716c3e..e61be2699cb 100644 --- a/doc/permissions.txt +++ b/doc/permissions.txt @@ -31,4 +31,5 @@ show_bossmobs : Ability to see boss mobs with @showmobs. 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...). hchsys_admin : Hercules Chat System Admin (Ability to modify channel settings regardless of ownership and join password-protected channels without requiring a password.) diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 661e84beeb8..2400151276b 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -2781,6 +2781,7 @@ recreate these items perfectly if they are destroyed. Here's what you get: @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 @@ -4450,6 +4451,42 @@ mess will this really cause. --------------------------------------- +*getitembound ,{,}; +*getitembound "",{,}; + +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 ,,,,,,,,{,}; +*getitembound2 "",,,,,,,,{,}; + +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 ; + +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 ,; *getnameditem "",; diff --git a/sql-files/main.sql b/sql-files/main.sql index 1f654ed1472..cf7521f683e 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -43,6 +43,7 @@ CREATE TABLE IF NOT EXISTS `cart_inventory` ( `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`) @@ -341,6 +342,7 @@ CREATE TABLE IF NOT EXISTS `guild_storage` ( `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 `guild_id` (`guild_id`) @@ -405,6 +407,7 @@ CREATE TABLE IF NOT EXISTS `inventory` ( `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`) @@ -688,6 +691,7 @@ CREATE TABLE IF NOT EXISTS `storage` ( `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`) @@ -713,5 +717,5 @@ CREATE TABLE IF NOT EXISTS `account_data` ( `account_id` int(11) unsigned NOT NULL default '0', `bank_vault` int(11) unsigned NOT NULL default '0', PRIMARY KEY (`account_id`) -) ENGINE=MyISAM; +) ENGINE=MyISAM; diff --git a/sql-files/upgrades/accountbound.sql b/sql-files/upgrades/accountbound.sql new file mode 100644 index 00000000000..eec7a1693d5 --- /dev/null +++ b/sql-files/upgrades/accountbound.sql @@ -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`; \ No newline at end of file diff --git a/src/char/char.c b/src/char/char.c index 574e4f66bec..2a412114845 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -727,6 +727,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`"); for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", `card%d`", j); + if( tableswitch != TABLE_GUILD_STORAGE ) + StrBuf->AppendStr(&buf, ", `bound`"); StrBuf->Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id); stmt = SQL->StmtMalloc(sql_handle); @@ -749,6 +751,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); for( j = 0; j < MAX_SLOTS; ++j ) SQL->StmtBindColumn(stmt, 8+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); + if( tableswitch != TABLE_GUILD_STORAGE ) + SQL->StmtBindColumn(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)); @@ -775,7 +779,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit 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 { @@ -784,7 +789,9 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u'", 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 ) - StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]); + StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]); + if( tableswitch != TABLE_GUILD_STORAGE ) + StrBuf->Printf(&buf, ", `bound`=%d", items[i].bound); StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id); if( SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) ) @@ -813,6 +820,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit StrBuf->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 ) StrBuf->Printf(&buf, ", `card%d`", j); + if( tableswitch != TABLE_GUILD_STORAGE ) + StrBuf->AppendStr(&buf, ", `bound`"); StrBuf->AppendStr(&buf, ") VALUES "); found = false; @@ -832,6 +841,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit 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 ) StrBuf->Printf(&buf, ", '%d'", items[i].card[j]); + if( tableswitch != TABLE_GUILD_STORAGE ) + StrBuf->Printf(&buf, ", '%d'", items[i].bound); StrBuf->AppendStr(&buf, ")"); updateLastUid(items[i].unique_id); // Unique Non Stackable Item ID @@ -868,7 +879,7 @@ int inventory_to_sql(const struct item items[], int max, int id) { // it significantly reduces cpu load on the database server. StrBuf->Init(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`"); + StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`"); for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`='%d'", inventory_db, id); @@ -892,8 +903,9 @@ int inventory_to_sql(const struct item items[], int max, int id) { SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &item.favorite, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 9, SQLDT_CHAR, &item.bound, 0, NULL, NULL); for( j = 0; j < MAX_SLOTS; ++j ) - SQL->StmtBindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); + SQL->StmtBindColumn(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)); @@ -919,14 +931,15 @@ int inventory_to_sql(const struct item items[], int max, int id) { 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. StrBuf->Clear(&buf); - StrBuf->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); - for( j = 0; j < MAX_SLOTS; ++j ) + StrBuf->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 ) StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]); StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id); @@ -950,7 +963,7 @@ int inventory_to_sql(const struct item items[], int max, int id) { SQL->StmtFree(stmt); StrBuf->Clear(&buf); - StrBuf->Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`", inventory_db); + StrBuf->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 ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->AppendStr(&buf, ") VALUES "); @@ -967,8 +980,8 @@ int inventory_to_sql(const struct item items[], int max, int id) { else found = true; - StrBuf->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); + StrBuf->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 ) StrBuf->Printf(&buf, ", '%d'", items[i].card[j]); StrBuf->AppendStr(&buf, ")"); @@ -1230,7 +1243,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything //read inventory //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`) StrBuf->Init(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`"); + StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`"); for( i = 0; i < MAX_SLOTS; ++i ) StrBuf->Printf(&buf, ", `card%d`", i); StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY); @@ -1247,10 +1260,11 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_SLOTS; ++i ) - if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) + if( SQL_ERROR == SQL->StmtBindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) @@ -1259,9 +1273,9 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything 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`) StrBuf->Clear(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`"); + StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`"); for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART); @@ -1277,10 +1291,11 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_SLOTS; ++i ) - if( SQL_ERROR == SQL->StmtBindColumn(stmt, 9+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) + if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_CART && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i ) diff --git a/src/char/int_mail.c b/src/char/int_mail.c index e4b88b5bfff..b69824d4b50 100644 --- a/src/char/int_mail.c +++ b/src/char/int_mail.c @@ -64,6 +64,7 @@ static int mail_fromsql(int char_id, struct mail_data* md) 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++) { @@ -184,6 +185,7 @@ static bool mail_loadmessage(int mail_id, struct mail_message* msg) 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++ ) { diff --git a/src/char/int_storage.c b/src/char/int_storage.c index 429b80105fd..a3b1c035549 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -39,7 +39,7 @@ int storage_fromsql(int account_id, struct storage_data* p) // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} StrBuf->Init(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`unique_id`"); + StrBuf->AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`bound`,`unique_id`"); for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ",`card%d`", j); StrBuf->Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id); @@ -60,10 +60,11 @@ int storage_fromsql(int account_id, struct storage_data* p) 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; @@ -118,6 +119,7 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p) 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); diff --git a/src/common/mmo.h b/src/common/mmo.h index ccba0fa7f6d..b2f3d570f4c 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -48,11 +48,11 @@ // 20120307 - 2012-03-07aRagexeRE+ - 0x970 #ifndef PACKETVER - #define PACKETVER 20120418 + #define PACKETVER 20130807 #endif // Comment the following line if your client is NOT ragexeRE (required because of conflicting packets in ragexe vs ragexeRE). -#define PACKETVER_RE +//#define PACKETVER_RE // Client support for experimental RagexeRE UI present in 2012-04-10 and 2012-04-18 #ifdef PACKETVER_RE @@ -223,6 +223,7 @@ struct item { short card[MAX_SLOTS]; unsigned int expire_time; char favorite; + char bound; uint64 unique_id; }; diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 8c560a7da07..aa93b810beb 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -1108,23 +1108,25 @@ ACMD(heal) /*========================================== * @item command (usage: @item ) (modified by [Yor] for pet_egg) + * @itembound command (usage: @itembound ) *------------------------------------------*/ ACMD(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->message(fd, msg_txt(983)); // Please enter an item name or ID (usage: @item ). + sprintf(atcmd_output, msg_txt(983), command+1); // Please enter an item name or ID (usage: @%s ). + clif->message(fd, atcmd_output); return false; } @@ -1137,12 +1139,23 @@ ACMD(item) clif->message(fd, msg_txt(19)); // Invalid item ID or name. return false; } - + + 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->message(fd, msg_txt(498)); // Cannot create bounded pet eggs or pet armors. + return false; + } get_count = 1; + } else if( bound ) { + clif->message(fd, msg_txt(499)); // Cannot create bounded stackable items. + return false; + } for (i = 0; i < number; i += get_count) { // if not pet egg @@ -1150,6 +1163,7 @@ ACMD(item) 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); @@ -1162,25 +1176,26 @@ ACMD(item) } /*========================================== - * + * @item2 and @itembound2 command *------------------------------------------*/ ACMD(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->message(fd, msg_txt(984)); // Please enter all parameters (usage: @item2 + sprintf(atcmd_output, msg_txt(984), command+1); // Please enter all parameters (usage: @%s + clif->message(fd, atcmd_output); clif->message(fd, msg_txt(985)); // ). return false; } @@ -1198,8 +1213,13 @@ ACMD(item2) 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->message(fd, msg_txt(498)); // Cannot create bounded pet eggs or pet armors. + return false; + } loop = number; get_count = 1; if (item_data->type == IT_PETEGG) { @@ -1211,6 +1231,10 @@ ACMD(item2) if (refine > MAX_REFINE) refine = MAX_REFINE; } else { + if( bound ) { + clif->message(fd, msg_txt(499)); // Cannot create bounded stackable items. + return false; + } identify = 1; refine = attr = 0; } @@ -1224,6 +1248,7 @@ ACMD(item2) 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); } @@ -9695,6 +9720,8 @@ void atcommand_basecommands(void) { 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), diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index 2a9e6a88e06..24d30ed7dae 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -290,7 +290,7 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int 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; diff --git a/src/map/clif.c b/src/map/clif.c index 0605098076f..ace46a19e9d 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1781,8 +1781,8 @@ void clif_selllist(struct map_session_data *sd) 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 ) @@ -2208,7 +2208,7 @@ void clif_additem(struct map_session_data *sd, int n, int amount, int fail) { p.HireExpireDate = sd->status.inventory[n].expire_time; #endif #if PACKETVER >= 20071002 - p.bindOnEquipType = 0; // unused + p.bindOnEquipType = sd->status.inventory[n].bound ? 2 : 0; #endif } p.result = (unsigned char)fail; @@ -2326,7 +2326,7 @@ void clif_inventorylist(struct map_session_data *sd) { 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) @@ -2416,7 +2416,7 @@ void clif_equiplist(struct map_session_data *sd) 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) @@ -2473,7 +2473,7 @@ void clif_storagelist(struct map_session_data* sd, struct item* items, int items 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++; } @@ -2553,7 +2553,7 @@ void clif_cartlist(struct map_session_data *sd) 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++; } @@ -9002,7 +9002,7 @@ void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* ts 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; @@ -15105,10 +15105,11 @@ void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) 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 + !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; } diff --git a/src/map/mail.c b/src/map/mail.c index 2378cbe2afe..b4f1452b8a6 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -82,7 +82,8 @@ unsigned char mail_setitem(struct map_session_data *sd, int idx, int amount) { 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)) ) + !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; diff --git a/src/map/pc.c b/src/map/pc.c index 157d9e28a36..6829a42737a 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -506,6 +506,14 @@ bool pc_can_give_items(struct map_session_data *sd) 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. *------------------------------------------*/ @@ -3886,7 +3894,7 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l { // 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; @@ -4453,6 +4461,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun { 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] ); }; @@ -7896,7 +7905,7 @@ int pc_setmadogear(TBL_PC* sd, int flag) *------------------------------------------*/ 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; @@ -10221,6 +10230,7 @@ void pc_defaults(void) { pc->class2idx = pc_class2idx; pc->get_group_level = pc_get_group_level; pc->can_give_items = pc_can_give_items; + pc->can_give_bounded_items = pc_can_give_bounded_items; pc->can_use_command = pc_can_use_command; pc->has_permission = pc_has_permission; diff --git a/src/map/pc.h b/src/map/pc.h index f770818c286..bf62828e6db 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -736,6 +736,7 @@ struct pc_interface { int (*get_group_level) (struct map_session_data *sd); //int (*getrefinebonus) (int lv,int type); FIXME: This function does not exist, nor it is ever called bool (*can_give_items) (struct map_session_data *sd); + bool (*can_give_bounded_items) (struct map_session_data *sd); bool (*can_use_command) (struct map_session_data *sd, const char *command); bool (*has_permission) (struct map_session_data *sd, enum e_pc_permission permission); diff --git a/src/map/pc_groups.c b/src/map/pc_groups.c index be02b5f159d..41bc19cbac0 100644 --- a/src/map/pc_groups.c +++ b/src/map/pc_groups.c @@ -55,6 +55,7 @@ const struct pc_permission_name_table pc_g_permission_name[NUM_PC_PERM] = { { "disable_pvp", PC_PERM_DISABLE_PVP }, { "disable_commands_when_dead", PC_PERM_DISABLE_CMD_DEAD }, { "hchsys_admin", PC_PERM_HCHSYS_ADMIN }, + { "can_trade_bounded", PC_PERM_TRADE_BOUNDED }, }; static DBMap* pc_group_db; // id -> GroupSettings diff --git a/src/map/pc_groups.h b/src/map/pc_groups.h index 8f350c2b6f5..63e7acc51af 100644 --- a/src/map/pc_groups.h +++ b/src/map/pc_groups.h @@ -30,6 +30,7 @@ enum e_pc_permission { PC_PERM_DISABLE_PVP = 0x080000, // #20 PC_PERM_DISABLE_CMD_DEAD = 0x100000, PC_PERM_HCHSYS_ADMIN = 0x200000, + PC_PERM_TRADE_BOUNDED = 0x400000, }; /// Total number of PC permissions (without PC_PERM_NONE). @@ -37,7 +38,7 @@ enum e_pc_permission { /// so it's possible to apply sizeof to it [C-FAQ 1.24] /// Whenever adding new permission: 1. add enum entry above, 2. add entry into /// pc_g_permission_name (in pc.c), 3. increase NUM_PC_PERM below by 1. -#define NUM_PC_PERM 22 +#define NUM_PC_PERM 23 struct pc_permission_name_table { const char *name; diff --git a/src/map/script.c b/src/map/script.c index c74eff07ba4..f6a54863a2a 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -5946,6 +5946,7 @@ BUILDIN(rentitem) 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)) ) { @@ -10847,6 +10848,7 @@ BUILDIN(successremovecards) { 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]; @@ -10920,6 +10922,7 @@ BUILDIN(failedremovecards) { 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]; @@ -11570,6 +11573,7 @@ BUILDIN(getinventorylist) pc->setreg(sd,reference_uid(script->add_str(card_var), j),sd->status.inventory[i].card[k]); } pc->setreg(sd,reference_uid(script->add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); + pc->setreg(sd,reference_uid(script->add_str("@inventorylist_bound"), j),sd->status.inventory[i].bound); j++; } } @@ -17455,6 +17459,172 @@ BUILDIN(bg_match_over) { return true; } +/*========================================== + * getitembound ,{,}; + * getitembound "",{,}; + *------------------------------------------*/ +BUILDIN(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) ) { // "" + const char *name = script->conv_str(st,data); + struct item_data *item_data = itemdb->search_name(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) ) { // + nameid = script->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 useless 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 ,,,,,,,,{,}; + * getitembound2 "",,,,,,,,{,}; + *------------------------------------------*/ +BUILDIN(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)); // + 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 = script->conv_str(st,data); + struct item_data *item_data = itemdb->search_name(name); + if( item_data ) + nameid = item_data->nameid; + else + nameid = UNKNOWN_ITEM_ID; + } else + nameid = script->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 ; + *------------------------------------------*/ +BUILDIN(equipisbounded) +{ + int i = -1, num; + TBL_PC *sd; + + if( (sd = script->rid2sd(st)) == NULL ) + return true; + + num = script_getnum(st,2); + if( num > 0 && num <= ARRAYLENGTH(script->equip) ) + i = pc->checkequip(sd,script->equip[num-1]); + + if( i >= 0 && sd->status.inventory[i].bound ) + script_pushint(st,1); + else + script_pushint(st,0); + + return true; +} + + // declarations that were supposed to be exported from npc_chat.c #ifdef PCRE_SUPPORT BUILDIN(defpattern); @@ -17950,6 +18120,12 @@ void script_parse_builtin(void) { 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"), diff --git a/src/map/storage.c b/src/map/storage.c index cc1100d2837..ddcbcf3ab42 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -107,7 +107,8 @@ int compare_item(struct item *a, struct item *b) 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++); @@ -429,7 +430,7 @@ int guild_storage_additem(struct map_session_data* sd, struct guild_storage* sto 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->message (sd->fd, msg_txt(264)); return 1; diff --git a/src/map/trade.c b/src/map/trade.c index 8dd30371b26..4e0d93d36f8 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -365,6 +365,12 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount) return; } + if( item->bound && !pc->can_give_bounded_items(sd) ) { // Account Bound + clif->message (sd->fd, msg_txt(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 diff --git a/src/map/vending.c b/src/map/vending.c index 7d6d02cfba0..122e8ab8127 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -257,6 +257,7 @@ void vending_openvending(struct map_session_data* sd, const char* message, const || !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;