diff --git a/conf/msg_conf/map_msg.conf b/conf/msg_conf/map_msg.conf index 57ec9d1a90..21cc6dd571 100644 --- a/conf/msg_conf/map_msg.conf +++ b/conf/msg_conf/map_msg.conf @@ -310,6 +310,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 // Guild Castles Number // -------------------- //299: ?? Castles diff --git a/doc/atcommands.txt b/doc/atcommands.txt index 1067ee4531..cf0ed2aaa1 100644 --- a/doc/atcommands.txt +++ b/doc/atcommands.txt @@ -642,6 +642,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 + +--------------------------------------- + +@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 + --------------------------------------- @produce <# of Very's> diff --git a/doc/permissions.txt b/doc/permissions.txt index 0b6b686e63..dc04ca5006 100644 --- a/doc/permissions.txt +++ b/doc/permissions.txt @@ -172,3 +172,9 @@ Allows player to use the client command /check (displays character status). Allows player to use the client command /changemaptype. --------------------------------------- + +*can_trade_bounded + +Allows player to do normal item actions (sell, trade, store, vend, auction, mail) with bounded items. + +--------------------------------------- diff --git a/doc/script_commands.txt b/doc/script_commands.txt index e9d5d0d754..a221211b41 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -2687,6 +2687,7 @@ recreate these items perfectly if they are destroyed. Here's what you get: @inventorylist_card4[] if the character owns an item made by a specific craftsman. @inventorylist_expire[] - expire time (Unix time stamp). 0 means never expires. +@inventorylist_bound[] - whether it is bound to the character @inventorylist_count - the number of items in these lists. This could be handy to save/restore a character's inventory, since no other @@ -4248,6 +4249,46 @@ 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 will give an amount of specified items to the invoking character. +If an optional account ID is specified, and the target character is currently +online, items will be created in their inventory instead. If they are not +online, nothing will happen. + +It works essentially the same as 'getitem', except that items created using +this command will bound the item to the player's account. Depending on the bound type issued, +the character may not be able to trade or store these items, however all bounded types +cannot be sold, vended, auctioned, or mailed. + +Bound Types: + 1 - Account Bound + 2 - Guild Bound + 3 - Party Bound + +--------------------------------------- + +*getitembound2 ,,,,,,,,,{,}; +*getitembound2 "",,,,,,,,,{,}; + +This command will give an amount of specified items to the invoking character. +If an optional account ID is specified, and the target character is currently +online, items will be created in their inventory instead. If they are not +online, nothing will happen. + +It works essentially the same as 'getitem2', except that items created using +this command will bound the item to the player's account. Depending on the bound type issued, +the character may not be able to trade or store these items, however all bounded types +cannot be sold, vended, auctioned, or mailed. + +Bound Types: + 1 - Account Bound + 2 - Guild Bound + 3 - Party Bound + --------------------------------------- *getnameditem ,; @@ -4386,6 +4427,30 @@ Expanded version of 'countitem' function, used for created/carded/forged items. This function will return the number of items for the specified item ID and other parameters that the invoking character has in the inventory. Check 'getitem2' to understand the arguments of the function. + +--------------------------------------- + +*countbound({}) + +This function will return a number of bounded items on the character. +Optionally, you may count a specific type of bounded item on the character. + +countbound will also build an array of items in the form of @bound_items. +Using countbound without a type counts all types of bounded items. + +Available types are: + 1 - Account Bound + 2 - Guild Bound + 3 - Party Bound + +Example: + mes "[Bound Counter]"; + mes "I see you currently have "+countbound+" bounded items."; + next; + mes "The list of bounded items include:"; + for(.@i = 0; .@ichar_id == cid)) { diff --git a/src/char/int_mail.c b/src/char/int_mail.c index 8d50c713fb..28f46c4b82 100644 --- a/src/char/int_mail.c +++ b/src/char/int_mail.c @@ -63,6 +63,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++) { @@ -183,6 +184,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 78852420ef..f3cb5bb14a 100644 --- a/src/char/int_storage.c +++ b/src/char/int_storage.c @@ -38,7 +38,7 @@ int storage_fromsql(int account_id, struct storage_data* p) // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} StringBuf_Init(&buf); - StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`unique_id`"); + StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`bound`,`unique_id`"); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ",`card%d`", j); StringBuf_Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id); @@ -59,10 +59,11 @@ int storage_fromsql(int account_id, struct storage_data* p) Sql_GetData(sql_handle, 5, &data, NULL); item->refine = atoi(data); Sql_GetData(sql_handle, 6, &data, NULL); item->attribute = atoi(data); Sql_GetData(sql_handle, 7, &data, NULL); item->expire_time = (unsigned int)atoi(data); - Sql_GetData(sql_handle, 8, &data, NULL); item->unique_id = strtoull(data, NULL, 10); + Sql_GetData(sql_handle, 8, &data, NULL); item->bound = atoi(data); + Sql_GetData(sql_handle, 9, &data, NULL); item->unique_id = strtoull(data, NULL, 10); for( j = 0; j < MAX_SLOTS; ++j ) { - Sql_GetData(sql_handle, 9+j, &data, NULL); item->card[j] = atoi(data); + Sql_GetData(sql_handle, 10+j, &data, NULL); item->card[j] = atoi(data); } } p->storage_amount = i; @@ -95,7 +96,7 @@ 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`} StringBuf_Init(&buf); - StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`unique_id`"); + StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`bound`,`unique_id`"); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ",`card%d`", j); StringBuf_Printf(&buf, " FROM `%s` WHERE `guild_id`='%d' ORDER BY `nameid`", guild_storage_db, guild_id); @@ -115,11 +116,12 @@ 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); + 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; 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; @@ -158,18 +160,19 @@ 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)); + 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; } @@ -200,7 +203,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; } @@ -234,6 +237,126 @@ int mapif_parse_SaveGuildStorage(int fd) return 0; } +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] +//------------------------------------------------ +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); + + StringBuf_Init(&buf); + StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`"); + for( j = 0; j < MAX_SLOTS; ++j ) + StringBuf_Printf(&buf, ", `card%d`", j); + StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'",inventory_db,char_id); + + stmt = SqlStmt_Malloc(sql_handle); + if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf)) + || SQL_ERROR == SqlStmt_Execute(stmt) ) + { + SqlStmt_ShowDebug(stmt); + SqlStmt_Free(stmt); + StringBuf_Destroy(&buf); + return 1; + } + + SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 3, SQLDT_USHORT, &item.equip, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 8, SQLDT_UINT, &item.bound, 0, NULL, NULL); + for( j = 0; j < MAX_SLOTS; ++j ) + SqlStmt_BindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); + + while( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) { + if(item.bound == 2) { + memcpy(&items[i],&item,sizeof(struct item)); + i++; + } + } + + if(!i) //No items found - No need to continue + return 0; + + //First we delete the character's items + StringBuf_Clear(&buf); + StringBuf_Printf(&buf, "DELETE FROM `%s` WHERE",inventory_db); + for(j=0; j ) (modified by [Yor] for pet_egg) + * @itembound command (usage: @itembound ) *------------------------------------------*/ ACMD_FUNC(item) { char item_name[100]; - int number = 0, item_id, flag = 0; + int number = 0, item_id, flag = 0, bound = 0; struct item item_tmp; struct item_data *item_data; int get_count, i; @@ -1124,9 +1125,15 @@ ACMD_FUNC(item) 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 + 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_displaymessage(fd, msg_txt(sd,295)); // Please enter an item name or ID (usage: @item ). + return -1; + } else if (!message || !*message || ( + sscanf(message, "\"%99[^\"]\" %d", item_name, &number) < 1 && + sscanf(message, "%99s %d", item_name, &number) < 1 )) { clif_displaymessage(fd, msg_txt(sd,983)); // Please enter an item name or ID (usage: @item ). return -1; @@ -1142,6 +1149,11 @@ ACMD_FUNC(item) return -1; } + if( bound < 0 || bound > 3 ) { + clif_displaymessage(fd, msg_txt(sd,298)); // Invalid bound type + return -1; + } + item_id = item_data->nameid; get_count = number; //Check if it's stackable. @@ -1154,6 +1166,7 @@ ACMD_FUNC(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); @@ -1173,17 +1186,23 @@ ACMD_FUNC(item2) struct item item_tmp; struct item_data *item_data; char item_name[100]; - int item_id, number = 0; + int item_id, number = 0, bound = 0; int identify = 0, refine = 0, attr = 0; int c1 = 0, c2 = 0, c3 = 0, c4 = 0; nullpo_retr(-1, sd); memset(item_name, '\0', sizeof(item_name)); - if (!message || !*message || ( + 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_displaymessage(fd, msg_txt(sd,296)); // Please enter all parameters (usage: @item2 + clif_displaymessage(fd, msg_txt(sd,297)); // ). + return -1; + } 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_displaymessage(fd, msg_txt(sd,984)); // Please enter all parameters (usage: @item2 clif_displaymessage(fd, msg_txt(sd,985)); // ). return -1; @@ -1192,6 +1211,11 @@ ACMD_FUNC(item2) if (number <= 0) number = 1; + if( bound < 0 || bound > 3 ) { + clif_displaymessage(fd, msg_txt(sd,298)); // Invalid bound type + return -1; + } + item_id = 0; if ((item_data = itemdb_searchname(item_name)) != NULL || (item_data = itemdb_exists(atoi(item_name))) != NULL) @@ -1228,6 +1252,7 @@ ACMD_FUNC(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); } @@ -9012,6 +9037,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 f4905f5f17..8a472d1b55 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -311,7 +311,7 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int return; } - if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) + if( sd->status.inventory[index].expire_time || (sd->status.inventory[index].bound && !pc_can_give_bounded_items(sd)) || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) {// non-tradable item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; diff --git a/src/map/clif.c b/src/map/clif.c index a71f9be980..0078982d25 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1790,8 +1790,11 @@ 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; if( val < 0 ) @@ -2194,7 +2197,7 @@ void clif_additem(struct map_session_data *sd, int n, int amount, int fail) WFIFOL(fd,offs+23)=sd->status.inventory[n].expire_time; #endif #if PACKETVER >= 20071002 - WFIFOW(fd,offs+27)=0; // HireExpireDate + WFIFOW(fd,offs+27)=sd->status.inventory[n].bound ? 2 : 0; #endif } @@ -2300,7 +2303,7 @@ void clif_item_sub(unsigned char *buf, int n, int idx, struct item *i, struct it clif_addcards(WBUFP(buf, n+12), i); //8B #if PACKETVER >= 20071002 WBUFL(buf,n+20)=i->expire_time; - WBUFW(buf,n+24)=0; //Unknown + WBUFW(buf,n+24)=i->bound ? 2 : 0; #endif #if PACKETVER >= 20100629 WBUFW(buf,n+26)= (id->equip&EQP_VISIBLE)?id->look:0; @@ -14199,6 +14202,11 @@ 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) ) { + clif_displaymessage(sd->fd, msg_txt(sd,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; diff --git a/src/map/guild.h b/src/map/guild.h index 0df93d1380..5306aac3ed 100644 --- a/src/map/guild.h +++ b/src/map/guild.h @@ -106,6 +106,7 @@ void guild_flag_remove(struct npc_data *nd); void guild_flags_clear(void); void guild_guildaura_refresh(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv); +void guild_retrieveitembound(int char_id,int aid,int guild_id); void do_final_guild(void); diff --git a/src/map/intif.c b/src/map/intif.c index ed5547a204..90807c31ca 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -39,7 +39,7 @@ static const int packet_len_table[]={ 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,-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 @@ -1002,15 +1002,19 @@ int 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 1; + sd=map_id2sd( RFIFOL(fd,4) ); - if(sd==NULL){ - ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); - return 1; + 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 1; + } } gstor=guild2storage(guild_id); if(!gstor) { @@ -1018,21 +1022,23 @@ int intif_parse_LoadGuildStorage(int fd) return 1; } 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); + 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 1; } 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); + 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 1; } - 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)); + 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 1; } - memcpy(gstor,RFIFOP(fd,12),sizeof(struct guild_storage)); - storage_guild_storageopen(sd); + memcpy(gstor,RFIFOP(fd,13),sizeof(struct guild_storage)); + if( flag ) + storage_guild_storageopen(sd); + return 0; } @@ -2152,6 +2158,31 @@ void intif_parse_MessageToFD(int fd) { return; } +/*========================================== + * Item Bound System + *------------------------------------------*/ + +void intif_itembound_req(int char_id,int aid,int guild_id) { + struct guild_storage *gstor = guild2storage2(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(char_fd,6); + + gstor = guild2storage2(guild_id); + if(gstor) gstor->lock = 0; //Unlock now that operation is completed +} + //----------------------------------------------------------------- // Communication from the inter server // Return a 0 (false) if there were any errors. @@ -2235,6 +2266,9 @@ int intif_parse(int fd) case 0x3854: intif_parse_Auction_message(fd); break; case 0x3855: intif_parse_Auction_bid(fd); break; +//Bound items + case 0x3856: intif_parse_itembound_ack(fd); break; + // Mercenary System case 0x3870: intif_parse_mercenary_received(fd); break; case 0x3871: intif_parse_mercenary_deleted(fd); break; diff --git a/src/map/intif.h b/src/map/intif.h index 75b48d6255..7e53ed4220 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -60,6 +60,7 @@ int intif_guild_notice(int guild_id, const char *mes1, const char *mes2); int intif_guild_emblem(int guild_id, int len, const char *data); int intif_guild_castle_dataload(int num, int *castle_ids); int intif_guild_castle_datasave(int castle_id, int index, int value); +void intif_itembound_req(int char_id, int aid, int guild_id); int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id, short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name); diff --git a/src/map/mail.c b/src/map/mail.c index 03b8227b52..904f593d6c 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -78,8 +78,9 @@ unsigned char mail_setitem(struct map_session_data *sd, int idx, int amount) { return 1; if( amount < 0 || amount > sd->status.inventory[idx].amount ) return 1; - if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time || - !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) ) + if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time + || !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) + || (sd->status.inventory[idx].bound && !pc_can_give_bounded_items(sd)) ) return 1; sd->mail.index = idx; diff --git a/src/map/party.c b/src/map/party.c index 9f66104879..12b912d3a7 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -552,12 +552,10 @@ int party_member_withdraw(int party_id, int account_id, int char_id) struct map_session_data* sd = map_id2sd(account_id); struct party_data* p = party_search(party_id); - if( p ) - { + if( p ) { int i; ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id ); - if( i < MAX_PARTY ) - { + if( i < MAX_PARTY ) { clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0); memset(&p->party.member[i], 0, sizeof(p->party.member[0])); memset(&p->data[i], 0, sizeof(p->data[0])); @@ -566,8 +564,12 @@ 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 ) - { + if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) { + int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion + int j,i; + j = pc_bound_chk(sd,3,idxlist); + for(i=0;istatus.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER); sd->status.party_id = 0; clif_charnameupdate(sd); //Update name display [Skotlex] //TODO: hp bars should be cleared too diff --git a/src/map/pc.c b/src/map/pc.c index ba58563e2f..0d9f85300c 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -509,6 +509,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. *------------------------------------------*/ @@ -914,7 +922,8 @@ 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; + int i, j; + int idxlist[MAX_INVENTORY]; unsigned long tick = gettick(); uint32 ip = session[sd->fd]->client_addr; @@ -1091,6 +1100,12 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim **/ pc_itemcd_do(sd,true); + // 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;istatus.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER); + } + // Request all registries (auth is considered completed whence they arrive) intif_request_registry(sd,7); return true; @@ -3878,7 +3893,7 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l { // Stackable | Non Rental for( i = 0; i < MAX_INVENTORY; i++ ) { - if( sd->status.inventory[i].nameid == item_data->nameid && memcmp(&sd->status.inventory[i].card, &item_data->card, sizeof(item_data->card)) == 0 ) + if( sd->status.inventory[i].nameid == item_data->nameid && sd->status.inventory[i].bound == item_data->bound && memcmp(&sd->status.inventory[i].card, &item_data->card, sizeof(item_data->card)) == 0 ) { if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->status.inventory[i].amount ) ) return 5; @@ -4418,7 +4433,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)) ) + if( !itemdb_cancartstore(item_data, pc_get_group_level(sd)) || ((item_data->bound == 2 || item_data->bound == 3) && !pc_can_give_bounded_items(sd))) { // Check item trade restrictions [Skotlex] clif_displaymessage (sd->fd, msg_txt(sd,264)); return 1; @@ -4431,7 +4446,7 @@ 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].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] ); }; @@ -4566,6 +4581,25 @@ int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount) return 1; } +/*========================================== + * Bound Item Check + * Type: + * 1 Account Bound + * 2 Guild Bound + * 3 Party 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 *------------------------------------------*/ @@ -7880,7 +7914,7 @@ int pc_setmadogear(TBL_PC* sd, int flag) *------------------------------------------*/ int pc_candrop(struct map_session_data *sd, struct item *item) { - if( item && item->expire_time ) + if( item && (item->expire_time || (item->bound && !pc_can_give_bounded_items(sd))) ) return 0; if( !pc_can_give_items(sd) ) //check if this GM level can drop items return 0; diff --git a/src/map/pc.h b/src/map/pc.h index 1a8a5ec1f4..7aabcda2cd 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -705,6 +705,7 @@ int pc_get_group_level(struct map_session_data *sd); int pc_get_group_id(struct map_session_data *sd); int pc_getrefinebonus(int lv,int type); bool pc_can_give_items(struct map_session_data *sd); +bool pc_can_give_bounded_items(struct map_session_data *sd); bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type); #define pc_has_permission(sd, permission) ( ((sd)->permissions&permission) != 0 ) @@ -748,6 +749,9 @@ int pc_additem(struct map_session_data*,struct item*,int,e_log_pick_type); int pc_getzeny(struct map_session_data*,int, enum e_log_pick_type, struct map_session_data*); int pc_delitem(struct map_session_data*,int,int,int,short,e_log_pick_type); +//Bound items +int pc_bound_chk(TBL_PC *sd,int type,int *idxlist); + // Special Shop System int pc_paycash( struct map_session_data *sd, int price, int points, e_log_pick_type type ); int pc_getcash( struct map_session_data *sd, int cash, int points, e_log_pick_type type ); diff --git a/src/map/pc_groups.h b/src/map/pc_groups.h index 9918913561..3307eb927c 100644 --- a/src/map/pc_groups.h +++ b/src/map/pc_groups.h @@ -44,6 +44,7 @@ enum e_pc_permission { PC_PERM_DISABLE_PVP = 0x080000, PC_PERM_DISABLE_CMD_DEAD = 0x100000, PC_PERM_CHANNEL_ADMIN = 0x200000, + PC_PERM_TRADE_BOUNDED = 0x400000, }; static const struct { @@ -72,6 +73,7 @@ static const struct { { "disable_pvp", PC_PERM_DISABLE_PVP }, { "disable_commands_when_dead", PC_PERM_DISABLE_CMD_DEAD }, { "channel_admin", PC_PERM_CHANNEL_ADMIN }, + { "can_trade_bounded", PC_PERM_TRADE_BOUNDED }, }; #endif // _PC_GROUPS_H_ diff --git a/src/map/script.c b/src/map/script.c index 831336ae76..9d2ce581a0 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -6270,6 +6270,13 @@ BUILDIN_FUNC(checkweight2){ /*========================================== * getitem ,{,}; * getitem "",{,}; + * + * getitembound ,,{,}; + * getitembound "",,{,}; + * Type: + * 1 - Account Bound + * 2 - Guild Bound + * 3 - Party Bound *------------------------------------------*/ BUILDIN_FUNC(getitem) { @@ -6280,8 +6287,7 @@ BUILDIN_FUNC(getitem) data=script_getdata(st,2); get_val(st,data); - if( data_isstring(data) ) - {// "" + if( data_isstring(data) ) {// "" const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); if( item_data == NULL ){ @@ -6289,8 +6295,7 @@ BUILDIN_FUNC(getitem) return 1; //No item created. } nameid=item_data->nameid; - } else if( data_isint(data) ) - {// + } else if( data_isint(data) ) {// nameid=conv_num(st,data); //Violet Box, Blue Box, etc - random item pick if( nameid < 0 ) { @@ -6317,7 +6322,18 @@ BUILDIN_FUNC(getitem) else it.identify=itemdb_isidentified(nameid); - if( script_hasdata(st,4) ) + if( !strcmp(script_getfuncname(st),"getitembound") ) { + char bound = script_getnum(st,4); + if( bound < 1 || bound > 3) { //Not a correct bound type + ShowError("script_getitembound: Not a correct bound type! Type=%d\n",bound); + return 1; + } + it.bound = bound; + if( script_hasdata(st,5) ) + sd=map_id2sd(script_getnum(st,5)); + else + sd=script_rid2sd(st); // Attached player + } else if( script_hasdata(st,4) ) sd=map_id2sd(script_getnum(st,4)); // else sd=script_rid2sd(st); // Attached player @@ -6355,12 +6371,23 @@ BUILDIN_FUNC(getitem2) { 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; - if( script_hasdata(st,11) ) + if( !strcmp(script_getfuncname(st),"getitembound2") ) { + bound = script_getnum(st,11); + if( bound < 1 || bound > 3) { //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 + } else if( script_hasdata(st,11) ) sd=map_id2sd(script_getnum(st,11)); // else sd=script_rid2sd(st); // Attached player @@ -6370,14 +6397,14 @@ BUILDIN_FUNC(getitem2) data=script_getdata(st,2); get_val(st,data); - if( data_isstring(data) ){ + if( data_isstring(data) ) { const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); if( item_data ) nameid=item_data->nameid; else nameid=UNKNOWN_ITEM_ID; - }else + } else nameid=conv_num(st,data); amount=script_getnum(st,3); @@ -6422,6 +6449,7 @@ BUILDIN_FUNC(getitem2) 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)) @@ -6496,6 +6524,7 @@ BUILDIN_FUNC(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)) ) { @@ -11371,6 +11400,7 @@ BUILDIN_FUNC(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]; @@ -11444,6 +11474,7 @@ BUILDIN_FUNC(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]; @@ -12107,6 +12138,7 @@ BUILDIN_FUNC(getinventorylist) pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]); } pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); + pc_setreg(sd,reference_uid(add_str("@inventorylist_bound"), j),sd->status.inventory[i].bound); j++; } } @@ -17660,6 +17692,40 @@ BUILDIN_FUNC(sit) return 0; } +/*========================================== + * countbound {}; + * Creates an array of bounded item IDs + * Returns amount of items found + * Type: + * 1 - Account Bound + * 2 - Guild Bound + * 3 - Party Bound + *------------------------------------------*/ +BUILDIN_FUNC(countbound) +{ + int i, type, j=0, k=0; + TBL_PC *sd; + + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + + 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(add_str("@bound_items"), k),sd->status.inventory[i].nameid); + k++; + j += sd->status.inventory[i].amount; + } + } + + script_pushint(st,j); + return 0; +} + // declarations that were supposed to be exported from npc_chat.c #ifdef PCRE_SUPPORT BUILDIN_FUNC(defpattern); @@ -18125,5 +18191,10 @@ struct script_function buildin_func[] = { BUILDIN_DEF(checkquest, "i?"), BUILDIN_DEF(changequest, "ii"), BUILDIN_DEF(showevent, "ii"), + + //Bound items [Xantara] & [Akinari] + BUILDIN_DEF2(getitem,"getitembound","vii?"), + BUILDIN_DEF2(getitem2,"getitembound2","viiiiiiiii?"), + BUILDIN_DEF(countbound, "?"), {NULL,NULL,NULL}, }; diff --git a/src/map/storage.c b/src/map/storage.c index 94dd93f62f..1dfccc6767 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -120,7 +120,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++); @@ -154,6 +155,11 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data, return 1; } + if( (item_data->bound == 2 || item_data->bound == 3) && !pc_can_give_bounded_items(sd) ) { + clif_displaymessage(sd->fd, msg_txt(sd,294)); + return 1; + } + if( itemdb_isstackable2(data) ) {//Stackable for( i = 0; i < MAX_STORAGE; i++ ) @@ -448,6 +454,11 @@ int guild_storage_additem(struct map_session_data* sd, struct guild_storage* sto return 1; } + if( (item_data->bound == 1 || item_data->bound == 3) && !pc_can_give_bounded_items(sd) ) { + clif_displaymessage(sd->fd, msg_txt(sd,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 9f960e077c..7ea3761281 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -368,6 +368,12 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount) return; } + if( ((item->bound == 1 || item->bound == 3) || (item->bound == 2 && sd->status.guild_id != target_sd->status.guild_id)) && !pc_can_give_bounded_items(sd) ) { // Item Bound + clif_displaymessage(sd->fd, msg_txt(sd,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 4e4bd11355..a7931d6cc0 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -267,6 +267,7 @@ void vending_openvending(struct map_session_data* sd, const char* message, const || !sd->status.cart[index].identify // unidentified item || sd->status.cart[index].attribute == 1 // broken item || sd->status.cart[index].expire_time // It should not be in the cart but just in case + || (sd->status.cart[index].bound && !pc_can_give_bounded_items(sd)) // can't trade account bound items and has no permission || !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item continue;