/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

		RoA MobProc code version 1.0 Beta

  mobproc.c	Realms of Aurealis MobProc code, specifically written
		for RoA with OLCable MobProcs in mind... note this is
		completely ORIGINAL code, as you can tell by its crude
		form :P, this is NOT an adaptation from any other type
		of MobProc form out there, specifically Merc, which I
		happen to loathe, and which in itself is not amenable
		to Online Creation...
		For use with RoAOLCv2.0 or any other similar OLC and
		only with the consent of myself, JRhone...
		See wizhelp files for fairly complete info on how to
		create and use these mobprocs and what commands are 
		available.

		******** 100% completely original code ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** 100% completely original code ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "db.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "magic.h"
#include "mobproc.h"
#include "acmd.h"
#include "quest.h"
#include "lists.h"
#include "htown.h"
#include "clicomm.h"
#include "global.h"

/* external vars */
extern char *genders[];
extern struct htown_data htowns[];

// external functions
extern struct mtrig_type *free_mtriggers(struct mtrig_type *head);

#define MAX_MOB_REACTIONS 10
#define MAX_MOB_REACTION_LENGTH 1024

/* simple structure for corresponding mobproc calls... */
const struct mproc_type mproc_calls[] = {
  {"mtarget", 		do_mtarget},
  {"mtargetroom",	do_mtarget},
  {"mtargetzone",	do_mtarget_zone},
  {"mtargetset",	do_mtarget_set},
  {"mreset",		do_mreset},
  {"mstart", 		do_mstart},
  {"mend", 		do_mend},
  {"mgoto", 		do_mgoto},
  {"mif", 		do_mif},
  {"mpause", 		do_mpause},
  {"mrandom", 		do_mrandom},
  {"mtelezone",		do_mtele_zone},
  {"mhunt",		do_mhunt},
  {"mtrigwait",		do_mtrig_wait},
  {"mtrigoff",		do_mtrig_off},
  {"mtrigon",		do_mtrig_on},
  {"mtrigwax",		do_mtrig_wax},
  {"mtrigadd",		do_mtrig_add},
  {"mtrigdelete",	do_mtrig_delete},
  {"msoundsendchar",	do_msoundsendchar},
  {"msoundsendroom",	do_msoundsendroom},
  {"mtag",	        do_mtag},
  {"", NULL} /* last one used for check of end of defines */
};

// what MIF checks are available?
char *mif_BOOLs[] = {
  "tarcharset",		"!tarcharset",
  "tarobjset",		"!tarobjset",
  "tarcharroom",	"!tarcharroom",
  "tarobjroom", 	"!tarobjroom",
  "tarcharzone",	"!tarcharzone",
  "tarobjzone", 	"!tarobjzone",
  "tarcharpc", 		"!tarcharpc",
  "tarcharcompquest",	"!tarcharcompquest",
  "tarcharlevel", 
  "inroom", 		"!inroom",
  "npcinroom", 		"!npcinroom",
  "objinroom", 		"!objinroom",
  "objinv", 		"!objinv",
  "objeq", 		"!objeq",
  "mfighting", 		"!mfighting",
  "transpresent", 	"!transpresent",
  "tarcharonquest", 	"!tarcharonquest",
  "tarcharrace",        "!tarcharrace",
  "tarcharclass",       "!tarcharclass",
  "tarchargender",      "!tarchargender",
  "\n"
};

// let immortal see all mobproc commands available
ACMD(do_mobproc_show)
{
  int i,j;

  one_argument(argument, arg);

  if (is_abbrev(arg, "mcommands"))
  {
    strcpy(buf, "%B%6MobProc Mob Commands%0:\n\r");
    for (i=j=0; *mproc_calls[i].arg; i++)
    {
      sprintf(buf+strlen(buf), "%%5%%B%-19.19s%%0", mproc_calls[i].arg);
      if (!(++j % 4))
        strcat(buf, "\n\r");
    }
    if ((j%4))
      strcat(buf, "\n\r");
    page_string(ch->desc, buf, 1);
  }
  else
  if (is_abbrev(arg, "mifs"))
  {
    strcpy(buf, "%B%6MobProc Mob MIF Args%0:\n\r");
    for (i=j=0; *mif_BOOLs[i] != '\n'; i++)
    {
      sprintf(buf+strlen(buf), "%%5%%B%-19.19s%%0", mif_BOOLs[i]);
      if (!(++j % 4))
        strcat(buf, "\n\r");
    }
    if ((j%4))
      strcat(buf, "\n\r");
    page_string(ch->desc, buf, 1);
  }
  else
    send_to_char("Usage: mprocshow < mcommands | mifs >.\n\r",ch);
}

// count number of objects in the mobs room
int num_obj_room(chdata *mob)
{
  int i;
  obdata *obj = world[mob->in_room].contents;
  for (i=0; obj; obj=obj->next_content, i++)
    ;
  return i;
}

// return ptr to the numTH item in mob's room
obdata *get_obj_room(chdata *mob, int num)
{
  int i;
  obdata *obj = world[mob->in_room].contents;
  for (i = 0; i < num && obj; obj=obj->next_content, i++)
    if (i == num) break;
  return obj;
}

// return ptr to the numTH object in mob's zone
obdata* get_obj_zone(chdata *mob, int num)
{
  int i;
  obdata *obj = object_list;

  for (i=0; obj && i < num; obj=obj->next)
  {
    if (INVALID_ROOM(obj->in_room)) continue;
    if (world[obj->in_room].zone == world[mob->in_room].zone)
      i++;
    if (i == num) return obj;
  }
  return NULL;
}

// return how many objects are in ROOMS in mob's zone
int num_obj_zone(chdata *mob)
{
  int i;
  obdata *obj = object_list;

  for (i=0; obj; obj=obj->next)
  {
    if (INVALID_ROOM(obj->in_room)) continue;
    if (world[obj->in_room].zone == world[mob->in_room].zone)
      i++;
  }
  return i;
}

// target counters for ROOMS
int num_pc_room(chdata *mob)
{
  int i;
  chdata *vict = world[mob->in_room].people;
  for (i=0; vict; vict = vict->next_in_room)
    if (IS_PC(vict) && vict != mob) i++;

  return i;
}
int num_npc_room(chdata *mob)
{
  int i;
  chdata *vict = world[mob->in_room].people;
  for (i=0; vict; vict = vict->next_in_room)
    if (IS_NPC(vict) && vict != mob) i++;

  return i;
}
int num_any_room(chdata *mob)
{
  int i;
  chdata *vict = world[mob->in_room].people;
  for (i=0; vict; vict = vict->next_in_room)
    if (vict != mob) i++;
  return i;
}

// target counters for ZONE
int num_any_zone(chdata *mob)
{
  int i;
  chdata *vict; 
  int zone = world[mob->in_room].zone;

  for (i=0, vict = character_list; vict; vict = vict->next)
    if (world[vict->in_room].zone == zone && vict != mob)
      i++;

  return i;
}

int num_npc_zone(chdata *mob)
{
  int i;
  chdata *vict; 
  int zone = world[mob->in_room].zone;

  for (i=0, vict = character_list; vict; vict = vict->next)
    if (IS_NPC(vict) && world[vict->in_room].zone == zone && vict != mob)
      i++;

  return i;
}

int num_pc_zone(chdata *mob)
{
  int i;
  chdata *vict; 
  int zone = world[mob->in_room].zone;

  for (i=0, vict = character_list; vict; vict = vict->next)
    if (IS_PC(vict) && world[vict->in_room].zone == zone && vict != mob)
      i++;

  return i;
}

// targetting functions for ROOM
chdata *get_any_room(chdata *mob, int num)
{
  int i;
  chdata *vict = world[mob->in_room].people;
  for (i = 0; i < num && vict; vict = vict->next_in_room)
  {
    if (vict != mob) i++;
    if (i == num) break;
  }
  return vict;
}
chdata *get_pc_room(chdata *mob, int num)
{
  int i;
  chdata *vict = world[mob->in_room].people;
  for (i = 0; i < num && vict; vict = vict->next_in_room)
  {
    if (IS_PC(vict) && vict != mob) i++;
    if (i == num) break;
  }
  return vict;
}
chdata *get_npc_room(chdata *mob, int num)
{
  int i;
  chdata *vict = world[mob->in_room].people;
  for (i = 0; i < num && vict; vict = vict->next_in_room)
  {
    if (IS_NPC(vict) && vict != mob) i++;
    if (i == num) break;
  }
  return vict;
}

// targetting functions for ZONE
chdata *get_any_zone(chdata *mob, int num)
{
  int i;
  chdata *vict;
  
  for (i = 0, vict = character_list; vict && i < num; vict = vict->next)
  {
    if (INVALID_ROOM(vict->in_room)) continue;
    if (world[vict->in_room].zone == world[mob->in_room].zone && mob != vict)
      i++;
    if (i == num) return vict;
  }

  return NULL;
}

