Index: src/char/char.c =================================================================== --- src/char/char.c (revision 17350) +++ src/char/char.c (working copy) @@ -775,7 +775,7 @@ // it significantly reduces cpu load on the database server. StringBuf_Init(&buf); - StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`"); + 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 `%s`='%d'", tablename, selectoption, id); @@ -798,8 +798,9 @@ 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, 8+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); + SqlStmt_BindColumn(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)); @@ -826,14 +827,15 @@ 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. StringBuf_Clear(&buf); - StringBuf_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); + StringBuf_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 ) StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]); StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id); @@ -861,7 +863,7 @@ SqlStmt_Free(stmt); StringBuf_Clear(&buf); - StringBuf_Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`", tablename, selectoption); + StringBuf_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 ) StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_AppendStr(&buf, ") VALUES "); @@ -879,8 +881,8 @@ else found = true; - StringBuf_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); + StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'", + id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].bound, items[i].unique_id); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ", '%d'", items[i].card[j]); StringBuf_AppendStr(&buf, ")"); @@ -919,7 +921,7 @@ // it significantly reduces cpu load on the database server. StringBuf_Init(&buf); - StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`"); + StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`"); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'", inventory_db, id); @@ -943,8 +945,9 @@ SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &item.favorite, 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 9, SQLDT_CHAR, &item.bound, 0, NULL, NULL); for( j = 0; j < MAX_SLOTS; ++j ) - SqlStmt_BindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); + SqlStmt_BindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); // bit array indicating which inventory items have already been matched flag = (bool*) aCalloc(max, sizeof(bool)); @@ -970,13 +973,14 @@ items[i].refine == item.refine && items[i].attribute == item.attribute && items[i].expire_time == item.expire_time && - items[i].favorite == item.favorite ) + items[i].favorite == item.favorite && + items[i].bound == item.bound ) ; //Do nothing. else { // update all fields. StringBuf_Clear(&buf); - StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%d'", - inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite); + StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%d', `bound`='%d'", + inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]); StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id); @@ -1001,7 +1005,7 @@ SqlStmt_Free(stmt); StringBuf_Clear(&buf); - StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`", inventory_db); + StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`", inventory_db); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_AppendStr(&buf, ") VALUES "); @@ -1018,8 +1022,8 @@ else found = true; - StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'", - id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].unique_id); + StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%d', '%"PRIu64"'", + id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound, items[i].unique_id); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ", '%d'", items[i].card[j]); StringBuf_AppendStr(&buf, ")"); @@ -1282,7 +1286,7 @@ //read inventory //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`) StringBuf_Init(&buf); - StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`"); + StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`"); for( i = 0; i < MAX_SLOTS; ++i ) StringBuf_Printf(&buf, ", `card%d`", i); StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY); @@ -1299,10 +1303,11 @@ || SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL) - || SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt,10, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_SLOTS; ++i ) - if( SQL_ERROR == SqlStmt_BindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) + if( SQL_ERROR == SqlStmt_BindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) @@ -1313,7 +1318,7 @@ //read cart //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `unique_id`) StringBuf_Clear(&buf); - StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`"); + StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`"); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART); @@ -1329,10 +1334,11 @@ || SQL_ERROR == SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) - || SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_SLOTS; ++i ) - if( SQL_ERROR == SqlStmt_BindColumn(stmt, 9+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) + if( SQL_ERROR == SqlStmt_BindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_CART && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) Index: src/char/int_mail.c =================================================================== --- src/char/int_mail.c (revision 17350) +++ src/char/int_mail.c (working copy) @@ -63,6 +63,7 @@ Sql_GetData(sql_handle,14, &data, NULL); item->identify = atoi(data); Sql_GetData(sql_handle,15, &data, NULL); item->unique_id = strtoull(data, NULL, 10); item->expire_time = 0; + item->bound = 0; for (j = 0; j < MAX_SLOTS; j++) { @@ -183,6 +184,7 @@ Sql_GetData(sql_handle,14, &data, NULL); msg->item.identify = atoi(data); Sql_GetData(sql_handle,15, &data, NULL); msg->item.unique_id = strtoull(data, NULL, 10); msg->item.expire_time = 0; + msg->item.bound = 0; for( j = 0; j < MAX_SLOTS; j++ ) { Index: src/char/int_storage.c =================================================================== --- src/char/int_storage.c (revision 17350) +++ src/char/int_storage.c (working copy) @@ -38,7 +38,7 @@ // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} StringBuf_Init(&buf); - StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`unique_id`"); + StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`bound`,`unique_id`"); for( j = 0; j < MAX_SLOTS; ++j ) StringBuf_Printf(&buf, ",`card%d`", j); StringBuf_Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id); @@ -59,10 +59,11 @@ Sql_GetData(sql_handle, 5, &data, NULL); item->refine = atoi(data); Sql_GetData(sql_handle, 6, &data, NULL); item->attribute = atoi(data); Sql_GetData(sql_handle, 7, &data, NULL); item->expire_time = (unsigned int)atoi(data); - Sql_GetData(sql_handle, 8, &data, NULL); item->unique_id = strtoull(data, NULL, 10); + Sql_GetData(sql_handle, 8, &data, NULL); item->bound = atoi(data); + Sql_GetData(sql_handle, 9, &data, NULL); item->unique_id = strtoull(data, NULL, 10); for( j = 0; j < MAX_SLOTS; ++j ) { - Sql_GetData(sql_handle, 9+j, &data, NULL); item->card[j] = atoi(data); + Sql_GetData(sql_handle, 10+j, &data, NULL); item->card[j] = atoi(data); } } p->storage_amount = i; @@ -95,7 +96,7 @@ // 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 @@ 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 @@ //--------------------------------------------------------- // 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_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,13 +237,134 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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), Index: src/map/buyingstore.c =================================================================== --- src/map/buyingstore.c (revision 17350) +++ src/map/buyingstore.c (working copy) @@ -311,7 +311,7 @@ return; } - if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) + if( sd->status.inventory[index].expire_time || (sd->status.inventory[index].bound && !pc_can_give_bounded_items(sd)) || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) {// non-tradable item clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); return; Index: src/map/clif.c =================================================================== --- src/map/clif.c (revision 17350) +++ src/map/clif.c (working copy) @@ -1790,9 +1790,12 @@ 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 ) continue; @@ -2194,7 +2197,7 @@ 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 @@ 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,14 @@ } // Auction checks... + if( sd->status.inventory[sd->auction.index].bound && !pc_can_give_bounded_items(sd) ) { + if( sd->status.inventory[sd->auction.index].bound == 1 ) + clif_displaymessage(sd->fd, msg_txt(sd,293)); + else + clif_displaymessage(sd->fd, msg_txt(sd,294)); + 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; Index: src/map/guild.c =================================================================== --- src/map/guild.c (revision 17350) +++ src/map/guild.c (working copy) @@ -860,6 +860,9 @@ if(online_member_sd == NULL) return 0; // noone online to inform + //Guild bound item check + guild_retrieveitembound(char_id,account_id,guild_id); + if(!flag) clif_guild_leave(online_member_sd, name, mes); else @@ -884,9 +887,40 @@ clif_charnameupdate(sd); //Update display name [Skotlex] //TODO: send emblem update to self and people around } + return 0; } +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* stor = guild2storage(sd->status.guild_id); + for(i=0;istatus.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); + } + storage_guild_storageclose(sd); //Close and save the storage + } + } + else { //Character is offline, ask char server to do the job + struct guild_storage* stor = guild2storage(guild_id); + if(stor->storage_status == 1) { //Someone is in guild storage, close them + struct s_mapiterator* iter; + iter = mapit_getallusers(); + for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) + if(sd->state.storage_flag == 2) + storage_guild_storageclose(sd); + mapit_free(iter); + } + intif_itembound_req(char_id,aid,guild_id); + } +} + int guild_send_memberinfoshort(struct map_session_data *sd,int online) { // cleaned up [LuzZza] struct guild *g; @@ -1696,7 +1730,8 @@ { struct guild *g = guild_search(guild_id); struct map_session_data *sd = NULL; - int i; + int i, j; + int idxlist[MAX_INVENTORY]; if(flag!=0 || g==NULL) return 0; @@ -1712,6 +1747,13 @@ } } + //Guild bound item check - Removes the bound flag + j = pc_bound_chk(sd,2,idxlist); + if(j) { + for(i=0;istatus.inventory[idxlist[i]].bound = 0; + } + guild_db->foreach(guild_db,guild_broken_sub,guild_id); castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id); guild_storage_delete(guild_id); Index: src/map/guild.h =================================================================== --- src/map/guild.h (revision 17350) +++ src/map/guild.h (working copy) @@ -106,6 +106,7 @@ 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); Index: src/map/intif.c =================================================================== --- src/map/intif.c (revision 17350) +++ src/map/intif.c (working copy) @@ -39,7 +39,7 @@ 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 @@ -1007,32 +1007,47 @@ guild_id = RFIFOL(fd,8); 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; - } + gstor=guild2storage(guild_id); if(!gstor) { ShowWarning("intif_parse_LoadGuildStorage: error guild_id %d not exist\n",guild_id); return 1; } if (gstor->storage_status == 1) { // Already open.. lets ignore this update + sd=map_id2sd( RFIFOL(fd,4) ); + if(sd==NULL){ + ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); + return 1; + } ShowWarning("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", sd->status.account_id, sd->status.char_id); return 1; } if (gstor->dirty) { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex] + sd=map_id2sd( RFIFOL(fd,4) ); + if(sd==NULL){ + ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); + return 1; + } 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 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( RFIFOL(fd,12) ){ //if flag != 0 open it + sd=map_id2sd( RFIFOL(fd,4) ); + if(sd==NULL){ + ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); + return 1; + } + storage_guild_storageopen(sd); + } + return 0; } @@ -2152,6 +2167,34 @@ 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 retrive process +} + +//3856 +void intif_parse_itembound_ack(int fd) { + struct guild_storage *gstor; + int aid = RFIFOL(char_fd,2); + int guild_id = RFIFOW(char_fd,6); + TBL_PC *sd = map_id2sd(aid); + + gstor = guild2storage2(guild_id); + if(gstor) gstor->lock = 0; //now could be used again + if(sd) storage_guild_storageclose(sd); //at this point guild_storage should have been open +} + //----------------------------------------------------------------- // Communication from the inter server // Return a 0 (false) if there were any errors. @@ -2235,6 +2278,9 @@ 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; Index: src/map/intif.h =================================================================== --- src/map/intif.h (revision 17350) +++ src/map/intif.h (working copy) @@ -60,6 +60,7 @@ 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); Index: src/map/mail.c =================================================================== --- src/map/mail.c (revision 17350) +++ src/map/mail.c (working copy) @@ -78,8 +78,9 @@ return 1; if( amount < 0 || amount > sd->status.inventory[idx].amount ) return 1; - if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time || - !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) ) + if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time + || !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) + || (sd->status.inventory[idx].bound && !pc_can_give_bounded_items(sd)) ) return 1; sd->mail.index = idx; Index: src/map/party.c =================================================================== --- src/map/party.c (revision 17350) +++ src/map/party.c (working copy) @@ -552,12 +552,10 @@ 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,14 @@ } } - 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); + if(j) { + 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 Index: src/map/pc.c =================================================================== --- src/map/pc.c (revision 17350) +++ src/map/pc.c (working copy) @@ -509,6 +509,14 @@ return pc_has_permission(sd, PC_PERM_TRADE); } +/** + * Determines if player can give / drop / trade / vend bounded items + */ +bool pc_can_give_bounded_items(struct map_session_data *sd) +{ + return pc_has_permission(sd, PC_PERM_TRADE_BOUNDED); +} + /*========================================== * prepares character for saving. *------------------------------------------*/ @@ -914,7 +922,8 @@ *------------------------------------------*/ 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 @@ **/ 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 @@ { // 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 @@ 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 @@ 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] ); }; @@ -4567,6 +4582,25 @@ } /*========================================== + * 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 *------------------------------------------*/ int pc_show_steal(struct block_list *bl,va_list ap) @@ -7880,7 +7914,7 @@ *------------------------------------------*/ int pc_candrop(struct map_session_data *sd, struct item *item) { - if( item && item->expire_time ) + if( item && (item->expire_time || (item->bound && !pc_can_give_bounded_items(sd))) ) return 0; if( !pc_can_give_items(sd) ) //check if this GM level can drop items return 0; Index: src/map/pc.h =================================================================== --- src/map/pc.h (revision 17350) +++ src/map/pc.h (working copy) @@ -705,6 +705,7 @@ int pc_get_group_id(struct map_session_data *sd); int pc_getrefinebonus(int lv,int type); bool pc_can_give_items(struct map_session_data *sd); +bool pc_can_give_bounded_items(struct map_session_data *sd); bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type); #define pc_has_permission(sd, permission) ( ((sd)->permissions&permission) != 0 ) @@ -748,6 +749,9 @@ 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 ); Index: src/map/pc_groups.h =================================================================== --- src/map/pc_groups.h (revision 17350) +++ src/map/pc_groups.h (working copy) @@ -44,6 +44,7 @@ PC_PERM_DISABLE_PVP = 0x080000, PC_PERM_DISABLE_CMD_DEAD = 0x100000, PC_PERM_CHANNEL_ADMIN = 0x200000, + PC_PERM_TRADE_BOUNDED = 0x400000, }; static const struct { @@ -72,6 +73,7 @@ { "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_ Index: src/map/script.c =================================================================== --- src/map/script.c (revision 17350) +++ src/map/script.c (working copy) @@ -6270,6 +6270,13 @@ /*========================================== * getitem ,{,}; * getitem "",{,}; + * + * getitembound ,,{,}; + * getitembound "",,{,}; + * Type: + * 1 - Account Bound + * 2 - Guild Bound + * 3 - Party Bound *------------------------------------------*/ BUILDIN_FUNC(getitem) { @@ -6280,8 +6287,7 @@ 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 @@ 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 @@ 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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,34 @@ return 0; } +/*========================================== + * hasbounded; + * Creates an array of bounded item IDs + * Returns amount of items found + *------------------------------------------*/ +BUILDIN_FUNC(hasbounded) +{ + int i, j=0; + TBL_PC *sd; + + if( (sd = script_rid2sd(st)) == NULL ) + return 0; + + for(i=0;istatus.inventory[i].nameid > 0 && sd->status.inventory[i].bound > 0) { + pc_setreg(sd,reference_uid(add_str("@bound_items"), j),sd->status.inventory[i].bound); + j++; + } + } + + if(j) + script_pushint(st,j); + else + script_pushint(st,0); + + return 0; +} + // declarations that were supposed to be exported from npc_chat.c #ifdef PCRE_SUPPORT BUILDIN_FUNC(defpattern); @@ -18125,5 +18185,10 @@ 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(hasbounded, "?"), {NULL,NULL,NULL}, }; Index: src/map/storage.c =================================================================== --- src/map/storage.c (revision 17350) +++ src/map/storage.c (working copy) @@ -120,7 +120,8 @@ a->identify == b->identify && a->refine == b->refine && a->attribute == b->attribute && - a->expire_time == b->expire_time ) + a->expire_time == b->expire_time && + a->bound == b->bound ) { int i; for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++); @@ -154,6 +155,11 @@ 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 @@ 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)) { Index: src/map/trade.c =================================================================== --- src/map/trade.c (revision 17350) +++ src/map/trade.c (working copy) @@ -368,6 +368,12 @@ 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 Index: src/map/vending.c =================================================================== --- src/map/vending.c (revision 17350) +++ src/map/vending.c (working copy) @@ -267,6 +267,7 @@ || !sd->status.cart[index].identify // unidentified item || sd->status.cart[index].attribute == 1 // broken item || sd->status.cart[index].expire_time // It should not be in the cart but just in case + || (sd->status.cart[index].bound && !pc_can_give_bounded_items(sd)) // can't trade account bound items and has no permission || !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item continue;