/** * \file mobprogs.c * Provides services for booting, creation and activation of mobile programs * * Description: All matters dealing with booting, creation and activation of * mobile programs. Eventually these will be supplemented with Lua scripts * * * Copyright 2005, Mary C. Huston, All rights reserved. * Copyright (C) 2004, Shadows of Isildur: Traithe * * The program(s) may be used and/or copied only with written * permission or in accordance with the terms and conditions * stipulated in the license from DIKU GAMMA (0.0) and SOI. * * \author Mary Huston * \author Email: auroness@gmail.com * ****************************************************************************** */ #include <string.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include "structs.h" #include "protos.h" #include "utils.h" #include "decl.h" VAR_DATA *global_vars = NULL; char current_line [MAX_STRING_LENGTH]; int mob_get_token (char **ptr, char *token, int token_type) { while ( **ptr == ' ' ) (*ptr)++; if ( token_type == MT_OPEN_PAREN || token_type == MT_COMMA || token_type == MT_EQUAL ) { *token++ = **ptr; *token = '\0'; if ( token_type == MT_OPEN_PAREN && **ptr != '(' ) return 0; if ( token_type == MT_COMMA && **ptr != ',' ) return 0; if ( token_type == MT_EQUAL && **ptr != '=' ) return 0; (*ptr)++; return 1; } if ( token_type == MT_VARIABLE ) { if ( !isalpha (**ptr) && **ptr != '_' ) return 0; while ( isalpha (**ptr) || isdigit (**ptr) || **ptr == '_' ) *token++ = *((*ptr)++); *token = '\0'; return 1; } return 0; } void require_open_paren (char **ptr) { while ( **ptr == ' ' ) (*ptr)++; (*ptr)++; return; } void deletevar (CHAR_DATA *mob, char *var_name) { struct var_data *tvar; struct var_data *tvar2; if ( !mob->vartab ) return; if ( !str_cmp (mob->vartab->name, var_name) ) { tvar = mob->vartab; mob->vartab = tvar->next; mem_free (tvar->name); mem_free (tvar); return; } for ( tvar = mob->vartab; tvar->next; tvar = tvar->next ) { if ( !str_cmp (tvar->next->name, var_name) ) { tvar2 = tvar->next; tvar->next = tvar2->next; mem_free (tvar2->name); mem_free (tvar2); return; } } } VAR_DATA *getvar (CHAR_DATA *mob, char *var_name) { VAR_DATA *var = NULL; if ( *var_name == '_' ) var = global_vars; else var = mob->vartab; for ( ; var; var = var->next ) if ( !str_cmp (var->name, var_name) ) return var; return NULL; } VAR_DATA *setvar (CHAR_DATA *mob, char *var_name, int value, int type) { VAR_DATA *var; if ( !(var = getvar (mob, var_name)) ) { var = malloc (sizeof (VAR_DATA)); var->name = str_dup (var_name); if ( *var_name == '_' ) { var->next = global_vars; global_vars = var; } else { var->next = mob->vartab; mob->vartab = var; } } var->type = type; var->value = value; return var; } void define_variable (CHAR_DATA *mob, MOBPROG_DATA *program, char *argument) { char var_type [MAX_STRING_LENGTH] = {'\0'}; char var_name [MAX_STRING_LENGTH] = {'\0'}; int type = 0; argument = one_argument (argument, var_type); if ( !str_cmp (var_type, "integer") ) type = MP_TYPE_INTEGER; else if ( !str_cmp (var_type, "char_data") ) type = MP_TYPE_CHAR_DATA; else if ( !str_cmp (var_type, "obj_data") ) type = MP_TYPE_OBJ_DATA; else if ( !str_cmp (var_type, "room_data") ) type = MP_TYPE_ROOM_DATA; else if ( !str_cmp (var_type, "string") ) type = MP_TYPE_STRING; else { system_log("Mob program with a variable problem.", TRUE); program->line = str_dup (current_line); SET_BIT (program->flags, MPF_BROKEN); } argument = one_argument (argument, var_name); if ( !*var_name ) { system_log("Variable name problem; no var_name.", TRUE); program->line = str_dup (current_line); SET_BIT (program->flags, MPF_BROKEN); } if ( !getvar (mob, var_name) ) (void)setvar (mob, var_name, 0, type); return; } void mob_char_indirect (CHAR_DATA *mob, char *token, int *value, int *type) { CHAR_DATA *ch = NULL; CHAR_DATA *person = NULL; int i = 0; struct ind_data_t { int which_case; char ind [15]; int type; } ind_data [] = { { 0, "str", MP_TYPE_INTEGER }, { 1, "dex", MP_TYPE_INTEGER }, { 2, "int", MP_TYPE_INTEGER }, { 3, "con", MP_TYPE_INTEGER }, { 4, "wil", MP_TYPE_INTEGER }, { 5, "aur", MP_TYPE_INTEGER }, { 6, "hit", MP_TYPE_INTEGER }, { 7, "maxhit", MP_TYPE_INTEGER }, { 8, "moves", MP_TYPE_INTEGER }, { 9, "maxmoves", MP_TYPE_INTEGER }, { 10, "deity", MP_TYPE_INTEGER }, { 11, "circle", MP_TYPE_INTEGER }, { 12, "race", MP_TYPE_INTEGER }, { 13, "sex", MP_TYPE_INTEGER }, { 14, "clan_1", MP_TYPE_INTEGER }, { 15, "piety", MP_TYPE_INTEGER }, { 16, "offense", MP_TYPE_INTEGER }, { 17, "clan_2", MP_TYPE_INTEGER }, { 18, "fightmode", MP_TYPE_INTEGER }, { 19, "next_in_room", MP_TYPE_CHAR_DATA }, { 20, "name", MP_TYPE_STRING }, { 21, "level", MP_TYPE_INTEGER }, { 22, "fighting", MP_TYPE_CHAR_DATA }, { 23, "realname", MP_TYPE_STRING }, { 24, "in_room", MP_TYPE_INTEGER }, { 25, "equip", MP_TYPE_OBJ_DATA }, { 26, "inv", MP_TYPE_OBJ_DATA }, { 27, "reset_room", MP_TYPE_INTEGER }, { 28, "prisoner", MP_TYPE_CHAR_DATA }, { 0, "\0", 0 } }; *type = -1; if ( !*value ) return; ch = (CHAR_DATA *) *value; for ( i = 0; *ind_data [i].ind; i++ ) { if ( !str_cmp (ind_data [i].ind, token) ) break; } if ( !*ind_data [i].ind ) return; *type = ind_data [i].type; switch ( ind_data [i].which_case ) { case 0: *value = GET_STR (ch); break; case 1: *value = GET_DEX (ch); break; case 2: *value = GET_INT (ch); break; case 3: *value = GET_CON (ch); break; case 4: *value = GET_WIL (ch); break; case 5: *value = GET_AUR (ch); break; case 6: *value = GET_HIT (ch); break; case 7: *value = GET_MAX_HIT (ch); break; case 8: *value = GET_MOVE (ch); break; case 9: *value = GET_MAX_MOVE (ch); break; case 10: *value = ch->deity; break; case 11: *value = ch->circle; break; case 12: *value = ch->race; break; case 13: *value = ch->sex; break; case 14: *value = 0; /* ch->clan_1; */ break; case 15: *value = ch->ppoints; break; case 16: *value = ch->offense; break; case 17: *value = 0; /* ch->clan_2; */ break; case 18: *value = ch->fight_mode; break; case 19: if ( !is_he_here (mob, ch, 0) ) person = NULL; else { person = ch->next_in_room; while ( person && !CAN_SEE (mob, person) ) person = person->next_in_room; } *value = (int) person; break; case 20: *value = (int) ch->short_descr; break; case 21: *value = GET_TRUST (ch); break; case 22: *value = (int) ch->fighting; break; case 23: *value = (int) GET_NAME (ch); break; case 24: *value = ch->in_room; break; case 25: *value = (int) ch->equip; break; case 26: if ( ch->right_hand ) *value = (int) ch->right_hand; else *value = (int) ch->left_hand; break; case 27: if ( ch->pc ) *value = 0; else if ( ch->mob->reset_zone != 0 || ch->mob->reset_cmd != 0 ) *value = zone_table [ch->mob->reset_zone].cmd [ch->mob->reset_cmd].arg1; else *value = 0; break; case 28: *value = (int) (IS_SUBDUER (ch) ? ch->subdue : NULL); break; } } void mob_obj_indirect (char *token, int *value, int *type) { OBJ_DATA *obj = NULL; int i = 0; struct ind_data_t { int which_case; char ind [15]; int type; } ind_data [] = { { 0, "virtual", MP_TYPE_INTEGER }, { 1, "zone", MP_TYPE_INTEGER }, { 2, "in_room", MP_TYPE_INTEGER }, { 3, "name", MP_TYPE_STRING }, { 4, "short", MP_TYPE_STRING }, { 5, "carried_by", MP_TYPE_CHAR_DATA }, { 6, "equiped_by", MP_TYPE_CHAR_DATA }, { 7, "in_obj", MP_TYPE_OBJ_DATA }, { 8, "contains", MP_TYPE_OBJ_DATA }, { 9, "next_content", MP_TYPE_OBJ_DATA }, { 10, "location", MP_TYPE_INTEGER }, { 11, "contained_wt", MP_TYPE_INTEGER }, { 12, "obj_weight", MP_TYPE_INTEGER }, { 13, "total_weight", MP_TYPE_INTEGER }, { 14, "oval0", MP_TYPE_INTEGER }, { 15, "oval1", MP_TYPE_INTEGER }, { 16, "oval2", MP_TYPE_INTEGER }, { 17, "oval3", MP_TYPE_INTEGER }, { 18, "oval4", MP_TYPE_INTEGER }, { 19, "oval5", MP_TYPE_INTEGER }, { 20, "cost", MP_TYPE_INTEGER }, { 0, "\0", 0 } }; *type = -1; if ( !*value ) return; obj = (OBJ_DATA *) *value; for ( i = 0; *ind_data [i].ind; i++ ) { if ( !str_cmp (ind_data [i].ind, token) ) break; } if ( !*ind_data [i].ind ) return; *type = ind_data [i].type; switch ( ind_data [i].which_case ) { case 0: *value = obj->virtual; break; case 1: *value = obj->zone; break; case 2: *value = obj->in_room; break; case 3: (void)one_argument (obj->name, s_buf); *value = (int) s_buf; break; case 4: *value = (int) obj->short_description; break; case 5: *value = (int) obj->carried_by; break; case 6: *value = (int) obj->equiped_by; break; case 7: *value = (int) obj->in_obj; break; case 8: *value = (int) obj->contains; break; case 9: *value = (int) obj->next_content; break; case 10: *value = obj->location; break; case 11: *value = obj->contained_wt; break; case 12: *value = obj->obj_flags.weight; break; case 13: *value = OBJ_MASS (obj); break; case 14: *value = obj->o.od.value [0]; break; case 15: *value = obj->o.od.value [1]; break; case 16: *value = obj->o.od.value [2]; break; case 17: *value = obj->o.od.value [3]; break; case 18: *value = obj->o.od.value [4]; break; case 19: *value = obj->o.od.value [5]; break; case 20: *value = obj->obj_flags.cost; break; } } void mob_room_indirect (CHAR_DATA *mob, char *token, int *value, int *type) { ROOM_DATA *room = NULL; CHAR_DATA *person = NULL; int i = 0; struct ind_data_t { int which_case; char ind [15]; int type; } ind_data [] = { { 0, "people", MP_TYPE_CHAR_DATA }, { 1, "sector_type", MP_TYPE_INTEGER }, { 2, "virtual", MP_TYPE_INTEGER }, { 3, "zone", MP_TYPE_INTEGER }, { 4, "deity", MP_TYPE_INTEGER }, { 5, "contents", MP_TYPE_OBJ_DATA }, { 6, "room_flags", MP_TYPE_INTEGER }, { 7, "light", MP_TYPE_INTEGER }, { 0, "\0", 0} }; *type = -1; if ( !*value ) return; room = (ROOM_DATA *) *value; for ( i = 0; *ind_data [i].ind; i++ ) { if ( !str_cmp (ind_data [i].ind, token) ) break; } if ( !*ind_data [i].ind ) return; *type = ind_data [i].type; switch ( ind_data [i].which_case ) { case 0: person = room->people; while ( person && !CAN_SEE (mob, person) ) person = person->next_in_room; *value = (int) person; break; case 1: *value = room->sector_type; break; case 2: *value = room->virtual; break; case 3: *value = room->zone; break; case 4: *value = room->deity; break; case 5: *value = (int) room->contents; break; case 6: *value = room->room_flags; break; case 7: *value = room->light; break; } return; } void mob_int_indirect (char *token, int *value, int *type) { if ( !str_cmp (token, "room") ) { *value = (int) vtor (*value); *type = MP_TYPE_ROOM_DATA; } else if ( !str_cmp (token, "obj") ) { *value = (int) vtoo (*value); *type = MP_TYPE_OBJ_DATA; } else if ( !str_cmp (token, "char") ) { *value = (int) vtom (*value); *type = MP_TYPE_CHAR_DATA; } else { *value = 0; *type = -1; } return; } CHAR_DATA *mp_whohasobj (CHAR_DATA *mob, int obj_virt) { CHAR_DATA *tch = NULL; OBJ_DATA *tobj = NULL; ROOM_DATA *room = NULL; room = vtor (mob->in_room); if ( !room ) return NULL; /* If the character is hiding the object in another object, this routine will not find it */ for ( tch = room->people; tch; tch = tch->next_in_room ) { if ( tch->right_hand && tch->right_hand->virtual == obj_virt && CAN_SEE_OBJ (mob, tch->right_hand) ) return tch; if ( tch->left_hand && tch->left_hand->virtual == obj_virt && CAN_SEE_OBJ (mob, tch->left_hand) ) return tch; for ( tobj = tch->equip; tobj; tobj = tobj->next_content ) if ( tobj->virtual == obj_virt && CAN_SEE_OBJ (mob, tobj) ) return tobj->equiped_by; } return NULL; } int mp_get_eval_token (CHAR_DATA *mob, char **p, int *token_type, int *value) { VAR_DATA *var = NULL; char token [MAX_INPUT_LENGTH]; int low_number = 0; int high_number = 0; int current_type = 0; int current_value = 0; int obj_virt = 0; CHAR_DATA *tch1 = NULL; CHAR_DATA *tch2 = NULL; *value = 0; *token_type = 0; while ( isspace (**p) ) (*p)++; /* Number */ if ( isdigit (**p) ) { while ( isdigit (**p) ) { *value = *value * 10 + ( **p - '0' ); (*p)++; } *token_type = MP_TYPE_INTEGER; return 1; } if ( !strncmp (*p, "==", 2) ) *token_type = MP_EQUAL; else if ( !strncmp (*p, ">=", 2) ) *token_type = MP_GREATER_EQUAL; else if ( !strncmp (*p, "<=", 2) ) *token_type = MP_LESSER_EQUAL; else if ( !strncmp (*p, "!=", 2) ) *token_type = MP_NOT_EQUAL; else if ( !strncmp (*p, "<>", 2) ) *token_type = MP_NOT_EQUAL; else if ( !strncmp (*p, "&&", 2) ) *token_type = MP_AND; else if ( !strncmp (*p, "||", 2) ) *token_type = MP_OR; if ( *token_type ) { (*p) += 2; return 1; } if ( **p == '+' ) *token_type = MP_PLUS; else if ( **p == '/' ) *token_type = MP_DIV; else if ( **p == '*' ) *token_type = MP_MULT; else if ( **p == '-' ) *token_type = MP_SUB; else if ( **p == '(' ) *token_type = MP_OPEN_PAR; else if ( **p == ')' ) *token_type = MP_CLOSE_PAR; else if ( **p == '<' ) *token_type = MP_LESS_THAN; else if ( **p == '>' ) *token_type = MP_GREATER_THAN; else if ( **p == '%' ) *token_type = MP_MOD; else if ( **p == ',' ) *token_type = MP_COMMA; if ( *token_type ) { (*p)++; return 1; } /* Numbers and operations are exhausted, try functions, then variables */ if ( !mob_get_token (p, token, MT_VARIABLE) ) return 0; if ( (var = getvar (mob, token)) ) { current_type = var->type; current_value = var->value; } else { if ( !str_cmp (token, "number") ) { require_open_paren (p); low_number = mp_eval_eq (mob, p); high_number = mp_eval_eq (mob, p); *value = number (low_number, high_number); *token_type = MP_TYPE_INTEGER; } else if ( !str_cmp (token, "dice") ) { require_open_paren (p); *value = dice (mp_eval_eq (mob, p), mp_eval_eq (mob, p)); *token_type = MP_TYPE_INTEGER; } else if ( !str_cmp (token, "mud_hour") ) { *value = time_info.hour; *token_type = MP_TYPE_INTEGER; } else if ( !str_cmp (token, "mud_day") ) { *value = time_info.day; *token_type = MP_TYPE_INTEGER; } else if ( !str_cmp (token, "mud_month") ) { *value = time_info.month; *token_type = MP_TYPE_INTEGER; } else if ( !str_cmp (token, "mud_year") ) { *value = time_info.month; *token_type = MP_TYPE_INTEGER; } else if ( !str_cmp (token, "mud_minute") ) { *value = 4 * (15 * 60 - (next_hour_update - time (0))) / 60; *token_type = MP_TYPE_INTEGER; } else if ( !str_cmp (token, "is_following") ) { require_open_paren (p); tch1 = (CHAR_DATA *) mp_eval_eq (mob, p); tch2 = (CHAR_DATA *) mp_eval_eq (mob, p); *value = (tch2->following == tch1); *token_type = MP_TYPE_INTEGER; } else if ( !str_cmp (token, "is_wielding") ) { require_open_paren (p); tch1 = (CHAR_DATA *) mp_eval_eq (mob, p); if ( get_equip (tch1, WEAR_PRIM) || get_equip (tch1, WEAR_SEC) || get_equip (tch1, WEAR_BOTH) ) *value = 1; else *value = 0; *token_type = MP_TYPE_INTEGER; require_open_paren (p); } else if ( !str_cmp (token, "whohasobj") ) { require_open_paren (p); tch1 = (CHAR_DATA *) mp_eval_eq (mob, p); obj_virt = mp_eval_eq (mob, p); *value = (int) mp_whohasobj (mob, obj_virt); *token_type = MP_TYPE_CHAR_DATA; } else if ( !str_cmp (token, "get_equip") ) { require_open_paren (p); tch1 = (CHAR_DATA *) mp_eval_eq (mob, p); if ( !mob_get_token (p, token, MT_VARIABLE) ) return 0; *value = 0; if ( !str_cmp (token, "FINGER_R") ) *value = (int) get_equip (tch1, WEAR_FINGER_R); else if ( !str_cmp (token, "FINGER_L") ) *value = (int) get_equip (tch1, WEAR_FINGER_L); else if ( !str_cmp (token, "NECK_1") ) *value = (int) get_equip (tch1, WEAR_NECK_1); else if ( !str_cmp (token, "NECK_2") ) *value = (int) get_equip (tch1, WEAR_NECK_2); else if ( !str_cmp (token, "BODY_1") ) *value = (int) get_equip (tch1, WEAR_BODY_1); else if ( !str_cmp (token, "BODY_2") ) *value = (int) get_equip (tch1, WEAR_BODY_2); else if ( !str_cmp (token, "BODY_3") ) *value = (int) get_equip (tch1, WEAR_BODY_3); else if ( !str_cmp (token, "BODY_4") ) *value = (int) get_equip (tch1, WEAR_BODY_4); else if ( !str_cmp (token, "BODY_5") ) *value = (int) get_equip (tch1, WEAR_BODY_5); else if ( !str_cmp (token, "HEAD") ) *value = (int) get_equip (tch1, WEAR_HEAD); else if ( !str_cmp (token, "LEGS_1") ) *value = (int) get_equip (tch1, WEAR_LEGS_1); else if ( !str_cmp (token, "LEGS_2") ) *value = (int) get_equip (tch1, WEAR_LEGS_2); else if ( !str_cmp (token, "FEET_1") ) *value = (int) get_equip (tch1, WEAR_FEET_1); else if ( !str_cmp (token, "FEET_2") ) *value = (int) get_equip (tch1, WEAR_FEET_2); else if ( !str_cmp (token, "HANDS") ) *value = (int) get_equip (tch1, WEAR_HANDS); else if ( !str_cmp (token, "ARMS") ) *value = (int) get_equip (tch1, WEAR_ARMS); else if ( !str_cmp (token, "SHIELD") ) *value = (int) get_equip (tch1, WEAR_SHIELD); else if ( !str_cmp (token, "ABOUT") ) *value = (int) get_equip (tch1, WEAR_ABOUT); else if ( !str_cmp (token, "WAIST") ) *value = (int) get_equip (tch1, WEAR_WAIST); else if ( !str_cmp (token, "WRIST_R") ) *value = (int) get_equip (tch1, WEAR_WRIST_R); else if ( !str_cmp (token, "WRIST_L") ) *value = (int) get_equip (tch1, WEAR_WRIST_L); else if ( !str_cmp (token, "PRIM") ) *value = (int) get_equip (tch1, WEAR_PRIM); else if ( !str_cmp (token, "SEC") ) *value = (int) get_equip (tch1, WEAR_SEC); else if ( !str_cmp (token, "BOTH") ) *value = (int) get_equip (tch1, WEAR_BOTH); else if ( !str_cmp (token, "BELT_1") ) *value = (int) get_equip (tch1, WEAR_BELT_1); else if ( !str_cmp (token, "BELT_2") ) *value = (int) get_equip (tch1, WEAR_BELT_2); else if ( !str_cmp (token, "BACK") ) *value = (int) get_equip (tch1, WEAR_BACK); *token_type = MP_TYPE_OBJ_DATA; require_open_paren (p); /* Looking for close paren, actually */ } else return 0; /* Not a variable, and not a function or special var */ /* If we get here, the function was evaluated. */ current_type = *token_type; current_value = *value; } while ( !strncmp (*p, "->", 2) ) { (*p) += 2; if ( !mob_get_token (p, token, MT_VARIABLE) ) /* looking after -> */ return 0; if ( current_type == MP_TYPE_CHAR_DATA ) mob_char_indirect (mob, token, ¤t_value, ¤t_type); else if ( current_type == MP_TYPE_ROOM_DATA ) mob_room_indirect (mob, token, ¤t_value, ¤t_type); else if ( current_type == MP_TYPE_OBJ_DATA ) mob_obj_indirect (token, ¤t_value, ¤t_type); else if ( current_type == MP_TYPE_INTEGER ) mob_int_indirect (token, ¤t_value, ¤t_type); else return 0; if ( current_type == -1 ) return 0; } *value = current_value; *token_type = current_type; return 1; } int mp_eval_eq (CHAR_DATA *mob, char **equation) { int token_type = 0; int token_value = 0; int left_side = 0; int right_side = 0; int sign_flag = 0; int operation = 0; left_side = 0; operation = MP_PLUS; while ( **equation ) { sign_flag = -1; do { sign_flag = -sign_flag; if ( !mp_get_eval_token (mob, equation, &token_type, &token_value) ) return -9998; } while ( token_type == MP_SUB ); if ( token_type == MP_OPEN_PAR ) right_side = mp_eval_eq (mob, equation); else if ( token_type == MP_TYPE_INTEGER || token_type == MP_TYPE_CHAR_DATA || token_type == MP_TYPE_OBJ_DATA || token_type == MP_TYPE_ROOM_DATA ) right_side = token_value; else { printf ("Failure to understand token-type: %d:%d\n", token_value, token_type); return -9997; } right_side = right_side * sign_flag; switch (operation) { case MP_PLUS: left_side = left_side + right_side; break; case MP_SUB: left_side = left_side - right_side; break; case MP_DIV: if ( !right_side ) return -9996; left_side = left_side / right_side; break; case MP_MULT: left_side = left_side * right_side; break; case MP_MOD: left_side = left_side % right_side; break; case MP_LESS_THAN: left_side = (left_side < right_side); break; case MP_GREATER_THAN: left_side = (left_side > right_side); break; case MP_EQUAL: left_side = (left_side == right_side); break; case MP_GREATER_EQUAL: left_side = (left_side >= right_side); break; case MP_LESSER_EQUAL: left_side = (left_side <= right_side); break; case MP_NOT_EQUAL: left_side = (left_side != right_side); break; case MP_AND: left_side = (left_side && right_side) ? 1 : 0; break; case MP_OR: left_side = (left_side || right_side) ? 1 : 0; break; } if ( !mp_get_eval_token (mob, equation, &operation, &token_value) || operation == MP_CLOSE_PAR || operation == MP_COMMA ) return left_side; if ( operation < MP_LESS_THAN || operation > MP_MULT ) return -9995; } return left_side; } char *get_prog_token (char **buf, char *token) { char *p = NULL; p = token; while ( **buf == ' ' || **buf == '\n' || **buf == '\t' || **buf == '\r' ) (*buf)++; while ( **buf && **buf != '\n' && **buf != ' ' && **buf != '\t' && **buf != '\r' ) { *token++ = **buf; (*buf)++; } *token = '\0'; return p; } char *get_line (char **buf, char *ret_buf) /* Used by aliases too */ { char *p = NULL; if ( !*buf ) return NULL; while ( **buf == ' ' || **buf == '\n' || **buf == '\t' || **buf == '\r' ) (*buf)++; p = *buf; while ( **buf && **buf != '\n' ) { *ret_buf++ = **buf; (*buf)++; } *ret_buf = '\0'; return *p ? p : NULL; } void mob_string_token (char **argument, char *arg_first, int *quoted) { char cEnd; while ( isspace( **argument ) ) (*argument)++; cEnd = ' '; if ( **argument == '\'' || **argument == '"' ) { cEnd = **argument; (*argument)++; *quoted = 1; } else *quoted = 0; while ( **argument != '\0' ) { if ( **argument == cEnd ) { (*argument)++; break; } if ( cEnd == ' ' ) *arg_first = LOWER (**argument); else *arg_first = **argument; arg_first++; (*argument)++; } *arg_first = '\0'; while ( isspace( **argument ) ) (*argument)++; /* If the next character is (, then we're probably dealing with a function. */ if ( **argument == '(' ) { *arg_first++ = ' '; /* Make sure there is a space before ( */ *arg_first = '\0'; while ( **argument != '\0' && **argument != ')' ) { *arg_first++ = LOWER (**argument); (*argument)++; } (*argument)++; *arg_first++ = ')'; *arg_first = '\0'; } while ( isspace( **argument ) ) (*argument)++; /* If the next characters are -> then we are dereferencing a variable or function */ if ( **argument == '-' && *(*argument + 1) == '>' ) { (*argument)++; (*argument)++; *arg_first++ = '-'; *arg_first++ = '>'; *arg_first = '\0'; while ( **argument != '\0' && **argument != ')' && **argument != ' ' ) { *arg_first++ = LOWER (**argument); (*argument)++; } *arg_first = '\0'; } } void mob_string (CHAR_DATA *mob, char **p, char *string) { int type = 0; int value = 0; int quoted = 0; char token [MAX_STRING_LENGTH] = {'\0'}; char buf [MAX_STRING_LENGTH] = {'\0'}; char **tmp_ptr; char *tptr = NULL; *string = '\0'; mob_string_token (p, token, "ed); while ( *token ) { if ( quoted ) strcat (string, token); else { tptr = token; tmp_ptr = &tptr; if ( !mp_get_eval_token (mob, tmp_ptr, &type, &value) ) { printf ("Failed to get token ;(\n"); fflush (stdout); snprintf (buf, MAX_STRING_LENGTH, "Failed to get mobprog token for mob VNUM %d.", mob->mob->virtual); system_log (buf, TRUE); return; } if ( type == MP_TYPE_INTEGER ) snprintf (string + strlen (string), MAX_STRING_LENGTH, "%d", value); else if ( type == MP_TYPE_STRING ) strcat (string, (char *) value); else { snprintf (string + strlen (string), MAX_STRING_LENGTH, "?%d:%d?", value, type); return; } } mob_string_token (p, token, "ed); } } void assignment (CHAR_DATA *mob, MOBPROG_DATA *program, char *target_name, char **p) { char buf [MAX_STRING_LENGTH] = {'\0'}; VAR_DATA *target; if ( !(target = getvar (mob, target_name)) ) { snprintf (buf, MAX_STRING_LENGTH, "Assignment to unknown variable: %s", target_name); system_log (buf, TRUE); snprintf (buf, MAX_STRING_LENGTH, "trigger %s, mob %d", program->trigger_name, mob->mob->virtual); system_log (buf, TRUE); program->line = str_dup (current_line); SET_BIT (program->flags, MPF_BROKEN); return; } if ( !mob_get_token (p, buf, MT_EQUAL) ) { system_log("Assignment needs equal.", TRUE); program->line = str_dup (current_line); SET_BIT (program->flags, MPF_BROKEN); return; } if ( target->type == MP_TYPE_INTEGER ) { target->value = mp_eval_eq (mob, p); return; } if ( target->type == MP_TYPE_CHAR_DATA || target->type == MP_TYPE_OBJ_DATA || target->type == MP_TYPE_ROOM_DATA ) { target->value = mp_eval_eq (mob, p); return; } else if ( target->type == MP_TYPE_STRING ) { mob_string (mob, p, buf); if ( target->value ) mem_free ((char *) target->value); target->value = (int) str_dup (buf); } } #define MAX_PROG_LINES 1000 #define MAX_DEPTH 500 void mobprog (CHAR_DATA *ch, CHAR_DATA *mob, MOBPROG_DATA *program, int trigger, char *argument, int *ret) { char line [MAX_STRING_LENGTH] = {'\0'}; char command [MAX_STRING_LENGTH] = {'\0'}; char *line_ptr = NULL; char *prog_ptr = NULL; char *prog = NULL; char token [MAX_STRING_LENGTH] = {'\0'}; int levels [MAX_PROG_LINES]; char *level_ptrs [MAX_PROG_LINES]; int level_ifs [MAX_DEPTH]; int lines; int level; int cur_level; int line_no; int i; int temp_arg = 0; return; if ( ch->desc ) { *ret = 0; return; } if ( port == BUILDER_PORT ) return; prog = program->prog; for ( lines = 0; lines < MAX_PROG_LINES; lines++ ) { levels [lines] = 0; level_ptrs [lines] = NULL; } (void)setvar (mob, "mob", (int) mob, MP_TYPE_CHAR_DATA); (void)setvar (mob, "ch", (int) ch, MP_TYPE_CHAR_DATA); temp_arg = (int) vtor (mob->in_room); (void)setvar (mob, "room", temp_arg, MP_TYPE_ROOM_DATA); prog_ptr = prog; level = 0; lines = 0; while ( (level_ptrs [lines] = get_line (&prog_ptr, line)) ) { line_ptr = line; (void)get_prog_token (&line_ptr, token); if ( !str_cmp (token, "if") || !str_cmp (token, "while") ) levels [lines] = level++; else if ( !str_cmp (token, "elseif") || !str_cmp (token, "else") ) levels [lines] = level - 1; else if ( !str_cmp (token, "endif") || !str_cmp (token, "endwhile") ) levels [lines] = --level; else levels [lines] = level; lines++; } line_no = 0; /* Re-entry for a delayed trigger */ if ( ch->trigger_id == trigger ) { /* Reset delayed trigger if trigger re-activated (vs the delay timer expired) */ if ( !ch->trigger_delay ) { /* Make it so all ifs (elseifs..) appear executed */ for ( i = 0; i < MAX_DEPTH; i++ ) level_ifs [i] = 1; line_no = ch->trigger_line; } ch->trigger_id = 0; ch->trigger_delay = 0; } prog_ptr = level_ptrs [line_no]; while ( get_line (&prog_ptr, line) ) { strcpy (current_line, line); if ( GET_POS (ch) == POSITION_DEAD ) { *ret = 0; return; } line_ptr = line; (void)get_prog_token (&line_ptr, token); /******TODO figure out what to do in the following cases if ( *token == '!' ) ; else if ( !*token || *token == '\n' || *token == '\r' || *token == ' ' ) ; else *********************/ if ( !str_cmp (token, "ci") ) { mob_string (mob, &line_ptr, command); if ( *command ) command_interpreter (mob, command); if ( mob->deleted ) return; if ( mob->delay || GET_FLAG (mob, FLAG_ENTERING) || GET_FLAG (mob, FLAG_LEAVING) ) { SET_BIT (mob->flags, FLAG_INHIBITTED); mob->trigger_delay = 1; mob->trigger_line = line_no + 1; mob->trigger_id = trigger; return; } } else if ( !str_cmp (token, "global") || !str_cmp (token, "var") ) define_variable (mob, program, line_ptr); else if ( !str_cmp (token, "while") ) { require_open_paren (&line_ptr); cur_level = levels [line_no]; if ( !mp_eval_eq (mob, &line_ptr) ) { line_no++; while ( levels [line_no] != cur_level ) line_no++; } } else if ( !str_cmp (token, "if") || !str_cmp (token, "elseif") ) { cur_level = levels [line_no]; if ( !str_cmp (token, "if") ) level_ifs [cur_level] = 0; if ( !level_ifs [cur_level] ) require_open_paren (&line_ptr); if ( level_ifs [cur_level] || !mp_eval_eq (mob, &line_ptr) ) { while ( levels [line_no + 1] != cur_level ) line_no++; } else level_ifs [cur_level] = 1; } else if ( !str_cmp (token, "else") ) { cur_level = levels [line_no]; if ( level_ifs [cur_level] ) { while ( levels [line_no + 1] != cur_level ) line_no++; } } else if ( !str_cmp (token, "reject_command") ) { *ret = 0; return; } else if ( !str_cmp (token, "endwhile") ) { cur_level = levels [line_no]; if ( line_no > 0 ) /* If there is an endwhile on the first line */ line_no--; while ( line_no && levels [line_no] != cur_level ) line_no--; line_no--; /* Gets incremented at the end of the while */ } /** TODO find out what to do with this statement **/ /* else if ( !str_cmp (token, "endif") ) */ /** do nothing in this case**/ /* ; */ else if ( !str_cmp (token, "delay") ) { /* A previously delayed trigger is forgotten */ mob->trigger_delay = mp_eval_eq (mob, &line_ptr); mob->trigger_line = line_no + 1; mob->trigger_id = trigger; return; } else if ( !str_cmp (token, "alarm") ) mob->alarm = mp_eval_eq (mob, &line_ptr); else if ( !str_cmp (token, "return") ) return; else assignment (mob, program, token, &line_ptr); prog_ptr = level_ptrs [++line_no]; } return; } /* The trigger command is activated for mobs in the room that ch occupies. */ int trigger (CHAR_DATA *ch, char *argument, int trigger) { CHAR_DATA *mob = NULL; ROOM_DATA *room = NULL; MOBPROG_DATA *prog = NULL; int ret = 1; /* if false, no more progs, don't do command */ room = ch->room; if ( !room ) return 1; if ( trigger == TRIG_MOBACT || trigger == TRIG_HOUR || trigger == TRIG_DAY || trigger == TRIG_ALARM || (trigger == ch->trigger_id && !ch->trigger_delay) ) { for ( prog = ch->prog; prog; prog = prog->next ) if ( !str_cmp (prog->trigger_name, mobprog_triggers [trigger]) && !IS_SET (prog->flags, MPF_BROKEN) ) mobprog (ch, ch, prog, trigger, argument, &ret); return ret; } for ( mob = room->people; mob && ret; mob = mob->next_in_room ) { if ( mob == ch ) continue; for ( prog = mob->prog; prog && ret; prog = prog->next ) { fflush (stdout); if ( !str_cmp (prog->trigger_name, mobprog_triggers [trigger]) && !IS_SET (prog->flags, MPF_BROKEN) ) mobprog (ch, mob, prog, trigger, argument, &ret); } } return ret; } void add_replace_mobprog_data (CHAR_DATA *ch, CHAR_DATA *mob, char *trigger_name, char *prog_data) { MOBPROG_DATA *prog = NULL; MOBPROG_DATA *last_prog = NULL; for ( prog = mob->prog; prog; prog = prog->next ) { last_prog = prog; if ( !str_cmp (prog->trigger_name, trigger_name) ) break; } if ( !prog ) { prog = malloc (sizeof (MOBPROG_DATA)); if ( last_prog ) last_prog->next = prog; else mob->prog = prog; prog->trigger_name = str_dup (trigger_name); prog->next = NULL; } else mem_free (prog->prog); prog->prog = str_dup (prog_data); prog->busy = 0; return; } void do_prog (CHAR_DATA *ch, char *argument, int cmd) { int ind = 0; int got_line = 0; char file_name [MAX_INPUT_LENGTH]; char *prog_data = NULL; char *trigger_name = NULL; char buf [MAX_STRING_LENGTH] = {'\0'}; char subcmd [MAX_STRING_LENGTH] = {'\0'}; CHAR_DATA *edit_mob = NULL; CHAR_DATA *tch = NULL; MOBPROG_DATA *prog = NULL; FILE *mp = NULL; FILE *fp = NULL; CHAR_DATA *temp_char = NULL; if ( IS_NPC (ch) ) { send_to_char ("This is a PC only command.\n\r", ch); return; } if ( !(edit_mob = vtom (ch->pc->edit_mob)) ) { edit_mob = ch->pc->edit_player; if ( !(ch->pc->edit_player) || !(edit_mob) ) { send_to_char ("Start by using the MOBILE command.\n\r", ch); return; } } if ( !IS_NPC (edit_mob) ) { send_to_char ("Too dangerous to use this on a PC. Try a mob.\n", ch); return; } argument = one_argument (argument, subcmd); if ( !*subcmd || !str_cmp (subcmd, "?") ) { send_to_char ("\n\r", ch); send_to_char ("prog clear <trigger>- reset error flags on mobs triggers\n\r", ch); send_to_char ("prog load <name> - load program from lib/mobprogs dir\n\r", ch); send_to_char ("prog save - save ALL programs to mobprogs.0\n\r", ch); send_to_char ("prog look <name> - read a program in lib/mobprogs dir\n\r", ch); send_to_char ("prog list - listing of lib/mobprogs directory\n\r", ch); send_to_char ("prog errors - listing of disabled mob progs\n\r", ch); send_to_char ("prog <trigger> - lists trigger for current mob\n\r", ch); return; } else if ( !str_cmp (subcmd, "load") ) { argument = one_argument (argument, file_name); if ( file_name [0] == '.' || file_name [0] == '/' ) { send_to_char ("Sorry, your programs must be located in the " "lib/mobprogs directory.\n\r", ch); send_to_char ("This enforces security.\n\r", ch); return; } snprintf (buf, MAX_STRING_LENGTH, "mobprogs/%s", file_name); if ( !(mp = fopen (buf, "r")) ) { send_to_char ("Unable to open file.\n\r", ch); return; } while ( *(trigger_name = fread_string (mp)) ) { prog_data = fread_string (mp); snprintf (buf, MAX_STRING_LENGTH, ". . . Adding program %s\n\r", trigger_name); send_to_char (buf, ch); add_replace_mobprog_data (ch, edit_mob, trigger_name, prog_data); } fclose (mp); } else if ( (ind = index_lookup (mobprog_triggers, subcmd)) != -1 ) { for ( prog = edit_mob->prog; prog; prog = prog->next ) if ( !str_cmp (prog->trigger_name, mobprog_triggers [ind]) ) { page_string (ch->desc, prog->prog); break; } if ( !prog ) send_to_char ("No such program defined.\n\r", ch); } else if ( !str_cmp (subcmd, "clear") ) { argument = one_argument (argument, subcmd); if ( (ind = index_lookup (mobprog_triggers, subcmd)) == -1 ) { send_to_char ("No such trigger.\n\r", ch); return; } for ( prog = edit_mob->prog; prog; prog = prog->next ) if ( !str_cmp (prog->trigger_name, mobprog_triggers [ind]) ) { REMOVE_BIT (prog->flags, MPF_BROKEN); return; } } else if ( !str_cmp (subcmd, "save") ) { if ( !mp_dirty ) send_to_char ("Mob programs don't really need writing...\n\r", ch); if ( !(mp = open_and_rename (ch, "mobprogs", 0)) ) { send_to_char ("Unable to open mobprogs!\n\r", ch); return; } mp_dirty = 0; for ( tch = full_mobile_list; tch; tch = tch->mob->lnext ) { for ( prog = tch->prog; prog; prog = prog->next ) { fprintf (mp, "#%d\n", tch->mob->virtual); fprintf (mp, "%s~\n", prog->trigger_name); fprintf (mp, "%s~\n", prog->prog); } } fprintf (mp, "$~\n"); fclose (mp); } else if ( !str_cmp (subcmd, "look") ) { argument = one_argument (argument, subcmd); if ( subcmd [0] == '.' || subcmd [0] == '/' ) { send_to_char ("Oh! Oh! You, you! ... That's what you are!\n\r", ch); return; } snprintf (buf, MAX_STRING_LENGTH, "mobprogs/%s", subcmd); if ( !(fp = fopen (buf, "r")) ) { send_to_char ("Sorry. I couldn't open that file.\n\r", ch); return; } while ( fgets (buf, 132, fp) ) { got_line = 1; send_to_char (buf, ch); } fclose (fp); } else if ( !str_cmp (subcmd, "errors") ) { for ( prog = full_prog_list; prog; prog = prog->next_full_prog ) { if ( IS_SET (prog->flags, MPF_BROKEN) ) { snprintf (buf, MAX_STRING_LENGTH, "Mob %-5d %-10s $N", prog->mob_virtual, prog->trigger_name); temp_char = vtom (prog->mob_virtual); act (buf, TRUE, ch, 0, temp_char, TO_CHAR); snprintf (buf, MAX_STRING_LENGTH, " %s\n", prog->line); send_to_char (buf, ch); } } } else send_to_char ("Unknown keyword.\n\r", ch); redefine_mobiles (edit_mob); return; } void delayed_trigger_activity () { CHAR_DATA *ch = NULL; for ( ch = character_list; ch; ch = ch->next ) { if ( ch->deleted ) continue; if ( GET_FLAG (ch, FLAG_INHIBITTED) ) { if ( !GET_FLAG (ch, FLAG_ENTERING) && !GET_FLAG (ch, FLAG_LEAVING) && !ch->delay ) REMOVE_BIT (ch->flags, FLAG_INHIBITTED); else continue; } if ( ch->trigger_delay && !--(ch->trigger_delay) ) trigger (ch, "", ch->trigger_id); } return; } void boot_mobprogs () { int virtual = 0; char file_name [MAX_STRING_LENGTH] = {'\0'}; char buf [MAX_STRING_LENGTH] = {'\0'}; MOBPROG_DATA *prog = NULL; MOBPROG_DATA *last_prog = NULL; CHAR_DATA *mob = NULL; FILE *mp = NULL; char *temp_arg = NULL; snprintf (file_name, MAX_STRING_LENGTH, "%s/mobprogs.0", REGIONS); if ( !(mp = fopen (file_name, "r")) ) { perror ("Unable to open mobprogs.0"); system_log("ERROR: NO MOB PROGRAMS WERE LOADED!", TRUE); return; } while ( 1 ) { if ( !fgets (buf, 81, mp) ) { system_log("Error reading mobprog file:", TRUE); perror ("Reading mobprog"); abort(); } if ( *buf == '#' ) { sscanf (buf, "#%d", &virtual); if ( !(mob = vtom (virtual)) ) { temp_arg = fread_string (mp); snprintf (buf, MAX_STRING_LENGTH, "Mob prog %s lost because mob %d doesn't exist.", temp_arg, virtual); system_log(buf, TRUE); (void)fread_string (mp); continue; } for ( last_prog = mob->prog; last_prog && last_prog->next; last_prog = last_prog->next ) ; prog = malloc (sizeof (MOBPROG_DATA)); if ( last_prog ) last_prog->next = prog; else mob->prog = prog; prog->trigger_name = fread_string (mp); prog->prog = fread_string (mp); prog->next = NULL; prog->busy = 0; prog->mob_virtual = mob->mob ? mob->mob->virtual : -1; /* Adding a mob program to this list THIS way will reverse the order from the file, hopefully this is ok. */ prog->next_full_prog = full_prog_list; full_prog_list = prog; } else if ( *buf == '$' ) break; } fclose (mp); }