dbna/clans/
dbna/councils/
dbna/deity/
dbna/gods/
dbna/houses/
dbna/space/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * v. 0.9: 6/19/95:  Converts an ascii map to rooms.                        *
 * v. 1.0: 7/05/95:  Read/write maps to .are files.  Efficient storage.     *
 *	             Room qualities based on map code. Can add & remove rms *
 *	                from a map. (Somewhat) intelligent exit decisions.  *
 * v. 1.1: 7/11/95:  Various display options.  See comments over draw_map   *
 *	                                                                    *
 ****************************************************************************/

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "mud.h"


/*
 * Useful Externals
 */
extern int top_exit;
void note_attach( CHAR_DATA * ch );

/*
 * Local defines.  Undef'ed at end of file.
 */

#define MID	MAP_INDEX_DATA
#define MD	MAP_DATA
#define RID	ROOM_INDEX_DATA
#define CD	CHAR_DATA
#define EDD     EXTRA_DESCR_DATA
#define OD      OBJ_DATA
#define OID     OBJ_INDEX_DATA
#define XD      EXIT_DATA

/*
 * Local function prototypes
 */
MID *make_new_map_index( int vnum );
void map_to_rooms( CD * ch, MID * m_index );
void map_stats( CD * ch, int *rooms, int *rows, int *cols );
int num_rooms_avail( CD * ch );
int add_new_room_to_map( CD * ch, MID * map, int row, int col, int proto_room, char code );
int number_to_room_num( int array_index );
int char_to_number( char code );
int exit_lookup( int vnum1, int vnum2 );
void draw_map( CD * ch, RID * rm, int flag, int mode );
char *you_are_here( int row, int col, char *map );
char get_map_code( RID * room, int mode );

/*
 * Local Variables & Structs
 */
char text_map[4150];
extern MID *first_map;  /* should be global */
struct map_stuff
{
  int vnum;
  int proto_vnum;
  int exits;
  int index;
  char code;
};


/*************************************************************/
/*                                                           */
/* First section for read/write of map to .are files and     */
/*  on-line map editing, such as it is.                      */
/*                                                           */
/*************************************************************/

/*
 * Be careful not to give
 * this an existing map_index
 */
MID *make_new_map_index( int vnum )
{
  MID *map_index;
  int i, j;

  CREATE( map_index, MID, 1 );
  map_index->vnum = vnum;
  for( i = 0; i < 49; i++ )
  {
    for( j = 0; j < 78; j++ )
    {
      map_index->map_of_vnums[i][j] = -1;
    }
  }
  map_index->next = first_map;
  first_map = map_index;
  return map_index;
}

/* 
 * output goes in global text_map 
 *  flag = 0, do a 'you_are_here'
 *  flag = 1, don't
 *
 *
 *  Modes  :  what characters mean :  character set
 *  --------------------------------------------------------------------
 *        0: room code             : 92 ascii chars, detailed elsewhere
 *        1: # of mobs             : 0 thru 9, +
 *        2: # of pc's             : 0 thru 9, +
 *        3: # of objs             : 0 thru 9, +
 *        4: # of exits            : 0 thru 6
 *        5: sectortype            : hex, 0 thru MAX_SECT
 *        6: light                 : 0 or 1 
 *        7: indoors               : X or O
 *        8: death                 : X or O
 *        9: safe                  : X or O
 *       10: nosummon              : X or O
 *       11: # of descr lines      : 0 thru 9, +
 */

char *const map_opts[] = {
  "code", "mobs", "pcs", "objs", "exits", "sector", "light", "indoors", "death", "safe", "nosummon", "descr", "descrlines"
};

char count_lines( char *txt )
{
  int i;
  char *c, buf[MAX_STRING_LENGTH];

  if( !txt )
    return ( char )'0';

  i = 1;
  for( c = txt; *c != '\0'; c++ )
    if( *c == '\n' )
      i++;

  if( i > 9 )
    return ( char )'+';
  sprintf( buf, "%d", i );
  return ( buf[0] );
}

char get_map_code( RID * room, int mode )
{
  char buf[MAX_STRING_LENGTH];
  CD *mob;
  OD *obj;
  int count /*, i */ ;  /* Unused */
  EXIT_DATA *pexit;

  if( !room )
    return ( char )'X';

  count = 0;
  switch ( mode )
  {
    case 11:
      return count_lines( room->description );
    case 10:
      if( xIS_SET( room->room_flags, ROOM_NO_SUMMON ) )
        return ( char )'X';
      return ( char )'O';
    case 9:
      if( xIS_SET( room->room_flags, ROOM_SAFE ) )
        return ( char )'X';
      return ( char )'O';
    case 8:
      if( xIS_SET( room->room_flags, ROOM_DEATH ) )
        return ( char )'X';
      return ( char )'O';
    case 7:
      if( xIS_SET( room->room_flags, ROOM_INDOORS ) )
        return ( char )'X';
      return ( char )'O';
    case 6:
      sprintf( buf, "%d", room->light );
      return ( buf[0] );
    case 5:
      sprintf( buf, "%d", room->sector_type );
      return ( buf[0] );
    case 4:
      for( pexit = room->first_exit; pexit; pexit = pexit->next )
        count++;
      sprintf( buf, "%d", count );
      return ( buf[0] );
    case 3:
      for( obj = room->first_content; obj; obj = obj->next_content )
        count++;
      if( count > 9 )
        return ( char )'+';
      sprintf( buf, "%d", count );
      return ( buf[0] );
    case 2:
      for( mob = room->first_person; mob; mob = mob->next_in_room )
      {
        if( !IS_NPC( mob ) )
          count++;
      }
      if( count > 9 )
        return ( char )'+';
      sprintf( buf, "%d", count );
      return ( buf[0] );
    case 1:
      for( mob = room->first_person; mob; mob = mob->next_in_room )
      {
        if( IS_NPC( mob ) )
          count++;
      }
      if( count > 9 )
        return ( char )'+';
      sprintf( buf, "%d", count );
      return ( buf[0] );
    default:
      if( !room->map )
        return ( char )'X';
      return ( ( char )room->map->entry );
  }
  return ( char )'?';
}

