ftp.nice.ch/pub/next/games/strategic/NetHack.s.tar.gz#/NetHackSource/src/sp_lev.c

This is sp_lev.c in view mode; [Download] [Up]

/*	SCCS Id: @(#)sp_lev.c	3.0	89/01/11
/*	Copyright (c) 1989 by Jean-Christophe Collet */
/* NetHack may be freely redistributed.  See license for details. */

/*
 * This file contains the various functions that are related to the special
 * levels.
 * It contains also the special level loader.
 *
 */

#include "hack.h"

#ifdef STRONGHOLD
#include "sp_lev.h"

#if defined(MACOS) || (defined(MSDOS) && !defined(AMIGA))
# define RDMODE "rb"
#else
# define RDMODE "r"
#endif

#define LEFT	1
#define CENTER	2
#define RIGHT	3
#define TOP	1
#define BOTTOM	3

static walk walklist[50];
extern int x_maze_max, y_maze_max;

#ifdef MACOS
char **Map;
#else
static char Map[COLNO][ROWNO];
#endif
static char robjects[10], rloc_x[10], rloc_y[10], rmonst[10],
	ralign[3] = { A_CHAOS, A_NEUTRAL, A_LAW };
static xchar xstart, ystart, xsize, ysize;

/*
 * Make walls of the area (x1, y1, x2, y2) non diggable
 */

static void
make_walls_nondiggable(x1,y1,x2,y2)
xchar x1, y1, x2, y2;
{
	register xchar x, y;

	for(y = y1; y <= y2; y++)
	    for(x = x1; x <= x2; x++)
		if(IS_WALL(levl[x][y].typ))
		    levl[x][y].diggable |= W_NONDIGGABLE;
}

/*
 * Choose randomly the state (nodoor, open, closed or locked) for a door
 */

static int
rnddoor()
{
	int i;
	
	i = 1 << rn2(5);
	i >>= 1;
	return i;
}

/* 
 * Select a random trap
 */

static int
rndtrap()
{
	return(rnd(TRAPNUM-1));
}

/* 
 * Coordinates in special level files are handled specially:
 *
 *	if x or y is -11, we generate a random coordinate.
 *	if x or y is between -1 and -10, we read one from the corresponding
 *	register (x0, x1, ... x9).
 *	if x or y is nonnegative, we convert it from relative to the local map
 *	to global coordinates.
 */

static void
get_location(x, y)
schar *x, *y;
{
	int cpt = 0;

	if (*x >= 0) {			/* normal locations */
		*x += xstart;
		*y += ystart;
	} else if (*x > -11) {		/* special locations */
		*y = ystart + rloc_y[ - *y - 1];
		*x = xstart + rloc_x[ - *x - 1];
	} else {			/* random location */
		do {
		    *x = xstart + rn2((int)xsize);
		    *y = ystart + rn2((int)ysize);
		} while (cpt < 100 &&
			 levl[*x][*y].typ != ROOM &&
			 levl[*x][*y].typ != CORR);
		if(cpt >= 100)
		    panic("get_location: can't find a place!");
	}

	if (*x < 0 || *x > x_maze_max || *y < 0 || *y > y_maze_max) {
	    impossible("get_location: (%d,%d) out of bounds", *x, *y);
	    *x = x_maze_max; *y = y_maze_max;
	}
}

/*
 * Shuffle the registers for locations, objects or monsters
 */

static void
shuffle(list, n)
char list[];
xchar n;
{
	int i, j;
	char k;

	for(i = n-1; i; i--) {
		j = rn2(i);

		k = list[j];
		list[j] = list[i];
		list[i] = k;
	}
}

/* 
 * Shuffle two arrays in the same order (for rloc_x & rloc_y)
 */

static void
shuffle2(list1, list2, n)
char list1[], list2[];
xchar n;
{
	int i, j;
	char k1, k2;

	for(i = n-1; i; i--) {
		j = rn2(i);

		k1 = list1[j];
		k2 = list2[j];

		list1[j] = list1[i];
		list2[j] = list2[i];

		list1[i] = k1;
		list2[i] = k2;
	}
}

/* 
 * NOT YET IMPLEMENTED!!!
 */

static boolean
load_rooms(fd)
FILE *fd;
{
	return FALSE;
}

/*
 * Select a random coordinate in the maze.
 *
 * We want a place not 'touched' by the loader.  That is, a place in
 * the maze outside every part of the special level.
 */

static void
maze1xy(m)
coord *m;
{
	do {
		m->x = rn1(x_maze_max - 3, 3);
		m->y = rn1(y_maze_max - 3, 3);
	} while (!(m->x % 2) || !(m->y % 2) || Map[m->x][m->y]);
}

