/****************************************************************************
*
* Random Dungeon Mapper, from Netherworld Dreamscape
*
* Written by Morpheus Nosferatu/Jeff Standish
*	morpheus@sage.cc.purdue.edu
*	standish@mentor.cc.purdue.edu
*
* Initial version, March 1993
*
* This program copyright by Jeff Standish, 1993.  All rights reserved.  You
* are free to copy and modify this program, so long as this header remains
* intact and appropriate attribution is given to the author.  This program
* may not be sold for profit without the author's consent.
*
* Compile this program with you favorite C compiler and run it with the
* -help or -h option, which will give usage information for the command
* line options.
*
* This program generates random dungeon maps, printing out LaTeX [default]
* or PostScript files.  For each level of the dungeon, a different file is
* generated by taking the base file name and appending the level of the
* dungeon and the appropriate format suffix [.tex or .ps].  Files can then
* be displayed on a graphics terminal viewer or sent to a laser printer
* capable of graphics (in the case of LaTeX files, after the .tex files
* have been converted to appropriate printable format by a "latex" program).
*
* The size of dungeon can be scaled, preferably in the range of 100 to 500.
* This scale is the maximum width of the dungeon in feet.  Since this program
* treates graphical coordinates in points (1 point = 1/72 inch), the maximum
* good resolution of a dungeon will be with a width of 500.  If the size is
* set larger than this, the lines will overlap and make the map difficult to
* read (even a map of size 500 is somewhat difficult to read due to how small
* it is).
*
* The entrance to the dungeon is either a doorway or a stairway.  The doorway
* will be placed in the middle of the wall such that the door faces in a
* given direction (a door facing north is in the south wall of the dungeon).
* If the entrance is a stairway, it is placed in the middle of the first
* level of the dungeon.
*
****************************************************************************/

#include <stdio.h>

#define MAX(a,b)	((a > b) ? (a) : (b))
#define MIN(a,b)	((a < b) ? (a) : (b))

#define EXITSTACK	0

#define ROOM		1
#define CHAMBER		2
#define PASSAGE		3
#define	JUNCTION	4
#define STAIRWAY	5

#define DOORWAY		10
#define ARCHWAY		11
#define OPENING		12

#define NORTH		20
#define SOUTH		21
#define EAST		22
#define WEST		23
#define UP		24
#define DOWN		25

#define XSIZE		500
#define YSIZE		700

#define PRINTLATEX	1
#define PRINTPS		2

struct queuestruct {
    struct exitstruct  *exit;
    struct queuestruct *queuenext;
};

struct exitstruct {
    int    orientation, x1, y1, x2, y2, exittype, locationtype, level;
    struct locationstruct *parent;
    struct exitstruct *next;
};

struct linestruct {
    int x1, y1, x2, y2;
    struct linestruct *next;
};

struct locationstruct {
    int    xmin, xmax, ymin, ymax, locationtype, locationdata, level, number;
    struct linestruct     *firstline;
    struct locationstruct *next, *previous;
};


typedef struct queuestruct QUEUE;
typedef struct exitstruct  EXITS;
typedef struct linestruct  LINES;
typedef struct locationstruct LOCAT;


static int stairwaydownlines5[4][11][4] = {
    {	{  0, 20,  5, 20 },
	{  0,  0,  0, 20 },
	{  5,  0,  5, 20 },
    	{  0,  4,  5,  4 },
	{  0,  6,  5,  6 },
	{  1,  8,  4,  8 },
	{  1, 10,  4, 10 },
	{  1, 12,  4, 12 },
	{  2, 14,  3, 14 },
	{  2, 16,  3, 16 },
	{  2, 18,  3, 18 } },
    {	{  0,-20, -5,-20 },
	{  0,  0,  0,-20 },
	{ -5,  0, -5,-20 },
   	{  0, -4, -5, -4 },
	{  0, -6, -5, -6 },
	{ -1, -8, -4, -8 },
	{ -1,-10, -4,-10 },
	{ -1,-12, -4,-12 },
	{ -2,-14, -3,-14 },
	{ -2,-16, -3,-16 },
	{ -2,-18, -3,-18 } },
    {	{ 20,  0, 20, -5 },
	{  0,  0, 20,  0 },
	{  0, -5, 20, -5 },
    	{  4,  0,  4, -5 },
	{  6,  0,  6, -5 },
	{  8, -1,  8, -4 },
	{ 10, -1, 10, -4 },
	{ 12, -1, 12, -4 },
	{ 14, -2, 14, -3 },
	{ 16, -2, 16, -3 },
	{ 18, -2, 18, -3 } },
    {	{-20,  0,-20,  5 },
	{  0,  0,-20,  0 },
	{  0,  5,-20,  5 },
    	{ -4,  0, -4,  5 },
	{ -6,  0, -6,  5 },
	{ -8,  1, -8,  4 },
	{-10,  1,-10,  4 },
	{-12,  1,-12,  4 },
	{-14,  2,-14,  3 },
	{-16,  2,-16,  3 },
	{-18,  2,-18,  3 } }
};

static int stairwayuplines5[4][11][4] = {
    {	{  0, 20,  5, 20 },
	{  0,  0,  0, 20 },
	{  5,  0,  5, 20 },
    	{  0, 18,  5, 18 },
	{  0, 16,  5, 16 },
	{  1, 14,  4, 14 },
	{  1, 12,  4, 12 },
	{  1, 10,  4, 10 },
	{  2,  8,  3,  8 },
	{  2,  6,  3,  6 },
	{  2,  4,  3,  4 } },
    {	{  0,-20, -5,-20 },
	{  0,  0,  0,-20 },
	{ -5,  0, -5,-20 },
   	{  0,-18, -5,-18 },
	{  0,-16, -5,-16 },
	{ -1,-14, -4,-14 },
	{ -1,-12, -4,-12 },
	{ -1,-10, -4,-10 },
	{ -2, -8, -3, -8 },
	{ -2, -6, -3, -6 },
	{ -2, -4, -3, -4 } },
    {	{ 20,  0, 20, -5 },
	{  0,  0, 20,  0 },
	{  0, -5, 20, -5 },
    	{ 18,  0, 18, -5 },
	{ 16,  0, 16, -5 },
	{ 14, -1, 14, -4 },
	{ 12, -1, 12, -4 },
	{ 10, -1, 10, -4 },
	{  8, -2,  8, -3 },
	{  6, -2,  6, -3 },
	{  4, -2,  4, -3 } },
    {	{-20,  0,-20,  5 },
	{  0,  0,-20,  0 },
	{  0,  5,-20,  5 },
    	{-18,  0,-18,  5 },
	{-16,  0,-16,  5 },
	{-14,  1,-14,  4 },
	{-12,  1,-12,  4 },
	{-10,  1,-10,  4 },
	{ -8,  2, -8,  3 },
	{ -6,  2, -6,  3 },
	{ -4,  2, -4,  3 } }
};

static int stairwaydownlines10[4][11][4] = {
    {	{  0, 20, 10, 20 },
	{  0,  0,  0, 20 },
	{ 10,  0, 10, 20 },
    	{  1,  4,  9,  4 },
	{  1,  6,  9,  6 },
	{  2,  8,  8,  8 },
	{  2, 10,  8, 10 },
	{  3, 12,  7, 12 },
	{  3, 14,  7, 14 },
	{  4, 16,  6, 16 },
	{  4, 18,  6, 18 } },
    {	{  0,-20,-10,-20 },
	{  0,  0,  0,-20 },
	{-10,  0,-10,-20 },
    	{ -1, -4, -9, -4 },
	{ -1, -6, -9, -6 },
	{ -2, -8, -8, -8 },
	{ -2,-10, -8,-10 },
	{ -3,-12, -7,-12 },
	{ -3,-14, -7,-14 },
	{ -4,-16, -6,-16 },
	{ -4,-18, -6,-18 } },
    {	{ 20,  0, 20,-10 },
	{  0,  0, 20,  0 },
	{  0,-10, 20,-10 },
    	{  4, -1,  4, -9 },
	{  6, -1,  6, -9 },
	{  8, -2,  8, -8 },
	{ 10, -2, 10, -8 },
	{ 12, -3, 12, -7 },
	{ 14, -3, 14, -7 },
	{ 16, -4, 16, -6 },
	{ 18, -4, 18, -6 } },
    {	{-20,  0,-20, 10 },
	{  0,  0,-20,  0 },
	{  0, 10,-20, 10 },
    	{ -4,  1, -4,  9 },
	{ -6,  1, -6,  9 },
	{ -8,  2, -8,  8 },
	{-10,  2,-10,  8 },
	{-12,  3,-12,  7 },
	{-14,  3,-14,  7 },
	{-16,  4,-16,  6 },
	{-18,  4,-18,  6 } }
};

static int stairwayuplines10[4][11][4] = {
    {	{  0, 20, 10, 20 },
	{  0,  0,  0, 20 },
	{ 10,  0, 10, 20 },
    	{  1, 18,  9, 18 },
	{  1, 16,  9, 16 },
	{  2, 14,  8, 14 },
	{  2, 12,  8, 12 },
	{  3, 10,  7, 10 },
	{  3,  8,  7,  8 },
	{  4,  6,  6,  6 },
	{  4,  4,  6,  4 } },
    {	{  0,-20,-10,-20 },
	{  0,  0,  0,-20 },
	{-10,  0,-10,-20 },
    	{ -1,-18, -9,-18 },
	{ -1,-16, -9,-16 },
	{ -2,-14, -8,-14 },
	{ -2,-12, -8,-12 },
	{ -3,-10, -7,-10 },
	{ -3, -8, -7, -8 },
	{ -4, -6, -6, -6 },
	{ -4, -4, -6, -4 } },
    {	{ 20,  0, 20,-10 },
	{  0,  0, 20,  0 },
	{  0,-10, 20,-10 },
    	{ 18, -1, 18, -9 },
	{ 16, -1, 16, -9 },
	{ 14, -2, 14, -8 },
	{ 12, -2, 12, -8 },
	{ 10, -3, 10, -7 },
	{  8, -3,  8, -7 },
	{  6, -4,  6, -6 },
	{  4, -4,  4, -6 } },
    {	{-20,  0,-20, 10 },
	{  0,  0,-20,  0 },
	{  0, 10,-20, 10 },
    	{-18,  1,-18,  9 },
	{-16,  1,-16,  9 },
	{-14,  2,-14,  8 },
	{-12,  2,-12,  8 },
	{-10,  3,-10,  7 },
	{ -8,  3, -8,  7 },
	{ -6,  4, -6,  6 },
	{ -4,  4, -4,  6 } }
};