void draw_map( CD * ch, RID * rm, int flag, int mode )
{
  MID *map_index;
  RID *tmp_rm;
  int i, x, y, nontriv;

  if( !rm->map )
  {
    sprintf( text_map, "(no rm->map)\n\r" );
    return;
  }
  if( ( map_index = get_map_index( rm->map->vnum ) ) == NULL )
  {
    bug( " No map_index with vnum %d\n\r", rm->map->vnum );
    sprintf( text_map, "-ERROR-\n\r" );
    return;
  }
  i = 0;
  nontriv = 0;
  for( y = 0; y < 49; y++ )
  {
    for( x = 0; x < 78; x++ )
    {
      if( map_index->map_of_vnums[y][x] < 1 )
      {
        text_map[i] = ' ';
        i++;
        continue;
      }
      /*
       * tmp_rm = map_index -> map_of_ptrs[y][x]; 
       */
      tmp_rm = get_room_index( map_index->map_of_vnums[y][x] );
      if( tmp_rm == NULL )
      {
        text_map[i] = ' ';
        i++;
        continue;
      }
      if( !tmp_rm->map )
      {
        text_map[i] = ' ';
        i++;
        continue;
      }

      /*
       *  Following's kinda convoluted...  If ch has ansi,
       *   bold the code of the room he's in.  If not, indicate
       *   room he's in by '*'
       */
      if( ( flag == 1 ) )
      {
        if( map_index->map_of_vnums[y][x] == ch->in_room->vnum )
        {
          if( xIS_SET( ch->act, PLR_ANSI ) )
          {
            text_map[i] = ( char )'\x1B'; /* Bold */
            i++;
            text_map[i] = ( char )'[';
            i++;
            text_map[i] = ( char )'1';
            i++;
            text_map[i] = ( char )'m';
            i++;
            /*
             * text_map[i] = (char) tmp_rm -> map -> entry;
             */
            text_map[i] = ( char )get_map_code( tmp_rm, mode );
            i++;
            text_map[i] = ( char )'\x1B'; /* Normal */
            i++;
            text_map[i] = ( char )'[';
            i++;
            text_map[i] = ( char )'0';
            i++;
            text_map[i] = ( char )'m';
            i++;

          }
          else
          {
            text_map[i] = ( char )'*';
            i++;
          }
        }
        else
        {
          text_map[i] = ( char )get_map_code( tmp_rm, mode );
          /*
           * text_map[i] = (char) tmp_rm -> map -> entry; 
           */
          i++;
        }
      }
      else
      {
        text_map[i] = ( char )get_map_code( tmp_rm, mode );
        /*
         * text_map[i] = (char) tmp_rm -> map -> entry;
         */
        i++;
      }
      nontriv = i;
    }
    text_map[i] = '\n';
    i++;
  }
  text_map[nontriv + 2] = '\n';
  text_map[nontriv + 3] = '\r';
  text_map[nontriv + 4] = '\0';
}

MID *get_map_index( int vnum )
{
  MID *map;

  for( map = first_map; map; map = map->next )
  {
    if( map->vnum == vnum )
      return map;
  }
  return NULL;
}



void init_maps(  )
{
  int i;

/*
    for (map_index = first_map; map_index; map_index = map_index -> next) {

	for (i = 0; i < 49; i++) {
	    for (j = 0; j < 78; j++) {
		   map_index -> map_of_ptrs[i][j] = 
		    get_room_index (map_index -> map_of_vnums[i][j]);
                   
	    }
	}

    }
*/

  for( i = 0; i < 49 * 78; i++ )
    text_map[i] = '\0';

  return;
}


/******************************************************************
 * These functions convert maps to rooms
 ******************************************************************/
void map_stats( CD * ch, int *rooms, int *rows, int *cols )
{
  int row, col, n;
  int leftmost, rightmost;
  char *l, c;

  if( !ch->pnote )
  {
    bug( "map_stats: ch->pnote==NULL!", 0 );
    return;
  }
  n = 0;
  row = col = 0;
  leftmost = rightmost = 0;

  l = ch->pnote->text;
  do
  {
    c = l[0];
    switch ( c )
    {
      case '\n':
        break;
      case '\r':
        col = 0;
        row++;
        break;
      case ' ':
        col++;
        break;
    }
    if( char_to_number( c ) > -1 )
    {
      if( col < leftmost )
        leftmost = col;
      if( col > rightmost )
        rightmost = col;
      col++;
      n++;
    };
    l++;
  }
  while( c != '\0' );

  *cols = rightmost - leftmost + 1;
  *rows = row;  /* [sic.] */
  *rooms = n;
}

int get_mode( char *type )
{
  int x;

  for( x = 0; x < 12; x++ )
    if( !str_cmp( type, map_opts[x] ) )
      return x;
  return -1;
}

