tfe-1.0/area/
tfe-1.0/files/
tfe-1.0/logs/
tfe-1.0/logs/immortal/
tfe-1.0/logs/mob/
tfe-1.0/logs/object/
tfe-1.0/logs/player/
tfe-1.0/logs/room/
tfe-1.0/notes/clans/
tfe-1.0/player/
tfe-1.0/prev/
tfe-1.0/prev/area/
tfe-1.0/prev/player/
tfe-1.0/prev/rooms/
tfe-1.0/rooms/
tfe-1.0/src-gc/
tfe-1.0/src-msvc/
tfe-1.0/src-unix/
tfe-1.0/www/
tfe-1.0/www/html/
#include "ctype.h"
#include "sys/types.h"
#include "stdio.h"
#include "stdlib.h"
#include "define.h"
#include "struct.h"


obj_data*   has_key        ( char_data*, int );
room_data*  create_room    ( char_data* );
void        create_exit    ( wizard_data*, int, char*, bool );
void        delete_exit    ( wizard_data*, exit_data*, bool );
int         direction_arg  ( char*& argument );


const char* dflag_name [] = { "is.door", "closed", "locked", "secret",
  "pickproof", "no.show", "no.open", "reset.closed", "reset.locked",
  "reset.open", "requires.climb", "searchable" };


/*
 *   EXIT_DATA CLASS
 */


Exit_Data :: Exit_Data( ) 
{
  record_new( sizeof( exit_data ), MEM_EXIT );

  name      = empty_string;
  keywords  = empty_string;
  exit_info = 0;
  key       = -1;
  strength  = 10;
  light     = 10;
  size      = SIZE_OGRE;
  direction = -1;  
  to_room   = NULL;
}


Exit_Data :: ~Exit_Data( )
{
  record_delete( sizeof( exit_data ), MEM_EXIT );

  free_string( name,     MEM_EXIT );
  free_string( keywords, MEM_EXIT );
}


/*
 *
 */


const char* Exit_Data :: Name( char_data* ch, int, bool )
{
  return Seen_Name( ch );
}


const char* Exit_Data :: Seen_Name( char_data* ch, int, bool )
{
  char* tmp = static_string( );

  if( ch->pcdata == NULL
    || !is_set( &ch->pcdata->message, MSG_DOOR_DIRECTION ) ) 
    sprintf( tmp, "the %s", name );
  else
    sprintf( tmp, "the %s %s", name,
      dir_table[ direction ].where );  

  return tmp;
}


const char* Exit_Data :: Keywords( char_data* ch )
{
  char* tmp = static_string( );

  sprintf( tmp, "%s 1 %s %s",
    keywords, dir_table[ direction ].name,
    Seen_Name( ch ) );

  return tmp;
}


bool Exit_Data :: Seen( char_data* ch )
{
  wizard_data* imm;

  if( !is_set( &exit_info, EX_CLOSED ) ) 
    return TRUE;

  if( ( imm = wizard( ch ) ) != NULL
    && is_set( ch->pcdata->pfile->flags, PLR_HOLYLIGHT ) )
    return TRUE;

  if( is_set( &exit_info, EX_NO_SHOW ) )
    return FALSE;
 
  if( is_set( &exit_info, EX_SECRET ) 
    && !includes( ch->seen_exits, this ) ) 
    return FALSE;

  return TRUE;
}


/*
 *   DISK ROUTINES
 */


exit_data* add_exit( room_data* room, int dir )
{
  exit_data* exit;

  exit             = new exit_data;
  exit->direction  = dir;

  for( int i = 0; ; i++ ) 
    if( i == room->exits.size || dir < room->exits[i]->direction ) {
      insert( room->exits.list, room->exits.size, exit, i );
      break;
      }

  return exit;
}


void read_exits( FILE* fp, room_data* room, int vnum )
{
  exit_data*    exit;
  char        letter;

  for( ; ; ) {
    if( ( letter = fread_letter( fp ) ) != 'D' )
      break;

    exit = add_exit( room, fread_number( fp ) );

    exit->name        = fread_string( fp, MEM_EXIT );
    exit->keywords    = fread_string( fp, MEM_EXIT );
    exit->exit_info   = fread_number( fp );
    exit->key         = fread_number( fp );
    exit->to_room     = (room_data*) fread_number( fp );
    exit->strength    = fread_number( fp );
    exit->light       = fread_number( fp );
    exit->size        = fread_number( fp );

    if( exit->direction < 0 || exit->direction > 6 ) 
      panic( "Fread_rooms: vnum %d has bad door number.", vnum );
    }

  ungetc( letter, fp );
}


/*
 *   DISPLAY ROUTINES
 */


void do_exits( char_data* ch, char* )
{
  exit_data*   exit;
  bool        found  = FALSE;

  if( !ch->Can_See( TRUE ) )
    return;

  send_underlined( ch, "Obvious Exits\n\r" );

  for( int i = 0; i < ch->in_room->exits; i++ ) {
    exit = ch->in_room->exits[i];
    if( exit->Seen( ch ) ) {
      found = TRUE;
      if( !is_set( &exit->exit_info, EX_CLOSED ) ) {
        send( ch, "%-5s - %s\n\r",
          dir_table[ exit->direction ].name, exit->to_room->is_dark( )
          ? "Too dark to tell" : exit->to_room->name );
        }
      else {
        send( ch, "%-5s - Closed %s\n\r",
          dir_table[ exit->direction ].name, exit->name );
        }
      }
    }

  if( !found )
    send( ch, "None.\n\r" );
}


