Timer - re-run

Posted by Samryn on Sat 06 Aug 2005 04:02 PM — 17 posts, 62,789 views.

United Kingdom #0
I'm kinda new with timers and aint had much experience with them, but i was just wondering if there is a way of making a timer RE-RUN once its completed it for the first time.
Reason i ask is because im doing a do_trance command which is slightly different than the other one thats on here, i want it so the player types trance, and it will allow the player to trance until it reachs its max_mana.

Here is what i have so far:-


void do_trance( CHAR_DATA *ch, char *argument )
{
   int percent;
   
    switch( ch->substate )
    {
        default:
            if ( IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM ) )
            {
                send_to_char( "You can't concentrate enough for that.\n\r", ch );
                return;
            }
            if ( ch->mount )
            {
                send_to_char( "You can't concentrate, whiles being mounted.\n\r", ch );
                return;
            }
			act( AT_ACTION, "You enter a peaceful trance, collecting mana as you rest.", ch, NULL, NULL, TO_CHAR );
			ch->position = POS_TRANCE;
			add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			return;
			
	   case 1:
	      	if (ch->position != POS_TRANCE)
			{
			 send_to_char("Something went wrong...\n\r", ch);
			 return;
			}
			  
	   case SUB_TIMER_DO_ABORT:
	   		if (ch->mana >= ch->max_mana)
		   	  {
			  send_to_char("You complete your trance.\n\r", ch);
			  return;
			  }
			  
	       if (ch->mana < ch->max_mana)
			 percent = number_percent( ) + number_percent( ) - ( ch->level / 10 );
	       	 if (can_use_skill( ch, number_percent(), gsn_trance ) )
			 {
			 send_to_char("You fail to gain any mana.\n\r", ch);
			 learn_from_failure( ch, gsn_trance );
			 add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			 return;
			 }else{
			 send_to_char("You gain some mana from your trance.\n\r", ch);
			 if (ch->mana < (ch->max_mana - 50))
			 {
			 ch->mana = (ch->mana + 50);
			 }else{
			 ch->mana = ch->max_mana;
			 }
			 add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			 learn_from_success( ch, gsn_trance );
			 return;
			 }
		   }
}


Thanks,
Samryn
Amended on Sat 06 Aug 2005 05:50 PM by Samryn
USA #1
Why not just call add_timer again? You don't really need to make the same one run again, just make a new one.
United Kingdom #2
how would i call the timer again? Im pretty new to timers so still trying to work them out.. sorry.
USA #3
Look at this code, from what you posted:
			act( AT_ACTION, "You enter a peaceful trance, collecting mana as you rest.", ch, NULL, NULL, TO_CHAR );
			ch->position = POS_TRANCE;
			add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 ); /* <-- THIS LINE */
			return;
United Kingdom #4
ohhh, i did that in the code anyway.


add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 ); <--- here..
learn_from_success( ch, gsn_trance );

and

learn_from_failure( ch, gsn_trance );
add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 ); <--- here..



but when the player type trance, it only runs throught the code once, and doesn't repeat the code again, which sucks as i need it to read the code again until the plaer has full many or has interupted the trance, but if im able to make it read the code again, i'll be able to do the interupting thing from there anyway... its just getting it to read it again which is the problem :(

-- Samryn
Amended on Sun 07 Aug 2005 01:33 AM by Samryn
USA #5
Then you probably want to start the timer again in the case1, which is where the code will enter when the command is called by the timer. You'd want to check max mana and all that, and if it's not at the maximum, do the timer again.
United Kingdom #6
ok did the changes and still getting the same reaction from it, some reason it just wont allow the timer to restart itself.

Just for testing purposes i put do_trance(ch, "auto"); inside the code, to make it self do the command again.. it works ok with that but one slight problem. It doesn't slow the process down, it all comes something like:-


You gain some mana from your trance.
You fail to gain any mana.
You gain some mana from your trance.
You fail to gain any mana.
You fail to gain any mana.
You gain some mana from your trance.
You gain some mana from your trance.
You gain some mana from your trance.
You gain some mana from your trance.
You complete your trance.


when really i want them to come out alot slower than that, and also allows the character cancel it, hence the reason i cant use the wait statement... Any ideas how i can get the timer working or something to slow it down? but without just lagging the player.
USA #7
Can you show me the code, please? It's hard to debug this without seeing what exactly you've done.
United Kingdom #8
sure :) and thanks for helping out...


