LPMUD/
LPMUD/BIN/
LPMUD/DOC/
LPMUD/MUDLIB/
LPMUD/MUDLIB/BANISH/
LPMUD/MUDLIB/D/
LPMUD/MUDLIB/DOC/
LPMUD/MUDLIB/DOC/DOMAINS/
LPMUD/MUDLIB/DOC/EFUN/
LPMUD/MUDLIB/DOC/EXAMPLES/
LPMUD/MUDLIB/DOC/EXAMPLES/ARMOUR/
LPMUD/MUDLIB/DOC/EXAMPLES/CONTAIN/
LPMUD/MUDLIB/DOC/EXAMPLES/FOOD/
LPMUD/MUDLIB/DOC/EXAMPLES/MAGIC/
LPMUD/MUDLIB/DOC/EXAMPLES/MONSTER/
LPMUD/MUDLIB/DOC/EXAMPLES/ROOM/
LPMUD/MUDLIB/DOC/EXAMPLES/WEAPONS/
LPMUD/MUDLIB/FUNCTION/
LPMUD/MUDLIB/INCLUDE/
LPMUD/MUDLIB/INCLUDE/FN_SPECS/
LPMUD/MUDLIB/INCLUDE/SKILLS/
LPMUD/MUDLIB/INFO/
LPMUD/MUDLIB/INHERIT/BASE/
LPMUD/MUDLIB/LOG/
LPMUD/MUDLIB/MANUALS/312/
LPMUD/MUDLIB/NEWS/
LPMUD/MUDLIB/OBJ/PARTY/
LPMUD/MUDLIB/OBJ/SHADOWS/
LPMUD/MUDLIB/OBJECTS/COMPONEN/
LPMUD/MUDLIB/OPEN/
LPMUD/MUDLIB/OPEN/LIBRARY/
LPMUD/MUDLIB/OPEN/PARTY/
LPMUD/MUDLIB/PLAYERS/
LPMUD/MUDLIB/PLAYERS/ZIL/
LPMUD/MUDLIB/ROOM/
LPMUD/MUDLIB/ROOM/CITY/ARENA/
LPMUD/MUDLIB/ROOM/CITY/CREATOR/
LPMUD/MUDLIB/ROOM/CITY/GARDEN/MONST/
LPMUD/MUDLIB/ROOM/CITY/OBJ/
LPMUD/MUDLIB/ROOM/CITY/PUB/
LPMUD/MUDLIB/ROOM/CITY/SHOP/
LPMUD/MUDLIB/ROOM/DEATH/
LPMUD/MUDLIB/ROOM/REGISTRY/
LPMUD/MUDLIB/SECURE/
LPMUD/MUDLIB/SECURE/UDP_CMD_/
LPMUD/MUDLIB/SKILLS/
LPMUD/MUDLIB/SKILLS/FIGHTER/
LPMUD/MUDLIB/SKILLS/THIEF/
LPMUD/MUDLIB/USR/
LPMUD/MUDLIB/USR/CREATORS/
LPMUD/MUDLIB/USR/PLAYERS/
/* This is revision 2 of room2 modules - since we use all its
   features it saves object overhead */

#include <mudlib.h>
inherit BASE;

#include <room.spc>
#include <vowel.h>
#include <exit_con.h>
#include <weather.h>
#include <i_to_str.h>
#include <door_mac.h>
#include <newline.h>

#define BLIND       present("Blindness",this_player())
#define NAME        (string)this_player()->query_name()
#define CAP         (string)this_player()->query_cap_name()
#define REAL        (string)this_player()->query_name(1)
#define QUERY_INT   (int)this_player()->query_intelligence()
#define QUERY_LOCKS (int)this_player()->query_locks()
#define QUERY_TRAPS (int)this_player()->query_traps()


string *dest_dir;             /* list of exit room|directions */
string *extra_objects;        /* list of objects that can be taken */
string *items;                /* list of items that can be looked at */
string *night_items;          /* list of items in night_desc */
string *day_items;            /* list of items in day_desc */
string *search_array;
int search_complex;


string day_desc;              /* extra description of long in daylight */
string night_desc;            /* extra description of long at night */

string enter_msg;             /* string written after entering a room */
string exit_msg;              /* string written before leaving room */

int climb;                    /* climbing difficulty for up|down */
int fall_dam;                 /* damage taken if one falls whilst climbing */
int no_track;                /* This will prevent you from looking into the   
                                     room magically */
int no_summon, no_fight;              /* This will prevent fighting and
summoning from a particular room; */

string custom_exits;          /* "" == no exit list in long */

mixed *doors;                 /* list of doors */ 
mixed *detect_attempts;
mixed *disarm_attempts;
mixed *lock_attempts;


/***********************************************************************/

void reset(status arg) {
  reset_doors(0);      /* reset_doors(1) will erase secret door finders */
  if(arg) return;
}

string query_object_type() { return "Room"; }

void init() {  
  int i, size;  
  string act, tmp;

  ::init();  
  fix_day();         /* night-day cycler */
  
  /* i'd rather this in the reset */

  if(!smell_msg)     set_smell("You smell nothing special.\n");  
  if(!listen_msg)    set_listen("You hear nothing special.\n");  
  if(!dest_dir)      dest_dir = ({});
  if(!doors)         doors = ({});
  if(!extra_objects) extra_objects = ({});
  if(!items)         items = ({});
  if(!night_items)   night_items = ({});
  if(!day_items)     day_items = ({});
  if(!search_array)  search_array = ({});

  /* normal exits */

  for(i = 1, size=sizeof(dest_dir); i < size; i += 2) {  
    act = dest_dir[i];
    sscanf(act,"%s %s",act,tmp);
    add_action("ready_to_move", act);  
    add_action("ready_to_move", convert_to_brief(act));
  }

  /* doors */

  if((size = sizeof(doors))) {
    for(i = 0; i < size; i++) {  
      if(!(act = DOOR_ID)) continue;
      sscanf(act,"%s %s",act,tmp);
      add_action("door_to_move", act); 
      add_action("door_to_move",convert_to_brief(act));
    }
  }  
  if(sizeof(extra_objects)) {
    add_action("get_extra_object", "get");
    add_action("get_extra_object", "take");
  }
  add_action("close","close"); 
  add_action("open","open"); 
  add_action("lock","lock");
  add_action("unlock","unlock");
  add_action("disarm_trap","disarm"); 
  add_action("arm_trap","arm"); 
  add_action("detect","detect");
  add_action("pick_lock", "pick"); 
  add_action("search_room","search");
}  


