btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * $Id: mech.maps.c,v 1.7 2005/08/03 21:40:54 av1-op Exp $
 *
 * Author: Markus Stenberg <fingon@iki.fi>
 *
 *  Copyright (c) 1996 Markus Stenberg
 *  Copyright (c) 1998-2002 Thomas Wouters
 *  Copyright (c) 2000-2002 Cord Awtry
 *  Copyright (c) 1999-2005 Kevin Stevens
 *       All rights reserved
 *
 * Last modified: Fri Sep 18 13:09:09 1998 fingon
 *
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/file.h>

#include "mech.h"
#include "create.h"
#include "mech.events.h"
#include "map.los.h"
#include "p.mech.utils.h"
#include "p.mech.los.h"
#include "p.eject.h"
#include "p.mech.restrict.h"
#include "p.mech.maps.h"
#include "p.mech.notify.h"
#include "p.ds.bay.h"
#include "p.bsuit.h"
#include "p.mech.utils.h"
#include "autopilot.h"

void mech_findcenter(dbref player, void *data, char *buffer)
{
    MECH *mech = (MECH *) data;
    float fx, fy;
    int x, y;

    cch(MECH_USUAL);
    x = MechX(mech);
    y = MechY(mech);
    MapCoordToRealCoord(x, y, &fx, &fy);
    notify(player, tprintf("Current hex: (%d,%d,%d)\tRange to center: %.2f\t"
			   "Bearing to center: %d", x, y, MechZ(mech),
			   FindHexRange(fx, fy, MechFX(mech), MechFY(mech)),
			   FindBearing(MechFX(mech), MechFY(mech), fx, fy)));
}

static int parse_tacargs(dbref player, MECH *mech, char ** args, int argc,
			 int maxrange, short * x, short * y)
{
    int bearing;
    float range, fx, fy;
    MECH * tempMech;
    MAP * map;

    switch (argc) {
    case 2:
	bearing = atoi(args[0]);
	range = atof(args[1]);
	DOCHECK0(!MechIsObservator(mech) &&
	    abs((int) range) > maxrange,
	    "Those coordinates are out of sensor range!");
	FindXY(MechFX(mech), MechFY(mech), bearing, range, &fx, &fy);
	RealCoordToMapCoord(x, y, fx, fy);
	return 1;
    case 1:
	map = getMap(mech->mapindex);
	tempMech = getMech(FindMechOnMap(map, args[0]));
	DOCHECK0(!tempMech, "No such target.");
	range = FlMechRange(mech_map, mech, tempMech);
	DOCHECK0(!InLineOfSight(mech, tempMech, MechX(tempMech),
				MechY(tempMech), range), "No such target.");
	DOCHECK0(abs((int) range) > maxrange,
		 "Target is out of scanner range.");
	*x = MechX(tempMech);
	*y = MechY(tempMech);
	return 1;
    case 0:
	*x = MechX(mech);
	*y = MechY(mech);
	return 1;
    default:
	notify(player, "Invalid number of parameters!");
	return 0;
    }
}

const char *GetTerrainName_base(int t)
{
    switch (t) {
    case GRASSLAND:
    case '_':
	return "Grassland";
    case HEAVY_FOREST:
	return "Heavy Forest";
    case LIGHT_FOREST:
	return "Light Forest";
    case ICE:
	return "Ice";
    case BRIDGE:
	return "Bridge";
    case HIGHWATER:
    case WATER:
	return "Water";
    case ROUGH:
	return "Rough";
    case MOUNTAINS:
	return "Mountains";
    case ROAD:
	return "Road";
    case BUILDING:
	return "Building";
    case FIRE:
	return "Fire";
    case SMOKE:
	return "Smoke";
    case WALL:
	return "Wall";
    }
    return "Unknown";
}

const char *GetTerrainName(MAP * map, int x, int y)
{
    return GetTerrainName_base(GetTerrain(map, x, y));
}

/* Player-customizable colors */

enum { SWATER_IDX, DWATER_IDX, BUILDING_IDX, ROAD_IDX, ROUGH_IDX, MOUNTAIN_IDX,
       FIRE_IDX, ICE_IDX, WALL_IDX, SNOW_IDX, SMOKE_IDX, LWOOD_IDX, HWOOD_IDX,
       UNKNOWN_IDX, CLIFF_IDX, SELF_IDX, FRIEND_IDX, ENEMY_IDX, DS_IDX,
       GOODLZ_IDX, BADLZ_IDX, NUM_COLOR_IDX
};

/* Default colour string is "BbWnYyRWWWXGgbRHYRn" */
/* internal rep has H instead of h and \0 instead of n */

#define DEFAULT_COLOR_STRING "BbWXYyRWWWXGgbRhYRnGR"
#define DEFAULT_COLOR_SCHEME "BbWXYyRWWWXGgbRHYR\0GR"

static char custom_color_str[NUM_COLOR_IDX + 1] = DEFAULT_COLOR_SCHEME;

static void set_colorscheme(dbref player)
{
    char *str = silly_atr_get(player, A_MAPCOLOR);
    int i;
    
    if (*str && strlen(str) <= NUM_COLOR_IDX) {
	strncpy(custom_color_str, DEFAULT_COLOR_STRING, NUM_COLOR_IDX);
	strncpy(custom_color_str, str, strlen(str));
	for (i = 0; i < NUM_COLOR_IDX; i++) {
	    switch (custom_color_str[i]) {
	    case 'f':
	    case 'F':
	    case 'I':
	    case 'i':
	    case 'H':
	    case 'x':
	    case 'X':
	    case 'r':
	    case 'R':
	    case 'g':
	    case 'G':
	    case 'y':
	    case 'Y':
	    case 'b':
	    case 'B':
	    case 'm':
	    case 'M':
	    case 'c':
	    case 'C':
	    case 'w':
	    case 'W':
		break;
	    case 'h':
		custom_color_str[i] = 'H';
		break;
	    case 'n':
		custom_color_str[i] = '\0';
		break;
	    default:
		notify(player, tprintf("Invalid character '%c' in MAPCOLOR "
				       "attribute!", custom_color_str[i]));
		notify(player, "Using default: " DEFAULT_COLOR_STRING);
		memcpy(custom_color_str, DEFAULT_COLOR_SCHEME, NUM_COLOR_IDX);
		return;
	    }
	}
	return;
    } else if (*str) {
	notify(player, "Invalid MAPCOLOR attribute!");
	notify(player, "Using default: " DEFAULT_COLOR_STRING);
    }
    memcpy(custom_color_str, DEFAULT_COLOR_SCHEME, NUM_COLOR_IDX);
}


