Parcourir la source

* Fixed some known and unknown player attached NPC timer problems
- Attached timer now can be stopped properly.
- Attached timer will now stop if the NPC is unloaded. (bugreport:2510)

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

Inkfish il y a 16 ans
Parent
commit
a366ac53ce
3 fichiers modifiés avec 135 ajouts et 89 suppressions
  1. 4 0
      Changelog-Trunk.txt
  2. 7 5
      doc/script_commands.txt
  3. 124 84
      src/map/npc.c

+ 4 - 0
Changelog-Trunk.txt

@@ -2,6 +2,10 @@ 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.
+09/05/09
+	* Fixed some known and unknown player attached NPC timer problems [Inkfish]
+	- Attached timer now can be stopped properly.
+	- Attached timer will now stop if the NPC is unloaded. (bugreport:2510)
 09/05/07
 	* Fixed up the base success rate of normal strip skills [Playtester]
 	- normal strip skills: 5% + 5%*level

+ 7 - 5
doc/script_commands.txt

@@ -5110,8 +5110,8 @@ The 'setnpctimer' command will explicitly set the timer to a given tick.
  0 - Will return the current tick count of the timer.
  1 - Will return 1 if there are remaining "OnTimer<ticks>:" labels in the 
      specified NPC waiting for execution.
- 2 - Will return the number of times the timer has triggered an "OnTimer<tick>:" 
-     label in the specified NPC.
+ 2 - Will return the number of times the timer has triggered and will trigger
+     an "OnTimer<tick>:"  label in the specified NPC.
 
 Example 1:
 
@@ -5135,7 +5135,7 @@ Example 1:
         npctalk "1";
         end;
     OnTimer10000:
-        stopnpctimer;
+        stopnpctimer; // This command is indeed not neccessary here because timer automaticly stopped due to no remaining events.
         mes "[Man]";
         mes "Ok we can talk now";
     }
@@ -5143,8 +5143,10 @@ Example 1:
 Example 2:
 
     OnTimer15000:
-	npctalk "Another 15 seconds have passed.";
-        setnpctimer 0;
+				npctalk "Another 15 seconds have passed.";
+        initnpctimer; // You have to use 'initnpctimer' instead of 'setnpctimer 0'.
+        							// This is equal to 'setnpctimer 0' + 'startnpctimer'.
+        							// Alternatively, you can also insert another 'OnTimer15001' label so that the timer won't stop.
         end;
        
     // This OnInit label will run when the script is loaded, so that the timer 

+ 124 - 84
src/map/npc.c

