diff options
Diffstat (limited to 'src/map/vending.c')
-rw-r--r-- | src/map/vending.c | 263 |
1 files changed, 133 insertions, 130 deletions
diff --git a/src/map/vending.c b/src/map/vending.c index c44cb333f..7ac83b69c 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -2,6 +2,8 @@ // For more information, see LICENCE in the main folder #include "../common/nullpo.h" +#include "../common/strlib.h" +#include "../common/utils.h" #include "clif.h" #include "itemdb.h" #include "atcommand.h" @@ -20,124 +22,113 @@ /*========================================== - * 露店閉鎖 + * Close shop *------------------------------------------*/ -void vending_closevending(struct map_session_data *sd) +void vending_closevending(struct map_session_data* sd) { nullpo_retv(sd); - sd->vender_id=0; + sd->vender_id = 0; clif_closevendingboard(&sd->bl,0); - if(use_irc && irc_announce_shop_flag) + + if( use_irc && irc_announce_shop_flag ) irc_announce_shop(sd,0); } /*========================================== - * 露店アイテムリスト要求 + * Request a shop's item list *------------------------------------------*/ -void vending_vendinglistreq(struct map_session_data *sd,int id) +void vending_vendinglistreq(struct map_session_data* sd, int id) { - struct map_session_data *vsd; + struct map_session_data* vsd; nullpo_retv(sd); - if( (vsd=map_id2sd(id)) == NULL ) - return; - if(vsd->vender_id==0) + if( (vsd = map_id2sd(id)) == NULL ) return; - clif_vendinglist(sd,id,vsd->vending); + if( vsd->vender_id == 0 ) + return; // not vending + + clif_vendinglist(sd, id, vsd->vending); } /*========================================== - * 露店アイテム購入 + * Purchase item(s) from a shop *------------------------------------------*/ -void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p) +void vending_purchasereq(struct map_session_data* sd, int id, const uint8* data, int count) { int i, j, w, new_ = 0, blank, vend_list[MAX_VENDING]; double z; unsigned short amount; short idx; - struct map_session_data *vsd = map_id2sd(id); - struct vending vending[MAX_VENDING]; // against duplicate packets + struct s_vending vending[MAX_VENDING]; // against duplicate packets + struct map_session_data* vsd = map_id2sd(id); nullpo_retv(sd); - if (vsd == NULL) - return; - if (vsd->vender_id == 0) - return; - if (vsd->vender_id == sd->bl.id) - return; - if (sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) - ) { - clif_buyvending(sd, 0, 32767, 4); // too far [Lupus] - //probably... we should add either a hack log / or a proper message. But normal player won't see ie anyway - return; - } - - - // check number of buying items - if (len < 8 + 4 || len > 8 + 4 * MAX_VENDING) { - clif_buyvending(sd, 0, 32767, 4); // not enough quantity (index and amount are unknown) - return; - } + if( vsd == NULL || vsd->vender_id == 0 || vsd->vender_id == sd->bl.id ) + return; // invalid shop + if( sd->bl.m != vsd->bl.m || !check_distance_bl(&sd->bl, &vsd->bl, AREA_SIZE) ) + return; // shop too far away + if( count < 1 || count > MAX_VENDING || count > vsd->vend_num ) + return; // invalid amount of purchased items blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory // duplicate item in vending to check hacker with multiple packets - memcpy(&vending, &vsd->vending, sizeof(struct vending) * MAX_VENDING); // copy vending list + memcpy(&vending, &vsd->vending, sizeof(vsd->vending)); // copy vending list // some checks - z = 0.; - w = 0; - for(i = 0; 8 + 4 * i < len; i++) { - amount = *(unsigned short*)(p + 4 * i); - idx = *(short*)(p + 2 + 4 * i) - 2; + z = 0.; // zeny counter + w = 0; // weight counter + for( i = 0; i < count; i++ ) + { + amount = *(uint16*)(data + 4*i + 0); + idx = *(uint16*)(data + 4*i + 2); + idx -= 2; - if (amount <= 0) + if( amount <= 0 ) return; // check of item index in the cart - if (idx < 0 || idx >= MAX_CART) + if( idx < 0 || idx >= MAX_CART ) return; - for(j = 0; j < vsd->vend_num; j++) { - if (vsd->vending[j].index == idx) { - vend_list[i] = j; - break; - } - } - if (j == vsd->vend_num) + ARR_FIND( 0, vsd->vend_num, j, vsd->vending[j].index == idx ); + if( j == vsd->vend_num ) return; //picked non-existing item + else + vend_list[i] = j; z += ((double)vsd->vending[j].value * (double)amount); - if (z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY) { // fix positiv overflow (buyer) + if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY ) + { clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny - return; // zeny s'< - } - if (z + (double)vsd->status.zeny > (double)MAX_ZENY) { // fix positiv overflow (merchand) - clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow - return; // zeny s'< + return; } w += itemdb_weight(vsd->status.cart[idx].nameid) * amount; - if (w + sd->weight > sd->max_weight) { + if( w + sd->weight > sd->max_weight ) + { clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight return; } - if (vending[j].amount > vsd->status.cart[idx].amount) //Check to see if cart/vend info is in sync. + //Check to see if cart/vend info is in sync. + if( vending[j].amount > vsd->status.cart[idx].amount ) vending[j].amount = vsd->status.cart[idx].amount; // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples). - // here, we check cumulativ amounts - if (vending[j].amount < amount) { + // here, we check cumulative amounts + if( vending[j].amount < amount ) + { // send more quantity is not a hack (an other player can have buy items just before) clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity return; - } else - vending[j].amount -= amount; + } + + vending[j].amount -= amount; - switch(pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount)) { + switch( pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount) ) { case ADDITEM_EXIST: break; //We'd add this item to the existing one (in buyers inventory) case ADDITEM_NEW: @@ -151,26 +142,25 @@ void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned cha } //Logs (V)ending Zeny [Lupus] - if(log_config.zeny > 0 ) + if( log_config.zeny > 0 ) log_zeny(vsd, "V", sd, (int)z); - //Logs pc_payzeny(sd, (int)z); - if (battle_config.vending_tax) - z = z*(1 - battle_config.vending_tax/10000); + if( battle_config.vending_tax ) + z -= z * (battle_config.vending_tax/10000.); pc_getzeny(vsd, (int)z); - for(i = 0; 8 + 4 * i < len; i++) { - amount = *(short*)(p + 4 *i); - idx = *(short*)(p + 2 + 4 * i) - 2; - //if (amount < 0) break; // tested at start of the function + for( i = 0; i < count; i++ ) + { + amount = *(uint16*)(data + 4*i + 0); + idx = *(uint16*)(data + 4*i + 2); + idx -= 2; //Logs sold (V)ending items [Lupus] if(log_config.enable_logs&0x4) { - log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, (struct item*)&vsd->status.cart[idx]); - log_pick_pc( sd, "V", vsd->status.cart[idx].nameid, amount, (struct item*)&vsd->status.cart[idx]); + log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, &vsd->status.cart[idx]); + log_pick_pc( sd, "V", vsd->status.cart[idx].nameid, amount, &vsd->status.cart[idx]); } - //Logs // vending item pc_additem(sd, &vsd->status.cart[idx], amount); @@ -179,7 +169,8 @@ void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned cha clif_vendingreport(vsd, idx, amount); //print buyer's name - if(battle_config.buyer_name) { + if( battle_config.buyer_name ) + { char temp[256]; sprintf(temp, msg_txt(265), sd->status.name); clif_disp_onlyself(vsd,temp,strlen(temp)); @@ -187,17 +178,20 @@ void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned cha } //Always save BOTH: buyer and customer - if (save_settings&2) { + if( save_settings&2 ) + { chrif_save(sd,0); chrif_save(vsd,0); } + //check for @AUTOTRADE users [durf] - if (vsd->state.autotrade) + if( vsd->state.autotrade ) { - //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex] - for(i = 0; i < vsd->vend_num && vsd->vending[i].amount < 1; i++); - if (i == vsd->vend_num) + //see if there is anything left in the shop + ARR_FIND( 0, vsd->vend_num, i, vsd->vending[i].amount > 0 ); + if( i == vsd->vend_num ) { + //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex] vending_closevending(vsd); map_quit(vsd); //They have no reason to stay around anymore, do they? } @@ -205,69 +199,78 @@ void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned cha } /*========================================== - * 露店開設 + * Open shop + * data := {<index>.w <amount>.w <value>.l}[count] *------------------------------------------*/ -void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p) +void vending_openvending(struct map_session_data* sd, const char* message, bool flag, const uint8* data, int count) { int i, j; int vending_skill_lvl; nullpo_retv(sd); - if (map[sd->bl.m].flag.novending) { - clif_displaymessage (sd->fd, msg_txt(276)); - return; //Can't vend in novending mapflag maps. - } + if( !flag ) // cancelled + return; // nothing to do - //check shopname len - if(message[0] == '\0') - return; + if (pc_istrading(sd)) + return; // can't have 2 shops at once vending_skill_lvl = pc_checkskill(sd, MC_VENDING); - if(!vending_skill_lvl || !pc_iscarton(sd)) { // cart skill and cart check [Valaris] + // skill level and cart check + if( !vending_skill_lvl || !pc_iscarton(sd) ) + { clif_skill_fail(sd, MC_VENDING, 0, 0); return; } - if (flag) { - // check number of items in shop - if (len < 85 + 8 || len > 85 + 8 * MAX_VENDING || len > 85 + 8 * (2 + vending_skill_lvl)) { - clif_skill_fail(sd, MC_VENDING, 0, 0); - return; - } - for(i = 0, j = 0; (85 + 8 * j < len) && (i < MAX_VENDING); i++, j++) { - sd->vending[i].index = *(short*)(p+8*j)-2; - if (sd->vending[i].index < 0 || sd->vending[i].index >= MAX_CART || - !itemdb_cantrade(&sd->status.cart[sd->vending[i].index], pc_isGM(sd), pc_isGM(sd))) - { - i--; //Preserve the vending index, skip to the next item. - continue; - } - sd->vending[i].amount = *(short*)(p+2+8*j); - sd->vending[i].value = *(int*)(p+4+8*j); - if(sd->vending[i].value > (unsigned int)battle_config.vending_max_value) - sd->vending[i].value = (unsigned int)battle_config.vending_max_value; - else if(sd->vending[i].value < 1) - sd->vending[i].value = 1000000; // auto set to 1 million [celest] - // カート内のアイテム数と販売するアイテム数に相違があったら中止 - if(pc_cartitem_amount(sd, sd->vending[i].index, sd->vending[i].amount) < 0) { // fixes by Valaris and fritz - clif_skill_fail(sd, MC_VENDING, 0, 0); - return; - } - } - if (i != j) - { //Some items were not vended. [Skotlex] - clif_displaymessage (sd->fd, msg_txt(266)); - } - sd->vender_id = sd->bl.id; - sd->vend_num = i; - memcpy(sd->message,message, MESSAGE_SIZE); - sd->message[MESSAGE_SIZE-1] = '\0'; - if (clif_openvending(sd,sd->vender_id,sd->vending) > 0){ - pc_stop_walking(sd,1); - clif_showvendingboard(&sd->bl,message,0); - if(use_irc && irc_announce_shop_flag) - irc_announce_shop(sd,1); - } else - sd->vender_id = 0; + // check number of items in shop + if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) + { // invalid item count + clif_skill_fail(sd, MC_VENDING, 0, 0); + return; + } + + // filter out invalid items + i = 0; + for( j = 0; j < count; j++ ) + { + int index = *(uint16*)(data + 8*j + 0); + unsigned int amount = *(uint16*)(data + 8*j + 2); + unsigned int value = *(uint32*)(data + 8*j + 4); + + index -= 2; // offset adjustment (client says that the first cart position is 2) + + if( index < 0 || index >= MAX_CART // invalid position + || pc_cartitem_amount(sd, index, amount) < 0 // invalid item or insufficient quantity + //NOTE: official server does not do any of the following checks! + || !sd->status.cart[index].identify // unidentified item + || sd->status.cart[index].attribute == 1 // broken item + || !itemdb_cantrade(&sd->status.cart[index], pc_isGM(sd), pc_isGM(sd)) ) // untradeable item + continue; + + sd->vending[i].index = index; + sd->vending[i].amount = amount; + sd->vending[i].value = cap_value(value, 1, (unsigned int)battle_config.vending_max_value); + + i++; // item successfully added + } + + if( i != j ) + clif_displaymessage (sd->fd, msg_txt(266)); //"Some of your items cannot be vended and were removed from the shop." + + if( i == 0 ) + { // no valid item found + clif_skill_fail(sd, MC_VENDING, 0, 0); // custom reply packet + return; } + + sd->vender_id = sd->bl.id; + sd->vend_num = i; + safestrncpy(sd->message, message, MESSAGE_SIZE); + + pc_stop_walking(sd,1); + clif_openvending(sd,sd->vender_id,sd->vending); + clif_showvendingboard(&sd->bl,message,0); + + if( use_irc && irc_announce_shop_flag ) + irc_announce_shop(sd,1); } |