Prechádzať zdrojové kódy

Bug Fixes:
- Failure when initializing autotrade persitance for PACKETVER < 20120201 or NEW_CARTS, ENABLE_SC_SAVING aren't defined
- Added 'feature.autotrade_open_delay' config at conf/battle/feature.conf to give delay to open vending/buyingstore by autotrade persistency (bugreport:9077)
- Add immediate save after vending/buyingstore opened to avoid missing sc_data (for SC_PUSH_CART) when server terminated before saving routine (bugreport:9077)
- Disable to trigger NPCE_LOGIN (and NPCE_LOADMAP?) for autotraders, avoid vending_reopen & buyingstore_reopen failure (bugreport:9077)
- Set player's state, 'monster_ignore' when autotrade
- EXP calculation overload (bugreport:9127)
- Added macro apply_rate(val,rate) & apply_rate2(val,rate,per)

Thank you all for reports & helps

Signed-off-by: Cydh Ramdh <house.bad@gmail.com>

Cydh Ramdh 11 rokov pred
rodič
commit
171e2f2be9

+ 3 - 0
conf/battle/feature.conf

@@ -43,3 +43,6 @@ feature.autotrade_direction: 4
 
 // Do you want your autotraders to sit? (Note 1)
 feature.autotrade_sit: yes
+
+// Delay in miliseconds to open vending/buyingsotre after player logged in.
+feature.autotrade_open_delay: 5000

+ 2 - 2
src/char/char_mapif.c