void mech_navigate(dbref player, void *data, char *buffer)
{
    MECH *mech = (MECH *) data;
    char mybuff[NAVIGATE_LINES][MBUF_SIZE];
    MAP *mech_map;
    char **maptext, *args[3];
    int i, dolos, argc;
    short x, y;
    
    cch(MECH_USUAL);

    mech_map = getMap(mech->mapindex);

    dolos = MapIsDark(mech_map) || (MechType(mech) == CLASS_MW &&
    				    mudconf.btech_mw_losmap);

    DOCHECK(mech_map->map_width <= 0 || mech_map->map_height <= 0,
	    "Nothing to see on this map, move along.");

    argc = mech_parseattributes(buffer, args, 3);
    if (!parse_tacargs(player, mech, args, argc, MechTacRange(mech), &x, &y))
	return;

    set_colorscheme(player);
    maptext = MakeMapText(player, mech, mech_map, x, y, 5, 5, 4, dolos);

    sprintf(mybuff[0],
	"              0                                          %.150s",
	maptext[0]);
    sprintf(mybuff[1],
	"         ___________                                     %.150s",
	maptext[1]);
    sprintf(mybuff[2],
	"        /           \\          Location:%4d,%4d, %3d   %.150s",
	MechX(mech), MechY(mech), MechZ(mech), maptext[2]);
    sprintf(mybuff[3],
	"  300  /             \\  60     Terrain: %14s   %.150s",
	GetTerrainName(mech_map, MechX(mech), MechY(mech)), maptext[3]);
    sprintf(mybuff[4],
	"      /               \\                                  %.150s",
	maptext[4]);
    sprintf(mybuff[5],
	"     /                 \\                                 %.150s",
	maptext[5]);
    sprintf(mybuff[6],
	"270 (                   )  90  Speed:           %6.1f   %.150s",
	MechSpeed(mech), maptext[6]);
    sprintf(mybuff[7],
	"     \\                 /       Vertical Speed:  %6.1f   %.150s",
	MechVerticalSpeed(mech), maptext[7]);
    sprintf(mybuff[8],
	"      \\               /        Heading:           %4d   %.150s",
	MechFacing(mech), maptext[8]);
    sprintf(mybuff[9],
	"  240  \\             /  120                              %.150s",
	maptext[9]);
    sprintf(mybuff[10],
	"        \\___________/                                    %.150s",
	maptext[10]);
    sprintf(mybuff[11], "                      ");
    sprintf(mybuff[12], "             180");

    navigate_sketch_mechs(mech, mech_map, x, y, mybuff);
    for (i = 0; i < NAVIGATE_LINES; i++)
	notify(player, mybuff[i]);
}


/* INDENT OFF */

/* 
                0
            ___________                                     /``\][/""\][/""\
           /           \          HEX Location: 254, 122    \`1/``\""/``\""/
     300  /             \  60     Terrain: Light Forest     /``\``/""\`3/""\
         /               \        Elevation:  0             \`2/``\"1/``\""/
        /                 \                                 /""\``|**\`3/""\
   270 (                   )  90  Speed: 0.0                \"4/``\"4/``\""/
        \                 /       Vertical Speed: 0.0       /""\`3/""\`3/""\
         \               /        Heading: 0                \"4/``\"4/``\""/
     240  \             /  120                              /""\`3/""\`3/""\
           \____*______/                                    \"4/][\"4/][\"4/
                180
 */

/* INDENT ON */

char GetLRSMechChar(MECH * mech, MECH * other)
{
    char c = 'u';

    if (mech == other)
	return '*';
    if (IsDS(other))
	c = 'd';
    switch (MechMove(other)) {
    case MOVE_FLY:
	c = 'a';
    case MOVE_BIPED:
	c = 'b';
	break;
    case MOVE_QUAD:
	c = 'q';
	break;
    case MOVE_TRACK:
	c = 't';
	break;
    case MOVE_WHEEL:
	c = 'w';
	break;
    case MOVE_HOVER:
	c = 'h';
	break;
    case MOVE_VTOL:
	c = 'v';
	break;
    case MOVE_HULL:
	c = 'n';
	break;
    case MOVE_SUB:
	c = 's';
	break;
    case MOVE_FOIL:
	c = 'f';
	break;
    }
    if (!MechSeemsFriend(mech, other))
	c = toupper(c);
    return c;
}

static inline char TerrainColorChar(char terrain, int elev)
{
    switch (terrain) {
    case HIGHWATER:
	return custom_color_str[DWATER_IDX];
    case WATER:
	if (elev < 2 || elev == '0' || elev == '1' || elev == '~')
	    return custom_color_str[SWATER_IDX];
	return custom_color_str[DWATER_IDX];
    case BUILDING:
	return custom_color_str[BUILDING_IDX];
    case ROAD:
	return custom_color_str[ROAD_IDX];
    case ROUGH:
	return custom_color_str[ROUGH_IDX];
    case MOUNTAINS:
	return custom_color_str[MOUNTAIN_IDX];
    case FIRE:
	return custom_color_str[FIRE_IDX];
    case ICE:
	return custom_color_str[ICE_IDX];
    case WALL:
	return custom_color_str[WALL_IDX];
    case SNOW:
	return custom_color_str[SNOW_IDX];
    case SMOKE:
	return custom_color_str[SMOKE_IDX];
    case LIGHT_FOREST:
	return custom_color_str[LWOOD_IDX];
    case HEAVY_FOREST:
	return custom_color_str[HWOOD_IDX];
    case UNKNOWN_TERRAIN:
	return custom_color_str[UNKNOWN_IDX];
    }
    return '\0';
}

static char *add_color(char newc, char * prevc, char c)
{
    static char buf[10]; /* won't be filled with more than 7 characters */
    buf[0] = '\0';

    if (newc == *prevc) {
	buf[0] = c;
	buf[1] = '\0';
	return buf;
    }

    if (!newc || ((isupper(*prevc)) && !isupper(newc)) ||
    	(newc == 'H' && *prevc))
	strcpy(buf, "%cn");
    else if (isupper(newc) && !isupper(*prevc))
	strcpy(buf, "%ch");

    if (!newc)
	sprintf(buf + strlen(buf), "%c", c);
    else
	sprintf(buf + strlen(buf), "%%c%c%c", tolower(newc), c);
    *prevc = newc;
    return buf;
}

static char *GetLRSMech(MECH * mech, MECH * other, int docolor, char *prevc)
{
    static char buf[2]; /* Won't be filled with more than 1 character */
    char c = GetLRSMechChar(mech, other);
    char newc;

    if (!docolor) {
	sprintf(buf, "%c", c);
	return buf;
    }

    if (mech == other)
	newc = custom_color_str[SELF_IDX];
    else if (!MechSeemsFriend(mech, other))
	newc = custom_color_str[ENEMY_IDX];
    else
	newc = custom_color_str[FRIEND_IDX];

    return add_color(newc, prevc, c);
    
}

static char *LRSTerrain(MAP * map, int x, int y, int docolor, char *prevc)
{
    static char buf[2]; /* Won't be filled with more than 1 character */

    char c = GetTerrain(map, x, y);
    char newc;

    if (!c || !docolor || c == ' ') {
	buf[0] = c;
	buf[1] = '\0';
	return buf;
    } else
	newc = TerrainColorChar(c, GetElev(map, x, y));

    return add_color(newc, prevc, c);
}

static char *LRSElevation(MAP * map, int x, int y, int docolor, char *prevc)
{
    static char buf[2]; /* Won't be filled with more than 1 character */

    int e = GetElev(map, x, y);
    char c = (e || docolor) ? '0' + e : ' ';
    char newc;

    if (!docolor) {
	buf[0] = c;
	buf[1] = '\0';
	return buf;
    } else
	newc = TerrainColorChar(GetTerrain(map, x, y), e);

    return add_color(newc, prevc, c);
}

#define LRS_TERRAINMODE		1
#define LRS_ELEVMODE		2
#define LRS_MECHMODE		4
#define LRS_LOSMODE		8
#define LRS_COLORMODE		16
#define LRS_ELEVCOLORMODE	32