chdata *get_pc_zone(chdata *mob, int num)
{
  int i;
  chdata *vict;
  
  for (i = 0, vict = character_list; vict && i < num; vict = vict->next)
  {
    if (INVALID_ROOM(vict->in_room)) continue;
    if (world[vict->in_room].zone == world[mob->in_room].zone &&
	mob != vict && IS_PC(vict))
      i++;
    if (i == num) return vict;
  }

  return NULL;
}

chdata *get_npc_zone(chdata *mob, int num)
{
  int i;
  chdata *vict;
  
  for (i = 0, vict = character_list; vict && i < num; vict = vict->next)
  {
    if (INVALID_ROOM(vict->in_room)) continue;
    if (world[vict->in_room].zone == world[mob->in_room].zone &&
	mob != vict && IS_NPC(vict))
      i++;
    if (i == num) return vict;
  }

  return NULL;
}

// actual mobproc functions begin

// msoundsendchar <char name> <soundfile name>
// char must be in same room and have client connection
int do_msoundsendchar(chdata *mob, char *comm)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  chdata *vict; 

  half_chop(comm, arg1, arg2);
  if (!*arg1 || !*arg2)
  {
    sprintf(buf, "SYSERR: Invalid arguments sent to msoundsendchar (%s).", GET_NAME(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    send_to_char("Invalid argument to msoundsendchar...\n\r",mob);
    return MP_NORMAL;
  }

  if (!(vict = get_char_room_vis(mob, arg1)))
  {
    send_to_char("Target not found...\n\r",mob);
    return MP_NORMAL;
  }

  if (IS_PC(vict) && vict->desc && HAS_CLIENT(vict->desc))
  {
    send_soundfile_to_client(vict->desc, arg2);
    send_to_char("Ok, soundfile sent...\n\r",mob);
  }
  else
    send_to_char("Target doesn't have client connection...\n\r",mob);

  return MP_NORMAL;
}

// msoundsendroom <soundfile name>
// chars must be in same room and have client connection
int do_msoundsendroom(chdata *mob, char *comm)
{
  char arg1[MAX_INPUT_LENGTH];
  chdata *vict;

  one_argument(comm, arg1);
  if (!*arg1)
  {
    sprintf(buf, "SYSERR: Invalid argument sent to msoundsendroom (%s).", GET_NAME(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    send_to_char("Invalid argument to msoundsendroom...\n\r",mob);
    return MP_NORMAL;
  }

  for (vict = world[mob->in_room].people; vict; vict=vict->next_in_room)
    if (IS_PC(vict) && vict->desc && HAS_CLIENT(vict->desc))
      send_soundfile_to_client(vict->desc, arg1);

  return MP_NORMAL;
}

/* set a mobile's target character/object (in zone) */
int do_mtarget_zone(chdata *mob, char *comm)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  int num, pick;
  chdata *vict = NULL;
  obdata *obj = NULL;
  BOOL pc, npc;

  pc = FALSE;
  npc = FALSE;
  half_chop(comm, arg1, arg2);
  if (!str_cmp(arg1, "char"))
  {
    if(TARGET_CHAR(mob))
      free_log(TARGET_CHAR(mob), "do_mtarget_zone");
    TARGET_CHAR(mob) = NULL;

    half_chop(arg2, arg1, arg2);
    if (!*arg1)
    {
      sprintf(buf, "SYSERR: Undefined char type (pc | npc | any) to mtargetzone (%s).",
	      GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }
    else 
    if (!str_cmp(arg1, "pc")) pc = TRUE;
    else
    if (!str_cmp(arg1, "npc")) npc = TRUE;
    else
    if (!str_cmp(arg1, "any")) 
    {
      npc = TRUE;
      pc = TRUE;
    }
    else
    {
      sprintf(buf, "SYSERR: Undefined char type (pc | npc | any) to mtargetzone (%s).", 
              GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }

    half_chop(arg2, arg1, arg2);
    if (!*arg1 || !is_number(arg1))
    {
      sprintf(buf, "SYSERR: Undefined character number to mtargetzone (%s).",GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }

    if ((num = atoi(arg1))) /* if a number besides 0 is wanted */
    {
      if ((!pc && (vict = get_npc_zone(mob, num))) || (!npc && (vict = get_pc_zone(mob, num))) ||
          (pc && npc && (vict = get_any_zone(mob, num))) )
      {
        if (MOB2_FLAGGED(vict, MOB2_NO_MTARGET))
          return MP_MTARGET;

        TARGET_CHAR(mob) = str_dup(fname(vict->player.name));
	sprintf(buf, "SYSUPD: %s setting char target to %s", GET_NAME(mob),fname(vict->player.name));
	mudlog(buf, BUG, LEV_IMM, FALSE);
      }
    }
    else
    if (!num) /* this means pick a random character in the zone */
    {
      pick = 0;
      if (!pc && num_npc_zone(mob))
	pick = number(1, num_npc_zone(mob));
      else
      if (!npc && num_pc_zone(mob))
	pick = number(1, num_pc_zone(mob));
      else
      if (pc && npc && num_any_zone(mob))
	pick = number(1, num_any_zone(mob));

      if (pick)
      {
        if ((!pc && (vict = get_npc_zone(mob, pick))) || (!npc && (vict = get_pc_zone(mob, pick))) ||
            (pc && npc && (vict = get_any_zone(mob, pick))) )
        {
          if (MOB2_FLAGGED(vict, MOB2_NO_MTARGET))
            return MP_MTARGET;

          TARGET_CHAR(mob) = str_dup(fname(vict->player.name));
	  sprintf(buf, "SYSUPD: %s setting char target to %s", GET_NAME(mob),
		  fname(vict->player.name));
	  mudlog(buf, BUG, LEV_IMM, FALSE);
        }
      }
    }
  }
  else
  if (!str_cmp(arg1, "obj"))
  {
    if(TARGET_OBJ(mob))
      free_log(TARGET_OBJ(mob), "do_mtarget_zone 2");
    TARGET_OBJ(mob) = NULL;

    half_chop(arg2, arg1, arg2);
    if (!*arg1 || !is_number(arg1))
    {
      sprintf(buf, "SYSERR: Undefined object number to mtargetzone (%s).",GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }

    if ((num = atoi(arg1))) /* if a number besides 0 is wanted */
    {
      num--;
      if ((obj = get_obj_zone(mob, num)))
      {
	sprintf(buf, "SYSUPD: %s setting obj target to %s", GET_NAME(mob),
		fname(obj->name));
	mudlog(buf, BUG, LEV_IMM, FALSE);
        TARGET_OBJ(mob) = str_dup(fname(obj->name));
      }
    }
    else
    if (!num && num_obj_zone(mob) &&
	(obj = get_obj_zone(mob, number(0, (num_obj_zone(mob)-1)))))
    {
      sprintf(buf, "SYSUPD: %s setting obj target to %s", GET_NAME(mob),
	      fname(obj->name));
      mudlog(buf, BUG, LEV_IMM, FALSE);
      TARGET_OBJ(mob) = str_dup(fname(obj->name));
    }
  }
  else
  {
    sprintf(buf, "SYSERR: Undefined arg (%s) to mtargetzone (%s).", arg1,
	    GET_NAME(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return MP_NORMAL;
  }

  return MP_MTARGET;
}

/* set a mobile's target character/object (in room) */
int do_mtarget(chdata *mob, char *comm)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  int num, pick;
  chdata *vict = NULL;
  obdata *obj = NULL;
  BOOL pc, npc;

  pc = FALSE;
  npc = FALSE;
  half_chop(comm, arg1, arg2);
  if (!str_cmp(arg1, "char"))
  {
    if(TARGET_CHAR(mob))
      free_log(TARGET_CHAR(mob), "do_mtarget");
    TARGET_CHAR(mob) = NULL;

    half_chop(arg2, arg1, arg2);
    if (!*arg1)
    {
      sprintf(buf, "SYSERR: Undefined char type (pc | npc | any) to mtarget (%s).",GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }
    else 
    if (!str_cmp(arg1, "pc")) pc = TRUE;
    else
    if (!str_cmp(arg1, "npc")) npc = TRUE;
    else
    if (!str_cmp(arg1, "any")) 
    {
      npc = TRUE;
      pc = TRUE;
    }
    else
    {
      sprintf(buf, "SYSERR: Undefined char type (pc | npc | any) to mtarget (%s).",GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }

    half_chop(arg2, arg1, arg2);
    if (!*arg1 || !is_number(arg1))
    {
      sprintf(buf, "SYSERR: Undefined character number to mtarget (%s).",GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }

    if ((num = atoi(arg1))) /* if a number besides 0 is wanted */
    {
      if ((!pc && (vict = get_npc_room(mob, num))) ||
	  (!npc && (vict = get_pc_room(mob, num))) ||
          (pc && npc && (vict = get_any_room(mob, num))) )
      {
        TARGET_CHAR(mob) = str_dup(fname(vict->player.name));
	sprintf(buf, "SYSUPD: %s setting char target to %s", GET_NAME(mob),
		fname(vict->player.name));
	mudlog(buf, BUG, LEV_IMM, FALSE);
      }
    }
    else
    if (!num) /* this means pick a random character in the room */
    {
      pick = 0;
      if (!pc && num_npc_room(mob))
	pick = number(1, num_npc_room(mob));
      else
      if (!npc && num_pc_room(mob))
	pick = number(1, num_pc_room(mob));
      else
      if (pc && npc && num_any_room(mob))
	pick = number(1, num_any_room(mob));
      if (pick)
      {
        if ((!pc && (vict = get_npc_room(mob, pick))) ||
 	    (!npc && (vict = get_pc_room(mob, pick))) ||
            (pc && npc && (vict = get_any_room(mob, pick))) )
        {
          TARGET_CHAR(mob) = str_dup(fname(vict->player.name));
	  sprintf(buf, "SYSUPD: %s setting char target to %s", GET_NAME(mob),
		  fname(vict->player.name));
	  mudlog(buf, BUG, LEV_IMM, FALSE);
        }
      }
    }
  }
  else
  if (!str_cmp(arg1, "obj"))
  {
    if(TARGET_OBJ(mob))
      free_log(TARGET_OBJ(mob), "do_mtarget 2");
    TARGET_OBJ(mob) = NULL;

    half_chop(arg2, arg1, arg2);
    if (!*arg1 || !is_number(arg1))
    {
      sprintf(buf, "SYSERR: Undefined object number to mtarget (%s).",GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }

    if ((num = atoi(arg1))) /* if a number besides 0 is wanted */
    {
      num--;
      if ((obj = get_obj_room(mob, num)))
      {
	sprintf(buf, "SYSUPD: %s setting obj target to %s", GET_NAME(mob),
		fname(obj->name));
	mudlog(buf, BUG, LEV_IMM, FALSE);
        TARGET_OBJ(mob) = str_dup(fname(obj->name));
      }
    }
    else
    if (!num && num_obj_room(mob) &&
	(obj = get_obj_room(mob, number(0, (num_obj_room(mob)-1)))))
    {
      sprintf(buf, "SYSUPD: %s setting obj target to %s", GET_NAME(mob),
	      fname(obj->name));
      mudlog(buf, BUG, LEV_IMM, FALSE);
      TARGET_OBJ(mob) = str_dup(fname(obj->name));
    }
  }
  else
  {
    sprintf(buf, "SYSERR: Undefined arg (%s) to mtarget (%s).", arg1,
	    GET_NAME(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return MP_NORMAL;
  }
  return MP_MTARGET;
}

// perform the mobproc subs
// if ch, use ch for subs, else use TARGET_CHAR
// if ob, use ob for subs, else use TARGET_OBJ
BOOL do_mobproc_subs(chdata *mob, char *comm, chdata *ch, obdata *ob)
{
  char newcomm[MAX_INPUT_LENGTH];
  int i, j;

  if (!strchr(comm, '+'))
    return FALSE;

  // if ch wasnt passed, try to get a chdata ptr from target char
  if (!ch)
    if (TARGET_CHAR(mob))
      if (!(ch = get_char_room_vis(mob, TARGET_CHAR(mob))))
	if (!(ch = get_char_zone_vis(mob, TARGET_CHAR(mob))))
	  if (!(ch = get_char_vis(mob, TARGET_CHAR(mob))))
	    ;

  for (i=0, j=0; comm[i]; i++)
  {
    newcomm[j] = '\0';
    if (comm[i] != '+')
    {
      newcomm[j] = comm[i];
      j++;
      continue;
    }
    else
    if (comm[i+1])
    {
      i++;
      switch (comm[i])
      {
	case '+': /* double (++) means sub in one + */
	  newcomm[j] = comm[i];
	  j++; i++;
	  break;
	case 'C':   /* sub SHORT (NPC alias, PC normal) name of char target */
	  if (ch)
	  {
	    if (IS_PC(ch))
	      strcat(newcomm + j, GET_NAME(ch));
	    else
	      strcat(newcomm + j, fname(ch->player.name));
	    j = strlen(newcomm);
	  }
	  else
	  if (TARGET_CHAR(mob))
	  {
	    strcat(newcomm + j, TARGET_CHAR(mob));
	    j = strlen(newcomm);
	  }
	  break;
        case 'N': case 'n':	// normal name for pc, short desc for npc
	  if (ch)
	  {
	    strcat(newcomm + j, GET_NAME(ch));
	    j = strlen(newcomm);
	  }
	  else
	  if (TARGET_CHAR(mob))
	  {
	    strcat(newcomm + j, TARGET_CHAR(mob));
	    j = strlen(newcomm);
	  }
	  /* if no sub there, ok, its as if we processed */
	 break;

	case 'R': case 'r':
	 if (ch)
	 {
	  strcat(newcomm + j, rcarray[GET_RACE(ch)].race_name);
	  j = strlen(newcomm);
	 }
	  break;

	case 'L': case 'l':
	 if (ch)
	 {
	  strcat(newcomm + j, gskill_names[SPEAKING(ch)]);
	  j = strlen(newcomm);
	 }
	  break;

	case 'H': case 'h':
         if (ch && htowns[GET_HTOWN(ch)].name)
         {
	  strcat(newcomm + j, htowns[GET_HTOWN(ch)].name);
	  j = strlen(newcomm);
	 }
	  break;

	case 'c':  /* class, if incog sub race instead */
	 if (ch)
	 {
	  if (PRF_FLAGGED(ch, PRF_INCOGNITO))
	    strcat(newcomm + j, rcarray[GET_RACE(ch)].race_name);
	  else
	    strcat(newcomm + j, clarray[(int)GET_CLASS(ch)].class_name);
	  j = strlen(newcomm);
	 }
	  break;

	case 'O': case 'o':  /* sub name of obj target */
	  if (ob)
	  {
	    strcat(newcomm + j, ob->shdesc);
	    j = strlen(newcomm);
	  }
	  else
	  if (TARGET_OBJ(mob))
	  {
	    strcat(newcomm + j, TARGET_OBJ(mob));
	    j = strlen(newcomm);
	  }
	  /* if no sub there, ok, its as if we processed */
	 break;
	default:
	  sprintf(buf, "SYSERR:  Undefined +(%c) to mobproc (%s)",comm[i+1], GET_NAME(mob));
      	  mudlog(buf, BRF, LEV_IMM, FALSE);
      	  return FALSE;
	  break;
      }
    }
    else
    {
      sprintf(buf, "SYSERR:  Undefined +sub to mobproc (%s)",GET_NAME(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return FALSE;
    } 
  }
  newcomm[j] = '\0';
  strncpy(comm, newcomm, MAX_INPUT_LENGTH - 1);
  send_to_char(comm, mob);
  send_to_char("\n\r",mob);
  return TRUE;
}

// if mobproc subs, sub them and send to command interpreter
BOOL sub_and_execute(chdata *mob, char *comm, chdata *ch, obdata *ob)
{
  char newcomm[MAX_INPUT_LENGTH];

  strncpy(newcomm, comm, MAX_INPUT_LENGTH - 1);
  if (do_mobproc_subs(mob, newcomm, ch, ob))
  {
    send_to_char(newcomm, mob);
    send_to_char("\n\r",mob);
    command_interpreter(mob, newcomm);
    return TRUE;
  }
  return FALSE;
}

// mrandom is the first mobproc command which utilizes blocks of commands
// i.e.  mrandom { blah blah blah }
// mrandom followed by a block of randoms, chooses one and does command
int do_mrandom(chdata *ch, char *argument)
{
  char *pt; // our placeholder in the mproc list
  char *tmp, *tmp2;
  int count = 0, pick, i;
  char comm[MAX_INPUT_LENGTH];

  // must be a start block!
  pt = MPROC_CUR(ch);
  skip_spaces(&pt);
  if (!pt || !*pt || *pt != '{')
  {
    sprintf(buf, "SYSERR: Mob #%d fatal mobproc error.  No block after mrandom.",GET_MOB_VNUM(ch));
    mudlog(buf, BUG, LEV_IMM, FALSE);
    send_to_char(strcat(buf, "\n\r"), ch);
    do_mend(ch, argument);  // end mobproc
  }

  // now count commands in the block
  for (tmp = pt, count = 0; *tmp && *tmp != '}'; tmp++)
  {
    tmp = scan_past_eol(tmp);
    skip_spaces(&tmp);
    if (!tmp || !*tmp || *tmp == '}') 
      break;
    else 
      count++;
  }

  if (!tmp || !*tmp)
  {
    sprintf(buf, "SYSERR: Mob #%d fatal mobproc error. Mrandom block never ended.",GET_MOB_VNUM(ch));
    mudlog(buf, BUG, LEV_IMM, FALSE);
    send_to_char(strcat(buf, "\n\r"), ch);
    do_mend(ch, argument);  // end mobproc
  }

  // skip past closing bracket
  tmp = scan_past_eol(tmp);  // this is where we wanna point after we done

  if (!count)
  {
    sprintf(buf, "SYSERR: Mob #%d fatal mobproc error. No randoms in mrandom block.",GET_MOB_VNUM(ch));
    mudlog(buf, BUG, LEV_IMM, FALSE);
    send_to_char(strcat(buf, "\n\r"), ch);
    do_mend(ch, argument);  // end mobproc
  }

  if (count > 1)
    pick = number(1, count);
  else
    pick = 1;

  // now go thru block and pick the pickth one
  pt = scan_past_eol(pt);

  for (count = 1, *comm = '\0'; count < pick; count++)
    pt = scan_past_eol(pt);
  skip_spaces(&pt);

  /* get length of next command */
  for (i=0, tmp2 = pt; *tmp2 && !ISNEWL(*tmp2) && (i < MAX_INPUT_LENGTH); 
       tmp2++, i++)
   ;

  /* copy this command line into comm */
  strncpy(comm, pt, i);
  comm[i] = '\0';

  /* show the mob what they typing */
  send_to_char(comm, ch);
  send_to_char("\n\r",ch);

  if (*comm != '&')  // process only if not our ignore character
    if (!sub_and_execute(ch, comm, NULL, NULL))
      command_interpreter(ch, comm);  

  MPROC_CUR(ch) = tmp;
  return MP_MRANDOM;
}

// pause a mobproc, 1 per 5 seconds for DTIME mobs
// 1 per 10 seconds for normal mobproc mobs
int do_mpause(chdata *ch, char *argument)
{
  int num = 0;

  one_argument(argument, arg);
  if (!is_number(arg)) return MP_NORMAL;
  num = atoi(arg);
  num = MAX(0, MIN(num, 99));
  ch->npc_specials.mob_wait = num;

  if (SPC_FLAGGED(ch, SPC_DOUBLETIME))
    num *= 5;
  else
    num *= 10;

  sprintf(buf, "Ok. Mob Proc waiting %d seconds (roughly).\n\r",num);
  S2C();  
  return MP_NORMAL;
}

/* simply start from where th proc last ended */
/* only if actually MENDED that is */
/* if no mproc_cur, then restart */
int do_mstart(chdata *ch, char *argument)
{
  if (ch->npc_specials.mob_wait >= 0) 
    return MP_NORMAL; 

  ch->npc_specials.mob_wait = 0; 

  /* ok, begin processing of commands with this mob */
  if (!MPROC_CUR(ch))  /* only jump to start if not pting to a comm */
    MPROC_CUR(ch) = MPROC_BEG(ch);

  send_to_char("Ok. Mob Proc started from last endpoint.\n\r",ch);
  return MP_NORMAL;
}

/* wipes out all waits, sets cur back to beginning */
/* does it regardless if in a proc or not */
int do_mreset(chdata *ch, char *argument)
{
  ch->npc_specials.mob_wait = 0;
  MPROC_CUR(ch) = MPROC_BEG(ch);
  TRIG_WAITING(ch) = FALSE;
  send_to_char("Ok. MobProc reset.\n\r",ch);
  return MP_NORMAL;
}

/* ends the processing of a mobproc, sets mobwait to -1 */
int do_mend(chdata *ch, char *argument)
{
  if (ch->npc_specials.mob_wait < 0) 
    return MP_NORMAL; /* already ended */
  ch->npc_specials.mob_wait = -1;
  send_to_char("Ok. MobProc ended.\n\r",ch);
  return MP_NORMAL;
}

// sets mob waiting for a trigger to go off -jtr
int do_mtrig_wait(chdata *ch, char *argument)
{
  if (TRIG_WAITING(ch))  // already awaiting trigger
    return MP_NORMAL;
  if (!TRIGS(ch))	 // no trigs? aint gonna wait
  {
    send_to_char("No triggers found... not waiting.\n\r",ch);
    return MP_NORMAL;
  }
  TRIG_WAITING(ch) = TRUE;
  send_to_char("Ok.  Awaiting trigger.\n\r",ch);
  return MP_NORMAL;
}

// mob will ignore triggers after this call
int do_mtrig_off(chdata *ch, char *argument)
{
  if (TRIG_IGNORE(ch))  // already ignoring triggers
    return MP_MTRIG;
  if (!TRIGS(ch))	 // no trigs? aint gonna ignore
  {
    send_to_char("No triggers found... no need to ignore.\n\r",ch);
    return MP_MTRIG;
  }
  TRIG_IGNORE(ch) = TRUE;
  send_to_char("Ok.  Ignoring triggers.\n\r",ch);
  return MP_MTRIG;
}

// mob will NOT ignore triggers after this call
int do_mtrig_on(chdata *ch, char *argument)
{
  if (!TRIG_IGNORE(ch))  // already listening to triggers
    return MP_MTRIG;
  if (!TRIGS(ch))	 // no trigs? why bother 
  {
    send_to_char("No triggers found... nothing to listen for.\n\r",ch);
    return MP_MTRIG;
  }
  TRIG_IGNORE(ch) = FALSE;
  send_to_char("Ok.  No longer ignoring triggers.\n\r",ch);
  return MP_MTRIG;
}

// add an mtrigger to chars list of trigs -jtr
int add_mtrigger(chdata *mob, char *trigger, char *reaction)
{
  struct mtrig_type *mt = NULL;

  if (!trigger || !reaction)
  {
    sprintf(buf, "SYSERR: Missing trig or react to %s (#%d).",GET_NAME(mob), GET_MOB_VNUM(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return MP_MTRIG;
  }

  // if adding existing trigger, replace reaction and return
  for (mt = TRIGS(mob); mt; mt=mt->next)
    if (!str_cmp(mt->trigger, trigger))
    {
      FREENULL(mt->reaction);
      mt->reaction = STR_DUP(reaction);
      send_to_char("Trigger found and updated.\n\r",mob);
      return MP_MTRIG;
    }

  // ok new one, create and insert to front
  CREATE(mt, struct mtrig_type, 1);
  mt->trigger = STR_DUP(trigger);
  mt->reaction = STR_DUP(reaction);
  mt->next = TRIGS(mob);
  TRIGS(mob) = mt;
  
  send_to_char("Added trigger:\n\r",mob);
  send_to_char(trigger, mob);
  send_to_char("\n\rWith reaction:\n\r",mob);
  send_to_char(reaction, mob);

  return MP_MTRIG;
}

// mtrigadd utilizes blocks of commands
// mtrigadd <trigger> {block of reactions} adds mtrig_type to mobs trigs list
int do_mtrig_add(chdata *ch, char *trigger)
{
  char *pt; // our placeholder in the mproc list
  int i;
  char reaction[MAX_MOB_REACTION_LENGTH];

  if (!trigger)
  {
    sprintf(buf, "SYSERR: %s (#%d) fatal mobproc error.  No trig to mtrigadd.",
            GET_NAME(ch), GET_MOB_VNUM(ch)); 
    mudlog(buf, BUG, LEV_IMM, FALSE);
    send_to_char(strcat(buf, "\n\r"), ch);
    do_mend(ch, trigger);  // end mobproc
    return MP_MTRIG;
  }

  // must be a start block!
  pt = MPROC_CUR(ch);
  skip_spaces(&pt);
  if (!pt || !*pt || *pt != '{')
  {
    sprintf(buf, "SYSERR: %s (#%d) fatal mobproc error.  No block after mtrigadd.",
	    GET_NAME(ch), GET_MOB_VNUM(ch));
    mudlog(buf, BUG, LEV_IMM, FALSE);
    send_to_char(strcat(buf, "\n\r"), ch);
    do_mend(ch, trigger);  // end mobproc
    return MP_MTRIG;
  }

  // yep, all the chars minus the two brackets
  i = chars_in_block(pt);

  if (i <= 0)
  {
    sprintf(buf, "SYSERR: %s (#%d) fatal mobproc error. Reaction block empty.",
            GET_NAME(ch), GET_MOB_VNUM(ch));
    mudlog(buf, BUG, LEV_IMM, FALSE);
    send_to_char(strcat(buf, "\n\r"), ch);
    do_mend(ch, trigger);  // end mobproc
    return MP_MTRIG;
  }

  if (i >= MAX_MOB_REACTION_LENGTH - 2)
  {
    sprintf(buf, "SYSERR: %s (#%d) fatal mobproc error. Reaction block too long.",
            GET_NAME(ch), GET_MOB_VNUM(ch));
    mudlog(buf, BUG, LEV_IMM, FALSE);
    send_to_char(strcat(buf, "\n\r"), ch);
    do_mend(ch, trigger);  // end mobproc
    return MP_MTRIG;
  }

  strncpy(reaction, pt+1, i+1);  // add one for newline...
  reaction[i] = '\0';
  add_mtrigger(ch, trigger, reaction);

  // OOOOkkk just skip the block...
  MPROC_CUR(ch) = skip_block(pt);
  if (!MPROC_CUR(ch) || !*MPROC_CUR(ch))
    MPROC_CUR(ch) = MPROC_BEG(ch);

  return MP_MTRIG;
}

// remove an mtrigger from chars list of trigs -jtr
int do_mtrig_delete(chdata *mob, char *trigger)
{
  struct mtrig_type *mt = NULL;
  struct mtrig_type *next_mt, *temp;
  BOOL found;

  if (!trigger)
  {
    sprintf(buf, "SYSERR: Missing trig to %s (#%d) (remove_mtrig).",GET_NAME(mob),
	    GET_MOB_VNUM(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return MP_MTRIG;
  }

  if (!TRIGS(mob))
    return MP_MTRIG;

  for (mt = TRIGS(mob), found = FALSE; mt; mt=next_mt)
  {
    next_mt = mt->next;
    if (!str_cmp(mt->trigger, trigger))
    {
      REMOVE_FROM_LIST(mt, TRIGS(mob), next);
      if (mt->trigger)
        free_log(mt->trigger, "do_mtrig_delete 1");
      if (mt->reaction)
        free_log(mt->reaction, "do_mtrig_delete 2");
      free_log(mt, "do_mtrig_delete 3");
      found = TRUE;
      send_to_char("Trigger found and removed from list.\n\r",mob);
    }
  }
  if (!found)
    send_to_char("No matching trigger found, list unchanged.\n\r",mob);

  return MP_MTRIG;
}

// wax entire trigger list by mobproc command
int do_mtrig_wax(chdata *mob, char *argument)
{
  if (!TRIGS(mob))
  {
    send_to_char("No triggers found.\n\r",mob);
    return MP_MTRIG;
  }
  TRIGS(mob) = free_mtriggers(TRIGS(mob));
  TRIG_WAITING(mob) = FALSE;	// cant be waiting if no list eh? -jtr
  send_to_char("Entire trigger list removed.\n\r",mob);
  return MP_MTRIG;
}

/* return the number of commands in a mobs mobproc (for gotos) */
int mproc_length(chdata *mob)
{
  char *tmp;
  int count;

  if (!SPC_FLAGGED(mob, SPC_MOBPROC) || !*MPROC_BEG(mob)) 
    return 0;

  for (count = 0, tmp = MPROC_BEG(mob); *tmp; tmp++)
    if (*tmp == '\n') count++;

  return count;
}

// scan a rproc for mtag <tname>
char *get_mtag(char *proc, char *tname)
{
  char *str = proc;
  char arg1[MAX_STRING_LENGTH];
  char arg2[MAX_STRING_LENGTH];

  skip_spaces(&str);
  while (*str)
  {
    half_chop(str, arg1, arg2);
    if (!str_cmp(arg1, "mtag") && is_abbrev(tname, arg2))
      return str;

    str = scan_past_eol(str);
    skip_spaces(&str);
    if (!str || !*str)
      break;
  }

  return NULL;
}

// do nothing but set tag placeholder for mgotos
// 6/14/98 -jtrhone
int do_mtag(chdata *mob, char *argument)
{
  if (!*argument)
  {
    sprintf(buf, "SYSERR: Undefined mtag tag (#%d).",GET_MOB_VNUM(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return MP_MTAG;
  }

  return MP_MTAG;
}

// goto a particular line of the mob proc
// updated for use with mtags 6/14/98 -jtrhone
int do_mgoto(chdata *mob, char *argument)
{
  int line, ctr;
  char *tmp;
  char arg1[MAX_INPUT_LENGTH];

  one_argument(argument, arg1);
  if (!*arg1) 
  {
    sprintf(buf, "SYSERR:  Undefined arg to mgoto (%s)",GET_NAME(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return MP_NORMAL;
  }

  // lookin for mtag...
  if (!is_number(arg1))
  {
    tmp = get_mtag(MPROC_BEG(mob), arg1);
    if (!tmp)
    {
      sprintf(buf, "SYSERR: Unable to locate mtag (%s) in mgoto (#%d).",arg1,GET_MOB_VNUM(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }

    // juuuust to make sure
    if (tmp < MPROC_BEG(mob))
    {
      sprintf(buf, "SYSERR: Lower bounds failure in mgoto (#%d).",GET_MOB_VNUM(mob));
      mudlog(buf, BRF, LEV_IMM, FALSE);
      return MP_NORMAL;
    }

    MPROC_CUR(mob) = tmp;
    sprintf(buf, "Mproc jumped to tag %s.\n\r",arg1);
    send_to_char(buf, mob);
    return MP_NORMAL;
  }

  line = atoi(arg1);
  if (line > mproc_length(mob) || line < 1)
  {
    sprintf(buf, "SYSERR: invalid line number to mgoto (%s)",GET_NAME(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return MP_NORMAL;
  }
  
  if (line == 1)
  {
    MPROC_CUR(mob) = MPROC_BEG(mob);
    send_to_char("Ok.  Jumped to line 1.\n\r",mob);
    return MP_NORMAL;
  }

  tmp = MPROC_BEG(mob);
  ctr = 1;
  while (ctr != line)
  {
    tmp = scan_past_eol(tmp);
    ctr++;
  }

  MPROC_CUR(mob) = tmp;
  sprintf(buf, "Ok. Jumped to line %d(target %d).\n\r",ctr, line);
  send_to_char(buf, mob);
  return MP_NORMAL;  
}

// skip a command goto next or jump to beginning if none left
// must add ability to skip mcommand blocks!, if the command structure
// uses them, such as mrandom... mrandom uses a block structure
// if we are pointing at a block, skip it! (mifs mostly)
// mtrigadd is also a block structure now -jtr 3/21/97
void goto_next_mcom(chdata *mob)
{
  char *tmp = MPROC_CUR(mob);
  char arg1[MAX_INPUT_LENGTH];
  
  one_argument(tmp, arg1);

  // jump to the top...
  if (!*tmp) 
  {
    MPROC_CUR(mob) = MPROC_BEG(mob);
    return;
  }

  // lets check the command we're skippin to see if it is a block command
  if (*arg1 == '{')
  {
    tmp = skip_block(tmp);
    if (!*tmp) 
      tmp = MPROC_BEG(mob);
    MPROC_CUR(mob) = tmp;
    return;
  }
  else
  if (is_abbrev(arg1, "mrandom") || is_abbrev(arg1, "mtrigadd"))
  {
    tmp = scan_past_eol(tmp);
    tmp = skip_block(tmp);
    if (!*tmp) 
      tmp = MPROC_BEG(mob);
    MPROC_CUR(mob) = tmp;
    return;
  }

  // else do the normal one line skip
  tmp = scan_past_eol(tmp);
  if (!*tmp) 
    tmp = MPROC_BEG(mob);

  MPROC_CUR(mob) = tmp;
}

/* RoA Mobprocs	-jtrhone aka Vall */
/* mif tests things based on arg */
// argument containes the mif comparisons, while MPROC_CUR pts to the block to be
// executed or skipped based on the comparisons
// added tarcharrace, tarcharclass, tarchargender 6/8/98 -jtrhone
int do_mif(chdata *mob, char *argument)
{
  static char mif_comp[MAX_INPUT_LENGTH];
  static char comp_args[MAX_INPUT_LENGTH];
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  int vroom, rroom;
  chdata *ch;
  obdata *obj;
  BOOL found;
  int i, cmd;

  int get_race_by_name(char *arg); // races.c, 8/22/98 -jtrhone

  half_chop(argument, mif_comp, comp_args);

  if ((cmd = search_block(mif_comp, mif_BOOLs, FALSE)) < 0)
  {
    send_to_char("Undefined MIF arg.  FALSE by default.\n\r",mob);
    sprintf(buf, "SYSUPD: Undefined mobproc MIF argument. #%d",GET_MOB_VNUM(mob));
    mudlog(buf, BUG, LEV_IMM, FALSE);
    sprintf(buf, "SYSUPD: Undefined MIF arg: %s",mif_comp);
    mudlog(buf, BUG, LEV_IMM, FALSE);
    goto_next_mcom(mob);
  }

  // add a little flexibility to these... 1/22/98 -jtrhone
  while (*MPROC_CUR(mob) && *MPROC_CUR(mob) != '{')
    MPROC_CUR(mob)++;

  if (*MPROC_CUR(mob) != '{')
  {
    send_to_char("No block following MIF.\n\r",mob);
    sprintf(buf, "SYSUPD: MIF with no following block. #%d",GET_MOB_VNUM(mob));
    mudlog(buf, BUG, LEV_IMM, FALSE);
    do_mend(mob, " ");
  }

  // no more string compares, lets use the #s  1/18/98 -jtrhone
  switch (cmd) {
    case 0: 	//tarcharset		
      if (!TARGET_CHAR(mob))
        goto_next_mcom(mob);
      break;

    case 1: 	//!tarcharset
      if (TARGET_CHAR(mob))
        goto_next_mcom(mob);
      break;

    case 2: 	//tarobjset
      if (!TARGET_OBJ(mob))
        goto_next_mcom(mob);
      break;

    case 3: 	//!tarobjset
      if (TARGET_OBJ(mob))
        goto_next_mcom(mob);
      break;

    case 4: 	//tarcharroom
      if (!TARGET_CHAR(mob) || !get_char_room_vis(mob, TARGET_CHAR(mob)))
        goto_next_mcom(mob);
      break;

    case 5: 	//!tarcharroom
      if (TARGET_CHAR(mob) && get_char_room_vis(mob, TARGET_CHAR(mob)))
        goto_next_mcom(mob);
      break;

    case 6: 	//tarobjroom 
      if (!TARGET_OBJ(mob) || 
          !get_obj_in_list_vis(mob, TARGET_OBJ(mob), world[mob->in_room].contents))
        goto_next_mcom(mob);
      break;

    case 7: 	//!tarobjroom
      if (TARGET_OBJ(mob) && 
          get_obj_in_list_vis(mob, TARGET_OBJ(mob), world[mob->in_room].contents))
        goto_next_mcom(mob);
      break;

    case 8: 	//tarcharzone
      if (!TARGET_CHAR(mob) || !get_char_zone_vis(mob, TARGET_CHAR(mob)))
        goto_next_mcom(mob);
      break;

    case 9: 	//!tarcharzone
      if (TARGET_CHAR(mob) && get_char_zone_vis(mob, TARGET_CHAR(mob)))
        goto_next_mcom(mob);
      break;

    case 10: 	//tarobjzone
      if (!TARGET_OBJ(mob) || !get_obj_zone_vis(mob, TARGET_OBJ(mob)))
        goto_next_mcom(mob);
      break;

    case 11: 	//!tarobjzone
      if (TARGET_OBJ(mob) && get_obj_zone_vis(mob, TARGET_OBJ(mob)))
        goto_next_mcom(mob);
      break;

    case 12: 	//tarcharpc
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      break;

    case 13: 	//!tarcharpc
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || IS_PC(ch))
        goto_next_mcom(mob);
      break;

    case 14: 	//tarcharcompquest
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || !VALID_QUEST(vroom))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid quest# check.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }
     
      // if is no target, or isnt visible, or isnt pc, or hasnt completed quest, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (!COMPLETED_QUEST(ch, vroom))
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 15: 	//!tarcharcompquest
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || !VALID_QUEST(vroom))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid quest# check.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }
    
      // if is no target, or isnt visible, or isnt pc, or has completed quest, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (COMPLETED_QUEST(ch, vroom))
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 16: 	//tarcharlevel 
      ch = NULL;
      if (TARGET_CHAR(mob))
      {
        if (!(ch = get_char_room_vis(mob, TARGET_CHAR(mob))))
          if (!(ch = get_char_zone_vis(mob, TARGET_CHAR(mob))))
            if (!(ch = get_char_vis(mob, TARGET_CHAR(mob))))
            {
              goto_next_mcom(mob);
              return MP_MIF;
            }
      }
      else
      {
        goto_next_mcom(mob);
        return MP_MIF;
      }

      half_chop(comp_args, arg1, arg2);
      if (!*arg1 || !*arg2) 
      {
        sprintf(buf, "SYSERR: mobproc mob #%d unknown tarcharlevel args.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }

      if (!is_number(arg2) || (i = atoi(arg2)) > LEV_IMPL || i <= 0)
      {
        sprintf(buf, "SYSERR: mproc tarcharlevel invalid level arg, mob #%d.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }

      found = FALSE; 
      switch (*arg1) {
       case '=':                // is equal to
        found = (GET_LEVEL(ch) == i);
        break;
       case '!':                // is != to 
        found = (GET_LEVEL(ch) != i);
        break;
       case '<':                // is less than equal to, or is less than
        if (*(arg1+1) == '=')
          found = (GET_LEVEL(ch) <= i);
        else
          found = (GET_LEVEL(ch) < i);
        break;
       case '>':                // is greater than equal to, or is greater than
        if (*(arg1+1) == '=')
          found = (GET_LEVEL(ch) > i);
        else
          found = (GET_LEVEL(ch) >= i);
        break;
       default:
        sprintf(buf, "SYSERR: Unknown mobproc comparison, mob #%d.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }                             

      if (!found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 17: 	//inroom
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_room(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid inroom arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }
      if (mob->in_room != rroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 18: 	//!inroom
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_room(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid inroom arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }
      if (mob->in_room == rroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 19: 	//npcinroom
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_mobile(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid npcinroom arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }
      for (found = FALSE, ch = world[mob->in_room].people; ch && !found; ch = ch->next_in_room)
        if (IS_NPC(ch) && GET_MOB_VNUM(ch) == vroom)
          found = TRUE;
      if (!found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 20: 	//!npcinroom
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_mobile(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid npcinroom arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }
      for (found = FALSE, ch = world[mob->in_room].people; ch && !found; ch = ch->next_in_room)
       if (IS_NPC(ch) && GET_MOB_VNUM(ch) == vroom)
          found = TRUE;
      if (found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 21: 	//objinroom
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_object(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid objinroom arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }
      for (found = FALSE, obj = world[mob->in_room].contents; obj && !found; obj = obj->next_content)
        if (GET_OBJ_VNUM(obj) == vroom)
          found = TRUE;
      if (!found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 22: 	//!objinroom
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_object(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid objinroom arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }
      for (found = FALSE, obj = world[mob->in_room].contents; obj && !found; obj = obj->next_content)
        if (GET_OBJ_VNUM(obj) == vroom)
          found = TRUE;
      if (found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 23: 	//objinv
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_object(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid objinv arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        goto_next_mcom(mob);
        return MP_NORMAL;
      }
      for (found = FALSE, obj = mob->carrying; obj && !found; obj = obj->next_content)
        if (GET_OBJ_VNUM(obj) == vroom)
          found = TRUE;
      if (!found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 24: 	//!objinv
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_object(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid objinv arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        goto_next_mcom(mob);
        return MP_NORMAL;
      }
      for (found = FALSE, obj = mob->carrying; obj && !found; obj = obj->next_content)
        if (GET_OBJ_VNUM(obj) == vroom)
          found = TRUE;
      if (found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 25: 	//objeq
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_object(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid objeq arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        goto_next_mcom(mob);
        return MP_NORMAL;
      }
      for (found = FALSE, i = 0; i < MAX_WEAR && !found; i++)
        if (EQ(mob, i) && GET_OBJ_VNUM(EQ(mob, i)) == vroom)
          found = TRUE;
      if (!found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 26: 	//!objeq
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || ((rroom = real_object(vroom)) <= 0))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid objeq arg.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        goto_next_mcom(mob);
        return MP_NORMAL;
      }
      for (found = FALSE, i = 0; i < MAX_WEAR && !found; i++)
        if (EQ(mob, i) && GET_OBJ_VNUM(EQ(mob, i)) == vroom)
          found = TRUE;
      if (found)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 27: 	//mfighting
      if (!FIGHTING(mob))
        goto_next_mcom(mob);
      break;

    case 28: 	//!mfighting
      if (FIGHTING(mob))
        goto_next_mcom(mob);
      break;

    case 29: 	//transpresent
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid trans arg (%s).", GET_MOB_VNUM(mob), arg1);
        mudlog(buf, BRF, LEV_IMM, FALSE);
        goto_next_mcom(mob);
        return MP_NORMAL;
      }
      if (world[mob->in_room].trans_present != atoi(arg1))
        goto_next_mcom(mob);

      break;

    case 30: 	//!transpresent
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid trans arg (%s).", GET_MOB_VNUM(mob), arg1);
        mudlog(buf, BRF, LEV_IMM, FALSE);
        goto_next_mcom(mob);
        return MP_NORMAL;
      }
      if (world[mob->in_room].trans_present == atoi(arg1))
        goto_next_mcom(mob);

      break;

    case 31: 	//tarcharonquest
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || !VALID_QUEST(vroom))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid quest# check.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }

      // if is no target, or isnt visible, or isnt pc, or isnt doing quest, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (ONQUEST(ch) != vroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 32: 	//!tarcharonquest
      half_chop(comp_args, arg1, arg2);
      if (!is_number(arg1) || (!(vroom = atoi(arg1))) || !VALID_QUEST(vroom))
      {
        sprintf(buf, "SYSERR: mobproc mob #%d invalid quest# check.", GET_MOB_VNUM(mob));
        mudlog(buf, BRF, LEV_IMM, FALSE);
        return MP_NORMAL;
      }

      // if is no target, or isnt visible, or isnt pc, or doing quest, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (ONQUEST(ch) == vroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

      break;

    case 33: 	//tarcharrace <>
      half_chop(comp_args, arg1, arg2);
      if (!*arg1 || (vroom = get_race_by_name(arg1)) <= 0)
      {
         sprintf(buf, "SYSERR: mobproc mob #%d invalid race check.", GET_MOB_VNUM(mob));
         mudlog(buf, BRF, LEV_IMM, FALSE);
         return MP_NORMAL;
      }

      // if is no target, or isnt visible, or isnt pc, or not race, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (GET_RACE(ch) != vroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));
     
      break;

    case 34: 	//!tarcharrace <>
      half_chop(comp_args, arg1, arg2);
      if (!*arg1 || (vroom = get_race_by_name(arg1)) <= 0)
      {
         sprintf(buf, "SYSERR: mobproc mob #%d invalid race check.", GET_MOB_VNUM(mob));
         mudlog(buf, BRF, LEV_IMM, FALSE);
         return MP_NORMAL;
      }

      // if is no target, or isnt visible, or isnt pc, or not race, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (GET_RACE(ch) == vroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));
      break;

    case 35: 	//tarcharclass <>
      half_chop(comp_args, arg1, arg2);
      if (!*arg1 || (vroom = get_class_num(arg1)) <= 0)
      {
         sprintf(buf, "SYSERR: mobproc mob #%d invalid class check.", GET_MOB_VNUM(mob));
         mudlog(buf, BRF, LEV_IMM, FALSE);
         return MP_NORMAL;
      }

      // if is no target, or isnt visible, or isnt pc, or not class, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (GET_CLASS(ch) != vroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));
     
      break;

    case 36: 	//!tarcharclass <>
      half_chop(comp_args, arg1, arg2);
      if (!*arg1 || (vroom = get_class_num(arg1)) <= 0)
      {
         sprintf(buf, "SYSERR: mobproc mob #%d invalid class check.", GET_MOB_VNUM(mob));
         mudlog(buf, BRF, LEV_IMM, FALSE);
         return MP_NORMAL;
      }

      // if is no target, or isnt visible, or isnt pc, or not class, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (GET_CLASS(ch) == vroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));
      break;

    case 37: 	//tarchargender <>
      half_chop(comp_args, arg1, arg2);
      if (!*arg1 || (vroom = search_block(arg1, genders, FALSE)) <= 0)
      {
         sprintf(buf, "SYSERR: mobproc mob #%d invalid gender check.", GET_MOB_VNUM(mob));
         mudlog(buf, BRF, LEV_IMM, FALSE);
         return MP_NORMAL;
      }

      // if is no target, or isnt visible, or isnt pc, or not class, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (GET_SEX(ch) != vroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));
     
      break;

    case 38: 	//!tarchargender <>
      half_chop(comp_args, arg1, arg2);
      if (!*arg1 || (vroom = search_block(arg1, genders, FALSE)) <= 0)
      {
         sprintf(buf, "SYSERR: mobproc mob #%d invalid gender check.", GET_MOB_VNUM(mob));
         mudlog(buf, BRF, LEV_IMM, FALSE);
         return MP_NORMAL;
      }

      // if is no target, or isnt visible, or isnt pc, or not class, skip block
      if (!TARGET_CHAR(mob) || !(ch = get_char_room_vis(mob, TARGET_CHAR(mob))) || !IS_PC(ch))
        goto_next_mcom(mob);
      else
      if (GET_SEX(ch) == vroom)
        goto_next_mcom(mob);
      else
        MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));
     
      break;

    default:
      MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));
      break;
  }

  return MP_MIF;
}

/* manually set the mobs target strings */
int do_mtarget_set(chdata *mob, char *comm)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];

  half_chop(comm, arg1, arg2);
  if (is_abbrev(arg1, "char") && *arg2)
  {
    if(TARGET_CHAR(mob))
      free_log(TARGET_CHAR(mob), "do_mtargetset 1");
    TARGET_CHAR(mob) = str_dup(arg2);
  }
  else
  if (is_abbrev(arg1, "obj") && *arg2)
  {
    if(TARGET_OBJ(mob))
      free_log(TARGET_OBJ(mob), "do_mtargetset 2");
    TARGET_OBJ(mob) = str_dup(arg2);
  }
  else
  {
    sprintf(buf, "SYSERR: mobproc (mob #%d) invalid mtargetset command.", GET_MOB_VNUM(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return MP_NORMAL;
  }
  return MP_MTARGET;
}

// mob teleports much like a immortal goto
int do_mtele_zone(chdata *mob, char *comm)
{
  char arg1[MAX_INPUT_LENGTH];
  int location;
  extern int find_target_room(chdata *ch, char *arg);

  one_argument(comm, arg1);

  if ((location = find_target_room(mob, arg1)) < 0)
     return MP_NORMAL;

  /* roa jtrhone */
  if (location == mob->in_room)
  { 
     send_to_char("You're already where you want to go.\n\r",mob);
     return MP_NORMAL;
  } 

  act("$n is engulfed by a swirling cloud.",TRUE,mob,0,0,TO_ROOM);
  char_from_room(mob);
  char_to_room(mob, location);
  act("$n arrives amidst a swirling cloud.",TRUE,mob,0,0,TO_ROOM);
  do_look_at_room(mob, 0, 0);
  return MP_NORMAL;
}

// similar to track and move, hunt tracks target and moves in its direction
int do_mhunt(chdata *mob, char *comm)
{
  char arg1[MAX_INPUT_LENGTH];
  chdata *vict; 
  int dir;
  extern int find_first_step(int src, int trg);

  one_argument(comm, arg1);

  if (!(vict = get_char_zone_vis(mob, arg1)))
  {
    send_to_char("Mhunt target not found in zone, ignoring hunt.\n\r",mob);
    return MP_NORMAL;
  }

  dir = find_first_step(mob->in_room, vict->in_room);
  if (dir < 0)
  {
    send_to_char("Mhunt cancelled.  No path or already there.\n\r",mob);
    return MP_NORMAL;
  }

  if (!EXIT(mob, dir))
  {
    send_to_char("Mhunt fatal error, exit doesnt exist.\n\r",mob);
    return MP_NORMAL;
  }

  if (ROOM_FLAGGED(EXIT(mob, dir)->to_room, DEATH) ||
      ROOM_FLAGGED(EXIT(mob, dir)->to_room, FLY_DEATH))
  {
    send_to_char("Mhunt would lead to DT.  Hunt ignored.\n\r",mob);
    return MP_NORMAL;
  }

  do_move(mob, "", dir+1, 0);
  return MP_NORMAL;
}

// execute and return true if comm is a mobproc command
int mobproc_interpreter(chdata *mob, char *comm, chdata *ch, obdata *ob)
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  int i;

  if (!SPC_FLAGGED(mob, SPC_MOBPROC)) 
  {
    if (!sub_and_execute(mob, comm, ch, ob))
      command_interpreter(mob, comm);
    return MP_NORMAL;
  }

  half_chop(comm, arg1, arg2);
  for (i = 0; *mproc_calls[i].arg; i++)
    if (is_abbrev(arg1, mproc_calls[i].arg))
    {
      do_mobproc_subs(mob, arg2, ch, ob);

      // ok, call appropriate function with this command
      return ((*mproc_calls[i].mproc_fn) (mob, arg2));
    }

  if (!sub_and_execute(mob, comm, ch, ob))
    command_interpreter(mob, comm);

  return MP_NORMAL;
}

// lets do some bounds checking just to make sure -jtrhone
BOOL check_mproc_bounds(chdata *mob)
{
  if (MPROC_CUR(mob) < MPROC_BEG(mob))
  {
    sprintf(buf, "SYSERR: mobproc #%d %%B%%1FAILED%%0 lower bounds check.",
	    GET_MOB_VNUM(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return FALSE;
  }

  if (MPROC_CUR(mob) > (MPROC_BEG(mob) + strlen(MPROC_BEG(mob))))
  {
    sprintf(buf, "SYSERR: mobproc #%d %%B%%1FAILED%%0 upper bounds check.",
	    GET_MOB_VNUM(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return FALSE;
  }

  return TRUE;
}

/* do a preset builder mob special procedure, step thru the string5
   portion of mob->npc_specials   -RoA jtrhone */
/* With specific mobproc commands such as mrandom, mtarget */

// Main mobproc interface begins here
// this is called from mobact.c in mobile_activity()
void do_mob_mobproc(chdata *mob)
{
  int i; 
  int current_command, command_count;
  char *tmp;
  char comm[MAX_INPUT_LENGTH+2];

  if (IS_PC(mob)) return;

  if (!MPROC_BEG(mob) || !*MPROC_BEG(mob))
  {
    sprintf(buf, "SYSERR: mobproc #%d missing instruction set.",GET_MOB_VNUM(mob));
    mudlog(buf, BRF, LEV_IMM, FALSE);
    return;
  }

  /* check wait states, to see if loop or waiting or finished */
  if (mob->npc_specials.mob_wait <= -1) return;  /* finished */
  else
  if (mob->npc_specials.mob_wait > 0) 
  {
    mob->npc_specials.mob_wait--;
    return;  /* waiting for now, see ya */
  }
  else
  if (TRIGS(mob) && TRIG_WAITING(mob))	// waiting for trigger
    return;

  current_command = MP_MIF;
  command_count   = 1;
  while ((current_command == MP_MIF || current_command == MP_MTARGET ||
          current_command == MP_MTAG || current_command == MP_MTRIG) && command_count < 5)
  {
    if (!check_mproc_bounds(mob))
      return;

    // skip leading white space, allow for nifty look mobproc code
    skip_spaces(&MPROC_CUR(mob));

    // if we're pointing at close brackets, just skip em
    while ( *MPROC_CUR(mob) && *MPROC_CUR(mob) == '}' )
      MPROC_CUR(mob) = scan_past_eol(MPROC_CUR(mob));

    // if null, reset to beginning
    if (!MPROC_CUR(mob) || !*MPROC_CUR(mob))
      MPROC_CUR(mob) = MPROC_BEG(mob);

    // get length of next command
    for (i=0, tmp = MPROC_CUR(mob); *tmp && !ISNEWL(*tmp) && (i < MAX_INPUT_LENGTH); tmp++, i++)
     ;

    /* copy this command line into comm */
    strncpy(comm, MPROC_CUR(mob), i);
    comm[i] = '\0';

    while (*tmp && ISNEWL(*tmp))
      tmp++;

    // reset back to beginning to mproc command list
    if (!*tmp)
      tmp = MPROC_BEG(mob);
    MPROC_CUR(mob) = tmp;

    // show the mob what they typing
    send_to_char(comm, mob);
    send_to_char("\n\r",mob);

    // send it onto mobproc processing
    current_command = mobproc_interpreter(mob, comm, NULL, NULL);

    // prevent infinity -roa
    command_count++;
  }
}

/* 
   following is for REACTOR mobs, react to certain triggers typed by
   players.... -RoA

   10/8/96 -jtr 
     streamlined this code, now recognizes newlines as a command
     terminator as well as the old * terminator
*/
void perform_reaction(chdata *mob, char *str, chdata *ch, obdata *ob, BOOL mobproc)
{
  int num=1, i;
  char comm[MAX_MOB_REACTION_LENGTH];
  char *tmp;

  if (ch && IS_NPC(ch)) return;

  if (!str || !*str) return;

  if (!mobproc)
    for (tmp = str, *comm = '\0'; tmp && *tmp; tmp++)
      if (*tmp == '\n' || *tmp == '*') 
        num++;   /* count the subcommands in string */
   
  if ((num > MAX_MOB_REACTIONS) || (strlen(str) > MAX_MOB_REACTION_LENGTH))
  {
    sprintf(buf, "SYSUPD: mreaction failed - %d reacs, length %d", num, strlen(str));
    mudlog(buf, BUG, LEV_IMM, TRUE);
    return;  
  }

  /* parse the string using * as pseudo EOL along with ISNEWL */
  for (tmp = str, i=0; tmp && *tmp; tmp++)
    if (*tmp && !ISNEWL(*tmp) && *tmp != '*')
	comm[i++] = *tmp;
    else
    {
        comm[i] = '\0';
        if (*comm)
	  mobproc_interpreter(mob, comm, ch, ob);
        *comm = '\0';
        i = 0;
    }

  comm[i] = '\0';
  if (*comm)
    mobproc_interpreter(mob, comm, ch, ob);
}

/* this checks to see if trigger is one of a possible 5 triggers
   chosen by builder and seperated by * character, or newline (10/6/96) 

   11/9/96 -roa
     added substring comparison, if the # is first character in trigger
*/
int in_trigger(char *trigger, char *string)
{
  char *trig;
  int num, i;
  char comm[MAX_MOB_REACTION_LENGTH];
  extern int match(char *str, char *str2);

  if (!string || !trigger || !*trigger || !*string ) return FALSE;
  else num = 1;

  for (trig = trigger; *trig; trig++)
    if (*trig == '\n' || *trig == '*') 
      num++;   /* count the subcommands in string */
   
  if ((num > MAX_MOB_REACTIONS) || (strlen(trig) > MAX_MOB_REACTION_LENGTH))
  {
    sprintf(buf, "SYSUPD: mtrigger failed - %d trigs, length %d", num, strlen(trig));
    mudlog(buf, BUG, LEV_IMM, TRUE);
    return FALSE; 
  }

  /* parse the trigger using * as pseudo EOL character */
  for (trig = trigger, i = 0; *trig; trig++)
    if (*trig && !ISNEWL(*trig) && *trig != '*')
    {
        comm[i] = *trig;
	i++;
    }
    else
    {
        comm[i] = '\0';
	if (*comm && *comm != '\n')
   	{
     	  if (*comm == '#')  // the pound indicates a SUBSTRING match(strstr)
	  {
	    if (*(comm + 1) && match((comm + 1), string))
	      return TRUE;
     	  }
     	  else
     	  if (!str_cmp(comm, string))
	    return TRUE;
	}
        *comm = '\0';
        i = 0;
    }

  /* now last subtrigger check */
  comm[i] = '\0';
  if (*comm && *comm != '\n')
  {
    if (*comm == '#')  // the pound indicates a SUBSTRING match (strstr)
    {
	if (*(comm + 1) && match((comm + 1), string))
	  return TRUE;
    }
    else
    if (!str_cmp(comm, string))
	return TRUE;
  }
  return FALSE;  /* we didnt find any matches */
}

/* does character's command trigger a mobile command? if so do it */
int check_reaction(chdata *ch, char *str)
{
  static chdata *mob;
  char tmp[MAX_INPUT_LENGTH];
  char *ptr;
  struct mtrig_type *mt;

  if (INVALID_ROOM(ch->in_room))
    return FALSE;

  for(mob = world[ch->in_room].people; mob ; mob = mob->next_in_room)
    if (SPC_FLAGGED(mob, SPC_REACTOR) ||
	(SPC_FLAGGED(mob, SPC_MOBPROC) && TRIGS(mob)))
      break;  /* ding ding ding, we got a winner */
    
  if (!mob || !str || !*str)
    return FALSE;  /* sorry pal, try again next time */

  // lower case entire string arg, cpy to temp buf first
  strcpy(tmp, str);
  for (ptr = tmp; *ptr; ptr++)
   if (isalpha(*ptr))
    *ptr = LOWER(*ptr);

  if (SPC_FLAGGED(mob, SPC_REACTOR))
  {
    if (MSTR1(mob) && in_trigger(MSTR1(mob), tmp))
    {
      perform_reaction(mob, MSTR2(mob), ch, NULL, FALSE);
      return TRUE;
    }
    else
    if (MSTR3(mob) && in_trigger(MSTR3(mob), tmp))
    {
      perform_reaction(mob, MSTR4(mob), ch, NULL, FALSE);
      return TRUE;
    }
  }

  // added mobproc trigger list capabilities 3/22/97 -jtr
  if (!TRIG_IGNORE(mob))
   for (mt = TRIGS(mob); mt; mt = mt->next)
    if (in_trigger(mt->trigger, tmp))
    {
      perform_reaction(mob, mt->reaction, ch, NULL, TRUE);

      // set new char target to the one who triggered us
      FREENULL(TARGET_CHAR(mob));
      TARGET_CHAR(mob) = STR_DUP(GET_NAME(ch));
      TRIG_WAITING(mob) = FALSE;
      send_to_char("Reaction triggered.  Trigwait removed.\n\r",mob);
      return TRUE;
    }

  return FALSE;
}

// The following is for RAND mobs, random messages semi emoted by mobs.
// NOTE: This type of mob is obsolete as a result of mrandom mobproc... 
//       Keep function around for legacy mobs.  -roa
void do_mob_random(chdata *mob)
{
  int room = mob->in_room;

  if (room == NOWHERE) return;

  switch (number(1,5)) 
  {
    case 1:
      if (MSTR1(mob) && *MSTR1(mob) != '&' && number(0,1))
	perform_reaction(mob, MSTR1(mob), NULL, NULL, FALSE);
      break;
    case 2:
      if (MSTR2(mob) && *MSTR2(mob) != '&' && number(0,1))
	perform_reaction(mob, MSTR2(mob), NULL, NULL, FALSE);
      break;
    case 3:
      if (MSTR3(mob) && *MSTR3(mob) != '&' && number(0,1))
	perform_reaction(mob, MSTR3(mob), NULL, NULL, FALSE);
      break;
    case 4:
      if (MSTR4(mob) && *MSTR4(mob) != '&' && number(0,1))
	perform_reaction(mob, MSTR4(mob), NULL, NULL, FALSE);
      break;
    case 5:
      if (MSTR5(mob) && *MSTR5(mob) != '&' && number(0,1))
	perform_reaction(mob, MSTR5(mob), NULL, NULL, FALSE);
      break;
    default: 
      break;
  }
}