static int stairwaydownlines15[4][11][4] = {
    {	{  0, 20, 15, 20 },
	{  0,  0,  0, 20 },
	{ 15,  0, 15, 20 },
    	{  0,  4, 15,  4 },
	{  1,  6, 14,  6 },
	{  2,  8, 13,  8 },
	{  3, 10, 12, 10 },
	{  4, 12, 11, 12 },
	{  5, 14, 10, 14 },
	{  6, 16,  9, 16 },
	{  7, 18,  8, 18 } },
    {	{  0,-20,-15,-20 },
	{  0,  0,  0,-20 },
	{-15,  0,-15,-20 },
    	{  0, -4,-15, -4 },
	{ -1, -6,-14, -6 },
	{ -2, -8,-13, -8 },
	{ -3,-10,-12,-10 },
	{ -4,-12,-11,-12 },
	{ -5,-14,-10,-14 },
	{ -6,-16, -9,-16 },
	{ -7,-18, -8,-18 } },
    {	{ 20,  0, 20,-15 },
	{  0,  0, 20,  0 },
	{  0,-15, 20,-15 },
    	{  4,  0,  4,-15 },
	{  6, -1,  6,-14 },
	{  8, -2,  8,-13 },
	{ 10, -3, 10,-12 },
	{ 12, -4, 12,-11 },
	{ 14, -5, 14,-10 },
	{ 16, -6, 16, -9 },
	{ 18, -7, 18, -8 } },
    {	{-20,  0,-20, 15 },
	{  0,  0,-20,  0 },
	{  0, 15,-20, 15 },
    	{ -4,  0, -4, 15 },
	{ -6,  1, -6, 14 },
	{ -8,  2, -8, 13 },
	{-10,  3,-10, 12 },
	{-12,  4,-12, 11 },
	{-14,  5,-14, 10 },
	{-16,  6,-16,  9 },
	{-18,  7,-18,  8 } }
};

static int stairwayuplines15[4][11][4] = {
    {	{  0, 20, 15, 20 },
	{  0,  0,  0, 20 },
	{ 15,  0, 15, 20 },
    	{  0, 18, 15, 18 },
	{  1, 16, 14, 16 },
	{  2, 14, 13, 14 },
	{  3, 12, 12, 12 },
	{  4, 10, 11, 10 },
	{  5,  8, 10,  8 },
	{  6,  6,  9,  6 },
	{  7,  4,  8,  4 } },
    {	{  0,-20,-15,-20 },
	{  0,  0,  0,-20 },
	{-15,  0,-15,-20 },
    	{  0,-18,-15,-18 },
	{ -1,-16,-14,-16 },
	{ -2,-14,-13,-14 },
	{ -3,-12,-12,-12 },
	{ -4,-10,-11,-10 },
	{ -5, -8,-10, -8 },
	{ -6, -6, -9, -6 },
	{ -7, -4, -8, -4 } },
    {	{ 20,  0, 20,-15 },
	{  0,  0, 20,  0 },
	{  0,-15, 20,-15 },
    	{ 18,  0, 18,-15 },
	{ 16, -1, 16,-14 },
	{ 14, -2, 14,-13 },
	{ 12, -3, 12,-12 },
	{ 10, -4, 10,-11 },
	{  8, -5,  8,-10 },
	{  6, -6,  6, -9 },
	{  4, -7,  4, -8 } },
    {	{-20,  0,-20, 15 },
	{  0,  0,-20,  0 },
	{  0, 15,-20, 15 },
    	{-18,  0,-18, 15 },
	{-16,  1,-16, 14 },
	{-14,  2,-14, 13 },
	{-12,  3,-12, 12 },
	{-10,  4,-10, 11 },
	{ -8,  5, -8, 10 },
	{ -6,  6, -6,  9 },
	{ -4,  7, -4,  8 } }
};

static int doorwaylines5[4][11][4] = {
    {	{  0,  0,  1,  0 },
	{  0,  1,  1,  1 },
	{  4,  0,  5,  0 },
	{  4,  1,  5,  1 },
	{  1, -1,  4, -1 },
	{  1,  2,  4,  2 },
	{  1, -1,  1,  2 },
	{  4, -1,  4,  2 } },
    {	{  0,  0, -1,  0 },
	{  0, -1, -1, -1 },
	{ -4,  0, -5,  0 },
	{ -4, -1, -5, -1 },
	{ -1,  1, -4,  1 },
	{ -1, -2, -4, -2 },
	{ -1,  1, -1, -2 },
	{ -4,  1, -4, -2 } },
    {	{  0,  0,  0, -1 },
	{  1,  0,  1, -1 },
	{  0, -4,  0, -5 },
	{  1, -4,  1, -5 },
	{ -1, -1, -1, -4 },
	{  2, -1,  2, -4 },
	{ -1, -1,  2, -1 },
	{ -1, -4,  2, -4 } },
    {	{  0,  0,  0,  1 },
	{ -1,  0, -1,  1 },
	{  0,  4,  0,  5 },
	{ -1,  4, -1,  5 },
	{  1,  1,  1,  4 },
	{ -2,  1, -2,  4 },
	{  1,  1, -2,  1 },
	{  1,  4, -2,  4 } }
};

static int doorwaylines10[4][8][4] = {
    {	{  0,  0,  2,  0 },
	{  0,  1,  2,  1 },
	{  8,  0, 10,  0 },
	{  8,  1, 10,  1 },
	{  2, -1,  8, -1 },
	{  2,  2,  8,  2 },
	{  2, -1,  2,  2 },
	{  8, -1,  8,  2 } },
    {	{  0,  0, -2,  0 },
	{  0, -1, -2, -1 },
	{ -8,  0,-10,  0 },
	{ -8, -1,-10, -1 },
	{ -2,  1, -8,  1 },
	{ -2, -2, -8, -2 },
	{ -2,  1, -2, -2 },
	{ -8,  1, -8, -2 } },
    {	{  0,  0,  0, -2 },
	{  1,  0,  1, -2 },
	{  0, -8,  0,-10 },
	{  1, -8,  1,-10 },
	{ -1, -2, -1, -8 },
	{  2, -2,  2, -8 },
	{ -1, -2,  2, -2 },
	{ -1, -8,  2, -8 } },
    {	{  0,  0,  0,  2 },
	{ -1,  0, -1,  2 },
	{  0,  8,  0, 10 },
	{ -1,  8, -1, 10 },
	{  1,  2,  1,  8 },
	{ -2,  2, -2,  8 },
	{  1,  2, -2,  2 },
	{  1,  8, -2,  8 } }
};

static int doorwaylines15[4][9][4] = {
   {	{  0,  0,  3,  0 },
	{  0,  1,  3,  1 },
	{ 12,  0, 15,  0 },
	{ 12,  1, 15,  1 },
	{  3, -1, 12, -1 },
	{  3,  2, 12,  2 },
	{  3, -1,  3,  2 },
	{  7, -1,  7,  2 },
	{ 12, -1, 12,  2 } },
    {	{  0,  0, -3,  0 },
	{  0, -1, -3, -1 },
	{-12,  0,-15,  0 },
	{-12, -1,-15, -1 },
	{ -3,  1,-12,  1 },
	{ -3, -2,-12, -2 },
	{ -3,  1, -3, -2 },
	{ -7,  1, -7, -2 },
	{-12,  1,-12, -2 } },
    {	{  0,  0,  0, -3 },
	{  1,  0,  1, -3 },
	{  0,-12,  0,-15 },
	{  1,-12,  1,-15 },
	{ -1, -3, -1,-12 },
	{  2, -3,  2,-12 },
	{ -1, -3,  2, -3 },
	{ -1, -7,  2, -7 },
	{ -1,-12,  2,-12 } },
    {	{  0,  0,  0,  3 },
	{ -1,  0, -1,  3 },
	{  0, 12,  0, 15 },
	{ -1, 12, -1, 15 },
	{  1,  3,  1, 12 },
	{ -2,  3, -2, 12 },
	{  1,  3, -2,  3 },
	{  1,  7, -2,  7 },
	{  1, 12, -2, 12 } }
};


#define LATEXHEADERLEN	10

static char *latexheader[LATEXHEADERLEN] = {
"\\documentstyle{report}",
"\\pagestyle{empty}",
"\\addtolength{\\textwidth}{3in}",
"\\addtolength{\\oddsidemargin}{-1.5in}",
"\\addtolength{\\topmargin}{-1.5in}",
"\\addtolength{\\textheight}{3in}",
"",
"\\begin{document}",
"\\begin{picture}(500,730)",
""
};


#define HELPLEN 18

static char *helpmessage[HELPLEN] = {
"    -help\t\t - this help message",
"    -entrancedirection [direction] - select the direction that",
"\t\t\t\t the entrance to the first level is facing,",
"\t\t\t\t select from \"north\", \"south\", \"east\",",
"\t\t\t\t or \"west\", defaults to \"north\"",
"    -entrancetype [type] - select form of entrance to first level,",
"\t\t\t\t select from \"doorway\" or \"stairway\"",
"\t\t\t\t defaults to \"stairway\"",
"    -output [format]\t - set the map file format as either \"latex\"",
"\t\t\t\t or \"postscript\", defaults to \"latex\"",
"    -maxlevel [level]\t - maximum level of the dungeon,",
"\t\t\t\t defaults to level = 1",
"    -mapwidth [size]\t - maximum width of the dungeon, height = width * 1.4",
"\t\t\t\t defaults to size = 250",
"\t\t\t\t warning: strange maps may result if size > 500",
"    -number\t\t - number the rooms on the map",
"    -dubug\t\t - turn on debugging messages",
"    filename\t\t - the base name of output file"
};


char	filename[100];
int	mapwidth = 250, mapheight = 350;
int	entrancetype = STAIRWAY, entrancedirection = NORTH;
int	startlevel = 1, maxlevel = 1;
int	gc = 0, gcnum = 0, printout = PRINTLATEX, numberoutput = 0;
int	debugging = 0;
EXITS	*exitfreestack = NULL;
LOCAT	*firstlocation = NULL, *lastlocation = NULL;
QUEUE	*queuehead = NULL, *queuetail = NULL, *queuefreestack = NULL;

EXITS	*new_exit_node();
EXITS	*dequeue_exit();
LINES	*new_line_node();
LOCAT	*new_location_node();
LOCAT	*box_empty();
QUEUE	*new_queue_node();



/****************************************************************************
*
* main() - read in command line arguments and call the functions for
*		generating and drawing the maps
*
****************************************************************************/

int main(argc, argv)
int  argc;
char **argv;
{
    int ac;

    strcpy(filename, "map");

    if (argc == 1) {
	print_usage(argv[0]);
	exit(0);
    }

	/* read in the various options */
    ac = 1;
    while (ac < argc) {
	if (!strcmp(argv[ac], "-entrancetype")) {
	    ++ac;
	    if (ac < argc) {
		if (!strcmp(argv[ac], "stairway"))
		    entrancetype = STAIRWAY;
		else if (!strcmp(argv[ac], "doorway"))
		    entrancetype = DOORWAY;
		else
		    fatal_error("unknown entrance type \"%s\"", argv[ac]);
	    }
	    else
		fatal_error("entrance type not given in command line");
	}
	else if (!strcmp(argv[ac], "-entrancedirection")) {
	    ++ac;
	    if (ac < argc) {
		if (!strcmp(argv[ac], "north"))
		    entrancedirection = NORTH;
		else if (!strcmp(argv[ac], "south"))
		    entrancedirection = SOUTH;
		else if (!strcmp(argv[ac], "east"))
		    entrancedirection = EAST;
		else if (!strcmp(argv[ac], "west"))
		    entrancedirection = WEST;
		else
		    fatal_error("invalid entrance direction \"%s\"", argv[ac]);
	    }
	    else
		fatal_error("entrance direction not given in command line");
	}
	else if (!strcmp(argv[ac], "-maxlevel")) {
	    ++ac;
	    if (ac < argc) {
		maxlevel = atoi(argv[ac]);
		if (maxlevel < 1)
		    fatal_error("invalid maximum level: %s", argv[ac]);
	    }
	    else
		fatal_error("maximum level value not given");
	}
	else if (!strcmp(argv[ac], "-mapwidth")) {
	    ++ac;
	    if (ac < argc) {
		mapwidth = atoi(argv[ac]);
		mapheight = mapwidth * 1.4;
	    }
	    else
		fatal_error("maximum map size not given");
	}
	else if (!strcmp(argv[ac], "-output")) {
	    ++ac;
	    if (ac < argc) {
		if (!strcmp(argv[ac], "latex"))
		    printout = PRINTLATEX;
		else if (!strcmp(argv[ac], "postscript"))
		    printout = PRINTPS;
		else
		    fatal_error("unknown output format \"%s\"", argv[ac]);
	    }
	    else
		fatal_error("output form not given");
	}
	else if (!strcmp(argv[ac], "-number")) {
	    numberoutput = 1;
	}
	else if (!strcmp(argv[ac], "-debug")) {
	    debugging = 1;
	}
	else if ((!strcmp(argv[ac], "-help")) || (!strcmp(argv[ac], "-h"))) {
	    print_usage(argv[0]);
	    exit(0);
	}
	else
	    break;

	++ac;
    }

	/* get the root name for the output files */
    if (ac < argc) {
	if (argv[ac][0] == '-') {
	    printf("invalid option \"%s\"\n\n", argv[ac]);
	    print_usage(argv[0]);
	    exit(1);
	}
	else if (argc > (ac + 1)) {
	    printf("extra information after filename\n\n");
	    print_usage(argv[0]);
	    exit(1);
	}
	else
	    strcpy(filename, argv[ac]);
    }
    else {
	printf("filename not given\n\n");
	print_usage(argv[0]);
	exit(1);
    }

    srand(time(0));

    create_entrance();
    process_exit_queue();

    printf("\n\n");

    stock_dungeon();

    if (printout == PRINTPS)
	print_postscript_file();
    else
	print_latex_file();

    return 0;
}