@@ -377,44 +377,49 @@ void npc_event_do_oninit(void)
  *------------------------------------------*/
 int npc_timerevent_import(char* lname, void* data, va_list ap)
 {
-	int pos=(int)data;
-	struct npc_data *nd=va_arg(ap,struct npc_data *);
-	int t=0,i=0;
-
-	if(sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':')
-	{
-		// タイマーイベント
-		struct npc_timerevent_list *te=nd->u.scr.timer_event;
-		int j,i=nd->u.scr.timeramount;
-		if(te==NULL) te=(struct npc_timerevent_list*)aMallocA(sizeof(struct npc_timerevent_list));
-		else te= (struct npc_timerevent_list*)aRealloc( te, sizeof(struct npc_timerevent_list) * (i+1) );
-		if(te==NULL){
+	int pos = (int)data;
+	struct npc_data *nd = va_arg(ap,struct npc_data *);
+	int t = 0, i = 0;
+
+	if( sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':' )
+	{
+		struct npc_timerevent_list *te= nd->u.scr.timer_event;
+		int j, i = nd->u.scr.timeramount;
+
+		if( te == NULL )
+			te = (struct npc_timerevent_list*)aMallocA( sizeof(struct npc_timerevent_list) );
+		else
+			te = (struct npc_timerevent_list*)aRealloc( te, sizeof(struct npc_timerevent_list) * (i+1) );
+
+		if( te == NULL )
+		{
 			ShowFatalError("npc_timerevent_import: out of memory !\n");
 			exit(EXIT_FAILURE);
 		}
+
 		ARR_FIND( 0, i, j, te[j].timer > t );
 		if( j < i )
 			memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(i-j));
-		te[j].timer=t;
-		te[j].pos=pos;
-		nd->u.scr.timer_event=te;
+		te[j].timer = t;
+		te[j].pos = pos;
+		nd->u.scr.timer_event = te;
 		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 from when timer started to when last time event triggered.
-	unsigned int otick; //Holds tick value at which timer sequence was started (that is, it stores the tick value for which T= 0
+	int next; //timer index (starts with 0, then goes up to nd->u.scr.timeramount)
+	int time; //holds total time elapsed for the script from when timer was started to when last time the event triggered.
 };
 
 /*==========================================
- * タイマーイベント実行
+ * triger 'OnTimerXXXX' events
  *------------------------------------------*/
 int npc_timerevent(int tid, unsigned int tick, int id, intptr data)
 {
-	int next,t,old_rid,old_timer;
+	int next;
+	int old_rid, old_timer;
 	unsigned int old_tick;
 	struct npc_data* nd=(struct npc_data *)map_id2bl(id);
 	struct npc_timerevent_list *te;
@@ -434,17 +439,30 @@ int npc_timerevent(int tid, unsigned int tick, int id, intptr data)
 		return 0;
 	}
 
-	old_rid = nd->u.scr.rid; //To restore it later.
-	nd->u.scr.rid = sd?sd->bl.id:0;
-	
+	// These stuffs might need to be restored.
+	old_rid = nd->u.scr.rid;	
 	old_tick = nd->u.scr.timertick;
-	nd->u.scr.timertick = ted->otick = gettick();
+	old_timer = nd->u.scr.timer;
+
+	// Set the values of the timer
+	nd->u.scr.rid = sd?sd->bl.id:0;	//attached rid
+	nd->u.scr.timertick = tick;		//current time tick
+	nd->u.scr.timer = ted->time;	//total time from beginning to now
+
+	// Run the script
 	te = nd->u.scr.timer_event + ted->next;
+	run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id);
 	
-	old_timer = nd->u.scr.timer;
-	t = nd->u.scr.timer = ted->time;
-	ted->next++;
 	
+	nd->u.scr.rid = old_rid; // Attached-rid should be restored anyway.
+	if( sd )
+	{ // Restore previous data, only if this timer is a player-attached one.
+		nd->u.scr.timer = old_timer;
+		nd->u.scr.timertick = old_tick;
+	}
+
+	// Arrange for the next event
+	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;
@@ -459,46 +477,43 @@ int npc_timerevent(int tid, unsigned int tick, int id, intptr data)
 		if( sd )
 			sd->npc_timer_id = -1;
 		else
+		{
 			nd->u.scr.timerid = -1;
+			nd->u.scr.timertick = 0; // NPC timer stopped
+		}
 		ers_free(timer_event_ers, ted);
-		nd->u.scr.timertick = 0;
-	}
-	run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id);
-	//Restore previous data, only if this timer is a player-attached one.
-	if( sd )
-	{
-		nd->u.scr.rid = old_rid;
-		nd->u.scr.timer = old_timer;
-		nd->u.scr.timertick = old_tick;
 	}
