diff options
-rw-r--r-- | sql-files/upgrade_svn16517.sql | 1 | ||||
-rw-r--r-- | src/char/char.c | 155 | ||||
-rw-r--r-- | src/common/mmo.h | 1 | ||||
-rw-r--r-- | src/map/clif.c | 54 | ||||
-rw-r--r-- | src/map/pc.c | 5 |
5 files changed, 202 insertions, 14 deletions
diff --git a/sql-files/upgrade_svn16517.sql b/sql-files/upgrade_svn16517.sql new file mode 100644 index 000000000..193490109 --- /dev/null +++ b/sql-files/upgrade_svn16517.sql @@ -0,0 +1 @@ +ALTER TABLE `inventory` ADD COLUMN `favorite` TINYINT UNSIGNED NOT NULL DEFAULT '0' AFTER `expire_time`; diff --git a/src/char/char.c b/src/char/char.c index f042c2d81..57ca73678 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -413,6 +413,7 @@ static DBData create_charstatus(DBKey key, va_list args) return db_ptr2data(cp); } +int inventory_to_sql(const struct item items[], int max, int id); int mmo_char_tosql(int char_id, struct mmo_charstatus* p) { @@ -432,17 +433,15 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) memset(save_status, 0, sizeof(save_status)); //map inventory data - if( memcmp(p->inventory, cp->inventory, sizeof(p->inventory)) ) - { - if (!memitemdata_to_sql(p->inventory, MAX_INVENTORY, p->char_id, TABLE_INVENTORY)) + if( memcmp(p->inventory, cp->inventory, sizeof(p->inventory)) ) { + if (!inventory_to_sql(p->inventory, MAX_INVENTORY, p->char_id)) strcat(save_status, " inventory"); else errors++; } //map cart data - if( memcmp(p->cart, cp->cart, sizeof(p->cart)) ) - { + if( memcmp(p->cart, cp->cart, sizeof(p->cart)) ) { if (!memitemdata_to_sql(p->cart, MAX_CART, p->char_id, TABLE_CART)) strcat(save_status, " cart"); else @@ -450,8 +449,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) } //map storage data - if( memcmp(p->storage.items, cp->storage.items, sizeof(p->storage.items)) ) - { + if( memcmp(p->storage.items, cp->storage.items, sizeof(p->storage.items)) ) { if (!memitemdata_to_sql(p->storage.items, MAX_STORAGE, p->account_id, TABLE_STORAGE)) strcat(save_status, " storage"); else @@ -849,6 +847,142 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit return errors; } +/* pretty much a copy of memitemdata_to_sql except it handles inventory_db exclusively, + * - this is required because inventory db is the only one with the 'favorite' column. */ +int inventory_to_sql(const struct item items[], int max, int id) { + StringBuf buf; + SqlStmt* stmt; + int i; + int j; + struct item item; // temp storage variable + bool* flag; // bit array for inventory matching + bool found; + int errors = 0; + + + // The following code compares inventory with current database values + // and performs modification/deletion/insertion only on relevant rows. + // This approach is more complicated than a trivial delete&insert, but + // 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`"); + 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); + + 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_CHAR, &item.favorite, 0, NULL, NULL); + for( j = 0; j < MAX_SLOTS; ++j ) + 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)); + + while( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) { + found = false; + // search for the presence of the item in the char's inventory + for( i = 0; i < max; ++i ) { + // skip empty and already matched entries + if( items[i].nameid == 0 || flag[i] ) + continue; + + if( items[i].nameid == item.nameid + && items[i].card[0] == item.card[0] + && items[i].card[2] == item.card[2] + && items[i].card[3] == item.card[3] + ) { //They are the same item. + ARR_FIND( 0, MAX_SLOTS, j, items[i].card[j] != item.card[j] ); + if( j == MAX_SLOTS && + items[i].amount == item.amount && + items[i].equip == item.equip && + items[i].identify == item.identify && + items[i].refine == item.refine && + items[i].attribute == item.attribute && + items[i].expire_time == item.expire_time && + items[i].favorite == item.favorite ) + ; //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); + 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); + + if( SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) { + Sql_ShowDebug(sql_handle); + errors++; + } + } + + found = flag[i] = true; //Item dealt with, + break; //skip to next item in the db. + } + } + if( !found ) {// Item not present in inventory, remove it. + if( SQL_ERROR == Sql_Query(sql_handle, "DELETE from `%s` where `id`='%d' LIMIT 1", inventory_db, item.id) ) { + Sql_ShowDebug(sql_handle); + errors++; + } + } + } + SqlStmt_Free(stmt); + + StringBuf_Clear(&buf); + StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`", inventory_db); + for( j = 0; j < MAX_SLOTS; ++j ) + StringBuf_Printf(&buf, ", `card%d`", j); + StringBuf_AppendStr(&buf, ") VALUES "); + + found = false; + // insert non-matched items into the db as new items + for( i = 0; i < max; ++i ) { + // skip empty and already matched entries + if( items[i].nameid == 0 || flag[i] ) + continue; + + if( found ) + StringBuf_AppendStr(&buf, ","); + else + found = true; + + StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d'", + 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); + for( j = 0; j < MAX_SLOTS; ++j ) + StringBuf_Printf(&buf, ", '%d'", items[i].card[j]); + StringBuf_AppendStr(&buf, ")"); + } + + if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ) { + Sql_ShowDebug(sql_handle); + errors++; + } + + StringBuf_Destroy(&buf); + aFree(flag); + + return errors; +} + int mmo_char_tobuf(uint8* buf, struct mmo_charstatus* p); @@ -1071,7 +1205,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything //read inventory //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`) 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`, `favorite`"); 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); @@ -1086,10 +1220,11 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything || SQL_ERROR == SqlStmt_BindColumn(stmt, 4, SQLDT_CHAR, &tmp_item.identify, 0, NULL, NULL) || 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, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL) ) SqlStmt_ShowDebug(stmt); for( i = 0; i < MAX_SLOTS; ++i ) - if( SQL_ERROR == SqlStmt_BindColumn(stmt, 8+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) + if( SQL_ERROR == SqlStmt_BindColumn(stmt, 9+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 ) diff --git a/src/common/mmo.h b/src/common/mmo.h index 7557d0ce3..269270bea 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -200,6 +200,7 @@ struct item { char attribute; short card[MAX_SLOTS]; unsigned int expire_time; + char favorite; }; struct point { diff --git a/src/map/clif.c b/src/map/clif.c index 0bbeb9361..25b53686a 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -2279,7 +2279,7 @@ void clif_item_sub(unsigned char *buf, int n, struct item *i, struct item_data * } } - +void clif_favorite_item(struct map_session_data* sd, unsigned short index); //Unified inventory function which sends all of the inventory (requires two packets, one for equipable items and one for stackable ones. [Skotlex] void clif_inventorylist(struct map_session_data *sd) { @@ -2367,7 +2367,16 @@ void clif_inventorylist(struct map_session_data *sd) WBUFW(bufe,2)=4+ne*se; clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF); } - +#if PACKETVER >= 20111122 + for( i = 0; i < MAX_INVENTORY; i++ ) { + if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL ) + continue; + + if ( sd->status.inventory[i].favorite ) + clif_favorite_item(sd, i); + } +#endif + if( buf ) aFree(buf); if( bufe ) aFree(bufe); } @@ -16115,6 +16124,45 @@ void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) { clif_menuskill_clear(sd); } +/// Move Item from or to Personal Tab (CZ_WHATSOEVER) [FE] +/// 0907 <index>.W +/// +/// R 0908 <index>.w <type>.b +/// type: +/// 0 = move item to personal tab +/// 1 = move item to normal tab +void clif_parse_MoveItem(int fd, struct map_session_data *sd) { +#if PACKETVER >= 20111122 + int index; + + if(pc_isdead(sd)) { + return; + } + index = RFIFOW(fd,2)-2; + if (index < 0 || index >= MAX_INVENTORY) + return; + if (sd->status.inventory[index].favorite && sd->status.inventory[index].favorite == 1) + sd->status.inventory[index].favorite = 0; + else + sd->status.inventory[index].favorite = 1; + + clif_favorite_item(sd, index); +#endif +} + + +/// Items that are in favorite tab of inventory (ZC_ITEM_FAVORITE). +/// 0900 <index>.W <favorite>.B +void clif_favorite_item(struct map_session_data* sd, unsigned short index) { + int fd = sd->fd; + + WFIFOHEAD(fd,packet_len(0x908)); + WFIFOW(fd,0) = 0x908; + WFIFOW(fd,2) = index+2; + WFIFOL(fd,4) = (sd->status.inventory[index].favorite == 1) ? 0 : 1; + WFIFOSET(fd,packet_len(0x908)); +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -16711,6 +16759,8 @@ static int packetdb_readdb(void) {clif_parse_SearchStoreInfoNextPage,"searchstoreinfonextpage"}, {clif_parse_CloseSearchStoreInfo,"closesearchstoreinfo"}, {clif_parse_SearchStoreInfoListItemClick,"searchstoreinfolistitemclick"}, + /* */ + { clif_parse_MoveItem , "moveitem" }, {NULL,NULL} }; diff --git a/src/map/pc.c b/src/map/pc.c index c697fd046..0f04cc017 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -3812,7 +3812,7 @@ int pc_delitem(struct map_session_data *sd,int n,int amount,int type, short reas sd->status.inventory[n].amount -= amount; sd->weight -= sd->inventory_data[n]->weight*amount ; - if(sd->status.inventory[n].amount<=0){ + if( sd->status.inventory[n].amount <= 0 ){ if(sd->status.inventory[n].equip) pc_unequipitem(sd,n,3); memset(&sd->status.inventory[n],0,sizeof(sd->status.inventory[0])); @@ -4267,6 +4267,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun sd->cart_num++; clif_cart_additem(sd,i,amount,0); } + sd->status.cart[i].favorite = 0;/* clear */ log_pick_pc(sd, log_type, amount, &sd->status.cart[i]); sd->cart_weight += w; @@ -4318,7 +4319,7 @@ int pc_putitemtocart(struct map_session_data *sd,int idx,int amount) if( item_data->nameid == 0 || amount < 1 || item_data->amount < amount || sd->state.vending ) return 1; - + if( pc_cart_additem(sd,item_data,amount,LOG_TYPE_NONE) == 0 ) return pc_delitem(sd,idx,amount,0,5,LOG_TYPE_NONE); |