/****************************************************************************
*
* print_usage() - print a short usage message and information on arguments
*
****************************************************************************/

print_usage(name)
char name[];
{
    int i;

    printf("Usage: %s [argument list] filename\n", name);
    for (i = 0; i < HELPLEN; ++i)
	printf("%s\n", helpmessage[i]);
}




/****************************************************************************
*
* stock_dungeon() - presently, this function merely scans through the dungeon
*		    and numbers the rooms and determines the maximum level
*		    that was generated
*
****************************************************************************/

stock_dungeon()
{
    int   lvl, count, roomnum;
    LOCAT *room;

    roomnum = 1;

    for (lvl = 1; lvl <= maxlevel; ++lvl) {
	count = 1;
	room = firstlocation;
	while (room != NULL) {
	    if (room->level == lvl) {
		if (((digits_in_number(count) * 4)
				<= (scale_x(room->xmax) - scale_x(room->xmin)))
			&& ((room->locationtype == ROOM)
				|| (room->locationtype == CHAMBER))) {

		    room->number = roomnum++;
		}

		++count;
	    }
	    room = room->next;
	}

	if (count == 1) {
	    maxlevel = lvl - 1;
	    break;
	}
    }
}



/****************************************************************************
*
* digits_in_number() - given an integer, return the number of digits in it
*
****************************************************************************/

int digits_in_number(foo)
int foo;
{
    int i = 1;

    while (foo > 9) {
	foo /= 10;
	++i;
    }

    return i;
}



/****************************************************************************
*
* create_entrance() - generate the entrance of the dungeon, either a stairway
*			positioned in the middle of the first level, or a
*			doorway in one of the walls (with the assumption
*			that the first level is above ground)
*
****************************************************************************/

create_entrance()
{
    int   xpos, ypos;
    LOCAT *room;
    EXITS *exit;

    room = new_location_node();
    add_location_to_queue(room);
    room->level = startlevel;
    room->locationtype = STAIRWAY;

    exit = new_exit_node();
    add_exit_to_queue(exit);
    exit->level = startlevel;
    exit->orientation = entrancedirection;
    exit->locationtype = ROOM;
    exit->parent = room;

    if (entrancetype == DOORWAY) {
	exit->exittype = DOORWAY;

	switch (entrancedirection) {
	    case NORTH:
		exit->x1 = (mapwidth / 2) - 5;
		exit->x2 = (mapwidth / 2) + 5;
		exit->y1 = 1;
		exit->y2 = 1;

		add_line_to_location(room, (mapwidth / 2) - 5, 1,
					(mapwidth / 2) - 5, 0);
		add_line_to_location(room, (mapwidth / 2) + 5, 1,
					(mapwidth / 2) + 5, 0);
		add_line_to_location(room, 0, 0, (mapwidth / 2) - 5, 0);
		add_line_to_location(room, mapwidth, 0, (mapwidth / 2) + 5, 0);
		break;

	    case SOUTH:
		exit->x1 = (mapwidth / 2) - 5;
		exit->x2 = (mapwidth / 2) + 5;
		exit->y1 = mapheight - 1;
		exit->y2 = mapheight - 1;

		add_line_to_location(room, (mapwidth / 2) - 5, mapheight - 1,
					(mapwidth / 2) - 5, mapheight);
		add_line_to_location(room, (mapwidth / 2) + 5, mapheight - 1,
					(mapwidth / 2) + 5, mapheight);
		add_line_to_location(room, 0, mapheight,
				(mapwidth / 2) - 5, mapheight);
		add_line_to_location(room, mapwidth, mapheight,
				(mapwidth / 2) + 5, mapheight);
		break;

	    case EAST:
		exit->x1 = 1;
		exit->x2 = 1;
		exit->y1 = (mapheight / 2) - 5;
		exit->y2 = (mapheight / 2) + 5;

		add_line_to_location(room, 1, (mapheight / 2) - 5,
				0, (mapheight / 2) - 5);
		add_line_to_location(room, 1, (mapheight / 2) + 5,
				0, (mapheight / 2) + 5);
		add_line_to_location(room, 0, 0, 0, (mapheight / 2) - 5);
		add_line_to_location(room, 0, mapheight, 0, (mapheight/2)+5);
		break;

	    case WEST:
		exit->x1 = mapwidth - 1;
		exit->x2 = mapwidth - 1;
		exit->y1 = (mapheight / 2) - 5;
		exit->y2 = (mapheight / 2) + 5;

		add_line_to_location(room, mapwidth - 1, (mapheight / 2) - 5,
				mapwidth, (mapheight / 2) - 5);
		add_line_to_location(room, mapwidth - 1, (mapheight / 2) + 5,
				mapwidth, (mapheight / 2) + 5);
		add_line_to_location(room, mapwidth, 0, mapwidth,
				(mapheight / 2) - 5);
		add_line_to_location(room, mapwidth, mapheight, mapwidth,
				(mapheight/2)+5);
		break;

	    default:
		fatal_error("create_entrance(): unknown entrance direction");
	}

	if (entrancedirection != NORTH)
	    add_line_to_location(room, 0, 0, mapwidth, 0);
	if (entrancedirection != SOUTH)
	    add_line_to_location(room, 0, mapheight, mapwidth, mapheight);
	if (entrancedirection != EAST)
	    add_line_to_location(room, 0, 0, 0, mapheight);
	if (entrancedirection != WEST)
	    add_line_to_location(room, mapwidth, 0, mapwidth, mapheight);
    }
    else if (entrancetype == STAIRWAY) {
	xpos = mapwidth / 2;
	ypos = mapheight / 2;

	exit->x1 = xpos;
	exit->y1 = ypos;
	exit->x2 = xpos;
	exit->y2 = ypos;
	exit->exittype = OPENING;

	switch (entrancedirection) {
	    case NORTH:
		exit->x2 -= 10;
		draw_stairs_up(room, SOUTH, 10, xpos, ypos);
		break;

	    case SOUTH:
		exit->x2 += 10;
		draw_stairs_up(room, NORTH, 10, xpos, ypos);
		break;

	    case EAST:
		exit->y2 += 10;
		draw_stairs_up(room, WEST, 10, xpos, ypos);
		break;

	    case WEST:
		exit->y2 -= 10;
		draw_stairs_up(room, EAST, 10, xpos, ypos);
		break;

	    default:
		fatal_error("create_entrance(): unknown entrance direction");
	}
	compress_allocated_space(room);
    }
    else
	fatal_error("create_entrance(): unknown entrance type");
}



/****************************************************************************
*
* process_exit_queue() - maintain a queue of all unfinished exits in the
*			 dungeon, when new exits are created, add them to
*			 the end of the queue, when ready to create a new
*			 location, grab an exit from the front of the queue,
*			 check if sufficient space can be allocated for the
*			 location, and if possible create the location
*			 (possibly with one or more exits), and removed the
*			 now-finished exit from the queue
*
****************************************************************************/

process_exit_queue()
{
    int   delta, width, xpos, ypos, flag;
    EXITS *exit;
    LOCAT *room;

	/* loop until all exits are finished */
    while (queuehead != NULL) {
	exit = dequeue_exit();

		/* if the exit is null, ignore it (it was probably nullified
		   by being merged with another exit to form a new location */
	if (exit == NULL)
	    continue;

		/* if the exit has no parent location, something is very
		   wrong */
	if (exit->parent == NULL)
	    fatal_error("process_exit_queue(): found exit with no parent");

		/* try to join two exits together to prevent cases where
		   locations on the map spread ever outward and never join
		   up, which would result it annoying, unrealistic maps */
	if (join_exits(exit)) {
	    free_exit_node(exit);
	    continue;
	}

	    /* allocate a new location for the exit */
	room = new_location_node();
	room->xmin = MIN(exit->x1, exit->x2);
	room->xmax = MAX(exit->x1, exit->x2);
	room->ymin = MIN(exit->y1, exit->y2);
	room->ymax = MAX(exit->y1, exit->y2);
	room->locationtype = exit->locationtype;
	room->level = exit->level;

	    /* shift the entrance to the room so that it does not overlap
		the previous room */
	switch(exit->orientation) {
	   case NORTH: room->ymin += 1; room->ymax += 1; break;
	   case SOUTH: room->ymin -= 1; room->ymax -= 1; break;
	   case EAST:  room->xmin += 1; room->xmax += 1; break;
	   case WEST:  room->xmin -= 1; room->xmax -= 1; break;
	}

	    /* determine the key position of the exit */
	find_exit_position(exit, &width, &xpos, &ypos);

	    /* expand the allocated space of the location depending upon
		what type of location it is */
	if (exit->locationtype == JUNCTION)
	    delta = expand_allocated_space(room, exit->orientation, width);
	else if (exit->locationtype == STAIRWAY)
	    delta = expand_allocated_space(room, exit->orientation, 20);
	else if (exit->locationtype == PASSAGE)
	    delta = expand_allocated_space(room, exit->orientation, 
				((rand() % 8) * 5) + 25);
	else
	    delta = expand_allocated_space(room, exit->orientation,
				((rand() % 10) * 5) + 10);

	    /* if insufficient space is allocated, then scrap the location
		and seal off the exit as if never existed */
	if (delta < width) {
	    seal_exit(exit);
	    free_exit_node(exit);
	    continue;
	}

	flag = 0;

	    /* create the type of location requested by the exit */
	switch (exit->locationtype) {
	    case ROOM:
	    case CHAMBER:
		flag = draw_room(room, exit);
		break;

	    case PASSAGE:
		flag = draw_passage(room, exit, delta);
		break;

	    case JUNCTION:
		flag = draw_junction(room, exit, delta, width);
		break;

	    case STAIRWAY:
		flag = draw_stairway(room, exit, delta, width, xpos, ypos);
		break;

	    default:
		fatal_error("process_exit_queue(): unknown location type");
	}

	    /* if an error occurred in location creation, scrap the exit */
	if (flag) {
	    seal_exit(exit);
	    free_exit_node(exit);
	    continue;
	}

	    /* otherwise draw the exit */
	draw_exit(exit);

		/* do not add locationf to queue until last so will not
		   interfer with checking of allocated space */
	compress_allocated_space(room);
	add_location_to_queue(room);
	free_exit_node(exit);
    }
}



