diff --git a/conf/battle/client.conf b/conf/battle/client.conf index 3bb10b7cd6..c429287279 100644 --- a/conf/battle/client.conf +++ b/conf/battle/client.conf @@ -138,3 +138,10 @@ spawn_direction: no // kRO removed the packet and this re-enables the message. // Official: Disabled. mvp_exp_reward_message: no + +// Send ping timer +// Interval in seconds for each timer invoke. +ping_timer_inverval: 30 + +// Send packets timeout in seconds before ping packet can be sent. +ping_time: 20 diff --git a/sql-files/main.sql b/sql-files/main.sql index 9d7df5d633..21cdedf361 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -254,6 +254,7 @@ CREATE TABLE IF NOT EXISTS `char` ( `uniqueitem_counter` int(11) unsigned NOT NULL default '0', `sex` ENUM('M','F','U') NOT NULL default 'U', `hotkey_rowshift` tinyint(3) unsigned NOT NULL default '0', + `hotkey_rowshift2` tinyint(3) unsigned NOT NULL default '0', `clan_id` int(11) unsigned NOT NULL default '0', `last_login` datetime DEFAULT NULL, `title_id` INT(11) unsigned NOT NULL default '0', diff --git a/sql-files/upgrades/upgrade_20200603.sql b/sql-files/upgrades/upgrade_20200603.sql new file mode 100644 index 0000000000..8520c4532d --- /dev/null +++ b/sql-files/upgrades/upgrade_20200603.sql @@ -0,0 +1 @@ +ALTER TABLE `char` ADD COLUMN `hotkey_rowshift2` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `hotkey_rowshift`; diff --git a/src/char/char.cpp b/src/char/char.cpp index 721593acc5..ea37316645 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -299,7 +299,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){ (p->rename != cp->rename) || (p->robe != cp->robe) || (p->character_moves != cp->character_moves) || (p->unban_time != cp->unban_time) || (p->font != cp->font) || (p->uniqueitem_counter != cp->uniqueitem_counter) || (p->hotkey_rowshift != cp->hotkey_rowshift) || (p->clan_id != cp->clan_id ) || (p->title_id != cp->title_id) || - (p->show_equip != cp->show_equip) + (p->show_equip != cp->show_equip) || (p->hotkey_rowshift2 != cp->hotkey_rowshift2) ) { //Save status if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d'," @@ -310,7 +310,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){ "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d'," "`delete_date`='%lu',`robe`='%d',`moves`='%d',`font`='%u',`uniqueitem_counter`='%u'," - "`hotkey_rowshift`='%d', `clan_id`='%d', `title_id`='%lu', `show_equip`='%d'" + "`hotkey_rowshift`='%d', `clan_id`='%d', `title_id`='%lu', `show_equip`='%d', `hotkey_rowshift2`='%d'" " WHERE `account_id`='%d' AND `char_id` = '%d'", schema_config.char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, @@ -322,7 +322,7 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){ mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename, (unsigned long)p->delete_date, // FIXME: platform-dependent size p->robe, p->character_moves, p->font, p->uniqueitem_counter, - p->hotkey_rowshift, p->clan_id, p->title_id, p->show_equip, + p->hotkey_rowshift, p->clan_id, p->title_id, p->show_equip, p->hotkey_rowshift2, p->account_id, p->char_id) ) { Sql_ShowDebug(sql_handle); @@ -924,7 +924,8 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf, uint8* coun "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`," "`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`," - "`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`title_id`,`show_equip`" + "`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`title_id`,`show_equip`," + "`hotkey_rowshift2`" " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", schema_config.char_db, sd->account_id, MAX_CHARS) || SQL_ERROR == SqlStmt_Execute(stmt) || SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL) @@ -972,6 +973,7 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf, uint8* coun || SQL_ERROR == SqlStmt_BindColumn(stmt, 42, SQLDT_UCHAR, &p.hotkey_rowshift, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 43, SQLDT_ULONG, &p.title_id, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 44, SQLDT_UINT16, &p.show_equip, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 45, SQLDT_UCHAR, &p.hotkey_rowshift2, 0, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); @@ -1039,7 +1041,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`," "`hair_color`,`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`," "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`, `moves`," - "`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`title_id`,`show_equip`" + "`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`title_id`,`show_equip`,`hotkey_rowshift2`" " FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db) || SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SqlStmt_Execute(stmt) @@ -1105,6 +1107,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev || SQL_ERROR == SqlStmt_BindColumn(stmt, 59, SQLDT_INT, &p->clan_id, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 60, SQLDT_ULONG, &p->title_id, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 61, SQLDT_UINT16, &p->show_equip, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 62, SQLDT_UCHAR, &p->hotkey_rowshift2, 0, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); @@ -1211,7 +1214,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev while( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) { - if( hotkey_num >= 0 && hotkey_num < MAX_HOTKEYS ) + if( hotkey_num >= 0 && hotkey_num < MAX_HOTKEYS_DB ) memcpy(&p->hotkeys[hotkey_num], &tmp_hotkey, sizeof(tmp_hotkey)); else ShowWarning("mmo_char_fromsql: ignoring invalid hotkey (hotkey=%d,type=%u,id=%u,lv=%u) of character %s (AID=%d,CID=%d)\n", hotkey_num, tmp_hotkey.type, tmp_hotkey.id, tmp_hotkey.lv, p->name, p->account_id, p->char_id); @@ -2318,7 +2321,8 @@ bool char_checkdb(void){ "`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`,`hair_color`,`clothes_color`,`weapon`," "`shield`,`head_top`,`head_mid`,`head_bottom`,`robe`,`last_map`,`last_x`,`last_y`,`save_map`," "`save_x`,`save_y`,`partner_id`,`online`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`," - "`moves`,`unban_time`,`font`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`,`title_id`,`show_equip`" + "`moves`,`unban_time`,`font`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`,`title_id`,`show_equip`," + "`hotkey_rowshift2`" " FROM `%s` LIMIT 1;", schema_config.char_db) ){ Sql_ShowDebug(sql_handle); return false; diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp index d30158aff9..6a41312646 100644 --- a/src/common/mmo.hpp +++ b/src/common/mmo.hpp @@ -33,6 +33,12 @@ #define MAX_HOTKEYS 38 #endif +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 + #define MAX_HOTKEYS_DB ((MAX_HOTKEYS) * 2) +#else + #define MAX_HOTKEYS_DB MAX_HOTKEYS +#endif + #define MAX_MAP_PER_SERVER 1500 /// Maximum amount of maps available on a server #define MAX_INVENTORY 100 ///Maximum items in player inventory /** Max number of characters per account. Note that changing this setting alone is not enough if the client is not hexed to support more characters as well. @@ -518,7 +524,7 @@ struct mmo_charstatus { struct s_friend friends[MAX_FRIENDS]; //New friend system [Skotlex] #ifdef HOTKEY_SAVING - struct hotkey hotkeys[MAX_HOTKEYS]; + struct hotkey hotkeys[MAX_HOTKEYS_DB]; #endif bool show_equip,allow_party; short rename; @@ -536,6 +542,7 @@ struct mmo_charstatus { uint32 uniqueitem_counter; unsigned char hotkey_rowshift; + unsigned char hotkey_rowshift2; unsigned long title_id; }; diff --git a/src/common/socket.cpp b/src/common/socket.cpp index 8638f5fc2b..0a4350763e 100644 --- a/src/common/socket.cpp +++ b/src/common/socket.cpp @@ -428,6 +428,8 @@ int send_from_fifo(int fd) if( len > 0 ) { + session[fd]->wdata_tick = last_tick; + // some data could not be transferred? // shift unsent data to the beginning of the queue if( (size_t)len < session[fd]->wdata_size ) @@ -587,6 +589,7 @@ int make_listen_bind(uint32 ip, uint16 port) create_session(fd, connect_client, null_send, null_parse); session[fd]->client_addr = 0; // just listens session[fd]->rdata_tick = 0; // disable timeouts on this socket + session[fd]->wdata_tick = 0; return fd; } @@ -727,6 +730,7 @@ static int create_session(int fd, RecvFunc func_recv, SendFunc func_send, ParseF session[fd]->func_send = func_send; session[fd]->func_parse = func_parse; session[fd]->rdata_tick = last_tick; + session[fd]->wdata_tick = last_tick; return 0; } diff --git a/src/common/socket.hpp b/src/common/socket.hpp index e5db4f4ec9..3a5eeeef65 100644 --- a/src/common/socket.hpp +++ b/src/common/socket.hpp @@ -98,6 +98,7 @@ struct socket_data size_t rdata_size, wdata_size; size_t rdata_pos; time_t rdata_tick; // time of last recv (for detecting timeouts); zero when timeout is disabled + time_t wdata_tick; // time of last send (for detecting timeouts); RecvFunc func_recv; SendFunc func_send; diff --git a/src/config/packets.hpp b/src/config/packets.hpp index 02c9687f49..91e6d37ee1 100644 --- a/src/config/packets.hpp +++ b/src/config/packets.hpp @@ -13,7 +13,7 @@ /// Do NOT edit this line! To set your client version, please do this instead: /// In Windows: Add this line in your src\custom\defines_pre.hpp file: #define PACKETVER YYYYMMDD /// In Linux: The same as above or run the following command: ./configure --enable-packetver=YYYYMMDD - #define PACKETVER 20180620 + #define PACKETVER 20200401 #endif #ifndef PACKETVER_RE @@ -24,6 +24,23 @@ #endif #endif +#ifndef PACKETVER_RE + #define PACKETVER_MAIN_NUM PACKETVER + + // Undefine all sakray server definitions + #undef PACKETVER_RE + #undef PACKETVER_RE_NUM +#else + // Undefine existing definition + #undef PACKETVER_RE + + #define PACKETVER_RE PACKETVER + #define PACKETVER_RE_NUM PACKETVER + + // Undefine all main server definitions + #undef PACKETVER_MAIN_NUM +#endif + #if PACKETVER >= 20110817 /// Comment to disable the official packet obfuscation support. /// This requires PACKETVER 2011-08-17 or newer. diff --git a/src/login/loginclif.cpp b/src/login/loginclif.cpp index 8672bf2cee..9d80fb309a 100644 --- a/src/login/loginclif.cpp +++ b/src/login/loginclif.cpp @@ -457,6 +457,20 @@ static int logclif_parse_reqcharconnec(int fd, struct login_session_data *sd, ch return 1; } +int logclif_parse_otp_login( int fd, struct login_session_data* sd ){ + RFIFOSKIP( fd, 68 ); + + WFIFOHEAD( fd, 34 ); + WFIFOW( fd, 0 ) = 0xae3; + WFIFOW( fd, 2 ) = 34; + WFIFOL( fd, 4 ) = 0; // normal login + safestrncpy( WFIFOCP( fd, 8 ), "S1000", 6 ); + safestrncpy( WFIFOCP( fd, 28 ), "token", 6 ); + WFIFOSET( fd, 34 ); + + return 1; +} + /** * Entry point from client to log-server. * Function that checks incoming command, then splits it to the correct handler. @@ -521,6 +535,10 @@ int logclif_parse(int fd) { break; // Sending request of the coding key case 0x01db: next = logclif_parse_reqkey(fd, sd); break; + // OTP token login + case 0x0acf: + next = logclif_parse_otp_login( fd, sd ); + break; // Connection request of a char-server case 0x2710: logclif_parse_reqcharconnec(fd,sd, ip); return 0; // processing will continue elsewhere default: diff --git a/src/map/battle.cpp b/src/map/battle.cpp index 0b1aa9fe47..892f22c3ad 100644 --- a/src/map/battle.cpp +++ b/src/map/battle.cpp @@ -8954,6 +8954,8 @@ static const struct _battle_data { { "bgqueue_nowarp_mapflag", &battle_config.bgqueue_nowarp_mapflag, 0, 0, 1, }, { "homunculus_exp_gain", &battle_config.homunculus_exp_gain, 10, 0, 100, }, { "rental_item_novalue", &battle_config.rental_item_novalue, 1, 0, 1, }, + { "ping_timer_inverval", &battle_config.ping_timer_interval, 30, 0, 99999999, }, + { "ping_time", &battle_config.ping_time, 20, 0, 99999999, }, #include "../custom/battle_config_init.inc" }; diff --git a/src/map/battle.hpp b/src/map/battle.hpp index d9dae4b05d..15767c13b9 100644 --- a/src/map/battle.hpp +++ b/src/map/battle.hpp @@ -678,6 +678,8 @@ struct Battle_Config int bgqueue_nowarp_mapflag; int homunculus_exp_gain; int rental_item_novalue; + int ping_timer_interval; + int ping_time; #include "../custom/battle_config_struct.inc" }; diff --git a/src/map/buyingstore.cpp b/src/map/buyingstore.cpp index e79b4137be..7d4ccbf6a7 100644 --- a/src/map/buyingstore.cpp +++ b/src/map/buyingstore.cpp @@ -113,8 +113,7 @@ int8 buyingstore_setup(struct map_session_data* sd, unsigned char slots){ * @param at Autotrader info, or NULL if requetsed not from autotrade persistance * @return 0 If success, 1 - Cannot open, 2 - Manner penalty, 3 - Mapflag restiction, 4 - Cell restriction, 5 - Invalid count/result, 6 - Cannot give item, 7 - Will be overweight */ -int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count, struct s_autotrader *at) -{ +int8 buyingstore_create( struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub* itemlist, unsigned int count, struct s_autotrader *at ){ unsigned int i, weight, listidx; char message_sql[MESSAGE_SIZE*2]; StringBuf buf; @@ -161,50 +160,53 @@ int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha weight = sd->weight; // check item list - for( i = 0; i < count; i++ ) - {// itemlist: .W .W .L - unsigned short nameid, amount; - int price, idx; - struct item_data* id; + for( i = 0; i < count; i++ ){ + const struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub *item = &itemlist[i]; - nameid = RBUFW(itemlist,i*8+0); - amount = RBUFW(itemlist,i*8+2); - price = RBUFL(itemlist,i*8+4); + struct item_data* id = itemdb_exists( item->itemId ); - if( ( id = itemdb_exists(nameid) ) == NULL || amount == 0 ) - {// invalid input + // invalid input + if( id == NULL || item->amount == 0 ){ break; } - if( price <= 0 || price > BUYINGSTORE_MAX_PRICE ) - {// invalid price: unlike vending, items cannot be bought at 0 Zeny + // invalid price: unlike vending, items cannot be bought at 0 Zeny + if( item->price <= 0 || item->price > BUYINGSTORE_MAX_PRICE ){ break; } - if( !id->flag.buyingstore || !itemdb_cantrade_sub(id, pc_get_group_level(sd), pc_get_group_level(sd)) || ( idx = pc_search_inventory(sd, nameid) ) == -1 ) - {// restrictions: allowed, no character-bound items and at least one must be owned + // restrictions: allowed and no character-bound items + if( !id->flag.buyingstore || !itemdb_cantrade_sub( id, pc_get_group_level( sd ), pc_get_group_level( sd ) ) ){ break; } - if( sd->inventory.u.items_inventory[idx].amount + amount > BUYINGSTORE_MAX_AMOUNT ) - {// too many items of same kind + int idx = pc_search_inventory( sd, item->itemId ); + + // At least one must be owned + if( idx < 0 ){ break; } - if( i ) - {// duplicate check. as the client does this too, only malicious intent should be caught here - ARR_FIND( 0, i, listidx, sd->buyingstore.items[listidx].nameid == nameid ); - if( listidx != i ) - {// duplicate - ShowWarning("buyingstore_create: Found duplicate item on buying list (nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", nameid, amount, sd->status.account_id, sd->status.char_id); + // too many items of same kind + if( sd->inventory.u.items_inventory[idx].amount + item->amount > BUYINGSTORE_MAX_AMOUNT ){ + break; + } + + // duplicate check. as the client does this too, only malicious intent should be caught here + if( i ){ + ARR_FIND( 0, i, listidx, sd->buyingstore.items[listidx].nameid == item->itemId ); + + // duplicate + if( listidx != i ){ + ShowWarning( "buyingstore_create: Found duplicate item on buying list (nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", item->itemId, item->amount, sd->status.account_id, sd->status.char_id ); break; } } - weight+= id->weight*amount; - sd->buyingstore.items[i].nameid = nameid; - sd->buyingstore.items[i].amount = amount; - sd->buyingstore.items[i].price = price; + weight+= id->weight*item->amount; + sd->buyingstore.items[i].nameid = item->itemId; + sd->buyingstore.items[i].amount = item->amount; + sd->buyingstore.items[i].price = item->price; } if( i != count ) @@ -322,10 +324,9 @@ void buyingstore_open(struct map_session_data* sd, uint32 account_id) * @param *itemlist List of sold items { .W, .W, .W }* * @param count Number of item on the itemlist */ -void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count) -{ +void buyingstore_trade( struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* itemlist, unsigned int count ){ int zeny = 0; - unsigned int i, weight, listidx, k; + unsigned int weight; struct map_session_data* pl_sd; nullpo_retv(sd); @@ -362,98 +363,94 @@ void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned searchstore_clearremote(sd); - if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit ) - {// buyer lost zeny in the mean time? fix the limit + // buyer lost zeny in the mean time? fix the limit + if( pl_sd->status.zeny < pl_sd->buyingstore.zenylimit ){ pl_sd->buyingstore.zenylimit = pl_sd->status.zeny; } weight = pl_sd->weight; // check item list - for( i = 0; i < count; i++ ) - {// itemlist: .W .W .W - unsigned short nameid, amount; - int index; + for( int i = 0; i < count; i++ ){ + const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* item = &itemlist[i]; - index = RBUFW(itemlist,i*6+0)-2; - nameid = RBUFW(itemlist,i*6+2); - amount = RBUFW(itemlist,i*6+4); - - if( i ) - {// duplicate check. as the client does this too, only malicious intent should be caught here - ARR_FIND( 0, i, k, RBUFW(itemlist,k*6+0)-2 == index ); - if( k != i ) - {// duplicate - ShowWarning("buyingstore_trade: Found duplicate item on selling list (prevnameid=%hu, prevamount=%hu, nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", - RBUFW(itemlist,k*6+2), RBUFW(itemlist,k*6+4), nameid, amount, sd->status.account_id, sd->status.char_id); - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + // duplicate check. as the client does this too, only malicious intent should be caught here + for( int k = 0; k < i; k++ ){ + // duplicate + if( itemlist[k].index == item->index && k != i ){ + ShowWarning( "buyingstore_trade: Found duplicate item on selling list (prevnameid=%hu, prevamount=%hu, nameid=%hu, amount=%hu, account_id=%d, char_id=%d).\n", itemlist[k].itemId, itemlist[k].amount, item->itemId, item->amount, sd->status.account_id, sd->status.char_id ); + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } } - if( index < 0 || index >= ARRAYLENGTH(sd->inventory.u.items_inventory) || sd->inventory_data[index] == NULL || sd->inventory.u.items_inventory[index].nameid != nameid || sd->inventory.u.items_inventory[index].amount < amount ) - {// invalid input - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + int index = item->index - 2; // TODO: clif::server_index + + // invalid input + if( index < 0 || index >= ARRAYLENGTH( sd->inventory.u.items_inventory ) || sd->inventory_data[index] == NULL || sd->inventory.u.items_inventory[index].nameid != item->itemId || sd->inventory.u.items_inventory[index].amount < item->amount ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - if( sd->inventory.u.items_inventory[index].expire_time || (sd->inventory.u.items_inventory[index].bound && !pc_can_give_bounded_items(sd)) || !itemdb_cantrade(&sd->inventory.u.items_inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->inventory.u.items_inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) - {// non-tradable item - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + // non-tradable item + if( sd->inventory.u.items_inventory[index].expire_time || ( sd->inventory.u.items_inventory[index].bound && !pc_can_give_bounded_items( sd ) ) || !itemdb_cantrade( &sd->inventory.u.items_inventory[index], pc_get_group_level( sd ), pc_get_group_level( pl_sd ) ) || memcmp( sd->inventory.u.items_inventory[index].card, buyingstore_blankslots, sizeof( buyingstore_blankslots ) ) ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid ); - if( listidx == pl_sd->buyingstore.slots || pl_sd->buyingstore.items[listidx].amount == 0 ) - {// there is no such item or the buyer has already bought all of them - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + int listidx; + + ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == item->itemId ); + + // there is no such item or the buyer has already bought all of them + if( listidx == pl_sd->buyingstore.slots || pl_sd->buyingstore.items[listidx].amount == 0 ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - if( pl_sd->buyingstore.items[listidx].amount < amount ) - {// buyer does not need that much of the item - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_COUNT, nameid); + // buyer does not need that much of the item + if( pl_sd->buyingstore.items[listidx].amount < item->amount ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_COUNT, item->itemId ); return; } - if( pc_checkadditem(pl_sd, nameid, amount) == CHKADDITEM_OVERAMOUNT ) - {// buyer does not have enough space for this item - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + // buyer does not have enough space for this item + if( pc_checkadditem( pl_sd, item->itemId, item->amount ) == CHKADDITEM_OVERAMOUNT ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - if( amount*(unsigned int)sd->inventory_data[index]->weight > pl_sd->max_weight-weight ) - {// normally this is not supposed to happen, as the total weight is - // checked upon creation, but the buyer could have gained items - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); + // normally this is not supposed to happen, as the total weight is + // checked upon creation, but the buyer could have gained items + if( item->amount * (unsigned int)sd->inventory_data[index]->weight > pl_sd->max_weight - weight ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_FAILED, item->itemId ); return; } - weight+= amount*sd->inventory_data[index]->weight; - if( amount*pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit-zeny ) - {// buyer does not have enough zeny - clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_ZENY, nameid); + weight += item->amount * sd->inventory_data[index]->weight; + + // buyer does not have enough zeny + if( item->amount * pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit - zeny ){ + clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_ZENY, item->itemId ); return; } - zeny+= amount*pl_sd->buyingstore.items[listidx].price; + + zeny += item->amount * pl_sd->buyingstore.items[listidx].price; } // process item list - for( i = 0; i < count; i++ ) - {// itemlist: .W .W .W - unsigned short nameid, amount; - int index; + for( int i = 0; i < count; i++ ){ + const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* item = &itemlist[i]; + int listidx; - index = RBUFW(itemlist,i*6+0)-2; - nameid = RBUFW(itemlist,i*6+2); - amount = RBUFW(itemlist,i*6+4); + ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == item->itemId ); + zeny = item->amount * pl_sd->buyingstore.items[listidx].price; - ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == nameid ); - zeny = amount*pl_sd->buyingstore.items[listidx].price; + int index = item->index - 2; // TODO: clif::server_index // move item - pc_additem(pl_sd, &sd->inventory.u.items_inventory[index], amount, LOG_TYPE_BUYING_STORE); - pc_delitem(sd, index, amount, 1, 0, LOG_TYPE_BUYING_STORE); - pl_sd->buyingstore.items[listidx].amount-= amount; + pc_additem(pl_sd, &sd->inventory.u.items_inventory[index], item->amount, LOG_TYPE_BUYING_STORE); + pc_delitem(sd, index, item->amount, 1, 0, LOG_TYPE_BUYING_STORE); + pl_sd->buyingstore.items[listidx].amount -= item->amount; if( pl_sd->buyingstore.items[listidx].amount > 0 ){ if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `buyingstore_id` = %d AND `index` = %d;", buyingstore_items_table, pl_sd->buyingstore.items[listidx].amount, pl_sd->buyer_id, listidx ) != SQL_SUCCESS ){ @@ -471,8 +468,8 @@ void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned pl_sd->buyingstore.zenylimit-= zeny; // notify clients - clif_buyingstore_delete_item(sd, index, amount, pl_sd->buyingstore.items[listidx].price); - clif_buyingstore_update_item(pl_sd, nameid, amount, sd->status.char_id, zeny); + clif_buyingstore_delete_item(sd, index, item->amount, pl_sd->buyingstore.items[listidx].price); + clif_buyingstore_update_item(pl_sd, item->itemId, item->amount, sd->status.char_id, zeny); } if( save_settings&CHARSAVE_VENDING ) { @@ -481,6 +478,7 @@ void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned } // check whether or not there is still something to buy + int i; ARR_FIND( 0, pl_sd->buyingstore.slots, i, pl_sd->buyingstore.items[i].amount != 0 ); if( i == pl_sd->buyingstore.slots ) {// everything was bought @@ -548,7 +546,7 @@ bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_st for( idx = 0; idx < s->item_count; idx++ ) { - ARR_FIND( 0, sd->buyingstore.slots, i, sd->buyingstore.items[i].nameid == s->itemlist[idx] && sd->buyingstore.items[i].amount ); + ARR_FIND( 0, sd->buyingstore.slots, i, sd->buyingstore.items[i].nameid == s->itemlist[idx].itemId && sd->buyingstore.items[i].amount ); if( i == sd->buyingstore.slots ) {// not found continue; @@ -591,23 +589,15 @@ void buyingstore_reopen( struct map_session_data* sd ){ // Ready to open buyingstore for this char if ((at = (struct s_autotrader *)uidb_get(buyingstore_autotrader_db, sd->status.char_id)) && at->count && at->entries) { - uint8 *data, *p; - uint16 j, count; + struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub* data; // Init buyingstore data for autotrader - CREATE(data, uint8, at->count * 8); + CREATE(data, struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub, at->count ); - for (j = 0, p = data, count = at->count; j < at->count; j++) { - struct s_autotrade_entry *entry = at->entries[j]; - unsigned short *item_id = (uint16*)(p + 0); - uint16 *amount = (uint16*)(p + 2); - uint32 *price = (uint32*)(p + 4); - - *item_id = entry->item_id; - *amount = entry->amount; - *price = entry->price; - - p += 8; + for( int j = 0; j < at->count; j++) { + data[j].itemId = at->entries[j]->item_id; + data[j].amount = at->entries[j]->amount; + data[j].price = at->entries[j]->price; } sd->state.autotrade = 1; @@ -633,7 +623,7 @@ void buyingstore_reopen( struct map_session_data* sd ){ chrif_save(sd, CSAVE_AUTOTRADE); ShowInfo("Buyingstore loaded for '" CL_WHITE "%s" CL_RESET "' with '" CL_WHITE "%d" CL_RESET "' items at " CL_WHITE "%s (%d,%d)" CL_RESET "\n", - sd->status.name, count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y); + sd->status.name, at->count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y); } aFree(data); } diff --git a/src/map/buyingstore.hpp b/src/map/buyingstore.hpp index 0c052d4722..eab26da3ac 100644 --- a/src/map/buyingstore.hpp +++ b/src/map/buyingstore.hpp @@ -56,10 +56,10 @@ struct s_autotrader { }; int8 buyingstore_setup(struct map_session_data* sd, unsigned char slots); -int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count, struct s_autotrader *at); +int8 buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub* itemlist, unsigned int count, struct s_autotrader *at); void buyingstore_close(struct map_session_data* sd); void buyingstore_open(struct map_session_data* sd, uint32 account_id); -void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count); +void buyingstore_trade(struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* itemlist, unsigned int count); bool buyingstore_search(struct map_session_data* sd, unsigned short nameid); bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_store_search* s); DBMap *buyingstore_getdb(void); diff --git a/src/map/cashshop.cpp b/src/map/cashshop.cpp index 862a02e59a..50d77d89c7 100644 --- a/src/map/cashshop.cpp +++ b/src/map/cashshop.cpp @@ -467,7 +467,7 @@ static void cashshop_read_db( void ){ * @param item_list Array of item ID * @return true: success, false: fail */ -bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, uint16* item_list ){ +bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, struct PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list ){ uint32 totalcash = 0; uint32 totalweight = 0; int i,new_; @@ -487,9 +487,9 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u new_ = 0; for( i = 0; i < n; ++i ){ - unsigned short nameid = *( item_list + i * 5 ); - uint32 quantity = *( item_list + i * 5 + 2 ); - uint8 tab = (uint8)*( item_list + i * 5 + 4 ); + unsigned short nameid = item_list[i].itemId; + uint32 quantity = item_list[i].amount; + uint16 tab = item_list[i].tab; int j; if( tab >= CASHSHOP_TAB_MAX ){ @@ -504,7 +504,7 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u return false; } - nameid = *( item_list + i * 5 ) = cash_shop_items[tab].item[j]->nameid; //item_avail replacement + nameid = item_list[i].itemId = cash_shop_items[tab].item[j]->nameid; //item_avail replacement id = itemdb_exists(nameid); if( !id ){ @@ -569,10 +569,10 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u } for( i = 0; i < n; ++i ){ - unsigned short nameid = *( item_list + i * 5 ); - uint32 quantity = *( item_list + i * 5 + 2 ); + unsigned short nameid = item_list[i].itemId; + uint32 quantity = item_list[i].amount; #if PACKETVER_SUPPORTS_SALES - uint16 tab = *(item_list + i * 5 + 4); + uint16 tab = item_list[i].tab; #endif struct item_data *id = itemdb_search(nameid); @@ -580,12 +580,12 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u continue; if (!pet_create_egg(sd, nameid)) { - unsigned short get_amt = quantity, j; + unsigned short get_amt = quantity; if (id->flag.guid || !itemdb_isstackable2(id)) get_amt = 1; - for (j = 0; j < quantity; j += get_amt) { + for (uint32 j = 0; j < quantity; j += get_amt) { struct item item_tmp = { 0 }; item_tmp.nameid = nameid; @@ -606,6 +606,8 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u return false; } + clif_cashshop_result( sd, nameid, CASHSHOP_RESULT_SUCCESS ); + #if PACKETVER_SUPPORTS_SALES if( tab == CASHSHOP_TAB_SALE ){ uint32 new_amount = sale->amount - get_amt; @@ -627,7 +629,6 @@ bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, u } } - clif_cashshop_result( sd, 0, CASHSHOP_RESULT_SUCCESS ); //Doesn't show any message? return true; } diff --git a/src/map/cashshop.hpp b/src/map/cashshop.hpp index a7c3530286..06636543fe 100644 --- a/src/map/cashshop.hpp +++ b/src/map/cashshop.hpp @@ -13,7 +13,7 @@ struct map_session_data; void do_init_cashshop( void ); void do_final_cashshop( void ); void cashshop_reloaddb( void ); -bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, uint16* item_list ); +bool cashshop_buylist( struct map_session_data* sd, uint32 kafrapoints, int n, struct PACKET_CZ_SE_PC_BUY_CASHITEM_LIST_sub* item_list ); // Taken from AEGIS enum CASH_SHOP_TAB_CODE diff --git a/src/map/clif.cpp b/src/map/clif.cpp index a01f5d188c..05dee826bf 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -1,4 +1,5 @@ // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL +// Copyright (c) Hercules Dev Team - Licensed under GNU GPL // For more information, see LICENCE in the main folder #include "clif.hpp" @@ -73,6 +74,7 @@ unsigned long color_table[COLOR_MAX]; #include "clif_obfuscation.hpp" static bool clif_session_isValid(struct map_session_data *sd); +static void clif_loadConfirm( struct map_session_data *sd ); #if PACKETVER >= 20150513 enum mail_type { @@ -83,6 +85,13 @@ enum mail_type { }; #endif +enum e_inventory_type{ + INVTYPE_INVENTORY = 0, + INVTYPE_CART = 1, + INVTYPE_STORAGE = 2, + INVTYPE_GUILD_STORAGE = 3, +}; + /** Converts item type to display it on client if necessary. * @param nameid: Item ID * @return item type. For IT_PETEGG will be displayed as IT_ARMOR. If Shadow Weapon of IT_SHADOWGEAR as IT_WEAPON and else as IT_ARMOR @@ -99,6 +108,44 @@ static inline int itemtype(unsigned short nameid) { return ( type == IT_PETEGG ) ? IT_ARMOR : type; } +// TODO: doc +static inline uint16 client_index( uint16 server_index ){ + return server_index + 2; +} + +static inline uint16 server_index( uint16 client_index ){ + return client_index - 2; +} + +static inline uint16 client_storage_index( uint16 server_index ){ + return server_index + 1; +} + +static inline uint16 server_storage_index( uint16 client_index ){ + return client_index - 1; +} + +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 +static inline uint32 client_nameid( uint32 server_nameid ){ + uint32 view = itemdb_viewid( server_nameid ); + + if( view > 0 ){ + return view; + }else{ + return server_nameid; + } +} +#else +static inline uint16 client_nameid( uint32 server_nameid ){ + uint32 view = itemdb_viewid( server_nameid ); + + if( view > 0 ){ + return (uint16)view; + }else{ + return (uint16)server_nameid; + } +} +#endif static inline void WBUFPOS(uint8* p, unsigned short pos, short x, short y, unsigned char dir) { p += pos; @@ -391,7 +438,7 @@ static int clif_send_sub(struct block_list *bl, va_list ap) * Packet Delegation (called on all packets that require data to be sent to more than one client) * functions that are sent solely to one use whose ID it posses use WFIFOSET *------------------------------------------*/ -int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target type) +int clif_send(const void* buf, int len, struct block_list* bl, enum send_target type) { int i; struct map_session_data *sd, *tsd; @@ -781,56 +828,46 @@ void clif_charselectok(int id, uint8 ok) /// 009E .L .W .B .W .W .B .B .W (ZC_ITEM_FALL_ENTRY) /// 084B .L .W .W .B .W .W .B .B .W (ZC_ITEM_FALL_ENTRY4) /// 0ADD .L .W .W .B .W .W .B .B .W .B .W (ZC_ITEM_FALL_ENTRY5) -void clif_dropflooritem(struct flooritem_data* fitem, bool canShowEffect) -{ -#if PACKETVER >= 20180418 - uint8 buf[22]; - uint32 header = 0xadd; -#elif PACKETVER >= 20130000 - uint8 buf[19]; - uint32 header=0x84b; -#else - uint8 buf[17]; - uint32 header=0x09e; -#endif - int view, offset=0; - +void clif_dropflooritem( struct flooritem_data* fitem, bool canShowEffect ){ nullpo_retv(fitem); - if (fitem->item.nameid == 0) + if( fitem->item.nameid <= 0 ){ return; + } - WBUFW(buf, offset+0) = header; - WBUFL(buf, offset+2) = fitem->bl.id; - WBUFW(buf, offset+6) = ((view = itemdb_viewid(fitem->item.nameid)) > 0) ? view : fitem->item.nameid; -#if PACKETVER >= 20130000 - WBUFW(buf, offset+8) = itemtype(fitem->item.nameid); - offset +=2; + int view = itemdb_viewid( fitem->item.nameid ); + + struct packet_dropflooritem p; + + p.PacketType = dropflooritemType; + p.ITAID = fitem->bl.id; + p.ITID = ( view > 0 ) ? view : fitem->item.nameid; +#if PACKETVER >= 20130000 /* not sure date */ + p.type = itemtype( fitem->item.nameid ); #endif - WBUFB(buf, offset+8) = fitem->item.identify; - WBUFW(buf, offset+9) = fitem->bl.x; - WBUFW(buf, offset+11) = fitem->bl.y; - WBUFB(buf, offset+13) = fitem->subx; - WBUFB(buf, offset+14) = fitem->suby; - WBUFW(buf, offset+15) = fitem->item.amount; -#if PACKETVER >= 20180418 + p.IsIdentified = fitem->item.identify ? 1 : 0; + p.xPos = fitem->bl.x; + p.yPos = fitem->bl.y; + p.subX = fitem->subx; + p.subY = fitem->suby; + p.count = fitem->item.amount; +#if defined(PACKETVER_ZERO) || PACKETVER >= 20180418 if( canShowEffect ){ - uint8 dropEffect = itemdb_dropeffect(fitem->item.nameid); + uint8 dropEffect = itemdb_dropeffect( fitem->item.nameid ); if( dropEffect > 0 ){ - WBUFB(buf, offset+17) = 1; - WBUFW(buf, offset+18) = dropEffect - 1; + p.showdropeffect = 1; + p.dropeffectmode = dropEffect - 1; }else{ - WBUFB(buf, offset+17) = 0; - WBUFW(buf, offset+18) = 0; + p.showdropeffect = 0; + p.dropeffectmode = 0; } }else{ - WBUFB(buf, offset+17) = 0; - WBUFW(buf, offset+18) = 0; + p.showdropeffect = 0; + p.dropeffectmode = 0; } #endif - - clif_send(buf, packet_len(header), &fitem->bl, AREA); + clif_send( &p, sizeof(p), &fitem->bl, AREA ); } @@ -982,349 +1019,398 @@ static int clif_setlevel(struct block_list* bl) { /*========================================== * Prepares 'unit standing/spawning' packet *------------------------------------------*/ -static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool spawn, bool option, bool walking, unsigned int option_val) -{ - struct map_session_data* sd; - struct status_change* sc = status_get_sc(bl); - struct view_data* vd = status_get_viewdata(bl); +static void clif_set_unit_idle( struct block_list* bl, bool walking, send_target target, struct block_list* tbl ){ + nullpo_retv( bl ); - unsigned char *buf = WBUFP(buffer, 0); -#if PACKETVER < 20091103 - bool type = !pcdb_checkid(vd->class_); -#endif - unsigned short offset = 0; -#if PACKETVER >= 20091103 - const char *name; -#endif - sd = BL_CAST(BL_PC, bl); - - if (!option) - option_val = ((sc) ? sc->option : 0); + struct map_session_data* sd = BL_CAST( BL_PC, bl ); + struct status_change* sc = status_get_sc( bl ); + struct view_data* vd = status_get_viewdata( bl ); + int g_id = status_get_guild_id( bl ); #if PACKETVER < 20091103 - if(type) - WBUFW(buf,0) = spawn ? 0x7c : 0x78; - else -#endif -#if PACKETVER < 4 - WBUFW(buf,0) = spawn ? 0x79 : 0x78; -#elif PACKETVER < 7 - WBUFW(buf,0) = spawn ? 0x1d9 : 0x1d8; -#elif PACKETVER < 20080102 - WBUFW(buf,0) = spawn ? 0x22b : 0x22a; -#elif PACKETVER < 20091103 - WBUFW(buf,0) = spawn ? 0x2ed : 0x2ee; -#elif PACKETVER < 20101124 - WBUFW(buf,0) = spawn ? 0x7f8 : 0x7f9; -#elif PACKETVER < 20120221 - WBUFW(buf,0) = spawn ? 0x858 : 0x857; -#elif PACKETVER < 20131223 - WBUFW(buf,0) = spawn ? 0x90f : 0x915; -#elif PACKETVER < 20150513 - WBUFW(buf,0) = spawn ? 0x9dc : 0x9dd; -#else - WBUFW(buf,0) = spawn ? 0x9fe : 0x9ff; -#endif + if( !pcdb_checkid( vd->class_ ) ){ + struct packet_idle_unit2 p; -#if PACKETVER >= 20091103 - name = status_get_name(bl); -#if PACKETVER < 20110111 - WBUFW(buf,2) = (uint16)((spawn ? 62 : 63)+strlen(name)); -#elif PACKETVER < 20120221 - WBUFW(buf,2) = (uint16)((spawn ? 64 : 65)+strlen(name)); -#elif PACKETVER < 20130807 - WBUFW(buf,2) = (uint16)((spawn ? 77 : 78)+strlen(name)); -#else - WBUFW(buf,2) = (uint16)((spawn ? 79 : 80)+strlen(name)); + p.PacketType = idle_unit2Type; +#if PACKETVER >= 20071106 + p.objecttype = clif_bl_type( bl, walking ); #endif - WBUFB(buf,4) = clif_bl_type(bl,walking); - offset+=3; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 20071106 - if (type) { //Non-player packets - WBUFB(buf,2) = clif_bl_type(bl); - offset++; - buf = WBUFP(buffer,offset); + p.GID = bl->id; + p.speed = status_get_speed( bl ); + p.bodyState = ( sc ) ? sc->opt1 : 0; + p.healthState = ( sc ) ? sc->opt2 : 0; + p.effectState = ( sc ) ? sc->option : 0; + p.job = vd->class_; + p.head = vd->hair_style; + p.weapon = vd->weapon; + p.accessory = vd->head_bottom; + if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ){ + // The hell, why flags work like this? + p.shield = status_get_emblem_id( bl ); + p.accessory2 = GetWord( g_id, 1 ); + p.accessory3 = GetWord( g_id, 0 ); + }else{ + p.shield = vd->shield; + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; + } + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = ( sd )? sd->head_dir : 0; + p.GUID = g_id; + p.GEmblemVer = status_get_emblem_id( bl ); + p.honor = ( sd ) ? sd->status.manner : 0; + p.virtue = ( sc ) ? sc->opt3 : 0; + p.isPKModeON = ( sd && sd->status.karma ) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS( &p.PosDir[0], 0, bl->x, bl->y, unit_getdir( bl ) ); + p.xSize = p.ySize = ( sd ) ? 5 : 0; + p.state = vd->dead_sit; + p.clevel = clif_setlevel( bl ); + + clif_send( &p, sizeof( p ), tbl, target ); + + return; } #endif - WBUFL(buf, 2) = bl->id; + + struct packet_idle_unit p; + + p.PacketType = idle_unitType; +#if PACKETVER >= 20091103 + p.PacketLength = sizeof(p); + p.objecttype = clif_bl_type( bl, walking ); +#endif #if PACKETVER >= 20131223 - WBUFL(buf,6) = (sd) ? sd->status.char_id : 0; // GID/CCODE - offset+=4; - buf = WBUFP(buffer,offset); -#endif - WBUFW(buf, 6) = status_get_speed(bl); - WBUFW(buf, 8) = (sc)? sc->opt1 : 0; - WBUFW(buf,10) = (sc)? sc->opt2 : 0; -#if PACKETVER < 20091103 - if (type&&spawn) { //uses an older and different packet structure - WBUFW(buf,12) = option_val; - WBUFW(buf,14) = vd->hair_style; - WBUFW(buf,16) = vd->weapon; - WBUFW(buf,18) = vd->head_bottom; - WBUFW(buf,20) = vd->class_; //Pet armor (ignored by client) - WBUFW(buf,22) = vd->shield; - } else { -#endif -#if PACKETVER >= 20091103 - WBUFL(buf,12) = option_val; - offset+=2; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 7 - if (!type) { - WBUFL(buf,12) = option_val; - offset+=2; - buf = WBUFP(buffer,offset); - } else - WBUFW(buf,12) = option_val; + p.AID = bl->id; + p.GID = (sd) ? sd->status.char_id : 0; // CCODE #else - WBUFW(buf,12) = option_val; + p.GID = bl->id; #endif - WBUFW(buf,14) = vd->class_; - WBUFW(buf,16) = vd->hair_style; - WBUFW(buf,18) = vd->weapon; -#if PACKETVER < 4 - WBUFW(buf,20) = vd->head_bottom; - WBUFW(buf,22) = vd->shield; -#else - WBUFW(buf,20) = vd->shield; - WBUFW(buf,22) = vd->head_bottom; -#endif -#if PACKETVER < 20091103 - } -#endif - WBUFW(buf,24) = vd->head_top; - WBUFW(buf,26) = vd->head_mid; + p.speed = status_get_speed(bl); + p.bodyState = (sc) ? sc->opt1 : 0; + p.healthState = (sc) ? sc->opt2 : 0; - if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ) - { //The hell, why flags work like this? - WBUFW(buf,22) = status_get_emblem_id(bl); - WBUFW(buf,24) = GetWord(status_get_guild_id(bl), 1); - WBUFW(buf,26) = GetWord(status_get_guild_id(bl), 0); - } + // npc option changed? + if( tbl && tbl->type == BL_PC && bl->type == BL_NPC ){ + struct map_session_data* sd = (struct map_session_data*)tbl; + struct npc_data* nd = (struct npc_data*)bl; + int option = (sc) ? sc->option : 0; - WBUFW(buf,28) = vd->hair_color; - WBUFW(buf,30) = vd->cloth_color; - WBUFW(buf,32) = (sd)? sd->head_dir : 0; -#if PACKETVER < 20091103 - if (type&&spawn) { //End of packet 0x7c - WBUFB(buf,34) = (sd) ? sd->status.karma : 0; // karma - WBUFB(buf,35) = vd->sex; - WBUFPOS(buf,36, bl->x, bl->y, unit_getdir(bl)); - WBUFB(buf,39) = 0; - WBUFB(buf,40) = 0; - return packet_len(0x7c); + if( !nd->vd.dead_sit ){ + if( std::find( sd->cloaked_npc.begin(), sd->cloaked_npc.end(), nd->bl.id ) != sd->cloaked_npc.end() ){ + option ^= OPTION_CLOAK; + } + } + + p.effectState = option; + }else{ + p.effectState = (sc) ? sc->option : 0; } + p.job = vd->class_; + p.head = vd->hair_style; + p.weapon = vd->weapon; +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + p.shield = vd->shield; #endif -#if PACKETVER >= 20110111 - WBUFW(buf,34) = vd->robe; - offset+= 2; - buf = WBUFP(buffer,offset); -#endif - WBUFL(buf,34) = status_get_guild_id(bl); - WBUFW(buf,38) = status_get_emblem_id(bl); - WBUFW(buf,40) = (sd)? sd->status.manner : 0; -#if PACKETVER >= 20091103 - WBUFL(buf,42) = (sc)? sc->opt3 : 0; - offset+=2; - buf = WBUFP(buffer,offset); -#elif PACKETVER >= 7 - if (!type) { - WBUFL(buf,42) = (sc)? sc->opt3 : 0; - offset+=2; - buf = WBUFP(buffer,offset); - } else - WBUFW(buf,42) = (sc)? sc->opt3 : 0; -#else - WBUFW(buf,42) = (sc)? sc->opt3 : 0; -#endif - WBUFB(buf,44) = (sd)? sd->status.karma : 0; - WBUFB(buf,45) = vd->sex; - WBUFPOS(buf,46,bl->x,bl->y,unit_getdir(bl)); - WBUFB(buf,49) = (sd)? 5 : 0; - WBUFB(buf,50) = (sd)? 5 : 0; - if (!spawn) { - WBUFB(buf,51) = vd->dead_sit; - offset++; - buf = WBUFP(buffer,offset); + if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ){ + // The hell, why flags work like this? + p.accessory = status_get_emblem_id( bl ); + p.accessory2 = GetWord( g_id, 1 ); + p.accessory3 = GetWord( g_id, 0 ); + }else{ + p.accessory = vd->head_bottom; + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; } - WBUFW(buf,51) = clif_setlevel(bl); -#if PACKETVER < 20091103 - if (type) //End for non-player packet - return packet_len(WBUFW(buffer,0)); + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = (sd)? sd->head_dir : 0; +#if PACKETVER >= 20101124 + p.robe = vd->robe; #endif + p.GUID = g_id; + p.GEmblemVer = status_get_emblem_id( bl ); + p.honor = (sd) ? sd->status.manner : 0; + p.virtue = (sc) ? sc->opt3 : 0; + p.isPKModeON = (sd && sd->status.karma) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS( &p.PosDir[0], 0, bl->x, bl->y, unit_getdir( bl ) ); + p.xSize = p.ySize = (sd) ? 5 : 0; + p.state = vd->dead_sit; + p.clevel = clif_setlevel( bl ); #if PACKETVER >= 20080102 - WBUFW(buf,53) = (sd ? sd->status.font : 0); + p.font = (sd) ? sd->status.font : 0; #endif #if PACKETVER >= 20120221 - if ( battle_config.monster_hp_bars_info && bl->type == BL_MOB && !map_getmapflag(bl->m, MF_HIDEMOBHPBAR) && (status_get_hp(bl) < status_get_max_hp(bl)) ) { - WBUFL(buf,55) = status_get_max_hp(bl); // maxHP - WBUFL(buf,59) = status_get_hp(bl); // HP - } else { - WBUFL(buf,55) = -1; // maxHP - WBUFL(buf,59) = -1; // HP + if( battle_config.monster_hp_bars_info && !map_getmapflag( bl->m, MF_HIDEMOBHPBAR ) && bl->type == BL_MOB && ( status_get_hp( bl ) < status_get_max_hp( bl ) ) ){ + p.maxHP = status_get_max_hp(bl); + p.HP = status_get_hp(bl); + }else{ + p.maxHP = -1; + p.HP = -1; } - - WBUFB(buf,63) = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0; // isBoss + p.isBoss = ( bl->type == BL_MOB && ( ( ( TBL_MOB *)bl )->db->mexp > 0 ) ) ? 1 : 0; #endif #if PACKETVER >= 20150513 - WBUFW(buf,64) = vd->body_style; // body - offset+= 2; - buf = WBUFP(buffer,offset); + p.body = vd->body_style; #endif +/* Might be earlier, this is when the named item bug began */ +#if PACKETVER >= 20131223 + safestrncpy(p.name, status_get_name( bl ), NAME_LENGTH); +#endif + + if( disguised( bl ) ){ #if PACKETVER >= 20091103 + p.objecttype = pcdb_checkid( status_get_viewdata( bl )->class_ ) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE +#if PACKETVER >= 20131223 + p.AID = -bl->id; +#else + p.GID = -bl->id; +#endif +#else + p.GID = -bl->id; +#endif + } + + clif_send( &p, sizeof( p ), tbl, target ); +} + +static void clif_spawn_unit( struct block_list *bl, enum send_target target ){ + nullpo_retv( bl ); + + struct map_session_data* sd = BL_CAST( BL_PC, bl ); + struct status_change* sc = status_get_sc( bl ); + struct view_data* vd = status_get_viewdata( bl ); + int g_id = status_get_guild_id( bl ); + +#if PACKETVER < 20091103 + if( !pcdb_checkid( vd->class_ ) ){ + struct packet_spawn_unit2 p; + + p.PacketType = spawn_unit2Type; +#if PACKETVER >= 20071106 + p.objecttype = clif_bl_type( bl, false ); +#endif + p.GID = bl->id; + p.speed = status_get_speed( bl ); + p.bodyState = ( sc ) ? sc->opt1 : 0; + p.healthState = ( sc ) ? sc->opt2 : 0; + p.effectState = ( sc ) ? sc->option : 0; + p.head = vd->hair_style; + p.weapon = vd->weapon; + p.accessory = vd->head_bottom; + p.job = vd->class_; + if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ){ + // The hell, why flags work like this? + p.shield = status_get_emblem_id( bl ); + p.accessory2 = GetWord( g_id, 1 ); + p.accessory3 = GetWord( g_id, 0 ); + }else{ + p.shield = vd->shield; + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; + } + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = ( sd ) ? sd->head_dir : 0; + p.isPKModeON = ( sd && sd->status.karma ) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS( &p.PosDir[0], 0, bl->x, bl->y, unit_getdir( bl ) ); + p.xSize = p.ySize = ( sd ) ? 5 : 0; + + clif_send( &p, sizeof( p ), bl, target ); + return; + } +#endif + + struct packet_spawn_unit p; + + p.PacketType = spawn_unitType; +#if PACKETVER >= 20091103 + p.PacketLength = sizeof(p); + p.objecttype = clif_bl_type( bl, false ); +#endif +#if PACKETVER >= 20131223 + p.AID = bl->id; + p.GID = (sd) ? sd->status.char_id : 0; // CCODE +#else + p.GID = bl->id; +#endif + p.speed = status_get_speed( bl ); + p.bodyState = (sc) ? sc->opt1 : 0; + p.healthState = (sc) ? sc->opt2 : 0; + p.effectState = (sc) ? sc->option : 0; + p.job = vd->class_; + p.head = vd->hair_style; + p.weapon = vd->weapon; +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + p.shield = vd->shield; +#endif + if( bl->type == BL_NPC && vd->class_ == JT_GUILD_FLAG ){ + // The hell, why flags work like this? + p.accessory = status_get_emblem_id( bl ); + p.accessory2 = GetWord( g_id, 1 ); + p.accessory3 = GetWord( g_id, 0 ); + }else{ + p.accessory = vd->head_bottom; + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; + } + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = (sd)? sd->head_dir : 0; +#if PACKETVER >= 20101124 + p.robe = vd->robe; +#endif + p.GUID = g_id; + p.GEmblemVer = status_get_emblem_id( bl ); + p.honor = (sd) ? sd->status.manner : 0; + p.virtue = (sc) ? sc->opt3 : 0; + p.isPKModeON = (sd && sd->status.karma) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS( &p.PosDir[0], 0, bl->x, bl->y, unit_getdir( bl ) ); + p.xSize = p.ySize = (sd) ? 5 : 0; + p.clevel = clif_setlevel( bl ); +#if PACKETVER >= 20080102 + p.font = (sd) ? sd->status.font : 0; +#endif #if PACKETVER >= 20120221 - safestrncpy(WBUFCP(buf,64), name, NAME_LENGTH); -#else - safestrncpy(WBUFCP(buf,55), name, NAME_LENGTH); + if( battle_config.monster_hp_bars_info && bl->type == BL_MOB && !map_getmapflag( bl->m, MF_HIDEMOBHPBAR ) && ( status_get_hp( bl ) < status_get_max_hp( bl ) ) ){ + p.maxHP = status_get_max_hp( bl ); + p.HP = status_get_hp( bl ); + }else{ + p.maxHP = -1; + p.HP = -1; + } + + p.isBoss = ( bl->type == BL_MOB && ( ( ( TBL_MOB *)bl)->db->mexp > 0 ) ) ? 1 : 0; #endif - return WBUFW(buffer,2); -#else - return packet_len(WBUFW(buffer,0)); +#if PACKETVER >= 20150513 + p.body = vd->body_style; #endif +/* Might be earlier, this is when the named item bug began */ +#if PACKETVER >= 20131223 + safestrncpy( p.name, status_get_name( bl ), NAME_LENGTH ); +#endif + + if( disguised( bl ) ){ + nullpo_retv( sd ); + + if( sd->status.class_ != sd->disguise ){ + clif_send( &p, sizeof( p ), bl, target ); + } + +#if PACKETVER >= 20091103 + p.objecttype = pcdb_checkid( status_get_viewdata(bl)->class_ ) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE +#if PACKETVER >= 20131223 + p.AID = -bl->id; +#else + p.GID = -bl->id; +#endif +#else + p.GID = -bl->id; +#endif + clif_send( &p, sizeof( p ), bl, SELF ); + }else{ + clif_send( &p, sizeof( p ), bl, target ); + } } /*========================================== * Prepares 'unit walking' packet *------------------------------------------*/ -static int clif_set_unit_walking(struct block_list* bl, struct unit_data* ud, unsigned char* buffer) -{ - struct map_session_data* sd; - struct status_change* sc = status_get_sc(bl); - struct view_data* vd = status_get_viewdata(bl); +static void clif_set_unit_walking( struct block_list *bl, struct map_session_data *tsd, struct unit_data *ud, enum send_target target ){ + nullpo_retv( bl ); + nullpo_retv( ud ); - unsigned char* buf = WBUFP(buffer,0); -#if PACKETVER >= 7 - unsigned short offset = 0; -#endif -#if PACKETVER >= 20091103 - const char *name; -#endif + struct map_session_data* sd; + struct status_change* sc = status_get_sc( bl ); + struct view_data* vd = status_get_viewdata( bl ); + struct packet_unit_walking p; + int g_id = status_get_guild_id(bl); sd = BL_CAST(BL_PC, bl); -#if PACKETVER < 4 - WBUFW(buf, 0) = 0x7b; -#elif PACKETVER < 7 - WBUFW(buf, 0) = 0x1da; -#elif PACKETVER < 20080102 - WBUFW(buf, 0) = 0x22c; -#elif PACKETVER < 20091103 - WBUFW(buf, 0) = 0x2ec; -#elif PACKETVER < 20101124 - WBUFW(buf, 0) = 0x7f7; -#elif PACKETVER < 20120221 - WBUFW(buf, 0) = 0x856; -#elif PACKETVER < 20131223 - WBUFW(buf, 0) = 0x914; -#elif PACKETVER < 20150513 - WBUFW(buf, 0) = 0x9db; -#else - WBUFW(buf, 0) = 0x9fd; -#endif - + p.PacketType = unit_walkingType; #if PACKETVER >= 20091103 - name = status_get_name(bl); -#if PACKETVER < 20110111 - WBUFW(buf, 2) = (uint16)(69+strlen(name)); -#elif PACKETVER < 20120221 - WBUFW(buf, 2) = (uint16)(71+strlen(name)); -#elif PACKETVER < 20130807 - WBUFW(buf, 2) = (uint16)(84+strlen(name)); -#else - WBUFW(buf, 2) = (uint16)(86+strlen(name)); -#endif - offset+=2; - buf = WBUFP(buffer,offset); + p.PacketLength = sizeof(p); #endif #if PACKETVER >= 20071106 - WBUFB(buf, 2) = clif_bl_type(bl,true); - offset++; - buf = WBUFP(buffer,offset); + p.objecttype = clif_bl_type( bl, true ); #endif - WBUFL(buf, 2) = bl->id; #if PACKETVER >= 20131223 - WBUFL(buf,6) = (sd) ? sd->status.char_id : 0; // GID/CCODE - offset+=4; - buf = WBUFP(buffer,offset); -#endif - WBUFW(buf, 6) = status_get_speed(bl); - WBUFW(buf, 8) = (sc)? sc->opt1 : 0; - WBUFW(buf,10) = (sc)? sc->opt2 : 0; -#if PACKETVER < 7 - WBUFW(buf,12) = (sc)? sc->option : 0; + p.AID = bl->id; + p.GID = (sd) ? sd->status.char_id : 0; // CCODE #else - WBUFL(buf,12) = (sc)? sc->option : 0; - offset+=2; //Shift the rest of elements by 2 bytes. - buf = WBUFP(buffer,offset); + p.GID = bl->id; #endif - WBUFW(buf,14) = vd->class_; - WBUFW(buf,16) = vd->hair_style; - WBUFW(buf,18) = vd->weapon; -#if PACKETVER < 4 - WBUFW(buf,20) = vd->head_bottom; - WBUFL(buf,22) = client_tick(gettick()); - WBUFW(buf,26) = vd->shield; -#else - WBUFW(buf,20) = vd->shield; - WBUFW(buf,22) = vd->head_bottom; - WBUFL(buf,24) = client_tick(gettick()); + p.speed = status_get_speed(bl); + p.bodyState = (sc) ? sc->opt1 : 0; + p.healthState = (sc) ? sc->opt2 : 0; + p.effectState = (sc) ? sc->option : 0; + p.job = vd->class_; + p.head = vd->hair_style; + p.weapon = vd->weapon; + p.accessory = vd->head_bottom; + p.moveStartTime = client_tick(gettick()); +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + p.shield = vd->shield; #endif - WBUFW(buf,28) = vd->head_top; - WBUFW(buf,30) = vd->head_mid; - WBUFW(buf,32) = vd->hair_color; - WBUFW(buf,34) = vd->cloth_color; - WBUFW(buf,36) = (sd)? sd->head_dir : 0; -#if PACKETVER >= 20110111 - WBUFW(buf,38) = vd->robe; - offset+= 2; - buf = WBUFP(buffer,offset); + p.accessory2 = vd->head_top; + p.accessory3 = vd->head_mid; + p.headpalette = vd->hair_color; + p.bodypalette = vd->cloth_color; + p.headDir = (sd) ? sd->head_dir : 0; +#if PACKETVER >= 20101124 + p.robe = vd->robe; #endif - WBUFL(buf,38) = status_get_guild_id(bl); - WBUFW(buf,42) = status_get_emblem_id(bl); - WBUFW(buf,44) = (sd)? sd->status.manner : 0; -#if PACKETVER < 7 - WBUFW(buf,46) = (sc)? sc->opt3 : 0; -#else - WBUFL(buf,46) = (sc)? sc->opt3 : 0; - offset+=2; //Shift the rest of elements by 2 bytes. - buf = WBUFP(buffer,offset); -#endif - WBUFB(buf,48) = (sd)? sd->status.karma : 0; - WBUFB(buf,49) = vd->sex; - WBUFPOS2(buf,50,bl->x,bl->y,ud->to_x,ud->to_y,8,8); - WBUFB(buf,56) = (sd)? 5 : 0; - WBUFB(buf,57) = (sd)? 5 : 0; - WBUFW(buf,58) = clif_setlevel(bl); + p.GUID = g_id; + p.GEmblemVer = status_get_emblem_id(bl); + p.honor = (sd) ? sd->status.manner : 0; + p.virtue = (sc) ? sc->opt3 : 0; + p.isPKModeON = (sd && sd->status.karma) ? 1 : 0; + p.sex = vd->sex; + WBUFPOS2( &p.MoveData[0], 0, bl->x, bl->y, ud->to_x, ud->to_y, 8, 8 ); + p.xSize = p.ySize = (sd) ? 5 : 0; + p.clevel = clif_setlevel(bl); #if PACKETVER >= 20080102 - WBUFW(buf,60) = (sd ? sd->status.font : 0); + p.font = (sd) ? sd->status.font : 0; #endif #if PACKETVER >= 20120221 - if ( battle_config.monster_hp_bars_info && !map_getmapflag(bl->m, MF_HIDEMOBHPBAR) && bl->type == BL_MOB && (status_get_hp(bl) < status_get_max_hp(bl)) ) { - WBUFL(buf,62) = status_get_max_hp(bl); // maxHP - WBUFL(buf,66) = status_get_hp(bl); // HP + if( battle_config.monster_hp_bars_info && !map_getmapflag(bl->m, MF_HIDEMOBHPBAR) && bl->type == BL_MOB && (status_get_hp(bl) < status_get_max_hp( bl ) ) ){ + p.maxHP = status_get_max_hp(bl); + p.HP = status_get_hp(bl); } else { - WBUFL(buf,62) = -1; // maxHP - WBUFL(buf,66) = -1; // HP + p.maxHP = -1; + p.HP = -1; } - WBUFB(buf,70) = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0; // isBoss + p.isBoss = ( bl->type == BL_MOB && (((TBL_MOB*)bl)->db->mexp > 0) ) ? 1 : 0; #endif #if PACKETVER >= 20150513 - WBUFW(buf,71) = vd->body_style; // body - offset+= 2; - buf = WBUFP(buffer,offset); + p.body = vd->body_style; #endif +/* Might be earlier, this is when the named item bug began */ +#if PACKETVER >= 20131223 + safestrncpy(p.name, status_get_name(bl), NAME_LENGTH); +#endif + + clif_send( &p, sizeof(p), tsd ? &tsd->bl : bl, target ); + + if( disguised( bl ) ){ #if PACKETVER >= 20091103 -#if PACKETVER >= 20120221 - safestrncpy(WBUFCP(buf,71), name, NAME_LENGTH); + p.objecttype = pcdb_checkid( status_get_viewdata(bl)->class_ ) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE +#if PACKETVER >= 20131223 + p.AID = -bl->id; #else - safestrncpy(WBUFCP(buf,62), name, NAME_LENGTH); + p.GID = -bl->id; #endif - return WBUFW(buffer,2); #else - return packet_len(WBUFW(buffer,0)); + p.GID = -bl->id; #endif + clif_send(&p,sizeof(p),bl,SELF); + } } //Modifies the buffer for disguise characters and sends it to self. @@ -1467,11 +1553,8 @@ void clif_weather(int16 m) /** * Main function to spawn a unit on the client (player/mob/pet/etc) **/ -int clif_spawn(struct block_list *bl, bool walking) -{ - unsigned char buf[128]; +int clif_spawn( struct block_list *bl, bool walking ){ struct view_data *vd; - int len; vd = status_get_viewdata(bl); if( !vd || vd->class_ == JT_INVISIBLE ) @@ -1483,10 +1566,11 @@ int clif_spawn(struct block_list *bl, bool walking) if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE)) return 0; - len = clif_set_unit_idle(bl, buf, (bl->type == BL_NPC && vd->dead_sit ? false : true), false, walking, 0); - clif_send(buf, len, bl, AREA_WOS); - if (disguised(bl)) - clif_setdisguise(bl, buf, len); + if( bl->type == BL_NPC && !vd->dead_sit ){ + clif_set_unit_idle( bl, walking, AREA_WOS, bl ); + }else{ + clif_spawn_unit( bl, AREA_WOS ); + } if (vd->cloth_color) clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS); @@ -1548,95 +1632,87 @@ int clif_spawn(struct block_list *bl, bool walking) /// Sends information about owned homunculus to the client . [orn] /// 022e .24B .B .W .W .W .W .W .W .W .W .W .W .W .W .W .W .W .W .L .L .W .W (ZC_PROPERTY_HOMUN) /// 09f7 .24B .B .W .W .W .W .W .W .W .W .W .W .W .W .L .L .W .W .L .L .W .W (ZC_PROPERTY_HOMUN_2) -void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag) -{ - struct status_data *status; - unsigned char buf[128]; - int offset; -#if PACKETVER < 20141016 - const int cmd = 0x22e; -#else - const int cmd = 0x9f7; -#endif - int htype; +void clif_hominfo( struct map_session_data *sd, struct homun_data *hd, int flag ){ +#if PACKETVER_MAIN_NUM >= 20101005 || PACKETVER_RE_NUM >= 20080827 || defined(PACKETVER_ZERO) + nullpo_retv( sd ); + nullpo_retv( hd ); - nullpo_retv(hd); + struct status_data *status = &hd->battle_status; + struct PACKET_ZC_PROPERTY_HOMUN p; - if (!clif_session_isValid(sd)) - return; - - status = &hd->battle_status; - htype = hom_class2type(hd->homunculus.class_); - - memset(buf,0,packet_len(cmd)); - WBUFW(buf,0) = cmd; - safestrncpy(WBUFCP(buf,2), hd->homunculus.name, NAME_LENGTH); + p.packetType = HEADER_ZC_PROPERTY_HOMUN; + safestrncpy( p.name, hd->homunculus.name, sizeof( p.name ) ); // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true) - WBUFB(buf,26) = (battle_config.hom_rename ? 0 : hd->homunculus.rename_flag) | (hd->homunculus.vaporize << 1) | (hd->homunculus.hp ? 0 : 4); - WBUFW(buf,27) = hd->homunculus.level; - WBUFW(buf,29) = hd->homunculus.hunger; - WBUFW(buf,31) = (unsigned short) (hd->homunculus.intimacy / 100) ; - WBUFW(buf,33) = 0; // equip id - WBUFW(buf,35) = cap_value(status->rhw.atk2 + status->batk, 0, INT16_MAX); - WBUFW(buf,37)=i16min(status->matk_max, INT16_MAX); //FIXME capping to INT16 here is too late - WBUFW(buf,39)=status->hit; - if (battle_config.hom_setting&HOMSET_DISPLAY_LUK) - WBUFW(buf,41) = status->luk/3 + 1; //crit is a +1 decimal value! Just display purpose.[Vicious] - else - WBUFW(buf,41) = status->cri/10; -#ifdef RENEWAL - WBUFW(buf,43) = status->def + status->def2; - WBUFW(buf,45) = status->mdef + status->mdef2; -#else - WBUFW(buf,43) = status->def + status->vit; - WBUFW(buf,45) = status->mdef; + p.flags = ( !battle_config.hom_rename && hd->homunculus.rename_flag ? 0x1 : 0x0 ) | ( hd->homunculus.vaporize == HOM_ST_REST ? 0x2 : 0 ) | ( hd->homunculus.hp > 0 ? 0x4 : 0 ); + p.level = hd->homunculus.level; + p.hunger = hd->homunculus.hunger; + p.intimacy = hd->homunculus.intimacy / 100; +#if !(PACKETVER_MAIN_NUM >= 20190619 || PACKETVER_RE_NUM >= 20190605 || PACKETVER_ZERO_NUM >= 20190626) + p.itemId = 0; // equip id #endif - WBUFW(buf,47) = status->flee; - WBUFW(buf,49) = (flag) ? 0 : status->amotion; + p.atk2 = cap_value( status->rhw.atk2 + status->batk, 0, INT16_MAX ); + p.matk = i16min( status->matk_max, INT16_MAX ); //FIXME capping to INT16 here is too late + p.hit = status->hit; + if( battle_config.hom_setting&HOMSET_DISPLAY_LUK ){ + p.crit = status->luk / 3 + 1; //crit is a +1 decimal value! Just display purpose.[Vicious] + }else{ + p.crit = status->cri / 10; + } +#ifdef RENEWAL + p.def = status->def + status->def2; + p.mdef = status->mdef + status->mdef2; +#else + p.def = status->def + status->vit; + p.mdef = status->mdef; +#endif + p.flee = status->flee; + p.amotion = (flag) ? 0 : status->amotion; #if PACKETVER >= 20141016 // Homunculus HP bar will screw up if the percentage calculation exceeds signed values // Tested maximum: 21474836(=INT32_MAX/100), any value above will screw up the HP bar - if (status->max_hp > (INT32_MAX/100)) { - WBUFL(buf,51) = status->hp/(status->max_hp/100); - WBUFL(buf,55) = 100; - } else { - WBUFL(buf,51) = status->hp; - WBUFL(buf,55) = status->max_hp; + if( status->max_hp > ( INT32_MAX / 100 ) ){ + p.hp = status->hp / ( status->max_hp / 100 ); + p.maxHp = 100; + }else{ + p.hp = status->hp; + p.maxHp = status->max_hp; } - offset = 4; #else - if (status->max_hp > INT16_MAX) { - WBUFW(buf,51) = status->hp/(status->max_hp/100); - WBUFW(buf,53) = 100; - } else { - WBUFW(buf,51) = status->hp; - WBUFW(buf,53) = status->max_hp; + if( status->max_hp > INT16_MAX ){ + p.hp = status->hp / ( status->max_hp / 100 ); + p.maxHp = 100; + }else{ + p.hp = status->hp; + p.maxHp = status->max_hp; } - offset = 0; #endif - if (status->max_sp > INT16_MAX) { - WBUFW(buf,55+offset) = status->sp/(status->max_sp/100); - WBUFW(buf,57+offset) = 100; - } else { - WBUFW(buf,55+offset) = status->sp; - WBUFW(buf,57+offset) = status->max_sp; + if( status->max_sp > INT16_MAX ){ + p.sp = status->sp / ( status->max_sp / 100 ); + p.maxSp = 100; + }else{ + p.sp = status->sp; + p.maxSp = status->max_sp; } - WBUFL(buf,59+offset) = hd->homunculus.exp; - WBUFL(buf,63+offset) = hd->exp_next; - switch( htype ) { + p.exp = hd->homunculus.exp; + p.expNext = hd->exp_next; + switch( hom_class2type( hd->homunculus.class_ ) ){ case HT_REG: case HT_EVO: - if( hd->homunculus.level >= battle_config.hom_max_level ) - WBUFL(buf,63+offset) = 0; + if( hd->homunculus.level >= battle_config.hom_max_level ){ + p.expNext = 0; + } break; case HT_S: - if( hd->homunculus.level >= battle_config.hom_S_max_level ) - WBUFL(buf,63+offset) = 0; + if( hd->homunculus.level >= battle_config.hom_S_max_level ){ + p.expNext = 0; + } break; } - WBUFW(buf,67+offset) = hd->homunculus.skillpts; - WBUFW(buf,69+offset) = status_get_range(&hd->bl); - clif_send(buf,packet_len(cmd),&sd->bl,SELF); + p.skillPoints = hd->homunculus.skillpts; + p.range = status_get_range( &hd->bl ); + + clif_send( &p, sizeof( p ), &sd->bl, SELF ); +#endif } @@ -1728,16 +1804,21 @@ void clif_homskillup(struct map_session_data *sd, uint16 skill_id) WFIFOSET(fd,packet_len(0x239)); } -int clif_hom_food(struct map_session_data *sd,int foodid,int fail) //[orn] -{ - int fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x22f)); - WFIFOW(fd,0)=0x22f; - WFIFOB(fd,2)=fail; - WFIFOW(fd,3)=foodid; - WFIFOSET(fd,packet_len(0x22f)); +/// Result of request to feed a homun/merc. +/// 022f .B .W (ZC_FEED_MER) +/// result: +/// 0 = failure +/// 1 = success +void clif_hom_food( struct map_session_data *sd, int foodid, int fail ){ + nullpo_retv( sd ); - return 0; + struct PACKET_ZC_FEED_MER p; + + p.packetType = 0x22f; + p.result = fail; + p.itemId = client_nameid( foodid ); + + clif_send( &p, sizeof( p ), &sd->bl, SELF ); } @@ -1755,19 +1836,13 @@ void clif_walkok(struct map_session_data *sd) } -static void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_data *ud) -{ - uint8 buf[128]; - int len; +static void clif_move2( struct block_list *bl, struct view_data *vd, struct unit_data *ud ){ struct status_change *sc = NULL; if ((sc = status_get_sc(bl)) && sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE|OPTION_CHASEWALK)) clif_ally_only = true; - len = clif_set_unit_walking(bl,ud,buf); - clif_send(buf,len,bl,AREA_WOS); - if (disguised(bl)) - clif_setdisguise(bl, buf, len); + clif_set_unit_walking( bl, nullptr, ud, AREA_WOS ); if(vd->cloth_color) clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS); @@ -1982,37 +2057,36 @@ void clif_npcbuysell(struct map_session_data* sd, int id) } -/// Presents list of items, that can be bought in an NPC shop (ZC_PC_PURCHASE_ITEMLIST). -/// 00c6 .W { .L .L .B .W }* -void clif_buylist(struct map_session_data *sd, struct npc_data *nd) -{ - int fd,i,c; - bool discount; +/// Presents list of items, that can be bought in an NPC shop. +/// 00c6 .W { .L .L .B .W }* (ZC_PC_PURCHASE_ITEMLIST) +void clif_buylist( struct map_session_data *sd, struct npc_data *nd ){ + nullpo_retv( sd ); + nullpo_retv( nd ); - nullpo_retv(sd); - nullpo_retv(nd); + int fd = sd->fd; - fd = sd->fd; - WFIFOHEAD(fd, 4 + nd->u.shop.count * 11); - WFIFOW(fd,0) = 0xc6; - - c = 0; - discount = npc_shop_discount(nd); - for( i = 0; i < nd->u.shop.count; i++ ) - { - struct item_data* id = itemdb_exists(nd->u.shop.shop_item[i].nameid); - int val = nd->u.shop.shop_item[i].value; - if( id == NULL ) - continue; - WFIFOL(fd, 4+c*11) = val; - WFIFOL(fd, 8+c*11) = (discount) ? pc_modifybuyvalue(sd,val) : val; - WFIFOB(fd,12+c*11) = itemtype(id->nameid); - WFIFOW(fd,13+c*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid; - c++; + if( !session_isActive( fd ) ){ + return; } - WFIFOW(fd,2) = 4 + c*11; - WFIFOSET(fd,WFIFOW(fd,2)); + uint16 len = sizeof( struct PACKET_ZC_PC_PURCHASE_ITEMLIST ) + nd->u.shop.count * sizeof( struct PACKET_ZC_PC_PURCHASE_ITEMLIST_sub ); + WFIFOHEAD( fd, len ); + struct PACKET_ZC_PC_PURCHASE_ITEMLIST *p = (struct PACKET_ZC_PC_PURCHASE_ITEMLIST *)WFIFOP( fd, 0 ); + p->packetType = 0xc6; + + int count = 0; + for( int i = 0, discount = npc_shop_discount( nd ); i < nd->u.shop.count; i++ ){ + int val = nd->u.shop.shop_item[i].value; + + p->items[count].price = val; + p->items[count].discountPrice = ( discount ) ? pc_modifybuyvalue( sd, val ) : val; + p->items[count].itemType = itemtype( nd->u.shop.shop_item[i].nameid ); + p->items[count].itemId = client_nameid( nd->u.shop.shop_item[i].nameid ); + count++; + } + + p->packetLength = sizeof( struct PACKET_ZC_PC_PURCHASE_ITEMLIST ) + count * sizeof( struct PACKET_ZC_PC_PURCHASE_ITEMLIST_sub ); + WFIFOSET( fd, p->packetLength ); } @@ -2067,39 +2141,44 @@ void clif_parse_NPCShopClosed(int fd, struct map_session_data *sd) { **/ void clif_npc_market_open(struct map_session_data *sd, struct npc_data *nd) { #if PACKETVER >= 20131223 - struct npc_item_list *shop = nd->u.shop.shop_item; - unsigned short shop_size = nd->u.shop.count, i, c, cmd = 0x9d5; - struct item_data *id = NULL; - struct s_packet_db *info; - int fd; + nullpo_retv( sd ); + nullpo_retv( nd ); - nullpo_retv(sd); - - if (sd->state.trading) + if( sd->state.trading ){ return; - - info = &packet_db[cmd]; - if (!info || info->len == 0) - return; - - fd = sd->fd; - - WFIFOHEAD(fd, 4 + shop_size * 13); - WFIFOW(fd,0) = cmd; - - for (i = 0, c = 0; i < shop_size; i++) { - if (shop[i].nameid && (id = itemdb_exists(shop[i].nameid))) { - WFIFOW(fd, 4+c*13) = shop[i].nameid; - WFIFOB(fd, 6+c*13) = itemtype(id->nameid); - WFIFOL(fd, 7+c*13) = shop[i].value; - WFIFOL(fd,11+c*13) = shop[i].qty; - WFIFOW(fd,15+c*13) = id->weight; - c++; - } } - WFIFOW(fd,2) = 4 + c*13; - WFIFOSET(fd,WFIFOW(fd,2)); + int fd = sd->fd; + + WFIFOHEAD( fd, sizeof( struct PACKET_ZC_NPC_MARKET_OPEN ) + nd->u.shop.count * sizeof( struct PACKET_ZC_NPC_MARKET_OPEN_sub ) ); + struct PACKET_ZC_NPC_MARKET_OPEN *p = (struct PACKET_ZC_NPC_MARKET_OPEN *)WFIFOP( fd, 0 ); + p->packetType = HEADER_ZC_NPC_MARKET_OPEN; + + int count = 0; + for( int i = 0; i < nd->u.shop.count; i++ ){ + struct npc_item_list *item = &nd->u.shop.shop_item[i]; + + if( !item->nameid ){ + continue; + } + + struct item_data *id = itemdb_exists( item->nameid ); + + if( !id ){ + continue; + } + + p->list[count].nameid = client_nameid( item->nameid ); + p->list[count].type = itemtype( item->nameid ); + p->list[count].price = item->value; + p->list[count].qty = item->qty; + p->list[count].weight = id->weight; + count++; + } + + p->packetLength = sizeof( struct PACKET_ZC_NPC_MARKET_OPEN ) + count * sizeof( struct PACKET_ZC_NPC_MARKET_OPEN_sub ); + WFIFOSET( fd, p->packetLength ); + sd->state.trading = 1; #endif } @@ -2116,42 +2195,45 @@ void clif_parse_NPCMarketClosed(int fd, struct map_session_data *sd) { /// Purchase item from Market shop. void clif_npc_market_purchase_ack(struct map_session_data *sd, uint8 res, uint8 n, struct s_npc_buy_list *list) { #if PACKETVER >= 20131223 - unsigned short cmd = 0x9d7, len = 0; - struct npc_data* nd; - uint8 result = (res == 0 ? 1 : 0); - int fd = 0; - struct s_packet_db *info; + nullpo_retv( sd ); + nullpo_retv( list ); - nullpo_retv(sd); - nullpo_retv((nd = map_id2nd(sd->npc_shopid))); + struct npc_data *nd = map_id2nd( sd->npc_shopid ); - info = &packet_db[cmd]; - if (!info || info->len == 0) - return; + nullpo_retv( nd ); - fd = sd->fd; - len = 5 + 8*n; + int fd = sd->fd; - WFIFOHEAD(fd, len); - WFIFOW(fd, 0) = cmd; - WFIFOW(fd, 2) = len; + WFIFOHEAD( fd, sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT ) + n * sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub ) ); + struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *p = (struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *)WFIFOP( fd, 0 ); + p->PacketType = HEADER_ZC_NPC_MARKET_PURCHASE_RESULT; - if (result) { - uint8 i, j; - struct npc_item_list *shop = nd->u.shop.shop_item; - unsigned short count = nd->u.shop.count; +#if PACKETVER_MAIN_NUM >= 20190807 || PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190814 + p->result = ( res == 0 ? 0 : -1 ); +#else + p->result = ( res == 0 ? 1 : 0 ); +#endif - for (i = 0; i < n; i++) { - WFIFOW(fd, 5+i*8) = list[i].nameid; - WFIFOW(fd, 7+i*8) = list[i].qty; + int count = 0; - ARR_FIND(0, count, j, list[i].nameid == shop[j].nameid); - WFIFOL(fd, 9+i*8) = (j != count) ? shop[j].value : 0; + if( p->result ){ + for( int i = 0, j; i < n; i++ ){ + ARR_FIND( 0, nd->u.shop.count, j, list[i].nameid == nd->u.shop.shop_item[j].nameid ); + + // Not found + if( j == nd->u.shop.count ){ + continue; + } + + p->list[count].ITID = client_nameid( list[i].nameid ); + p->list[count].qty = list[i].qty; + p->list[count].price = nd->u.shop.shop_item[j].value; + count++; } } - WFIFOB(fd, 4) = n; - WFIFOSET(fd, len); + p->PacketLength = sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT ) + count * sizeof( struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub ); + WFIFOSET( fd, p->PacketLength ); #endif } @@ -2159,31 +2241,30 @@ void clif_npc_market_purchase_ack(struct map_session_data *sd, uint8 res, uint8 /// Purchase item from Market shop. void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) { #if PACKETVER >= 20131223 - struct s_packet_db* info; - struct s_npc_buy_list *item_list; - uint16 len = 0, i = 0; - uint8 res = 0, n = 0; + nullpo_retv( sd ); - nullpo_retv(sd); - - if (!sd->npc_shopid) + if( !sd->npc_shopid ){ return; - - info = &packet_db[RFIFOW(fd,0)]; - if (!info || info->len == 0) - return; - len = RFIFOW(fd,info->pos[0]); - n = (len-4) / 6; - - CREATE(item_list, struct s_npc_buy_list, n); - for (i = 0; i < n; i++) { - item_list[i].nameid = RFIFOW(fd,info->pos[1]+i*6); - item_list[i].qty = (uint16)min(RFIFOL(fd,info->pos[2]+i*6),USHRT_MAX); } - res = npc_buylist(sd, n, item_list); - clif_npc_market_purchase_ack(sd, res, n, item_list); - aFree(item_list); + const struct packet_npc_market_purchase *p = (struct packet_npc_market_purchase *)RFIFOP( fd, 0 ); + + int count = ( p->PacketLength - sizeof( struct packet_npc_market_purchase ) ) / sizeof( struct packet_npc_market_purchase_sub ); + + struct s_npc_buy_list *list; + + CREATE( list, struct s_npc_buy_list, count ); + + // Sadly order is reverse + for( int i = 0; i < count; i++ ){ + list[i].nameid = p->list[i].ITID; + list[i].qty = p->list[i].qty; + } + + uint8 res = npc_buylist( sd, count, list ); + clif_npc_market_purchase_ack( sd, res, count, list ); + + aFree( list ); #endif } @@ -2473,76 +2554,98 @@ void clif_cutin(struct map_session_data* sd, const char* image, int type) /*========================================== * Fills in card data from the given item and into the buffer. [Skotlex] *------------------------------------------*/ -static void clif_addcards(unsigned char* buf, struct item* item) -{ - int i=0,j; - if( item == NULL ) { //Blank data - WBUFW(buf,0) = 0; - WBUFW(buf,2) = 0; - WBUFW(buf,4) = 0; - WBUFW(buf,6) = 0; +static void clif_addcards( struct EQUIPSLOTINFO* buf, struct item* item ){ + nullpo_retv( buf ); + + // Blank data + if( item == nullptr ){ + buf->card[0] = 0; + buf->card[1] = 0; + buf->card[2] = 0; + buf->card[3] = 0; return; } - if( item->card[0] == CARD0_PET ) { //pet eggs - WBUFW(buf,0) = 0; - WBUFW(buf,2) = 0; - WBUFW(buf,4) = 0; - WBUFW(buf,6) = item->card[3]; //Pet renamed flag. + + // Pet eggs + if( item->card[0] == CARD0_PET ){ + buf->card[0] = 0; + buf->card[1] = 0; + buf->card[2] = 0; + buf->card[3] = item->card[3]; //Pet renamed flag. return; } - if( item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE ) { //Forged/created items - WBUFW(buf,0) = item->card[0]; - WBUFW(buf,2) = item->card[1]; - WBUFW(buf,4) = item->card[2]; - WBUFW(buf,6) = item->card[3]; + + // Forged/created items + if( item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE ){ + buf->card[0] = item->card[0]; + buf->card[1] = item->card[1]; + buf->card[2] = item->card[2]; + buf->card[3] = item->card[3]; return; } - //Client only receives four cards.. so randomly send them a set of cards. [Skotlex] - if( MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4 ) - i = rnd()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rnd()%3; - //Normal items. - if( item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,0) = j; - else - WBUFW(buf,0) = item->card[i]; + int i = 0, j; - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,2) = j; - else - WBUFW(buf,2) = item->card[i]; + // Client only receives four cards.. so randomly send them a set of cards. [Skotlex] + if( MAX_SLOTS > 4 && ( j = itemdb_slot( item->nameid ) ) > 4 ){ + i = rnd() % ( j - 3 ); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rnd()%3; + } - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,4) = j; - else - WBUFW(buf,4) = item->card[i]; + // Normal items. + if( item->card[i] > 0 && ( j = itemdb_viewid( item->card[i] ) ) > 0 ){ + buf->card[0] = j; + }else{ + buf->card[0] = item->card[i]; + } - if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) - WBUFW(buf,6) = j; - else - WBUFW(buf,6) = item->card[i]; + if( item->card[++i] > 0 && ( j = itemdb_viewid( item->card[i] ) ) > 0 ){ + buf->card[1] = j; + }else{ + buf->card[1] = item->card[i]; + } + + if( item->card[++i] > 0 && ( j = itemdb_viewid( item->card[i] ) ) > 0 ){ + buf->card[2] = j; + }else{ + buf->card[2] = item->card[i]; + } + + if( item->card[++i] > 0 && ( j = itemdb_viewid( item->card[i] ) ) > 0 ){ + buf->card[3] = j; + }else{ + buf->card[3] = item->card[i]; + } } /// Fills in part of the item buffers that calls for variable bonuses data. [Napster] /// A maximum of 5 random options can be supported. -void clif_add_random_options(unsigned char* buf, struct item *it) { -#if PACKETVER >= 20150225 - int i; +static uint8 clif_add_random_options( struct ItemOptions buf[MAX_ITEM_RDM_OPT], struct item* it ){ + nullpo_retr( 0, it ); - for (i = 0; i < MAX_ITEM_RDM_OPT; i++) { - WBUFW(buf, i*5 + 0) = it->option[i].id; // OptIndex - WBUFW(buf, i*5 + 2) = it->option[i].value; // Value - WBUFB(buf, i*5 + 4) = it->option[i].param; // Param1 + uint8 count = 0; + + for( int i = 0; i < MAX_ITEM_RDM_OPT; i++ ){ + if( it->option[i].id ){ + buf[i].index = it->option[i].id; // OptIndex + buf[i].value = it->option[i].value; // Value + buf[i].param = it->option[i].param; // Param1 + count++; + }else{ + buf[i].index = 0; + buf[i].value = 0; + buf[i].param = 0; + } } #if MAX_ITEM_RDM_OPT < 5 - for ( ; i < 5; i++) { - WBUFW(buf, i*5 + 0) = 0; // OptIndex - WBUFW(buf, i*5 + 2) = 0; // Value - WBUFB(buf, i*5 + 4) = 0; // Param1 + for( ; i < 5; i++ ){ + buf[i].index = 0; // OptIndex + buf[i].value = 0; // Value + buf[i].param = 0; // Param1 } #endif -#endif + + return count; } /// Notifies the client, about a received inventory item or the result of a pick-up request. @@ -2552,105 +2655,55 @@ void clif_add_random_options(unsigned char* buf, struct item *it) { /// 0990 .W .W .W .B .B .B .W .W .W .W .L .B .B .L .W (ZC_ITEM_PICKUP_ACK_V5) /// 0a0c .W .W .W .B .B .B .W .W .W .W .L .B .B .L .W {