/*___________________________________________________________________________* )()( 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; }