+
 	return 0;
 }
 /*==========================================
- * タイマーイベント開始
+ * Start/Resume NPC timer
  *------------------------------------------*/
 int npc_timerevent_start(struct npc_data* nd, int rid)
 {
 	int j, next;
+	unsigned int tick = gettick();
 	struct map_session_data *sd = NULL; //Player to whom script is attached.
 	struct timer_event_data *ted;
 		
 	nullpo_retr(0, nd);
 
+	// No need to start because of no events
 	if( nd->u.scr.timeramount == 0 )
 		return 0;
 
 	// Check if there is an OnTimer Event
 	ARR_FIND( 0, nd->u.scr.timeramount, j, nd->u.scr.timer_event[j].timer > nd->u.scr.timer );
-	if( j >= nd->u.scr.timeramount ) // Check if there is an OnTimer Event
+	if( j >= nd->u.scr.timeramount ) // No need to start because of no events left to trigger
 		return 0;
 
 	if( nd->u.scr.rid > 0 && !(sd = map_id2sd(nd->u.scr.rid)) )
-	{ //Failed to attach timer to this player.
+	{ // Failed to attach timer to this player.
 		ShowError("npc_timerevent_start: Attached player not found!\n");
 		return 1;
 	}
 
-	//Check if timer is already started.
+	// Check if timer is already started.
 	if( sd )
 	{
 		if( sd->npc_timer_id != -1 )
@@ -506,30 +521,33 @@ int npc_timerevent_start(struct npc_data* nd, int rid)
 	}
 	else if( nd->u.scr.timerid != -1 )
 		return 0;
-		
+
+	// Arrange for the next event		
 	ted = ers_alloc(timer_event_ers, struct timer_event_data);
 	ted->next = j; // Set event index
-	nd->u.scr.timertick = ted->otick = gettick(); // Set when timer is started
-
-	//Attach only the player if attachplayerrid was used.
-	ted->rid = sd?sd->bl.id:0;
-
-	next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer;
 	ted->time = nd->u.scr.timer_event[j].timer;
+	next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer;
 	if( sd )
-		sd->npc_timer_id = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(intptr)ted);
+	{
+		ted->rid = sd->bl.id; // Attach only the player if attachplayerrid was used.
+		sd->npc_timer_id = add_timer(tick+next,npc_timerevent,nd->bl.id,(intptr)ted);
+	}
 	else
-		nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(intptr)ted);
+	{
+		nd->u.scr.timertick = tick; // Set when timer is started
+		nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,nd->bl.id,(intptr)ted);
+	}
+
 	return 0;
 }
 /*==========================================
- * タイマーイベント終了
+ * Stop NPC timer
  *------------------------------------------*/
 int npc_timerevent_stop(struct npc_data* nd)
 {
 	struct map_session_data *sd = NULL;
 	const struct TimerData *td = NULL;
-	int *tid;
+	int tid;
 
 	nullpo_retr(0, nd);
 
@@ -539,25 +557,27 @@ int npc_timerevent_stop(struct npc_data* nd)
 		return 1;
 	}
 	
-	tid = sd?&sd->npc_timer_id:&nd->u.scr.timerid;
-	if( *tid == -1 ) //Nothing to stop
+	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) 
+	// Delete timer
+	td = get_timer(tid);
+	if( td && td->data ) 
 		ers_free(timer_event_ers, (void*)td->data);
-	delete_timer(*tid,npc_timerevent);
-	*tid = -1;
+	delete_timer(tid,npc_timerevent);
+	tid = -1;
 
-	//Set 'timer' 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);
-	//Set 'tick' to zero so that we know it's off.
-	nd->u.scr.timertick = 0;
+	if( !sd )
+	{
+		nd->u.scr.timer += DIFF_TICK(gettick(),nd->u.scr.timertick); // Set 'timer' to the time that has passed since the beginning of the timers
+		nd->u.scr.timertick = 0; // Set 'tick' to zero so that we know it's off.
+	}
 
 	return 0;
 }
 /*==========================================
- * Aborts a running npc timer that is attached to a player.
+ * Aborts a running NPC timer that is attached to a player.
  *------------------------------------------*/
 void npc_timerevent_quit(struct map_session_data* sd)
 {
@@ -565,6 +585,7 @@ void npc_timerevent_quit(struct map_session_data* sd)
 	struct npc_data* nd;
 	struct timer_event_data *ted;
 
+	// Check timer existance
 	if( sd->npc_timer_id == -1 )
 		return;
 	if( !(td = get_timer(sd->npc_timer_id)) )
@@ -573,13 +594,15 @@ void npc_timerevent_quit(struct map_session_data* sd)
 		return;
 	}
 
+	// Delete timer
 	nd = (struct npc_data *)map_id2bl(td->id);
 	ted = (struct timer_event_data*)td->data;
 	delete_timer(sd->npc_timer_id, npc_timerevent);
 	sd->npc_timer_id = -1;
 
+	// Execute OnTimerQuit
 	if( nd && nd->bl.type == BL_NPC )