/* 
 * The Big Thing: special maze loader
 *
 * Could be cleaner, but it works.
 */

static boolean
load_maze(fd)
FILE *fd;
{
    xchar   x, y, n, typ;
    char    c;

    xchar   numpart = 0, nwalk = 0;
    uchar   halign, valign;

    int     xi, yi, dir;
    coord   mm;
    int     mapcount, mapcountmax, mapfact;

    region  tmpregion;
    door    tmpdoor;
    trap    tmptrap;
    monster tmpmons;
    object  tmpobj;
    drawbridge tmpdb;
    walk    tmpwalk;
    digpos  tmpdig;
    lad     tmplad;
#ifdef ALTARS
    altar   tmpaltar;
#endif

    /* shuffle alignments */
    shuffle(ralign,3);

    /* Initialize map */
    xupstair = yupstair = xdnstair = ydnstair = doorindex = 0;
    for(x = 2; x <= x_maze_max; x++)
	for(y = 2; y <= y_maze_max; y++) {
#ifndef WALLIFIED_MAZE
	    levl[x][y].typ = STONE;
#else
	    levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL;
#endif
	    Map[x][y] = 0;
	}

    /* Start reading the file */
    numpart = fgetc(fd); /* Number of parts */
    if (!numpart || numpart > 9)
	panic("load_maze error: numpart = %d", (int) numpart);

    while (numpart--) {
	halign = fgetc(fd); /* Horizontal alignment */
	valign = fgetc(fd); /* Vertical alignment */
	xsize  = fgetc(fd); /* size in X */
	ysize  = fgetc(fd); /* size in Y */

	switch((int) halign) {
	    case LEFT:	    xstart = 3; 				break;
	    case CENTER:    xstart = 2+((x_maze_max-2-xsize)/2);	break;
	    case RIGHT:     xstart = x_maze_max-xsize-1;		break;
	}
	switch((int) valign) {
	    case TOP:	    ystart = 3; 				break;
	    case CENTER:    ystart = 2+((y_maze_max-2-ysize)/2);	break;
	    case BOTTOM:    ystart = y_maze_max-ysize-1;		break;
	}
	if (!(xstart % 2)) xstart++;
	if (!(ystart % 2)) ystart++;

	/* Load the map */
	for(y = ystart; y < ystart+ysize; y++)
	    for(x = xstart; x < xstart+xsize; x++) {
		levl[x][y].typ = fgetc(fd);
		initsym(x,y);
		/* secret doors default closed */
		if (levl[x][y].typ == SDOOR)
		  levl[x][y].doormask = D_CLOSED;
		Map[x][y] = 1;
	    }

	n = fgetc(fd); /* Random objects */
	if(n) {
		(void) fread((genericptr_t)robjects, 1, (int) n, fd);
		shuffle(robjects, n);
	}

	n = fgetc(fd); /* Random locations */
	if(n) {
		(void) fread((genericptr_t)rloc_x, 1, (int) n, fd);
		(void) fread((genericptr_t)rloc_y, 1, (int) n, fd);
		shuffle2(rloc_x, rloc_y, n);
	}

	n = fgetc(fd); /* Random monsters */
	if(n) {
		(void) fread((genericptr_t)rmonst, 1, (int) n, fd);
		shuffle(rmonst, n);
	}

	n = fgetc(fd); /* Number of subrooms */
	while(n--) {
		(void) fread((genericptr_t)&tmpregion, sizeof(tmpregion), 1, fd);
		if (nroom >= MAXNROFROOMS) continue;

		get_location(&tmpregion.x1, &tmpregion.y1);
		get_location(&tmpregion.x2, &tmpregion.y2);

		rooms[nroom].lx = tmpregion.x1;
		rooms[nroom].ly = tmpregion.y1;
		rooms[nroom].hx = tmpregion.x2;
		rooms[nroom].hy = tmpregion.y2;
		rooms[nroom].rtype = tmpregion.rtype;
		rooms[nroom].rlit = tmpregion.rlit;
		if (tmpregion.rlit == 1)
			for(x = rooms[nroom].lx-1; x <= rooms[nroom].hx+1; x++)
				for(y = rooms[nroom].ly-1; y <= rooms[nroom].hy+1; y++)
					levl[x][y].lit = 1;

		rooms[nroom].fdoor = rooms[nroom].doorct = 0;

		++nroom;
		rooms[nroom].hx = -1;
	}

	n = fgetc(fd); /* Number of doors */
	while(n--) {
		struct mkroom *croom = &rooms[0], *broom;
		int tmp;

		(void) fread((genericptr_t)&tmpdoor, sizeof(tmpdoor), 1, fd);

		x = tmpdoor.x;	y = tmpdoor.y;
		typ = tmpdoor.mask == -1 ? rnddoor() : tmpdoor.mask;

		get_location(&x, &y);
		levl[x][y].doormask = typ;
		mnewsym(x,y);

		/* Now the complicated part, list it with each subroom */
		/* The dog move and mail daemon routines use this */
		while(croom->hx >= 0 && doorindex < DOORMAX) {
		    if(croom->hx >= x-1 && croom->lx <= x+1 &&
		       croom->hy >= y-1 && croom->ly <= y+1) {
			/* Found it */
			croom->doorct++;

			/* Append or insert into doors[] */
			broom = croom+1;
			if(broom->hx < 0) tmp = doorindex;
			else
			    for(tmp = doorindex; tmp > broom->fdoor; tmp--)
				doors[tmp] = doors[tmp-1];

			doors[tmp].x = x;
			doors[tmp].y = y;
			doorindex++;

			for( ; broom->hx >= 0; broom++) broom->fdoor++;
		    }
		    croom++;
		}
	}

	n = fgetc(fd); /* Number of traps */
	while(n--) {
		(void) fread((genericptr_t)&tmptrap, sizeof(tmptrap), 1, fd);

		x = tmptrap.x;	y = tmptrap.y;
		typ = (tmptrap.type == -1 ? rndtrap() : tmptrap.type);

		get_location(&x, &y);
		(void) maketrap(x, y, typ);
	}

	n = fgetc(fd);	/* Number of monsters */
	while(n--) {
		(void) fread((genericptr_t)&tmpmons, sizeof(tmpmons), 1, fd);

		x = tmpmons.x;	y = tmpmons.y;
		get_location(&x, &y);

		if	(tmpmons.class >= 0)
			c = tmpmons.class;
		else if (tmpmons.class > -11)
			c = rmonst[-tmpmons.class - 1];
		else
			c = 0;

		if (!c)
			(void) makemon((struct permonst *) 0, x, y);
		else if (tmpmons.id != -1)
			(void) makemon(&mons[tmpmons.id], x, y);
		else
			(void) makemon(mkclass(c), x, y);
	}

	n = fgetc(fd); /* Number of objects */
	while(n--) {
		(void) fread((genericptr_t) &tmpobj, sizeof(object),1, fd);

		x = tmpobj.x;  y = tmpobj.y;
		get_location(&x, &y);

		if	(tmpobj.class >= 0)
			c = tmpobj.class;
		else if (tmpobj.class > -11)
			c = robjects[-tmpobj.class - 1];
		else
			c = 0;

		if (!c)
			(void) mkobj_at(0, x, y);
		else if (tmpobj.id != -1)
			(void) mksobj_at(tmpobj.id, x, y);
		else
			(void) mkobj_at(c, x, y);
	}

	n = fgetc(fd); /* Number of drawbridges */
	while(n--) {
		(void) fread((genericptr_t)&tmpdb, sizeof(tmpdb), 1, fd);

		x = tmpdb.x;  y = tmpdb.y;
		get_location(&x, &y);

		if (!create_drawbridge(x, y, tmpdb.dir, tmpdb.open))
		    impossible("Cannot create drawbridge.");
	}

	n = fgetc(fd); /* Number of mazewalks */
	while(n--) {
		(void) fread((genericptr_t)&tmpwalk, sizeof(tmpwalk), 1, fd);

		get_location(&tmpwalk.x, &tmpwalk.y);

		walklist[nwalk++] = tmpwalk;
	}

	n = fgetc(fd); /* Number of non_diggables */
	while(n--) {
		(void) fread((genericptr_t)&tmpdig, sizeof(tmpdig), 1, fd);

		get_location(&tmpdig.x1, &tmpdig.y1);
		get_location(&tmpdig.x2, &tmpdig.y2);

		make_walls_nondiggable(tmpdig.x1, tmpdig.y1,
				       tmpdig.x2, tmpdig.y2);
	}

	n = fgetc(fd); /* Number of ladders */
	while(n--) {
		(void) fread((genericptr_t)&tmplad, sizeof(tmplad), 1, fd);

		x = tmplad.x;  y = tmplad.y;
		get_location(&x, &y);

		levl[x][y].typ = LADDER;
		if (tmplad.up == 1) {
			xupladder = x;	yupladder = y;
			levl[x][y].ladder = LA_UP;
		} else {
			xdnladder = x;	ydnladder = y;
			levl[x][y].ladder = LA_DOWN;
		}
	}

#ifdef ALTARS
	n = fgetc(fd); /* Number of altars */
	while(n--) {
		(void) fread((genericptr_t)&tmpaltar, sizeof(tmpaltar), 1, fd);

		x = tmpaltar.x;  y = tmpaltar.y;
		get_location(&x, &y);

		typ = tmpaltar.align == -11 ? rn2(3) :
		      (tmpaltar.align < 0    ? ralign[-tmpaltar.align-1] :
					      tmpaltar.align);
		if (tmpaltar.shrine)
		    typ |= A_SHRINE;

		levl[x][y].typ = ALTAR;
		levl[x][y].altarmask = typ;
	}
#endif /* ALTARS /**/
    }

    while(nwalk--) {
	    xi = walklist[nwalk].x;
	    yi = walklist[nwalk].y;
	    dir = walklist[nwalk].dir;

	    move(&xi, &yi, dir);
	    x = xi;
	    y = yi;

#ifndef WALLIFIED_MAZE
	    levl[x][y].typ = CORR;
#else
	    levl[x][y].typ = ROOM;
#endif

	    /*
	     * We must be sure that the parity of the coordinates for
	     * walkfrom() is odd.  But we must also take into account
	     * what direction was chosen.
	     */
	    if(!(x % 2))
		if (dir == W_EAST)
		    x++;
		else
		    x--;

#ifndef WALLIFIED_MAZE
	    levl[x][y].typ = CORR;
#else
	    levl[x][y].typ = ROOM;
#endif

	    if (!(y % 2))
		if (dir == W_SOUTH)
		    y++;
		else
		    y--;

	    walkfrom(x, y);
    }
    wallification(2, 2, x_maze_max, y_maze_max, TRUE);

    /*
     * If there's a significant portion of maze unused by the special level,
     * we don't want it empty.
     *
     * Makes the number of traps, monsters, etc. proportional
     * to the size of the maze.
     */
    mapcountmax = mapcount = (x_maze_max - 2) * (y_maze_max - 2);

    for(x = 2; x < x_maze_max; x++)
	for(y = 2; y < y_maze_max; y++)
	    if(Map[x][y]) mapcount--;

    if (mapcount > (int) (mapcountmax / 10)) {
	    mapfact = (int) ((mapcount * 100L) / mapcountmax);
	    for(x = rnd((int) (20 * mapfact) / 100); x; x--) {
		    maze1xy(&mm);
		    (void) mkobj_at(rn2(2) ? GEM_SYM : 0, mm.x, mm.y);
	    }
	    for(x = rnd((int) (12 * mapfact) / 100); x; x--) {
		    maze1xy(&mm);
		    (void) mksobj_at(BOULDER, mm.x, mm.y);
	    }
	    maze1xy(&mm);
	    (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y);
	    for(x = rnd((int) (12 * mapfact) / 100); x; x--) {
		    maze1xy(&mm);
		    (void) makemon((struct permonst *) 0, mm.x, mm.y);
	    }
	    for(x = rn2((int) (15 * mapfact) / 100); x; x--) {
		    maze1xy(&mm);
		    mkgold(0L,mm.x,mm.y);
	    }
	    for(x = rn2((int) (15 * mapfact) / 100); x; x--) {
		    maze1xy(&mm);
		    (void) maketrap(mm.x, mm.y,rndtrap());
	    }
    }
    return TRUE;
}

/*
 * General loader
 */

boolean
load_special(name)
const char *name;
{
	FILE *fd;
	boolean result;
	schar c;

#ifdef OS2_CODEVIEW
	{
	char tmp[PATHLEN];

	Strcpy(tmp,hackdir);
	append_slash(tmp);
	Strcat(tmp,name);
	fd = fopen(tmp, RDMODE);
#else
	fd = fopen(name, RDMODE);
# ifdef MACOS
	if (!fd)
		fd = openFile(name, RDMODE);
# endif
#endif
#ifdef OS2_CODEVIEW
	}
#endif
	if (!fd) return FALSE;

	if ((c = fgetc(fd)) == EOF) {
		(void)fclose(fd);
		return FALSE;
	}

	switch (c) {
		case 1: 	/* Alas, this is not yet implemented. */
		    result = load_rooms(fd);
		    break;
		case 2: 	/* But this is implemented :-) */
		    result = load_maze(fd);
		    break;
		default:	/* ??? */
		    result = FALSE;
	}
	(void)fclose(fd);
	return result;
}
#endif /* STRONGHOLD /**/

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.