@@ -348,7 +348,7 @@ int chmapif_parse_regmapuser(int fd, int id){
  */
 int chmapif_parse_reqsavechar(int fd, int id){
 	if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
-            return 0;
+		return 0;
 	{
 		int aid = RFIFOL(fd,4), cid = RFIFOL(fd,8), size = RFIFOW(fd,2);
 		struct online_char_data* character;
@@ -449,7 +449,7 @@ int chmapif_parse_authok(int fd){
 //Request to save skill cooldown data
 int chmapif_parse_req_saveskillcooldown(int fd){
 	if( RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2) )
-            return 0;
+		return 0;
 	else {
 		int count, aid, cid;
 		aid = RFIFOL(fd,4);

+ 7 - 1
src/common/utils.h

@@ -14,9 +14,15 @@ void ShowDump(const void* buffer, size_t length);
 void findfile(const char *p, const char *pat, void (func)(const char*));
 bool exists(const char* filename);
 
-//Caps values to min/max
+/// Caps values to min/max
 #define cap_value(a, min, max) ((a >= max) ? max : (a <= min) ? min : a)
 
+/// Apply rate for val, divided by 100)
+#define apply_rate(val, rate) (((rate) == 100) ? (val) : ((val) > 100000) ? (val) / 100 * (rate) : (val) * (rate) / 100)
+
+/// Apply rate for val, divided by per
+#define apply_rate2(val, rate, per) (((rate) == (per)) ? (val) : ((val) > 100000) ? (val) / (per) * (rate) : (val) * (rate) / (per))
+
 /// calculates the value of A / B, in percent (rounded down)
 unsigned int get_percentage(const unsigned int A, const unsigned int B);
 

+ 1 - 0
src/map/atcommand.c

@@ -5752,6 +5752,7 @@ ACMD_FUNC(autotrade) {
 	}
 
 	sd->state.autotrade = 1;
+	sd->state.monster_ignore = 1;
 
 	if( sd->state.vending ){
 		if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;", vendings_db, sd->vender_id ) != SQL_SUCCESS ){

+ 1 - 0
src/map/battle.c

@@ -7754,6 +7754,7 @@ static const struct _battle_data {
 	{ "feature.autotrade",					&battle_config.feature_autotrade,				1,		0,		1,				},
 	{ "feature.autotrade_direction",		&battle_config.feature_autotrade_direction,		4,		0,		7,				},
 	{ "feature.autotrade_sit",				&battle_config.feature_autotrade_sit,			1,		0,		1,				},
+	{ "feature.autotrade_open_delay",		&battle_config.feature_autotrade_open_delay,	5000,	1000,	INT_MAX,		},
 	{ "disp_serverbank_msg",				&battle_config.disp_serverbank_msg,				0,		0,		1,				},
 	{ "warg_can_falcon",                    &battle_config.warg_can_falcon,                 0,      0,      1,              },
 	{ "path_blown_halt",                    &battle_config.path_blown_halt,                 1,      0,      1,              },

+ 1 - 0
src/map/battle.h

@@ -537,6 +537,7 @@ extern struct Battle_Config
 	int feature_autotrade;
 	int feature_autotrade_direction;
 	int feature_autotrade_sit;
+	int feature_autotrade_open_delay;
 
 	// Fame points
 	int fame_taekwon_mission;

+ 59 - 28
src/map/buyingstore.c

@@ -73,27 +73,32 @@ static unsigned int buyingstore_getuid(void)
 	return ++buyingstore_nextid;
 }
 
-
-bool buyingstore_setup(struct map_session_data* sd, unsigned char slots){
+/**
+* Attempt to setup buying store fast check before create new one
+* @param sd
+* @param slots Number of item on the list
+* @return 0 If success, 1 - Cannot open, 2 - Manner penalty, 3 - Mapflag restiction, 4 - Cell restriction
+*/
+char buyingstore_setup(struct map_session_data* sd, unsigned char slots){
 	if (!battle_config.feature_buying_store || sd->state.vending || sd->state.buyingstore || sd->state.trading || slots == 0) {
-		return false;
+		return 1;
 	}
 
 	if( sd->sc.data[SC_NOCHAT] && (sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) )
 	{// custom: mute limitation
-		return false;
+		return 2;
 	}
 
 	if( map[sd->bl.m].flag.novending )
 	{// custom: no vending maps
 		clif_displaymessage(sd->fd, msg_txt(sd,276)); // "You can't open a shop on this map"
-		return false;
+		return 3;
 	}
 
 	if( map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) )
 	{// custom: no vending cells
 		clif_displaymessage(sd->fd, msg_txt(sd,204)); // "You can't open a shop on this cell."
-		return false;
+		return 4;
 	}
 
 	if( slots > MAX_BUYINGSTORE_SLOTS )
@@ -105,25 +110,34 @@ bool buyingstore_setup(struct map_session_data* sd, unsigned char slots){
 	sd->buyingstore.slots = slots;
 	clif_buyingstore_open(sd);
 
-	return true;
+	return 0;
 }
 
-
-bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count)
+/**
+* Attempt to create new buying store
+* @param sd
+* @param zenylimit
+* @param result
+* @param storename
+* @param *itemlist { <nameid>.W, <amount>.W, <price>.L }*
+* @param count Number of item on the itemlist
+* @return 0 If success, 1 - Cannot open, 2 - Manner penalty, 3 - Mapflag restiction, 4 - Cell restriction, 5 - Invalid count/result, 6 - Cannot give item, 7 - Will be overweight
+*/
+char buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count)
 {
 	unsigned int i, weight, listidx;
 	char message_sql[MESSAGE_SIZE*2];
 
 	if( !result || count == 0 )
 	{// canceled, or no items
-		return false;
+		return 5;
 	}
 
 	if( !battle_config.feature_buying_store || pc_istrading(sd) || sd->buyingstore.slots == 0 || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || !storename[0] )
 	{// disabled or invalid input
 		sd->buyingstore.slots = 0;
 		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
-		return false;
+		return 1;
 	}
 
 	if( !pc_can_give_items(sd) )
@@ -131,24 +145,24 @@ bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha
 		sd->buyingstore.slots = 0;
 		clif_displaymessage(sd->fd, msg_txt(sd,246));
 		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
-		return false;
+		return 6;
 	}
 
 	if( sd->sc.data[SC_NOCHAT] && (sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM) )
 	{// custom: mute limitation
-		return false;
+		return 2;
 	}
 
 	if( map[sd->bl.m].flag.novending )
 	{// custom: no vending maps
 		clif_displaymessage(sd->fd, msg_txt(sd,276)); // "You can't open a shop on this map"
-		return false;
+		return 3;
 	}
 
 	if( map_getcell(sd->bl.m, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING) )
 	{// custom: no vending cells
 		clif_displaymessage(sd->fd, msg_txt(sd,204)); // "You can't open a shop on this cell."
-		return false;
+		return 4;
 	}
 
 	weight = sd->weight;
@@ -204,14 +218,14 @@ bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha
 	{// invalid item/amount/price
 		sd->buyingstore.slots = 0;
 		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0);
-		return false;
+		return 5;
 	}
 
 	if( (sd->max_weight*90)/100 < weight )
 	{// not able to carry all wanted items without getting overweight (90%)
 		sd->buyingstore.slots = 0;
 		clif_buyingstore_open_failed(sd, BUYINGSTORE_CREATE_OVERWEIGHT, weight);
-		return false;
+		return 7;
 	}
 
 	// success
@@ -236,10 +250,13 @@ bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned cha
 	clif_buyingstore_myitemlist(sd);
 	clif_buyingstore_entry(sd);
 
-	return true;
+	return 0;
 }
 
-
+/**
+* Close buying store
+* @param sd
+*/
 void buyingstore_close(struct map_session_data* sd)
 {
 	if( sd->state.buyingstore )
@@ -262,7 +279,11 @@ void buyingstore_close(struct map_session_data* sd)
 	}
 }
 
-
+/**
+* Open buying store from buyer
+* @param sd Player
+* @param account_id Buyer account ID
+*/
 void buyingstore_open(struct map_session_data* sd, int account_id)
 {
 	struct map_session_data* pl_sd;
@@ -292,7 +313,13 @@ void buyingstore_open(struct map_session_data* sd, int account_id)
 	clif_buyingstore_itemlist(sd, pl_sd);
 }
 
-
+/**
+* Start transaction
+* @param sd Player/Seller
+* @param account_id Buyer account ID
+* @param *itemlist List of sold items { <index>.W, <nameid>.W, <amount>.W }*
+* @param count Number of item on the itemlist
+*/
 void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count)
 {
 	int zeny = 0;
@@ -544,14 +571,15 @@ bool buyingstore_searchall(struct map_session_data* sd, const struct s_search_st
 	return true;
 }
 
-/** Open buyingstore for Autotrader
+/**
+* Open buyingstore for Autotrader
 * @param sd Player as autotrader
 */
 void buyingstore_reopen( struct map_session_data* sd ){
 	// Ready to open buyingstore for this char
 	if ( sd && autotrader_count > 0 && autotraders){
 		uint16 i;
-		uint8 *data, *p;
+		uint8 *data, *p, fail = 0;
 		uint16 j, count;
 
 		ARR_FIND(0,autotrader_count,i,autotraders[i] && autotraders[i]->char_id == sd->status.char_id);
@@ -576,11 +604,11 @@ void buyingstore_reopen( struct map_session_data* sd ){
 		}
 
 		// Open the buyingstore again
-		if( buyingstore_setup( sd, (unsigned char)autotraders[i]->count ) &&
-			buyingstore_create( sd, autotraders[i]->limit, 1, autotraders[i]->title, data, autotraders[i]->count ) )
+		if( (fail = buyingstore_setup( sd, (unsigned char)autotraders[i]->count ) == 0) &&
+			(fail = buyingstore_create( sd, autotraders[i]->limit, 1, autotraders[i]->title, data, autotraders[i]->count ) == 0) )
 		{
-			ShowInfo("Loaded autotrade buyingstore data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
-				sd->status.name,count,mapindex_id2name(sd->mapindex),sd->bl.x,sd->bl.y);
+			ShowInfo("Loaded buyingstore for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
+				sd->status.name, count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y);
 
 			// Set him to autotrade
 			if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;",
@@ -594,9 +622,12 @@ void buyingstore_reopen( struct map_session_data* sd ){
 
 			if( battle_config.feature_autotrade_sit )
 				pc_setsit(sd);
+
+			// Immediate save
+			chrif_save(sd, 3);
 		}else{
 			// Failed to open the buyingstore, set him offline
-			ShowWarning("Failed to load autotrade buyingstore data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", sd->status.name, count );
+			ShowError("Failed (%d) to load autotrade buyingstore data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", fail, sd->status.name, count );
 
 			map_quit( sd );
 		}

+ 2 - 2
src/map/buyingstore.h

@@ -22,8 +22,8 @@ struct s_buyingstore
 	unsigned char slots;
 };
 
-bool buyingstore_setup(struct map_session_data* sd, unsigned char slots);
-bool buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count);
+char buyingstore_setup(struct map_session_data* sd, unsigned char slots);
+char buyingstore_create(struct map_session_data* sd, int zenylimit, unsigned char result, const char* storename, const uint8* itemlist, unsigned int count);
 void buyingstore_close(struct map_session_data* sd);
 void buyingstore_open(struct map_session_data* sd, int account_id);
 void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int buyer_id, const uint8* itemlist, unsigned int count);

+ 6 - 17
src/map/chrif.c

@@ -287,12 +287,12 @@ int chrif_save(struct map_session_data *sd, int flag) {
 
 	if (flag && sd->state.active) { //Store player data which is quitting
 		//FIXME: SC are lost if there's no connection at save-time because of the way its related data is cleared immediately after this function. [Skotlex]
-	if (chrif_isconnected()) {
-		chrif_save_scdata(sd);
-		chrif_skillcooldown_save(sd);
-		chrif_save_bsdata(sd);
-		chrif_req_login_operation(sd->status.account_id, sd->status.name, 7, 0, 2, sd->status.bank_vault); //save Bank data
-	}
+		if (chrif_isconnected()) {
+			chrif_save_scdata(sd);
+			chrif_skillcooldown_save(sd);
+			chrif_save_bsdata(sd);
+			chrif_req_login_operation(sd->status.account_id, sd->status.name, 7, 0, 2, sd->status.bank_vault); //save Bank data
+		}
 		if ( flag != 3 && !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) )
 			ShowError("chrif_save: Failed to set up player %d:%d for proper quitting!\n", sd->status.account_id, sd->status.char_id);
 	}
@@ -1378,17 +1378,6 @@ int chrif_load_scdata(int fd) {
 
 	pc_scdata_received(sd);
 #endif
-
-	if( sd->state.autotrade ) {
-		buyingstore_reopen( sd );
-		vending_reopen( sd );
-
-		if (!sd->vender_id && !sd->buyer_id) {
-			sd->state.autotrade = 0;
-			map_quit(sd);
-		}
-	}
-
 	return 0;
 }
 

+ 6 - 3
src/map/clif.c

@@ -9703,8 +9703,10 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		// Set the initial idle time
 		sd->idletime = last_tick;
 
-		//Login Event
-		npc_script_event(sd, NPCE_LOGIN);
+		if (!sd->state.autotrade) { // Don't trigger NPC event or opening vending/buyingstore will be failed
+			//Login Event
+			npc_script_event(sd, NPCE_LOGIN);
+		}
 	} else {
 		//For some reason the client "loses" these on warp/map-change.
 		clif_updatestatus(sd,SP_STR);
@@ -9803,7 +9805,8 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		clif_showvendingboard(&sd->bl,sd->message,0);
 	}
 
-	if(map[sd->bl.m].flag.loadevent) // Lance
+	// Don't trigger NPC event or opening vending/buyingstore will be failed
+	if(!sd->state.autotrade && map[sd->bl.m].flag.loadevent) // Lance
 		npc_script_event(sd, NPCE_LOADMAP);
 
 	if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd))

