LOP/
LOP/area/
LOP/boards/
LOP/channels/
LOP/clans/
LOP/color/
LOP/councils/
LOP/deity/
LOP/src/specials/
/*****************************************************************************
 * DikuMUD (C) 1990, 1991 by:                                                *
 *   Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen,   *
 *   and Katja Nyboe.                                                        *
 *---------------------------------------------------------------------------*
 * MERC 2.1 (C) 1992, 1993 by:                                               *
 *   Michael Chastain, Michael Quan, and Mitchell Tse.                       *
 *---------------------------------------------------------------------------*
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider.                    *
 *   Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
 *         gorog, Grishnakh, Nivek, Tricops, and Fireblade.                  *
 *---------------------------------------------------------------------------*
 * SMAUG 1.7 FUSS by: Samson and others of the SMAUG community.              *
 *                    Their contributions are greatly appreciated.           *
 *---------------------------------------------------------------------------*
 * LoP (C) 2006, 2007, 2008, 2009 by: the LoP team.                          *
 *---------------------------------------------------------------------------*
 * 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.                                *
 *****************************************************************************/

#include <stdio.h>
#include "h/mud.h"

typedef struct map_index_data MAP_INDEX_DATA;   /* maps */
struct map_index_data
{
   MAP_INDEX_DATA *next;
   int vnum;                  /* vnum of the map */
   int map_of_vnums[49][81];  /* room vnums aranged as a map */
};
MAP_INDEX_DATA *first_map; /* maps */

MAP_INDEX_DATA *get_map_index( int vnum );
void init_maps( void );

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

/* Local function prototypes */
MAP_INDEX_DATA *make_new_map_index( int vnum );
void map_to_rooms( CHAR_DATA *ch, MAP_INDEX_DATA *m_index );
void map_stats( CHAR_DATA *ch, int *rooms, int *rows, int *cols );
int num_rooms_avail( CHAR_DATA *ch );

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

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

   CREATE( map_index, MAP_INDEX_DATA, 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;
}

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 )'+';
   snprintf( buf, sizeof( buf ), "%d", i );
   return ( buf[0] );
}

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

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

void map_stats( CHAR_DATA *ch, int *rooms, int *rows, int *cols )
{
   int row, col, n;
   int leftmost, rightmost;
   char *l, c;

   if( !ch->pnote )
   {
      bug( "%s: ch->pnote is NULL!", __FUNCTION__ );
      return;
   }
   n = 0;
   row = col = 0;
   leftmost = rightmost = 0;
   l = ch->pnote->text;
   do
   {
      c = l[0];
      switch( c )
      {
         case '\r':
            break;
         case '\n':
            col = 0;
            row++;
            break;
         case ' ':
            col++;
            break;
      }
      if( c == 'I' || c == 'C' || c == 'f' || c == 'F' || c == 'H' || c == 'M' || c == 's' || c == 'S'
      || c == 'A' || c == 'D' || c == 'O' || c == 'u' || c == 'U' || c == 'L' || c == 'W' || c == '#' )
      {
         /* This is a room */
         n++;

         /* This will later handle a letter etc */
         col++;

         if( col < leftmost )
            leftmost = col;
         if( col > rightmost )
            rightmost = col;
      }
      if( c == ' ' || c == '-' || c == '|' || c == '=' || c == '\\' || c == '/' || c == '^' || c == ':' || c == 'X' || c == '+' )
         col++;
      l++;
   }
   while( c != '\0' );
   *cols = ( rightmost - leftmost );
   *rows = row;   /* [sic.] */
   *rooms = n;
}

