Browse Source

- Improved the NPC timer system to enable to have multiple timers going on at a time with different players attached to each. Now npc event timers are of two types: attached or global. The global timers don't have a player attached and can be started/halted by anyone. The character timers have a player attached, and they can only be stopped by a script that has the same player attached.
- Now player attached scripts will auto-abort when the atteched player quits the map server.


git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@5167 54d463be-8e91-2dee-dedb-b68131a5f0ec

skotlex 19 years ago
parent
commit
28c32a9532
8 changed files with 188 additions and 44 deletions
  1. 9 0
      Changelog-Trunk.txt
  2. 2 0
      src/map/map.c
  3. 2 1
      src/map/map.h
  4. 2 5
      src/map/mob.c
  5. 157 37
      src/map/npc.c
  6. 1 0
      src/map/npc.h
  7. 2 0
      src/map/pc.c
  8. 13 1
      src/map/script.c

+ 9 - 0
Changelog-Trunk.txt

@@ -5,6 +5,15 @@ IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.  EV
 GOES INTO TRUNK AND WILL BE MERGED INTO STABLE BY VALARIS AND WIZPUTER. -- VALARIS
 
 2006/02/02
+	* Improved the NPC timer system to enable multiple timers going on at a
+	  time with different players attached to each. Now npc event timers are of
+	  two types: attached or global. The global timers don't have a player
+	  attached and can be started/halted by anyone. The character timers have a
+	  player attached, and they can only be stopped by a script that has the same
+	  player attached. [Skotlex]
+	- Now player attached scripts will auto-abort when the atteched player
+	  quits the map server.
+	- Of course, this requires some major testing as it's prone to have bugs...
 	* Applied the Entry Reusage System to the battle delay damage timers.
 	  [Skotlex]
 	* Fixed the map server complain when using the default user/password

+ 2 - 0
src/map/map.c