+ 3 - 0
src/map/map.c

@@ -1652,6 +1652,9 @@ int map_quit(struct map_session_data *sd) {
 	if (sd->npc_timer_id != INVALID_TIMER) //Cancel the event timer.
 		npc_timerevent_quit(sd);
 
+	if (sd->autotrade_tid != INVALID_TIMER)
+		delete_timer(sd->autotrade_tid, pc_autotrade_timer);
+
 	if (sd->npc_id)
 		npc_event_dequeue(sd);
 

+ 17 - 7
src/map/mob.c

@@ -2266,13 +2266,21 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 
 			if (map[m].flag.nobaseexp || !md->db->base_exp)
 				base_exp = 0;
-			else
-				base_exp = (unsigned int)cap_value(md->db->base_exp * per * bonus/100. * map[m].adjust.bexp/100., 1, UINT_MAX);
+			else {
+				double exp = apply_rate2(md->db->base_exp, per, 1);
+				exp = apply_rate(exp, bonus);
+				exp = apply_rate(exp, map[m].adjust.bexp);
+				base_exp = (unsigned int)cap_value(exp, 1, UINT_MAX);
+			}
 
 			if (map[m].flag.nojobexp || !md->db->job_exp || md->dmglog[i].flag == MDLF_HOMUN) //Homun earned job-exp is always lost.
 				job_exp = 0;
-			else
-				job_exp = (unsigned int)cap_value(md->db->job_exp * per * bonus/100. * map[m].adjust.jexp/100., 1, UINT_MAX);
+			else {
+				double exp = apply_rate2(md->db->job_exp, per, 1);
+				exp = apply_rate(exp, bonus);
+				exp = apply_rate(exp, map[m].adjust.jexp);
+				job_exp = (unsigned int)cap_value(exp, 1, UINT_MAX);
+			}
 
 			if ( ( temp = tmpsd[i]->status.party_id)>0 ) {
 				int j;
@@ -2310,8 +2318,10 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 					if( md->dmglog[i].flag != MDLF_PET || battle_config.pet_attack_exp_to_master ) {
 #ifdef RENEWAL_EXP
 						int rate = pc_level_penalty_mod(tmpsd[i], md->level, md->status.class_, 1);
-						base_exp = (unsigned int)cap_value(base_exp * rate / 100, 1, UINT_MAX);
-						job_exp = (unsigned int)cap_value(job_exp * rate / 100, 1, UINT_MAX);
+						if (rate != 100) {
+							base_exp = (unsigned int)cap_value(apply_rate(base_exp, rate), 1, UINT_MAX);
+							job_exp = (unsigned int)cap_value(apply_rate(job_exp, rate), 1, UINT_MAX);
+						}
 #endif
 						pc_gainexp(tmpsd[i], &md->bl, base_exp, job_exp, false);
 					}
@@ -2395,7 +2405,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 			}
 #ifdef RENEWAL_DROP
 			if( drop_modifier != 100 ) {
-				drop_rate = drop_rate * drop_modifier / 100;
+				drop_rate = apply_rate(drop_rate, drop_modifier);
 				if( drop_rate < 1 )
 					drop_rate = 1;
 			}

+ 36 - 3
src/map/pc.c

@@ -1136,6 +1136,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
 	sd->npc_timer_id = INVALID_TIMER;
 	sd->pvp_timer = INVALID_TIMER;
 	sd->expiration_tid = INVALID_TIMER;
+	sd->autotrade_tid = INVALID_TIMER;
 
 #ifdef SECURE_NPCTIMEOUT
 	// Initialize to defaults/expected
@@ -1453,8 +1454,12 @@ void pc_reg_received(struct map_session_data *sd)
 		clif_changeoption( &sd->bl );
 	}
 