void long(status wiz) {  
  int i, day;  
  object obj;  

  if(wiz) {
    write("File: "+file_name(this_object()) +"\n\n");
  }
  ::long(wiz);
  if(query_day()) {  
    if(day_desc) write(process_msg(day_desc));  
  }  
  else {  
    if(night_desc) write(process_msg(night_desc));  
  }  
}  


/* ids of items in room */

status id(string str) {
  int i, size;

  if(get_id_index(str,items) != -1) {
    return 1;
  }
  else if(get_id_index(str,extra_objects) != -1) {
    return 1;
  }
  else if(query_day() && get_id_index(str,day_items) != -1) {
    return 1;
  }
  else if(!query_day() && get_id_index(str,night_items) != -1) {
    return 1;
  }
  else {
    if(!doors) doors = ({});
    for(i = 0, size = sizeof(doors); i < size; i++) {
      if(DOOR_ID == str) {
        if(SECRET_DOOR && member_array(REAL,SECRET_DOOR_FINDERS) == -1) {
          return 0;  /* haven't found the secret door */
        }
        return 1;
      }
    }
  }
  return 0;  /* nothing in 'room' matching id matching 'str' */
}


/*********************************************************************/
/* sets */
  
string set_day_desc(string str)       { return day_desc = str; }  
string set_night_desc(string str)     { return night_desc = str; }  
string set_enter_msg(string s)        { return enter_msg = s; }
string set_exit_msg(string s)         { return exit_msg = s; }

string *set_exits(string *arr) {
  dest_dir = (arr) ? arr : ({});
  update_actions();
  return dest_dir;
}
string *set_extra_objects(string *arr) {
  extra_objects = arr;
  update_actions();
  return extra_objects;
}

string *set_items(string *arr)        { return items = arr; }
string *set_day_items(string *arr)    { return day_items = arr; }
string *set_night_items(string *arr)  { return night_items = arr; }

string *set_search_array(string *arr) { return search_array = arr; }
int set_search_complex(int i)         { return search_complex = i; }
 

mixed *set_doors(mixed *arr) {
  doors = (arr) ? arr : ({});
  update_actions();
  return doors;
}


int set_climb(int i)    { return climb = i; }  
int set_fall_dam(int i) { return fall_dam = i; }  

mixed *set_detect_attempts(mixed *arg) { return detect_attempts = arg; }
mixed *set_disarm_attempts(mixed *arg) { return disarm_attempts = arg; }
mixed *set_lock_attempts(mixed *arg)   { return lock_attempts = arg; }

int set_no_fight(int i)          { return no_fight = i; }
int set_no_track(int i)          { return no_track = i; }
int set_no_summon(int i)         { return no_summon = i; }

/**********************************************************************/
/* queries */


string query_day_desc()       { return day_desc;   }  
string query_night_desc()     { return night_desc; }  
string query_enter_msg()      { return enter_msg; }
string query_exit_msg()       { return exit_msg; }

string *query_dest_dir()      { return dest_dir; }  
string *query_extra_objects() { return extra_objects; }

string *query_items()         { return items; }
string *query_day_items()     { return day_items; }
string *query_night_items()   { return night_items; }

string *query_search_array(string *arr) { return search_array;   }
int query_search_complex(int i)         { return search_complex; }

mixed  *query_doors()         { return doors; }

mixed *query_detect_attempts() { return detect_attempts; }
mixed *query_disarm_attempts() { return disarm_attempts; }
mixed *query_lock_attempts()   { return lock_attempts; }

string query_exit_filename(string str) { 
  int i;
 
  if(!(sizeof(dest_dir) || str)) return 0;  
  if((i = member_array(str,dest_dir)) > -1) return dest_dir[i-1];  
  return 0;  
}  
  
string query_file_exitname(string file) {  
  int i;  

  if(!file) return 0;
  sscanf(file,"/%",file);
  if((i = member_array(file, dest_dir)) > -1) return dest_dir[i+1];  
  return 0;
}  
  

varargs string *query_door_exits(int arg) { /* get door exits only */
  int i;
  string *door_exits;

  door_exits = ({});
  for(i = 0; i < sizeof(doors); i++) {
    if(!arg || (arg == 1 && !CLOSED_FLAG) || (arg == 2 && CLOSED_FLAG) ||
       (arg == 3 && SECRET_DOOR)) { 
      door_exits += ({ doors[i][0], doors[i][1], });    
    }
  }
  return door_exits;
}

string *query_all_exits()    { return dest_dir + query_door_exits(); }

string *query_open_exits()   { return dest_dir + query_door_exits(1); } 

string *query_closed_exits() { return query_door_exits(2); }

string *query_exits()        { return dest_dir; }

int query_no_fight()         { return no_fight; }

int query_no_track()           {return no_track; }
int query_no_summon()         { return no_summon; }


/************************************************************************/
/* exits */

/*
 * write the exits to the room
 */

static status test_next_door(int i, int door_size, string finder) {
 i += 1;
 if(i >= door_size) return 0;
 if(SECRET_DOOR && member_array(finder,SECRET_DOOR_FINDERS) == -1) {
   return test_next_door(i,door_size,finder);
 }
 return 1;
}


