--- src/generate.c	Wed Jul 26 03:57:48 2000
+++ fraccave/generate.c	Sat Jan 13 10:03:21 2001
@@ -106,6 +106,7 @@
 #define DUN_ROOMS	50	/* Number of rooms to attempt */
 #define DUN_UNUSUAL	200	/* Level/chance of unusual room */
 #define DUN_DEST	30	/* 1/chance of having a destroyed level */
+#define DUN_ARENA	30	/* 1/chance of having an arena level */
 
 /*
  * Dungeon tunnel generation values
@@ -170,7 +171,7 @@
 /*
  * Maximal number of room types
  */
-#define ROOM_MAX	9
+#define ROOM_MAX	10
 
 
 
@@ -258,7 +259,8 @@
 	{ 0, 0, -1, 1, 5 },		/* 5 = Monster nest (33x11) */
 	{ 0, 0, -1, 1, 5 },		/* 6 = Monster pit (33x11) */
 	{ 0, 1, -1, 1, 5 },		/* 7 = Lesser vault (33x22) */
-	{ -1, 2, -2, 3, 10 }	/* 8 = Greater vault (66x44) */
+	{ -1, 2, -2, 3, 10 },		/* 8 = Greater vault (66x44) */
+	{ -1, 1, -1, 1, 1}		/* 9 = Fractal cave (33x33) */
 };
 
 
@@ -955,6 +957,7 @@
  *   6 -- monster pits
  *   7 -- simple vaults
  *   8 -- greater vaults
+ *   9 -- fractal cave system
  */
 
 
@@ -2366,6 +2369,492 @@
 }
 
 