void do_lookmap( CD * ch, char *argument )
{
  char arg1[MAX_STRING_LENGTH];
  char buf[MAX_STRING_LENGTH];
  int mode;


  if( ch->in_room->map )
  {
    if( !argument )
    {
      mode = 0;
    }
    else
    {
      argument = one_argument( argument, arg1 );
      mode = get_mode( arg1 );
    }

    set_char_color( AT_PLAIN, ch );
    sprintf( buf, ".------[Map %5.5d]-----------------------------------------------------------.\n\r",
             ch->in_room->map->vnum );
    send_to_char( buf, ch );
    draw_map( ch, ch->in_room, 1, mode );
    send_to_char( text_map, ch );
    sprintf( buf, "`----------------------------------------------------------------------------'\n\r" );
    send_to_char( buf, ch );
  }
  else
  {
    send_to_char( "You see no map here.\n\r", ch );
  }
  return;
}

void do_mapout( CD * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  char arg[MAX_INPUT_LENGTH];
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  char arg3[MAX_INPUT_LENGTH];  /* growl */
  char arg4[MAX_INPUT_LENGTH];  /* rediculous */
  OD *map_obj;  /* an obj made with map as an ed */
  OID *map_obj_index; /*    obj_index for previous     */
  EDD *ed;  /*    the ed for it to go in     */
  MID *map_index; /* the "vnum" of map_index to use */
  MD *map, *tmp;  /* for new per-room map info to goin */
  RID *this_rm, *tmp_r = NULL;  /* room ch is standing in */
  XD *tmp_x;  /* exit data */
  char code;
  int rooms, rows,  /* ints for stats & looping */
    cols, row, col, mapnum, x, y, avail_rooms;

  if( !ch )
  {
    bug( "do_mapout: null ch", 0 );
    return;
  }
  if( IS_NPC( ch ) )
  {
    send_to_char( "Not in mobs.\n\r", ch );
    return;
  }
  if( !ch->desc )
  {
    bug( "do_mapout: no descriptor", 0 );
    return;
  }
  switch ( ch->substate )
  {
    default:
      break;
    case SUB_WRITING_NOTE:
      if( ch->dest_buf != ch->pnote )
        bug( "do_mapout: sub_writing_map: ch->dest_buf != ch->pnote", 0 );
      STRFREE( ch->pnote->text );
      ch->pnote->text = copy_buffer( ch );
      stop_editing( ch );
      return;
  }

  set_char_color( AT_NOTE, ch );
  argument = one_argument( argument, arg );
  smash_tilde( argument );

  if( !str_cmp( arg, "stat" ) )
  {
    if( !ch->pnote )
    {
      send_to_char( "You have no map in progress.\n\r", ch );
      return;
    }
    map_stats( ch, &rooms, &rows, &cols );
    sprintf( buf, "Map represents %d rooms, and has %d rows and %d columns\n\r", rooms, rows, cols );
    send_to_char( buf, ch );
    avail_rooms = num_rooms_avail( ch );
    sprintf( buf, "You currently have %d unused rooms.\n\r", avail_rooms );
    send_to_char( buf, ch );

    act( AT_ACTION, "$n glances at an etherial map.", ch, NULL, NULL, TO_ROOM );
    return;
  }


  /*
   *  Adds an existing room to a map
   */
  if( !str_cmp( arg, "continue" ) )
  {
    if( ch->prev_cmd == do_north )
    {
      send_to_char( "Your last command was north.\n\r", ch );
      argument = one_argument( argument, arg1 );

      if( ( arg1[0] == '\0' ) )
      {
        code = '#';
      }
      else
      {
        code = arg1[0];
      }

      tmp = NULL;
      tmp_x = get_exit( ch->in_room, DIR_SOUTH );
      if( tmp_x )
        tmp_r = tmp_x->to_room;
      if( tmp_r )
        tmp = tmp_r->map;
      if( !tmp )
      {
        send_to_char( "No exit to south, or no map in south room. Aborting. \n\r", ch );
        return;
      }

      row = ( tmp->y ) - 1;
      col = ( tmp->x );

      if( row < 0 )
      {
        send_to_char( "Can't map off the top of the buffer.\n\r", ch );
        return;
      }
      if( row > 48 )
      {
        send_to_char( "Can't map off the bottom of the buffer.\n\r", ch );
        return;
      }

      mapnum = tmp->vnum;
      if( ( map_index = get_map_index( mapnum ) ) == NULL )
      {
        sprintf( buf, "Trouble accessing map.(No such map?).\n\r" );
        send_to_char( buf, ch );
        return;
      }
      sprintf( buf, "addroom %d %d %d %c", mapnum, row, col, code );
      do_mapout( ch, buf );
      return;
    }

    if( ch->prev_cmd == do_east )
    {
      send_to_char( "Your last command started was east\n\r", ch );
      argument = one_argument( argument, arg1 );

      if( ( arg1[0] == '\0' ) )
      {
        code = '#';
      }
      else
      {
        code = arg1[0];
      }
      tmp = NULL;
      tmp_x = get_exit( ch->in_room, DIR_WEST );
      if( tmp_x )
        tmp_r = tmp_x->to_room;
      if( tmp_r )
        tmp = tmp_r->map;

      if( !tmp )
      {
        send_to_char( "No exit to west, or no map in west room. Aborting. \n\r", ch );
        return;
      }

      row = ( tmp->y );
      col = ( tmp->x ) + 1;

      if( col < 0 )
      {
        send_to_char( "Can't map off the left of the buffer.\n\r", ch );
        return;
      }
      if( row > 78 )
      {
        send_to_char( "Can't map off the right of the buffer.\n\r", ch );
        return;
      }

      mapnum = tmp->vnum;
      if( ( map_index = get_map_index( mapnum ) ) == NULL )
      {
        sprintf( buf, "Trouble accessing map.(No such map?).\n\r" );
        send_to_char( buf, ch );
        return;
      }
      sprintf( buf, "addroom %d %d %d %c", mapnum, row, col, code );
      do_mapout( ch, buf );
      return;
    }

    if( ch->prev_cmd == do_south )
    {
      send_to_char( "Your last command was south\n\r", ch );
      argument = one_argument( argument, arg1 );

      if( ( arg1[0] == '\0' ) )
      {
        code = '#';
      }
      else
      {
        code = arg1[0];
      }
      tmp = NULL;
      tmp_x = get_exit( ch->in_room, DIR_NORTH );
      if( tmp_x )
        tmp_r = tmp_x->to_room;
      if( tmp_r )
        tmp = tmp_r->map;

      if( !tmp )
      {
        send_to_char( "No exit to north, or no map in north room. Aborting. \n\r", ch );
        return;
      }

      row = ( tmp->y ) + 1;
      col = ( tmp->x );

      if( row < 0 )
      {
        send_to_char( "Can't map off the top of the buffer.\n\r", ch );
        return;
      }
      if( row > 48 )
      {
        send_to_char( "Can't map off the bottom of the buffer.\n\r", ch );
        return;
      }

      mapnum = tmp->vnum;
      if( ( map_index = get_map_index( mapnum ) ) == NULL )
      {
        sprintf( buf, "Trouble accessing map.(No such map?).\n\r" );
        send_to_char( buf, ch );
        return;
      }
      sprintf( buf, "addroom %d %d %d %c", mapnum, row, col, code );
      do_mapout( ch, buf );
      return;
    }

    if( ch->prev_cmd == do_west )
    {
      send_to_char( "Your last command was west\n\r", ch );
      argument = one_argument( argument, arg1 );

      if( ( arg1[0] == '\0' ) )
      {
        code = '#';
      }
      else
      {
        code = arg1[0];
      }

      tmp = NULL;
      tmp_x = get_exit( ch->in_room, DIR_EAST );
      if( tmp_x )
        tmp_r = tmp_x->to_room;
      if( tmp_r )
        tmp = tmp_r->map;

      if( !tmp )
      {
        send_to_char( "No exit to east, or no map in east room. Aborting. \n\r", ch );
        return;
      }

      row = ( tmp->y );
      col = ( tmp->x ) - 1;

      if( col < 0 )
      {
        send_to_char( "Can't map off the left of the buffer.\n\r", ch );
        return;
      }
      if( row > 78 )
      {
        send_to_char( "Can't map off the right of the buffer.\n\r", ch );
        return;
      }

      mapnum = tmp->vnum;
      if( ( map_index = get_map_index( mapnum ) ) == NULL )
      {
        sprintf( buf, "Trouble accessing map.(No such map?).\n\r" );
        send_to_char( buf, ch );
        return;
      }
      sprintf( buf, "addroom %d %d %d %c", mapnum, row, col, code );
      do_mapout( ch, buf );
      return;
    }

    sprintf( buf, "Your previous command was something I cannot backtrack..\n\r" );
    send_to_char( buf, ch );
    return;
  }

  /*
   *  Adds an existing room to a map
   */
  if( !str_cmp( arg, "addroom" ) )
  {

    argument = one_argument( argument, arg1 );
    argument = one_argument( argument, arg2 );
    argument = one_argument( argument, arg3 );
    argument = one_argument( argument, arg4 );

    mapnum = atoi( arg1 );  /* i don't like this */
    y = atoi( arg2 );
    x = atoi( arg3 );

    if( ( arg1[0] == '\0' ) || ( arg2[0] == '\0' ) || ( arg3[0] == '\0' ) )
    {
      send_to_char( "Syntax:                                   \n\r", ch );
      send_to_char( "mapout addroom <mapnum> <row> <col> [code]\n\r", ch );
      send_to_char( "                                          \n\r", ch );
      send_to_char( "where: <mapnum> is the vnum of map to use\n\r", ch );
      send_to_char( "       <row> is row of room (start 0)\n\r", ch );
      send_to_char( "       <col> is col of room (start 0)\n\r", ch );
      send_to_char( "   [code] is optional  room character code \n\r", ch );
      return;
    }

    if( ( arg4[0] == '\0' ) )
    {
      code = '#';
    }
    else
    {
      code = arg4[0];
    }


    if( ( map_index = get_map_index( mapnum ) ) == NULL )
    {
#ifdef HURM
      sprintf( buf, "Trouble accessing map.(No such map?).\n\r" );
      send_to_char( buf, ch );
      return;
#endif
      map_index = make_new_map_index( mapnum );
      if( map_index == NULL )
      {
        send_to_char( "Could neither find nor make a map index with that number.\n\r", ch );
        return;
      }
    }

    this_rm = ch->in_room;
    if( this_rm->map != NULL )
    {
      sprintf( buf, "This room (vnum %d) is already in map %d.\n\r", ch->in_room->vnum, ch->in_room->map->vnum );
      send_to_char( buf, ch );
      return;
    }

    if( ( x < 0 ) || ( x > 78 ) )
    {
      sprintf( buf, "Bad map x coordinate. Room(vnum %d), x= %d \n\r", ch->in_room->vnum, x );
      send_to_char( buf, ch );
      return;
    }
    if( ( y < 0 ) || ( y > 48 ) )
    {
      sprintf( buf, "Bad map y coordinate. Room(vnum %d), y= %d \n\r", ch->in_room->vnum, y );
      send_to_char( buf, ch );
      return;
    }
    if( map_index->map_of_vnums[y][x] != -1 )
    {
      sprintf( buf, "That (x,y) coordinate (%d, %d) is already taken by room %d.\n\r", x, y, map_index->map_of_vnums[y][x] );
      send_to_char( buf, ch );
      return;
    }

    /*
     * all error checking done 
     */
    CREATE( map, MAP_DATA, 1 );
    map->vnum = mapnum;
    map->x = x;
    map->y = y;
    map->entry = code;
    ch->in_room->map = map;

    map_index->map_of_vnums[y][x] = ch->in_room->vnum;

    send_to_char( "Added.\n\r", ch );
    return;


  }

  /*
   *  Removes a room from a map
   */
  if( !str_cmp( arg, "removeroom" ) )
  {
    this_rm = ch->in_room;
    if( !this_rm->map )
    {
      sprintf( buf, "This room (vnum %d) is in no map \n\r", ch->in_room->vnum );
      send_to_char( buf, ch );
      return;
    }

    if( ( map_index = get_map_index( this_rm->map->vnum ) ) == NULL )
    {
      sprintf( buf, "Trouble accessing map room(vnum %d), map vnum %d \n\r", ch->in_room->vnum, this_rm->map->vnum );
      send_to_char( buf, ch );
      return;
    }
    if( ( this_rm->map->x < 0 ) || ( this_rm->map->x > 78 ) )
    {
      sprintf( buf, "Bad map x coordinate. Room(vnum %d), x= %d \n\r", ch->in_room->vnum, this_rm->map->x );
      send_to_char( buf, ch );
      return;
    }
    if( ( this_rm->map->y < 0 ) || ( this_rm->map->y > 48 ) )
    {
      sprintf( buf, "Bad map y coordinate. Room(vnum %d), y= %d \n\r", ch->in_room->vnum, this_rm->map->y );
      send_to_char( buf, ch );
      return;
    }
    /*
     * now that all that's out of the way.... 
     */
    sprintf( buf, "Removing room (vnum %d), from map %d \n\r", ch->in_room->vnum, this_rm->map->vnum );
    send_to_char( buf, ch );

    /*
     * Thanks to Nick Gammon for pointing out x and y being
     * uninitialized.  -Thoric 
     */
    x = this_rm->map->x;
    y = this_rm->map->y;

    map_index->map_of_vnums[y][x] = -1;
    this_rm->map->vnum = 0;
    this_rm->map->x = -1;
    this_rm->map->y = -1;
    this_rm->map->entry = ' ';
    DISPOSE( this_rm->map );
    this_rm->map = NULL;  /* redundant? */
    send_to_char( "Removed.\n\r", ch );
    return;
  }

  if( !str_cmp( arg, "write" ) )
  {
    note_attach( ch );
    ch->substate = SUB_WRITING_NOTE;
    ch->dest_buf = ch->pnote;
    start_editing( ch, ch->pnote->text );
    return;
  }
  if( !str_cmp( arg, "clear" ) )
  {
    if( ch->pnote )
    {
      STRFREE( ch->pnote->text );
      STRFREE( ch->pnote->subject );
      STRFREE( ch->pnote->to_list );
      STRFREE( ch->pnote->date );
      STRFREE( ch->pnote->sender );
      DISPOSE( ch->pnote );
    }
    ch->pnote = NULL;

    send_to_char( "Map cleared.\n\r", ch );
    return;
  }
  if( !str_cmp( arg, "show" ) )
  {
    if( !ch->pnote )
    {
      send_to_char( "You have no map in progress.\n\r", ch );
      return;
    }
    /*
     * send_to_char(buf, ch); 
     */
    send_to_char( ch->pnote->text, ch );
    do_mapout( ch, "stat" );
    return;
  }
  if( !str_cmp( arg, "redo" ) )
  {
    if( !ch->pnote )
    {
      send_to_char( "You have no map in progress.\n\r", ch );
      return;
    }
    send_to_char( "This option not yet supported.\n\r", ch );
    return;

  }
  if( !str_cmp( arg, "create" ) )
  {
    if( !ch->pnote )
    {
      send_to_char( "You have no map in progress.\n\r", ch );
      return;
    }
    map_stats( ch, &rooms, &rows, &cols );
    avail_rooms = num_rooms_avail( ch );

    /*
     * check for not enough rooms 
     */
    if( rooms > avail_rooms )
    {
      send_to_char( "You don't have enough unused rooms allocated!\n\r", ch );
      return;
    }
    act( AT_ACTION, "$n warps the very dimensions of space!", ch, NULL, NULL, TO_ROOM );

    map_to_rooms( ch, NULL ); /* this does the grunt work */

    map_obj_index = get_obj_index( 5013 );
    if( !map_obj_index )
    {
      map_obj = create_object( map_obj_index, 0 );
      ed = SetOExtra( map_obj, "runes map scrawls" );
      STRFREE( ed->description );
      ed->description = QUICKLINK( ch->pnote->text );
      obj_to_char( map_obj, ch );
    }
    else
    {
      send_to_char( "Couldn't give you a map object.  Need Great Eastern Desert\n\r", ch );
      return;
    }

    do_mapout( ch, "clear" );
    send_to_char( "Ok.\n\r", ch );
    return;
  }
  send_to_char( "mapout write: create a map in edit buffer.\n\r", ch );
  send_to_char( "mapout stat: get information about a written, but not yet created map.\n\r", ch );
  send_to_char( "mapout clear: clear a written, but not yet created map.\n\r", ch );
  send_to_char( "mapout show: show a written, but not yet created map.\n\r", ch );
  send_to_char( "mapout create: turn a written map into rooms in your assigned room vnum range.\n\r", ch );
  return;
}



