/* source/generate.c: initialize/create a dungeon or town level

   Copyright (C) 1989-2008 James E. Wilson, Robert A. Koeneke, 
                           David J. Grabiner

   This file is part of Umoria.

   Umoria is free software; you can redistribute it and/or modify 
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Umoria is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License 
   along with Umoria.  If not, see <http://www.gnu.org/licenses/>. */

#include "config.h"
#include "constant.h"
#include "types.h"
#include "externs.h"

#if defined(USG) && !defined(VMS) && !defined(MAC)
#if !defined(ATARIST_MWC) && !defined(AMIGA)
#if !defined(__TURBOC__)
#include <memory.h>
#else
#ifndef ATARIST_TC
#include <mem.h>
#endif
#endif
#endif
#endif

#if defined(MAC)
#include <string.h>
#endif

#ifdef ATARIST_TC
#include <string.h>
#endif

typedef struct coords {
  int x, y;
} coords;

#if defined(LINT_ARGS)
static void correct_dir(int *, int * , int, int, int, int);
static void rand_dir(int *,int *);
static void blank_cave(void);
static void fill_cave(int);
static void place_boundary(void);
static void place_streamer(int, int);
static void place_open_door(int, int);
static void place_broken_door(int, int);
static void place_closed_door(int, int);
static void place_locked_door(int, int);
static void place_stuck_door(int, int);
static void place_secret_door(int, int);
static void place_door(int, int);
static void place_up_stairs(int, int);
static void place_down_stairs(int, int);
static void place_stairs(int, int, int);
static void vault_trap(int, int, int, int, int);
static void vault_monster(int, int, int);
static void build_room(int, int);
static void build_type1(int, int);
static void build_type2(int, int);
static void build_type3(int, int);
static void build_tunnel(int, int, int, int);
static int next_to(int, int);
static void try_door(int, int);
static void new_spot(int16 *, int16 *);
static void cave_gen(void);
static void build_store(int, int, int);
static void tlink(void);
static void mlink(void);
static void town_gen(void);
#endif

static coords doorstk[100];
static int doorindex;


/* Always picks a correct direction		*/
static void correct_dir(rdir, cdir, y1, x1, y2, x2)
int *rdir, *cdir;
register int y1, x1, y2, x2;
{
  if (y1 < y2)
    *rdir =  1;
  else if (y1 == y2)
    *rdir =  0;
  else
    *rdir = -1;
  if (x1 < x2)
    *cdir =  1;
  else if (x1 == x2)
    *cdir =  0;
  else
    *cdir = -1;
  if ((*rdir != 0) && (*cdir != 0))
    {
      if (randint (2) == 1)
	*rdir = 0;
      else
	*cdir = 0;
    }
}


/* Chance of wandering direction			*/
static void rand_dir(rdir, cdir)
int *rdir, *cdir;
{
  register int tmp;

  tmp = randint(4);
  if (tmp < 3)
    {
      *cdir = 0;
      *rdir = -3 + (tmp << 1); /* tmp=1 -> *rdir=-1; tmp=2 -> *rdir=1 */
    }
  else
    {
      *rdir = 0;
      *cdir = -7 + (tmp << 1); /* tmp=3 -> *cdir=-1; tmp=4 -> *cdir=1 */
    }
}


/* Blanks out entire cave				-RAK-	*/
static void blank_cave()
{
#ifndef USG
  bzero ((char *)&cave[0][0], sizeof (cave));
#else
#ifdef MAC
  /* On the mac, cave is a pointer, so sizeof(cave) = 4! */
  (void)memset((char *)&cave[0][0], 0,
	       (long) sizeof(cave_type) * MAX_HEIGHT * MAX_WIDTH);
#else
  (void)memset((char *)&cave[0][0], 0, sizeof (cave));
#endif
#endif
}


/* Fills in empty spots with desired rock		-RAK-	*/
/* Note: 9 is a temporary value.				*/
static void fill_cave(fval)
register int fval;
{
  register int i, j;
  register cave_type *c_ptr;

  /* no need to check the border of the cave */

  for (i = cur_height - 2; i > 0; i--)
    {
      c_ptr = &cave[i][1];
      for (j = cur_width - 2; j > 0; j--)
	{
	  if ((c_ptr->fval == NULL_WALL) || (c_ptr->fval == TMP1_WALL) ||
	      (c_ptr->fval == TMP2_WALL))
	    c_ptr->fval = fval;
	  c_ptr++;
	}
    }
}

#ifdef DEBUG
#include <assert.h>
#endif

/* Places indestructible rock around edges of dungeon	-RAK-	*/
static void place_boundary()
{
  register int i;
  register cave_type *top_ptr, *bottom_ptr;
  cave_type (*left_ptr)[MAX_WIDTH];
  cave_type (*right_ptr)[MAX_WIDTH];

  /* put permanent wall on leftmost row and rightmost row */
  left_ptr = (cave_type (*)[MAX_WIDTH]) &cave[0][0];
  right_ptr = (cave_type (*)[MAX_WIDTH]) &cave[0][cur_width - 1];

  for (i = 0; i < cur_height; i++)
    {
#ifdef DEBUG
      assert ((cave_type *)left_ptr == &cave[i][0]);
      assert ((cave_type *)right_ptr == &cave[i][cur_width-1]);
#endif

      ((cave_type *)left_ptr)->fval	= BOUNDARY_WALL;
      left_ptr++;
      ((cave_type *)right_ptr)->fval	= BOUNDARY_WALL;
      right_ptr++;
    }

  /* put permanent wall on top row and bottom row */
  top_ptr = &cave[0][0];
  bottom_ptr = &cave[cur_height - 1][0];

  for (i = 0; i < cur_width; i++)
    {
#ifdef DEBUG
      assert (top_ptr == &cave[0][i]);
      assert (bottom_ptr == &cave[cur_height - 1][i]);
#endif

      top_ptr->fval	= BOUNDARY_WALL;
      top_ptr++;
      bottom_ptr->fval	= BOUNDARY_WALL;
      bottom_ptr++;
    }
}


