/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,       *
 *  Michael Seifert, Hans Henrik Strfeldt, Tom Madsen, and Katja Nyboe.    *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
 *    ROM 2.4 is copyright 1993-1998 Russ Taylor                             *
 *    ROM has been brought to you by the ROM consortium                      *
 *        Russ Taylor (rtaylor@hypercube.org)                                *
 *        Gabrielle Taylor (gtaylor@hypercube.org)                           *
 *        Brian Moore (zump@rom.org)                                         *
 *    By using this code, you have agreed to follow the terms of the         *
 *    ROM license, in the file Rom24/doc/rom.license                         *
 ****************************************************************************/

/***************************************************************************
 * MapMaker originally created by
 * Wendigo of Oblivionmud.static.net port 9000
 * If you use this code, enjoy it, have bug fixes or simply want to
 * chat, please feel free to email me at  kcaffet@hotmail.com (msn as well)
 ****************************************************************************/

/*
 * Installation of this snippet should be rather easy.  Simply put the file
 * in with the rest of your source code, edit your Makefile and add the
 * appropriate $.o file (where $ is the name of this file)
 * Declare a do_map in interp.h and add the command into interp.c
 *
 * This is a stand-alone file.  However, it is recommended that you move
 * many of these functions into their appropriate files (All the recycle
 * info into recycle.c and wnatnot.
 */

/* Commands for do_map are as follows:
 * (Note: Commands do not need to be issued in any order)
 *
 * world: 	Area boundaries do not apply. It will go out as far as it
 * 			can into the world.  Not specifying world leaves it at the
 * 			default of area only
 *
 * tight:	Do not show room links(Not specifying will show links)
 *
 * # > 0:	Ex: 4, 6, 30  Will show that many spaces away
 * 			from you, forming a box(not specifying will go until finished)
 *
 * # < 0:	Ex: -1, -5, -20  Will limit the map to that depth (positive)
 * 			(not specifying will go until finished)
 *
 * doors:	Will not go past closed doors.(Not Specified will go through
 * 			all doors)
 *
 * mob:		Will highlight squares with mobs in them (colour coded) (Not
 * 			specified will not show mob colours)
 *
 * terrain:	Will toggle terrain mode (rooms shown with terrain symbols)
 * 			(not specified will show regular area rooms)
 *
 * fog:		Enables fog mode (Will not go past certain terrain types)
 * 			(Not specified will go through all terrain types)
 *
 * text:	Enables room description text to be displayed to the right
 * 			of the map
 *
 * border:	remove the pretty border around the map.
 *
 * Mulet: added this to spot players as well, of course only give it to imms
 * players: how players position and create an indexed color
 *
 * Ex: 	map tight 8    	Will stay in the area, not show room links and only
 * 						go 8 spaces away from the user
 *
 * 		map 20 world	Area boundaries ignored, shows room links, goes 20
 * 						spaces away from the user
 *
 * 		map world tight mob terrain fog border doors
 * 						Yah..  Look at all the above options, apply them
 *
 * 	As a side note, colour code had to be reduced to a bare minimum due
 * 	to a resource limit on how much can be displayed at one time.
 * 	One line of 80 characters can take up a lot of space if every
 * 	spot is used and has an extra 4-8 colour code characters in it.
 * 	While the line  "bobo likes pie"  may seem small it gets quite large
 * 	when it's "{rb{ro{rb{ro{lw {lgl{lgi{lgk{lge{lgs{lw {rp{ri{re{lw"
 *
 *  Mulet:this previous comment is mitigated  as I rewrited the color management
 *
 * 	If you begin to experience disconnections when using the map at it's
 * 	maximum grid size, simply reduce the grid size.
 */

#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include "an.h"
#include "mapmakerv2.h"
#include "interp.h"

// External information, used by many things.
MAP_QD *map_queue;
MAP_VISIT *map_visit[MAX_MAP_HASH];
MAP_VISIT *map_visit_free;
int map[MAX_MAP][MAX_MAP];
MAP_PARAM *mp = NULL;

bool_t is_room_visited(CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex) {
    if ( !ch || !ch->pcdata || !pRoomIndex)
        return FALSE;
    if (IS_DEMI(ch))
        return TRUE;
    return ch->pcdata->visitedroom[pRoomIndex->vnum / 8] & (1 << (pRoomIndex->vnum % 8)) ? TRUE : FALSE;
}

bool_t is_room_sn_visited(CHAR_DATA *ch, unsigned int vnum) {
    if ( !ch || !ch->pcdata)
        return FALSE;
    if (IS_DEMI(ch))
        return TRUE;
    return ch->pcdata->visitedroom[vnum / 8] & (1 << (vnum % 8)) ? TRUE : FALSE;
}

void set_room_visited(CHAR_DATA *ch, ROOM_INDEX_DATA *pRoomIndex) {
    if ( !ch || !ch->pcdata || !pRoomIndex || IS_SET(pRoomIndex->room_flags, ROOM_NO_MAP))
        return;
    ch->pcdata->visitedroom[pRoomIndex->vnum / 8] |= (1 << (pRoomIndex->vnum % 8));
}

void set_room_sn_visited(CHAR_DATA *ch, unsigned int vnum) {
    ch->pcdata->visitedroom[vnum / 8] |= (1 << (vnum % 8));
}

void unset_room_sn_visited(CHAR_DATA *ch, unsigned int vnum) {
    ch->pcdata->visitedroom[vnum / 8] &= ~(1 << (vnum % 8));
}

static char previousColor[7];