int add_new_room_to_map( CD * ch, MID * map, int row, int col, int proto_room, char code )
{
  int i;
  char buf[MAX_STRING_LENGTH];
  RID *location, *rm;


  /*
   * Get a room to copy from
   */
  rm = get_room_index( proto_room );
  if( !rm )
    rm = ch->in_room;


  /*
   * Get an unused room to copy to
   */
  for( i = ch->pcdata->r_range_lo; i <= ch->pcdata->r_range_hi; i++ )
  {
    if( get_room_index( i ) == NULL )
    {
      location = make_room( i );
      if( !location )
      {
        bug( "next_rooms_avail: make_room failed", 0 );
        return -1;
      }
      /*
       * Clones current room  (quietly)
       */
      location->area = ch->pcdata->area;
      location->name = QUICKLINK( rm->name );
      location->description = QUICKLINK( rm->description );
      CREATE( location->map, MAP_DATA, 1 );
      location->map->vnum = map->vnum;  /* not working? */
      location->map->x = col;
      location->map->y = row;
      location->map->entry = code;

//      location -> room_flags = ROOM_PROTOTYPE && rm -> room_flags;
      location->light = rm->light;
      location->sector_type = rm->sector_type;
      return i;
    }
  }
  sprintf( buf, "No available room!\n\r" );
  send_to_char( buf, ch );
  return -1;
}