/* Places "streamers" of rock through dungeon		-RAK-	*/
static void place_streamer(fval, treas_chance)
int fval;
int treas_chance;
{
  register int i, tx, ty;
  int y, x, t1, t2, dir;
  register cave_type *c_ptr;

  /* Choose starting point and direction		*/
  y = (cur_height / 2) + 11 - randint(23);
  x = (cur_width / 2)  + 16 - randint(33);

  dir = randint(8);	/* Number 1-4, 6-9	*/
  if (dir > 4)
    dir = dir + 1;

  /* Place streamer into dungeon			*/
  t1 = 2*DUN_STR_RNG + 1;	/* Constants	*/
  t2 =	 DUN_STR_RNG + 1;
  do
    {
      for (i = 0; i < DUN_STR_DEN; i++)
	{
	  ty = y + randint(t1) - t2;
	  tx = x + randint(t1) - t2;
	  if (in_bounds(ty, tx))
	    {
	      c_ptr = &cave[ty][tx];
	      if (c_ptr->fval == GRANITE_WALL)
		{
		  c_ptr->fval = fval;
		  if (randint(treas_chance) == 1)
		    place_gold(ty, tx);
		}
	    }
	}
    }
  while (mmove(dir, &y, &x));
}


static void place_open_door(y, x)
int y, x;
{
  register int cur_pos;
  register cave_type *cave_ptr;

  cur_pos = popt();
  cave_ptr = &cave[y][x];
  cave_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_OPEN_DOOR);
  cave_ptr->fval  = CORR_FLOOR;
}


static void place_broken_door(y, x)
int y, x;
{
  register int cur_pos;
  register cave_type *cave_ptr;

  cur_pos = popt();
  cave_ptr = &cave[y][x];
  cave_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_OPEN_DOOR);
  cave_ptr->fval  = CORR_FLOOR;
  t_list[cur_pos].p1 = 1;
}


static void place_closed_door(y, x)
int y, x;
{
  register int cur_pos;
  register cave_type *cave_ptr;

  cur_pos = popt();
  cave_ptr = &cave[y][x];
  cave_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_CLOSED_DOOR);
  cave_ptr->fval  = BLOCKED_FLOOR;
}


static void place_locked_door(y, x)
int y, x;
{
  register int cur_pos;
  register cave_type *cave_ptr;

  cur_pos = popt();
  cave_ptr = &cave[y][x];
  cave_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_CLOSED_DOOR);
  cave_ptr->fval  = BLOCKED_FLOOR;
  t_list[cur_pos].p1 = randint(10) + 10;
}


static void place_stuck_door(y, x)
int y, x;
{
  register int cur_pos;
  register cave_type *cave_ptr;

  cur_pos = popt();
  cave_ptr = &cave[y][x];
  cave_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_CLOSED_DOOR);
  cave_ptr->fval  = BLOCKED_FLOOR;
  t_list[cur_pos].p1 = -randint(10) - 10;
}


static void place_secret_door(y, x)
int y, x;
{
  register int cur_pos;
  register cave_type *cave_ptr;

  cur_pos = popt();
  cave_ptr = &cave[y][x];
  cave_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_SECRET_DOOR);
  cave_ptr->fval  = BLOCKED_FLOOR;
}


static void place_door(y, x)
int y, x;
{
  register int tmp;

  tmp = randint(3);
  if (tmp == 1)
    {
      if (randint(4) == 1)
	place_broken_door(y, x);
      else
	place_open_door(y, x);
    }
  else if (tmp == 2)
    {
      tmp = randint(12);
      if (tmp > 3)
	place_closed_door(y, x);
      else if (tmp == 3)
	place_stuck_door(y, x);
      else
	place_locked_door(y, x);
    }
  else
    place_secret_door(y, x);
}


/* Place an up staircase at given y, x			-RAK-	*/
static void place_up_stairs(y, x)
int y, x;
{
  register int cur_pos;
  register cave_type *cave_ptr;

  cave_ptr = &cave[y][x];
  if (cave_ptr->tptr != 0)
    (void) delete_object(y, x);
  cur_pos = popt();
  cave_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_UP_STAIR);
}


/* Place a down staircase at given y, x			-RAK-	*/
static void place_down_stairs(y, x)
int y, x;
{
  register int cur_pos;
  register cave_type *cave_ptr;

  cave_ptr = &cave[y][x];
  if (cave_ptr->tptr != 0)
    (void) delete_object(y, x);
  cur_pos = popt();
  cave_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_DOWN_STAIR);
}


/* Places a staircase 1=up, 2=down			-RAK-	*/
static void place_stairs(typ, num, walls)
int typ, num, walls;
{
  register cave_type *cave_ptr;
  int i, j, flag;
  register int y1, x1, y2, x2;

  for (i = 0; i < num; i++)
    {
      flag = FALSE;
      do
	{
	  j = 0;
	  do
	    {
	      /* Note: don't let y1/x1 be zero, and don't let y2/x2 be equal
		 to cur_height-1/cur_width-1, these values are always
		 BOUNDARY_ROCK. */
	      y1 = randint(cur_height - 14);
	      x1 = randint(cur_width  - 14);
	      y2 = y1 + 12;
	      x2 = x1 + 12;
	      do
		{
		  do
		    {
		      cave_ptr = &cave[y1][x1];
		      if (cave_ptr->fval <= MAX_OPEN_SPACE
			  && (cave_ptr->tptr == 0)
			  && (next_to_walls(y1, x1) >= walls))
			{
			  flag = TRUE;
			  if (typ == 1)
			    place_up_stairs(y1, x1);
			  else
			    place_down_stairs(y1, x1);
			}
		      x1++;
		    }
		  while ((x1 != x2) && (!flag));
		  x1 = x2 - 12;
		  y1++;
		}
	      while ((y1 != y2) && (!flag));
	      j++;
	    }
	  while ((!flag) && (j <= 30));
	  walls--;
	}
      while (!flag);
    }
}


