/**************************************************************************/
// mob_prog.cpp - Mobprogs engine, most enhancements by Kal
/***************************************************************************
* The Dawn of Time v1.69r (c)1997-2004 Michael Garratt *
* >> A number of people have contributed to the Dawn codebase, with the *
* majority of code written by Michael Garratt - www.dawnoftime.org *
* >> To use this source code, you must fully comply with all the licenses *
* in licenses.txt... In particular, you may not remove this copyright *
* notice. *
***************************************************************************
* >> Original Diku Mud copyright (c)1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, & Katja Nyboe. *
* >> Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* >> ROM 2.4 is copyright 1993-1995 Russ Taylor and has been brought to *
* you by the ROM consortium: Russ Taylor(rtaylor@pacinfo.com), *
* Gabrielle Taylor(gtaylor@pacinfo.com) & Brian Moore(rom@rom.efn.org) *
* >> Oblivion 1.2 is copyright 1996 Wes Wagner *
**************************************************************************/
/***************************************************************************
* MOBprograms for ROM 2.4 v0.98g (C) M.Nylander 1996 *
* Based on MERC 2.2 MOBprograms concept by N'Atas-ha. *
* Written and adapted to ROM 2.4 by *
* Markku Nylander (markku.nylander@uta.fi) *
***************************************************************************/
#include "include.h" // dawn standard includes
#include "security.h"
extern int flag_lookup( const char *word, const struct flag_type *flag_table );
/*
* These defines correspond to the entries in fn_keyword[] table.
* If you add a new if_check, you must also add a #define here.
*/
#define CHK_RAND (0)
#define CHK_MOBHERE (1)
#define CHK_OBJHERE (2)
#define CHK_MOBEXISTS (3)
#define CHK_OBJEXISTS (4)
#define CHK_PEOPLE (5)
#define CHK_PLAYERS (6)
#define CHK_MOBS (7)
#define CHK_CLONES (8)
#define CHK_ORDER (9)
#define CHK_HOUR (10)
#define CHK_ISPC (11)
#define CHK_ISNPC (12)
#define CHK_ISGOOD (13)
#define CHK_ISEVIL (14)
#define CHK_ISNEUTRAL (15)
#define CHK_ISIMMORT (16)
#define CHK_ISCHARM (17)
#define CHK_ISFOLLOW (18)
#define CHK_ISACTIVE (19)
#define CHK_ISDELAY (20)
#define CHK_ISVISIBLE (21)
#define CHK_HASTARGET (22)
#define CHK_ISTARGET (23)
#define CHK_EXISTS (24)
#define CHK_AFFECTED (25)
#define CHK_ACT (26)
#define CHK_OFF (27)
#define CHK_IMM (28)
#define CHK_CARRIES (29)
#define CHK_WEARS (30)
#define CHK_HAS (31)
#define CHK_USES (32)
#define CHK_NAME (33)
#define CHK_POS (34)
#define CHK_CLAN (35)
#define CHK_RACE (36)
#define CHK_CLASS (37)
#define CHK_OBJTYPE (38)
#define CHK_VNUM (39)
#define CHK_HPCNT (40)
#define CHK_ROOM (41)
#define CHK_SEX (42)
#define CHK_LEVEL (43)
#define CHK_ALIGN (44)
#define CHK_MONEY (45)
#define CHK_OBJVAL0 (46)
#define CHK_OBJVAL1 (47)
#define CHK_OBJVAL2 (48)
#define CHK_OBJVAL3 (49)
#define CHK_OBJVAL4 (50)
#define CHK_GRPSIZE (51)
#define CHK_EXACT_NAME (52)
#define CHK_AREA (53)
#define CHK_SECTOR (54)
#define CHK_ROOMFLAG (55)
#define CHK_ISQUESTER (56)
#define CHK_HASPKTIMER (57)
#define CHK_HASTOKEN (58)
#define CHK_PKKILLS (59)
#define CHK_PKDEFEATS (60)
#define CHK_KARNS (61)
#define CHK_RPS (62)
#define CHK_DRUNK (63)
#define CHK_FULL (64)
#define CHK_THIRST (65)
#define CHK_HUNGER (66)
#define CHK_TIRED (67)
#define CHK_ISCOURT (68)
#define CHK_ACT2 (69)
#define CHK_VALUE (70)
#define CHK_CHARHERE (71)
#define CHK_HASSKILL (72)
#define CHK_HASTRAINS (73)
#define CHK_HASPRACS (74)
#define CHK_HASBONUS (75)
#define CHK_RACESIZE (76)
#define CHK_ISEXITOPEN (77)
#define CHK_ISTHIEF (78)
#define CHK_ISKILLER (79)
#define CHK_ISSHEATHED (80)
#define CHK_ISCONCEALED (81)
/*
* These defines correspond to the entries in fn_evals[] table.
*/
#define EVAL_EQ 0
#define EVAL_GE 1
#define EVAL_LE 2
#define EVAL_GT 3
#define EVAL_LT 4
#define EVAL_NE 5
/**************************************************************************/
/*
* if-check keywords:
*/
struct fn_keyword_type
{
const char * name;
const char * descript;
const char * syntax;
const char * notes;
const char * example;
};
const struct fn_keyword_type fn_keyword[] =
{
{"rand", "random number check", "if rand <number>",
"Will be true if a random number between 1 and 100 is less than <number>.",
"if rand 30"},
{"mobhere", "check if a mob is in the room by vnum", "if mobhere <mobvnum>",
"While technically it could be used like 'if mobhere dog' if charhere is better for that, since charhere supports 'if charhere $q' etc."
"if mobhere 3001"},
{"objhere", "check if an object is in the room by vnum/keyword", "if objhere <keyword|vnum>",
"",
"if objhere 3001`1if objhere bottle"},
{"mobexists", "check if a mob exists somewhere in the game", "if mobexists <mob>"},
{"objexists", "check if a object exists somewhere in the game", "if objexists <keyword>"},
{"people", "use to check for a count of people in the room (mobs + players)", "if people <operation> <number>",
"'if people > 4' is true when does room contain more than 4 people"},
{"players", "same as people check, only for players"},
{"mobs", "same as people check, only for mobs"},
{"clones", "if clones > 3 - are there > 3 mobs of same vnum here"},
{"order", "if order == 0 - is mob the first in room"},
{"hour", "if hour > 11 - is the time > 11 o'clock"},
{"ispc", "if ispc $n - is $n a pc "},
{"isnpc", "if isnpc $n - is $n a mobile "},
{"isgood", "if isgood $n - is $n good "},
{"isevil", "if isevil $n - is $n evil "},
{"isneutral", "if isneutral $n - is $n neutral "},
{"isimmort", "if isimmort $n - is $n immortal "},
{"ischarm", "if ischarm $n - is $n charmed "},
{"isfollow", "if isfollow $n - is $n following someone "},
{"isactive", "if isactive $n - is $n's position > SLEEPING ",
"useful in mobprogs to wake a mob up if they are asleep`1"
" (on triggers that work on asleep mobs)",
"The following code will wakeup a mob, put this at the top of a prog`1"
" (isactive can obviously used on players also)",
"`1"
" if !isactive $i`1"
" mob echoaround $i $I yawns and stretches.`1"
" mq2 wake`1"
" mq5 '>$n Hello there.`1"
" else`1"
" '>$n Hello there.`1"
" endif"},
{"isdelay", "if isdelay $i - does $i have mobprog pending "},
{"isvisible", "if isvisible $n - can mob see $n "},
{"hastarget", "if hastarget $i - does $i have a valid target "},
{"istarget", "if istarget $n - is $n mob's target "},
{"exists", "if exists $n - does $n exist somewhere "},
{"affected", "if affected $n blind - is $n affected by blind "},
{"act", "if act $i sentinel - is $i flagged sentinel "},
{"off", "if off $i berserk - is $i flagged berserk "},
{"imm", "if imm $i fire - is $i immune to fire "},
{"carries", "if carries $n sword - does $n have a 'sword'`1"
"if carries $n 1233 - does $n have obj vnum 1233"},
{"wears", "if wears $n lantern - is $n wearing a 'lantern'`1"
"if wears $n 1233 - is $n wearing obj vnum 1233"},
{"has", "if has $n weapon - does $n have obj of type weapon "},
{"uses", "if uses $n armor - is $n wearing obj of type armor"},
{"name", "if name $n puff - does $n's name contain 'puff', 'if name $o iron' - is the word iron in the objects name."},
{"pos", "if pos $n standing - is $n standing"},
{"clan", "if clan $n 'whatever'- does $n belong to clan 'whatever'"},
{"race", "if race $n elf - is $n of 'elf' race"},
{"class", "if class $n mage - is $n's class 'mage'"},
{"objtype", "if objtype $p scroll - is $p a scroll"},
{"vnum", "if vnum $i == 1233 - virtual number check"},
{"hpcnt", "if hpcnt $i > 30 - hit point percent check"},
{"room", "if room $i == 1233 - room virtual number"},
{"sex", "if sex $i == 0 - sex check, 0=it, 1=male,2=female"},
{"level", "'if level $n < 5' or 'if level $o < 5' etc - character/object level check"},
{"align", "if align $n < -1000 - alignment check"},
{"money", "if money $n"},
{"objval0", "if objval0 $o > 1000 - object value[] checks 0..4"},
{"objval1", ""},
{"objval2", ""},
{"objval3", ""},
{"objval4", ""},
{"grpsize", "if grpsize $n > 6 - group size check"},
{"exactname", "if exactname $n puff - is $n's exactname 'puff'"},
{"area", "if area $n 'full exact name of area' - check if "
"$n is in the area which name matches."},
{"sector", "if sector $n sectortype - strprefix checking not exact"},
{"roomflag", "if room $n roomflag"},
{"isquester", "if quester $n"},
{"haspktimer", "if haspktimer $n"},
{"hastoken", "if hastoken $n 3087 - is $n carrying 1 or more of token 3087`1"
"if hastoken $n 3.1233 - is $n carrying 3 or more of token 1233`1"
"if hastoken $n tokendescr - does $n have a 'tokendescr'"},
{"pkkills", "if pkkills $n > 1000 - ch pkkill checks"},
{"pkdefeats", "if pkdefeats $n > 1000 - ch pkdefeat checks"},
{"karns", "if karns $n > 1000 - ch karn checks"},
{"rps", "if rps $n > 1000 - ch rps checks"},
{"drunk", "if drunk $n > 10"},
{"full", "if full $n > 10"},
{"thirst", "if thirst $n > 10"},
{"hunger", "if hunger $n > 10"},
{"tired", "if tired $n > 10"},
{"iscourt", "errr, if is in the court thingie"},
{"act2", "act2 flags"},
{"value", "use this to check room numbers in premove triggers (see mobhelp preventmove)"},
{"charhere", "if charhere $n"},
{"hasskill", "if hasskill $n parry > 10"},
{"hastrains","if hastrains $n > 1 - necessary for mob setskill to ensure enuff trains exist"},
{"haspracs", "if haspracs $n > 1 - necessary for mob setskill to ensure enuff pracs exist"},
{"hasbonus", "if hasbonus $n strength > 10 - Strength bonus of $n > 10"},
{"racesize", "if racesize $n > 1 - checks racial size -- sizes are:`1"
" tiny=0 small=1 medium=2 large=3 huge=4 giant=5"},
{"isexitopen", "if isexitopen <direction> - returns true if <direction> is an open exit (open door, no door etc)`1"
" <direction> can be a word for the direction or the direction number."},
{"isthief", "if isthief $n - true if player is currently flagged as a thief"},
{"iskiller","if iskiller $n - true if player is currently flagged as a killer"},
{"issheathed", "if issheathed $n - has weapon sheathed"}, // Ix
{"isconcealed", "if isconcealed $n - has weapon concealed"}, // Ix
{"", ""}, // Table terminator
};
/**************************************************************************/
const char *fn_evals[] =
{
"==", ">=", "<=", ">", "<", "!=", "\n"
};
/**************************************************************************/
// a command to display all the mobprog if checks
void do_ifhelp( char_data *ch, char *argument )
{
int cmd;
// do the checks to make sure they can use the command here
if (!HAS_SECURITY(ch,1))
{
ch->printf("The ifhelp command is an olc mobprog programming related command, "
"you dont have olc permissions.\r\n");
return;
}
if ( !HAS_SECURITY(ch, MPEDIT_MINSECURITY))
{
ch->printlnf("ifhelp: Insufficient olc security to use this command %d required.",
MPEDIT_MINSECURITY);
return;
}
// Return a valid keyword from a keyword table
if(IS_NULLSTR(argument)){
// command list syntax
for ( cmd = 0; !IS_NULLSTR(fn_keyword[cmd].name); cmd++ )
{
ch->printlnf("%s%-11s `S- `B%s",
(IS_NULLSTR(fn_keyword[cmd].notes)?"`W":"`Y"),
capitalize(fn_keyword[cmd].name),
IS_NULLSTR(fn_keyword[cmd].descript)?"`Sno descripton":fn_keyword[cmd].descript);
}
ch->println("`xTo see more info on a given command, type `=Cifhelp <command>`x");
ch->println("`xNote: you can put an ! before any keyword to negate its operation (e.g. if !class $n mage)");
ch->println("`YYellow commands have example code/notes`x");
}else{
// specific list syntax
for ( cmd = 0; !IS_NULLSTR(fn_keyword[cmd].name); cmd++ )
{
// filter in only required commands
if(str_infix(argument,fn_keyword[cmd].name))
continue;
ch->printlnf("`W%-11s `S- `B%s\r\n"
" `Ssyntax: `Y%s\r\n"
" `Snotes: `g%s\r\n"
" `Sexample:`x%s\r\n",
capitalize(fn_keyword[cmd].name),
IS_NULLSTR(fn_keyword[cmd].descript)?"`Sno descripton":fn_keyword[cmd].descript,
IS_NULLSTR(fn_keyword[cmd].syntax)?"`Sno syntax":fn_keyword[cmd].syntax,
IS_NULLSTR(fn_keyword[cmd].notes)?"`Sno notes":fn_keyword[cmd].notes,
IS_NULLSTR(fn_keyword[cmd].example)?"`Sno example":fn_keyword[cmd].example);
}
}
}
/**************************************************************************/
// Return a valid keyword from a keyword table
int keyword_lookup( const char **table, char *keyword )
{
register int i;
for( i = 0; table[i][0] != '\n'; i++ )
if( !str_cmp( table[i], keyword ))
return( i );
return -1;
}
/**************************************************************************/
// Return a valid keyword from a keyword table
int fn_keyword_lookup( char *keyword )
{
register int i;
for( i = 0; !IS_NULLSTR(fn_keyword[i].name); i++ ){
if( !str_cmp( fn_keyword[i].name, keyword )){
return( i );
}
}
return -1;
}
/**************************************************************************/
// Perform numeric evaluation.
// Called by cmd_eval()
int num_eval( int lval, int oper, int rval )
{
switch( oper )
{
case EVAL_EQ:
return ( lval == rval );
case EVAL_GE:
return ( lval >= rval );
case EVAL_LE:
return ( lval <= rval );
case EVAL_NE:
return ( lval != rval );
case EVAL_GT:
return ( lval > rval );
case EVAL_LT:
return ( lval < rval );
default:
bug("num_eval: invalid oper");
return 0;
}
}
/**************************************************************************/
/*
* ---------------------------------------------------------------------
* UTILITY FUNCTIONS USED BY CMD_EVAL()
* ----------------------------------------------------------------------
*/
/*
* Get a random PC in the room (for $r parameter)
*/
char_data *get_random_char( char_data *mob )
{
char_data *vch, *victim = NULL;
int now = 0, highest = 0;
for( vch = mob->in_room->people; vch; vch = vch->next_in_room )
{
if ( mob != vch
&& !IS_NPC( vch )
&& can_see( mob, vch )
&& ( now = number_percent() ) > highest )
{
victim = vch;
highest = now;
}
}
return victim;
}
/**************************************************************************/
/*
* How many other players / mobs are there in the room
* iFlag: 0: all, 1: players, 2: mobiles 3: mobs w/ same vnum 4: same group
*/
int count_people_room( char_data *mob, int iFlag )
{
char_data *vch;
int count;
for ( count = 0, vch = mob->in_room->people; vch; vch = vch->next_in_room )
if ( mob != vch
&& (iFlag == 0
|| (iFlag == 1 && !IS_NPC( vch ))
|| (iFlag == 2 && IS_NPC( vch ))
|| (iFlag == 3 && IS_NPC( mob ) && mob->vnum() == vch->vnum() )
|| (iFlag == 4 && is_same_group( mob, vch )) )
&& can_see( mob, vch ) )
count++;
return ( count );
}
/**************************************************************************/
/*
* Get the order of a mob in the room. Useful when several mobs in
* a room have the same trigger and you want only the first of them
* to act
*/
int get_order( char_data *ch )
{
char_data *vch;
int i;
if ( !IS_NPC(ch) )
return 0;
for ( i = 0, vch = ch->in_room->people; vch; vch = vch->next_in_room )
{
if ( vch == ch )
return i;
if ( IS_NPC(vch)
&& vch->vnum() == ch->vnum() )
i++;
}
return 0;
}
/**************************************************************************/
/*
* Check if ch has a given item or item type
* vnum: item vnum or -1
* item_type: item type or -1
* fWear: true: item must be worn, false: don't care
*/
bool has_item( char_data *ch, vn_int vnum, int item_type, bool fWear )
{
OBJ_DATA *obj;
for ( obj = ch->carrying; obj; obj = obj->next_content ){
if ( ( vnum < 0 || obj->pIndexData->vnum == vnum )
&& ( item_type < 0 || obj->pIndexData->item_type == item_type )
&& ( !fWear || obj->wear_loc != WEAR_NONE ) )
{
// can't find the token with this sytsem
if(obj->item_type == ITEM_TOKEN){
mpbug( "trying to match a token using a syntax other than 'if hastoken'.");
return false;
}
return true;
}
}
return false;
}
/**************************************************************************/
// Check if ch has a given token - Kal August 99
//
// Supports the hastoken trigger... can do things like
// if hastoken $* 3.3005 to see if the player has 3 or more of token 3005
// or also supports using 3.textnameoftoken
bool has_token( char_data *ch, char *argument)
{
char arg[MIL];
OBJ_DATA *obj;
int number;
int count;
int tokenvnum;
number = number_argument( argument, arg );
count = 0;
tokenvnum=0;
if(is_number(arg)){
tokenvnum=atoi(arg);
}
if(tokenvnum){ // token is being referenced by number
for ( obj = ch->carrying; obj; obj = obj->next_content ) {
if ( obj->item_type == ITEM_TOKEN
&& obj->pIndexData
&& obj->pIndexData->vnum==tokenvnum) {
if ( ++count == number )
return true;
}
}
}else{ // token is being referenced by textname
for ( obj = ch->carrying; obj; obj = obj->next_content ) {
if ( obj->item_type == ITEM_TOKEN
&& is_name( arg, obj->name )) {
if ( ++count == number )
return true;
}
}
}
return false;
}
/**************************************************************************/
/*
* Check if there's a mob with given vnum in the room
*/
bool get_mob_vnum_room( char_data *ch, int vnum )
{
char_data *mob;
for ( mob = ch->in_room->people; mob; mob = mob->next_in_room )
if ( IS_NPC( mob ) && mob->vnum() == vnum )
return true;
return false;
}
/**************************************************************************/
/*
* Check if there's an object with given vnum in the room
*/
bool get_obj_vnum_room( char_data *ch, int vnum )
{
OBJ_DATA *obj;
for ( obj = ch->in_room->contents; obj; obj = obj->next_content )
if ( obj->pIndexData->vnum == vnum )
return true;
return false;
}
/**************************************************************************/
/* ---------------------------------------------------------------------
* CMD_EVAL
* This monster evaluates an if/or/and statement
* There are five kinds of statement:
* 1) keyword and value (no $-code) if random 30
* 2) keyword, comparison and value if people > 2
* 3) keyword and actor if isnpc $n
* 4) keyword, actor and value if carries $n sword
* 5) keyword, actor, comparison and value if level $n >= 10
*----------------------------------------------------------------------
*/
bool cmd_eval( int vnum, char *line, int check,
char_data *mob, char_data *ch,
const void *arg1, const void *arg2, char_data *rch )
{
char_data *lval_char = mob;
char_data *vch = (char_data *) arg2;
OBJ_DATA *obj1 = (OBJ_DATA *) arg1;
OBJ_DATA *obj2 = (OBJ_DATA *) arg2;
OBJ_DATA *lval_obj = NULL;
char *original, buf[MIL], code='\0';
int lval = 0, oper = 0, rval = -1;
original = line;
line = one_argument( line, buf );
if ( buf[0] == '\0' || mob == NULL )
return false;
/*
* If this mobile has no target, let's assume our victim is the one
*/
if ( mob->mprog_target == NULL )
mob->mprog_target = ch;
// had to put this up here since the hasskill check doesn't comply with
// any of the 5 cmd_eval types, and making a sixth one would mean a
// rewrite of #5, and by that time, all the arguments and such have been
// really destroyed beyond recognition, needing the use of original, and
// since this is a unique situation, a little mini-hack was the simplest
// solution, and we all know very well.... K.I.S.S. :)
//
// keyword, actor, >keyword2<, comp, value -- if hasskill $n parry > 10
// <MINIHACK>
if ( check == CHK_HASSKILL )
{
char actor[MIL];
char buf2[MIL];
strcpy( actor, buf );
if ( IS_NULLSTR( line ))
{
mpbug("Cmd_eval(): prog %d if hasskill (null)", vnum );
return false;
}
if ( actor[0] != '$' || buf[1] == '\0' )
{
mpbug( "Cmd_eval: prog %d syntax error in actor '%s'", vnum, original );
return false;
}
else
code = buf[1];
switch( code )
{
case 'i': lval_char = mob; break;
case 'n': lval_char = ch; break;
case 't': lval_char = vch; break;
case 'r': lval_char = rch == NULL ? get_random_char( mob ) : rch ; break;
case 'q': lval_char = mob->mprog_target; break;
default:
mpbug( "Cmd_eval: prog %d syntax error in actor '%s'", vnum, original );
return false;
}
line = one_argument( line, buf ); // buf has skillname
line = one_argument( line, buf2 ); // buf2 has operator
if (( oper = keyword_lookup( fn_evals, buf2 )) < 0 )
{
mpbug( "Cmd_eval: prog %d incorrect operator value '%s'", vnum, original );
return false;
}
rval = atoi( line );
if ( lval_char != NULL && !IS_NPC( lval_char )) {
lval = get_skill( lval_char, skill_lookup(buf));
logf( "lval %d rval %d", lval,rval );
}
else
{
mpbug("Cmd_eval(): prog %d is buggy '%s'", vnum, original );
return false;
}
if(num_eval( lval, oper, rval )){
return true;
}
return false;
}
if ( check == CHK_HASBONUS )
{
char actor[MIL];
char buf2[MIL];
strcpy( actor, buf );
if ( IS_NULLSTR( line ))
{
mpbug("Cmd_eval(): prog %d if hasbonus (null)", vnum );
return false;
}
if ( actor[0] != '$' || buf[1] == '\0' )
{
mpbug( "Cmd_eval: prog %d syntax error in actor '%s'", vnum, original );
return false;
}
else
code = buf[1];
switch( code )
{
case 'i': lval_char = mob; break;
case 'n': lval_char = ch; break;
case 't': lval_char = vch; break;
case 'r': lval_char = rch == NULL ? get_random_char( mob ) : rch ; break;
case 'q': lval_char = mob->mprog_target; break;
default:
mpbug( "Cmd_eval: prog %d syntax error in actor '%s'", vnum, original );
return false;
}
line = one_argument( line, buf ); // buf has bonus
line = one_argument( line, buf2 ); // buf2 has operator
if (( oper = keyword_lookup( fn_evals, buf2 )) < 0 )
{
mpbug( "Cmd_eval: prog %d incorrect operator value '%s'", vnum, original );
return false;
}
rval = atoi( line );
if ( lval_char != NULL && !IS_NPC( lval_char )) {
// lval = get_skill( lval_char, skill_lookup(buf));
lval = lval_char->modifiers[stat_lookup(buf)];
logf( "lval %d rval %d", lval,rval );
}
else
{
mpbug("Cmd_eval(): prog %d is buggy '%s'", vnum, original );
return false;
}
if( num_eval( lval, oper, rval )){
return true;
}
return false;
}
// </MINIHACK> ps - ker->geekfactor += 1; for using html-like comments :)
switch ( check )
{
/*
* Case 1: keyword and value
*/
case CHK_RAND:
{
int numval;
if(is_number(buf))
{
numval=atoi( buf );
}
else
{
mpbug( "Cmd_eval: prog %d syntax with rand command - '%s ' is not a number!",
vnum, buf);
return false;
}
int numpercent=number_percent();
// logf("prog=%d, numval=%d, numpercent=%d, returning %s",
// vnum, numval, numpercent, numval>numpercent?"true":"false");
return( numval>numpercent);
}
case CHK_MOBHERE:
if ( is_number( buf ))
return( get_mob_vnum_room( mob, (vn_int)atoi(buf) ) );
else{
if(buf[0] == '$'){
mpbug( "Cmd_eval - mobhere if check: Cant process variable arguments (parameters with $ in them, use 'if charhere $n' etc.");
return false;
}
return( (bool) (get_char_room( mob, buf) != NULL) );
}
case CHK_OBJHERE:
if ( is_number( buf ) ){
return( get_obj_vnum_room(mob, atoi(buf)) );
}else{
return( (bool) (get_obj_here( mob, buf) != NULL) );
}
case CHK_MOBEXISTS:
return( (bool) (get_char_world( mob, buf) != NULL) );
case CHK_OBJEXISTS:
return( (bool) (get_obj_world( mob, buf) != NULL) );
case CHK_ISEXITOPEN:
{
int processed_dir;
if(IS_NULLSTR(buf)){
mpbug("Cmd_eval(): prog %d if isexitopen (null)", vnum );
return false;
}
// calculate the direction
if(is_number(buf)){
processed_dir=atoi( buf);
}else{
processed_dir=dir_lookup(buf);
}
if(processed_dir<0 || processed_dir>=MAX_DIR){
mpbug("cmd_eval(): Unrecognised direction '%s' in is isexitopen check - program %d.",
buf, vnum);
return false;
}
if(mob->in_room
&& mob->in_room->exit[processed_dir]
&& mob->in_room->exit[processed_dir]->u1.to_room
&& can_see_room(mob, mob->in_room->exit[processed_dir]->u1.to_room)
&& !IS_SET(mob->in_room->exit[processed_dir]->exit_info, EX_CLOSED)){
return true;
}
return false;
}
break;
case CHK_ISTHIEF:
return( lval_char != NULL && !IS_NPC(lval_char) && IS_THIEF(lval_char) );
case CHK_ISKILLER:
return( lval_char != NULL && !IS_NPC(lval_char) && IS_KILLER(lval_char) );
/*
* Case 2 begins here: We sneakily use rval to indicate need
* for numeric eval...
*/
case CHK_PEOPLE:
rval = count_people_room( mob, 0 ); break;
case CHK_PLAYERS:
rval = count_people_room( mob, 1 ); break;
case CHK_MOBS:
rval = count_people_room( mob, 2 ); break;
case CHK_CLONES:
rval = count_people_room( mob, 3 ); break;
case CHK_ORDER:
rval = get_order( mob ); break;
case CHK_HOUR:
rval = time_info.hour; break;
case CHK_VALUE:
{
if(is_number(buf)){
rval = atoi(buf);
}else{
mpbug( "Cmd_eval - 'value' if check: non numeric input on line '%s'",
original );
return false;
}
line = one_argument( line, buf );
}
break;
default:;
}
/*
* Case 2 continued: evaluate expression
*/
if ( rval >= 0 )
{
if ( (oper = keyword_lookup( fn_evals, buf )) < 0 )
{
mpbug( "Cmd_eval: prog %d syntax error(2) '%s'",
vnum, original );
return false;
}
one_argument( line, buf );
lval = rval;
rval = atoi( buf );
if( num_eval( lval, oper, rval )){
return true;
}
return false;
}
/*
* Case 3,4,5: Grab actors from $* codes
*/
if ( buf[0] != '$' || buf[1] == '\0' )
{
mpbug( "Cmd_eval: prog %d syntax error(3) '%s'",
vnum, original );
return false;
}
else
code = buf[1];
switch( code )
{
case 'i':
lval_char = mob; break;
case 'n':
lval_char = ch; break;
case 't':
lval_char = vch; break;
case 'r':
lval_char = rch == NULL ? get_random_char( mob ) : rch ; break;
case 'o':
lval_obj = obj1; break;
case 'p':
lval_obj = obj2; break;
case 'q':
lval_char = mob->mprog_target; break;
default:
mpbug( "Cmd_eval: prog %d syntax error(4) '%s'",
vnum, original );
return false;
}
/*
* From now on, we need an actor, so if none was found, bail out
*/
if ( lval_char == NULL && lval_obj == NULL )
return false;
/*
* Case 3: Keyword, comparison and value
*/
switch( check )
{
case CHK_ISPC:
return( lval_char != NULL && !IS_NPC( lval_char ) );
case CHK_ISNPC:
return( lval_char != NULL && IS_NPC( lval_char ) );
case CHK_ISGOOD:
return( lval_char != NULL && IS_GOOD( lval_char ) );
case CHK_ISEVIL:
return( lval_char != NULL && IS_EVIL( lval_char ) );
case CHK_ISNEUTRAL:
return( lval_char != NULL && IS_NEUTRAL( lval_char ) );
case CHK_ISIMMORT:
return( lval_char != NULL && IS_IMMORTAL( lval_char ) );
case CHK_ISCOURT:
// using IS_SET to check for the court flag since when switched it
// shouldn't get the court status of the controling player, but the mob
return( lval_char != NULL && !IS_NPC(lval_char) && IS_COURT(lval_char) );
case CHK_ISQUESTER:
return( lval_char != NULL && !IS_NPC(lval_char) && IS_SET(lval_char->act, PLR_QUESTER) );
case CHK_HASPKTIMER:
return( lval_char != NULL && !IS_NPC(lval_char)
&& (UMAX(lval_char->pknoquit,lval_char->pknorecall)>0) );
case CHK_ISCHARM: /* A relic from MERC 2.2 MOBprograms */
return( lval_char != NULL && IS_AFFECTED( lval_char, AFF_CHARM ) );
case CHK_ISFOLLOW:
return( lval_char != NULL && lval_char->master != NULL
&& lval_char->master->in_room == lval_char->in_room );
case CHK_ISACTIVE:
return( lval_char != NULL && lval_char->position > POS_SLEEPING );
case CHK_ISDELAY:
return( lval_char != NULL && lval_char->mprog_delay > 0 );
case CHK_CHARHERE:
return( lval_char != NULL && lval_char->in_room == mob->in_room);
case CHK_ISVISIBLE:
switch( code )
{
default :
case 'i':
case 'n':
case 't':
case 'r':
case 'q':
return( lval_char != NULL && can_see( mob, lval_char ) );
case 'o':
case 'p':
return( lval_obj != NULL && can_see_obj( mob, lval_obj ) );
}
case CHK_HASTARGET:
return( lval_char != NULL && lval_char->mprog_target != NULL
&& lval_char->in_room == lval_char->mprog_target->in_room );
case CHK_ISTARGET:
return( lval_char != NULL && mob->mprog_target == lval_char );
case CHK_ISSHEATHED:
{
if(lval_char == NULL){
return false;
}
if ( get_eq_char( lval_char, WEAR_SHEATHED ) != NULL ){
return true;
}
return false;
}
case CHK_ISCONCEALED:
{
if(lval_char == NULL){
return false;
}
if ( get_eq_char( lval_char, WEAR_CONCEALED ) != NULL ){
return true;
}
return false;
}
default:;
}
/*
* Case 4: Keyword, actor and value
*/
line = one_argument( line, buf );
switch( check )
{
case CHK_AFFECTED:
{
if(!lval_char){
mpbug("affected check, lval==NULL prob target of check wasn't found.");
return false;
}
if(NO_FLAG!=flag_lookup(buf, affect_flags))
{
if(IS_AFFECTED( lval_char, flag_lookup(buf, affect_flags))){
return true;
}
}
int sn=skill_lookup( buf);
if(sn<0){
mpbug("affected check for '%s', '%s' wasn't found.", buf, buf);
return false;
}
if(is_affected( lval_char, sn)){
return true;
}
return false;
// return( lval_char != NULL
// && IS_SET(lval_char->affected_by, flag_lookup(buf, affect_flags)) );
}
case CHK_ACT:
return( lval_char != NULL
&& IS_SET(lval_char->act, flag_lookup(buf, act_flags)) );
case CHK_ACT2:
return( lval_char != NULL
&& IS_SET(lval_char->act2, flag_lookup(buf, act2_flags)) );
case CHK_IMM:
return( lval_char != NULL
&& IS_SET(lval_char->imm_flags, flag_lookup(buf, imm_flags)) );
case CHK_OFF:
return( lval_char != NULL
&& IS_SET(lval_char->off_flags, flag_lookup(buf, off_flags)) );
case CHK_CARRIES:
if ( is_number( buf ) )
return( lval_char != NULL && has_item( lval_char, (vn_int)atoi(buf), -1, false ) );
else
return( lval_char != NULL && (get_obj_carry( lval_char, buf ) != NULL) );
case CHK_HASTOKEN:
return( lval_char != NULL && has_token( lval_char, buf) );
case CHK_WEARS:
if ( is_number( buf ) )
return( lval_char != NULL && has_item( lval_char, (vn_int)atoi(buf), -1, true ) );
else
return( lval_char != NULL && (get_obj_wear( lval_char, buf ) != NULL) );
case CHK_HAS:
return( lval_char != NULL && has_item( lval_char, -1, (short) item_lookup(buf), false ) );
case CHK_USES:
return( lval_char != NULL && has_item( lval_char, -1, (short) item_lookup(buf), true ));
case CHK_NAME:
switch( code )
{
default :
case 'i':
case 'n':
case 't':
case 'r':
case 'q':
return( lval_char != NULL && is_name( buf, lval_char->name ) );
case 'o':
case 'p':
return( lval_obj != NULL && is_name( buf, lval_obj->name ) );
}
case CHK_EXACT_NAME:
switch( code )
{
default :
case 'i':
case 'n':
case 't':
case 'r':
case 'q':
return( lval_char != NULL && is_exact_name( buf, lval_char->name ) );
case 'o':
case 'p':
return( lval_obj != NULL && is_exact_name( buf, lval_obj->name ) );
}
case CHK_POS:
return( lval_char != NULL && lval_char->position == position_lookup( buf ) );
case CHK_CLAN:
return( lval_char != NULL && lval_char->clan == clan_nlookup( buf ) );
case CHK_RACE:
{
if (race_lookup( buf )==-1 )
return( lval_char != NULL && lval_char->race == 0 );
else
return( lval_char != NULL && lval_char->race == race_lookup( buf ) );
}
case CHK_CLASS:
return( lval_char != NULL && lval_char->clss == class_lookup( buf ) );
case CHK_OBJTYPE:
return( lval_obj != NULL && lval_obj->item_type == item_lookup( buf ) );
case CHK_AREA:
// return( lval_obj != NULL && lval_char->in_room
return( lval_char->in_room
&& lval_char->in_room->area==area_lookup( buf ));
case CHK_SECTOR:
return( lval_char != NULL && lval_char->in_room->sector_type == sector_lookup( buf ));
case CHK_ROOMFLAG:
return( lval_char != NULL
&& IS_SET( lval_char->in_room->room_flags, flag_lookup( buf, room_flags )));
default:;
}
/*
* Case 5: Keyword, actor, comparison and value
*/
if ( (oper = keyword_lookup( fn_evals, buf )) < 0 )
{
mpbug( "Cmd_eval: prog %d syntax error(5): '%s'", vnum, original );
return false;
}
one_argument( line, buf );
rval = atoi( buf );
switch( check )
{
case CHK_VNUM:
switch( code )
{
default :
case 'i':
case 'n':
case 't':
case 'r':
case 'q':
if( lval_char != NULL && IS_NPC( lval_char ) )
lval = lval_char->vnum();
break;
case 'o':
case 'p':
if ( lval_obj != NULL )
lval = lval_obj->pIndexData->vnum;
}
break;
case CHK_HPCNT:
if ( lval_char != NULL ) lval = (lval_char->hit * 100)/(UMAX(1,lval_char->max_hit)); break;
case CHK_ROOM:
if ( lval_char != NULL && lval_char->in_room != NULL )
lval = lval_char->in_room->vnum; break;
case CHK_SEX:
if ( lval_char != NULL ) lval = lval_char->sex; break;
case CHK_LEVEL:
switch( code )
{
default :
case 'i':
case 'n':
case 't':
case 'r':
case 'q':
if ( lval_char != NULL ) lval = lval_char->level;
break;
case 'o':
case 'p':
if ( lval_obj != NULL ) lval = lval_obj->level;
}
break;
case CHK_ALIGN:
if ( lval_char != NULL ) lval = lval_char->alliance; break;
case CHK_MONEY: /* Money is converted to silver... */
if ( lval_char != NULL )
lval = lval_char->gold + (lval_char->silver * 100); break;
case CHK_OBJVAL0:
if ( lval_obj != NULL ) lval = lval_obj->value[0]; break;
case CHK_OBJVAL1:
if ( lval_obj != NULL ) lval = lval_obj->value[1]; break;
case CHK_OBJVAL2:
if ( lval_obj != NULL ) lval = lval_obj->value[2]; break;
case CHK_OBJVAL3:
if ( lval_obj != NULL ) lval = lval_obj->value[3]; break;
case CHK_OBJVAL4:
if ( lval_obj != NULL ) lval = lval_obj->value[4]; break;
case CHK_GRPSIZE:
if( lval_char != NULL ) lval = count_people_room( lval_char, 4 ); break;
case CHK_PKKILLS:
if ( lval_char != NULL ) lval = lval_char->pkkills; break;
case CHK_PKDEFEATS:
if ( lval_char != NULL ) lval = lval_char->pkdefeats; break;
case CHK_KARNS:
if ( lval_char != NULL )
lval = IS_NPC(lval_char)?0:lval_char->pcdata->karns; break;
case CHK_RPS:
if ( lval_char != NULL )
lval = IS_NPC(lval_char)?0:lval_char->pcdata->rp_points; break;
case CHK_DRUNK:
if ( lval_char != NULL )
lval = IS_NPC(lval_char)?0:lval_char->pcdata->condition[COND_DRUNK];
break;
case CHK_FULL:
if ( lval_char != NULL )
lval = IS_NPC(lval_char)?0:lval_char->pcdata->condition[COND_FULL];
break;
case CHK_THIRST:
if ( lval_char != NULL )
lval = IS_NPC(lval_char)?0:lval_char->pcdata->condition[COND_THIRST];
break;
case CHK_HUNGER:
if ( lval_char != NULL )
lval = IS_NPC(lval_char)?0:lval_char->pcdata->condition[COND_HUNGER];
break;
case CHK_TIRED:
if ( lval_char != NULL )
lval = IS_NPC(lval_char)?0:lval_char->pcdata->tired;
break;
case CHK_HASTRAINS:
if ( lval_char != NULL )
lval = lval_char->train;
break;
case CHK_HASPRACS:
if ( lval_char != NULL )
lval = lval_char->practice;
break;
case CHK_RACESIZE:
if ( lval_char != NULL )
lval = IS_NPC(lval_char)?0:race_table[lval_char->race]->size;
break;
default:
return false;
}
if( num_eval( lval, oper, rval )){
return true;
}
return false;
}
/**************************************************************************/
/*
* ------------------------------------------------------------------------
* EXPAND_ARG
* This is a hack of act() in comm.c. I've added some safety guards,
* so that missing or invalid $-codes do not crash the server
* ------------------------------------------------------------------------
*/
void expand_arg(
char *buf, const char *format, char_data *mob, char_data *ch,
const void *arg1, const void *arg2, char_data *rch )
{
const char *someone = "someone";
const char *something = "something";
const char *someones = "someone's";
char fname[MIL];
char_data *vch = (char_data *) arg2;
OBJ_DATA *obj1 = (OBJ_DATA *) arg1;
OBJ_DATA *obj2 = (OBJ_DATA *) arg2;
const char *str;
const char *i;
char *point;
/*
* Discard null and zero-length messages.
*/
if ( format == NULL || format[0] == '\0' )
return;
point = buf;
str = format;
while ( *str != '\0' )
{
if ( *str != '$' )
{
*point++ = *str++;
continue;
}
++str;
switch ( *str )
{
default: bugf( "Expand_arg: bad code %d (%c).", *str, *str);
i = " <@@@> ";
break;
case 'i':
one_argument( mob->name, fname );
i = fname;
break;
case 'I':
i = mob->short_descr;
break;
case 'n':
i = someone;
if ( ch != NULL && can_see( mob, ch ) )
{
one_argument( ch->name, fname );
i = capitalize(fname);
}
break;
case 'N':
i = (ch != NULL && can_see( mob, ch ) )
? (ch->short_descr): someone;
break;
// i = (ch != NULL && can_see( mob, ch ) )
// ? ( IS_NPC( ch ) ? ch->short_descr : ch->name )
// : someone;
// break;
case 't':
i = someone;
if ( vch != NULL && can_see( mob, vch ) )
{
one_argument( vch->name, fname );
i = capitalize(fname);
}
break;
case 'T':
i = (vch != NULL && can_see( mob, vch ))
? ( IS_NPC( vch ) ? vch->short_descr : vch->name )
: someone;
break;
case 'r':
if ( rch == NULL )
rch = get_random_char( mob );
i = someone;
if( rch != NULL && can_see( mob, rch ) )
{
one_argument( rch->name, fname );
i = capitalize(fname);
}
break;
case 'R':
if ( rch == NULL )
rch = get_random_char( mob );
i = ( rch != NULL && can_see( mob, rch ) )
?rch->short_descr:someone;
break;
case 'q':
i = someone;
if ( mob->mprog_target != NULL && can_see( mob, mob->mprog_target ) )
{
one_argument( mob->mprog_target->name, fname );
i = capitalize( fname );
}
break;
case 'Q':
i = (mob->mprog_target != NULL && can_see( mob, mob->mprog_target ))
? ( IS_NPC( mob->mprog_target ) ? mob->mprog_target->short_descr : mob->mprog_target->name )
: someone;
break;
case 'j':
i = he_she [URANGE(0, mob->sex, 2)];
break;
case 'e':
i = (ch != NULL && can_see( mob, ch ))
? he_she [URANGE(0, ch->sex, 2)]
: someone;
break;
case 'E':
i = (vch != NULL && can_see( mob, vch ))
? he_she [URANGE(0, vch->sex, 2)]
: someone;
break;
case 'J':
i = (rch != NULL && can_see( mob, rch ))
? he_she [URANGE(0, rch->sex, 2)]
: someone;
break;
case 'X':
i = (mob->mprog_target != NULL && can_see( mob, mob->mprog_target))
? he_she [URANGE(0, mob->mprog_target->sex, 2)]
: someone;
break;
case 'k':
i = him_her [URANGE(0, mob->sex, 2)];
break;
case 'm':
i = (ch != NULL && can_see( mob, ch ))
? him_her [URANGE(0, ch ->sex, 2)]
: someone;
break;
case 'M':
i = (vch != NULL && can_see( mob, vch ))
? him_her [URANGE(0, vch ->sex, 2)]
: someone;
break;
case 'K':
if ( rch == NULL )
rch = get_random_char( mob );
i = (rch != NULL && can_see( mob, rch ))
? him_her [URANGE(0, rch ->sex, 2)]
: someone;
break;
case 'Y':
i = (mob->mprog_target != NULL && can_see( mob, mob->mprog_target ))
? him_her [URANGE(0, mob->mprog_target->sex, 2)]
: someone;
break;
case 'l':
i = his_her [URANGE(0, mob ->sex, 2)];
break;
case 's':
i = (ch != NULL && can_see( mob, ch ))
? his_her [URANGE(0, ch ->sex, 2)]
: someones;
break;
case 'S':
i = (vch != NULL && can_see( mob, vch ))
? his_her [URANGE(0, vch ->sex, 2)]
: someones;
break;
case 'L':
if ( rch == NULL )
rch = get_random_char( mob );
i = ( rch != NULL && can_see( mob, rch ) )
? his_her [URANGE(0, rch ->sex, 2)]
: someones;
break;
case 'Z':
i = (mob->mprog_target != NULL && can_see( mob, mob->mprog_target ))
? his_her [URANGE(0, mob->mprog_target->sex, 2)]
: someones;
break;
case 'o':
i = something;
if ( obj1 != NULL && can_see_obj( mob, obj1 ) )
{
one_argument( obj1->name, fname );
i = fname;
}
break;
case 'O':
i = (obj1 != NULL && can_see_obj( mob, obj1 ))
? obj1->short_descr
: something;
break;
case 'p':
i = something;
if ( obj2 != NULL && can_see_obj( mob, obj2 ) )
{
one_argument( obj2->name, fname );
i = fname;
}
break;
case 'P':
i = (obj2 != NULL && can_see_obj( mob, obj2 ))
? obj2->short_descr
: something;
break;
case 'V': // saved room
{
static char room_vnum_text[10];
sprintf(room_vnum_text,"%d", mob->mprog_remember_room_vnum);
i = room_vnum_text;
}
break;
}
++str;
while ( ( *point = *i ) != '\0' )
++point, ++i;
}
*point = '\0';
return;
}
/**************************************************************************/
char * mprog_type_to_name ( int type );
/**************************************************************************/
// used for mptrace command
#define MAX_MPTRACE 500 // Number of mobprogs to trace the calls of
int mptrace_current;
int mptrace_calledby[MAX_MPTRACE];
vn_int mptrace_pvnum[MAX_MPTRACE];
vn_int mptrace_mvnum[MAX_MPTRACE];
vn_int mptrace_rvnum[MAX_MPTRACE];
unsigned char mptrace_calllevel[MAX_MPTRACE];
time_t mptrace_time[MAX_MPTRACE];
bool mptrace_aborted[MAX_MPTRACE];
/**************************************************************************/
void do_mptrace(char_data *ch, char *argument )
{
char arg1[MIL];
BUFFER *output;
int lines_to_show;
if (!HAS_SECURITY(ch,1))
{
ch->println("The mptrace command is an olc command, you dont have olc permissions.");
return;
}
if (!HAS_SECURITY(ch,7))
{
ch->println("You must have an OLC security of 7 or higher to use mptrace.");
return;
}
argument = one_argument( argument, arg1 );
if (IS_NULLSTR(arg1))
{
if(ch->lines){
lines_to_show=ch->lines-2;
}else{
lines_to_show=PAGELEN-2; // default page length
}
lines_to_show%=MAX_MPTRACE;
}
else if(is_number( arg1 ))
{
lines_to_show= atoi(arg1);
if (lines_to_show<1)
{
ch->println("Lines to show, increased to 1");
lines_to_show=1;
}
else if(lines_to_show>MAX_MPTRACE)
{
ch->printlnf("Lines to show, decreased to the number logged in the trace - %d",
MAX_MPTRACE);
lines_to_show=MAX_MPTRACE;
}
}
else
{
ch->printf("`RThe only parameter for mptrace must be a numeric value\r\n"
"for the number of lines of the trace you wish to see.`x\r\n");
return;
}
ch->printlnf("Number of trace lines to show=%d", lines_to_show);
output = new_buf();
add_buf(output,"`xMPTrace `G#mobprog`x, `Ymobvnum`x, `Binroom`x\r\n");
// generate our lines of info
int line=mptrace_current+MAX_MPTRACE-lines_to_show;
int count;
int i;
int lc=0;
char buf[MIL];
for( count=0; count<lines_to_show; count++)
{
++line%=MAX_MPTRACE;
if(mptrace_time[line]==0)
continue;
// put the line count and time per line
char * tbuf = ctime( &mptrace_time[line] );
tbuf[str_len(tbuf)-6] = '\0';
sprintf(buf,"[%3d]%s ",++lc, (char *)&tbuf[11]);
add_buf( output, buf);
// indent to represent nested calling
for(i=0; i<mptrace_calllevel[line]; i++){
add_buf( output, " ");
}
sprintf(buf,"`G%5d`x,`Y%5d`x,`B%5d`x, %s\r\n",
mptrace_pvnum[line],
mptrace_mvnum[line],
mptrace_rvnum[line],
mprog_type_to_name(mptrace_calledby[line]));
add_buf( output, buf);
}
ch->sendpage(buf_string(output));
free_buf(output);
}
/**************************************************************************/
DECLARE_DO_FUN( do_mpdzecho ); // Kal
void mpbuggy_prog(MPROG_LIST *program, char * fmt, ...);
/**************************************************************************/
/*
* ------------------------------------------------------------------------
* PROGRAM_FLOW
* This is the program driver. It parses the mob program code lines
* and passes "executable" commands to interpret()
* Lines beginning with 'mob' are passed to mob_interpret() to handle
* special mob commands (in mob_cmds.c)
*-------------------------------------------------------------------------
*/
#define MAX_NESTED_LEVEL 12 /* Maximum nested if-else-endif's (stack size) */
#define BEGIN_BLOCK 0 /* Flag: Begin of if-else-endif block */
#define IN_BLOCK -1 /* Flag: Executable statements */
#define END_BLOCK -2 /* Flag: End of if-else-endif block */
void program_flow( MPROG_LIST *program,
char_data *mob, char_data *ch, const void *arg1, const void *arg2 )
{
char *source=program->prog->code;
static bool init_mptrace=true;
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
if(init_mptrace){
memset(&mptrace_time[0], 0, sizeof(time_t));
init_mptrace=false;
}
if(!program){
bug("program_flow(): program==NULL!!!");
mpbug("program_flow(): program==NULL!!!");
return;
}
vn_int pvnum=program->prog->vnum;
char_data *rch = NULL;
char *code, *line;
char buf[MSL];
char control[MIL], data[MSL];
// moved to be a global
// static int call_level; /* Keep track of nested "mpcall"s */
static time_t last_wiznet;
static int count_since_wiznet;
int level, check;
bool eval;
int state[MAX_NESTED_LEVEL], /* Block state (BEGIN,IN,END) */
cond[MAX_NESTED_LEVEL]; /* Boolean value based on the last if-check */
// moved to be globals
// callstack system written by Kalahn - april 98
// static int callstack_pvnum[MAX_NESTED_LEVEL];
// static int callstack_mvnum[MAX_NESTED_LEVEL];
// if the mob isn't considered valid, don't run progs on it.
if(!mob || !IS_VALID(mob)){
logf("Mob prog %d running on mob vnum %d - which is no longer valid!", pvnum, mob->vnum());
if(mob){
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
}
return;
}
vn_int mvnum = mob->vnum();
callstack_pvnum[call_level]=pvnum;
callstack_mvnum[call_level]=mvnum;
callstack_rvnum[call_level]=mob->in_room->vnum;
callstack_aborted[call_level]= true; // assumed abort till completed
callstack_line[call_level]=0;
++mptrace_current%=MAX_MPTRACE;
mptrace_calledby[mptrace_current]=program->trig_type;
mptrace_pvnum[mptrace_current]=pvnum;
mptrace_mvnum[mptrace_current]=mvnum;
mptrace_rvnum[mptrace_current]=mob->in_room->vnum;
mptrace_time[mptrace_current]=current_time;
mptrace_calllevel[mptrace_current]=call_level;
if( ++call_level > MAX_CALL_LEVEL )
{
char buf[MSL];
int i;
//bug( "MOBprogs: MAX_CALL_LEVEL exceeded, vnum %d", mob->vnum() );
sprintf(buf, "MOBprogs BUG: MAX_CALL_LEVEL exceeded, running prog %d on mob %d",
pvnum, mvnum );
log_string(buf);
mpbug(buf);
// display the call stack
log_string("Mobprog callstack:\n");
for (i=0; i<MAX_CALL_LEVEL;i++)
{
sprintf(buf, "[%2d] MOBprog %d on mob %d (in room %d)", i,
callstack_pvnum[i], callstack_mvnum[i], callstack_rvnum[i]);
log_string(buf);
}
// dont log the bug on wiznet more than once every 5 minutes
if (last_wiznet<current_time-300)
{
mpbug(buf);
// put it on the bug wiznet channel
wiznet(buf,NULL,NULL,WIZ_BUGS,0,AVATAR);
sprintf(buf, "WIZNET_BUGS: In the last 5 mins the bug has happened %d time%s.\r\n",
count_since_wiznet, (count_since_wiznet==1?"":"s") );
wiznet(buf,NULL,NULL,WIZ_BUGS,0,AVATAR);
wiznet("Mobprog callstack:\r\n",NULL,NULL,WIZ_BUGS,0,AVATAR); // put it on the bug wiznet channel
// display the call stack
for (i=0; i<MAX_CALL_LEVEL;i++)
{
sprintf(buf, "[%2d] MOBprog %d on mob %d (in room %d)", i,
callstack_pvnum[i], callstack_mvnum[i], callstack_rvnum[i]);
wiznet(buf,NULL,NULL,WIZ_BUGS,0,AVATAR);
}
wiznet("Use `=Cmpinfo`x to see the info on mobprogs at anytime.\r\n"
"and `=Cmpreset`x to reset the mobprogs (normally once the offending prog is fixed).\r\n",
NULL,NULL,WIZ_BUGS,0,AVATAR);
last_wiznet = current_time;
count_since_wiznet=0;
}else{
count_since_wiznet++;
}
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
return;
}
// check if a prog is disabled
if(program->prog->disabled)
{
char bufmsg[MSL];
if(ch){
sprintf(bufmsg,"Prog %d triggered (%s) on me by '%s', not run cause prog is disabled.",
program->prog->vnum, mprog_type_to_name(program->trig_type), ch->name);
}else{
sprintf(bufmsg,"Prog %d triggered (%s), not run cause prog is disabled.",
program->prog->vnum, mprog_type_to_name(program->trig_type));
}
do_mpdzecho(mob, bufmsg);
call_level--;
callstack_aborted[call_level]= false;
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
return;
}
/*
* Reset "stack"
*/
for ( level = 0; level < MAX_NESTED_LEVEL; level++ )
{
state[level] = IN_BLOCK;
cond[level] = true;
}
level = 0;
code = source;
SET_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
/*
* Parse the MOBprog code
*/
while ( *code && IS_VALID(mob))
{
bool first_arg = true;
char *b = buf, *c = control, *d = data;
/*
* Get a command line. We sneakily get both the control word
* (if/and/or) and the rest of the line in one pass.
*/
while( is_space( *code ) && *code ) {
if(*code == '\n'){
callstack_line[call_level-1]++;
}
code++;
}
while ( *code )
{
if ( *code == '\r' || *code == '\n'){
break; // end of line - dont count the lines here... count them above
}
else if ( is_space(*code) )
{
if ( first_arg ){
first_arg = false;
}else{
*d++ = *code;
}
}
else
{
if ( first_arg ){
*c++ = *code;
}else{
*d++ = *code;
}
}
*b++ = *code++;
}
*b = *c = *d = '\0';
if ( buf[0] == '\0' ){ // end of the source code
break;
}
if ( buf[0] == '*' ){ // Comment - skip to end of line
continue;
}
line = data;
// Match control words
if ( !str_cmp( control, "if" ) )
{
if ( state[level] == BEGIN_BLOCK )
{
mpbuggy_prog(program, "Mobprog: misplaced if statement, mob %d prog %d",
mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
state[level] = BEGIN_BLOCK;
if ( ++level >= MAX_NESTED_LEVEL )
{
mpbuggy_prog(program, "Mobprog: Max nested level exceeded, mob %d prog %d",
mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
if ( level && cond[level-1] == false )
{
cond[level] = false;
continue;
}
line = one_argument( line, control );
bool negate=false;
char *pControl=control;
if(*pControl=='!'){
negate=true;
pControl++; // skip over the !
}
if ( ( check = fn_keyword_lookup( pControl ) ) >= 0 )
{
if(negate){
cond[level] = !cmd_eval( pvnum, line, check, mob, ch, arg1, arg2, rch );
}else{
cond[level] = cmd_eval( pvnum, line, check, mob, ch, arg1, arg2, rch );
}
}
else
{
mpbuggy_prog(program, "Mobprog: invalid if_check (if) '%s', mob %d prog %d", pControl, mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
state[level] = END_BLOCK;
}
else if ( !str_cmp( control, "or" ) )
{
if ( !level || state[level-1] != BEGIN_BLOCK )
{
mpbuggy_prog(program, "Mobprog: or without if, mob %d prog %d", mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
if ( level && cond[level-1] == false ){
continue; // if the parent nest is false, no point in processing the or
}
line = one_argument( line, control );
bool negate=false;
char *pControl=control;
if(*pControl=='!'){
negate=true;
pControl++; // skip over the !
}
if ( ( check = fn_keyword_lookup( pControl ) ) >= 0 )
{
eval = cmd_eval( pvnum, line, check, mob, ch, arg1, arg2, rch );
if(negate){
eval=!eval;
}
}
else
{
mpbuggy_prog(program, "Mobprog: invalid if_check (or), mob %d prog %d", mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
cond[level] = ((bool)eval == true) ? true : cond[level];
}
else if ( !str_cmp( control, "and" ) )
{
if ( !level || state[level-1] != BEGIN_BLOCK )
{
mpbuggy_prog(program, "Mobprog: and without if, mob %d prog %d", mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
if ( level && cond[level-1] == false ) {
continue; // if the parent nest is false, no point in processing the or
}
line = one_argument( line, control );
bool negate=false;
char *pControl=control;
if(*pControl=='!'){
negate=true;
pControl++; // skip over the !
}
if ( ( check = fn_keyword_lookup( pControl ) ) >= 0 )
{
eval = cmd_eval( pvnum, line, check, mob, ch, arg1, arg2, rch );
if(negate){
eval=!eval;
}
}
else
{
mpbuggy_prog(program, "Mobprog: invalid if_check (and), mob %d prog %d", mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
if(!(cond[level] && eval)){
cond[level]=false;
}
}
else if ( !str_cmp( control, "endif" ) )
{
if ( !level || state[level-1] != BEGIN_BLOCK )
{
mpbuggy_prog(program, "Mobprog: endif without if, mob %d prog %d", mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
cond[level] = true;
state[level] = IN_BLOCK;
state[--level] = END_BLOCK;
}
else if ( !str_cmp( control, "else" ) )
{
if ( !level || state[level-1] != BEGIN_BLOCK )
{
mpbuggy_prog(program, "Mobprog: else without if, mob %d prog %d", mvnum, pvnum );
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
if ( level && cond[level-1] == false ) continue;
state[level] = IN_BLOCK;
if(cond[level]){
cond[level]=false;
}else{
cond[level]=true;
}
}
else if ( cond[level] && ( !str_cmp( control, "break" ) || !str_cmp( control, "end" ) ) )
{
call_level--;
callstack_aborted[call_level]= false;
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
REMOVE_BIT(mob->dyn,DYN_RUNNING_MOBPROG_CMD);
return;
}
else if ( (!level || cond[level]) && buf[0] != '\0' )
{
state[level] = IN_BLOCK;
expand_arg( data, buf, mob, ch, arg1, arg2, rch );
if ( !str_cmp( control, "mob" ) ){
// Found a mob restricted command, pass it to mob interpreter
line = one_argument( data, control );
mob_interpret( mob, line );
}else{
//Found a normal mud command, pass it to interpreter
interpret( mob, data );
}
}
}
call_level--;
callstack_aborted[call_level]= false;
REMOVE_BIT(mob->dyn,DYN_MOB_SEE_ALL);
}
/**************************************************************************/
// global place to disable a trigger running due to something about a
// player, something about the trigger or something about the mobprog
// e.g. quester only triggers etc
// true means run the program
bool run_trigger_by_on( MPROG_LIST *prg, char_data *mob, char_data *ch)
{
// doesn't bother with prg yet - may do one day
if(!ch) {
return true;
}
assert(IS_NPC(mob));
// do position checks
if(prg && prg->pos_flags){
if(!IS_SET(prg->pos_flags, (1<<mob->position))){
return false;
}
}
if(!IS_NPC(ch))
{
if(IS_SET(mob->act, ACT_MPIGN_QUESTER) && IS_QUESTER(ch))
return false;
if(IS_SET(mob->act, ACT_MPIGN_NONQUESTER) && !IS_QUESTER(ch))
return false;
}
return true;
}
/**************************************************************************/
/*
* ---------------------------------------------------------------------
* Trigger handlers. These are called from various parts of the code
* when an event is triggered.
* ---------------------------------------------------------------------
*/
/*
* A general purpose string trigger. Matches argument to a string trigger
* phrase.
*/
void mp_act_trigger(
char *argument, char_data *mob, char_data *ch,
const void *arg1, const void *arg2, int type )
{
MPROG_LIST *prg;
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) return;
for ( prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next )
{
if ( prg->trig_type == type
&& (!str_infix(prg->trig_phrase, argument)
|| !str_cmp(prg->trig_phrase, "*always_trigger*")))
{
program_flow( prg, mob, ch, arg1, arg2 );
break;
}
}
return;
}
/**************************************************************************/
bool mp_cmd_trigger(
char *argument, char_data *mob, char_data *ch,
const void *arg1, const void *arg2, int type )
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) return false;
MPROG_LIST *prg;
bool cmdfound = false;
for ( prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next )
{
if ( prg->trig_type == type
&& !str_cmp(prg->trig_phrase, argument))
{
program_flow( prg, mob, ch, arg1, arg2 );
cmdfound = true;
break;
}
}
return cmdfound;
}
/**************************************************************************/
/*
* A general purpose percentage trigger. Checks if a random percentage
* number is less than trigger phrase
*/
bool mp_percent_trigger(
char_data *mob, char_data *ch,
const void *arg1, const void *arg2, int type )
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) return false;
MPROG_LIST *prg;
for ( prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next )
{
if ( prg->trig_type == type
&& number_percent() < atoi( prg->trig_phrase ) )
{
program_flow( prg, mob, ch, arg1, arg2 );
return ( true );
}
}
return ( false );
}
/**************************************************************************/
void mp_bribe_trigger( char_data *mob, char_data *ch, int amount )
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) return;
MPROG_LIST *prg;
MPROG_LIST *highprog=NULL;
int highest=-1;
// first find the most expensive bribe trigger we gave
// the money for - if there are multiple progs
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next )
{
if ( prg->trig_type == TRIG_BRIBE
&& amount >= atoi( prg->trig_phrase ))
{
if(highest< atoi( prg->trig_phrase)){
highest=atoi( prg->trig_phrase);
highprog=prg;
}
}
}
// run the highest prog if one was found
if(highprog){
program_flow( highprog, mob, ch, NULL, NULL );
}
return;
}
/**************************************************************************/
void mp_hour_trigger( char_data *ch )
{
MPROG_LIST *prg;
int hour;
for ( prg = ch->pIndexData->mprogs; prg; prg = prg->next )
{
hour = time_info.hour;
if ( prg->trig_type == TRIG_HOUR
&& ( hour %= 24 ) == atoi( prg->trig_phrase ))
{
program_flow( prg, ch, NULL, NULL, NULL );
return;
}
}
return;
}
/**************************************************************************/
bool mp_exit_trigger_no_letpass=true;
/**************************************************************************/
bool mp_exit_trigger( char_data *ch, int dir )
{
char_data *mob;
MPROG_LIST *prg;
int processed_dir=0;
// exit triggers dont work on wizi LEVEL_IMMORTAL+ imms
if (INVIS_LEVEL(ch)>=LEVEL_IMMORTAL
&& IS_IMMORTAL(ch)){
return false;
}
mp_exit_trigger_no_letpass=true; // use mob letpass to set this to false
mob = ch->in_room->people;
int number_in_room=ch->in_room->number_in_room;
// note: number_in_room is used to prevent a mobprog on two mobs in
// a room creating an endless loop by removing themself from
// the room and putting themselves back in the room - Kal, June 01
for ( ; mob && --number_in_room>=0; mob = mob->next_in_room )
{
if ( IS_NPC( mob )
&& ( HAS_TRIGGER(mob, TRIG_EXIT) || HAS_TRIGGER(mob, TRIG_EXALL) ) )
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) continue;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next )
{
/*
* Exit trigger works only if the mobile is not busy
* (fighting etc.). If you want to be sure all players
* are caught, use ExAll trigger
*/
// check for an all direction trigger
if ( prg->trig_type == TRIG_EXIT || prg->trig_type == TRIG_EXALL)
{
if(is_exact_name("all", prg->trig_phrase)){
if ( prg->trig_type == TRIG_EXIT
&& mob->position == mob->pIndexData->default_pos
&& can_see( mob, ch ) )
{
program_flow( prg, mob, ch, NULL, NULL );
return mp_exit_trigger_no_letpass;
}else{
if ( prg->trig_type == TRIG_EXALL)
{
program_flow( prg, mob, ch, NULL, NULL );
return mp_exit_trigger_no_letpass;
}
}
}
}
// figure out the direction
if ( prg->trig_type == TRIG_EXIT || prg->trig_type == TRIG_EXALL)
{
if(is_number(prg->trig_phrase)){
processed_dir=atoi( prg->trig_phrase );
}else{
processed_dir=dir_lookup(prg->trig_phrase);
}
if(processed_dir<0 || processed_dir>=MAX_DIR){
mpbug("exit_trigger: direction phrase '%s' isn't a valid direction, must be direction number or text version (n,s,ne, southwest,... etc)",
prg->trig_phrase);
continue;
}
}
if ( prg->trig_type == TRIG_EXIT
&& dir == processed_dir
&& mob->position == mob->pIndexData->default_pos
&& can_see( mob, ch ) )
{
program_flow( prg, mob, ch, NULL, NULL );
return mp_exit_trigger_no_letpass;
}else{
if ( prg->trig_type == TRIG_EXALL
&& dir == processed_dir )
{
program_flow( prg, mob, ch, NULL, NULL );
return mp_exit_trigger_no_letpass;
}
}
}
}
}
return false;
}
/**************************************************************************/
// returns true if the trigger would normally be run
bool mp_would_run_give_trigger( char_data *mob, char_data *ch, OBJ_DATA *obj )
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) return false;
char buf[MIL], *p;
MPROG_LIST *prg;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next ){
if ( prg->trig_type == TRIG_GIVE ){
p = prg->trig_phrase;
if ( is_number( p ) ){
// Vnum argument
if ( obj->pIndexData->vnum == atoi(p) ){
return true;
}
}else{
// Object name argument, e.g. 'sword'
while( *p ){
p = one_argument( p, buf );
if ( is_name( buf, obj->name )
|| !str_cmp( "all", buf ) )
{
return true;
}
}
}
}
}
return false;
}
/**************************************************************************/
void mp_give_trigger( char_data *mob, char_data *ch, OBJ_DATA *obj )
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) return;
char buf[MIL], *p;
MPROG_LIST *prg;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next ){
if ( prg->trig_type == TRIG_GIVE )
{
p = prg->trig_phrase;
/*
* Vnum argument
*/
if ( is_number( p ) )
{
if ( obj->pIndexData->vnum == atoi(p) )
{
program_flow(prg, mob, ch, (void *) obj, NULL);
return;
}
}
/*
* Object name argument, e.g. 'sword'
*/
else
{
while( *p )
{
p = one_argument( p, buf );
if ( is_name( buf, obj->name )
|| !str_cmp( "all", buf ) )
{
program_flow(prg, mob, ch, (void *) obj, NULL);
return;
}
}
}
}
}
}
/**************************************************************************/
void mp_greet_trigger( char_data *ch )
{
char_data *mob;
// greet triggers dont work on wizi LEVEL_IMMORTAL+ imms
if (INVIS_LEVEL(ch)>=LEVEL_IMMORTAL
&& IS_IMMORTAL(ch)){
return;
}
mob = ch->in_room->people;
int number_in_room=ch->in_room->number_in_room;
// note: number_in_room is used to prevent a mobprog on two mobs in
// a room creating an endless loop by removing themself from
// the room and putting themselves back in the room - Kal, June 01
for ( ; mob && --number_in_room>=0; mob = mob->next_in_room )
{
if ( IS_NPC( mob ))
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) continue;
// Greet trigger works only if the mobile is in their
// default position and can see the character
// If you want to catch all players, use a GrAll trigger
if ( HAS_TRIGGER( mob,TRIG_GREET )
&& mob->position == mob->pIndexData->default_pos
&& can_see( mob, ch )
&& !IS_AFFECTED(ch, AFF_SNEAK) )
{
mp_percent_trigger( mob, ch, NULL, NULL, TRIG_GREET );
}
// GrAll triggers will greet all players except
// wizi LEVEL_IMMORTAL+ immortals
if ( HAS_TRIGGER( mob, TRIG_GRALL )){
mp_percent_trigger( mob, ch, NULL, NULL, TRIG_GRALL );
}
}
}
return;
}
/**************************************************************************/
void mp_hprct_trigger( char_data *mob, char_data *ch )
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) return;
MPROG_LIST *prg;
for ( prg = mob->pIndexData->mprogs; prg != NULL; prg = prg->next )
if ( ( prg->trig_type == TRIG_HPCNT )
&& ( (100 * mob->hit / mob->max_hit) < atoi( prg->trig_phrase ) ) )
{
program_flow( prg, mob, ch, NULL, NULL );
break;
}
}
/**************************************************************************/
void do_mpreset( char_data *ch, char *)
{
int i;
if (!HAS_SECURITY(ch,1))
{
ch->println("The mpreset command is an olc command, you dont have olc permissions.");
return;
}
if (!HAS_SECURITY(ch,7))
{
ch->println("You must have an OLC security of 7 or higher to use mpreset.");
return;
}
ch->println("Mobprogs reseted. (mobprog callstack depth to 0)");
for (i=0; i<MAX_CALL_LEVEL;i++)
{
callstack_pvnum[i]=0;
callstack_mvnum[i]=0;
callstack_rvnum[i]=0;
callstack_aborted[i]=false;
}
call_level=0;
mpbug( "%s reset the mobprog callstack", TRUE_CH(ch)->name);
return;
}
/**************************************************************************/
void do_mpinfo( char_data *ch, char *argument)
{
char buf[MSL], buf2[MSL];
char arg1 [MIL];
int i;
BUFFER *output;
if (!HAS_SECURITY(ch,1))
{
ch->println("The mpinfo command is an olc command, you dont have olc permissions.");
return;
}
if (!HAS_SECURITY(ch,7))
{
ch->println("You must have an OLC security of 7 or higher to use mpinfo.");
return;
}
output= new_buf();
sprintf( buf,"`?`#%s`x", makef_titlebar("MOBPROGS INFO"));
add_buf(output,buf);
sprintf(buf," Displaying mobprog call abort history - calllevel currently is %d\r\n", call_level);
add_buf(output,buf);
// display the call stack
for (i=0; i<MAX_CALL_LEVEL;i++)
{
if (callstack_aborted[i])
{
sprintf(buf, " [%2d] MOBprog %d on mob %d (in room %d) `RABORTED!!! (BUGGY?)`X\r\n", i,
callstack_pvnum[i], callstack_mvnum[i], callstack_rvnum[i]);
add_buf(output,buf);
}
else
{
sprintf(buf, " [%2d] MOBprog %d on mob %d (in room %d)\r\n", i,
callstack_pvnum[i], callstack_mvnum[i], callstack_rvnum[i]);
add_buf(output,buf);
}
}
add_buf(output,"`xMPBUG `G#mobprog`x, `Ymobvnum`x, `Binroom`x,`rline`x,`Ccall_level`x: `WNOTE: see also `=CMPTRACE`x \r\n");
{ // loop thru finding all disabled progs
MPROG_CODE *prg;
char msgbuf[MSL];
for( prg = mprog_list; prg; prg = prg->next )
{
if ( prg->disabled){
sprintf(msgbuf,"`SMobprog %d is disabled with the following text:`x.\r\n",
prg->vnum);
add_buf(output,msgbuf);
add_buf(output,prg->disabled_text);
add_buf(output,"`x");
}
}
}
// now display the specified number of lines of the log file.
argument = one_argument( argument, arg1 );
if (IS_NULLSTR(arg1))
{
sprintf(buf, "tail " MPBUG_FILE " -n 10");
add_buf(output, "\r\n You can select the number of loglines, type mpinfo <number of lines>");
}
else if (is_number ( arg1 ))
{
int value = atoi(arg1);
if (value<1 || value>20000)
{
add_buf(output,"\r\n`RNumber of lines to tail must be between 1 and 20000.`x\r\n");
sprintf(buf, "tail " MPBUG_FILE " -n 10");
}
else
{
sprintf(buf, "tail " MPBUG_FILE " -n %d", value);
}
}
else
{
add_buf(output, "\r\n`RThe only parameter for this command must be a "
"numeric value\r\nfor the number of lines of the error "
"log you wish to see.`x\r\n");
sprintf(buf, "tail " MPBUG_FILE " -n 10");
}
sprintf( buf2,"\r\n`^%s`x", makef_titlebar("Piping:`x %s", buf));
add_buf(output,buf2);
add_buf(output,get_piperesult(buf));
ch->sendpage(buf_string(output));
free_buf(output);
return;
}
/**************************************************************************/
bool mp_prekill_trigger_result;
/**************************************************************************/
// return true if our triggers stops them
bool mp_prekill_trigger( char_data *mob, char_data *attacker)
{
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, attacker)) return false;
mp_prekill_trigger_result=false;
MPROG_LIST *prg;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next )
{
if ( prg->trig_type == TRIG_PREKILL
&& number_percent() < atoi( prg->trig_phrase ) )
{
program_flow( prg, mob, attacker, NULL, NULL);
return mp_prekill_trigger_result;
}
}
return false;
}
/**************************************************************************/
bool mp_premove_trigger_allowmove=true;
int active_premove_trigger_count=0; // dont allow more than 3
/**************************************************************************/
// return true if they can move - default
bool mp_premove_trigger( char_data *mob, int vnum, int dir)
{
if(active_premove_trigger_count>3){
mpbug("premove_trigger: active_premove_trigger_count is %d - "
"greater than 3, trigger ignored.", active_premove_trigger_count);
return true;
}
active_premove_trigger_count++;
mp_premove_trigger_allowmove=true; // default to allowing movement
// record the vnum of where the mob is moving to
mob->mprog_remember_room_vnum=vnum;
int processed_dir;
MPROG_LIST *prg;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next )
{
// check for an all direction trigger
if ( prg->trig_type == TRIG_PREMOVE)
{
if(is_exact_name("all", prg->trig_phrase)){
program_flow( prg, mob, mob, NULL, NULL );
active_premove_trigger_count--;
return mp_premove_trigger_allowmove;
}
// calculate the direction
if(is_number(prg->trig_phrase)){
processed_dir=atoi( prg->trig_phrase );
}else{
processed_dir=dir_lookup(prg->trig_phrase);
}
if(processed_dir<0 || processed_dir>=MAX_DIR){
mpbug("premove_trigger: direction phrase '%s' isn't a "
"valid direction, must be direction number or "
"text version (n,s,ne, southwest,... etc)",
prg->trig_phrase);
continue;
}
if(processed_dir==dir){
program_flow( prg, mob, mob, NULL, NULL );
active_premove_trigger_count--;
return mp_premove_trigger_allowmove;
}
}
}
active_premove_trigger_count--;
return true;
}
/**************************************************************************/
bool mp_login_trigger_processed=false;
/**************************************************************************/
// mp_login_trigger runs two triggers, loginroom, loginarea.
// if a mob called 'mob loginprocessed' then no more triggers on mobs
// for this login will be run.
// loginroom triggers are run before loginarea
// ch = character logging in, ran just after the player has been placed in the room
void mp_login_trigger( char_data *ch)
{
mp_login_trigger_processed=false; // default to no mob processed the login
if(IS_NPC(ch)) return; // dont trigger on pet logins
assertp(ch->in_room);
assertp(ch->in_room->area);
//loop thru all mobs in the room first - TRIG_LOGINROOM
MPROG_LIST *prg;
char_data *mob, *mob_next_in_room;
for(mob=ch->in_room->people; mob; mob=mob_next_in_room){
mob_next_in_room=mob->next_in_room;
// ** reasons for the prog not to run etc
if(!IS_NPC(mob)) continue;
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) continue;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next )
{
if ( prg->trig_type != TRIG_LOGINROOM
|| number_percent()>atoi( prg->trig_phrase)){
continue;
}
program_flow( prg, mob, ch, NULL, NULL);
if(mp_login_trigger_processed){
return; // a mobprog marked the login as processed
}
}
}
//loop thru all mobs in the area - TRIG_LOGINAREA
char_data *mob_next;
for(mob=char_list; mob; mob=mob_next){
mob_next=mob->next;
// ** reasons for the prog not to run etc
if(!IS_NPC(mob)) continue;
if(!mob->in_room || mob->in_room->area!=ch->in_room->area) continue;
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) continue;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next )
{
if ( prg->trig_type != TRIG_LOGINAREA
|| number_percent()>atoi( prg->trig_phrase)){
continue;
}
program_flow( prg, mob, ch, NULL, NULL);
if(mp_login_trigger_processed){
return; // a mobprog marked the login as processed
}
}
}
}
/**************************************************************************/
bool mp_logout_trigger_processed=false;
/**************************************************************************/
// mp_logout_trigger runs two triggers, logoutroom, logoutarea.
// if a mob called 'mob logoutprocessed' then no more triggers on mobs
// for this logout will be run.
// logoutroom triggers are run before logoutarea
// ch = character logging out, is run just before any of the logout checks - like pknoquit etc
void mp_logout_trigger( char_data *ch)
{
mp_logout_trigger_processed=false; // default to no mob processed the logout
if(IS_NPC(ch)) return; // dont trigger on pet logout
assertp(ch->in_room);
assertp(ch->in_room->area);
//loop thru all mobs in the room first - TRIG_LOGOUTROOM
MPROG_LIST *prg;
char_data *mob, *mob_next_in_room;
for(mob=ch->in_room->people; mob; mob=mob_next_in_room){
mob_next_in_room=mob->next_in_room;
// ** reasons for the prog not to run etc
if(!IS_NPC(mob)) continue;
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) continue;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next )
{
if ( prg->trig_type != TRIG_LOGOUTROOM
|| number_percent()>atoi( prg->trig_phrase)){
continue;
}
program_flow( prg, mob, ch, NULL, NULL);
if(mp_logout_trigger_processed){
return; // a mobprog marked the logout as processed
}
}
}
//loop thru all mobs in the area - TRIG_LOGOUTAREA
char_data *mob_next;
for(mob=char_list; mob; mob=mob_next){
mob_next=mob->next;
// ** reasons for the prog not to run etc
if(!IS_NPC(mob)) continue;
if(!mob->in_room || mob->in_room->area!=ch->in_room->area) continue;
// check for flags on mob and player that might prevent prog being run
if (!run_trigger_by_on( NULL, mob, ch)) continue;
for ( prg = mob->pIndexData->mprogs; prg; prg = prg->next )
{
if ( prg->trig_type != TRIG_LOGOUTAREA
|| number_percent()>atoi( prg->trig_phrase)){
continue;
}
program_flow( prg, mob, ch, NULL, NULL);
if(mp_logout_trigger_processed){
return; // a mobprog marked the logout as processed
}
}
}
}
/**************************************************************************/
/**************************************************************************/