void do_trance( CHAR_DATA *ch, char *argument )
{
   int percent;
   
    switch( ch->substate )
    {
        default:
            if ( IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM ) )
            {
                send_to_char( "You can't concentrate enough for that.\n\r", ch );
                return;
            }
			
            if ( ch->mount )
            {
                send_to_char( "You can't concentrate, whiles being mounted.\n\r", ch );
                return;
            }
			
			act( AT_MAGIC, "You enter a peaceful trance, collecting mana as you rest.", ch, NULL, NULL, TO_CHAR );
			ch->position = POS_TRANCE;
			add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			return;
			
	   case 1:
	   		if (ch->mana >= ch->max_mana)
		   	  {
			  send_to_char_color("&BYou complete your trance.\n\r", ch);
			  ch->position = POS_STANDING;
			  return;
			  }
			  
	       if (ch->mana < ch->max_mana)
		     {
			 percent = number_percent( ) + number_percent( ) - ( ch->level / 10 );
	       	 if (can_use_skill( ch, percent, gsn_trance ) )
			 {
			 send_to_char_color("&BYou fail to gain any mana.\n\r", ch);
			 learn_from_failure( ch, gsn_trance );
			 ch->position = POS_STANDING;
			 add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			 return;
			 }else{
			 send_to_char_color("&BYou gain some mana from your trance.\n\r", ch);
			   if (ch->mana < (ch->max_mana - 50))
			   {
			   ch->mana = (ch->mana + 50);
			   }else{
			   ch->mana = ch->max_mana;
			   }
			 learn_from_success( ch, gsn_trance );
			 ch->position = POS_STANDING;
			 add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			 return;
			 }
			 }
			  
	   case SUB_TIMER_DO_ABORT:
	      if (ch->position != POS_TRANCE)
			{
			 send_to_char("Something went wrong...\n\r", ch);
			 bug("do_trance: Character didn't go in POS_TRANCE -report to samryn",0);
			 return;
			}
	  }
}
USA #9
So, just to make sure I have all the details, when you run that code you get:
- "You enter a peaceful trance" etc.
- 3 seconds go by
- You get the first trance result message
- nothing more happens

This won't fix your problem, but it probably isn't right: why are you resetting the character position to standing right before adding another timer?
United Kingdom #10
because it keeps not working correctly so, i have to knock the person back into POS_STANDING, and its also the minpos on the spell... after sset'ing it to minpos 12.

So if i didn't do it, the character will stay in POS_TRANCE and will not be removed from it. As soon as i can get it to recall to itself and allow the code to finish itself to the "You complete your trance" i can then make them go back to the position. Or if they break out, i can change the position by interp.c

Thanks for your help again, much appreciated.

and yep, your right about the way it shows..

- "You enter a peaceful trance" etc.
- 3 seconds go by
- You get the first trance result message
- nothing more happens


-- Samryn
USA #11
I think I found the source of your problem. On my copy of SMAUG - which, admittedly, is based off of SMAUG 1.0 - I have this in the 'violence_update' function:
        for ( timer = ch->first_timer; timer; timer = timer_next )
        {
            timer_next = timer->next;
            if ( --timer->count <= 0 )
            {
                if ( timer->type == TIMER_DO_FUN )
                {
                    int tempsub;

                    tempsub = ch->substate;
                    ch->substate = timer->value;
                    (timer->do_fun)( ch, "" );
                    if ( char_died(ch) )
                        break;
                    ch->substate = tempsub; 
                }
                extract_timer( ch, timer );
            }
        }


What does this mean? Well, it means that after executing a timer, it is removed. However, if we look at the 'add_timer' function...
void add_timer( CHAR_DATA *ch, sh_int type, sh_int count, DO_FUN *fun, int value )
{
    TIMER *timer;
    
    for ( timer = ch->first_timer; timer; timer = timer->next )
    {
        if ( timer->type == type )
        {
           timer->count  = count;
           timer->do_fun = fun;
           timer->value  = value;
           break;
        }
    }

    if ( !timer )
    {
        CREATE( timer, TIMER, 1 );
        timer->count    = count;
        timer->type = type;
        timer->do_fun   = fun;
        timer->value    = value;
        LINK( timer, ch->first_timer, ch->last_timer, next, prev );
    }
}
Here, we see that when we add a timer, we first loop through the existing timers, seeing if we already have one of that type.

In your case, we do have a timer, because we're in the middle of executing one, and we haven't removed it yet. So, we'll edit it, and then we'll remove it right after running it!