/* Place a trap with a given displacement of point	-RAK-	*/
static void vault_trap(y, x, yd, xd, num)
int y, x, yd, xd, num;
{
  register int count, y1, x1;
  int i, flag;
  register cave_type *c_ptr;

  for (i = 0; i < num; i++)
    {
      flag = FALSE;
      count = 0;
      do
	{
	  y1 = y - yd - 1 + randint(2*yd+1);
	  x1 = x - xd - 1 + randint(2*xd+1);
	  c_ptr = &cave[y1][x1];
	  if ((c_ptr->fval != NULL_WALL) && (c_ptr->fval <= MAX_CAVE_FLOOR)
	      && (c_ptr->tptr == 0))
	    {
	      place_trap(y1, x1, randint(MAX_TRAP)-1);
	      flag = TRUE;
	    }
	  count++;
	}
      while ((!flag) && (count <= 5));
    }
}


/* Place a trap with a given displacement of point	-RAK-	*/
static void vault_monster(y, x, num)
int y, x, num;
{
  register int i;
  int y1, x1;

  for (i = 0; i < num; i++)
    {
      y1 = y;
      x1 = x;
      (void) summon_monster(&y1, &x1, TRUE);
    }
}


/* Builds a room at a row, column coordinate		-RAK-	*/
static void build_room(yval, xval)
int yval, xval;
{
  register int i, j, y_depth, x_right;
  int y_height, x_left;
  int8u floor;
  register cave_type *c_ptr, *d_ptr;

  if (dun_level <= randint(25))
    floor = LIGHT_FLOOR;	/* Floor with light	*/
  else
    floor = DARK_FLOOR;		/* Dark floor		*/

  y_height = yval - randint(4);
  y_depth  = yval + randint(3);
  x_left   = xval - randint(11);
  x_right  = xval + randint(11);

  /* the x dim of rooms tends to be much larger than the y dim, so don't
     bother rewriting the y loop */

  for (i = y_height; i <= y_depth; i++)
    {
      c_ptr = &cave[i][x_left];
      for (j = x_left; j <= x_right; j++)
	{
	  c_ptr->fval  = floor;
	  c_ptr->lr = TRUE;
	  c_ptr++;
	}
    }

  for (i = (y_height - 1); i <= (y_depth + 1); i++)
    {
      c_ptr = &cave[i][x_left-1];
      c_ptr->fval   = GRANITE_WALL;
      c_ptr->lr = TRUE;
      c_ptr = &cave[i][x_right+1];
      c_ptr->fval  = GRANITE_WALL;
      c_ptr->lr = TRUE;
    }

  c_ptr = &cave[y_height - 1][x_left];
  d_ptr = &cave[y_depth + 1][x_left];
  for (i = x_left; i <= x_right; i++)
    {
      c_ptr->fval  = GRANITE_WALL;
      c_ptr->lr = TRUE;
      c_ptr++;
      d_ptr->fval   = GRANITE_WALL;
      d_ptr->lr = TRUE;
      d_ptr++;
    }
}


/* Builds a room at a row, column coordinate		-RAK-	*/
/* Type 1 unusual rooms are several overlapping rectangular ones	*/
static void build_type1(yval, xval)
int yval, xval;
{
  int y_height, y_depth;
  int x_left, x_right, limit;
  register int i0, i, j;
  int8u floor;
  register cave_type *c_ptr, *d_ptr;

  if (dun_level <= randint(25))
    floor = LIGHT_FLOOR;	/* Floor with light	*/
  else
    floor = DARK_FLOOR;		/* Dark floor		*/
  limit = 1 + randint(2);
  for (i0 = 0; i0 < limit; i0++)
    {
      y_height = yval - randint(4);
      y_depth  = yval + randint(3);
      x_left   = xval - randint(11);
      x_right  = xval + randint(11);

      /* the x dim of rooms tends to be much larger than the y dim, so don't
	 bother rewriting the y loop */

      for (i = y_height; i <= y_depth; i++)
	{
	  c_ptr = &cave[i][x_left];
	  for (j = x_left; j <= x_right; j++)
	    {
	      c_ptr->fval  = floor;
	      c_ptr->lr = TRUE;
	      c_ptr++;
	    }
	}
      for (i = (y_height - 1); i <= (y_depth + 1); i++)
	{
	  c_ptr = &cave[i][x_left-1];
	  if (c_ptr->fval != floor)
	    {
	      c_ptr->fval  = GRANITE_WALL;
	      c_ptr->lr = TRUE;
	    }
	  c_ptr = &cave[i][x_right+1];
	  if (c_ptr->fval != floor)
	    {
	      c_ptr->fval  = GRANITE_WALL;
	      c_ptr->lr = TRUE;
	    }
	}
      c_ptr = &cave[y_height - 1][x_left];
      d_ptr = &cave[y_depth + 1][x_left];
      for (i = x_left; i <= x_right; i++)
	{
	  if (c_ptr->fval != floor)
	    {
	      c_ptr->fval  = GRANITE_WALL;
	      c_ptr->lr = TRUE;
	    }
	  c_ptr++;
	  if (d_ptr->fval != floor)
	    {
	      d_ptr->fval  = GRANITE_WALL;
	      d_ptr->lr = TRUE;
	    }
	  d_ptr++;
	}
    }
}


