* 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
This commit is contained in:
Inkfish 2009-05-09 01:45:11 +00:00
parent fcc3ce7b3b
commit a366ac53ce
3 changed files with 134 additions and 88 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
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]==':')
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){
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();
te = nd->u.scr.timer_event + ted->next;
old_timer = nd->u.scr.timer;
t = nd->u.scr.timer = ted->time;
ted->next++;
// 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);
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);