int exits_prompt( char* tmp, char_data* ch )
{
  exit_data*  exit;
  char*     string  = tmp;
  bool       water  = FALSE;
  int        exits  = 0;

  if( !ch->Can_See( ) ) {
    strcpy( tmp, "??" );
    return 2;
    }

  if( !ch->in_room->Seen( ch ) ) {
    strcpy( tmp, "-Dark-" );
    return 6;
    }

  for( int i = 0; i < ch->in_room->exits; i++ ) {
    exit = ch->in_room->exits[i];

    if( !exit->Seen( ch ) )
      continue;

    if( !is_set( &exit->exit_info, EX_CLOSED ) 
      && water_logged( exit->to_room ) ) {
      if( !water ) {
        strcpy( string, blue( ch ) );
        string += strlen( string );
        water = TRUE;
        }
      }
    else {
      if( water ) {
        strcpy( string, normal( ch ) );
        string += strlen( string );
        water = FALSE;
        }
      }

    *string = *dir_table[ exit->direction ].name;

    if( !is_set( &exit->exit_info, EX_CLOSED ) )
      *string = toupper( *string );

    exits++;
    string++;
    } 

  if( water ) 
    strcpy( string, normal( ch ) );
  else if( string == tmp ) {
    strcpy( tmp, "none" );
    return 4;
    }
  else 
    *string = '\0';

  return exits;
}  


void autoexit( char_data* ch )
{ 
  char          buf  [ TWO_LINES ];
  exit_data*   exit;
  room_data*   room;

  if( !is_set( ch->pcdata->pfile->flags, PLR_AUTO_EXIT ) )
    return;

  *buf = '\0';

  for( int i = 0; i < ch->in_room->exits; i++ ) {
    exit = ch->in_room->exits[i];
    if( !is_set( &exit->exit_info, EX_CLOSED )
      || ( !is_set( &exit->exit_info, EX_SECRET ) 
      && !is_set( &exit->exit_info, EX_NO_SHOW ) ) ) {
      if( *buf == '\0' )
        strcpy( buf, "[Exits:" );
      room = exit->to_room;
      if( is_builder( ch ) )
        sprintf( buf+strlen( buf ), " #%d", room->vnum  );
      if( water_logged( room ) ) 
        sprintf( buf+strlen( buf ), " %s%s%s", blue( ch ),
          dir_table[ exit->direction ].name, normal( ch ) );
      else
        sprintf( buf+strlen( buf ), " %s",
          dir_table[ exit->direction ].name );
      }
    }

  if( *buf == '\0' ) {
    send( ch, "[Exits: none]\n\r" );
    }
  else {
    strcat( buf, "]\n\r" );
    send( ch, buf );
    }
}


/*
 *   RANDOM EXIT ROUTINE
 */


exit_data* random_movable_exit( char_data* ch )
{
  room_data*  room  = ch->in_room;
  int        count  = 0;

  for( int i = 0; i < room->exits; i++ )
    if( ch->Can_Move( room->exits[i] ) )
      count++;

  if( count == 0 )
    return NULL;

  count = number_range( 1, count );

  for( int i = 0; i < room->exits; i++ ) 
    if( ch->Can_Move( room->exits[i] ) && --count == 0  )
      return room->exits[i];

  return NULL;
}


exit_data* random_open_exit( room_data* room )
{
  int count  = 0;

  for( int i = 0; i < room->exits; i++ )
    if( !is_set( &room->exits[i]->exit_info, EX_CLOSED ) )
      count++;

  if( count == 0 )
    return NULL;

  count = number_range( 1, count );

  for( int i = 0; i < room->exits; i++ ) 
    if( !is_set( &room->exits[i]->exit_info, EX_CLOSED )
      && --count == 0  )
      return room->exits[i];

  return NULL;
}


exit_data* random_exit( room_data* room )
{
  int i;

  if( room->exits == 0 )
    return NULL;

  i = number_range( 0, room->exits.size-1 );

  return room->exits[i];
}


/*
 *   SUPPORT ROUTINES
 */


int direction_arg( char*& argument )
{
  for( int i = 0; i < MAX_DOOR; i++ )
    if( matches( argument, dir_table[i].name ) )
      return i;

  return -1;
}


exit_data* exit_direction( room_data* room, int door )
{
  for( int i = 0; i < room->exits; i++ )
    if( room->exits[i]->direction == door )
      return room->exits[i];

  return NULL;
}


exit_data* reverse( exit_data* exit )
{
  exit_array*  array  = &exit->to_room->exits;
  int           door  = dir_table[ exit->direction ].reverse;

  for( int i = 0; i < *array; i++ )
    if( array->list[i]->direction == door )
      return array->list[i];

  return NULL;
}


void show_secret( exit_data* )
{
}


/*
 *   OPEN/CLOSE ROUTINES
 */


