/***************************************************************************
 * MOBProgram ported for CircleMUD 3.0 by Mattias Larsson		   *
 * Traveller@AnotherWorld (ml@eniac.campus.luth.se 4000) 		   *
 **************************************************************************/

/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
 *  The MOBprograms have been contributed by N'Atas-ha.  Any support for   *
 *  these routines should not be expected from Merc Industries.  However,  *
 *  under no circumstances should the blame for bugs, etc be placed on     *
 *  Merc Industries.  They are not guaranteed to work on all systems due   *
 *  to their frequent use of strxxx functions.  They are also not the most *
 *  efficient way to perform their tasks, but hopefully should be in the   *
 *  easiest possible way to install and begin using. Documentation for     *
 *  such installation can be found in INSTALL.  Enjoy...         N'Atas-Ha *
 ***************************************************************************/

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "structs.h"
#include "utils.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"

char buf2[MAX_STRING_LENGTH];

extern struct index_data *mob_index;
extern struct index_data *obj_index;
extern struct room_data *world;

extern void death_cry (struct char_data *ch);
extern bool str_prefix (const char *astr, const char *bstr);
extern int number_percent(void);
extern int number_range(int from, int to);

#define bug(x, y) { sprintf(buf2, (x), (y)); log(buf2); }


/*
 * Local function prototypes
 */

char *	mprog_next_command	(char* clist);
int     mprog_seval		(char *lhs, char *opr, char *rhs);
int	mprog_veval		(int lhs, char* opr, int rhs);
int	mprog_do_ifchck		(char* ifchck, struct char_data* mob,
				       struct char_data* actor, struct obj_data* obj,
				       void* vo, struct char_data* rndm);
char *	mprog_process_if	(char* ifchck, char* com_list, 
				       struct char_data* mob, struct char_data* actor,
				       struct obj_data* obj, void* vo,
				       struct char_data* rndm);
void	mprog_translate		(char ch, char* t, struct char_data* mob,
				       struct char_data* actor, struct obj_data* obj,
				       void* vo, struct char_data* rndm);
void	mprog_process_cmnd	(char* cmnd, struct char_data* mob, 
				       struct char_data* actor, struct obj_data* obj,
				       void* vo, struct char_data* rndm);
void	mprog_driver		(char* com_list, struct char_data* mob,
				       struct char_data* actor, struct obj_data* obj,
				       void* vo);

/***************************************************************************
 * Local function code and brief comments.
 */

/* Used to get sequential lines of a multi line string (separated by "\n\r")
 * Thus its like one_argument(), but a trifle different. It is destructive
 * to the multi line string argument, and thus clist must not be shared.
 */

char *mprog_next_command(char *clist)
{

  char *pointer = clist;

  if (*pointer == '\r')
    pointer++;  
  if (*pointer == '\n')
    pointer++;

  while (*pointer != '\n' && *pointer != '\0' && *pointer != '\r')
    pointer++;
  if (*pointer == '\n') {
    *pointer = '\0';
    pointer++; }
  if (*pointer == '\r') {
    *pointer = '\0';
    pointer++; }
    
  return (pointer);

}

/* we need str_infix here because strstr is not case insensitive */

bool str_infix(const char *astr, const char *bstr)
{
    int sstr1;
    int sstr2;
    int ichar;
    char c0;

    if ((c0 = LOWER(astr[0])) == '\0')
        return FALSE;

    sstr1 = strlen(astr);
    sstr2 = strlen(bstr);

    for (ichar = 0; ichar <= sstr2 - sstr1; ichar++) {
        if (c0 == LOWER(bstr[ichar]) && !str_prefix(astr, bstr + ichar))
            return FALSE;
    }

    return TRUE;
}

/* These two functions do the basic evaluation of ifcheck operators.
 *  It is important to note that the string operations are not what
 *  you probably expect.  Equality is exact and division is substring.
 *  remember that lhs has been stripped of leading space, but can
 *  still have trailing spaces so be careful when editing since:
 *  "guard" and "guard " are not equal.
 */
int mprog_seval(char *lhs, char *opr, char *rhs)
{

  if (!str_cmp(opr, "=="))
    return (!str_cmp(lhs, rhs));
  if (!str_cmp(opr, "!="))
    return (str_cmp(lhs, rhs));
  if (!str_cmp(opr, "/"))
    return (!str_infix(rhs, lhs));
  if (!str_cmp(opr, "!/"))
    return (str_infix(rhs, lhs));

  log("Improper MOBprog operator");
  return 0;

}

int mprog_veval(int lhs, char *opr, int rhs)
{

  if (!str_cmp(opr, "=="))
    return (lhs == rhs);
  if (!str_cmp(opr, "!="))
    return (lhs != rhs);
  if (!str_cmp(opr, ">"))
    return (lhs > rhs);
  if (!str_cmp(opr, "<"))
    return (lhs < rhs);
  if (!str_cmp(opr, "<="))
    return (lhs <= rhs);
  if (!str_cmp(opr, ">="))
    return (lhs >= rhs);
  if (!str_cmp(opr, "&"))
    return (lhs & rhs);
  if (!str_cmp(opr, "|"))
    return (lhs | rhs);

  log("Improper MOBprog operator");
  return 0;

}

