/* $NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $	 */

/* create.c		Larn is copyrighted 1986 by Noah Morgan. */

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: create.c,v 1.12 2012/06/19 05:30:43 dholland Exp $");
#endif				/* not lint */

#include "header.h"
#include "extern.h"
#include <unistd.h>

static void makemaze(int);
static int cannedlevel(int);
static void treasureroom(int);
static void troom(int, int, int, int, int, int);
static void makeobject(int);
static void fillmroom(int, int, int);
static void froom(int, int, int);
static void fillroom(int, int);
static void sethp(int);
static void checkgen(void);

/*
	makeplayer()

	subroutine to create the player and the players attributes
	this is called at the beginning of a game and at no other time
 */
void
makeplayer(void)
{
	int i;
	scbr();
	clear();
	c[HPMAX] = c[HP] = 10;	/* start player off with 15 hit points	 */
	c[LEVEL] = 1;		/* player starts at level one		 */
	c[SPELLMAX] = c[SPELLS] = 1;	/* total # spells starts off as 3 */
	c[REGENCOUNTER] = 16;
	c[ECOUNTER] = 96;	/* start regeneration correctly */
	c[SHIELD] = c[WEAR] = c[WIELD] = -1;
	for (i = 0; i < 26; i++)
		iven[i] = 0;
	spelknow[0] = spelknow[1] = 1;	/* he knows protection, magic missile */
	if (c[HARDGAME] <= 0) {
		iven[0] = OLEATHER;
		iven[1] = ODAGGER;
		ivenarg[1] = ivenarg[0] = c[WEAR] = 0;
		c[WIELD] = 1;
	}
	playerx = rnd(MAXX - 2);
	playery = rnd(MAXY - 2);
	oldx = 0;
	oldy = 25;
	gltime = 0;		/* time clock starts at zero	 */
	cbak[SPELLS] = -50;
	for (i = 0; i < 6; i++)
		c[i] = 12;	/* make the attributes, ie str, int, etc. */
	recalc();
}


/*
	newcavelevel(level)
	int level;

	function to enter a new level.  This routine must be called anytime the
	player changes levels.  If that level is unknown it will be created.
	A new set of monsters will be created for a new level, and existing
	levels will get a few more monsters.
	Note that it is here we remove genocided monsters from the present level.
 */
void
newcavelevel(int x)
{
	int             i, j;
	if (beenhere[level])
		savelevel();	/* put the level back into storage	 */
	level = x;		/* get the new level and put in working
				 * storage */
	if (beenhere[x]) {
		getlevel();
		sethp(0);
		checkgen();
		return;
	}

	/* fill in new level */
	for (i = 0; i < MAXY; i++)
		for (j = 0; j < MAXX; j++)
			know[j][i] = mitem[j][i] = 0;
	makemaze(x);
	makeobject(x);
	beenhere[x] = 1;
	sethp(1);
	checkgen();		/* wipe out any genocided monsters */

#if WIZID
	if (wizard || x == 0)
#else
	if (x == 0)
#endif
		for (j = 0; j < MAXY; j++)
			for (i = 0; i < MAXX; i++)
				know[i][j] = 1;
}

/*
	makemaze(level)
	int level;

	subroutine to make the caverns for a given level.  only walls are made.
 */
static int      mx, mxl, mxh, my, myl, myh, tmp2;