static char *get_lrshexstr(MECH * mech, MAP *map, int x, int y,
			   char * prevc, int mode, MECH ** mechs, int lm,
			   hexlosmap_info * losmap)
{
    int losflag = MAPLOSHEX_SEE | MAPLOSHEX_SEEN;

    if (mode & LRS_MECHMODE) {
	while (mechs[lm] && MechY(mechs[lm]) < y)
	    lm++;
	while (mechs[lm] && MechY(mechs[lm]) == y && MechX(mechs[lm]) < x)
	    lm++;
	if (mechs[lm] && MechY(mechs[lm]) == y && MechX(mechs[lm]) == x)
	    return GetLRSMech(mech, mechs[lm], mode & LRS_COLORMODE, prevc);
    }

    if (losmap)
	losflag = LOSMap_GetFlag(losmap, x, y);

    /* If the losmap doesn't contain this hex, we return X in bold red
     * in both terrain and elevation mode.
     */
    if (!(losflag & MAPLOSHEX_SEEN))
	return add_color('R', prevc, 'X');

    if (((mode & LRS_TERRAINMODE) && !(losflag & MAPLOSHEX_SEETERRAIN)) ||
	((mode & LRS_ELEVMODE) && !(losflag & MAPLOSHEX_SEEELEV)))
	return add_color(TerrainColorChar(UNKNOWN_TERRAIN, 0), prevc, '?');
    
    if (mode & LRS_ELEVMODE)
	return LRSElevation(map, x, y, mode & LRS_ELEVCOLORMODE, prevc);
    if (mode & LRS_TERRAINMODE)
	return LRSTerrain(map, x, y, mode & LRS_COLORMODE, prevc);
	    
    SendError(tprintf("Unknown LRS mode, mech #%d mode 0x%x.",
		      mech->mynum, mode));
    return add_color('R', prevc, 'Y');

}

static void show_lrs_map(dbref player, MECH * mech, MAP * map, int x,
			 int y, int displayHeight, int mode)
{
    int loop, b_width, e_width, b_height, e_height, i;
    MECH *oMech;

    /* topbuff and botbuff must be capable of holding enough
     * characters to colorize all hexes in the most inefficient
     * way. This means 15 characters per 2 hexes, or 8 per
     * hex. topbuff and botbuff both hold half the hexes on the row,
     * so that ends up 4 * LRS_DISPLAY_WIDTH (plus some padding thrown
     * in for good measure.)
     *
     * midbuff is only used for the lables, and only needs a few
     * characters more than the display width.
     */
    char topbuff[4 * LRS_DISPLAY_WIDTH + 30] = "    ";
    char botbuff[4 * LRS_DISPLAY_WIDTH + 30] = "    ";
    char midbuff[8 + LRS_DISPLAY_WIDTH] = "    ";
    char trash1[5]; /* temp var to hold the max-three-digit number of map Y */
    short oddcol = 0;
    MECH *mechs[MAX_MECHS_PER_MAP];
    int last_mech = 0;
    char prevct = 0, prevcb = 0;
    hexlosmap_info * losmap = NULL;


    /* x and y hold the viewing center of the map */
    b_width = x - LRS_DISPLAY_WIDTH / 2;
    b_width = MAX(b_width, 0);
    e_width = b_width + LRS_DISPLAY_WIDTH;
    if (e_width >= map->map_width) {
	e_width = map->map_width - 1;
	b_width = e_width - LRS_DISPLAY_WIDTH;
	b_width = MAX(b_width, 0);
    }

    if (b_width % 2)
	oddcol = 1;

    b_height = y - displayHeight / 2;
    b_height = MAX(b_height, 0);
    e_height = b_height + displayHeight;
    if (e_height > map->map_height) {
	e_height = map->map_height;
	b_height = e_height - displayHeight;
	b_height = MAX(b_height, 0);
    }

    /* Display the top labels */
    for (i = b_width; i <= e_width; i++) {
	sprintf(trash1, "%3d", i);
	sprintf(topbuff + strlen(topbuff), "%c", trash1[0]);
	sprintf(midbuff + strlen(midbuff), "%c", trash1[1]);
	sprintf(botbuff + strlen(botbuff), "%c", trash1[2]);
    }
    notify(player, topbuff);
    notify(player, midbuff);
    notify(player, botbuff);


    if (mode & LRS_MECHMODE) {
	for (i = 0; i < map->first_free; i++) {
	    if ((oMech = getMech(map->mechsOnMap[i]))) {
		if ((mech == oMech) ||
		    (MechY(oMech) >= b_height && MechY(oMech) <= e_height &&
		     MechX(oMech) >= b_width && MechX(oMech) <= e_width &&
		     InLineOfSight(mech, oMech, MechX(oMech), MechY(oMech),
				   FlMechRange(map, mech, oMech))))
		    mechs[last_mech++] = oMech;
	    }
	}
	for (i = 0; i < (last_mech - 1); i++)	/* Bubble-sort the list 
						 *  to y/x order */
	    for (loop = (i + 1); loop < last_mech; loop++) {
		if (MechY(mechs[i]) > MechY(mechs[loop])) {
		    oMech = mechs[i];
		    mechs[i] = mechs[loop];
		    mechs[loop] = oMech;
		} else if (MechY(mechs[i]) == MechY(mechs[loop]) &&
		    MechX(mechs[i]) > MechX(mechs[loop])) {
		    oMech = mechs[i];
		    mechs[i] = mechs[loop];
		    mechs[loop] = oMech;
		}
	    }
	mechs[last_mech] = NULL;
	last_mech = 0;
    }

    if (mode & LRS_LOSMODE)
	losmap = CalculateLOSMap(map, mech, b_width, b_height,
				 e_width - b_width, e_height - b_height);

    for (loop = b_height; loop < e_height; loop++) {
	sprintf(topbuff, "%3d ", loop);
	strcpy(botbuff, "    ");
	if (mode & LRS_MECHMODE)
	    while (mechs[last_mech] && MechY(mechs[last_mech]) < loop)
		last_mech++;

	for (i = b_width; i < e_width; i += 2) {
	    sprintf(topbuff + strlen(topbuff), oddcol ? "%s " : " %s",
		    get_lrshexstr(mech, map, i + !oddcol, loop, &prevct,
				  mode, mechs, last_mech, losmap));

	    sprintf(botbuff + strlen(botbuff), oddcol ? " %s" : "%s ",
		    get_lrshexstr(mech, map, i + oddcol, loop, &prevcb,
				  mode, mechs, last_mech, losmap));
	}
	if (i == e_width && !oddcol) {
	    sprintf(botbuff + strlen(botbuff), "%s",
		    get_lrshexstr(mech, map, i, loop, &prevcb, mode,
				  mechs, last_mech, losmap));
	} else if (i == e_width) {
	    sprintf(topbuff + strlen(topbuff), "%s",
		    get_lrshexstr(mech, map, i, loop, &prevct, mode,
				  mechs, last_mech, losmap));
	    strcat(botbuff, " ");
	}

	if (mode & (LRS_COLORMODE|LRS_ELEVCOLORMODE)) {
	    if (prevct) {
		strcat(topbuff, "%cn");
		prevct = 0;
	    }
	    if (prevcb) {
		strcat(botbuff, "%cn");
		prevcb = 0;
	    }
	}
	sprintf(botbuff + strlen(botbuff), " %-3d", loop);
	notify(player, topbuff);
	notify(player, botbuff);
    }
}

