/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* mklev.c version 1.0.1 - new makecorridor() */
#ifndef MKLEV
#define	MKLEV
#endif
#include <stdio.h>
#include "hack.h"
#include "def.trap.h"

extern struct monst *makemon();

static char tspe[2],**args;
static int doorindex;
static schar nxcor;

char ismklev;
struct mkroom *croom, *troom;
boolean goldseen;
int nroom;
int smeq[MAXNROFROOMS+1];
char *tfile;

#ifdef WIZARD
extern boolean wizard;
#endif

#define somex() ((rand()%(croom->hx-croom->lx+1))+croom->lx)
#define somey() ((rand()%(croom->hy-croom->ly+1))+croom->ly)

extern char nul[40];
extern struct rm levl[COLNO][ROWNO];
extern struct monst *fmon;
extern struct obj *fobj;
extern struct gen *fgold, *ftrap;
extern char *fut_geno;      /* monsters that should not be created anymore */
extern struct mkroom rooms[MAXNROFROOMS+1];
extern coord doors[DOORMAX];
extern int comp();
extern xchar dlevel;
extern xchar xdnstair,xupstair,ydnstair,yupstair;
   
zeroout(addr, len)
char *addr;
int len;
{
	while(--len>0) *addr++=0;
}

mklev()
{
	register unsigned tryct;

	tfile = lock;
	troom = NULL;
	doorindex = 0;
	nroom = 0;

	if(getbones()) return;
	ismklev = 1;
	if(dlevel < rn1(3, 26)) tspe[0] = 'a';   /* normal level */
	else tspe[0] = 'b';         /* maze */
	tspe[1] = 0;

	/* zap the object bases */
	{int i;extern int bases[];
	for(i=0;i<15;bases[i++]=0);}

	/* zap the room pictures */
	zeroout(levl,COLNO*ROWNO*sizeof(struct rm));

	fmon = NULL;
	fobj = NULL;
	fgold = ftrap = NULL;
   
	zeroout( doors, sizeof(coord)*DOORMAX);

	xdnstair = xupstair = ydnstair = yupstair = 0;
	zeroout(rooms, (MAXNROFROOMS+1)*sizeof(struct mkroom));

	init_objects();
	rooms[0].hx = -1;   /* in case we are in a maze */

	/* a: normal; b: maze */
	if(*tspe == 'b') {
		makemaz();
#ifdef DOSAVE
		{
		int fd;
		if((fd = creat(tfile,FMASK)) < 0) 
			panic("Cannot create %s\n", tfile);
		savelev(fd);
		close(fd);
		}
#endif
		ismklev = 0;
		return(0);
	}

	/* construct the rooms */
	while(nroom < (MAXNROFROOMS/3)) {
		croom = rooms;
		nroom = 0;
		(void) makerooms(0);      /* not secret */
	}

	/* for each room: put things inside */
	for(croom = rooms; croom->hx > 0; croom++) {

		/* put a sleeping monster inside */
		if(!rn2(3)) (void)
			makemon((struct permonst *) 0, somex(), somey());

		/* put traps and mimics inside */
		goldseen = FALSE;
		while(!rn2(8-(dlevel/6))) mktrap(0,0);
		if(!goldseen && !rn2(3)) mkgold(0,somex(),somey());
		if(!rn2(3)) {
			mkobj_at(0, somex(), somey());
			tryct = 0;
			while(!rn2(5)) {
				if(++tryct > 100){
					myprintf("tryct overflow4\n");
					break;
				}
				mkobj_at(0, somex(), somey());
			}
		}
	}
	tryct = 0;
	do {
		if(++tryct > 1000) panic("Cannot make dnstairs\n");
		croom = &rooms[rn2(nroom)];
		xdnstair = somex();
		ydnstair = somey();
	} while((*tspe =='n' && (!(xdnstair%2) || !(ydnstair%2))) ||
		g_at(xdnstair,ydnstair,ftrap));
	levl[xdnstair][ydnstair].scrsym ='>';
	levl[xdnstair][ydnstair].typ = STAIRS;
	troom = croom;
	do {
		if(++tryct > 2000) panic("Cannot make upstairs\n");
		croom = &rooms[rn2(nroom)];
		xupstair = somex();
		yupstair = somey();
	} while(croom == troom || m_at(xupstair,yupstair) ||
		g_at(xupstair,yupstair,ftrap));
	levl[xupstair][yupstair].scrsym ='<';
	levl[xupstair][yupstair].typ = STAIRS;

#ifdef DEBUG
	dumpit();
#endif
	qsort((char *) rooms, nroom, sizeof(struct mkroom), comp);
	makecorridors();
	make_niches();

	/* make a secret treasure vault, not connected to the rest */
	if(nroom < (2*MAXNROFROOMS/3)) if(!rn2(3)) {
		register int x,y;
		troom = croom = &rooms[nroom];
		if(makerooms(1)) {      /* make secret room */
			troom->rtype = 6;      /* treasure vault */
			for(x = troom->lx; x <= troom->hx; x++)
			for(y = troom->ly; y <= troom->hy; y++)
				mkgold(rnd(dlevel*100) + 50, x, y);
			if(!rn2(3))
				makevtele();
		}
	}

#ifdef WIZARD
	if(wizard){
		if(rn2(3)) mkshop(); else mkzoo();
	} else
#endif WIZARD
	if(dlevel > 1 && dlevel < 20 && rn2(dlevel) < 2)
		mkshop();
	else
		if(dlevel > 6 && !rn2(7) )
			mkzoo();
#ifdef DOSAVE
	{
	int fd;
	if((fd = creat(tfile,FMASK)) < 0) 
		panic("Cannot create %s\n", tfile);
	savelev(fd);
	close(fd);
	}
#endif
	ismklev = 0;
	return(0);
}