static void
makemaze(int k)
{
	int             i, j, tmp;
	int             z;
	if (k > 1 && (rnd(17) <= 4 || k == MAXLEVEL - 1 || k == MAXLEVEL + MAXVLEVEL - 1)) {
		if (cannedlevel(k))
			return;		/* read maze from data file */
	}
	if (k == 0)
		tmp = 0;
	else
		tmp = OWALL;
	for (i = 0; i < MAXY; i++)
		for (j = 0; j < MAXX; j++)
			item[j][i] = tmp;
	if (k == 0)
		return;
	eat(1, 1);
	if (k == 1)
		item[33][MAXY - 1] = 0;	/* exit from dungeon */

	/* now for open spaces -- not on level 10	 */
	if (k != MAXLEVEL - 1) {
		tmp2 = rnd(3) + 3;
		for (tmp = 0; tmp < tmp2; tmp++) {
			my = rnd(11) + 2;
			myl = my - rnd(2);
			myh = my + rnd(2);
			if (k < MAXLEVEL) {
				mx = rnd(44) + 5;
				mxl = mx - rnd(4);
				mxh = mx + rnd(12) + 3;
				z = 0;
			} else {
				mx = rnd(60) + 3;
				mxl = mx - rnd(2);
				mxh = mx + rnd(2);
				z = makemonst(k);
			}
			for (i = mxl; i < mxh; i++)
				for (j = myl; j < myh; j++) {
					item[i][j] = 0;
					if ((mitem[i][j] = z))
						hitp[i][j] = monster[z].hitpoints;
				}
		}
	}
	if (k != MAXLEVEL - 1) {
		my = rnd(MAXY - 2);
		for (i = 1; i < MAXX - 1; i++)
			item[i][my] = 0;
	}
	if (k > 1)
		treasureroom(k);
}

/*
	function to eat away a filled in maze
 */
void
eat(int xx, int yy)
{
	int             dir, try;
	dir = rnd(4);
	try = 2;
	while (try) {
		switch (dir) {
		case 1:
			if (xx <= 2)
				break;	/* west	 */
			if ((item[xx - 1][yy] != OWALL) || (item[xx - 2][yy] != OWALL))
				break;
			item[xx - 1][yy] = item[xx - 2][yy] = 0;
			eat(xx - 2, yy);
			break;

		case 2:
			if (xx >= MAXX - 3)
				break;	/* east	 */
			if ((item[xx + 1][yy] != OWALL) || (item[xx + 2][yy] != OWALL))
				break;
			item[xx + 1][yy] = item[xx + 2][yy] = 0;
			eat(xx + 2, yy);
			break;

		case 3:
			if (yy <= 2)
				break;	/* south	 */
			if ((item[xx][yy - 1] != OWALL) || (item[xx][yy - 2] != OWALL))
				break;
			item[xx][yy - 1] = item[xx][yy - 2] = 0;
			eat(xx, yy - 2);
			break;

		case 4:
			if (yy >= MAXY - 3)
				break;	/* north	 */
			if ((item[xx][yy + 1] != OWALL) || (item[xx][yy + 2] != OWALL))
				break;
			item[xx][yy + 1] = item[xx][yy + 2] = 0;
			eat(xx, yy + 2);
			break;
		};
		if (++dir > 4) {
			dir = 1;
			--try;
		}
	}
}

/*
 *	function to read in a maze from a data file
 *
 *	Format of maze data file:  1st character = # of mazes in file (ascii digit)
 *				For each maze: 18 lines (1st 17 used) 67 characters per line
 *
 *	Special characters in maze data file:
 *
 *		#	wall			D	door			.	random monster
 *		~	eye of larn		!	cure dianthroritis
 *		-	random object
 */
static int
cannedlevel(int k)
{
	char           *row;
	int             i, j;
	int             it, arg, mit, marg;
	if (lopen(larnlevels) < 0) {
		write(1, "Can't open the maze data file\n", 30);
		died(-282);
		return (0);
	}
	i = lgetc();
	if (i <= '0') {
		died(-282);
		return (0);
	}
	for (i = 18 * rund(i - '0'); i > 0; i--)
		lgetl();	/* advance to desired maze */
	for (i = 0; i < MAXY; i++) {
		row = lgetl();
		for (j = 0; j < MAXX; j++) {
			it = mit = arg = marg = 0;
			switch (*row++) {
			case '#':
				it = OWALL;
				break;
			case 'D':
				it = OCLOSEDDOOR;
				arg = rnd(30);
				break;
			case '~':
				if (k != MAXLEVEL - 1)
					break;
				it = OLARNEYE;
				mit = rund(8) + DEMONLORD;
				marg = monster[mit].hitpoints;
				break;
			case '!':
				if (k != MAXLEVEL + MAXVLEVEL - 1)
					break;
				it = OPOTION;
				arg = 21;
				mit = DEMONLORD + 7;
				marg = monster[mit].hitpoints;
				break;
			case '.':
				if (k < MAXLEVEL)
					break;
				mit = makemonst(k + 1);
				marg = monster[mit].hitpoints;
				break;
			case '-':
				it = newobject(k + 1, &arg);
				break;
			};
			item[j][i] = it;
			iarg[j][i] = arg;
			mitem[j][i] = mit;
			hitp[j][i] = marg;

#if WIZID
			know[j][i] = (wizard) ? 1 : 0;
#else
			know[j][i] = 0;
#endif
		}
	}
	lrclose();
	return (1);
}