-	if( sd->state.autotrade )
+	pc_check_expiration(sd);
+
+	if( sd->state.autotrade ) {
 		clif_parse_LoadEndAck(sd->fd, sd);
+		sd->autotrade_tid = add_timer(gettick() + battle_config.feature_autotrade_open_delay, pc_autotrade_timer, sd->bl.id, 0);
+	}
 }
 
 static int pc_calc_skillpoint(struct map_session_data* sd)
@@ -8228,7 +8233,7 @@ void pc_setoption(struct map_session_data *sd,int type)
 	} else if( !( type&OPTION_CART ) && p_type&OPTION_CART ){ //Cart Off
 		clif_clearcart(sd->fd);
 		if(pc_checkskill(sd, MC_PUSHCART) < 10)
-			status_calc_pc(sd,0); //Remove speed penalty.
+			status_calc_pc(sd,SCO_NONE); //Remove speed penalty.
 	}
 #endif
 
@@ -10757,9 +10762,17 @@ void pc_damage_log_clear(struct map_session_data *sd, int id)
 
 /* Status change data arrived from char-server */
 void pc_scdata_received(struct map_session_data *sd) {
+	// Nothing todo yet
+	return;
+}
+
+/** Check expiration time and rental items
+* @param sd
+*/
+void pc_check_expiration(struct map_session_data *sd) {
 	pc_inventory_rentals(sd);
 
-	if( sd->expiration_time != 0 ) { //Don't display if it's unlimited or unknow value
+	if (sd->expiration_time != 0) { //Don't display if it's unlimited or unknow value
 		time_t exp_time = sd->expiration_time;
 		char tmpstr[1024];
 
@@ -10785,6 +10798,25 @@ int pc_expiration_timer(int tid, unsigned int tick, int id, intptr_t data) {
 	return 0;
 }
 
+int pc_autotrade_timer(int tid, unsigned int tick, int id, intptr_t data) {
+	struct map_session_data *sd = map_id2sd(id);
+
+	if (!sd)
+		return 0;
+
+	sd->autotrade_tid = INVALID_TIMER;
+
+	buyingstore_reopen(sd);
+	vending_reopen(sd);
+
+	if (sd && !sd->vender_id && !sd->buyer_id) {
+		sd->state.autotrade = 0;
+		map_quit(sd);
+	}
+
+	return 0;
+}
+
 /* this timer exists only when a character with a expire timer > 24h is online */
 /* it loops thru online players once an hour to check whether a new < 24h is available */
 int pc_global_expiration_timer(int tid, unsigned int tick, int id, intptr_t data) {
@@ -11157,6 +11189,7 @@ void do_init_pc(void) {
 	add_timer_func_list(pc_talisman_timer, "pc_talisman_timer");
 	add_timer_func_list(pc_global_expiration_timer, "pc_global_expiration_timer");
 	add_timer_func_list(pc_expiration_timer, "pc_expiration_timer");
+	add_timer_func_list(pc_autotrade_timer, "pc_autotrade_timer");
 
 	add_timer(gettick() + autosave_interval, pc_autosave, 0, 0);
 

+ 4 - 0
src/map/pc.h

@@ -610,6 +610,7 @@ struct map_session_data {
 	time_t expiration_time;
 
 	short last_addeditem_index; /// Index of latest item added
+	int autotrade_tid;
 };
 
 struct eri *pc_sc_display_ers; /// Player's SC display table
@@ -837,6 +838,7 @@ short pc_checkequip(struct map_session_data *sd,int pos);
 bool pc_checkequip2(struct map_session_data *sd, unsigned short nameid, int min, int max);
 
 void pc_scdata_received(struct map_session_data *sd);
+void pc_check_expiration(struct map_session_data *sd);
 int pc_expiration_timer(int tid, unsigned int tick, int id, intptr_t data);
 int pc_global_expiration_timer(int tid, unsigned tick, int id, intptr_t data);
 void pc_expire_check(struct map_session_data *sd);
@@ -1111,6 +1113,8 @@ bool pc_is_same_equip_index(enum equip_index eqi, int *equip_index, int8 index);
 /// Check if player is Taekwon Ranker and the level is >= 90 (battle_config.taekwon_ranker_min_lv)
 #define pc_is_taekwon_ranker(sd) (((sd)->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && (sd)->status.base_level >= battle_config.taekwon_ranker_min_lv && pc_famerank((sd)->status.char_id,MAPID_TAEKWON))
 
+int pc_autotrade_timer(int tid, unsigned int tick, int id, intptr_t data);
+
 #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP)
 int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_class, int type);
 #endif

+ 1 - 1
src/map/skill.c

@@ -8320,7 +8320,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case ALL_BUYING_STORE:
 		if( sd )
 		{// players only, skill allows 5 buying slots
-			clif_skill_nodamage(src, bl, skill_id, skill_lv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS));
+			clif_skill_nodamage(src, bl, skill_id, skill_lv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS) ? 0 : 1);
 		}
 		break;
 	case RK_ENCHANTBLADE:

+ 24 - 14
src/map/vending.c

@@ -297,29 +297,30 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
  *	data := {<index>.w <amount>.w <value>.l}[count]
  * @param count : number of different items
  */
-bool vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count) {
+char vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count) {
 	int i, j;
 	int vending_skill_lvl;
 	char message_sql[MESSAGE_SIZE*2];
 	
 	nullpo_retr(false,sd);
 
-	if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd))
-		return false; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once
+	if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd)) {
+		return 1; // can't open vendings lying dead || didn't use via the skill (wpe/hack) || can't have 2 shops at once
+	}
 
 	vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
 	
 	// skill level and cart check
 	if( !vending_skill_lvl || !pc_iscarton(sd) ) {
 		clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
-		return false;
+		return 2;
 	}
 
 	// 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, USESKILL_FAIL_LEVEL, 0);