void exits(status brief) {  
  string str, name, pad;
  int i, size;
  int count;  


  if(custom_exits) {
    write(custom_exits);
    return;
  }

  brief = (status)this_player()->query_brief();

  /* door exits */

  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(SECRET_DOOR && member_array(REAL,SECRET_DOOR_FINDERS) == -1) continue;
    if(!count) str = "There is ";
    if(str == "") write("\n");
    str += (!count) ? "" : ((test_next_door(i,size,REAL)) ? ", " : " and ");
    str += (vowel(DOOR_ID)) ? "an " : "a ";
    str += DOOR_ID + ((CLOSED_FLAG) ? "(closed)" : "(open)");
    if(strlen(str) > 70) {
      write(str);
      str = "";
    } 
    count += 1;
  }
  if(count) write(str +".\n");
  
  size = sizeof(dest_dir);
  if(!(count || size)) {
    write("No Obvious Exits.\n");
    return;
  }
  if(!size) return;
  
  /* exits */

  str = "There "+((size == 2) ? "is " : "are ")+int_to_str(size/2)+" obvious";
  str += " exit"+ ((size == 2) ? ": " : "s: ");
  pad = "                                                               ";
  pad = extract(pad,0,strlen(str)-1);   

  for(i = 1; i < size; i += 2) {
    if(str == "") {
      write("\n");
      str = pad;
    }
    str += " "+ ((brief) ? convert_to_brief(dest_dir[i]) : dest_dir[i]);
    str += (i == size - 1) ? "."  : ((i == size - 2) ? " and" : ",");
    if(strlen(str) > 70) {
      write(str);
      str = "";
    } 
  }
  write(str +"\n");
} 

  
/**************************************************************************/
/* get ready to move */


status ready_to_move(string str) {   
  string direction, func;   
  int climb_skill, i;   
   
  direction = query_verb();   
  direction = convert_exit_name(direction);   
  if(str) direction += " " + str;

  if((direction == "up" || direction == "down") && climb > 0){   
    climb_skill = (int)this_player()->query_climb();   
    if(climb_skill < 1) climb_skill = 1;   
   
    /*** there is always a 1 in "climb" chance of climbing ***/   
   
    if(random(climb) > random(climb_skill)) {   
      write("You fall down.....\naahhhhhhhhhhhhhh!!\nThud!\n");   
      say(this_player()->query_name() + " falls down.\n");   
   
      /*** fall damage should be no greater then 6 ***/   
   
      if(fall_dam) this_player()->hit_player(random(fall_dam));   
      if(direction == "up") return 1;   
    }   
  }   

  if((i = member_array(direction,dest_dir)) == -1) return 0;
  if(sscanf(dest_dir[i-1],"@%s",func) == 1) { /* requested by tamsyn */
    return (status)call_other(this_object(),func,direction);
  }
  str = direction +"#"+ dest_dir[i-1];
 
  /*** its always slower to move in stealth ***/   
     
  if(this_player()->query_stealth_on()){   
    write("You sneak slowly "+ direction +"...\n");   
    call_out("room_move", 1, str);   
  }   
  else {   
    room_move(str);   
  }   
  return 1;   
} 


/****************************************************************************/
/* move player */
  
void room_move(string str) {
  string tmp_msg;

  if(!this_player()->move_player(str)) return; /* failed to move */    
  if(exit_msg) write(process_msg(exit_msg));   
  if((tmp_msg = (string)environment(this_player())->query_enter_msg())) {
    write(process_msg(tmp_msg));
  }
}  

    
/***************************************************************************/
/* look direction */

status look_dir(string dir) {
  int i;
  status wiz;

  if(!dir) return 0;
  dir = convert_exit_name(dir);
  if((i = member_array(dir, dest_dir)) == -1) return 0; /* failure */
  wiz = (this_player()->query_security_level()) ? 1 : 0;
  call_other(dest_dir[i-1],"long",wiz);
  return 1;
}

  
/************************************************************************/
/* dynamic exits */   


status add_exit(string where, string dest) {  
  int i;  

  if(!dest_dir) dest_dir = ({});
  if((i = member_array(dest, dest_dir)) > -1) return 0; /* failure */
  dest_dir += ({ where, dest });  
  update_actions();
  return 1;  
}  
  

status remove_exit(string dest) {  
  int i, size;  

  if((i=member_array(dest,dest_dir)) < 0) return 0; /* failure */
  if((size = sizeof(dest_dir)) == 2)
    dest_dir = ({});
  else
    dest_dir = dest_dir[0..i-2] + dest_dir[i+1..size];  
  update_actions();
  return 1;
}
  

status change_exit_room(string old_dest,string new_dest) {  
    int i;  

    if((i=member_array(old_dest,dest_dir)) == -1) return 0;
    dest_dir[i] = new_dest;  
    update_actions();
    return 1;  
}  


/**********************************************************************/
/* dynamic items */


varargs status add_item(string id_item, string item_str, mixed type) {
  int i;
  string str;
  str = type +"";
  switch(str) {
    case "1": case "day":
      if(!day_items) day_items = ({});
      if((i = member_array(id_item, day_items)) != -1) return 0;
      day_items += ({ id_item, item_str, });
    break;

    case "2": case "night":
      if(!night_items) night_items = ({});
      if((i = member_array(id_item, night_items)) != -1) return 0;
      night_items += ({ id_item, item_str, });
    break;

    default:
      if(!items) items = ({});
      if((i = member_array(id_item, items)) != -1) return 0;
      items += ({ id_item, item_str, });
    break;
  }
  return 1;
}


varargs status remove_item(string id_item, mixed type) {
  int i, size;  
  string str;
  
  str = type +"";
  switch(str) {
    case "1": case "day":
      if(!day_items) day_items = ({});
      if((i=get_id_index(id_item, day_items)) == -1) return 0;
      if((size = sizeof(day_items)) == 2)
        day_items = ({});
      else
      day_items = day_items[0..i-1]+ day_items[i+2..size];  
    break;

    case "2": case "night":
      if(!night_items) night_items = ({});
      if((i=get_id_index(id_item, night_items)) == -1) return 0;
      if((size = sizeof(night_items)) == 2)
        night_items = ({});
      else
        night_items = night_items[0..i-1]+ night_items[i+2..size];  
    break;

    default:
      if(!items) items = ({});
      if((i=get_id_index(id_item, items)) == -1) return 0;
      if((size = sizeof(items)) == 2)
        items = ({});
      else
        items = items[0..i-1]+ items[i+2..size];  
    break;
  }
  return 1;
}

  
varargs status change_item(string id_item, string item_str, mixed type) {
  int i;
  string str;

  str = type +"";
  switch(str) {
    case "1": case "day":
      if(!day_items) day_items = ({});
      if((i = get_id_index(id_item, day_items)) == -1) return 0;
      day_items[i+1] = item_str;
    break;

    case "2": case "night":
      if(!night_items) night_items = ({});
      if((i = get_id_index(id_item, night_items)) == -1) return 0;
      night_items[i+1] = item_str;
    break;

    default:
      if(!items) items = ({});
      if((i = get_id_index(id_item, items)) == -1) return 0;
      items[i+1] = item_str;
    break;
  }
  return 1;
}