void do_open( char_data* ch, char* argument )
{
  thing_data*  thing;
  obj_data*      obj;
  exit_data*    door;

  if( is_confused_pet( ch ) )
    return;

  if( ( thing = one_thing( ch, argument, "open",
    (thing_array*) &ch->in_room->exits, ch->array,
    &ch->contents ) ) == NULL )
    return;

  if( ( obj = object( thing ) ) != NULL ) {
    include_closed = FALSE;
    open_object( ch, obj );
    include_closed = TRUE;
    return;
    }

  if( ( door = exit( thing ) ) != NULL ) {
    open_door( ch, door );   
    return;
    }

  send( ch, "%s isn't something you can open.\n\r", thing );
}


const char* open_msg [] =
{
  "to_char",  "You open $1.\n\r",
  "to_room",  "$1 opens $2.\n\r",
  "to_side",  "The $1 opens.\n\r",
  ""
};


void open_object( char_data* ch, obj_data* obj )
{
  if( obj->pIndexData->item_type != ITEM_CONTAINER ) {
    send( ch, "%s is not a container.\n\r", obj );
    return;
    }

  if( !is_set( &obj->value[1], CONT_CLOSED ) ) {
    send( ch, "%s is already open.\n\r", obj );
    return;
    }

  if( !is_set( &obj->value[1], CONT_CLOSEABLE ) ) {
    send( ch, "You can't do that.\n\r", ch );
    return;
    }

  if( is_set( &obj->value[1], CONT_LOCKED ) ) {
    send( ch, "%s is locked.\n\r", obj );
    return;
    }

  remove_bit( &obj->value[1], CONT_CLOSED );
  send( ch, "You open %s.\n\r", obj );
  send( *ch->array, "%s opens %s.\n\r", ch, obj );
}


bool open_door( char_data* ch, exit_data* exit ) 
{
  action_data*   action;
  room_data*       room  = ch->in_room;
 
  if( !is_set( &exit->exit_info, EX_ISDOOR ) ) {
    send( ch, "There is no door or wall there.\n\r" );
    return FALSE;
    }

  if( !is_set( &exit->exit_info, EX_CLOSED ) ) {
    send( ch, "%s is already open.\n\r", exit );
    return FALSE;
    }

  if( is_set( &exit->exit_info, EX_LOCKED ) ) {
    send( ch, "%s is locked.\n\r", exit );
    return FALSE;
    }

  if( is_set( &exit->exit_info, EX_NO_OPEN ) ) {
    send( ch, "You see no way to open %s.\n\r", exit );
    return FALSE;
    }

  for( action = room->action; action != NULL; action = action->next )
    if( action->trigger == TRIGGER_OPEN_DOOR
      && is_set( &action->flags, exit->direction ) ) {
      var_ch   = ch;
      var_room = room;
      if( !execute( action ) || ch->in_room != room )
        return FALSE;
      break;
      }

  act( ch, prog_msg( action, open_msg[0], open_msg[1] ), exit );
  act_notchar( prog_msg( action, open_msg[2], open_msg[3] ), ch, exit );
  remove_bit( &exit->exit_info, EX_CLOSED );

  room = exit->to_room; 

  if( ( exit = reverse( exit ) ) != NULL ) {
    remove_bit( &exit->exit_info, EX_CLOSED );
    act_room( room, prog_msg( action, open_msg[4], open_msg[5] ),
      exit );
    }

  return TRUE;
}


/*
 *   CLOSE
 */


const char* close_msg [] =
{
  "to_char",  "You close $1.\n\r",
  "to_room",  "$1 closes $2.\n\r",
  "to_side",  "$1 closes.\n\r",
  ""
};


void do_close( char_data* ch, char* argument )
{
  obj_data*         obj;
  exit_data*       door;
  thing_data*     thing;

  if( ( thing = one_thing( ch, argument, "close",
    (thing_array*) &ch->in_room->exits, ch->array,
    &ch->contents ) ) == NULL )
    return;

  if( ( obj = object( thing ) ) != NULL ) {
    include_closed = FALSE;
    close_object( ch, obj );
    include_closed = TRUE;
    return;
    }

  if( ( door = exit( thing ) ) != NULL ) {
    close_door( ch, door );   
    return;
    }

  send( ch, "%s isn't closable.\n\r", thing );
}


bool close_door( char_data* ch, exit_data* exit ) 
{
  room_data*       room;
  action_data*   action;

  if( !is_set( &exit->exit_info, EX_ISDOOR ) ) {
    send( ch, "There is nothing there that closes.\n\r" );
    return FALSE;
    }

  if( is_set( &exit->exit_info, EX_CLOSED ) ) {
    send( ch, "%s is already closed.\n\r", exit );
    return FALSE;
    }

  if( is_set( &exit->exit_info, EX_NO_OPEN ) ) {
    send( ch, "You don't see anyway to close %s.\n\r", exit );
    return FALSE;
    }

  room = ch->in_room;

  for( action = room->action; action != NULL; action = action->next )
    if( action->trigger == TRIGGER_CLOSE_DOOR
      && is_set( &action->flags, exit->direction ) ) {
      var_ch   = ch;
      var_room = room;
      if( !execute( action ) || ch->in_room != room )
        return FALSE;
      break;
      }

  act( ch, prog_msg( action, close_msg[0], close_msg[1] ), exit );
  act_notchar( prog_msg( action, close_msg[2], close_msg[3] ), ch, exit );
  set_bit( &exit->exit_info, EX_CLOSED );

  room = exit->to_room;

  if( ( exit = reverse( exit ) ) != NULL ) {
    set_bit( &exit->exit_info, EX_CLOSED );
    act_room( room, prog_msg( action, close_msg[4], close_msg[5] ),
      exit );
    }

  return FALSE;
}