char *mpcolourCH(CHAR_DATA *ch, char *arg, bool_t forcedColor) {
    static char colorch[7];
    //to prevent bleeding background
    if ( !str_prefix_cs("{G", previousColor))
        sprintf(colorch, "&%d", ch ? COLOR_TABLE[(ch->color[C_NRM])].number : C_NRM);
    else
        colorch[0] = '\0';
    if ( !str_cmp(arg, "{nr")) //neutral color
        sprintf(colorch, "%s&%d", colorch, ch ? COLOR_TABLE[(ch->color[C_NRM])].number : C_NRM);
    else //magenta for border, only case I have to sort yet
        sprintf(colorch, "%s&%d", colorch, ch ? COLOR_TABLE[(ch->color[C_NRM])].number : C_NRM);
    if (str_cmp_cs(previousColor, arg) || forcedColor) {
        sprintf(previousColor, "%s", arg);
        return colorch;
    }
    return "";
}

/*
 * ugly hack to make so colors are adapted to Arcane Nites code... (we use &1 to %23 colors)
 * adapt it to yours (G means background color, b bold, l low (dim normal whatever)
 * second letter is the color
 */
char *mpcolour(char *arg) {
    if (mp && !mp->color)
        return "";
    if (!mp)
        previousColor[0] = '\0';

    static char color[7];
    //if previous color was a background one, and following is bold
    //they will overlap and give a bold color on previous background
    //so we insert a dummy light color to prevent it...
    //this code will only work here as the map do not send other combination
    if ( !str_prefix_cs("{G", previousColor) && !str_prefix_cs("{b", arg))
        sprintf(color, "%s", "&1");
    else
        color[0] = '\0';

    if ( !str_prefix_cs("{GB", arg))       sprintf(color, "%s&%d", color, C_BG_BLUE);
    else if ( !str_prefix_cs("{GR", arg))  sprintf(color, "%s&%d", color, C_BG_RED);
    else if ( !str_prefix_cs("{GW", arg))  sprintf(color, "%s&%d", color, C_BG_WHITE);
    else if ( !str_prefix_cs("{GG", arg))  sprintf(color, "%s&%d", color, C_BG_GREEN);
    else if ( !str_prefix_cs("{GY", arg))  sprintf(color, "%s&%d", color, C_BG_YELLOW);
    else if ( !str_prefix_cs("{GC", arg))  sprintf(color, "%s&%d", color, C_BG_CYAN);
    else if ( !str_prefix_cs("{BM", arg))  sprintf(color, "%s&%d", color, C_BG_MAGENTA);
    else if ( !str_prefix_cs("{lw", arg))  sprintf(color, "%s&%d", color, C_WHT);
    else if ( !str_prefix_cs("{bW", arg))  sprintf(color, "%s&%d", color, C_B_WHT);
    else if ( !str_prefix_cs("{ly", arg))  sprintf(color, "%s&%d", color, C_YEL);
    else if ( !str_prefix_cs("{bY", arg))  sprintf(color, "%s&%d", color, C_B_YEL);
    else if ( !str_prefix_cs("{lg", arg))  sprintf(color, "%s&%d", color, C_GRN);
    else if ( !str_prefix_cs("{bG", arg))  sprintf(color, "%s&%d", color, C_B_GRN);
    else if ( !str_prefix_cs("{lb", arg))  sprintf(color, "%s&%d", color, C_BLU);
    else if ( !str_prefix_cs("{bB", arg))  sprintf(color, "%s&%d", color, C_B_BLU);
    else if ( !str_prefix_cs("{lc", arg))  sprintf(color, "%s&%d", color, C_CYN);
    else if ( !str_prefix_cs("{bC", arg))  sprintf(color, "%s&%d", color, C_B_CYN);
    else if ( !str_prefix_cs("{lr", arg))  sprintf(color, "%s&%d", color, C_RED);
    else if ( !str_prefix_cs("{bR", arg))  sprintf(color, "%s&%d", color, C_B_RED);
    else if ( !str_prefix_cs("{lm", arg))  sprintf(color, "%s&%d", color, C_MAG);
    else if ( !str_prefix_cs("{bM", arg))  sprintf(color, "%s&%d", color, C_B_MAG);
    if (strlen(arg) > 3) {
        if ( !str_suffix_cs("{lw", arg))       sprintf(color, "%s&%d", color, C_WHT);
        else if ( !str_suffix_cs("{bW", arg))  sprintf(color, "%s&%d", color, C_B_WHT);
        else if ( !str_suffix_cs("{bG", arg))  sprintf(color, "%s&%d", color, C_B_GRN);
        else if ( !str_suffix_cs("{lg", arg))  sprintf(color, "%s&%d", color, C_GRN);
        else if ( !str_suffix_cs("{ly", arg))  sprintf(color, "%s&%d", color, C_YEL);
        else if ( !str_suffix_cs("{bY", arg))  sprintf(color, "%s&%d", color, C_B_YEL);
        else if ( !str_suffix_cs("{bB", arg))  sprintf(color, "%s&%d", color, C_B_BLU);
        else if ( !str_suffix_cs("{lb", arg))  sprintf(color, "%s&%d", color, C_BLU);
        else if ( !str_suffix_cs("{bC", arg))  sprintf(color, "%s&%d", color, C_B_CYN);
        else if ( !str_suffix_cs("{bR", arg))  sprintf(color, "%s&%d", color, C_B_RED);
        else if ( !str_suffix_cs("{lm", arg))  sprintf(color, "%s&%d", color, C_MAG);
        else if ( !str_suffix_cs("{bM", arg))  sprintf(color, "%s&%d", color, C_B_MAG);
    }
    if (str_cmp(previousColor, arg)) {
        sprintf(previousColor, "%s", arg);
        return color;
    }
    return "";
}