/* Builds an unusual room at a row, column coordinate	-RAK-	*/
/* Type 2 unusual rooms all have an inner room:			*/
/*   1 - Just an inner room with one door			*/
/*   2 - An inner room within an inner room			*/
/*   3 - An inner room with pillar(s)				*/
/*   4 - Inner room has a maze					*/
/*   5 - A set of four inner rooms				*/
static void build_type2(yval, xval)
int yval, xval;
{
  register int i, j, y_height, x_left;
  int y_depth, x_right, tmp;
  int8u floor;
  register cave_type *c_ptr, *d_ptr;

  if (dun_level <= randint(25))
    floor = LIGHT_FLOOR;	/* Floor with light	*/
  else
    floor = DARK_FLOOR;		/* Dark floor		*/
  y_height = yval - 4;
  y_depth  = yval + 4;
  x_left   = xval - 11;
  x_right  = xval + 11;

  /* the x dim of rooms tends to be much larger than the y dim, so don't
     bother rewriting the y loop */

  for (i = y_height; i <= y_depth; i++)
    {
      c_ptr = &cave[i][x_left];
      for (j = x_left; j <= x_right; j++)
	{
	  c_ptr->fval  = floor;
	  c_ptr->lr = TRUE;
	  c_ptr++;
	}
    }
  for (i = (y_height - 1); i <= (y_depth + 1); i++)
    {
      c_ptr = &cave[i][x_left-1];
      c_ptr->fval   = GRANITE_WALL;
      c_ptr->lr = TRUE;
      c_ptr = &cave[i][x_right+1];
      c_ptr->fval  = GRANITE_WALL;
      c_ptr->lr = TRUE;
    }
  c_ptr = &cave[y_height - 1][x_left];
  d_ptr = &cave[y_depth + 1][x_left];
  for (i = x_left; i <= x_right; i++)
    {
      c_ptr->fval  = GRANITE_WALL;
      c_ptr->lr = TRUE;
      c_ptr++;
      d_ptr->fval   = GRANITE_WALL;
      d_ptr->lr = TRUE;
      d_ptr++;
    }
  /* The inner room		*/
  y_height = y_height + 2;
  y_depth  = y_depth  - 2;
  x_left   = x_left   + 2;
  x_right  = x_right  - 2;
  for (i = (y_height - 1); i <= (y_depth + 1); i++)
    {
      cave[i][x_left-1].fval = TMP1_WALL;
      cave[i][x_right+1].fval = TMP1_WALL;
    }
  c_ptr = &cave[y_height-1][x_left];
  d_ptr = &cave[y_depth+1][x_left];
  for (i = x_left; i <= x_right; i++)
    {
      c_ptr->fval = TMP1_WALL;
      c_ptr++;
      d_ptr->fval = TMP1_WALL;
      d_ptr++;
    }
  /* Inner room variations		*/
  switch(randint(5))
    {
    case 1:	/* Just an inner room.	*/
      tmp = randint(4);
      if (tmp < 3) {	/* Place a door	*/
	if (tmp == 1) place_secret_door(y_height-1, xval);
	else	      place_secret_door(y_depth+1, xval);
      } else {
	if (tmp == 3) place_secret_door(yval, x_left-1);
	else	      place_secret_door(yval, x_right+1);
      }
      vault_monster(yval, xval, 1);
      break;

    case 2:	/* Treasure Vault	*/
      tmp = randint(4);
      if (tmp < 3) {	/* Place a door	*/
	if (tmp == 1) place_secret_door(y_height-1, xval);
	else	      place_secret_door(y_depth+1, xval);
      } else {
	if (tmp == 3) place_secret_door(yval, x_left-1);
	else	      place_secret_door(yval, x_right+1);
      }

      for (i = yval-1; i <= yval+1; i++)
	{
	  cave[i][xval-1].fval	 = TMP1_WALL;
	  cave[i][xval+1].fval	 = TMP1_WALL;
	}
      cave[yval-1][xval].fval  = TMP1_WALL;
      cave[yval+1][xval].fval  = TMP1_WALL;

      tmp = randint(4);	/* Place a door	*/
      if (tmp < 3)
	place_locked_door(yval-3+(tmp<<1), xval); /* 1 -> yval-1; 2 -> yval+1*/
      else
	place_locked_door(yval, xval-7+(tmp<<1));

      /* Place an object in the treasure vault	*/
      tmp = randint(10);
      if (tmp > 2)
	place_object(yval, xval, FALSE);
      else if (tmp == 2)
	place_down_stairs(yval, xval);
      else
	place_up_stairs(yval, xval);

      /* Guard the treasure well		*/
      vault_monster(yval, xval, 2+randint(3));
      /* If the monsters don't get 'em.	*/
      vault_trap(yval, xval, 4, 10, 2+randint(3));
      break;

    case 3:	/* Inner pillar(s).	*/
      tmp = randint(4);
      if (tmp < 3) {	/* Place a door	*/
	if (tmp == 1) place_secret_door(y_height-1, xval);
	else	      place_secret_door(y_depth+1, xval);
      } else {
	if (tmp == 3) place_secret_door(yval, x_left-1);
	else	      place_secret_door(yval, x_right+1);
      }

      for (i = yval-1; i <= yval+1; i++)
	{
	  c_ptr = &cave[i][xval-1];
	  for (j = xval-1; j <= xval+1; j++)
	    {
	      c_ptr->fval = TMP1_WALL;
	      c_ptr++;
	    }
	}
      if (randint(2) == 1)
	{
	  tmp = randint(2);
	  for (i = yval-1; i <= yval+1; i++)
	    {
	      c_ptr = &cave[i][xval-5-tmp];
	      for (j = xval-5-tmp; j <= xval-3-tmp; j++)
		{
		  c_ptr->fval = TMP1_WALL;
		  c_ptr++;
		}
	    }
	  for (i = yval-1; i <= yval+1; i++)
	    {
	      c_ptr = &cave[i][xval+3+tmp];
	      for (j = xval+3+tmp; j <= xval+5+tmp; j++)
		{
		  c_ptr->fval  = TMP1_WALL;
		  c_ptr++;
		}
	    }
	}

      if (randint(3) == 1)	/* Inner rooms	*/
	{
	  c_ptr = &cave[yval-1][xval-5];
	  d_ptr = &cave[yval+1][xval-5];
	  for (i = xval-5; i <= xval+5; i++)
	    {
	      c_ptr->fval  = TMP1_WALL;
	      c_ptr++;
	      d_ptr->fval  = TMP1_WALL;
	      d_ptr++;
	    }
	  cave[yval][xval-5].fval = TMP1_WALL;
	  cave[yval][xval+5].fval = TMP1_WALL;
	  place_secret_door(yval-3+(randint(2)<<1), xval-3);
	  place_secret_door(yval-3+(randint(2)<<1), xval+3);
	  if (randint(3) == 1)	place_object(yval, xval-2, FALSE);
	  if (randint(3) == 1)	place_object(yval, xval+2, FALSE);
	  vault_monster(yval, xval-2, randint(2));
	  vault_monster(yval, xval+2, randint(2));
	}
      break;

    case 4:	/* Maze inside.	*/
      tmp = randint(4);
      if (tmp < 3) {	/* Place a door	*/
	if (tmp == 1) place_secret_door(y_height-1, xval);
	else	      place_secret_door(y_depth+1, xval);
      } else {
	if (tmp == 3) place_secret_door(yval, x_left-1);
	else	      place_secret_door(yval, x_right+1);
      }

      for (i = y_height; i <= y_depth; i++)
	for (j = x_left; j <= x_right; j++)
	  if (0x1 & (j+i))
	    cave[i][j].fval = TMP1_WALL;

      /* Monsters just love mazes.		*/
      vault_monster(yval, xval-5, randint(3));
      vault_monster(yval, xval+5, randint(3));
      /* Traps make them entertaining.	*/
      vault_trap(yval, xval-3, 2, 8, randint(3));
      vault_trap(yval, xval+3, 2, 8, randint(3));
      /* Mazes should have some treasure too..	*/
      for (i = 0; i < 3; i++)
	random_object(yval, xval, 1);
      break;

    case 5:	/* Four small rooms.	*/
      for (i = y_height; i <= y_depth; i++)
	cave[i][xval].fval = TMP1_WALL;

      c_ptr = &cave[yval][x_left];
      for (i = x_left; i <= x_right; i++)
	{
	  c_ptr->fval = TMP1_WALL;
	  c_ptr++;
	}

      if (randint(2) == 1)
	{
	  i = randint(10);
	  place_secret_door(y_height-1, xval-i);
	  place_secret_door(y_height-1, xval+i);
	  place_secret_door(y_depth+1, xval-i);
	  place_secret_door(y_depth+1, xval+i);
	}
      else
	{
	  i = randint(3);
	  place_secret_door(yval+i, x_left-1);
	  place_secret_door(yval-i, x_left-1);
	  place_secret_door(yval+i, x_right+1);
	  place_secret_door(yval-i, x_right+1);
	}

      /* Treasure in each one.		*/
      random_object(yval, xval, 2+randint(2));
      /* Gotta have some monsters.		*/
      vault_monster(yval+2, xval-4, randint(2));
      vault_monster(yval+2, xval+4, randint(2));
      vault_monster(yval-2, xval-4, randint(2));
      vault_monster(yval-2, xval+4, randint(2));
      break;
    }
}