void close_object( char_data* ch, obj_data* obj )
{
  if( obj->pIndexData->item_type != ITEM_CONTAINER ) {
    send( ch, "%s isn't a container.\n\r", obj );
    return;
    }

  if( is_set( &obj->value[1], CONT_CLOSED ) ) {
    send( ch, "%s is already closed.\n\r", obj );
    return;
    }

  if( !is_set( &obj->value[1], CONT_CLOSEABLE ) ) {
    send( ch, "You can't do that.\n\r" );
    return;
    }

 set_bit( &obj->value[1], CONT_CLOSED );
 send( ch, "You close %s.\n\r", obj );
 send( *ch->array, "%s closes %s.\n\r", ch, obj );
}


/*
 *   KEYS
 */


obj_data* has_key( char_data* ch, int vnum )
{
  obj_data*   obj;
  obj_data*  obj2;

  for( int i = 0; i < ch->contents; i++ ) {
    if( ( obj = object( ch->contents[i] ) ) == NULL )
      continue;
    if( obj->pIndexData->vnum == vnum ) {
      obj->selected = 1;
      return obj;
      }
    if( obj->pIndexData->item_type == ITEM_KEYRING )
      for( int j = 0; j < obj->contents; j++ ) {
        if( ( obj2 = object( obj->contents[j] ) ) != NULL
          && obj2->pIndexData->vnum == vnum ) {
          obj2->selected = 1;
          return obj2;
	  }
        }
    }

  return NULL;
}


/*
 *   LOCK ROUTINES
 */


const char* lock_msg [] =
{
  "to_char",  "You lock $1 with $2.\n\r",
  "to_room",  "$1 locks $2 with $3.\n\r",
  ""
};


const char* lock_door_msg [] =
{
  "to_char",  "You lock $1 with $2.\n\r",
  "to_room",  "$1 locks $2 with $3.\n\r",
  "to_side",  "You hear a key turn in $1.\n\r",
  ""
};


void do_lock( char_data* ch, char *argument )
{
  exit_data*       door;
  obj_data*         obj;
  obj_data*         key;
  oprog_data*     oprog;
  thing_data*     thing;

  if( ( thing = one_thing( ch, argument, "lock",
    (thing_array*) &ch->in_room->exits, ch->array,
    &ch->contents ) ) == NULL )
    return;
   
  if( ( obj = object( thing ) ) != NULL ) {
    if( obj->pIndexData->item_type != ITEM_CONTAINER ) {
      send( ch, "That's not a container.\n\r" );
      return;
      }

    if( !is_set( &obj->value[1], CONT_CLOSED ) ) {
      send( ch, "It's not closed.\n\r" );
      return;
      }

    if( obj->pIndexData->value[2] < 0 ) {
      send( ch, "It can't be locked.\n\r" );
      return;
      }

    if( ( key = has_key( ch, obj->pIndexData->value[2] ) ) == NULL ) {
      send( ch, "You lack the key.\n\r" );
      return;
      }

    if( is_set( &obj->value[1], CONT_LOCKED ) ) {
      send( ch, "It's already locked.\n\r" );
      return;
      }

    for( oprog = obj->pIndexData->oprog; oprog != NULL;
      oprog = oprog->next )
      if( oprog->trigger == OPROG_TRIGGER_LOCK ) {
        var_ch        = ch;
        var_room      = ch->in_room;
        var_obj       = key;
        var_container = obj;
        execute( oprog );
        break;
        }

    act( ch, prog_msg( oprog, lock_msg[0], lock_msg[1] ),
      obj, key );
    act_notchar( prog_msg( oprog, lock_msg[2], lock_msg[3] ),
      ch, obj, key );
    set_bit( &obj->value[1], CONT_LOCKED );
    return;
    }

  if( ( door = exit( thing ) ) != NULL ) {
    lock_door( ch, door );   
    return;
    }

  send( ch, "%s isn't lockable.\n\r", thing );
}

 
bool lock_door( char_data* ch, exit_data* exit )
{  
  room_data*       room;
  action_data*   action;
  obj_data*         key;

  if( !is_set( &exit->exit_info, EX_CLOSED ) ) {
    send( ch, "%s is not closed.\n\r", exit );
    return FALSE;
    }
  
  if( exit->key < 0 ) {
    send( ch, "%s can't be locked.\n\r", exit );
    return FALSE;
    }

  if( ( key = has_key( ch, exit->key ) ) == NULL ) {
    send( ch, "You lack the key.\n\r" );
    return FALSE;
    }

  if( is_set( &exit->exit_info, EX_LOCKED ) ) {
    send( ch, "%s is already locked.\n\r", exit );
    return FALSE;
    }

  room = ch->in_room;

  for( action = room->action; action != NULL; action = action->next )
    if( action->trigger == TRIGGER_LOCK_DOOR
      && is_set( &action->flags, exit->direction ) ) {
      var_ch   = ch;
      var_room = room;
      if( !execute( action ) || ch->in_room != room )
        return FALSE;
      break;
      }

  act( ch, prog_msg( action, lock_door_msg[0], lock_door_msg[1] ),
    key, exit );
  act_notchar( prog_msg( action, lock_door_msg[2], lock_door_msg[3] ),
    ch, key, exit );
  set_bit( &exit->exit_info, EX_LOCKED );

  room = exit->to_room;

  if( ( exit = reverse( exit ) ) != NULL ) {
    set_bit( &exit->exit_info, EX_LOCKED );
    act_room( room, prog_msg( action, lock_door_msg[4], lock_door_msg[5] ),
      exit );
    }

  return TRUE;
}


