/****************************************************************************
 * [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 