-	{	//Execute OnTimerQuit
+	{
 		char buf[NAME_LENGTH*2+3];
 		struct event_data *ev;
 
@@ -596,13 +619,12 @@ void npc_timerevent_quit(struct map_session_data* sd)
 			unsigned int old_tick;
 
 			//Set timer related info.
-			old_rid = nd->u.scr.rid;
-			nd->u.scr.rid = sd->bl.id;
-
+			old_rid = (nd->u.scr.rid == sd->bl.id ? 0 : nd->u.scr.rid); // Detach rid if the last attached player logged off.
 			old_tick = nd->u.scr.timertick;
-			nd->u.scr.timertick = ted->otick = gettick();
-
 			old_timer = nd->u.scr.timer;
+
+			nd->u.scr.rid = sd->bl.id;			
+			nd->u.scr.timertick = gettick();			
 			nd->u.scr.timer = ted->time;
 		
 			//Execute label
@@ -615,11 +637,10 @@ void npc_timerevent_quit(struct map_session_data* sd)
 		}
 	}
 	ers_free(timer_event_ers, ted);
-	nd->u.scr.timertick = 0;
 }
 
 /*==========================================
- * Get the tick value of a npc timer
+ * Get the tick value of an NPC timer
  * If it's stopped, return stopped time
  *------------------------------------------*/
 int npc_gettimerevent_tick(struct npc_data* nd)
@@ -627,7 +648,9 @@ int npc_gettimerevent_tick(struct npc_data* nd)
 	int tick;
 	nullpo_retr(0, nd);
 
-	tick = nd->u.scr.timer; // The last time it's active(stop or event trigger)
+	// TODO: Get player attached timer's tick. Now we can just get it by using 'getnpctimer' inside OnTimer event.
+
+	tick = nd->u.scr.timer; // The last time it's active(start, stop or event trigger)
 	if( nd->u.scr.timertick ) // It's a running timer
 		tick += DIFF_TICK(gettick(), nd->u.scr.timertick);
 
@@ -640,27 +663,24 @@ int npc_gettimerevent_tick(struct npc_data* nd)
 int npc_settimerevent_tick(struct npc_data* nd, int newtimer)
 {
 	bool flag;
+	int old_rid;
 	struct map_session_data *sd = NULL;
 
 	nullpo_retr(0, nd);
 
-	//Check if timer is started
-	if( nd->u.scr.rid )
-	{
-		if( !(sd = map_id2sd(nd->u.scr.rid)) )
-		{
-			ShowError("npc_settimerevent_tick: Attached player not found!\n");
-			return 1;
-		}
-		flag = (sd->npc_timer_id != -1);
-	}
-	else
-		flag = (nd->u.scr.timerid != -1);
+	// TODO: Set player attached timer's tick.	
+
+	old_rid = nd->u.scr.rid;
+	nd->u.scr.rid = 0;
+
+	// Check if timer is started
+	flag = (nd->u.scr.timerid != INVALID_TIMER);
 
 	if( flag ) npc_timerevent_stop(nd);
 	nd->u.scr.timer = newtimer;
 	if( flag ) npc_timerevent_start(nd, -1);
 
+	nd->u.scr.rid = old_rid;
 	return 0;
 }
 
@@ -1425,7 +1445,27 @@ int npc_unload(struct npc_data* nd)
 	else
 	if( nd->subtype == SCRIPT )
 	{
-		ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related.
+		struct s_mapiterator* iter;
+		struct block_list* bl;		
+
+		ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related
+
+		iter = mapit_geteachpc();  
+		for( bl = (struct block_list*)mapit_first(iter); mapit_exists(iter); bl = (struct block_list*)mapit_next(iter) )  
+		{
+			struct map_session_data *sd = map_id2sd(bl->id);
+			if( sd && sd->npc_timer_id != INVALID_TIMER )
+			{
+				const struct TimerData *td = NULL;
+				td = get_timer(sd->npc_timer_id);
+				if (td && td->data) 
+					ers_free(timer_event_ers, (void*)td->data);
+				delete_timer(sd->npc_timer_id, npc_timerevent);
+				sd->npc_timer_id = INVALID_TIMER;
+			}
+		}  
+		mapit_free(iter);
+
 		if (nd->u.scr.timerid != -1) {
 			const struct TimerData *td = NULL;
 			td = get_timer(nd->u.scr.timerid);