/* Builds a room at a row, column coordinate		-RAK-	*/
/* Type 3 unusual rooms are cross shaped				*/
static void build_type3(yval, xval)
int yval, xval;
{
  int y_height, y_depth;
  int x_left, x_right;
  register int tmp, i, j;
  int8u floor;
  register cave_type *c_ptr;

  if (dun_level <= randint(25))
    floor = LIGHT_FLOOR;	/* Floor with light	*/
  else
    floor = DARK_FLOOR;		/* Dark floor		*/
  tmp = 2 + randint(2);
  y_height = yval - tmp;
  y_depth  = yval + tmp;
  x_left   = xval - 1;
  x_right  = xval + 1;
  for (i = y_height; i <= y_depth; i++)
    for (j = x_left; j <= x_right; j++)
      {
	c_ptr = &cave[i][j];
	c_ptr->fval = floor;
	c_ptr->lr = TRUE;
      }
  for (i = (y_height - 1); i <= (y_depth + 1); i++)
    {
      c_ptr = &cave[i][x_left-1];
      c_ptr->fval  = GRANITE_WALL;
      c_ptr->lr = TRUE;
      c_ptr = &cave[i][x_right+1];
      c_ptr->fval  = GRANITE_WALL;
      c_ptr->lr = TRUE;
    }
  for (i = x_left; i <= x_right; i++)
    {
      c_ptr = &cave[y_height-1][i];
      c_ptr->fval  = GRANITE_WALL;
      c_ptr->lr = TRUE;
      c_ptr = &cave[y_depth+1][i];
      c_ptr->fval  = GRANITE_WALL;
      c_ptr->lr = TRUE;
    }
  tmp = 2 + randint(9);
  y_height = yval - 1;
  y_depth  = yval + 1;
  x_left   = xval - tmp;
  x_right  = xval + tmp;
  for (i = y_height; i <= y_depth; i++)
    for (j = x_left; j <= x_right; j++)
      {
	c_ptr = &cave[i][j];
	c_ptr->fval = floor;
	c_ptr->lr = TRUE;
      }
  for (i = (y_height - 1); i <= (y_depth + 1); i++)
    {
      c_ptr = &cave[i][x_left-1];
      if (c_ptr->fval != floor)
	{
	  c_ptr->fval  = GRANITE_WALL;
	  c_ptr->lr = TRUE;
	}
      c_ptr = &cave[i][x_right+1];
      if (c_ptr->fval != floor)
	{
	  c_ptr->fval  = GRANITE_WALL;
	  c_ptr->lr = TRUE;
	}
    }
  for (i = x_left; i <= x_right; i++)
    {
      c_ptr = &cave[y_height-1][i];
      if (c_ptr->fval != floor)
	{
	  c_ptr->fval  = GRANITE_WALL;
	  c_ptr->lr = TRUE;
	}
      c_ptr = &cave[y_depth+1][i];
      if (c_ptr->fval != floor)
	{
	  c_ptr->fval  = GRANITE_WALL;
	  c_ptr->lr = TRUE;
	}
    }
  /* Special features.			*/
  switch(randint(4))
    {
    case 1:	/* Large middle pillar		*/
      for (i = yval-1; i <= yval+1; i++)
	{
	  c_ptr = &cave[i][xval-1];
	  for (j = xval-1; j <= xval+1; j++)
	    {
	      c_ptr->fval = TMP1_WALL;
	      c_ptr++;
	    }
	}
      break;

    case 2:	/* Inner treasure vault		*/
      for (i = yval-1; i <= yval+1; i++)
	{
	  cave[i][xval-1].fval	 = TMP1_WALL;
	  cave[i][xval+1].fval	 = TMP1_WALL;
	}
      cave[yval-1][xval].fval  = TMP1_WALL;
      cave[yval+1][xval].fval  = TMP1_WALL;

      tmp = randint(4);	/* Place a door	*/
      if (tmp < 3)
	place_secret_door(yval-3+(tmp<<1), xval);
      else
	place_secret_door(yval, xval-7+(tmp<<1));

      /* Place a treasure in the vault		*/
      place_object(yval, xval, FALSE);
      /* Let's guard the treasure well.	*/
      vault_monster(yval, xval, 2+randint(2));
      /* Traps naturally			*/
      vault_trap(yval, xval, 4, 4, 1+randint(3));
      break;

    case 3:
      if (randint(3) == 1)
	{
	  cave[yval-1][xval-2].fval = TMP1_WALL;
	  cave[yval+1][xval-2].fval = TMP1_WALL;
	  cave[yval-1][xval+2].fval = TMP1_WALL;
	  cave[yval+1][xval+2].fval = TMP1_WALL;
	  cave[yval-2][xval-1].fval = TMP1_WALL;
	  cave[yval-2][xval+1].fval = TMP1_WALL;
	  cave[yval+2][xval-1].fval = TMP1_WALL;
	  cave[yval+2][xval+1].fval = TMP1_WALL;
	  if (randint(3) == 1)
	    {
	      place_secret_door(yval, xval-2);
	      place_secret_door(yval, xval+2);
	      place_secret_door(yval-2, xval);
	      place_secret_door(yval+2, xval);
	    }
	}
      else if (randint(3) == 1)
	{
	  cave[yval][xval].fval = TMP1_WALL;
	  cave[yval-1][xval].fval = TMP1_WALL;
	  cave[yval+1][xval].fval = TMP1_WALL;
	  cave[yval][xval-1].fval = TMP1_WALL;
	  cave[yval][xval+1].fval = TMP1_WALL;
	}
      else if (randint(3) == 1)
	cave[yval][xval].fval = TMP1_WALL;
      break;

    case 4:
      break;
    }
}