int num_rooms_avail( CD * ch )
{
  int i, n;

  n = 0;
  for( i = ch->pcdata->r_range_lo; i <= ch->pcdata->r_range_hi; i++ )
    if( !get_room_index( i ) )
      n++;

  return n;
}

/*
 * This function takes the character string in ch->pnote and
 *  creates rooms laid out in the appropriate configuration.
 */
void map_to_rooms( CD * ch, MID * m_index )
{
  struct map_stuff map[49][78]; /* size of edit buffer */
  int row, col, i, n, x, y, tvnum, proto_vnum, leftmost, rightmost;
  char *l, c;
  RID *newrm;
  MID *map_index = NULL, *tmp;
  XD *xit;  /* these are for exits */
  int exit_type;

  if( !ch->pnote )
  {
    bug( "map_to_rooms: ch->pnote==NULL!", 0 );
    return;
  }
  n = 0;
  row = col = 0;
  leftmost = rightmost = 0;

  /*
   * Check to make sure map_index exists.  
   * If not, then make a new one.
   */
  if( !m_index )
  { /* Make a new vnum */
    for( i = ch->pcdata->r_range_lo; i <= ch->pcdata->r_range_hi; i++ )
    {
      if( ( tmp = get_map_index( i ) ) == NULL )
      {
        map_index = make_new_map_index( i );
        break;
      }
    }
  }
  else
  {
    map_index = m_index;
  }

  /*
   *  
   */
  if( !map_index )
  {
    send_to_char( "Couldn't find or make a map_index for you!\n\r", ch );
    bug( "map_to_rooms: Couldn't find or make a map_index\n\r", 0 );
    /*
     * do something. return failed or somesuch 
     */
  }


  for( x = 0; x < 49; x++ )
  {
    for( y = 0; y < 78; y++ )
    {
      map[x][y].vnum = 0;
      map[x][y].proto_vnum = 0;
      map[x][y].exits = 0;
      map[x][y].index = 0;
    }
  }
  l = ch->pnote->text;
  do
  {
    c = l[0];
    switch ( c )
    {
      case '\n':
        break;
      case '\r':
        col = 0;
        row++;
        break;
      case ' ':
        col++;
        break;
    }
    if( ( map[row][col].index = char_to_number( c ) ) > -1 )
    {
      proto_vnum = number_to_room_num( map[row][col].index );
      map[row][col].vnum = add_new_room_to_map( ch, map_index, row, col, proto_vnum, c );

      map_index->map_of_vnums[row][col] = map[row][col].vnum;
      map[row][col].proto_vnum = proto_vnum;
      map[row][col].code = c;
      col++;
      n++;

    }
    else
    {
      map_index->map_of_vnums[row][col] = 0;
      map[row][col].vnum = 0;
      map[row][col].exits = 0;
    }

    l++;
  }
  while( c != '\0' );

  for( y = 0; y < row + 1; y++ )
  { /* rows */
    for( x = 0; x < 78; x++ )
    { /* cols (78, i think) */

      if( map[y][x].vnum == 0 )
        continue;

      newrm = get_room_index( map[y][x].vnum );
      CREATE( newrm->map, MAP_DATA, 1 );
      newrm->map->vnum = map_index->vnum;
      newrm->map->x = x;
      newrm->map->y = y;
      newrm->map->entry = map[y][x].code;

      /*
       * Check north
       */
      if( y > 0 )
      {
        if( ( tvnum = map[y - 1][x].vnum ) != 0 )
        {
          exit_type = exit_lookup( map[y][x].proto_vnum, map[y - 1][x].proto_vnum );
          if( exit_type > -1 )
          {
            xit = make_exit( newrm, get_room_index( tvnum ), DIR_NORTH );
            xit->keyword = STRALLOC( "" );
            xit->description = STRALLOC( "" );
            xit->key = -1;
            xit->exit_info = exit_type;
          }
        }
      }
      /*
       * east 
       */
      if( x < 79 )
      {
        if( ( tvnum = map[y][x + 1].vnum ) != 0 )
        {
          exit_type = exit_lookup( map[y][x].proto_vnum, map[y][x + 1].proto_vnum );
          if( exit_type > -1 )
          {
            xit = make_exit( newrm, get_room_index( tvnum ), DIR_EAST );
            xit->keyword = STRALLOC( "" );
            xit->description = STRALLOC( "" );
            xit->key = -1;
            xit->exit_info = exit_type;
          }
        }
      }
      /*
       * south 
       */
      if( y < 48 )
      {
        if( ( tvnum = map[y + 1][x].vnum ) != 0 )
        {
          exit_type = exit_lookup( map[y][x].proto_vnum, map[y + 1][x].proto_vnum );
          if( exit_type > -1 )
          {
            xit = make_exit( newrm, get_room_index( tvnum ), DIR_SOUTH );
            xit->keyword = STRALLOC( "" );
            xit->description = STRALLOC( "" );
            xit->key = -1;
            xit->exit_info = exit_type;
          }
        }
      }
      /*
       * west 
       */
      if( x > 0 )
      {
        if( ( tvnum = map[y][x - 1].vnum ) != 0 )
        {
          exit_type = exit_lookup( map[y][x].proto_vnum, map[y][x - 1].proto_vnum );
          if( exit_type > -1 )
          {
            xit = make_exit( newrm, get_room_index( tvnum ), DIR_WEST );
            xit->keyword = STRALLOC( "" );
            xit->description = STRALLOC( "" );
            xit->key = -1;
            xit->exit_info = exit_type;
          }
        }
      }
    }
  }
}

