From c47d8e4b8f9d63ab6b4e7e286e91cd50a5bdd151 Mon Sep 17 00:00:00 2001 From: sevenzz23 Date: Thu, 31 Oct 2013 12:07:06 +0700 Subject: No error after compilation, but when logging in at map server it will crash. Im pretty sure its on the clif.c Signed-off-by: sevenzz23 --- conf/messages.conf | 41 +++++- doc/atcommands.txt | 16 +++ doc/permissions.txt | 3 +- doc/script_commands.txt | 46 +++++++ sql-files/main.sql | 4 + sql-files/upgrades/2013-10-31--07-49.sql | 4 + src/char/char.c | 94 +++++++------ src/char/int_mail.c | 2 + src/char/int_storage.c | 167 +++++++++++++++++++--- src/char/inter.c | 4 +- src/common/mmo.h | 6 +- src/config/core.h | 4 + src/map/atcommand.c | 78 ++++++++--- src/map/buyingstore.c | 4 +- src/map/clif.c | 26 ++-- src/map/guild.c | 52 +++++++ src/map/guild.h | 4 + src/map/intif.c | 63 +++++++-- src/map/intif.h | 4 + src/map/mail.c | 5 +- src/map/packets_struct.h | 1 + src/map/party.c | 9 +- src/map/pc.c | 59 ++++++-- src/map/pc.h | 7 +- src/map/pc_groups.c | 1 + src/map/pc_groups.h | 3 +- src/map/script.c | 229 ++++++++++++++++++++++++++++++- src/map/storage.c | 17 ++- src/map/trade.c | 6 + src/map/vending.c | 3 +- 30 files changed, 825 insertions(+), 137 deletions(-) create mode 100644 sql-files/upgrades/2013-10-31--07-49.sql diff --git a/conf/messages.conf b/conf/messages.conf index 7b7a99301..d2e57bb0e 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -316,6 +316,13 @@ 290: The player is no longer killable. 291: Weather effects will dispell on warp/refresh 292: Killer state reset. +//Item Bound System +293: This bounded item cannot be traded to that character. +294: This bounded item cannot be stored there. +295: Please enter an item name or ID (usage: @item ). +296: Please enter all parameters (usage: @item2 +297: ). +298: Invalid bound type. Valid types are - 1:Account 2:Guild 3:Party 4:Character // Guild Castles Number // -------------------- //299: ?? Castles @@ -455,6 +462,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 +725,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 @@ -1360,7 +1376,8 @@ 1361: Already using this font. // @new_mount -1362: NOTICE: If you crash with mount your LUA is outdated. +//1362: NOTICE: If you crash with mount your LUA is outdated. +1362: .. 1363: You have mounted. 1364: You have released your mount. @@ -1534,5 +1551,19 @@ //CashShop mapflag 1489: Cash Shop is disabled in this map + +// @autoloottype +1490: You're already autolooting this item type. +1491: Your autoloottype list has all item types. You can remove some items with @autoloottype -. +1492: Autolooting item type: '%s' {%d} +1493: You're currently not autolooting this item type. +1494: Removed item type: '%s' {%d} from your autoloottype list. +1495: To add an item type to the list, use "@aloottype +". To remove an item type, use "@aloottype -". +1496: Type List: healing = 0, usable = 2, etc = 3, weapon = 4, armor = 5, card = 6, petegg = 7, petarmor = 8, ammo = 10 +1497: "@aloottype reset" will clear your autoloottype list. +1498: Your autoloottype list is empty. +1499: Item types on your autoloottype list: +1500: Your autoloottype list has been reset. + //Custom translations import: conf/import/msg_conf.txt diff --git a/doc/atcommands.txt b/doc/atcommands.txt index 42b085cd6..a7377b563 100644 --- a/doc/atcommands.txt +++ b/doc/atcommands.txt @@ -625,6 +625,22 @@ Creates an item with the given parameters (the 'cards' can be any item). identify_flag: 0 = unidentified, 1 = identified attribute: 0 = not broken, 1 = broken +--------------------------------------- + +@itembound + +Creates the specified item and bounds it to the account. +bound_type: 1 = Account, 2 = Guild, 3 = Party, 4 = Character + +--------------------------------------- + +@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 +bound_type: 1 = Account, 2 = Guild, 3 = Party, 4 = Character + --------------------------------------- @produce <# of Very's> diff --git a/doc/permissions.txt b/doc/permissions.txt index 9760f716c..c2aeba081 100644 --- a/doc/permissions.txt +++ b/doc/permissions.txt @@ -3,7 +3,7 @@ //===== By: ================================================== //= Hercules Dev Team //===== Current Version: ===================================== -//= 20130528 +//= 20131031 //===== Description: ========================================= //= Player group permissions, configured in /conf/groups.conf. //============================================================ @@ -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 bd0b032f1..23bacec5f 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 @@ -4448,6 +4449,51 @@ command, creating a pet which is the same, but simultaneously exists in two eggs, and may hatch from either, although, I'm not sure what kind of a mess will this really cause. +--------------------------------------- +*getitembound ,,{,}; +*getitembound "",,{,}; + +This command behaves identically to 'getitem', but the items created will be +bound to the target character as specified by the bound type. All items created +in this manner cannot be dropped, sold, vended, auctioned, or mailed, and in +some cases cannot be traded or stored. + +Valid bound types are: + 1 - Account Bound + 2 - Guild Bound + 3 - Party Bound + 4 - Character Bound + +--------------------------------------- + +*getitembound2 ,,,,,,,,,; +*getitembound2 "",,,,,,,,,; + +This command behaves identically to 'getitem2', but the items created will be +bound to the target character as specified by the bound type. All items created +in this manner cannot be dropped, sold, vended, auctioned, or mailed, and in +some cases cannot be traded or stored. + +For a list of bound types see 'getitembound'. + +--------------------------------------- + +*countbound({}) + +This function will return the number of bounded items in the character's +inventory, and sets an array @bound_items[] containing all item IDs of the +counted items. If a bound type is specified, only those items will be counted. + +For a list of bound types see 'getitembound'. + +Example: + mes "You currently have "+countbound()+" bounded items."; + next; + mes "The list of bounded items include:"; + for(set .@i,0; .@i,; diff --git a/sql-files/main.sql b/sql-files/main.sql index 2c91d58b1..3f425d411 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`) @@ -342,6 +343,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`) @@ -406,6 +408,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`) @@ -691,6 +694,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`) diff --git a/sql-files/upgrades/2013-10-31--07-49.sql b/sql-files/upgrades/2013-10-31--07-49.sql new file mode 100644 index 000000000..40e0421c8 --- /dev/null +++ b/sql-files/upgrades/2013-10-31--07-49.sql @@ -0,0 +1,4 @@ +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`; +ALTER TABLE `guild_storage` ADD COLUMN `bound` TINYINT(3) 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 4a04c521d..61a1e24e0 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -724,8 +724,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit // 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`"); - for( j = 0; j < MAX_SLOTS; ++j ) + StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`"); + for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id); @@ -747,8 +747,9 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); 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); + SQL->StmtBindColumn(stmt, 8, SQLDT_UINT, &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); // bit array indicating which inventory items have already been matched flag = (bool*) aCalloc(max, sizeof(bool)); @@ -775,17 +776,18 @@ 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 && + 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'", - 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, " WHERE `id`='%d' LIMIT 1", item.id); + StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `bound`='%d'", + tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].bound); + for( j = 0; j < MAX_SLOTS; ++j )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); if( SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) ) { @@ -810,8 +812,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit SQL->StmtFree(stmt); StrBuf->Clear(&buf); - 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, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", tablename, selectoption); + for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->AppendStr(&buf, ") VALUES "); @@ -828,9 +830,9 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit else found = true; - StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%"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].unique_id); - for( j = 0; j < MAX_SLOTS; ++j ) + 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].bound, items[i].unique_id); + for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", '%d'", items[i].card[j]); StrBuf->AppendStr(&buf, ")"); @@ -868,8 +870,8 @@ 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`"); - for( j = 0; j < MAX_SLOTS; ++j ) + 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 +894,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); - for( j = 0; j < MAX_SLOTS; ++j ) - SQL->StmtBindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 9, SQLDT_CHAR, &item.bound, 0, NULL, NULL); + for( j = 0; j < MAX_SLOTS; ++j ) + 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,15 +922,16 @@ 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, ", `card%d`=%d", j, items[i].card[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); if( SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) ) { @@ -950,8 +954,8 @@ 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); - for( j = 0; j < MAX_SLOTS; ++j ) + 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,9 +971,9 @@ 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); - for( j = 0; j < MAX_SLOTS; ++j ) + 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, ")"); @@ -1229,10 +1233,10 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything strcat(t_msg, " memo"); //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`"); - for( i = 0; i < MAX_SLOTS; ++i ) + //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `bound`, `unique_id`) + StrBuf->Init(&buf); + 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); @@ -1248,10 +1252,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) ) - SqlStmt_ShowDebug(stmt); + || 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 ) @@ -1260,10 +1265,10 @@ 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`) - StrBuf->Clear(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`"); - for( j = 0; j < MAX_SLOTS; ++j ) + //`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`, `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); @@ -1278,10 +1283,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) ) - SqlStmt_ShowDebug(stmt); + || 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 ) @@ -2947,7 +2953,7 @@ int parse_frommap(int fd) break; } //Check account only if this ain't final save. Final-save goes through because of the char-map reconnect - if (RFIFOB(fd,12) || ( + if (RFIFOB(fd,12) || RFIFOB(fd,13) || ( (character = (struct online_char_data*)idb_get(online_char_db, aid)) != NULL && character->char_id == cid)) { diff --git a/src/char/int_mail.c b/src/char/int_mail.c index e4b88b5bf..b69824d4b 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 429b80105..6443aa743 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -39,8 +39,8 @@ 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`"); - for( j = 0; j < MAX_SLOTS; ++j ) + 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); - for( j = 0; j < MAX_SLOTS; ++j ) + 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; @@ -96,8 +97,8 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p) // storage {`guild_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`,`unique_id`"); - for( j = 0; j < MAX_SLOTS; ++j ) + StrBuf->AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`bound`,`unique_id`"); + for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ",`card%d`", j); StrBuf->Printf(&buf, " FROM `%s` WHERE `guild_id`='%d' ORDER BY `nameid`", guild_storage_db, guild_id); @@ -116,11 +117,13 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p) SQL->GetData(sql_handle, 4, &data, NULL); item->identify = atoi(data); 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->unique_id = strtoull(data, NULL, 10); - item->expire_time = 0; + SQL->GetData(sql_handle, 7, &data, NULL); item->bound = atoi(data); + SQL->GetData(sql_handle, 8, &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); + SQL->GetData(sql_handle, 9+j, &data, NULL); item->card[j] = atoi(data); } } p->storage_amount = i; @@ -159,19 +162,20 @@ int inter_guild_storage_delete(int guild_id) //--------------------------------------------------------- // packet from map server -int mapif_load_guild_storage(int fd,int account_id,int guild_id) +int mapif_load_guild_storage(int fd,int account_id,int guild_id, char flag) { if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id) ) Sql_ShowDebug(sql_handle); else if( SQL->NumRows(sql_handle) > 0 ) {// guild exists - WFIFOHEAD(fd, sizeof(struct guild_storage)+12); + WFIFOHEAD(fd, sizeof(struct guild_storage)+13); WFIFOW(fd,0) = 0x3818; - WFIFOW(fd,2) = sizeof(struct guild_storage)+12; + WFIFOW(fd,2) = sizeof(struct guild_storage)+13; WFIFOL(fd,4) = account_id; WFIFOL(fd,8) = guild_id; - guild_storage_fromsql(guild_id, (struct guild_storage*)WFIFOP(fd,12)); - WFIFOSET(fd, WFIFOW(fd,2)); + WFIFOB(fd,12) = flag; //1 open storage, 0 don't open + guild_storage_fromsql(guild_id, (struct guild_storage*)WFIFOP(fd,13)); + WFIFOSET(fd, WFIFOW(fd,2)); return 0; } // guild does not exist @@ -201,7 +205,7 @@ int mapif_save_guild_storage_ack(int fd,int account_id,int guild_id,int fail) int mapif_parse_LoadGuildStorage(int fd) { RFIFOHEAD(fd); - mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6)); + mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6),1); return 0; } @@ -235,6 +239,134 @@ int mapif_parse_SaveGuildStorage(int fd) return 0; } +#ifdef BOUND_ITEMS +int mapif_itembound_ack(int fd, int aid, int guild_id) +{ + WFIFOHEAD(fd,8); + WFIFOW(fd,0) = 0x3856; + WFIFOL(fd,2) = aid; + WFIFOW(fd,6) = guild_id; + WFIFOSET(fd,8); + return 0; +} + +//------------------------------------------------ +//Guild bound items pull for offline characters [Akinari] +//Revised by [Mhalicot] +//------------------------------------------------ +int mapif_parse_itembound_retrieve(int fd) +{ + StringBuf buf; + SqlStmt* stmt; + struct item item; + int j, i=0, s; + bool found=false; + struct item items[MAX_INVENTORY]; + int char_id = RFIFOL(fd,2); + int aid = RFIFOL(fd,6); + int guild_id = RFIFOW(fd,10); + + StrBuf->Init(&buf); + StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `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,char_id); + + stmt = SQL->StmtMalloc(sql_handle); + if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) + || SQL_ERROR == SQL->StmtExecute(stmt) ) + { + Sql_ShowDebug(sql_handle); + SQL->StmtFree(stmt); + StrBuf->Destroy(&buf); + return 1; + } + + SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 3, SQLDT_USHORT, &item.equip, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); + 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_UINT, &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); + + while( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) { + if(item.bound == 2) { + memcpy(&items[i],&item,sizeof(struct item)); + i++; + } + } + SQL->FreeResult(sql_handle); + + if(!i) { //No items found - No need to continue + StrBuf->Destroy(&buf); + SQL->StmtFree(stmt); + return 0; + } + + //First we delete the character's items + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE",inventory_db); + for(j=0; jAppendStr(&buf, " OR"); + else + found = true; + StrBuf->Printf(&buf, " `id`=%d",items[j].id); + } + + if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) + || SQL_ERROR == SQL->StmtExecute(stmt) ) + { + Sql_ShowDebug(sql_handle); + SQL->StmtFree(stmt); + StrBuf->Destroy(&buf); + return 1; + } + + //Now let's update the guild storage with those deleted items + found = false; + StrBuf->Clear(&buf); + StrBuf->Printf(&buf, "INSERT INTO `%s` (`guild_id`, `nameid`, `amount`, `identify`, `refine`, `attribute`, `expire_time`, `bound`", guild_storage_db); + for( j = 0; j < MAX_SLOTS; ++j ) + StrBuf->Printf(&buf, ", `card%d`", j); + StrBuf->AppendStr(&buf, ") VALUES "); + + for( j = 0; j < i; ++j ) { + if( found ) + StrBuf->AppendStr(&buf, ","); + else + found = true; + + StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d'", + guild_id, items[j].nameid, items[j].amount, items[j].identify, items[j].refine, items[j].attribute, items[j].expire_time, items[j].bound); + for( s = 0; s < MAX_SLOTS; ++s ) + StrBuf->Printf(&buf, ", '%d'", items[j].card[s]); + StrBuf->AppendStr(&buf, ")"); + } + + if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) + || SQL_ERROR == SQL->StmtExecute(stmt) ) + { + Sql_ShowDebug(sql_handle); + SQL->StmtFree(stmt); + StrBuf->Destroy(&buf); + return 1; + } + + StrBuf->Destroy(&buf); + SQL->StmtFree(stmt); + + //Finally reload storage and tell map we're done + mapif_load_guild_storage(fd,aid,guild_id,0); + mapif_itembound_ack(fd,aid,guild_id); + return 0; +} +#endif int inter_storage_parse_frommap(int fd) { @@ -242,6 +374,9 @@ int inter_storage_parse_frommap(int fd) switch(RFIFOW(fd,0)){ case 0x3018: mapif_parse_LoadGuildStorage(fd); break; case 0x3019: mapif_parse_SaveGuildStorage(fd); break; +#ifdef BOUND_ITEMS + case 0x3056: mapif_parse_itembound_retrieve(fd); break; +#endif default: return 0; } diff --git a/src/char/inter.c b/src/char/inter.c index 040246c31..54672faee 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -52,8 +52,8 @@ int inter_recv_packet_length[] = { -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- -1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- - -1,-1,10,10, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus] - 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish] + -1,-1,10,10, 0,-1, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus] [Mhalicot] + 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish] -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil] 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080- -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator] diff --git a/src/common/mmo.h b/src/common/mmo.h index 5f75f35da..d5bf45adf 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 @@ -222,7 +222,7 @@ struct item { char attribute; short card[MAX_SLOTS]; unsigned int expire_time; - char favorite; + char favorite, bound; uint64 unique_id; }; diff --git a/src/config/core.h b/src/config/core.h index b5ad1b794..481c35af8 100644 --- a/src/config/core.h +++ b/src/config/core.h @@ -61,6 +61,10 @@ /// By enabling it, the system will create an unique id for each new non stackable item created //#define NSI_UNIQUE_ID +/// Comment to disable Guild/Party Bound item system +/// By default, we recover/remove Guild/Party Bound items automatically +#define BOUND_ITEMS + /// Uncomment to enable real-time server stats (in and out data and ram usage). [Ai4rei] //#define SHOW_SERVER_STATS diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 146159c63..2900fde03 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -1081,22 +1081,29 @@ ACMD(heal) /*========================================== * @item command (usage: @item ) (modified by [Yor] for pet_egg) + * @itembound command (usage: @itembound ) (revised by [Mhalicot]) *------------------------------------------*/ 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; memset(item_name, '\0', sizeof(item_name)); - - 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 ). + + if (!strcmpi(command+1,"itembound") && (!message || !*message || ( + sscanf(message, "\"%99[^\"]\" %d %d", item_name, &number, &bound) < 2 && + sscanf(message, "%99s %d %d", item_name, &number, &bound) < 2 + ))) { + clif->message(fd, msg_txt(295)); // Please enter an item name or ID (usage: @itembound ). + return -1; + } else 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 ). return false; } @@ -1110,11 +1117,24 @@ ACMD(item) return false; } + if( bound < 0 || bound > 4 ) { + clif->message(fd, msg_txt(298)); // Invalid bound type + return false; + } + item_id = item_data->nameid; get_count = number; //Check if it's stackable. - if (!itemdb->isstackable2(item_data)) - get_count = 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; + } + 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 @@ -1122,6 +1142,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); @@ -1134,21 +1155,27 @@ ACMD(item) } /*========================================== - * + * @item2 and @itembound2 command (revised by [Mhalicot]) *------------------------------------------*/ 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; memset(item_name, '\0', sizeof(item_name)); - 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 && + if (!strcmpi(command+1,"itembound2") && (!message || !*message || ( + sscanf(message, "\"%99[^\"]\" %d %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 && + sscanf(message, "%99s %d %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 ))) { + clif->message(fd, msg_txt(296)); // Please enter all parameters (usage: @itembound2 + clif->message(fd, msg_txt(297)); // ). + return false; + } else 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 @@ -1158,7 +1185,12 @@ ACMD(item2) if (number <= 0) number = 1; - + + if( bound < 0 || bound > 4 ) { + clif->message(fd, msg_txt(298)); // Invalid bound type + return -1; + } + item_id = 0; if ((item_data = itemdb->search_name(item_name)) != NULL || (item_data = itemdb->exists(atoi(item_name))) != NULL) @@ -1169,9 +1201,14 @@ 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) { - loop = number; + 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) { identify = 1; @@ -1182,6 +1219,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; } @@ -1195,6 +1236,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); } @@ -9230,6 +9272,8 @@ void atcommand_basecommands(void) { ACMD_DEF(heal), ACMD_DEF(item), ACMD_DEF(item2), + ACMD_DEF2("itembound", item), + ACMD_DEF2("itembound2", item2), ACMD_DEF(itemreset), ACMD_DEF(clearstorage), ACMD_DEF(cleargstorage), diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index 2a9e6a88e..44ece49c6 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -290,8 +290,8 @@ 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)) ) - {// non-tradable item + 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 c1e7cb1c9..99f7c87d7 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1802,10 +1802,13 @@ 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 + + if( sd->status.inventory[i].bound && !pc->can_give_bounded_items(sd)) + continue; // Don't allow sale of bound items - val=sd->inventory_data[i]->value_sell; + val=sd->inventory_data[i]->value_sell; if( val < 0 ) continue; WFIFOW(fd,4+c*10)=i+2; @@ -2229,7 +2232,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; @@ -2341,7 +2344,7 @@ void clif_item_equip(short idx, struct EQUIPITEM_INFO *p, struct item *i, struct #endif #if PACKETVER >= 20080102 - p->bindOnEquipType = 0; + p->bindOnEquipType = i->bound ? 2 : 0; #endif #if PACKETVER >= 20100629 @@ -2378,6 +2381,7 @@ void clif_item_normal(short idx, struct NORMALITEM_INFO *p, struct item *i, stru #if PACKETVER >= 20080102 p->HireExpireDate = i->expire_time; + p->bindOnEquipType = i->bound ? 2 : 0; #endif #if PACKETVER >= 20120925 @@ -15061,8 +15065,9 @@ void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) 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 - clif->auction_setitem(sd->fd, idx, true); + !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; } @@ -15139,9 +15144,10 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd) } // Auction checks... - if( sd->status.zeny < (auction.hours * battle_config.auction_feeperhour) ) { - clif->auction_message(fd, 5); // You do not have enough zeny to pay the Auction Fee. - return; + if( sd->status.inventory[sd->auction.index].bound && !pc->can_give_bounded_items(sd) ) { + clif->message(sd->fd, msg_txt(293)); + clif->auction_message(fd, 2); // The auction has been canceled + return; } if( auction.buynow > battle_config.auction_maximumprice ) diff --git a/src/map/guild.c b/src/map/guild.c index 0ae45bede..517a49cc4 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -865,6 +865,11 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c online_member_sd = guild->getavailablesd(g); if(online_member_sd == NULL) return 0; // noone online to inform + +#ifdef BOUND_ITEMS + //Guild bound item check + guild->retrieveitembound(char_id,account_id,guild_id); +#endif if(!flag) clif->guild_leave(online_member_sd, name, mes); @@ -899,6 +904,41 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c return 0; } +#ifdef BOUND_ITEMS +void guild_retrieveitembound(int char_id,int aid,int guild_id) +{ + TBL_PC *sd = map->id2sd(aid); + if(sd){ //Character is online + int idxlist[MAX_INVENTORY]; + int j,i; + j = pc->bound_chk(sd,2,idxlist); + if(j) { + struct guild_storage *gstor = gstorage->id2storage(guild_id); + for(i=0;iadditem(sd,gstor,&sd->status.inventory[idxlist[i]],sd->status.inventory[idxlist[i]].amount); + pc->delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,4,LOG_TYPE_GSTORAGE); + } + gstorage->close(sd); //Close and save the storage + } + } + else { //Character is offline, ask char server to do the job + struct guild_storage *gstor = gstorage->id2storage2(guild_id); + if(gstor && gstor->storage_status == 1) { //Someone is in guild storage, close them + struct s_mapiterator* iter = mapit_getallusers(); + for( sd = (TBL_PC*)mapit->first(iter); mapit->exists(iter); sd = (TBL_PC*)mapit->next(iter) ) { + if(sd->status.guild_id == guild_id && sd->state.storage_flag == 2) { + gstorage->close(sd); + break; + } + } + mapit->free(iter); + } + intif->itembound_req(char_id,aid,guild_id); + } +} +#endif + int guild_send_memberinfoshort(struct map_session_data *sd,int online) { // cleaned up [LuzZza] struct guild *g; @@ -1813,6 +1853,11 @@ int guild_break(struct map_session_data *sd,char *name) { struct guild *g; struct unit_data *ud; int i; + +#ifdef BOUND_ITEMS + int j; + int idxlist[MAX_INVENTORY]; +#endif nullpo_ret(sd); @@ -1855,6 +1900,13 @@ int guild_break(struct map_session_data *sd,char *name) { skill->del_unitgroup(groups[i],ALC_MARK); } } + +#ifdef BOUND_ITEMS + //Guild bound item check - Removes the bound flag + j = pc->bound_chk(sd,2,idxlist); + for(i=0;istatus.inventory[idxlist[i]].bound = 0; +#endif intif->guild_break(g->guild_id); return 1; diff --git a/src/map/guild.h b/src/map/guild.h index 348a6c7e4..57148867a 100644 --- a/src/map/guild.h +++ b/src/map/guild.h @@ -147,6 +147,10 @@ struct guild_interface { void (*flags_clear) (void); /* guild aura */ void (*aura_refresh) (struct map_session_data *sd, uint16 skill_id, uint16 skill_lv); + /* item bound [Mhalicot]*/ +#ifdef BOUND_ITEMS + void (*retrieveitembound) (int char_id,int aid,int guild_id); +#endif /* */ int (*payexp_timer) (int tid, int64 tick, int id, intptr_t data); TBL_PC* (*sd_check) (int guild_id, int account_id, int char_id); diff --git a/src/map/intif.c b/src/map/intif.c index f31ab0f5a..b8b16a356 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -982,15 +982,19 @@ void intif_parse_LoadGuildStorage(int fd) { struct guild_storage *gstor; struct map_session_data *sd; - int guild_id; + int guild_id, flag; guild_id = RFIFOL(fd,8); + flag = RFIFOL(fd,12); if(guild_id <= 0) return; sd=map->id2sd( RFIFOL(fd,4) ); - if(sd==NULL){ - ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); - return; + if( flag ){ //If flag != 0, we attach a player and open the storage + if(sd==NULL){ + ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); + return; + } + } gstor=gstorage->id2storage(guild_id); if(!gstor) { @@ -998,20 +1002,21 @@ void intif_parse_LoadGuildStorage(int fd) return; } if (gstor->storage_status == 1) { // Already open.. lets ignore this update - ShowWarning("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", sd->status.account_id, sd->status.char_id); - return; + ShowWarning("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", flag?sd->status.account_id:1, flag?sd->status.char_id:1); + return; } if (gstor->dirty) { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex] - ShowWarning("intif_parse_LoadGuildStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", sd->status.account_id, sd->status.char_id); - return; + ShowWarning("intif_parse_LoadGuildStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", flag?sd->status.account_id:1, flag?sd->status.char_id:1); + return; } - if( RFIFOW(fd,2)-12 != sizeof(struct guild_storage) ){ - ShowError("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-12 , sizeof(struct guild_storage)); - gstor->storage_status = 0; + if( RFIFOW(fd,2)-13 != sizeof(struct guild_storage) ){ + ShowError("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-13 , sizeof(struct guild_storage)); + gstor->storage_status = 0; return; } - memcpy(gstor,RFIFOP(fd,12),sizeof(struct guild_storage)); + memcpy(gstor,RFIFOP(fd,13),sizeof(struct guild_storage)); + if( flag ) gstorage->open(sd); } @@ -2005,7 +2010,31 @@ void intif_parse_MessageToFD(int fd) { return; } +/*========================================== + * Item Bound System [Xantara][Mhalicot] + *------------------------------------------*/ +#ifdef BOUND_ITEMS +void intif_itembound_req(int char_id,int aid,int guild_id) { + struct guild_storage *gstor = gstorage->id2storage2(guild_id); + WFIFOHEAD(inter_fd,12); + WFIFOW(inter_fd,0) = 0x3056; + WFIFOL(inter_fd,2) = char_id; + WFIFOL(inter_fd,6) = aid; + WFIFOW(inter_fd,10) = guild_id; + WFIFOSET(inter_fd,12); + if(gstor) + gstor->lock = 1; //Lock for retrieval process +} + +//3856 +void intif_parse_Itembound_ack(int fd) { + struct guild_storage *gstor; + int guild_id = RFIFOW(fd,6); + gstor = gstorage->id2storage2(guild_id); + if(gstor) gstor->lock = 0; //Unlock now that operation is completed +} +#endif //----------------------------------------------------------------- // Communication from the inter server // Return a 0 (false) if there were any errors. @@ -2088,7 +2117,10 @@ int intif_parse(int fd) case 0x3853: intif->pAuctionClose(fd); break; case 0x3854: intif->pAuctionMessage(fd); break; case 0x3855: intif->pAuctionBid(fd); break; - + //Bound items +#ifdef BOUND_ITEMS + case 0x3856: intif->pItembound_ack(fd); break; +#endif // Mercenary System case 0x3870: intif->pMercenaryReceived(fd); break; case 0x3871: intif->pMercenaryDeleted(fd); break; @@ -2127,8 +2159,8 @@ void intif_defaults(void) { 39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830 -1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840 - -1,-1, 7, 7, 7,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] - -1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish] + -1,-1, 7, 7, 7,11, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] itembound[Akinari] + -1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish] -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil] 11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880 -1,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] @@ -2263,6 +2295,7 @@ void intif_defaults(void) { intif->pAuctionClose = intif_parse_AuctionClose; intif->pAuctionMessage = intif_parse_AuctionMessage; intif->pAuctionBid = intif_parse_AuctionBid; + intif->pItembound_ack = intif_parse_Itembound_ack; intif->pMercenaryReceived = intif_parse_MercenaryReceived; intif->pMercenaryDeleted = intif_parse_MercenaryDeleted; intif->pMercenarySaved = intif_parse_MercenarySaved; diff --git a/src/map/intif.h b/src/map/intif.h index 768e735de..577d58923 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -75,6 +75,9 @@ struct intif_interface { int (*guild_emblem) (int guild_id, int len, const char *data); int (*guild_castle_dataload) (int num, int *castle_ids); int (*guild_castle_datasave) (int castle_id, int index, int value); +#ifdef BOUND_ITEMS + void (*itembound_req) (int char_id, int aid, int guild_id); +#endif int (*request_petdata) (int account_id, int char_id, int pet_id); int (*save_petdata) (int account_id, struct s_pet *p); int (*delete_petdata) (int pet_id); @@ -161,6 +164,7 @@ struct intif_interface { void (*pAuctionClose) (int fd); void (*pAuctionMessage) (int fd); void (*pAuctionBid) (int fd); + void (*pItembound_ack) (int fd); void (*pMercenaryReceived) (int fd); void (*pMercenaryDeleted) (int fd); void (*pMercenarySaved) (int fd); diff --git a/src/map/mail.c b/src/map/mail.c index 2378cbe2a..6b1537d87 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -82,8 +82,9 @@ 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)) ) - return 1; + !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; sd->mail.nameid = sd->status.inventory[idx].nameid; diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index 813aebee0..e6f68ea4f 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -212,6 +212,7 @@ struct NORMALITEM_INFO { #endif #if PACKETVER >= 20080102 int HireExpireDate; + unsigned short bindOnEquipType; #endif #if PACKETVER >= 20120925 struct { diff --git a/src/map/party.c b/src/map/party.c index ab05c23f7..69b343fb7 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -547,7 +547,14 @@ int party_member_withdraw(int party_id, int account_id, int char_id) } if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) { - sd->status.party_id = 0; +#ifdef BOUND_ITEMS + int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion + int j,i; + j = pc->bound_chk(sd,3,idxlist); + for(i=0;idelitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER); +#endif + sd->status.party_id = 0; clif->charnameupdate(sd); //Update name display [Skotlex] //TODO: hp bars should be cleared too if( p->instances ) diff --git a/src/map/pc.c b/src/map/pc.c index 35d883b6f..ba445b4f4 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -568,6 +568,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. *------------------------------------------*/ @@ -991,6 +999,10 @@ int pc_isequip(struct map_session_data *sd,int n) *------------------------------------------*/ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, struct mmo_charstatus *st, bool changing_mapservers) { int i; +#ifdef BOUND_ITEMS + int j; + int idxlist[MAX_INVENTORY]; +#endif int64 tick = timer->gettick(); uint32 ip = session[sd->fd]->client_addr; @@ -1190,7 +1202,15 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim * Check if player have any item cooldowns on **/ pc->itemcd_do(sd,true); - + +#ifdef BOUND_ITEMS + // Party bound item check + if(sd->status.party_id == 0 && (j = pc->bound_chk(sd,3,idxlist))) { // Party was deleted while character offline + for(i=0;idelitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER); + } +#endif + /* [Ind/Hercules] */ sd->sc_display = NULL; sd->sc_display_count = 0; @@ -3948,8 +3968,8 @@ 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; sd->status.inventory[i].amount += amount; @@ -4500,8 +4520,8 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun return 1; } - if( !itemdb_cancartstore(item_data, pc->get_group_level(sd)) ) - { // Check item trade restrictions [Skotlex] + if( !itemdb_cancartstore(item_data, pc->get_group_level(sd)) || (item_data->bound > 1 && !pc->can_give_bounded_items(sd))) + { // Check item trade restrictions [Skotlex] clif->message (sd->fd, msg_txt(264)); return 1;/* TODO: there is no official response to this? */ } @@ -4513,8 +4533,8 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun if( itemdb->isstackable2(data) && !item_data->expire_time ) { ARR_FIND( 0, MAX_CART, i, - sd->status.cart[i].nameid == item_data->nameid && - sd->status.cart[i].card[0] == item_data->card[0] && sd->status.cart[i].card[1] == item_data->card[1] && + 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] ); }; @@ -4647,7 +4667,25 @@ int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount) return flag; } - + /*========================================== + * Bound Item Check + * Type: + * 1 Account Bound + * 2 Guild Bound + * 3 Party Bound + * 4 Character Bound + *------------------------------------------*/ +int pc_bound_chk(TBL_PC *sd,int type,int *idxlist) +{ + int i=0, j=0; + for(i=0;istatus.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0 && sd->status.inventory[i].bound == type) { + idxlist[j] = i; + j++; + } + } + return j; +} /*========================================== * Display item stolen msg to player sd *------------------------------------------*/ @@ -7965,8 +8003,8 @@ int pc_setmadogear(TBL_PC* sd, int flag) *------------------------------------------*/ int pc_candrop(struct map_session_data *sd, struct item *item) { - if( item && item->expire_time ) - return 0; + 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; return (itemdb_isdropable(item, pc->get_group_level(sd))); @@ -10293,6 +10331,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 fc37d0ef2..d517d8fcf 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -749,7 +749,8 @@ 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); int (*set_group) (struct map_session_data *sd, int group_id); @@ -788,6 +789,10 @@ struct pc_interface { int (*additem) (struct map_session_data *sd,struct item *item_data,int amount,e_log_pick_type log_type); int (*getzeny) (struct map_session_data *sd,int zeny, enum e_log_pick_type type, struct map_session_data *tsd); int (*delitem) (struct map_session_data *sd,int n,int amount,int type, short reason, e_log_pick_type log_type); + + //Bound items + int (*bound_chk) (TBL_PC *sd,int type,int *idxlist); + // Special Shop System int (*paycash) (struct map_session_data *sd, int price, int points); int (*getcash) (struct map_session_data *sd, int cash, int points); diff --git a/src/map/pc_groups.c b/src/map/pc_groups.c index be02b5f15..41bc19cba 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 8f350c2b6..63e7acc51 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 d51f27ce9..174d12316 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)) ) { @@ -10850,6 +10851,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]; @@ -10923,7 +10925,8 @@ 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]; @@ -11573,7 +11576,8 @@ 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); - j++; + pc->setreg(sd,reference_uid(script->add_str("@inventorylist_bound"), j),sd->status.inventory[i].bound); + j++; } } pc->setreg(sd,script->add_str("@inventorylist_count"),j); @@ -17529,6 +17533,219 @@ BUILDIN(bg_join_team) { return true; } + +/*==============[Mhalicot]================== + * getitembound ,,{,}; + * getitembound "",,{,}; + * Type: + * 1 - Account Bound + * 2 - Guild Bound + * 3 - Party Bound + * 4 - Character Bound + *------------------------------------------*/ +BUILDIN(getitembound) +{ + int nameid, amount, i, flag; + struct item it; + struct script_data *data; + char bound = script_getnum(st,4); + 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.identify = 1; + it.bound = bound; + + if( bound < 1 || bound > 4) { //Not a correct bound type + ShowError("script_getitembound: Not a correct bound type! Type=%d\n",bound); + return 1; + } + + if( script_hasdata(st,5) ) + sd=map->id2sd(script_getnum(st,5)); // 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; +} + +/*==============[Mhalicot]================== + * getitembound2 ,,,,,,,,,; + * getitembound2 "",,,,,,,,,; + *------------------------------------------*/ +BUILDIN(getitembound2) +{ + int nameid,amount,get_count,i,flag = 0; + int iden,ref,attr,c1,c2,c3,c4; + char bound=0; + struct item_data *item_data; + struct item item_tmp; + TBL_PC *sd; + struct script_data *data; + + bound = script_getnum(st,11); + if( bound < 1 || bound > 4) { //Not a correct bound type + ShowError("script_getitembound2: Not a correct bound type! Type=%d\n",bound); + return 1; + } + if( script_hasdata(st,12) ) + sd=map->id2sd(script_getnum(st,12)); + else + sd=script->rid2sd(st); // Attached player + + if( sd == NULL ) // no target + return true; + + data=script_getdata(st,2); + script->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) { // Invalide nameid + nameid = -nameid; + flag = 1; + } + + if(nameid > 0) { + memset(&item_tmp,0,sizeof(item_tmp)); + item_data=itemdb->exists(nameid); + if (item_data == NULL) + return -1; + if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR){ + if(ref > MAX_REFINE) ref = MAX_REFINE; + } + else if(item_data->type==IT_PETEGG) { + iden = 1; + ref = 0; + } + else { + iden = 1; + ref = attr = 0; + } + + item_tmp.nameid=nameid; + if(!flag) + item_tmp.identify=iden; + else if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR) + item_tmp.identify=0; + 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=bound; + + //Check if it's stackable. + if (!itemdb->isstackable(nameid)) + get_count = 1; + else + get_count = amount; + + for (i = 0; i < amount; i += get_count) { + // if not pet egg + if (!pet->create_egg(sd, nameid)) { + if ((flag = pc->additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) { + clif->additem(sd, 0, 0, flag); + if( pc->candrop(sd,&item_tmp) ) + map->addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); + } + } + } + } + + return true; +} +/*==============[Mhalicot]================== + * countbound {}; + * Creates an array of bounded item IDs + * Returns amount of items found + * Type: + * 1 - Account Bound + * 2 - Guild Bound + * 3 - Party Bound + *------------------------------------------*/ +BUILDIN(countbound) +{ + int i, type, j=0, k=0; + TBL_PC *sd; + + if( (sd = script->rid2sd(st)) == NULL ) + return false; + + type = script_hasdata(st,2)?script_getnum(st,2):0; + + for(i=0;istatus.inventory[i].nameid > 0 && ( + (!type && sd->status.inventory[i].bound > 0) || + (type && sd->status.inventory[i].bound == type) + )) { + pc->setreg(sd,reference_uid(script->add_str("@bound_items"), k),sd->status.inventory[i].nameid); + k++; + j += sd->status.inventory[i].amount; + } + } + + script_pushint(st,j); + return 0; +} + /* bg_match_over( arena_name {, optional canceled } ) */ /* returns 0 when successful, 1 otherwise */ BUILDIN(bg_match_over) { @@ -18109,7 +18326,13 @@ void script_parse_builtin(void) { BUILDIN_DEF(bindatcmd, "ss???"), BUILDIN_DEF(unbindatcmd, "s"), BUILDIN_DEF(useatcmd, "s"), - + /** + * Item bound [Mhalicot\Hercules] + **/ + BUILDIN_DEF(getitembound,"vii?"), + BUILDIN_DEF(getitembound2,"viiiiiiiii?"), + BUILDIN_DEF(countbound, "?"), + //Quest Log System [Inkfish] BUILDIN_DEF(questinfo, "ii??"), BUILDIN_DEF(setquest, "i"), diff --git a/src/map/storage.c b/src/map/storage.c index cc1100d28..df406257d 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++); @@ -140,6 +141,11 @@ int storage_additem(struct map_session_data* sd, struct item* item_data, int amo return 1; } + if( (item_data->bound > 1) && !pc->can_give_bounded_items(sd) ) { + clif->message(sd->fd, msg_txt(294)); + return 1; + } + if( itemdb->isstackable2(data) ) {//Stackable for( i = 0; i < MAX_STORAGE; i++ ) @@ -429,12 +435,17 @@ 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 ) - { //Check if item is storable. [Skotlex] + 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; } + if( (item_data->bound == 1 || item_data->bound > 2) && !pc->can_give_bounded_items(sd) ) { + clif->message(sd->fd, msg_txt(294)); + return 1; + } + if(itemdb->isstackable2(data)){ //Stackable for(i=0;iitems[i], item_data)) { diff --git a/src/map/trade.c b/src/map/trade.c index 8dd30371b..7085fdda3 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 == 1 || item->bound > 2) || (item->bound == 2 && sd->status.guild_id != target_sd->status.guild_id)) && !pc->can_give_bounded_items(sd) ) { // Item Bound + clif->message(sd->fd, msg_txt(293)); + 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 7d6d02cfb..be3826754 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -257,7 +257,8 @@ 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 - || !itemdb_cantrade(&sd->status.cart[index], pc->get_group_level(sd), pc->get_group_level(sd)) ) // untradeable item + || (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; sd->vending[i].index = index; -- cgit v1.2.3-60-g2f50 From c5795013b52c95847491e13a5549381016629640 Mon Sep 17 00:00:00 2001 From: sevenzz23 Date: Fri, 1 Nov 2013 02:22:55 +0700 Subject: Item Binding System Final. Fixed, some issue thanks to Master Ind ;) Signed-off-by: sevenzz23 --- conf/groups.conf | 1 + conf/messages.conf | 4 +- npc/custom/itembind.txt | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ npc/scripts_custom.conf | 1 + src/map/pc.c | 1 + 5 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 npc/custom/itembind.txt diff --git a/conf/groups.conf b/conf/groups.conf index 7c97352dc..9a4c57126 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 d2e57bb0e..a20b1353b 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -319,8 +319,8 @@ //Item Bound System 293: This bounded item cannot be traded to that character. 294: This bounded item cannot be stored there. -295: Please enter an item name or ID (usage: @item ). -296: Please enter all parameters (usage: @item2 +295: Please enter an item name or ID (usage: @itembound ). +296: Please enter all parameters (usage: @itembound2 297: ). 298: Invalid bound type. Valid types are - 1:Account 2:Guild 3:Party 4:Character // Guild Castles Number diff --git a/npc/custom/itembind.txt b/npc/custom/itembind.txt new file mode 100644 index 000000000..f290c48ef --- /dev/null +++ b/npc/custom/itembind.txt @@ -0,0 +1,132 @@ +//===== rAthena Script ======================================= +//= Item Bound Script +//===== By: ================================================== +//= Akinari +//===== Compatible With: ===================================== +//= Revision 17351+ (rAthena Only) +//===== Description: ========================================= +//= Item Bound Script +//= Allows users to pay a price to make an item bound to +//= Account, Character, or Guild +//============================================================ + +prontera,144,174,4 script Bound Items 429,{ + + mes "I can bind your items to your account, guild, or character"+((.bindprice)?" for a ^0000FF"+.bindprice+"^000000 zeny fee":"")+"."; + next; + mes "With this, you can rest assured your items are safe."; + next; + mes "What would you like to do?"; + if(select("Bind:Unbind") == 1) { + if(zeny < .bindprice) { + mes "You don't have enough zeny to bind an item."; + close; + } + mes "What kind of bind?"; + .@boundtype = 1 << (select("Account:Guild:Character")-1); + if(.@boundtype == 2 && (!getcharid(2) || getguildmaster(getcharid(2)) != strcharinfo(0))) { + mes "In order for me to bind an item to a guild you must be the master of one."; + close; + } + getinventorylist(); + for(.@i = 0; .@i < @inventorylist_count; .@i++) { + //We only show the items that you allow to be bound + //Allows equipment (default) + if(@inventorylist_bound[.@i]) + continue; + if(((.allowbind & 1) && (getiteminfo(@inventorylist_id[.@i],2) == (4|5))) || + ((.allowbind & 2) && (getiteminfo(@inventorylist_id[.@i],2) == (0|2|11|18))) || + ((.allowbind & 4) && (getiteminfo(@inventorylist_id[.@i],2) == (3|6|7|8|10))) + ) { + set .@bindlist$, .@bindlist$ + ":" + getitemname(@inventorylist_id[.@i]) + " - " + @inventorylist_id[.@i]; + set .@bindlist[.@j],.@i; + .@j++; + } + } + .@item = .@bindlist[select(.@bindlist$)-2]; + next; + mes "Before I continue, I want you to know I can't tell the difference between multiple items. If you have a specific item you want bounded, please remove any duplicates from inventory."; + if(select("I understand, continue:Wait a minute") == 2) { + next; + mes "I'll be here when you're ready."; + close; + } + next; + mes "Are you sure you'd like to bind your "+ getitemname(@inventorylist_id[.@item]) +" to your "+.boundtypes$[.@boundtype]+"?"; + if(select("Yes:No") == 1) { + zeny -= .bindprice; + delitem2 @inventorylist_id[.@item],@inventorylist_amount[.@item],@inventorylist_identify[.@item],@inventorylist_refine[.@item],@inventorylist_attribute[.@item],@inventorylist_card1[.@item],@inventorylist_card2[.@item],@inventorylist_card3[.@item],@inventorylist_card4[.@item]; + getitembound2 @inventorylist_id[.@item],@inventorylist_amount[.@item],@inventorylist_identify[.@item],@inventorylist_refine[.@item],@inventorylist_attribute[.@item],@inventorylist_card1[.@item],@inventorylist_card2[.@item],@inventorylist_card3[.@item],@inventorylist_card4[.@item],.@boundtype; + mes "All done!"; + if(.logbinds) + logmes "Bound "+ @inventorylist_amount[.@item]+" "+@inventorylist_id[.@item]+" as "+.boundtypes$[.@boundtype]+" type."; + } + } else { + /*if(!countbound()) { + mes "You don't have any bound items in your inventory. Not much I can do here."; + close; + } + countbound(2); + if(.unbindprice) { + mes "Unbinding an item has a fee of ^0000FF"+.unbindprice+"^000000 zeny."; + if(zeny < .unbindprice) { + mes "You don't have enough to unbind an item."; + close; + } + }*/ + getinventorylist(); + for(.@i = 0; .@i < @inventorylist_count; .@i++) { + if(@inventorylist_bound[.@i]) { + set .@bindlist$, .@bindlist$ + ":" + getitemname(@inventorylist_id[.@i]) + " - " + @inventorylist_id[.@i]; + set .@bindlist[.@j],.@i; + .@j++; + } + } + .@item = .@bindlist[select(.@bindlist$)-2]; + next; + for(.@i = 0; .@i < getarraysize(@bound_items); .@i++) { + if(@inventorylist_id[.@item] == @bound_items[.@i] && + (!getcharid(2) || getguildmaster(getcharid(2)) != strcharinfo(0)) + ) { + mes "I will only unbind guild bound items that the guild master requests."; + close; + } + } + mes "Before I continue, I want you to know I can't tell the difference between multiple items. If you have a specific item you want unbounded, please remove any duplicates from inventory."; + if(select("I understand, continue:Wait a minute") == 2) { + next; + mes "I'll be here when you're ready."; + close; + } + next; + mes "Are you sure you'd like to unbind your "+ getitemname(@inventorylist_id[.@item]) +"?"; + if(select("Yes:No") == 1) { + zeny -= .unbindprice; + delitem2 @inventorylist_id[.@item],@inventorylist_amount[.@item],@inventorylist_identify[.@item],@inventorylist_refine[.@item],@inventorylist_attribute[.@item],@inventorylist_card1[.@item],@inventorylist_card2[.@item],@inventorylist_card3[.@item],@inventorylist_card4[.@item]; + getitem2 @inventorylist_id[.@item],@inventorylist_amount[.@item],@inventorylist_identify[.@item],@inventorylist_refine[.@item],@inventorylist_attribute[.@item],@inventorylist_card1[.@item],@inventorylist_card2[.@item],@inventorylist_card3[.@item],@inventorylist_card4[.@item]; + mes "All done!"; + if(.logbinds) + logmes "Unbound "+ @inventorylist_amount[.@item]+" "+@inventorylist_id[.@item]+"."; + } + } + close; + +OnInit: + //* Configuration *\\ + //Price + .bindprice = 0; + .unbindprice = 100000; + + //What to allow to be bound - Add as necessary + //1 = Equipment - 2 = Consumables - 4 = Etc + .allowbind = 1; + + //Log binds via NPC? + .logbinds = 1; + + //Other stuff + .boundtypes$[1] = "account"; + .boundtypes$[2] = "guild"; + .boundtypes$[4] = "character"; + end; +} \ No newline at end of file diff --git a/npc/scripts_custom.conf b/npc/scripts_custom.conf index c6e550864..aeae9e22f 100644 --- a/npc/scripts_custom.conf +++ b/npc/scripts_custom.conf @@ -7,6 +7,7 @@ //npc: npc/location/to/script.txt // ----------------------- Basic Scripts ----------------------- +//npc: npc/custom/itembind.txt //npc: npc/custom/warper.txt //npc: npc/custom/jobmaster.txt //npc: npc/custom/platinum_skills.txt diff --git a/src/map/pc.c b/src/map/pc.c index ba445b4f4..6c4ebe149 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -10331,6 +10331,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->bound_chk = pc_bound_chk; pc->can_give_bounded_items = pc_can_give_bounded_items; pc->can_use_command = pc_can_use_command; -- cgit v1.2.3-60-g2f50 From a8ae62ebf1073501e0ff8a2bf159dc88a0a8f178 Mon Sep 17 00:00:00 2001 From: sevenzz23 Date: Fri, 1 Nov 2013 02:41:21 +0700 Subject: Deleted extra messages lines. Signed-off-by: sevenzz23 --- conf/messages.conf | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/conf/messages.conf b/conf/messages.conf index a20b1353b..54fd24fde 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -726,14 +726,13 @@ 982: Invalid color. -// @item / @itembound -983: Please enter an item name or ID (usage: @%s ). +// @item +983: Please enter an item name or ID (usage: @item ). -// @item2 / @itembound2 -984: Please enter all parameters (usage: @%s ). +// @item2 +984: Please enter all parameters (usage: @item2 ). 985: ). - // @baselevelup 986: Please enter a level adjustment (usage: @lvup/@blevel/@baselvlup ). @@ -1376,8 +1375,7 @@ 1361: Already using this font. // @new_mount -//1362: NOTICE: If you crash with mount your LUA is outdated. -1362: .. +1362: NOTICE: If you crash with mount your LUA is outdated. 1363: You have mounted. 1364: You have released your mount. @@ -1548,22 +1546,8 @@ 1486: Cannot transform into monster while in disguise. 1487: Character cannot be disguised while in monster form. 1488: Transforming into monster is not allowed in Guild Wars. - //CashShop mapflag 1489: Cash Shop is disabled in this map -// @autoloottype -1490: You're already autolooting this item type. -1491: Your autoloottype list has all item types. You can remove some items with @autoloottype -. -1492: Autolooting item type: '%s' {%d} -1493: You're currently not autolooting this item type. -1494: Removed item type: '%s' {%d} from your autoloottype list. -1495: To add an item type to the list, use "@aloottype +". To remove an item type, use "@aloottype -". -1496: Type List: healing = 0, usable = 2, etc = 3, weapon = 4, armor = 5, card = 6, petegg = 7, petarmor = 8, ammo = 10 -1497: "@aloottype reset" will clear your autoloottype list. -1498: Your autoloottype list is empty. -1499: Item types on your autoloottype list: -1500: Your autoloottype list has been reset. - //Custom translations import: conf/import/msg_conf.txt -- cgit v1.2.3-60-g2f50 From 6be40f56a21e48505baa4ff69acc198aa6206bf1 Mon Sep 17 00:00:00 2001 From: sevenzz23 Date: Tue, 5 Nov 2013 05:32:36 +0700 Subject: Fixed grammar Signed-off-by: sevenzz23 --- conf/messages.conf | 3 ++- npc/custom/itembind.txt | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/conf/messages.conf b/conf/messages.conf index 54fd24fde..a56a7a45b 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -316,7 +316,7 @@ 290: The player is no longer killable. 291: Weather effects will dispell on warp/refresh 292: Killer state reset. -//Item Bound System +//Item Bind System 293: This bounded item cannot be traded to that character. 294: This bounded item cannot be stored there. 295: Please enter an item name or ID (usage: @itembound ). @@ -1546,6 +1546,7 @@ 1486: Cannot transform into monster while in disguise. 1487: Character cannot be disguised while in monster form. 1488: Transforming into monster is not allowed in Guild Wars. + //CashShop mapflag 1489: Cash Shop is disabled in this map diff --git a/npc/custom/itembind.txt b/npc/custom/itembind.txt index f290c48ef..a95de3577 100644 --- a/npc/custom/itembind.txt +++ b/npc/custom/itembind.txt @@ -1,11 +1,12 @@ -//===== rAthena Script ======================================= -//= Item Bound Script +//===== Hercules Script ======================================= +//= Item Bind Script //===== By: ================================================== //= Akinari //===== Compatible With: ===================================== -//= Revision 17351+ (rAthena Only) +//= Revision 17351+ (rAthena) +//= Revision 12949+ (Hercules) //===== Description: ========================================= -//= Item Bound Script +//= Item Bind Script //= Allows users to pay a price to make an item bound to //= Account, Character, or Guild //============================================================ -- cgit v1.2.3-60-g2f50 From 52864b40d6f9df5682b257ac2f36d645895364be Mon Sep 17 00:00:00 2001 From: shennetsind Date: Wed, 6 Nov 2013 22:41:24 -0200 Subject: Update Fix Re-added the changes that were gone in the update conflict. Signed-off-by: shennetsind --- conf/messages.conf | 16 +++++++++++++++- src/common/mmo.h | 2 +- src/map/pc_groups.c | 1 + src/map/pc_groups.h | 1 + 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/conf/messages.conf b/conf/messages.conf index a9f0a5984..3564de043 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -316,6 +316,13 @@ 290: The player is no longer killable. 291: Weather effects will dispell on warp/refresh 292: Killer state reset. +//Item Bind System +293: This bounded item cannot be traded to that character. +294: This bounded item cannot be stored there. +295: Please enter an item name or ID (usage: @itembound ). +296: Please enter all parameters (usage: @itembound2 +297: ). +298: Invalid bound type. Valid types are - 1:Account 2:Guild 3:Party 4:Character // Guild Castles Number // -------------------- //299: ?? Castles @@ -455,6 +462,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 @@ -715,7 +729,7 @@ 983: Please enter an item name or ID (usage: @item ). // @item2 -984: Please enter all parameters (usage: @item2 +984: Please enter all parameters (usage: @item2 ). 985: ). // @baselevelup diff --git a/src/common/mmo.h b/src/common/mmo.h index b1a1caf24..f676893ea 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -222,7 +222,7 @@ struct item { char attribute; short card[MAX_SLOTS]; unsigned int expire_time; - char favorite; + char favorite, bound; uint64 unique_id; }; diff --git a/src/map/pc_groups.c b/src/map/pc_groups.c index 78d111b1c..c44e2634a 100644 --- a/src/map/pc_groups.c +++ b/src/map/pc_groups.c @@ -417,6 +417,7 @@ void do_init_pc_groups(void) { { "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 }, }; unsigned char i, len = ARRAYLENGTH(pc_g_defaults); diff --git a/src/map/pc_groups.h b/src/map/pc_groups.h index 28c82d619..df99b19df 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, }; // Cached config settings for quick lookup -- cgit v1.2.3-60-g2f50 From b9b32ac7609e33c21b488d722103cc0eea16b12c Mon Sep 17 00:00:00 2001 From: shennetsind Date: Thu, 7 Nov 2013 16:04:51 -0200 Subject: Item Bound Fixes/Adjustments/Improvements Special Thanks to Haruna Signed-off-by: shennetsind --- conf/groups.conf | 2 +- conf/messages.conf | 4 +- doc/permissions.txt | 2 +- sql-files/main.sql | 1 + sql-files/upgrades/2013-10-31--07-49.sql | 4 +- sql-files/upgrades/index.txt | 3 +- src/char/char.c | 32 ++-- src/char/int_storage.c | 58 ++++--- src/char/inter.c | 18 +-- src/common/mmo.h | 12 +- src/config/core.h | 3 +- src/map/atcommand.c | 20 +-- src/map/clif.c | 33 ++-- src/map/clif.h | 8 + src/map/guild.c | 39 ++--- src/map/guild.h | 2 - src/map/intif.c | 28 ++-- src/map/intif.h | 2 - src/map/mail.c | 6 +- src/map/packets_struct.h | 1 - src/map/party.c | 10 +- src/map/pc.c | 92 +++++++---- src/map/pc.h | 5 +- src/map/pc_groups.c | 2 +- src/map/pc_groups.h | 2 +- src/map/script.c | 252 ++++++++----------------------- src/map/script.h | 1 + src/map/storage.c | 8 +- src/map/trade.c | 23 +-- src/map/vending.c | 2 +- 30 files changed, 285 insertions(+), 390 deletions(-) diff --git a/conf/groups.conf b/conf/groups.conf index c1a8d10e6..d31f46f24 100644 --- a/conf/groups.conf +++ b/conf/groups.conf @@ -261,7 +261,7 @@ groups: ( log_commands: true permissions: { can_trade: true - can_trade_bounded: false + can_trade_bound: false can_party: true all_skill: false all_equipment: false diff --git a/conf/messages.conf b/conf/messages.conf index 3564de043..2d429d79e 100644 --- a/conf/messages.conf +++ b/conf/messages.conf @@ -317,8 +317,8 @@ 291: Weather effects will dispell on warp/refresh 292: Killer state reset. //Item Bind System -293: This bounded item cannot be traded to that character. -294: This bounded item cannot be stored there. +293: This bound item cannot be traded to that character. +294: This bound item cannot be stored there. 295: Please enter an item name or ID (usage: @itembound ). 296: Please enter all parameters (usage: @itembound2 297: ). diff --git a/doc/permissions.txt b/doc/permissions.txt index c2aeba081..7280395dc 100644 --- a/doc/permissions.txt +++ b/doc/permissions.txt @@ -31,5 +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...). +can_trade_bound: Ability to trade or otherwise distribute bound 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/sql-files/main.sql b/sql-files/main.sql index ddf3b6712..387d01893 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -666,6 +666,7 @@ INSERT INTO `sql_updates` (`timestamp`) VALUES (1381423003); INSERT INTO `sql_updates` (`timestamp`) VALUES (1382892428); INSERT INTO `sql_updates` (`timestamp`) VALUES (1383162785); INSERT INTO `sql_updates` (`timestamp`) VALUES (1383167577); +INSERT INTO `sql_updates` (`timestamp`) VALUES (1383205740); -- -- Table structure for table `sstatus` diff --git a/sql-files/upgrades/2013-10-31--07-49.sql b/sql-files/upgrades/2013-10-31--07-49.sql index 40e0421c8..e18c7e20d 100644 --- a/sql-files/upgrades/2013-10-31--07-49.sql +++ b/sql-files/upgrades/2013-10-31--07-49.sql @@ -1,4 +1,6 @@ +#1383205740 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`; -ALTER TABLE `guild_storage` ADD COLUMN `bound` TINYINT(3) UNSIGNED NOT NULL default '0' AFTER `expire_time`; \ No newline at end of file +ALTER TABLE `guild_storage` ADD COLUMN `bound` TINYINT(3) UNSIGNED NOT NULL default '0' AFTER `expire_time`; +INSERT INTO `sql_updates` (`timestamp`) VALUES (1383205740); \ No newline at end of file diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt index 1fc0ebd77..133366319 100644 --- a/sql-files/upgrades/index.txt +++ b/sql-files/upgrades/index.txt @@ -8,4 +8,5 @@ 2013-10-10--16-36.sql 2013-10-27--16-47.sql 2013-10-30--19-53.sql -2013-10-30--21-12.sql \ No newline at end of file +2013-10-30--21-12.sql +2013-10-31--07-49.sql \ No newline at end of file diff --git a/src/char/char.c b/src/char/char.c index 9e84f0245..ce4fcbe53 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -747,7 +747,7 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); 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_UINT, &item.bound, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &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); @@ -894,7 +894,7 @@ 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); + SQL->StmtBindColumn(stmt, 9, SQLDT_UCHAR, &item.bound, 0, NULL, NULL); for( j = 0; j < MAX_SLOTS; ++j ) SQL->StmtBindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); @@ -1243,17 +1243,17 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SQL->StmtExecute(stmt) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &tmp_item.equip, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL) - || 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_CHAR, &tmp_item.favorite, 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) ) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &tmp_item.id, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_SHORT, &tmp_item.nameid, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_SHORT, &tmp_item.amount, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_UINT, &tmp_item.equip, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL) + || 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_CHAR, &tmp_item.favorite, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UCHAR, &tmp_item.bound, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_UINT64, &tmp_item.unique_id, 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_SLOTS; ++i ) if( SQL_ERROR == SQL->StmtBindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) @@ -1283,8 +1283,8 @@ 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_CHAR, &tmp_item.bound, 0, NULL, NULL) - || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &tmp_item.bound, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &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) ) @@ -2959,7 +2959,7 @@ int parse_frommap(int fd) break; } //Check account only if this ain't final save. Final-save goes through because of the char-map reconnect - if (RFIFOB(fd,12) || RFIFOB(fd,13) || ( + if (RFIFOB(fd,12) || ( (character = (struct online_char_data*)idb_get(online_char_db, aid)) != NULL && character->char_id == cid)) { diff --git a/src/char/int_storage.c b/src/char/int_storage.c index 6443aa743..30671df5e 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -107,8 +107,7 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p) StrBuf->Destroy(&buf); - for( i = 0; i < MAX_GUILD_STORAGE && SQL_SUCCESS == SQL->NextRow(sql_handle); ++i ) - { + for( i = 0; i < MAX_GUILD_STORAGE && SQL_SUCCESS == SQL->NextRow(sql_handle); ++i ) { item = &p->items[i]; SQL->GetData(sql_handle, 0, &data, NULL); item->id = atoi(data); SQL->GetData(sql_handle, 1, &data, NULL); item->nameid = atoi(data); @@ -120,9 +119,8 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p) SQL->GetData(sql_handle, 7, &data, NULL); item->bound = atoi(data); SQL->GetData(sql_handle, 8, &data, NULL); item->unique_id = strtoull(data, NULL, 10); item->expire_time = 0; - item->bound = 0; - for( j = 0; j < MAX_SLOTS; ++j ) - { + + for( j = 0; j < MAX_SLOTS; ++j ) { SQL->GetData(sql_handle, 9+j, &data, NULL); item->card[j] = atoi(data); } } @@ -239,14 +237,15 @@ int mapif_parse_SaveGuildStorage(int fd) return 0; } -#ifdef BOUND_ITEMS int mapif_itembound_ack(int fd, int aid, int guild_id) { +#ifdef GP_BOUND_ITEMS WFIFOHEAD(fd,8); WFIFOW(fd,0) = 0x3856; WFIFOL(fd,2) = aid; WFIFOW(fd,6) = guild_id; WFIFOSET(fd,8); +#endif return 0; } @@ -254,23 +253,23 @@ int mapif_itembound_ack(int fd, int aid, int guild_id) //Guild bound items pull for offline characters [Akinari] //Revised by [Mhalicot] //------------------------------------------------ -int mapif_parse_itembound_retrieve(int fd) +int mapif_parse_ItemBoundRetrieve(int fd) { +#ifdef GP_BOUND_ITEMS StringBuf buf; SqlStmt* stmt; struct item item; int j, i=0, s; - bool found=false; struct item items[MAX_INVENTORY]; int char_id = RFIFOL(fd,2); int aid = RFIFOL(fd,6); int guild_id = RFIFOW(fd,10); StrBuf->Init(&buf); - StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`"); + 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`='%d'",inventory_db,char_id); + StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`='%d' AND `bound` = '%d'",inventory_db,char_id,IBT_GUILD); stmt = SQL->StmtMalloc(sql_handle); if( SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) @@ -290,15 +289,14 @@ int mapif_parse_itembound_retrieve(int fd) SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); 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_UINT, &item.bound, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 8, SQLDT_UCHAR, &item.bound, 0, NULL, NULL); + SQL->StmtBindColumn(stmt, 9, SQLDT_UINT64, &item.unique_id, 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); while( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) { - if(item.bound == 2) { - memcpy(&items[i],&item,sizeof(struct item)); - i++; - } + memcpy(&items[i],&item,sizeof(struct item)); + i++; } SQL->FreeResult(sql_handle); @@ -312,10 +310,9 @@ int mapif_parse_itembound_retrieve(int fd) StrBuf->Clear(&buf); StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE",inventory_db); for(j=0; jAppendStr(&buf, " OR"); - else - found = true; + StrBuf->Printf(&buf, " `id`=%d",items[j].id); } @@ -329,21 +326,18 @@ int mapif_parse_itembound_retrieve(int fd) } //Now let's update the guild storage with those deleted items - found = false; StrBuf->Clear(&buf); - StrBuf->Printf(&buf, "INSERT INTO `%s` (`guild_id`, `nameid`, `amount`, `identify`, `refine`, `attribute`, `expire_time`, `bound`", guild_storage_db); + StrBuf->Printf(&buf, "INSERT INTO `%s` (`guild_id`, `nameid`, `amount`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", guild_storage_db); for( j = 0; j < MAX_SLOTS; ++j ) StrBuf->Printf(&buf, ", `card%d`", j); StrBuf->AppendStr(&buf, ") VALUES "); for( j = 0; j < i; ++j ) { - if( found ) + if( j ) StrBuf->AppendStr(&buf, ","); - else - found = true; StrBuf->Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d'", - guild_id, items[j].nameid, items[j].amount, items[j].identify, items[j].refine, items[j].attribute, items[j].expire_time, items[j].bound); + guild_id, items[j].nameid, items[j].amount, items[j].identify, items[j].refine, items[j].attribute, items[j].expire_time, items[j].bound, items[j].unique_id); for( s = 0; s < MAX_SLOTS; ++s ) StrBuf->Printf(&buf, ", '%d'", items[j].card[s]); StrBuf->AppendStr(&buf, ")"); @@ -364,21 +358,21 @@ int mapif_parse_itembound_retrieve(int fd) //Finally reload storage and tell map we're done mapif_load_guild_storage(fd,aid,guild_id,0); mapif_itembound_ack(fd,aid,guild_id); +#endif return 0; } -#endif int inter_storage_parse_frommap(int fd) { RFIFOHEAD(fd); switch(RFIFOW(fd,0)){ - case 0x3018: mapif_parse_LoadGuildStorage(fd); break; - case 0x3019: mapif_parse_SaveGuildStorage(fd); break; -#ifdef BOUND_ITEMS - case 0x3056: mapif_parse_itembound_retrieve(fd); break; + case 0x3018: mapif_parse_LoadGuildStorage(fd); break; + case 0x3019: mapif_parse_SaveGuildStorage(fd); break; +#ifdef GP_BOUND_ITEMS + case 0x3056: mapif_parse_ItemBoundRetrieve(fd); break; #endif - default: - return 0; + default: + return 0; } return 1; } diff --git a/src/char/inter.c b/src/char/inter.c index 54672faee..b213f1608 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -48,15 +48,15 @@ unsigned int party_share_level = 10; // recv. packet list int inter_recv_packet_length[] = { -1,-1, 7,-1, -1,13,36, (2 + 4 + 4 + 4 + NAME_LENGTH), 0, 0, 0, 0, 0, 0, 0, 0, // 3000- - 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- - -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party - -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- - -1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- - -1,-1,10,10, 0,-1, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus] [Mhalicot] - 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish] - -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil] - 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080- - -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator] + 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- + -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party + -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- + -1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- + -1,-1,10,10, 0,-1,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus], Item Bound [Mhalicot] + 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish] + -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil] + 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080- + -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator] }; struct WisData { diff --git a/src/common/mmo.h b/src/common/mmo.h index f676893ea..369f5c894 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -222,7 +222,8 @@ struct item { char attribute; short card[MAX_SLOTS]; unsigned int expire_time; - char favorite, bound; + char favorite; + unsigned char bound; uint64 unique_id; }; @@ -249,6 +250,15 @@ enum e_mmo_charstatus_opt { OPT_ALLOW_PARTY = 0x2, }; +enum e_item_bound_type { + IBT_MIN = 0x1, + IBT_ACCOUNT = 0x1, + IBT_GUILD = 0x2, + IBT_PARTY = 0x3, + IBT_CHARACTER = 0x4, + IBT_MAX = 0x4, +}; + struct s_skill { unsigned short id; unsigned char lv; diff --git a/src/config/core.h b/src/config/core.h index 481c35af8..daadc6455 100644 --- a/src/config/core.h +++ b/src/config/core.h @@ -62,8 +62,7 @@ //#define NSI_UNIQUE_ID /// Comment to disable Guild/Party Bound item system -/// By default, we recover/remove Guild/Party Bound items automatically -#define BOUND_ITEMS +#define GP_BOUND_ITEMS /// Uncomment to enable real-time server stats (in and out data and ram usage). [Ai4rei] //#define SHOW_SERVER_STATS diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 3f3e9679e..e19428d36 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -1104,7 +1104,7 @@ ACMD(item) sscanf(message, "%99s %d %d", item_name, &number, &bound) < 2 ))) { clif->message(fd, msg_txt(295)); // Please enter an item name or ID (usage: @itembound ). - return -1; + return false; } else if (!message || !*message || ( sscanf(message, "\"%99[^\"]\" %d", item_name, &number) < 1 && sscanf(message, "%99s %d", item_name, &number) < 1 )) @@ -1123,7 +1123,7 @@ ACMD(item) return false; } - if( bound < 0 || bound > 4 ) { + if( bound < IBT_MIN || bound > IBT_MAX ) { clif->message(fd, msg_txt(298)); // Invalid bound type return false; } @@ -1137,9 +1137,6 @@ ACMD(item) 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) { @@ -1148,7 +1145,7 @@ ACMD(item) memset(&item_tmp, 0, sizeof(item_tmp)); item_tmp.nameid = item_id; item_tmp.identify = 1; - item_tmp.bound = bound; + item_tmp.bound = (unsigned char)bound; if ((flag = pc->additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND))) clif->additem(sd, 0, 0, flag); @@ -1192,9 +1189,9 @@ ACMD(item2) if (number <= 0) number = 1; - if( bound < 0 || bound > 4 ) { + if( bound < IBT_MIN || bound > IBT_MAX ) { clif->message(fd, msg_txt(298)); // Invalid bound type - return -1; + return false; } item_id = 0; @@ -1225,10 +1222,6 @@ 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; } @@ -1238,11 +1231,12 @@ ACMD(item2) item_tmp.identify = identify; item_tmp.refine = refine; item_tmp.attribute = attr; + item_tmp.bound = (unsigned char)bound; item_tmp.card[0] = c1; 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); } diff --git a/src/map/clif.c b/src/map/clif.c index 04614158a..fecf0be0e 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1802,13 +1802,13 @@ 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 || (sd->status.inventory[i].bound && !pc->can_give_bounded_items(sd)) ) - continue; // Cannot Sell Rental Items or Account Bounded Items + if( sd->status.inventory[i].expire_time ) + continue; // Cannot Sell Rental Items if( sd->status.inventory[i].bound && !pc->can_give_bounded_items(sd)) continue; // Don't allow sale of bound items - val=sd->inventory_data[i]->value_sell; + val=sd->inventory_data[i]->value_sell; if( val < 0 ) continue; WFIFOW(fd,4+c*10)=i+2; @@ -2232,7 +2232,10 @@ 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 = sd->status.inventory[n].bound ? 2 : 0; + /* why restrict the flag to non-stackable? because this is the only packet allows stackable to, + * show the color, and therefore it'd be inconsistent with the rest (aka it'd show yellow, you relog/refresh and boom its gone) + */ + p.bindOnEquipType = sd->status.inventory[n].bound && !itemdb->isstackable2(sd->inventory_data[n]) ? 2 : 0; #endif } p.result = (unsigned char)fail; @@ -2381,7 +2384,6 @@ void clif_item_normal(short idx, struct NORMALITEM_INFO *p, struct item *i, stru #if PACKETVER >= 20080102 p->HireExpireDate = i->expire_time; - p->bindOnEquipType = i->bound ? 2 : 0; #endif #if PACKETVER >= 20120925 @@ -15067,8 +15069,8 @@ void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) 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 + !itemdb_canauction(&sd->status.inventory[idx],pc->get_group_level(sd)) || // Quest Item or something else + (sd->status.inventory[idx].bound && !pc->can_give_bounded_items(sd)) ) { clif->auction_setitem(sd->fd, idx, true); return; } @@ -15145,13 +15147,11 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd) return; } - // Auction checks... - if( sd->status.inventory[sd->auction.index].bound && !pc->can_give_bounded_items(sd) ) { - clif->message(sd->fd, msg_txt(293)); - clif->auction_message(fd, 2); // The auction has been canceled - return; + if( sd->status.zeny < (auction.hours * battle_config.auction_feeperhour) ) { + clif_Auction_message(fd, 5); // You do not have enough zeny to pay the Auction Fee. + return; } - + if( auction.buynow > battle_config.auction_maximumprice ) { // Zeny Limits auction.buynow = battle_config.auction_maximumprice; @@ -15177,6 +15177,13 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd) return; } + // Auction checks... + if( sd->status.inventory[sd->auction.index].bound && !pc->can_give_bounded_items(sd) ) { + clif->message(sd->fd, msg_txt(293)); + clif->auction_message(fd, 2); // The auction has been canceled + return; + } + safestrncpy(auction.item_name, item->jname, sizeof(auction.item_name)); auction.type = item->type; memcpy(&auction.item, &sd->status.inventory[sd->auction.index], sizeof(struct item)); diff --git a/src/map/clif.h b/src/map/clif.h index 710cb6590..cc222d8aa 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -450,6 +450,14 @@ enum e_UNEQUIP_ITEM_ACK { #endif }; +enum e_trade_item_ok { + TIO_SUCCESS = 0x0, + TIO_OVERWEIGHT = 0x1, + TIO_CANCEL = 0x2, + /* feedback-friendly code that causes the client not to display a error message */ + TIO_INDROCKS = 0x9, +}; + /** * Structures **/ diff --git a/src/map/guild.c b/src/map/guild.c index 719d6bf69..cba568bd8 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -866,7 +866,7 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c if(online_member_sd == NULL) return 0; // noone online to inform -#ifdef BOUND_ITEMS +#ifdef GP_BOUND_ITEMS //Guild bound item check guild->retrieveitembound(char_id,account_id,guild_id); #endif @@ -904,25 +904,12 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c return 0; } -#ifdef BOUND_ITEMS -void guild_retrieveitembound(int char_id,int aid,int guild_id) -{ +void guild_retrieveitembound(int char_id,int aid,int guild_id) { +#ifdef GP_BOUND_ITEMS TBL_PC *sd = map->id2sd(aid); if(sd){ //Character is online - int idxlist[MAX_INVENTORY]; - int j,i; - j = pc->bound_chk(sd,2,idxlist); - if(j) { - struct guild_storage *gstor = gstorage->id2storage(guild_id); - for(i=0;iadditem(sd,gstor,&sd->status.inventory[idxlist[i]],sd->status.inventory[idxlist[i]].amount); - pc->delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,4,LOG_TYPE_GSTORAGE); - } - gstorage->close(sd); //Close and save the storage - } - } - else { //Character is offline, ask char server to do the job + pc->bound_clear(sd,IBT_GUILD); + } else { //Character is offline, ask char server to do the job struct guild_storage *gstor = gstorage->id2storage2(guild_id); if(gstor && gstor->storage_status == 1) { //Someone is in guild storage, close them struct s_mapiterator* iter = mapit_getallusers(); @@ -936,8 +923,8 @@ void guild_retrieveitembound(int char_id,int aid,int guild_id) } intif->itembound_req(char_id,aid,guild_id); } -} #endif +} int guild_send_memberinfoshort(struct map_session_data *sd,int online) { // cleaned up [LuzZza] @@ -1854,11 +1841,6 @@ int guild_break(struct map_session_data *sd,char *name) { struct unit_data *ud; int i; -#ifdef BOUND_ITEMS - int j; - int idxlist[MAX_INVENTORY]; -#endif - nullpo_ret(sd); if( (g=sd->guild)==NULL ) @@ -1901,11 +1883,8 @@ int guild_break(struct map_session_data *sd,char *name) { } } -#ifdef BOUND_ITEMS - //Guild bound item check - Removes the bound flag - j = pc->bound_chk(sd,2,idxlist); - for(i=0;istatus.inventory[idxlist[i]].bound = 0; +#ifdef GP_BOUND_ITEMS + pc->bound_clear(sd,IBT_GUILD); #endif intif->guild_break(g->guild_id); @@ -2380,4 +2359,6 @@ void guild_defaults(void) { guild->check_member = guild_check_member; guild->get_alliance_count = guild_get_alliance_count; guild->castle_reconnect_sub = guild_castle_reconnect_sub; + /* */ + guild->retrieveitembound = guild_retrieveitembound; } diff --git a/src/map/guild.h b/src/map/guild.h index 9326d8d5a..1a04a98ef 100644 --- a/src/map/guild.h +++ b/src/map/guild.h @@ -148,9 +148,7 @@ struct guild_interface { /* guild aura */ void (*aura_refresh) (struct map_session_data *sd, uint16 skill_id, uint16 skill_lv); /* item bound [Mhalicot]*/ -#ifdef BOUND_ITEMS void (*retrieveitembound) (int char_id,int aid,int guild_id); -#endif /* */ int (*payexp_timer) (int tid, int64 tick, int id, intptr_t data); TBL_PC* (*sd_check) (int guild_id, int account_id, int char_id); diff --git a/src/map/intif.c b/src/map/intif.c index b8b16a356..5cf385fe4 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -994,7 +994,6 @@ void intif_parse_LoadGuildStorage(int fd) ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); return; } - } gstor=gstorage->id2storage(guild_id); if(!gstor) { @@ -1002,12 +1001,12 @@ void intif_parse_LoadGuildStorage(int fd) return; } if (gstor->storage_status == 1) { // Already open.. lets ignore this update - ShowWarning("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", flag?sd->status.account_id:1, flag?sd->status.char_id:1); - return; + ShowWarning("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", flag?sd->status.account_id:0, flag?sd->status.char_id:0); + return; } if (gstor->dirty) { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex] - ShowWarning("intif_parse_LoadGuildStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", flag?sd->status.account_id:1, flag?sd->status.char_id:1); - return; + ShowWarning("intif_parse_LoadGuildStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", flag?sd->status.account_id:0, flag?sd->status.char_id:0); + return; } if( RFIFOW(fd,2)-13 != sizeof(struct guild_storage) ){ ShowError("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-13 , sizeof(struct guild_storage)); @@ -1017,7 +1016,7 @@ void intif_parse_LoadGuildStorage(int fd) memcpy(gstor,RFIFOP(fd,13),sizeof(struct guild_storage)); if( flag ) - gstorage->open(sd); + gstorage->open(sd); } // ACK guild_storage saved @@ -2013,8 +2012,8 @@ void intif_parse_MessageToFD(int fd) { /*========================================== * Item Bound System [Xantara][Mhalicot] *------------------------------------------*/ -#ifdef BOUND_ITEMS void intif_itembound_req(int char_id,int aid,int guild_id) { +#ifdef GP_BOUND_ITEMS struct guild_storage *gstor = gstorage->id2storage2(guild_id); WFIFOHEAD(inter_fd,12); WFIFOW(inter_fd,0) = 0x3056; @@ -2024,17 +2023,20 @@ void intif_itembound_req(int char_id,int aid,int guild_id) { WFIFOSET(inter_fd,12); if(gstor) gstor->lock = 1; //Lock for retrieval process +#endif } //3856 void intif_parse_Itembound_ack(int fd) { +#ifdef GP_BOUND_ITEMS struct guild_storage *gstor; int guild_id = RFIFOW(fd,6); gstor = gstorage->id2storage2(guild_id); - if(gstor) gstor->lock = 0; //Unlock now that operation is completed -} + if(gstor) + gstor->lock = 0; //Unlock now that operation is completed #endif +} //----------------------------------------------------------------- // Communication from the inter server // Return a 0 (false) if there were any errors. @@ -2118,9 +2120,9 @@ int intif_parse(int fd) case 0x3854: intif->pAuctionMessage(fd); break; case 0x3855: intif->pAuctionBid(fd); break; //Bound items -#ifdef BOUND_ITEMS +#ifdef GP_BOUND_ITEMS case 0x3856: intif->pItembound_ack(fd); break; -#endif +#endif // Mercenary System case 0x3870: intif->pMercenaryReceived(fd); break; case 0x3871: intif->pMercenaryDeleted(fd); break; @@ -2160,7 +2162,7 @@ void intif_defaults(void) { 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830 -1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840 -1,-1, 7, 7, 7,11, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] itembound[Akinari] - -1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish] + -1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish] -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil] 11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880 -1,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] @@ -2248,6 +2250,8 @@ void intif_defaults(void) { /* */ intif->CheckForCharServer = CheckForCharServer; /* */ + intif->itembound_req = intif_itembound_req; + /* parse functions */ intif->pWisMessage = intif_parse_WisMessage; intif->pWisEnd = intif_parse_WisEnd; intif->pWisToGM_sub = mapif_parse_WisToGM_sub; diff --git a/src/map/intif.h b/src/map/intif.h index 577d58923..5e996b6fe 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -75,9 +75,7 @@ struct intif_interface { int (*guild_emblem) (int guild_id, int len, const char *data); int (*guild_castle_dataload) (int num, int *castle_ids); int (*guild_castle_datasave) (int castle_id, int index, int value); -#ifdef BOUND_ITEMS void (*itembound_req) (int char_id, int aid, int guild_id); -#endif int (*request_petdata) (int account_id, int char_id, int pet_id); int (*save_petdata) (int account_id, struct s_pet *p); int (*delete_petdata) (int pet_id); diff --git a/src/map/mail.c b/src/map/mail.c index 6b1537d87..76c94879b 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -82,9 +82,9 @@ 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)) || - (sd->status.inventory[idx].bound && !pc->can_give_bounded_items(sd)) ) - return 1; + !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; sd->mail.nameid = sd->status.inventory[idx].nameid; diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index e6f68ea4f..813aebee0 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -212,7 +212,6 @@ struct NORMALITEM_INFO { #endif #if PACKETVER >= 20080102 int HireExpireDate; - unsigned short bindOnEquipType; #endif #if PACKETVER >= 20120925 struct { diff --git a/src/map/party.c b/src/map/party.c index 16b9d99f9..0a7467162 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -547,14 +547,10 @@ int party_member_withdraw(int party_id, int account_id, int char_id) } if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) { -#ifdef BOUND_ITEMS - int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion - int j,i; - j = pc->bound_chk(sd,3,idxlist); - for(i=0;idelitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER); +#ifdef GP_BOUND_ITEMS + pc->bound_clear(sd,IBT_PARTY); #endif - sd->status.party_id = 0; + sd->status.party_id = 0; clif->charnameupdate(sd); //Update name display [Skotlex] //TODO: hp bars should be cleared too if( p->instances ) diff --git a/src/map/pc.c b/src/map/pc.c index 31a38372a..752151888 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -573,7 +573,7 @@ bool pc_can_give_items(struct map_session_data *sd) */ bool pc_can_give_bounded_items(struct map_session_data *sd) { - return pc->has_permission(sd, PC_PERM_TRADE_BOUNDED); + return pc->has_permission(sd, PC_PERM_TRADE_BOUND); } /*========================================== @@ -999,10 +999,6 @@ int pc_isequip(struct map_session_data *sd,int n) *------------------------------------------*/ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, struct mmo_charstatus *st, bool changing_mapservers) { int i; -#ifdef BOUND_ITEMS - int j; - int idxlist[MAX_INVENTORY]; -#endif int64 tick = timer->gettick(); uint32 ip = session[sd->fd]->client_addr; @@ -1203,12 +1199,9 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim **/ pc->itemcd_do(sd,true); -#ifdef BOUND_ITEMS - // Party bound item check - if(sd->status.party_id == 0 && (j = pc->bound_chk(sd,3,idxlist))) { // Party was deleted while character offline - for(i=0;idelitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER); - } +#ifdef GP_BOUND_ITEMS + if( sd->status.party_id == 0 ) + pc->bound_clear(sd,IBT_PARTY); #endif /* [Ind/Hercules] */ @@ -3962,6 +3955,28 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l if(sd->weight + w > sd->max_weight) return 2; + if( item_data->bound ) { + switch( (enum e_item_bound_type)item_data->bound ) { + case IBT_CHARACTER: + case IBT_ACCOUNT: + break; /* no restrictions */ + case IBT_PARTY: + if( !sd->status.party_id ) { + ShowError("pc_additem: can't add party_bound item to character without party!\n"); + ShowError("pc_additem: %s - x%d %s (%d)\n",sd->status.name,amount,data->jname,data->nameid); + return 7;/* need proper code? */ + } + break; + case IBT_GUILD: + if( !sd->status.guild_id ) { + ShowError("pc_additem: can't add guild_bound item to character without guild!\n"); + ShowError("pc_additem: %s - x%d %s (%d)\n",sd->status.name,amount,data->jname,data->nameid); + return 7;/* need proper code? */ + } + break; + } + } + i = MAX_INVENTORY; if( itemdb->isstackable2(data) && item_data->expire_time == 0 ) @@ -3969,7 +3984,7 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l for( i = 0; i < MAX_INVENTORY; i++ ) { 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; sd->status.inventory[i].amount += amount; @@ -4520,7 +4535,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun return 1; } - if( !itemdb_cancartstore(item_data, pc->get_group_level(sd)) || (item_data->bound > 1 && !pc->can_give_bounded_items(sd))) + if( !itemdb_cancartstore(item_data, pc->get_group_level(sd)) || (item_data->bound > IBT_ACCOUNT && !pc->can_give_bounded_items(sd))) { // Check item trade restrictions [Skotlex] clif->message (sd->fd, msg_txt(264)); return 1;/* TODO: there is no official response to this? */ @@ -4667,24 +4682,38 @@ int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount) return flag; } - /*========================================== - * Bound Item Check - * Type: - * 1 Account Bound - * 2 Guild Bound - * 3 Party Bound - * 4 Character Bound - *------------------------------------------*/ -int pc_bound_chk(TBL_PC *sd,int type,int *idxlist) -{ - int i=0, j=0; - for(i=0;istatus.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0 && sd->status.inventory[i].bound == type) { - idxlist[j] = i; - j++; - } +void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type type) { + int i; + + switch( type ) { + /* both restricted to inventory */ + case IBT_PARTY: + case IBT_CHARACTER: + for( i = 0; i < MAX_INVENTORY; i++ ){ + if( sd->status.inventory[i].bound == type ) { + pc->delitem(sd,i,sd->status.inventory[i].amount,0,1,LOG_TYPE_OTHER); + } + } + break; + case IBT_ACCOUNT: + ShowError("Helllo! You reached pc_bound_clear for IBT_ACCOUNT, unfortunately no scenario was expected for this!\n"); + break; + case IBT_GUILD: { + struct guild_storage *gstor = gstorage->id2storage(sd->status.guild_id); + + for( i = 0; i < MAX_INVENTORY; i++ ){ + if(sd->status.inventory[i].bound == type) { + if( gstor ) + gstorage->additem(sd,gstor,&sd->status.inventory[i],sd->status.inventory[i].amount); + pc->delitem(sd,i,sd->status.inventory[i].amount,0,1,gstor?LOG_TYPE_GSTORAGE:LOG_TYPE_OTHER); + } + } + if( gstor ) + gstorage->close(sd); + } + break; } - return j; + } /*========================================== * Display item stolen msg to player sd @@ -10366,7 +10395,6 @@ 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->bound_chk = pc_bound_chk; pc->can_give_bounded_items = pc_can_give_bounded_items; pc->can_use_command = pc_can_use_command; @@ -10600,4 +10628,6 @@ void pc_defaults(void) { pc->rental_expire = pc_rental_expire; pc->scdata_received = pc_scdata_received; + + pc->bound_clear = pc_bound_clear; } diff --git a/src/map/pc.h b/src/map/pc.h index 3645fc599..93463d19b 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -791,9 +791,6 @@ struct pc_interface { int (*getzeny) (struct map_session_data *sd,int zeny, enum e_log_pick_type type, struct map_session_data *tsd); int (*delitem) (struct map_session_data *sd,int n,int amount,int type, short reason, e_log_pick_type log_type); - //Bound items - int (*bound_chk) (TBL_PC *sd,int type,int *idxlist); - // Special Shop System int (*paycash) (struct map_session_data *sd, int price, int points); int (*getcash) (struct map_session_data *sd, int cash, int points); @@ -986,6 +983,8 @@ struct pc_interface { void (*rental_expire) (struct map_session_data *sd, int i); void (*scdata_received) (struct map_session_data *sd); + + void (*bound_clear) (struct map_session_data *sd, enum e_item_bound_type type); }; struct pc_interface *pc; diff --git a/src/map/pc_groups.c b/src/map/pc_groups.c index c44e2634a..f95878e97 100644 --- a/src/map/pc_groups.c +++ b/src/map/pc_groups.c @@ -417,7 +417,7 @@ void do_init_pc_groups(void) { { "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 }, + { "can_trade_bound", PC_PERM_TRADE_BOUND }, }; unsigned char i, len = ARRAYLENGTH(pc_g_defaults); diff --git a/src/map/pc_groups.h b/src/map/pc_groups.h index df99b19df..943fb7fa5 100644 --- a/src/map/pc_groups.h +++ b/src/map/pc_groups.h @@ -30,7 +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, + PC_PERM_TRADE_BOUND = 0x400000, }; // Cached config settings for quick lookup diff --git a/src/map/script.c b/src/map/script.c index 6dce018ef..cf3c3fb50 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -3779,6 +3779,17 @@ int script_reload(void) { return 0; } +/* returns name of current function being run, from within the stack [Ind/Hercules] */ +const char *script_getfuncname(struct script_state *st) { + struct script_data *data; + + data = &st->stack->stack_data[st->start]; + + if( data->type == C_NAME && script->str_data[data->u.num].type == C_FUNC ) + return script->get_str(data->u.num); + + return NULL; +} //----------------------------------------------------------------------------- // buildin functions @@ -5763,10 +5774,13 @@ BUILDIN(checkweight2) /*========================================== * getitem ,{,}; * getitem "",{,}; + * + * getitembound ,,{,}; + * getitembound "",,{,}; *------------------------------------------*/ BUILDIN(getitem) { - int nameid,amount,get_count,i,flag = 0; + int nameid,amount,get_count,i,flag = 0, offset = 0; struct item it; TBL_PC *sd; struct script_data *data; @@ -5778,7 +5792,7 @@ BUILDIN(getitem) {// "" const char *name=script->conv_str(st,data); if( (item_data = itemdb->search_name(name)) == NULL ){ - ShowError("buildin_getitem: Nonexistant item %s requested.\n", name); + ShowError("buildin_%s: Nonexistant item %s requested.\n", script->getfuncname(st), name); return false; //No item created. } nameid=item_data->nameid; @@ -5790,11 +5804,11 @@ BUILDIN(getitem) flag = 1; } if( nameid <= 0 || !(item_data = itemdb->exists(nameid)) ){ - ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid); + ShowError("buildin_%s: Nonexistant item %d requested.\n", script->getfuncname(st), nameid); return false; //No item created. } } else { - ShowError("buildin_getitem: invalid data type for argument #1 (%d).", data->type); + ShowError("buildin_%s: invalid data type for argument #1 (%d).", script->getfuncname(st), data->type); return false; } @@ -5804,13 +5818,28 @@ BUILDIN(getitem) memset(&it,0,sizeof(it)); it.nameid=nameid; + if(!flag) it.identify=1; else it.identify=itemdb->isidentified2(item_data); - if( script_hasdata(st,4) ) - sd=map->id2sd(script_getnum(st,4)); // + if( !strcmp(script->getfuncname(st),"getitembound") ) { + int bound = script_getnum(st,4); + if( bound < IBT_MIN || bound > IBT_MAX ) { //Not a correct bound type + ShowError("script_getitembound: Not a correct bound type! Type=%d\n",bound); + return false; + } + if( item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR ) { + ShowError("script_getitembound: can't bind a pet egg/armor!\n",bound); + return false; + } + it.bound = (unsigned char)bound; + offset += 1; + } + + if( script_hasdata(st,4+offset) ) + sd=map->id2sd(script_getnum(st,4+offset)); // else sd=script->rid2sd(st); // Attached player @@ -5842,15 +5871,24 @@ BUILDIN(getitem) *------------------------------------------*/ BUILDIN(getitem2) { - int nameid,amount,get_count,i,flag = 0; - int iden,ref,attr,c1,c2,c3,c4; + int nameid,amount,get_count,i,flag = 0, offset = 0; + int iden,ref,attr,c1,c2,c3,c4, bound = 0; struct item_data *item_data; struct item item_tmp; TBL_PC *sd; struct script_data *data; - if( script_hasdata(st,11) ) - sd=map->id2sd(script_getnum(st,11)); // + if( !strcmp(script->getfuncname(st),"getitembound2") ) { + bound = script_getnum(st,11); + if( bound < IBT_MIN || bound > IBT_MAX ) { //Not a correct bound type + ShowError("script_getitembound2: Not a correct bound type! Type=%d\n",bound); + return false; + } + offset += 1; + } + + if( script_hasdata(st,11+offset) ) + sd=map->id2sd(script_getnum(st,11+offset)); // else sd=script->rid2sd(st); // Attached player @@ -5878,6 +5916,11 @@ BUILDIN(getitem2) c3=(short)script_getnum(st,9); c4=(short)script_getnum(st,10); + if( bound && (itemdb_type(nameid) == IT_PETEGG || itemdb_type(nameid) == IT_PETARMOR) ) { + ShowError("script_getitembound2: can't bind a pet egg/armor!\n",bound); + return false; + } + if(nameid<0) { // Invalide nameid nameid = -nameid; flag = 1; @@ -5907,6 +5950,7 @@ BUILDIN(getitem2) item_tmp.identify=0; item_tmp.refine=ref; item_tmp.attribute=attr; + item_tmp.bound=(unsigned char)bound; item_tmp.card[0]=(short)c1; item_tmp.card[1]=(short)c2; item_tmp.card[2]=(short)c3; @@ -17562,185 +17606,6 @@ BUILDIN(bg_join_team) { return true; } - -/*==============[Mhalicot]================== - * getitembound ,,{,}; - * getitembound "",,{,}; - * Type: - * 1 - Account Bound - * 2 - Guild Bound - * 3 - Party Bound - * 4 - Character Bound - *------------------------------------------*/ -BUILDIN(getitembound) -{ - int nameid, amount, i, flag; - struct item it; - struct script_data *data; - char bound = script_getnum(st,4); - 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.identify = 1; - it.bound = bound; - - if( bound < 1 || bound > 4) { //Not a correct bound type - ShowError("script_getitembound: Not a correct bound type! Type=%d\n",bound); - return 1; - } - - if( script_hasdata(st,5) ) - sd=map->id2sd(script_getnum(st,5)); // 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; -} - -/*==============[Mhalicot]================== - * getitembound2 ,,,,,,,,,; - * getitembound2 "",,,,,,,,,; - *------------------------------------------*/ -BUILDIN(getitembound2) -{ - int nameid,amount,get_count,i,flag = 0; - int iden,ref,attr,c1,c2,c3,c4; - char bound=0; - struct item_data *item_data; - struct item item_tmp; - TBL_PC *sd; - struct script_data *data; - - bound = script_getnum(st,11); - if( bound < 1 || bound > 4) { //Not a correct bound type - ShowError("script_getitembound2: Not a correct bound type! Type=%d\n",bound); - return 1; - } - if( script_hasdata(st,12) ) - sd=map->id2sd(script_getnum(st,12)); - else - sd=script->rid2sd(st); // Attached player - - if( sd == NULL ) // no target - return true; - - data=script_getdata(st,2); - script->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) { // Invalide nameid - nameid = -nameid; - flag = 1; - } - - if(nameid > 0) { - memset(&item_tmp,0,sizeof(item_tmp)); - item_data=itemdb->exists(nameid); - if (item_data == NULL) - return -1; - if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR){ - if(ref > MAX_REFINE) ref = MAX_REFINE; - } - else if(item_data->type==IT_PETEGG) { - iden = 1; - ref = 0; - } - else { - iden = 1; - ref = attr = 0; - } - - item_tmp.nameid=nameid; - if(!flag) - item_tmp.identify=iden; - else if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR) - item_tmp.identify=0; - 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=bound; - - //Check if it's stackable. - if (!itemdb->isstackable(nameid)) - get_count = 1; - else - get_count = amount; - - for (i = 0; i < amount; i += get_count) { - // if not pet egg - if (!pet->create_egg(sd, nameid)) { - if ((flag = pc->additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) { - clif->additem(sd, 0, 0, flag); - if( pc->candrop(sd,&item_tmp) ) - map->addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); - } - } - } - } - - return true; -} /*==============[Mhalicot]================== * countbound {}; * Creates an array of bounded item IDs @@ -17749,6 +17614,7 @@ BUILDIN(getitembound2) * 1 - Account Bound * 2 - Guild Bound * 3 - Party Bound + * 4 - Character Bound *------------------------------------------*/ BUILDIN(countbound) { @@ -18355,11 +18221,12 @@ void script_parse_builtin(void) { BUILDIN_DEF(bindatcmd, "ss???"), BUILDIN_DEF(unbindatcmd, "s"), BUILDIN_DEF(useatcmd, "s"), + /** - * Item bound [Mhalicot\Hercules] + * Item bound [Xantara] [Akinari] [Mhalicot/Hercules] **/ - BUILDIN_DEF(getitembound,"vii?"), - BUILDIN_DEF(getitembound2,"viiiiiiiii?"), + BUILDIN_DEF2(getitem,"getitembound","vii?"), + BUILDIN_DEF2(getitem2,"getitembound2","viiiiiiiii?"), BUILDIN_DEF(countbound, "?"), //Quest Log System [Inkfish] @@ -18652,6 +18519,7 @@ void script_defaults(void) { script->buildin_mobuseskill_sub = buildin_mobuseskill_sub; script->cleanfloor_sub = script_cleanfloor_sub; script->run_func = run_func; + script->getfuncname = script_getfuncname; /* script_config base */ script->config.warn_func_mismatch_argtypes = 1; diff --git a/src/map/script.h b/src/map/script.h index 6aebc8a30..a846365dd 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -630,6 +630,7 @@ struct script_interface { int (*buildin_mobuseskill_sub) (struct block_list *bl, va_list ap); int (*cleanfloor_sub) (struct block_list *bl, va_list ap); int (*run_func) (struct script_state *st); + const char *(*getfuncname) (struct script_state *st); }; struct script_interface *script; diff --git a/src/map/storage.c b/src/map/storage.c index cffbf23ec..0a2e6d118 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -141,7 +141,7 @@ int storage_additem(struct map_session_data* sd, struct item* item_data, int amo return 1; } - if( (item_data->bound > 1) && !pc->can_give_bounded_items(sd) ) { + if( item_data->bound > IBT_ACCOUNT && !pc->can_give_bounded_items(sd) ) { clif->message(sd->fd, msg_txt(294)); return 1; } @@ -435,13 +435,13 @@ 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 || (item_data->bound && !pc->can_give_bounded_items(sd)) ) + if( !itemdb_canguildstore(item_data, pc->get_group_level(sd)) || item_data->expire_time ) { //Check if item is storable. [Skotlex] clif->message (sd->fd, msg_txt(264)); return 1; } - if( (item_data->bound == 1 || item_data->bound > 2) && !pc->can_give_bounded_items(sd) ) { + if( item_data->bound && item_data->bound != IBT_GUILD && !pc->can_give_bounded_items(sd) ) { clif->message(sd->fd, msg_txt(294)); return 1; } @@ -531,6 +531,8 @@ int storage_guild_storageadd(struct map_session_data* sd, int index, int amount) if(gstorage->additem(sd,stor,&sd->status.inventory[index],amount)==0) pc->delitem(sd,index,amount,0,4,LOG_TYPE_GSTORAGE); + else + clif->dropitem(sd, index,0); return 1; } diff --git a/src/map/trade.c b/src/map/trade.c index 7085fdda3..327d19880 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -335,7 +335,7 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount) if( amount == 0 ) { //Why do this.. ~.~ just send an ack, the item won't display on the trade window. - clif->tradeitemok(sd, index, 0); + clif->tradeitemok(sd, index, TIO_SUCCESS); return; } @@ -354,35 +354,38 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount) (pc->get_partner(sd) != target_sd || !itemdb_canpartnertrade(item, src_lv, dst_lv)) ) //Can't partner-trade { clif->message (sd->fd, msg_txt(260)); - clif->tradeitemok(sd, index+2, 1); + clif->tradeitemok(sd, index+2, TIO_INDROCKS); return; } if( item->expire_time ) { // Rental System clif->message (sd->fd, msg_txt(260)); - clif->tradeitemok(sd, index+2, 1); + clif->tradeitemok(sd, index+2, TIO_INDROCKS); return; } - if( ((item->bound == 1 || item->bound > 2) || (item->bound == 2 && sd->status.guild_id != target_sd->status.guild_id)) && !pc->can_give_bounded_items(sd) ) { // Item Bound + if( item->bound && + !( item->bound == IBT_GUILD && sd->status.guild_id == target_sd->status.guild_id ) && + !( item->bound == IBT_PARTY && sd->status.party_id == target_sd->status.party_id ) + && !pc->can_give_bounded_items(sd) ) { clif->message(sd->fd, msg_txt(293)); - clif->tradeitemok(sd, index+2, 1); + clif->tradeitemok(sd, index+2, TIO_INDROCKS); 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 { - clif->tradeitemok(sd, index+2, 1); + clif->tradeitemok(sd, index+2, TIO_OVERWEIGHT); return; } trade_weight = sd->inventory_data[index]->weight * amount; if( target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight ) { //fail to add item -- the player was over weighted. - clif->tradeitemok(sd, index+2, 1); + clif->tradeitemok(sd, index+2, TIO_OVERWEIGHT); return; } @@ -402,7 +405,7 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount) } sd->deal.weight += trade_weight; - clif->tradeitemok(sd, index+2, 0); // Return the index as it was received + clif->tradeitemok(sd, index+2, TIO_SUCCESS); // Return the index as it was received clif->tradeadditem(sd, target_sd, index+2, amount); } @@ -446,7 +449,7 @@ void trade_tradeok(struct map_session_data *sd) { return; } sd->state.deal_locked = 1; - clif->tradeitemok(sd, 0, 0); + clif->tradeitemok(sd, 0, TIO_SUCCESS); clif->tradedeal_lock(sd, 0); clif->tradedeal_lock(target_sd, 1); } diff --git a/src/map/vending.c b/src/map/vending.c index 14a5e64d1..d5e5d1804 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -257,7 +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 + || (sd->status.cart[index].bound && !pc->can_give_bounded_items(sd)) // can't trade bound items w/o permission || !itemdb_cantrade(&sd->status.cart[index], pc->get_group_level(sd), pc->get_group_level(sd)) ) // untradeable item continue; -- cgit v1.2.3-60-g2f50 From 8083daec0d94f78510411c9b225abcfa7e829e95 Mon Sep 17 00:00:00 2001 From: shennetsind Date: Thu, 7 Nov 2013 17:05:56 -0200 Subject: Further item bind adjustments Thanks to Haruna! Signed-off-by: shennetsind --- sql-files/upgrades/2013-10-31--07-49.sql | 2 +- src/map/atcommand.c | 4 ++-- src/map/buyingstore.c | 2 +- src/map/clif.c | 6 +++--- src/map/intif.c | 6 +++++- src/map/mail.c | 2 +- src/map/pc.c | 8 ++++---- src/map/pc.h | 2 +- src/map/storage.c | 4 ++-- src/map/trade.c | 2 +- src/map/vending.c | 2 +- 11 files changed, 22 insertions(+), 18 deletions(-) diff --git a/sql-files/upgrades/2013-10-31--07-49.sql b/sql-files/upgrades/2013-10-31--07-49.sql index e18c7e20d..f0c51b0d9 100644 --- a/sql-files/upgrades/2013-10-31--07-49.sql +++ b/sql-files/upgrades/2013-10-31--07-49.sql @@ -2,5 +2,5 @@ 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`; -ALTER TABLE `guild_storage` ADD COLUMN `bound` TINYINT(3) UNSIGNED NOT NULL default '0' AFTER `expire_time`; +ALTER TABLE `guild_storage` ADD COLUMN `bound` TINYINT(1) UNSIGNED NOT NULL default '0' AFTER `expire_time`; INSERT INTO `sql_updates` (`timestamp`) VALUES (1383205740); \ No newline at end of file diff --git a/src/map/atcommand.c b/src/map/atcommand.c index e19428d36..316e5a39b 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -1123,7 +1123,7 @@ ACMD(item) return false; } - if( bound < IBT_MIN || bound > IBT_MAX ) { + if(!strcmpi(command+1,"itembound") && !(bound >= IBT_MIN && bound <= IBT_MAX) ) { clif->message(fd, msg_txt(298)); // Invalid bound type return false; } @@ -1189,7 +1189,7 @@ ACMD(item2) if (number <= 0) number = 1; - if( bound < IBT_MIN || bound > IBT_MAX ) { + if( !strcmpi(command+1,"itembound2") && !(bound >= IBT_MIN && bound <= IBT_MAX) ) { clif->message(fd, msg_txt(298)); // Invalid bound type return false; } diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index 44ece49c6..a9f1412ed 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 || (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)) ) + if( sd->status.inventory[index].expire_time || (sd->status.inventory[index].bound && !pc->can_give_bound_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 fecf0be0e..70b81ffab 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1805,7 +1805,7 @@ void clif_selllist(struct map_session_data *sd) if( sd->status.inventory[i].expire_time ) continue; // Cannot Sell Rental Items - if( sd->status.inventory[i].bound && !pc->can_give_bounded_items(sd)) + if( sd->status.inventory[i].bound && !pc->can_give_bound_items(sd)) continue; // Don't allow sale of bound items val=sd->inventory_data[i]->value_sell; @@ -15070,7 +15070,7 @@ void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) 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 - (sd->status.inventory[idx].bound && !pc->can_give_bounded_items(sd)) ) { + (sd->status.inventory[idx].bound && !pc->can_give_bound_items(sd)) ) { clif->auction_setitem(sd->fd, idx, true); return; } @@ -15178,7 +15178,7 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd) } // Auction checks... - if( sd->status.inventory[sd->auction.index].bound && !pc->can_give_bounded_items(sd) ) { + if( sd->status.inventory[sd->auction.index].bound && !pc->can_give_bound_items(sd) ) { clif->message(sd->fd, msg_txt(293)); clif->auction_message(fd, 2); // The auction has been canceled return; diff --git a/src/map/intif.c b/src/map/intif.c index 5cf385fe4..e6ff91af7 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -2120,9 +2120,13 @@ int intif_parse(int fd) case 0x3854: intif->pAuctionMessage(fd); break; case 0x3855: intif->pAuctionBid(fd); break; //Bound items + case 0x3856: #ifdef GP_BOUND_ITEMS - case 0x3856: intif->pItembound_ack(fd); break; + intif->pItembound_ack(fd); +#else + ShowWarning("intif_parse: Received 0x3856 with GP_BOUND_ITEMS disabled !!!\n") #endif + break; // Mercenary System case 0x3870: intif->pMercenaryReceived(fd); break; case 0x3871: intif->pMercenaryDeleted(fd); break; diff --git a/src/map/mail.c b/src/map/mail.c index 76c94879b..007f7592d 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -83,7 +83,7 @@ unsigned char mail_setitem(struct map_session_data *sd, int idx, int 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)) || - (sd->status.inventory[idx].bound && !pc->can_give_bounded_items(sd)) ) + (sd->status.inventory[idx].bound && !pc->can_give_bound_items(sd)) ) return 1; sd->mail.index = idx; diff --git a/src/map/pc.c b/src/map/pc.c index 752151888..07fba6e57 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -571,7 +571,7 @@ bool pc_can_give_items(struct map_session_data *sd) /** * Determines if player can give / drop / trade / vend bounded items */ -bool pc_can_give_bounded_items(struct map_session_data *sd) +bool pc_can_give_bound_items(struct map_session_data *sd) { return pc->has_permission(sd, PC_PERM_TRADE_BOUND); } @@ -4535,7 +4535,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun return 1; } - if( !itemdb_cancartstore(item_data, pc->get_group_level(sd)) || (item_data->bound > IBT_ACCOUNT && !pc->can_give_bounded_items(sd))) + if( !itemdb_cancartstore(item_data, pc->get_group_level(sd)) || (item_data->bound > IBT_ACCOUNT && !pc->can_give_bound_items(sd))) { // Check item trade restrictions [Skotlex] clif->message (sd->fd, msg_txt(264)); return 1;/* TODO: there is no official response to this? */ @@ -8058,7 +8058,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 || (item->bound && !pc->can_give_bounded_items(sd))) ) + if( item && (item->expire_time || (item->bound && !pc->can_give_bound_items(sd))) ) return 0; if( !pc->can_give_items(sd) ) //check if this GM level can drop items return 0; @@ -10395,7 +10395,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_give_bound_items = pc_can_give_bound_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 93463d19b..32ca214c0 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -750,7 +750,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_give_bound_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, unsigned int permission); diff --git a/src/map/storage.c b/src/map/storage.c index 0a2e6d118..db85b3c98 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -141,7 +141,7 @@ int storage_additem(struct map_session_data* sd, struct item* item_data, int amo return 1; } - if( item_data->bound > IBT_ACCOUNT && !pc->can_give_bounded_items(sd) ) { + if( item_data->bound > IBT_ACCOUNT && !pc->can_give_bound_items(sd) ) { clif->message(sd->fd, msg_txt(294)); return 1; } @@ -441,7 +441,7 @@ int guild_storage_additem(struct map_session_data* sd, struct guild_storage* sto return 1; } - if( item_data->bound && item_data->bound != IBT_GUILD && !pc->can_give_bounded_items(sd) ) { + if( item_data->bound && item_data->bound != IBT_GUILD && !pc->can_give_bound_items(sd) ) { clif->message(sd->fd, msg_txt(294)); return 1; } diff --git a/src/map/trade.c b/src/map/trade.c index 327d19880..ffd1336f5 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -368,7 +368,7 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount) if( item->bound && !( item->bound == IBT_GUILD && sd->status.guild_id == target_sd->status.guild_id ) && !( item->bound == IBT_PARTY && sd->status.party_id == target_sd->status.party_id ) - && !pc->can_give_bounded_items(sd) ) { + && !pc->can_give_bound_items(sd) ) { clif->message(sd->fd, msg_txt(293)); clif->tradeitemok(sd, index+2, TIO_INDROCKS); return; diff --git a/src/map/vending.c b/src/map/vending.c index d5e5d1804..f16a208e4 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -257,7 +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 bound items w/o permission + || (sd->status.cart[index].bound && !pc->can_give_bound_items(sd)) // can't trade bound items w/o permission || !itemdb_cantrade(&sd->status.cart[index], pc->get_group_level(sd), pc->get_group_level(sd)) ) // untradeable item continue; -- cgit v1.2.3-60-g2f50