toc/
toc/account/a/
toc/area/backup/
toc/area/imc/
toc/caste/
toc/caste/backup/
toc/clans/
toc/classes/
toc/crash/
toc/gods/
toc/guilds/
toc/lname/s/
toc/maps/backup/
toc/player/a/
toc/src/
toc/system/backup/
toc/tableprog/
/****************************************************************************
 * [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[MSL];

   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[MSL];
   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[MSL];
   char buf[MSL];
   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[MSL];
   char arg[MIL];
   char arg1[MIL];
   char arg2[MIL];
   char arg3[MIL]; /* growl */
   char arg4[MIL]; /* 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);
      set_editor_desc(ch, "A map description");
      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[MSL];
   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