/******************************************************************
 * Constants & constant-like functions follow
 ******************************************************************/

char *const standard_room_names[] = {
  "Hut", "Tent", "Hovel", "Campsite",
  "Shack", "Cabin", "Homested", "Keep",
  "Fortress", "Castle", "GuardHse", "Temple",
  "Store", "Graveyard", "Monastry", "Stable", "Tavern",
  "Basemnt", "Bedroom", "BnquetRm", "Corridor", "Attic",
  "Vault", "SittingRm", "Study", "Passage", "Tower",
  "Crypt", "WorkRoom", "Lab", "Hallway", "Turret",
  "StorRm", "Kitchen", "Larder", "Stairway", "Rooftop",
  "Closet", "Office", "Treasury", "Landing", "Balcony",
  "Foyer", "DrawingRm", "Den", "Ladder", "Catwalk",
  "Entrnce", "Arboretum", "Library", "Vent", "Shaft",
  "Gate", "AudiencRm", "Consrvty", "DumbWatr", "Chimney",
  "Porch", "ClassRoom", "CloakRm", "Lawn", "Garden",
  "Lake", "Forest", "Swamp", "Well", "Street",
  "River", "Canyon", "Beach", "Mine", "Road",
  "Stream", "Clearing", "SnakePit", "Tunnel", "Path",
  "Rapids", "Desert", "SandStrm", "Rope", "Cliff",
  "CaveRiv", "Jungle", "Sandbar", "RopeBrdg", "Bridge",
  "CaveLak", "Cave", "None", "RopeLadr", "NatlBrdg"
};