/*
	function to make a treasure room on a level
	level 10's treasure room has the eye in it and demon lords
	level V3 has potion of cure dianthroritis and demon prince
 */
static void
treasureroom(int lv)
{
	int             tx, ty, xsize, ysize;

	for (tx = 1 + rnd(10); tx < MAXX - 10; tx += 10)
		if ((lv == MAXLEVEL - 1) || (lv == MAXLEVEL + MAXVLEVEL - 1) || rnd(13) == 2) {
			xsize = rnd(6) + 3;
			ysize = rnd(3) + 3;
			ty = rnd(MAXY - 9) + 1;	/* upper left corner of room */
			if (lv == MAXLEVEL - 1 || lv == MAXLEVEL + MAXVLEVEL - 1)
				troom(lv, xsize, ysize, tx = tx + rnd(MAXX - 24), ty, rnd(3) + 6);
			else
				troom(lv, xsize, ysize, tx, ty, rnd(9));
		}
}

/*
 *	subroutine to create a treasure room of any size at a given location
 *	room is filled with objects and monsters
 *	the coordinate given is that of the upper left corner of the room
 */
static void
troom(int lv, int xsize, int ysize, int tx, int ty, int glyph)
{
	int             i, j;
	int             tp1, tp2;
	for (j = ty - 1; j <= ty + ysize; j++)
		for (i = tx - 1; i <= tx + xsize; i++)	/* clear out space for
							 * room */
			item[i][j] = 0;
	for (j = ty; j < ty + ysize; j++)
		for (i = tx; i < tx + xsize; i++) {	/* now put in the walls */
			item[i][j] = OWALL;
			mitem[i][j] = 0;
		}
	for (j = ty + 1; j < ty + ysize - 1; j++)
		for (i = tx + 1; i < tx + xsize - 1; i++)	/* now clear out
								 * interior */
			item[i][j] = 0;

	switch (rnd(2)) {	/* locate the door on the treasure room */
	case 1:
		item[i = tx + rund(xsize)][j = ty + (ysize - 1) * rund(2)] = OCLOSEDDOOR;
		iarg[i][j] = glyph;	/* on horizontal walls */
		break;
	case 2:
		item[i = tx + (xsize - 1) * rund(2)][j = ty + rund(ysize)] = OCLOSEDDOOR;
		iarg[i][j] = glyph;	/* on vertical walls */
		break;
	};

	tp1 = playerx;
	tp2 = playery;
	playery = ty + (ysize >> 1);
	if (c[HARDGAME] < 2)
		for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2)
			for (i = 0, j = rnd(6); i <= j; i++) {
				something(lv + 2);
				createmonster(makemonst(lv + 1));
			}
	else
		for (playerx = tx + 1; playerx <= tx + xsize - 2; playerx += 2)
			for (i = 0, j = rnd(4); i <= j; i++) {
				something(lv + 2);
				createmonster(makemonst(lv + 3));
			}

	playerx = tp1;
	playery = tp2;
}


/*
	***********
	MAKE_OBJECT
	***********
	subroutine to create the objects in the maze for the given level
 */