makerooms(secret)
int secret;
{
	register int lowx, lowy;
	register int tryct = 0;

   while(nroom < (MAXNROFROOMS/2) || secret)
      for(lowy = rn1(3,3); lowy < ROWNO-7; lowy += rn1(2,4))
         {
         for(lowx = rn1(3,4); lowx < COLNO-10; lowx += rn1(2,7))
            {
            if (tryct++ > 10000) return(0);

            if ((lowy += (rn2(5)-2)) < 3)
               lowy = 3;
            else
               if(lowy > ROWNO-6)
                  lowy = ROWNO-6;

            if(levl[lowx][lowy].typ) continue;

            if ((secret && maker(lowx, 1, lowy, 1)) ||
                (!secret && maker(lowx,rn1(9,2),lowy,rn1(4,2))
                && nroom+2 > MAXNROFROOMS)) return(1);
            }
         }
	return(1);
}

comp(x,y)
register struct mkroom *x,*y;
{
   if(x->lx < y->lx) return(-1);
   return(x->lx > y->lx);
}

coord
finddpos(xl,yl,xh,yh) {
coord ff;
register int x,y;
   ff.x = (xl == xh) ? xl : (xl + rn2(xh-xl+1));
   ff.y = (yl == yh) ? yl : (yl + rn2(yh-yl+1));
   if(okdoor(ff.x, ff.y)) return(ff);
   if(xl < xh) for(x = xl; x <= xh; x++)
      if(okdoor(x, ff.y)){
         ff.x = x;
         return(ff);
      }
   if(yl < yh) for(y = yl; y <= yh; y++)
      if(okdoor(ff.x, y)){
         ff.y = y;
         return(ff);
      }
   return(ff);
}

/* if allowable, create a door at [x,y] */
okdoor(x,y)
register int x,y;
{
	if(levl[x-1][y].typ == DOOR || levl[x+1][y].typ == DOOR ||
	   levl[x][y+1].typ == DOOR || levl[x][y-1].typ == DOOR ||
	   levl[x-1][y].typ == SDOOR || levl[x+1][y].typ == SDOOR ||
	   levl[x][y-1].typ == SDOOR || levl[x][y+1].typ == SDOOR ||
	   (levl[x][y].typ != HWALL && levl[x][y].typ != VWALL) ||
	   doorindex >= DOORMAX)
		return(0);
	return(1);
}

dodoor(x,y,aroom)
register int x,y;
register struct mkroom *aroom;
{
	if(doorindex >= DOORMAX) panic("DOORMAX exceeded?");
	if(!okdoor(x,y) && nxcor) return;
	dosdoor(x,y,aroom,rn2(8) ? DOOR : SDOOR);
}

