Commits

rmtew committed 2d54c73 Merge

Merged in changes from v1.7.1.

  • Participants
  • Parent commits 8ab089e, 0f80494
  • Branches unofficial
  • Tags v1.7.1-unofficial

Comments (0)

Files changed (27)

 8c126ace337a606fc07cac444089f4737cf9651b v1.6.2
 1a674d05007d26c56037f400c4fdb2914b06c2e8 v1.6.4
 95b967b07d76368cabd7ce1e5ada49315152fb49 v1.7.0-unofficial
+9bec984cae90d2208a016ef581e70a3278ae7829 v1.7.0
+10b93b04575ac8218919b0de04642d1d0b18032c v1.7.1

BrogueCode/Architect.c

  *  
  *  This file is part of Brogue.
  *
- *  Brogue 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.
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Affero General Public License as
+ *  published by the Free Software Foundation, either version 3 of the
+ *  License, or (at your option) any later version.
  *
- *  Brogue is distributed in the hope that it will be useful,
+ *  This program 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.
+ *  GNU Affero General Public License for more details.
  *
- *  You should have received a copy of the GNU General Public License
- *  along with Brogue.  If not, see <http://www.gnu.org/licenses/>.
+ *  You should have received a copy of the GNU Affero General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "Rogue.h"
 #include "IncludeGlobals.h"
 
 short topBlobMinX, topBlobMinY, blobWidth, blobHeight;
