in mud.h add TIMER_SPELL_FUN to timer_types and add SPELL_FUN * spell_fun; to timer_data /* Supporting function */ void add_spell_timer( CHAR_DATA *ch, sh_int type, sh_int count, SPELL_FUN *fun, int value ) { TIMER *timer; for ( timer = ch->first_timer; timer; timer = timer->next ) if ( timer->type == type && type != TIMER_SPELL_FUN ) { timer->count = count; timer->spell_fun = fun; timer->do_fun = NULL; timer->value = value; break; } if ( !timer ) { CREATE( timer, TIMER, 1 ); timer->count = count; timer->type = type; timer->spell_fun = fun; timer->do_fun = NULL; timer->value = value; LINK( timer, ch->first_timer, ch->last_timer, next, prev ); } } /* Supporting function */ int sn_by_skill_fun( SPELL_FUN *spell_fun ) { int sn; for ( sn = 0; sn < top_sn; sn++ ) if ( skill_table[sn]->spell_fun == spell_fun ) return sn; return -1; } /* Sample continuous spell. */ ch_ret sample_continuous_spell( int sn, int level, CHAR_DATA *ch, void *vo ) { if ( IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM ) ) { send_to_char( "You can't concentrate enough for that.\n\r", ch ); return rSPELL_FAILED; } switch( ch->substate ) { default: /* This is where you would put an initial message and any initial actions if any. */ add_spell_timer( ch, TIMER_SPELL_FUN, UMAX(skill_table[sn]->beats / 2, 3), sample_spell, 1); act( AT_DANGER, "Begun", ch, NULL, NULL, TO_ROOM ); ch->tempnum = 0; return rNONE; case 1: /* This is where you would put a repeating action. */ ch_printf( ch, "%d iterations.\n\r", ch->tempnum ); break; case SUB_TIMER_DO_ABORT: /* This is where you would put a handler for if they interrupt. You can make interrupts impossible by setting ch->substate to SUB_TIMER_CANT_ABORT and returning rNONE doing this will force them to complete the spell, no matter what else they try to do. If you've made the loop infinite like in this sample, they can NEVER stop casting this spell short of dying. Useful for compulsion spells like ottos irresistible dance, as long as you make the victim the one who gets the timer. */ act( AT_DANGER, "Interrupted", ch, NULL, NULL, TO_ROOM ); ch->substate = SUB_NONE; ch->tempnum = 0; return rSPELL_FAILED; } /* we're using tempnum to keep track of how many rounds this has gone on for, useful for spells that get more powerful the longer you're casting them, or to determine how taxed a character should by nonstop casting. In this form of loop, the info below here occurs every single time it's recast, because case 1 breaks instead of returning. */ ch->tempnum++; add_spell_timer( ch, TIMER_SPELL_FUN, 3, sample_spell, 1); act( AT_DANGER, "Continuing", ch, NULL, NULL, TO_ROOM ); return rNONE; } /* Sample multi-step spell. */ ch_ret sample_fourpart_spell( int sn, int level, CHAR_DATA *ch, void *vo ) { if ( IS_NPC(ch) && IS_AFFECTED( ch, AFF_CHARM ) ) { send_to_char( "You can't concentrate enough for that.\n\r", ch ); return rSPELL_FAILED; } switch( ch->substate ) { default: /* This is where you would put an initial message and any initial actions if any. */ add_spell_timer( ch, TIMER_SPELL_FUN, UMAX(skill_table[sn]->beats / 2, 3), sample_fourpart_spell, 1); // notice the 1 sets which step comes next. act( AT_DANGER, "Begun", ch, NULL, NULL, TO_ROOM ); ch->tempnum = 0; return rNONE; case 1: /* This is what happens during step one. */ act( AT_DANGER, "1st message", ch, NULL, NULL, TO_ROOM ); add_spell_timer( ch, TIMER_SPELL_FUN, UMAX(skill_table[sn]->beats / 2, 3), sample_fourpart_spell, 2); // notice the 2 sets which step comes next. break; case 2: /* This is what happens during step two. */ act( AT_DANGER, "2nd message", ch, NULL, NULL, TO_ROOM ); add_spell_timer( ch, TIMER_SPELL_FUN, UMAX(skill_table[sn]->beats / 2, 3), sample_fourpart_spell, 3); // notice the 3 sets which step comes next. break; case 3: /* This is what happens during step three. */ act( AT_DANGER, "3rd message", ch, NULL, NULL, TO_ROOM ); add_spell_timer( ch, TIMER_SPELL_FUN, UMAX(skill_table[sn]->beats / 2, 3), sample_fourpart_spell, 4); // notice the 4 sets which step comes next. break; case 4: /* This is what happens during step four. */ act( AT_DANGER, "4th message", ch, NULL, NULL, TO_ROOM ); ch_printf( ch, "%d iterations.\n\r", ch->tempnum ); break; case SUB_TIMER_DO_ABORT: /* This is where you would put a handler for if they interrupt. You can make interrupts impossible by setting ch->substate to SUB_TIMER_CANT_ABORT and returning rNONE doing this will force them to complete the spell, no matter what else they try to do. */ act( AT_DANGER, "Interrupted", ch, NULL, NULL, TO_ROOM ); ch->substate = SUB_NONE; ch->tempnum = 0; return rSPELL_FAILED; } /* You can add stuff down here that happens every round, or you can change the breaks above into returns, depends on how you want things to work. */ return rNONE; } in violence_update: before: if ( timer->type == TIMER_DO_FUN ) add: /* Check spell to see if should be doing something. */ if ( timer->type == TIMER_SPELL_FUN && timer->spell_fun != NULL ) { int tempsub, sn; if ((sn = sn_by_skill_fun( timer->spell_fun )) != -1) { tempsub = ch->substate; ch->substate = timer->value; (*timer->spell_fun) ( sn, ch->level, ch, NULL ); if ( char_died(ch) ) break; ch->substate = tempsub; } } in interpret.c in interpret: after this section's last end } /* check for a timer delayed command (search, dig, detrap, etc) */ if ( (timer=get_timerptr(ch, TIMER_DO_FUN)) != NULL ) add: /* Check spell to see if should be doing something. */ if ( ((timer=get_timerptr(ch, TIMER_SPELL_FUN)) != NULL)) { int tempsub, sn; if ((sn = sn_by_skill_fun( timer->spell_fun )) != -1) { tempsub = ch->substate; ch->substate = SUB_TIMER_DO_ABORT; (timer->spell_fun) ( sn, ch->level, ch, NULL ); if ( char_died(ch) ) return; if ( ch->substate != SUB_TIMER_CANT_ABORT ) { ch->substate = tempsub; extract_timer( ch, timer ); } else { ch->substate = tempsub; return; } } } }