static void
makeobject(int j)
{
	int             i;
	if (j == 0) {
		fillroom(OENTRANCE, 0);	/* entrance to dungeon			 */
		fillroom(ODNDSTORE, 0);	/* the DND STORE				 */
		fillroom(OSCHOOL, 0);	/* college of Larn				 */
		fillroom(OBANK, 0);	/* 1st national bank of larn 	 */
		fillroom(OVOLDOWN, 0);	/* volcano shaft to temple 	 */
		fillroom(OHOME, 0);	/* the players home & family 	 */
		fillroom(OTRADEPOST, 0);	/* the trading post			 */
		fillroom(OLRS, 0);	/* the larn revenue service 	 */
		return;
	}
	if (j == MAXLEVEL)
		fillroom(OVOLUP, 0);	/* volcano shaft up from the temple */

	/* make the fixed objects in the maze STAIRS	 */
	if ((j > 0) && (j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1))
		fillroom(OSTAIRSDOWN, 0);
	if ((j > 1) && (j != MAXLEVEL))
		fillroom(OSTAIRSUP, 0);

	/* make the random objects in the maze		 */

	fillmroom(rund(3), OBOOK, j);
	fillmroom(rund(3), OALTAR, 0);
	fillmroom(rund(3), OSTATUE, 0);
	fillmroom(rund(3), OPIT, 0);
	fillmroom(rund(3), OFOUNTAIN, 0);
	fillmroom(rnd(3) - 2, OIVTELETRAP, 0);
	fillmroom(rund(2), OTHRONE, 0);
	fillmroom(rund(2), OMIRROR, 0);
	fillmroom(rund(2), OTRAPARROWIV, 0);
	fillmroom(rnd(3) - 2, OIVDARTRAP, 0);
	fillmroom(rund(3), OCOOKIE, 0);
	if (j == 1)
		fillmroom(1, OCHEST, j);
	else
		fillmroom(rund(2), OCHEST, j);
	if ((j != MAXLEVEL - 1) && (j != MAXLEVEL + MAXVLEVEL - 1))
		fillmroom(rund(2), OIVTRAPDOOR, 0);
	if (j <= 10) {
		fillmroom((rund(2)), ODIAMOND, rnd(10 * j + 1) + 10);
		fillmroom(rund(2), ORUBY, rnd(6 * j + 1) + 6);
		fillmroom(rund(2), OEMERALD, rnd(4 * j + 1) + 4);
		fillmroom(rund(2), OSAPPHIRE, rnd(3 * j + 1) + 2);
	}
	for (i = 0; i < rnd(4) + 3; i++)
		fillroom(OPOTION, newpotion());	/* make a POTION	 */
	for (i = 0; i < rnd(5) + 3; i++)
		fillroom(OSCROLL, newscroll());	/* make a SCROLL	 */
	for (i = 0; i < rnd(12) + 11; i++)
		fillroom(OGOLDPILE, 12 * rnd(j + 1) + (j << 3) + 10);	/* make GOLD	 */
	if (j == 5)
		fillroom(OBANK2, 0);	/* branch office of the bank */
	froom(2, ORING, 0);	/* a ring mail 			 */
	froom(1, OSTUDLEATHER, 0);	/* a studded leather	 */
	froom(3, OSPLINT, 0);	/* a splint mail		 */
	froom(5, OSHIELD, rund(3));	/* a shield				 */
	froom(2, OBATTLEAXE, rund(3));	/* a battle axe			 */
	froom(5, OLONGSWORD, rund(3));	/* a long sword			 */
	froom(5, OFLAIL, rund(3));	/* a flail				 */
	froom(4, OREGENRING, rund(3));	/* ring of regeneration */
	froom(1, OPROTRING, rund(3));	/* ring of protection	 */
	froom(2, OSTRRING, 4);	/* ring of strength + 4 */
	froom(7, OSPEAR, rnd(5));	/* a spear				 */
	froom(3, OORBOFDRAGON, 0);	/* orb of dragon slaying */
	froom(4, OSPIRITSCARAB, 0);	/* scarab of negate spirit */
	froom(4, OCUBEofUNDEAD, 0);	/* cube of undead control	 */
	froom(2, ORINGOFEXTRA, 0);	/* ring of extra regen		 */
	froom(3, ONOTHEFT, 0);	/* device of antitheft 		 */
	froom(2, OSWORDofSLASHING, 0);	/* sword of slashing */
	if (c[BESSMANN] == 0) {
		froom(4, OHAMMER, 0);	/* Bessman's flailing hammer */
		c[BESSMANN] = 1;
	}
	if (c[HARDGAME] < 3 || (rnd(4) == 3)) {
		if (j > 3) {
			froom(3, OSWORD, 3);	/* sunsword + 3  		 */
			froom(5, O2SWORD, rnd(4));	/* a two handed sword	 */
			froom(3, OBELT, 4);	/* belt of striking		 */
			froom(3, OENERGYRING, 3);	/* energy ring			 */
			froom(4, OPLATE, 5);	/* platemail + 5 		 */
		}
	}
}

