From 41951f3f6be59e77117a5332c3be6cca92cedf90 Mon Sep 17 00:00:00 2001 From: ultramage Date: Mon, 1 Oct 2007 14:55:35 +0000 Subject: * Fixed various trading/vending glitches - fixed vending_tax not working at all (integer division in r10182) - undid change from r8273 where pc_getzeny() treated zeny overflow as an error condition; officially, the value is just bounded to MAX_ZENY - fixed stupid code that, instead of properly checking and filtering invalid items during shop setup, opted to 'hide' these items from the vending list instead... - removed some custom error message packets related to vending - fixed a glitch where the server would open a shop with no items when all entered items were tagged as invalid - split zeny handling from trade_tradeadditem() into a separate func (trade_tradeaddzeny()) - removed loads of redundant code from vending.c git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@11344 54d463be-8e91-2dee-dedb-b68131a5f0ec --- Changelog-Trunk.txt | 14 ++ src/map/clif.c | 438 +++++++++++++++++++++++++--------------------------- src/map/clif.h | 30 ++-- src/map/map.h | 4 +- src/map/pc.c | 71 +++++---- src/map/trade.c | 139 ++++++++++------- src/map/trade.h | 1 + src/map/vending.c | 263 +++++++++++++++---------------- src/map/vending.h | 9 +- 9 files changed, 501 insertions(+), 468 deletions(-) diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index 9b8e89635..e6ecc8df8 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -3,6 +3,20 @@ Date Added AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK. IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. +2007/10/01 + * Fixed various trading/vending glitches [ultramage] + - fixed vending_tax not working at all (integer division in r10182) + - undid change from r8273 where pc_getzeny() treated zeny overflow as + an error condition; officially, the value is just bounded to MAX_ZENY + (this fixes stuff like shops that you can't buy items from). + - fixed stupid code that, instead of properly checking and filtering + invalid items during shop setup, opted to 'hide' these items from + the vending list instead... + - removed some custom error message packets related to vending + - fixed a glitch where the server would open a shop with no items + when all entered items were tagged as invalid + - split zeny handling from trade_tradeadditem() into a separate func + - removed loads of redundant code from vending.c 2007/09/30 * Removed redundant 'subnet' s_subnet structure variable. [ultramage] 2007/09/28 diff --git a/src/map/clif.c b/src/map/clif.c index ef530ea6d..6303552d2 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1958,51 +1958,51 @@ int clif_cutin(struct map_session_data* sd, const char* image, int type) 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; + if( item == NULL ) { //Blank data + WBUFW(buf,0) = 0; + WBUFW(buf,2) = 0; + WBUFW(buf,4) = 0; + WBUFW(buf,6) = 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. + 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. 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]; + 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]; 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) + if( MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4 ) i = rand()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rand()%3; //Normal items. - if (item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0) - WBUFW(buf,0)=j; + if( item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) + WBUFW(buf,0) = j; else - WBUFW(buf,0)= item->card[i]; + WBUFW(buf,0) = item->card[i]; - if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0) - WBUFW(buf,2)=j; + if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) + WBUFW(buf,2) = j; else - WBUFW(buf,2)=item->card[i]; + WBUFW(buf,2) = item->card[i]; - if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0) - WBUFW(buf,4)=j; + if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) + WBUFW(buf,4) = j; else - WBUFW(buf,4)=item->card[i]; + WBUFW(buf,4) = item->card[i]; - if (item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0) - WBUFW(buf,6)=j; + if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 ) + WBUFW(buf,6) = j; else - WBUFW(buf,6)=item->card[i]; + WBUFW(buf,6) = item->card[i]; } /*========================================== @@ -3348,59 +3348,51 @@ void clif_leavechat(struct chat_data* cd, struct map_session_data* sd, bool flag } /*========================================== - * 取り引き要請受け + * Opens a trade request window from char 'name' + * R 00e5 .24B *------------------------------------------*/ -int clif_traderequest(struct map_session_data* sd, const char* name) +void clif_traderequest(struct map_session_data* sd, const char* name) { int fd; + nullpo_retv(sd); - nullpo_retr(0, sd); - - fd=sd->fd; - + fd = sd->fd; WFIFOHEAD(fd,packet_len(0xe5)); - WFIFOW(fd,0)=0xe5; - - strcpy((char*)WFIFOP(fd,2),name); - + WFIFOW(fd,0) = 0xe5; + safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH); WFIFOSET(fd,packet_len(0xe5)); - - return 0; } /*========================================== * 取り引き要求応答 *------------------------------------------*/ -int clif_tradestart(struct map_session_data *sd,int type) +void clif_tradestart(struct map_session_data* sd, int type) { int fd; + nullpo_retv(sd); - nullpo_retr(0, sd); - - fd=sd->fd; + fd = sd->fd; WFIFOHEAD(fd,packet_len(0xe7)); - WFIFOW(fd,0)=0xe7; - WFIFOB(fd,2)=type; + WFIFOW(fd,0) = 0xe7; + WFIFOB(fd,2) = type; WFIFOSET(fd,packet_len(0xe7)); - - return 0; } /*========================================== * 相手方からのアイテム追加 *------------------------------------------*/ -int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount) +void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd, int index, int amount) { int fd; + nullpo_retv(sd); + nullpo_retv(tsd); - nullpo_retr(0, sd); - nullpo_retr(0, tsd); - - fd=tsd->fd; + fd = tsd->fd; WFIFOHEAD(fd,packet_len(0xe9)); - WFIFOW(fd,0)=0xe9; - WFIFOL(fd,2)=amount; - if(index==0){ + WFIFOW(fd,0) = 0xe9; + WFIFOL(fd,2) = amount; + if( index == 0 ) + { WFIFOW(fd,6) = 0; // type id WFIFOB(fd,8) = 0; //identify flag WFIFOB(fd,9) = 0; // attribute @@ -3410,8 +3402,9 @@ int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,i WFIFOW(fd,15)= 0; //card (4w) WFIFOW(fd,17)= 0; //card (4w) } - else{ - index-=2; //index fix + else + { + index -= 2; //index fix if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0) WFIFOW(fd,6) = sd->inventory_data[index]->view_id; else @@ -3422,80 +3415,66 @@ int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,i clif_addcards(WFIFOP(fd, 11), &sd->status.inventory[index]); } WFIFOSET(fd,packet_len(0xe9)); - - return 0; } /*========================================== * アイテム追加成功/失敗 *------------------------------------------*/ -int clif_tradeitemok(struct map_session_data *sd,int index,int fail) +void clif_tradeitemok(struct map_session_data* sd, int index, int fail) { int fd; + nullpo_retv(sd); - nullpo_retr(0, sd); - - fd=sd->fd; + fd = sd->fd; WFIFOHEAD(fd,packet_len(0xea)); - WFIFOW(fd,0)=0xea; - WFIFOW(fd,2)=index; - WFIFOB(fd,4)=fail; + WFIFOW(fd,0) = 0xea; + WFIFOW(fd,2) = index; + WFIFOB(fd,4) = fail; WFIFOSET(fd,packet_len(0xea)); - - return 0; } /*========================================== * 取り引きok押し *------------------------------------------*/ -int clif_tradedeal_lock(struct map_session_data *sd,int fail) +void clif_tradedeal_lock(struct map_session_data* sd, int fail) { int fd; + nullpo_retv(sd); - nullpo_retr(0, sd); - - fd=sd->fd; + fd = sd->fd; WFIFOHEAD(fd,packet_len(0xec)); - WFIFOW(fd,0)=0xec; - WFIFOB(fd,2)=fail; // 0=you 1=the other person + WFIFOW(fd,0) = 0xec; + WFIFOB(fd,2) = fail; // 0=you 1=the other person WFIFOSET(fd,packet_len(0xec)); - - return 0; } /*========================================== * 取り引きがキャンセルされました *------------------------------------------*/ -int clif_tradecancelled(struct map_session_data *sd) +void clif_tradecancelled(struct map_session_data* sd) { int fd; + nullpo_retv(sd); - nullpo_retr(0, sd); - - fd=sd->fd; + fd = sd->fd; WFIFOHEAD(fd,packet_len(0xee)); - WFIFOW(fd,0)=0xee; + WFIFOW(fd,0) = 0xee; WFIFOSET(fd,packet_len(0xee)); - - return 0; } /*========================================== * 取り引き完了 *------------------------------------------*/ -int clif_tradecompleted(struct map_session_data *sd,int fail) +void clif_tradecompleted(struct map_session_data* sd, int fail) { int fd; + nullpo_retv(sd); - nullpo_retr(0, sd); - - fd=sd->fd; + fd = sd->fd; WFIFOHEAD(fd,packet_len(0xf0)); - WFIFOW(fd,0)=0xf0; - WFIFOB(fd,2)=fail; + WFIFOW(fd,0) = 0xf0; + WFIFOB(fd,2) = fail; WFIFOSET(fd,packet_len(0xf0)); - - return 0; } /*========================================== @@ -3639,12 +3618,14 @@ int clif_storageclose(struct map_session_data *sd) void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd) { int len; - if(dstsd->chatID){ + if(dstsd->chatID) + { struct chat_data *cd; cd=(struct chat_data*)map_id2bl(dstsd->chatID); if(cd && cd->usersd[0]==dstsd) clif_dispchat(cd,sd->fd); } + if(dstsd->vender_id) clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd); @@ -5461,193 +5442,178 @@ int clif_cart_delitem(struct map_session_data *sd,int n,int amount) } /*========================================== - * 露店開設 + * Opens the shop creation menu. + * R 012d .w + * 'num' is the number of allowed item slots *------------------------------------------*/ -int clif_openvendingreq(struct map_session_data *sd,int num) +void clif_openvendingreq(struct map_session_data* sd, int num) { int fd; - nullpo_retr(0, sd); + nullpo_retv(sd); - fd=sd->fd; + fd = sd->fd; WFIFOHEAD(fd,packet_len(0x12d)); - WFIFOW(fd,0)=0x12d; - WFIFOW(fd,2)=num; + WFIFOW(fd,0) = 0x12d; + WFIFOW(fd,2) = num; WFIFOSET(fd,packet_len(0x12d)); - - return 0; } /*========================================== - * 露店看板表示 + * Displays a vending board to target/area + * R 0131 .l .80B *------------------------------------------*/ -int clif_showvendingboard(struct block_list* bl, const char* message, int fd) +void clif_showvendingboard(struct block_list* bl, const char* message, int fd) { unsigned char buf[128]; - nullpo_retr(0, bl); + nullpo_retv(bl); - WBUFW(buf,0)=0x131; - WBUFL(buf,2)=bl->id; - strncpy((char*)WBUFP(buf,6),message,80); - if(fd){ + WBUFW(buf,0) = 0x131; + WBUFL(buf,2) = bl->id; + safestrncpy((char*)WBUFP(buf,6), message, 80); + + if( fd ) { WFIFOHEAD(fd,packet_len(0x131)); memcpy(WFIFOP(fd,0),buf,packet_len(0x131)); WFIFOSET(fd,packet_len(0x131)); - }else{ + } else { clif_send(buf,packet_len(0x131),bl,AREA_WOS); } - return 0; } /*========================================== - * 露店看板消去 + * Removes a vending board from screen *------------------------------------------*/ -int clif_closevendingboard(struct block_list* bl,int fd) +void clif_closevendingboard(struct block_list* bl, int fd) { unsigned char buf[16]; - nullpo_retr(0, bl); + nullpo_retv(bl); - WBUFW(buf,0)=0x132; - WBUFL(buf,2)=bl->id; - if(fd){ + WBUFW(buf,0) = 0x132; + WBUFL(buf,2) = bl->id; + if( fd ) { WFIFOHEAD(fd,packet_len(0x132)); memcpy(WFIFOP(fd,0),buf,packet_len(0x132)); WFIFOSET(fd,packet_len(0x132)); - }else{ + } else { clif_send(buf,packet_len(0x132),bl,AREA_WOS); } - - return 0; } + /*========================================== - * 露店アイテムリスト + * Sends a list of items in a shop + * R 0133 .w .l {.l .w .w .B .w .B .B .B .4w}.22B *------------------------------------------*/ -int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending) +void clif_vendinglist(struct map_session_data* sd, int id, struct s_vending* vending) { - struct item_data *data; - int i,n,index,fd; - struct map_session_data *vsd; - unsigned char *buf; + int i,fd; + int count; + struct map_session_data* vsd; - nullpo_retr(0, sd); - nullpo_retr(0, vending); - nullpo_retr(0, vsd=map_id2sd(id)); + nullpo_retv(sd); + nullpo_retv(vending); + nullpo_retv(vsd=map_id2sd(id)); - fd=sd->fd; - WFIFOHEAD(fd, 8+vsd->vend_num*22); - buf = WFIFOP(fd,0); - for(i=0,n=0;ivend_num;i++){ - if(vending[i].amount<=0) - continue; - WBUFL(buf,8+n*22)=vending[i].value; - WBUFW(buf,12+n*22)=vending[i].amount; - WBUFW(buf,14+n*22)=(index=vending[i].index)+2; - if(vsd->status.cart[index].nameid <= 0 || vsd->status.cart[index].amount <= 0) - continue; - data = itemdb_search(vsd->status.cart[index].nameid); - WBUFB(buf,16+n*22)=itemtype(data->type); - if(data->view_id > 0) - WBUFW(buf,17+n*22)=data->view_id; - else - WBUFW(buf,17+n*22)=vsd->status.cart[index].nameid; - WBUFB(buf,19+n*22)=vsd->status.cart[index].identify; - WBUFB(buf,20+n*22)=vsd->status.cart[index].attribute; - WBUFB(buf,21+n*22)=vsd->status.cart[index].refine; - clif_addcards(WBUFP(buf, 22+n*22), &vsd->status.cart[index]); - n++; - } - if(n > 0){ - WBUFW(buf,0)=0x133; - WBUFW(buf,2)=8+n*22; - WBUFL(buf,4)=id; - WFIFOSET(fd,WFIFOW(fd,2)); - } + fd = sd->fd; + count = vsd->vend_num; - return 0; + WFIFOHEAD(fd, 8+count*22); + WFIFOW(fd,0) = 0x133; + WFIFOW(fd,2) = 8+count*22; + WFIFOL(fd,4) = id; + for( i = 0; i < count; i++ ) + { + int index = vending[i].index; + struct item_data* data = itemdb_search(vsd->status.cart[index].nameid); + WFIFOL(fd, 8+i*22) = vending[i].value; + WFIFOW(fd,12+i*22) = vending[i].amount; + WFIFOW(fd,14+i*22) = vending[i].index + 2; + WFIFOB(fd,16+i*22) = itemtype(data->type); + WFIFOW(fd,17+i*22) = ( data->view_id > 0 ) ? data->view_id : vsd->status.cart[index].nameid; + WFIFOB(fd,19+i*22) = vsd->status.cart[index].identify; + WFIFOB(fd,20+i*22) = vsd->status.cart[index].attribute; + WFIFOB(fd,21+i*22) = vsd->status.cart[index].refine; + clif_addcards(WFIFOP(fd, 22+i*22), &vsd->status.cart[index]); + } + WFIFOSET(fd,WFIFOW(fd,2)); } /*========================================== - * 露店アイテム購入失敗 + * Shop purchase failure + * R 0135 .w .w .B + * fail=1 - not enough zeny + * fail=2 - overweight + * fail=4 - out of stock + * fail=5 - "cannot use an npc shop while in a trade" *------------------------------------------*/ -int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail) +void clif_buyvending(struct map_session_data* sd, int index, int amount, int fail) { int fd; - nullpo_retr(0, sd); + nullpo_retv(sd); - fd=sd->fd; + fd = sd->fd; WFIFOHEAD(fd,packet_len(0x135)); - WFIFOW(fd,0)=0x135; - WFIFOW(fd,2)=index+2; - WFIFOW(fd,4)=amount; - WFIFOB(fd,6)=fail; + WFIFOW(fd,0) = 0x135; + WFIFOW(fd,2) = index+2; + WFIFOW(fd,4) = amount; + WFIFOB(fd,6) = fail; WFIFOSET(fd,packet_len(0x135)); - - return 0; } /*========================================== - * 露店開設成功 + * Shop creation success + * R 0136 .w .l {.l .w .w .B .w .B .B .B .4w}.22B* *------------------------------------------*/ -int clif_openvending(struct map_session_data *sd,int id,struct vending *vending) +void clif_openvending(struct map_session_data* sd, int id, struct s_vending* vending) { - struct item_data *data; - int i,n,index,fd; - unsigned char *buf; + int i,fd; + int count; - nullpo_retr(0, sd); + nullpo_retv(sd); - fd=sd->fd; - WFIFOHEAD(fd, 8+sd->vend_num*22); - buf = WFIFOP(fd,0); - for(i = 0, n = 0; i < sd->vend_num; i++) { - if (sd->vend_num > 2+pc_checkskill(sd,MC_VENDING)) return 0; - WBUFL(buf,8+n*22)=vending[i].value; - WBUFW(buf,12+n*22)=(index=vending[i].index)+2; - WBUFW(buf,14+n*22)=vending[i].amount; - if(sd->status.cart[index].nameid <= 0 || sd->status.cart[index].amount <= 0 || !sd->status.cart[index].identify || - sd->status.cart[index].attribute==1) // Prevent unidentified and broken items from being sold [Valaris] - continue; - data = itemdb_search(sd->status.cart[index].nameid); - WBUFB(buf,16+n*22)=itemtype(data->type); - if(data->view_id > 0) - WBUFW(buf,17+n*22)=data->view_id; - else - WBUFW(buf,17+n*22)=sd->status.cart[index].nameid; - WBUFB(buf,19+n*22)=sd->status.cart[index].identify; - WBUFB(buf,20+n*22)=sd->status.cart[index].attribute; - WBUFB(buf,21+n*22)=sd->status.cart[index].refine; - clif_addcards(WBUFP(buf, 22+n*22), &sd->status.cart[index]); - n++; - } - if(n > 0) { - WBUFW(buf,0)=0x136; - WBUFW(buf,2)=8+n*22; - WBUFL(buf,4)=id; - WFIFOSET(fd,WFIFOW(fd,2)); + fd = sd->fd; + count = sd->vend_num; + + WFIFOHEAD(fd, 8+count*22); + WFIFOW(fd,0) = 0x136; + WFIFOW(fd,2) = 8+count*22; + WFIFOL(fd,4) = id; + for( i = 0; i < count; i++ ) + { + int index = vending[i].index; + struct item_data* data = itemdb_search(sd->status.cart[index].nameid); + WFIFOL(fd, 8+i*22) = vending[i].value; + WFIFOW(fd,12+i*22) = vending[i].index + 2; + WFIFOW(fd,14+i*22) = vending[i].amount; + WFIFOB(fd,16+i*22) = itemtype(data->type); + WFIFOW(fd,17+i*22) = ( data->view_id > 0 ) ? data->view_id : sd->status.cart[index].nameid; + WFIFOB(fd,19+i*22) = sd->status.cart[index].identify; + WFIFOB(fd,20+i*22) = sd->status.cart[index].attribute; + WFIFOB(fd,21+i*22) = sd->status.cart[index].refine; + clif_addcards(WFIFOP(fd,22+count*22), &sd->status.cart[index]); } - return n; + WFIFOSET(fd,WFIFOW(fd,2)); } /*========================================== - * 露店アイテム販売報告 + * Inform merchant that someone has bought an item. + * R 0137 .w .w *------------------------------------------*/ -int clif_vendingreport(struct map_session_data *sd,int index,int amount) +void clif_vendingreport(struct map_session_data* sd, int index, int amount) { int fd; - nullpo_retr(0, sd); + nullpo_retv(sd); - fd=sd->fd; + fd = sd->fd; WFIFOHEAD(fd,packet_len(0x137)); - WFIFOW(fd,0)=0x137; - WFIFOW(fd,2)=index+2; - WFIFOW(fd,4)=amount; + WFIFOW(fd,0) = 0x137; + WFIFOW(fd,2) = index+2; + WFIFOW(fd,4) = amount; WFIFOSET(fd,packet_len(0x137)); - - return 0; } /*========================================== * パーティ作成完了 @@ -9345,7 +9311,13 @@ void clif_parse_TradeAck(int fd,struct map_session_data *sd) *------------------------------------------*/ void clif_parse_TradeAddItem(int fd,struct map_session_data *sd) { - trade_tradeadditem(sd,RFIFOW(fd,2),RFIFOL(fd,4)); + int index = RFIFOW(fd,2); + int amount = RFIFOL(fd,4); + + if( index == 0 ) + trade_tradeaddzeny(sd, amount); + else + trade_tradeadditem(sd, index, amount); } /*========================================== @@ -10209,7 +10181,7 @@ void clif_parse_PartyMessage(int fd, struct map_session_data* sd) /*========================================== * 露店閉鎖 *------------------------------------------*/ -void clif_parse_CloseVending(int fd, struct map_session_data *sd) +void clif_parse_CloseVending(int fd, struct map_session_data* sd) { vending_closevending(sd); } @@ -10217,31 +10189,49 @@ void clif_parse_CloseVending(int fd, struct map_session_data *sd) /*========================================== * 露店アイテムリスト要求 *------------------------------------------*/ -void clif_parse_VendingListReq(int fd, struct map_session_data *sd) +void clif_parse_VendingListReq(int fd, struct map_session_data* sd) { vending_vendinglistreq(sd,RFIFOL(fd,2)); - if(sd->npc_id) + + if( sd->npc_id ) npc_event_dequeue(sd); } /*========================================== - * 露店アイテム購入 + * Shop item(s) purchase request + * S 0134 .w .l {.w .w}.4B* *------------------------------------------*/ -void clif_parse_PurchaseReq(int fd, struct map_session_data *sd) +void clif_parse_PurchaseReq(int fd, struct map_session_data* sd) { - vending_purchasereq(sd, RFIFOW(fd,2), RFIFOL(fd,4), RFIFOP(fd,8)); + int len = (int)RFIFOW(fd,2) - 8; + int id = (int)RFIFOL(fd,4); + const uint8* data = (uint8*)RFIFOP(fd,8); + + vending_purchasereq(sd, id, data, len/4); } /*========================================== - * 露店開設 + * Confirm or cancel the shop preparation window + * S 01b2 .w .80B .B {.w .w .l}.8B* + * flag: 0=cancel, 1=confirm *------------------------------------------*/ -void clif_parse_OpenVending(int fd,struct map_session_data *sd) +void clif_parse_OpenVending(int fd, struct map_session_data* sd) { - if (pc_istrading(sd)) - return; + short len = (short)RFIFOW(fd,2) - 85; + const char* message = (char*)RFIFOP(fd,4); + bool flag = (bool)RFIFOB(fd,84); + const uint8* data = (uint8*)RFIFOP(fd,85); + if (sd->sc.data[SC_NOCHAT].timer!=-1 && sd->sc.data[SC_NOCHAT].val1&MANNER_NOROOM) return; - vending_openvending(sd, RFIFOW(fd,2), (char*)RFIFOP(fd,4), RFIFOB(fd,84), RFIFOP(fd,85)); + if (map[sd->bl.m].flag.novending) { + clif_displaymessage (sd->fd, msg_txt(276)); // "You can't open shop on this map" + return; + } + if( message[0] == '\0' ) // invalid input + return; + + vending_openvending(sd, message, flag, data, len/8); } /*========================================== diff --git a/src/map/clif.h b/src/map/clif.h index 79f69c87d..0e0667503 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -19,7 +19,7 @@ struct npc_data; struct chat_data; struct flooritem_data; struct skill_unit; -struct vending; +struct s_vending; struct party; struct party_data; struct guild; @@ -166,13 +166,13 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd); void clif_hotkeys_send(struct map_session_data *sd); // trade -int clif_traderequest(struct map_session_data* sd, const char* name); -int clif_tradestart(struct map_session_data *sd,int type); -int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount); -int clif_tradeitemok(struct map_session_data *sd,int index,int fail); -int clif_tradedeal_lock(struct map_session_data *sd,int fail); -int clif_tradecancelled(struct map_session_data *sd); -int clif_tradecompleted(struct map_session_data *sd,int fail); +void clif_traderequest(struct map_session_data* sd, const char* name); +void clif_tradestart(struct map_session_data* sd, int type); +void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd, int index, int amount); +void clif_tradeitemok(struct map_session_data* sd, int index, int fail); +void clif_tradedeal_lock(struct map_session_data* sd, int fail); +void clif_tradecancelled(struct map_session_data* sd); +void clif_tradecompleted(struct map_session_data* sd, int fail); // storage #include "storage.h" @@ -266,13 +266,13 @@ int clif_mvp_exp(struct map_session_data *sd,unsigned long exp); void clif_changed_dir(struct block_list *bl, int area); // vending -int clif_openvendingreq(struct map_session_data *sd,int num); -int clif_showvendingboard(struct block_list* bl, const char* message, int fd); -int clif_closevendingboard(struct block_list* bl,int fd); -int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending); -int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail); -int clif_openvending(struct map_session_data *sd,int id,struct vending *vending); -int clif_vendingreport(struct map_session_data *sd,int index,int amount); +void clif_openvendingreq(struct map_session_data* sd, int num); +void clif_showvendingboard(struct block_list* bl, const char* message, int fd); +void clif_closevendingboard(struct block_list* bl,int fd); +void clif_vendinglist(struct map_session_data* sd,int id, struct s_vending* vending); +void clif_buyvending(struct map_session_data* sd, int index, int amount, int fail); +void clif_openvending(struct map_session_data* sd, int id, struct s_vending* vending); +void clif_vendingreport(struct map_session_data* sd, int index, int amount); int clif_movetoattack(struct map_session_data *sd,struct block_list *bl); diff --git a/src/map/map.h b/src/map/map.h index c26b00505..eef71e7c1 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -397,7 +397,7 @@ struct status_change { unsigned int option;// effect state }; -struct vending { +struct s_vending { short index; unsigned short amount; unsigned int value; @@ -771,7 +771,7 @@ struct map_session_data { int vender_id; int vend_num; char message[MESSAGE_SIZE]; - struct vending vending[MAX_VENDING]; + struct s_vending vending[MAX_VENDING]; struct pet_data *pd; struct homun_data *hd; // [blackhole89] diff --git a/src/map/pc.c b/src/map/pc.c index 7a60b7232..1c762ca44 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -67,37 +67,36 @@ char motd_text[MOTD_LINE_SIZE][256]; // Message of the day buffer [Valaris] static const char feel_var[3][NAME_LENGTH] = {"PC_FEEL_SUN","PC_FEEL_MOON","PC_FEEL_STAR"}; static const char hate_var[3][NAME_LENGTH] = {"PC_HATE_MOB_SUN","PC_HATE_MOB_MOON","PC_HATE_MOB_STAR"}; -int pc_isGM(struct map_session_data *sd) +int pc_isGM(struct map_session_data* sd) { int i; - nullpo_retr(0, sd); - if(sd->bl.type!=BL_PC ) + if( sd->bl.type != BL_PC ) return 0; - for(i = 0; i < GM_num; i++) - if (gm_account[i].account_id == sd->status.account_id) - return gm_account[i].level; - return 0; - + ARR_FIND( 0, GM_num, i, gm_account[i].account_id == sd->status.account_id ); + return ( i < GM_num ) ? gm_account[i].level : 0; } int pc_set_gm_level(int account_id, int level) { int i; - for (i = 0; i < GM_num; i++) { - if (account_id == gm_account[i].account_id) { - gm_account[i].level = level; - return 0; - } - } - GM_num++; - gm_account = (struct gm_account *) aRealloc(gm_account, sizeof(struct gm_account) * GM_num); - gm_account[GM_num - 1].account_id = account_id; - gm_account[GM_num - 1].level = level; - return 0; + ARR_FIND( 0, GM_num, i, account_id == gm_account[i].account_id ); + if( i < GM_num ) + { + gm_account[i].level = level; + } + else + { + gm_account = (struct gm_account *) aRealloc(gm_account, (GM_num + 1) * sizeof(struct gm_account)); + gm_account[GM_num].account_id = account_id; + gm_account[GM_num].level = level; + GM_num++; + } + + return 0; } static int pc_invincible_timer(int tid,unsigned int tick,int id,int data) @@ -2708,16 +2707,16 @@ int pc_payzeny(struct map_session_data *sd,int zeny) { nullpo_retr(0, sd); - if(sd->state.finalsave) + if( sd->state.finalsave ) return 1; - if (zeny < 0) + if( zeny < 0 ) return pc_getzeny(sd, -zeny); - if (sd->status.zeny < zeny) + if( sd->status.zeny < zeny ) return 1; //Not enough. - sd->status.zeny-=zeny; + sd->status.zeny -= zeny; clif_updatestatus(sd,SP_ZENY); return 0; @@ -2730,23 +2729,25 @@ int pc_getzeny(struct map_session_data *sd,int zeny) { nullpo_retr(0, sd); - if(sd->state.finalsave) + if( sd->state.finalsave ) return 1; - if(zeny < 0) + if( zeny < 0 ) return pc_payzeny(sd, -zeny); - if (sd->status.zeny > MAX_ZENY -zeny) - return 1; //Overflow + if( zeny > MAX_ZENY - sd->status.zeny ) + zeny = MAX_ZENY - sd->status.zeny; - sd->status.zeny+=zeny; + sd->status.zeny += zeny; clif_updatestatus(sd,SP_ZENY); - if(zeny > 0 && sd->state.showzeny){ + if( zeny > 0 && sd->state.showzeny ) + { char output[255]; sprintf(output, "Gained %dz.", zeny); clif_disp_onlyself(sd,output,strlen(output)); } + return 0; } @@ -3219,17 +3220,19 @@ int pc_putitemtocart(struct map_session_data *sd,int idx,int amount) { /*========================================== * カ?ト?のアイテム?確認(個?の差分を返す) *------------------------------------------*/ -int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount) +int pc_cartitem_amount(struct map_session_data* sd, int idx, int amount) { - struct item *item_data; + struct item* item_data; nullpo_retr(-1, sd); - nullpo_retr(-1, item_data=&sd->status.cart[idx]); - if( item_data->nameid==0 || !item_data->amount) + item_data = &sd->status.cart[idx]; + if( item_data->nameid == 0 || item_data->amount == 0 ) return -1; - return item_data->amount-amount; + + return item_data->amount - amount; } + /*========================================== * カ?トからアイテム移動 *------------------------------------------*/ diff --git a/src/map/trade.c b/src/map/trade.c index a58df1eeb..0bf7e4432 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -310,95 +310,110 @@ int trade_check(struct map_session_data *sd, struct map_session_data *tsd) } /*========================================== - * Adds an item/qty to the trade window [rewrite by Skotlex] + * Adds an item/qty to the trade window *------------------------------------------*/ void trade_tradeadditem(struct map_session_data *sd, int index, int amount) { struct map_session_data *target_sd; struct item *item; int trade_i, trade_weight; + int src_lv, dst_lv; nullpo_retv(sd); - if (!sd->state.trading || sd->state.deal_locked > 0) + if( !sd->state.trading || sd->state.deal_locked > 0 ) return; //Can't add stuff. - if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) { + if( (target_sd = map_id2sd(sd->trade_partner)) == NULL ) + { trade_tradecancel(sd); return; } - if (amount == 0) + if( amount == 0 ) { //Why do this.. ~.~ just send an ack, the item won't display on the trade window. clif_tradeitemok(sd, index, 0); return; } - if (index == 0) - { //Adding Zeny - if (amount >= 0 && amount <= sd->status.zeny && // check amount - (amount <= MAX_ZENY - target_sd->status.zeny)) // fix positiv overflow - { //Check Ok - sd->deal.zeny = amount; - clif_tradeadditem(sd, target_sd, 0, amount); - } else //Send overweight when trying to add too much zeny? Hope they get the idea... - clif_tradeitemok(sd, 0, 1); - return; - } + index -= 2; // 0 is for zeny, 1 is unknown. Gravity, go figure... - index = index -2; //Why the actual index used is -2? //Item checks... - if (index < 0 || index >= MAX_INVENTORY) + if( index < 0 || index >= MAX_INVENTORY ) return; - if (amount < 0 || amount > sd->status.inventory[index].amount) + if( amount < 0 || amount > sd->status.inventory[index].amount ) return; item = &sd->status.inventory[index]; - trade_i = pc_isGM(sd); //Recycling the variables to check for trad restrict. - trade_weight = pc_isGM(target_sd); - if (!itemdb_cantrade(item, trade_i, trade_weight) && //Can't trade - (pc_get_partner(sd) != target_sd || - !itemdb_canpartnertrade(item, trade_i, trade_weight))) //Can't partner-trade + src_lv = pc_isGM(sd); + dst_lv = pc_isGM(target_sd); + if( !itemdb_cantrade(item, src_lv, dst_lv) && //Can't trade + (pc_get_partner(sd) != target_sd || !itemdb_canpartnertrade(item, src_lv, dst_lv)) ) //Can't partner-trade { clif_displaymessage (sd->fd, msg_txt(260)); clif_tradeitemok(sd, index+2, 1); return; } - for(trade_i = 0; trade_i < 10; trade_i++) - { //Locate a trade position - if (sd->deal.item[trade_i].index == index || - sd->deal.item[trade_i].amount == 0) - break; - } - if (trade_i >= 10) //No space left + //Locate a trade position + ARR_FIND( 0, 10, trade_i, sd->deal.item[trade_i].index == index || sd->deal.item[trade_i].amount == 0 ); + if( trade_i == 10 ) //No space left { clif_tradeitemok(sd, index+2, 1); return; } trade_weight = sd->inventory_data[index]->weight * amount; - if (target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight) + if( target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight ) { //fail to add item -- the player was over weighted. clif_tradeitemok(sd, index+2, 1); return; } - if (sd->deal.item[trade_i].index == index) + if( sd->deal.item[trade_i].index == index ) { //The same item as before is being readjusted. - if (sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount) + if( sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount ) { //packet deal exploit check amount = sd->status.inventory[index].amount - sd->deal.item[trade_i].amount; trade_weight = sd->inventory_data[index]->weight * amount; } sd->deal.item[trade_i].amount += amount; - } else { //New deal item + } + else + { //New deal item sd->deal.item[trade_i].index = index; sd->deal.item[trade_i].amount = amount; } sd->deal.weight += trade_weight; clif_tradeitemok(sd, index+2, 0); // Return the index as it was received - clif_tradeadditem(sd, target_sd, index+2, amount); //index fix + clif_tradeadditem(sd, target_sd, index+2, amount); +} + +/*========================================== + * Adds the specified amount of zeny to the trade window + *------------------------------------------*/ +void trade_tradeaddzeny(struct map_session_data* sd, int amount) +{ + struct map_session_data* target_sd; + nullpo_retv(sd); + + if( !sd->state.trading || sd->state.deal_locked > 0 ) + return; //Can't add stuff. + + if( (target_sd = map_id2sd(sd->trade_partner)) == NULL ) + { + trade_tradecancel(sd); + return; + } + + if( amount < 0 || amount > sd->status.zeny || amount > MAX_ZENY - target_sd->status.zeny ) + { // invalid values, no appropriate packet for it => abort + trade_tradecancel(sd); + return; + } + + sd->deal.zeny = amount; + clif_tradeadditem(sd, target_sd, 0, amount); } /*========================================== @@ -511,36 +526,41 @@ void trade_tradecommit(struct map_session_data *sd) } // trade is accepted and correct. - for(trade_i = 0; trade_i < 10; trade_i++) { + for( trade_i = 0; trade_i < 10; trade_i++ ) + { int n; - if (sd->deal.item[trade_i].amount) { + if (sd->deal.item[trade_i].amount) + { n = sd->deal.item[trade_i].index; flag = pc_additem(tsd, &sd->status.inventory[n], sd->deal.item[trade_i].amount); - if (flag == 0) { + if (flag == 0) + { //Logs (T)rade [Lupus] - if(log_config.enable_logs&0x2) { + if(log_config.enable_logs&0x2) + { log_pick_pc(sd, "T", sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]); log_pick_pc(tsd, "T", sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]); } - //Logs pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1); } else clif_additem(sd, n, sd->deal.item[trade_i].amount, 0); sd->deal.item[trade_i].index = 0; sd->deal.item[trade_i].amount = 0; } - if (tsd->deal.item[trade_i].amount) { + if (tsd->deal.item[trade_i].amount) + { n = tsd->deal.item[trade_i].index; flag = pc_additem(sd, &tsd->status.inventory[n], tsd->deal.item[trade_i].amount); - if (flag == 0) { + if (flag == 0) + { //Logs (T)rade [Lupus] - if(log_config.enable_logs&0x2) { + if(log_config.enable_logs&0x2) + { log_pick_pc(tsd, "T", tsd->status.inventory[n].nameid, -(tsd->deal.item[trade_i].amount), &tsd->status.inventory[n]); log_pick_pc(sd, "T", tsd->status.inventory[n].nameid, tsd->deal.item[trade_i].amount, &tsd->status.inventory[n]); } - //Logs pc_delitem(tsd, n, tsd->deal.item[trade_i].amount, 1); } else clif_additem(tsd, n, tsd->deal.item[trade_i].amount, 0); @@ -548,21 +568,21 @@ void trade_tradecommit(struct map_session_data *sd) tsd->deal.item[trade_i].amount = 0; } } - if (sd->deal.zeny || tsd->deal.zeny) { - if (sd->deal.zeny) { - sd->status.zeny -= sd->deal.zeny; - tsd->status.zeny += sd->deal.zeny; - if (log_config.zeny) - log_zeny(tsd, "T", sd, sd->deal.zeny);//Logs Zeny (T)rade [Lupus] - sd->deal.zeny = 0; - } - if (tsd->deal.zeny) { - tsd->status.zeny -= tsd->deal.zeny; - sd->status.zeny += tsd->deal.zeny; - if (log_config.zeny) - log_zeny(sd, "T", tsd, tsd->deal.zeny);//Logs Zeny (T)rade [Lupus] - tsd->deal.zeny = 0; - } + + if( sd->deal.zeny || tsd->deal.zeny ) + { + sd->status.zeny += tsd->deal.zeny - sd->deal.zeny; + tsd->status.zeny += sd->deal.zeny - tsd->deal.zeny; + + //Logs Zeny (T)rade [Lupus] + if( sd->deal.zeny && log_config.zeny ) + log_zeny(tsd, "T", sd, sd->deal.zeny); + if( tsd->deal.zeny && log_config.zeny ) + log_zeny(sd, "T", tsd, tsd->deal.zeny); + + sd->deal.zeny = 0; + tsd->deal.zeny = 0; + clif_updatestatus(sd, SP_ZENY); clif_updatestatus(tsd, SP_ZENY); } @@ -577,6 +597,7 @@ void trade_tradecommit(struct map_session_data *sd) clif_tradecompleted(sd, 0); clif_tradecompleted(tsd, 0); + // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players if (save_settings&1) { diff --git a/src/map/trade.h b/src/map/trade.h index cdcae7492..724ed2253 100644 --- a/src/map/trade.h +++ b/src/map/trade.h @@ -10,6 +10,7 @@ struct map_session_data; void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd); void trade_tradeack(struct map_session_data *sd,int type); void trade_tradeadditem(struct map_session_data *sd,int index,int amount); +void trade_tradeaddzeny(struct map_session_data *sd,int amount); void trade_tradeok(struct map_session_data *sd); void trade_tradecancel(struct map_session_data *sd); void trade_tradecommit(struct map_session_data *sd); 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 := {.w .w .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); } diff --git a/src/map/vending.h b/src/map/vending.h index 3b247e549..e65f55e93 100644 --- a/src/map/vending.h +++ b/src/map/vending.h @@ -4,12 +4,13 @@ #ifndef _VENDING_H_ #define _VENDING_H_ +#include "../common/cbasetypes.h" //#include "map.h" struct map_session_data; -void vending_closevending(struct map_session_data *sd); -void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p); -void vending_vendinglistreq(struct map_session_data *sd,int id); -void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p); +void vending_closevending(struct map_session_data* sd); +void vending_openvending(struct map_session_data* sd, const char* message, bool flag, const uint8* data, int count); +void vending_vendinglistreq(struct map_session_data* sd, int id); +void vending_purchasereq(struct map_session_data* sd, int id, const uint8* data, int count); #endif /* _VENDING_H_ */ -- cgit v1.2.3-70-g09d2