void mech_lrsmap(dbref player, void *data, char *buffer)
{
    MECH *mech = (MECH *) data;
    MAP *map;
    int argc, mode = 0;
    short x, y;
    char *args[5], *str;
    int displayHeight = LRS_DISPLAY_HEIGHT;

    cch(MECH_USUAL);

    if (Ansimap(player))
	mode |= LRS_COLORMODE;

    map = getMap(mech->mapindex);

    argc = mech_parseattributes(buffer, args, 4);
    DOCHECK(!MechLRSRange(mech), "Your system seems to be inoperational.");
    if (!parse_tacargs(player, mech, &args[1], argc - 1,
		       MechLRSRange(mech), &x, &y))
	return;
    switch(args[0][0]) {
    case 'M':
    case 'm':
	mode |= LRS_MECHMODE | LRS_TERRAINMODE;
	break;
    case 'E':
    case 'e':
	mode |= LRS_ELEVMODE;
	break;
    case 'C':
    case 'c':
	mode |= LRS_ELEVMODE | LRS_ELEVCOLORMODE;
	break;
    case 'T':
    case 't':
	mode |= LRS_TERRAINMODE;
	break;
    case 'L':
    case 'l':
	mode |= LRS_LOSMODE | LRS_TERRAINMODE;
	break;
    case 'H':
    case 'h':
	mode |= LRS_LOSMODE | LRS_ELEVMODE;
	break;
    case 'S':
    case 's':
	mode |= LRS_LOSMODE | LRS_MECHMODE | LRS_TERRAINMODE;
	break;
    default:
	notify(player, tprintf("Unknown LRS sensor type '%s'!", args[0]));
	return;
    }
    
    if (MapIsDark(map) || (MechType(mech) == CLASS_MW &&
    				mudconf.btech_mw_losmap))
	mode |= LRS_LOSMODE;

    str = silly_atr_get(player, A_LRSHEIGHT);
    if (*str) {
	displayHeight = atoi(str);
	if (displayHeight < 10 || displayHeight > 40) {
	    notify(player,
		   "Illegal LRSHeight attribute.  Must be between 10 and 40");
	    displayHeight = LRS_DISPLAY_HEIGHT;
	}
    }

    displayHeight = MIN(displayHeight, 2 * MechLRSRange(mech));
    displayHeight = MIN(displayHeight, map->map_height);

    if (!(displayHeight % 2))
	displayHeight++;

    set_colorscheme(player);

    show_lrs_map(player, mech, map, x, y, displayHeight, mode);
}
	
static inline int is_oddcol(int col)
{
    /*
     * The only real trick here is to handle negative
     * numbers correctly.
     */
    return (unsigned) col & 1;
}

static inline int tac_dispcols(int hexcols)
{
    return hexcols * 3 + 1;
}

static inline int tac_hex_offset(int x, int y, int dispcols, int oddcol1)
{
    int oddcolx = is_oddcol(x + oddcol1);

    return (y * 2 + 1 - oddcolx) * dispcols + x * 3 + 1;
}

static inline void sketch_tac_row(char *pos, int left_offset,
    char const *src, int len)
{
    memset(pos, ' ', left_offset);
    memcpy(pos + left_offset, src, len);
    pos[left_offset + len] = '\0';
}

static void sketch_tac_map(char *buf, MAP * map, MECH * mech, int sx,
    int sy, int wx, int wy, int dispcols, int top_offset, int left_offset,
    int docolour, int dohexlos)
{
#if 0
    static char const hexrow[2][76] = {
	"\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/",
	"/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\"
    };
#else
    static char const hexrow[2][310] = {    
        "\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/]["
        "\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/]["
        "\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/]["
        "\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/]["
        "\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/",
        "/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\]["
        "/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\]["
        "/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\]["
        "/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\]["
        "/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\][/][\\"
    };
#endif
    int x, y;
    int oddcol1 = is_oddcol(sx);	/* One iff first hex col is odd */
    char *pos;
    int mapcols = tac_dispcols(wx);
    hexlosmap_info * losmap = NULL;

    /*
     * First create a blank hex map.
     */
    pos = buf;
    for (y = 0; y < top_offset; y++) {
	memset(pos, ' ', dispcols - 1);
	pos[dispcols - 1] = '\0';
	pos += dispcols;
    }
    for (y = 0; y < wy; y++) {
	sketch_tac_row(pos, left_offset, hexrow[oddcol1], mapcols);
	pos += dispcols;
	sketch_tac_row(pos, left_offset, hexrow[!oddcol1], mapcols);
	pos += dispcols;
    }
    sketch_tac_row(pos, left_offset, hexrow[oddcol1], mapcols);

    /*
     * Now draw the terrain and elevation. 
     */
    pos = buf + top_offset * dispcols + left_offset;
    wx = MIN(wx, map->map_width - sx);
    wy = MIN(wy, map->map_height - sy);

    if (dohexlos)
	losmap = CalculateLOSMap(map, mech, MAX(0, sx), MAX(0, sy),
				 wx, wy);

    for (y = MAX(0, -sy); y < wy; y++) {
	for (x = MAX(0, -sx); x < wx; x++) {
	    int terr, elev, losflag = MAPLOSHEX_SEE | MAPLOSHEX_SEEN;
	    char *base;
	    char topchar, botchar;
	    
	    if (losmap)
		losflag = LOSMap_GetFlag(losmap, sx+x, sy+y);

	    if (!(losflag & MAPLOSHEX_SEEN)) {
		terr = 'X';
		elev = 40; /* 'X' */
	    } else {
		
		if (losflag & MAPLOSHEX_SEETERRAIN)
		    terr = GetTerrain(map, sx + x, sy + y);
		else
		    terr = UNKNOWN_TERRAIN;
		
		if (losflag & MAPLOSHEX_SEEELEV)
		    elev = GetElev(map, sx + x, sy + y);
		else
		    elev = 15; /* Ugly hack: '0' + 15 == '?' */
	    }
	    base = pos + tac_hex_offset(x, y, dispcols, oddcol1);

	    switch (terr) {
	    case WATER:
		/*
		 * Colour hack:  Draw deep water with '\242'
		 * if using colour so colourize_tac_map()
		 * knows to use dark blue rather than light
		 * blue
		 */
		if (docolour && elev >= 2) {
		    topchar = '\242';
		    botchar = '\242';
		} else {
		    topchar = '~';
		    botchar = '~';
		}
		break;

	    case HIGHWATER:
		topchar = '~';
		botchar = '+';
		break;

	    case BRIDGE:
		topchar = '#';
		botchar = '+';
		break;

	    case ' ':		/* GRASSLAND */
		topchar = ' ';
		botchar = '_';
		break;

	    case UNKNOWN_TERRAIN:
		topchar = '?';
		botchar = '?';
		break;

	    default:
		topchar = terr;
		botchar = terr;
		break;
	    }

	    base[0] = topchar;
	    base[1] = topchar;
	    base[dispcols + 0] = botchar;
	    if (elev > 0) {
		botchar = '0' + elev;
	    }
	    base[dispcols + 1] = botchar;
	}
    }
}