// Free's all visited rooms from the map_visit hash table
void free_map_memory(void) {
    MAP_VISIT *mapv, *mapv_next;
    int hash;
    for (hash = 0;hash < MAX_MAP_HASH;hash ++) {
        for (mapv = map_visit[hash];mapv;mapv = mapv_next) {
            mapv_next = mapv->next;
            free(mapv);
        }
        map_visit[hash] = NULL;
    }
    MAP_QD *mapqd, *mapqd_next;
    // Lets add it at the end.  It has to wait in line..
    for (mapqd = map_queue;mapv;mapqd = mapqd_next) {
        mapqd_next = mapqd->next;
        free(mapqd);
    }
    unsigned short int x, y;
    for (x = 0;x < MAX_MAP;x ++)
        for (y = 0;y < MAX_MAP;y ++)
            map[x][y] = -1;
}

// Adds a room as visited
void add_visited(unsigned int room) {
    int hash = room % MAX_MAP_HASH;
    MAP_VISIT *mv = malloc(sizeof( *mv));
    mv->room = room;
    mv->next = map_visit[hash];
    map_visit[hash] = mv;
}

// Returns T/F if room was visited
bool_t has_visited(unsigned int room) {
    MAP_VISIT *map;
    unsigned int hash = room % MAX_MAP_HASH;

    for (map = map_visit[hash];map;map = map->next)
        if (map->room == room)
            return  TRUE;
    return FALSE;
}

void init_mp(CHAR_DATA *ch, char* argument) {
    mp = malloc(sizeof( *mp));
    mp->desc = calloc(4608, sizeof(char)); // max string
    mp->desc[0] = '\0';
    short int size = -1;
    char arg[MSL];
    mp->area = FALSE;
    mp->tight = FALSE;
    mp->fog = FALSE;
    mp->size = MAP_MID;
    mp->border = TRUE;
    mp->doors = TRUE;
    mp->depth = -1;
    mp->mobs = FALSE;
    mp->ter = FALSE;
    mp->text = FALSE;
    mp->players = FALSE;
    mp->doShare = FALSE;
    mp->doForget = FALSE;
    mp->mxp = FALSE;
    mp->share = NULL;
    mp->doStoreCoord = FALSE;
    mp->color = !ch ? FALSE : IS_SET(ch->comm, COMM_COLOR);
    for (argument = one_argument(argument, arg);arg[0] != '\0';) { // For every argument given..
        if (is_number(arg)) {
            size = strtol(arg, NULL, 10);
            if (size >= MAP_MID) {
                mp->size = MAP_MID;
                chprintf(ch, "Maximum map size is:%d", MAP_MID);
            }
            else if (size > 0)
                mp->size = size;
            else if (size < 0)
                mp->depth = size * -1;
        }
        else if ( !str_prefix(arg, "share")) {
            mp->doShare = TRUE;
            break;
        }
        else if ( !str_prefix(arg, "forget")) {
            mp->doForget = TRUE;
            break;
        }
        else if ( !str_prefix(arg, "coord"))    mp->doStoreCoord = TRUE;
        else if ( !str_prefix(arg, "world"))    mp->area = TRUE;
        else if ( !str_prefix(arg, "tight"))    mp->tight = TRUE;
        else if ( !str_prefix(arg, "fog"))      mp->fog = TRUE;
        else if ( !str_prefix(arg, "border"))   mp->border = FALSE;
        else if ( !str_prefix(arg, "doors"))    mp->doors = FALSE;
        else if ( !str_prefix(arg, "mobs"))     mp->mobs = TRUE;
        else if ( !str_prefix(arg, "terrain"))  mp->ter = TRUE;
        else if ( !str_prefix(arg, "text"))     mp->text = TRUE;
        else if ( !str_prefix(arg, "mxp"))      mp->mxp = TRUE;
        else if ( !str_prefix(arg, "players") && IS_IMMORTAL(ch))
            mp->players = TRUE;

        arg[0] = '\0';
        argument = one_argument(argument, arg);
    }
    if (ch && ch->in_room) // Can call function with NULL (if used externally)
        sprintf(mp->desc, "%s%s", get_color_ch(ch, COL_NORM), ch->in_room->desc);

    if ( !mp->tight && mp->size > 0) // It's not a tight map, so we double it's size to make up for the extra links
        mp->size *= 2;

    if (mp->doShare) {
        char target[MAX_INPUT_LENGTH];
        argument = one_argument(argument, target);
        one_argument(argument, mp->shareTarget);
        //get the player with which you want to share the map
        mp->share = get_char_room(ch, target);
    }

    if (mp->doForget)
        one_argument(argument, mp->shareTarget);
}

void free_mp() {
    if (!mp)
        return;
    free_string(&mp->desc);
    free(mp);
    mp = NULL;
}

// In a function incase I want to mod it later
char *finish_map_text(void) {
    return mp->desc;
}

// Returns the colour of the link
char *get_linkc(int link) {
    switch (link) {
        case NS_2WAYD:
        case NS_1WAYND:
        case NS_1WAYSD:
        case NS_HITD:
        case EW_2WAYD:
        case EW_1WAYED:
        case EW_1WAYWD:
        case EW_HITD:
        case NS_UNND:
        case NS_UNSD:
        case EW_UNED:
        case EW_UNWD:
            return mpcolour("{bG");
    }
    // Something goofed
    return mpcolour("{lg");
}

// Returns the character of the link
char *get_link(int link) {
    switch (link) {
        case NS_2WAY: case NS_2WAYD:    return "|";
        case NS_1WAYN:case NS_1WAYND:   return "^";
        case NS_1WAYS:case NS_1WAYSD:   return "v";
        case NS_HIT:  case NS_HITD:     return "/";
        case EW_2WAY: case EW_2WAYD:    return "-";
        case EW_1WAYE:case EW_1WAYED:   return ">";
        case EW_1WAYW:case EW_1WAYWD:   return "<";
        case EW_HIT:  case EW_HITD:     return "=";
        case NS_UNN:  case NS_UNND:     return ",";
        case NS_UNS:  case NS_UNSD:     return "`";
        case EW_UNE:  case EW_UNED:     return ")";
        case EW_UNW:  case EW_UNWD:     return "(";
    }
    return "?";
}