/****************************************************************************
*
* join_exits() - try to join the given exit with a currently exiting exit
*		 so can have multiple paths to the same location, otherwise
*		 will end up with only one possible path connected two
*		 locations which looks strange and unrealistic
*
****************************************************************************/

int join_exits(exit)
EXITS *exit;
{
    int   mergeflag;
    int   sl, sr, nl, nr, et, eb, wt, wb;
    int   sflag, nflag, eflag, wflag;
    int   delta;
    EXITS *otherexit;
    LOCAT *newroom;
    QUEUE *que;

	/* do not join all exits that possibly can, only do some of them */
    if (rand() % 2)
	return 0;

	/* do not mess with exits for stairways */
    if (exit->locationtype == STAIRWAY)
	return 0;

	/* search for an exit to merge with */
    mergeflag = 0;
    nflag = sflag = eflag = wflag = 0;
    que = queuehead;
    while (que != NULL) {
	if ((que->exit != NULL) && (que->exit->level == exit->level)
			&& (que->exit->locationtype != STAIRWAY)) {

	    otherexit = que->exit;

	    if (otherexit->parent == NULL)
		fatal_error("join_exits(): second exit has no parent\n");

	    if (exit->orientation == NORTH) {
		sl = MIN(exit->x1, exit->x2);
		sr = MAX(exit->x1, exit->x2);
		sflag = 1;
		if (otherexit->orientation == EAST) {
		    wt = MAX(otherexit->y1, otherexit->y2);
		    wb = MIN(otherexit->y1, otherexit->y2);
		    if ((otherexit->x1 < sl) && (exit->y1 < wb)
				&& ((wt - exit->y1) <= 61)
				&& ((sr - otherexit->x1) <= 61)
				&& ((newroom = box_empty(wt, exit->y1+1,
					otherexit->x1+1, sr, exit->level))
						!= NULL)) {
			wflag = 1;
			mergeflag = SOUTH;
			break;
		    }
		}
		else if (otherexit->orientation == SOUTH) {
		    nl = MIN(otherexit->x1, otherexit->x2);
		    nr = MAX(otherexit->x1, otherexit->x2);
		    if (((otherexit->y1 - 1) > (exit->y1 + 1))
				&& ((otherexit->y1 - exit->y1) <= 62)
				&& ((otherexit->y1 - exit->y1) > 11)
				&& (abs(nl - sl) < 60)
				&& ((newroom = box_empty(otherexit->y1 - 1,
					exit->y1 + 1, MIN(nl, sl), MAX(nr, sr),
						exit->level)) != NULL)) {
			nflag = 1;
			mergeflag = SOUTH;
			break;
		    }
		}
		else if (otherexit->orientation == WEST) {
		    et = MAX(otherexit->y1, otherexit->y2);
		    eb = MIN(otherexit->y1, otherexit->y2);
		    if ((otherexit->x1 > sr) && (exit->y1 < eb)
				&& ((et - exit->y1) <= 61)
				&& ((otherexit->x1 - sl) <= 61)
				&& ((newroom = box_empty(et, exit->y1 + 1, sl,
					otherexit->x1 - 1, exit->level))
						!= NULL)) {
			eflag = 1;
			mergeflag = SOUTH;
			break;
		    }
		}
	    }
	    else if (exit->orientation == SOUTH) {
		nl = MIN(exit->x1, exit->x2);
		nr = MAX(exit->x1, exit->x2);
		nflag = 1;
		if (otherexit->orientation == EAST) {
		    wt = MAX(otherexit->y1, otherexit->y2);
		    wb = MIN(otherexit->y1, otherexit->y2);
		    if ((otherexit->x1 < nl) && (exit->y1 > wt)
				&& ((exit->y1 - wb) <= 61)
				&& ((nr - otherexit->x1) <= 61)
				&& ((newroom = box_empty(exit->y1-1, wb,
					otherexit->x1+1, nr, exit->level))
						!= NULL)) {
			wflag = 1;
			mergeflag = NORTH;
			break;
		    }
		}
		else if (otherexit->orientation == NORTH) {
		    sl = MIN(otherexit->x1, otherexit->x2);
		    sr = MAX(otherexit->x1, otherexit->x2);
		    if (((otherexit->y1 + 1) < (exit->y1 - 1))
				&& ((exit->y1 - otherexit->y1) <= 62)
				&& ((exit->y1 - otherexit->y1) > 11)
				&& (abs(nl - sl) <= 60)
				&& ((newroom = box_empty(exit->y1 - 1,
					otherexit->y1 + 1, MIN(nl,sl),
					MAX(nr,sr), exit->level)) != NULL)) {
			sflag = 1;
			mergeflag = NORTH;
			break;
		    }
		}
		else if (otherexit->orientation == WEST) {
		    et = MAX(otherexit->y1, otherexit->y2);
		    eb = MIN(otherexit->y1, otherexit->y2);
		    if ((otherexit->x1 > nr) && (exit->y1 > et)
				&& ((exit->y1 - eb) <= 61)
				&& ((otherexit->x1 - nl) <= 61)
				&& ((newroom = box_empty(exit->y1 - 1, eb,
					nl, otherexit->x1 - 1, exit->level))
						!= NULL)) {
			eflag = 1;
			mergeflag = NORTH;
			break;
		    }
		}
	    }
	    else if (exit->orientation == EAST) {
		wt = MAX(exit->y1, exit->y2);
		wb = MIN(exit->y1, exit->y2);
		wflag = 1;
		if (otherexit->orientation == NORTH) {
		    sl = MIN(otherexit->x1, otherexit->x2);
		    sr = MAX(otherexit->x1, otherexit->x2);
		    if ((exit->x1 < sl) && (otherexit->y1 < wb)
				&& ((sr - exit->x1) <= 61)
				&& ((wt - otherexit->y1) <= 61)
				&& ((newroom = box_empty(wt, otherexit->y1 + 1,
					exit->x1 + 1, sr, exit->level))
						!= NULL)) {
			sflag = 1;
			mergeflag = WEST;
			break;
		    }
		}
		else if (otherexit->orientation == SOUTH) {
		    nl = MIN(otherexit->x1, otherexit->x2);
		    nr = MAX(otherexit->x1, otherexit->x2);
		    if ((exit->x1 < nl) && (wt < otherexit->y1)
				&& ((nr - exit->x1) <= 61)
				&& ((otherexit->y1 - wb) <= 61)
				&& ((newroom = box_empty(otherexit->y1 - 1, wb,
					exit->x1 + 1, nr, exit->level))
						!= NULL)) {
			nflag = 1;
			mergeflag = WEST;
			break;
		    }
		}
		else if (otherexit->orientation == WEST) {
		    et = MAX(otherexit->y1, otherexit->y2);
		    eb = MIN(otherexit->y1, otherexit->y2);
		    if (((exit->x1 + 1) < (otherexit->x1 - 1))
				&& ((otherexit->x1 - exit->x1) <= 62)
				&& ((otherexit->x1 - exit->x1) > 11)
				&& (abs(et - wt) < 60)
				&& ((newroom = box_empty(MAX(et, wt),
					MIN(eb, wb), exit->x1 + 1,
					otherexit->x1 - 1, exit->level))
						!= NULL)) {
			eflag = 1;
			mergeflag = WEST;
			break;
		    }
		}
	    }
	    else if (exit->orientation == WEST) {
		et = MAX(exit->y1, exit->y2);
		eb = MIN(exit->y1, exit->y2);
		eflag = 1;
		if (otherexit->orientation == NORTH) {
		    sl = MIN(otherexit->x1, otherexit->x2);
		    sr = MAX(otherexit->x1, otherexit->x2);
		    if ((otherexit->y1 < eb) && (sr < exit->x1)
				&& ((exit->x1 - sl) <= 61)
				&& ((et - otherexit->y1) <= 61)
				&& ((newroom = box_empty(et, otherexit->y1 + 1,
					sl, exit->x1 - 1, exit->level))
						!= NULL)) {
			sflag = 1;
			mergeflag = EAST;
			break;
		    }
		}
		else if (otherexit->orientation == SOUTH) {
		    nl = MIN(otherexit->x1, otherexit->x2);
		    nr = MAX(otherexit->x1, otherexit->x2);
		    if ((nr < exit->x1) && (et < otherexit->y1)
				&& ((exit->x1 - nl) <= 61)
				&& ((otherexit->y1 - eb) <= 61)
				&& ((newroom = box_empty(otherexit->y1 - 1,
					eb, nl, exit->x1 - 1, exit->level))
						!= NULL)) {
			nflag = 1;
			mergeflag = EAST;
			break;
		    }
		}
		else if (otherexit->orientation == EAST) {
		    wt = MAX(otherexit->y1, otherexit->y2);
		    wb = MIN(otherexit->y1, otherexit->y2);
		    if (((otherexit->x1 + 1) < (exit->x1 - 1))
				&& ((exit->x1 - otherexit->x1) <= 62)
				&& ((exit->x1 - otherexit->x1) > 11)
				&& (abs(et - wt) <= 61)
				&& ((newroom = box_empty(MAX(et, wt),
					MIN(eb, wb), otherexit->x1 + 1,
					exit->x1 - 1, exit->level))
						!= NULL)) {
			wflag = 1;
			mergeflag = EAST;
			break;
		    }
		}
	    }
	}

	que = que->queuenext;
    }

    if (!mergeflag)
	return (0);

	/* if reach this point, then have successfully found an exit to
	   merge with */
    if (debugging)
	printf("successful merging of two exits\n");

	/* decide upon a type for the location, make small locations into
	   junctions to try and avoid having room numbers fall in the middle
	   of what appear to be hallways */
    if (MIN(newroom->xmax - newroom->xmin, newroom->ymax - newroom->ymin) <=15)
	newroom->locationtype = JUNCTION;
    else
	newroom->locationtype = ROOM;

    newroom->level = exit->level;
    add_location_to_queue(newroom);

	/* draw the exits into this location, and try to add a few new ones */
    if (nflag) {
	if (mergeflag == NORTH)
	    draw_exit(exit);
	else {
	    draw_exit(otherexit);
	    que->exit = NULL;
	    free_exit_node(otherexit);
	}
    }
    else {
	delta = expand_allocated_space(newroom, NORTH, (rand() % 3) * 5);
	if (rand() % 2) {
	    nflag = 1;
	    create_exit(newroom, NORTH, &nr, &nl);
	}
    }

    if (sflag) {
	if (mergeflag == SOUTH)
	    draw_exit(exit);
	else {
	    draw_exit(otherexit);
	    que->exit = NULL;
	    free_exit_node(otherexit);
	}
    }
    else {
	delta = expand_allocated_space(newroom, SOUTH, (rand() % 3) * 5);
	if (rand() % 2) {
	    sflag = 1;
	    create_exit(newroom, SOUTH, &sr, &sl);
	}
    }

    if (eflag) {
	if (mergeflag == EAST)
	    draw_exit(exit);
	else {
	    draw_exit(otherexit);
	    que->exit = NULL;
	    free_exit_node(otherexit);
	}
    }
    else {
	delta = expand_allocated_space(newroom, EAST, (rand() % 3) * 5);
	if (rand() % 2) {
	    eflag = 1;
	    create_exit(newroom, EAST, &et, &eb);
	}
    }

    if (wflag) {
	if (mergeflag == WEST)
	    draw_exit(exit);
	else {
	    draw_exit(otherexit);
	    que->exit = NULL;
	    free_exit_node(otherexit);
	}
    }
    else {
	delta = expand_allocated_space(newroom, WEST, (rand() % 3) * 5);
	if (rand() % 2) {
	    wflag = 1;
	    create_exit(newroom, WEST, &wt, &wb);
	}
    }

	/* draw the outline of the location */

    if (nflag) {
	add_line_to_location(newroom, newroom->xmin, newroom->ymax, nl,
				newroom->ymax);
	add_line_to_location(newroom, nr, newroom->ymax, newroom->xmax,
				newroom->ymax);
    }
    else
	add_line_to_location(newroom, newroom->xmin, newroom->ymax,
					newroom->xmax, newroom->ymax);

    if (sflag) {
	add_line_to_location(newroom, newroom->xmin, newroom->ymin, sl,
				newroom->ymin);
	add_line_to_location(newroom, sr, newroom->ymin, newroom->xmax,
				newroom->ymin);
    }
    else
	add_line_to_location(newroom, newroom->xmin, newroom->ymin,
					newroom->xmax, newroom->ymin);

    if (eflag) {
	add_line_to_location(newroom, newroom->xmax, newroom->ymax,
				newroom->xmax, et);
	add_line_to_location(newroom, newroom->xmax, eb, newroom->xmax,
				newroom->ymin);
    }
    else
	add_line_to_location(newroom, newroom->xmax, newroom->ymax,
					newroom->xmax, newroom->ymin);

    if (wflag) {
	add_line_to_location(newroom, newroom->xmin, newroom->ymax,
				newroom->xmin, wt);
	add_line_to_location(newroom, newroom->xmin, wb, newroom->xmin,
				newroom->ymin);
    }
    else
	add_line_to_location(newroom, newroom->xmin, newroom->ymax,
					newroom->xmin, newroom->ymin);

    return 1;
}



