Ver Fonte

* 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://svn.code.sf.net/p/rathena/svn/trunk@11344 54d463be-8e91-2dee-dedb-b68131a5f0ec

ultramage há 17 anos atrás
pai
commit
6a2e87e03b
9 ficheiros alterados com 501 adições e 468 exclusões
  1. 14 0
      Changelog-Trunk.txt
  2. 214 224
      src/map/clif.c
  3. 15 15
      src/map/clif.h
  4. 2 2
      src/map/map.h
  5. 37 34
      src/map/pc.c
  6. 80 59
      src/map/trade.c
  7. 1 0
      src/map/trade.h
  8. 133 130
      src/map/vending.c
  9. 5 4
      src/map/vending.h

+ 14 - 0
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

+ 214 - 224
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 <nick>.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 <num>.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 <ID>.l <message>.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 <len>.w <ID>.l {<value>.l <amount>.w <index>.w <type>.B <item ID>.w <identify flag>.B <attribute?>.B <refine>.B <card>.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;i<vsd->vend_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 <index>.w <amount>.w <fail>.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 <len>.w <ID>.l {<value>.l <index>.w <amount>.w <type>.B <item ID>.w <identify flag>.B <attribute?>.B <refine>.B <card>.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 <index>.w <amount>.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 <len>.w <ID>.l {<amount>.w <index>.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 <len>.w <message>.80B <flag>.B {<index>.w <amount>.w <value>.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);
 }
 
 /*==========================================

+ 15 - 15
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);
 

+ 2 - 2
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]

+ 37 - 34
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;
 }
+
 /*==========================================
  * カ?トからアイテム移動
  *------------------------------------------*/

+ 80 - 59
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)
   	{

+ 1 - 0
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);

+ 133 - 130
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);
 }

+ 5 - 4
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_ */