/* Constructs a tunnel between two points		*/
static void build_tunnel(row1, col1, row2, col2)
int row1, col1, row2, col2;
{
  register int tmp_row, tmp_col, i, j;
  register cave_type *c_ptr;
  cave_type *d_ptr;
  coords tunstk[1000], wallstk[1000];
  coords *tun_ptr;
  int row_dir, col_dir, tunindex, wallindex;
  int stop_flag, door_flag, main_loop_count;
  int start_row, start_col;

  /* Main procedure for Tunnel			*/
  /* Note: 9 is a temporary value		*/
  stop_flag = FALSE;
  door_flag = FALSE;
  tunindex    = 0;
  wallindex   = 0;
  main_loop_count = 0;
  start_row = row1;
  start_col = col1;
  correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);

  do
    {
      /* prevent infinite loops, just in case */
      main_loop_count++;
      if (main_loop_count > 2000)
	stop_flag = TRUE;

      if (randint(100) > DUN_TUN_CHG)
	{
	  if (randint(DUN_TUN_RND) == 1)
	    rand_dir(&row_dir, &col_dir);
	  else
	    correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
	}
      tmp_row = row1 + row_dir;
      tmp_col = col1 + col_dir;
      while (!in_bounds(tmp_row, tmp_col))
	{
	  if (randint(DUN_TUN_RND) == 1)
	    rand_dir(&row_dir, &col_dir);
	  else
	    correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
	  tmp_row = row1 + row_dir;
	  tmp_col = col1 + col_dir;
	}
      c_ptr = &cave[tmp_row][tmp_col];
      if (c_ptr->fval == NULL_WALL)
	{
	  row1 = tmp_row;
	  col1 = tmp_col;
	  if (tunindex < 1000)
	    {
	      tunstk[tunindex].y = row1;
	      tunstk[tunindex].x = col1;
	      tunindex++;
	    }
	  door_flag = FALSE;
	}
      else if (c_ptr->fval == TMP2_WALL)
	/* do nothing */
	;
      else if (c_ptr->fval == GRANITE_WALL)
	{
	  row1 = tmp_row;
	  col1 = tmp_col;
	  if (wallindex < 1000)
	    {
	      wallstk[wallindex].y = row1;
	      wallstk[wallindex].x = col1;
	      wallindex++;
	    }
	  for (i = row1-1; i <= row1+1; i++)
	    for (j = col1-1; j <= col1+1; j++)
	      if (in_bounds(i, j))
		{
		  d_ptr = &cave[i][j];
		  /* values 11 and 12 are impossible here, place_streamer
		     is never run before build_tunnel */
		  if (d_ptr->fval == GRANITE_WALL)
		    d_ptr->fval = TMP2_WALL;
		}
	}
      else if (c_ptr->fval == CORR_FLOOR || c_ptr->fval == BLOCKED_FLOOR)
	{
	  row1 = tmp_row;
	  col1 = tmp_col;
	  if (!door_flag)
	    {
	      if (doorindex < 100)
		{
		  doorstk[doorindex].y = row1;
		  doorstk[doorindex].x = col1;
		  doorindex++;
		}
	      door_flag = TRUE;
	    }
	  if (randint(100) > DUN_TUN_CON)
	    {
	      /* make sure that tunnel has gone a reasonable distance
		 before stopping it, this helps prevent isolated rooms */
	      tmp_row = row1 - start_row;
	      if (tmp_row < 0) tmp_row = -tmp_row;
	      tmp_col = col1 - start_col;
	      if (tmp_col < 0) tmp_col = -tmp_col;
	      if (tmp_row > 10 || tmp_col > 10)
		stop_flag = TRUE;
	    }
	}
      else  /* c_ptr->fval != NULL, TMP2, GRANITE, CORR */
	{
	  row1 = tmp_row;
	  col1 = tmp_col;
	}
    }
  while (((row1 != row2) || (col1 != col2)) && (!stop_flag));

  tun_ptr = &tunstk[0];
  for (i = 0; i < tunindex; i++)
    {
      d_ptr = &cave[tun_ptr->y][tun_ptr->x];
      d_ptr->fval  = CORR_FLOOR;
      tun_ptr++;
    }
  for (i = 0; i < wallindex; i++)
    {
      c_ptr = &cave[wallstk[i].y][wallstk[i].x];
      if (c_ptr->fval == TMP2_WALL)
	{
	  if (randint(100) < DUN_TUN_PEN)
	    place_door(wallstk[i].y, wallstk[i].x);
	  else
	    {
	      /* these have to be doorways to rooms */
	      c_ptr->fval  = CORR_FLOOR;
	    }
	}
    }
}