/* This function performs the evaluation of the if checks.  It is
 * here that you can add any ifchecks which you so desire. Hopefully
 * it is clear from what follows how one would go about adding your
 * own. The syntax for an if check is: ifchck (arg) [opr val]
 * where the parenthesis are required and the opr and val fields are
 * optional but if one is there then both must be. The spaces are all
 * optional. The evaluation of the opr expressions is farmed out
 * to reduce the redundancy of the mammoth if statement list.
 * If there are errors, then return -1 otherwise return boolean 1,0
 */
int mprog_do_ifchck(char *ifchck, struct char_data *mob, struct char_data *actor,
		     struct obj_data *obj, void *vo, struct char_data *rndm)
{

  char buf[MAX_INPUT_LENGTH];
  char arg[MAX_INPUT_LENGTH];
  char opr[MAX_INPUT_LENGTH];
  char val[MAX_INPUT_LENGTH];
  struct char_data *vict = (struct char_data *) vo;
  struct obj_data *v_obj = (struct obj_data  *) vo;
  char     *bufpt = buf;
  char     *argpt = arg;
  char     *oprpt = opr;
  char     *valpt = val;
  char     *point = ifchck;
  int       lhsvl;
  int       rhsvl;

  if (*point == '\0') 
    {
      bug ("Mob: %d null ifchck", (int)mob_index[mob->nr].virtual); 
      return -1;
    }   
  /* skip leading spaces */
  while (*point == ' ')
    point++;

  /* get whatever comes before the left paren.. ignore spaces */
  while (*point != '(') 
    if (*point == '\0') 
      {
	bug ("Mob: %d ifchck syntax error", mob_index[mob->nr].virtual); 
	return -1;
      }   
    else
      if (*point == ' ')
	point++;
      else 
	*bufpt++ = *point++; 

  *bufpt = '\0';
  point++;

  /* get whatever is in between the parens.. ignore spaces */
  while (*point != ')') 
    if (*point == '\0') 
      {
	bug ("Mob: %d ifchck syntax error", mob_index[mob->nr].virtual); 
	return -1;
      }   
    else
      if (*point == ' ')
	point++;
      else 
	*argpt++ = *point++; 

  *argpt = '\0';
  point++;

  /* check to see if there is an operator */
  while (*point == ' ')
    point++;
  if (*point == '\0') 
    {
      *opr = '\0';
      *val = '\0';
    }   
  else /* there should be an operator and value, so get them */
    {
      while ((*point != ' ') && (!isalnum(*point))) 
	if (*point == '\0') 
	  {
	    bug ("Mob: %d ifchck operator without value",
		 mob_index[mob->nr].virtual); 
	    return -1;
	  }
	else
	  *oprpt++ = *point++; 

      *oprpt = '\0';
 
      /* finished with operator, skip spaces and then get the value */
      while (*point == ' ')
	point++;
      for(;;)
	{
	  if ((*point != ' ') && (*point == '\0')) 
	    break;
	  else
	    *valpt++ = *point++; 
	}

      *valpt = '\0';
    }
  bufpt = buf;
  argpt = arg;
  oprpt = opr;
  valpt = val;

  /* Ok... now buf contains the ifchck, arg contains the inside of the
   *  parentheses, opr contains an operator if one is present, and val
   *  has the value if an operator was present.
   *  So.. basically use if statements and run over all known ifchecks
   *  Once inside, use the argument and expand the lhs. Then if need be
   *  send the lhs,opr,rhs off to be evaluated.
   */

  if (!str_cmp(buf, "rand"))
    {
      return (number(0,100) <= atoi(arg));
    }