dosdoor(x,y,aroom,type)
register int x,y;
register struct mkroom *aroom;
register int type;
{
	register struct mkroom *broom;
	register int tmp;

	levl[x][y].typ = type;
	if(type == DOOR)
		levl[x][y].scrsym ='+';
	aroom->doorct++;
	broom = aroom+1;
	if(broom->hx < 0) tmp = doorindex; else
	for(tmp = doorindex; tmp > broom->fdoor; tmp--)
		doors[tmp] = doors[tmp-1];
	doorindex++;
	doors[tmp].x = x;
	doors[tmp].y = y;
	for( ; broom->hx >= 0; broom++) broom->fdoor++;
}

/* Only called from makerooms() */
maker(lowx,ddx,lowy,ddy)
schar lowx,ddx,lowy,ddy;
{
	register int x, y, hix = lowx+ddx, hiy = lowy+ddy;

	if(nroom >= MAXNROFROOMS) return(0);
	if(hix > COLNO-5) hix = COLNO-5;
	if(hiy > ROWNO-4) hiy = ROWNO-4;
chk:
	if(hix <= lowx || hiy <= lowy) return(0);

	/* check area around room (and make room smaller if necessary) */
	for(x = lowx-4; x <= hix+4; x++)
		for(y = lowy-3; y <= hiy+3; y++)
			if(levl[x][y].typ) {
				if(rn2(3)) return(0);
				lowx = x+5;
				lowy = y+4;
				goto chk;
			}

	/* on low levels the room is lit (usually) */
	/* secret vaults are always lit */
	if((rnd(dlevel) < 10 && rn2(77)) || (ddx == 1 && ddy == 1))
		for(x = lowx-1; x <= hix+1; x++)
			for(y = lowy-1; y <= hiy+1; y++)
				levl[x][y].lit = 1;
	croom->lx = lowx;
	croom->hx = hix;
	croom->ly = lowy;
	croom->hy = hiy;
	croom->rtype = croom->doorct = croom->fdoor = 0;
	for(x = lowx-1; x <= hix+1; x++)
	    for(y = lowy-1; y <= hiy+1; y += (hiy-lowy+2)) {
		levl[x][y].scrsym = '-';
		levl[x][y].typ = HWALL;
	}
	for(x = lowx-1; x <= hix+1; x += (hix-lowx+2))
	    for(y = lowy; y <= hiy; y++) {
		levl[x][y].scrsym = '|';
		levl[x][y].typ = VWALL;
	}
	for(x = lowx; x <= hix; x++)
	    for(y = lowy; y <= hiy; y++) {
		levl[x][y].scrsym = '.';
		levl[x][y].typ = ROOM;
	}
	croom++;
	croom->hx = -1;
	smeq[nroom] = nroom;
	nroom++;
	return(1);
}

makecorridors() {
	register int a,b;

	nxcor = 0;
	for(a = 0; a < nroom-1; a++)
		join(a, a+1);
	for(a = 0; a < nroom-2; a++)
	    if(smeq[a] != smeq[a+2])
		join(a, a+2);
	for(a = 0; a < nroom; a++)
	    for(b = 0; b < nroom; b++)
		if(smeq[a] != smeq[b])
		    join(a, b);
	if(nroom > 2)
	    for(nxcor = rn2(nroom) + 4; nxcor; nxcor--) {
		a = rn2(nroom);
		b = rn2(nroom-2);
		if(b >= a) b += 2;
		join(a, b);
	    }
}