/**********************************************************************/
/* extra objects */


status add_extra_object(string id_extra_object, string file_extra_object) {
  int i;

  if(!extra_objects) extra_objects = ({});
  if((i = member_array(id_extra_object,extra_objects)) != -1) return 0;
  extra_objects += ({ id_extra_object, file_extra_object, });
  return 1;
}


status remove_extra_object(string id_extra_object) {
  int i, size;  
  
  if((i=member_array(id_extra_object,extra_objects)) == -1) return 0;
  if((size = sizeof(extra_objects)) == 2)
    extra_objects = ({});
  else
    extra_objects = extra_objects[0..i-1]+extra_objects[i+2..size];  
  return 1;
}


status get_extra_object(string id_extra_object) {
  int i;
  object ob;  

  if(!id_extra_object) {
    write("Usage: get <item>\n");
    return 1;
  }
  if(present(id_extra_object,this_object())) return 0;
  if((i = get_id_index(id_extra_object,extra_objects)) == -1) return 0;
  ob = clone_object(extra_objects[i+1]);
  if(!this_player()->add_weight((int)ob->query_weight())) {
    write(capitalize(id_extra_object)+" is too heavy for you.\n");
    destruct(ob);
    return 1;
  }
#ifdef NATIVE_MODE
  ob->move(this_player());
#else
  move_object(ob, this_player());
#endif /* NATIVE_MODE */
  write("You get the "+capitalize(id_extra_object)+".\n");
  say(this_player()->query_name()+" gets a "+id_extra_object+".\n");
  return 1;
}


/****************************************************************************/
/* item_look */

status look_at(string str) {
  int i, size;
  string lock;

  if(!str) return 0;

  /* look thru items list */

  if((i = get_id_index(str,items)) != -1) {
    write(process_msg(add_newline(items[i+1])));
    return 1;
  }

  /* look thru day items list */

  if(query_day() && (i = get_id_index(str,day_items)) != -1) {
    write(process_msg(add_newline(day_items[i+1])));
    return 1;
  }

  /* look thru night items list */

  if(!query_day() && (i = get_id_index(str,night_items)) != -1) {
    write(process_msg(add_newline(night_items[i+1])));
    return 1;
  }

  /* look thru extra objects */

  else if((i = get_id_index(str,extra_objects)) != -1) {
    call_other(extra_objects[i+1],"long");
    return 1;
  }
  /* look in a direction */

  else if(look_dir(str)) {
    return 1;
  }

  /* look at doors */

  sscanf(str,"%s on %s", lock, str);
  sscanf(str,"%s on %s", lock, str);

  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(DOOR_ID == str) {
      if(SECRET_DOOR && member_array(REAL,SECRET_DOOR_FINDERS) == -1) {
        return 0;  /* haven't found the secret door */
      }
      if(LOCK_ID && LOCK_ID == lock) {
        write(process_msg(add_newline(LOCK_DESC)));
        return 1;
      }
      if(!(str = DOOR_DESC)) {
        write("It looks like a door. Perhaps you should "+
              ((CLOSED_FLAG) ? "open" : "close") +" it.\n");
      }
      else {
        write(process_msg(add_newline(str)));
      }
      if(!CLOSED_FLAG) {
        write("Through the "+ DOOR_ID +" you see,\n");
        call_other(DOOR_DEST,"long");
      }
      else if(LOCKED_FLAG) {
        write("The "+ DOOR_ID +" appears to be locked.\n");
      }
      return 1;
    }
  }
  return 0;  /* nothing in 'room' matching id matching 'str' */
}



/***************************************************************************/
/* load door */

void load_door(mixed *arg) {
  int j, i, old_door;
  string door_id;

  if(!doors) doors = ({});
  i = sizeof(doors);
  doors += ({ allocate(DOOR_MAC_SIZE), });
  CLOSED_FLAG = 1; /* default is closed */
  for(j = 0; j < sizeof(arg); j++) {
    if(!stringp(arg[j])) continue;
    switch(arg[j]) {
case "destination": case "file":              DOOR_DEST     = arg[++j]; break;
case "direction": case "name":                DOOR_ID       = arg[++j]; break;
case "closed":                                CLOSED_FLAG   = 1;        break;
case "open":                                  CLOSED_FLAG   = 0;        break;
case "locked":                                LOCKED_FLAG   = 1;        break;
case "unlocked":                              LOCKED_FLAG   = 0;        break;
case "long": case "desc": case "description": DOOR_DESC     = arg[++j]; break;
case "lock id":                               LOCK_ID       = arg[++j]; break;
case "lock description":                      LOCK_DESC     = arg[++j]; break;
case "lock difficulty":                       DOOR_LOCK     = arg[++j]; break;
case "key":  case "key id":                   DOOR_KEY      = arg[++j]; break;
case "secret": case "secret door":            SECRET_DOOR   = arg[++j]; break;
case "door number": case "number":            DOOR_NUM      = arg[++j]; break;
case "trap on":                               TRAP_ON       = 1;        break;
case "trap off":                              TRAP_ON       = 0;        break;
case "trap description": case "trap desc":    TRAP_DESC     = arg[++j]; break;
case "trap damage":                           DOOR_TRAP     = arg[++j]; break;
case "trap message":                          DOOR_TRAP_MSG = arg[++j]; break;
case "trap room message":                DOOR_TRAP_MSG_ROOM = arg[++j]; break;
case "secret door finders":             SECRET_DOOR_FINDERS = arg[++j]; break;
    }
  }
  /* defaults */
  if(!DOOR_KEY)               DOOR_KEY  = "no_key";
  if(!LOCK_ID && LOCK_DESC)   LOCK_ID   = "lock";
  if(!DOOR_ID)                DOOR_ID   = "door";
  if(!DOOR_DESC)              DOOR_DESC = "A "+ DOOR_ID +".\n";
  if(DOOR_KEY == "city key")  LOCKED_FLAG = 0; /* city doors are unlocked */
  
  /* remove old versions */
  
  door_id = DOOR_ID;
  for(j = i,i = 0; i < sizeof(doors); i++) {
    if(door_id == DOOR_ID && i < j) {
      old_door += 1;
    }
  }
  while(old_door--) remove_door(door_id);  
  update_actions();
}  