  if (!str_cmp(buf, "ispc"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return 0;
	case 'n': if (actor)
 	             return (!IS_NPC(actor));
	          else return -1;
	case 't': if (vict)
                     return (!IS_NPC(vict));
	          else return -1;
	case 'r': if (rndm) 
                     return (!IS_NPC(rndm));
	          else return -1;
	default:
	  bug ("Mob: %d bad argument to 'ispc'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "isnpc"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return 1;
	case 'n': if (actor)
	             return IS_NPC(actor);
	          else return -1;
	case 't': if (vict)
                     return IS_NPC(vict);
	          else return -1;
	case 'r': if (rndm)
	             return IS_NPC(rndm);
	          else return -1;
	default:
	  bug ("Mob: %d bad argument to 'isnpc'",
               mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "isgood"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return IS_GOOD(mob);
	case 'n': if (actor) 
	          return IS_GOOD(actor);
	          else { return -1; }
	case 't': if (vict)
	             return IS_GOOD(vict);
	          else return -1;
	case 'r': if (rndm)
	             return IS_GOOD(rndm);
	          else return -1;
	default:
	  bug("Mob: %d bad argument to 'isgood'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "isfight"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return (FIGHTING(mob)) ? 1 : 0;
	case 'n': if (actor)
	             return(FIGHTING(actor)) ? 1 : 0;
	          else return -1;
	case 't': if (vict)
	             return (FIGHTING(vict)) ? 1 : 0;
	          else return -1;
	case 'r': if (rndm)
	             return (FIGHTING(rndm)) ? 1 : 0;
	          else return -1;
	default:
	  bug ("Mob: %d bad argument to 'isfight'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "isimmort"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return (GET_LEVEL(mob) > LVL_IMMORT);
	case 'n': if (actor)
	             return (GET_LEVEL(actor) > LVL_IMMORT);
  	          else return -1;
	case 't': if (vict)
	             return (GET_LEVEL(vict) > LVL_IMMORT);
                  else return -1;
	case 'r': if (rndm)
	             return (GET_LEVEL(rndm) > LVL_IMMORT);
                  else return -1;
	default:
	  bug ("Mob: %d bad argument to 'isimmort'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "ischarmed"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return IS_AFFECTED(mob, AFF_CHARM);
	case 'n': if (actor)
	             return IS_AFFECTED(actor, AFF_CHARM);
	          else return -1;
	case 't': if (vict)
	             return IS_AFFECTED(vict, AFF_CHARM);
	          else return -1;
	case 'r': if (rndm)
	             return IS_AFFECTED(rndm, AFF_CHARM);
	          else return -1;
	default:
	  bug ("Mob: %d bad argument to 'ischarmed'",
	       mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "isfollow"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return (mob->master != NULL
			  && mob->master->in_room == mob->in_room);
	case 'n': if (actor)
	             return (actor->master != NULL
			     && actor->master->in_room == actor->in_room);
	          else return -1;
	case 't': if (vict)
	             return (vict->master != NULL
			     && vict->master->in_room == vict->in_room);
	          else return -1;
	case 'r': if (rndm)
	             return (rndm->master != NULL
			     && rndm->master->in_room == rndm->in_room);
	          else return -1;
	default:
	  bug ("Mob: %d bad argument to 'isfollow'", 
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "isaffected"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return (mob->char_specials.saved.affected_by & atoi(arg));
	case 'n': if (actor)
	             return (actor->char_specials.saved.affected_by & atoi(arg));
	          else return -1;
	case 't': if (vict)
	             return (vict->char_specials.saved.affected_by & atoi(arg));
	          else return -1;
	case 'r': if (rndm)
	             return (rndm->char_specials.saved.affected_by & atoi(arg));
	          else return -1;
	default:
	  bug ("Mob: %d bad argument to 'isaffected'",
	       mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "hitprcnt"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': lhsvl = mob->points.hit / mob->points.max_hit;
	          rhsvl = atoi(val);
         	  return mprog_veval(lhsvl, opr, rhsvl);
	case 'n': if (actor)
	          {
		    lhsvl = actor->points.hit / actor->points.max_hit;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 't': if (vict)
	          {
		    lhsvl = vict->points.hit / vict->points.max_hit;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'r': if (rndm)
	          {
		    lhsvl = rndm->points.hit / rndm->points.max_hit;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'hitprcnt'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "inroom"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': lhsvl = mob->in_room;
	          rhsvl = real_room(atoi(val));
	          return mprog_veval(lhsvl, opr, rhsvl);
	case 'n': if (actor)
	          {
		    lhsvl = actor->in_room;
		    rhsvl = real_room(atoi(val));
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 't': if (vict)
	          {
		    lhsvl = vict->in_room;
		    rhsvl = real_room(atoi(val));
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'r': if (rndm)
	          {
		    lhsvl = rndm->in_room;
		    rhsvl = real_room(atoi(val));
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'inroom'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "sex"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': lhsvl = mob->player.sex;
	          rhsvl = atoi(val);
	          return mprog_veval(lhsvl, opr, rhsvl);
	case 'n': if (actor)
	          {
		    lhsvl = actor->player.sex;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 't': if (vict)
	          {
		    lhsvl = vict->player.sex;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'r': if (rndm)
	          {
		    lhsvl = rndm->player.sex;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'sex'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "position"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': lhsvl = mob->char_specials.position;
	          rhsvl = atoi(val);
	          return mprog_veval(lhsvl, opr, rhsvl);
	case 'n': if (actor)
	          {
		    lhsvl = actor->char_specials.position;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 't': if (vict)
	          {
		    lhsvl = vict->char_specials.position;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'r': if (rndm)
	          {
		    lhsvl = rndm->char_specials.position;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'position'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "level"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': lhsvl = GET_LEVEL(mob);
	          rhsvl = atoi(val);
	          return mprog_veval(lhsvl, opr, rhsvl);
	case 'n': if (actor)
	          {
		    lhsvl = GET_LEVEL(actor);
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else 
		    return -1;
	case 't': if (vict)
	          {
		    lhsvl = GET_LEVEL(vict);
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'r': if (rndm)
	          {
		    lhsvl = GET_LEVEL(rndm);
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'level'",
                mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "class"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': lhsvl = mob->player.class;
	          rhsvl = atoi(val);
                  return mprog_veval(lhsvl, opr, rhsvl);
	case 'n': if (actor)
	          {
		    lhsvl = actor->player.class;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else 
		    return -1;
	case 't': if (vict)
	          {
		    lhsvl = vict->player.class;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'r': if (rndm)
	          {
		    lhsvl = rndm->player.class;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'class'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "goldamt"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': lhsvl = mob->points.gold;
                  rhsvl = atoi(val);
                  return mprog_veval(lhsvl, opr, rhsvl);
	case 'n': if (actor)
	          {
		    lhsvl = actor->points.gold;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 't': if (vict)
	          {
		    lhsvl = vict->points.gold;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'r': if (rndm)
	          {
		    lhsvl = rndm->points.gold;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'goldamt'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "objtype"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'o': if (obj)
	          {
		    lhsvl = obj->obj_flags.type_flag;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	         else
		   return -1;
	case 'p': if (v_obj)
	          {
		    lhsvl = v_obj->obj_flags.type_flag;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'objtype'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "objval0"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'o': if (obj)
	          {
		    lhsvl = obj->obj_flags.value[0];
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'p': if (v_obj)
	          {
		    lhsvl = v_obj->obj_flags.value[0];
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else 
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'objval0'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "objval1"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'o': if (obj)
	          {
		    lhsvl = obj->obj_flags.value[1];
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'p': if (v_obj)
	          {
		    lhsvl = v_obj->obj_flags.value[1];
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'objval1'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "objval2"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'o': if (obj)
	          {
		    lhsvl = obj->obj_flags.value[2];
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'p': if (v_obj)
	          {
		    lhsvl = v_obj->obj_flags.value[2];
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'objval2'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "objval3"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'o': if (obj)
	          {
		    lhsvl = obj->obj_flags.value[3];
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'p': if (v_obj) 
	          {
		    lhsvl = v_obj->obj_flags.value[3];
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'objval3'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "number"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': lhsvl = mob->points.gold;
	          rhsvl = atoi(val);
	          return mprog_veval(lhsvl, opr, rhsvl);
	case 'n': if (actor)
	          {
		    if IS_NPC(actor)
		    {
		      lhsvl = mob_index[actor->nr].virtual;
		      rhsvl = atoi(val);
		      return mprog_veval(lhsvl, opr, rhsvl);
		    }
		  }
	          else
		    return -1;
	case 't': if (vict)
	          {
		    if IS_NPC(actor)
		    {
		      lhsvl = mob_index[vict->nr].virtual;
		      rhsvl = atoi(val);
		      return mprog_veval(lhsvl, opr, rhsvl);
		    }
		  }
                  else
		    return -1;
	case 'r': if (rndm)
	          {
		    if IS_NPC(actor)
		    {
		      lhsvl = mob_index[rndm->nr].virtual;
		      rhsvl = atoi(val);
		      return mprog_veval(lhsvl, opr, rhsvl);
		    }
		  }
	         else return -1;
	case 'o': if (obj)
	          {
		    lhsvl = obj_index[obj->item_number].virtual;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	case 'p': if (v_obj)
	          {
		    lhsvl = obj_index[v_obj->item_number].virtual;
		    rhsvl = atoi(val);
		    return mprog_veval(lhsvl, opr, rhsvl);
		  }
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'number'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  if (!str_cmp(buf, "name"))
    {
      switch (arg[1])  /* arg should be "$*" so just get the letter */
	{
	case 'i': return mprog_seval(mob->player.name, opr, val);
	case 'n': if (actor)
	            return mprog_seval(actor->player.name, opr, val);
	          else
		    return -1;
	case 't': if (vict)
	            return mprog_seval(vict->player.name, opr, val);
	          else
		    return -1;
	case 'r': if (rndm)
	            return mprog_seval(rndm->player.name, opr, val);
	          else
		    return -1;
	case 'o': if (obj)
	            return mprog_seval(obj->name, opr, val);
	          else
		    return -1;
	case 'p': if (v_obj)
	            return mprog_seval(v_obj->name, opr, val);
	          else
		    return -1;
	default:
	  bug ("Mob: %d bad argument to 'name'", mob_index[mob->nr].virtual); 
	  return -1;
	}
    }

  /* Ok... all the ifchcks are done, so if we didnt find ours then something
   * odd happened.  So report the bug and abort the MOBprogram (return error)
   */
  bug ("Mob: %d unknown ifchck", mob_index[mob->nr].virtual); 
  return -1;

}
/* Quite a long and arduous function, this guy handles the control
 * flow part of MOBprograms.  Basicially once the driver sees an
 * 'if' attention shifts to here.  While many syntax errors are
 * caught, some will still get through due to the handling of break
 * and errors in the same fashion.  The desire to break out of the
 * recursion without catastrophe in the event of a mis-parse was
 * believed to be high. Thus, if an error is found, it is bugged and
 * the parser acts as though a break were issued and just bails out
 * at that point. I havent tested all the possibilites, so I'm speaking
 * in theory, but it is 'guaranteed' to work on syntactically correct
 * MOBprograms, so if the mud crashes here, check the mob carefully!
 */

char null[1];

char *mprog_process_if(char *ifchck, char *com_list, struct char_data *mob,
		       struct char_data *actor, struct obj_data *obj, void *vo,
		       struct char_data *rndm)
{

 char buf[ MAX_INPUT_LENGTH ];
 char *morebuf = '\0';
 char    *cmnd = '\0';
 int loopdone = FALSE;
 int     flag = FALSE;
 int  legal;

 *null = '\0';

 /* check for trueness of the ifcheck */
 if ((legal = mprog_do_ifchck(ifchck, mob, actor, obj, vo, rndm))) 
   if(legal != 0)
     flag = TRUE;
   else
     return null;

 while(loopdone == FALSE) /*scan over any existing or statements */
 {
     cmnd     = com_list;
     com_list = mprog_next_command(com_list);
     while (*cmnd == ' ')
       cmnd++;
     if (*cmnd == '\0')
     {
	 bug ("Mob: %d no commands after IF/OR", mob_index[mob->nr].virtual); 
	 return null;
     }
     morebuf = one_argument(cmnd,buf);
     if (!str_cmp(buf, "or"))
     {
	 if ((legal = mprog_do_ifchck(morebuf,mob,actor,obj,vo,rndm)))
	   if (legal != 0)
	     flag = TRUE;
	   else
	     return null;
     }
     else
       loopdone = TRUE;
 }
 
 if (flag)
   for (; ;) /*ifcheck was true, do commands but ignore else to endif*/ 
   {
       if (!str_cmp(buf, "if"))
       { 
	   com_list = mprog_process_if(morebuf,com_list,mob,actor,obj,vo,rndm);
	   while (*cmnd==' ')
	     cmnd++;
	   if (*com_list == '\0')
	     return null;
	   cmnd     = com_list;
	   com_list = mprog_next_command(com_list);
	   morebuf  = one_argument(cmnd,buf);
	   continue;
       }
       if (!str_cmp(buf, "break"))
	 return null;
       if (!str_cmp(buf, "endif"))
	 return com_list; 
       if (!str_cmp(buf, "else")) 
       {
	   while (str_cmp(buf, "endif")) 
	   {
	       cmnd     = com_list;
	       com_list = mprog_next_command(com_list);
	       while (*cmnd == ' ')
		 cmnd++;
	       if (*cmnd == '\0')
	       {
		   bug ("Mob: %d missing endif after else",
			mob_index[mob->nr].virtual);
		   return null;
	       }
	       morebuf = one_argument(cmnd,buf);
	   }
	   return com_list; 
       }
       mprog_process_cmnd(cmnd, mob, actor, obj, vo, rndm);
       cmnd     = com_list;
       com_list = mprog_next_command(com_list);
       while (*cmnd == ' ')
	 cmnd++;
       if (*cmnd == '\0')
       {
           bug ("Mob: %d missing else or endif", mob_index[mob->nr].virtual); 
           return null;
       }
       morebuf = one_argument(cmnd, buf);
   }
 else /*false ifcheck, find else and do existing commands or quit at endif*/
   {
     while ((str_cmp(buf, "else")) && (str_cmp(buf,"endif")))
       {
	 cmnd     = com_list;
	 com_list = mprog_next_command(com_list);
	 while (*cmnd == ' ')
	   cmnd++;
	 if (*cmnd == '\0')
	   {
	     bug ("Mob: %d missing an else or endif",
		  mob_index[mob->nr].virtual); 
	     return null;
	   }
	 morebuf = one_argument(cmnd, buf);
       }

     /* found either an else or an endif.. act accordingly */
     if (!str_cmp(buf, "endif")) {
       return com_list;
	}
     cmnd     = com_list;
     com_list = mprog_next_command(com_list);
     while (*cmnd == ' ')
       cmnd++;
     if (*cmnd == '\0')
       { 
	 bug ("Mob: %d missing endif", mob_index[mob->nr].virtual); 
	 return null;
       }
     morebuf = one_argument(cmnd, buf);
     
     for (; ;) /*process the post-else commands until an endif is found.*/
       {
	 if (!str_cmp(buf, "if"))
	   { 
	     com_list = mprog_process_if(morebuf, com_list, mob, actor,
					 obj, vo, rndm);
	     while (*cmnd == ' ')
	       cmnd++;
	     if (*com_list == '\0')
	       return null;
	     cmnd     = com_list;
	     com_list = mprog_next_command(com_list);
	     morebuf  = one_argument(cmnd,buf);
	     continue;
	   }
	 if (!str_cmp(buf, "else")) 
	   {
	     bug ("Mob: %d found else in an else section",
		  mob_index[mob->nr].virtual); 
	     return null;
	   }
	 if (!str_cmp(buf, "break"))
	   return null;
	 if (!str_cmp(buf, "endif"))
	   return com_list; 
	 mprog_process_cmnd(cmnd, mob, actor, obj, vo, rndm);
	 cmnd     = com_list;
	 com_list = mprog_next_command(com_list);
	 while (*cmnd == ' ')
	   cmnd++;
	 if (*cmnd == '\0')
	   {
	     bug ("Mob:%d missing endif in else section",
		  mob_index[mob->nr].virtual); 
	     return null;
	   }
	 morebuf = one_argument(cmnd, buf);
       }
   }
}

/* This routine handles the variables for command expansion.
 * If you want to add any go right ahead, it should be fairly
 * clear how it is done and they are quite easy to do, so you
 * can be as creative as you want. The only catch is to check
 * that your variables exist before you use them. At the moment,
 * using $t when the secondary target refers to an object 
 * i.e. >prog_act drops~<nl>if ispc($t)<nl>sigh<nl>endif<nl>~<nl>
 * probably makes the mud crash (vice versa as well) The cure
 * would be to change act() so that vo becomes vict & v_obj.
 * but this would require a lot of small changes all over the code.
 */
void mprog_translate(char ch, char *t, struct char_data *mob, struct char_data *actor,
                    struct obj_data *obj, void *vo, struct char_data *rndm)
{
 static char *he_she        [] = { "it",  "he",  "she" };
 static char *him_her       [] = { "it",  "him", "her" };
 static char *his_her       [] = { "its", "his", "her" };
 struct char_data   *vict             = (struct char_data *) vo;
 struct obj_data    *v_obj            = (struct obj_data  *) vo;

 *t = '\0';
 switch (ch) {
     case 'i':
         one_argument(mob->player.name, t);
      break;

     case 'I':
         strcpy(t, mob->player.short_descr);
      break;

     case 'n':
         if (actor) {
	   if (CAN_SEE(mob,actor)) {
             if (!IS_NPC(actor)) {
               strcpy(t, actor->player.name);
             } else
	       one_argument(actor->player.name, t);
           } else
             strcpy(t, "Someone");
         }
      break;

     case 'N':
         if (actor) 
            if (CAN_SEE(mob, actor))
	       if (IS_NPC(actor))
		 strcpy(t, actor->player.short_descr);
	       else
	       {
		   strcpy(t, actor->player.name);
		   strcat(t, " ");
		   strcat(t, actor->player.title);
	       }
	    else
	      strcpy(t, "someone");
	 break;

     case 't':
         if (vict) {
	   if (CAN_SEE(mob, vict)) {
             if (!IS_NPC(vict))
               strcpy(t, vict->player.name);
             else
	       one_argument(vict->player.name, t);
           } else
             strcpy(t, "Someone");
         }
	 break;

     case 'T':
         if (vict) 
            if (CAN_SEE(mob, vict))
	       if (IS_NPC(vict))
		 strcpy(t, vict->player.short_descr);
	       else
	       {
		 strcpy(t, vict->player.name);
		 strcat(t, " ");
		 strcat(t, vict->player.title);
	       }
	    else
	      strcpy(t, "someone");
	 break;
     
     case 'r':
         if (rndm) {
	   if (CAN_SEE(mob, rndm)) {
             if (!IS_NPC(rndm))
               strcpy(t, rndm->player.name);
	     else
               one_argument(rndm->player.name, t);
           } else
             strcpy(t, "Someone");
         }
      break;

     case 'R':
         if (rndm) 
            if (CAN_SEE(mob, rndm))
	       if (IS_NPC(rndm))
		 strcpy(t,rndm->player.short_descr);
	       else
	       {
		 strcpy(t, rndm->player.name);
		 strcat(t, " ");
		 strcat(t, rndm->player.title);
	       }
	    else
	      strcpy(t, "someone");
	 break;

     case 'e':
         if (actor)
	   CAN_SEE(mob, actor) ? strcpy(t, he_she[(int) actor->player.sex ])
	                         : strcpy(t, "someone");
	 break;
  
     case 'm':
         if (actor)
	   CAN_SEE(mob, actor) ? strcpy(t, him_her[(int) actor->player.sex ])
                                 : strcpy(t, "someone");
	 break;
  
     case 's':
         if (actor)
	   CAN_SEE(mob, actor) ? strcpy(t, his_her[(int) actor->player.sex ])
	                         : strcpy(t, "someone's");
	 break;
     
     case 'E':
         if (vict)
	   CAN_SEE(mob, vict) ? strcpy(t, he_she[(int) vict->player.sex ])
                                : strcpy(t, "someone");
	 break;
  
     case 'M':
         if (vict)
	   CAN_SEE(mob, vict) ? strcpy(t, him_her[(int) vict->player.sex ])
                                : strcpy(t, "someone");
	 break;
  
     case 'S':
         if (vict)
	   CAN_SEE(mob, vict) ? strcpy(t, his_her[(int) vict->player.sex ])
                                : strcpy(t, "someone's"); 
	 break;

     case 'j':
	 strcpy(t, he_she[(int) mob->player.sex ]);
	 break;
  
     case 'k':
	 strcpy(t, him_her[(int) mob->player.sex ]);
	 break;
  
     case 'l':
	 strcpy(t, his_her[(int) mob->player.sex ]);
	 break;

     case 'J':
         if (rndm)
	   CAN_SEE(mob, rndm) ? strcpy(t, he_she[(int) rndm->player.sex ])
	                        : strcpy(t, "someone");
	 break;
  
     case 'K':
         if (rndm)
	   CAN_SEE(mob, rndm) ? strcpy(t, him_her[(int) rndm->player.sex ])
                                : strcpy(t, "someone");
	 break;
  
     case 'L':
         if (rndm)
	   CAN_SEE(mob, rndm) ? strcpy(t, his_her[(int) rndm->player.sex ])
	                        : strcpy(t, "someone's");
	 break;

     case 'o':
         if (obj)
	   CAN_SEE_OBJ(mob, obj) ? one_argument(obj->name, t)
                                   : strcpy(t, "something");
	 break;

     case 'O':
         if (obj)
	   CAN_SEE_OBJ(mob, obj) ? strcpy(t, obj->short_description)
                                   : strcpy(t, "something");
	 break;

     case 'p':
         if (v_obj)
	   CAN_SEE_OBJ(mob, v_obj) ? one_argument(v_obj->name, t)
                                     : strcpy(t, "something");
	 break;

     case 'P':
         if (v_obj)
	   CAN_SEE_OBJ(mob, v_obj) ? strcpy(t, v_obj->short_description)
                                     : strcpy(t, "something");
      break;

     case 'a':
         if (obj) 
          switch (*(obj->name))
	  {
	    case 'a': case 'e': case 'i':
            case 'o': case 'u': strcpy(t, "an");
	      break;
            default: strcpy(t, "a");
          }
	 break;

     case 'A':
         if (v_obj) 
          switch (*(v_obj->name))
	  {
            case 'a': case 'e': case 'i':
	    case 'o': case 'u': strcpy(t, "an");
	      break;
            default: strcpy(t, "a");
          }
	 break;

     case '$':
         strcpy(t, "$");
	 break;

     default:
         bug("Mob: %d bad $var", mob_index[mob->nr].virtual);
	 break;
       }

 return;

}

/* This procedure simply copies the cmnd to a buffer while expanding
 * any variables by calling the translate procedure.  The observant
 * code scrutinizer will notice that this is taken from act()
 */
void mprog_process_cmnd(char *cmnd, struct char_data *mob, struct char_data *actor,
			struct obj_data *obj, void *vo, struct char_data *rndm)
{
  char buf[ MAX_INPUT_LENGTH ];
  char tmp[ MAX_INPUT_LENGTH ];
  char *str;
  char *i;
  char *point;
  int j;

  point   = buf;
  str     = cmnd;

  while (*str != '\0')
  {
    if (*str != '$')
    {
      *point++ = *str++;
      continue;
    }
    str++;
    mprog_translate(*str, tmp, mob, actor, obj, vo, rndm);
    i = tmp;
    ++str;
    while ((*point = *i) != '\0')
      ++point, ++i;
  }
  *point = '\0';
  str = buf;
  j = 1;
  while (j < MAX_INPUT_LENGTH-2)
  {
   if(str[j] == '\n') {
     str[j] = '\0';
     break;
     }
   if(str[j] == '\r') {
     str[j] = '\0';
     break;
     }
   if(str[j] == '\0')
     break;
   j++;
  }

  command_interpreter(mob, buf);

  return;

}

/* The main focus of the MOBprograms.  This routine is called 
 *  whenever a trigger is successful.  It is responsible for parsing
 *  the command list and figuring out what to do. However, like all
 *  complex procedures, everything is farmed out to the other guys.
 */
void mprog_driver (char *com_list, struct char_data *mob, struct char_data *actor,
		   struct obj_data *obj, void *vo)
{

 char tmpcmndlst[ MAX_STRING_LENGTH ];
 char buf       [ MAX_INPUT_LENGTH ];
 char *morebuf;
 char *command_list;
 char *cmnd;
 struct char_data *rndm  = NULL;
 struct char_data *vch   = NULL;
 int        count = 0;

 if IS_AFFECTED(mob, AFF_CHARM)
   return;

 /* get a random visable mortal player who is in the room with the mob */
 for (vch = world[mob->in_room].people; vch; vch = vch->next_in_room)
   if (!IS_NPC(vch)
       &&  vch->player.level < LVL_IMMORT
       &&  CAN_SEE(mob, vch))
     {
       if (number(0, count) == 0)
	 rndm = vch;
       count++;
     }
  
 strcpy(tmpcmndlst, com_list);
 command_list = tmpcmndlst;
 cmnd         = command_list;
 command_list = mprog_next_command(command_list);
 while (*cmnd != '\0')
   {
     morebuf = one_argument(cmnd, buf);
     if (!str_cmp(buf, "if"))
       command_list = mprog_process_if(morebuf, command_list, mob,
				       actor, obj, vo, rndm);
     else
       mprog_process_cmnd(cmnd, mob, actor, obj, vo, rndm);
     cmnd         = command_list;
     command_list = mprog_next_command(command_list);
   }

 return;

}

/***************************************************************************
 * Global function code and brief comments.
 */

/* The next two routines are the basic trigger types. Either trigger
 *  on a certain percent, or trigger on a keyword or word phrase.
 *  To see how this works, look at the various trigger routines..
 */
void mprog_wordlist_check(char *arg, struct char_data *mob, struct char_data *actor,
			  struct obj_data *obj, void *vo, int type)
{

  char        temp1[ MAX_STRING_LENGTH ];
  char        temp2[ MAX_INPUT_LENGTH ];
  char        word[ MAX_INPUT_LENGTH ];
  MPROG_DATA *mprg;
  char       *list;
  char       *start;
  char       *dupl;
  char       *end;
  int         i;

  for (mprg = mob_index[mob->nr].mobprogs; mprg != NULL; mprg = mprg->next)
    if (mprg->type & type)
      {
	strcpy(temp1, mprg->arglist);
	list = temp1;
        while(isspace(*list)) list++;
	for (i = 0; i < strlen(list); i++)
	  list[i] = LOWER(list[i]);
	strcpy(temp2, arg);
	dupl = temp2;
	for (i = 0; i < strlen(dupl); i++)
	  dupl[i] = LOWER(dupl[i]);
	if ((list[0] == 'p') && (list[1] == ' '))
	  {
	    list += 2;
	    while ((start = strstr(dupl, list)))
	      if ((start == dupl || *(start-1) == ' ')
		  && (*(end = start + strlen(list)) == ' '
		      || *end == '\n'
		      || *end == '\r'
		      || *end == '\0'))
		{
		  mprog_driver(mprg->comlist, mob, actor, obj, vo);
		  break;
		}
	      else
		dupl = start+1;
	  }
	else
	  {
	    list = one_argument(list, word);
	    for(; word[0] != '\0'; list = one_argument(list, word))
	      while ((start = strstr(dupl, word)))
		if ((start == dupl || *(start-1) == ' ')
		    && (*(end = start + strlen(word)) == ' '
			|| *end == '\n'
			|| *end == '\r'
			|| *end == '\0'))
		  {
		    mprog_driver(mprg->comlist, mob, actor, obj, vo);
		    break;
		  }
		else
		  dupl = start+1;
	  }
      }

  return;

}

void mprog_percent_check(struct char_data *mob, struct char_data *actor, struct obj_data *obj,
			 void *vo, int type)
{
 MPROG_DATA * mprg;

 for (mprg = mob_index[mob->nr].mobprogs; mprg != NULL; mprg = mprg->next)
   if ((mprg->type & type)
       && (number(0,100) < atoi(mprg->arglist)))
     {
       mprog_driver(mprg->comlist, mob, actor, obj, vo);
       if (type != GREET_PROG && type != ALL_GREET_PROG)
	 break;
     }

 return;

}

/* The triggers.. These are really basic, and since most appear only
 * once in the code (hmm. i think they all do) it would be more efficient
 * to substitute the code in and make the mprog_xxx_check routines global.
 * However, they are all here in one nice place at the moment to make it
 * easier to see what they look like. If you do substitute them back in,
 * make sure you remember to modify the variable names to the ones in the
 * trigger calls.
 */
void mprog_act_trigger(char *buf, struct char_data *mob, struct char_data *ch,
		       struct obj_data *obj, void *vo)
{

  MPROG_ACT_LIST * tmp_act;

  if (IS_NPC(mob)
      && (mob_index[mob->nr].progtypes & ACT_PROG)
      && (mob != ch))
    {
      tmp_act = malloc(sizeof(MPROG_ACT_LIST));
      if (mob->mpactnum > 0)
	tmp_act->next = mob->mpact;
      else
	tmp_act->next = NULL;

      mob->mpact      = tmp_act;
      mob->mpact->buf = str_dup(buf);
      mob->mpact->ch  = ch; 
      mob->mpact->obj = obj; 
      mob->mpact->vo  = vo; 
      mob->mpactnum++;

    }
  return;

}

void mprog_bribe_trigger(struct char_data *mob, struct char_data *ch, int amount)
{

  MPROG_DATA *mprg;
  struct obj_data   *obj;

  if (IS_NPC(mob)
      && (mob_index[mob->nr].progtypes & BRIBE_PROG))
    {
      obj = create_money(amount);
      obj_to_char(obj, mob);
      mob->points.gold -= amount;

      for (mprg = mob_index[mob->nr].mobprogs; mprg != NULL; mprg = mprg->next)
	if ((mprg->type & BRIBE_PROG)
	    && (amount >= atoi(mprg->arglist)))
	  {
	    mprog_driver(mprg->comlist, mob, ch, obj, NULL);
	    break;
	  }
    }
  
  return;

}

void mprog_death_trigger(struct char_data *mob, struct char_data *killer)
{

 if (IS_NPC(mob)
     && (mob_index[mob->nr].progtypes & DEATH_PROG))
   {
     mprog_percent_check(mob, killer, NULL, NULL, DEATH_PROG);
   }

 death_cry(mob);
 return;

}

void mprog_entry_trigger(struct char_data *mob)
{

 if (IS_NPC(mob)
     && (mob_index[mob->nr].progtypes & ENTRY_PROG))
   mprog_percent_check(mob, NULL, NULL, NULL, ENTRY_PROG);

 return;

}

void mprog_fight_trigger(struct char_data *mob, struct char_data *ch)
{

 if (IS_NPC(mob)
     && (mob_index[mob->nr].progtypes & FIGHT_PROG))
   mprog_percent_check(mob, ch, NULL, NULL, FIGHT_PROG);

 return;

}

void mprog_give_trigger(struct char_data *mob, struct char_data *ch, struct obj_data *obj)
{

 char        buf[MAX_INPUT_LENGTH];
 MPROG_DATA *mprg;

 if (IS_NPC(mob)
     && (mob_index[mob->nr].progtypes & GIVE_PROG))
   for (mprg = mob_index[mob->nr].mobprogs; mprg != NULL; mprg = mprg->next)
     {
       one_argument(mprg->arglist, buf);
       if ((mprg->type & GIVE_PROG)
	   && ((!str_infix(obj->name, mprg->arglist))
	       || (!str_cmp("all", buf))))
	 {
	   mprog_driver(mprg->comlist, mob, ch, obj, NULL);
	   break;
	 }
     }

 return;

}

void mprog_greet_trigger(struct char_data *ch)
{

 struct char_data *vmob;

 for (vmob = world[ch->in_room].people; vmob != NULL; vmob = vmob->next_in_room)
   if (IS_NPC(vmob)
       && ch != vmob
       && CAN_SEE(vmob, ch)
       && (vmob->char_specials.fighting == NULL)
       && AWAKE(vmob)
       && (mob_index[vmob->nr].progtypes & GREET_PROG))
     mprog_percent_check(vmob, ch, NULL, NULL, GREET_PROG);
   else
     if (IS_NPC(vmob)
	 && (vmob->char_specials.fighting == NULL)
	 && AWAKE(vmob)
	 && (mob_index[vmob->nr].progtypes & ALL_GREET_PROG))
       mprog_percent_check(vmob,ch,NULL,NULL,ALL_GREET_PROG);

 return;

}

void mprog_hitprcnt_trigger(struct char_data *mob, struct char_data *ch)
{

 MPROG_DATA *mprg;

 if (IS_NPC(mob)
     && (mob_index[mob->nr].progtypes & HITPRCNT_PROG))
   for (mprg = mob_index[mob->nr].mobprogs; mprg != NULL; mprg = mprg->next)
     if ((mprg->type & HITPRCNT_PROG)
	 && ((100*mob->points.hit / mob->points.max_hit) < atoi(mprg->arglist)))
       {
	 mprog_driver(mprg->comlist, mob, ch, NULL, NULL);
	 break;
       }
 
 return;

}

void mprog_random_trigger(struct char_data *mob)
{
  if (mob_index[mob->nr].progtypes & RAND_PROG)
    mprog_percent_check(mob,NULL,NULL,NULL,RAND_PROG);

  return;

}

void mprog_speech_trigger(char *txt, struct char_data *mob)
{

  struct char_data *vmob;

  for (vmob = world[mob->in_room].people; vmob != NULL; vmob = vmob->next_in_room)
    if ((mob != vmob) && IS_NPC(vmob) && (mob_index[vmob->nr].progtypes & SPEECH_PROG))
      mprog_wordlist_check(txt, vmob, mob, NULL, NULL, SPEECH_PROG);
  
  return;

}