/****************************************************************************
*
* box_empty() - given a box on a certain level of the dungeon, see if there
*		are any locations that fall within that box, if not then
*		allocate a location to fill that box and return the pointer
*		to that location (return NULL if unable to create location)
*
****************************************************************************/

LOCAT *box_empty(top, bottom, left, right, level)
int top, bottom, left, right, level;
{
    LOCAT *room, *newroom;

    room = firstlocation;
    while (room != NULL) {
	if ((room->level == level)
		&& ((room->xmax >= left) && (room->xmin <= right))
		&& ((room->ymax >= bottom) && (room->ymin <= top)))
	    return (NULL);

	room = room->next;
    }

	/* if reach here, then the location is vacant, allocate it and return
	   a pointer to it */
    newroom = new_location_node();
    newroom->xmax = right;
    newroom->xmin = left;
    newroom->ymax = top;
    newroom->ymin = bottom;

    return (newroom);
}



/****************************************************************************
*
* location_beyond_opening() - decide upon what type of a location should be
*				found on the other side of an opening
*
****************************************************************************/

int location_beyond_opening()
{
    int i, rtn;

    i = rand() % 14;

    if (i < 3)
	rtn = CHAMBER;
    else if (i < 5)
	rtn = PASSAGE;
    else if (i < 13)
	rtn = JUNCTION;
    else
	rtn = STAIRWAY;

    return (rtn);
}



/****************************************************************************
*
* location_beyond_doorway() - decide what kind of location should be found
*				on the other side of a doorway
*
****************************************************************************/

int location_beyond_doorway()
{
    int i, rtn;

    i = rand() % 18;

    if (i < 8)
	rtn = ROOM;
    else if (i < 10)
	rtn = CHAMBER;
    else if (i < 14)
	rtn = PASSAGE;
    else
	rtn = JUNCTION;

    return (rtn);
}



/****************************************************************************
*
* find_exit_postion() - given an exit, determine its key location and width,
*			exits facing different directions with the same key
*			position appear to revolve around that position
*
****************************************************************************/

find_exit_position(exit, width, xpos, ypos)
EXITS *exit;
int   *width, *xpos, *ypos;
{
    switch (exit->orientation) {
	case NORTH:
		*width = MAX(exit->x1, exit->x2) - MIN(exit->x1, exit->x2);
		*xpos = MIN(exit->x1, exit->x2);
		*ypos = exit->y1;
		break;

	case SOUTH:
		*width = MAX(exit->x1, exit->x2) - MIN(exit->x1, exit->x2);
		*xpos = MAX(exit->x1, exit->x2);
		*ypos = exit->y1;
		break;

	case EAST:
		*width = MAX(exit->y1, exit->y2) - MIN(exit->y1, exit->y2);
		*xpos = exit->x1;
		*ypos = MAX(exit->y1, exit->y2);
		break;

	case WEST:
		*width = MAX(exit->y1, exit->y2) - MIN(exit->y1, exit->y2);
		*xpos = exit->x1;
		*ypos = MIN(exit->y1, exit->y2);
		break;
    }
}



/****************************************************************************
*
* draw_room() - expand the space for a room, create some exits for it, and
*		draw all of the lines for that room/chamber
*
****************************************************************************/

int draw_room(room, exit)
LOCAT *room;
EXITS *exit;
{
    int   xne, yne, xse, yse, xsw, ysw, xnw, ynw;
    int   nflag, sflag, eflag, wflag;
    int   sl, sr, nl, nr, et, eb, wt, wb;
    int   delta2, delta3;

    if (debugging)
	printf("drawing a room\n");

	/* expand space to either side of exit so that it does not look like
	   a passage */
    delta2 = expand_allocated_space(room, left_direction(exit->orientation),
					(rand() % 5) * 5);
    delta3 = expand_allocated_space(room, right_direction(exit->orientation),
					(rand() % 5) * 5);

	/* get the four corners of the room */
    xne = xse = room->xmax;
    xnw = xsw = room->xmin;
    yne = ynw = room->ymax;
    yse = ysw = room->ymin;

	/* make certain that the room measures at least ten feet on a side */
    if (MIN((room->xmax - room->xmin), (room->ymax - room->ymin)) < 10)
	return 1;

    nflag = sflag = eflag = wflag = 0;

	/* if the entrance to the room faces a certain direction, get either
	   side of it, otherwise randomly place an exit in that wall */
    if (exit->orientation == NORTH) {
	sl = MIN(exit->x1, exit->x2);
	sr = MAX(exit->x1, exit->x2);
	sflag = 1;
    }
    else {
	if (rand() % 2) {
	    sflag = 1;
	    create_exit(room, SOUTH, &sr, &sl);
	}
    }

    if (exit->orientation == SOUTH) {
	nl = MIN(exit->x1, exit->x2);
	nr = MAX(exit->x1, exit->x2);
	nflag = 1;
    }
    else {
	if (rand() % 2) {
	    nflag = 1;
	    create_exit(room, NORTH, &nr, &nl);
	}
    }

    if (exit->orientation == EAST) {
	wt = MAX(exit->y1, exit->y2);
	wb = MIN(exit->y1, exit->y2);
	wflag = 1;
    }
    else {
	if (rand() % 2) {
	    wflag = 1;
	    create_exit(room, WEST, &wt, &wb);
	}
    }

    if (exit->orientation == WEST) {
	et = MAX(exit->y1, exit->y2);
	eb = MIN(exit->y1, exit->y2);
	eflag = 1;
    }
    else {
	if (rand() % 2) {
	    eflag = 1;
	    create_exit(room, EAST, &et, &eb);
	}
    }

	/* draw the four walls of the room, leaving openings for exits */

    if (nflag) {
	add_line_to_location(room, xnw, ynw, nl, yne);
	add_line_to_location(room, nr, ynw, xne, yne);
    }
    else
	add_line_to_location(room, xnw, ynw, xne, yne);

    if (sflag) {
	add_line_to_location(room, xsw, ysw, sl, yse);
	add_line_to_location(room, sr, ysw, xse, yse);
    }
    else
	add_line_to_location(room, xsw, ysw, xse, yse);

    if (eflag) {
	add_line_to_location(room, xne, yne, xne, et);
	add_line_to_location(room, xse, eb, xse, yse);
    }
    else
	add_line_to_location(room, xne, yne, xse, yse);

    if (wflag) {
	add_line_to_location(room, xnw, ynw, xnw, wt);
	add_line_to_location(room, xsw, wb, xsw, ysw);
    }
    else
	add_line_to_location(room, xnw, ynw, xsw, ysw);

    return 0;
}



/****************************************************************************
*
* create_exit() - create an exit facing a certain direction
*
****************************************************************************/

create_exit(room, orient, high, low)
LOCAT *room;
int   orient, *high, *low;
{
    int   i, length, over, exitwidth;
    EXITS *newexit;

	/* get length of wall exit is in */
    if ((orient == NORTH) || (orient == SOUTH))
	length = room->xmax - room->xmin;
    else
	length = room->ymax - room->ymin;

    i = rand() % 10;

	/* decide upon a size for the exit */
    if ((i < 2) && (length >= 15))
	exitwidth = 15;
    else if ((i < 8) && (length >= 10))
	exitwidth = 10;
    else
	exitwidth = 5;

	/* decide upon where to place the exit in the wall */
    if ((length - exitwidth) > 1)
	over = rand() % (length - exitwidth);
    else
	over = 0;

	/* create the exit and add it to the queue */
    newexit = new_exit_node();
    add_exit_to_queue(newexit);
    newexit->parent = room;
    newexit->level = room->level;
    newexit->orientation = orient;

	/* set the location of the exit */
    if ((orient == NORTH) || (orient == SOUTH)) {
	newexit->x1 = *high = room->xmax - over;
	newexit->x2 = *low = room->xmax - over - exitwidth;

	if (orient == NORTH)
	    newexit->y1 = newexit->y2 = room->ymax;
	else
	    newexit->y1 = newexit->y2 = room->ymin;
    }
    else {
	newexit->y1 = *high = room->ymax - over;
	newexit->y2 = *low = room->ymax - over - exitwidth;

	if (orient == EAST)
	    newexit->x1 = newexit->x2 = room->xmax;
	else
	    newexit->x1 = newexit->x2 = room->xmin;
    }

	/* determine what type of location lies on the other side of the
	   exit */
    if (room->locationtype == ROOM) {
	newexit->exittype = DOORWAY;
	newexit->locationtype = location_beyond_doorway();
    }
    else {
	newexit->exittype = OPENING;
	newexit->locationtype = location_beyond_opening();
    }
}



/****************************************************************************
*
* draw_passage() - draw a passage from an exit: basically extend a hallway
*		   in the direction the exit is facing and place a new exit
*		   in the far end of the hallway
*
****************************************************************************/

