This snippet is fairly simple to install, the only major change is to do_cast and pasting in the other support functions.  What it will do is remove the wait_state that is normally applied after casting a spell and instead makes the caster concentrate on the spell before hand.  It should basically elminate spamming casting commands and allows for a greater realism in spellcasting.

This snippet was installed into a stock Quickmud, but should be appliable to a stock ROM or adapted to most derivs being that not many people have changed how casting has been handled.


//              merc.h

// under the i am lazy category  :P

#define stc 	send_to_char

//add defines for the character's action state

#define ACTION_NONE	0
#define ACTION_CASTING	1

//in struct char_data

    sh_int	       action[2];
    sh_int	       spell_vars[5];
    void	      * vo;


//               magic.c

// in the local functions near the top of the file

/* imported functions */
bool remove_obj args ((CHAR_DATA * ch, int iWear, bool fReplace));
void wear_obj args ((CHAR_DATA * ch, OBJ_DATA * obj, bool fReplace));
+  void stop_spell args ((CHAR_DATA *ch));


// now the fun part,  change do_cast into to two seperate functions, the first one checks for
// basic legality and then sets the variables in the char_data

// in function do_cast find the say_spell line and start chopping

-    if (str_cmp (skill_table[sn].name, "ventriloquate"))
-        say_spell (ch, sn);

-    WAIT_STATE (ch, skill_table[sn].beats);

-    if (number_percent () > get_skill (ch, sn))
-    {
-        send_to_char ("You lost your concentration.\n\r", ch);
-        check_improve (ch, sn, FALSE, 1);
-        ch->mana -= mana / 2;
-    }
-    else
-    {
-        ch->mana -= mana;
-        if (IS_NPC (ch) || class_table[ch->class].fMana)
-            /* class has spells */
-            (*skill_table[sn].spell_fun) (sn, ch->level, ch, vo, target);
-        else
-            (*skill_table[sn].spell_fun) (sn, 3 * ch->level / 4, ch, vo, target);
-        check_improve (ch, sn, TRUE, 1);
-    }
-
-    if ((skill_table[sn].target == TAR_CHAR_OFFENSIVE
-         || (skill_table[sn].target == TAR_OBJ_CHAR_OFF
-             && target == TARGET_CHAR)) && victim != ch
-        && victim->master != ch)
-    {
-        CHAR_DATA *vch;
-        CHAR_DATA *vch_next;
-
-        for (vch = ch->in_room->people; vch; vch = vch_next)
-        {
-            vch_next = vch->next_in_room;
-            if (victim == vch && victim->fighting == NULL)
-            {
-                check_killer (victim, ch);
-                multi_hit (victim, ch, TYPE_UNDEFINED);
-                break;
-            }
-        }
-    }
-
-    return;
- }


// replace all of that with this

    ch->spell_vars[0] = sn;
    ch->spell_vars[1] = ch->level;
    ch->spell_vars[2] = target;
    ch->spell_vars[4] = mana;
    ch->vo = vo;
    ch->spell_vars[3] = skill_table[sn].beats / 6;
    ch->action[0] = ACTION_CASTING;

    stc ("You begin to focus on casting a spell.\n\r", ch);
    act ("$n begins to cast a spell.", ch, NULL, NULL, TO_ROOM);
    
    return;
}

// Now add in the release function that is called when the timer reaches 0
// has_object is a quick hack function that takes OBJ_DATA and checks if the player carries it

bool has_object ( CHAR_DATA *ch, OBJ_DATA *obj)
{
   OBJ_DATA *obj1;

        for ( obj1 = ch->carrying; obj != NULL; obj = obj->next_content )
        {
            if ( obj1 == obj )
              return TRUE;
        }
      return FALSE;
}