/*
 * Draw one of the seven hexes that a Dropship takes up on a tac map.
 */
static void sketch_tac_ds(char *base, int dispcols, char terr)
{
    /*
     * Becareful not to overlay a 'mech id or terrain elevation.
     */
    if (!isalpha((unsigned char) base[0])) {
	base[0] = terr;
	base[1] = terr;
    }
    base[dispcols + 0] = terr;
    if (!isdigit((unsigned char) base[dispcols + 1])) {
	base[dispcols + 1] = terr;
    }
}

extern int dirs[6][2];

static void sketch_tac_ownmech(char *buf, MAP * map, MECH * mech, int sx,
    int sy, int wx, int wy, int dispcols, int top_offset, int left_offset)
{

    int oddcol1 = is_oddcol(sx);
    char *pos = buf + top_offset * dispcols + left_offset;
    char *base;
    int x = MechX(mech) - sx;
    int y = MechY(mech) - sy;

    if (x < 0 || x >= wx || y < 0 || y >= wy) {
	return;
    }
    base = pos + tac_hex_offset(x, y, dispcols, oddcol1);
    base[0] = '*';
    base[0] = '*';
}

static void sketch_tac_mechs(char *buf, MAP * map, MECH * player_mech,
    int sx, int sy, int wx, int wy, int dispcols, int top_offset,
    int left_offset, int docolour, int labels)
{
    int i;
    char *pos = buf + top_offset * dispcols + left_offset;
    int oddcol1 = is_oddcol(sx);

    /*
     * Draw all the 'mechs on the map.
     */
    for (i = 0; i < map->first_free; i++) {
	int x, y;
	char *base;
	MECH *mech;

	if (map->mechsOnMap[i] == -1) {
	    continue;
	}

	mech = getMech(map->mechsOnMap[i]);
	if (mech == NULL) {
	    continue;
	}

	/*
	 * Check to see if the 'mech is on the tac map and 
	 * that its in LOS of the player's 'mech.
	 */
	x = MechX(mech) - sx;
	y = MechY(mech) - sy;
	if (x < 0 || x >= wx || y < 0 || y >= wy) {
	    continue;
	}

	if (mech != player_mech &&
	    !InLineOfSight(player_mech, mech, MechX(mech), MechY(mech),
		FlMechRange(map, player_mech, mech))) {
	    continue;
	}

	base = pos + tac_hex_offset(x, y, dispcols, oddcol1);
	if (!(MechSpecials2(mech) & CARRIER_TECH) && IsDS(mech) &&
	    ((MechZ(mech) >= ORBIT_Z && mech != player_mech) || Landed(mech) || !Started(mech)))
	    {
	    int ts = DSBearMod(mech);
	    int dir;

	    /*
	     * Dropships are a special case.  They take up
	     * seven hexes on a tac map.  First draw the
	     * center hex and then the six surronding hexes.
	     */

	    if (docolour) {
		/*
		 * Colour hack: 'X' would be confused with
		 * any enemy con by colourize_tac_map()
		 */
		sketch_tac_ds(base, dispcols, '$');
	    } else {
		sketch_tac_ds(base, dispcols, 'X');
	    }

	    for (dir = 0; dir < 6; dir++) {
		int tx = x + dirs[dir][0];
		int ty = y + dirs[dir][1];

		if ((tx + oddcol1) % 2 == 0 && dirs[dir][0] != 0) {
		    ty--;
		}
		if (tx < 0 || tx >= wx || ty < 0 || ty >= wy) {
		    continue;
		}
		base = pos + tac_hex_offset(tx, ty, dispcols, oddcol1);
		if (Find_DS_Bay_Number(mech, (dir - ts + 6) % 6)
		    >= 0) {
		    sketch_tac_ds(base, dispcols, '@');
		} else {
		    sketch_tac_ds(base, dispcols, '=');
		}
	    }
	} else if (mech == player_mech) {
	    base[0] = '*';
	    base[1] = '*';
	} else {
	    char *id = MechIDS(mech, MechSeemsFriend(player_mech, mech));

	    base[0] = id[0];
	    base[1] = id[1];
	}
    }
}

static void sketch_tac_cliffs(char *buf, MAP * map, int sx, int sy, int wx,
    int wy, int dispcols, int top_offset, int left_offset, int cliff_size)
{
    char *pos = buf + top_offset * dispcols + left_offset;
    int y, x;
    int oddcol1 = is_oddcol(sx);

    wx = MIN(wx, map->map_width - sx);
    wy = MIN(wy, map->map_height - sy);
    for (y = MAX(0, -sy); y < wy; y++) {
	int ty = sy + y;

	for (x = MAX(0, -sx); x < wx; x++) {
	    int tx = sx + x;
	    int oddcolx = is_oddcol(tx);
	    int elev = Elevation(map, tx, ty);
	    char *base = pos + tac_hex_offset(x, y, dispcols,
		oddcol1);
	    char c;


	    /*
	     * Copy the elevation up to the top of the hex 
	     * so we can draw a bottom hex edge on every hex.
	     */
	    c = base[dispcols + 1];
	    if (base[0] == '*') {
		base[0] = '*';
		base[1] = '*';
	    } else if (isdigit((unsigned char) c)) {
		base[1] = c;
	    }


	    /*
	     * For each hex on the map check to see if each
	     * of it's 240, 180, and 120 hex sides is a cliff. 
	     * Don't check for cliffs between hexes that are on
	     * the tac map and those that are off of it.
	     */

	    if (x != 0 && (y < wy - 1 || oddcolx)
		&& abs(Elevation(map, tx - 1, ty + 1 - oddcolx)
		    - elev) >= cliff_size) {

		base[dispcols - 1] = '|';
	    }
	    if (y < wy - 1 && abs(Elevation(map, tx, ty + 1) - elev)
		>= cliff_size) {
		base[dispcols] = ',';
		base[dispcols + 1] = ',';
	    } else {
		base[dispcols] = '_';
		base[dispcols + 1] = '_';
	    }
	    if (x < wx - 1 && (y < wy - 1 || oddcolx)
		&& abs(Elevation(map, tx + 1, ty + 1 - oddcolx)
		    - elev) >= cliff_size) {
		base[dispcols + 2] = '!';
	    }
	}
    }
}
static void sketch_tac_dslz(char *buf, MAP * map, MECH * mech, int sx,
			    int sy, int wx, int wy, int dispcols,
			    int top_offset, int left_offset, int cliff_size,
			    int docolour)
{
    char *pos = buf + top_offset * dispcols + left_offset;
    int y, x;
    int oddcol1 = is_oddcol(sx);

    wx = MIN(wx, map->map_width - sx);
    wy = MIN(wy, map->map_height - sy);
    for (y = MAX(0, -sy); y < wy; y++) {
	int ty = sy + y;

	for (x = MAX(0, -sx); x < wx; x++) {
	    int tx = sx + x;
	    char *base = pos + tac_hex_offset(x, y, dispcols, oddcol1);

	    if (ImproperLZ(mech, tx, ty))
		base[dispcols] = docolour ? '\241' : 'X';
	    else
		base[dispcols] = docolour ? '\240' : 'O';
	}
    }
}

/*
 * Colourize a sketch tac map.  Uses dynmaically allocated buffers
 * which are overwritten on each call.
 */