int draw_passage(room, exit, delta)
LOCAT *room;
EXITS *exit;
int   delta;
{
    EXITS *newexit;

    if (debugging)
	printf("drawing a passage\n");

	/* create the exit in the far end of the hallway */
    newexit = new_exit_node();
    add_exit_to_queue(newexit);
    newexit->orientation = exit->orientation;
    newexit->parent = room;
    newexit->level = room->level;
    newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);

	/* set the type of location beyond the exit */
    if (newexit->exittype == DOORWAY)
	newexit->locationtype = location_beyond_doorway();
    else
	newexit->locationtype = location_beyond_opening();

	/* draw the walls of the passage and set the position of new exit */
    switch (exit->orientation) {
	case NORTH:
	case SOUTH:
		if (exit->orientation == NORTH) {
		    newexit->y1 = exit->y1 + delta + 1;
		    newexit->y2 = exit->y2 + delta + 1;
		}
		else {
		    newexit->y1 = exit->y1 - delta - 1;
		    newexit->y2 = exit->y2 - delta - 1;
		}

		newexit->x1 = exit->x1;
		newexit->x2 = exit->x2;
		add_line_to_location(room, room->xmin, room->ymin,
					room->xmin, room->ymax);
		add_line_to_location(room, room->xmax, room->ymin,
					room->xmax, room->ymax);
		break;

	case EAST:
	case WEST:
		if (exit->orientation == EAST) {
		    newexit->x1 = exit->x1 + delta + 1;
		    newexit->x2 = exit->x2 + delta + 1;
		}
		else {
		    newexit->x1 = exit->x1 - delta - 1;
		    newexit->x2 = exit->x2 - delta - 1;
		}

		newexit->y1 = exit->y1;
		newexit->y2 = exit->y2;
		add_line_to_location(room, room->xmin, room->ymin,
					room->xmax, room->ymin);
		add_line_to_location(room, room->xmin, room->ymax,
					room->xmax, room->ymax);
		break;
    }

    return 0;
}



/****************************************************************************
*
* draw_junction() - draw a passage junction, which is basically a location
*		    with an exit that takes the entire length of a wall
*		    but each wall is only the width of a passage
*
****************************************************************************/

int draw_junction(room, exit, delta, width)
LOCAT *room;
EXITS *exit;
int   delta, width;
{
    int   xnw, ynw, xne, yne, xse, yse, xsw, ysw, count;
    EXITS *newexit;

    if (debugging)
	printf("drawing a junction\n");

    count = 0;

	/* make sure a square area was allocated for the junction */
    if (delta < width)
	seal_exit(exit);
    else {
	switch (exit->orientation) {
	    case NORTH:
		xnw = xsw = MIN(exit->x1, exit->x2);
		xne = xse = MAX(exit->x1, exit->x2);
		ynw = yne = exit->y1 + 1 + width;
		ysw = yse = exit->y1 + 1;
		break;

	    case SOUTH:
		xnw = xsw = MIN(exit->x1, exit->x2);
		xne = xse = MAX(exit->x1, exit->x2);
		ynw = yne = exit->y1 - 1;
		ysw = yse = exit->y1 - 1 - width;
		break;

	    case EAST:
		xne = xse = exit->x1 + 1 + width;
		xnw = xsw = exit->x1 + 1;
		ynw = yne = MAX(exit->y1, exit->y2);
		ysw = yse = MIN(exit->y1, exit->y2);
		break;

	    case WEST:
		xne = xse = exit->x1 - 1;
		xnw = xsw = exit->x1 - 1 - width;
		ynw = yne = MAX(exit->y1, exit->y2);
		ysw = yse = MIN(exit->y1, exit->y2);
		break;
	}

		/* add zero-length lines at each corner to assure that
		   the necessary allocated space is not compressed */
	add_line_to_location(room, xnw, ynw, xnw, ynw);
	add_line_to_location(room, xne, yne, xne, yne);
	add_line_to_location(room, xse, yse, xse, yse);
	add_line_to_location(room, xsw, ysw, xsw, ysw);

	    /* if there is no exit in the north wall (facing south), then
		plane an exit in the north wall with .666 probability */
	if (exit->orientation != SOUTH) {
	    if (rand() % 3) {
		++count;
		newexit = new_exit_node();
		add_exit_to_queue(newexit);
		newexit->orientation = NORTH;
		newexit->parent = room;
		newexit->level = room->level;
		newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
		newexit->x1 = xnw;
		newexit->y1 = ynw;
		newexit->x2 = xne;
		newexit->y2 = yne;

		if (newexit->exittype == DOORWAY)
		    newexit->locationtype = location_beyond_doorway();
		else
		    newexit->locationtype = location_beyond_opening();
	    }
	    else
		add_line_to_location(room, xnw, ynw, xne, yne);
	}

	if (exit->orientation != NORTH) {
	    if (rand() % 3) {
		++count;
		newexit = new_exit_node();
		add_exit_to_queue(newexit);
		newexit->orientation = SOUTH;
		newexit->parent = room;
		newexit->level = room->level;
		newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
		newexit->x1 = xse;
		newexit->y1 = yse;
		newexit->x2 = xsw;
		newexit->y2 = ysw;

		if (newexit->exittype == DOORWAY)
		    newexit->locationtype = location_beyond_doorway();
		else
		    newexit->locationtype = location_beyond_opening();
	    }
	    else
		add_line_to_location(room, xsw, ysw, xse, yse);
	}

	if (exit->orientation != WEST) {
	    if (rand() % 3) {
		++count;
		newexit = new_exit_node();
		add_exit_to_queue(newexit);
		newexit->orientation = EAST;
		newexit->parent = room;
		newexit->level = room->level;
		newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
		newexit->x1 = xne;
		newexit->y1 = yne;
		newexit->x2 = xse;
		newexit->y2 = yse;

		if (newexit->exittype == DOORWAY)
		    newexit->locationtype = location_beyond_doorway();
		else
		    newexit->locationtype = location_beyond_opening();
	    }
	    else
		add_line_to_location(room, xne, yne, xse, yse);
	}

	if (exit->orientation != EAST) {
	    if (rand() % 3) {
		++count;
		newexit = new_exit_node();
		add_exit_to_queue(newexit);
		newexit->orientation = WEST;
		newexit->parent = room;
		newexit->level = room->level;
		newexit->exittype = ((rand() % 4) ? OPENING : DOORWAY);
		newexit->x1 = xsw;
		newexit->y1 = ysw;
		newexit->x2 = xnw;
		newexit->y2 = ynw;

		if (newexit->exittype == DOORWAY)
		    newexit->locationtype = location_beyond_doorway();
		else
		    newexit->locationtype = location_beyond_opening();
	    }
	    else
		add_line_to_location(room, xnw, ynw, xsw, ysw);
	}
    }

	/* return true if no exits were added to the junction, so that
	   the junction will be junked */
    return (!count);
}



/****************************************************************************
*
* draw_stairway() - draw a stairway randomly going up or down, but abort if
*		    go above the first level or below the maximum level
*
****************************************************************************/

int draw_stairway(room, exit, delta, width, xpos, ypos)
LOCAT *room;
EXITS *exit;
int   delta, width, xpos, ypos;
{
    LOCAT *newroom;
    EXITS *newexit;

    if (debugging)
	printf("drawing a stairway\n");

	/* space must be 20 feet long for the stairway */
    if (delta < 20)
	return 1;

	/* set the location of the stairway on the next level */
    newroom = new_location_node();
    newroom->xmin = MIN(exit->x1, exit->x2);
    newroom->xmax = MAX(exit->x1, exit->x2);
    newroom->ymin = MIN(exit->y1, exit->y2);
    newroom->ymax = MAX(exit->y1, exit->y2);
    newroom->locationtype = STAIRWAY;

	/* give an exit for the stairway on the new level */
    newexit = new_exit_node();
    newexit->x1 = exit->x1;
    newexit->y1 = exit->y1;
    newexit->x2 = exit->x2;
    newexit->y2 = exit->y2;
    newexit->orientation = exit->orientation;
    newexit->locationtype = location_beyond_opening();
    newexit->exittype = OPENING;
    newexit->parent = newroom;

	/* determine the postion of the exit at the top/bottom of the stairs */
    switch (exit->orientation) {
	case NORTH: newexit->y1 += 20; newexit->y2 += 20; break;
	case SOUTH: newexit->y1 -= 20; newexit->y2 -= 20; break;
	case EAST:  newexit->x1 += 20; newexit->x2 += 20; break;
	case WEST:  newexit->x1 -= 20; newexit->x2 -= 20; break;
    }

	/* stairs go up or down with random probability */
    if (rand() % 2) {
	if (room->level <= startlevel)
	    return 1;

		/* set the stairway to go up (decreasing level number) */
	room->locationdata = UP;
	newroom->locationdata = DOWN;
	newroom->level = room->level - 1;

	delta = expand_allocated_space(newroom, exit->orientation, 20);

	if (delta < 20)
	    return 1;

	draw_stairs_up(room, exit->orientation, width, xpos, ypos);

	switch (exit->orientation) {
	    case NORTH:
		draw_stairs_down(newroom, SOUTH, width, xpos+width, ypos+20);
		break;
	    case SOUTH:
		draw_stairs_down(newroom, NORTH, width, xpos-width, ypos-20);
		break;
	    case EAST:
		draw_stairs_down(newroom, WEST, width, xpos+20, ypos-width);
		break;
	    case WEST:
		draw_stairs_down(newroom, EAST, width, xpos-20, ypos+width);
		break;
	}
    }
    else {
	if (room->level >= maxlevel)
	    return 1;

		/* set the stairway going down (increasing level number) */
	room->locationdata = DOWN;
	newroom->locationdata = UP;
	newroom->level = room->level + 1;

	delta = expand_allocated_space(newroom, exit->orientation, 20);

	if (delta < 20)
	    return 1;

	draw_stairs_down(room, exit->orientation, width, xpos, ypos);

	switch (exit->orientation) {
	    case NORTH:
		draw_stairs_up(newroom, SOUTH, width, xpos+width, ypos+20);
		break;
	    case SOUTH:
		draw_stairs_up(newroom, NORTH, width, xpos-width, ypos-20);
		break;
	    case EAST:
		draw_stairs_up(newroom, WEST, width, xpos+20, ypos-width);
		break;
	    case WEST:
		draw_stairs_up(newroom, EAST, width, xpos-20, ypos+width);
		break;
	}
    }

    newexit->level = newroom->level;
    add_location_to_queue(newroom);
    add_exit_to_queue(newexit);

	/* always return with no error condition */
    return 0;
}



/****************************************************************************
*
* draw_stairs_down() - draw the lines for a stairway going down
*
****************************************************************************/

draw_stairs_down(room, orient, width, xpos, ypos)
LOCAT *room;
int   orient, width, xpos, ypos;
{
    int i;

    for (i = 0; i < 11; ++i) {
	switch (width) {
	    case 5:
		add_line_to_location(room,
			xpos + stairwaydownlines5[orient-NORTH][i][0],
			ypos + stairwaydownlines5[orient-NORTH][i][1],
			xpos + stairwaydownlines5[orient-NORTH][i][2],
			ypos + stairwaydownlines5[orient-NORTH][i][3]);
		break;

	    case 10:
		add_line_to_location(room,
			xpos + stairwaydownlines10[orient-NORTH][i][0],
			ypos + stairwaydownlines10[orient-NORTH][i][1],
			xpos + stairwaydownlines10[orient-NORTH][i][2],
			ypos + stairwaydownlines10[orient-NORTH][i][3]);
		break;

	    case 15:
		add_line_to_location(room,
			xpos + stairwaydownlines15[orient-NORTH][i][0],
			ypos + stairwaydownlines15[orient-NORTH][i][1],
			xpos + stairwaydownlines15[orient-NORTH][i][2],
			ypos + stairwaydownlines15[orient-NORTH][i][3]);
		break;

	    default:
		printf("width: %d\n", width);
		fatal_error("draw_stairs_down(): bad width of stairway");
	}
    }
}