char* add_mxp_path(unsigned int vnum, char *room) {
    if (!mp || !mp->mxp)
        return room;
    static char buf[MAX_INPUT_LENGTH];
    sprintf(buf, "\t<send \"path %d\" hint=\"goto\">%s\t</send>", vnum, room);
    return buf;
}

/**
 * if ch param is used this will return the terrain character in wich the player is like it would
 * appear with map terrain param
 * do NOT use it without ch param unless you initialized map before or it will crash
 */
char *get_mroom(CHAR_DATA *ch, unsigned int room) {
    ROOM_INDEX_DATA *rm = ch ? ch->in_room : get_room_index(room);
    if (!rm)
        return " ";
    SHOP_DATA *shop = NULL;
    unsigned int vnum = rm->vnum;
    // Check for a shop.
    CHAR_DATA *keeper;
    for (keeper = rm->people;keeper;keeper = keeper->next_in_room) {
        if (IS_NPC(keeper) && (shop = keeper->pIndexData->pShop))
            break;
    }
    if (ch || mp->ter) {// Terrain mode
        switch (rm->sectorType) {
            case SECT_INSIDE:       return add_mxp_path(vnum, "+");
            case SECT_CITY:         return add_mxp_path(vnum, "#");
            case SECT_FIELD:        return add_mxp_path(vnum, "o");
            case SECT_FOREST:       return add_mxp_path(vnum, "T");
            case SECT_HILLS:        return add_mxp_path(vnum, "n");
            case SECT_MOUNTAIN:     return add_mxp_path(vnum, "M");
            case SECT_WATER_SWIM:   return add_mxp_path(vnum, "~");
            case SECT_WATER_NOSWIM: return add_mxp_path(vnum, "~");
            case SECT_AIR:          return add_mxp_path(vnum, "^");
            case SECT_DESERT:       return add_mxp_path(vnum, ".");
            case SECT_FIRE:         return add_mxp_path(vnum, "!");
            case SECT_SUBZERO:      return add_mxp_path(vnum, "!");
            case SECT_TORNADO:      return add_mxp_path(vnum, "@");
            case SECT_QUICKSAND:    return add_mxp_path(vnum, "s");
            case SECT_ACID:         return add_mxp_path(vnum, "!");
            case SECT_UWATER:       return add_mxp_path(vnum, "=");
        }
        return add_mxp_path(vnum, "*");
    }
    // End Terrain mode

    if (IS_SET (rm->room_flags, ROOM_PET_SHOP)) // How about a pet shop?
        return add_mxp_path(vnum, "P");
    if (shop) // Do we have a shop??
        return add_mxp_path(vnum, "S");

    // Default.
    return add_mxp_path(vnum, "*");
}

char** playerInLine = NULL;


char *get_terrain_color(ROOM_INDEX_DATA *rm) {
    if (!rm)
        return mpcolour("{lg");
    switch (rm->sectorType) {
        case SECT_INSIDE:       return mpcolour("{lw");
        case SECT_CITY:         return mpcolour("{bW");
        case SECT_FIELD:        return mpcolour("{ly");
        case SECT_FOREST:       return mpcolour("{bG");
        case SECT_HILLS:        return mpcolour("{lg");
        case SECT_MOUNTAIN:     return mpcolour("{bY");
        case SECT_WATER_SWIM:   return mpcolour("{bB");
        case SECT_WATER_NOSWIM: return mpcolour("{lb");
        case SECT_AIR:          return mpcolour("{bC");
        case SECT_DESERT:       return mpcolour("{bR");
        case SECT_FIRE:         return mpcolour("{GR");
        case SECT_SUBZERO:      return mpcolour("{GW");
        case SECT_TORNADO:      return mpcolour("{GC");
        case SECT_QUICKSAND:    return mpcolour("{GY");
        case SECT_ACID:         return mpcolour("{BM");
        case SECT_UWATER:       return mpcolour("{GB");
        default:
            return mpcolour("{lg");
    }
}

/* Returns the colour code of the room */
char *get_mroomc(CHAR_DATA *ch, int room, short int line) {
    CHAR_DATA *mob;
    unsigned char mtype = 0;
    bool_t ptype = 0;

    ROOM_INDEX_DATA *rm = get_room_index(room);
    for (mob = rm->people;mob;mob = mob->next_in_room) {
        if (IS_NPC(mob)) {//only show mobs that are sentinel, it is a map, not a radar
            if (IS_SET(mob->act, ACT_SENTINEL)) {
                if (IS_SET(mob->act, ACT_AGGRESSIVE))
                    mtype = 2;
                mtype = UMAX(mtype, 1);
            }
        }
        else {
            ptype = TRUE; //add the char to the list to display on this line
            if (strlen(playerInLine[line]) + strlen(mob->short_descr) < 490) {
                if (strlen(playerInLine[line]) < 495) //because it is define to 500
                    sprintf(playerInLine[line], "%s %s ", playerInLine[line], "...");
            }
            else
                sprintf(playerInLine[line], "%s %s ", playerInLine[line], mob->short_descr);
        }
    }

    /* Terrain mode colour, if needed.  This will override exit and mob colours.  Warning:  May cause disconnect on
     * large display due to extra colour codes - hopefully the new colour control code has fixed that */
    if (mp->ter)
        return get_terrain_color(rm);


    // Does it have an up and a down? Can they get there?
    if ((rm->exit[DIR_UP] && can_see_room(ch, rm->exit[DIR_UP]->u1.to_room)) && (rm->exit[DIR_DOWN] && can_see_room(ch,
                    rm->exit[DIR_DOWN]->u1.to_room)) && (mp->doors || ( !IS_SET(rm->exit[DIR_UP]->exit_info, EX_CLOSED)
                    || !IS_SET(rm->exit[DIR_DOWN]->exit_info, EX_CLOSED)))) {
        if (ptype)
            return mpcolour("{GY{bR");
        if (mp->mobs && (mtype == 1 || mtype == 2))
            return mtype == 1 ? mpcolour("{GB{bR") : mpcolour("{GR{bR");
        return mpcolour("{bR");
    }
    if (rm->exit[DIR_UP] && can_see_room(ch, rm->exit[DIR_UP]->u1.to_room) && (mp->doors
                    || !IS_SET(rm->exit[DIR_UP]->exit_info, EX_CLOSED))) {// Going up?
        if (ptype)
            return mpcolour("{GY{bM");
        if (mp->mobs && (mtype == 1 || mtype == 2))
            return mtype == 1 ? mpcolour("{GB{bM") : mpcolour("{GR{bM");
        return mpcolour("{bM");
    }
    if (rm->exit[DIR_DOWN] && can_see_room(ch, rm->exit[DIR_DOWN]->u1.to_room) && (mp->doors
                    || !IS_SET(rm->exit[DIR_DOWN]->exit_info, EX_CLOSED))) {
        if (ptype)
            return mpcolour("{GY{bB");
        if (mp->mobs && (mtype == 1 || mtype == 2))
            return mtype == 1 ? mpcolour("{GB{bB") : mpcolour("{GR{bB");
        return mpcolour("{bB");
    }
    // Default.
    if (ptype)
        return mpcolour("{GY{bG");
    if (mp->mobs && (mtype == 1 || mtype == 2))
        return mtype == 1 ? mpcolour("{GB{bG") : mpcolour("{GR{bG");
    return mpcolour("{lg");
}

