/***************************************************************************
* 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 "class.h"
#include "objs.h"
#include "rooms.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;
}