void do_mapout( CHAR_DATA *ch, char *argument )
{
   char arg[MIL];
   OBJ_DATA *map_obj;   /* an obj made with map as an ed */
   OBJ_INDEX_DATA *map_obj_index;   /*    obj_index for previous     */
   EXTRA_DESCR_DATA *ed;   /*    the ed for it to go in     */
   int rooms, rows, cols, avail_rooms;

   if( !ch )
   {
      bug( "%s: null ch", __FUNCTION__ );
      return;
   }
   if( is_npc( ch ) )
   {
      send_to_char( "Not for mobs.\r\n", ch );
      return;
   }
   if( !ch->desc )
   {
      bug( "%s: no descriptor", __FUNCTION__ );
      return;
   }
   switch( ch->substate )
   {
      default:
         break;
      case SUB_MAP_EDIT:
         if( ch->dest_buf != ch->pnote )
            bug( "%s: sub_writing_map: ch->dest_buf != ch->pnote", __FUNCTION__ );
         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 );

   if( !str_cmp( arg, "stat" ) )
   {
      if( !ch->pnote )
      {
         send_to_char( "You have no map in progress.\r\n", ch );
         return;
      }
      map_stats( ch, &rooms, &rows, &cols );
      ch_printf( ch, "Map represents %d rooms, %d rows, and %d columns\r\n", rooms, rows, cols );
      avail_rooms = num_rooms_avail( ch );
      ch_printf( ch, "You currently have %d unused rooms.\r\n", avail_rooms );
      act( AT_ACTION, "$n glances at an etherial map.", ch, NULL, NULL, TO_ROOM );
      return;
   }

   if( !str_cmp( arg, "write" ) )
   {
      note_attach( ch );
      ch->substate = SUB_MAP_EDIT;
      ch->dest_buf = ch->pnote;
      start_editing( ch, ch->pnote->text );
      return;
   }
   if( !str_cmp( arg, "clear" ) )
   {
      if( !ch->pnote )
      {
         send_to_char( "You have no map in progress\r\n", ch );
         return;
      }
      STRFREE( ch->pnote->text );
      STRFREE( ch->pnote->subject );
      STRFREE( ch->pnote->to_list );
      STRFREE( ch->pnote->sender );
      DISPOSE( ch->pnote );
      ch->pnote = NULL;
      send_to_char( "Map cleared.\r\n", ch );
      return;
   }
   if( !str_cmp( arg, "show" ) )
   {
      if( !ch->pnote )
      {
         send_to_char( "You have no map in progress.\r\n", ch );
         return;
      }
      send_to_char( ch->pnote->text, ch );
      do_mapout( ch, (char *)"stat" );
      return;
   }
   if( !str_cmp( arg, "create" ) )
   {
      if( !ch->pnote )
      {
         send_to_char( "You have no map in progress.\r\n", 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!\r\n", 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 */

      if( ( map_obj_index = get_obj_index( OBJ_VNUM_MAP ) ) )
      {
         if( !( map_obj = create_object( map_obj_index, 0 ) ) )
         {
            ch_printf( ch, "Couldn't give you a map. create_object failed for object vnum %d\r\n", OBJ_VNUM_MAP );
            return;
         }
         ed = SetOExtra( map_obj, (char *)"runes map scrawls" );
         STRSET( ed->description, ch->pnote->text );
         obj_to_char( map_obj, ch );
      }
      else
      {
         ch_printf( ch, "Couldn't give you a map. Need object vnum %d\r\n", OBJ_VNUM_MAP );
         return;
      }

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

int add_new_room_to_map( CHAR_DATA *ch, char code )
{
   int i, start, end;
   ROOM_INDEX_DATA *location;

   /* Get an unused room to copy to */
   if( ch->pcdata->area )
   {
      start = ch->pcdata->area->low_vnum;
      end = ch->pcdata->area->hi_vnum;
   }
   else
   {
      start = ch->pcdata->range_lo;
      end = ch->pcdata->range_hi;
   }
   for( i = start; i <= end; i++ )
   {
      if( !get_room_index( i ) )
      {
         if( !( location = make_room( i, ch->pcdata->area ) ) )
         {
            bug( "%s: make_room failed", __FUNCTION__ );
            return -1;
         }
         location->area = ch->pcdata->area;
         location->name = STRALLOC( "Floating in a Void" );
         location->description = NULL;
         xCLEAR_BITS( location->room_flags );
         location->light = 0;
         if( code == 'I' )
            location->sector_type = SECT_INSIDE;
         else if( code == 'C' )
            location->sector_type = SECT_CITY;
         else if( code == 'f' )
            location->sector_type = SECT_FIELD;
         else if( code == 'F' )
            location->sector_type = SECT_FOREST;
         else if( code == 'H' )
            location->sector_type = SECT_HILLS;
         else if( code == 'M' )
            location->sector_type = SECT_MOUNTAIN;
         else if( code == 's' )
            location->sector_type = SECT_WATER_SWIM;
         else if( code == 'S' )
            location->sector_type = SECT_WATER_NOSWIM;
         else if( code == 'A' )
            location->sector_type = SECT_AIR;
         else if( code == 'D' )
            location->sector_type = SECT_DESERT;
         else if( code == 'O' )
            location->sector_type = SECT_OCEANFLOOR;
         else if( code == 'u' )
            location->sector_type = SECT_UNDERGROUND;
         else if( code == 'U' )
            location->sector_type = SECT_UNDERWATER;
         else if( code == 'L' )
            location->sector_type = SECT_LAVA;
         else if( code == 'W' )
            location->sector_type = SECT_SWAMP;
         else
            location->sector_type = SECT_DUNNO;
         return i;
      }
   }
   send_to_char( "No available room in your vnums!", ch );
   return -1;
}

int num_rooms_avail( CHAR_DATA *ch )
{
   int i, n = 0, start = 0, end = 0;

   if( !ch || !ch->pcdata )
      return 0;
   if( ch->pcdata->area )
   {
      start = ch->pcdata->area->low_vnum;
      end = ch->pcdata->area->hi_vnum;
   }
   else
   {
      start = ch->pcdata->range_lo;
      end = ch->pcdata->range_hi;
   }
   if( !start && !end )
      return 0;
   for( i = start; i <= end; 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( CHAR_DATA *ch, MAP_INDEX_DATA *m_index )
{
   struct map_stuff map[49][78]; /* size of edit buffer */
   int row, col, i, n, x, y, tvnum, leftmost, rightmost;
   int newx, newy, start, end;
   char *l, c;
   ROOM_INDEX_DATA *newrm;
   MAP_INDEX_DATA *map_index = NULL, *tmp;
   EXIT_DATA *xit;   /* these are for exits */

   if( !ch->pnote )
   {
      bug( "%s: ch->pnote is NULL!", __FUNCTION__ );
      return;
   }

   n = 0;
   row = col = 0;
   leftmost = rightmost = 0;

   if( ch->pcdata->area )
   {
      start = ch->pcdata->area->low_vnum;
      end = ch->pcdata->area->hi_vnum;
   }
   else
   {
      start = ch->pcdata->range_lo;
      end = ch->pcdata->range_hi;
   }

   /*
    * Check to make sure map_index exists.  
    * If not, then make a new one.
    */
   if( !m_index )
   {  /* Make a new vnum */
      for( i = start; i <= end; i++ )
      {
         if( !( tmp = get_map_index( i ) ) )
         {
            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!\r\n", ch );
      bug( "%s: Couldn't find or make a map_index.", __FUNCTION__ );
      return;
   }

   for( x = 0; x < 49; x++ )
   {
      for( y = 0; y < 78; y++ )
      {
         map[x][y].vnum = 0;
         map[x][y].exits = 0;
         map[x][y].index = 0;
      }
   }

   l = ch->pnote->text;
   do
   {
      c = l[0];
      switch( c )
      {
         case '\r':
            break;
         case '\n':
            col = 0;
            row++;
            break;
      }
      if( c != ' ' && c != '-' && c != '|' && c != '=' && c != '\\' && c != '/' && c != '^'
      && c != ':' && c != '^' && c != 'X' && c != '+' && c != 'W' && c != 'L' && c != 'U'
      && c != 'u' && c != 'O' && c != 'D' && c != 'A' && c != 'S' && c != 's' && c != 'M'
      && c != 'H' && c != 'F' && c != 'f' && c != 'C' && c != 'I' && c != '#' )
      {
         l++;
         continue;
      }
      if( c == 'W' || c == 'L' || c == 'U' || c == 'u' || c == 'O' || c == 'D' || c == 'A'
      || c == 'S' || c == 's' || c == 'M' || c == 'H' || c == 'F' || c == 'f' || c == 'C'
      || c == 'I' || c == '#' )
      {
         n++;

         /* Actual room info */
         map[row][col].vnum = add_new_room_to_map( ch, c );
         map_index->map_of_vnums[row][col] = map[row][col].vnum;
      }
      else
      {
         map_index->map_of_vnums[row][col] = 0;
         map[row][col].vnum = 0;
         map[row][col].exits = 0;
      }
      map[row][col].code = c;
      col++;
      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;

         /* Continue if no newrm */
         if( !( newrm = get_room_index( map[y][x].vnum ) ) )
            continue;

         /* Check up */
         if( y > 1 )
         {
            newx = x;
            newy = y;
            newy--;
            while( newy >= 0 && ( map[newy][x].code == '^' ) )
               newy--;
            if( map[y][x].vnum != map[newy][x].vnum && ( tvnum = map[newy][x].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_UP );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check down */
         if( y < 48 )
         {
            newx = x;
            newy = y;
            newy++;
            while( newy <= 48 && ( map[newy][x].code == '^' ) )
               newy++;
            if( map[y][x].vnum != map[newy][x].vnum && ( tvnum = map[newy][x].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_DOWN );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check north */
         if( y > 1 )
         {
            newx = x;
            newy = y;
            newy--;
            while( newy >= 0 && ( map[newy][x].code == '|' || map[newy][x].code == ':' || map[newy][x].code == '='
            || map[newy][x].code == '+' ) )
               newy--;
            if( map[y][x].vnum != map[newy][x].vnum && ( tvnum = map[newy][x].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_NORTH );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               if( map[newy + 1][x].code == ':' || map[newy + 1][x].code == '=' )
               {
                  xSET_BIT( xit->exit_info, EX_ISDOOR );
                  xSET_BIT( xit->exit_info, EX_CLOSED );
               }
               else
                  xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check south */
         if( y < 48 )
         {
            newx = x;
            newy = y;
            newy++;
            while( newy <= 48 && ( map[newy][x].code == '|' || map[newy][x].code == ':' || map[newy][x].code == '='
            || map[newy][x].code == '+' ) )
               newy++;
            if( map[y][x].vnum != map[newy][x].vnum && ( tvnum = map[newy][x].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_SOUTH );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               if( map[newy - 1][x].code == ':' || map[newy - 1][x].code == '=' )
               {
                  xSET_BIT( xit->exit_info, EX_ISDOOR );
                  xSET_BIT( xit->exit_info, EX_CLOSED );
               }
               else
                  xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check east */
         if( x < 79 )
         {
            newx = x;
            newy = y;
            newx++;
            while( newx <= 79 && ( map[y][newx].code == '-' || map[y][newx].code == ':' || map[y][newx].code == '='
            || map[y][newx].code == '+' ) )
               newx++;
            if( map[y][x].vnum != map[y][newx].vnum && ( tvnum = map[y][newx].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_EAST );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               if( map[y][newx - 2].code == ':' || map[y][newx - 2].code == '=' )
               {
                  xSET_BIT( xit->exit_info, EX_ISDOOR );
                  xSET_BIT( xit->exit_info, EX_CLOSED );
               }
               else
                  xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check west */
         if( x > 1 )
         {
            newx = x;
            newy = y;
            newx--;
            while( newx >= 0 && ( map[y][newx].code == '-' || map[y][newx].code == ':' || map[y][newx].code == '='
            || map[y][newx].code == '+' ) )
               newx--;
            if( map[y][x].vnum != map[y][newx].vnum && ( tvnum = map[y][newx].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_WEST );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               if( map[y][newx + 2].code == ':' || map[y][newx + 2].code == '=' )
               {
                  xSET_BIT( xit->exit_info, EX_ISDOOR );
                  xSET_BIT( xit->exit_info, EX_CLOSED );
               }
               else
                  xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check southeast */
         if( y < 48 && x < 79 )
         {
            newx = x;
            newy = y;
            newx++;
            newy++;
            while( newx <= 79 && newy <= 48 
            && ( map[newy][newx].code == '\\' || map[newy][newx].code == ':'
            || map[newy][newx].code == '=' || map[newy][newx].code == 'X' ) )
            {
               newx++;
               newy++;
            }
            if( map[y][x].vnum != map[newy][newx].vnum && ( tvnum = map[newy][newx].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_SOUTHEAST );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check northeast */
         if( y > 1 && x < 79 )
         {
            newx = x;
            newy = y;
            newx++;
            newy--;
            while( newx >= 0 && newy <= 48
            && ( map[newy][newx].code == '/' || map[newy][newx].code == ':'
            || map[newy][newx].code == '=' || map[newy][newx].code == 'X' ) )
            {
               newx++;
               newy--;
            }
            if( map[y][x].vnum != map[newy][newx].vnum && ( tvnum = map[newy][newx].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_NORTHEAST );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check northwest */
         if( y > 1 && x > 1 )
         {
            newx = x;
            newy = y;
            newx--;
            newy--;
            while( newx >= 0 && newy >= 0
            && ( map[newy][newx].code == '\\' || map[newy][newx].code == ':'
            || map[newy][newx].code == '=' || map[newy][newx].code == 'X' ) )
            {
               newx--;
               newy--;
            }
            if( map[y][x].vnum != map[newy][newx].vnum && ( tvnum = map[newy][newx].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_NORTHWEST );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               xCLEAR_BITS( xit->exit_info );
            }
         }

         /* Check southwest */
         if( y < 48 && x > 1 )
         {
            newx = x;
            newy = y;
            newx--;
            newy++;
            while( newx >= 0 && newy <= 48
            && ( map[newy][newx].code == '/' || map[newy][newx].code == ':'
            || map[newy][newx].code == '=' || map[newy][newx].code == 'X' ) )
            {
               newx--;
               newy++;
            }
            if( map[y][x].vnum != map[newy][newx].vnum && ( tvnum = map[newy][newx].vnum ) != 0 )
            {
               xit = make_exit( newrm, get_room_index( tvnum ), DIR_SOUTHWEST );
               xit->keyword = NULL;
               xit->description = NULL;
               xit->key = -1;
               xCLEAR_BITS( xit->exit_info );
            }
         }
      }
   }
}