-levelSpecProfile levelSpec;
-
-unsigned long wallNum;
 
 #ifdef BROGUE_ASSERTS // otherwise handled as a macro in rogue.h
 boolean cellHasTerrainFlag(short x, short y, unsigned long flagMask) {
 	boolean inString;
 	short newX, newY, dir, sdir;
 	short numStrings, maxStringLength, currentStringLength;
-	const short cDirs[8][2] = {{0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}};
 	
 	if (!(pmap[x][y].flags & IN_LOOP)) {
 		return false;
 //		Zero means there are no impassable tiles adjacent.
 //		One means it is adjacent to a wall.
 //		Two means it is in a hallway or something similar.
-//		Three means it is the center of a T-intersection, or something analogous.
+//		Three means it is the center of a T-intersection or something similar.
 //		Four means it is in the intersection of two hallways.
 //		Five or more means there is a bug.
 short passableArcCount(short x, short y) {
 	short arcCount, dir, oldX, oldY, newX, newY;
-	const short cDirs[8][2] = {{0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}};
 	
 #ifdef BROGUE_ASSERTS
 	assert(coordinatesAreInMap(x, y));
 	short i, j, i2, j2, dir, newX, newY, oldX, oldY, passableArcCount, cellCount;
 	char grid[DCOLS][DROWS], passMap[DCOLS][DROWS];
 	boolean designationSurvives;
-	const short cDirs[8][2] = {{0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}};
 	
 	// first find all of the loops
 	rogue.staleLoopMap = false;
 	for(i=0; i<DCOLS; i++) {
 		for(j=0; j<DROWS; j++) {
 			if (cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)
-				&& !cellHasTerrainFlag(i, j, T_IS_SECRET)) {
+				&& !cellHasTMFlag(i, j, TM_IS_SECRET)) {
                 
 				pmap[i][j].flags &= ~IN_LOOP;
 				passMap[i][j] = false;
 
 // Assumes (startX, startY) is in the machine.
 // Returns true if everything went well, and false if we ran into a machine component
-// that was already there -- we don't want to build a machine around it.
+// that was already there, as we don't want to build a machine around it.
 boolean addTileToMachineInteriorAndIterate(char interior[DCOLS][DROWS], short startX, short startY) {
 	short dir, newX, newY;
 	boolean goodSoFar = true;
 	for (dir = 0; dir < 4 && goodSoFar; dir++) {
 		newX = startX + nbDirs[dir][0];
 		newY = startY + nbDirs[dir][1];
-		if ((pmap[newX][newY].flags & HAS_ITEM)
-			|| ((pmap[newX][newY].flags & IS_IN_MACHINE) && !(pmap[newX][newY].flags & IS_GATE_SITE))) {
-			// Abort if there's an item in the room.
-			// Items haven't been populated yet, so the only way this could happen is if another machine
-			// previously placed an item here.
-			// Also abort if we're touching another machine at any point other than a gate tile.
-			return false;
-		}
-		if (coordinatesAreInMap(newX, newY)
-			&& !interior[newX][newY]
-			&& chokeMap[newX][newY] <= chokeMap[startX][startY] // don't have to worry about walls since they're all 30000
-			&& !(pmap[newX][newY].flags & IS_IN_MACHINE)) {
-			//goodSoFar = goodSoFar && addTileToMachineInteriorAndIterate(interior, newX, newY);
-            if (goodSoFar) {
-                goodSoFar = addTileToMachineInteriorAndIterate(interior, newX, newY);
+        if (coordinatesAreInMap(newX, newY)) {
+            if ((pmap[newX][newY].flags & HAS_ITEM)
+                || ((pmap[newX][newY].flags & IS_IN_MACHINE) && !(pmap[newX][newY].flags & IS_GATE_SITE))) {
+                // Abort if there's an item in the room.
+                // Items haven't been populated yet, so the only way this could happen is if another machine
+                // previously placed an item here.
+                // Also abort if we're touching another machine at any point other than a gate tile.
+                return false;
             }
-		}
+            if (!interior[newX][newY]
+                && chokeMap[newX][newY] <= chokeMap[startX][startY] // don't have to worry about walls since they're all 30000
+                && !(pmap[newX][newY].flags & IS_IN_MACHINE)) {
+                //goodSoFar = goodSoFar && addTileToMachineInteriorAndIterate(interior, newX, newY);
+                if (goodSoFar) {
+                    goodSoFar = addTileToMachineInteriorAndIterate(interior, newX, newY);
+                }
+            }
+        }
 	}
 	return goodSoFar;
 }
 
 void expandMachineInterior(char interior[DCOLS][DROWS], short minimumInteriorNeighbors) {
     boolean madeChange;
-    short nbcount, newX, newY, i, j, dir, layer;
+    short nbcount, newX, newY, i, j, layer;
+    enum directions dir;
     
     do {
         madeChange = false;
         for(i=1; i<DCOLS-1; i++) {
             for(j=1; j < DROWS-1; j++) {
-                if (cellHasTerrainFlag(i, j, T_PATHING_BLOCKER) ) {
-                    //&& (!pmap[i][j].flags & IS_IN_MACHINE)) {
+                if (cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)) {
                     
                     // Count up the number of interior open neighbors out of eight:
                     for (nbcount = dir = 0; dir < 8; dir++) {
                             newX = i + nbDirs[dir][0];
                             newY = j + nbDirs[dir][1];
                             if (!interior[newX][newY]
-                                && !cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)) {
+                                && (!cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY) || pmap[newX][newY].machineNumber != 0)) {
                                 nbcount++;
                                 break;
                             }
                         }
                         if (!nbcount) {
-                            // Eliminate this obstruction; welcome its location into the room.
+                            // Eliminate this obstruction; welcome its location into the machine.
                             madeChange = true;
                             interior[i][j] = true;
                             for (layer = 0; layer < NUMBER_TERRAIN_LAYERS; layer++) {
                                 newX = i + nbDirs[dir][0];
                                 newY = j + nbDirs[dir][1];
                                 if (pmap[newX][newY].layers[DUNGEON] == GRANITE) {
-                                    pmap[newX][newY].layers[DUNGEON] = TOP_WALL;
+                                    pmap[newX][newY].layers[DUNGEON] = WALL;
                                 }
                             }
                         }
     
     zeroOutGrid(interior);
     
-    distanceMap = allocDynamicGrid();
-    fillDynamicGrid(distanceMap, 30000);
+    distanceMap = allocGrid();
+    fillGrid(distanceMap, 30000);
     distanceMap[originX][originY] = 0;
     
-    costMap = allocDynamicGrid();
+    costMap = allocGrid();
     populateGenericCostMap(costMap);
     for(i=0; i<DCOLS; i++) {
         for(j=0; j<DROWS; j++) {
     }
     costMap[originX][originY] = 1;
     dijkstraScan(distanceMap, costMap, false);
-    freeDynamicGrid(costMap);
+    freeGrid(costMap);
     
     qualifyingTileCount = 0; // Keeps track of how many interior cells we've added.
     totalFreq = rand_range(blueprintCatalog[bp].roomSize[0], blueprintCatalog[bp].roomSize[1]); // Keeps track of the goal size.
     
-    for (i=0; i<DCOLS; i++) {
-        sCols[i] = i;
-    }
+    fillSequentialList(sCols, DCOLS);
     shuffleList(sCols, DCOLS);
-    for (i=0; i<DROWS; i++) {
-        sRows[i] = i;
-    }
+    fillSequentialList(sRows, DROWS);
     shuffleList(sRows, DROWS);
     
     for (k=0; k<1000 && qualifyingTileCount < totalFreq; k++) {
                && levelIsDisconnectedWithBlockingMap(interior, true) < 100) {
         success = false;
     }
-    freeDynamicGrid(distanceMap);
+    freeGrid(distanceMap);
     return success;
 }
 
 	
 	creature *monst, *nextMonst, *torchBearer = NULL, *leader = NULL;
 	
-	item *theItem, *torch = NULL, *spawnedItems[MACHINES_BUFFER_LENGTH] = {0}, *spawnedItemsSub[MACHINES_BUFFER_LENGTH] = {0};
+	item *theItem = NULL, *torch = NULL, *spawnedItems[MACHINES_BUFFER_LENGTH] = {0}, *spawnedItemsSub[MACHINES_BUFFER_LENGTH] = {0};
 	creature *spawnedMonsters[MACHINES_BUFFER_LENGTH] = {0}, *spawnedMonstersSub[MACHINES_BUFFER_LENGTH] = {0};
 	
 	const machineFeature *feature;
         tryAgain = false;
 		if (--failsafe <= 0) {
 			if (distanceMap) {
-				freeDynamicGrid(distanceMap);
+				freeGrid(distanceMap);
 			}
 			DEBUG {
 				if (chooseBP || chooseLocation) {
 			
 			if (!totalFreq) { // If no suitable blueprints are in the library, fail.
 				if (distanceMap) {
-					freeDynamicGrid(distanceMap);
+					freeGrid(distanceMap);
 				}
 				DEBUG printf("\nDepth %i: Failed to build a machine because no suitable blueprints were available.",
 							 rogue.depthLevel);
 				} else {
 					// If no suitable sites, abort.
 					if (distanceMap) {
-						freeDynamicGrid(distanceMap);
+						freeGrid(distanceMap);
 					}
 					DEBUG printf("\nDepth %i: Failed to build a machine; there was no eligible door candidate for the chosen room machine from blueprint %i.",
 								 rogue.depthLevel,
             if (chooseLocation) {
                 // Door machines must have locations passed in. We can't pick one ourselves.
                 if (distanceMap) {
-                    freeDynamicGrid(distanceMap);
+                    freeGrid(distanceMap);
                 }
                 DEBUG printf("\nDepth %i: ERROR: Attempted to build a door machine from blueprint %i without a location being provided.",
                              rogue.depthLevel,
             }
             if (!fillInteriorForVestibuleMachine(interior, bp, originX, originY)) {
                 if (distanceMap) {
-                    freeDynamicGrid(distanceMap);
+                    freeGrid(distanceMap);
                 }
                 DEBUG printf("\nDepth %i: Failed to build a door machine from blueprint %i; not enough room.",
                              rogue.depthLevel,
 				}
 				
 				if (!distanceMap) {
-					distanceMap = allocDynamicGrid();
+					distanceMap = allocGrid();
 				}
-				fillDynamicGrid(distanceMap, 0);
+				fillGrid(distanceMap, 0);
 				calculateDistances(distanceMap, originX, originY, T_PATHING_BLOCKER, NULL, true, false);
 				qualifyingTileCount = 0; // Keeps track of how many interior cells we've added.
 				totalFreq = rand_range(blueprintCatalog[bp].roomSize[0], blueprintCatalog[bp].roomSize[1]); // Keeps track of the goal size.
 				
-				for (i=0; i<DCOLS; i++) {
-					sCols[i] = i;
-				}
+                fillSequentialList(sCols, DCOLS);
 				shuffleList(sCols, DCOLS);
-				for (i=0; i<DROWS; i++) {
-					sRows[i] = i;
-				}
+                fillSequentialList(sRows, DROWS);
 				shuffleList(sRows, DROWS);
 				
 				for (k=0; k<1000 && qualifyingTileCount < totalFreq; k++) {
 								interior[sCols[i]][sRows[j]] = true;
 								qualifyingTileCount++;
 								
-								if (pmap[sCols[i]][sRows[j]].flags & (HAS_ITEM | IS_IN_MACHINE)) {
-									// Abort if we've entered another machine or engulfed another machine's item.
+								if (pmap[sCols[i]][sRows[j]].flags & (HAS_ITEM | HAS_MONSTER | IS_IN_MACHINE)) {
+									// Abort if we've entered another machine or engulfed another machine's item or monster.
 									tryAgain = true;
 									qualifyingTileCount = totalFreq; // This is a hack to drop out of these three for-loops.
 								}
 		// then there is nothing to try again, so just fail.
 		if (tryAgain && !chooseBP && !chooseLocation) {
 			if (distanceMap) {
-				freeDynamicGrid(distanceMap);
+				freeGrid(distanceMap);
 			}
 			return false;
 		}
 							&& !interior[newX][newY]
 							&& !cellHasTerrainFlag(newX, newY, T_OBSTRUCTS_PASSABILITY)
 							&& !(pmap[newX][newY].flags & IS_GATE_SITE)
-							&& !pmap[newX][newY].machineNumber) {
+							&& !pmap[newX][newY].machineNumber
+                            && cellHasTerrainFlag(newX, newY, T_PATHING_BLOCKER)) {
 							for (layer=0; layer<NUMBER_TERRAIN_LAYERS; layer++) {
-								pmap[newX][newY].layers[layer] = (layer == DUNGEON ? TOP_WALL : 0);
+								pmap[newX][newY].layers[layer] = (layer == DUNGEON ? WALL : 0);
 							}
 						}
 					}
 	if (blueprintCatalog[bp].flags & BP_IMPREGNABLE) {
 		for(i=0; i<DCOLS; i++) {
 			for(j=0; j<DROWS; j++) {
-				if (interior[i][j]) {
-					pmap[i][j].flags |= IMPREGNABLE;
-					if (!(pmap[i][j].flags & IS_GATE_SITE)) {
-						for (dir=0; dir<8; dir++) {
-							newX = i + nbDirs[dir][0];
-							newY = j + nbDirs[dir][1];
-							if (coordinatesAreInMap(newX, newY)
-								&& !interior[newX][newY]) {
-								pmap[newX][newY].flags |= IMPREGNABLE;
-							}
-						}
+				if (interior[i][j]
+                    && !(pmap[i][j].flags & IS_GATE_SITE)) {
+                    
+                    pmap[i][j].flags |= IMPREGNABLE;
+                    for (dir=0; dir<8; dir++) {
+                        newX = i + nbDirs[dir][0];
+                        newY = j + nbDirs[dir][1];
+                        if (coordinatesAreInMap(newX, newY)
+                            && !interior[newX][newY]
+                            && !(pmap[newX][newY].flags & IS_GATE_SITE)) {
+                            
+                            pmap[newX][newY].flags |= IMPREGNABLE;
+                        }
 					}
 				}
 			}
 	
 	// If necessary, label the interior as IS_IN_AREA_MACHINE or IS_IN_ROOM_MACHINE and mark down the number.
 	machineNumber = ++rogue.machineNumber; // Reserve this machine number, starting with 1.
-	if (!(blueprintCatalog[bp].flags & BP_NO_INTERIOR_FLAG)) {
+	//if (!(blueprintCatalog[bp].flags & BP_NO_INTERIOR_FLAG)) {
 		for(i=0; i<DCOLS; i++) {
 			for(j=0; j<DROWS; j++) {
 				if (interior[i][j]) {
 				}
 			}
 		}
-	}
+	//}
 	
 //	DEBUG printf("\n\nWorking on blueprint %i, with origin at (%i, %i). Here's the initial interior map:", bp, originX, originY);
 //	DEBUG logBuffer(interior);
 	// Calculate the distance map (so that features that want to be close to or far from the origin can be placed accordingly)
 	// and figure out the 33rd and 67th percentiles for features that want to be near or far from the origin.
 	if (!distanceMap) {
-		distanceMap = allocDynamicGrid();
+		distanceMap = allocGrid();
 	}
-	fillDynamicGrid(distanceMap, 0);
+	fillGrid(distanceMap, 0);
 	calculateDistances(distanceMap, originX, originY, T_PATHING_BLOCKER, NULL, true, true);
 	qualifyingTileCount = 0;
 	for (i=0; i<100; i++) {
 			
 			if (D_INSPECT_MACHINES) {
 				dumpLevelToScreen();
-				hiliteGrid(viewMap, &omniscienceColor, 75);
+				hiliteCharGrid(viewMap, &omniscienceColor, 75);
 				temporaryMessage("Showing visibility.", true);
 			}
 		}
 			
 			if (D_INSPECT_MACHINES) {
 				dumpLevelToScreen();
-				hiliteGrid(occupied, &red, 75);
-				hiliteGrid(candidates, &green, 75);
-				hiliteGrid(interior, &blue, 75);
+				hiliteCharGrid(occupied, &red, 75);
+				hiliteCharGrid(candidates, &green, 75);
+				hiliteCharGrid(interior, &blue, 75);
 				temporaryMessage("Indicating: Occupied (red); Candidates (green); Interior (blue).", true);
 			}
 			
 				}
 				
 				if (DFSucceeded && terrainSucceeded) { // Proceed only if the terrain stuff for this instance succeeded.
+                    
+                    theItem = NULL;
 					
 					// Mark the feature location as part of the machine, in case it is not already inside of it.
-					if (!(blueprintCatalog[bp].flags & BP_NO_INTERIOR_FLAG)) {
-						pmap[featX][featY].flags |= ((blueprintCatalog[bp].flags & BP_ROOM) ? IS_IN_ROOM_MACHINE : IS_IN_AREA_MACHINE);
-						pmap[featX][featY].machineNumber = machineNumber;
-					}
+					//if (!(blueprintCatalog[bp].flags & BP_NO_INTERIOR_FLAG)) {
+                    pmap[featX][featY].flags |= ((blueprintCatalog[bp].flags & BP_ROOM) ? IS_IN_ROOM_MACHINE : IS_IN_AREA_MACHINE);
+                    pmap[featX][featY].machineNumber = machineNumber;
+					//}
 					
 					// Mark the feature location as impregnable if requested.
 					if (feature->flags & MF_IMPREGNABLE) {
                             // failure! abort!
                             copyMap(levelBackup, pmap);
                             abortItemsAndMonsters(spawnedItems, spawnedMonsters);
-                            freeDynamicGrid(distanceMap);
+                            freeGrid(distanceMap);
                             return false;
                         }
                         theItem = NULL;
 			// Restore the map to how it was before we touched it.
 			copyMap(levelBackup, pmap);
 			abortItemsAndMonsters(spawnedItems, spawnedMonsters);
-			freeDynamicGrid(distanceMap);
+			freeGrid(distanceMap);
 			return false;
 		}
 	}
+    
+    // Clear out the interior flag for all non-wired cells, if requested.
+	if (blueprintCatalog[bp].flags & BP_NO_INTERIOR_FLAG) {
+        for(i=0; i<DCOLS; i++) {
+            for(j=0; j<DROWS; j++) {
+                if (pmap[i][j].machineNumber == machineNumber
+                    && !cellHasTMFlag(i, j, TM_IS_WIRED)) {
+                    
+                    pmap[i][j].flags &= ~IS_IN_MACHINE;
+                    pmap[i][j].machineNumber = 0;
+                }
+            }
+        }
+	}
 	
 	if (torchBearer && torch) {
 		if (torchBearer->carriedItem) {
 		torchBearer->carriedItem = torch;
 	}
 	
-	freeDynamicGrid(distanceMap);
+	freeGrid(distanceMap);
 	DEBUG printf("\nDepth %i: Built a machine from blueprint %i with an origin at (%i, %i).", rogue.depthLevel, bp, originX, originY);
 	return true;
 }
 	analyzeMap(true);
 	
 	// Add reward rooms first, if any:
-	
 	machineCount = 0;
-	
 	while (rogue.depthLevel <= AMULET_LEVEL
 		&& (rogue.rewardRoomsGenerated + machineCount) * 4 + 2 < rogue.depthLevel * MACHINES_FACTOR) {
 		// try to build at least one every four levels on average
 					if (D_INSPECT_LEVELGEN) {
 						dumpLevelToScreen();
 						hiliteCell(x, y, &yellow, 50, true);
-						message("Dungeon feature added.", true);
+						temporaryMessage("Dungeon feature added.", true);
 					}
 				}
 				
 						if (D_INSPECT_LEVELGEN) {
 							dumpLevelToScreen();
 							hiliteCell(x, y, &yellow, 50, true);
-							message("Terrain added.", true);
+							temporaryMessage("Terrain added.", true);
 						}
 					}
 				}
 void cleanUpLakeBoundaries() {
 	short i, j, x, y, layer;
 	boolean reverse, madeChange;
+    unsigned long subjectFlags;
 	
 	reverse = true;
 	
 				
 				//assert(i >= 1 && i <= DCOLS - 2 && j >= 1 && j <= DROWS - 2);
 				
-				if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)
-                    && !cellHasTerrainFlag(i, j, T_IS_SECRET)
+				//if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)
+                if (cellHasTerrainFlag(i, j, T_LAKE_PATHING_BLOCKER | T_OBSTRUCTS_PASSABILITY)
+                    && !cellHasTMFlag(i, j, TM_IS_SECRET)
 					&& !(pmap[i][j].flags & IMPREGNABLE)) {
+                    
+                    subjectFlags = terrainFlags(i, j) & (T_LAKE_PATHING_BLOCKER | T_OBSTRUCTS_PASSABILITY);
 					
 					x = y = 0;
-					if ((terrainFlags(i - 1, j) & T_LAKE_PATHING_BLOCKER)
-						&& (terrainFlags(i - 1, j) & T_LAKE_PATHING_BLOCKER) == (terrainFlags(i + 1, j) & T_LAKE_PATHING_BLOCKER)) {
+					if ((terrainFlags(i - 1, j) & T_LAKE_PATHING_BLOCKER & ~subjectFlags)
+                        && !cellHasTMFlag(i - 1, j, TM_IS_SECRET)
+                        && !cellHasTMFlag(i + 1, j, TM_IS_SECRET)
+						&& (terrainFlags(i - 1, j) & T_LAKE_PATHING_BLOCKER & ~subjectFlags) == (terrainFlags(i + 1, j) & T_LAKE_PATHING_BLOCKER & ~subjectFlags)) {
 						x = i + 1;
 						y = j;
-					} else if ((terrainFlags(i, j - 1) & T_LAKE_PATHING_BLOCKER)
-							   && (terrainFlags(i, j - 1) & T_LAKE_PATHING_BLOCKER) == (terrainFlags(i, j + 1) & T_LAKE_PATHING_BLOCKER)) {
+					} else if ((terrainFlags(i, j - 1) & T_LAKE_PATHING_BLOCKER & ~subjectFlags)
+                               && !cellHasTMFlag(i, j - 1, TM_IS_SECRET)
+                               && !cellHasTMFlag(i, j + 1, TM_IS_SECRET)
+							   && (terrainFlags(i, j - 1) & T_LAKE_PATHING_BLOCKER & ~subjectFlags) == (terrainFlags(i, j + 1) & T_LAKE_PATHING_BLOCKER & ~subjectFlags)) {
 						x = i;
 						y = j + 1;
 					}
 				for (k=0; k<=1; k++) {
 					if (!(tileCatalog[pmap[i + k][j].layers[DUNGEON]].flags & T_OBSTRUCTS_PASSABILITY)
 						&& (tileCatalog[pmap[i + (1-k)][j].layers[DUNGEON]].flags & T_OBSTRUCTS_PASSABILITY)
+						&& (tileCatalog[pmap[i + (1-k)][j].layers[DUNGEON]].flags & T_OBSTRUCTS_DIAGONAL_MOVEMENT)
 						&& (tileCatalog[pmap[i + k][j+1].layers[DUNGEON]].flags & T_OBSTRUCTS_PASSABILITY)
+						&& (tileCatalog[pmap[i + k][j+1].layers[DUNGEON]].flags & T_OBSTRUCTS_DIAGONAL_MOVEMENT)
 						&& !(tileCatalog[pmap[i + (1-k)][j+1].layers[DUNGEON]].flags & T_OBSTRUCTS_PASSABILITY)) {
 						
 						if (rand_percent(50)) {
 	} while (diagonalCornerRemoved == true);
 }
 
-// This is the master function for digging out a dungeon level. Finishing touches -- items, monsters, staircases, etc. -- are handled elsewhere.
-void digDungeon() {
-	short i, j, k;
-	short x1, x2, y1, y2;
+void insertRoomAt(short **dungeonMap, short **roomMap, short roomToDungeonX, short roomToDungeonY) {
+    short xRoom, yRoom;
+    
+    for (xRoom = 0; xRoom < DCOLS; xRoom++) {
+        for (yRoom = 0; yRoom < DROWS; yRoom++) {
+            if (roomMap[xRoom][yRoom]
+                && coordinatesAreInMap(xRoom + roomToDungeonX, yRoom + roomToDungeonY)) {
+                
+                dungeonMap[xRoom + roomToDungeonX][yRoom + roomToDungeonY] = true;
+            }
+        }
+    }
+}
+
+void designCavern(short **grid, short minWidth, short maxWidth, short minHeight, short maxHeight) {
+	short destX, destY;
+    short caveX, caveY, caveWidth, caveHeight;
+    short **blobGrid;
+    blobGrid = allocGrid();
+    
+    fillGrid(grid, 0);
+    createBlobOnGrid(blobGrid,
+                     &caveX, &caveY, &caveWidth, &caveHeight,
+                     5, minWidth, minHeight, maxWidth, maxHeight, 55, "ffffffttt", "ffffttttt");
+    
+//    colorOverDungeon(&darkGray);
+//    hiliteGrid(blobGrid, &tanColor, 80);
+//    temporaryMessage("Here's the cave:", true);
+    
+	// Position the new cave in the middle of the grid...
+	destX = (DCOLS - caveWidth) / 2;
+	destY = (DROWS - caveHeight) / 2;
+	// ...and copy it to the master grid.
+    insertRoomAt(grid, blobGrid, destX - caveX, destY - caveY);
+    freeGrid(blobGrid);
+}
+
+// This is a special room that appears at the entrance to the dungeon on depth 1.
+void designEntranceRoom(short **grid) {
 	short roomWidth, roomHeight, roomWidth2, roomHeight2, roomX, roomY, roomX2, roomY2;
-	short randIndex, doorCandidateX, doorCandidateY;
-	short dir;
-	short numLoops;
-	short lakeMaxWidth, lakeMaxHeight;
-	short depthRoomSizeScaleFactor;
-	short deepLiquid, shallowLiquid, shallowLiquidWidth;
-	char wreathMap[DCOLS][DROWS], unfilledLakeMap[DCOLS][DROWS], connectedMap[DCOLS][DROWS];
-	boolean isCorridor = false;
-	boolean isCross = false;
-	boolean roomWasPlaced = true;
-	boolean foundExposure;
-	room *builtRoom = NULL, *fromRoom = NULL;
-	
-	rogue.machineNumber = 0;
-	
-	topBlobMinX = topBlobMinY = blobWidth = blobHeight = 0;
-	
-	rogue.scentTurnNumber = 10000;
-	
-	for (i=0; i<4; i++) {
-		numberOfWalls[i] = -1;
-	}
-	for (i=0; i<4; i++) {
-		for (j=0; j<DCOLS*DROWS; j++) {
-			listOfWallsX[i][j] = 0;
-			listOfWallsY[i][j] = 0;
+    
+    fillGrid(grid, 0);
+    
+    roomWidth = 8;
+    roomHeight = 10;
+    roomWidth2 = 20;
+    roomHeight2 = 5;
+    roomX = DCOLS/2 - roomWidth/2 - 1;
+    roomY = DROWS - roomHeight - 2;
+    roomX2 = DCOLS/2 - roomWidth2/2 - 1;
+    roomY2 = DROWS - roomHeight2 - 2;
+    
+    drawRectangleOnGrid(grid, roomX, roomY, roomWidth, roomHeight, 1);
+    drawRectangleOnGrid(grid, roomX2, roomY2, roomWidth2, roomHeight2, 1);
+}
+
+void designCrossRoom(short **grid) {
+	short roomWidth, roomHeight, roomWidth2, roomHeight2, roomX, roomY, roomX2, roomY2;
+    
+    fillGrid(grid, 0);
+    
+    roomWidth = rand_range(3, 12);
+    roomX = rand_range(max(0, DCOLS/2 - (roomWidth - 1)), min(DCOLS, DCOLS/2));
+    roomWidth2 = rand_range(4, 20);
+    roomX2 = (roomX + (roomWidth / 2) + rand_range(0, 2) + rand_range(0, 2) - 3) - (roomWidth2 / 2);
+    
+    roomHeight = rand_range(3, 7);
+    roomY = (DROWS/2 - roomHeight);
+    
+    roomHeight2 = rand_range(2, 5);
+    roomY2 = (DROWS/2 - roomHeight2 - (rand_range(0, 2) + rand_range(0, 1)));
+    
+    drawRectangleOnGrid(grid, roomX - 5, roomY + 5, roomWidth, roomHeight, 1);
+    drawRectangleOnGrid(grid, roomX2 - 5, roomY2 + 5, roomWidth2, roomHeight2, 1);
+}
+
+void designCircularRoom(short **grid) {
+	short radius;
+    
+    if (rand_percent(5)) {
+        radius = rand_range(4, 10);
+    } else {
+        radius = rand_range(2, 4);
+    }
+    
+    fillGrid(grid, 0);
+    drawCircleOnGrid(grid, DCOLS/2, DROWS/2, radius, 1);
+}
+
+void designChunkyRoom(short **grid) {
+    short i, x, y;
+	short minX, maxX, minY, maxY;
+    short chunkCount = rand_range(2, 8);
+    
+    fillGrid(grid, 0);
+    drawCircleOnGrid(grid, DCOLS/2, DROWS/2, 2, 1);
+    minX = DCOLS/2 - 3;
+    maxX = DCOLS/2 + 3;
+    minY = DROWS/2 - 3;
+    maxY = DROWS/2 + 3;
+    
+    for (i=0; i<chunkCount;) {
+        x = rand_range(minX, maxX);
+        y = rand_range(minY, maxY);
+        if (grid[x][y]) {
+//            colorOverDungeon(&darkGray);
+//            hiliteGrid(grid, &white, 100);
+            
+            drawCircleOnGrid(grid, x, y, 2, 1);
+            i++;
+            minX = max(1, min(x - 3, minX));
+            maxX = min(DCOLS - 2, max(x + 3, maxX));
+            minY = max(1, min(y - 3, minY));
+            maxY = min(DROWS - 2, max(y + 3, maxY));
+            
+//            hiliteGrid(grid, &green, 50);
+//            temporaryMessage("Added a chunk:", true);
+        }
+    }
+}
+
+// If the indicated tile is a wall on the room stored in grid, and it could be the site of
+// a door out of that room, then return the outbound direction that the door faces.
+// Otherwise, return NO_DIRECTION.
+enum directions directionOfDoorSite(short **grid, short x, short y) {
+    enum directions dir, solutionDir;
+    short newX, newY, oppX, oppY;
+    
+    if (grid[x][y]) { // Already interior
+        return NO_DIRECTION;
+    }
+    
+    solutionDir = NO_DIRECTION;
+    for (dir=0; dir<4; dir++) {
+        newX = x + nbDirs[dir][0];
+        newY = y + nbDirs[dir][1];
+        oppX = x - nbDirs[dir][0];
+        oppY = y - nbDirs[dir][1];
+        if (coordinatesAreInMap(oppX, oppY)
+            && coordinatesAreInMap(newX, newY)
+            && grid[oppX][oppY]) {
+            
+            // This grid cell would be a valid tile on which to place a door that, facing outward, points dir.
+            if (solutionDir != NO_DIRECTION) {
+                // Already claimed by another direction; no doors here!
+                return NO_DIRECTION;
+            }
+            solutionDir = dir;
+        }
+    }
+    return solutionDir;
+}
+
+void chooseRandomDoorSites(short **roomMap, short doorSites[4][2]) {
+    short i, j, k, newX, newY;
+    enum directions dir;
+    short **grid;
+    boolean doorSiteFailed;
+    
+    grid = allocGrid();
+    copyGrid(grid, roomMap);
+    
+//    colorOverDungeon(&darkGray);
+//    hiliteGrid(grid, &blue, 100);
+//    temporaryMessage("Generating this room:", true);
+//    const char dirChars[] = "^v<>";
+    
+    for (i=0; i<DCOLS; i++) {
+        for (j=0; j<DROWS; j++) {
+            if (!grid[i][j]) {
+                dir = directionOfDoorSite(roomMap, i, j);
+                if (dir != NO_DIRECTION) {
+                    // Trace a ray 10 spaces outward from the door site to make sure it doesn't intersect the room.
+                    // If it does, it's not a valid door site.
+                    newX = i + nbDirs[dir][0];
+                    newY = j + nbDirs[dir][1];
+                    doorSiteFailed = false;
+                    for (k=0; k<10 && coordinatesAreInMap(newX, newY) && !doorSiteFailed; k++) {
+                        if (grid[newX][newY]) {
+                            doorSiteFailed = true;
+                        }
+                        newX += nbDirs[dir][0];
+                        newY += nbDirs[dir][1];
+                    }
+                    if (!doorSiteFailed) {
+//                        plotCharWithColor(dirChars[dir], mapToWindowX(i), mapToWindowY(j), &black, &green);
+                        grid[i][j] = dir + 2; // So as not to conflict with 0 or 1, which are used to indicate exterior/interior.
+                    }
+                }
+            }
+        }
+    }
+    
+//    temporaryMessage("Door candidates:", true);
+    
+    // Pick four doors, one in each direction, and store them in doorSites[dir].
+    for (dir=0; dir<4; dir++) {
+        randomLocationInGrid(grid, &(doorSites[dir][0]), &(doorSites[dir][1]), dir + 2);
+    }
+    
+    freeGrid(grid);
+}
+
+void attachHallwayTo(short **grid, short doorSites[4][2]) {
+    short i, x, y, newX, newY, dirs[4];
+    short length;
+    enum directions dir, dir2;
+    boolean allowObliqueHallwayExit;
+    
+    // Pick a direction.
+    fillSequentialList(dirs, 4);
+    shuffleList(dirs, 4);
+    for (i=0; i<4; i++) {
+        dir = dirs[i];
+        if (doorSites[dir][0] != -1
+            && doorSites[dir][1] != -1
+            && coordinatesAreInMap(doorSites[dir][0] + nbDirs[dir][0] * HORIZONTAL_CORRIDOR_MAX_LENGTH,
+                                   doorSites[dir][1] + nbDirs[dir][1] * VERTICAL_CORRIDOR_MAX_LENGTH)) {
+                break; // That's our direction!
+        }
+    }
+    if (i==4) {
+        return; // No valid direction for hallways.
+    }
+    
+    if (dir == UP || dir == DOWN) {
+        length = rand_range(VERTICAL_CORRIDOR_MIN_LENGTH, VERTICAL_CORRIDOR_MAX_LENGTH);
+    } else {
+        length = rand_range(HORIZONTAL_CORRIDOR_MIN_LENGTH, HORIZONTAL_CORRIDOR_MAX_LENGTH);
+    }
+    
+    x = doorSites[dir][0];
+    y = doorSites[dir][1];
+    for (i = 0; i < length; i++) {
+        if (coordinatesAreInMap(x, y)) {
+            grid[x][y] = true;
+        }
+        x += nbDirs[dir][0];
+        y += nbDirs[dir][1];
+    }
+    x = clamp(x - nbDirs[dir][0], 0, DCOLS - 1);
+    y = clamp(y - nbDirs[dir][1], 0, DROWS - 1); // Now (x, y) points at the last interior cell of the hallway.
+    allowObliqueHallwayExit = rand_percent(15);
+    for (dir2 = 0; dir2 < 4; dir2++) {
+        newX = x + nbDirs[dir2][0];
+        newY = y + nbDirs[dir2][1];
+        
+        if ((dir2 != dir && !allowObliqueHallwayExit)
+            || !coordinatesAreInMap(newX, newY)
+            || grid[newX][newY]) {
+            
+            doorSites[dir2][0] = -1;
+            doorSites[dir2][1] = -1;
+        } else {
+            doorSites[dir2][0] = newX;
+            doorSites[dir2][1] = newY;
+        }
+    }
+}
+
+// Put a random room shape somewhere on the binary grid, and optionally record the coordinates of up to four door sites in doorSites.
+// If attachHallway is true, then it will bolt a perpendicular hallway onto the room at one of the four standard door sites,
+// and then relocate three of the door sites to radiate from the end of the hallway. (The fourth is defunct.)
+// RoomTypeFrequencies specifies the probability of each room type, in the following order:
+//      1. Cross room
+//      2. Circular room
+//      3. Chunky room
+//      4. Cave
+//      5. Cavern (the kind that fills a level)
+//      6. Entrance room (the big upside-down T room at the start of depth 1)
+
+void designRandomRoom(short **grid, boolean attachHallway, short doorSites[4][2], const short roomTypeFrequencies[ROOM_TYPE_COUNT]) {
+    short randIndex, i, sum;
+    enum directions dir;
+    
+    sum = 0;
+    for (i=0; i<ROOM_TYPE_COUNT; i++) {
+        sum += roomTypeFrequencies[i];
+    }
+    randIndex = rand_range(0, sum - 1);
+    for (i=0; i<ROOM_TYPE_COUNT; i++) {
+        if (randIndex < roomTypeFrequencies[i]) {
+            break; // "i" is our room type.
+        } else {
+            randIndex -= roomTypeFrequencies[i];
+        }
+    }
+    switch (i) {
+        case 0:
+            designCrossRoom(grid);
+            break;
+        case 1:
+            designCircularRoom(grid);
+            break;
+        case 2:
+            designChunkyRoom(grid);
+            break;
+        case 3:
+            switch (rand_range(0, 2)) {
+                case 0:
+                    designCavern(grid, 3, 12, 4, 8); // Compact cave room.
+                    break;
+                case 1:
+                    designCavern(grid, 3, 12, 15, DROWS-2); // Large north-south cave room.
+                    break;
+                case 2:
+                    designCavern(grid, 20, DROWS-2, 4, 8); // Compact cave room.
+                    break;
+                default:
+                    break;
+            }
+            break;
+        case 4:
+            designCavern(grid, CAVE_MIN_WIDTH, DCOLS - 2, CAVE_MIN_HEIGHT, DROWS - 2);
+            break;
+        case 5:
+            designEntranceRoom(grid);
+            break;
+        default:
+            break;
+    }
+    
+    if (doorSites) {
+        chooseRandomDoorSites(grid, doorSites);
+        if (attachHallway) {
+            dir = rand_range(0, 3);
+            for (i=0; doorSites[dir][0] == -1 && i < 3; i++) {
+                dir = (dir + 1) % 4; // Each room will have at least 2 valid directions for doors.
+            }
+            attachHallwayTo(grid, doorSites);
+        }
+    }
+}
+
+boolean roomFitsAt(short **dungeonMap, short **roomMap, short roomToDungeonX, short roomToDungeonY) {
+    short xRoom, yRoom, xDungeon, yDungeon, i, j;
+    
+    for (xRoom = 0; xRoom < DCOLS; xRoom++) {
+        for (yRoom = 0; yRoom < DROWS; yRoom++) {
+            if (roomMap[xRoom][yRoom]) {
+                xDungeon = xRoom + roomToDungeonX;
+                yDungeon = yRoom + roomToDungeonY;
+                
+                for (i = xDungeon - 1; i <= xDungeon + 1; i++) {
+                    for (j = yDungeon - 1; j <= yDungeon + 1; j++) {
+                        if (!coordinatesAreInMap(i, j)
+                            || dungeonMap[i][j]) {
+                            return false;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return true;
+}
+
+// Called by digDungeon().
+// Slaps a bunch of rooms and hallways into the grid.
+// On the grid, a 0 denotes granite, a 1 denotes floor, and a 2 denotes a possible door site.
+// Parent function will translate this grid into pmap[][] to make floors, walls, doors, etc.
+void carveDungeon(short **grid) {
+    short roomsBuilt, roomsAttempted;
+    short **roomMap;
+    short doorSites[4][2];
+    short i, x, y, sCoord[DCOLS*DROWS];
+    enum directions dir, oppDir;
+    const short descentPercent = clamp(100 * (rogue.depthLevel - 1) / (AMULET_LEVEL - 1), 0, 100);
+    
+    // Room frequencies:
+    //      1. Cross room
+    //      2. Circular room
+    //      3. Chunky room
+    //      4. Cave
+    //      5. Cavern (the kind that fills a level)
+    //      6. Entrance room (the big upside-down T room at the start of depth 1)
+    const short roomFrequencies[ROOM_TYPE_COUNT] = {
+        2 + 20 * (100 - descentPercent) / 100,
+        1 + 7 * (100 - descentPercent) / 100,
+        7,
+        1 + 10 * descentPercent / 100,
+        0,
+        0};
+    
+    const short firstRoomFrequencies[ROOM_TYPE_COUNT]   = {10, 3, 7, 10, 10 + 50 * descentPercent / 100, 0};
+    
+    const short corridorPercent = 10 + 80 * (100 - descentPercent) / 100;
+    
+    fillSequentialList(sCoord, DCOLS*DROWS);
+    shuffleList(sCoord, DCOLS*DROWS);
+    
+    if (rogue.depthLevel == 1) {
+        designEntranceRoom(grid);
+    } else {
+        designRandomRoom(grid, false, NULL, firstRoomFrequencies);
+    }
+    
+    if (D_INSPECT_LEVELGEN) {
+        colorOverDungeon(&darkGray);
+        hiliteGrid(grid, &white, 100);
+        temporaryMessage("First room placed:", true);
+    }
+    
+    roomMap = allocGrid();
+    for (roomsBuilt = roomsAttempted = 0; roomsBuilt < 35 && roomsAttempted < 35; roomsAttempted++) {
+        // Build a room in hyperspace.
+        fillGrid(roomMap, 0);
+        designRandomRoom(roomMap, roomsAttempted <= 30 && rand_percent(corridorPercent), doorSites, roomFrequencies);
+        
+        if (D_INSPECT_LEVELGEN) {
+            colorOverDungeon(&darkGray);
+            hiliteGrid(roomMap, &blue, 100);
+            if (doorSites[0][0] != -1) plotCharWithColor('^', mapToWindowX(doorSites[0][0]), mapToWindowY(doorSites[0][1]), &black, &green);
+            if (doorSites[1][0] != -1) plotCharWithColor('v', mapToWindowX(doorSites[1][0]), mapToWindowY(doorSites[1][1]), &black, &green);
+            if (doorSites[2][0] != -1) plotCharWithColor('<', mapToWindowX(doorSites[2][0]), mapToWindowY(doorSites[2][1]), &black, &green);
+            if (doorSites[3][0] != -1) plotCharWithColor('>', mapToWindowX(doorSites[3][0]), mapToWindowY(doorSites[3][1]), &black, &green);
+            temporaryMessage("Generating this room:", true);
+        }
+        
+        // Slide hyperspace across real space, in a random but predetermined order, until the room matches up with a wall.
+        for (i = 0; i < DCOLS*DROWS; i++) {
+            x = sCoord[i] / DROWS;
+            y = sCoord[i] % DROWS;
+        
+            dir = directionOfDoorSite(grid, x, y);
+            oppDir = oppositeDirection(dir);
+            if (dir != NO_DIRECTION
+                && doorSites[oppDir][0] != -1
+                && roomFitsAt(grid, roomMap, x - doorSites[oppDir][0], y - doorSites[oppDir][1])) {
+                
+                // Room fits here.
+                if (D_INSPECT_LEVELGEN) {
+                    colorOverDungeon(&darkGray);
+                    
+                    hiliteGrid(grid, &white, 100);
+                }
+                insertRoomAt(grid, roomMap, x - doorSites[oppDir][0], y - doorSites[oppDir][1]);
+                grid[x][y] = 2; // Door site.
+                if (D_INSPECT_LEVELGEN) {
+                    hiliteGrid(grid, &green, 50);
+                    temporaryMessage("Added room.", true);
+                }
+                roomsBuilt++;
+                break;
+            }
+        }
+    }
+    
+    freeGrid(roomMap);
+    
+//    colorOverDungeon(&darkGray);
+//    hiliteGrid(grid, &white, 100);
+//    temporaryMessage("How does this finished level look?", true);
+}
+
+void finishWalls(boolean includingDiagonals) {
+    short i, j, x1, y1;
+    boolean foundExposure;
+    enum directions dir;
+    
+    for (i=0; i<DCOLS; i++) {
+		for (j=0; j<DROWS; j++) {
+			if (pmap[i][j].layers[DUNGEON] == GRANITE) {
+				foundExposure = false;
+				for (dir = 0; dir < (includingDiagonals ? 8 : 4) && !foundExposure; dir++) {
+					x1 = i + nbDirs[dir][0];
+					y1 = j + nbDirs[dir][1];
+					if (coordinatesAreInMap(x1, y1)
+						&& (!cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_VISION) || !cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_PASSABILITY))) {
+                        
+						pmap[i][j].layers[DUNGEON] = WALL;
+						foundExposure = true;
+					}
+				}
+			} else if (pmap[i][j].layers[DUNGEON] == WALL) {
+				foundExposure = false;
+				for (dir = 0; dir < (includingDiagonals ? 8 : 4) && !foundExposure; dir++) {
+					x1 = i + nbDirs[dir][0];
+					y1 = j + nbDirs[dir][1];
+					if (coordinatesAreInMap(x1, y1)
+						&& (!cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_VISION) || !cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_PASSABILITY))) {
+                        
+						foundExposure = true;
+					}
+				}
+				if (foundExposure == false) {
+					pmap[i][j].layers[DUNGEON] = GRANITE;
+				}
+			}
 		}
 	}
+}
+
+// Add some loops to the otherwise simply connected network of rooms.
+void addLoops(short **grid) {
+    short newX, newY, oppX, oppY;
+    short **pathMap, **costMap;
+    short i, d, x, y, sCoord[DCOLS*DROWS];
+    const short dirCoords[2][2] = {{1, 0}, {0, 1}};
+    
+    fillSequentialList(sCoord, DCOLS*DROWS);
+    shuffleList(sCoord, DCOLS*DROWS);
+    
+    if (D_INSPECT_LEVELGEN) {
+        colorOverDungeon(&darkGray);
+        hiliteGrid(grid, &white, 100);
+    }
+    
+    pathMap = allocGrid();
+    costMap = allocGrid();
+    copyGrid(costMap, grid);
+    findReplaceGrid(costMap, 0, 0, PDS_OBSTRUCTION);
+    findReplaceGrid(costMap, 1, 30000, 1);
+    
+    for (i = 0; i < DCOLS*DROWS; i++) {
+        x = sCoord[i]/DROWS;
+        y = sCoord[i] % DROWS;
+        if (!grid[x][y]) {
+            for (d=0; d <= 1; d++) { // Try a horizontal door, and then a vertical door.
+                newX = x + dirCoords[d][0];
+                oppX = x - dirCoords[d][0];
+                newY = y + dirCoords[d][1];
+                oppY = y - dirCoords[d][1];
+                if (coordinatesAreInMap(newX, newY)
+                    && coordinatesAreInMap(oppX, oppY)
+                    && grid[newX][newY]
+                    && grid[oppX][oppY]) { // If the tile being inspected has floor on both sides,
+                    
+                    fillGrid(pathMap, 30000);
+                    pathMap[newX][newY] = 0;
+                    dijkstraScan(pathMap, costMap, false);
+                    if (pathMap[oppX][oppY] > 20) { // and if the pathing distance between the two flanking floor tiles exceeds 20,
+                        grid[x][y] = 2;             // then turn the tile into a doorway.
+                        costMap[x][y] = 1;          // (Cost map also needs updating.)
+                        if (D_INSPECT_LEVELGEN) {
+                            plotCharWithColor(DOOR_CHAR, mapToWindowX(x), mapToWindowY(y), &black, &green);
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    if (D_INSPECT_LEVELGEN) {
+        temporaryMessage("Added secondary connections:", true);
+    }
+    freeGrid(pathMap);
+    freeGrid(costMap);
+}
+
+void liquidType(short *deep, short *shallow, short *shallowWidth) {
+	short randMin, randMax, rand;
 	
-#ifdef AUDIT_RNG
-	char RNGMessage[100];
-	sprintf(RNGMessage, "\n\n\nDigging dungeon level %i:\n", rogue.depthLevel);
-	RNGLog(RNGMessage);
-#endif
+	randMin = (rogue.depthLevel < 4 ? 1 : 0); // no lava before level 4
+	randMax = (rogue.depthLevel < 17 ? 2 : 3); // no brimstone before level 18
+	rand = rand_range(randMin, randMax);
+    if (rogue.depthLevel == DEEPEST_LEVEL) {
+        rand = 1;
+    }
 	
-	// Clear level and fill with granite
-	for( i=0; i<DCOLS; i++ ) {
-		for( j=0; j<DROWS; j++ ) {	
-			pmap[i][j].layers[DUNGEON] = GRANITE;
-			pmap[i][j].layers[LIQUID] = NOTHING;
-			pmap[i][j].layers[GAS] = NOTHING;
-			pmap[i][j].layers[SURFACE] = NOTHING;
-			pmap[i][j].machineNumber = 0;
-			pmap[i][j].rememberedTerrain = NOTHING;
-			pmap[i][j].rememberedItemCategory = 0;
-			pmap[i][j].flags = 0;
-			scentMap[i][j] = 0;
-			pmap[i][j].volume = 0;
+	switch(rand) {
+		case 0:
+			*deep = LAVA;
+			*shallow = NOTHING;
+			*shallowWidth = 0;
+			break;
+		case 1:
+			*deep = DEEP_WATER;
+			*shallow = SHALLOW_WATER;
+			*shallowWidth = 2;
+			break;
+		case 2:
+			*deep = CHASM;
+			*shallow = CHASM_EDGE;
+			*shallowWidth = 1;
+			break;
+		case 3:
+			*deep = INERT_BRIMSTONE;
+			*shallow = OBSIDIAN;
+			*shallowWidth = 2;
+			break;
+	}
+}
+
+// Fills a lake marked in unfilledLakeMap with the specified liquid type, scanning outward to reach other lakes within scanWidth.
+// Any wreath of shallow liquid must be done elsewhere.
+void fillLake(short x, short y, short liquid, short scanWidth, char wreathMap[DCOLS][DROWS], short **unfilledLakeMap) {
+	short i, j;
+	
+	for (i = x - scanWidth; i <= x + scanWidth; i++) {
+		for (j = y - scanWidth; j <= y + scanWidth; j++) {
+			if (coordinatesAreInMap(i, j) && unfilledLakeMap[i][j]) {
+				unfilledLakeMap[i][j] = false;
+				pmap[i][j].layers[LIQUID] = liquid;
+				wreathMap[i][j] = 1;
+				fillLake(i, j, liquid, scanWidth, wreathMap, unfilledLakeMap);	// recursive
+			}
 		}
 	}
-	
-	//depthRoomSizeScaleFactor = 1000 - 500 * (min(rogue.depthLevel - 1, 25)) / 25;
-	depthRoomSizeScaleFactor = 1000;
-	levelSpec.roomMinWidth = max(MIN_SCALED_ROOM_DIMENSION, ROOM_MIN_WIDTH * depthRoomSizeScaleFactor / 1000);
-	levelSpec.roomMaxWidth = max(MIN_SCALED_ROOM_DIMENSION, ROOM_MAX_WIDTH * depthRoomSizeScaleFactor / 1000);
-	levelSpec.roomMinHeight = max(MIN_SCALED_ROOM_DIMENSION, ROOM_MIN_HEIGHT * depthRoomSizeScaleFactor / 1000);
-	levelSpec.roomMaxHeight = max(MIN_SCALED_ROOM_DIMENSION, ROOM_MAX_HEIGHT * depthRoomSizeScaleFactor / 1000);
-	levelSpec.horCorrMinLength = max(MIN_SCALED_ROOM_DIMENSION, HORIZONTAL_CORRIDOR_MIN_LENGTH * depthRoomSizeScaleFactor / 1000);
-	levelSpec.horCorrMaxLength = max(MIN_SCALED_ROOM_DIMENSION, HORIZONTAL_CORRIDOR_MAX_LENGTH * depthRoomSizeScaleFactor / 1000);
-	levelSpec.vertCorrMinLength = max(MIN_SCALED_ROOM_DIMENSION, VERTICAL_CORRIDOR_MIN_LENGTH * depthRoomSizeScaleFactor / 1000);
-	levelSpec.vertCorrMaxLength = max(MIN_SCALED_ROOM_DIMENSION, VERTICAL_CORRIDOR_MAX_LENGTH * depthRoomSizeScaleFactor / 1000);
-	levelSpec.crossRoomMinWidth = max(MIN_SCALED_ROOM_DIMENSION, CROSS_ROOM_MIN_WIDTH * depthRoomSizeScaleFactor / 1000);
-	levelSpec.crossRoomMaxWidth = max(MIN_SCALED_ROOM_DIMENSION, CROSS_ROOM_MAX_WIDTH * depthRoomSizeScaleFactor / 1000);
-	levelSpec.crossRoomMinHeight = max(MIN_SCALED_ROOM_DIMENSION, CROSS_ROOM_MIN_HEIGHT * depthRoomSizeScaleFactor / 1000);
-	levelSpec.crossRoomMaxHeight = max(MIN_SCALED_ROOM_DIMENSION, CROSS_ROOM_MAX_HEIGHT * depthRoomSizeScaleFactor / 1000);
-	levelSpec.secretDoorChance = max(0, min(67, (rogue.depthLevel - 1) * 67 / 25));
-	levelSpec.numberOfTraps = rand_range((rogue.depthLevel - 1) / 4, (rogue.depthLevel - 1) / 2);
-	
-	// Build a rectangular seed room or cave at a random place
-	if (rogue.depthLevel == 1) {
-		roomWidth = levelSpec.crossRoomMaxWidth - 3;
-		roomHeight = levelSpec.roomMaxHeight + 3;
-		roomWidth2 = levelSpec.roomMaxWidth;
-		roomHeight2 = levelSpec.crossRoomMaxHeight - 1;
-		roomX = DCOLS/2 - roomWidth/2 - 1;
-		roomY = DROWS - roomHeight - 2;
-		roomX2 = DCOLS/2 - roomWidth2/2 - 1;
-		roomY2 = DROWS - roomHeight2 - 2;
-		
-		carveRectangle(roomX, roomY, roomWidth, roomHeight);
-		carveRectangle(roomX2, roomY2, roomWidth2, roomHeight2);
-		markRectangle(roomX, roomY, roomWidth, roomHeight);
-		markRectangle(roomX2, roomY2, roomWidth2, roomHeight2);
-		allocateRoom(roomX, roomY, roomWidth, roomHeight, roomX2, roomY2, roomWidth2, roomHeight2);
-	} else if (rand_percent(thisLevelProfile->caveLevelChance)) {
-		generateCave();
-	} else {		
-		roomWidth = rand_range(4, 25);
-		roomHeight = rand_range(2, 7);
-		roomX = rand_range(1, DCOLS - roomWidth - 1);
-		roomY = rand_range(1, DROWS - roomHeight - 1);
-		carveRectangle(roomX, roomY, roomWidth, roomHeight);
-		markRectangle(roomX, roomY, roomWidth, roomHeight);
-		allocateRoom(roomX, roomY, roomWidth, roomHeight, 0, 0, 0, 0);
-	}
-	
-	numberOfRooms = 1;
-	
-	if (D_INSPECT_LEVELGEN) {
-		dumpLevelToScreen();
-		message("First room placed.", true);
-	}
-	
-	// Now start placing random rooms branching off of preexisting rooms; make 600 attempts
-	
-	for(k=0; k<600 && numberOfRooms <= thisLevelProfile->maxNumberOfRooms; k++) {
-		
-		dir = rand_range(0,3); // Are we going to try to branch up, down, left, or right? Pick randomly.
-		
-		// Is it a corridor? Harder to place corridors, since you need space for a room at the end,
-		// so the last 225 attempts are hard-wired not to be corridors so that they can fill in any
-		// big gaps left by the corridor requirement.
-		isCorridor = (( k > 375) ? false : (rand_percent(thisLevelProfile->corridorChance))); // boolean: is it a corridor?
-		
-		if (roomWasPlaced) { // We only randomize the crossRoom status if the previous room succeeded
-			if (!isCorridor) {
-				isCross = (rand_percent(thisLevelProfile->crossRoomChance));
-			}
-		}
-		
-		// Now choose a random wall cell of the given direction
-		randIndex = rand_range(0, numberOfWalls[dir]);
-		doorCandidateX = listOfWallsX[dir][randIndex];
-		doorCandidateY = listOfWallsY[dir][randIndex];
-		
-		builtRoom = attemptRoom(doorCandidateX, doorCandidateY, dir, isCorridor, isCross, (isCorridor ? 10 : 5));
-		// note: if it is a corridor, this call will also build a
-		// non-corridor room at the end of the corridor, or it will fail.
-		
-		roomWasPlaced = (builtRoom != 0);
-		
-		if (builtRoom) {
-			fromRoom = roomContainingCell(doorCandidateX + nbDirs[oppositeDirection(dir)][0],
-										  doorCandidateY + nbDirs[oppositeDirection(dir)][1]);
-			connectRooms(fromRoom, builtRoom, doorCandidateX, doorCandidateY, dir);
-		}
-		if (D_INSPECT_LEVELGEN && builtRoom) {
-			dumpLevelToScreen();
-			message("Room placed", true);
-		}
-	}
-	//DEBUG printf("%i non-corridor rooms placed.", numberOfRooms);
-	
-	// now we add some loops to the otherwise simply connected network of rooms
-	numLoops = 0;
-	for (k=0; k<500 && numLoops <= thisLevelProfile->maxNumberOfLoops; k++) {
-		
-		dir = rand_range(0,3); // Are we going to try to loop up, down, left, or right? Pick randomly.
-		
-		// Now choose a random wall cell of the given direction
-		randIndex = rand_range(0, numberOfWalls[dir]);
-		doorCandidateX = listOfWallsX[dir][randIndex];
-		doorCandidateY = listOfWallsY[dir][randIndex];
-		
-		x1 = doorCandidateX + nbDirs[dir][0];
-		x2 = doorCandidateX + nbDirs[oppositeDirection(dir)][0];
-		y1 = doorCandidateY + nbDirs[dir][1];
-		y2 = doorCandidateY + nbDirs[oppositeDirection(dir)][1];
-		
-		if (coordinatesAreInMap(x1, y1) && coordinatesAreInMap(x2, y2)
-			&& pmap[x1][y1].layers[DUNGEON] == FLOOR && pmap[x2][y2].layers[DUNGEON] == FLOOR &&
-			distanceBetweenRooms(roomContainingCell(x1, y1), roomContainingCell(x2, y2)) > 2) {
-			pmap[doorCandidateX][doorCandidateY].layers[DUNGEON] = (rand_percent(thisLevelProfile->doorChance) ? DOOR : FLOOR);
-			pmap[doorCandidateX][doorCandidateY].flags |= DOORWAY;
-			removeWallFromList(dir, doorCandidateX, doorCandidateY);
-			connectRooms(roomContainingCell(x2, y2), roomContainingCell(x1, y1), doorCandidateX, doorCandidateY, dir);
-			numLoops++;
-		}
-	}
-	
-	if (D_INSPECT_LEVELGEN) {
-		dumpLevelToScreen();
-		message("Loops added.", true);
-	}
-	//DEBUG printf("\n%i loops created.", numLoops);
-	//DEBUG logLevel();
-	
-	// Time to add lakes and chasms. Strategy is to generate a series of blob lakes of decreasing size. For each lake,
-	// propose a position, and then check via a flood fill that the level would remain connected with that placement (i.e. that
-	// each passable tile can still be reached). If not, make 9 more placement attempts before abandoning that lake
-	// and proceeding to generate the next smaller one.
-	// Canvas sizes start at 30x15 and decrease by 2x1 at a time down to a minimum of 20x10. Min generated size is always 4x4.
-	
-	// DEBUG logLevel();
-	
-	zeroOutGrid(unfilledLakeMap);
+}
+
+void lakeFloodFill(short x, short y, short **floodMap, short **grid, short **lakeMap, short dungeonToGridX, short dungeonToGridY) {
+    short newX, newY;
+    enum directions dir;
+    
+    floodMap[x][y] = true;
+    for (dir=0; dir<4; dir++) {
+        newX = x + nbDirs[dir][0];
+        newY = y + nbDirs[dir][1];
+        if (coordinatesAreInMap(newX, newY)
+            && !floodMap[newX][newY]
+            && !cellHasTerrainFlag(newX, newY, T_PATHING_BLOCKER)
+            && !lakeMap[newX][newY]
+            && (!coordinatesAreInMap(newX+dungeonToGridX, newY+dungeonToGridY) || !grid[newX+dungeonToGridX][newY+dungeonToGridY])) {
+            
+            lakeFloodFill(newX, newY, floodMap, grid, lakeMap, dungeonToGridX, dungeonToGridY);
+        }
+    }
+}
+
+boolean lakeDisruptsPassability(short **grid, short **lakeMap, short dungeonToGridX, short dungeonToGridY) {
+    boolean result;
+    short i, j, x, y;
+    short **floodMap;
+    
+    floodMap = allocGrid();
+    fillGrid(floodMap, 0);
+    x = y = -1;
+    // Get starting location for the fill.
+    for (i=0; i<DCOLS && x == -1; i++) {
+        for (j=0; j<DROWS && x == -1; j++) {
+            if (!cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)
+                && !lakeMap[i][j]
+                && (!coordinatesAreInMap(i+dungeonToGridX, j+dungeonToGridY) || !grid[i+dungeonToGridX][j+dungeonToGridY])) {
+                
+                x = i;
+                y = j;
+            }
+        }
+    }
+#ifdef BROGUE_ASSERTS
+    assert(x != -1);
+#endif
+    // Do the flood fill.
+    lakeFloodFill(x, y, floodMap, grid, lakeMap, dungeonToGridX, dungeonToGridY);
+    
+    // See if any dry tiles weren't reached by the flood fill.
+    result = false;
+    for (i=0; i<DCOLS && result == false; i++) {
+        for (j=0; j<DROWS && result == false; j++) {
+            if (!cellHasTerrainFlag(i, j, T_PATHING_BLOCKER)
+                && !lakeMap[i][j]
+                && !floodMap[i][j]
+                && (!coordinatesAreInMap(i+dungeonToGridX, j+dungeonToGridY) || !grid[i+dungeonToGridX][j+dungeonToGridY])) {
+                
+//                if (D_INSPECT_LEVELGEN) {
+//                    dumpLevelToScreen();
+//                    hiliteGrid(lakeMap, &darkBlue, 75);
+//                    hiliteGrid(floodMap, &white, 20);
+//                    plotCharWithColor('X', mapToWindowX(i), mapToWindowY(j), &black, &red);
+//                    temporaryMessage("Failed here.", true);
+//                }
+                
+                result = true;
+            }
+        }
+    }
+    
+    freeGrid(floodMap);
+    return result;
+}
+
+void designLakes(short **lakeMap) {
+    short i, j, k;
+    short x, y;
+    short lakeMaxHeight, lakeMaxWidth;
+    short lakeX, lakeY, lakeWidth, lakeHeight;
+    
+    short **grid; // Holds the current lake.
+    
+    grid = allocGrid();
+    fillGrid(lakeMap, 0);
 	for (lakeMaxHeight = 15, lakeMaxWidth = 30; lakeMaxHeight >=10; lakeMaxHeight--, lakeMaxWidth -= 2) { // lake generations
 		
-		cellularAutomata(4, 4, lakeMaxWidth, lakeMaxHeight, 55, "ffffftttt", "ffffttttt");
+        fillGrid(grid, 0);
+        createBlobOnGrid(grid, &lakeX, &lakeY, &lakeWidth, &lakeHeight, 5, 4, 4, lakeMaxWidth, lakeMaxHeight, 55, "ffffftttt", "ffffttttt");
 		
+//        if (D_INSPECT_LEVELGEN) {
+//            colorOverDungeon(&darkGray);
+//            hiliteGrid(grid, &white, 100);
+//            temporaryMessage("Generated a lake.", true);
+//        }
+        
 		for (k=0; k<20; k++) { // placement attempts
+			// propose a position for the top-left of the grid in the dungeon
+			x = rand_range(1 - lakeX, DCOLS - lakeWidth - lakeX - 2);
+			y = rand_range(1 - lakeY, DROWS - lakeHeight - lakeY - 2);
 			
-			// propose a position
-			roomX = rand_range(1, DCOLS - blobWidth - 1);
-			roomY = rand_range(1, DROWS - blobHeight - 1);
-			
-			if (checkLakePassability(roomX, roomY, connectedMap)) { // level with lake is completely connected
+            if (!lakeDisruptsPassability(grid, lakeMap, -x, -y)) { // level with lake is completely connected
 				//printf("Placed a lake!");
 				
 				// copy in lake
-				for (i = roomX; i < roomX + blobWidth; i++) {
-					for (j = roomY; j < roomY + blobHeight; j++) {
-						if (connectedMap[i][j] == 101) { // It's in the lake.
-							unfilledLakeMap[i][j] = true;
-							pmap[i][j].layers[LIQUID] = FLOOR; // Just need a non-NOTHING value so createWreath() works.
-							pmap[i][j].layers[DUNGEON] = FLOOR;
-							if (pmap[i][j].flags & DOORWAY) {
-								pmap[i][j].flags &= ~DOORWAY; // so that waypoint selection is not blocked by former doorways
-							}
-//							for (l=0; l<8; l++) {
-//								if (coordinatesAreInMap(i+nbDirs[l][0], j+nbDirs[l][1]) &&
-//									pmap[i+nbDirs[l][0]][j+nbDirs[l][1]].layers[DUNGEON] == GRANITE) {
-//									pmap[i+nbDirs[l][0]][j+nbDirs[l][1]].layers[DUNGEON] = PERM_WALL;
-//								}
-//							}
-						}
+				for (i = 0; i < lakeWidth; i++) {
+					for (j = 0; j < lakeHeight; j++) {
+                        if (grid[i + lakeX][j + lakeY]) {
+                            lakeMap[i + lakeX + x][j + lakeY + y] = true;
+                            pmap[i + lakeX + x][j + lakeY + y].layers[DUNGEON] = FLOOR;
+                        }
 					}
 				}
 				
 				if (D_INSPECT_LEVELGEN) {
 					dumpLevelToScreen();
-					hiliteGrid(unfilledLakeMap, &white, 75);
-					message("Added a lake location.", true);
+					hiliteGrid(lakeMap, &white, 50);
+					temporaryMessage("Added a lake location.", true);
 				}
-				
-				// DEBUG logLevel();
 				break;
 			}
 		}
 	}
-	
-	// Now fill all the lakes with various liquids
+    freeGrid(grid);
+}
+
+void createWreath(short shallowLiquid, short wreathWidth, char wreathMap[DCOLS][DROWS]) {
+	short i, j, k, l;
 	for (i=0; i<DCOLS; i++) {
 		for (j=0; j<DROWS; j++) {
-			if (unfilledLakeMap[i][j]) {
+			if (wreathMap[i][j]) {
+				for (k = i-wreathWidth; k<= i+wreathWidth; k++) {
+					for (l = j-wreathWidth; l <= j+wreathWidth; l++) {
+						if (coordinatesAreInMap(k, l) && pmap[k][l].layers[LIQUID] == NOTHING
+							&& (i-k)*(i-k) + (j-l)*(j-l) <= wreathWidth*wreathWidth) {
+							pmap[k][l].layers[LIQUID] = shallowLiquid;
+							if (pmap[k][l].layers[DUNGEON] == DOOR) {
+								pmap[k][l].layers[DUNGEON] = FLOOR;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
+void fillLakes(short **lakeMap) {
+	short deepLiquid, shallowLiquid, shallowLiquidWidth;
+	char wreathMap[DCOLS][DROWS];
+    short i, j;
+    
+    for (i=0; i<DCOLS; i++) {
+		for (j=0; j<DROWS; j++) {
+			if (lakeMap[i][j]) {
 				liquidType(&deepLiquid, &shallowLiquid, &shallowLiquidWidth);
 				zeroOutGrid(wreathMap);
-				fillLake(i, j, deepLiquid, 4, wreathMap, unfilledLakeMap);
+				fillLake(i, j, deepLiquid, 4, wreathMap, lakeMap);
 				createWreath(shallowLiquid, shallowLiquidWidth, wreathMap);
 				
 				if (D_INSPECT_LEVELGEN) {
 					dumpLevelToScreen();
-					hiliteGrid(unfilledLakeMap, &white, 75);
-					message("Lake filled.", true);
+					hiliteGrid(lakeMap, &white, 75);
+					temporaryMessage("Lake filled.", true);
 				}
 			}
 		}
 	}
-	
-	// Now replace all left, right and bottom walls with TOP_WALLS for homogeneity
-	for (i=0; i<DCOLS; i++) {
-		for (j=0; j<DROWS; j++) {
-			if (pmap[i][j].layers[DUNGEON] == LEFT_WALL
-				|| pmap[i][j].layers[DUNGEON] == RIGHT_WALL
-				|| pmap[i][j].layers[DUNGEON] == BOTTOM_WALL) {
-				pmap[i][j].layers[DUNGEON] = TOP_WALL;
-			}
-		}
-	}
-	
-	// Now run the autoGenerators.
-	runAutogenerators();
-	
-	// Now remove diagonal openings.
-	removeDiagonalOpenings();
-	
-	if (D_INSPECT_LEVELGEN) {
-		dumpLevelToScreen();
-		message("Diagonal openings removed.", true);
-	}
-	
-	if (D_INSPECT_LEVELGEN) {
-		dumpLevelToScreen();
-		message("Bridges added.", true);
-	}
-	
-	// Now add some treasure machines.
-	addMachines();
-	
-	if (D_INSPECT_LEVELGEN) {
-		dumpLevelToScreen();
-		message("Machines added.", true);
-	}
-	
-	// Now knock down the boundaries between similar lakes where possible.
-	cleanUpLakeBoundaries();
-	
-	// Now add some bridges.
-	while (buildABridge());
-	
-	// Now remove orphaned doors and upgrade some doors to secret doors
+}
+
+void finishDoors() {
+    short i, j;
+    const short secretDoorChance = clamp((rogue.depthLevel - 1) * 67 / 25, 0, 67);
 	for (i=1; i<DCOLS-1; i++) {
 		for (j=1; j<DROWS-1; j++) {
 			if (pmap[i][j].layers[DUNGEON] == DOOR) {
 					// If the door has three or more pathing blocker neighbors in the four cardinal directions,
 					// then the door is orphaned and must be removed.
 					pmap[i][j].layers[DUNGEON] = FLOOR;
-				} else if (rand_percent(levelSpec.secretDoorChance)) {
+				} else if (rand_percent(secretDoorChance)) {
 					pmap[i][j].layers[DUNGEON] = SECRET_DOOR;
 				}
 			}
 		}
 	}
-	
-	// Now finish any exposed granite with walls and revert any unexposed walls to granite
-	for (i=0; i<DCOLS; i++) {
-		for (j=0; j<DROWS; j++) {
-			if (pmap[i][j].layers[DUNGEON] == GRANITE) {
-				foundExposure = false;
-				for (dir = 0; dir < 8 && !foundExposure; dir++) {
-					x1 = i + nbDirs[dir][0];
-					y1 = j + nbDirs[dir][1];
-					if (coordinatesAreInMap(x1, y1)
-						&& (!cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_VISION) || !cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_PASSABILITY))) {
-                        
-						pmap[i][j].layers[DUNGEON] = PERM_WALL;
-						foundExposure = true;
-					}
-				}
-			} else if (pmap[i][j].layers[DUNGEON] == TOP_WALL || pmap[i][j].layers[DUNGEON] == PERM_WALL) {
-				foundExposure = false;
-				for (dir = 0; dir < 8 && !foundExposure; dir++) {
-					x1 = i + nbDirs[dir][0];
-					y1 = j + nbDirs[dir][1];
-					if (coordinatesAreInMap(x1, y1)
-						&& (!cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_VISION) || !cellHasTerrainFlag(x1, y1, T_OBSTRUCTS_PASSABILITY))) {
-                        
-						foundExposure = true;
-					}
-				}
-				if (foundExposure == false) {
-					pmap[i][j].layers[DUNGEON] = GRANITE;
-				}
-			}
+}
+
+void clearLevel() {
+    short i, j;
+    
+    for( i=0; i<DCOLS; i++ ) {
+		for( j=0; j<DROWS; j++ ) {
+			pmap[i][j].layers[DUNGEON] = GRANITE;
+			pmap[i][j].layers[LIQUID] = NOTHING;
+			pmap[i][j].layers[GAS] = NOTHING;
+			pmap[i][j].layers[SURFACE] = NOTHING;
+			pmap[i][j].machineNumber = 0;
+			pmap[i][j].rememberedTerrain = NOTHING;
+			pmap[i][j].rememberedItemCategory = 0;
+			pmap[i][j].flags = 0;
+			pmap[i][j].volume = 0;
 		}
 	}
-	
-	if (D_INSPECT_LEVELGEN) {
-		dumpLevelToScreen();
-		message("Finishing touches added. Level has been generated.", true);
-	}
 }
 
 // Scans the map in random order looking for a good place to build a bridge.
 	bridgeRatioX = (short) (100 + (100 + 100 * rogue.depthLevel / 9) * rand_range(10, 20) / 10);
 	bridgeRatioY = (short) (100 + (400 + 100 * rogue.depthLevel / 18) * rand_range(10, 20) / 10);
 	
-	for (i=0; i<DCOLS; i++) {
-		nCols[i] = i;
-	}
-	for (i=0; i<DROWS; i++) {
-		nRows[i] = i;
-	}
-	shuffleList(nCols, DCOLS);
-	shuffleList(nRows, DROWS);
+	fillSequentialList(nCols, DCOLS);
+    shuffleList(nCols, DCOLS);
+    fillSequentialList(nRows, DROWS);
+    shuffleList(nRows, DROWS);
 	
 	for (i2=1; i2<DCOLS-1; i2++) {
 		i = nCols[i2];
 					}
 				}
 				if (k < DCOLS
-					&& (k - i > 3) // Can't have bridges shorter than 3 meters.
+					&& (k - i > 3) // Can't have bridges shorter than 3 spaces.
 					&& foundExposure
 					&& !cellHasTerrainFlag(k, j, T_PATHING_BLOCKER | T_CAN_BE_BRIDGED) // Must end on an unobstructed land tile.
 					&& !pmap[k][j].machineNumber // Cannot end in a machine.
 	return false;
 }
 
+// This is the master function for digging out a dungeon level.
+// Finishing touches -- items, monsters, staircases, etc. -- are handled elsewhere.
+void digDungeon() {
+	short i, j;
+    
+    short **grid;
+	
+	rogue.machineNumber = 0;
+	
+	topBlobMinX = topBlobMinY = blobWidth = blobHeight = 0;
+	
+#ifdef AUDIT_RNG
+	char RNGMessage[100];
+	sprintf(RNGMessage, "\n\n\nDigging dungeon level %i:\n", rogue.depthLevel);
+	RNGLog(RNGMessage);
+#endif
+	
+	// Clear level and fill with granite
+	clearLevel();
+    
+    grid = allocGrid();
+    carveDungeon(grid);
+    addLoops(grid);
+    for (i=0; i<DCOLS; i++) {
+        for (j=0; j<DROWS; j++) {
+            if (grid[i][j] == 1) {
+                pmap[i][j].layers[DUNGEON] = FLOOR;
+            } else if (grid[i][j] == 2) {
+                pmap[i][j].layers[DUNGEON] = (rand_percent(60) && rogue.depthLevel < DEEPEST_LEVEL ? DOOR : FLOOR);
+            }
+        }
+    }
+    freeGrid(grid);
+    
+    finishWalls(false);
+	
+	if (D_INSPECT_LEVELGEN) {
+		dumpLevelToScreen();
+        temporaryMessage("Carved into the granite:", true);
+	}
+	//DEBUG printf("\n%i loops created.", numLoops);
+	//DEBUG logLevel();
+	
+	// Time to add lakes and chasms. Strategy is to generate a series of blob lakes of decreasing size. For each lake,
+	// propose a position, and then check via a flood fill that the level would remain connected with that placement (i.e. that
+	// each passable tile can still be reached). If not, make 9 more placement attempts before abandoning that lake
+	// and proceeding to generate the next smaller one.
+	// Canvas sizes start at 30x15 and decrease by 2x1 at a time down to a minimum of 20x10. Min generated size is always 4x4.
+	
+	// DEBUG logLevel();
+	
+	// Now design the lakes and then fill them with various liquids (lava, water, chasm, brimstone).
+    short **lakeMap = allocGrid();
+	designLakes(lakeMap);
+	fillLakes(lakeMap);
+    freeGrid(lakeMap);
+	
+	// Now run the autoGenerators.
+	runAutogenerators();
+	
+	// Now remove diagonal openings.
+	removeDiagonalOpenings();
+	
+	if (D_INSPECT_LEVELGEN) {
+		dumpLevelToScreen();
+		temporaryMessage("Diagonal openings removed.", true);
+	}
+	
+	if (D_INSPECT_LEVELGEN) {
+		dumpLevelToScreen();
+		temporaryMessage("Bridges added.", true);
+	}
+	
+	// Now add some treasure machines.
+	addMachines();
+	
+	if (D_INSPECT_LEVELGEN) {
+		dumpLevelToScreen();
+		temporaryMessage("Machines added.", true);
+	}
+	
+	// Now knock down the boundaries between similar lakes where possible.
+	cleanUpLakeBoundaries();
+	
+	// Now add some bridges.
+	while (buildABridge());
+    
+	// Now remove orphaned doors and upgrade some doors to secret doors
+    finishDoors();
+	
+	// Now finish any exposed granite with walls and revert any unexposed walls to granite
+	finishWalls(true);
+	
+	if (D_INSPECT_LEVELGEN) {
+		dumpLevelToScreen();
+		temporaryMessage("Finishing touches added. Level has been generated.", true);
+	}
+}
+
 void updateMapToShore() {
 	short i, j;
 	short **costMap;
 	
 	rogue.updatedMapToShoreThisTurn = true;
 	
-	costMap = allocDynamicGrid();
+	costMap = allocGrid();
 	
 	// Calculate the map to shore for this level
 	if (!rogue.mapToShore) {
-		rogue.mapToShore = allocDynamicGrid();
-		fillDynamicGrid(rogue.mapToShore, 0);
+		rogue.mapToShore = allocGrid();
+		fillGrid(rogue.mapToShore, 0);
 	}
 	for (i=0; i<DCOLS; i++) {
 		for (j=0; j<DROWS; j++) {
 			if (cellHasTerrainFlag(i, j, T_OBSTRUCTS_PASSABILITY)) {
-				costMap[i][j] = PDS_OBSTRUCTION;
+				costMap[i][j] = cellHasTerrainFlag(i, j, T_OBSTRUCTS_DIAGONAL_MOVEMENT) ? PDS_OBSTRUCTION : PDS_FORBIDDEN;
 				rogue.mapToShore[i][j] = 30000;
 			} else {
 				costMap[i][j] = 1;
 				rogue.mapToShore[i][j] = (cellHasTerrainFlag(i, j, T_LAVA_INSTA_DEATH | T_IS_DEEP_WATER | T_AUTO_DESCENT)
-										  && !cellHasTerrainFlag(i, j, T_IS_SECRET)) ? 30000 : 0;
+										  && !cellHasTMFlag(i, j, TM_IS_SECRET)) ? 30000 : 0;
 			}
 		}
 	}
 	dijkstraScan(rogue.mapToShore, costMap, true);
-	freeDynamicGrid(costMap);
+	freeGrid(costMap);
 }
 
-void augmentAccessMapWithWaypoint(char accessMap[DCOLS][DROWS], boolean WPactive[MAX_WAYPOINTS], short x, short y) {
-	short i, j;
+// Calculates the distance map for the given waypoint.
+// This is called on all waypoints during setUpWaypoints(),
+// and then one waypoint is recalculated per turn thereafter.
+void refreshWaypoint(short wpIndex) {
+    short **costMap;
+    creature *monst;
+    
+    costMap = allocGrid();
+    populateGenericCostMap(costMap);
+    for (monst = monsters->nextCreature; monst != NULL; monst = monst->nextCreature) {
+        if ((monst->creatureState == MONSTER_SLEEPING || (monst->info.flags & MONST_IMMOBILE) || (monst->bookkeepingFlags & MONST_CAPTIVE))
+            && costMap[monst->xLoc][monst->yLoc] >= 0) {
+            
+            costMap[monst->xLoc][monst->yLoc] = PDS_FORBIDDEN;
+        }
+    }
+    fillGrid(rogue.wpDistance[wpIndex], 30000);
+    rogue.wpDistance[wpIndex][rogue.wpCoordinates[wpIndex][0]][rogue.wpCoordinates[wpIndex][1]] = 0;
+    dijkstraScan(rogue.wpDistance[wpIndex], costMap, true);
+    freeGrid(costMap);
+}
+
+void setUpWaypoints() {
+	short i, j, sCoord[DCOLS * DROWS], x, y;
 	char grid[DCOLS][DROWS];
 	
 	zeroOutGrid(grid);
-	getFOVMask(grid, x, y, WAYPOINT_SIGHT_RADIUS, T_WAYPOINT_BLOCKER, DOORWAY, false);
-	
 	for (i=0; i<DCOLS; i++) {
 		for (j=0; j<DROWS; j++) {
-			if (grid[i][j] && accessMap[i][j] != 3) { // i.e. if it's in the FOV mask and is not a wall/trap/lava/water
-				accessMap[i][j] = 1;
-			}
-		}
-	}
-	accessMap[x][y] = 1;
-	
-	for (i = 0; i < numberOfWaypoints; i++) {
-		if (!WPactive[i] && (accessMap[waypoints[i].x][waypoints[i].y] == 1 || accessMap[waypoints[i].x][waypoints[i].y] == 2)) {
-			WPactive[i] = true;
-			if (waypoints[i].pointsTo[0] && waypoints[i].pointsTo[1]) {
-				accessMap[waypoints[i].pointsTo[0]][waypoints[i].pointsTo[1]] = 1;
-			}
-			augmentAccessMapWithWaypoint(accessMap, WPactive, waypoints[i].x, waypoints[i].y); // recurse!
-		}
-	}
-}
-
-short findEdges(char accessMap[DCOLS][DROWS], char centralityMap[DCOLS][DROWS]) {
-	short i, j, count = 0;
-	enum directions dir;
-	
-	for (i=0; i<DCOLS; i++) {
-		for (j=0; j<DROWS; j++) {
-			if (accessMap[i][j] == 2) {
-				count += centralityMap[i][j];
-			} else if (accessMap[i][j] == 1) {
-				for (dir = 0; dir < 4; dir++) {
-					if (accessMap[i + nbDirs[dir][0]][j + nbDirs[dir][1]] == 0) {
-						accessMap[i][j] = 2;
-						count += centralityMap[i][j];
-						break;
-					}
-				}
-			}
-		}
-	}
-	return count;
-}
-
-boolean waypointLinkedTo(short i, short j) {
-	short k;
-	if (waypoints[i].pointsTo[0] == waypoints[j].x && waypoints[i].pointsTo[1] == waypoints[j].y) {
-		return true;
-	}
-	for (k = 0; k < waypoints[i].connectionCount; k++) {
-		if (waypoints[i].connection[k][0] == waypoints[j].x && waypoints[i].connection[k][1] == waypoints[j].y) {
-			return true;
-		}
-	}
-	return false;
-}
-
-void setUpWaypoints() {
-	short i, j, k, x, y, edgesWeightCount, randIndex;
-	room *theRoom;
-	char accessMap[DCOLS][DROWS], centralityMap[DCOLS][DROWS], grid[DCOLS][DROWS];
-	boolean WPactive[MAX_WAYPOINTS];
-	
-	numberOfWaypoints = 0;
-	
-	for (i = 0; i < MAX_WAYPOINTS; i++) {
-		waypoints[i].connectionCount = 0;
-		waypoints[i].pointsTo[0] = 0;
-		waypoints[i].pointsTo[1] = 0;
-		WPactive[i] = false;
-	}
-	
-	// 1. generate the waypoints.
-	
-	// initialize the accessMap
-	
-	// set up access map
-	// key: 0 = passable but not accessible
-	//		1 = accessible
-	//		2 = accessible and adjacent to type 0
-	//		3 = wall, pit, lava, deep water, pit trap
-	
-	zeroOutGrid(accessMap);
-	
-	for (i=0; i<DCOLS; i++) {
-		for (j=0; j<DROWS; j++) {
-			if (cellHasTerrainFlag(i, j, T_WAYPOINT_BLOCKER) || (pmap[i][j].flags & DOORWAY)) {
-				accessMap[i][j] = 3;
-			}
-		}
-	}
-	
-	// DEBUG logBuffer(accessMap);
-	
-	// Set up centrality map, to be used as probability weights for picking waypoint candidates.
-	// The idea here is that we want a weight map of how many cells are visible from a particular cell.
-	// Could just do a FOV from each cell and add it all up, but that would be very expensive.
-	// Instead, randomly choose N passable points from around the map, compute FOV for them,
-	// and add all the FOV maps. So it's really a map of how many of these random points are visible
-	// from any particular cell -- a monte carlo approximation which approaches the right proportions
-	// as N->infty. I use N=200.
-	
-	for (i=0; i<DCOLS; i++) {
-		for (j=0; j<DROWS; j++) {
-			centralityMap[i][j] = 1; // starting it at 0 makes the generation prone to failure if unlucky
-		}
-	}
-	for (k=0; k<200; k++) {
-		x = rand_range(1, DCOLS - 2);
-		y = rand_range(1, DROWS - 2);
-		if (accessMap[x][y] == 3) { // i.e. passable
-			zeroOutGrid(grid);
-			getFOVMask(grid, x, y, WAYPOINT_SIGHT_RADIUS, T_WAYPOINT_BLOCKER, DOORWAY, false);
-			for (i=1; i<DCOLS - 1; i++) {
-				for (j=1; j<DROWS - 1; j++) {
-					if (grid[i][j]) {
-						centralityMap[i][j]++;
-					}
-				}
-			}
-		}
-	}
-	
-	// DEBUG logBuffer(accessMap);
-	
-	//	a. place them on either side of doors and make them point to each other.
-	
-	// cycle through the rooms
-	for (theRoom = rooms->nextRoom; theRoom != NULL; theRoom = theRoom->nextRoom) {
-		// cycle through the doors of the room
-		for (i = 0; i < theRoom->numberOfSiblings; i++) {
-			
-			// waypoint is immediately inside the door
-			waypoints[numberOfWaypoints].x = theRoom->doors[i].x +
-			nbDirs[oppositeDirection(theRoom->doors[i].direction)][0];
-			waypoints[numberOfWaypoints].y = theRoom->doors[i].y +
-			nbDirs[oppositeDirection(theRoom->doors[i].direction)][1];
-			
-			//augmentAccessMapWithWaypoint(accessMap, WPactive, waypoints[numberOfWaypoints].x, waypoints[numberOfWaypoints].y);
-			
-			// DEBUG logBuffer(accessMap);
-			
-			// waypoint points to the cell immediately on the other side of the door
-			waypoints[numberOfWaypoints].pointsTo[0] = theRoom->doors[i].x + nbDirs[theRoom->doors[i].direction][0];
-			waypoints[numberOfWaypoints].pointsTo[1] = theRoom->doors[i].y + nbDirs[theRoom->doors[i].direction][1];
-			
-			// link waypoint to the other waypoints of the room, if they're not too far apart
-			waypoints[numberOfWaypoints].connectionCount = 0;
-			for (j = 0; j < theRoom->numberOfSiblings; j++) {
-				if (i != j // don't want to link the waypoint to itself
-					&& (distanceBetween(waypoints[numberOfWaypoints].x, waypoints[numberOfWaypoints].y,
-											  theRoom->doors[j].x, theRoom->doors[j].y) < 21
-						|| specifiedPathBetween(waypoints[numberOfWaypoints].x, waypoints[numberOfWaypoints].y,
-												theRoom->doors[j].x, theRoom->doors[j].y, T_WAYPOINT_BLOCKER, DOORWAY))
-					&& theRoom->width <= 20) { // not a cave
-					waypoints[numberOfWaypoints].connection[waypoints[numberOfWaypoints].connectionCount][0]
-					= theRoom->doors[j].x +	nbDirs[oppositeDirection(theRoom->doors[j].direction)][0];
-					
-					waypoints[numberOfWaypoints].connection[waypoints[numberOfWaypoints].connectionCount][1]
-					= theRoom->doors[j].y +	nbDirs[oppositeDirection(theRoom->doors[j].direction)][1];
-					
-					waypoints[numberOfWaypoints].connectionCount++;
-				}
-			}
-			
-			numberOfWaypoints++;
-		}
-	}
-	
-	// b. Place them near anywhere that is not within line of sight of a waypoint. This will cover caves and also certain
-	// rooms in which there is an alcove not visible from the doors.
-	
-	// Activate the first waypoint! This process of activation is to ensure that all the WP's are connected.
-	WPactive[0] = true;
-	accessMap[waypoints[0].pointsTo[0]][waypoints[0].pointsTo[1]] = 1;
-	augmentAccessMapWithWaypoint(accessMap, WPactive, waypoints[0].x, waypoints[0].y);
-	
-	edgesWeightCount = findEdges(accessMap, centralityMap);
-	// DEBUG logBuffer(accessMap);
-	while (edgesWeightCount) {
-		randIndex = rand_range(1, edgesWeightCount);
-		
-		for (i=0; i<DCOLS; i++)	{
-			for (j=0; j<DROWS; j++) {
-				if (accessMap[i][j] == 2) {
-					randIndex -= centralityMap[i][j];
-					if (randIndex <= 0) {
-						// this is the edge we've chosen. Add a waypoint.
-						augmentAccessMapWithWaypoint(accessMap, WPactive, i, j);
-						
-						waypoints[numberOfWaypoints].x = i;
-						waypoints[numberOfWaypoints].y = j;
-						numberOfWaypoints++;
-						
-						// crappy way to break out of both loops:
-						i = DCOLS;
-						j = DROWS;
-					}
-				}
-			}
-		}
-		
-		edgesWeightCount = findEdges(accessMap, centralityMap);
-		// DEBUG logBuffer(accessMap);
-		// DEBUG printf("\n%i waypoints in the above map\n", numberOfWaypoints);
-	}
-	
-	// 2. link the waypoints together.
-	
-	// i is the waypoint we're connecting
-	for (i=0; i < numberOfWaypoints; i++) {
-		x = waypoints[i].x;
-		y = waypoints[i].y;
-		
-		zeroOutGrid(grid);
-		getFOVMask(grid, x, y, WAYPOINT_SIGHT_RADIUS, T_WAYPOINT_BLOCKER, DOORWAY, false);
-		
-		// j is the waypoint to which we're contemplating linking it
-		for (j=0; j < numberOfWaypoints && waypoints[i].connectionCount <= 10; j++) {
-			if (i == j) {
-				continue; // don't link to itself
-			}
-			
-			// verify that the waypoint isn't already connected to the target
-			if (!waypointLinkedTo(i, j)) {
-				// check to make sure the path between is passable
-				if (grid[waypoints[j].x][waypoints[j].y]) {
-					waypoints[i].connection[waypoints[i].connectionCount][0] = waypoints[j].x;
-					waypoints[i].connection[waypoints[i].connectionCount][1] = waypoints[j].y;					
-					waypoints[i].connectionCount++;
-					
-					// create reciprocal link if it doesn't already exist (important to avoid one-way links)
-					if (!waypointLinkedTo(j, i)) {
-						waypoints[j].conn