/*
 *   UNLOCK ROUTINES
 */


const char* unlock_msg [] =
{
  "to_char",  "You unlock $1 with $2.\n\r",
  "to_room",  "$1 unlocks $2 with $3.\n\r",
  ""
};


const char* unlock_door_msg [] =
{
  "to_char",  "You unlock $1 with $2.\n\r",
  "to_room",  "$1 unlocks $2 with $3.\n\r",
  "to_side",  "You hear a key turn in $1.\n\r",
  ""
};


void do_unlock( char_data* ch, char* argument )
{
  obj_data*         key;
  exit_data*       door;
  obj_data*         obj;
  oprog_data*     oprog;
  thing_data*     thing;

  if( ( thing = one_thing( ch, argument, "unlock",
    (thing_array*) &ch->in_room->exits, ch->array,
    &ch->contents ) ) == NULL )
    return;
   
  if( ( obj = object( thing ) ) != NULL ) {
    if( obj->pIndexData->item_type != ITEM_CONTAINER ) {
      send( ch, "%s isn't a container.\n\r", obj );
      return;
      }
    if( !is_set( &obj->value[1], CONT_CLOSED ) ) {
      send( ch, "%s isn't closed.\n\r", obj );
      return;
      }
    if( obj->pIndexData->value[2] < 0 ) {
      send( ch, "%s can't be unlocked.\n\r", obj );
      return;
      }
    if( ( key = has_key( ch, obj->pIndexData->value[2] ) ) == NULL ) {
      send( ch, "You lack the key.\n\r" );
      return;
      }
    if( !is_set( &obj->value[1], CONT_LOCKED ) ) {
      send( ch, "%s is already unlocked.\n\r", obj );
      return;
      }

    for( oprog = obj->pIndexData->oprog; oprog != NULL;
      oprog = oprog->next )
      if( oprog->trigger == OPROG_TRIGGER_UNLOCK ) {
        var_ch        = ch;
        var_room      = ch->in_room;
        var_obj       = key;
        var_container = obj;
        execute( oprog );
        break;
        }

    act( ch, prog_msg( oprog, unlock_msg[0], unlock_msg[1] ),
      obj, key );
    act_notchar( prog_msg( oprog, unlock_msg[2], unlock_msg[3] ),
      ch, obj, key );
    remove_bit( &obj->value[1], CONT_LOCKED );

    return;
    }

  if( ( door = exit( thing ) ) != NULL ) {
    unlock_door( ch, door );
    return;
    }

  send( ch, "%s isn't unlockable.\n\r", thing );
}
  

bool unlock_door( char_data* ch, exit_data* exit )
{ 
  action_data*  action;
  room_data*      room;
  obj_data*        key;

  if( !is_set( &exit->exit_info, EX_CLOSED ) ) {
    send( ch, "The %s exit is not associated with a door.\n\r",
      dir_table[ exit->direction ].name );
    return FALSE;
    }

  if( !is_set( &exit->exit_info, EX_CLOSED ) ) {
    send( ch, "%s is not closed.\n\r", exit );
    return FALSE;
    }

  if( exit->key < 0 ) {
    send( ch, "You see no obvious way to unlock %s.\n\r", exit );
    return FALSE;
    }

  if( ( key = has_key( ch, exit->key ) ) == NULL ) {
    send( ch, "You lack the key - perhaps try picking it.\n\r" );
    return FALSE;
    }

  if( !is_set( &exit->exit_info, EX_LOCKED ) ) {
    send( ch, "%s is already unlocked.\n\r", exit );
    return FALSE;
    }

  room = ch->in_room;

  for( action = room->action; action != NULL; action = action->next )
    if( action->trigger == TRIGGER_UNLOCK_DOOR
      && is_set( &action->flags, exit->direction ) ) {
      var_ch   = ch;
      var_room = room;
      if( !execute( action ) || ch->in_room != room )
        return TRUE;
      break;
      }

  act( ch, prog_msg( action, unlock_door_msg[0], unlock_door_msg[1] ),
    key, exit );
  act_notchar( prog_msg( action, unlock_door_msg[2], unlock_door_msg[3] ),
    ch, key, exit );
  remove_bit( &exit->exit_info, EX_LOCKED );

  room = exit->to_room;

  if( ( exit = reverse( exit ) ) != NULL ) {
    remove_bit( &exit->exit_info, EX_LOCKED );
    act_room( room, prog_msg( action, unlock_door_msg[4],
      unlock_door_msg[5] ), exit );
    }

  return TRUE;
}