/*
	subroutine to fill in a number of objects of the same kind
 */

static void
fillmroom(int n, int what_i, int arg)
{
	int             i;
	char            what;

	/* truncate to char width (just in case it matters) */
	what = (char)what_i;
	for (i = 0; i < n; i++)
		fillroom(what, arg);
}

static void
froom(int n, int theitem, int arg)
{
	if (rnd(151) < n)
		fillroom(theitem, arg);
}

/*
	subroutine to put an object into an empty room
 *	uses a random walk
 */
static void
fillroom(int what_i, int arg)
{
	int             x, y;
	char            what;

	/* truncate to char width (just in case it matters) */
	what = (char)what_i;

#ifdef EXTRA
	c[FILLROOM]++;
#endif

	x = rnd(MAXX - 2);
	y = rnd(MAXY - 2);
	while (item[x][y]) {

#ifdef EXTRA
		c[RANDOMWALK]++;/* count up these random walks */
#endif

		x += rnd(3) - 2;
		y += rnd(3) - 2;
		if (x > MAXX - 2)
			x = 1;
		if (x < 1)
			x = MAXX - 2;
		if (y > MAXY - 2)
			y = 1;
		if (y < 1)
			y = MAXY - 2;
	}
	item[x][y] = what;
	iarg[x][y] = arg;
}

/*
	subroutine to put monsters into an empty room without walls or other
	monsters
 */
int
fillmonst(int what)
{
	int             x, y, trys;
	for (trys = 5; trys > 0; --trys) {	/* max # of creation attempts */
		x = rnd(MAXX - 2);
		y = rnd(MAXY - 2);
		if ((item[x][y] == 0) && (mitem[x][y] == 0) && ((playerx != x) || (playery != y))) {
			mitem[x][y] = what;
			know[x][y] = 0;
			hitp[x][y] = monster[what].hitpoints;
			return (0);
		}
	}
	return (-1);		/* creation failure */
}

/*
	creates an entire set of monsters for a level
	must be done when entering a new level
	if sethp(1) then wipe out old monsters else leave them there
 */
static void
sethp(int flg)
{
	int             i, j;
	if (flg)
		for (i = 0; i < MAXY; i++)
			for (j = 0; j < MAXX; j++)
				stealth[j][i] = 0;
	if (level == 0) {
		c[TELEFLAG] = 0;
		return;
	}			/* if teleported and found level 1 then know
				 * level we are on */
	if (flg)
		j = rnd(12) + 2 + (level >> 1);
	else
		j = (level >> 1) + 1;
	for (i = 0; i < j; i++)
		fillmonst(makemonst(level));
	positionplayer();
}

/*
 *	Function to destroy all genocided monsters on the present level
 */
static void
checkgen(void)
{
	int             x, y;
	for (y = 0; y < MAXY; y++)
		for (x = 0; x < MAXX; x++)
			if (monster[mitem[x][y]].genocided)
				mitem[x][y] = 0;	/* no more monster */
}