/***************************************************************************/
/* set door attribute - change an attibute on an existing door */

status set_door_attribute(mixed door_id, string attribute, mixed arg) {
  int i;
  string file;
  status call_by_other_room;

  if(!doors) doors = ({});
  if(!sizeof(doors)) return 0;
  if(intp(door_id)) {
    if(previous_object()) {
      file = file_name(previous_object());
      for(i = 0; i < sizeof(doors); i++) {
        if(DOOR_DEST == file && DOOR_NUM == door_id) break;
      }
      if(i == sizeof(doors)) return 0;
      call_by_other_room = 1;
    }
    else {
      return 0;
    }
  }
  else { /* assume stringp */
    for(i = 0; i < sizeof(doors) && DOOR_ID != door_id; i++);
    if(i == sizeof(doors)) return 0;
  }
  switch(attribute) {
    case "destination": case "file":              DOOR_DEST     = arg; break;
    case "direction": case "name":                DOOR_ID       = arg; break;
    case "closed":                                CLOSED_FLAG   = 1;   break;
    case "open":                                  CLOSED_FLAG   = 0;   break;
    case "locked":                                LOCKED_FLAG   = 1;   break;
    case "unlocked":                              LOCKED_FLAG   = 0;   break;
    case "long": case "desc": case "description": DOOR_DESC     = arg; break;
    case "lock id":                               LOCK_ID       = arg; break;
    case "lock description":                      LOCK_DESC     = arg; break;
    case "lock difficulty":                       DOOR_LOCK     = arg; break;
    case "key":  case "key id":                   DOOR_KEY      = arg; break;
    case "secret": case "secret door":            SECRET_DOOR   = arg; break;
    case "secret door finders":             SECRET_DOOR_FINDERS = arg; break;
    case "trap on":                               TRAP_ON       = 1;   break;
    case "trap off":                              TRAP_ON       = 0;   break;
    case "trap description": case "trap desc":    TRAP_DESC     = arg; break;
    case "trap damage":                           DOOR_TRAP     = arg; break;
    case "trap message":                          DOOR_TRAP_MSG = arg; break;
    case "trap room message":                DOOR_TRAP_MSG_ROOM = arg; break;
    case "door number":                           DOOR_NUM      = arg; break;
    default:
      return 0;
    break;
  }
  if(call_by_other_room) return 1;
  if(catch(call_other(DOOR_DEST,"set_door_attribute",DOOR_NUM,attribute,arg))){
    write("Error loading "+ DOOR_DEST +"\n");
  }
  return 1;
}


/***************************************************************************/
/* query door attribute -  on an existing door */


mixed query_door_attribute(string door_id, string attribute) {
  int i;

  if(!doors) doors = ({});
  if(!sizeof(doors)) return -1;
  for(i = 0; i < sizeof(doors) && DOOR_ID != door_id; i++);
  if(i == sizeof(doors)) return -1;
  switch(attribute) {
    case "destination": case "file":              return DOOR_DEST;
    case "direction": case "name":                return DOOR_ID;
    case "closed":                                return CLOSED_FLAG;
    case "open":                                  return !CLOSED_FLAG;
    case "locked":                                return LOCKED_FLAG;
    case "unlocked":                              return !LOCKED_FLAG;
    case "long": case "desc": case "description": return DOOR_DESC;
    case "lock id":                               return LOCK_ID;
    case "lock description":                      return LOCK_DESC;
    case "lock difficulty":                       return DOOR_LOCK;
    case "key":  case "key id":                   return DOOR_KEY;
    case "secret": case "secret door":            return SECRET_DOOR;
    case "trap on":                               return TRAP_ON;
    case "trap off":                              return !TRAP_ON;
    case "trap description": case "trap desc":    return TRAP_DESC;
    case "trap damage":                           return DOOR_TRAP;
    case "trap message":                          return DOOR_TRAP_MSG;
    case "trap room message":                     return DOOR_TRAP_MSG_ROOM;
    case "door number":                           return DOOR_NUM;
    case "secret door finders":                   return SECRET_DOOR_FINDERS;
    default:
      return -1;
    break;
  }
  return -1;
}


/*****************************************************************/
/* remove a door from room */

status remove_door(string str) {  
  int i, size;  

  if(!str) return 0;
  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(str == DOOR_ID) {
      if(size == 1) 
        doors = ({});
      else
        doors = doors[0..i-1] + doors[i+1..size-1];  
      return 1;
    }
  }
  return 0;  
}


/**********************************************************************/
/* start movement thru door (from add_action) */

status door_to_move(string str) {   
  string direction;   
  int i, size;
   
  direction = query_verb();   
  direction = convert_exit_name(direction);   
  /* default door exit appendages */
  if(!str && id(direction +" door")) str = "door";
  if(!str && id(direction +" gate")) str = "gate";

  if(str) direction += " " + str;

  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(direction == DOOR_ID) {
      if(SECRET_DOOR && member_array(REAL,SECRET_DOOR_FINDERS) < 0) {
        return 0;  /* haven't found the secret door */
      }
      if(CLOSED_FLAG) {
        write("The "+ DOOR_ID +" is closed\n");
        return 1;
      }
             
      /*** its always slower to move in stealth ***/   
     
      if(this_player()->query_stealth_on()){   
        write("You sneak slowly "+ direction +"...\n");   
        call_out("room_move", 1, DOOR_ID+"#"+DOOR_DEST);   
      }   
      else {   
        room_move(DOOR_ID+"#"+DOOR_DEST);   
      }
      if(SECRET_DOOR) {
        CLOSED_FLAG = 1;
        CLOSE_NEXT_ROOM(1);
        write("The "+DOOR_ID+" closes behind you.\n");   
      }
      return 1;   
    }
  }
  return 0; /* nothing in that direction */
}   