static int next_to(y, x)
register int y, x;
{
  register int next;

  if (next_to_corr(y, x) > 2)
    if ((cave[y-1][x].fval >= MIN_CAVE_WALL)
	&& (cave[y+1][x].fval >= MIN_CAVE_WALL))
      next = TRUE;
    else if ((cave[y][x-1].fval >= MIN_CAVE_WALL)
	     && (cave[y][x+1].fval >= MIN_CAVE_WALL))
      next = TRUE;
    else
      next = FALSE;
  else
    next = FALSE;
  return(next);
}

/* Places door at y, x position if at least 2 walls found	*/
static void try_door(y, x)
register int y, x;
{
  if ((cave[y][x].fval == CORR_FLOOR) && (randint(100) > DUN_TUN_JCT)
      && next_to(y, x))
    place_door(y, x);
}


/* Returns random co-ordinates				-RAK-	*/
static void new_spot(y, x)
int16 *y, *x;
{
  register int i, j;
  register cave_type *c_ptr;

  do
    {
      i = randint(cur_height - 2);
      j = randint(cur_width - 2);
      c_ptr = &cave[i][j];
    }
  while (c_ptr->fval >= MIN_CLOSED_SPACE || (c_ptr->cptr != 0)
	 || (c_ptr->tptr != 0));
  *y = i;
  *x = j;
}


/* Cave logic flow for generation of new dungeon		*/
static void cave_gen()
{
  struct spot_type
    {
      int endx;
      int endy;
    };
  int room_map[20][20];
  register int i, j, k;
  int y1, x1, y2, x2, pick1, pick2, tmp;
  int row_rooms, col_rooms, alloc_level;
  int16 yloc[400], xloc[400];

  row_rooms = 2*(cur_height/SCREEN_HEIGHT);
  col_rooms = 2*(cur_width /SCREEN_WIDTH);
  for (i = 0; i < row_rooms; i++)
    for (j = 0; j < col_rooms; j++)
      room_map[i][j] = FALSE;
  k = randnor(DUN_ROO_MEA, 2);
  for (i = 0; i < k; i++)
    room_map[randint(row_rooms)-1][randint(col_rooms)-1] = TRUE;
  k = 0;
  for (i = 0; i < row_rooms; i++)
    for (j = 0; j < col_rooms; j++)
      if (room_map[i][j] == TRUE)
	{
	  yloc[k] = i * (SCREEN_HEIGHT >> 1) + QUART_HEIGHT;
	  xloc[k] = j * (SCREEN_WIDTH >> 1) + QUART_WIDTH;
	  if (dun_level > randint(DUN_UNUSUAL))
	    {
	      tmp = randint(3);
	      if (tmp == 1)	 build_type1(yloc[k], xloc[k]);
	      else if (tmp == 2) build_type2(yloc[k], xloc[k]);
	      else		 build_type3(yloc[k], xloc[k]);
	    }
	  else
	    build_room(yloc[k], xloc[k]);
	  k++;
#ifdef MAC
	  SystemTask ();
#endif
	}
  for (i = 0; i < k; i++)
    {
      pick1 = randint(k) - 1;
      pick2 = randint(k) - 1;
      y1 = yloc[pick1];
      x1 = xloc[pick1];
      yloc[pick1] = yloc[pick2];
      xloc[pick1] = xloc[pick2];
      yloc[pick2] = y1;
      xloc[pick2] = x1;
      }
  doorindex = 0;
  /* move zero entry to k, so that can call build_tunnel all k times */
  yloc[k] = yloc[0];
  xloc[k] = xloc[0];
  for (i = 0; i < k; i++)
    {
      y1 = yloc[i];
      x1 = xloc[i];
      y2 = yloc[i+1];
      x2 = xloc[i+1];
      build_tunnel(y2, x2, y1, x1);
    }
#ifdef MAC
  SystemTask ();
#endif
  fill_cave(GRANITE_WALL);
  for (i = 0; i < DUN_STR_MAG; i++)
    place_streamer(MAGMA_WALL, DUN_STR_MC);
  for (i = 0; i < DUN_STR_QUA; i++)
    place_streamer(QUARTZ_WALL, DUN_STR_QC);
  place_boundary();
  /* Place intersection doors	*/
  for (i = 0; i < doorindex; i++)
    {
      try_door(doorstk[i].y, doorstk[i].x-1);
      try_door(doorstk[i].y, doorstk[i].x+1);
      try_door(doorstk[i].y-1, doorstk[i].x);
      try_door(doorstk[i].y+1, doorstk[i].x);
    }
#ifdef MAC
  SystemTask ();
#endif
  alloc_level = (dun_level/3);
  if (alloc_level < 2)
    alloc_level = 2;
  else if (alloc_level > 10)
    alloc_level = 10;
  place_stairs(2, randint(2)+2, 3);
  place_stairs(1, randint(2), 3);
  /* Set up the character co-ords, used by alloc_monster, place_win_monster */
  new_spot(&char_row, &char_col);
  alloc_monster((randint(8)+MIN_MALLOC_LEVEL+alloc_level), 0, TRUE);
  alloc_object(set_corr, 3, randint(alloc_level));
  alloc_object(set_room, 5, randnor(TREAS_ROOM_ALLOC, 3));
  alloc_object(set_floor, 5, randnor(TREAS_ANY_ALLOC, 3));
  alloc_object(set_floor, 4, randnor(TREAS_GOLD_ALLOC, 3));
  alloc_object(set_floor, 1, randint(alloc_level));
  if (dun_level >= WIN_MON_APPEAR)  place_win_monster();
}