void do_pick( char_data* ch, char* argument )
{
  obj_data*       obj;
  thing_data*   thing;
  exit_data*     door; 

  if( ( thing = one_thing( ch, argument, "pick",
    (thing_array*) &ch->in_room->exits, ch->array,
    &ch->contents ) ) == NULL )
    return;

  if( ( obj = object( thing ) ) != NULL ) {
    if( obj->pIndexData->item_type != ITEM_CONTAINER ) {
      send( ch, "%s isn't a container.\n\r", obj );
      return;
      }
    if( !is_set( &obj->value[1], CONT_CLOSED ) ) { 
      send( ch, "%s is not closed.\n\r", obj );
      return;
      }
    if( !is_set( &obj->value[1], CONT_LOCKED ) ) {
      send( ch, "%s is already unlocked.\n\r", obj );
      return;
      }
    if( ch->species != NULL
      || !ch->check_skill( SKILL_PICK_LOCK ) ) {
      send( ch, "You failed.\n\r" );
      return;
      }      
    if( is_set( &obj->value[1], CONT_PICKPROOF ) ) {
      send( ch, "You fail to pick the lock and are fairly sure it is impossible to pick.\n\r" ); 
      return;
      }
    send( ch, "*Click*\n\r" );
    send_seen( ch, "%s picks %s.\n\r", ch, obj );
    return;
    }

  if( ( door = exit( thing ) ) != NULL ) {
    pick_door( ch, door );
    return;
    }

  send( ch, "%s isn't pickable.\n\r", thing );
} 


bool pick_door( char_data* ch, exit_data* exit )
{
  if( !is_set( &exit->exit_info, EX_CLOSED ) ) {
    send( ch, "%s is not closed.\n\r", exit );
    return FALSE;
    }

  if( !is_set( &exit->exit_info, EX_LOCKED ) ) {
    send( ch, "%s is already unlocked.\n\r", exit );
    return FALSE;
    }

  if( exit->key < 0 ) {
    send( ch, "You see no keyhole in the door to pick.\n\r" );
    return FALSE;
    }

  if( is_set( &exit->exit_info, EX_PICKPROOF ) ) {
    send( ch, "You failed.\n\r" );
    return FALSE;
    }
  
  if( ch->species != NULL || !ch->check_skill( SKILL_PICK_LOCK ) ) {
    send( ch, "You failed.\n\r" );
    return FALSE;
    }

  remove_bit( &exit->exit_info, EX_LOCKED );

  send( ch, "*Click*\n\r" );
  send_seen( ch, "%s picks %s.\n\r", ch, exit );
    
  ch->improve_skill( SKILL_PICK_LOCK );

  if( ( exit = reverse( exit ) ) != NULL ) 
    remove_bit( &exit->exit_info, EX_LOCKED );

  return TRUE;
}


/*
 *   KNOCK
 */


const char* knock_door_msg [] =
{
  "to_char",  "You knock on $1.\n\r",
  "to_room",  "$1 knocks $2.\n\r",
  "to_side",  "You hear a knock on $1.\n\r",
  ""
};


void do_knock( char_data* ch, char* argument )
{
  exit_data*     door;
  thing_data*   thing;

  if( is_confused_pet( ch ) )
    return;

  if( ( thing = one_thing( ch, argument, "knock",
    (thing_array*) &ch->in_room->exits ) ) == NULL )
    return;

  if( ( door = exit( thing ) ) != NULL ) {
    knock_door( ch, door );
    return;
    }

  send( ch, "Knocking on %s would serve no purpose.\n\r", thing );
}


void knock_door( char_data* ch, exit_data* exit )
{
  action_data*  action;
  room_data*      room;

  if( !is_set( &exit->exit_info, EX_ISDOOR ) ) {
    send( ch, "There is no door %s to knock on.\n\r",
      dir_table[ exit->direction ].where );
    return;
    }

  if( !is_set( &exit->exit_info, EX_CLOSED ) ) {
    send( ch, "It's not closed, so no reason to knock.\n\r" );
    return;
    }

  room = ch->in_room;

  for( action = room->action; action != NULL; action = action->next )
    if( action->trigger == TRIGGER_KNOCK_DOOR
      && is_set( &action->flags,  exit->direction  ) ) {
      var_ch   = ch;
      var_room = room;
      if( !execute( action ) || ch->in_room != room )
        return;
      break;
      }

  act( ch, prog_msg( action, knock_door_msg[0], knock_door_msg[1] ),
    exit );
  act_notchar( prog_msg( action, knock_door_msg[2], knock_door_msg[3] ),
    ch, exit );

  room = exit->to_room;

  if( ( exit = reverse( exit ) ) != NULL ) 
    act_room( room, prog_msg( action, knock_door_msg[4],
      knock_door_msg[5] ), exit );
}


/*
 *   DOOR BASH ROUTINE
 */