// Finds out what type of link to put between rooms
// returns the link number
char put_link(ROOM_INDEX_DATA *room, int next, unsigned char dir) {
    ROOM_INDEX_DATA *other;
    EXIT_DATA *exit;

    // Get the reverse dir
    unsigned char dir2 = rev_dir[dir];

    if (next > 0) {// Do we have a room there already?
        other = get_room_index(next);
        exit = other->exit[dir2];
    }
    else {
        exit = room->exit[dir]->u1.to_room->exit[dir2];
        other = room->exit[dir]->u1.to_room;
    }

    EXIT_DATA *org = room->exit[dir];

    if (dir == DIR_NORTH) {// I'm going north
        if (!exit) {// 1 way?
            if (other && org->u1.to_room == other) // Is the link to that room?
                return IS_SET(org->exit_info, EX_ISDOOR) ? NS_1WAYND : NS_1WAYN;
            // Goes that way but not to that room
            return IS_SET(org->exit_info, EX_ISDOOR) ? NS_UNND : NS_UNN;
        }
        if (exit->u1.to_room == room) // 2 way?
            return (IS_SET(exit->exit_info, EX_ISDOOR) || IS_SET(org->exit_info, EX_ISDOOR)) ? NS_2WAYD : NS_2WAY;
        if (exit->u1.to_room != room) // 2 way collide?
            return (IS_SET(exit->exit_info, EX_ISDOOR) || IS_SET(org->exit_info, EX_ISDOOR)) ? NS_HITD : NS_HIT;
        return -1;
    }
    if (dir == DIR_SOUTH) {// I'm going south
        if (!exit) {// 1 way?
            if (org->u1.to_room == other) // Is the link to that room?
                return IS_SET(org->exit_info, EX_ISDOOR) ? NS_1WAYSD : NS_1WAYS;
            // Goes that way but not to that room
            return IS_SET(org->exit_info, EX_ISDOOR) ? NS_UNSD : NS_UNS;
        }
        if (exit->u1.to_room == room) // 2 way?
            return (IS_SET(exit->exit_info, EX_ISDOOR) || IS_SET(org->exit_info, EX_ISDOOR)) ? NS_2WAYD : NS_2WAY;
        if (exit->u1.to_room != room) // 2 way collide?
            return (IS_SET(exit->exit_info, EX_ISDOOR) || IS_SET(org->exit_info, EX_ISDOOR)) ? NS_HITD : NS_HIT;
        return -1;
    }
    if (dir == DIR_EAST) {// I'm going east
        if (!exit) {// 1 way?
            if (org->u1.to_room == other) // Is the link to that room?
                return IS_SET(org->exit_info, EX_ISDOOR) ? EW_1WAYED : EW_1WAYE;
            // Goes that way but no to that room
            return IS_SET(org->exit_info, EX_ISDOOR) ? EW_UNED : EW_UNE;
        }
        if (exit->u1.to_room == room) // 2 way?
            return (IS_SET(exit->exit_info, EX_ISDOOR) || IS_SET(org->exit_info, EX_ISDOOR)) ? EW_2WAYD : EW_2WAY;
        if (exit->u1.to_room != room) // 2 way collide?
            return (IS_SET(exit->exit_info, EX_ISDOOR) || IS_SET(org->exit_info, EX_ISDOOR)) ? EW_HITD : EW_HIT;
        return -1;
    }
    if (dir == DIR_WEST) {// I'm going west
        if (!exit) {// 1 way?
            if (org->u1.to_room == other) // Is the link to that room?
                return IS_SET(org->exit_info, EX_ISDOOR) ? EW_1WAYWD : EW_1WAYW;
            return IS_SET(org->exit_info, EX_ISDOOR) ? EW_UNWD : EW_UNW;
        }
        if (exit->u1.to_room == room)
            return (IS_SET(exit->exit_info, EX_ISDOOR) || IS_SET(org->exit_info, EX_ISDOOR)) ? EW_2WAYD : EW_2WAY;
        if (exit->u1.to_room != room)
            return (IS_SET(exit->exit_info, EX_ISDOOR) || IS_SET(org->exit_info, EX_ISDOOR)) ? EW_HITD : EW_HIT;
        return -1;
    }
    return -1;
}