int const standard_room_vnums[] = {
  9500, 9501, 9502, 9503,
  9504, 9505, 9506, 9507,
  9508, 9509, 9510, 9511,
  9512, 9513, 9514, 9515, 9516,
  9517, 9518, 9519, 9520, 9521,
  9522, 9523, 9524, 9525, 9526,
  9527, 9528, 9529, 9530, 9531,
  9532, 9533, 9534, 9535, 9536,
  9537, 9538, 9539, 9540, 9541,
  9542, 9543, 9544, 9545, 9546,
  9547, 9548, 9549, 9550, 9551,
  9552, 9553, 9554, 9555, 9556,
  9557, 9558, 9559, 9560, 9561,
  9562, 9563, 9564, 9565, 9566,
  9567, 9568, 9569, 9570, 9571,
  9572, 9573, 9574, 9575, 9576,
  9577, 9578, 9579, 9580, 9581,
  9582, 9583, 9584, 9585, 9586,
  9587, 9588, 9589, 9590, 9591
};


/*
 *  Picks an entry from standard_room_vnums[], checking
 *   that it's legal
 */
int number_to_room_num( int array_index )
{
  if( ( array_index < 0 ) || ( array_index > 91 ) )
    return 2;

  return standard_room_vnums[array_index];
}

/*
 * Attempts to intellignetly determineif two adjecent rooms should
 *  be linked with an exit, and if so, what kind.
 *
 * This fn _depends_ on standard_room_vnums[] to make decisions.
 */