+/* XXX XXX XXX Fractal cave patch: */
+
+/*
+ * cave_feat's that aren't processed yet
+ */
+#define FRAC_NOT_DONE	255
+
+/*
+ * Store routine for the fractal cave generator
+ * this routine probably should be an inline function or a macro
+ */
+
+static void sorit(int x, int y, int x0, int y0, byte val, int hsize, int cutoff)
+{
+	/* only write to points that are "blank" */
+	if (cave_feat[y0 - hsize + y][x0 - hsize + x] != FRAC_NOT_DONE)
+		return;
+
+	/* if on boundary set val>cutoff so walls are not as square */
+	if (((x == 0) || (y == 0) || (x == hsize * 2) || (y == hsize * 2))
+	    && (val <= cutoff)) val = cutoff + 1;
+
+	/* store the value - Hack */
+	cave_feat[y0 - hsize + y][x0 - hsize + x] = val;
+
+	return;
+}
+
+/*
+ * Read from the array - this should probably be inline
+ */
+static byte redit(int x, int y, int x0, int y0, int hsize)
+{
+	/* read in the hacked format */
+	return cave_feat[y0 - hsize + y][x0 - hsize + x];
+}
+
+/*
+ * Note that this uses the cave_feat array in a very hackish way.
+ * The values are first set to zero, and then each array location
+ * is used as a "heightmap". The heightmap then needs to be converted
+ * back into the "feat" format.
+ *
+ * grd=level at which fractal turns on.  smaller gives more mazelike caves
+ * roug=roughness level.  16=normal.  higher values make things more convoluted
+ * small values are good for smooth walls.
+ * size=length of the side of the square cave system.
+ */
+
+
+/*
+ * An attempt to avoid using floating point math (^ ^)v
+ * Since everything is rounded to integer in the end, we don't need
+ * very high precision.
+ */
+#define FACTOR 128
+
+/*
+ * Scaling factor to remove some of the "gridding" effect of the square
+ * lattice. The value is obtained by rounding sqrt(2) * FACTOR.
+ */
+#define ROOTTWO 181
+
+
+static void generate_fracave(int y0, int x0, int siz, int grd, int roug, int cutoff)
+{
+	int astep, bstep, i, j, size;
+	/* half of size - used as an offset from the center */
+	int hsize;
+
+	/* redefine size so can change the value if out of range */
+	size = siz;
+
+	/* Paranoia about size of the system of caves*/
+	if (size > 254) size = 254;
+	if (size < 4) size = 4;
+	hsize = size / 2;
+
+
+	/* Clear the section */
+	for (i = 0; i <= size; i++)
+	{
+		for(j = 0; j <= size; j++)
+		{
+			/* Mark the section with the "not done yet" magic number */
+			cave_feat[y0-hsize+j][x0-hsize+i] = FRAC_NOT_DONE;
+
+			/* Clear icky if we are redoing the cave */
+			cave_info[y0-hsize+j][x0-hsize+i] &= ~(CAVE_ICKY);
+		}
+	}
+
+
+	/* Set the corner values just in case grd > size. */
+	sorit(0, 0, x0, y0, size, hsize, cutoff);
+	sorit(0, size, x0, y0, size, hsize, cutoff);
+	sorit(size, 0, x0, y0, size, hsize, cutoff);
+	sorit(size, size, x0, y0, size, hsize, cutoff);
+
+	/* Set the middle square to be an open area. */
+	sorit(hsize, hsize, x0, y0, 0, hsize, cutoff);
+
+
+	/* 
+	 * fill in the square with fractal height data - like the 'plasma
+	 * fractal' in fractint.
+	 */
+	size = size * FACTOR;
+	bstep = astep = size;
+	grd = grd * FACTOR;
+
+	while (bstep > FACTOR)
+	{
+		astep = bstep;
+		bstep = bstep / 2;
+
+		/* middle top to bottom */
+		for (i = bstep; i <= size - bstep; i += astep)
+		{
+			for(j = 0; j <= size; j += astep)
+			{
+				/*
+				 * if step size greater than grid level
+				 * randomize instead of averaging+-random
+				 * amount
+				 */
+				if (bstep > grd)
+					sorit(i / FACTOR, j / FACTOR, x0, y0,
+					      randint(size / FACTOR),
+					      hsize, cutoff);
+				else
+	sorit(i / FACTOR, j / FACTOR, x0, y0,
+	      (redit((i - bstep) / FACTOR, j / FACTOR, x0, y0, hsize)
+	       +  redit((i + bstep) / FACTOR, j / FACTOR, x0, y0, hsize)) / 2
+	      + (randint(astep) - bstep) * roug / 16 / FACTOR,
+	      hsize, cutoff);
+			}
+		}
+
+		/* middle left to right */
+		for (j = bstep; j <= size - bstep; j += astep)
+		{
+			for (i = 0; i <= size; i += astep)
+			{
+				if (bstep > grd)
+					sorit(i / FACTOR, j / FACTOR, x0, y0,
+					      randint(size / FACTOR),
+					      hsize, cutoff);
+				else
+	sorit(i / FACTOR, j / FACTOR, x0, y0,
+	      (redit(i / FACTOR, (j - bstep) / FACTOR, x0, y0, hsize)
+	       + redit(i / FACTOR, (j + bstep) / FACTOR, x0, y0, hsize)) / 2
+	      + (randint(astep) - bstep) * roug / 16 / FACTOR,
+	      hsize, cutoff);
+			}
+		}
+
+		/* center */
+		for (i = bstep; i <= size - bstep; i += astep)
+		{
+			for (j = bstep; j <= size - bstep; j += astep)
+			{
+				/*
+				 * average over all four corners + scal by
+				 * root(2) to reduce the effect of the square
+				 * grid on the shape of the fractal
+				 */
+				if (bstep > grd)
+					sorit(i / FACTOR, j / FACTOR, x0, y0,
+					      randint(size / FACTOR),
+					      hsize, cutoff);
+				else
+	sorit(i / FACTOR, j / FACTOR, x0, y0,
+	  (redit((i - bstep) / FACTOR, (j - bstep) / FACTOR, x0, y0, hsize)
+	   + redit((i - bstep) / FACTOR, (j + bstep) / FACTOR, x0, y0, hsize)
+	   + redit((i + bstep) / FACTOR, (j - bstep) / FACTOR, x0, y0, hsize)
+	   + redit((i + bstep) / FACTOR, (j + bstep) / FACTOR, x0, y0, hsize)) / 4
+	   + (randint(astep) - bstep) * ROOTTWO / 16 * roug / FACTOR,
+	   hsize, cutoff);
+			}
+		}
+	}
+}
+
+
+static bool hack_isnt_wall(int y, int x, int cutoff)
+{
+	/* already done */
+	if (cave_info[y][x] & CAVE_ICKY)
+		return FALSE;
+
+	/* flag that have looked at this square */
+	cave_info[y][x] |= CAVE_ICKY;
+
+	/* less than cutoff so is a floor */
+	if (cave_feat[y][x] <= cutoff)
+	{
+		cave_feat[y][x] = FEAT_FLOOR; 
+		return TRUE;
+	}
+
+	/*greater than cutoff so is a wall */
+	else
+	{
+		cave_feat[y][x] = FEAT_WALL_OUTER;
+		return FALSE;
+	}
+}
+
+/* quick and nasty fill routine */
+static void fill_hack(int y0, int x0, int y, int x, int size, int cutoff, int *amount)
+{
+	int i, j;
+
+	/*
+	 * check 8 neigbours +self (self is caught in the isnt_wall function)
+	 */
+	for (i = -1; i <= 1; i++)
+	{
+		for (j = -1; j <= 1; j++) 
+		{
+			/* Within bounds */
+			if ((x + i > 0) && (x + i < size)
+			    && (y + j > 0) && (y + j < size))
+			{
+				/* not a wall or floor done before */			
+				if (hack_isnt_wall(y + j + y0 - size / 2,
+						   x + i + x0 - size / 2,
+						   cutoff))
+		 		{
+					/* fill from the new point then */
+					fill_hack(y0, x0, y + j, x + i,
+						  size, cutoff, amount);
+					/* keep tally of size of cave system */
+					(*amount)++;
+				}
+			}
+			/* affect boundary */
+			else
+				cave_info[y0 + y + j - size / 2]
+					 [x0 + x + i - size / 2] |= CAVE_ICKY;
+		}
+	}
+}
+
+
+static bool generate_multicave2(int y0, int x0, int size, int cutoff, bool light, bool room)
+{
+	int x, y, i, amount;
+	int cx1, cy1, cx2, cy2;
+	int hsize = size / 2;
+
+	/* tally = 0 */
+	amount = 0;
+
+	/*
+	 * select region connected to center of cave system
+	 * this gets rid of alot of isolated one-sqaures that can make
+	 * teleport traps insadeaths...
+	 */
+	fill_hack(y0, x0, hsize, hsize, size, cutoff, &amount);
+
+	/* too small */
+	if (amount < 10) return FALSE;
+
+	/*
+	 * Do boundarys- check to see if they are next to a filled region
+	 * If not then they are set to normal granite
+	 * If so then they are marked as room walls.
+	 */
+	for (i = 0; i <= size; i++)
+	{
+		cx1 = x0 + i - hsize;
+		cy1 = y0 + 0 - hsize;
+
+		if (cave_info[cy1][cx1] & CAVE_ICKY)
+		{
+			cave_feat[cy1][cx1] = FEAT_WALL_OUTER;
+			cave_info[cy1][cx1] |= CAVE_WALL;
+			if (light) cave_info[cy1][cx1] |= CAVE_GLOW;
+			if (room) cave_info[cy1][cx1] |= CAVE_ROOM;
+		}
+		else
+		{
+			cave_feat[cy1][cx1] = FEAT_WALL_EXTRA;
+			cave_info[cy1][cx1] |= CAVE_WALL;
+		}
+
+		cx2 = x0 + i - hsize;
+		cy2 = y0 + size - hsize;
+
+		if (cave_info[cy2][cx2] & CAVE_ICKY)
+		{
+			cave_feat[cy2][cx2] = FEAT_WALL_OUTER;
+			cave_info[cy2][cx2] |= CAVE_WALL;
+			if (light) cave_info[cy2][cx2] |= CAVE_GLOW;
+			if (room) cave_info[cy2][cx2] |= CAVE_ROOM;
+		}
+		else
+		{
+			cave_feat[cy2][cx2] = FEAT_WALL_EXTRA;
+			cave_info[cy2][cx2] |= CAVE_WALL;
+		}
+
+		cave_info[cy1][cx1] &= ~(CAVE_ICKY);
+		cave_info[cy2][cx2] &= ~(CAVE_ICKY);
+	}
+
+	for (i = 1; i < size; ++i)
+	{
+		cx1 = x0 + 0 - hsize;
+		cy1 = y0 + i - hsize;
+
+		if (cave_info[cy1][cx1] & CAVE_ICKY)
+		{
+			cave_feat[cy1][cx1] = FEAT_WALL_OUTER;
+			cave_info[cy1][cx1] |= CAVE_WALL;
+			if (light) cave_info[cy1][cx1] |= CAVE_GLOW;
+			if (room) cave_info[cy1][cx1] |= CAVE_ROOM;
+		}
+		else
+		{
+			cave_feat[cy1][cx1] = FEAT_WALL_EXTRA;
+			cave_info[cy1][cx1] |= (CAVE_WALL);
+		}
+
+		cx2 = x0 + size - hsize;
+		cy2 = y0 + i - hsize;
+
+		if (cave_info[cy2][cx2] & CAVE_ICKY)
+		{
+			cave_feat[cy2][cx2] = FEAT_WALL_OUTER;
+			cave_info[cy2][cx2] |= CAVE_WALL;
+			if (light) cave_info[cy2][cx2] |= CAVE_GLOW;
+			if (room) cave_info[cy2][cx2] |= CAVE_ROOM;
+		}
+		else
+		{
+			cave_feat[cy2][cx2] = FEAT_WALL_EXTRA;
+			cave_info[cy2][cx2] |= CAVE_WALL;
+		}
+
+		cave_info[cy1][cx1] &= ~(CAVE_ICKY);
+		cave_info[cy2][cx2] &= ~(CAVE_ICKY);
+	}
+
+	/*
+	 * Do the rest: convert back to the normal format
+	 * In other varients, may want to check to see if cave_feat < some value
+	 * if so, set to be water:- this will make interesting pools etc.
+	 * (I don't do this for standard angband.)
+	 */
+	for (x = 1; x < size; x++)
+	{
+		for (y = 1; y < size; y++)
+		{
+			cx1 = x0 + x - hsize;
+			cy1 = y0 + y - hsize;
+
+			/* Clear the icky flag in the filled region */
+			if ((cave_feat[cy1][cx1] == FEAT_FLOOR)
+		 	    && (cave_info[cy1][cx1] & CAVE_ICKY))
+			{
+				cave_info[cy1][cx1] &= ~(CAVE_ICKY|CAVE_WALL);
+				/*Set appropriate flags */
+				if (light) cave_info[cy1][cx1] |= CAVE_GLOW;
+				if (room) cave_info[cy1][cx1] |= CAVE_ROOM;
+			}
+
+			/* Walls */
+			else if ((cave_feat[cy1][cx1] == FEAT_WALL_OUTER)
+				 && (cave_info[cy1][cx1] & CAVE_ICKY))
+			{
+				cave_info[cy1][cx1] &= ~(CAVE_ICKY);
+				cave_info[cy1][cx1] |= CAVE_WALL;
+				if (light) cave_info[cy1][cx1] |= CAVE_GLOW;
+				if (room) cave_info[cy1][cx1] |= CAVE_ROOM;
+			}
+
+			/*Clear the unconnected regions*/
+			else
+			{
+				cave_feat[cy1][cx1] = FEAT_WALL_EXTRA;
+				cave_info[cy1][cx1] &= ~(CAVE_ICKY|CAVE_ROOM);
+				cave_info[cy1][cx1] |= CAVE_WALL;
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+
+/*
+ * Type 9 -- Fractal cave system
+ *
+ * Driver routine to create fractal cave system
+ */
+static void build_type9(int y0, int x0)
+{
+	int grd, roug, cutoff, size;
+	bool done, light, room;
+
+	light = done = FALSE;
+	room = TRUE;
+	if (p_ptr->depth <= randint(25)) light = TRUE;
+
+	while (!done)
+	{
+		/*
+		 * Note: size must be even or there are rounding problems
+		 * This causes the tunnels not to connect properly to the room
+		 */
+
+		/* Must be less than 33 because of values in room_data */
+		size = randint(15) * 2 + 2;
+
+		/* testing values for these parameters feel free to adjust */
+		grd = 2 ^ (randint(4) - 1);
+
+		/* want average of about 16 */
+		roug = randint(8) * randint(4);
+
+		/* about size/2 */
+		cutoff = randint(size / 4) + randint(size / 4)
+			 + randint(size / 4) + randint(size / 4); 
+
+		/* make it */
+		generate_fracave(y0, x0, size, grd, roug, cutoff);
+
+		/* Convert to normal format+ clean up*/
+		done = generate_multicave2(y0, x0, size, cutoff, light, room);
+	}
+}
+
+
+/*
+ * makes a cave system in the center of the dungeon
+ */
+static void build_arena(void)
+{
+	int grd, roug, cutoff, size, x0, y0;
+	bool done, light, room;
+
+	light = done = room = FALSE;
+	if (p_ptr->depth <= randint(25)) light = TRUE;
+
+	size = DUNGEON_HGT - 1;
+	y0 = size / 2;
+	/* Paranoia: make size even */
+	size = y0 * 2;
+	x0 = randint(DUNGEON_WID - DUNGEON_HGT) + y0;
+
+/*
+ * note: it IS possible to make a rectangular cave system, and thus fill the
+ * whole dungeon, but it isn't really worth the effort. (It would require
+ * doubling the number of variables in generate_fracave, with separate step
+ * sizes for x and y. )
+ * If any one really wants this, just mention it on the news group. Steven
+ */
+
+	while (!done)
+	{
+		/*
+		 * Note: size must be even or there are rounding problems
+		 * This causes the tunnels not to connect properly to the room:
+		 * not good
+		 */	
+
+		/* testing values for these parameters feel free to adjust*/
+		grd = 2 ^ (randint(4) - 1);
+
+		/*want average of about 16 */
+		roug = randint(8) * randint(4);
+
+		/* about size/2 */
+		cutoff = size / 2;
+
+		/* make it */
+		generate_fracave(y0, x0, size, grd, roug, cutoff);
+
+		/* Convert to normal format + clean up*/
+		done = generate_multicave2(y0, x0, size, cutoff, light, room);
+	}
+}
+
 
 /*
  * Constructs a tunnel between two points
@@ -2762,6 +3251,7 @@
 	switch (typ)
 	{
 		/* Build an appropriate room */
+		case 9: build_type9(y, x); break;
 		case 8: build_type8(y, x); break;
 		case 7: build_type7(y, x); break;
 		case 6: build_type6(y, x); break;
@@ -2812,6 +3302,7 @@
 	int by, bx;
 
 	bool destroyed = FALSE;
+	bool arena = FALSE;
 
 	dun_data dun_body;
 
@@ -2830,6 +3321,13 @@
 		}
 	}
 
+	/* Possible "arena" level */
+	if ((p_ptr->depth > 10) && (rand_int(DUN_ARENA) == 0))
+	{
+	 	/* make a large fractal cave in the middle of the dungeon */
+		arena = TRUE;
+		build_arena();
+	}
 
 	/* Possible "destroyed" level */
 	if ((p_ptr->depth > 10) && (rand_int(DUN_DEST) == 0)) destroyed = TRUE;
@@ -2879,8 +3377,20 @@
 		/* Destroyed levels are boring */
 		if (destroyed)
 		{
-			/* Attempt a "trivial" room */
-			if (room_build(by, bx, 1)) continue;
+			/* The deeper you are, the more cavelike the rooms are */
+			k = randint(100);
+
+			/* No caves when arena exists: they look bad */
+			if ((k < p_ptr->depth) && !arena)
+			{
+				/* Attemt a cave */
+				if (room_build(by, bx, 9)) continue;
+			}
+			else
+			{
+				/* Attempt a "trivial" room */
+				if (room_build(by, bx, 1)) continue;
+			}
 
 			/* Never mind */
 			continue;
@@ -2918,8 +3428,21 @@
 			if ((k < 100) && room_build(by, bx, 2)) continue;
 		}
 
-		/* Attempt a trivial room */
-		if (room_build(by, bx, 1)) continue;
+		/* The deeper you are, the more cavelike the rooms are */
+		k = randint(100);
+
+		/* No caves when arena exists: they look bad */
+		if ((k < p_ptr->depth) && !arena)
+		{
+			/* Attempt a cave */
+			if (room_build(by, bx, 9)) continue;
+		}
+		else
+		{
+			/* Attempt a "trivial" room */
+			if (room_build(by, bx, 1)) continue;
+		}
+		continue;
 	}
 
 
