/****************************************************************************
* [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 <stdio.h>
#include "mud.h"
struct map_data /* contains per-room data */
{
int vnum; /* which map this room belongs to */
int x; /* horizontal coordinate */
int y; /* vertical coordinate */
char entry; /* code that shows up on map */
};
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 *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 );
int number_to_room_num( int array_index );
int exit_lookup( int vnum1, int vnum2 );
void draw_map( CHAR_DATA * ch, ROOM_INDEX_DATA * rm, int flag, int mode );
char *you_are_here( int row, int col, char *map );
/* Local Variables & Structs */
char text_map[MAX_STRING_LENGTH];
extern MAP_INDEX_DATA *first_map; /* should be global */
struct map_stuff
{
int vnum;
int proto_vnum;
int exits;
int index;
char code;
};
/* Lets make it check the map and make sure it uses [ ] instead of [] */
char *check_map( char *str )
{
static char newstr[MAX_STRING_LENGTH];
int i, j;
for( i = j = 0; str[i] != '\0'; i++ )
{
if( str[i - 1] == '[' && str[i] == ']' )
{
newstr[j++] = ' ';
newstr[j++] = str[i];
}
else
newstr[j++] = str[i];
}
newstr[j] = '\0';
return newstr;
}
/* 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[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 )'+';
snprintf( buf, MAX_STRING_LENGTH, "%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", "map_stats: ch->pnote == NULL!" );
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( c == '[' )
{
/*
* Increase col
*/
col++;
/*
* This is a room since in [ ]
*/
n++;
/*
* This will later handle a letter etc
*/
col++;
/*
* Make sure it has a closeing ]
*/
if( c == ']' )
col++;
if( col < leftmost )
leftmost = col;
if( col > rightmost )
rightmost = col;
}
if( ( c == ' ' || c == '-' || c == '|' || c == '=' || c == '\\' || c == '/' || c == '^' || c == ':' ) )
col++;
l++;
}
while( c != '\0' );
*cols = ( rightmost - leftmost );
*rows = row; /* [sic.] */
*rooms = n;
return;
}
void do_mapout( CHAR_DATA * ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
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", "do_mapout: null ch" );
return;
}
if( IS_NPC( ch ) )
{
send_to_char( "Not in mobs.\r\n", ch );
return;
}
if( !ch->desc )
{
bug( "%s", "do_mapout: no descriptor" );
return;
}
switch ( ch->substate )
{
default:
break;
case SUB_WRITING_NOTE:
if( ch->dest_buf != ch->pnote )
bug( "%s", "do_mapout: sub_writing_map: ch->dest_buf != ch->pnote" );
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.\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_WRITING_NOTE;
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->date );
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, "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 */
map_obj_index = get_obj_index( 91 );
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\r\n", ch );
return;
}
do_mapout( ch, "clear" );
send_to_char( "Ok.\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 );
return;
}
int add_new_room_to_map( CHAR_DATA * ch, char code )
{
int i;
ROOM_INDEX_DATA *location;
/*
* 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 )
{
if( !( location = make_room( i, ch->pcdata->area ) ) )
{
bug( "%s: make_room failed", __FUNCTION__ );
return -1;
}
/*
* Clones current room (quietly)
*/
location->area = ch->pcdata->area;
location->name = STRALLOC( "Newly Created Room" );
location->description = STRALLOC( "Newly Created Room\r\n" );
location->room_flags = ROOM_PROTOTYPE;
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 == 'U' )
location->sector_type = SECT_DUNNO;
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
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;
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( CHAR_DATA * ch, MAP_INDEX_DATA * m_index )
{
struct map_stuff map[49][78]; /* size of edit buffer */
char *newmap;
int row, col, i, n, x, y, tvnum, proto_vnum = 0, leftmost, rightmost;
int newx, newy;
char *l, c;
ROOM_INDEX_DATA *newrm;
MAP_INDEX_DATA *map_index = NULL, *tmp;
EXIT_DATA *xit; /* these are for exits */
bool getroomnext = FALSE;
if( !ch->pnote )
{
bug( "%s", "map_to_rooms: ch->pnote==NULL!" );
return;
}
/*
* Make sure format is right
*/
newmap = check_map( ch->pnote->text );
STRFREE( ch->pnote->text );
ch->pnote->text = STRALLOC( newmap );
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!\r\n", ch );
bug( "%s", "map_to_rooms: Couldn't find or make a map_index\r\n" );
/*
* do something. return failed or somesuch
*/
return;
}
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;
}
if( c != ' ' && c != '-' && c != '|' && c != '=' && c != '\\' && c != '/' && c != '^'
&& c != ':' && c != '[' && c != ']' && c != '^' && !getroomnext )
{
l++;
continue;
}
if( getroomnext )
{
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;
map[row][col].proto_vnum = proto_vnum;
getroomnext = FALSE;
}
else
{
map_index->map_of_vnums[row][col] = 0;
map[row][col].vnum = 0;
map[row][col].exits = 0;
}
map[row][col].code = c;
/*
* Handle rooms
*/
if( c == '[' )
getroomnext = TRUE;
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;
newrm = get_room_index( map[y][x].vnum );
/*
* Continue if no newrm
*/
if( !newrm )
continue;
/*
* Check up
*/
if( y > 1 )
{
newx = x;
newy = y;
newy--;
while( newy >= 0 && ( map[newy][x].code == '^' ) )
newy--;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[newy][x].vnum )
break;
if( ( tvnum = map[newy][x].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_UP );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
xit->exit_info = 0;
}
}
/*
* Check down
*/
if( y < 48 )
{
newx = x;
newy = y;
newy++;
while( newy <= 48 && ( map[newy][x].code == '^' ) )
newy++;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[newy][x].vnum )
break;
if( ( tvnum = map[newy][x].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_DOWN );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
xit->exit_info = 0;
}
}
/*
* 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 == '=' ) )
newy--;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[newy][x].vnum )
break;
if( ( tvnum = map[newy][x].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_NORTH );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
if( map[newy + 1][x].code == ':' || map[newy + 1][x].code == '=' )
{
SET_BIT( xit->exit_info, EX_ISDOOR );
SET_BIT( xit->exit_info, EX_CLOSED );
}
else
xit->exit_info = 0;
}
}
/*
* 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 == '=' ) )
newy++;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[newy][x].vnum )
break;
if( ( tvnum = map[newy][x].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_SOUTH );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
if( map[newy - 1][x].code == ':' || map[newy - 1][x].code == '=' )
{
SET_BIT( xit->exit_info, EX_ISDOOR );
SET_BIT( xit->exit_info, EX_CLOSED );
}
else
xit->exit_info = 0;
}
}
/*
* 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 == '[' || map[y][newx].code == ']' ) )
newx++;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[y][newx].vnum )
break;
if( ( tvnum = map[y][newx].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_EAST );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
if( map[y][newx - 2].code == ':' || map[y][newx - 2].code == '=' )
{
SET_BIT( xit->exit_info, EX_ISDOOR );
SET_BIT( xit->exit_info, EX_CLOSED );
}
else
xit->exit_info = 0;
}
}
/*
* 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 == '[' || map[y][newx].code == ']' ) )
newx--;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[y][newx].vnum )
break;
if( ( tvnum = map[y][newx].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_WEST );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
if( map[y][newx + 2].code == ':' || map[y][newx + 2].code == '=' )
{
SET_BIT( xit->exit_info, EX_ISDOOR );
SET_BIT( xit->exit_info, EX_CLOSED );
}
else
xit->exit_info = 0;
}
}
/*
* Check southeast
*/
if( y < 48 && x < 79 )
{
newx = x;
newy = y;
newx += 2;
newy++;
while( newx <= 79 && newy <= 48 && ( map[newy][newx].code == '\\' || map[newy][newx].code == ':'
|| map[newy][newx].code == '=' ) )
{
newx++;
newy++;
}
if( map[newy][newx].code == '[' )
newx++;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[newy][newx].vnum )
break;
if( ( tvnum = map[newy][newx].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_SOUTHEAST );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
xit->exit_info = 0;
}
}
/*
* Check northeast
*/
if( y > 1 && x < 79 )
{
newx = x;
newy = y;
newx += 2;
newy--;
while( newx >= 0 && newy <= 48 && ( map[newy][newx].code == '/' || map[newy][newx].code == ':'
|| map[newy][newx].code == '=' ) )
{
newx++;
newy--;
}
if( map[newy][newx].code == '[' )
newx++;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[newy][newx].vnum )
break;
if( ( tvnum = map[newy][newx].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_NORTHEAST );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
xit->exit_info = 0;
}
}
/*
* Check northwest
*/
if( y > 1 && x > 1 )
{
newx = x;
newy = y;
newx -= 2;
newy--;
while( newx >= 0 && newy >= 0 && ( map[newy][newx].code == '\\' || map[newy][newx].code == ':'
|| map[newy][newx].code == '=' ) )
{
newx--;
newy--;
}
if( map[newy][newx].code == ']' )
newx--;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[newy][newx].vnum )
break;
if( ( tvnum = map[newy][newx].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_NORTHWEST );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
xit->exit_info = 0;
}
}
/*
* Check southwest
*/
if( y < 48 && x > 1 )
{
newx = x;
newy = y;
newx -= 2;
newy++;
while( newx >= 0 && newy <= 48 && ( map[newy][newx].code == '/' || map[newy][newx].code == ':'
|| map[newy][newx].code == '=' ) )
{
newx--;
newy++;
}
if( map[newy][newx].code == ']' )
newx--;
/*
* dont link it to itself
*/
if( map[y][x].vnum == map[newy][newx].vnum )
break;
if( ( tvnum = map[newy][newx].vnum ) != 0 )
{
xit = make_exit( newrm, get_room_index( tvnum ), DIR_SOUTHWEST );
xit->keyword = STRALLOC( "" );
xit->description = STRALLOC( "" );
xit->key = -1;
xit->exit_info = 0;
}
}
}
}
}