join(a,b)
register int a,b;
{
	coord cc,tt;
	register int tx, ty, xx, yy;
	register struct rm *crm;
	register int dx, dy, dix, diy, cct;

	croom = &rooms[a];
	troom = &rooms[b];

	/* find positions cc and tt for doors in croom and troom
	   and direction for a corridor between them */

   if(troom->hx < 0 || croom->hx < 0 || doorindex >= DOORMAX) return;
   if(troom->lx > croom->hx) {
      dx = 1;
      dy = 0;
      xx = croom->hx+1;
      tx = troom->lx-1;
      cc = finddpos(xx,croom->ly,xx,croom->hy);
      tt = finddpos(tx,troom->ly,tx,troom->hy);
   } else if(troom->hy < croom->ly) {
      dy = -1;
      dx = 0;
      yy = croom->ly-1;
      cc = finddpos(croom->lx,yy,croom->hx,yy);
      ty = troom->hy+1;
      tt = finddpos(troom->lx,ty,troom->hx,ty);
   } else if(troom->hx < croom->lx) {
      dx = -1;
      dy = 0;
      xx = croom->lx-1;
      tx = troom->hx+1;
      cc = finddpos(xx,croom->ly,xx,croom->hy);
      tt = finddpos(tx,troom->ly,tx,troom->hy);
   } else {
      dy = 1;
      dx = 0;
      yy = croom->hy+1;
      ty = troom->ly-1;
      cc = finddpos(croom->lx,yy,croom->hx,yy);
      tt = finddpos(troom->lx,ty,troom->hx,ty);
   }
	xx = cc.x;
	yy = cc.y;
	tx = tt.x - dx;
	ty = tt.y - dy;
	if(nxcor && levl[xx+dx][yy+dy].typ)
		return;
	dodoor(xx,yy,croom);

	cct = 0;
	while(xx != tx || yy != ty) {
	    xx += dx;
	    yy += dy;

	    /* loop: dig corridor at [xx,yy] and find new [xx,yy] */
	    if(cct++ > 500 || (nxcor && !rn2(35)))
		return;

	    if(xx == COLNO-1 || xx == 0 || yy == 0 || yy == ROWNO-1)
		return;		/* impossible */

	    crm = &levl[xx][yy];
	    if(!(crm->typ)) {
		if(rn2(100)) {
			crm->typ = CORR;
			crm->scrsym = CORR_SYM;
		} else {
			crm->typ = SCORR;
			crm->scrsym = ' ';
		}
		if(nxcor && !rn2(50)) {
			mkobj_at(ROCK_SYM, xx, yy);
		}
	    } else
	    if(crm->typ != CORR && crm->typ != SCORR) {
		/* strange ... */
      return;
	    }

	    /* find next corridor position */
	    dix = abs(xx-tx);
	    diy = abs(yy-ty);

	    /* do we have to change direction ? */
	    if(dy && dix > diy) {
		register int ddx = (xx > tx) ? -1 : 1;

		crm = &levl[xx+ddx][yy];
		if(!crm->typ || crm->typ == CORR || crm->typ == SCORR) {
		    dx = ddx;
		    dy = 0;
		    continue;
		}
	    } else if(dx && diy > dix) {
		register int ddy = (yy > ty) ? -1 : 1;

		crm = &levl[xx][yy+ddy];
		if(!crm->typ || crm->typ == CORR || crm->typ == SCORR) {
		    dy = ddy;
		    dx = 0;
		    continue;
		}
	    }

	    /* continue straight on? */
	    crm = &levl[xx+dx][yy+dy];
	    if(!crm->typ || crm->typ == CORR || crm->typ == SCORR)
		continue;

	    /* no, what must we do now?? */
	    if(dx) {
		dx = 0;
		dy = (ty < yy) ? -1 : 1;
		crm = &levl[xx+dx][yy+dy];
		if(!crm->typ || crm->typ == CORR || crm->typ == SCORR)
		    continue;
		dy = -dy;
		continue;
	    } else {
		dy = 0;
		dx = (tx < xx) ? -1 : 1;
		crm = &levl[xx+dx][yy+dy];
		if(!crm->typ || crm->typ == CORR || crm->typ == SCORR)
		    continue;
		dx = -dx;
		continue;
	    }
   }
	/* we succeeded in digging the corridor */
	dodoor(tt.x, tt.y, troom);

	if(smeq[a] < smeq[b])
		smeq[b] = smeq[a];
	else
		smeq[a] = smeq[b];
}

make_niches()
{
	register int ct = rn2(nroom/2 + 1)+1;
	while(ct--) makeniche(FALSE);
}

makevtele()
{
	makeniche(TRUE);
}