To fix this, we'll have to make sure that the timer is not extracted in violence_update if it has count left. This should be as simple as adding:
if ( timer->count == 0 )
before the 'extract_timer' call.
United Kingdom #12
after adding the little bit just before the new add_timer... i got this error message after trying to compile.

skills.c: In function `do_trance':
skills.c:4128: error: `timer' undeclared (first use in this function)
skills.c:4128: error: (Each undeclared identifier is reported only once
skills.c:4128: error: for each function it appears in.)
make[1]: *** [o/skills.o] Error 1
make: *** [all] Error 2

Sorry for the hassel this is causing...

-- Samryn
USA #13
Why are you adding it in skills.c? You were supposed to add it before the extract_timer call I showed you from violence_update, which is in fight.c. :-)
United Kingdom #14
Hey sorry about taking so long to reply, had a powercut.

and it works great! thanks for all the time you've put in, muchly appreciated...

-- Samryn
USA #15

void do_trance( CHAR_DATA *ch, char *argument )
{
   int percent;
   
    switch( ch->substate )
    {
        default:
            if ( IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM ) )
            {
                send_to_char( "You can't concentrate enough for that.\n\r", ch );
                return;
            }
			
            if ( ch->mount )
            {
                send_to_char( "You can't concentrate, whiles being mounted.\n\r", ch );
                return;
            }
			
			act( AT_MAGIC, "You enter a peaceful trance, collecting mana as you rest.", ch, NULL, NULL, TO_CHAR );
			ch->position = POS_TRANCE;
			add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			return;
			
	   case 1:
	   		if (ch->mana >= ch->max_mana)
		   	  {
			  send_to_char_color("&BYou complete your trance.\n\r", ch);
			  ch->position = POS_STANDING;
			  return;
			  }
			  
	       if (ch->mana < ch->max_mana)
		     {
			 percent = number_percent( ) + number_percent( ) - ( ch->level / 10 );
	       	 if (can_use_skill( ch, percent, gsn_trance ) )
			 {
			 send_to_char_color("&BYou fail to gain any mana.\n\r", ch);
			 learn_from_failure( ch, gsn_trance );
			 ch->position = POS_STANDING;
			 add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			 return;
			 }else{
			 send_to_char_color("&BYou gain some mana from your trance.\n\r", ch);
			   if (ch->mana < (ch->max_mana - 50))
			   {
			   ch->mana = (ch->mana + 50);
			   }else{
			   ch->mana = ch->max_mana;
			   }
			 learn_from_success( ch, gsn_trance );
			 ch->position = POS_STANDING;
			 add_timer( ch, TIMER_DO_FUN, 3, do_trance, 2 );
			 return;
			 }
			 }

	   case 2:
	   		if (ch->mana >= ch->max_mana)
		   	  {
			  send_to_char_color("&BYou complete your trance.\n\r", ch);
			  ch->position = POS_STANDING;
			  return;
			  }
			  
	       if (ch->mana < ch->max_mana)
		     {
			 percent = number_percent( ) + number_percent( ) - ( ch->level / 10 );
	       	 if (can_use_skill( ch, percent, gsn_trance ) )
			 {
			 send_to_char_color("&BYou fail to gain any mana.\n\r", ch);
			 learn_from_failure( ch, gsn_trance );
			 ch->position = POS_STANDING;
			 add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			 return;
			 }else{
			 send_to_char_color("&BYou gain some mana from your trance.\n\r", ch);
			   if (ch->mana < (ch->max_mana - 50))
			   {
			   ch->mana = (ch->mana + 50);
			   }else{
			   ch->mana = ch->max_mana;
			   }
			 learn_from_success( ch, gsn_trance );
			 ch->position = POS_STANDING;
			 add_timer( ch, TIMER_DO_FUN, 3, do_trance, 1 );
			 return;
			 }
			 }
			  
	   case SUB_TIMER_DO_ABORT:
	      if (ch->position != POS_TRANCE)
			{
			 send_to_char("Something went wrong...\n\r", ch);
			 bug("do_trance: Character didn't go in POS_TRANCE -report to samryn",0);
			 return;
			}
	  }
}


i had replied like this on another post, but this is how smaug timers are dealt with. You must toggle between the 2 substates. in case 1: you do add_timer(....., 2) in case 2: you do add_timer(....., 1);

this will loop it how you want it as well, i did the change that was posted, the removing of extract_timer, and my codebase uses a lot of these types of timers, and i had a few of them mess up considerably.
USA #16
As I said in the other post (http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=5779) what you suggest will not work in a version of SMAUG that behaves as does the code I posted. Just mentioning this as a disclaimer to people who have trouble with this code.