/****************************************************************************
*
* draw_stairs_up() - draw the lines for a stairway going up
*
****************************************************************************/
draw_stairs_up(room, orient, width, xpos, ypos)
LOCAT *room;
int   orient, width, xpos, ypos;
{
    int i;

    for (i = 0; i < 11; ++i) {
	switch (width) {
	    case 5:
		add_line_to_location(room,
				xpos + stairwayuplines5[orient-NORTH][i][0],
				ypos + stairwayuplines5[orient-NORTH][i][1],
				xpos + stairwayuplines5[orient-NORTH][i][2],
				ypos + stairwayuplines5[orient-NORTH][i][3]);
		break;

	    case 10:
		add_line_to_location(room,
				xpos + stairwayuplines10[orient-NORTH][i][0],
				ypos + stairwayuplines10[orient-NORTH][i][1],
				xpos + stairwayuplines10[orient-NORTH][i][2],
				ypos + stairwayuplines10[orient-NORTH][i][3]);
		break;

	    case 15:
		add_line_to_location(room,
				xpos + stairwayuplines15[orient-NORTH][i][0],
				ypos + stairwayuplines15[orient-NORTH][i][1],
				xpos + stairwayuplines15[orient-NORTH][i][2],
				ypos + stairwayuplines15[orient-NORTH][i][3]);
		break;

	    default:
		printf("width: %d\n", width);
		fatal_error("draw_stairs_up(): bad width of stairway");
	}
    }
}



/****************************************************************************
*
* draw_exit() - given an exit, draw the actual lines that form it, the lines
*		which form an exit are placed with the parent of the exit
*
****************************************************************************/

draw_exit(exit)
EXITS *exit;
{
    int width, xpos, ypos;

    find_exit_position(exit, &width, &xpos, &ypos);

    if (exit->parent == NULL)
	fatal_error("draw_exit(): exit has no parent pointer");

    if (exit->exittype == DOORWAY)
	draw_doorway(exit->parent, exit->orientation, width, xpos, ypos);
    else if (exit->exittype == OPENING)
	draw_opening(exit->parent, exit->orientation, width, xpos, ypos);
    else
	fatal_error("process_exit_queue(): unknown exit type");
}



/****************************************************************************
*
* draw_doorway() - draw the lines for a doorway
*
****************************************************************************/

draw_doorway(room, orient, width, xpos, ypos)
LOCAT *room;
int   orient, width, xpos, ypos;
{
    int i;

    if (debugging)
	printf("drawing a doorway\n");

    switch (width) {
	case 5:
		for (i = 0; i < 8; ++i)
		    add_line_to_location(room,
				xpos + doorwaylines5[orient-NORTH][i][0],
				ypos + doorwaylines5[orient-NORTH][i][1],
				xpos + doorwaylines5[orient-NORTH][i][2],
				ypos + doorwaylines5[orient-NORTH][i][3]);
		break;

	case 10:
		for (i = 0; i < 8; ++i)
		    add_line_to_location(room,
				xpos + doorwaylines10[orient-NORTH][i][0],
				ypos + doorwaylines10[orient-NORTH][i][1],
				xpos + doorwaylines10[orient-NORTH][i][2],
				ypos + doorwaylines10[orient-NORTH][i][3]);
		break;

	case 15:
		for (i = 0; i < 9; ++i)
		    add_line_to_location(room,
				xpos + doorwaylines15[orient-NORTH][i][0],
				ypos + doorwaylines15[orient-NORTH][i][1],
				xpos + doorwaylines15[orient-NORTH][i][2],
				ypos + doorwaylines15[orient-NORTH][i][3]);
		break;

	default:
		printf("width given as %d\n", width);
		fatal_error("draw_doorway(): bad width of doorway");
    }
}



/****************************************************************************
*
* draw_opening() - draw the lines for an opening in the wall
*
****************************************************************************/

draw_opening(room, orient, width, xpos, ypos)
LOCAT *room;
int   orient, width, xpos, ypos;
{
    if (debugging)
	printf("drawing an opening\n");

    switch (orient) {
	case NORTH:
		add_line_to_location(room, xpos, ypos, xpos, ypos+1);
		add_line_to_location(room, xpos+width, ypos, xpos+width,
				ypos+1);
		break;

	case SOUTH:
		add_line_to_location(room, xpos, ypos, xpos, ypos-1);
		add_line_to_location(room, xpos-width, ypos, xpos-width,
				ypos-1);
		break;

	case EAST:
		add_line_to_location(room, xpos, ypos, xpos+1, ypos);
		add_line_to_location(room, xpos, ypos-width, xpos+1,
				ypos-width);
		break;

	case WEST:
		add_line_to_location(room, xpos, ypos, xpos-1, ypos);
		add_line_to_location(room, xpos, ypos+width, xpos-1,
				ypos+width);
		break;
   }
}



/****************************************************************************
*
* seal_exit() - seal off an exit by drawing a line across the space in the
*		wall where it should have gone
*
****************************************************************************/

seal_exit(exit)
EXITS *exit;
{
    if (debugging)
	printf("sealing an exit\n");

    add_line_to_location(exit->parent, exit->x1, exit->y1, exit->x2, exit->y2);
}



/****************************************************************************
*
* add_line_to_location() - given the endpoints of a line, place the line in
*			   the list of lines for the room
*
****************************************************************************/

add_line_to_location(room, x1, y1, x2, y2)
LOCAT *room;
int   x1, y1, x2, y2;
{
    LINES *line;

    line = new_line_node();
    line->next = room->firstline;
    room->firstline = line;
    line->x1 = x1;
    line->y1 = y1;
    line->x2 = x2;
    line->y2 = y2;
}



/****************************************************************************
*
* compress_allocated_space() - find the minimum amount of space occupied by
*				a location, to make sure the location is not
*				accidentally allocated more than it needs
*
****************************************************************************/

compress_allocated_space(room)
LOCAT *room;
{
    int   minx = mapwidth, miny = mapheight, maxx = 0, maxy = 0;
    LINES *line;

    line = room->firstline;

	/* do nothing if here are no lines */
    if (line == NULL)
	return;

    while (line != NULL) {
	minx = MIN(minx, line->x1);
	minx = MIN(minx, line->x2);
	maxx = MAX(maxx, line->x1);
	maxx = MAX(maxx, line->x2);
	miny = MIN(miny, line->y1);
	miny = MIN(miny, line->y2);
	maxy = MAX(maxy, line->y1);
	maxy = MAX(maxy, line->y2);

	line = line->next;
    }

    room->xmin = minx;
    room->xmax = maxx;
    room->ymin = miny;
    room->ymax = maxy;
}



/****************************************************************************
*
* expand_allocated_space() - expand the space allocated to a location in
*				a given direction for a given distance,
*				and return the amount actually expanded
*
****************************************************************************/

int expand_allocated_space(location, direction, maxdelta)
LOCAT *location;
int   direction, maxdelta;
{
    int   delta, lvl;
    LOCAT *room;

	/* just return zero if amount to expand is less than one */
    if (maxdelta < 1)
	return (0);

    lvl = location->level;

    switch (direction) {
	case NORTH:

			/* try to expand the allocated space as far as
			   possible to the north */
		delta = mapheight;
		room = firstlocation;
		while (room != NULL) {
		    if ((room->level == lvl)
				&& (room->xmax >= location->xmin)
				&& (room->xmin <= location->xmax)
				&& (room->ymax >= location->ymin))
			delta = MIN(delta, room->ymin);
		    room = room->next;
		}
			/* get the amount of change and decrement it, so
			   allocated space does not overlap another location */
		delta = delta - location->ymax - 1;

			/* make sure do not expand it more than requested */
		if (delta > maxdelta)
		    delta = maxdelta;

			/* if actually allocated some space, increase the
			   amount of space allocated */
		if (delta > 0)
		    location->ymax += delta;
		break;

	case SOUTH:
		delta = 0;
		room = firstlocation;
		while (room != NULL) {
		    if ((room->level == lvl)
				&& (room->xmax >= location->xmin)
				&& (room->xmin <= location->xmax)
				&& (room->ymin <= location->ymax))
			delta = MAX(delta, room->ymax);
		    room = room->next;
		}
		delta = location->ymin - delta - 1;
		if (delta > maxdelta)
		    delta = maxdelta;
		if (delta > 0)
		    location->ymin -= delta;
		break;

	case EAST:
		delta = mapwidth;
		room = firstlocation;
		while (room != NULL) {
		    if ((room->level == lvl)
				&& (room->ymax >= location->ymin)
				&& (room->ymin <= location->ymax)
				&& (room->xmax >= location->xmin))
			delta = MIN(delta, room->xmin);
		    room = room->next;
		}
		delta = delta - location->xmax - 1;
		if (delta > maxdelta)
		    delta = maxdelta;
		if (delta > 0)
		    location->xmax += delta;
		break;

	case WEST:
		delta = 0;
		room = firstlocation;
		while (room != NULL) {
		    if ((room->level == lvl)
				&& (room->ymax >= location->ymin)
				&& (room->ymin <= location->ymax)
				&& (room->xmin <= location->xmax))
			delta = MAX(delta, room->xmax);
		    room = room->next;
		}
		delta = location->xmin - delta - 1;
		if (delta > maxdelta)
		    delta = maxdelta;
		if (delta > 0)
		    location->xmin -= delta;
		break;

	default:
		fatal_error("expand_allocated_space(): unknown direction");
    }

	/* return the amount that the allocated space was expanded */
    return (delta);
}



/****************************************************************************
*
* opposite_direction() - return the direction opposite to that given
*
****************************************************************************/

int opposite_direction(dir)
int dir;
{
    int i;

    switch (dir) {
	case NORTH: i = SOUTH; break;
	case SOUTH: i = NORTH; break;
	case EAST:  i = WEST;  break;
	case WEST:  i = EAST;  break;
	case UP:    i = DOWN;  break;
	case DOWN:  i = UP;    break;
	default: fatal_error("opposite_direction(): unknown direction");
    }

    return (i);
}



/****************************************************************************
*
* left_direction() - return the direction to the left of that given
*
****************************************************************************/

int left_direction(dir)
int dir;
{
    int i;

    switch (dir) {
	case NORTH: i = WEST;  break;
	case SOUTH: i = EAST;  break;
	case EAST:  i = NORTH; break;
	case WEST:  i = SOUTH; break;
	default: fatal_error("left_direction(): unknown direction");
    }

    return (i);
}



/****************************************************************************
*
* right_direction() - return the direction to the right of that given
*
****************************************************************************/

int right_direction(dir)
int dir;
{
    int i;

    switch (dir) {
	case NORTH: i = EAST;  break;
	case SOUTH: i = WEST;  break;
	case EAST:  i = SOUTH; break;
	case WEST:  i = NORTH; break;
	default: fatal_error("right_direction(): unknown direction");
    }

    return (i);
}



/****************************************************************************
*
* print_postscript_file() - print out the dungeon in PostScript format
*
****************************************************************************/