// Returns a new map queue data
MAP_QD *new_map_qd(void) {
    MAP_QD *map = malloc(sizeof( *map));
    map->next = NULL;
    map->room = 0;
    map->x = 0;
    map->y = 0;
    return map;
}

// Add an MQD to the queue
void add_map_qd(MAP_QD *mqd) {
    // Mark the room as visited
    add_visited(mqd->room);

    // Is there a mqd in the queue?  If not lets start it off
    if (!map_queue) {
        map_queue = mqd;
        return;
    }

    MAP_QD *map;
    // Lets add it at the end.  It has to wait in line..
    for (map = map_queue;map;map = map->next) {
        if (map->next == NULL) {
            map->next = mqd;
            break;
        }
    }
}

// Returns the next queue waiting in line
MAP_QD *next_map_qd(void) {
    if ( !map_queue)
        return NULL;

    MAP_QD *map = map_queue;
    map_queue = map_queue->next;
    return map;
}

unsigned char const mapdir[] = {DIR_NORTH, DIR_EAST, DIR_SOUTH, DIR_WEST};

// Uses a combination of a queue and recursion.  Takes you, does all the rooms around you
// After that, it does all those rooms, then the rooms those generated..  until it stops
void add_to_map(CHAR_DATA *ch, ROOM_INDEX_DATA *room, unsigned char x, unsigned char y, short int depth) {
    EXIT_DATA *exit;
    MAP_QD *qd;
    bool_t setCoordinate = FALSE;
    unsigned char idir, dir, num;

    // Go in all 4 directions NESW, add the lines then add to map the next room
    for (idir = 0;idir < 4;idir ++) {
        dir = mapdir[idir];
        exit = room->exit[dir];
        if ( !exit || !exit->u1.to_room)
            continue;

        setCoordinate = FALSE;
        // Add your conditions for not mapping that room here.
        if ( (ch && !(is_room_visited(ch, exit->u1.to_room) || !can_see_room(ch, exit->u1.to_room))) || (mp->fog
                        && exit->u1.to_room->sectorType == SECT_MOUNTAIN) // Is fog mod on?
                        || ( !mp->doors && IS_SET(exit->exit_info, EX_CLOSED))
                        || (mp->depth >= 0 && depth == mp->depth))
            continue;
        num = mp->tight ? 1 : 2; // Num is used to control if we do every second point as a room, or every point
        switch (dir) {
            case DIR_NORTH: // y - 1
                if ( !mp->tight) {// Do we want our room links?
                    if (y - 2 < 0) // Will we oversteap our bounds if we put try to put in this link?
                        break;
                    map[x][y - 1] = put_link(room, map[x][y - 2], dir);
                }

                // Cases where we WON'T add this room, and thereby search it
                if (y - num < 0 || map[x][y - num] != -1 || (room->area != exit->u1.to_room->area && !mp->area)
                                || has_visited(exit->u1.to_room->vnum))
                    break;
                // It's passed the test, lets add it
                map[x][y - num] = exit->u1.to_room->vnum;
                qd = new_map_qd();
                qd->x = x;
                qd->y = y - num;
                qd->room = exit->u1.to_room->vnum;
                qd->depth = depth + 1;
                add_map_qd(qd);
                setCoordinate = TRUE;
            break;

            case DIR_EAST: // x + 1
                if ( !mp->tight) {// See all comments above.
                    if (x + 2 > MAX_MAP - 1)
                        break;
                    map[x + 1][y] = put_link(room, map[x + 2][y], dir);
                }

                if (x + num > MAX_MAP - 1 || map[x + num][y] != -1 || (room->area != exit->u1.to_room->area
                                && !mp->area) || has_visited(exit->u1.to_room->vnum))
                    break;
                map[x + num][y] = exit->u1.to_room->vnum;
                qd = new_map_qd();
                qd->x = x + num;
                qd->y = y;
                qd->room = exit->u1.to_room->vnum;
                qd->depth = depth + 1;
                add_map_qd(qd);
                setCoordinate = TRUE;
            break;

            case DIR_SOUTH: // y + 1
                if ( !mp->tight) {
                    if (y + 2 > MAX_MAP - 1)
                        break;
                    map[x][y + 1] = put_link(room, map[x][y + 2], dir);
                }

                if (y + num > MAX_MAP - 1 || map[x][y + num] != -1 || (room->area != exit->u1.to_room->area
                                && !mp->area) || has_visited(exit->u1.to_room->vnum))
                    break;
                map[x][y + num] = exit->u1.to_room->vnum;
                qd = new_map_qd();
                qd->x = x;
                qd->y = y + num;
                qd->room = exit->u1.to_room->vnum;
                qd->depth = depth + 1;
                add_map_qd(qd);
                setCoordinate = TRUE;
            break;

            case DIR_WEST: // x - 1
                if ( !mp->tight) {
                    if (x - 2 < 0)
                        break;
                    map[x - 1][y] = put_link(room, map[x - 2][y], dir);
                }

                if (x - num < 0 || map[x - num][y] != -1 || (room->area != exit->u1.to_room->area && !mp->area)
                                || has_visited(exit->u1.to_room->vnum))
                    break;
                map[x - num][y] = exit->u1.to_room->vnum;
                qd = new_map_qd();
                qd->x = x - num;
                qd->y = y;
                qd->room = exit->u1.to_room->vnum;
                qd->depth = depth + 1;
                add_map_qd(qd);
                setCoordinate = TRUE;
            break;
        } // End of dir switch
        if (setCoordinate && mp->doStoreCoord) {
            ROOM_INDEX_DATA *room = get_room_index(qd->room );
            room->coord.x = qd->x;
            room->coord.y = qd->y;
        }

    } // end of searching NESW from our point

    MAP_QD *next_qd = next_map_qd(); // But wait!  Is there another room in the queue to search?
    if ( !next_qd)
        return;

    // Looks like it.  Lets search this point just like we did when we first started this whole thing
    unsigned int qroom = next_qd->room;
    unsigned char qx = next_qd->x;
    unsigned char qy = next_qd->y;
    unsigned char qdepth = next_qd->depth;
    // This way I can free the qdata before the recursive call, meaning it's available right away instead of after all the recursion is done.

    free(next_qd);
    // Is it a valid room?  Lets double check
    if (get_room_index(qroom))
        add_to_map(ch, get_room_index(qroom), qx, qy, qdepth);

}