/***************************************************************************/
/* here */

status ob_here(string str) {
  if(!str) return 0;
  return (present(str) || present(str,this_player())) ? 1 : 0;
}

/***************************************************************************/
/* search room for secret doors */

int search_room(string str) {
  int i, size, search;
  string race;
  status found_flag; /* search finds something */
  string tmp1, tmp2;

  if(!str) {
    notify_fail("search what?\n");
    return 0;
  }
  search = QUERY_INT;
  if((race = (string)this_player()->query_race())) {
    if(sscanf(race,"%self%s", tmp1, tmp2)) {
      search += 3; /* racial search bonus */
    }
  }
  if(id(str)) {
    if((i = get_id_index(str,search_array)) != -1) {
      if(random(search_complex+1) <= random(search)) {
        write(process_msg(add_newline(search_array[i+1])));
        return 1;
      }
    }
    notify_fail("You find nothing of interest.\n");
    return 0;
  }
  else if(str != "room") {
    notify_fail("There is no "+ str +" here.\n");
    return 0;
  }
  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(SECRET_DOOR) {
      if(random(SECRET_DOOR) <= random(search)) {
        if(member_array(REAL,SECRET_DOOR_FINDERS) < 0) {
          SECRET_DOOR_FINDERS += ({ REAL, });
          if(!found_flag)
            write("You find a secret door!\n");
          else
            write("You find another secret door!\n");
          found_flag = 1;
        }
      }
    }
  }
  if(!found_flag) {
    write("You find nothing of interest.\n");
  }
  return 1;
}


/************************************************************************/
/* open the door */


status open(string str) {
  int i, size;

  if(!str) {
    notify_fail("open what?\n");
    return 0;
  }
  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(DOOR_ID == str) {
      if(SECRET_DOOR && member_array(REAL,SECRET_DOOR_FINDERS) == -1) {
        return 0; /* its a secret door */
      }
      if(DOOR_TRAP && TRAP_ON) {     /* Is door trap set? */
        TRAP_ON = 0;    /* trap_flag off */
        TRAP_NEXT_ROOM(0);
        write(process_msg(DOOR_TRAP_MSG));
        say(process_msg(DOOR_TRAP_MSG_ROOM));
        if(this_object()->trap_sprung(i)) return 1;
        this_player()->hit_player(DOOR_TRAP); 
      }
      if(!CLOSED_FLAG) {
        write("The "+ str +" is already open.\n");
        say(NAME +" tries to open an OPEN "+ str +"!!\n");
      }
      else {
        if(!LOCKED_FLAG || DOOR_KEY == "no_key") {
          write("You open the "+ str +".\n");
          say(NAME +" opens the "+ str +".\n");
          CLOSED_FLAG = 0; /* open the door */
          CLOSE_NEXT_ROOM(0);
        }
        else {
          write("The "+ str +" appears to be locked.\n");
          say(NAME +" cannot open the "+ str +".\n");
          if(DOOR_KEY && DOOR_KEY != "no_key") {
            write("There is a "+ LOCK_ID +".\n");
          }
        }
      }
      return 1;
    }   
  }
  if(id(str) || ob_here(str))
    notify_fail("You can't do that!\n");
  else
    notify_fail("There is no "+ str +" here.\n");
  return 0;
}


/***************************************************************************/
/* close the door */

status close(string str) {
  int i, size;

  if(!str) {
    notify_fail("close what?\n");
    return 0;
  }
  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(DOOR_ID == str) {
      if(SECRET_DOOR && member_array(REAL,SECRET_DOOR_FINDERS) == -1) {
        return 0; /* its a secret door */
      }
      if(DOOR_TRAP && TRAP_ON) {     /* Is door trap set? */
        TRAP_ON = 0;    /* trap_flag off */
        TRAP_NEXT_ROOM(0);
        write(process_msg(DOOR_TRAP_MSG));
        say(process_msg(DOOR_TRAP_MSG_ROOM));
        if(this_object()->trap_sprung(i)) return 1;
        this_player()->hit_player(DOOR_TRAP); 
      }
      if(!CLOSED_FLAG) {
        write("You close the "+ str +".\n");
        say(NAME +" closes the "+ str +".\n");
        CLOSED_FLAG = 1;
        CLOSE_NEXT_ROOM(1);
      }
      else {
        write("The "+ str +" is already closed.\n");
        say(NAME +" runs into the "+ str +".\n");
      }
      return 1;
    }   
  }
  if(id(str) || ob_here(str))
    notify_fail("You can't do that!\n");
  else
    notify_fail("There is no "+ str +" here.\n");
  return 0;
}

/**************************************************************************/
/* lock door */

status lock(string str) {  
  object key;  
  int i;
  string verb;
  string key_name;  
    
  if(!str || sscanf(str,"%s with %s", str, key_name) != 2) {
    notify_fail("lock <what> with <item>?\n");
    return 0;
  }
  for(i = sizeof(doors); i--; ) {
    if(DOOR_ID == str) {
      if(LOCKED_FLAG) {
        write("The "+ str +" is already locked.\n");
        return 1;
      }
      if(!(key = present(key_name, this_player()))){ 
        write("But you don't have a "+ capitalize(key_name) +"!\n"); 
        return 1; 
      } 
      if(key->id(DOOR_KEY) || DOOR_KEY == "no_key") {
        if(!CLOSED_FLAG) {
          write("You close the "+ str +".\n");
          CLOSED_FLAG = 1;
          CLOSE_NEXT_ROOM(1);
        }
        write("You lock the "+ str +" with the "+ key_name +".\n");
        say(NAME +" locks the "+ str +".\n");
        LOCKED_FLAG = 1;
        LOCK_NEXT_ROOM(1);
        return 1;
      }
    }
  }  
  if(id(str) || ob_here(str))
    notify_fail("You can't do that!\n");
  else
    notify_fail("There is no "+ capitalize(str) +" here.\n");
  return 0; 
}