void bash_door( char_data* ch, exit_data* exit )
{
  if(  is_mounted( ch )
    || is_entangled( ch, "bash doors" ) )
    return;

  if( !is_set( &exit->exit_info, EX_ISDOOR ) ) {
    send( ch, "There is no door %s and you can't bash an open exit.\n\r",
      dir_table[ exit->direction ].where );
    return;
    }

  if( !is_set( &exit->exit_info, EX_CLOSED ) ) {
    send( ch, "%s isn't closed and thus no point in bashing it.\n\r",
      exit->name );
    return;
    }

  if(  exit->direction  == DIR_UP ||  exit->direction  == DIR_DOWN ) {
    send( ch, "You can't effectively bash doors in the ceiling or\
 ground.\n\r" );
    return;
    }

  fsend( ch, "You take a running charge and throw yourself at %s,\
 but do no damage to it.", exit->name );
  fsend_seen( ch, "%s takes a running charges and throws %sself at %s,\
 but does no damage to it.", ch, ch->Him_Her( ), exit->name );
} 


/*
 *   ONLINE EDITING ROUTINES
 */


void do_dedit( char_data* ch, char* argument )
{
  wizard_data*    imm  = (wizard_data*) ch;
  exit_data*     exit;
  int           flags;
  int             dir;

  if( !get_flags( ch, argument, &flags, "1", "dedit" ) )
    return;

  if( matches( argument, "delete" ) ) {
    if( ( exit = (exit_data*) one_thing( ch, argument, "dedit delete", 
      (thing_array*) &ch->in_room->exits ) ) != NULL )
      delete_exit( imm, exit, is_set( &flags, 0 ) );
    return;
    }

  if( matches( argument, "new" ) ) {
    if( ( dir = direction_arg( argument ) ) != -1 ) 
      create_exit( imm, dir, argument, is_set( &flags, 0 ) );
    return;
    }

  if( ( exit = (exit_data*) one_thing( ch, argument, "dedit", 
    (thing_array*) &ch->in_room->exits ) ) == NULL )
    return;

  imm->exit_edit = exit;

  send( ch, "Dflag and dset now act on the exit %s.\n\r",
    dir_table[ exit->direction ].where );
}


void delete_exit( wizard_data* imm, exit_data* exit, bool one_way )
{
  exit_data* back;

  if( !can_edit( imm, imm->in_room ) ) 
    return;

  if( ( back = reverse( exit ) ) != NULL && !one_way ) {
    if( !can_edit( imm, exit->to_room, FALSE ) ) {
      send( imm,
        "You don't have permission to delete the return exit.\n\r" );
      return;
      }
    imm->exit_edit = back;
    extract( imm, offset( &imm->exit_edit, imm ), "exit" );
    exit->to_room->exits -= back;
    delete back;
    }

  imm->exit_edit = exit;
  extract( imm, offset( &imm->exit_edit, imm ), "exit" );
  imm->in_room->exits -= exit;
  delete exit;

  send( imm, "You remove %sthe exit %s.\n\r",
    back != NULL && one_way ? "just this side of " : "",
    dir_table[ exit->direction ].where );

  return;
}


void create_exit( wizard_data* ch, int dir, char* argument, bool one_way )
{
  room_data*  room  = ch->in_room;
  exit_data*  exit;

  if( exit_direction( room, dir ) != NULL ) {
    send( ch, "An exit already exists %s.\n\r",
      dir_table[ dir ].where );
    return;
    }

  if( *argument == '\0' ) {
    if( ( room = create_room( ch ) ) == NULL )
      return;
    }
  else {
    if( ( room = get_room_index( atoi( argument ), FALSE ) ) == NULL ) {
      send( ch, "No room with that vnum exists.\n\r" );
      return;
      }
    if( !one_way
      && exit_direction( room, dir_table[dir].reverse ) != NULL ) {
      send( ch, "Door returning already exists.\n\r" );  
      return;
      }
    if( !can_edit( ch, room, FALSE ) ) {
      send( ch, "You don't have permission to tunnel into that area.\n\r" );
      return;
      }
    }

  exit                        = add_exit( ch->in_room, dir );
  exit->to_room               = room;
  ch->exit_edit               = exit;
  ch->in_room->area->modified = TRUE;

  if( !one_way ) {
    exit                 = add_exit( room, dir_table[dir].reverse );
    exit->to_room        = ch->in_room;
    room->area->modified = TRUE;
    }

  send( ch, "%s-way exit %s to room %d added.\n\r",
    one_way ? "One" : "Two", dir_table[dir].name, room->vnum );
}


room_data* create_room( char_data* ch )
{
  room_data*  room;
  int         vnum  = 1;

  for( vnum = ch->in_room->area->room_first->vnum+1; ; vnum++ ) {
    if( ( room = get_room_index( vnum, FALSE ) ) == NULL )
      break;
    if( room->area != ch->in_room->area ) {
      send( ch, "Area is out of numbers.\n\r" );
      return NULL;
      }
    }

  room               = new room_data;
  room->area         = ch->in_room->area;
  room->vnum         = vnum;
  room->name         = alloc_string( ch->in_room->name, MEM_ROOM );
  room->description  = alloc_string( "Under Construction.\n\r", MEM_ROOM );
  room->comments     = empty_string;
  room->room_flags   = ch->in_room->room_flags;
  room->sector_type  = ch->in_room->sector_type;

  append( ch->in_room->area->room_first, room );

  return room;
}