void release_spell( int sn, int level, CHAR_DATA *ch, void *vo, int target)
{
    OBJ_DATA *obj;
    CHAR_DATA *victim = NULL;

/* check to be sure the target is still there */
   switch (target)
   {
	default:
            break;

        case TARGET_CHAR:
            victim = (CHAR_DATA *) vo;
            if (victim == NULL || victim->in_room != ch->in_room)
            {
                send_to_char ("They are no longer here.\n\r", ch);
                stop_spell( ch );
                return;
            }
            if (is_safe (ch, victim) && ch != victim)
            {
                send_to_char ("Something isn't right...\n\r", ch);
		stop_spell( ch );
                return;
            }
            break;


        case TARGET_OBJ:
            obj = ( OBJ_DATA *) vo;
           if ( obj == NULL || (obj->in_room != ch->in_room && !has_object(ch, obj)))
           {
	      stc ("That item is no longer here.\n\r", ch);
	      stop_spell( ch);
	      return;
           }

            break;

     }

    if (number_percent () > get_skill(ch, sn))
    {
        send_to_char ("You lost your concentration.\n\r", ch);
        check_improve (ch, sn, FALSE, 1);
        ch->mana -= ch->spell_vars[4] / 2;
    }
    else
    {
        ch->mana -= ch->spell_vars[4];
        if (IS_NPC (ch) || class_table[ch->class].fMana)
            /* class has spells */
            (*skill_table[sn].spell_fun) (sn, level, ch, vo, target);
        else
            (*skill_table[sn].spell_fun) (sn, 3 * level / 4, ch, vo, target);
        check_improve (ch, sn, TRUE, 1);
    if (str_cmp (skill_table[sn].name, "ventriloquate"))
        say_spell ( ch);

    }

     ch->action[0] = ACTION_NONE;

    if ((skill_table[sn].target == TAR_CHAR_OFFENSIVE
         || (skill_table[sn].target == TAR_OBJ_CHAR_OFF
             && target == TARGET_CHAR)) && victim != ch
        && victim->master != ch)
    {
        CHAR_DATA *vch;
        CHAR_DATA *vch_next;

        for (vch = ch->in_room->people; vch; vch = vch_next)
        {
            vch_next = vch->next_in_room;
            if (victim == vch && victim->fighting == NULL)
            {
                check_killer (victim, ch);
                multi_hit (victim, ch, TYPE_UNDEFINED);
                break;
            }
        }
    }

return;
}

// add in stop_spell to reset the various data

void stop_spell( CHAR_DATA *ch )
{

   if (ch->action[0] != ACTION_CASTING)
      return;

     ch->spell_vars[0] = 0;
     ch->spell_vars[1] = 0;
     ch->spell_vars[2] = 0;
     ch->vo            = NULL;
     ch->mana -= ch->spell_vars[4] / 4;

   ch->action[0] = ACTION_NONE;
return;
}

// the update_casting is called from violence_update   decreases the timer and calls 
// release_spell when the timer reaches 0

void update_casting( CHAR_DATA *ch )
{
 
  if ( --ch->spell_vars[3] == 0)
    release_spell( ch->spell_vars[0], ch->spell_vars[1], ch, ch->vo, ch->spell_vars[2]);
  else
  {
    stc("You continue to draw energy for the spell.\n\r", ch);
    act("$n continues to recite a spell.", ch, NULL, NULL, TO_ROOM);
  }

return;
}

// Thats it for magic.c   now on to fight.c to add the update casting call
// add update_casting to the local functions listing

void raw_kill args ((CHAR_DATA * victim));
void set_fighting args ((CHAR_DATA * ch, CHAR_DATA * victim));
void disarm args ((CHAR_DATA * ch, CHAR_DATA * victim));
+  void update_casting args((CHAR_DATA *ch ));


//  in violence_update at the beginning of the first for() loop

    for (ch = char_list; ch != NULL; ch = ch_next)
    {
        ch_next = ch->next;

+        if ( ch->action[0] == ACTION_CASTING )
+          update_casting(ch);

        if ((victim = ch->fighting) == NULL || ch->in_room == NULL)
            continue;



There you go, this is the very basic form but should be enough to get you started.  Some other simple things I have done to work along with this is to add messages to each spell that are used instead of say_spell to allow spells to be more unique.  Just alter the message in update_casting and get rid of say_spell.  Also added a check in bool damage so that a mage in the middle of casting a spell that takes damage has a chance of losing their concentration.  I also added a do_stop command ( basically a call to stop_spell) so spell casting can be halted.  The only major problem I ran into was that the immortal command do_at had to be reworked into a direct call to the spell function instead of a normal do_cast call.

Any questions I can normally be found lurking about the major mud boards or hacking away at my mud lanera.net  port 7000