/**************************************************************************/
/* unlock door */

status unlock(string str) {  
  object key;  
  int i;
  string verb;
  string key_name;  
    
  if(!str || sscanf(str,"%s with %s", str, key_name) != 2) {
    notify_fail("unlock <what> with <item>?\n");
    return 0;
  }
  for(i = sizeof(doors); i--; ) {
    if(DOOR_ID == str) {
      if(!LOCKED_FLAG) {
        write("The "+ str +" is already unlocked.\n");
        return 1;
      }
      if(!(key = present(key_name, this_player()))){ 
        write("But you don't have a "+ capitalize(key_name) +"!\n"); 
        return 1; 
      } 
      if(key->id(DOOR_KEY) || DOOR_KEY == "no_key") {
        write("You unlock the "+ str +" with the "+ key_name +".\n");
        say(NAME +" unlocks the "+ str +".\n");
        LOCKED_FLAG = 0;
        LOCK_NEXT_ROOM(0);
        return 1;
      }
    }
  }  
  if(id(str) || ob_here(str))
    notify_fail("You can't do that!\n");
  else
    notify_fail("There is no "+ capitalize(str) +" here.\n");
  return 0; 
}


/**************************************************************************/
/* pick lock */

status pick_lock(string str) {
  object ob;
  int i, j;

  if(QUERY_LOCKS == 0) {
    notify_fail("You do not have the skill to pick locks.\n");
    return 0;
  }
  if(!str || sscanf(str,"lock on %s",str) != 1) {
    notify_fail("pick lock on what?\n");
    return 0;
  }
  for(i = sizeof(doors); i--; ) {
    if(DOOR_ID == str) {
      if(SECRET_DOOR && member_array(REAL,SECRET_DOOR_FINDERS) == -1) {
        return 0; /* its a secret door */
      }
      if(DOOR_TRAP && TRAP_ON) {     /* Is door trap set? */
        TRAP_ON = 0;    /* trap_flag off */
        TRAP_NEXT_ROOM(0);
        write(process_msg(DOOR_TRAP_MSG));
        say(process_msg(DOOR_TRAP_MSG_ROOM));
        if(this_object()->trap_sprung(i)) return 1;
        this_player()->hit_player(DOOR_TRAP); 
      }
      if(!CLOSED_FLAG) {
        write("You close the "+ DOOR_ID +".\n");
        CLOSED_FLAG = 1;
        CLOSE_NEXT_ROOM(1);
      }
      write("You attempt to pick the lock.\n");
      if(!lock_attempts) lock_attempts = ({});
      if((j = member_array(str,lock_attempts)) == -1) {
        lock_attempts += ({ str, ({}), });
        j = member_array(str, lock_attempts);
      }
      if(member_array(REAL, lock_attempts[j+1]) != -1) {
        write("You give up...\n");
        return 1;
      }
      if(member_array(CAP, lock_attempts) != -1 ||
         random(QUERY_LOCKS+1) >= random(DOOR_LOCK+1)) {
        LOCKED_FLAG = !LOCKED_FLAG;
        tell_room(environment(this_player()),"Click!\n");
        write("You "+((LOCKED_FLAG) ? "" : "un")+"lock the "+ DOOR_ID +".\n");
        if(member_array(CAP, lock_attempts[j+1]) == -1) {
          lock_attempts[j+1] += ({ CAP, });
        }
      }
      else {
        write("You give up...\n");
        lock_attempts += ({ REAL, });
      }
      NEXT_ROOM_LOCKS();
      return 1;
    }
  }
  if(id(str) || ob_here(str))
    notify_fail("You can't do that!\n");
  else
    notify_fail("There is no "+ str +" here.\n");
  return 0;
}


/**************************************************************************/
/* disarm door trap */

status disarm_trap(string str) {
  int i, j;

  if(!str || sscanf(str,"trap on %s",str) != 1) {
    notify_fail("disarm trap on what?\n");
    return 0;
  }
  if(!detect_attempts) detect_attempts = ({});
  if((i = member_array(str, detect_attempts)) == -1 ||
    (member_array(REAL,detect_attempts[i+1]) == -1 
    && member_array(CAP,detect_attempts[i+1]) == -1)) {
    notify_fail("You have to detect a trap before you can disarm it.\n");
    return 0;
  }
  if(member_array(REAL,detect_attempts[i+1]) != -1) {
    write("You have not found a trap to disarm.\n");
    return 1;
  } 
  if(!disarm_attempts) disarm_attempts = ({});
  if((j = member_array(str, disarm_attempts)) == -1) {
    disarm_attempts += ({ str, ({}), });
    j = member_array(str, disarm_attempts);
  }
  if(member_array(REAL, disarm_attempts[j+1]) != -1) {
    write("You think you have disarmed it already.\n");
    return 1;
  }
  for(i = sizeof(doors); i--; ) {
    if(DOOR_ID == str) {
      if(random(DOOR_LOCK+1) <= random(QUERY_LOCKS+1)) {
        TRAP_ON = 0;
        TRAP_NEXT_ROOM(0);
      }
      write("You think you have disarmed the trap.\n");
      break;
    }
  }
  disarm_attempts[j+1] += ({ REAL, });
  NEXT_ROOM_DISARMS();
  return 1;
}    


/***************************************************************************/
/* arm door trap */

