text: /**************************************************************************
  *  File: dg_variables.c                                    Part of tbaMUD *
  *  Usage: Contains the functions dealing with variable substitution.      *
  *                                                                         *
  *  $Author: Mark A. Heilpern/egreen/Welcor $                              *
  *  $Date: 2004/10/11 12:07:00 $                                           *
  *  $Revision: 1.0.14 $                                                    *
  **************************************************************************/
  
  #include "conf.h"
  #include "sysdep.h"
  #include "structs.h"
  #include "dg_scripts.h"
  #include "utils.h"
  #include "comm.h"
  #include "interpreter.h"
  #include "handler.h"
  #include "dg_event.h"
  #include "db.h"
  #include "fight.h"
  #include "screen.h"
  #include "constants.h"
  #include "spells.h"
  #include "oasis.h"
  #include "class.h"
  #include "quest.h"
  #include "act.h"
  #include "genobj.h"
  
  /* Utility functions */
  
  /* Thanks to James Long for his assistance in plugging the memory leak that
   * used to be here. - Welcor */
  /* Adds a variable with given name and value to trigger. */
  void add_var(struct trig_var_data **var_list, const char *name, const char *value, long id)
  {
    struct trig_var_data *vd;
  
    if (strchr(name, '.')) {
      log("add_var() : Attempt to add illegal var: %s", name);
      return;
    }
  
    for (vd = *var_list; vd && str_cmp(vd->name, name); vd = vd->next);
  
    if (vd && (!vd->context || vd->context==id)) {
      free(vd->value);
      CREATE(vd->value, char, strlen(value) + 1);
    }
  
    else {
      CREATE(vd, struct trig_var_data, 1);
  
      CREATE(vd->name, char, strlen(name) + 1);
      strcpy(vd->name, name);                            /* strcpy: ok*/
  
      CREATE(vd->value, char, strlen(value) + 1);
  
      vd->next = *var_list;
      vd->context = id;
      *var_list = vd;
    }
  
    strcpy(vd->value, value);                            /* strcpy: ok*/
  }
  
  /* perhaps not the best place for this, but I didn't want a new file */
  char *skill_percent(struct char_data *ch, char *skill)
  {
    static char retval[16];
    int skillnum;
  
    skillnum = find_skill_num(skill);
    if (skillnum<=0) return("unknown skill");
  
    snprintf(retval, sizeof(retval), "%d", GET_SKILL(ch, skillnum));
    return retval;
  }
  
  /* Search through all the persons items, including containers. 0 if it doesnt
   * exist, and greater then 0 if it does! Jamie Nelson.  Now also searches by
   * vnum and returns the number of matching objects. - Welcor */
  int item_in_list(char *item, obj_data *list)
  {
    obj_data *i;
    int count = 0;
  
    if (!item || !*item)
      return 0;
  
    if (*item == UID_CHAR) {
      long id = atol(item + 1);
  
      for (i = list; i; i = i->next_content) {
        if (id == i->script_id)
          count ++;
        if (GET_OBJ_TYPE(i) == ITEM_CONTAINER)
          count += item_in_list(item, i->contains);
      }
    } else if (is_number(item)) { /* check for vnum */
      obj_vnum ovnum = atoi(item);
  
      for (i = list; i; i = i->next_content) {
        if (GET_OBJ_VNUM(i) == ovnum)
          count++;
        if (GET_OBJ_TYPE(i) == ITEM_CONTAINER)
          count += item_in_list(item, i->contains);
      }
    } else {
      for (i = list; i; i = i->next_content) {
        if (isname(item, i->name))
          count++;
        if (GET_OBJ_TYPE(i) == ITEM_CONTAINER)
          count += item_in_list(item, i->contains);
      }
    }
    return count;
  }
  
  /* BOOLEAN return, just check if a player or mob has an item of any sort,
   * searched for by name or id. Searching equipment as well as inventory, and
   * containers. Jamie Nelson */
  int char_has_item(char *item, struct char_data *ch)
  {
  
    /* If this works, no more searching needed */
    if (get_object_in_equip(ch, item) != NULL)
      return 1;
  
    if (item_in_list(item, ch->carrying) == 0)
      return 0;
    else
      return 1;
  }
  
  static int handle_oset(struct obj_data * obj, char * argument)
  {
    int i = 0;
    bool found = FALSE;
    char value[MAX_INPUT_LENGTH];
    
    struct oset_handler {
      const char * type;
      bool (* name)(struct obj_data *, char *);
    } handler[] = {
      { "alias",     oset_alias },
      { "apply",     oset_apply },
      { "longdesc",  oset_long_description },
      { "shortdesc", oset_short_description},
      { "\n", NULL }
    };
    
    if (!obj || !*argument)
      return 0;
    
    argument = one_argument(argument, value);
    
    while (*handler[i].type != '\n') {
      if (is_abbrev(value, handler[i].type)) {
        found = TRUE;
        break;
      }
      i++;
    } 
    
    if (!found)
      return 0;
      
    handler[i].name(obj, argument);  
    return 1;
  }
  
  int text_processed(char *field, char *subfield, struct trig_var_data *vd,
                     char *str, size_t slen)
  {
    char *p, *p2;
    char tmpvar[MAX_STRING_LENGTH];
  
    if (!str_cmp(field, "strlen")) {                     /* strlen    */
      snprintf(str, slen, "%d", (int)strlen(vd->value));
      return TRUE;
    } else if (!str_cmp(field, "toupper")) {             /* toupper   */
      char *upper = vd->value;
      if (*upper)
        snprintf(str, slen, "%c%s", UPPER(*upper), upper + 1);
      return TRUE;
    } else if (!str_cmp(field, "trim")) {                /* trim      */
      /* trim whitespace from ends */
      snprintf(tmpvar, sizeof(tmpvar)-1 , "%s", vd->value); /* -1 to use later*/
      p = tmpvar;
      p2 = tmpvar + strlen(tmpvar) - 1;
      while (*p && isspace(*p)) p++;
      while ((p<=p2) && isspace(*p2)) p2--;
      if (p>p2) { /* nothing left */
        *str = '\0';
        return TRUE;
      }
      *(++p2) = '\0';                                         /* +1 ok (see above) */
      snprintf(str, slen, "%s", p);
      return TRUE;
    } else if (!str_cmp(field, "contains")) {            /* contains  */
      if (str_str(vd->value, subfield))
        strcpy(str, "1");
      else
        strcpy(str, "0");
      return TRUE;
    } else if (!str_cmp(field, "car")) {                 /* car       */
      char *car = vd->value;
      while (*car && !isspace(*car))
        *str++ = *car++;
      *str = '\0';
      return TRUE;
  
    } else if (!str_cmp(field, "cdr")) {                 /* cdr       */
      char *cdr = vd->value;
      while (*cdr && !isspace(*cdr)) cdr++; /* skip 1st field */
      while (*cdr && isspace(*cdr)) cdr++;  /* skip to next */
  
      snprintf(str, slen, "%s", cdr);
      return TRUE;
    } else if (!str_cmp(field, "charat")) {              /* CharAt    */
      size_t len = strlen(vd->value), cindex = atoi(subfield);
      if (cindex > len || cindex < 1)
        strcpy(str, "");
      else
        snprintf(str, slen, "%c", vd->value[cindex - 1]);
      return TRUE;
    } else if (!str_cmp(field, "mudcommand")) {
      /* find the mud command returned from this text */
  /* NOTE: you may need to replace "cmd_info" with "complete_cmd_info", */
  /* depending on what patches you've got applied.                      */
  /* on older source bases:    extern struct command_info *cmd_info; */
      int length, cmd;
      for (length = strlen(vd->value), cmd = 0;
           *cmd_info[cmd].command != '\n'; cmd++)
        if (!strncmp(cmd_info[cmd].command, vd->value, length))
          break;
  
      if (*cmd_info[cmd].command == '\n')
        *str = '\0';
      else
        snprintf(str, slen, "%s", cmd_info[cmd].command);
      return TRUE;
    }
  
    return FALSE;
  }
  
  /* sets str to be the value of var.field */
  void find_replacement(void *go, struct script_data *sc, trig_data *trig,
                  int type, char *var, char *field, char *subfield, char *str, size_t slen)
  {
    struct trig_var_data *vd=NULL;
    char_data *ch, *c = NULL, *rndm;
    obj_data *obj, *o = NULL;
    struct room_data *room, *r = NULL;
    char *name;
    int num, count, i, j, doors;
  
    char *log_cmd[]        = {"mlog ",        "olog ",        "wlog "       };
    char *send_cmd[]       = {"msend ",       "osend ",       "wsend "      };
    char *echo_cmd[]       = {"mecho ",       "oecho ",       "wecho "      };
    char *echoaround_cmd[] = {"mechoaround ", "oechoaround ", "wechoaround "};
    char *door[]           = {"mdoor ",       "odoor ",       "wdoor "      };
    char *force[]          = {"mforce ",      "oforce ",      "wforce "     };
    char *load[]           = {"mload ",       "oload ",       "wload "      };
    char *purge[]          = {"mpurge ",      "opurge ",      "wpurge "     };
    char *teleport[]       = {"mteleport ",   "oteleport ",   "wteleport "  };
    /* the x kills a 'shadow' warning in gcc. */
    char *xdamage[]        = {"mdamage ",     "odamage ",     "wdamage "    };
    char *zoneecho[]       = {"mzoneecho ",   "ozoneecho ",   "wzoneecho "  };
    char *asound[]         = {"masound ",     "oasound ",     "wasound "    };
    char *at[]             = {"mat ",         "oat ",         "wat "        };
    /* there is no such thing as wtransform, thus the wecho below  */
    char *transform[]      = {"mtransform ",  "otransform ",  "wecho "      };
    char *recho[]          = {"mrecho ",      "orecho ",      "wrecho "     };
    /* there is no such thing as mmove, thus the mecho below  */
    char *omove[]          = {"mecho ",      "omove ",      "wmove "     };
  
    *str = '\0';
  
    /* X.global() will have a NULL trig */
    if (trig)
      for (vd = GET_TRIG_VARS(trig); vd; vd = vd->next)
        if (!str_cmp(vd->name, var))
          break;
  
    /* some evil waitstates could crash the mud if sent here with sc==NULL*/
    if (!vd && sc)
      for (vd = sc->global_vars; vd; vd = vd->next)
        if (!str_cmp(vd->name, var) &&
            (vd->context==0 || vd->context==sc->context))
          break;
  
    if (!*field) {
      if (vd)
        snprintf(str, slen, "%s", vd->value);
      else {
        if (!str_cmp(var, "self")) {
          switch (type) {
          case MOB_TRIGGER:
            snprintf(str, slen, "%c%ld", UID_CHAR, char_script_id((char_data *) go));
            break;
          case OBJ_TRIGGER:
            snprintf(str, slen, "%c%ld", UID_CHAR, obj_script_id((obj_data *) go));
            break;
          case WLD_TRIGGER:
            snprintf(str, slen, "%c%ld", UID_CHAR, room_script_id((room_data *)go));
            break;
          }
        }
        else if (!str_cmp(var, "global")) {
          /* so "remote varname %global%" will work */
          snprintf(str, slen, "%d", ROOM_ID_BASE);
          return;
        }
        else if (!str_cmp(var, "door"))
          snprintf(str, slen, "%s", door[type]);
        else if (!str_cmp(var, "force"))
          snprintf(str, slen, "%s", force[type]);
        else if (!str_cmp(var, "load"))
          snprintf(str, slen, "%s", load[type]);
        else if (!str_cmp(var, "purge"))
          snprintf(str, slen, "%s", purge[type]);
        else if (!str_cmp(var, "teleport"))
          snprintf(str, slen, "%s", teleport[type]);
        else if (!str_cmp(var, "damage"))
          snprintf(str, slen, "%s", xdamage[type]);
        else if (!str_cmp(var, "send"))
          snprintf(str, slen, "%s", send_cmd[type]);
        else if (!str_cmp(var, "echo"))
          snprintf(str, slen, "%s", echo_cmd[type]);
        else if (!str_cmp(var, "echoaround"))
          snprintf(str, slen, "%s", echoaround_cmd[type]);
        else if (!str_cmp(var, "zoneecho"))
          snprintf(str, slen, "%s", zoneecho[type]);
        else if (!str_cmp(var, "asound"))
          snprintf(str, slen, "%s", asound[type]);
        else if (!str_cmp(var, "at"))
          snprintf(str, slen, "%s", at[type]);
        else if (!str_cmp(var, "transform"))
          snprintf(str, slen, "%s", transform[type]);
        else if (!str_cmp(var, "recho"))
          snprintf(str, slen, "%s", recho[type]);
        else if (!str_cmp(var, "move"))
          snprintf(str, slen, "%s", omove[type]);
        else if (!str_cmp(var, "log"))
          snprintf(str, slen, "%s", log_cmd[type]);
        else
          *str = '\0';
      }
  
      return;
    }
  
    else if (vd && text_processed(field, subfield, vd, str, slen)) return;
  
    else {
      if (vd) {
        name = vd->value;
  
        switch (type) {
        case MOB_TRIGGER:
          ch = (char_data *) go;
  
          if ((o = get_object_in_equip(ch, name)));
          else if ((o = get_obj_in_list(name, ch->carrying)));
          else if (IN_ROOM(ch) != NOWHERE && (c = get_char_in_room(&world[IN_ROOM(ch)], name)));
          else if ((o = get_obj_in_list(name,world[IN_ROOM(ch)].contents)));
          else if ((c = get_char(name)));
          else if ((o = get_obj(name)));
          else if ((r = get_room(name))) {}
  
          break;
        case OBJ_TRIGGER:
          obj = (obj_data *) go;
  
          if ((c = get_char_by_obj(obj, name)));
          else if ((o = get_obj_by_obj(obj, name)));
          else if ((r = get_room(name))) {}
  
          break;
        case WLD_TRIGGER:
          room = (struct room_data *) go;
  
          if ((c = get_char_by_room(room, name)));
          else if ((o = get_obj_by_room(room, name)));
          else if ((r = get_room(name))) {}
  
          break;
        }
      }
  
      else {
        if (!str_cmp(var, "self")) {
          switch (type) {
          case MOB_TRIGGER:
            c = (char_data *) go;
            r = NULL;
            o = NULL;  /* NULL assignments added to avoid self to always be    */
            break;     /* the room.  - Welcor        */
          case OBJ_TRIGGER:
            o = (obj_data *) go;
            c = NULL;
            r = NULL;
            break;
          case WLD_TRIGGER:
            r = (struct room_data *) go;
            c = NULL;
            o = NULL;
            break;
          }
        }
  
        else if (!str_cmp(var, "global")) {
          struct script_data *thescript = SCRIPT(&world[0]);
          *str = '\0';
          if (!thescript) {
            script_log("Attempt to find global var. Apparently the void has no script.");
            return;
          }
          for (vd = thescript->global_vars; vd ; vd = vd->next)
            if (!str_cmp(vd->name, field))
              break;
  
          if (vd)
            snprintf(str, slen, "%s", vd->value);
  
          return;
        }
        else if (!str_cmp(var, "people")) {
          snprintf(str, slen, "%d",((num = atoi(field)) > 0) ? trgvar_in_room(num) : 0);
          return;
        }
        else if (!str_cmp(var, "happyhour")) {
          if (!str_cmp(field, "qp") && IS_HAPPYHOUR)
            snprintf(str, slen, "%d", HAPPY_QP);
          else if (!str_cmp(field, "exp") && IS_HAPPYHOUR)
            snprintf(str, slen, "%d", HAPPY_EXP);
          else if (!str_cmp(field, "gold") && IS_HAPPYHOUR)
            snprintf(str, slen, "%d", HAPPY_GOLD);
          else snprintf(str, slen, "%d", HAPPY_TIME);
          return;
        }
        else if (!str_cmp(var, "time")) {
          if (!str_cmp(field, "hour"))
            snprintf(str, slen, "%d", time_info.hours);
          else if (!str_cmp(field, "day"))
            snprintf(str, slen, "%d", time_info.day + 1);
          else if (!str_cmp(field, "month"))
            snprintf(str, slen, "%d", time_info.month + 1);
          else if (!str_cmp(field, "year"))
            snprintf(str, slen, "%d", time_info.year);
          else *str = '\0';
          return;
        }
  /* %findobj.<room vnum X>(<object vnum/id/name>)%
   *  - count number of objects in room X with this name/id/vnum
   * %findmob.<room vnum X>(<mob vnum Y>)%
   *  - count number of mobs in room X with vnum Y
   * For example you want to check how many PC's are in room with vnum 1204. PC's
   * have the vnum -1 so: %echo% players in room 1204: %findmob.1204(-1)%
   * Or say you had a bank, and you want a script to check the number of bags of
   * gold (vnum: 1234). In the vault (vnum: 453). Use: %findobj.453(1234)% and it
   * will return the number of bags of gold.
   * Addition inspired by Jamie Nelson */
        else if (!str_cmp(var, "findmob")) {
          if (!field || !*field || !subfield || !*subfield) {
            script_log("findmob.vnum(mvnum) - illegal syntax");
            strcpy(str, "0");
          } else {
            room_rnum rrnum = real_room(atoi(field));
            mob_vnum mvnum = atoi(subfield);
  
            if (rrnum == NOWHERE) {
              script_log("findmob.vnum(ovnum): No room with vnum %d", atoi(field));
              strcpy(str, "0");
            } else {
              for (i = 0, ch = world[rrnum].people; ch; ch = ch->next_in_room)
                if (GET_MOB_VNUM(ch) == mvnum)
                  i++;
  
              snprintf(str, slen, "%d", i);
            }
          }
        }
        /* Addition inspired by Jamie Nelson. */
        else if (!str_cmp(var, "findobj")) {
          if (!field || !*field || !subfield || !*subfield) {
            script_log("findobj.vnum(ovnum) - illegal syntax");
            strcpy(str, "0");
          } else {
            room_rnum rrnum = real_room(atoi(field));
  
            if (rrnum == NOWHERE) {
              script_log("findobj.vnum(ovnum): No room with vnum %d", atoi(field));
              strcpy(str, "0");
            } else {
              /* item_in_list looks within containers as well. */
              snprintf(str, slen, "%d", item_in_list(subfield, world[rrnum].contents));
            }
          }
        }
        else if (!str_cmp(var, "random")) {
          if (!str_cmp(field, "char")) {
            rndm = NULL;
            count = 0;
  
            if (type == MOB_TRIGGER) {
              ch = (char_data *) go;
              for (c = world[IN_ROOM(ch)].people; c; c = c->next_in_room)
                if ((c != ch) && valid_dg_target(c, DG_ALLOW_GODS) &&
                    CAN_SEE(ch, c)) {
                  if (!rand_number(0, count))
                    rndm = c;
                  count++;
                }
            }
  
            else if (type == OBJ_TRIGGER) {
              for (c = world[obj_room((obj_data *) go)].people; c;
                   c = c->next_in_room)
                if (valid_dg_target(c, DG_ALLOW_GODS)) {
                  if (!rand_number(0, count))
                    rndm = c;
                  count++;
                }
            }
  
            else if (type == WLD_TRIGGER) {
              for (c = ((struct room_data *) go)->people; c;
                   c = c->next_in_room)
                if (valid_dg_target(c, DG_ALLOW_GODS)) {
  
                  if (!rand_number(0, count))
                    rndm = c;
                  count++;
                }
            }
  
            if (rndm)
              snprintf(str, slen, "%c%ld", UID_CHAR, char_script_id(rndm));
            else
              *str = '\0';
          }
  
          else if (!str_cmp(field, "dir")) {
            room_rnum in_room = NOWHERE;
  
            switch (type) {
              case WLD_TRIGGER:
                in_room = real_room(((struct room_data *) go)->number);
                break;
              case OBJ_TRIGGER:
                in_room = obj_room((struct obj_data *) go);
                break;
              case MOB_TRIGGER:
                in_room = IN_ROOM((struct char_data *)go);
                break;
            }
            if (in_room == NOWHERE) {
              *str = '\0';
            } else {
              doors = 0;
              room = &world[in_room];
              for (i = 0; i < DIR_COUNT; i++)
                if (R_EXIT(room, i))
                  doors++;
  
              if (!doors) {
                *str = '\0';
              } else {
                for ( ; ; ) {
                  doors = rand_number(0, DIR_COUNT-1);
                  if (R_EXIT(room, doors))
                    break;
                }
                snprintf(str, slen, "%s", dirs[doors]);
              }
            }
          }
          else
            snprintf(str, slen, "%d", ((num = atoi(field)) > 0) ? rand_number(1, num) : 0);
  
          return;
        }
      }
  
      if (c) {
        if (!str_cmp(field, "global")) { /* get global of something else */
          if (IS_NPC(c) && c->script) {
            find_replacement(go, c->script, NULL, MOB_TRIGGER,
              subfield, NULL, NULL, str, slen);
          }
        }
        /* set str to some 'non-text' first */
        *str = '\x1';
  
        switch (LOWER(*field)) {
          case 'a':
            if (!str_cmp(field, "affect")) {
              if (subfield && *subfield) {
                int spell = find_skill_num(subfield);
                if (affected_by_spell(c, spell))
                  strcpy(str, "1");
                else
                  strcpy(str, "0");
              } else
                strcpy(str, "0");
            }
            else if (!str_cmp(field, "alias"))
              snprintf(str, slen, "%s", GET_PC_NAME(c));
  
            else if (!str_cmp(field, "align")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
               GET_ALIGNMENT(c) = MAX(-1000, MIN(addition, 1000));
              }
  	    snprintf(str, slen, "%d", GET_ALIGNMENT(c));
            }
            else if (!str_cmp(field, "armor"))
              snprintf(str, slen, "%d", compute_armor_class(c));
            break;
          case 'c':
            if (!str_cmp(field, "canbeseen")) {
              if ((type == MOB_TRIGGER) && !CAN_SEE(((char_data *)go), c))
                strcpy(str, "0");
              else
                strcpy(str, "1");
            }
            else if (!str_cmp(field, "cha")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                int max = (IS_NPC(c) || GET_LEVEL(c) >= LVL_GRGOD) ? 25 : 18;
                c->real_abils.cha += addition;
                if (c->real_abils.cha > max) c->real_abils.cha = max;
                if (c->real_abils.cha < 3) c->real_abils.cha = 3;
                affect_total(c);
              }
              snprintf(str, slen, "%d", GET_CHA(c));
            }
            else if (!str_cmp(field, "class")) {
              if (subfield && *subfield) {
                int cl = get_class_by_name(subfield);
                if (cl != -1) {
                  GET_CLASS(c) = cl;
                  snprintf(str, slen, "1");
                } else {
                  snprintf(str, slen, "0");
                }
              } else
                sprinttype(GET_CLASS(c), pc_class_types, str, slen);
            }
            else if (!str_cmp(field, "con")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                int max = (IS_NPC(c) || GET_LEVEL(c) >= LVL_GRGOD) ? 25 : 18;
                c->real_abils.con += addition;
                if (c->real_abils.con > max) c->real_abils.con = max;
                if (c->real_abils.con < 3) c->real_abils.con = 3;
                affect_total(c);
              }
              snprintf(str, slen, "%d", GET_CON(c));
            }
            break;
          case 'd':
            if (!str_cmp(field, "damroll")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_DAMROLL(c) = MAX(1, GET_DAMROLL(c) + addition);
              }
              snprintf(str, slen, "%d", GET_DAMROLL(c));
              } else if (!str_cmp(field, "dex")) {
                if (subfield && *subfield) {
                  int addition = atoi(subfield);
                  int max = (IS_NPC(c) || GET_LEVEL(c) >= LVL_GRGOD) ? 25 : 18;
                  c->real_abils.dex += addition;
                  if (c->real_abils.dex > max) c->real_abils.dex = max;
                  if (c->real_abils.dex < 3) c->real_abils.dex = 3;
                  affect_total(c);
                }
              snprintf(str, slen, "%d", GET_DEX(c));
            }
            else if (!str_cmp(field, "drunk")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_COND(c, DRUNK) = MAX(-1, MIN(addition, 24));
              }
              snprintf(str, slen, "%d", GET_COND(c, DRUNK));
            }
            break;
          case 'e':
            if (!str_cmp(field, "eq")) {
              int pos;
              if (!subfield || !*subfield)
                *str = '\0';
              else if (*subfield == '*') {
                for (i = 0, j = 0; i < NUM_WEARS; i++)
                  if (GET_EQ(c, i)) {
                    j++;
                    break;
                  }
                if (j > 0)
                  strcpy(str,"1");
                else
                  *str = '\0';
              } else if ((pos = find_eq_pos_script(subfield)) < 0 || !GET_EQ(c, pos))
                *str = '\0';
              else
                snprintf(str, slen, "%c%ld",UID_CHAR, obj_script_id(GET_EQ(c, pos)));
            }
            else if (!str_cmp(field, "exp")) {
              if (subfield && *subfield) {
                int addition = MIN(atoi(subfield), 1000);
  
                gain_exp(c, addition);
              }
              snprintf(str, slen, "%d", GET_EXP(c));
            }
            break;
          case 'f':
            if (!str_cmp(field, "fighting")) {
              if (FIGHTING(c))
                snprintf(str, slen, "%c%ld", UID_CHAR, char_script_id(FIGHTING(c)));
              else
                *str = '\0';
            }
            else if (!str_cmp(field, "follower")) {
              if (!c->followers || !c->followers->follower)
                *str = '\0';
              else
                snprintf(str, slen, "%c%ld", UID_CHAR, char_script_id(c->followers->follower));
            }
            break;
          case 'g':
            if (!str_cmp(field, "gold")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                increase_gold(c, addition);
              }
              snprintf(str, slen, "%d", GET_GOLD(c));
            }
            break;
          case 'h':
            if (!str_cmp(field, "has_item")) {
              if (!(subfield && *subfield))
                *str = '\0';
              else
                snprintf(str, slen, "%d", char_has_item(subfield, c));
            }
            else if (!str_cmp(field, "hasattached")) {
              if (!(subfield && *subfield) || !IS_NPC(c))
                *str = '\0';
              else {
                i = atoi(subfield);
                snprintf(str, slen, "%d", trig_is_attached(SCRIPT(c), i));
              }
            }
            else if (!str_cmp(field, "heshe"))
              snprintf(str, slen, "%s", HSSH(c));
            else if (!str_cmp(field, "himher"))
              snprintf(str, slen, "%s", HMHR(c));
            else if (!str_cmp(field, "hisher"))
              snprintf(str, slen, "%s", HSHR(c));
            else if (!str_cmp(field, "hitp")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_HIT(c) += addition;
                update_pos(c);
              }
              snprintf(str, slen, "%d", GET_HIT(c));
            }
            else if (!str_cmp(field, "hitroll")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_HITROLL(c) = MAX(1, GET_HITROLL(c) + addition);
              }
              snprintf(str, slen, "%d", GET_HITROLL(c));
            }
            else if (!str_cmp(field, "hunger")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_COND(c, HUNGER) = MAX(-1, MIN(addition, 24));
              }
              snprintf(str, slen, "%d", GET_COND(c, HUNGER));
            }
            break;
          case 'i':
            if (!str_cmp(field, "id"))
              snprintf(str, slen, "%ld", char_script_id(c));
            /* new check for pc/npc status */
            else if (!str_cmp(field, "is_pc")) {
              if (IS_NPC(c))
                strcpy(str, "0");
              else
                strcpy(str, "1");
            }
            else if (!str_cmp(field, "int")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                int max = (IS_NPC(c) || GET_LEVEL(c) >= LVL_GRGOD) ? 25 : 18;
                c->real_abils.intel += addition;
                if (c->real_abils.intel > max) c->real_abils.intel = max;
                if (c->real_abils.intel < 3) c->real_abils.intel = 3;
                affect_total(c);
              }
              snprintf(str, slen, "%d", GET_INT(c));
            }
            else if (!str_cmp(field, "inventory")) {
              if(subfield && *subfield) {
                for (obj = c->carrying;obj;obj=obj->next_content) {
                  if(GET_OBJ_VNUM(obj)==atoi(subfield)) {
                    snprintf(str, slen, "%c%ld", UID_CHAR, obj_script_id(obj)); /* arg given, found */
                    return;
                  }
                }
                if (!obj)
                  *str = '\0'; /* arg given, not found */
              } else { /* no arg given */
                if (c->carrying) {
                  snprintf(str, slen, "%c%ld", UID_CHAR, obj_script_id(c->carrying));
                } else {
                  *str = '\0';
                }
              }
            }
            else if (!str_cmp(field, "is_killer")) {
              if (subfield && *subfield) {
                if (!str_cmp("on", subfield))
                  SET_BIT_AR(PLR_FLAGS(c), PLR_KILLER);
                else if (!str_cmp("off", subfield))
                  REMOVE_BIT_AR(PLR_FLAGS(c), PLR_KILLER);
              }
              if (PLR_FLAGGED(c, PLR_KILLER))
                strcpy(str, "1");
              else
                strcpy(str, "0");
            }
            else if (!str_cmp(field, "is_thief")) {
              if (subfield && *subfield) {
                if (!str_cmp("on", subfield))
                  SET_BIT_AR(PLR_FLAGS(c), PLR_THIEF);
                else if (!str_cmp("off", subfield))
                  REMOVE_BIT_AR(PLR_FLAGS(c), PLR_THIEF);
              }
              if (PLR_FLAGGED(c, PLR_THIEF))
                strcpy(str, "1");
              else
                strcpy(str, "0");
            }
            break;
          case 'l':
            if (!str_cmp(field, "level")) {
              if (subfield && *subfield) {
                int lev = atoi(subfield);
                GET_LEVEL(c) = MIN(MAX(lev, 0), LVL_IMMORT-1);
              } else
                snprintf(str, slen, "%d", GET_LEVEL(c));
            }
            break;
          case 'm':
            if (!str_cmp(field, "mana")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_MANA(c) += addition;
              }
              snprintf(str, slen, "%d", GET_MANA(c));
            }
            else if (!str_cmp(field, "master")) {
              if (!c->master)
                *str = '\0';
              else
                snprintf(str, slen, "%c%ld", UID_CHAR, char_script_id(c->master));
            }
            else if (!str_cmp(field, "maxhitp")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_MAX_HIT(c) = MAX(GET_MAX_HIT(c) + addition, 1);
              }
              snprintf(str, slen, "%d", GET_MAX_HIT(c));
            }
            else if (!str_cmp(field, "maxmana")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_MAX_MANA(c) = MAX(GET_MAX_MANA(c) + addition, 1);
              }
              snprintf(str, slen, "%d", GET_MAX_MANA(c));
            }
            else if (!str_cmp(field, "maxmove")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_MAX_MOVE(c) = MAX(GET_MAX_MOVE(c) + addition, 1);
              }
              snprintf(str, slen, "%d", GET_MAX_MOVE(c));
            }
            else if (!str_cmp(field, "move")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_MOVE(c) += addition;
              }
              snprintf(str, slen, "%d", GET_MOVE(c));
            }
            break;
          case 'n':
            if (!str_cmp(field, "name"))
              snprintf(str, slen, "%s", GET_NAME(c));
  
            else if (!str_cmp(field, "next_in_room")) {
              if (c->next_in_room)
                snprintf(str, slen,"%c%ld",UID_CHAR, char_script_id(c->next_in_room));
              else
                *str = '\0';
            }
            else if (!str_cmp(field, "npcflag")) {
              if (subfield && *subfield) {
                 char buf[MAX_STRING_LENGTH];
                 sprintbitarray(MOB_FLAGS(c), action_bits, PM_ARRAY_MAX, buf);
                  if (str_str(buf, subfield))
                    snprintf(str, slen, "1");
                  else
                    snprintf(str, slen, "0");
                }
                else {
                  snprintf(str, slen, "0");
                }
              }
            break;
          case 'p':
            /* Thanks to Christian Ejlertsen for this idea
               And to Ken Ray for speeding the implementation up :)*/
            if (!str_cmp(field, "pos")) {
              if (subfield && *subfield) {
                for (i = POS_SLEEPING; i <= POS_STANDING; i++) {
                  /* allows : Sleeping, Resting, Sitting, Fighting, Standing */
                  if (!strn_cmp(subfield, position_types[i], strlen(subfield))) {
                    GET_POS(c) = i;
                    break;
                  }
                }
              }
              snprintf(str, slen, "%s", position_types[GET_POS(c)]);
            }
            else if (!str_cmp(field, "prac")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_PRACTICES(c) = MAX(0, GET_PRACTICES(c) + addition);
              }
              snprintf(str, slen, "%d", GET_PRACTICES(c));
            }
            else if (!str_cmp(field, "pref")) {
              if (subfield && *subfield) {
                int pref = get_flag_by_name(preference_bits, subfield);
                if (!IS_NPC(c) && pref != NOFLAG && PRF_FLAGGED(c, pref))
                  strcpy(str, "1");
                else
                  strcpy(str, "0");
              } else
                strcpy(str, "0");
            }
            break;
          case 'q':
            if (!IS_NPC(c) && (!str_cmp(field, "questpoints") ||
                !str_cmp(field, "qp") || !str_cmp(field, "qpnts")))
            {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_QUESTPOINTS(c) += addition;
              }
              snprintf(str, slen, "%d", GET_QUESTPOINTS(c));
            }
             else if (!str_cmp(field, "quest"))
             {
                 if (!IS_NPC(c) && (GET_QUEST(c) != NOTHING) && (real_quest(GET_QUEST(c)) != NOTHING))
                   snprintf(str, slen, "%d", GET_QUEST(c));
                 else
                   strcpy(str, "0");
               }
             else if (!str_cmp(field, "questdone"))
             {
                 if (!IS_NPC(c) && subfield && *subfield) {
                   int q_num = atoi(subfield);
                   if (is_complete(c, q_num))
                     strcpy(str, "1");
                   else
                     strcpy(str, "0");
                 }
                 else
                   strcpy(str, "0");
               }
            break;
          case 'r':
            if (!str_cmp(field, "room")) {  /* in NOWHERE, return the void */
  /* see note in dg_scripts.h */
  #ifdef ACTOR_ROOM_IS_UID
              snprintf(str, slen, "%c%ld",UID_CHAR,
                 (IN_ROOM(c)!= NOWHERE) ? room_script_id(world + IN_ROOM(c)) : ROOM_ID_BASE);
  #else
              snprintf(str, slen, "%d", (IN_ROOM(c)!= NOWHERE) ? world[IN_ROOM(c)].number : 0);
  #endif
            }
            break;
          case 's':
            if (!str_cmp(field, "saving_breath")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_SAVE(c, SAVING_BREATH) += addition;
              }
              snprintf(str, slen, "%d", GET_SAVE(c, SAVING_BREATH));
            }
            else if (!str_cmp(field, "saving_para")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_SAVE(c, SAVING_PARA) += addition;
              }
              snprintf(str, slen, "%d", GET_SAVE(c, SAVING_PARA));
            }
            else if (!str_cmp(field, "saving_petri")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_SAVE(c, SAVING_PETRI) += addition;
              }
              snprintf(str, slen, "%d", GET_SAVE(c, SAVING_PETRI));
            }
            else if (!str_cmp(field, "saving_rod")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_SAVE(c, SAVING_ROD) += addition;
              }
              snprintf(str, slen, "%d", GET_SAVE(c, SAVING_ROD));
            }
            else if (!str_cmp(field, "saving_spell")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_SAVE(c, SAVING_SPELL) += addition;
              }
              snprintf(str, slen, "%d", GET_SAVE(c, SAVING_SPELL));
            }
            else if (!str_cmp(field, "sex"))
              snprintf(str, slen, "%s", genders[(int)GET_SEX(c)]);
            else if (!str_cmp(field, "skill"))
              snprintf(str, slen, "%s", skill_percent(c, subfield));
            else if (!str_cmp(field, "skillset")) {
              if (!IS_NPC(c) && subfield && *subfield) {
                char skillname[MAX_INPUT_LENGTH], *amount;
                amount = one_word(subfield, skillname);
                skip_spaces(&amount);
                if (amount && *amount && is_number(amount)) {
                  int skillnum = find_skill_num(skillname);
                  if (skillnum > 0) {
                    int new_value = MAX(0, MIN(100, atoi(amount)));
                    SET_SKILL(c, skillnum, new_value);
                  }
                }
              }
              *str = '\0'; /* so the parser know we recognize 'skillset' as a field */
            }
            else if (!str_cmp(field, "str")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                int max = (IS_NPC(c) || GET_LEVEL(c) >= LVL_GRGOD) ? 25 : 18;
                c->real_abils.str += addition;
                if (c->real_abils.str > max) c->real_abils.str = max;
                if (c->real_abils.str < 3) c->real_abils.str = 3;
                affect_total(c);
              }
              snprintf(str, slen, "%d", GET_STR(c));
            }
            else if (!str_cmp(field, "stradd")) {
              if (GET_STR(c) >= 18) {
                if (subfield && *subfield) {
                  int addition = atoi(subfield);
                  c->real_abils.str_add += addition;
                  if (c->real_abils.str_add > 100) c->real_abils.str_add = 100;
                  if (c->real_abils.str_add < 0) c->real_abils.str_add = 0;
                  affect_total(c);
                }
                snprintf(str, slen, "%d", GET_ADD(c));
              }
            }
            break;
          case 't':
            if (!str_cmp(field, "thirst")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_COND(c, THIRST) = MAX(-1, MIN(addition, 24));
              }
              snprintf(str, slen, "%d", GET_COND(c, THIRST));
            }
            else if (!str_cmp(field, "title")) {
              if (!IS_NPC(c) && subfield && *subfield && valid_dg_target(c, DG_ALLOW_GODS)) {
                if (GET_TITLE(c)) free(GET_TITLE(c));
                  GET_TITLE(c) = strdup(subfield);
              }
              snprintf(str, slen, "%s", IS_NPC(c) ? "" : GET_TITLE(c));
            }
            break;
  	case 'v':
            if (!str_cmp(field, "varexists")) {
              struct trig_var_data *remote_vd;
              strcpy(str, "0");
              if (SCRIPT(c)) {
                for (remote_vd = SCRIPT(c)->global_vars; remote_vd; remote_vd = remote_vd->next) {
                  if (!str_cmp(remote_vd->name, subfield)) break;
                }
                if (remote_vd) strcpy(str, "1");
              }
            }
            else if (!str_cmp(field, "vnum")) {
              if (subfield && *subfield) {
               /* When this had -1 at the end of the line it returned true for PC's if you did
                * something like if %actor.vnum(500)%. It should return false for PC's instead 
                * -- Fizban 02/18
                */ 
                snprintf(str, slen, "%d", IS_NPC(c) ? (int)(GET_MOB_VNUM(c) == atoi(subfield)) : 0 );
              } else {
                if (IS_NPC(c))
                  snprintf(str, slen, "%d", GET_MOB_VNUM(c));
                else
                /*
                 * for compatibility with unsigned indexes
                 * - this is deprecated - use %actor.is_pc% to check
                 * instead of %actor.vnum% == -1  --Welcor 09/03
                 */
                  strcpy(str, "-1");
              }
            }
            break;
          case 'w':
            if (!str_cmp(field, "weight"))
              snprintf(str, slen, "%d", GET_WEIGHT(c));
            else if (!str_cmp(field, "wis")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                int max = (IS_NPC(c) || GET_LEVEL(c) >= LVL_GRGOD) ? 25 : 18;
                c->real_abils.wis += addition;
                if (c->real_abils.wis > max) c->real_abils.wis = max;
                if (c->real_abils.wis < 3) c->real_abils.wis = 3;
                affect_total(c);
              }
              snprintf(str, slen, "%d", GET_WIS(c));
            }
            
            else if (!str_cmp(field, "wait")) 
            {
              if (subfield && *subfield)
              {
                int addition = atoi(subfield);
                WAIT_STATE(c, addition * ( PULSE_VIOLENCE / 2) ); // by default violence is 2 seconds
              }
              snprintf(str, slen, "%d", GET_WAIT_STATE(c));
            }
  
            break;
        } /* switch *field */
  
        if (*str == '\x1') { /* no match found in switch */
          if (SCRIPT(c)) {
            for (vd = (SCRIPT(c))->global_vars; vd; vd = vd->next)
              if (!str_cmp(vd->name, field))
                break;
            if (vd)
              snprintf(str, slen, "%s", vd->value);
            else {
              *str = '\0';
              script_log("Trigger: %s, VNum %d. unknown char field: '%s'",
                         GET_TRIG_NAME(trig), GET_TRIG_VNUM(trig), field);
            }
          } else {
            *str = '\0';
            script_log("Trigger: %s, VNum %d. unknown char field: '%s'",
                       GET_TRIG_NAME(trig), GET_TRIG_VNUM(trig), field);
          }
        }
      } /* if (c) ...*/
  
      else if (o) {
  
        *str = '\x1';
        switch (LOWER(*field)) {
          case 'a':
            if (!str_cmp(field, "affects")) {
              if (subfield && *subfield) {
                if (check_flags_by_name_ar(GET_OBJ_AFFECT(o), NUM_AFF_FLAGS, subfield, affected_bits) == TRUE)
                  snprintf(str, slen, "1");
                else
                  snprintf(str, slen, "0");
              } else
                snprintf(str, slen, "0");
            }
  	case 'c':
            if (!str_cmp(field, "cost")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_OBJ_COST(o) = MAX(1, addition + GET_OBJ_COST(o));
              }
              snprintf(str, slen, "%d", GET_OBJ_COST(o));
            }
  
            else if (!str_cmp(field, "cost_per_day")) {
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_OBJ_RENT(o) = MAX(1, addition + GET_OBJ_RENT(o));
              }
              snprintf(str, slen, "%d", GET_OBJ_RENT(o));
            }
  
            else if (!str_cmp(field, "carried_by")) {
              if (o->carried_by)
                snprintf(str, slen,"%c%ld",UID_CHAR, char_script_id(o->carried_by));
              else
                *str = '\0';
            }
  
            else if (!str_cmp(field, "contents")) {
              if (o->contains)
                snprintf(str, slen, "%c%ld", UID_CHAR, obj_script_id(o->contains));
              else
                *str = '\0';
            }
            /* thanks to Jamie Nelson (Mordecai of 4 Dimensions MUD) */
            else if (!str_cmp(field, "count")) {
              if (GET_OBJ_TYPE(o) == ITEM_CONTAINER)
                snprintf(str, slen, "%d", item_in_list(subfield, o->contains));
              else
              	strcpy(str, "0");
            }
            break;
          case 'e':
            if (!str_cmp(field, "extra")) {
              if (subfield && *subfield) {
                if (check_flags_by_name_ar(GET_OBJ_EXTRA(o), NUM_ITEM_FLAGS, subfield, extra_bits) > 0)
                  snprintf(str, slen, "1");
                else
                  snprintf(str, slen, "0");
              } else {
                sprintbitarray(GET_OBJ_EXTRA(o), extra_bits, EF_ARRAY_MAX, str);
              }
            }
            break;
  	case 'h':
            /* thanks to Jamie Nelson (Mordecai of 4 Dimensions MUD) */
            if (!str_cmp(field, "has_in")) {
              if (GET_OBJ_TYPE(o) == ITEM_CONTAINER)
                snprintf(str, slen, "%s", (item_in_list(subfield, o->contains) ? "1" : "0"));
              else
                strcpy(str, "0");
            }
            else if (!str_cmp(field, "hasattached")) {
              if (!(subfield && *subfield))
                *str = '\0';
              else {
                i = atoi(subfield);
                snprintf(str, slen, "%d", trig_is_attached(SCRIPT(o), i));
              }
            }
            break;
          case 'i':
            if (!str_cmp(field, "id"))
              snprintf(str, slen, "%ld", obj_script_id(o));
  
            else if (!str_cmp(field, "is_inroom")) {
              if (IN_ROOM(o) != NOWHERE)
                snprintf(str, slen,"%c%ld",UID_CHAR, room_script_id(world + IN_ROOM(o)));
              else
                *str = '\0';
            }
            else if (!str_cmp(field, "is_pc")) {
              strcpy(str, "-1");
            }
  	  break;
          case 'n':
            if (!str_cmp(field, "name"))
              snprintf(str, slen, "%s",  o->name);
  
            else if (!str_cmp(field, "next_in_list")) {
              if (o->next_content)
                snprintf(str, slen,"%c%ld",UID_CHAR, obj_script_id(o->next_content));
              else
                *str = '\0';
            }
            break;
          case 'o':
            if (!str_cmp(field, "oset")) {
              if (subfield && *subfield) {
                if (handle_oset(o, subfield))
                  strcpy(str, "1");
                else
                  strcpy(str, "0");
              }
            }
            break;
          case 'r':
            if (!str_cmp(field, "room")) {
              if (obj_room(o) != NOWHERE)
                snprintf(str, slen,"%c%ld",UID_CHAR, room_script_id(world + obj_room(o)));
              else
                *str = '\0';
            }
            break;
          case 's':
            if (!str_cmp(field, "shortdesc"))
              snprintf(str, slen, "%s",  o->short_description);
            break;
          case 't':
            if (!str_cmp(field, "type"))
              sprinttype(GET_OBJ_TYPE(o), item_types, str, slen);
  
            else if (!str_cmp(field, "timer"))
              snprintf(str, slen, "%d", GET_OBJ_TIMER(o));
            break;
          case 'v':
            if (!str_cmp(field, "vnum"))
              if (subfield && *subfield) {
                snprintf(str, slen, "%d", (int)(GET_OBJ_VNUM(o) == atoi(subfield)));
              } else {
                snprintf(str, slen, "%d", GET_OBJ_VNUM(o));
              }
            else if (!str_cmp(field, "val0"))
              snprintf(str, slen, "%d", GET_OBJ_VAL(o, 0));
  
            else if (!str_cmp(field, "val1"))
              snprintf(str, slen, "%d", GET_OBJ_VAL(o, 1));
  
            else if (!str_cmp(field, "val2"))
              snprintf(str, slen, "%d", GET_OBJ_VAL(o, 2));
  
            else if (!str_cmp(field, "val3"))
              snprintf(str, slen, "%d", GET_OBJ_VAL(o, 3));
            break;
          case 'w':
            if (!str_cmp(field, "wearflag")) {
  	    if (subfield && *subfield) {
  	      if (can_wear_on_pos(o, find_eq_pos_script(subfield)))
  	        snprintf(str, slen, "1");
  	      else
  	         snprintf(str, slen, "0");
  	    } else
                snprintf(str, slen, "0");
  	  }
  
  	  else if (!str_cmp(field, "weight")){
              if (subfield && *subfield) {
                int addition = atoi(subfield);
                GET_OBJ_WEIGHT(o) = MAX(1, addition + GET_OBJ_WEIGHT(o));
              }
              snprintf(str, slen, "%d", GET_OBJ_WEIGHT(o));
            }
  
            else if (!str_cmp(field, "worn_by")) {
              if (o->worn_by)
                snprintf(str, slen,"%c%ld",UID_CHAR, char_script_id(o->worn_by));
              else
                *str = '\0';
            }
            break;
        } /* switch *field */
  
  
        if (*str == '\x1') { /* no match in switch */
          if (SCRIPT(o)) { /* check for global var */
            for (vd = (SCRIPT(o))->global_vars; vd; vd = vd->next)
              if (!str_cmp(vd->name, field))
                break;
            if (vd)
              snprintf(str, slen, "%s", vd->value);
            else {
              *str = '\0';
              script_log("Trigger: %s, VNum %d, type: %d. unknown object field: '%s'",
                         GET_TRIG_NAME(trig), GET_TRIG_VNUM(trig), type, field);
            }
          } else {
            *str = '\0';
            script_log("Trigger: %s, VNum %d, type: %d. unknown object field: '%s'",
                       GET_TRIG_NAME(trig), GET_TRIG_VNUM(trig), type, field);
          }
        }
      } /* if (o) ... */
  
      else if (r) {
  
        /* special handling of the void, as it stores all 'full global' variables */
        if (r->number == 0) {
          if (!SCRIPT(r)) {
            *str = '\0';
            script_log("Trigger: %s, Vnum %d, type %d. Trying to access Global var list of void. Apparently this has not been set up!",
                       GET_TRIG_NAME(trig), GET_TRIG_VNUM(trig), type);
          } else {
            for (vd = (SCRIPT(r))->global_vars; vd; vd = vd->next)
              if (!str_cmp(vd->name, field))
                break;
            if (vd)
              snprintf(str, slen, "%s", vd->value);
            else
              *str = '\0';
          }
        }
  
        else if (!str_cmp(field, "name"))
          snprintf(str, slen, "%s",  r->name);
  
        else if (!str_cmp(field, "sector"))
          sprinttype(r->sector_type, sector_types, str, slen);
  
        else if (!str_cmp(field, "vnum")) {
          if (subfield && *subfield) {
            snprintf(str, slen, "%d", (int)(r->number == atoi(subfield)));
          } else {
            snprintf(str, slen,"%d",r->number);
          }
        } else if (!str_cmp(field, "contents")) {
          if (subfield && *subfield) {
            for (obj = r->contents; obj; obj = obj->next_content) {
              if (GET_OBJ_VNUM(obj) == atoi(subfield)) {
                /* arg given, found */
                snprintf(str, slen, "%c%ld", UID_CHAR, obj_script_id(obj));
                return;
              }
            }
            if (!obj)
              *str = '\0'; /* arg given, not found */
          } else { /* no arg given */
            if (r->contents) {
              snprintf(str, slen, "%c%ld", UID_CHAR, obj_script_id(r->contents));
            } else {
              *str = '\0';
            }
          }
        }
  
        else if (!str_cmp(field, "people")) {
          if (r->people)
            snprintf(str, slen, "%c%ld", UID_CHAR, char_script_id(r->people));
          else
            *str = '\0';
        }
        else if (!str_cmp(field, "id")) {
          room_rnum rnum = real_room(r->number);
          if (rnum != NOWHERE)
            snprintf(str, slen, "%ld", room_script_id(world + rnum));
          else
            *str = '\0';
        }
        else if (!str_cmp(field, "weather")) {
          const char *sky_look[] = {
            "sunny",
            "cloudy",
            "rainy",
            "lightning"
          };
  
          if (!IS_SET_AR(r->room_flags, ROOM_INDOORS))
            snprintf(str, slen, "%s", sky_look[weather_info.sky]);
          else
            *str = '\0';
        }
        else if (!str_cmp(field, "hasattached")) {
          if (!(subfield && *subfield))
            *str = '\0';
          else {
            i = atoi(subfield);
            snprintf(str, slen, "%d", trig_is_attached(SCRIPT(r), i));
          }
        }
        else if (!str_cmp(field, "zonenumber"))
          snprintf(str, slen, "%d",  zone_table[r->zone].number);
        else if (!str_cmp(field, "zonename"))
          snprintf(str, slen, "%s",  zone_table[r->zone].name);
        else if (!str_cmp(field, "roomflag")) {
          if (subfield && *subfield) {
            room_rnum thisroom = real_room(r->number);
            if (check_flags_by_name_ar(ROOM_FLAGS(thisroom), NUM_ROOM_FLAGS, subfield, room_bits) == TRUE)
              snprintf(str, slen, "1");
            else
              snprintf(str, slen, "0");
          } else
            snprintf(str, slen, "0");
        }
        else if (!str_cmp(field, "north")) {
          if (R_EXIT(r, NORTH)) {
            if (subfield && *subfield) {
              if (!str_cmp(subfield, "vnum"))
                snprintf(str, slen, "%d", GET_ROOM_VNUM(R_EXIT(r, NORTH)->to_room));
              else if (!str_cmp(subfield, "key"))
                snprintf(str, slen, "%d", R_EXIT(r, NORTH)->key);
              else if (!str_cmp(subfield, "bits"))
                sprintbit(R_EXIT(r, NORTH)->exit_info ,exit_bits, str, slen);
              else if (!str_cmp(subfield, "room")) {
                if (R_EXIT(r, NORTH)->to_room != NOWHERE)
                  snprintf(str, slen, "%c%ld", UID_CHAR, room_script_id(world + R_EXIT(r, NORTH)->to_room));
                else
                  *str = '\0';
              }
            } else /* no subfield - default to bits */
              sprintbit(R_EXIT(r, NORTH)->exit_info ,exit_bits, str, slen);
          } else
            *str = '\0';
        }
        else if (!str_cmp(field, "east")) {
          if (R_EXIT(r, EAST)) {
            if (subfield && *subfield) {
              if (!str_cmp(subfield, "vnum"))
                snprintf(str, slen, "%d", GET_ROOM_VNUM(R_EXIT(r, EAST)->to_room));
              else if (!str_cmp(subfield, "key"))
                snprintf(str, slen, "%d", R_EXIT(r, EAST)->key);
              else if (!str_cmp(subfield, "bits"))
                sprintbit(R_EXIT(r, EAST)->exit_info ,exit_bits, str, slen);
              else if (!str_cmp(subfield, "room")) {
                if (R_EXIT(r, EAST)->to_room != NOWHERE)
                  snprintf(str, slen, "%c%ld", UID_CHAR, room_script_id(world + R_EXIT(r, EAST)->to_room));
                else
                  *str = '\0';
              }
            } else /* no subfield - default to bits */
              sprintbit(R_EXIT(r, EAST)->exit_info ,exit_bits, str, slen);
          } else
            *str = '\0';
        }
        else if (!str_cmp(field, "south")) {
          if (R_EXIT(r, SOUTH)) {
            if (subfield && *subfield) {
              if (!str_cmp(subfield, "vnum"))
                snprintf(str, slen, "%d", GET_ROOM_VNUM(R_EXIT(r, SOUTH)->to_room));
              else if (!str_cmp(subfield, "key"))
                snprintf(str, slen, "%d", R_EXIT(r, SOUTH)->key);
              else if (!str_cmp(subfield, "bits"))
                sprintbit(R_EXIT(r, SOUTH)->exit_info ,exit_bits, str, slen);
              else if (!str_cmp(subfield, "room")) {
                if (R_EXIT(r, SOUTH)->to_room != NOWHERE)
                  snprintf(str, slen, "%c%ld", UID_CHAR, room_script_id(world + R_EXIT(r, SOUTH)->to_room));
                else
                  *str = '\0';
              }
            } else /* no subfield - default to bits */
              sprintbit(R_EXIT(r, SOUTH)->exit_info ,exit_bits, str, slen);
          } else
            *str = '\0';
        }
        else if (!str_cmp(field, "west")) {
          if (R_EXIT(r, WEST)) {
            if (subfield && *subfield) {
              if (!str_cmp(subfield, "vnum"))
                snprintf(str, slen, "%d", GET_ROOM_VNUM(R_EXIT(r, WEST)->to_room));
              else if (!str_cmp(subfield, "key"))
                snprintf(str, slen, "%d", R_EXIT(r, WEST)->key);
              else if (!str_cmp(subfield, "bits"))
                sprintbit(R_EXIT(r, WEST)->exit_info ,exit_bits, str, slen);
              else if (!str_cmp(subfield, "room")) {
                if (R_EXIT(r, WEST)->to_room != NOWHERE)
                  snprintf(str, slen, "%c%ld", UID_CHAR, room_script_id(world + R_EXIT(r, WEST)->to_room));
                else
                  *str = '\0';
              }
            } else /* no subfield - default to bits */
              sprintbit(R_EXIT(r, WEST)->exit_info ,exit_bits, str, slen);
          } else
            *str = '\0';
        }
        else if (!str_cmp(field, "up")) {
          if (R_EXIT(r, UP)) {
            if (subfield && *subfield) {
              if (!str_cmp(subfield, "vnum"))
                snprintf(str, slen, "%d", GET_ROOM_VNUM(R_EXIT(r, UP)->to_room));
              else if (!str_cmp(subfield, "key"))
                snprintf(str, slen, "%d", R_EXIT(r, UP)->key);
              else if (!str_cmp(subfield, "bits"))
                sprintbit(R_EXIT(r, UP)->exit_info ,exit_bits, str, slen);
              else if (!str_cmp(subfield, "room")) {
                if (R_EXIT(r, UP)->to_room != NOWHERE)
                  snprintf(str, slen, "%c%ld", UID_CHAR, room_script_id(world + R_EXIT(r, UP)->to_room));
                else
                  *str = '\0';
              }
            } else /* no subfield - default to bits */
              sprintbit(R_EXIT(r, UP)->exit_info ,exit_bits, str, slen);
          } else
            *str = '\0';
        }
        else if (!str_cmp(field, "down")) {
          if (R_EXIT(r, DOWN)) {
            if (subfield && *subfield) {
              if (!str_cmp(subfield, "vnum"))
                snprintf(str, slen, "%d", GET_ROOM_VNUM(R_EXIT(r, DOWN)->to_room));
              else if (!str_cmp(subfield, "key"))
                snprintf(str, slen, "%d", R_EXIT(r, DOWN)->key);
              else if (!str_cmp(subfield, "bits"))
                sprintbit(R_EXIT(r, DOWN)->exit_info ,exit_bits, str, slen);
              else if (!str_cmp(subfield, "room")) {
                if (R_EXIT(r, DOWN)->to_room != NOWHERE)
                  snprintf(str, slen, "%c%ld", UID_CHAR, room_script_id(world + R_EXIT(r, DOWN)->to_room));
                else
                  *str = '\0';
              }
            } else /* no subfield - default to bits */
              sprintbit(R_EXIT(r, DOWN)->exit_info ,exit_bits, str, slen);
          } else
            *str = '\0';
        }
        else {
          if (SCRIPT(r)) { /* check for global var */
            for (vd = (SCRIPT(r))->global_vars; vd; vd = vd->next)
              if (!str_cmp(vd->name, field))
                break;
            if (vd)
              snprintf(str, slen, "%s", vd->value);
            else {
              *str = '\0';
              script_log("Trigger: %s, VNum %d, type: %d. unknown room field: '%s'",
                           GET_TRIG_NAME(trig), GET_TRIG_VNUM(trig), type, field);
            }
          } else {
            *str = '\0';
            script_log("Trigger: %s, VNum %d, type: %d. unknown room field: '%s'",
                       GET_TRIG_NAME(trig), GET_TRIG_VNUM(trig), type, field);
          }
        }
      } /* if (r).. */
    }
  }
  
  /* Now automatically checks if the variable has more then one field in it. And
   * if the field returns a name or a script UID or the like it can recurse. If
   * you supply a value like, %actor.int.str% it wont blow up on you either. Now
   * also lets subfields have variables parsed inside of them so that: %echo%
   * %actor.gold(%actor.gold%)% will double the actors gold every time its called.
   * - Jamie Nelson */
  
  /* substitutes any variables into line and returns it as buf */
  void var_subst(void *go, struct script_data *sc, trig_data *trig,
                 int type, char *line, char *buf)
  {
    char tmp[MAX_INPUT_LENGTH], repl_str[MAX_INPUT_LENGTH - 20]; // - 20 to make room for "eval tmpvr "
    char *var = NULL, *field = NULL, *p = NULL;
    char tmp2[MAX_INPUT_LENGTH];
    char *subfield_p, subfield[MAX_INPUT_LENGTH];
    int left, len;
    int paren_count = 0;
    int dots = 0;
  
    /* skip out if no %'s */
    if (!strchr(line, '%')) {
      strcpy(buf, line);
      return;
    }
    /*lets just empty these to start with*/
    *repl_str = *tmp = *tmp2 = '\0';
  
    p = strcpy(tmp, line);
    subfield_p = subfield;
  
    left = MAX_INPUT_LENGTH - 1;
  
    while (*p && (left > 0)) {
  
  
      /* copy until we find the first % */
      while (*p && (*p != '%') && (left > 0)) {
        *(buf++) = *(p++);
        left--;
      }
  
      *buf = '\0';
  
      /* double % */
      if (*p && (*(++p) == '%') && (left > 0)) {
        *(buf++) = *(p++);
        *buf = '\0';
        left--;
        continue;
      }
  
      /* so it wasn't double %'s */
      else if (*p && (left > 0)) {
  
        /* search until end of var or beginning of field */
        for (var = p; *p && (*p != '%') && (*p != '.'); p++);
  
        field = p;
        if (*p == '.') {
          *(p++) = '\0';
          dots = 0;
          for (field = p; *p && ((*p != '%')||(paren_count > 0) || (dots)); p++) {
            if (dots > 0) {
              *subfield_p = '\0';
              find_replacement(go, sc, trig, type, var, field, subfield, repl_str, sizeof(repl_str));
              if (*repl_str) {
                snprintf(tmp2, sizeof(tmp2), "eval tmpvr %s", repl_str); //temp var
                process_eval(go, sc, trig, type, tmp2);
                strcpy(var, "tmpvr");
                field = p;
                dots = 0;
                continue;
              }
              dots = 0;
            } else if (*p=='(') {
              *p = '\0';
              paren_count++;
            } else if (*p==')') {
              *p = '\0';
              paren_count--;
            } else if (paren_count > 0) {
              *subfield_p++ = *p;
            } else if (*p=='.') {
              *p = '\0';
              dots++;
            }
          } /* for (field.. */
        } /* if *p == '.' */
  
        *(p++) = '\0';
        *subfield_p = '\0';
  
        if (*subfield) {
          var_subst(go, sc, trig, type, subfield, tmp2);
          strcpy(subfield, tmp2);
        }
  
        find_replacement(go, sc, trig, type, var, field, subfield, repl_str, sizeof(repl_str));
  
        strncat(buf, repl_str, left);
        len = strlen(repl_str);
        buf += len;
        left -= len;
      } /* else if *p .. */
    } /* while *p .. */
    buf[sizeof(buf) - 1] = '\0';
  }
  