@@ -1475,6 +1475,8 @@ int map_quit(struct map_session_data *sd) {
 
 
 	if(!sd->state.waitingdisconnect) {
+		if (sd->npc_timer_id != -1) //Cancel the event timer.
+			npc_timerevent_quit(sd);
 		if (sd->state.event_disconnect) {
 			if (script_config.event_script_type == 0) {
 				struct npc_data *npc;

+ 2 - 1
src/map/map.h

@@ -491,6 +491,7 @@ struct map_session_data {
 	unsigned char *npc_script,*npc_scriptroot;
 	int  npc_scriptstate;
 	char npc_str[256];
+	int npc_timer_id; //For player attached npc timers. [Skotlex]
 	unsigned int chatID;
 	time_t idletime;
 
@@ -786,7 +787,7 @@ struct npc_data {
 			unsigned char *script;
 			short xs,ys;
 			int guild_id;
-			int timer,timerid,timeramount,nexttimer,rid;
+			int timer,timerid,timeramount,rid;
 			unsigned int timertick;
 			struct npc_timerevent_list *timer_event;
 			int label_list_num;

+ 2 - 5
src/map/mob.c

@@ -620,8 +620,6 @@ int mob_can_reach(struct mob_data *md,struct block_list *bl,int range, int state
 	if( range>0 && !check_distance_bl(&md->bl, bl, range))
 		return 0;
 
-	dx=abs(bl->x - md->bl.x);
-	dy=abs(bl->y - md->bl.y);
 	// Obstacle judging
 	wpd.path_len=0;
 	wpd.path_pos=0;
@@ -629,10 +627,9 @@ int mob_can_reach(struct mob_data *md,struct block_list *bl,int range, int state
 	if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x,bl->y,easy)!=-1)
 		return 1;
 
-	if(bl->type!=BL_PC && bl->type!=BL_MOB)
-		return 0;
-
 	// It judges whether it can adjoin or not.
+	dx=abs(bl->x - md->bl.x);
+	dy=abs(bl->y - md->bl.y);
 	dx=(dx>0)?1:((dx<0)?-1:0);
 	dy=(dy>0)?1:((dy<0)?-1:0);
 	if(path_search(&wpd,md->bl.m,md->bl.x,md->bl.y,bl->x-dx,bl->y-dy,easy)!=-1)

+ 157 - 37
src/map/npc.c

@@ -13,6 +13,7 @@
 #include "../common/malloc.h"
 #include "../common/grfio.h"
 #include "../common/showmsg.h"
+#include "../common/ers.h"
 #include "map.h"
 #include "log.h"
 #include "npc.h"
@@ -59,6 +60,7 @@ struct event_data {
 };
 static struct tm ev_tm_b;	// 時計イベント用
 
+static struct eri *timer_event_ers; //For the npc timer data. [Skotlex]
 static int npc_walktimer(int,unsigned int,int,int); // [Valaris]
 static int npc_walktoxy_sub(struct npc_data *nd); // [Valaris]
 
@@ -537,35 +539,74 @@ int npc_timerevent_import(char *lname,void *data,va_list ap)
 		te[j].timer=t;
 		te[j].pos=pos;
 		nd->u.scr.timer_event=te;
-		nd->u.scr.timeramount=i+1;
+		nd->u.scr.timeramount++;
 	}
 	return 0;
 }
+struct timer_event_data {
+	int rid; //Attached player for this timer.
+	int next; //timer index (starts with 0, then goes up to nd->u.scr.timeramount
+	int time; //holds total time elapsed for the script since time 0 (whenthe timers started)
+	unsigned int otick; //Holds tick value at which timer sequence was started (that is, it stores the tick value for which T= 0
+};
+
 /*==========================================
  * タイマーイベント実行
  *------------------------------------------
  */
 int npc_timerevent(int tid,unsigned int tick,int id,int data)
 {
-	int next,t;
+	int next,t,old_rid,old_timer;
+	unsigned int old_tick;
 	struct npc_data* nd=(struct npc_data *)map_id2bl(id);
 	struct npc_timerevent_list *te;
-	if( nd==NULL || nd->u.scr.nexttimer<0 ){
-		ShowError("npc_timerevent: ??\n");
+	struct timer_event_data *ted = (struct timer_event_data*)ted;
+	struct map_session_data *sd=NULL;
+	
+	if( nd==NULL ){
+		ShowError("npc_timerevent: NPC not found??\n");
 		return 0;
 	}
-	nd->u.scr.timertick=tick;
-	te=nd->u.scr.timer_event+ nd->u.scr.nexttimer;
-	nd->u.scr.timerid = -1;
-
-	t = nd->u.scr.timer+=data;
-	nd->u.scr.nexttimer++;
-	if( nd->u.scr.timeramount>nd->u.scr.nexttimer ){
-		next= nd->u.scr.timer_event[ nd->u.scr.nexttimer ].timer - t;
-		nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,id,next);
+	if (ted->rid) {
+		sd = map_id2sd(ted->rid);
+		if (!sd) {
+			if(battle_config.error_log)
+				ShowError("npc_timerevent: Attached player not found.\n");
+			ers_free(timer_event_ers, ted);
+			return 0;
+		}
+	}
+	old_rid = nd->u.scr.rid; //To restore it later.
+	nd->u.scr.rid = sd?sd->bl.id:0;
+	
+	old_tick = nd->u.scr.timertick;
+	nd->u.scr.timertick=ted->otick;
+	te=nd->u.scr.timer_event+ ted->next;
+	
+	old_timer = nd->u.scr.timer;
+	t = nd->u.scr.timer=ted->time;
+	ted->next++;
+	
+	if( nd->u.scr.timeramount> ted->next){
+		next= nd->u.scr.timer_event[ ted->next ].timer
+			- nd->u.scr.timer_event[ ted->next-1 ].timer;
+		ted->time+=next;
+		if (sd)
+			sd->npc_timer_id = add_timer(tick+next,npc_timerevent,id,(int)ted);
+		else
+			nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,id,(int)ted);
+	} else {
+		if (sd)
+			sd->npc_timer_id = -1;
+		else
+			nd->u.scr.timerid = -1;
+		ers_free(timer_event_ers, ted);
 	}
-
 	run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id);
+	//Restore previous data.
+	nd->u.scr.rid = old_rid;
+	nd->u.scr.timer = old_timer;
+	nd->u.scr.timertick = old_tick;
 	return 0;
 }
 /*==========================================
@@ -575,11 +616,13 @@ int npc_timerevent(int tid,unsigned int tick,int id,int data)
 int npc_timerevent_start(struct npc_data *nd, int rid)
 {
 	int j,n, next;
-
+	struct map_session_data *sd=NULL; //Player to whom script is attached.
+	struct timer_event_data *ted;
+		
 	nullpo_retr(0, nd);
 
 	n=nd->u.scr.timeramount;
-	if( nd->u.scr.nexttimer>=0 || n==0 )
+	if( n==0 )
 		return 0;
 
 	for(j=0;j<n;j++){
@@ -588,14 +631,32 @@ int npc_timerevent_start(struct npc_data *nd, int rid)
 	}
 	if(j>=n) // check if there is a timer to use !!BEFORE!! you write stuff to the structures [Shinomori]
 		return 0;
+	if (nd->u.scr.rid > 0) {
+		//Try to attach timer to this player.
+		sd = map_id2sd(nd->u.scr.rid);
+		if (!sd) {
+			if(battle_config.error_log)
+				ShowError("npc_timerevent_start: Attached player not found!\n");
+			return 1;
+		}
+	}
+	ted = ers_alloc(timer_event_ers, struct timer_event_data);
+	ted->next = j;
+	nd->u.scr.timertick=ted->otick=gettick();
+
+	//Attach only the player if attachplayerrid was used.
+	ted->rid = sd?sd->bl.id:0;
 
-	nd->u.scr.nexttimer=j;
-	nd->u.scr.timertick=gettick();
-	if (rid >= 0) nd->u.scr.rid=rid;	// changed to: attaching to given rid by default [Shinomori]
+// Do not store it to make way to two types of timers: globals and personals.	
+//	if (rid >= 0) nd->u.scr.rid=rid;	// changed to: attaching to given rid by default [Shinomori]
 	// if rid is less than 0 leave it unchanged [celest]
 
 	next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer;
-	nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,next);
+	ted->time = nd->u.scr.timer_event[j].timer;
+	if (sd)
+		sd->npc_timer_id = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted);
+	else
+		nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted);
 	return 0;
 }
 /*==========================================
@@ -604,18 +665,51 @@ int npc_timerevent_start(struct npc_data *nd, int rid)
  */
 int npc_timerevent_stop(struct npc_data *nd)
 {
+	struct map_session_data *sd =NULL;
+	struct TimerData *td = NULL;
+	int *tid;
 	nullpo_retr(0, nd);
-
-	if( nd->u.scr.nexttimer>=0 ){
-		nd->u.scr.nexttimer = -1;
-		nd->u.scr.timer += (int)(gettick() - nd->u.scr.timertick);
-		if(nd->u.scr.timerid!=-1)
-			delete_timer(nd->u.scr.timerid,npc_timerevent);
-		nd->u.scr.timerid = -1;
-		nd->u.scr.rid = 0;
+	if (nd->u.scr.rid) {
+		sd = map_id2sd(nd->u.scr.rid);
+		if (!sd) {
+			if(battle_config.error_log)
+				ShowError("npc_timerevent_stop: Attached player not found!\n");
+			return 1;
+		}
 	}
+	
+	tid = sd?&sd->npc_timer_id:&nd->u.scr.timerid;
+	
+	if (*tid == -1) //Nothing to stop
+		return 0;
+	td = get_timer(*tid);
+	if (td && td->data) 
+		ers_free(timer_event_ers, (struct event_timer_data*)td->data);
+	delete_timer(*tid,npc_timerevent);
+	*tid = -1;
+	//Set the timer tick to the time that has passed since the beginning of the timers and now.
+	nd->u.scr.timer = DIFF_TICK(gettick(),nd->u.scr.timertick);
+	nd->u.scr.rid = 0;
 	return 0;
 }
+/*==========================================
+ * Aborts a running npc timer that is attached to a player.
+ *------------------------------------------
+ */
+void npc_timerevent_quit(struct map_session_data *sd) {
+	struct TimerData *td;
+	if (sd->npc_timer_id == -1)
+		return;
+	td = get_timer(sd->npc_timer_id);
+	if (!td) {
+		sd->npc_timer_id = -1;
+		return; //??
+	}
+	delete_timer(sd->npc_timer_id,npc_timerevent);
+	sd->npc_timer_id = -1;
+	ers_free(timer_event_ers, (struct event_timer_data*)td->data);
+}
+
 /*==========================================
  * タイマー値の所得
  *------------------------------------------
@@ -623,13 +717,29 @@ int npc_timerevent_stop(struct npc_data *nd)
 int npc_gettimerevent_tick(struct npc_data *nd)
 {
 	int tick;
+	struct map_session_data *sd =NULL;
 
 	nullpo_retr(0, nd);
 
 	tick=nd->u.scr.timer;
 
-	if( nd->u.scr.nexttimer>=0 )
-		tick += (int)(gettick() - nd->u.scr.timertick);
+	if (nd->u.scr.rid) {
+		sd = map_id2sd(nd->u.scr.rid);
+		if (!sd) {
+			if(battle_config.error_log)
+				ShowError("npc_gettimerevent_tick: Attached player not found!\n");
+			return tick;
+		}
+	}
+	//If within a timer, set the tick value to the time passed since the beginning of the timer.
+	if (sd) {
+		if(sd->npc_timer_id != -1)
+			tick = DIFF_TICK(gettick(), nd->u.scr.timertick);
+	} else {
+		if(nd->u.scr.timerid!=-1)
+			tick = DIFF_TICK(gettick(), nd->u.scr.timertick);
+	}
+
 	return tick;
 }
 /*==========================================
@@ -639,14 +749,24 @@ int npc_gettimerevent_tick(struct npc_data *nd)
 int npc_settimerevent_tick(struct npc_data *nd,int newtimer)
 {
 	int flag;
+	struct map_session_data *sd=NULL;
 
 	nullpo_retr(0, nd);
 
-	flag= nd->u.scr.nexttimer;
-
-	npc_timerevent_stop(nd);
+	if (nd->u.scr.rid) {
+		sd = map_id2sd(nd->u.scr.rid);
+		if (!sd) {
+			if(battle_config.error_log)
+				ShowError("npc_settimerevent_tick: Attached player not found!\n");
+			return 1;
+		}
+		flag= sd->npc_timer_id != -1 ;
+	} else
+		flag= nd->u.scr.timer != -1 ;
+	if(flag)
+		npc_timerevent_stop(nd);
 	nd->u.scr.timer=newtimer;
-	if(flag>=0)
+	if(flag)
 		npc_timerevent_start(nd, -1);
 	return 0;
 }
@@ -2050,10 +2170,9 @@ static int npc_parse_script (char *w1,char *w2,char *w3,char *w4,char *first_lin
 			te[j].timer = t;
 			te[j].pos = pos;
 			nd->u.scr.timer_event = te;
-			nd->u.scr.timeramount = k+1;
+			nd->u.scr.timeramount++;
 		}
 	}
-	nd->u.scr.nexttimer = -1;
 	nd->u.scr.timerid = -1;
 
 	return 0;
@@ -2751,7 +2870,7 @@ int do_final_npc(void)
 	//There is no free function for npcname_db because at this point there shouldn't be any npcs left!
 	//So if there is anything remaining, let the memory manager catch it and report it.
 	npcname_db->destroy(npcname_db, NULL);
-
+	ers_destroy(timer_event_ers);
 	npc_clearsrcfile();
 
 	return 0;
@@ -2778,6 +2897,7 @@ int do_init_npc(void)
 	npcname_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_BASE,NAME_LENGTH);
 
 	memset(&ev_tm_b, -1, sizeof(ev_tm_b));
+	timer_event_ers = ers_new((uint32)sizeof(struct timer_event_data));
 
 	for (nsl = npc_src_first; nsl; nsl = nsl->next) {
 		npc_parsesrcfile(nsl->name);

+ 1 - 0
src/map/npc.h

@@ -59,6 +59,7 @@ int npc_event_doall_id(const unsigned char *name, int id);
 
 int npc_timerevent_start(struct npc_data *nd, int rid);
 int npc_timerevent_stop(struct npc_data *nd);
+void npc_timerevent_quit(struct map_session_data *sd);
 int npc_gettimerevent_tick(struct npc_data *nd);
 int npc_settimerevent_tick(struct npc_data *nd,int newtimer);
 int npc_remove_map(struct npc_data *nd);

+ 2 - 0
src/map/pc.c

@@ -786,6 +786,8 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t
 	for(i = 0; i < MAX_EVENTTIMER; i++)
 		sd->eventtimer[i] = -1;
 
+	sd->npc_timer_id = -1;
+	
 	// Moved PVP timer initialisation before set_pos
 	sd->pvp_timer = -1;
 

+ 13 - 1
src/map/script.c

@@ -5639,6 +5639,7 @@ int buildin_stopnpctimer(struct script_state *st)
 int buildin_getnpctimer(struct script_state *st)
 {
 	struct npc_data *nd;
+	struct map_session_data *sd;
 	int type=conv_num(st,& (st->stack->stack_data[st->start+2]));
 	int val=0;
 	if( st->end > st->start+3 )
@@ -5648,7 +5649,18 @@ int buildin_getnpctimer(struct script_state *st)
 
 	switch(type){
 	case 0: val=npc_gettimerevent_tick(nd); break;
-	case 1: val= (nd->u.scr.nexttimer>=0); break;
+	case 1:
+		if (nd->u.scr.rid) {
+			sd = map_id2sd(nd->u.scr.rid);
+			if (!sd) {
+				if(battle_config.error_log)
+					ShowError("buildin_getnpctimer: Attached player not found!\n");
+				break;
+			}
+			val = (sd->npc_timer_id != -1);
+		} else
+			  val= (nd->u.scr.timerid !=-1);
+		break;
 	case 2: val= nd->u.scr.timeramount; break;
 	}
 	push_val(st->stack,C_INT,val);