makeniche(with_trap)
boolean with_trap;
{
	register struct mkroom *aroom;
	register struct rm *rm;
	register int vct = 8;
	coord dd;
	register int dy,xx,yy;
	register struct gen *gtmp;

	if(doorindex < DOORMAX)
	  while(vct--) {
	    aroom = &rooms[rn2(nroom-1)];
	    if(aroom->rtype != 0) continue;	/* not an ordinary room */
	    if(rn2(2)) {
		dy = 1;
		dd = finddpos(aroom->lx,aroom->hy+1,aroom->hx,aroom->hy+1);
	    } else {
		dy = -1;
		dd = finddpos(aroom->lx,aroom->ly-1,aroom->hx,aroom->ly-1);
	    }
	    xx = dd.x;
	    yy = dd.y;
	    if((rm = &levl[xx][yy+dy])->typ) continue;
	    if(with_trap || !rn2(4)) {
		rm->typ = SCORR;
		rm->scrsym = ' ';
		if(with_trap) {
		    gtmp = newgen();
		    gtmp->gx = xx;
		    gtmp->gy = yy+dy;
		    gtmp->gflag = TELEP_TRAP | ONCE;
		    gtmp->ngen = ftrap;
		    ftrap = gtmp;
		    make_engr_at(xx,yy-dy,"ad ae?ar um");
		}
		dosdoor(xx,yy,aroom,SDOOR);
	    } else {
		rm->typ = CORR;
		rm->scrsym = CORR_SYM;
		if(rn2(7))
		    dosdoor(xx,yy,aroom,rn2(5) ? SDOOR : DOOR);
		else {
		    mksobj_at(SCROLL_SYM,SCR_TELEPORTATION,xx,yy+dy);
		    if(!rn2(3)) mkobj_at(0,xx,yy+dy);
		}
	    }
	    return;
      }
}

/* make a trap somewhere (in croom if mazeflag = 0) */
mktrap(num,mazeflag)
register int num,mazeflag;
{
	register struct gen *gtmp;
	register int kind,nopierc,nomimic,fakedoor,fakegold,tryct = 0;
	register xchar mx,my;


   if(!num || num >= TRAPNUM) {
      nopierc = (dlevel < 4) ? 1 : 0;
      nomimic = (dlevel < 9 || goldseen ) ? 1 : 0;
      if(index(fut_geno, 'M')) nomimic = 1;
      kind = rn2(TRAPNUM - nopierc - nomimic);
      /* note: PIERC = 7, MIMIC = 8, TRAPNUM = 9 */
   } else kind = num;

   if(kind == MIMIC) {
		register struct monst *mtmp;

      fakedoor = (!rn2(3) && !mazeflag);
      fakegold = (!fakedoor && !rn2(2));
      if(fakegold) goldseen = TRUE;
      do {
         if(++tryct > 200) return;
         if(fakedoor) {
            /* note: fakedoor maybe on actual door */
            if(rn2(2)){
               if(rn2(2))
                  mx = croom->hx+1;
               else mx = croom->lx-1;
               my = somey();
            } else {
               if(rn2(2))
                  my = croom->hy+1;
               else my = croom->ly-1;
               mx = somex();
            }
         } else if(mazeflag) {
            extern coord mazexy();
            coord mm;
            mm = mazexy();
            mx = mm.x;
            my = mm.y;
         } else {
            mx = somex();
            my = somey();
         }
      } while(m_at(mx,my));
      if(mtmp = makemon(PM_MIMIC,mx,my))
          mtmp->mimic =
         fakegold ? '$' : fakedoor ? '+' :
         (mazeflag && rn2(2)) ? AMULET_SYM :
         "=/)%?![<>" [ rn2(9) ];
      return;
   }
   gtmp = newgen();
   gtmp->gflag = kind;
   do {
      if(++tryct > 200){
         free((char *) gtmp);
         return;
      }
      if(mazeflag){
         extern coord mazexy();
         coord mm;
         mm = mazexy();
         gtmp->gx = mm.x;
         gtmp->gy = mm.y;
      } else {
         gtmp->gx = somex();
         gtmp->gy = somey();
      }
   } while(g_at(gtmp->gx, gtmp->gy, ftrap));
   gtmp->ngen = ftrap;
   ftrap = gtmp;
   if(mazeflag && !rn2(10) && gtmp->gflag < PIERC) gtmp->gflag |= SEEN;
}

#ifdef DEBUG
dumpit()
   {
   int x, y;
   struct rm *room;

   cgetret(); 

   /* kludge in making everything visible */
   for(y=0; y < ROWNO; y++)
      for(x=0; x < COLNO-3; x++)
         if ( (room = &levl[x][y])->typ)
            at(x,y, (room->scrsym) ? room->scrsym : '¿');
   }
#endif