void do_dflag( char_data* ch, char* argument )
{
  wizard_data*   imm  = (wizard_data*) ch; 
  exit_data*    exit;
  exit_data*    back;
  int          flags;

  if( !get_flags( ch, argument, &flags, "1", "dflag" ) )
    return;

  if( ( exit = imm->exit_edit ) == NULL ) {
    send( ch, "Use dedit to specify direction.\n\r" );
    return;
    }

  if( *argument == '\0' ) {
    display_flags( "Door Flags", &dflag_name[0], &dflag_name[1],
      &exit->exit_info, MAX_DFLAG, ch );
    return;
    }

  if( !can_edit( ch, ch->in_room ) ) 
    return;

  ch->in_room->area->modified = TRUE;

  for( int i = 0; i < MAX_DFLAG; i++ ) 
    if( fmatches( argument, dflag_name[i] ) ) {
      switch_bit( &exit->exit_info, i );
      if( ( back = reverse( exit ) ) != NULL
        && !is_set( &flags, 0 ) )
        assign_bit( &back->exit_info, i, is_set( &exit->exit_info,i ) );
      send( ch, "%s set %s on %sthe %s exit.\n\r",
        dflag_name[i], true_false( &exit->exit_info, i ),
        back != NULL && !is_set( &flags, 0 ) ? "both sides of " : "",
        dir_table[ exit->direction ].name );
      return;
      }

  send( ch, "Unknown flag - see dflag with no arguments for list.\n\r" );
}


void do_dstat( char_data* ch, char* argument )
{
  wizard_data*      imm  = (wizard_data*) ch;
  exit_data*       exit;
  obj_clss_data*    key;

  if( *argument == '\0' ) { 
    if( ( exit = imm->exit_edit ) == NULL ) {
      send( ch, "Use dedit to specify direction.\n\r" );
      return;
      }
    }
  else 
    if( ( exit = (exit_data*) one_thing( ch, argument, "dstat",
      (thing_array*) &ch->in_room->exits ) ) == NULL )
      return;
   
  send( ch, "[%s]\n\r", dir_table[ exit->direction ].name );
  send( ch, "  Leads to : %s (%d)\n\r",
    exit->to_room->name, exit->to_room->vnum );

  if( ( key = get_obj_index( exit->key ) ) == NULL )  
    send( ch, "       Key : None\n\r" );
  else  
    send( ch, "       Key : %s (%d)\n\r", key->Name( ), exit->key );

  send( ch, "      Name : %s\n\r",   exit->name );
  send( ch, "  Keywords : %s\n\r",   exit->keywords );
  send( ch, "  Strength : %d\n\r",   exit->strength );
  send( ch, "     Light : %d%%\n\r", exit->light );
  send( ch, "      Size : %s\n\r",   size_name[ exit->size ] );
}


void do_dset( char_data* ch, char* argument )
{ 
  wizard_data*   imm  = (wizard_data*) ch;
  exit_data*    exit;
  obj_data*      obj;
  int          flags;

  if( !get_flags( ch, argument, &flags, "1", "dset" ) )
    return;

  if( *argument == '\0' ) {
    do_dstat( ch, "" );
    return;
    }
  
  if( !can_edit( ch, ch->in_room ) )
    return;

  if( ( exit = imm->exit_edit ) == NULL ) {
    send( ch, "You must specify a direction with dedit.\n\r" );
    return;
    }

  ch->in_room->area->modified = TRUE;

  if( matches( argument, "key" ) ) {
    if( *argument == '\0' ) {
      exit->key = -1;
      send( ch, "Key for lock on %s exit set to none.\n\r",
        dir_table[ exit->direction ].name );
      return;
      }
    if( ( obj = one_object( ch, argument,
      "key", &ch->contents ) ) == NULL )
      return;
    if( obj->pIndexData->item_type != ITEM_KEY ) {
      send( ch, "%s isn't a key.\n\r", obj );
      return;
      }  
    exit->key = obj->pIndexData->vnum;
    send( ch, "Key for lock on %s exit set to %s.\n\r",
      dir_table[ exit->direction ].name, obj->pIndexData->Name( ) );
    return;
    }

  class type_field type_list[] = {
    { "size",  MAX_SIZE,  &size_name[0],  &size_name[1],  &exit->size },
    { "",      0,         NULL,           NULL,           NULL        }
    };

  if( process( type_list, ch, dir_table[ exit->direction ].name,
    argument ) )
    return;

  class string_field string_list[] = {
    { "name",      MEM_EXIT,  &exit->name,       NULL },
    { "keywords",  MEM_EXIT,  &exit->keywords,   NULL },
    { "",          0,         NULL,              NULL },   
    };

  if( process( string_list, ch, dir_table[ exit->direction ].name,
    argument ) )
    return;

  class int_field int_list[] = {
    { "strength",          0,   10,  &exit->strength  },
    { "light",             0,  100,  &exit->light     },
    { "",                  0,    0,  NULL             }
    };

  if( process( int_list, ch, dir_table[ exit->direction ].name,
    argument ) )
    return;

  send( ch, "Unknown field - See help dset.\n\r" );
}


/*
 *   SPELLS
 */


bool spell_wizard_lock( char_data*, char_data*, void*, int, int )
{
  return TRUE;
}