// make the map.  Meat of the whole thing
void make_map_no_char(ROOM_INDEX_DATA* roomStart, unsigned char x, unsigned char y) {
    if (!roomStart)
        return;
    // Lets start out with a fresh grid and hash table
    free_map_memory();

    // Add your starting point
    map[x][y] = roomStart->vnum;
    if (mp->doStoreCoord) {
        roomStart->coord.x = x;
        roomStart->coord.y = y;
    }

    // Say you've visited your starting point
    add_visited(roomStart->vnum);

    // Use your starting point to begin the graphing process, with you in the middle
    add_to_map(NULL, roomStart, x, y, 0);
}

// make the map.  Meat of the whole thing
void make_map(CHAR_DATA *ch, unsigned char x, unsigned char y) {
    if (!ch)
        return;
    // Lets start out with a fresh grid and hash table
    free_map_memory();

    // Add your starting point
    map[x][y] = ch->in_room->vnum;
    if (mp->doStoreCoord) {
        ch->in_room->coord.x = x;
        ch->in_room->coord.y = y;
    }

    // Say you've visited your starting point
    add_visited(ch->in_room->vnum);

    // Use your starting point to begin the graphing process, with you in the middle
    add_to_map(ch, ch->in_room, x, y, 0);
}


// get_?_start and get_?_to  functions are used to trim the map down so only the smallet, visible map is displayed
// returns their appropriate boundaries
unsigned char get_x_start(void) {
    if (mp->size < MAP_MID)
        return MAP_MID - mp->size;

    unsigned char y, x;
    for (x = 0;x < MAX_MAP;x ++)
        for (y = 0;y < MAX_MAP;y ++)
            if (map[x][y] != -1)
                return x;

    return MAX_MAP - 1;
}
// See above
unsigned char get_y_start(void) {
    if (mp->size < MAP_MID)
        return MAP_MID - mp->size;

    unsigned char y, x;
    for (y = 0;y < MAX_MAP;y ++)
        for (x = 0;x < MAX_MAP;x ++)
            if (map[x][y] != -1)
                return y;

    return MAX_MAP - 1;
}
// See above
unsigned char get_x_to(void) {
    if (mp->size < MAP_MID)
        return MAX_MAP - MAP_MID + mp->size - 1;

    unsigned char y, x;
    for (x = MAX_MAP - 1;x != 0;x --)
        for (y = 0;y < MAX_MAP;y ++)
            if (map[x][y] != -1)
                return x;
    return 0;
}

// See above
unsigned char get_y_to(void) {
    if (mp->size < MAP_MID)
        return MAX_MAP - MAP_MID + mp->size - 1;

    unsigned char y, x;
    for (y = MAX_MAP - 1;y != 0;y --)
        for (x = 0;x < MAX_MAP;x ++)
            if (map[x][y] != -1)
                return y;
    return 0;
}

char* get_map(CHAR_DATA *ch, unsigned char xx, unsigned char yy) {
    // Get our trimming thresholds
    unsigned char y_from = get_y_start();
    unsigned char y_to = get_y_to();
    unsigned char x_from = get_x_start();
    unsigned char x_to = get_x_to();
    playerInLine = malloc((y_to + 1) * sizeof(char*));
    unsigned char i;
    for (i = 0;i <= y_to;i ++) {
        playerInLine[i] = malloc(500);
        playerInLine[i][0] = '\0';
    }

    // Start out our buffer
    char buf[MSL];
    static char totalbuf[MSL * 6]; //this entirely depends on how large your world is
    totalbuf[0] = '\0';
    unsigned char x, y;
    if (mp->border) { //fanciful border
        sprintf(totalbuf, "%s(@", mpcolourCH(ch, "{lm", FALSE));
        for (x = 0;x <= (x_to - x_from) / 2;x ++)
            strcat(totalbuf,  "*@");
        strcat(totalbuf,(x_to - x_from) % 2 == 1 ? "))\n\r" : ")\n\r");
    }

    for (y = y_from;y <= y_to;y ++) {
        buf[0] = '\0';
        for (x = x_from;x <= x_to;x ++) {
            if (map[x][y] != -1) {// Is there something here?
                if (map[x][y] < -1) // ? < -1 means a link type.  Lets buffer it
                    sprintf(buf, "%s%s%s", buf, get_linkc(map[x][y]), get_link(map[x][y]));
                else if (xx == x && yy == y) {// This is where we started.  Make sure to mark it with a special character
                    sprintf(buf, "%s%s@", buf, mpcolour("{bR"));
                    ROOM_INDEX_DATA *rm = get_room_index(map[xx][yy]);
                    CHAR_DATA *player; //for other rooms we already parse the char list in each of them
                    for (player = rm->people;player;player = player->next_in_room)
                        if (IS_PC(player))
                            sprintf(playerInLine[yy], "%s %s ", playerInLine[yy], player->short_descr);
                }
                else // Must be a room
                    sprintf(buf, "%s%s%s", buf, get_mroomc(ch, map[x][y], y), get_mroom(NULL, map[x][y]));
            }
            else // Nothing's here..  Make a blank space
                sprintf(buf, "%s%s ", buf, mpcolourCH(ch, "{nr", FALSE));
        }
        if (mp->border)
            sprintf(totalbuf, "%s%s((%s%s))%s\n\r", totalbuf, mpcolourCH(ch, "{lm", TRUE), buf, mpcolourCH(ch, "{lm", FALSE),
                            mp->players && playerInLine[y] ? playerInLine[y] : "");
        else
            sprintf(totalbuf, "%s%s%s%s\n\r", totalbuf, buf, mpcolourCH(ch, "{nr", TRUE),
                            mp->players && playerInLine[y] ? playerInLine[y] : "");
    }

    if (mp->border) {
        sprintf(totalbuf, "%s%s(@", totalbuf, mpcolourCH(ch, "{lm", TRUE));
        for (x = 0;x <= (x_to - x_from) / 2;x ++)
            strcat(totalbuf, "*@");
        if ((x_to - x_from) % 2 == 1)
            strcat(totalbuf, ")");
        sprintf(totalbuf, "%s)%s\n\r", totalbuf, mpcolour("{lw"));
    }

    if (mp->text)
        sprintf(totalbuf, "%s%s%s", totalbuf, mpcolourCH(ch, "{nr", FALSE), finish_map_text());

    sprintf(totalbuf, "%s%s", totalbuf, mpcolourCH(ch, "{nr", FALSE));
    //log_param("map max size:%d currentsize:%d", MSL*12, strlen(totalbuf));
    // We're done, so free the visits
    for (i = 0;i <= y_to;i ++)
        free(playerInLine[i]);
    free(playerInLine);

    free_map_memory();
    free_mp();
    return totalbuf;
}