status arm_trap(string str) {
  int i, j, k;
  string door_id;

  if(!str || sscanf(str,"trap on %s",str) != 1) {
    notify_fail("arm trap on what?\n");
    return 0;
  }
  if(!detect_attempts) detect_attempts = ({});
  if((k = member_array(str, detect_attempts)) == -1 ||
    (member_array(REAL,detect_attempts[k+1]) == -1 
    && member_array(CAP,detect_attempts[k+1]) == -1)) {
    notify_fail("You have to detect any traps first.\n");
    return 0;
  }
  if(member_array(REAL,detect_attempts[k+1]) != -1) {
    write("You have not found a trap to arm.\n");
    return 1;
  } 
  if(!disarm_attempts) disarm_attempts = ({});
  if((j = member_array(str, disarm_attempts)) == -1) {
    disarm_attempts += ({ str, ({}), });
    j = member_array(str, disarm_attempts);
  }
  for(i = sizeof(doors); i--; ) {
    if(DOOR_ID == str) {
      if(random(DOOR_LOCK+1) <= random(QUERY_LOCKS+1)) {
        TRAP_ON = 1;
        TRAP_NEXT_ROOM(1);
        disarm_attempts[j+1] = ({});
        detect_attempts[k+1] = ({ CAP, });       
        write("You arm the trap.\n");
        NEXT_ROOM_DISARMS();
        NEXT_ROOM_DETECTS();
      }
      else {
        TRAP_ON = 0;    /* trap_flag off */
        TRAP_NEXT_ROOM(0);
        write(process_msg(DOOR_TRAP_MSG));
        say(process_msg(DOOR_TRAP_MSG_ROOM));
        if(this_object()->trap_sprung(i)) return 1;
        this_player()->hit_player(DOOR_TRAP);
      }
      return 1;
    }
  }
  return 1;
}    

/**********************************************************************/
/* detect door trap */

status detect(string str) {
  int i, j;

  if(QUERY_TRAPS == 0) {
    notify_fail("You do not have the skill to detect traps.\n");
    return 0;
  }
  if(!str || sscanf(str,"trap on %s",str) != 1) {
    notify_fail("detect trap on what?\n");
    return 0;
  }
  say(this_player()->query_name()+" examines the door for traps.\n");
  for(i = sizeof(doors); i--; ) {
    if(DOOR_ID == str) {
      if(SECRET_DOOR && member_array(REAL,SECRET_DOOR_FINDERS) == -1) {
        break; /* its a secret door */
      }
      if(!detect_attempts) detect_attempts = ({});
      if((j = member_array(str,detect_attempts)) == -1) {
        detect_attempts += ({ str, ({}), });
        j = member_array(str, detect_attempts);
      }
      if(!DOOR_TRAP || member_array(REAL,detect_attempts[j+1]) != -1) {
        write("There are no traps that you can detect.\n");
        if(member_array(REAL, detect_attempts[j+1]) == -1) {
          detect_attempts[j+1] += ({ REAL, });
        }
        return 1;
      }
      if(member_array(CAP,detect_attempts[j+1]) != -1) {
        write(process_msg(TRAP_DESC));
        return 1;
      }
      if(random(DOOR_LOCK+1) <= random(QUERY_TRAPS+1)) {
        TRAP_ON = 0;
        TRAP_NEXT_ROOM(0);
        detect_attempts[j+1] += ({ CAP, });
        write(process_msg(TRAP_DESC));
      }
      NEXT_ROOM_DETECTS();
      return 1;
    }
  }
  if(id(str) || ob_here(str))
    notify_fail("You can't do that!\n");
  else
    notify_fail("There is no "+ str +" here.\n");
  return 0;
}    


/***************************************************************************/
/* set door trap flag */

varargs status set_door_trap(string filename, status trap, int door_num) {
  int i, size;

  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(DOOR_DEST == filename) {
      if(door_num != DOOR_NUM) continue;
      TRAP_ON = trap;
      return 1;
    }
  }
  return 0;
}


/**************************************************************************/
/* set door closed flag */

varargs status set_door_closed(string filename, status close, int door_num) {
  int i, size;

  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(DOOR_DEST == filename) {
      if(door_num != DOOR_NUM) continue;
      CLOSED_FLAG = close;
      tell_room(this_object(),
        "The "+DOOR_ID+((close) ? " closes" : " opens")+".\n");
      return 1;
    }
  }
  return 0;
}

/**************************************************************************/
/* set door locked flag */

varargs status set_door_lock(string filename, status locked, int door_num) {
  int i, size;

  for(i = 0, size = sizeof(doors); i < size; i++) {
    if(DOOR_DEST == filename) {
      if(door_num != DOOR_NUM) continue;
      LOCKED_FLAG = locked;
      tell_room(this_object(),"Click!.\n");
      return 1;
    }
  }
  return 0;
}
   

/***************************************************************************/
/* close all doors */

void reset_doors(status arg) {
  int i, size;
  object room;

  if(!doors) doors = ({});
  for(i = 0, size = sizeof(doors); i < size; i++) {
    TRAP_ON = 1;
    TRAP_NEXT_ROOM(1);
    CLOSED_FLAG = 1;
    CLOSE_NEXT_ROOM(1);
    LOCKED_FLAG = (DOOR_KEY == "city key") ? 0 : 1;
    LOCK_NEXT_ROOM(LOCKED_FLAG);
    detect_attempts = ({});
    NEXT_ROOM_DETECTS();
    disarm_attempts = ({});
    NEXT_ROOM_DISARMS();
    lock_attempts   = ({});
    NEXT_ROOM_LOCKS();
    if(arg) SECRET_DOOR_FINDERS = ({});
  }
}


/***************************************************************************/
/* get index */

int get_id_index(string str,string *arr) {
  int i;
  string *ids;

  if(!arr) return -1;
  for(i = 0; i < sizeof(arr); i += 2) {
    if(!arr[i]) continue;
#ifdef OLD_EXPLODE
    ids = explode(arr[i]+"#", "#");
#else
    ids = explode(arr[i], "#");
#endif
    if(member_array(str,ids) != -1) return i;
  }
  return -1;
}     

/****************************************************************************/
/* set_sign */

varargs void set_sign_message(string str, string lang) {
  object obj;

  if(!str) return;
  obj = clone_object("inherit/sign");
  obj->set_message(str);
  if(lang) obj->set_language(lang);
#ifdef NATIVE_MODE
  obj->move(this_object());
#else
  move_object(obj, this_object());
#endif /* NATIVE_MODE */
}
string query_name() { return file_name(this_object()); }