static char **colourize_tac_map(char const *sketch, int dispcols,
    int disprows)
{
    static char *buf = NULL;
    static int buf_len = 5000;
    static char **lines = NULL;
    static int lines_len = 100;
    int pos = 0;
    int line = 0;
    unsigned char cur_colour = '\0';
    const char *line_start;
    char const *src = sketch;

    if (buf == NULL) {
	Create(buf, char, buf_len);
    }
    if (lines == NULL) {
	Create(lines, char *, lines_len);
    }

    line_start = (char *) src;
    lines[0] = buf;
    while (lines > 0) {
	unsigned char new_colour;
	unsigned char c = *src++;

	if (c == '\0') {
	    /*
	     * End of line.
	     */
	    if (cur_colour != '\0') {
		buf[pos++] = '%';
		buf[pos++] = 'c';
		buf[pos++] = 'n';
	    }
	    buf[pos++] = '\0';
	    line++;
	    if (line >= disprows) {
		break;		/* Done */
	    }
	    if (line + 1 >= lines_len) {
		lines_len *= 2;
		ReCreate(lines, char *, lines_len);
	    }
	    line_start += dispcols;
	    src = line_start;
	    lines[line] = buf + pos;
	    continue;
	}

	switch (c) {
	case (unsigned char) '\242':	/* Colour Hack: Deep Water */
	    c = '~';
	    new_colour = custom_color_str[DWATER_IDX];
	    break;

	case (unsigned char) '\241':	/* Colour Hack: improper LZ */
	    c = 'X';
	    new_colour = custom_color_str[BADLZ_IDX];
	    break;
	case (unsigned char) '\240':	/* Colour Hack: proper LZ */
	    c = 'O';
	    new_colour = custom_color_str[GOODLZ_IDX];
	    break;
	case '?':
	    c = '?';
	    new_colour = custom_color_str[UNKNOWN_IDX];
	    break;

	case '$':		/* Colour Hack: Drop Ship */
	    c = 'X';
	    new_colour = custom_color_str[DS_IDX];
	    break;

	case '!':		/* Cliff hex edge */
	    c = '/';
	    new_colour = custom_color_str[CLIFF_IDX];
	    break;

	case '|':		/* Cliff hex edge */
	    c = '\\';
	    new_colour = custom_color_str[CLIFF_IDX];
	    break;

	case ',':		/* Cliff hex edge */
	    c = '_';
	    new_colour = custom_color_str[CLIFF_IDX];
	    break;
	case '*':		/* mech itself. */
	    new_colour = custom_color_str[SELF_IDX];
	    break;

	default:
	    if (islower(c)) {	/* Friendly con */
		new_colour = custom_color_str[FRIEND_IDX];
	    } else if (isupper(c)) {	/* Enemy con */
		new_colour = custom_color_str[ENEMY_IDX];
	    } else if (isdigit(c)) {	/* Elevation */
		new_colour = cur_colour;
	    } else {
		new_colour = TerrainColorChar(c, 0);
	    }
	    break;
	}

	if (isupper(new_colour) != isupper(cur_colour)) {
	    if (isupper(new_colour)) {
		buf[pos++] = '%';
		buf[pos++] = 'c';
		buf[pos++] = 'h';
	    } else {
		buf[pos++] = '%';
		buf[pos++] = 'c';
		buf[pos++] = 'n';
		cur_colour = '\0';
	    }
	}
	if (tolower(new_colour) != tolower(cur_colour)) {
	    buf[pos++] = '%';
	    buf[pos++] = 'c';
	    if (new_colour == '\0') {
		buf[pos++] = 'n';
	    } else if (new_colour == 'H') {
		buf[pos++] = 'n';
		buf[pos++] = '%';
		buf[pos++] = 'c';
		buf[pos++] = tolower(new_colour);
	    } else {
		buf[pos++] = tolower(new_colour);
	    }
	    cur_colour = new_colour;
	}
	buf[pos++] = c;
	if (pos + 11 > buf_len) {
	    /*
	     * If we somehow run out of room then we don't
	     * bother to reallocate 'buf' and potentially have
	     * a bunch of invalid pointers in 'lines' to fix up.
	     * We just restart from scratch with a bigger 'buf'.
	     */
	    buf_len *= 2;
	    free(buf);
	    buf = NULL;
	    return colourize_tac_map(sketch, dispcols, disprows);
	}
    }
    lines[line] = NULL;
    return lines;
}

/*
 * Draw a tac map for the TACTICAL and NAVIGATE commands.
 *
 * This used to be "one MOFO of a function" but has been simplified
 * in a number of ways.  One is that it used to statically allocated
 * buffers which limit the map drawn to MAP_DISPLAY_WIDTH hexes across
 * and 24 hexes down in size.  The return value should no longer be
 * freed with KillText().
 *
 * player   = dbref of player wanting map (mostly irrelevant)
 * mech     = mech player's in (or NULL, if on map) 
 * map      = map obj itself
 * cx       = middle of the map (x)
 * cy       = middle of the map (y)
 * wx       = width in x
 * wy       = width in y
 * labels   = bit array
 *    1 = the 'top numbers'
 *    2 = the 'side numbers'
 *    4 = navigate mode
 *    8 = show mech cliffs
 *   16 = show tank cliffs
 *   32 = show DS LZ's
 *
 * If navigate mode, wx and wy should be equal and odd.  Navigate maps
 * cannot have top or side labels.
 *
 */

