/*___________________________________________________________________________*
)()( DalekenMUD 1.12 (C) 2000 )()(
`][' by Martin Thomson, Lee Brooks, `]['
|| Ken Herbert and David Jacques ||
|| ----------------------------------------------------------------- ||
|| Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, ||
|| David Love, Guilherme 'Willie' Arnold, and Mitchell Tse. ||
|| Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael ||
|| Chastain, Michael Quan, and Mitchell Tse. ||
|| Original Diku Mud copyright (C) 1990, 1991 ||
|| by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt, ||
|| Tom Madsen, and Katja Nyboe. ||
|| ----------------------------------------------------------------- ||
|| Any use of this software must follow the licenses of the ||
|| creators. 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. ||
|| ----------------------------------------------------------------- ||
|| mud_prog.c ||
|| MUDProgram parsing code. Based on N'Atas-ha's MOBPrograms and ||
|| SMAUG's MUDPrograms. ||
*_/<>\_________________________________________________________________/<>\_*/
/****************************************************************************
* This version of MOBprograms use the same basic format as N'Atas-ha with *
* major modifications to the code. Parsing is hopefully tidier and now *
* they should run to a certain extent with variables. I trust you will *
* find these useful and I hope that they will increase the flexibility of *
* your MOBprograms. Note that some of the functions in this file are non- *
* standard, I use OLC and have found that the table lookup system is *
* infinitely useful, these references can be removed if you are careful. *
* --Symposium '98 *
* *
* News Flash! *
* MOBProgs now work more like shell scripts, substitutions and arithmetic *
* are now supported to a certain degree and the variables have been *
* integrated into the parsing. I hope this makes your intelligent mobiles *
* a little smarter. *
* --Sym 3/99 *
* *
* MOBProgs no more, enter MUDPrograms! *
* MUDPrograms include programs for objects and rooms, which are fairly *
* similar to the MOBProgs of old. There are now tons of possibilities for *
* programs, with heaps of extra triggers. There are only a few *
* restrictions on the capability of room and object programs, see the *
* documentation for further info. *
* --Sym 6/99 *
****************************************************************************/
#include <stdarg.h>
#include "mud.h"
#include "olc.h"
#include "regexp.h"
#include "event.h"
/* make this equal to the maximum number of args to any function */
#define MAX_ARGS 4
DECLARE_DO_FUN( mp_aecho ); /* Daleken */
DECLARE_DO_FUN( mp_asound );
DECLARE_DO_FUN( mp_at );
DECLARE_DO_FUN( mp_burrow ); /* Daleken */
DECLARE_DO_FUN( mp_cast ); /* Daleken */
DECLARE_DO_FUN( mp_close ); /* Daleken */
DECLARE_DO_FUN( mp_closepassage ); /* Daleken */
DECLARE_DO_FUN( mp_damage ); /* Daleken */
DECLARE_DO_FUN( mp_delay ); /* Daleken */
DECLARE_DO_FUN( mp_delete ); /* Daleken */
DECLARE_DO_FUN( mp_deposit );
DECLARE_DO_FUN( mp_dream ); /* Daleken */
DECLARE_DO_FUN( mp_echo );
DECLARE_DO_FUN( mp_echoaround );
DECLARE_DO_FUN( mp_echoat );
DECLARE_DO_FUN( mp_force );
DECLARE_DO_FUN( mp_goto );
DECLARE_DO_FUN( mp_grant ); /* Daleken */
DECLARE_DO_FUN( mp_junk );
DECLARE_DO_FUN( mp_kill );
DECLARE_DO_FUN( mp_lock ); /* Daleken */
DECLARE_DO_FUN( mp_mload );
DECLARE_DO_FUN( mp_mset ); /* Daleken */
DECLARE_DO_FUN( mp_oload );
DECLARE_DO_FUN( mp_open ); /* Daleken */
DECLARE_DO_FUN( mp_openpassage ); /* Daleken */
DECLARE_DO_FUN( mp_oset ); /* Daleken */
DECLARE_DO_FUN( mp_otransfer ); /* Daleken */
DECLARE_DO_FUN( mp_peace ); /* Daleken */
DECLARE_DO_FUN( mp_purge );
DECLARE_DO_FUN( mp_qforce ); /* Daleken */
DECLARE_DO_FUN( mp_set ); /* Daleken */
DECLARE_DO_FUN( mp_transfer );
DECLARE_DO_FUN( mp_trigger ); /* Daleken */
DECLARE_DO_FUN( mp_unlock ); /* Daleken */
DECLARE_DO_FUN( mp_withdraw );
#define OP_PAREN '(' /* ( - special */
#define OP_NEGATIVE 'M' /* - */
#define OP_POSITIVE 'P' /* - */
#define OP_INVERSE '~' /* ~ */
#define OP_NOT 'N' /* ! */
#define OP_PLUS '+' /* + */
#define OP_MINUS '-' /* - */
#define OP_MULTIPLY '*' /* * */
#define OP_DIVIDE '/' /* / */
#define OP_MODULUS '%' /* % */
#define OP_XOR '^' /* ^ */
#define OP_OR '|' /* | */
#define OP_AND '&' /* & */
#define OP_SHL '{' /* << */
#define OP_SHR '}' /* >> */
#define OP_LOR 'O' /* || */
#define OP_LAND 'A' /* && */
#define OP_EQUAL '=' /* == */
#define OP_NOTEQUAL '!' /* != */
#define OP_GT '>' /* > */
#define OP_LT '<' /* < */
#define OP_GTE 'G' /* >= */
#define OP_LTE 'L' /* <= */
#define PSTACK_INCR 20 /* initial size of the stack and amount that
* it grows by when it runs out of space. */
typedef struct
{
char *operators;
int *values;
int pos;
int size;
} POLISH_STACK;
struct supermob_data *supermob_list, *supermob_free;
MPROG_LINE *mprog_line_free;
MPROG_GLOBAL *global_progs[ MPROG_GLOBAL_HASH ];
char nul[] = { '\0', '\0' };
MPROG_ACT_LIST *room_act_list;
MPROG_ACT_LIST *obj_act_list;
MPROG_ACT_LIST *mob_act_list;
/*
* Local function prototypes
*/
void prog_process_cmnd args( ( const char *cmnd, MPROG_INFO *info ) );
int mprog_driver args( ( char *com_list, CHAR_DATA *mob,
CHAR_DATA *actor, OBJ_DATA *obj,
void *vo, const char *arg ) );
int prog_driver args( ( char *com_list, MPROG_INFO *info ) );
/* helper functions for parse() */
MPROG_LINE *new_mprog_line args( ( void ) );
MPROG_LINE *split_lines args( ( const char *commands ) );
void discard_lines args( ( MPROG_LINE *head ) );
MPROG_STACK *new_stack args( ( void ) );
void add_stack args( ( MPROG_STACK *st, int value ) );
int remove_stack args( ( MPROG_STACK *st ) );
void set_stack args( ( MPROG_STACK *st, int value ) );
int current_stack args( ( MPROG_STACK *st ) );
void delete_stack args( ( MPROG_STACK *st ) );
void copy_line args( ( char *dest, const char *src ) );
/* helper functions for parse_number() */
bool isoperator args( ( char *op ) );
int operate args( ( int left, char operator, int right ) );
char get_unary args( ( char c ) );
const char *next_word args( ( const char *str, char *target ) );
const char *next_operator args( ( const char *str, char *target ) );
const char *next_number args( ( const char *str, int *target ) );
int parse_number args( ( const char *str, MPROG_INFO *info ) );
void fix_string_arg args( ( char *dest, char *src, MPROG_INFO *info ) );
int prog_number_func args( ( char *point, MPROG_INFO *info ) );
char *prog_string_func args( ( char *target, char *point,
MPROG_INFO *info ) );
const char *matching_parentheses args( ( const char *str ) );
/* miscellaneous */
void expand_to_eol args( ( char *t, const char *src, MPROG_INFO *info ) );
char *get_num_args args( ( char *buf, const char *trigger,
const char *str ) );
MPROG_DATA *prog_keyword_check args( ( char *arg, MPROG_DATA *mprg,
MPROG_INFO *info, int type ) );
CHAR_DATA *new_supermob args( ( void ) );
bool is_time args( ( char *p, PLANE_DATA *plane ) );
bool comm_trig_aux args( ( MPROG_DATA *mprg, int *progtypes,
const char *command, MPROG_INFO *info ) );
/* variable stuff */
char *get_program_var args( ( MPROG_INFO *info, const char *name ) );
void new_local_var args( ( MPROG_INFO *info, const char *name,
const char *value ) );
void set_local_var args( ( MPROG_INFO *info, const char *name,
const char *value ) );
void delete_local_var args( ( MPROG_INFO *info, const char *name ) );
/*
* Find the proper global mud prog.
*/
MPROG_GLOBAL *get_global_mudprog_index( int vnum )
{
MPROG_GLOBAL *mprg;
if( vnum < 0 )
return NULL;
for( mprg = global_progs[vnum % MPROG_GLOBAL_HASH];
mprg; mprg = mprg->next )
{
if( mprg->vnum == vnum )
return mprg;
}
return NULL;
}
MPROG_GLOBAL *get_global_prog( MPROG_DATA *mprg )
{
MPROG_GLOBAL *glob = NULL;
if( mprg->type == GLOBAL_PROG )
{
if( !is_number( mprg->arglist ) )
bug( "Bad vnum for global prog (%s).", mprg->arglist );
else
glob = get_global_mudprog_index( atoi( mprg->arglist ) );
}
return glob;
}
/*************************************************************************
* Central command interpreter for special mob commands.
*/
const struct
{
const char *const name;
DO_FUN *do_fun;
} prog_command_table[] = {
{ "asound", mp_asound },
{ "at", mp_at },
{ "burrow", mp_burrow },
{ "cast", mp_cast },
{ "close", mp_close },
{ "damage", mp_damage },
{ "delay", mp_delay },
{ "delete", mp_delete },
{ "dream", mp_dream },
{ "echo", mp_echo },
{ "echoat", mp_echoat },
{ "echoaround", mp_echoaround },
{ "force", mp_force },
{ "goto", mp_goto },
{ "grant", mp_grant },
{ "junk", mp_junk },
{ "kill", mp_kill },
{ "lock", mp_lock },
{ "mload", mp_mload },
{ "mset", mp_mset },
{ "oload", mp_oload },
{ "open", mp_open },
{ "oset", mp_oset },
{ "otransfer", mp_otransfer },
{ "passageclose", mp_closepassage },
{ "passageopen", mp_openpassage },
{ "peace", mp_peace },
{ "purge", mp_purge },
{ "qforce", mp_qforce },
{ "set", mp_set, },
{ "transfer", mp_transfer },
{ "trigger", mp_trigger },
{ "unlock", mp_unlock },
{ "unset", mp_delete },
{ "", do_credits }
};
bool prog_only_cmnd( CHAR_DATA *ch, const char *argument )
{
char command[MAX_INPUT_LENGTH];
bool found = FALSE;
int cmd;
argument = one_argument( argument, command );
if( !str_cmp( "cmd", command ) )
return FALSE;
for( cmd = 0; prog_command_table[cmd].name[0] != '\0'; cmd++ )
{
if( command[0] == prog_command_table[cmd].name[0]
&& !str_prefix( command, prog_command_table[cmd].name ) )
{
found = TRUE;
break;
}
}
if( !found )
return FALSE;
sprintf( last_command, "MP[%5d] %s in [%5d] %s: %s %s",
IS_NPC( ch ) ? ch->pIndexData->vnum : 0,
IS_NPC( ch ) ? ch->short_descr : ch->name,
ch->in_room ? ch->in_room->vnum : 0,
ch->in_room ? ch->in_room->name : "(not in a room)",
command, argument );
( *prog_command_table[cmd].do_fun ) ( ch, argument );
strcat( last_command, " (Finished)" );
return TRUE;
}
/* This procedure simply copies the cmnd to a buffer while expanding
* any variables by calling the translate procedure.
*/
void prog_process_cmnd( const char *cmnd, MPROG_INFO *info )
{
char buf[MAX_INPUT_LENGTH];
const char *p;
struct supermob_data *sup;
expand_to_eol( buf, cmnd, info );
if( !str_prefix( "sub ", buf ) )
{
if( ( sup = get_supermob( info->mob ) ) )
{
if( sup->prog_obj )
bug( "Supermob for object %d: calling 'sub'",
sup->prog_obj->pIndexData->vnum );
else if( sup->prog_room )
bug( "Supermob for room %d: calling 'sub'",
sup->prog_room->vnum );
else
bug( "Supermob is calling 'sub' on it's own!" );
return;
}
else
{
sprintf( buf, "%d", mprog_sub_trigger( info->mob, info->actor, &buf[4] ) );
set_local_var( info, "subreturn", buf );
}
}
else if( !str_prefix( "local ", buf ) )
{
char var[MAX_INPUT_LENGTH];
p = first_arg( buf, var, FALSE ); /* discard 'local' */
p = first_arg( p, var, FALSE );
set_local_var( info, var, p );
}
else if( !str_prefix( "regmatch ", buf ) )
{
regexp *preg;
int i, len;
bool match = TRUE;
char patbuf[MAX_INPUT_LENGTH];
p = one_argument( buf, patbuf ); /* dump the 'regmatch' */
p = one_argument( p, patbuf ); /* get the pattern (quoted) */
if( !( preg = regcomp( patbuf, 1 ) ) )
match = FALSE;
if( !regexec( preg, &buf[p - buf] ) )
match = FALSE;
for( i = 0; i < NSUBEXP; ++i )
{
char name[MAX_INPUT_LENGTH];
char value[MAX_INPUT_LENGTH];
len = preg->endp[i] - preg->startp[i];
sprintf( name, "r%d", i );
if( !match || len <= 0 )
delete_local_var( info, name );
else
{
strncpy( value, preg->startp[i], len );
value[len] = '\0';
set_local_var( info, name, value );
}
}
free( preg );
}
else if( !prog_only_cmnd( info->mob, buf ) )
interpret( info->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.
*/
int mprog_driver( char *com_list, CHAR_DATA *mob, CHAR_DATA *actor,
OBJ_DATA *obj, void *vo, const char *arg )
{
MPROG_INFO info;
char buf[MAX_STRING_LENGTH];
int retval;
if( IS_AFFECTED( mob, AFF_CHARM ) )
return 0;
info.local = NULL;
info.mob = mob;
info.actor = actor;
info.obj = obj;
info.vo = vo;
if( arg )
strcpy( buf, arg );
else
buf[0] = '\0';
set_local_var( &info, "trigger", buf );
info.rndm = NULL;
retval = prog_driver( com_list, &info );
delete_all_locals( &info );
return retval;
}
int prog_driver( char *com_list, MPROG_INFO *info )
{
CHAR_DATA *vch;
int count = 0, retval;
bool prog_allready = FALSE;
if( IS_AFFECTED( info->mob, AFF_CHARM ) )
return 0;
/* get a random visable mortal player who is in the room with the mob */
for( vch = info->mob->in_room->people; vch; vch = vch->next_in_room )
if( !IS_NPC( vch )
&& vch->level < MAX_LEVEL - 3
&& can_see( info->mob, vch ) )
{
if( number_range( 0, count ) == 0 )
info->rndm = vch;
count++;
}
if( xIS_SET( info->mob->act, ACT_MUDPROG ) )
prog_allready = TRUE;
xSET_BIT( info->mob->act, ACT_MUDPROG );
/*
* NOTE: the parsing function uses a backwards replace to enable it to
* reverse along the com_list, a '\0' is searched for and replaced
* with a '\n' so the command list can be reused, thus a '\0' needs
* to be placed before the first line.
* --Symposium
*/
retval = parse( com_list, info, NULL, 0 );
if( !prog_allready )
xREMOVE_BIT( info->mob->act, ACT_MUDPROG );
return retval;
}
MPROG_LINE *new_mprog_line()
{
MPROG_LINE *line;
if( mprog_line_free )
{
line = mprog_line_free;
mprog_line_free = mprog_line_free->next;
}
else
{
line = alloc_perm( sizeof( MPROG_LINE ) );
}
line->next = NULL;
return line;
}
/*
* Places each line from a multiline mud program into separate
* MPROG_LINE objects. Additionally it removes lines starting with
* a '#' character to reduce strain on the parser.
*/
MPROG_LINE *split_lines( const char *commands )
{
MPROG_LINE *head, *current;
head = new_mprog_line();
head->line = commands;
commands = strchr( commands, '\n' );
if( !commands )
return head;
while( isspace( *commands ) )
commands++;
current = head;
while( commands[0] != '\0' )
{
if( commands[0] != '#' ) /* Remove comments */
{
current->next = new_mprog_line();
current = current->next;
current->line = commands;
}
commands = strchr( commands, '\n' );
while( isspace( *commands ) )
commands++;
}
return head;
}
void discard_lines( MPROG_LINE *head )
{
MPROG_LINE *tmp;
for( tmp = head; tmp && tmp->next; tmp = tmp->next )
;
tmp->next = mprog_line_free;
mprog_line_free = head;
}
MPROG_STACK *new_stack()
{
MPROG_STACK *st = alloc_mem( sizeof( MPROG_STACK ) );
st->max_size = 5;
st->stack = alloc_mem( st->max_size * sizeof( int ) );
st->current = -1;
return st;
}
void add_stack( MPROG_STACK *st, int value )
{
++st->current;
if( st->current >= st->max_size )
{
int *tmp;
st->max_size += 5;
tmp = alloc_mem( st->max_size * sizeof( int ) );
memcpy( tmp, st->stack, ( st->max_size - 10 ) * sizeof( int ) );
free_mem( st->stack, st->max_size * sizeof( int ) );
st->stack = tmp;
}
st->stack[st->current] = value;
}
int remove_stack( MPROG_STACK *st )
{
int tmp;
tmp = st->stack[st->current];
if( st->current >= 0 )
st->current--;
return tmp;
}
void set_stack( MPROG_STACK *st, int value )
{
st->stack[st->current] = value;
}
int current_stack( MPROG_STACK *st )
{
if( st->current < 0 )
return 1; /* 1 means executing... */
return st->stack[st->current];
}
void delete_stack( MPROG_STACK *st )
{
free_mem( st->stack, st->max_size * sizeof( int ) );
free_mem( st, sizeof( MPROG_STACK ) );
}
void copy_line( char *dest, const char *src )
{
while( *src && *src != '\n' )
*dest++ = *src++;
*dest = '\0';
}
/*
* The brains of the operation.
*
* The stack we use here maintains some sort of reference as to where we are.
* Each entry is an integer (32 bits) and this integer is split into fields.
*
* BIT_00 - the execute flag, TRUE if we are actually running code.
* BIT_01 - ifdone flag, TRUE when the if statement has already had code
* executed some time before (for else if control).
* BIT_02 to BIT_07 - while loop counter, this is 0 for an 'if' and
* 1 - 63 for a 'while' statement.
* BIT_08 and onward - the line number of the start of the current block.
*/
int parse( const char *commands, MPROG_INFO *info,
MPROG_STACK *stack, int line_no )
{
MPROG_LINE *head, *current;
char linebuf[MAX_INPUT_LENGTH];
int i, retval = 1;
bool ifchk_last = FALSE;
/*
* Break the command up into separate lines and find the line we
* should start on.
*/
head = split_lines( commands );
current = head;
for( i = 0; i < line_no && current; ++i )
current = current->next;
/*
* Initialise the stack object if we don't already have one.
*/
if( stack == NULL )
stack = new_stack();
/*
* For each line, check for special command,
* act accordingly.
*/
for( ; current; current = current->next, line_no++ )
{
char cmd[MAX_INPUT_LENGTH];
const char *rest;
int tmp;
copy_line( linebuf, current->line );
rest = one_argument( linebuf, cmd );
/*
* Multiple expansion, might come in handy for some tricksters.
*/
while( !str_cmp( cmd, "eval" ) )
{
char tmp[MAX_INPUT_LENGTH];
strcpy( tmp, rest );
expand_to_eol( linebuf, tmp, info );
rest = one_argument( linebuf, cmd );
}
if( !str_cmp( cmd, "if" ) )
{
if( IS_SET( current_stack( stack ), 0x01 ) )
{
tmp = parse_number( rest, info );
if( tmp == NO_FLAG )
{
progbug( info->mob, "parse: bad if statement, line %d.",
line_no + 1 );
break;
}
add_stack( stack, ( line_no << 8 ) | ( tmp ? 1 : 0 ) );
}
else
add_stack( stack, ( line_no << 8 ) );
}
else if( ifchk_last && !str_cmp( cmd, "or" ) )
{
if( !IS_SET( current_stack( stack ), 0x01 ) )
{
tmp = parse_number( rest, info );
if( tmp == NO_FLAG )
{
progbug( info->mob, "parse: bad or statement, line %d.",
line_no + 1);
break;
}
if( tmp )
set_stack( stack, current_stack( stack ) | 1 );
}
}
else if( !str_cmp( cmd, "else" ) )
{
if( current_stack( stack ) & 0xFC )
{
progbug(
info->mob, "parse: else found, endwhile expected, line %d.",
line_no + 1 );
break;
}
/*
* Conditions that must be satisfied in order for the block to be
* executed:
* - The outer block must be running.
* - The 'ifdone' flag (0x02) must not be set for the statement.
* - If an if check exists it must be passed.
*/
tmp = remove_stack( stack );
if( IS_SET( current_stack( stack ), 0x01 ) )
{
if( IS_SET( tmp, 0x01 ) || IS_SET( tmp, 0x02 ) )
add_stack( stack, ( tmp | 0x02 ) & ~0x01 );
else
{
rest = one_argument( rest, cmd );
if( !str_cmp( cmd, "if" ) )
{
tmp = parse_number( rest, info );
if( tmp == NO_FLAG )
{
progbug( info->mob, "parse: bad else if statement, line %d.",
line_no + 1 );
break;
}
add_stack( stack, ( line_no << 8 ) | ( tmp ? 1 : 0 ) );
}
else
add_stack( stack, current_stack( stack ) | 0x01 );
}
}
else
add_stack( stack, tmp & ~0x01 );
}
else if( !str_cmp( cmd, "endif" ) )
{
if( current_stack( stack ) & 0xFC )
{
progbug(
info->mob, "parse: endif found, endwhile expected, line %d",
line_no + 1 );
break;
}
remove_stack( stack );
}
else if( !str_cmp( cmd, "while" ) )
{
if( IS_SET( current_stack( stack ), 0x01 ) )
{
tmp = parse_number( rest, info );
if( tmp == NO_FLAG )
{
progbug( info->mob, "parse: bad while statement, line %d.",
line_no + 1 );
break;
}
add_stack( stack, ( line_no << 8 ) | 0x04 | ( tmp ? 1 : 0 ) );
}
else
add_stack( stack, ( line_no << 8 ) | 0x04 );
}
else if( !str_cmp( cmd, "endwhile" ) )
{
int old_stack;
MPROG_LINE *start_line;
int tmp_line_no = 0;
if( !( current_stack( stack ) & 0xFC ) )
{
progbug(
info->mob, "parse: endwhile found, else/endif expected, line %d.",
line_no + 1 );
break;
}
old_stack = remove_stack( stack );
if( IS_SET( old_stack, 0x01 ) )
{
for( start_line = head;
tmp_line_no < ( old_stack >> 8 ) && start_line;
start_line = start_line->next )
tmp_line_no++;
if( !start_line )
{
progbug(
info->mob, "parse: while expected, program must have changed, line %d.",
( old_stack >> 8 ) + 1 );
break;
}
copy_line( linebuf, start_line->line );
rest = one_argument( linebuf, cmd );
if( str_cmp( cmd, "while" ) )
{
progbug(
info->mob, "parse: while expected, program must have changed, line %d.",
tmp_line_no + 1 );
break;
}
tmp = parse_number( rest, info );
if( tmp == NO_FLAG )
{
progbug( info->mob, "parse: bad while statement, line %d.",
line_no + 1 );
break;
}
if( tmp )
{
/* Check for overflow of the loop counter. */
if( !( ( old_stack + 0x04 ) & 0xFC ) )
{
progbug(
info->mob, "parse: looping too much, line %d.",
tmp_line_no + 1 );
break;
}
add_stack( stack, old_stack + 0x04 );
line_no = tmp_line_no;
current = start_line;
}
}
}
else if( !str_cmp( cmd, "break" ) )
{
if( IS_SET( current_stack( stack ), 0x01 ) )
{
int count = 1;
if( rest[0] != '\0' )
count = parse_number( rest, info );
if( count == NO_FLAG )
{
progbug( info->mob, "parse: bad number for break, line %d.",
line_no + 1 );
break;
}
for( tmp = stack->current; count > 0 && tmp >= 0; tmp-- )
{
stack->stack[tmp] &= ~0x01;
if( stack->stack[tmp] & 0xFC )
count--;
}
}
}
else if( !str_cmp( cmd, "wait" ) )
{
MPROG_CALLBACK *callback = alloc_mem( sizeof( MPROG_CALLBACK ) );
struct supermob_data *sup;
EVENT *e;
bool ticks = FALSE;
if( !str_prefix( "ticks ", rest ) )
{
ticks = TRUE;
rest = one_argument( rest, cmd );
}
else if( !str_prefix( "seconds ", rest )
|| !str_prefix( "sec ", rest ) )
rest = one_argument( rest, cmd );
tmp = parse_number( rest, info );
if( tmp < 0 || tmp == NO_FLAG )
{
progbug( info->mob, "parse: bad wait statement, line %d.",
line_no + 1 );
break;
}
/*
* Copy all the values from the old into the new.
* Since we also copy the variables, ensure that they
* don't get deleted after this function exits with the
* old info struct by making the old one point to nothing.
*/
callback->commands = commands;
memcpy( &callback->info, info, sizeof( MPROG_INFO ) );
info->local = NULL;
callback->stack = stack;
callback->line_no = line_no + 1;
if( ticks )
tmp *= PULSE_TICK;
else
tmp *= PULSE_PER_SECOND;
tmp = number_fuzzy( tmp );
/* find which type of thing caused the program and then register
an event to finish this program */
sup = get_supermob( info->mob );
if( sup )
{
if( sup->prog_obj )
e = create_obj_event( sup->prog_obj, evn_prog_wait,
tmp );
else if( sup->prog_room )
e = create_room_event( sup->prog_room, evn_prog_wait,
tmp );
else
{
progbug( info->mob, "Cannot register 'wait' event." );
break;
}
}
else
e = create_char_event( info->mob, evn_prog_wait,
tmp );
e->extra.type = TARGET_VOID;
e->extra.target.typeless = callback;
discard_lines( head );
return retval;
}
else if( !str_cmp( cmd, "halt" ) )
{
if( IS_SET( current_stack( stack ), 0x01 ) )
break;
}
else if( !str_cmp( cmd, "retval" ) )
{
if( IS_SET( current_stack( stack ), 0x01 ) )
retval = parse_number( rest, info );
}
else if( !str_cmp( cmd, "return" ) )
{
if( IS_SET( current_stack( stack ), 0x01 ) )
{
if( rest[0] != '\0' )
retval = parse_number( rest, info );
break;
}
}
else if( IS_SET( current_stack( stack ), 0x01 ) )
prog_process_cmnd( linebuf, info );
if( !str_cmp( cmd, "if" ) || ( ifchk_last && !str_cmp( cmd, "or" ) ) )
ifchk_last = TRUE;
else
ifchk_last = FALSE;
}
discard_lines( head );
delete_stack( stack );
return retval;
}
/***************************************************************************
* Value parser utilises the variable system below to set values.
* Allows for functions and variables as well as integers.
* Variables and functions must start with alphabet characters but they
* may contain digits and _'s.
*/
/*
* Takes an alphanumeric sequence from 'str' and puts it in 'target'.
* Returns a pointer to after where it finished.
*/
const char *next_word( const char *str, char *target )
{
const char *p;
while( isalpha( *str ) || isdigit( *str ) || *str == '_' || *str == '.' )
*target++ = *str++;
while( isspace( *str ) && *str != '\n' && *str != '\r' )
++str;
if( *str == '(' )
{
p = str;
str = matching_parentheses( str );
if( !str )
{
bug( "Too many parentheses or badly constructed statement." );
str = &str_empty[0];
}
++str;
while( p <= str )
*target++ = *p++;
}
*target = '\0';
return str;
}
/*
* Translate a character into the proper unary operator code. Returns 0 if
* this isn't a unary operator.
*/
char get_unary( char c )
{
switch( c )
{
case '+':
return OP_POSITIVE;
case '-':
return OP_NEGATIVE;
case '~':
return OP_INVERSE;
case '!':
return OP_NOT;
default:
return 0;
}
}
/*
* Gets the next operator.
* Takes the string and places a one character symbol for the operator
* in 'target', returns what is left of the string.
*/
const char *next_operator( const char *str, char *target )
{
*target = 0;
switch( *str++ )
{
case '+':
*target = OP_PLUS;
break;
case '-':
*target = OP_MINUS;
break;
case '*':
*target = OP_MULTIPLY;
break;
case '/':
*target = OP_DIVIDE;
break;
case '%':
*target = OP_MODULUS;
break;
case '^':
*target = OP_XOR;
break;
case '|':
if( *str == '|' )
{
str++;
*target = OP_LOR;
}
else
*target = OP_OR;
break;
case '&':
if( *str == '&' )
{
str++;
*target = OP_LAND;
}
else
*target = OP_AND;
break;
case '<':
if( *str == '<' )
{
str++;
*target = OP_SHL;
}
else if( *str == '=' )
{
str++;
*target = OP_LTE;
}
else
*target = OP_LT;
break;
case '>':
if( *str == '>' )
{
str++;
*target = OP_SHR;
}
else if( *str == '=' )
{
str++;
*target = OP_GTE;
}
else
*target = OP_GT;
break;
case '=':
if( *str == '=' )
{
++str;
*target = OP_EQUAL;
}
break;
case '!':
if( *str == '=' )
{
++str;
*target = OP_NOTEQUAL;
}
break;
}
return str;
}
/* Check if the next bit of the string is an operator. */
bool isoperator( char *op )
{
char tgt;
(void)next_operator( op, &tgt );
return tgt ? TRUE : FALSE;
}
/*
* Compare the precedence of two operators.
*/
int operatorcmp( char op1, char op2 )
{
int i;
struct
{
char op;
char val;
} precedence[] = {
{ OP_PAREN, 0 },
{ OP_LOR, 1 },
{ OP_LAND, 2 },
{ OP_LT, 3 }, { OP_GT, 3 }, { OP_LTE, 3 }, { OP_GTE, 3 },
{ OP_EQUAL, 3 }, { OP_NOTEQUAL, 3 },
{ OP_OR, 4 }, { OP_AND, 4 }, { OP_XOR, 4 },
{ OP_PLUS, 4 }, { OP_MINUS, 4 },
{ OP_SHL, 5 }, { OP_SHR, 5 },
{ OP_MULTIPLY, 6 }, { OP_DIVIDE, 6 }, { OP_MODULUS, 6 },
{ OP_POSITIVE, 7, }, { OP_NEGATIVE, 7 },
{ OP_INVERSE, 7 }, { OP_NOT, 7 },
{ '\0', 0 },
};
if( op1 == op2 )
return 0;
for( i = 0; precedence[i].op != '\0'; ++i )
{
if( precedence[i].op == op1 )
op1 = precedence[i].val;
else if( precedence[i].op == op2 )
op2 = precedence[i].val;
}
if( op1 < op2 )
return -1;
else if( op1 == op2 )
return 0;
return 1;
}
/*
* Given an operator code, perform a mathematical operation.
*/
int operate( int left, char operator, int right )
{
switch( operator )
{
default:
case OP_PLUS:
return left + right;
case OP_MINUS:
return left - right;
case OP_MULTIPLY:
return left * right;
case OP_DIVIDE:
if( right == 0 )
return 0;
return left / right;
case OP_MODULUS:
if( right == 0 )
return 0;
return left % right;
case OP_OR:
return left | right;
case OP_XOR:
return left ^ right;
case OP_AND:
return left & right;
case OP_LOR:
return ( left || right ) ? 1 : 0;
case OP_LAND:
return ( left && right ) ? 1 : 0;
case OP_EQUAL:
return ( left == right ) ? 1 : 0;
case OP_NOTEQUAL:
return ( left != right ) ? 1 : 0;
case OP_LT:
return ( left < right ) ? 1 : 0;
case OP_LTE:
return ( left <= right ) ? 1 : 0;
case OP_GT:
return ( left > right ) ? 1 : 0;
case OP_GTE:
return ( left >= right ) ? 1 : 0;
case OP_SHL:
return left << right;
case OP_SHR:
return left >> right;
}
}
/*
* From 'str', gets a number and puts it in 'target'.
* Returns a pointer to str where it left off.
*/
const char *next_number( const char *str, int *target )
{
bool negative = FALSE;
*target = 0;
if( *str == '+' )
++str;
else if( *str == '-' )
{
++str;
negative = TRUE;
}
while( isdigit( *str ) )
*target = *target * 10 + *str++ - '0';
if( negative )
*target *= -1;
return str;
}
/* Initialise a stack variable */
POLISH_STACK *pstack_init( void )
{
POLISH_STACK *s = alloc_mem( sizeof( POLISH_STACK ) );
s->size = PSTACK_INCR;
s->values = alloc_mem( s->size * ( sizeof( int ) + sizeof( char ) ) );
s->operators = (char *)s->values + ( s->size * sizeof( int ) );
s->pos = -1;
return s;
}
/* Deallocation the stack */
void pstack_free( POLISH_STACK *s )
{
free_mem( s->values, s->size * ( sizeof( int ) + sizeof( char ) ) );
free_mem( s, sizeof( POLISH_STACK ) );
}
/* Push the operator/value pair onto the stack. This function automatically
* grows a full stack object */
void pstack_push( POLISH_STACK *s, char operator, int val )
{
char *tmp_ptr;
if( ++s->pos >= s->size )
{
tmp_ptr = alloc_mem( ( s->size + PSTACK_INCR )
* ( sizeof( int ) + sizeof( char ) ) );
memcpy( (char *)tmp_ptr, (char *)s->values, s->size * sizeof( int ) );
memcpy( (char *)tmp_ptr + ( ( s->size + PSTACK_INCR ) * sizeof( int ) ),
(char *)s->values + ( s->size * sizeof( int ) ),
s->size );
free_mem( s->values, s->size * ( sizeof( int ) + sizeof( char ) ) );
s->size += PSTACK_INCR;
s->values = (int *)tmp_ptr;
s->operators = tmp_ptr + ( s->size * sizeof( int ) );
}
s->operators[s->pos] = operator;
s->values[s->pos] = val;
}
/* Grab the last entry off the stack */
void pstack_pop( POLISH_STACK *s, char *operator, int *val )
{
if( s->pos < 0 )
{
bug( "Popping an empty number stack.\n" );
return;
}
*operator = s->operators[s->pos];
*val = s->values[s->pos--];
}
/* Grab the last operator off the stack */
char pstack_pop_op( POLISH_STACK *s )
{
if( s->pos < 0 )
{
bug( "Popping an empty number stack.\n" );
return '+';
}
return s->operators[s->pos--];
}
/* Grab the last value off the stack */
int pstack_pop_val( POLISH_STACK *s )
{
if( s->pos < 0 )
{
bug( "Popping an empty number stack.\n" );
return 0;
}
return s->values[s->pos--];
}
/* Parse a number using a reverse polish parser. */
int parse_number( const char *str, MPROG_INFO *info )
{
POLISH_STACK *s_val = pstack_init();
POLISH_STACK *s_op = pstack_init();
char buf[MAX_INPUT_LENGTH];
bool need_op = FALSE;
char tmp_op;
int tmp_val;
while( str && *str )
{
while( isspace( *str ) && *str != '\n' && *str != '\r' )
++str;
if( *str == '(' )
{
pstack_push( s_op, OP_PAREN, 0 );
++str;
need_op = FALSE;
}
else if( *str == ')' )
{
while( s_op->operators[s_op->pos] != OP_PAREN )
{
pstack_pop( s_op, &tmp_op, &tmp_val );
pstack_push( s_val, tmp_op, tmp_val );
if( s_op->pos < 0 )
{
progbug( info->mob, "Unmatched ')'\n" );
str = NULL;
return NO_FLAG;
}
}
pstack_pop( s_op, &tmp_op, &tmp_val );
++str;
need_op = TRUE;
}
else if( isdigit( *str ) )
{
str = next_number( str, &tmp_val );
pstack_push( s_val, '\0', tmp_val );
need_op = TRUE;
}
else if( isalpha( *str ) ) /* string, function/variable */
{
str = next_word( str, buf );
if( str )
{
if( strchr( buf, '(' ) ) /* parse function */
{
tmp_val = prog_number_func( buf, info );
if( tmp_val == NO_FLAG )
{
progbug( info->mob, "Illegal number from %s.", buf );
tmp_val = 0;
}
}
else /* mob variable */
{
if( *get_mob_var( info->mob, buf ) == '\0' )
tmp_val = 0;
else if( !is_number( get_mob_var( info->mob, buf ) ) )
{
progbug( info->mob, "Variable %s isn't a number.", buf );
tmp_val = 0;
}
else
tmp_val = atoi( get_mob_var( info->mob, buf ) );
}
pstack_push( s_val, '\0', tmp_val );
need_op = TRUE;
}
}
else if( !need_op && get_unary( *str ) )
{
pstack_push( s_op, get_unary( *str++ ), 0 );
}
else /* an operator */
{
char curr_op;
str = next_operator( str, &curr_op );
while( s_op->pos >= 0
&& operatorcmp( s_op->operators[s_op->pos], curr_op ) >= 0 )
{
pstack_push( s_val, pstack_pop_op( s_op ), 0 );
}
pstack_push( s_op, curr_op, 0 );
need_op = FALSE;
}
}
while( str && s_val->pos >= 0 )
{
pstack_pop( s_val, &tmp_op, &tmp_val );
pstack_push( s_op, tmp_op, tmp_val );
}
while( str && s_op->pos >= 0 )
{
int reg_a, reg_b;
pstack_pop( s_op, &tmp_op, &tmp_val );
switch( tmp_op )
{
case '\0':
pstack_push( s_val, '\0', tmp_val );
break;
case OP_POSITIVE: /* NOP */
break;
case OP_NEGATIVE:
pstack_push( s_val, '\0', -pstack_pop_val( s_val ) );
break;
case OP_INVERSE:
pstack_push( s_val, '\0', ~pstack_pop_val( s_val ) );
break;
case OP_NOT:
pstack_push( s_val, '\0', !pstack_pop_val( s_val ) );
break;
default:
reg_b = pstack_pop_val( s_val );
reg_a = pstack_pop_val( s_val );
pstack_push( s_val, '\0', operate( reg_a, tmp_op, reg_b ) );
break;
}
}
tmp_val = str ? pstack_pop_val( s_val ) : NO_FLAG;
pstack_free( s_val );
pstack_free( s_op );
return tmp_val;
}
void fix_string_arg( char *dest, char *src, MPROG_INFO *info )
{
char *p;
p = strchr( src, '\0' );
while( isspace( *(--p) ) )
*p = '\0';
if( src[0] == '"' )
{
*p = '\0';
strcpy( dest, src + 1 );
}
else if( strchr( src, '(' ) && strchr( src, ')' ) )
prog_string_func( dest, src, info );
else if( !str_cmp( src, "*" ) )
strcpy( dest, get_program_var( info, "trigger" ) );
else if( isdigit( src[0] ) )
get_num_args( dest, get_program_var( info, "trigger" ), src );
else
strcpy( dest, get_program_var( info, src ) );
}
/*------------------------------------------------------------------------
parse a number function in the form:
<name> ( [ argv1 ] [ , argv2 ] [ , argv3 ] ... )
<name> is the name of the function
[ argv1 ] optional first argument
[ , argv2/3 ] optional second/third argument
Note that substitution is done on the arguments,
see note below on string funcs.
*/
int prog_number_func( char *point, MPROG_INFO *info )
{
CHAR_DATA *mob = info->mob;
CHAR_DATA *actor = info->actor;
OBJ_DATA *obj = info->obj;
CHAR_DATA *vict = (CHAR_DATA *)info->vo;
OBJ_DATA *v_obj = (OBJ_DATA *)info->vo;
CHAR_DATA *rndm = info->rndm;
CHAR_DATA *tarchar = NULL;
OBJ_DATA *tarobj = NULL;
char func[MAX_INPUT_LENGTH];
char *funcpt = func;
char argv[MAX_ARGS][MAX_INPUT_LENGTH];
char *argvpt;
char *p;
int i;
/* Crash saver using magic numbers, thanks Erwin A. */
if( vict && vict->magic == MAGIC_NUM_CHAR )
v_obj = NULL;
else
vict = NULL;
if( !point || *point == '\0' )
{
progbug( mob, "null number function" );
return NO_FLAG;
}
/* skip leading spaces */
while( *point == ' ' )
point++;
/* get whatever comes before the left paren.. ignore spaces */
while( *point && *point != '(' )
if( *point == '\0' )
{
progbug( mob, "number function syntax error" );
return NO_FLAG;
}
else if( *point == ' ' )
point++;
else
*funcpt++ = *point++;
*funcpt = '\0';
point++;
/*
* Extract arguments.
*/
for( i = 0; i < MAX_ARGS; ++i )
{
char buf[20];
char *lastbrace;
/* skip leading spaces */
while( *point == ' ' )
point++;
argvpt = &argv[i][0];
lastbrace = buf;
*lastbrace++ = ')';
while( lastbrace - 1 > buf || ( *point != ')' && *point != ',' ) )
{
if( *point == '\0' )
{
progbug( mob, "number func syntax error" );
return NO_FLAG;
}
if( *point == '(' )
*lastbrace++ = ')';
else if( *point == '[' )
*lastbrace++ = ']';
else if( *point == '{' )
*lastbrace++ = '}';
else if( *point == *(lastbrace - 1) )
lastbrace--;
*argvpt++ = *point++;
}
*argvpt = '\0';
if( *point == ')' )
{
++i;
break;
}
point++;
}
for( ; i < MAX_ARGS; ++i )
argv[i][0] = '\0';
/* Special case function. */
if( !str_cmp( func, "exist" ) )
{
switch( argv[0][1] ) /* arg should be "$*" so just get the letter */
{
case 'i': return 1;
case 'n':
return ( actor ) ? 1 : 0;
case 't':
return ( vict ) ? 1 : 0;
case 'r':
return ( rndm ) ? 1 : 0;
case 'o':
return ( obj ) ? 1 : 0;
case 'p':
return ( v_obj ) ? 1 : 0;
default:
progbug( mob, "bad argument to '%s'", func );
return NO_FLAG;
}
}
/*
* Find default '$x' type targets.
*/
if( argv[0][0] == '$' && argv[0][2] == '\0' )
{
switch( argv[0][1] )
{
case 'i': tarchar = mob; break;
case 'n': tarchar = actor; break;
case 't': tarchar = vict; break;
case 'r': tarchar = rndm; break;
case 'o': tarobj = obj; break;
case 'p': tarobj = v_obj; break;
default:
progbug( mob, "bad argument to '%s'", func );
return NO_FLAG;
}
}
else if( argv[0][0] == '\0' )
tarchar = mob;
/*
* Substitutions.
*/
for( i = 0; i < MAX_ARGS; ++i )
{
char buf[MAX_INPUT_LENGTH];
if( argv[i][0] == '\0' )
break;
strcpy( buf, argv[i] );
expand_to_eol( argv[i], buf, info );
}
if( !tarchar && !tarobj )
{
tarchar = get_char_world( mob, argv[0] );
tarobj = get_obj_here( mob, argv[0] );
}
/*
* General functions
*/
if( !str_cmp( func, "rand" ) )
{
if( argv[0][0] != '\0' && is_number( argv[0] )
&& argv[1][0] == '\0' )
return number_range( 1, atoi( argv[0] ) );
if( is_number( argv[1] ) )
return number_range( atoi( argv[0] ), atoi( argv[1] ) );
return number_percent();
}
if( !str_cmp( func, "dice" ) )
{
if( argv[0][0] != '\0' && is_number( argv[0] )
&& argv[1][0] != '\0' && is_number( argv[1] ) )
return dice( atoi( argv[0] ), atoi( argv[1] ) );
return NO_FLAG;
}
if( !str_cmp( func, "time" ) )
{
if( !str_cmp( argv[0], "day" ) )
return mob->in_room->area->plane->time.day;
if( !str_cmp( argv[0], "month" ) )
return mob->in_room->area->plane->time.month;
if( !str_cmp( argv[0], "year" ) )
return mob->in_room->area->plane->time.year;
return mob->in_room->area->plane->time.hour;
}
if( !str_cmp( func, "economy" ) )
return mob->in_room->area->economy;
if( !str_cmp( func, "mobinarea" ) )
return mob->in_room->area->nmobile;
if( !str_cmp( func, "people" ) )
{
ROOM_INDEX_DATA *pRoom = NULL;
int num = 0;
if( tarchar )
pRoom = tarchar->in_room;
else if( tarobj )
pRoom = tarobj->in_room;
else if( argv[0][0] != '\0' )
pRoom = find_location( mob, argv[0] );
if( !pRoom )
pRoom = mob->in_room;
for( vict = pRoom->people; vict; vict = vict->next_in_room )
if( !vict->deleted )
num++;
return num;
}
if( !str_cmp( func, "mobs" ) )
{
ROOM_INDEX_DATA *pRoom = NULL;
int num = 0;
if( tarchar )
pRoom = tarchar->in_room;
else if( tarobj )
pRoom = tarobj->in_room;
else if( argv[0][0] != '\0' )
pRoom = find_location( mob, argv[0] );
if( !pRoom )
pRoom = mob->in_room;
for( vict = pRoom->people; vict; vict = vict->next_in_room )
if( !vict->deleted && IS_NPC( vict ) )
num++;
return num;
}
if( !str_cmp( func, "players" ) )
{
ROOM_INDEX_DATA *pRoom = NULL;
int num = 0;
if( tarchar )
pRoom = tarchar->in_room;
else if( tarobj )
pRoom = tarobj->in_room;
else if( argv[0][0] != '\0' )
pRoom = find_location( mob, argv[0] );
if( !pRoom )
pRoom = mob->in_room;
for( vict = pRoom->people; vict; vict = vict->next_in_room )
if( !vict->deleted && !IS_NPC( vict ) )
num++;
return num;
}
if( !str_cmp( func, "playerinarea" ) )
return mob->in_room->area->nplayer;
if( !str_cmp( func, "timeskilled" ) )
{
if( mob->pIndexData )
return mob->pIndexData->killed;
return 0;
}
if( !str_cmp( func, "isexit" ) )
{
int door;
if( !mob->in_room )
return NO_FLAG;
if( ( door = find_door( mob, argv[0] ) ) >= 0
&& mob->in_room->exit[door] )
return 1;
return 0;
}
if( !str_cmp( func, "isclear" ) )
{
int door;
if( !mob->in_room )
return NO_FLAG;
if( ( door = find_door( mob, argv[0] ) ) >= 0
&& mob->in_room->exit[door]
&& !IS_SET( mob->in_room->exit[door]->exit_info, EX_CLOSED ) )
return 1;
return 0;
}
if( !str_cmp( func, "islocked" ) )
{
int door;
if( !mob->in_room )
return NO_FLAG;
if( ( door = find_door( mob, argv[0] ) ) >= 0
&& mob->in_room->exit[door]
&& IS_SET( mob->in_room->exit[door]->exit_info, EX_LOCKED ) )
return 1;
return 0;
}
/* Locate a character or object. */
if( !str_prefix( "find", func ) )
{
if( !str_cmp( func, "findchar" ) )
return tarchar ? 1 : 0;
if( !str_cmp( func, "findobj" ) )
{
if( argv[1][0] == '\0' )
return tarobj ? 1 : 0;
if( !tarchar )
return 0;
return get_obj_list( mob, argv[1], tarchar->carrying ) ? 1 : 0;
}
}
/*
* Character targeted functions.
*/
if( tarchar )
{
if( !str_cmp( func, "ispc" ) )
return ( !IS_NPC( tarchar ) ? 1 : 0 );
if( !str_cmp( func, "uid" ) )
return tarchar->unique_key;
if( !str_cmp( func, "isgood" ) )
return ( IS_GOOD( tarchar ) ? 1 : 0 );
if( !str_cmp( func, "isevil" ) )
return ( IS_EVIL( tarchar ) ? 1 : 0 );
if( !str_cmp( func, "isimmort" ) )
return ( get_trust( tarchar ) >= LEVEL_IMMORTAL ) ? 1 : 0;
if( !str_cmp( func, "ischarmed" ) )
return ( IS_AFFECTED( tarchar, AFF_CHARM ) ) ? 1 : 0;
if( !str_cmp( func, "hitprcnt" ) )
return 100 * tarchar->hit / tarchar->max_hit;
if( !str_cmp( func, "inroom" ) )
return tarchar->in_room->vnum;
if( !str_cmp( func, "sex" ) )
return tarchar->sex;
if( !str_cmp( func, "position" ) )
return tarchar->position;
if( !str_cmp( func, "level" ) )
return get_trust( tarchar );
if( !str_cmp( func, "wait" ) )
return tarchar->wait;
if( !str_cmp( func, "class" ) )
return tarchar->class;
if( !str_cmp( func, "race" ) )
return tarchar->race;
if( !str_cmp( func, "origclass" ) )
return get_first_class( tarchar );
if( !str_cmp( func, "goldamt" ) )
return tarchar->gold;
if( !str_cmp( func, "isopen" ) )
return ( IS_NPC( tarchar ) && tarchar->pIndexData->pShop
&& tarchar->in_room->area->plane->time.hour
>= tarchar->pIndexData->pShop->open_hour
&& tarchar->in_room->area->plane->time.hour
<= tarchar->pIndexData->pShop->close_hour )
? 1 : 0;
if( !str_cmp( func, "isflying" ) )
return ( IS_SET( tarchar->body_parts, BODY_PART_WINGS )
|| IS_AFFECTED( tarchar, AFF_FLYING ) ) ? 1 : 0;
if( !str_cmp( func, "isoutlaw" ) )
{
if( IS_NPC( tarchar ) )
{
progbug( mob, "checking outlaw on NPC." );
return NO_FLAG;
}
return xIS_SET( tarchar->act, PLR_OUTLAW ) ? 1 : 0;
}
if( !str_cmp( func, "hp" ) )
return tarchar->hit;
if( !str_cmp( func, "maxhp" ) )
return tarchar->max_hit;
if( !str_cmp( func, "mana" ) )
return total_mana( tarchar->mana );
if( !str_prefix( "mana", func ) )
{
int which = flag_value( NULL, magic_flags, &func[4] );
which = URANGE( 0, which, MAGIC_MAX - 1 );
return tarchar->mana[which];
}
if( !str_cmp( func, "maxmana" ) )
return total_mana( tarchar->max_mana );
if( !str_prefix( "maxmana", func ) )
{
int which = flag_value( NULL, magic_flags, &func[4] );
which = URANGE( 0, which, MAGIC_MAX - 1 );
return tarchar->max_mana[which];
}
if( !str_cmp( func, "move" ) )
return tarchar->move;
if( !str_cmp( func, "maxmove" ) )
return tarchar->max_move;
if( !str_cmp( func, "str" ) )
return get_curr_str( tarchar );
if( !str_cmp( func, "int" ) )
return get_curr_int( tarchar );
if( !str_cmp( func, "wis" ) )
return get_curr_wis( tarchar );
if( !str_cmp( func, "dex" ) )
return get_curr_dex( tarchar );
if( !str_cmp( func, "con" ) )
return get_curr_con( tarchar );
if( !str_prefix( "magic", func ) )
{
int which = flag_value( NULL, magic_flags, &func[4] );
return get_magic( tarchar, URANGE( 0, which, 4 ) );
}
if( !str_cmp( func, "getskill" ) )
{
int sn;
if( !argv[1] || argv[1][0] == '\0'
|| ( sn = skill_lookup( argv[1] ) ) < 0 )
{
progbug( mob, "Bad skill name '%s' for '%s'.",
argv[1], func );
return NO_FLAG;
}
if( IS_NPC( tarchar ) )
{
progbug( mob, "%s: target character '%s' NPC.",
func, tarchar->name );
return NO_FLAG;
}
return tarchar->pcdata->learned[sn];
}
if( !str_cmp( func, "getsuccess" ) )
{
int sn, scale = 100;
if( !argv[1] || argv[1][0] == '\0'
|| ( sn = skill_lookup( argv[1] ) ) < 0 )
{
progbug( mob, "Bad skill name '%s' for '%s'.",
argv[1], func );
return NO_FLAG;
}
if( argv[2] && is_number( argv[2] ) )
scale = URANGE( 0, atoi( argv[2] ), 500 );
return get_success( tarchar, sn, scale );
}
if( !str_cmp( func, "isfight" ) )
{
CHAR_DATA *ch2;
if( argv[1][0] == '\0' )
return ( ( tarchar->fighting ) ? 1 : 0 );
ch2 = get_char_world( mob, argv[0] );
if( !ch2 )
return NO_FLAG;
return ( ( tarchar->fighting == ch2 ) ? 1 : 0 );
}
if( !str_cmp( func, "cansee" ) )
{
CHAR_DATA *ch2;
if( argv[1][0] == '\0' )
return ( can_see( mob, tarchar ) ? 1 : 0 );
ch2 = get_char_world( mob, argv[0] );
if( !ch2 )
return NO_FLAG;
return ( can_see( tarchar, ch2 ) ? 1 : 0 );
}
if( !str_cmp( func, "isfollow" ) )
{
CHAR_DATA *ch2;
if( argv[1][0] == '\0' )
return ( ( tarchar->master
&& tarchar->master->in_room == tarchar->in_room )
? 1 : 0 );
ch2 = get_char_world( mob, argv[0] );
if( !ch2 )
return NO_FLAG;
return ( ( tarchar->master == ch2
&& tarchar->master->in_room == tarchar->in_room )
? 1 : 0 );
}
if( !str_cmp( func, "isaffected" ) )
{
int vect[MAX_VECTOR];
if( argv[1][0] == '\0' )
return NO_FLAG;
if( flag_value( vect, affect_flags, argv[1] ) == NO_FLAG )
return NO_FLAG;
for( i = 0; i < MAX_VECTOR; ++i )
if( ( tarchar->affected_by[i] | vect[i] ) )
return 1;
return 0;
}
if( !str_cmp( func, "bodypart" ) )
{
int part;
if( argv[1][0] == '\0' )
return NO_FLAG;
if( flag_value( &part, body_part_flags, argv[1] ) == NO_FLAG )
return NO_FLAG;
return ( tarchar->body_parts | part ) ? 1 : 0;
}
if( !str_cmp( func, "variable" ) )
{
/* second argument follows same rules as the str?? functions */
fix_string_arg( argv[2], argv[1], info );
p = get_mob_var( tarchar, argv[2] );
return atoi( p );
}
if( !str_cmp( func, "sub" ) )
{
for( i = 2; i < MAX_ARGS; ++i )
{
strcat( argv[1], " " );
strcat( argv[1], argv[i] );
}
return mprog_sub_trigger( tarchar, actor, argv[1] );
}
}
/*
* Object target functions.
*/
if( tarobj )
{
if( !str_cmp( func, "uid" ) )
return tarobj->unique_key;
if( !str_cmp( func, "objtype" ) )
return tarobj->item_type;
if( !str_cmp( func, "objval" ) )
{
int ind = 0;
ind = argv[1][0] - '0';
if( ind < 0 || ind >= 4 )
{
progbug( mob, "bad argument to '%s': %s",
func, argv[1] );
return NO_FLAG;
}
return tarobj->value[ind];
}
}
if( !str_cmp( func, "vnum" ) )
{
if( tarobj )
return tarobj->pIndexData->vnum;
else if( tarchar && tarchar->pIndexData )
return tarchar->pIndexData->vnum;
return NO_FLAG;
}
if( !str_cmp( func, "isvar" ) )
{
MPROG_VAR * var;
for( var = mob->variables; var; var = var->next )
if( !str_cmp( var->name, argv[0] ) )
return 1;
return 0;
}
/*
* String functions.
* These all start with "str" and have the following rules:
* 1) plain strings are assumed to be the name of a variable.
* 2) a double quote signifies a literal string, this assumes that
* the last character is also a quote and this is chopped off,
* normal '$' substitutions are performed on these strings.
* 3) anything with a pair of parentheses is considered a string
* function.
*
* e.g. target - the value of the variable 'target'
* "hello $n" - hello xxx, with xxx being the actor's name
* name($n) - the actor's full name, from prog_string_func()
*/
if( !str_prefix( "str", func ) )
{
fix_string_arg( argv[2], argv[0], info );
fix_string_arg( argv[3], argv[1], info );
if( !str_cmp( func, "strcmp" ) )
return !str_cmp( argv[2], argv[3] ) ? 1 : 0;
if( !str_cmp( func, "strprefix" ) )
return !str_prefix( argv[2], argv[3] ) ? 1 : 0;
if( !str_cmp( func, "strinfix" ) )
return str_infix( argv[2], argv[3] ) ? 0 : 1;
if( !str_cmp( func, "strkey" ) )
return is_name( argv[2], argv[3] ) ? 1 : 0;
if( !str_cmp( func, "strlen" ) )
return strlen( argv[2] );
}
/*
* Miscellaneous string-to-constant functions.
*/
fix_string_arg( argv[2], argv[0], info );
strcpy( argv[0], argv[2] );
fix_string_arg( argv[3], argv[1], info );
strcpy( argv[1], argv[3] );
if( !str_cmp( func, "speclookup" ) )
return spec_lookup( argv[0] );
if( !str_cmp( func, "slookup" ) )
return skill_lookup( argv[0] );
if( !str_cmp( func, "racelookup" ) )
return race_lookup( argv[0] );
if( !str_cmp( func, "table" ) /* enable table(name,value) */
|| !str_cmp( func, "lookup" ) ) /* enable lookup(name,value) */
{
strcpy( func, argv[0] );
strcpy( argv[0], argv[1] );
}
if( !str_prefix( "table-", func ) ) /* enable table-name(value) */
strcpy( func, func + 6 );
for( i = 0; bit_table[i].structure; ++i )
{
int vect[MAX_VECTOR];
if( !str_cmp( func, bit_table[i].command ) )
return flag_value( vect, bit_table[i].structure, argv[0] );
}
/* 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)
*/
progbug( mob, "unknown function '%s'", func );
return NO_FLAG;
}
char *prog_string_func( char *target, char *point, MPROG_INFO *info )
{
CHAR_DATA *mob = info->mob;
CHAR_DATA *actor = info->actor;
OBJ_DATA *obj = info->obj;
CHAR_DATA *vict = (CHAR_DATA *)info->vo;
OBJ_DATA *v_obj = (OBJ_DATA *)info->vo;
CHAR_DATA *rndm = info->rndm;
CHAR_DATA *tarchar = NULL;
OBJ_DATA *tarobj = NULL;
char func[MAX_INPUT_LENGTH];
char *funcpt = func;
char argv[MAX_ARGS][MAX_INPUT_LENGTH];
char *argvpt;
int i;
/* Crash protection using magic numbers, thanks to Erwin A. */
if( vict && vict->magic == MAGIC_NUM_CHAR )
v_obj = NULL;
else
vict = NULL;
if( !point || *point == '\0' )
{
progbug( mob, "string func null function" );
return strcpy( target, "" );
}
/* skip leading spaces */
while( *point == ' ' )
point++;
/* get whatever comes before the left paren.. ignore spaces */
while( *point && *point != '(' )
if( *point == '\0' )
{
progbug( mob, "string func syntax error" );
return strcpy( target, "" );
}
else if( *point == ' ' )
point++;
else
*funcpt++ = *point++;
*funcpt = '\0';
point++;
for( i = 0; i < MAX_ARGS; ++i )
{
char buf[20];
char *lastbrace;
/* skip leading spaces */
while( isspace( *point ) )
point++;
argvpt = &argv[i][0];
lastbrace = buf;
*lastbrace++ = ')';
while( lastbrace - 1 > buf || ( *point != ')' && *point != ',' ) )
{
if( *point == '\0' )
{
progbug( mob, "string func syntax error" );
return strcpy( target, "" );
}
if( *point == '(' )
*lastbrace++ = ')';
else if( *point == '[' )
*lastbrace++ = ']';
else if( *point == '{' )
*lastbrace++ = '}';
else if( *point == *(lastbrace - 1) )
lastbrace--;
*argvpt++ = *point++;
}
*argvpt = '\0';
if( *point == ')' )
{
++i;
break;
}
point++;
}
for( ; i < MAX_ARGS; ++i )
argv[i][0] = '\0';
if( argv[0][0] == '$' && argv[0][2] == '\0' )
{
switch( argv[0][1] )
{
case 'i': tarchar = mob; break;
case 'n': tarchar = actor; break;
case 't': tarchar = vict; break;
case 'r': tarchar = rndm; break;
case 'o': tarobj = obj; break;
case 'p': tarobj = v_obj; break;
default:
progbug( mob, "bad argument to '%s'", func );
return strcpy( target, "" );
}
}
for( i = 0; i < MAX_ARGS; ++i )
{
char buf[MAX_INPUT_LENGTH];
if( argv[i][0] == '\0' )
break;
strcpy( buf, argv[i] );
expand_to_eol( argv[i], buf, info );
}
if( !tarchar && !tarobj )
{
tarchar = get_char_world( mob, argv[0] );
tarobj = get_obj_here( mob, argv[0] );
}
if( tarchar )
{
if( !str_cmp( func, "name" ) )
return strcpy( target, tarchar->name );
if( !str_cmp( func, "short" ) )
return strcpy( target, tarchar->short_descr );
if( !str_cmp( func, "long" ) )
return strcpy( target, tarchar->long_descr );
if( !str_cmp( func, "pers" ) )
{
CHAR_DATA *ch2;
if( argv[1][0] == '\0' )
return strcpy( target, PERS( tarchar, mob ) );
ch2 = get_char_world( mob, argv[0] );
if( !ch2 )
{
progbug(
mob, "strfunc: 2nd argument to pers '%s' isn't right.",
argv[1] );
return strcpy( target, "" );
}
return strcpy( target, PERS( tarchar, ch2 ) );
}
}
if( tarobj )
{
if( !str_cmp( func, "name" ) )
return strcpy( target, tarobj->name );
if( !str_cmp( func, "short" ) )
return strcpy( target, tarobj->short_descr );
if( !str_cmp( func, "long" ) )
return strcpy( target, tarobj->description );
if( !str_cmp( func, "action" ) )
return strcpy( target, tarobj->action );
}
if( !str_cmp( func, "table" ) )
{
int val, vect[MAX_VECTOR];
val = prog_number_func( argv[1], info );
if( val == NO_FLAG )
{
progbug( mob, "strfunc: 2nd argument to table '%s' isn't a number.",
argv[1] );
return strcpy( target, "" );
}
vzero( vect );
vect[0] = val;
if( !str_cmp( argv[0], "class" ) )
return strcpy( target, class_table[URANGE( 0, val,
MAX_CLASS - 1 )].name );
if( !str_cmp( argv[0], "race" ) )
return strcpy( target, race_table[URANGE( 0, val, MAX_RACE - 1 )].name );
if( !str_cmp( argv[0], "skill" ) )
return strcpy( target, skill_table[URANGE( 0, val, MAX_SKILL - 1 )].name );
if( !str_cmp( argv[0], "spec" ) )
return strcpy( target, spec_table[val].spec_name );
for( i = 0; bit_table[i].structure; ++i )
{
if( !str_cmp( argv[0], bit_table[i].command ) )
return strcpy( target, flag_string( bit_table[i].structure,
vect ) );
}
progbug( mob, "strfunc: unknown table '%s'", argv[0] );
return strcpy( target, "" );
}
progbug( mob, "strfunc: unknown function" );
return strcpy( target, "" );
}
const char *matching_parentheses( const char *str )
{
char bracebuf[32];
char *lastbrace = &bracebuf[0];
switch( *str )
{
case '(':
*lastbrace++ = ')'; break;
case '[':
*lastbrace++ = ']'; break;
case '{':
*lastbrace++ = '}'; break;
}
while( *str != '\0'
&& lastbrace > bracebuf && lastbrace < bracebuf + 32 )
{
str++;
if( *str == '\\' )
str++;
else if( *str == '"' )
*lastbrace = '"';
else if( *str == '(' )
*lastbrace++ = ')';
else if( *str == '[' )
*lastbrace++ = ']';
else if( *str == '{' )
*lastbrace++ = '}';
else if( *str == *(lastbrace - 1) )
lastbrace--;
}
if( *str == '\0' || lastbrace >= bracebuf + 32 )
return "";
return str;
}
/*
* Substitution.
* Turns all those $ codes into something more readable.
*/
void expand_to_eol( char *t, const char *src, MPROG_INFO *info )
{
static const char *he_she[] = { "it", "he", "she" };
static const char *him_her[] = { "it", "him", "her" };
static const char *his_her[] = { "its", "his", "her" };
CHAR_DATA *mob = info->mob;
CHAR_DATA *actor = info->actor;
OBJ_DATA *obj = info->obj;
CHAR_DATA *vict = (CHAR_DATA *)info->vo;
OBJ_DATA *v_obj = (OBJ_DATA *)info->vo;
CHAR_DATA *rndm = info->rndm;
char buf[MAX_INPUT_LENGTH];
const char *p;
bool byuid;
/* Crash protection using magic numbers, thanks to Erwin A.
Such a wonderful hack, only possible in C, but not neccessary
in other languages. */
if( vict && vict->magic == MAGIC_NUM_CHAR )
v_obj = NULL;
else
vict = NULL;
while( *src != '\0' && *src != '\n' )
{
char key;
if( *src == '\\' )
{
src++;
*t++ = *src++;
continue;
}
if( *src != '$' )
{
*t++ = *src++;
continue;
}
src++;
key = *src++;
byuid = FALSE;
if( key == '{' || key == '[' || key == '(' )
{
p = matching_parentheses( src - 1 );
if( !p )
continue;
strncpy( buf, src, p - src );
buf[p - src] = '\0';
src = p + 1;
}
else if( key == '#' )
{
byuid = TRUE;
key = *src++;
}
switch( key )
{
case '{':
if( isdigit( buf[0] ) )
get_num_args( t, get_program_var( info, "trigger" ), buf );
else
strcpy( t, get_program_var( info, buf ) );
break;
case '[':
sprintf( t, "%d", parse_number( buf, info ) );
break;
case '(':
prog_string_func( t, buf, info );
break;
case '*':
strcpy( t, get_program_var( info, "trigger" ) );
break;
case 'i':
if( byuid )
sprintf( t, "%d", mob->unique_key );
else
one_argument( mob->name, t );
break;
case 'I':
strcpy( t, mob->short_descr );
break;
case 'n':
if( actor )
{
if( byuid )
sprintf( t, "%d", actor->unique_key );
else if( can_see( mob, actor ) )
{
one_argument( actor->name, t );
if( !IS_NPC( actor ) )
*t = UPPER( *t );
}
else
strcpy( t, "someone" );
}
break;
case 'N':
if( actor )
{
if( can_see( mob, actor ) )
{
if( IS_NPC( actor ) )
strcpy( t, actor->short_descr );
else
{
strcpy( t, actor->name );
strcat( t, " " );
strcat( t, actor->pcdata->title );
}
}
else
strcpy( t, "someone" );
}
break;
case 't':
if( vict )
{
if( byuid )
sprintf( t, "%d", vict->unique_key );
else if( can_see( mob, vict ) )
{
one_argument( vict->name, t );
if( !IS_NPC( vict ) )
*t = UPPER( *t );
}
else
strcpy( t, "someone" );
}
break;
case 'T':
if( vict )
{
if( can_see( mob, vict ) )
{
if( IS_NPC( vict ) )
strcpy( t, vict->short_descr );
else
{
strcpy( t, vict->name );
strcat( t, " " );
strcat( t, vict->pcdata->title );
}
}
else
strcpy( t, "someone" );
}
break;
case 'r':
if( rndm )
{
if( byuid )
sprintf( t, "%d", rndm->unique_key );
else if( can_see( mob, rndm ) )
{
one_argument( rndm->name, t );
if( !IS_NPC( rndm ) )
*t = UPPER( *t );
}
else
strcpy( t, "someone" );
}
break;
case 'R':
if( rndm )
{
if( can_see( mob, rndm ) )
{
if( IS_NPC( rndm ) )
strcpy( t, rndm->short_descr );
else
{
strcpy( t, rndm->name );
strcat( t, " " );
strcat( t, rndm->pcdata->title );
}
}
else
strcpy( t, "someone" );
}
break;
case 'e':
if( actor )
can_see( mob, actor ) ? strcpy( t, he_she[actor->sex] )
: strcpy( t, "someone" );
break;
case 'm':
if( actor )
can_see( mob, actor ) ? strcpy( t, him_her[actor->sex] )
: strcpy( t, "someone" );
break;
case 's':
if( actor )
can_see( mob, actor ) ? strcpy( t, his_her[actor->sex] )
: strcpy( t, "someone's" );
break;
case 'E':
if( vict )
can_see( mob, vict ) ? strcpy( t, he_she[vict->sex] )
: strcpy( t, "someone" );
break;
case 'M':
if( vict )
can_see( mob, vict ) ? strcpy( t, him_her[vict->sex] )
: strcpy( t, "someone" );
break;
case 'S':
if( vict )
can_see( mob, vict ) ? strcpy( t, his_her[vict->sex] )
: strcpy( t, "someone's" );
break;
case 'j':
strcpy( t, he_she[mob->sex] );
break;
case 'k':
strcpy( t, him_her[mob->sex] );
break;
case 'l':
strcpy( t, his_her[mob->sex] );
break;
case 'J':
if( rndm )
can_see( mob, rndm ) ? strcpy( t, he_she[rndm->sex] )
: strcpy( t, "someone" );
break;
case 'K':
if( rndm )
can_see( mob, rndm ) ? strcpy( t, him_her[rndm->sex] )
: strcpy( t, "someone" );
break;
case 'L':
if( rndm )
can_see( mob, rndm ) ? strcpy( t, his_her[rndm->sex] )
: strcpy( t, "someone's" );
break;
case 'o':
if( obj )
{
if( byuid )
sprintf( t, "%d", obj->unique_key );
else if( can_see_obj( mob, obj ) )
one_argument( obj->name, t );
else
strcpy( t, "something" );
}
break;
case 'O':
if( obj )
can_see_obj( mob, obj ) ? strcpy( t, obj->short_descr )
: strcpy( t, "something" );
break;
case 'p':
if( v_obj )
{
if( byuid )
sprintf( t, "%d", v_obj->unique_key );
else if( can_see_obj( mob, v_obj ) )
one_argument( v_obj->name, t );
else
strcpy( t, "something" );
}
break;
case 'P':
if( v_obj )
can_see_obj( mob, v_obj ) ? strcpy( t, v_obj->short_descr )
: 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:
progbug( mob, "bad $var" );
return;
}
t = strchr( t, '\0' );
}
*t = '\0';
return;
}
char *get_num_args( char *buf, const char *trigger, const char *str )
{
const char *start, *end;
char *p;
int i, j;
buf[0] = '\0';
if( trigger[0] == '\0' )
return buf;
if( !strcmp( str, "*" ) )
return strcpy( buf, trigger );
if( ( p = strchr( str, '+' ) ) && *(p + 1) == '\0' )
{
*p = '\0';
j = -1;
i = atoi( str );
}
else if( ( p = strchr( str, '-' ) ) && p > str )
{
*p++ = '\0';
i = atoi( str );
j = atoi( p );
}
else if( is_number( str ) )
{
i = atoi( str );
j = i + 1;
}
else
{
bug( "get_num_args: bad variable (integer start of name)" );
return buf;
}
if( i <= 0 )
{
bug( "get_num_args: bad variable (-ve number)" );
return buf;
}
for( start = trigger; start[0] != '\0' && i > 1; i--, j-- )
start = first_arg( start, buf, FALSE );
if( j < i )
return strcpy( buf, start );
if( i + 1 == j )
{
first_arg( start, buf, FALSE );
return buf;
}
for( end = start; end[0] != '\0' && j > 1; j-- )
end = first_arg( end, buf, FALSE );
memset( buf, 0, ((char *)end - (char *)start) + 2 );
return strncpy( buf, start, end - start );
}
/***************************************************************************
* Global function code and brief comments.
*/
/* The next few 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..
*/
MPROG_DATA *prog_keyword_check( char *arg, MPROG_DATA *mprg,
MPROG_INFO *info, int type )
{
char temp1[MAX_STRING_LENGTH];
char temp2[MAX_INPUT_LENGTH];
char word[MAX_INPUT_LENGTH];
const char *list;
char *start;
char *dupl;
char *end;
MPROG_GLOBAL *glob;
for( ; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type != type )
glob = NULL;
if( glob || mprg->type == type )
{
if( glob )
expand_to_eol( temp1, glob->arglist, info );
else
expand_to_eol( temp1, mprg->arglist, info );
list = temp1;
strcpy( temp2, arg );
dupl = temp2;
if( ( list[0] == 'p' ) && ( list[1] == ' ' ) )
{
list += 2;
while( ( start = str_str( dupl, list ) ) )
if( ( start == dupl || *( start - 1 ) == ' ' )
&& ( *( end = start + strlen( list ) ) == ' '
|| *end == '\n'
|| *end == '\r'
|| *end == '\0' ) )
{
set_local_var( info, "trigger", arg );
return mprg;
}
else
dupl = start + 1;
}
else if( list[0] == 'l' && list[1] == ' ' )
{
list += 2;
/* created in act, this is compensation */
strcat( temp1, "\n\r" );
if( !str_cmp( list, temp2 ) )
{
set_local_var( info, "trigger", arg );
return mprg;
}
}
else if( LOWER( list[0] ) == 'r' && list[1] == ' ' )
{
regexp *preg;
int i, len;
for( start = &temp1[2]; isspace( *start ); ++start )
;
if( strchr( temp2, '\n' ) )
*strchr( temp2, '\n' ) = '\0';
if( !( preg = regcomp( start, 1 ) ) )
return NULL;
if( !regexec( preg, temp2 ) )
{
free( preg );
return NULL;
}
/* a match! now we create a set of variables to suit */
for( i = 0; i < NSUBEXP; ++i )
{
len = preg->endp[i] - preg->startp[i];
if( len <= 0 )
break;
sprintf( temp1, "r%d", i );
strncpy( temp2, preg->startp[i], len );
temp2[len] = '\0';
set_local_var( info, temp1, temp2 );
}
free( preg );
set_local_var( info, "trigger", arg );
return mprg;
}
else
{
list = one_argument( list, word );
for( ; word[0] != '\0'; list = one_argument( list, word ) )
while( ( start = str_str( dupl, word ) ) )
{
if( ( start == dupl || *( start - 1 ) == ' ' )
&& ( *( end = start + strlen( word ) ) == ' '
|| *end == '\n'
|| *end == '\r'
|| *end == '\0' ) )
{
set_local_var( info, "trigger", arg );
return mprg;
}
else
dupl = start + 1;
}
}
}
}
return NULL;
}
void mprog_wordlist_check( char *arg, CHAR_DATA *mob, CHAR_DATA *actor,
OBJ_DATA *obj, void *vo, int type )
{
MPROG_DATA *mprg;
MPROG_INFO info;
MPROG_GLOBAL *glob;
info.local = NULL;
info.mob = mob;
info.actor = actor;
info.obj = obj;
info.vo = vo;
info.rndm = NULL;
mprg = mob->pIndexData->mudprogs;
while( ( mprg = prog_keyword_check( arg, mprg, &info, type ) ) )
{
glob = get_global_prog( mprg );
if( glob && glob->type == type )
prog_driver( glob->comlist, &info );
else
prog_driver( mprg->comlist, &info );
delete_all_locals( &info );
mprg = mprg->next;
}
return;
}
void oprog_wordlist_check( char *arg, CHAR_DATA *actor,
OBJ_DATA *obj, void *vo, int type )
{
MPROG_DATA *mprg;
MPROG_INFO info;
MPROG_GLOBAL *glob;
info.local = NULL;
info.mob = oset_supermob( obj );
info.actor = actor;
info.obj = obj;
info.vo = vo;
info.rndm = NULL;
mprg = obj->pIndexData->mudprogs;
while( ( mprg = prog_keyword_check( arg, mprg, &info, type ) ) )
{
glob = get_global_prog( mprg );
if( glob && glob->type == type )
prog_driver( glob->comlist, &info );
else
prog_driver( mprg->comlist, &info );
delete_all_locals( &info );
mprg = mprg->next;
}
release_supermob( );
return;
}
void rprog_wordlist_check( char *arg, ROOM_INDEX_DATA *room, CHAR_DATA *actor,
OBJ_DATA *obj, void *vo, int type )
{
MPROG_DATA *mprg;
MPROG_INFO info;
MPROG_GLOBAL *glob;
info.local = NULL;
info.mob = rset_supermob( room );
info.actor = actor;
info.obj = obj;
info.vo = vo;
info.rndm = NULL;
mprg = room->mudprogs;
while( ( mprg = prog_keyword_check( arg, mprg, &info, type ) ) )
{
glob = get_global_prog( mprg );
if( glob && glob->type == type )
prog_driver( glob->comlist, &info );
else
prog_driver( mprg->comlist, &info );
delete_all_locals( &info );
mprg = mprg->next;
}
release_supermob();
return;
}
int mprog_sub_trigger( CHAR_DATA *mob, CHAR_DATA *actor,
const char *argument )
{
char name[MAX_INPUT_LENGTH];
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
argument = one_argument( argument, name );
for( mprg = mob->pIndexData->mudprogs; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == SUB_PROG && !str_cmp( name, mprg->arglist ) )
{
return mprog_driver( glob->comlist, mob, actor, NULL, NULL, argument );
}
else if( mprg->type == SUB_PROG && !str_cmp( name, mprg->arglist ) )
{
return mprog_driver( mprg->comlist, mob, actor, NULL, NULL, argument );
}
}
return 0;
}
/**********************************************************************
* Supermob code.
* Since rooms and objects can't act in the same way as mobiles, they
* are given a special mob in lieu of actual powers.
*/
CHAR_DATA *new_supermob()
{
struct supermob_data *tmp;
if( supermob_free )
{
tmp = supermob_free;
supermob_free = supermob_free->next;
tmp->next = supermob_list;
supermob_list = tmp;
/* if they already existed then they should have been in a room */
char_from_room( tmp->supermob );
}
else
{
tmp = alloc_perm( sizeof( struct supermob_data ) );
tmp->next = supermob_list;
supermob_list = tmp;
supermob_list->supermob = create_mobile( get_mob_index( MOB_VNUM_SUPERMOB ) );
}
return supermob_list->supermob;
}
CHAR_DATA *oset_supermob( OBJ_DATA *obj )
{
ROOM_INDEX_DATA *room;
OBJ_DATA *in_obj;
char buf[MAX_INPUT_LENGTH];
CHAR_DATA *supermob;
if( !obj )
return NULL;
for( in_obj = obj; in_obj->in_obj; in_obj = in_obj->in_obj )
;
if( in_obj->carried_by )
room = in_obj->carried_by->in_room;
else
room = in_obj->in_room;
if( !room )
return NULL;
supermob = new_supermob();
supermob_list->prog_obj = obj;
supermob_list->prog_room = NULL;
supermob->level = obj->level;
if( supermob->short_descr )
free_string( supermob->short_descr );
supermob->short_descr = str_dup( obj->short_descr );
/* Added by Jenny to allow bug messages to show the vnum
of the object, and not just supermob's vnum */
sprintf( buf, "Object #%d (%s)", obj->pIndexData->vnum, obj->short_descr );
free_string( supermob->description );
supermob->description = str_dup( buf );
char_to_room( supermob, room );
return supermob;
}
CHAR_DATA *rset_supermob( ROOM_INDEX_DATA *room )
{
char buf[MAX_INPUT_LENGTH];
CHAR_DATA *supermob;
if( !room )
return NULL;
supermob = new_supermob();
supermob_list->prog_room = room;
supermob_list->prog_obj = NULL;
supermob->level = room->area->ave;
free_string( supermob->short_descr );
supermob->short_descr = str_dup( room->name );
free_string( supermob->name );
supermob->name = str_dup( room->name );
/* Added by Jenny to allow bug messages to show the vnum of the
room, and not just supermob's vnum */
sprintf( buf, "Room #%d (%s)", room->vnum, room->name );
free_string( supermob->description );
supermob->description = str_dup( buf );
char_to_room( supermob, room );
return supermob;
}
/* Since progs run to their completion, even when they are called from
* other progs the only supermob needed to be freed is the last one to
* be created.
*/
void release_supermob( )
{
OBJ_DATA *obj, *objnext;
struct supermob_data *tmp;
tmp = supermob_list;
supermob_list = supermob_list->next;
tmp->next = supermob_free;
supermob_free = tmp;
tmp->prog_obj = NULL;
tmp->prog_room = NULL;
free_string( tmp->supermob->description );
tmp->supermob->description = &str_empty[0];
stop_fighting( tmp->supermob, TRUE );
/* clear inventory if the prog didn't work too well. */
for( obj = tmp->supermob->carrying; obj; obj = objnext )
{
objnext = obj->next_content;
extract_obj( obj );
}
char_from_room( tmp->supermob );
char_to_room( tmp->supermob, get_room_index( ROOM_VNUM_LIMBO ) );
}
/*
* Supermob initialisation.
* When we start of ensure there is at least one supermob available.
*/
void init_supermob( void )
{
ROOM_INDEX_DATA *office;
supermob_free = alloc_perm( sizeof( struct supermob_data ) );
supermob_free->supermob = create_mobile( get_mob_index( MOB_VNUM_SUPERMOB ) );
supermob_free->next = NULL;
office = get_room_index( ROOM_VNUM_LIMBO );
char_to_room( supermob_free->supermob, office );
}
struct supermob_data *get_supermob( CHAR_DATA *mob )
{
struct supermob_data *tmp;
for( tmp = supermob_list; tmp; tmp = tmp->next )
{
if( tmp->supermob == mob )
return tmp;
}
return NULL;
}
/*
* Check to run a "percentage chance" program. Note how the bit for this
* paricular program is removed for the running of the program. This ensures
* that no recursion occurs.
*/
int mprog_percent_check( CHAR_DATA *mob, CHAR_DATA *actor, OBJ_DATA *obj,
void *vo, int type )
{
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
int retval = NO_FLAG;
for( mprg = mob->pIndexData->mudprogs; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == type
&& ( number_percent( ) < atoi( mprg->arglist ) ) )
{
xREMOVE_BIT( mob->pIndexData->progtypes, type );
retval = mprog_driver( glob->comlist, mob, actor, obj, vo, NULL );
xSET_BIT( mob->pIndexData->progtypes, type );
if( type != GREET_PROG && type != ALL_GREET_PROG )
break;
}
else if( ( mprg->type == type )
&& ( number_percent( ) < atoi( mprg->arglist ) ) )
{
xREMOVE_BIT( mob->pIndexData->progtypes, type );
retval = mprog_driver( mprg->comlist, mob, actor, obj, vo, NULL );
xSET_BIT( mob->pIndexData->progtypes, type );
if( type != GREET_PROG && type != ALL_GREET_PROG )
break;
}
}
return retval;
}
int oprog_percent_check( CHAR_DATA *actor, OBJ_DATA *obj,
void *vo, int type )
{
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
CHAR_DATA *supermob;
int retval = NO_FLAG;
for( mprg = obj->pIndexData->mudprogs; mprg; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == type
&& ( number_percent( ) < atoi( mprg->arglist ) ) )
{
xREMOVE_BIT( obj->pIndexData->progtypes, type );
supermob = oset_supermob( obj );
retval = mprog_driver( glob->comlist, supermob, actor, obj, vo, FALSE );
release_supermob();
xSET_BIT( obj->pIndexData->progtypes, type );
if( type != GREET_PROG )
break;
}
else if( mprg->type == type
&& ( number_percent( ) <= atoi( mprg->arglist ) ) )
{
xREMOVE_BIT( obj->pIndexData->progtypes, type );
supermob = oset_supermob( obj );
retval = mprog_driver( mprg->comlist, supermob, actor, obj, vo, FALSE );
release_supermob();
xSET_BIT( obj->pIndexData->progtypes, type );
if( type != GREET_PROG )
break;
}
}
return retval;
}
int rprog_percent_check( ROOM_INDEX_DATA *room, CHAR_DATA *actor,
OBJ_DATA *obj, void *vo, int type )
{
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
CHAR_DATA *supermob;
int retval = 1;
if( !room )
return NO_FLAG;
for( mprg = room->mudprogs; mprg; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == type
&& ( number_percent( ) < atoi( mprg->arglist ) ) )
{
xREMOVE_BIT( room->progtypes, type );
supermob = rset_supermob( room );
retval = mprog_driver( glob->comlist, supermob, actor, obj, vo, FALSE );
release_supermob();
xSET_BIT( room->progtypes, type );
}
else if( mprg->type == type
&& number_percent() <= atoi( mprg->arglist ) )
{
xREMOVE_BIT( room->progtypes, type );
supermob = rset_supermob( room );
retval = mprog_driver( mprg->comlist, supermob, actor, obj, vo, FALSE );
release_supermob();
xSET_BIT( room->progtypes, type );
}
}
return retval;
}
/* A little time checking diddy.
*/
bool is_time( char *p, PLANE_DATA *plane )
{
int val = 0;
while( isspace( *p ) )
++p;
if( !*p )
return FALSE;
for( ; p && *p; p++ )
{
if( !isdigit( *p ) && *p != ' ' )
{
bug( "Invalid argument to time_prog." );
return FALSE;
}
if( *p == ' ' )
{
if( val == plane->time.hour )
return TRUE;
while( isspace( *(p + 1) ) )
++p;
val = 0;
}
else
val = val * 10 + *p - '0';
}
if( val == plane->time.hour )
return TRUE;
return FALSE;
}
/* A smart bug feature, so we can tell where the problem is, even down
* to objects and rooms.
*/
void progbug( CHAR_DATA *mob, const char *fmt, ... )
{
char fmt2[MAX_INPUT_LENGTH];
char buf[MAX_STRING_LENGTH];
va_list args;
va_start( args, fmt );
/* Check if we're dealing with supermob, which means the bug occurred
in a room or obj prog. */
if( mob->pIndexData && mob->pIndexData->vnum == MOB_VNUM_SUPERMOB )
{
/* It's supermob. In set_supermob and rset_supermob, the description
was set to indicate the object or room, so we just need to show
the description in the bug message. */
sprintf( fmt2, "[*%s*] %s.", mob->description == NULL
? "(unknown)" : mob->description,
fmt );
}
else if( mob->pIndexData )
sprintf( fmt2, "[*Mob %d*] %s", mob->pIndexData->vnum, fmt );
else
sprintf( fmt2, "[*Player*] %s", fmt );
vsprintf( buf, fmt2, args );
va_end( args );
bug( buf );
return;
}
/*
* Rather than having act progs run when they trigger, this update allows
* them to seem as if they have occured _after_ the trigger. This is also
* much neater.
*/
void update_act_progs( void )
{
MPROG_ACT_LIST *tmp_act;
CHAR_DATA *mob;
OBJ_DATA *obj;
ROOM_INDEX_DATA *room;
while( ( tmp_act = mob_act_list ) )
{
mob = (CHAR_DATA *)tmp_act->target;
if( !mob->deleted )
prog_driver( tmp_act->mprg->comlist, tmp_act->info );
mob_act_list = tmp_act->next;
delete_all_locals( tmp_act->info );
if( tmp_act->mprg->type == GLOBAL_PROG )
free_mprog( tmp_act->mprg );
free_mem( tmp_act->info, sizeof( MPROG_INFO ) );
free_mem( tmp_act, sizeof( MPROG_ACT_LIST ) );
}
while( ( tmp_act = obj_act_list ) )
{
obj = (OBJ_DATA *)tmp_act->target;
if( !obj->deleted )
{
tmp_act->info->mob = oset_supermob( obj );
prog_driver( tmp_act->mprg->comlist, tmp_act->info );
release_supermob();
}
obj_act_list = tmp_act->next;
delete_all_locals( tmp_act->info );
if( tmp_act->mprg->type == GLOBAL_PROG )
free_mprog( tmp_act->mprg );
free_mem( tmp_act->info, sizeof( MPROG_INFO ) );
free_mem( tmp_act, sizeof( MPROG_ACT_LIST ) );
}
while( ( tmp_act = room_act_list ) )
{
room = (ROOM_INDEX_DATA *)tmp_act->target;
tmp_act->info->mob = rset_supermob( room );
prog_driver( tmp_act->mprg->comlist, tmp_act->info );
release_supermob();
room_act_list = tmp_act->next;
delete_all_locals( tmp_act->info );
if( tmp_act->mprg->type == GLOBAL_PROG )
free_mprog( tmp_act->mprg );
free_mem( tmp_act->info, sizeof( MPROG_INFO ) );
free_mem( tmp_act, sizeof( MPROG_ACT_LIST ) );
}
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 prog_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.
*
* Supermob note: These programs trigger without a supermob so the function
* uses less time. For the purposes of figuring out whether the trigger has
* worked, the mob is the same as the actor.
*/
void roomobj_act_trigger( char *buf, ROOM_INDEX_DATA *room, CHAR_DATA *ch,
OBJ_DATA *obj, void *vo )
{
MPROG_ACT_LIST *tmp_act;
MPROG_DATA *mprg, *mprg_tmp;
MPROG_INFO info;
char tmp[MAX_INPUT_LENGTH];
MPROG_GLOBAL *glob;
kill_colour( tmp, buf );
info.local = NULL;
info.mob = ch;
info.actor = ch;
info.obj = obj;
info.vo = vo;
info.rndm = NULL;
if( xIS_SET( room->progtypes, ACT_PROG )
&& ( mprg = prog_keyword_check( tmp, room->mudprogs, &info, ACT_PROG ) ) )
{
tmp_act = alloc_mem( sizeof( MPROG_ACT_LIST ) );
tmp_act->target = ch->in_room;
if( ( glob = get_global_prog( mprg ) ) )
{
mprg_tmp = new_mprog( );
mprg_tmp->type = GLOBAL_PROG;
free_string( mprg_tmp->comlist );
mprg_tmp->comlist = str_dup( glob->comlist );
tmp_act->mprg = mprg_tmp;
}
else
tmp_act->mprg = mprg;
tmp_act->info = alloc_mem( sizeof( MPROG_INFO ) );
tmp_act->info->local = info.local;
info.local = NULL;
tmp_act->info->mob = NULL;
tmp_act->info->actor = ch;
tmp_act->info->obj = obj;
tmp_act->info->vo = vo;
tmp_act->info->rndm = NULL;
tmp_act->next = room_act_list;
room_act_list = tmp_act;
}
/* yes, I know this overrides the obj parameter, but it makes
it very easy, Symposium */
for( obj = room->contents; obj; obj = obj->next_content )
{
if( obj->deleted && !xIS_SET( obj->pIndexData->progtypes, ACT_PROG ) )
continue;
if( ( mprg = prog_keyword_check( tmp, obj->pIndexData->mudprogs,
&info, ACT_PROG ) ) )
{
tmp_act = alloc_mem( sizeof( MPROG_ACT_LIST ) );
tmp_act->target = obj;
if( ( glob = get_global_prog( mprg ) ) )
{
mprg_tmp = new_mprog( );
mprg_tmp->type = GLOBAL_PROG;
free_string( mprg_tmp->comlist );
mprg_tmp->comlist = str_dup( glob->comlist );
tmp_act->mprg = mprg_tmp;
}
else
tmp_act->mprg = mprg;
tmp_act->info = alloc_mem( sizeof( MPROG_INFO ) );
tmp_act->info->local = info.local;
info.local = NULL;
tmp_act->info->mob = NULL;
tmp_act->info->actor = ch;
tmp_act->info->obj = obj;
tmp_act->info->vo = vo;
tmp_act->info->rndm = NULL;
tmp_act->next = obj_act_list;
obj_act_list = tmp_act;
}
}
return;
}
void mob_act_trigger( char *buf, CHAR_DATA *mob, CHAR_DATA *ch,
OBJ_DATA *obj, void *vo )
{
MPROG_ACT_LIST *tmp_act;
MPROG_DATA *mprg, *mprg_tmp;
MPROG_GLOBAL *glob;
MPROG_INFO info;
char tmp[MAX_INPUT_LENGTH];
if( !IS_NPC( mob ) || !xIS_SET( mob->pIndexData->progtypes, ACT_PROG )
|| ch->pIndexData == mob->pIndexData )
return;
kill_colour( tmp, buf );
info.local = NULL;
info.mob = mob;
info.actor = ch;
info.obj = obj;
info.vo = vo;
info.rndm = NULL;
/* Don't let a mob trigger itself, nor one instance of a mob
trigger another instance. */
if( ( mprg = prog_keyword_check( tmp, mob->pIndexData->mudprogs,
&info, ACT_PROG ) ) )
{
tmp_act = alloc_mem( sizeof( MPROG_ACT_LIST ) );
tmp_act->target = mob;
if( ( glob = get_global_prog( mprg ) ) )
{
mprg_tmp = new_mprog( );
mprg_tmp->type = GLOBAL_PROG;
free_string( mprg_tmp->comlist );
mprg_tmp->comlist = str_dup( glob->comlist );
tmp_act->mprg = mprg_tmp;
}
else
tmp_act->mprg = mprg;
tmp_act->info = alloc_mem( sizeof( MPROG_INFO ) );
tmp_act->info->local = info.local;
info.local = NULL;
tmp_act->info->mob = mob;
tmp_act->info->actor = ch;
tmp_act->info->obj = obj;
tmp_act->info->vo = vo;
tmp_act->info->rndm = NULL;
tmp_act->next = mob_act_list;
mob_act_list = tmp_act;
}
return;
}
void mprog_bribe_trigger( CHAR_DATA *mob, CHAR_DATA *ch, int amount )
{
char buf[MAX_STRING_LENGTH];
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
OBJ_DATA *obj;
if( IS_NPC( mob )
&& xIS_SET( mob->pIndexData->progtypes, BRIBE_PROG ) )
{
obj = create_object( get_obj_index( OBJ_VNUM_MONEY_SOME ), 0 );
sprintf( buf, obj->short_descr, amount );
free_string( obj->short_descr );
obj->short_descr = str_dup( buf );
obj->value[0] = amount;
obj_to_char( obj, mob );
mob->gold -= amount;
for( mprg = mob->pIndexData->mudprogs; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == BRIBE_PROG
&& ( amount >= atoi( mprg->arglist ) ) )
{
mprog_driver( glob->comlist, mob, ch, obj, NULL, NULL );
break;
}
else if( mprg->type == BRIBE_PROG
&& ( amount >= atoi( mprg->arglist ) ) )
{
mprog_driver( mprg->comlist, mob, ch, obj, NULL, NULL );
break;
}
}
}
return;
}
void mprog_give_trigger( CHAR_DATA *mob, CHAR_DATA *ch, OBJ_DATA *obj )
{
char buf[MAX_INPUT_LENGTH];
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
if( IS_NPC( mob )
&& xIS_SET( mob->pIndexData->progtypes, GIVE_PROG ) )
for( mprg = mob->pIndexData->mudprogs; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob )
{
one_argument( glob->arglist, buf );
if( glob->type == GIVE_PROG
&& ( !str_cmp( obj->name, glob->arglist )
|| !str_cmp( "all", buf )
|| atoi( glob->arglist ) == obj->pIndexData->vnum ) )
{
mprog_driver( glob->comlist, mob, ch, obj, NULL, NULL );
break;
}
}
else
{
one_argument( mprg->arglist, buf );
if( mprg->type == GIVE_PROG
&& ( !str_cmp( obj->name, mprg->arglist )
|| !str_cmp( "all", buf )
|| atoi( mprg->arglist ) == obj->pIndexData->vnum ) )
{
mprog_driver( mprg->comlist, mob, ch, obj, NULL, NULL );
break;
}
}
}
return;
}
void oprog_container_putin( CHAR_DATA *ch, OBJ_DATA *cont, OBJ_DATA *obj )
{
char buf[MAX_INPUT_LENGTH];
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
if( obj->deleted || xIS_SET( cont->pIndexData->progtypes, PUTIN_PROG ) )
return;
for( mprg = cont->pIndexData->mudprogs; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob )
{
one_argument( glob->arglist, buf );
if( glob->type == GIVE_PROG
&& ( !str_cmp( obj->name, glob->arglist )
|| !str_cmp( "all", buf )
|| atoi( glob->arglist ) == obj->pIndexData->vnum ) )
{
mprog_driver( mprg->comlist, oset_supermob( cont ),
ch, cont, obj, NULL );
release_supermob();
break;
}
}
else
{
one_argument( mprg->arglist, buf );
if( mprg->type == GIVE_PROG
&& ( !str_cmp( obj->name, mprg->arglist )
|| !str_cmp( "all", buf )
|| atoi( mprg->arglist ) == obj->pIndexData->vnum ) )
{
mprog_driver( mprg->comlist, oset_supermob( cont ),
ch, cont, obj, NULL );
release_supermob();
break;
}
}
}
return;
}
void mprog_hitprcnt_trigger( CHAR_DATA *mob )
{
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
for( mprg = mob->pIndexData->mudprogs; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == HITPRCNT_PROG
&& ( ( 100 * mob->hit / mob->max_hit ) < atoi( mprg->arglist ) ) )
{
mprog_driver( glob->comlist, mob, mob->fighting, NULL, NULL, NULL );
break;
}
else if( mprg->type == HITPRCNT_PROG
&& ( ( 100 * mob->hit / mob->max_hit ) < atoi( mprg->arglist ) ) )
{
mprog_driver( mprg->comlist, mob, mob->fighting, NULL, NULL, NULL );
break;
}
}
}
void mprog_time_trigger( CHAR_DATA *mob )
{
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
for( mprg = mob->pIndexData->mudprogs; mprg; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == TIME_PROG
&& is_time( glob->arglist, mob->in_room->area->plane ) )
mprog_driver( glob->comlist, mob, NULL, NULL, NULL, NULL );
else if( mprg->type == TIME_PROG
&& is_time( mprg->arglist, mob->in_room->area->plane ) )
mprog_driver( mprg->comlist, mob, NULL, NULL, NULL, NULL );
}
return;
}
void rprog_time_trigger( ROOM_INDEX_DATA *room, CHAR_DATA *actor )
{
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
for( mprg = room->mudprogs; mprg; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == TIME_PROG
&& is_time( glob->arglist, room->area->plane ) )
{
mprog_driver( glob->comlist, rset_supermob( room ),
actor, NULL, NULL, NULL );
release_supermob();
}
else if( mprg->type == TIME_PROG
&& is_time( mprg->arglist, room->area->plane ) )
{
mprog_driver( mprg->comlist, rset_supermob( room ),
actor, NULL, NULL, NULL );
release_supermob();
}
}
return;
}
void oprog_time_trigger( OBJ_DATA *obj )
{
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
for( mprg = obj->pIndexData->mudprogs; mprg; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( glob && glob->type == TIME_PROG
&& is_time( glob->arglist, obj->pIndexData->area->plane ) )
{
mprog_driver( glob->comlist, oset_supermob( obj ),
NULL, obj, NULL, NULL );
release_supermob();
}
else if( mprg->type == TIME_PROG
&& is_time( mprg->arglist, obj->pIndexData->area->plane ) )
{
mprog_driver( mprg->comlist, oset_supermob( obj ),
NULL, obj, NULL, NULL );
release_supermob();
}
}
return;
}
void mprog_cast_trigger( CHAR_DATA *mob, CHAR_DATA *actor, int sn )
{
const char *arg;
char buf[MAX_INPUT_LENGTH];
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
for( mprg = mob->pIndexData->mudprogs; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( ( !glob && mprg->type != CAST_PROG )
|| ( glob && glob->type != CAST_PROG ) )
continue;
if( glob )
arg = glob->arglist;
else
arg = mprg->arglist;
while( arg != NULL && arg[0] != '\0' )
{
arg = one_argument( arg, buf );
if( !str_prefix( buf, skill_table[sn].name ) )
{
if( glob )
mprog_driver( glob->comlist, mob, actor,
NULL, NULL, skill_table[sn].name );
else
mprog_driver( mprg->comlist, mob, actor,
NULL, NULL, skill_table[sn].name );
return;
}
}
}
return;
}
/*
* Allow a trigger in the form: <percentage> [direction] [dir..]
* for instance: 75 north south
* returns TRUE mostly, FALSE signifies something blocking passage.
*/
bool greet_leave_trigger( CHAR_DATA *actor, CHAR_DATA *mob, ROOM_INDEX_DATA *room,
OBJ_DATA *obj, int dir, int type )
{
const char *arg;
char buf[MAX_INPUT_LENGTH];
MPROG_DATA *mprg;
MPROG_GLOBAL *glob;
char dirbuf[8];
MPROG_INFO info;
int retval = TRUE;
if( mob )
{
info.mob = mob;
mprg = mob->pIndexData->mudprogs;
}
else if( obj )
{
info.mob = oset_supermob( obj );
mprg = obj->pIndexData->mudprogs;
}
else if( room )
{
info.mob = rset_supermob( room );
mprg = room->mudprogs;
}
else
{
bug( "greet_leave_trigger: no source for programs." );
return TRUE;
}
info.local = NULL;
info.actor = actor;
info.obj = obj;
info.vo = NULL;
info.rndm = NULL;
dir = 1 << dir;
strcpy( dirbuf, flag_string( direction_flags, &dir ) );
for( ; mprg != NULL; mprg = mprg->next )
{
int flags;
glob = get_global_prog( mprg );
if( ( !glob && mprg->type != type ) || ( glob && glob->type != type ) )
continue;
if( glob )
arg = one_argument( glob->arglist, buf );
else
arg = one_argument( mprg->arglist, buf );
if( number_percent( ) > atoi( buf ) )
continue;
expand_to_eol( buf, arg, &info );
flags = flag_value( NULL, direction_flags, buf );
if( arg[0] == '\0'
|| ( flags != NO_FLAG && IS_SET( flags, dir ) ) )
{
set_local_var( &info, "trigger", dirbuf );
if( ( glob && !prog_driver( glob->comlist, &info ) )
|| ( !glob && !prog_driver( mprg->comlist, &info ) ) )
retval = FALSE;
delete_all_locals( &info );
}
}
if( !mob )
release_supermob();
return retval;
}
/*
* The trigger for handling commands through mudprogs.
* Recursion protection in comm_trig_aux() means that whilst the program
* is being run, the obj/room/mob can't be triggered by another command.
*/
bool command_trigger( CHAR_DATA *ch, const char *comm, const char *argument )
{
MPROG_INFO info;
char cmnd_buf[ MAX_INPUT_LENGTH ];
bool progrun;
if( IS_NPC( ch ) )
return FALSE;
info.local = NULL;
info.mob = NULL;
info.actor = ch;
info.obj = NULL;
info.vo = NULL;
info.rndm = NULL;
sprintf( cmnd_buf, "(%s) %s", comm, argument );
if( xIS_SET( ch->in_room->progtypes, COMMAND_PROG ) )
{
info.mob = rset_supermob( ch->in_room );
progrun = comm_trig_aux( ch->in_room->mudprogs, ch->in_room->progtypes,
cmnd_buf, &info );
release_supermob( );
if( progrun )
return TRUE;
}
for( info.mob = ch->in_room->people; info.mob;
info.mob = info.mob->next_in_room )
{
if( info.mob->deleted || !IS_NPC( info.mob )
|| !xIS_SET( info.mob->pIndexData->progtypes, COMMAND_PROG ) )
continue;
progrun = comm_trig_aux( info.mob->pIndexData->mudprogs,
info.mob->pIndexData->progtypes,
cmnd_buf, &info );
if( progrun )
return TRUE;
}
for( info.obj = ch->in_room->contents; info.obj;
info.obj = info.obj->next_content )
{
if( !info.obj->deleted
&& xIS_SET( info.obj->pIndexData->progtypes, COMMAND_PROG ) )
{
info.mob = oset_supermob( info.obj );
progrun = comm_trig_aux( info.obj->pIndexData->mudprogs,
info.obj->pIndexData->progtypes,
cmnd_buf, &info );
release_supermob( );
if( progrun )
return TRUE;
}
}
for( info.obj = ch->carrying; info.obj;
info.obj = info.obj->next_content )
{
if( !info.obj->deleted && info.obj->wear_loc != WEAR_NONE
&& xIS_SET( info.obj->pIndexData->progtypes, COMMAND_PROG ) )
{
info.mob = oset_supermob( info.obj );
progrun = comm_trig_aux( info.obj->pIndexData->mudprogs,
info.obj->pIndexData->progtypes,
cmnd_buf, &info );
release_supermob( );
if( progrun )
return TRUE;
}
}
return FALSE;
}
/*
* Auxiliary function to find a command_prog and execute it.
* Recursion protection by de-flagging the room/obj/mob of it's
* COMMAND_PROG flag.
*/
bool comm_trig_aux( MPROG_DATA *mprg, int *progtypes, const char *cmnd_line,
MPROG_INFO *info )
{
const char *arg;
char argbuf[MAX_INPUT_LENGTH];
char buf[MAX_INPUT_LENGTH];
char command[MAX_INPUT_LENGTH];
int progrun;
MPROG_GLOBAL *glob;
one_argument( cmnd_line, command );
for( ; mprg != NULL; mprg = mprg->next )
{
glob = get_global_prog( mprg );
if( ( glob && glob->type != COMMAND_PROG )
|| ( !glob && mprg->type != COMMAND_PROG ) )
continue;
if( glob )
expand_to_eol( argbuf, glob->arglist, info );
else
expand_to_eol( argbuf, mprg->arglist, info );
arg = &argbuf[0];
while( arg != NULL && arg[0] != '\0' )
{
char *p;
arg = one_argument( arg, buf );
/* Check for pre|fix command matching
* So for "abcd|efg", check for "abcd" being prefix to command
* and command being prefix to "abcdefg".
*/
if( ( p = strchr( buf, '|' ) ) )
{
*p = '\0';
if( str_prefix( buf, command ) )
p = NULL;
else
{
strcpy( p, p + 1 );
if( str_prefix( command, buf ) )
p = NULL;
}
}
if( p || !str_cmp( command, buf ) )
{
xREMOVE_BIT( progtypes, COMMAND_PROG );
set_local_var( info, "trigger", cmnd_line );
if( glob )
progrun = prog_driver( glob->comlist, info );
else
progrun = prog_driver( mprg->comlist, info );
delete_all_locals( info );
xSET_BIT( progtypes, COMMAND_PROG );
if( progrun )
return TRUE;
}
}
}
return FALSE;
}
/**************************************************************************
* Mob variables.
* These functions allow you to save information on a mob using the above
* programs.
*/
char *get_program_var( MPROG_INFO *info, const char *name )
{
MPROG_VAR *var;
struct supermob_data *sup;
if( ( sup = get_supermob( info->mob ) ) )
{
if( sup->prog_obj )
var = sup->prog_obj->variables;
else if( sup->prog_room )
var = sup->prog_room->variables;
else
{
bug( "Mob %d was found to be supermob to nothing.",
info->mob->pIndexData->vnum );
var = NULL;
}
}
else
var = info->mob->variables;
for( ; var; var = var->next )
if( !str_cmp( name, var->name ) )
break;
if( var )
return var->value;
for( var = info->local; var; var = var->next )
if( !str_cmp( name, var->name ) )
break;
if( var )
return var->value;
else
return &str_empty[0];
}
void new_local_var( MPROG_INFO *info, const char *name, const char *value )
{
MPROG_VAR *var;
var = new_mpvar( );
var->name = str_dup( name );
var->value = str_dup( value );
var->next = info->local;
info->local = var;
}
void set_local_var( MPROG_INFO *info, const char *name, const char *value )
{
MPROG_VAR *var;
for( var = info->local; var; var = var->next )
if( !str_cmp( name, var->name ) )
break;
if( !var )
new_local_var( info, name, value );
else
{
free_string( var->value );
var->value = str_dup( value );
}
return;
}
void delete_local_var( MPROG_INFO *info, const char *name )
{
MPROG_VAR *var = NULL, *last;
if( !str_cmp( info->local->name, name ) )
{
var = info->local;
info->local = info->local->next;
}
else
{
for( last = info->local; last; last = last->next )
if( !str_cmp( last->next->name, name ) )
{
var = last->next;
break;
}
if( var )
last->next = var->next;
}
if( var )
free_mpvar( var );
return;
}
void delete_all_locals( MPROG_INFO *info )
{
MPROG_VAR *var;
while( ( var = info->local ) )
{
info->local = info->local->next;
free_mpvar( var );
}
return;
}
char *get_mob_var( CHAR_DATA *mob, const char *name )
{
MPROG_VAR *var;
struct supermob_data *sup;
if( ( sup = get_supermob( mob ) ) )
{
if( sup->prog_obj )
var = sup->prog_obj->variables;
else if( sup->prog_room )
var = sup->prog_room->variables;
else
{
bug( "Mob %d was found to be supermob to nothing.",
mob->pIndexData->vnum );
var = NULL;
}
}
else
var = mob->variables;
for( ; var; var = var->next )
if( !str_cmp( name, var->name ) )
break;
if( var )
return var->value;
else
return &str_empty[0];
}
void new_mob_var( CHAR_DATA *mob, const char *name, const char *value )
{
MPROG_VAR *var;
struct supermob_data *sup;
var = new_mpvar( );
var->name = str_dup( name );
var->value = str_dup( value );
if( ( sup = get_supermob( mob ) ) )
{
if( sup->prog_obj )
{
var->next = sup->prog_obj->variables;
sup->prog_obj->variables = var;
}
else if( sup->prog_room )
{
var->next = sup->prog_room->variables;
sup->prog_room->variables = var;
}
else
bug( "Mob %d was found to be supermob to nothing.",
mob->pIndexData->vnum );
}
else
{
var->next = mob->variables;
mob->variables = var;
}
}
void set_mob_var( CHAR_DATA *mob, const char *name, const char *value )
{
MPROG_VAR *var;
struct supermob_data *sup;
if( ( sup = get_supermob( mob ) ) )
{
if( sup->prog_obj )
var = sup->prog_obj->variables;
else if( sup->prog_room )
var = sup->prog_room->variables;
else
{
bug( "Mob %d was found to be supermob to nothing.",
mob->pIndexData->vnum );
var = NULL;
}
}
else
var = mob->variables;
for( ; var; var = var->next )
if( !str_cmp( name, var->name ) )
break;
if( !var )
new_mob_var( mob, name, value );
else
{
free_string( var->value );
var->value = str_dup( value );
}
return;
}
void delete_mob_var( CHAR_DATA * mob, const char *name )
{
MPROG_VAR **handle, *var, *last;
struct supermob_data *sup;
if( ( sup = get_supermob( mob ) ) )
{
if( sup->prog_obj )
handle = &sup->prog_obj->variables;
else if( sup->prog_room )
handle = &sup->prog_room->variables;
else
{
bug( "Mob %d was found to be supermob to nothing.",
mob->pIndexData->vnum );
return;
}
}
else
handle = &mob->variables;
for( var = *handle; var; var = var->next )
if( !str_cmp( name, var->name ) )
break;
if( !var )
{
bug( "Mob %d: unknown variable '%s'.",
mob->pIndexData->vnum, name );
return;
}
if( var == *handle )
{
*handle = (*handle)->next;
}
else
{
for( last = *handle; last; last = last->next )
if( last->next == var )
break;
last->next = var->next;
}
free_mpvar( var );
return;
}