int exit_lookup( int vnum1, int vnum2 )
{
  int sect1, sect2;
  ROOM_INDEX_DATA *rm1, *rm2;
  int exit_flag;

  rm1 = get_room_index( vnum1 );
  rm2 = get_room_index( vnum2 );

  if( ( !rm1 ) || ( !rm2 ) )
  {
    bug( "bad room index in exit_lookup!\n\r", 0 );
    return ( 0 );
  }
  exit_flag = 0;

  sect1 = rm1->sector_type;
  sect2 = rm2->sector_type;


  if( rm1 == rm2 )  /* adjacent rooms with same std_rm_vnum */
    return 0; /* assume they're simply linked */

  if( ( vnum1 == 9589 ) || ( vnum2 == 9589 ) )
    return 0; /* if 'none,' no assumptions */



  if( ( sect1 == SECT_INSIDE ) && ( sect2 != SECT_INSIDE ) )
  {
    switch ( vnum1 )
    {
      case 9500: /* hut  *//* these are almost always */
      case 9501: /* tent *//* single-roomed buildings */
      case 9502: /* hovel *//* so let them be open on */
      case 9504: /* shack *//* all sides -- user fixes */
      case 9505: /* cabin */
      case 9506: /* homestd */
      case 9510: /* guardhse */
      case 9511: /* temple */
      case 9512: /* store */
      case 9515: /* stable */
      case 9516: /* tavern */
        return 0;
      case 9542: /* foyer *//* these are always closed drs 
                   */
      case 9547: /* entrance */
        return 3;
      default:
        exit_flag = -1;
    }
  }
  if( ( sect2 == SECT_INSIDE ) && ( sect1 != SECT_INSIDE ) )
  {
    switch ( vnum2 )
    {
      case 9500: /* hut  *//* these are almost always */
      case 9501: /* tent *//* single-roomed buildings */
      case 9502: /* hovel *//* so let them be open on */
      case 9504: /* shack *//* all sides -- user fixes */
      case 9505: /* cabin */
      case 9506: /* homestd */
      case 9510: /* guardhse */
      case 9511: /* temple */
      case 9512: /* store */
      case 9515: /* stable */
      case 9516: /* tavern */
        return 0;
      case 9542: /* foyer *//* these are always closed drs 
                   */
      case 9547: /* entrance */
        return 3;
      default:
        exit_flag = -1;
    }
  }
  /*
   * Can look at these cases again
   * 
   */
  if( ( sect1 == SECT_CITY ) && ( sect2 != SECT_CITY ) )
  {
    switch ( vnum1 )
    {
      case 9511: /* temple *//* these are always open */
      case 9512: /* store */
      case 9516: /* tavern */
        return 0;
      case 9552: /* gate *//* these are always closed drs 
                   */
      case 9547:
        return 3;
      default:
        exit_flag = -1;
    }
  }
  if( ( sect2 == SECT_CITY ) && ( sect1 != SECT_CITY ) )
  {
    switch ( vnum2 )
    {
      case 9511: /* temple *//* these are always open */
      case 9512: /* store */
      case 9516: /* tavern */
        return 0;
      case 9552: /* gate *//* these are always closed drs 
                   */
      case 9547:
        return 3;
      default:
        exit_flag = -1;
    }
  }
  return exit_flag;
}

/*
 *  Given a character 'code' in a map, returns the location
 *   of the corresponding room vnum in the constant array
 *   standard_room_vnums.  If 'code' is illegal, it returns
 *   -1.  To lookup the corresponding room vnum, call
 *   number_to_room_num on value returned here.
 */
int char_to_number( char code )
{

  switch ( code )
  {
    case 'a':
      return 0;
    case 'b':
      return 1;
    case 'c':
      return 2;
    case 'd':
      return 3;
    case 'e':
      return 4;
    case 'f':
      return 5;
    case 'g':
      return 6;
    case 'h':
      return 7;
    case 'i':
      return 8;
    case 'j':
      return 9;
    case 'k':
      return 10;
    case 'l':
      return 11;
    case 'm':
      return 12;
    case 'n':
      return 13;
    case 'o':
      return 14;
    case 'p':
      return 15;
    case 'q':
      return 16;
    case 'r':
      return 17;
    case 's':
      return 18;
    case 't':
      return 19;
    case 'u':
      return 20;
    case 'v':
      return 21;
    case 'w':
      return 22;
    case 'x':
      return 23;
    case 'y':
      return 24;
    case 'z':
      return 25;
    case 'A':
      return 26;
    case 'B':
      return 27;
    case 'C':
      return 28;
    case 'D':
      return 29;
    case 'E':
      return 30;
    case 'F':
      return 31;
    case 'G':
      return 32;
    case 'H':
      return 33;
    case 'I':
      return 34;
    case 'J':
      return 35;
    case 'K':
      return 36;
    case 'L':
      return 37;
    case 'M':
      return 38;
    case 'N':
      return 39;
    case 'O':
      return 40;
    case 'P':
      return 41;
    case 'Q':
      return 42;
    case 'R':
      return 43;
    case 'S':
      return 44;
    case 'T':
      return 45;
    case 'U':
      return 46;
    case 'V':
      return 47;
    case 'W':
      return 48;
    case 'X':
      return 49;
    case 'Y':
      return 50;
    case 'Z':
      return 51;
    case '0':
      return 52;
    case '1':
      return 53;
    case '2':
      return 54;
    case '3':
      return 55;
    case '4':
      return 56;
    case '5':
      return 57;
    case '6':
      return 58;
    case '7':
      return 59;
    case '8':
      return 60;
    case '9':
      return 61;
    case '!':
      return 62;
    case '@':
      return 63;
    case '#':
      return 64;
    case '$':
      return 65;
    case '%':
      return 66;
    case '^':
      return 67;
    case '&':
      return 68;
    case '*':
      return 69;
    case '(':
      return 70;
    case ')':
      return 71;

    case '-':
      return 72;
    case '_':
      return 73;
    case '+':
      return 74;
    case '=':
      return 75;
    case '|':
      return 76;

      /*
       * case '\\': return 77; 
       */
      /*
       * case '~': return 78; 
       */
    case '`':
      return 79;
    case '{':
      return 80;
    case '[':
      return 81;

    case '}':
      return 82;
    case ']':
      return 83;
    case ':':
      return 84;
    case '"':
      return 85;
    case '\'':
      return 86;

    case '<':
      return 87;
    case ',':
      return 88;
    case '>':
      return 89;
    case '.':
      return 90;
    case '?':
      return 91;
    default:
      return -1;
  }
}

#undef MID
#undef MD
#undef RID
#undef CD
#undef EDD
#undef OD
#undef OID
#undef XD