char **MakeMapText(dbref player, MECH * mech, MAP * map, int cx, int cy,
    int wx, int wy, int labels, int dohexlos)
{
    int docolour = Ansimap(player);
    int dispcols;
    int disprows;
    int mapcols;
    int left_offset = 0;
    int top_offset = 0;
    int navigate = 0;
    int sx, sy;
    int i;
    char *base;
    int oddcol1;
    enum {
	MAX_WIDTH = 40,
	MAX_HEIGHT = 24,
	TOP_LABEL = 3,
	LEFT_LABEL = 4,
	RIGHT_LABEL = 3
    };
    static char sketch_buf[((LEFT_LABEL + 1 + MAX_WIDTH * 3 + RIGHT_LABEL +
	    1) * (TOP_LABEL + 1 + MAX_HEIGHT * 2) + 2) * 5];
    static char *lines[(TOP_LABEL + 1 + MAX_HEIGHT * 2 + 1) * 5];

    if (labels & 4) {
	navigate = 1;
	labels = 0;
    }

    /*
     * Figure out the extent of the tac map to draw.  
     */
    wx = MIN(MAX_WIDTH, wx);
    wy = MIN(MAX_HEIGHT, wy);

    sx = cx - wx / 2;
    sy = cy - wy / 2;
    if (!navigate) {
	/*
	 * Only allow navigate maps to include off map hexes.
	 */
	sx = MAX(0, MIN(sx, map->map_width - wx));
	sy = MAX(0, MIN(sy, map->map_height - wy));
	wx = MIN(wx, map->map_width);
	wy = MIN(wy, map->map_height);
    }

    mapcols = tac_dispcols(wx);
    dispcols = mapcols + 1;
    disprows = wy * 2 + 1;
    oddcol1 = is_oddcol(sx);

    if (navigate) {
	if (oddcol1) {
	    /*
	     * Insert blank line at the top where we can put
	     * a "__" to make the navigate map look pretty.
	     */
	    top_offset = 1;
	    disprows++;
	}
    } else {
	/*
	 * Allow room for the labels.
	 */
	if (labels & 1) {
	    left_offset = LEFT_LABEL;
	    dispcols += LEFT_LABEL + RIGHT_LABEL;
	}
	if (labels & 2) {
	    top_offset = TOP_LABEL;
	    disprows += TOP_LABEL;
	}
    }

    /*
     * Create a sketch tac map including terrain and elevation.
     */
    sketch_tac_map(sketch_buf, map, mech, sx, sy, wx, wy, dispcols,
	top_offset, left_offset, docolour, dohexlos);

    /*
     * Draw the top and side labels.
     */
    if (labels & 1) {
	int x;

	for (x = 0; x < wx; x++) {
	    char scratch[4];
	    int label = sx + x;

	    if (label < 0 || label > 999) {
		continue;
	    }
	    sprintf(scratch, "%3d", label);
	    base = sketch_buf + left_offset + 1 + x * 3;
	    base[0] = scratch[0];
	    base[1 * dispcols] = scratch[1];
	    base[2 * dispcols] = scratch[2];
	}
    }

    if (labels & 2) {
	int y;

	for (y = 0; y < wy; y++) {
	    int label = sy + y;

	    base = sketch_buf + (top_offset + 1 + y * 2)
		* dispcols;
	    if (label < 0 || label > 999) {
		continue;
	    }

	    sprintf(base, "%3d", label);
	    base[3] = ' ';
	    sprintf(base + (dispcols - RIGHT_LABEL - 1), "%3d", label);
	}
    }

    if (labels & 8) {
	if (mech != NULL) {
	    sketch_tac_ownmech(sketch_buf, map, mech, sx, sy, wx, wy,
		dispcols, top_offset, left_offset);
	}
	sketch_tac_cliffs(sketch_buf, map, sx, sy, wx, wy, dispcols,
	    top_offset, left_offset, 3);
    } else if (labels & 16) {
	if (mech != NULL) {
	    sketch_tac_ownmech(sketch_buf, map, mech, sx, sy, wx, wy,
		dispcols, top_offset, left_offset);
	}
	sketch_tac_cliffs(sketch_buf, map, sx, sy, wx, wy, dispcols,
	    top_offset, left_offset, 2);
    } else if (labels & 32) {
	if (mech != NULL) {
	    sketch_tac_ownmech(sketch_buf, map, mech, sx, sy, wx, wy,
			       dispcols, top_offset, left_offset);
	}
	sketch_tac_dslz(sketch_buf, map, mech, sx, sy, wx, wy, dispcols,
			top_offset, left_offset, 2, docolour);
    } else if (mech != NULL) {
	sketch_tac_mechs(sketch_buf, map, mech, sx, sy, wx, wy, dispcols,
	    top_offset, left_offset, docolour, labels);
    }


    if (navigate) {
	int n = wx / 2;		/* Hexagon radius */

	/*
	 * Navigate hack: erase characters from the sketch map
	 * to turn it into a pretty hexagonal shaped map.
	 */
	if (oddcol1) {
	    /*
	     * Don't need the last line in this case. 
	     */
	    disprows--;
	}

	for (i = 0; i < n; i++) {
	    int len;

	    base = sketch_buf + (i + 1) * dispcols + left_offset;
	    len = (n - i - 1) * 3 + 1;
	    memset(base, ' ', len);
	    base[len] = '_';
	    base[len + 1] = '_';
	    base[mapcols - len - 2] = '_';
	    base[mapcols - len - 1] = '_';
	    base[mapcols - len] = '\0';

	    base =
		sketch_buf + (disprows - i - 1) * dispcols + left_offset;
	    len = (n - i) * 3;
	    memset(base, ' ', len);
	    base[mapcols - len] = '\0';
	}

	memset(sketch_buf + left_offset, ' ', n * 3 + 1);
	sketch_buf[left_offset + n * 3 + 1] = '_';
	sketch_buf[left_offset + n * 3 + 2] = '_';
	sketch_buf[left_offset + n * 3 + 3] = '\0';
    }

    if (docolour) {
	/*
	 * If using colour then colourize the sketch map and
	 * return the result.
	 */
	return colourize_tac_map(sketch_buf, dispcols, disprows);
    }

    /*
     * If not using colour, the sketch map can be used as is.
     */
    for (i = 0; i < disprows; i++) {
	lines[i] = sketch_buf + dispcols * i;
    }
    lines[i] = NULL;
    return lines;
}

/* Draws the map for the player when they use the 
 * TACTICAL [C | T | L] [<BEARING> <RANGE> | <TARGET-ID>]
 * command inside a unit */
void mech_tacmap(dbref player, void *data, char *buffer)
{
    MECH *mech = (MECH *) data;
    int argc, i;
    short x, y;
    int mapx, mapy;
    char *args_vec[4];
    char **args = args_vec;
    MAP *mech_map;
    int displayHeight = MAP_DISPLAY_HEIGHT, displayWidth = MAP_DISPLAY_WIDTH;
    char *str;
    char **maptext;
    int flags = 3, dohexlos = 0;

    /* Basic checks for pilot and mech */
    cch(MECH_USUAL);

    /* Get the map info */
    mech_map = getMap(mech->mapindex);
    mapx = MechX(mech);
    mapy = MechY(mech);

    /* Various checks for conditions and system of mech */
    argc = mech_parseattributes(buffer, args, 4);
    DOCHECK(!MechTacRange(mech), "Your system seems to be inoperational.");

    if (MapIsDark(mech_map) || (MechType(mech) == CLASS_MW &&
    				mudconf.btech_mw_losmap))
	    dohexlos = 1;

    /* Check to see which type of tactical to display 
     * if they specified a particular one */
    if (argc > 0 && isalpha((unsigned char) args[0][0])
	    && args[0][1] == '\0') {
	    
        switch (tolower((unsigned char) args[0][0])) {
	        case 'c':
	            flags |= 8;		/* Show cliffs */
	            break;

	        case 't':
	            flags |= 16;	/* Show tank cliffs */
	            break;

	        case 'l':
	            dohexlos = 1;
	            break;

	        case 'b':
	            flags |= 32;
	            break;

	        default:
	            notify(player, "Invalid tactical map flag.");
	            return;
	    }

	args++;
	argc--;
    }

    DOCHECK(dohexlos && (flags & (8|16|32)), "You can't see that much here!");

    if (!parse_tacargs(player, mech, args, argc, MechTacRange(mech), &x, &y))
	    return;

    /* Get the Tacsize attribute from
     * the player, if doesn't exist set the height and width to
     * default params. If it does exist, check the values and
     * make sure they are legit. */
    str = silly_atr_get(player, A_TACSIZE);
    if (!*str) {
	    displayHeight = MAP_DISPLAY_HEIGHT;
        displayWidth = MAP_DISPLAY_WIDTH;
    } else if (sscanf(str, "%d %d", &displayHeight, &displayWidth) != 2 || 
            displayHeight > 24 || displayHeight < 5 || displayWidth > 40 || 
            displayWidth < 5) {
	
        notify(player,
            "Illegal Tacsize attribute. Must be in format "
            "'Height Width' . Height : 5-24 Width : 5-40");
	    displayHeight = MAP_DISPLAY_HEIGHT;
        displayWidth = MAP_DISPLAY_WIDTH;
    }

    /* Everything worked but lets check the mech's tac range
     * and the map size */
    displayHeight = (displayHeight <= 2 * MechTacRange(mech)
	    ? displayHeight : 2 * MechTacRange(mech));
    displayWidth = (displayWidth <= 2 * MechTacRange(mech)
        ? displayWidth : 2 * MechTacRange(mech));

    displayHeight = (displayHeight <= mech_map->map_height)
	    ? displayHeight : mech_map->map_height;
    displayWidth = (displayWidth <= mech_map->map_width)
        ? displayWidth : mech_map->map_width;

    set_colorscheme(player);

    /* Get the data to draw the map */
    maptext =
	    MakeMapText(player, mech, mech_map, x, y, displayWidth,
	    displayHeight, flags, dohexlos);

    /* Draw the map for the player */
    for (i = 0; maptext[i]; i++)
	    notify(player, maptext[i]);
}