print_postscript_file()
{
    char  fname[100];
    int   lvl;
    FILE  *fopen(), *outfile;
    LOCAT *room;
    LINES *line;

	/* print out a separate file for each level, since they can get big */
    for (lvl = startlevel; lvl <= maxlevel; ++lvl) {

		/* get the name of the file */
	sprintf(fname, "%s%d.ps", filename, lvl);
	printf("Drawing dungeon level %d to file \"%s\".\n", lvl, fname);

	if ((outfile = fopen(fname, "w")) == NULL)
	    fatal_error("unable to open file \"%s\" for output", fname);

		/* print out the postscript header */
	fprintf(outfile, "%%!PS-Adobe-1.0\n\n");
	fprintf(outfile, "/Times-Roman findfont 12 scalefont\nsetfont\n\n");
	fprintf(outfile, "newpath\n 50 750 moveto\n(Level %d) show\n\n", lvl);
	fprintf(outfile, "/Times-Roman findfont 6 scalefont\nsetfont\n\n");

		/* print out a boarder for the level if not built into the
		   level due to having a doorway entrance to the level */
	if ((lvl != startlevel) || (entrancetype != DOORWAY)) {
	    fprintf(outfile, "newpath\n   50   30 moveto\n   50 %4d lineto\n",
			YSIZE + 30);
	    fprintf(outfile, "   50   30 moveto\n %4d   30 lineto\n",
			XSIZE + 50);
	    fprintf(outfile, " %4d   30 moveto\n %4d %4d lineto\n",
			XSIZE + 50, XSIZE + 50, YSIZE + 30);
	    fprintf(outfile, "   50 %4d moveto\n %4d %4d lineto\nstroke\n\n",
			YSIZE + 30, XSIZE + 50, YSIZE + 30);
	}

		/* loop through all locations and print out the ones on the
		   current level */
	room = firstlocation;
	while (room != NULL) {
	    if (room->level == lvl) {
		line = room->firstline;

			/* print the room number if it is a numbered location
			   and the user has requested numbering be turned on */
		if (numberoutput && (room->number != 0))
		    fprintf(outfile, "newpath\n %d %d moveto\n(%d) show\n\n",
			scale_x(room->xmin + 2) + 50,
			scale_y(room->ymin + 2) + 30, room->number);

			/* print out all lines associated with this location
			   (provided the lines have a non-zero length */
		fprintf(outfile, "newpath\n");
		while (line != NULL) {
		    if ((line->x1 != line->x2) || (line->y1 != line->y2))
			fprintf(outfile, " %d %d moveto\n %d %d lineto\n",
				scale_x(line->x1) + 50, scale_y(line->y1) +30,
				scale_x(line->x2) + 50, scale_y(line->y2) +30);

		    line = line->next;
		}
		fprintf(outfile, "stroke\n\n");
	    }
	    room = room->next;
	}

		/* close the file for the current level */
	fprintf(outfile, "showpage\n");
	fclose(outfile);
    }
}



/****************************************************************************
****************************************************************************/

print_latex_file()
{
    char  fname[100];
    int   i, dx, dy, length, lvl;
    FILE  *fopen(), *outfile;
    LOCAT *room;
    LINES *line;

	/* print out a separate file for each level, since they can get big */
    for (lvl = startlevel; lvl <= maxlevel; ++lvl) {

		/* get the name of the file */
	sprintf(fname, "%s%d.tex", filename, lvl);
	printf("Drawing dungeon level %d to file \"%s\".\n", lvl, fname);

	if ((outfile = fopen(fname, "w")) == NULL)
	    fatal_error("unable to open file \"%s\" for output", fname);

		/* print out the latex header */
	for (i = 0; i < LATEXHEADERLEN; ++i)
	    fprintf(outfile, "%s\n", latexheader[i]);

	fprintf(outfile, "\n\\put(10,720){\\shortstack{Level %d}}\n\n", lvl);

		/* print out a boarder for the level if not built into the
		   level due to having a doorway entrance to the level */
	if ((lvl != startlevel) || (entrancetype != DOORWAY)) {
	    fprintf(outfile, "\\put(0,0){\\line(0,1){%d}}\n",
		YSIZE);
	    fprintf(outfile, "\\put(0,0){\\line(1,0){%d}}\n",
		XSIZE);
	    fprintf(outfile, "\\put(0,%d){\\line(1,0){%d}}\n",
		YSIZE, XSIZE);
	    fprintf(outfile, "\\put(%d,0){\\line(0,1){%d}}\n",
		XSIZE, YSIZE);
	}

		/* loop through all locations and print out the ones on the
		   current level */
	room = firstlocation;
	while (room != NULL) {
	    if (room->level == lvl) {

			/* print the room number if it is a numbered location
			   and the user has requested numbering be turned on */
		if (numberoutput && (room->number != 0))
		    fprintf(outfile,"\\put(%d,%d){\\shortstack{\\small{%d}}}\n",
			scale_x(room->xmin + 2), scale_x(room->ymin + 2),
			room->number);

			/* print out all lines associated with this location
			   (provided the lines have a non-zero length */
		line = room->firstline;
		while (line != NULL) {
		    dx = 0;
		    dy = 0;
		    if (line->x1 != line->x2) {
			dx = ((line->x1 < line->x2) ? (1) : (-1));
			length = abs(scale_x(line->x1) - scale_x(line->x2));
		    }
		    else if (line->y1 != line->y2) {
			dy = ((line->y1 < line->y2) ? (1) : (-1));
			length = abs(scale_y(line->y1) - scale_y(line->y2));
		    }
		    else {
			line = line->next;
			continue;
		    }

		    fprintf(outfile, "\\put(%d,%d){\\line(%d,%d){%d}}\n",
				scale_x(line->x1), scale_y(line->y1),
				dx, dy, length);

		    line = line->next;
		}
	    }
	    room = room->next;
	}

		/* close the file for the current level */
	fprintf(outfile, "\n\\end{picture}\n\\end{document}\n");
	fclose(outfile);
    }
}



/****************************************************************************
*
* scale_x() - scale the position of a point to the map's scale
*
****************************************************************************/

int scale_x(foo)
int foo;
{
    foo = ((float)XSIZE / (float)mapwidth) * (float)foo;
    return (foo);
}



/****************************************************************************
*
* scale_y() - scale a point's position so it fits the size of the map
*
****************************************************************************/

int scale_y(foo)
int foo;
{
    foo = ((float)YSIZE / (float)mapheight) * (float)foo;
    return (foo);
}



/****************************************************************************
*
* new_exit_node() - return a pointer to a new (or reused) exit node
*
****************************************************************************/

EXITS *new_exit_node()
{
    EXITS *ext;

    if (exitfreestack != NULL) {
	ext = exitfreestack;
	exitfreestack = exitfreestack->next;
    }
    else if ((ext = (EXITS*)malloc(sizeof(EXITS))) == NULL)
	fatal_error("new_Exit_node(): malloc memory failed");

    ext->orientation = 0;
    ext->x1 = 0;
    ext->y1 = 0;
    ext->x2 = 0;
    ext->y2 = 0;
    ext->exittype = 0;
    ext->locationtype = ROOM;
    ext->parent = NULL;
    ext->next = NULL;
    ext->level = 0;

    return (ext);
}



/****************************************************************************
*
* free_exit_node() - free up the memory used by an exit node
*
****************************************************************************/

free_exit_node(ext)
EXITS *ext;
{
    ext->parent = NULL;
    ext->next = exitfreestack;
    exitfreestack = ext;
}



/****************************************************************************
*
* new_line_node() - return a pointer to a new line node
*
****************************************************************************/

LINES *new_line_node()
{
    LINES *lin;

    if ((lin = (LINES*)malloc(sizeof(LINES))) == NULL)
	fatal_error("new_line_node(): malloc memory failed");

    lin->x1 = 0;
    lin->y1 = 0;
    lin->x2 = 0;
    lin->y2 = 0;
    lin->next = NULL;

    return (lin);
}



/****************************************************************************
*
* new_location_node() - return a pointer to a new location node
*
****************************************************************************/

LOCAT *new_location_node()
{
    LOCAT *loc;

    if ((loc = (LOCAT*)malloc(sizeof(LOCAT))) == NULL)
	fatal_error("new_location_node(): malloc memory failed");

    loc->xmin = 0;
    loc->xmax = 0;
    loc->ymin = 0;
    loc->ymax = 0;
    loc->locationtype = ROOM;
    loc->locationdata = -666;
    loc->level = 0;
    loc->number = 0;
    loc->firstline = NULL;
    loc->next = NULL;
    loc->previous = NULL;

    return (loc);
}



/****************************************************************************
*
* new_queue_node() - return a pointer to a new (or reused) exit queue node
*
****************************************************************************/

QUEUE *new_queue_node()
{
    QUEUE *que;

    if (queuefreestack != NULL) {
	que = queuefreestack;
	queuefreestack = queuefreestack->queuenext;
    }
    else if ((que = (QUEUE*)malloc(sizeof(QUEUE))) == NULL)
	fatal_error("new_queue_node(): malloc memory failed");

    que->exit = NULL;
    que->queuenext = NULL;

    return (que);
}



/****************************************************************************
*
* free_queue_node() - free up the memory used by an exit queue node
*
****************************************************************************/

free_queue_node(que)
QUEUE *que;
{
    que->queuenext = queuefreestack;
    queuefreestack = que;
    que->exit = NULL;
}



/****************************************************************************
*
* add_exit_to_queue() - and an exit to the queue of exit nodes
*
****************************************************************************/

add_exit_to_queue(exit)
EXITS *exit;
{
    QUEUE *que;

    ++gc;

    if (debugging)
	printf("adding exit to queue, newsize: %d\n", gc);
    else {
	printf("%4d", gc);
	++gcnum;
	if (gcnum > 15) {
	    gcnum = 0;
	    printf("\n");
	}
    }

    que = new_queue_node();
    que->exit = exit;

    if (queuehead == NULL)
	queuehead = queuetail = que;
    else {

	    /* decude whether to place new exit at front or end of queue */
	if (EXITSTACK) {	/* do as stack */
	    que->queuenext = queuehead;
	    queuehead = que;
	}
	else {			/* do as queue */
	    queuetail->queuenext = que;
	    queuetail = que;
	}
    }
}



/****************************************************************************
*
* dequeue_exit() - remove an exit from the queue of exit nodes, note that
*		   the pointer which is returned may still be NULL, which
*		   typically results from joining two exits and NULLing
*		   the pointer to a node in the middle of the queue, which
*		   is easier than trying to completely yank the node from
*		   the middle of the queue
*
****************************************************************************/

EXITS *dequeue_exit()
{
    EXITS *ext;
    QUEUE *que;

    --gc;

    if (!debugging) {
	printf("%4d", gc);
	++gcnum;
	if (gcnum > 15) {
	    gcnum = 0;
	    printf("\n");
	}
    }

	/* make sure there is an exit queue node */
    if (queuehead == NULL)
	fatal_error("dequeue_exit(): attempt to dequeue from empty queue");

    ext = queuehead->exit;
    que = queuehead;

    if (queuehead->queuenext == NULL)
	queuehead = queuetail = NULL;
    else
	queuehead = queuehead->queuenext;

    free_queue_node(que);

    return (ext);
}



/****************************************************************************
*
* add_location_to_queue() - add a location to the list of locations
*
****************************************************************************/

add_location_to_queue(ptr)
LOCAT *ptr;
{
    if (firstlocation == NULL)
	firstlocation = lastlocation = ptr;
    else {
	ptr->next = NULL;
	ptr->previous = lastlocation;
	lastlocation->next = ptr;
	lastlocation = ptr;
    }
}



/****************************************************************************
*
* fatal_error() - print out a fatal error message and abort execution
*
****************************************************************************/

fatal_error(strg, arg)
char strg[], arg[];
{
    printf("Error: ");
    printf(strg, arg);
    printf("\n\n");
    exit(1);
}