-		return false;
+		return 3;
 	}
 
 	if (save_settings&2) // Avoid invalid data from saving
@@ -356,7 +357,7 @@ bool vending_openvending(struct map_session_data* sd, const char* message, const
 			sprintf(msg, msg_txt(sd, 733), idb->jname);
 			clif_displaymessage(sd->fd, msg);
 			clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
-			return false;
+			return 4;
 		}
 
 		i++; // item successfully added
@@ -367,7 +368,7 @@ bool vending_openvending(struct map_session_data* sd, const char* message, const
 
 	if( i == 0 ) { // no valid item found
 		clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0); // custom reply packet
-		return false;
+		return 5;
 	}
 	sd->state.prevend = 0;
 	sd->state.vending = true;
@@ -392,7 +393,7 @@ bool vending_openvending(struct map_session_data* sd, const char* message, const
 
 	idb_put(vending_db, sd->status.char_id, sd);
 
-	return true;
+	return 0;
 }
 
 /**
@@ -475,14 +476,15 @@ bool vending_searchall(struct map_session_data* sd, const struct s_search_store_
 	return true;
 }
 
-/** Open vending for Autotrader
+/**
+* Open vending for Autotrader
 * @param sd Player as autotrader
 */
 void vending_reopen( struct map_session_data* sd ){
 	// Ready to open vending for this char
 	if ( sd && autotrader_count > 0 && autotraders){
 		uint16 i;
-		uint8 *data, *p;
+		uint8 *data, *p, fail = 0;
 		uint16 j, count;
 
 		ARR_FIND(0,autotrader_count,i,autotraders[i] && autotraders[i]->char_id == sd->status.char_id);
@@ -517,8 +519,12 @@ void vending_reopen( struct map_session_data* sd ){
 		// Set him into a hacked prevend state
 		sd->state.prevend = 1;
 
+		// Make sure abort all NPCs
+		npc_event_dequeue(sd);
+		pc_cleareventtimer(sd);
+
 		// Open the vending again
-		if( vending_openvending(sd, autotraders[i]->title, data, count) ){
+		if( (fail = vending_openvending(sd, autotraders[i]->title, data, count)) == 0 ){
 			// Set him to autotrade
 			if (Sql_Query( mmysql_handle, "UPDATE `%s` SET `autotrade` = 1 WHERE `id` = %d;",
 				vendings_db, sd->vender_id ) != SQL_SUCCESS )
@@ -532,11 +538,14 @@ void vending_reopen( struct map_session_data* sd ){
 			if( battle_config.feature_autotrade_sit )
 				pc_setsit(sd);
 
-			ShowInfo("Loaded autotrade vending data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
-				sd->status.name,count,mapindex_id2name(sd->mapindex),sd->bl.x,sd->bl.y);
+			// Immediate save
+			chrif_save(sd, 3);
+
+			ShowInfo("Loaded vending for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items at "CL_WHITE"%s (%d,%d)"CL_RESET"\n",
+				sd->status.name, count, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y);
 		}else{
 			// Failed to open the vending, set him offline
-			ShowWarning("Failed to load autotrade vending data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", sd->status.name, count );
+			ShowError("Failed (%d) to load autotrade vending data for '"CL_WHITE"%s"CL_RESET"' with '"CL_WHITE"%d"CL_RESET"' items\n", fail, sd->status.name, count );
 
 			map_quit( sd );
 		}
@@ -600,6 +609,7 @@ void do_init_vending_autotrade( void ) {
 				pc_setnewpc(autotraders[i]->sd, autotraders[i]->account_id, autotraders[i]->char_id, 0, gettick(), autotraders[i]->sex, 0);
 			
 				autotraders[i]->sd->state.autotrade = 1;
+				autotraders[i]->sd->state.monster_ignore = 1;
 				chrif_authreq(autotraders[i]->sd, true);
 				i++;
 			}

+ 1 - 1
src/map/vending.h

@@ -22,7 +22,7 @@ void do_init_vending_autotrade( void );
  
 void vending_reopen( struct map_session_data* sd );
 void vending_closevending(struct map_session_data* sd);
-bool vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count);
+char vending_openvending(struct map_session_data* sd, const char* message, const uint8* data, int count);
 void vending_vendinglistreq(struct map_session_data* sd, int id);
 void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count);
 bool vending_search(struct map_session_data* sd, unsigned short nameid);