/***************************************************************************
* 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;
}