/* XXX Fix 'enterbase <dir>' */
static void mech_enter_event(MUXEVENT * e)
{
    MECH *mech = (MECH *) e->data, *tmpm = NULL;
    mapobj *mapo;
    MAP *map = getMap(mech->mapindex), *newmap;
    int target = (int) e->data2;
    int x, y;

    if (!(mapo = find_entrance_by_xy(map, MechX(mech), MechY(mech))))
	return;
    if (!Started(mech) || Uncon(mech) || Jumping(mech) ||
	(MechType(mech) == CLASS_MECH && (Fallen(mech) || Standing(mech)))
	|| OODing(mech) || (fabs(MechSpeed(mech)) * 5 >= MMaxSpeed(mech) &&
	    fabs(MMaxSpeed(mech)) >= MP1) || (MechType(mech) == CLASS_VTOL
	    && AeroFuel(mech) <= 0))
	return;
    if (!(newmap = getMap(mapo->obj)))
	return;
    if (!find_entrance(newmap, target, &x, &y))
	return;

    if (!can_pass_lock(mech->mynum, newmap->mynum, A_LENTER) &&
	    (BuildIsSafe(newmap) || newmap->cf >= (newmap->cfmax / 2))) {
        char *msg = silly_atr_get(newmap->mynum, A_FAIL);
        if (!msg || !*msg)
            msg = "The hangar is locked."; 
        mech_notify(mech, MECHALL, msg);
        return;
    }

    StopBSuitSwarmers(FindObjectsData(mech->mapindex), mech, 1);
    mech_notify(mech, MECHALL, tprintf("You enter %s.",
	    structure_name(mapo)));
    MechLOSBroadcast(mech, tprintf("has entered %s at %d,%d.",
	    structure_name(mapo), MechX(mech), MechY(mech)));
    MarkForLOSUpdate(mech);
    if (MechType(mech) == CLASS_MW && !In_Character(mapo->obj)) {
	enter_mw_bay(mech, mapo->obj);
	return;
    }
    if (MechCarrying(mech) > 0)
	tmpm = getMech(MechCarrying(mech));
    mech_Rsetmapindex(GOD, (void *) mech, tprintf("%d", (int) mapo->obj));
    mech_Rsetxy(GOD, (void *) mech, tprintf("%d %d", x, y));
    MechLOSBroadcast(mech, tprintf("has entered %s at %d,%d.",
	    structure_name(mapo), MechX(mech), MechY(mech)));
    loud_teleport(mech->mynum, mapo->obj);
    if (tmpm) {
	mech_Rsetmapindex(GOD, (void *) tmpm, tprintf("%d",
		(int) mapo->obj));
	mech_Rsetxy(GOD, (void *) tmpm, tprintf("%d %d", x, y));
	loud_teleport(tmpm->mynum, mapo->obj);
    }
    auto_cal_mapindex(mech);
}


void mech_enterbase(dbref player, void *data, char *buffer)
{
    MECH *mech = (MECH *) data;
    MAP *map, *newmap;
    int x, y;
    mapobj *mapo;
    char target, *tmpc;
    char *args[2];
    int argc;

    char fail_mesg[SBUF_SIZE];

    argc = mech_parseattributes(buffer, args, 2);
    DOCHECK(argc > 1, "Invalid arguments to command!");
    tmpc = args[0];
    if (argc > 0 && *tmpc && !(*(tmpc + 1)))
	target = tolower(*tmpc);
    else
	target = 0;
    cch(MECH_USUAL);
    map = getMap(mech->mapindex);
    /* For now, no dir checks */
    DOCHECK(Jumping(mech), "While in mid-jump? No way.");
    DOCHECK(MechType(mech) == CLASS_MECH && (Fallen(mech) ||
	    Standing(mech)), "Crawl inside? I think not. Stand first.");
    DOCHECK(OODing(mech), "While in mid-flight? No way.");
    DOCHECK(MechType(mech) == CLASS_VTOL &&
	AeroFuel(mech) <= 0, "You lack fuel to maneuver in!");
    DOCHECK(FlyingT(mech) &&
	!Landed(mech),
	"You need to land before you can enter the hangar.");
    DOCHECK(IsDS(mech),
	"Heh, you're trying to be funny, right, a DropShip entering hangar?");
    DOCHECK(fabs(MechSpeed(mech)) * 5 >= MMaxSpeed(mech) &&
	fabs(MMaxSpeed(mech)) >= MP1,
	"You are moving too fast to enter the hangar!");
    DOCHECK(!(mapo =
	    find_entrance_by_xy(map, MechX(mech), MechY(mech))),
	"You see nothing to enter here!");
    /* Wow, *gasp*, we got something to enter */
    if (!(newmap = FindObjectsData(mapo->obj))) {
	mech_notify(mech, MECHALL,
	    "You sense wrongness in fabric of space..");
	SendError(tprintf
	    ("Error: No map existing for mapindex #%d (@ %d,%d of #%d)",
		(int) mapo->obj, mapo->x, mapo->y, mech->mapindex));
	return;
    }
    if (!find_entrance(newmap, target, &x, &y)) {
	mech_notify(mech, MECHALL,
	    "You sense wrongness in fabric of space..");
	SendError(tprintf
	    ("Error: No entrance existing for mapindex #%d (@ %d,%d of #%d)",
		(int) mapo->obj, mapo->x, mapo->y, mech->mapindex));
	return;
    }
    
    if (!can_pass_lock(mech->mynum, newmap->mynum, A_LENTER) &&
	    (BuildIsSafe(newmap) || newmap->cf >= (newmap->cfmax / 2))) {

        /* Trigger FAIL & AFAIL */
        memset(fail_mesg, 0, sizeof(fail_mesg));
        snprintf(fail_mesg, LBUF_SIZE, "The hangar is locked.");

        did_it(player, newmap->mynum, A_FAIL, fail_mesg, 0, NULL, A_AFAIL,
                (char **) NULL, 0);

        return;
    }

    DOCHECK(EnteringHangar(mech), "You are already entering the hangar!");
    /* XXX Check for other mechs in the hex possibly doing this as well (ick) */
    HexLOSBroadcast(map, MechX(mech), MechY(mech),
	"The doors at $h start to open..");
    MECHEVENT(mech, EVENT_ENTER_HANGAR, mech_enter_event, 18,
	(int) target);
}