/* Builds a store at a row, column coordinate			*/
static void build_store(store_num, y, x)
int store_num, y, x;
{
  int yval, y_height, y_depth;
  int xval, x_left, x_right;
  register int i, j;
  int cur_pos, tmp;
  register cave_type *c_ptr;

  yval	   = y*10 + 5;
  xval	   = x*16 + 16;
  y_height = yval - randint(3);
  y_depth  = yval + randint(4);
  x_left   = xval - randint(6);
  x_right  = xval + randint(6);
  for (i = y_height; i <= y_depth; i++)
    for (j = x_left; j <= x_right; j++)
      cave[i][j].fval	 = BOUNDARY_WALL;
  tmp = randint(4);
  if (tmp < 3)
    {
      i = randint(y_depth-y_height) + y_height - 1;
      if (tmp == 1) j = x_left;
      else	    j = x_right;
    }
  else
    {
      j = randint(x_right-x_left) + x_left - 1;
      if (tmp == 3) i = y_depth;
      else	    i = y_height;
    }
  c_ptr = &cave[i][j];
  c_ptr->fval  = CORR_FLOOR;
  cur_pos = popt();
  c_ptr->tptr = cur_pos;
  invcopy(&t_list[cur_pos], OBJ_STORE_DOOR + store_num);
}


/* Link all free space in treasure list together		*/
static void tlink()
{
  register int i;

  for (i = 0; i < MAX_TALLOC; i++)
    invcopy(&t_list[i], OBJ_NOTHING);
  tcptr = MIN_TRIX;
}


/* Link all free space in monster list together			*/
static void mlink()
{
  register int i;

  for (i = 0; i < MAX_MALLOC; i++)
      m_list[i] = blank_monster;
  mfptr = MIN_MONIX;
}


/* Town logic flow for generation of new town		*/
static void town_gen()
{
  register int i, j, l, m;
  register cave_type *c_ptr;
  int rooms[6], k;

  set_seed(town_seed);
  for (i = 0; i < 6; i++)
    rooms[i] = i;
  l = 6;
  for (i = 0; i < 2; i++)
    for (j = 0; j < 3; j++)
      {
	k = randint(l) - 1;
	build_store(rooms[k], i, j);
	for (m = k; m < l-1; m++)
	  rooms[m] = rooms[m+1];
	l--;
      }
  fill_cave(DARK_FLOOR);
  /* make stairs before reset_seed, so that they don't move around */
  place_boundary();
  place_stairs(2, 1, 0);
  reset_seed();
  /* Set up the character co-ords, used by alloc_monster below */
  new_spot(&char_row, &char_col);
  if (0x1 & (turn / 5000))
    {		/* Night	*/
      for (i = 0; i < cur_height; i++)
	{
	  c_ptr = &cave[i][0];
	  for (j = 0; j < cur_width; j++)
	    {
	      if (c_ptr->fval != DARK_FLOOR)
		c_ptr->pl = TRUE;
	      c_ptr++;
	    }
#ifdef MAC
	  SystemTask ();
#endif
	}
      alloc_monster(MIN_MALLOC_TN, 3, TRUE);
    }
  else
    {		/* Day	*/
      for (i = 0; i < cur_height; i++)
	{
	  c_ptr = &cave[i][0];
	  for (j = 0; j < cur_width; j++)
	    {
	      c_ptr->pl = TRUE;
	      c_ptr++;
	    }
#ifdef MAC
	  SystemTask ();
#endif
	}
      alloc_monster(MIN_MALLOC_TD, 3, TRUE);
    }
  store_maint();
}


/* Generates a random dungeon level			-RAK-	*/
void generate_cave()
{
  panel_row_min	= 0;
  panel_row_max	= 0;
  panel_col_min	= 0;
  panel_col_max	= 0;
  char_row = -1;
  char_col = -1;

#ifdef MAC
  macbeginwait ();
#endif

  tlink();
  mlink();
  blank_cave();

  if (dun_level == 0)
    {
      cur_height = SCREEN_HEIGHT;
      cur_width	 = SCREEN_WIDTH;
      max_panel_rows = (cur_height/SCREEN_HEIGHT)*2 - 2;
      max_panel_cols = (cur_width /SCREEN_WIDTH )*2 - 2;
      panel_row = max_panel_rows;
      panel_col = max_panel_cols;
      town_gen();
    }
  else
    {
      cur_height = MAX_HEIGHT;
      cur_width	 = MAX_WIDTH;
      max_panel_rows = (cur_height/SCREEN_HEIGHT)*2 - 2;
      max_panel_cols = (cur_width /SCREEN_WIDTH )*2 - 2;
      panel_row = max_panel_rows;
      panel_col = max_panel_cols;
      cave_gen();
    }
#ifdef MAC
  macendwait ();
#endif
}