// The map display function
void show_map(CHAR_DATA *ch, unsigned char xx, unsigned char yy) {
    send_to_char(get_map(ch, xx, yy), ch);
}

// Command to start it all.
bool_t do_map(CHAR_DATA *ch, char *argument) {
    if (!IS_PC(ch)) {
        send_to_char("Huh?\n\r", ch);
        return FALSE;
    }

    if (ch->fighting) {
        send_to_char("You are quite too busy for that right now, aren't you ?\n\r", ch);
        return FALSE;
    }

    if (IS_AFFECTED(ch,AFF_BLIND)) {
        send_to_char("You cannot see !\n\r", ch);
        return FALSE;
    }
    init_mp(ch, argument);
    if (mp->doShare) {
        CHAR_DATA *victim = mp->share;
        char shareTarget[MAX_INPUT_LENGTH];
        sprintf(shareTarget, "%s", mp->shareTarget);
        free_mp();
        if ( !victim) {
            send_to_char("Share your map with who?\n\r", ch);
            return FALSE;
        }
        if (victim == ch) {
            send_to_char("You swap your map from one pocket to another, happy?\n\r", ch);
            return FALSE;
        }
        if (victim->fighting) {
            chprintf(ch, "%s is too busy to copy your map right now!\n\r", victim->short_descr);
            return FALSE;
        }
        if (IS_NPC(victim)) {
            chprintf(ch, "%s has no use for it.\n\r", victim->short_descr);
            return FALSE;
        }

        if (shareTarget[0] == '\0') {
            send_to_char("Share all your map or only one area?\n\r", ch);
            return FALSE;
        }

        unsigned int sn;
        if ( !str_cmp(shareTarget, "all")) {
            //start to replicate the whole map
            for (sn = 0;sn <= top_vnum_room;sn ++)
                if (is_room_sn_visited(ch, sn))
                    set_room_sn_visited(victim, sn);
            chprintf(ch, "%s now knows everything you do.\n\r", victim->short_descr);
            chprintf(victim, "You now know everything %s did.\n\r", ch->short_descr);
            return TRUE;
        }
        AREA_DATA *pArea;
        for (pArea = area_first;pArea;pArea = pArea->next) {
            if ( !strstr(lower_str(pArea->name), shareTarget))
                continue;

            for (sn = pArea->lvnum;sn <= pArea->uvnum;sn ++)
                if (is_room_sn_visited(ch, sn))
                    set_room_sn_visited(victim, sn);

            chprintf(ch, "%s now knows everything you do on %s.\n\r", victim->short_descr, pArea->name);
            chprintf(victim, "You now know everything %s did on %s.\n\r", ch->short_descr, pArea->name);

            return TRUE;
        }
        send_to_char("You know no area by that name.\n\r", ch);
        return FALSE;
    }
    if (mp->doForget) {
        char shareTarget[MAX_INPUT_LENGTH];
        sprintf(shareTarget, "%s", mp->shareTarget);
        free_mp();
        if (shareTarget[0] == '\0') {
            send_to_char("forget all your maps or only one specific area?\n\r", ch);
            return FALSE;
        }
        AREA_DATA *pArea;
        unsigned int sn;
        if ( !str_cmp(shareTarget, "all")) {
            //start to replicate the whole map
            for (sn = 0;sn <= top_vnum_room;sn ++)
                unset_room_sn_visited(ch, sn);
            send_to_char("You forgot everything about the realm.\n\r", ch);
            return TRUE;
        }
        for (pArea = area_first;pArea;pArea = pArea->next) {
            if ( !strstr(lower_str(pArea->name), shareTarget))
                continue;

            for (sn = pArea->lvnum;sn <= pArea->uvnum;sn ++)
                unset_room_sn_visited(ch, sn);

            chprintf(ch, "You forgot everything about %s.\n\r", pArea->name);
            return TRUE;
        }
        send_to_char("You know no area by that name.\n\r", ch);
        return FALSE;
    }
    make_map(ch, MAP_MID, MAP_MID); // Create your map - most important part
    show_map(ch, MAP_MID, MAP_MID); // Use the show_map routine to display it - can create different routines to suit taste or need
    do_wait(ch, PULSE_VIOLENCE);
    return TRUE;
}